Skip to content

✅ test: add unit test for src/server/translation.ts #7268

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 26 additions & 42 deletions src/server/translation.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
// @vitest-environment node
import { cookies } from 'next/headers';
import * as fs from 'node:fs';
import * as path from 'node:path';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';

import { DEFAULT_LANG, LOBE_LOCALE_COOKIE } from '@/const/locale';
Expand All @@ -15,15 +13,6 @@ vi.mock('next/headers', () => ({
cookies: vi.fn(),
}));

vi.mock('node:fs', () => ({
existsSync: vi.fn(),
readFileSync: vi.fn(),
}));

vi.mock('node:path', () => ({
join: vi.fn(),
}));

vi.mock('@/const/locale', () => ({
DEFAULT_LANG: 'en-US',
LOBE_LOCALE_COOKIE: 'LOBE_LOCALE',
Expand All @@ -37,6 +26,24 @@ vi.mock('@/utils/env', () => ({
isDev: false,
}));

const translations = {
key1: 'Value 1',
key2: 'Value 2 with {{param}}',
nested: { key: 'Nested value' },
};

vi.mock('@/locales/default/common', () => ({
...translations,
default: translations,
}));

vi.mock('@/../locales/en-US/common.json', () => ({
...translations,
nonexistent: undefined,
}));

vi.mock('@/../locales/zh-CN/common.json', () => translations);

describe('getLocale', () => {
const mockCookieStore = {
get: vi.fn(),
Expand All @@ -61,17 +68,9 @@ describe('getLocale', () => {
});

describe('translation', () => {
const mockTranslations = {
key1: 'Value 1',
key2: 'Value 2 with {{param}}',
nested: { key: 'Nested value' },
};

beforeEach(() => {
vi.clearAllMocks();
(fs.existsSync as any).mockReturnValue(true);
(fs.readFileSync as any).mockReturnValue(JSON.stringify(mockTranslations));
(path.join as any).mockImplementation((...args: any) => args.join('/'));
(env.isDev as any) = false;
});

it('should return correct translation object', async () => {
Expand All @@ -93,32 +92,17 @@ describe('translation', () => {
expect(t('nonexistent.key')).toBe('nonexistent.key');
});

it('should use fallback language if specified locale file does not exist', async () => {
(fs.existsSync as any).mockReturnValueOnce(false);
await translation('common', 'nonexistent-LANG');
expect(fs.readFileSync).toHaveBeenCalledWith(
expect.stringContaining(`/${DEFAULT_LANG}/common.json`),
'utf8',
);
});

it('should use zh-CN in dev mode when fallback is needed', async () => {
(fs.existsSync as any).mockReturnValueOnce(false);
(env.isDev as unknown as boolean) = true;
await translation('common', 'nonexistent-LANG');
expect(fs.readFileSync).toHaveBeenCalledWith(
expect.stringContaining('/zh-CN/common.json'),
'utf8',
);
it('should use zh-CN translations in dev mode for zh-CN locale', async () => {
(env.isDev as any) = true;
const result = await translation('common', 'zh-CN');
expect(result.locale).toBe('zh-CN');
expect(result.t('key1')).toBe('Value 1');
});

it('should handle file reading errors', async () => {
it('should handle import errors gracefully', async () => {
const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
(fs.readFileSync as any).mockImplementation(() => {
throw new Error('File read error');
});

const result = await translation('common', 'en-US');
const result = await translation('common', 'invalid-locale');
expect(result.t('any.key')).toBe('any.key');
expect(consoleErrorSpy).toHaveBeenCalledWith(
'Error while reading translation file',
Expand Down
13 changes: 2 additions & 11 deletions src/server/translation.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
'use server';

import { get } from 'lodash-es';
import { existsSync, readFileSync } from 'node:fs';
import { join } from 'node:path';

import { DEFAULT_LANG } from '@/const/locale';
import { Locales, NS, normalizeLocale } from '@/locales/resources';
Expand All @@ -17,15 +15,8 @@ export const translation = async (ns: NS = 'common', hl: string) => {
let i18ns = {};
const lng = await getLocale(hl);
try {
let filepath = join(process.cwd(), `locales/${normalizeLocale(lng)}/${ns}.json`);
const isExist = existsSync(filepath);
if (!isExist)
filepath = join(
process.cwd(),
`locales/${normalizeLocale(isDev ? 'zh-CN' : DEFAULT_LANG)}/${ns}.json`,
);
const file = readFileSync(filepath, 'utf8');
i18ns = JSON.parse(file);
if (isDev && lng === 'zh-CN') i18ns = await import(`@/locales/default/${ns}`);
i18ns = await import(`@/../locales/${normalizeLocale(lng)}/${ns}.json`);
} catch (e) {
console.error('Error while reading translation file', e);
}
Expand Down
Loading