refactor: extend vitest setup with full chrome/browser mocks and i18n utility mock

This commit is contained in:
雨霖铃
2026-05-22 23:02:41 +08:00
parent 7efed8673a
commit 756ece31d3
+36 -13
View File
@@ -1,18 +1,29 @@
import '@testing-library/jest-dom'; import '@testing-library/jest-dom';
import { vi, beforeEach, afterEach } from 'vitest'; import { afterEach, beforeEach, vi } from 'vitest';
vi.mock('@/utils/useLazyTranslation', () => ({
useLazyTranslation: (ns?: string) => ({
// 自动承接命名空间前缀过滤,100% 模拟真实多语种直出
t: (key: string) => (ns ? `${ns}:${key}` : key),
i18n: {
changeLanguage: vi.fn().mockResolvedValue(undefined),
language: 'zh-CN',
},
isLoaded: true,
}),
}));
// Mock react-i18next
vi.mock('react-i18next', () => ({ vi.mock('react-i18next', () => ({
useTranslation: () => ({ useTranslation: () => ({
t: (key: string) => key, t: (key: string) => key,
i18n: { i18n: {
changeLanguage: vi.fn(), changeLanguage: vi.fn().mockResolvedValue(undefined),
language: 'zh-CN', language: 'zh-CN',
}, },
}), }),
initReactI18next: { initReactI18next: {
type: '3rdParty', type: '3rdParty',
init: vi.fn(), init: vi.fn().mockResolvedValue(undefined),
}, },
})); }));
@@ -36,14 +47,14 @@ const storageMock = {
}, },
}; };
Object.defineProperty(global, 'chrome', { const webExtensionMock = {
value: {
storage: storageMock, storage: storageMock,
tabs: { tabs: {
query: vi.fn().mockResolvedValue([]), query: vi.fn().mockResolvedValue([]),
get: vi.fn(), get: vi.fn().mockResolvedValue({}),
sendMessage: vi.fn().mockResolvedValue(undefined), sendMessage: vi.fn().mockResolvedValue(undefined),
create: vi.fn().mockResolvedValue({}), create: vi.fn().mockResolvedValue({}),
reload: vi.fn().mockResolvedValue(undefined), // ✅ 承接 MessageAction.RELOAD_TAB 刷新单元测试
}, },
runtime: { runtime: {
id: 'test-extension-id', id: 'test-extension-id',
@@ -59,6 +70,7 @@ Object.defineProperty(global, 'chrome', {
addListener: vi.fn(), addListener: vi.fn(),
removeListener: vi.fn(), removeListener: vi.fn(),
}, },
openPopup: vi.fn().mockResolvedValue(undefined),
}, },
sidePanel: { sidePanel: {
open: vi.fn().mockResolvedValue(undefined), open: vi.fn().mockResolvedValue(undefined),
@@ -70,6 +82,13 @@ Object.defineProperty(global, 'chrome', {
getAll: vi.fn().mockResolvedValue([]), getAll: vi.fn().mockResolvedValue([]),
remove: vi.fn().mockResolvedValue(undefined), remove: vi.fn().mockResolvedValue(undefined),
}, },
alarms: {
create: vi.fn().mockResolvedValue(undefined),
onAlarm: {
addListener: vi.fn(),
removeListener: vi.fn(),
},
},
contextMenus: { contextMenus: {
create: vi.fn(), create: vi.fn(),
onClicked: { onClicked: {
@@ -92,13 +111,18 @@ Object.defineProperty(global, 'chrome', {
ACTION: 'action', ACTION: 'action',
}, },
}, },
}, };
writable: true,
}); Object.defineProperty(globalThis, 'chrome', { value: webExtensionMock, writable: true });
Object.defineProperty(globalThis, 'browser', { value: webExtensionMock, writable: true });
if (typeof window !== 'undefined') {
Object.defineProperty(window, 'chrome', { value: webExtensionMock, writable: true });
Object.defineProperty(window, 'browser', { value: webExtensionMock, writable: true });
}
beforeEach(() => { beforeEach(() => {
vi.clearAllMocks(); vi.clearAllMocks();
// Ensure storage mocks return objects even after clearAllMocks
storageMock.local.get.mockResolvedValue({}); storageMock.local.get.mockResolvedValue({});
storageMock.session.get.mockResolvedValue({}); storageMock.session.get.mockResolvedValue({});
}); });
@@ -107,8 +131,7 @@ afterEach(() => {
vi.restoreAllMocks(); vi.restoreAllMocks();
}); });
// 全局 matchMedia mockThemeModeProvider 依赖) // 全局 matchMedia 极客级环境模拟ThemeModeProvider 依赖)
// 必须在 beforeEach 中设置,因为 afterEach 的 restoreAllMocks 会清除它
beforeEach(() => { beforeEach(() => {
Object.defineProperty(window, 'matchMedia', { Object.defineProperty(window, 'matchMedia', {
writable: true, writable: true,