diff --git a/src/modules/overseas-master-data/pages/DriversBehavior.test.ts b/src/modules/overseas-master-data/pages/DriversBehavior.test.ts
index 24106b5e..8e12686c 100644
--- a/src/modules/overseas-master-data/pages/DriversBehavior.test.ts
+++ b/src/modules/overseas-master-data/pages/DriversBehavior.test.ts
@@ -29,7 +29,7 @@ const layout = { template: '
' }
const button = { props: ['disabled', 'loading'], template: '' }
const dialog = { name: 'AppDialog', props: ['modelValue'], emits: ['confirm', 'update:modelValue'], template: '
' }
const input = { name: 'ElInput', props: ['modelValue'], emits: ['update:modelValue'], template: '' }
-const table = { name: 'DataTable', props: ['data', 'page', 'pageSize', 'total'], emits: ['update:page', 'update:pageSize'], template: '' }
+const table = { name: 'DataTable', props: ['data', 'page', 'pageSize', 'total', 'loading'], emits: ['update:page', 'update:pageSize'], template: '' }
const stubs = { AdminPage: layout, ListPageLayout: layout, DetailPageLayout: layout, FormSection: layout, DataTable: table, QueryToolbar: true, RowActions: true, StatusBadge: true, LoadErrorState: true, AppDialog: dialog, DriverEditorDialog: true, DriverLicensePreview: true, ElButton: button, ElForm: layout, ElFormItem: layout, ElInput: input, ElSelect: { ...input, name: 'ElSelect' }, ElOption: true, ElDatePicker: { ...input, name: 'ElDatePicker' } }
const wrappers: VueWrapper[] = []
function mount(component: object, props: Record = {}): VueWrapper {
@@ -63,6 +63,65 @@ beforeEach(() => {
afterEach(() => { wrappers.splice(0).forEach(wrapper => wrapper.unmount()); vi.unstubAllGlobals() })
describe('driver page workflows', () => {
+ it.each(['success', 'failure'] as const)('keeps B after cached A detail arrives with late %s', async outcome => {
+ let resolveA!: (value: DriverDetail) => void, rejectA!: (reason: Error) => void
+ const pendingA = new Promise((resolve, reject) => { resolveA = resolve; rejectA = reject })
+ const other = { ...structuredClone(driver), id: '9223372036854775702', driverNo: 'DR-OTHER', details: { ...driver.details, name: '另一司机' } }
+ vi.mocked(api.getDriver).mockImplementation(id => id === driver.id ? pendingA : Promise.resolve(other))
+ const { wrapper, show } = cachedDetails(); await flushPromises()
+ expect(api.getDriver).toHaveBeenCalledExactlyOnceWith(driver.id)
+ show(null); await flushPromises(); show(other.id); await flushPromises()
+ expect(wrapper.text()).toContain('另一司机')
+ const before = wrapper.text()
+ if (outcome === 'success') resolveA({ ...structuredClone(driver), details: { ...driver.details, name: '迟到A司机' } })
+ else rejectA(new Error('迟到A详情失败'))
+ await flushPromises()
+ expect(wrapper.text()).toBe(before)
+ expect(wrapper.findComponent({ name: 'LoadErrorState' }).exists()).toBe(false)
+ const fresh = { ...structuredClone(driver), details: { ...driver.details, name: '新请求A司机' } }
+ vi.mocked(api.getDriver).mockResolvedValueOnce(fresh)
+ show(null); await flushPromises(); show(driver.id); await flushPromises()
+ expect(api.getDriver).toHaveBeenCalledTimes(3)
+ expect(api.getDriver).toHaveBeenLastCalledWith(driver.id)
+ expect(wrapper.text()).toContain('新请求A司机')
+ expect(wrapper.text()).not.toContain('迟到A')
+ expect(wrapper.findComponent({ name: 'LoadErrorState' }).exists()).toBe(false)
+ })
+
+ it.each(['success', 'failure'] as const)('keeps B history after cached A history arrives with late %s', async outcome => {
+ type HistoryPage = Awaited>
+ let resolveA!: (value: HistoryPage) => void, rejectA!: (reason: Error) => void
+ const pendingA = new Promise((resolve, reject) => { resolveA = resolve; rejectA = reject })
+ const other = { ...structuredClone(driver), id: '9223372036854775702', driverNo: 'DR-OTHER', details: { ...driver.details, name: '另一司机' } }
+ const history = (snapshot: DriverDetail, total: number): HistoryPage => ({ records: [{ recordVersion: snapshot.recordVersion, action: 'CREATE', reason: null, operatorId: 'test-operator', occurredAt: '2026-10-02T12:00:00', snapshot }], page: 1, size: 20, total, pages: 1 })
+ const bHistory = history(other, 15), aHistory = history(driver, 3)
+ vi.mocked(api.getDriver).mockImplementation(async id => structuredClone(id === driver.id ? driver : other))
+ vi.mocked(api.listDriverHistory).mockImplementation(id => id === driver.id ? pendingA : Promise.resolve(bHistory))
+ const { wrapper, show } = cachedDetails(); await flushPromises()
+ await findButton(wrapper, '查看操作历史').trigger('click'); await flushPromises()
+ expect(api.listDriverHistory).toHaveBeenCalledExactlyOnceWith(driver.id, 1, 20)
+ show(null); await flushPromises(); show(other.id); await flushPromises()
+ await findButton(wrapper, '查看操作历史').trigger('click'); await flushPromises()
+ const before = wrapper.text(), tableB = wrapper.findComponent({ name: 'DataTable' })
+ expect(tableB.props('data')).toEqual(bHistory.records); expect(tableB.props('total')).toBe(15); expect(tableB.props('loading')).toBe(false)
+ if (outcome === 'success') resolveA(aHistory)
+ else rejectA(new Error('迟到A历史失败'))
+ await flushPromises()
+ expect(wrapper.text()).toBe(before)
+ expect(tableB.props('data')).toEqual(bHistory.records); expect(tableB.props('total')).toBe(15); expect(tableB.props('loading')).toBe(false)
+ expect(wrapper.findComponent({ name: 'LoadErrorState' }).exists()).toBe(false)
+ show(null); await flushPromises(); show(driver.id); await flushPromises()
+ expect(wrapper.findComponent({ name: 'DataTable' }).exists()).toBe(false)
+ expect(api.listDriverHistory).toHaveBeenCalledTimes(2)
+ vi.mocked(api.listDriverHistory).mockResolvedValueOnce(aHistory)
+ await findButton(wrapper, '查看操作历史').trigger('click'); await flushPromises()
+ expect(api.listDriverHistory).toHaveBeenCalledTimes(3)
+ expect(api.listDriverHistory).toHaveBeenLastCalledWith(driver.id, 1, 20)
+ expect(wrapper.findComponent({ name: 'DataTable' }).props('data')).toEqual(aHistory.records)
+ expect(wrapper.findComponent({ name: 'DataTable' }).props('total')).toBe(3)
+ expect(wrapper.findComponent({ name: 'LoadErrorState' }).exists()).toBe(false)
+ })
+
it('does not fetch an absent driver on return to list and loads the valid driver on reentry', async () => {
const { wrapper, show } = cachedDetails(); await flushPromises()
expect(api.getDriver).toHaveBeenCalledExactlyOnceWith(driver.id)