fix(drivers): refresh cached list and summary when returning from details
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||||
import { flushPromises, shallowMount, type VueWrapper } from '@vue/test-utils'
|
import { flushPromises, shallowMount, type VueWrapper } from '@vue/test-utils'
|
||||||
|
import { defineComponent, h, KeepAlive, ref } from 'vue'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import { ApiResponseError } from '@/shared/api/http'
|
import { ApiResponseError } from '@/shared/api/http'
|
||||||
import * as api from '../api/drivers'
|
import * as api from '../api/drivers'
|
||||||
@@ -44,6 +45,47 @@ beforeEach(() => {
|
|||||||
afterEach(() => { wrappers.splice(0).forEach(wrapper => wrapper.unmount()); vi.unstubAllGlobals() })
|
afterEach(() => { wrappers.splice(0).forEach(wrapper => wrapper.unmount()); vi.unstubAllGlobals() })
|
||||||
|
|
||||||
describe('driver page workflows', () => {
|
describe('driver page workflows', () => {
|
||||||
|
it('refreshes a cached list once on return and retains applied filters and pagination', async () => {
|
||||||
|
const showingList = ref(true)
|
||||||
|
const host = defineComponent({ setup: () => () => h(KeepAlive, null, { default: () => showingList.value ? h(DriversView) : h('div', '司机详情') }) })
|
||||||
|
const wrapper = shallowMount(host, { global: { stubs: { ...stubs, KeepAlive: false, DriversView: false }, directives: { loading: () => undefined } } })
|
||||||
|
wrappers.push(wrapper)
|
||||||
|
await flushPromises()
|
||||||
|
expect(api.listDrivers).toHaveBeenCalledTimes(1)
|
||||||
|
const list = wrapper.findComponent(DriversView)
|
||||||
|
const toolbar = list.findComponent({ name: 'QueryToolbar' })
|
||||||
|
toolbar.vm.$emit('update:modelValue', { keyword: 'DR-TEST', licenseState: 'EXPIRING' })
|
||||||
|
toolbar.vm.$emit('search'); await flushPromises()
|
||||||
|
list.findComponent({ name: 'DataTable' }).vm.$emit('update:page', 2); await flushPromises()
|
||||||
|
expect(api.listDrivers).toHaveBeenCalledTimes(3)
|
||||||
|
showingList.value = false; await flushPromises()
|
||||||
|
const refreshed = { ...structuredClone(driver), recordVersion: 3, reviewStatus: 'APPROVED' as const }
|
||||||
|
vi.mocked(api.listDrivers).mockResolvedValueOnce({ records: [refreshed], page: 2, size: 20, total: 1, pages: 1, summary: { total: 1, owned: 1, dispatched: 0, expiringSoon: 1, expired: 0 } })
|
||||||
|
showingList.value = true; await flushPromises()
|
||||||
|
expect(api.listDrivers).toHaveBeenCalledTimes(4)
|
||||||
|
expect(api.listDrivers).toHaveBeenLastCalledWith({ keyword: 'DR-TEST', licenseState: 'EXPIRING', region: undefined, ownership: undefined, active: undefined, reviewStatus: undefined, page: 2, size: 20 })
|
||||||
|
expect(wrapper.findComponent({ name: 'DataTable' }).props('data')).toEqual([refreshed])
|
||||||
|
expect(wrapper.text()).toContain('司机总数1')
|
||||||
|
expect(api.getDriver).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('ignores a list response that arrives while its cached page is deactivated', async () => {
|
||||||
|
let resolveList!: (value: Awaited<ReturnType<typeof api.listDrivers>>) => void
|
||||||
|
vi.mocked(api.listDrivers).mockReturnValueOnce(new Promise(resolve => { resolveList = resolve }))
|
||||||
|
const showingList = ref(true)
|
||||||
|
const host = defineComponent({ setup: () => () => h(KeepAlive, null, { default: () => showingList.value ? h(DriversView) : h('div', '司机详情') }) })
|
||||||
|
const wrapper = shallowMount(host, { global: { stubs: { ...stubs, KeepAlive: false, DriversView: false }, directives: { loading: () => undefined } } })
|
||||||
|
wrappers.push(wrapper)
|
||||||
|
await flushPromises()
|
||||||
|
const list = wrapper.findComponent(DriversView)
|
||||||
|
showingList.value = false; await flushPromises()
|
||||||
|
resolveList({ records: [driver], page: 1, size: 20, total: 24, pages: 2, summary: { total: 24, owned: 20, dispatched: 4, expiringSoon: 3, expired: 1 } }); await flushPromises()
|
||||||
|
expect(list.findComponent({ name: 'DataTable' }).props('data')).toEqual([])
|
||||||
|
showingList.value = true; await flushPromises()
|
||||||
|
expect(api.listDrivers).toHaveBeenCalledTimes(2)
|
||||||
|
expect(wrapper.findComponent({ name: 'DataTable' }).props('data')).toEqual([driver])
|
||||||
|
})
|
||||||
|
|
||||||
it('loads one paged request with server summary and does not request each driver or file', async () => {
|
it('loads one paged request with server summary and does not request each driver or file', async () => {
|
||||||
const wrapper = mount(DriversView)
|
const wrapper = mount(DriversView)
|
||||||
await flushPromises()
|
await flushPromises()
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import '@/styles/adminDensity.css'
|
import '@/styles/adminDensity.css'
|
||||||
import { onBeforeUnmount, onMounted, ref, watch } from 'vue'
|
import { onActivated, onBeforeUnmount, onDeactivated, onMounted, ref, watch } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
import { Plus } from '@lucide/vue'
|
import { Plus } from '@lucide/vue'
|
||||||
@@ -26,6 +26,7 @@ const rows = ref<DriverDetail[]>([]), total = ref(0), page = ref(1), pageSize =
|
|||||||
const error = ref<ApiErrorDetails | null>(null), summary = ref<DriverSummary | null>(null)
|
const error = ref<ApiErrorDetails | null>(null), summary = ref<DriverSummary | null>(null)
|
||||||
const filters = ref<QueryModel>({ keyword: '', region: '', ownership: '', active: '', reviewStatus: '', licenseState: '' })
|
const filters = ref<QueryModel>({ keyword: '', region: '', ownership: '', active: '', reviewStatus: '', licenseState: '' })
|
||||||
let applied: Omit<DriverQuery, 'page' | 'size'> = {}, request = 0
|
let applied: Omit<DriverQuery, 'page' | 'size'> = {}, request = 0
|
||||||
|
let refreshOnReturn = false
|
||||||
const editorOpen = ref(false), editing = ref<DriverDetail | null>(null), actionId = ref('')
|
const editorOpen = ref(false), editing = ref<DriverDetail | null>(null), actionId = ref('')
|
||||||
const previewOpen = ref(false), preview = ref<DriverDetail | null>(null)
|
const previewOpen = ref(false), preview = ref<DriverDetail | null>(null)
|
||||||
const fields: QueryField[] = [
|
const fields: QueryField[] = [
|
||||||
@@ -101,6 +102,10 @@ function saved(row: DriverDetail) {
|
|||||||
watch(editorOpen, (value, previous) => { if (!value && previous) void load() })
|
watch(editorOpen, (value, previous) => { if (!value && previous) void load() })
|
||||||
watch([page, pageSize], () => void load())
|
watch([page, pageSize], () => void load())
|
||||||
onMounted(load)
|
onMounted(load)
|
||||||
|
onDeactivated(() => { request++; refreshOnReturn = true })
|
||||||
|
onActivated(() => {
|
||||||
|
if (refreshOnReturn) { refreshOnReturn = false; void load() }
|
||||||
|
})
|
||||||
onBeforeUnmount(() => { request++ })
|
onBeforeUnmount(() => { request++ })
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user