373fc0496c
统一简化多个页面里的CopyButton调用,删除不再需要的空回调参数 * feat: 添加clearCookies函数的单元测试并修复cookie域名处理逻辑 * feat: 增强 data URI 处理,支持带参数的前缀并更新相关测试 * fix: 修正 AGENTS.md 中 TypeScript 类型检查命令的描述 * feat: 添加 settings.local.json 文件以配置 Bash 权限 * perf: 预设背景色避免 Popup 弹窗白屏闪烁 * perf: 避免图标过早实例化,传递组件引用而非 JSX 节点 * feat: 添加 useLazyTranslation 和 preloadNamespaces 函数以支持动态加载 i18n 命名空间 * feat: 使用 useLazyTranslation 替换 useTranslation 以支持懒加载翻译 * feat: 添加 PageSkeleton 组件及其测试用例以支持页面加载骨架屏 * feat: 使用骨架屏替换加载状态指示器,优化用户体验 * feat: 优化 CopyButton 组件的复制功能,添加定时器管理复制状态 * feat: 调整 chunk 大小警告阈值以优化构建性能
176 lines
4.9 KiB
TypeScript
176 lines
4.9 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
import { renderHook, waitFor } from '@testing-library/react';
|
|
import { preloadNamespaces } from '@/utils/useLazyTranslation';
|
|
|
|
// Mock i18n
|
|
vi.mock('@/i18n', () => ({
|
|
default: {
|
|
language: 'en',
|
|
addResourceBundle: vi.fn(),
|
|
},
|
|
}));
|
|
|
|
// Mock useTranslation
|
|
vi.mock('react-i18next', () => ({
|
|
useTranslation: vi.fn((ns: string[]) => ({
|
|
t: (key: string) => `${ns.join(',')}:${key}`,
|
|
i18n: { language: 'en' },
|
|
ready: true,
|
|
})),
|
|
}));
|
|
|
|
// Mock 动态导入
|
|
const mockTimestampModule = { default: { 'timestamp.key': 'Timestamp Value' } };
|
|
const mockJwtModule = { default: { 'jwt.key': 'JWT Value' } };
|
|
|
|
vi.mock('@/i18n/locales/en/timestamp.json', () => mockTimestampModule);
|
|
vi.mock('@/i18n/locales/en/jwt.json', () => mockJwtModule);
|
|
vi.mock('@/i18n/locales/zh/timestamp.json', () => ({ default: { 'timestamp.key': '时间戳值' } }));
|
|
|
|
describe('preloadNamespaces', () => {
|
|
let i18n: { addResourceBundle: ReturnType<typeof vi.fn> };
|
|
|
|
beforeEach(async () => {
|
|
vi.clearAllMocks();
|
|
i18n = (await import('@/i18n')).default as any;
|
|
// 清除缓存
|
|
const { __test_clearCache } = await import('@/utils/useLazyTranslation');
|
|
__test_clearCache?.();
|
|
});
|
|
|
|
it('应该加载指定的命名空间', async () => {
|
|
await preloadNamespaces(['timestamp']);
|
|
|
|
expect(i18n.addResourceBundle).toHaveBeenCalledWith(
|
|
'en',
|
|
'timestamp',
|
|
mockTimestampModule.default,
|
|
true,
|
|
true,
|
|
);
|
|
});
|
|
|
|
it('应该并行加载多个命名空间', async () => {
|
|
await preloadNamespaces(['timestamp', 'jwt']);
|
|
|
|
expect(i18n.addResourceBundle).toHaveBeenCalledTimes(2);
|
|
expect(i18n.addResourceBundle).toHaveBeenCalledWith(
|
|
'en',
|
|
'timestamp',
|
|
mockTimestampModule.default,
|
|
true,
|
|
true,
|
|
);
|
|
expect(i18n.addResourceBundle).toHaveBeenCalledWith(
|
|
'en',
|
|
'jwt',
|
|
mockJwtModule.default,
|
|
true,
|
|
true,
|
|
);
|
|
});
|
|
|
|
it('应该缓存已加载的命名空间,避免重复加载', async () => {
|
|
await preloadNamespaces(['timestamp']);
|
|
await preloadNamespaces(['timestamp']);
|
|
|
|
// 只应调用一次
|
|
expect(i18n.addResourceBundle).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it('应该使用当前语言(中文)', async () => {
|
|
const i18nModule = await import('@/i18n');
|
|
(i18nModule.default as any).language = 'zh-CN';
|
|
|
|
await preloadNamespaces(['timestamp']);
|
|
|
|
expect(i18n.addResourceBundle).toHaveBeenCalledWith(
|
|
'zh',
|
|
'timestamp',
|
|
{ 'timestamp.key': '时间戳值' },
|
|
true,
|
|
true,
|
|
);
|
|
|
|
// 恢复
|
|
(i18nModule.default as any).language = 'en';
|
|
});
|
|
|
|
it('应该跳过不存在的命名空间', async () => {
|
|
await preloadNamespaces(['nonExistentNamespace']);
|
|
|
|
// 不应调用 addResourceBundle
|
|
expect(i18n.addResourceBundle).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe('useLazyTranslation', () => {
|
|
beforeEach(async () => {
|
|
vi.clearAllMocks();
|
|
// 清除缓存
|
|
const { __test_clearCache } = await import('@/utils/useLazyTranslation');
|
|
__test_clearCache?.();
|
|
});
|
|
|
|
it('应该在挂载时加载命名空间', async () => {
|
|
const { useLazyTranslation } = await import('@/utils/useLazyTranslation');
|
|
const i18n = (await import('@/i18n')).default as any;
|
|
|
|
const { result } = renderHook(() => useLazyTranslation('timestamp'));
|
|
|
|
// 初始状态应该是未加载
|
|
expect(result.current.isLoaded).toBe(false);
|
|
|
|
await waitFor(() => {
|
|
expect(result.current.isLoaded).toBe(true);
|
|
});
|
|
|
|
expect(i18n.addResourceBundle).toHaveBeenCalledWith(
|
|
'en',
|
|
'timestamp',
|
|
mockTimestampModule.default,
|
|
true,
|
|
true,
|
|
);
|
|
});
|
|
|
|
it('应该返回 useTranslation 的结果', async () => {
|
|
const { useLazyTranslation } = await import('@/utils/useLazyTranslation');
|
|
|
|
const { result } = renderHook(() => useLazyTranslation('timestamp'));
|
|
|
|
await waitFor(() => {
|
|
expect(result.current.isLoaded).toBe(true);
|
|
});
|
|
|
|
expect(result.current.t('key')).toBe('timestamp:key');
|
|
expect(result.current.ready).toBe(true);
|
|
});
|
|
|
|
it('应该支持多个命名空间', async () => {
|
|
const { useLazyTranslation } = await import('@/utils/useLazyTranslation');
|
|
const i18n = (await import('@/i18n')).default as any;
|
|
|
|
const { result } = renderHook(() => useLazyTranslation(['timestamp', 'jwt']));
|
|
|
|
await waitFor(() => {
|
|
expect(result.current.isLoaded).toBe(true);
|
|
});
|
|
|
|
expect(i18n.addResourceBundle).toHaveBeenCalledTimes(2);
|
|
expect(result.current.t('key')).toBe('timestamp,jwt:key');
|
|
});
|
|
|
|
it('应该支持字符串形式的单个命名空间', async () => {
|
|
const { useLazyTranslation } = await import('@/utils/useLazyTranslation');
|
|
|
|
const { result } = renderHook(() => useLazyTranslation('timestamp'));
|
|
|
|
await waitFor(() => {
|
|
expect(result.current.isLoaded).toBe(true);
|
|
});
|
|
|
|
expect(result.current.t('key')).toBe('timestamp:key');
|
|
});
|
|
});
|