/** * 测试 harness:为「页面/功能」组件测试提供统一装配。 * * 设计原则: * - 所有请求都收敛在 @/services/api,测试时只需 vi.mock('@/services/api') 即可确定性驱动状态。 * - pinia 带 persist 插件(与生产一致),避免 stores 在测试里行为漂移。 * - 用 memory history 路由,避免真实 History API 副作用。 * - 子组件默认 shallowMount 打桩,页面测试只关注「页面自身逻辑」:把子组件事件 emit 出来即可。 * * 用法见 tests/views/*.spec.js */ import { shallowMount } from '@vue/test-utils' import { createPinia, setActivePinia } from 'pinia' import piniaPluginPersistedstate from 'pinia-plugin-persistedstate' import { createRouter, createMemoryHistory } from 'vue-router' /** * 创建一个带 persist 插件的全新 pinia,并设为 active(便于测试里直接 useXStore())。 * * 注意:pinia 4 的 `pinia.use()` 在实例未安装到 app 前只把插件放入 toBeInstalled 队列, * 必须经过 `install(app)` 才会真正注册。页面测试经 @vue/test-utils 的 plugins 挂载会自动 install, * 但"纯 store 层测试"(setActivePinia + useXStore)不会,导致 persist 插件静默失效、 * store 上缺少 $hydrate/$persist。这里用一个假 app 触发 install,保证两类测试行为一致。 */ export function createTestPinia() { const pinia = createPinia() pinia.use(piniaPluginPersistedstate) pinia.install({ provide() {}, config: { globalProperties: {} } }) setActivePinia(pinia) return pinia } /** 创建一个 memory history 路由,并可预先定位到某个 location(含 query) */ export function createTestRouter(initialLocation = '/') { const router = createRouter({ history: createMemoryHistory(), routes: [ { path: '/:pathMatch(.*)*', name: 'catch-all', component: { template: '
' } }, ], }) router.push(initialLocation) return router } /** * 挂载一个页面/功能组件。 * @param {object} Component 组件定义(通常 import 自 @/views/...) * @param {object} [options] * @param {string} [options.location] 初始路由 location,如 '/login?redirect=/dashboard' * @param {object} [options.stubs] 额外子组件打桩(覆盖 shallowMount 默认打桩) * @param {object} [options.global] 透传给 @vue/test-utils 的 global 选项 * @param {object} [options.mountOptions] 透传其余 mount 选项 * @returns {Promise<{ wrapper, router, pinia }>} */ export async function mountPage(Component, options = {}) { const pinia = createTestPinia() const router = createTestRouter(options.location || '/') await router.isReady() const wrapper = shallowMount(Component, { global: { plugins: [pinia, router], stubs: options.stubs || {}, ...options.global, }, ...options.mountOptions, }) return { wrapper, router, pinia } }