import { flushPromises, mount } from '@vue/test-utils' import { createPinia, setActivePinia } from 'pinia' import { afterEach, beforeEach, expect, it, vi } from 'vitest' import { http } from '@/shared/api/http' import WorkCommandForm from './WorkCommandForm.vue' import { submitWorkCommand } from './workApi' import { followAction, workDetail, workItem } from './work.test-support' import { useAuthStore } from '@/core/auth/auth' vi.mock('@/core/auth/platformAuthorization', () => ({ listCurrentPlatformPermissionCodes: vi.fn().mockResolvedValue(['platform:appointment:followup']) })) vi.mock('@/core/access/accessPreview', () => ({ useAccessPreviewStore: () => ({ can: () => true }) })) vi.mock('element-plus', () => ({ ElMessage: { warning: vi.fn(), success: vi.fn() }, ElMessageBox: { confirm: vi.fn() } })) vi.mock('./workApi', async original => ({ ...await original(), submitWorkCommand: vi.fn() })) function token(userId: string, exp = Math.floor(Date.now() / 1000) + 1800) { // Keep the numeric JSON literal intact; constructing a JS number would lose precision before decoding. const payload = `{"user_id":${userId},"username":"test_operator","app_code":"PLATFORM","exp":${exp}}` return `header.${Buffer.from(payload).toString('base64url')}.signature` } beforeEach(() => { const values = new Map() vi.stubGlobal('localStorage', { getItem: (key: string) => values.get(key) ?? null, setItem: (key: string, value: string) => values.set(key, value), removeItem: (key: string) => values.delete(key), }) setActivePinia(createPinia()) vi.mocked(submitWorkCommand).mockResolvedValue({ item: workItem(), affectedWorkItemKeys: [] }) }) afterEach(() => { vi.restoreAllMocks(); vi.unstubAllGlobals() }) it.each([ ['login', '2110664514823360515'], ['login', '"2110664514823360515"'], ['restore', '2110664514823360515'], ['restore', '"2110664514823360515"'], ])('preserves the %s session ID through the real follow-up form for JSON %s', async (mode, userId) => { const accessToken = token(userId) if (mode === 'restore') { localStorage.setItem('cc-admin-token', accessToken) localStorage.setItem('cc-admin-permission-codes', '[]') } const auth = useAuthStore() if (mode === 'login') { vi.spyOn(http, 'post').mockResolvedValue({ code: '200', data: { accessToken, refreshToken: 'test-refresh', expiresIn: 1800, refreshExpiresIn: 604800 } }) await auth.login('test_operator', 'test-password') } expect(auth.user?.id).toBe('2110664514823360515') expect(auth.isAuthenticated).toBe(true) const wrapper = mount(WorkCommandForm, { props: { detail: workDetail({ item: workItem({ owner: null }) }), action: followAction, refreshing: false }, global: { provide: { 'appointment-work-leave-guard': () => () => undefined }, stubs: { ElButton: { template: '' }, LoadErrorState: true, ApiErrorMeta: true }, }, }) try { expect((wrapper.get('select').element as HTMLSelectElement).value).toBe('2110664514823360515') await wrapper.get('textarea').setValue('已联系负责人') await wrapper.get('form').trigger('submit') await flushPromises() expect(submitWorkCommand).toHaveBeenCalledWith('work-delivery-1', expect.objectContaining({ command: 'SAVE_FOLLOWUP', payload: { ownerId: '2110664514823360515', followupDate: '2026-10-15', note: '已联系负责人' }, })) } finally { wrapper.unmount() } }) it.each([token('2110664514823360515', 1), 'header.%.signature'])('does not restore an expired or malformed token: %s', accessToken => { localStorage.setItem('cc-admin-token', accessToken) const auth = useAuthStore() expect(auth.user).toBeNull() expect(auth.isAuthenticated).toBe(false) expect(localStorage.getItem('cc-admin-token')).toBeNull() })