mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-14 02:42:04 +08:00
Add E2E tests for internationalization across multiple pages
- Implemented navigation.spec.ts to test language switching and translation of navigation elements. - Created sessions-page.spec.ts to verify translations on the sessions page, including headers, status badges, and date formatting. - Developed settings-page.spec.ts to ensure settings page content is translated and persists across sessions. - Added skills-page.spec.ts to validate translations for skill categories, action buttons, and empty states.
This commit is contained in:
174
ccw/frontend/src/test/i18n.tsx
Normal file
174
ccw/frontend/src/test/i18n.tsx
Normal file
@@ -0,0 +1,174 @@
|
||||
// ========================================
|
||||
// i18n Test Helpers
|
||||
// ========================================
|
||||
// Test utilities for internationalization
|
||||
|
||||
import { render as originalRender, type RenderOptions } from '@testing-library/react';
|
||||
import { ReactElement } from 'react';
|
||||
import { IntlProvider } from 'react-intl';
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { vi } from 'vitest';
|
||||
import type { Locale } from '../types/store';
|
||||
|
||||
// Mock translation messages for testing
|
||||
const mockMessages: Record<Locale, Record<string, string>> = {
|
||||
en: {
|
||||
// Common
|
||||
'common.appName': 'CCW',
|
||||
'common.save': 'Save',
|
||||
'common.cancel': 'Cancel',
|
||||
'common.delete': 'Delete',
|
||||
'common.edit': 'Edit',
|
||||
'common.close': 'Close',
|
||||
'common.loading': 'Loading...',
|
||||
'common.error': 'Error',
|
||||
'common.success': 'Success',
|
||||
// Aria labels
|
||||
'common.aria.toggleNavigation': 'Toggle navigation',
|
||||
'common.aria.switchToDarkMode': 'Switch to dark mode',
|
||||
'common.aria.switchToLightMode': 'Switch to light mode',
|
||||
'common.aria.refreshWorkspace': 'Refresh workspace',
|
||||
'common.aria.userMenu': 'User menu',
|
||||
// Navigation - Header
|
||||
'navigation.header.brand': 'CCW Dashboard',
|
||||
'navigation.header.brandShort': 'CCW',
|
||||
'navigation.header.noProject': 'No project selected',
|
||||
'navigation.header.settings': 'Settings',
|
||||
'navigation.header.logout': 'Logout',
|
||||
// Navigation - Sidebar
|
||||
'navigation.home': 'Home',
|
||||
'navigation.sessions': 'Sessions',
|
||||
'navigation.issues': 'Issues',
|
||||
'navigation.orchestrator': 'Orchestrator',
|
||||
'navigation.settings': 'Settings',
|
||||
},
|
||||
zh: {
|
||||
// Common
|
||||
'common.appName': 'CCW',
|
||||
'common.save': '保存',
|
||||
'common.cancel': '取消',
|
||||
'common.delete': '删除',
|
||||
'common.edit': '编辑',
|
||||
'common.close': '关闭',
|
||||
'common.loading': '加载中...',
|
||||
'common.error': '错误',
|
||||
'common.success': '成功',
|
||||
// Aria labels
|
||||
'common.aria.toggleNavigation': '切换导航',
|
||||
'common.aria.switchToDarkMode': '切换到深色模式',
|
||||
'common.aria.switchToLightMode': '切换到浅色模式',
|
||||
'common.aria.refreshWorkspace': '刷新工作区',
|
||||
'common.aria.userMenu': '用户菜单',
|
||||
// Navigation - Header
|
||||
'navigation.header.brand': 'CCW 控制台',
|
||||
'navigation.header.brandShort': 'CCW',
|
||||
'navigation.header.noProject': '未选择项目',
|
||||
'navigation.header.settings': '设置',
|
||||
'navigation.header.logout': '退出登录',
|
||||
// Navigation - Sidebar
|
||||
'navigation.home': '首页',
|
||||
'navigation.sessions': '会话',
|
||||
'navigation.issues': '问题',
|
||||
'navigation.orchestrator': '编排器',
|
||||
'navigation.settings': '设置',
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a test QueryClient
|
||||
*/
|
||||
function createTestQueryClient() {
|
||||
return new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
retry: false,
|
||||
gcTime: 0,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper component that includes i18n providers
|
||||
*/
|
||||
interface I18nWrapperProps {
|
||||
children: React.ReactNode;
|
||||
locale?: Locale;
|
||||
}
|
||||
|
||||
function I18nWrapper({ children, locale = 'en' }: I18nWrapperProps) {
|
||||
const queryClient = createTestQueryClient();
|
||||
|
||||
return (
|
||||
<MemoryRouter>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<IntlProvider locale={locale} messages={mockMessages[locale]}>
|
||||
{children}
|
||||
</IntlProvider>
|
||||
</QueryClientProvider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom render function with i18n support
|
||||
*/
|
||||
interface RenderWithI18nOptions extends Omit<RenderOptions, 'wrapper'> {
|
||||
locale?: Locale;
|
||||
}
|
||||
|
||||
export function renderWithI18n(
|
||||
ui: ReactElement,
|
||||
{ locale = 'en', ...renderOptions }: RenderWithI18nOptions = {}
|
||||
) {
|
||||
return originalRender(ui, {
|
||||
wrapper: ({ children }) => <I18nWrapper locale={locale}>{children}</I18nWrapper>,
|
||||
...renderOptions,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Mock locale utilities
|
||||
*/
|
||||
export const mockLocaleUtils = {
|
||||
getInitialLocale: (locale: Locale = 'en'): Locale => locale,
|
||||
updateIntl: vi.fn(),
|
||||
getIntl: vi.fn(() => ({
|
||||
formatMessage: ({ id }: { id: string }) => id,
|
||||
})),
|
||||
formatMessage: (id: string) => id,
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a mock i18n context
|
||||
*/
|
||||
export function mockI18nContext(locale: Locale = 'en') {
|
||||
return {
|
||||
locale,
|
||||
messages: mockMessages[locale],
|
||||
formatMessage: (id: string, values?: Record<string, unknown>) => {
|
||||
const message = mockMessages[locale][id];
|
||||
if (!message) return id;
|
||||
if (!values) return message;
|
||||
|
||||
// Simple placeholder replacement
|
||||
return message.replace(/\{(\w+)\}/g, (_, key) => String(values[key] ?? key));
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-export commonly used testing utilities
|
||||
*/
|
||||
export {
|
||||
screen,
|
||||
waitFor,
|
||||
waitForElementToBeRemoved,
|
||||
within,
|
||||
fireEvent,
|
||||
} from '@testing-library/react';
|
||||
export { default as userEvent } from '@testing-library/user-event';
|
||||
|
||||
// Export renderWithI18n as the default render for convenience
|
||||
export { renderWithI18n as render };
|
||||
Reference in New Issue
Block a user