import { mount, type VueWrapper } from '@vue/test-utils'; import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import { nextTick } from 'vue'; import App from '../App.vue'; import { LOCALE_STORAGE_KEY, createViewerI18n } from '../i18n'; import { router } from '../router'; // i18n 切换机制(顶栏 chrome 层):活动页级的「切换语言 → 重取内容」由 // activity.spec.ts 覆盖,此处聚焦首页 chrome(无需 api mock)。 // 逐挂载独立 i18n + 逐用例卸载(背景见 activity.spec.ts 头注)。 let i18n: ReturnType; let wrapper: VueWrapper; describe('viewer i18n', () => { beforeEach(async () => { localStorage.clear(); i18n = createViewerI18n('zh-CN'); await router.push('/'); await router.isReady(); wrapper = mount(App, { global: { plugins: [router, i18n] } }); }); afterEach(() => { wrapper.unmount(); }); it('switching locale re-renders messages and persists the choice', async () => { expect(wrapper.text()).toContain('打开相册链接即可观看'); expect(document.title).toBe('现场相册'); // UI-02 V01 紧凑语言控件:展开菜单 → 选择 English。 await wrapper.find('.locale-trigger').trigger('click'); await nextTick(); const english = wrapper.findAll('.locale-option').find((btn) => btn.text() === 'English'); expect(english).toBeDefined(); await english!.trigger('click'); await nextTick(); expect(wrapper.text()).toContain('Open an album link to start viewing'); expect(document.title).toBe('Live Gallery'); expect(document.documentElement.getAttribute('lang')).toBe('en'); expect(localStorage.getItem(LOCALE_STORAGE_KEY)).toBe('en'); // 切回 zh-CN 同样生效(菜单内当前语言 English 为勾选态)。 await wrapper.find('.locale-trigger').trigger('click'); await nextTick(); const options = wrapper.findAll('.locale-option'); const englishNow = options.find((btn) => btn.text() === 'English'); expect(englishNow!.attributes('aria-checked')).toBe('true'); const chinese = options.find((btn) => btn.text() === '中文'); expect(chinese!.attributes('aria-checked')).toBe('false'); await chinese!.trigger('click'); await nextTick(); expect(wrapper.text()).toContain('打开相册链接即可观看'); expect(localStorage.getItem(LOCALE_STORAGE_KEY)).toBe('zh-CN'); }); // UI-02 Task 6(V05 §5.4 分享链路闭环):分享 URL 携带 ?lang=,接收端 // 首载据此定语言——优先于本地持久化偏好;不合法值(fr/auto)自然回退。 it('initial locale honors the ?lang= share parameter over the stored preference', () => { localStorage.setItem(LOCALE_STORAGE_KEY, 'zh-CN'); window.history.pushState({}, '', '/a/token?lang=en'); expect(createViewerI18n().global.locale.value).toBe('en'); // 非法/不支持值:回落到持久化偏好。 window.history.pushState({}, '', '/a/token?lang=auto'); expect(createViewerI18n().global.locale.value).toBe('zh-CN'); window.history.pushState({}, '', '/a/token?lang=fr'); expect(createViewerI18n().global.locale.value).toBe('zh-CN'); // 无参数:持久化偏好照旧生效。 window.history.pushState({}, '', '/a/token'); expect(createViewerI18n().global.locale.value).toBe('zh-CN'); // 参数不写 localStorage——接收者未做选择,显式切换才持久化。 expect(localStorage.getItem(LOCALE_STORAGE_KEY)).toBe('zh-CN'); }); });