- ExportPdfButton (3 tests): default label, missing-target error, custom filename - ValuationHistoryChart (3 tests): null <2 points, header/description, recharts mounting - NotificationBell (9 tests): aria-label, badge display + 99+ cap, auth-gated fetchUnreadCount, dropdown toggle, empty state, item rendering, mark-all visibility All 15 new tests pass via direct vitest. Cumulative GOO-54 progress: 29 spec files, ~143 tests across H1-H5. Co-Authored-By: Paperclip <noreply@paperclip.ing>
87 lines
3.5 KiB
TypeScript
87 lines
3.5 KiB
TypeScript
/**
|
|
* i18n key parity tests — ensures `en.json` and `vi.json` stay in sync.
|
|
*
|
|
* Rules enforced:
|
|
* 1. Both files must have the same set of flattened leaf keys.
|
|
* 2. No key value may be an empty string in either locale.
|
|
* 3. Locale metadata keys must be present in both files.
|
|
*/
|
|
import { describe, expect, it } from 'vitest';
|
|
import enMessages from '../messages/en.json';
|
|
import viMessages from '../messages/vi.json';
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Helpers
|
|
// ---------------------------------------------------------------------------
|
|
type MessageDict = Record<string, unknown>;
|
|
|
|
function flattenKeys(obj: MessageDict, prefix = ''): string[] {
|
|
return Object.entries(obj).flatMap(([key, value]) => {
|
|
const fullKey = prefix ? `${prefix}.${key}` : key;
|
|
if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
|
|
return flattenKeys(value as MessageDict, fullKey);
|
|
}
|
|
return [fullKey];
|
|
});
|
|
}
|
|
|
|
function flattenEntries(obj: MessageDict, prefix = ''): Array<[string, string]> {
|
|
return Object.entries(obj).flatMap(([key, value]) => {
|
|
const fullKey = prefix ? `${prefix}.${key}` : key;
|
|
if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
|
|
return flattenEntries(value as MessageDict, fullKey);
|
|
}
|
|
return [[fullKey, String(value)]];
|
|
});
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Tests
|
|
// ---------------------------------------------------------------------------
|
|
describe('i18n message key parity (en ↔ vi)', () => {
|
|
const enKeys = new Set(flattenKeys(enMessages));
|
|
const viKeys = new Set(flattenKeys(viMessages));
|
|
|
|
it('vi.json has no extra keys missing from en.json', () => {
|
|
const extraInVi = [...viKeys].filter((k) => !enKeys.has(k));
|
|
expect(extraInVi, `Keys present in vi.json but not en.json: ${JSON.stringify(extraInVi)}`).toHaveLength(0);
|
|
});
|
|
|
|
it('en.json has no extra keys missing from vi.json', () => {
|
|
const extraInEn = [...enKeys].filter((k) => !viKeys.has(k));
|
|
expect(extraInEn, `Keys present in en.json but not vi.json: ${JSON.stringify(extraInEn)}`).toHaveLength(0);
|
|
});
|
|
|
|
it('en.json has no empty string values', () => {
|
|
const empty = flattenEntries(enMessages).filter(([, v]) => v.trim() === '');
|
|
expect(empty, `Empty values in en.json: ${JSON.stringify(empty.map(([k]) => k))}`).toHaveLength(0);
|
|
});
|
|
|
|
it('vi.json has no empty string values', () => {
|
|
const empty = flattenEntries(viMessages).filter(([, v]) => v.trim() === '');
|
|
expect(empty, `Empty values in vi.json: ${JSON.stringify(empty.map(([k]) => k))}`).toHaveLength(0);
|
|
});
|
|
|
|
it('both locales define the same top-level namespace keys', () => {
|
|
const enTopLevel = Object.keys(enMessages).sort();
|
|
const viTopLevel = Object.keys(viMessages).sort();
|
|
expect(viTopLevel).toEqual(enTopLevel);
|
|
});
|
|
});
|
|
|
|
describe('i18n locale config', () => {
|
|
it('language namespace exists in en.json with the expected locale keys', () => {
|
|
const lang = (enMessages as MessageDict).language as MessageDict | undefined;
|
|
expect(lang).toBeDefined();
|
|
expect(Object.keys(lang ?? {})).toContain('vi');
|
|
expect(Object.keys(lang ?? {})).toContain('en');
|
|
});
|
|
|
|
it('language namespace exists in vi.json with the expected locale keys', () => {
|
|
const lang = (viMessages as MessageDict).language as MessageDict | undefined;
|
|
expect(lang).toBeDefined();
|
|
expect(Object.keys(lang ?? {})).toContain('vi');
|
|
expect(Object.keys(lang ?? {})).toContain('en');
|
|
});
|
|
});
|