@logistically/i18n-cli
Version:
Enterprise-grade CLI tool for extracting and managing translations in Logistically microservices
446 lines (381 loc) • 14.3 kB
text/typescript
/// <reference types="jest" />
import { TranslationReplacer } from './replacer';
import * as fs from 'fs-extra';
import { glob } from 'glob';
// Mock dependencies
jest.mock('fs-extra');
jest.mock('glob', () => ({
glob: jest.fn()
}));
const mockFs = fs as jest.Mocked<typeof fs>;
const mockGlob = glob as jest.MockedFunction<typeof glob>;
describe('TranslationReplacer', () => {
let replacer: TranslationReplacer;
beforeEach(() => {
replacer = new TranslationReplacer();
jest.clearAllMocks();
// Default mock implementations
mockGlob.mockResolvedValue([]);
mockFs.readJson.mockResolvedValue({ keys: [] });
(mockFs.readFile as any).mockResolvedValue('');
(mockFs.writeFile as any).mockResolvedValue(undefined);
});
describe('replace', () => {
it('should replace hardcoded strings with translation keys', async () => {
const mockKeys = [
{
key: 'AUTH.AUTH_SERVICE.EXCEPTION.USER_ALREADY_EXISTS',
text: 'User already exists with this email',
type: 'EXCEPTION',
file: 'auth/src/services/auth.service.ts',
line: 10
}
];
const mockKeysData = {
keys: mockKeys,
totalKeys: 1,
services: ['auth'],
generatedAt: '2024-01-01T00:00:00.000Z'
};
const mockContent = `
throw new BadRequestException('User already exists with this email');
return { message: 'Validation failed' };
errors.push(\`The field '\${fieldName}' is required\`);
`;
// Mock specific file discovery
mockGlob.mockResolvedValue(['auth/src/services/auth.service.ts']);
mockFs.readJson.mockResolvedValue(mockKeysData);
(mockFs.readFile as any).mockResolvedValue(mockContent);
(mockFs.writeFile as any).mockResolvedValue(undefined);
await replacer.replace({
path: '.',
keysFile: 'translation-keys.json',
backup: true,
dryRun: false,
verbose: false
});
expect(mockFs.readJson).toHaveBeenCalledWith('translation-keys.json');
expect(mockFs.readFile).toHaveBeenCalledWith('auth/src/services/auth.service.ts', 'utf8');
expect(mockFs.writeFile).toHaveBeenCalledWith(
'auth/src/services/auth.service.ts',
expect.stringContaining('this.translate(\'AUTH.AUTH_SERVICE.EXCEPTION.USER_ALREADY_EXISTS\')')
);
});
it('should handle simple exception replacement', async () => {
const mockKeys = [
{
key: 'AUTH.AUTH_SERVICE.EXCEPTION.USER_ALREADY_EXISTS',
text: 'User already exists with this email',
type: 'EXCEPTION',
file: 'auth/src/services/auth.service.ts',
line: 10
}
];
const mockKeysData = {
keys: mockKeys,
totalKeys: 1,
services: ['auth'],
generatedAt: '2024-01-01T00:00:00.000Z'
};
const mockContent = `throw new BadRequestException('User already exists with this email');`;
mockGlob.mockResolvedValue(['auth/src/services/auth.service.ts']);
mockFs.readJson.mockResolvedValue(mockKeysData);
(mockFs.readFile as any).mockResolvedValue(mockContent);
(mockFs.writeFile as any).mockResolvedValue(undefined);
await replacer.replace({
path: '.',
keysFile: 'translation-keys.json',
backup: true,
dryRun: false,
verbose: false
});
expect(mockFs.writeFile).toHaveBeenCalledWith(
'auth/src/services/auth.service.ts',
expect.stringContaining('this.translate(\'AUTH.AUTH_SERVICE.EXCEPTION.USER_ALREADY_EXISTS\')')
);
});
it('should handle return object replacement', async () => {
const mockKeys = [
{
key: 'AUTH.AUTH_SERVICE.RETURN_MESSAGE.VALIDATION_FAILED',
text: 'Validation failed',
type: 'RETURN_MESSAGE',
file: 'auth/src/services/auth.service.ts',
line: 15
}
];
const mockKeysData = {
keys: mockKeys,
totalKeys: 1,
services: ['auth'],
generatedAt: '2024-01-01T00:00:00.000Z'
};
const mockContent = `return { message: 'Validation failed' };`;
mockGlob.mockResolvedValue(['auth/src/services/auth.service.ts']);
mockFs.readJson.mockResolvedValue(mockKeysData);
(mockFs.readFile as any).mockResolvedValue(mockContent);
(mockFs.writeFile as any).mockResolvedValue(undefined);
await replacer.replace({
path: '.',
keysFile: 'translation-keys.json',
backup: true,
dryRun: false,
verbose: false
});
expect(mockFs.writeFile).toHaveBeenCalledWith(
'auth/src/services/auth.service.ts',
expect.stringContaining('this.translate(\'AUTH.AUTH_SERVICE.RETURN_MESSAGE.VALIDATION_FAILED\')')
);
});
it('should handle template literal replacement', async () => {
const mockKeys = [
{
key: 'AUTH.AUTH_SERVICE.TEMPLATE_LITERAL.FIELD_REQUIRED',
text: 'The field \'${fieldName}\' is required',
type: 'TEMPLATE_LITERAL',
file: 'auth/src/services/auth.service.ts',
line: 20
}
];
const mockKeysData = {
keys: mockKeys,
totalKeys: 1,
services: ['auth'],
generatedAt: '2024-01-01T00:00:00.000Z'
};
const mockContent = `errors.push(\`The field '\${fieldName}' is required\`);`;
mockGlob.mockResolvedValue(['auth/src/services/auth.service.ts']);
mockFs.readJson.mockResolvedValue(mockKeysData);
(mockFs.readFile as any).mockResolvedValue(mockContent);
(mockFs.writeFile as any).mockResolvedValue(undefined);
await replacer.replace({
path: '.',
keysFile: 'translation-keys.json',
backup: true,
dryRun: false,
verbose: false
});
expect(mockFs.writeFile).toHaveBeenCalledWith(
'auth/src/services/auth.service.ts',
expect.stringContaining('this.translate(\'AUTH.AUTH_SERVICE.TEMPLATE_LITERAL.FIELD_REQUIRED\')')
);
});
it('should handle multiple replacements in same file', async () => {
const mockKeys = [
{
key: 'AUTH.AUTH_SERVICE.EXCEPTION.USER_ALREADY_EXISTS',
text: 'User already exists with this email',
type: 'EXCEPTION',
file: 'auth/src/services/auth.service.ts',
line: 10
},
{
key: 'AUTH.AUTH_SERVICE.RETURN_MESSAGE.VALIDATION_FAILED',
text: 'Validation failed',
type: 'RETURN_MESSAGE',
file: 'auth/src/services/auth.service.ts',
line: 15
}
];
const mockKeysData = {
keys: mockKeys,
totalKeys: 2,
services: ['auth'],
generatedAt: '2024-01-01T00:00:00.000Z'
};
const mockContent = `
throw new BadRequestException('User already exists with this email');
return { message: 'Validation failed' };
`;
mockGlob.mockResolvedValue(['auth/src/services/auth.service.ts']);
mockFs.readJson.mockResolvedValue(mockKeysData);
(mockFs.readFile as any).mockResolvedValue(mockContent);
(mockFs.writeFile as any).mockResolvedValue(undefined);
await replacer.replace({
path: '.',
keysFile: 'translation-keys.json',
backup: true,
dryRun: false,
verbose: false
});
const writtenContent = (mockFs.writeFile as any).mock.calls[1][1]; // Check the actual file, not backup
expect(writtenContent).toContain('this.translate(\'AUTH.AUTH_SERVICE.EXCEPTION.USER_ALREADY_EXISTS\')');
expect(writtenContent).toContain('this.translate(\'AUTH.AUTH_SERVICE.RETURN_MESSAGE.VALIDATION_FAILED\')');
});
it('should handle file reading errors gracefully', async () => {
const mockKeys = [
{
key: 'AUTH.AUTH_SERVICE.EXCEPTION.USER_ALREADY_EXISTS',
text: 'User already exists with this email',
type: 'EXCEPTION',
file: 'auth/src/services/auth.service.ts',
line: 10
}
];
const mockKeysData = {
keys: mockKeys,
totalKeys: 1,
services: ['auth'],
generatedAt: '2024-01-01T00:00:00.000Z'
};
mockGlob.mockResolvedValue(['auth/src/services/auth.service.ts']);
mockFs.readJson.mockResolvedValue(mockKeysData);
(mockFs.readFile as any).mockRejectedValue(new Error('Permission denied'));
await replacer.replace({
path: '.',
keysFile: 'translation-keys.json',
backup: true,
dryRun: false,
verbose: false
});
// Should handle the error gracefully without throwing
expect(mockFs.readFile).toHaveBeenCalledWith('auth/src/services/auth.service.ts', 'utf8');
});
it('should handle backup creation', async () => {
const mockKeys = [
{
key: 'AUTH.AUTH_SERVICE.EXCEPTION.USER_ALREADY_EXISTS',
text: 'User already exists with this email',
type: 'EXCEPTION',
file: 'auth/src/services/auth.service.ts',
line: 10
}
];
const mockKeysData = {
keys: mockKeys,
totalKeys: 1,
services: ['auth'],
generatedAt: '2024-01-01T00:00:00.000Z'
};
const mockContent = `throw new BadRequestException('User already exists with this email');`;
mockGlob.mockResolvedValue(['auth/src/services/auth.service.ts']);
mockFs.readJson.mockResolvedValue(mockKeysData);
(mockFs.readFile as any).mockResolvedValue(mockContent);
(mockFs.writeFile as any).mockResolvedValue(undefined);
await replacer.replace({
path: '.',
keysFile: 'translation-keys.json',
backup: true,
dryRun: false,
verbose: false
});
expect(mockFs.writeFile).toHaveBeenCalledWith(
'auth/src/services/auth.service.ts.backup',
mockContent
);
});
it('should handle exception with object parameters', async () => {
const mockKeys = [
{
key: 'AUTH.AUTH_SERVICE.EXCEPTION.USER_ALREADY_EXISTS',
text: 'User already exists with this email',
type: 'EXCEPTION',
file: 'auth/src/services/auth.service.ts',
line: 10
}
];
const mockKeysData = {
keys: mockKeys,
totalKeys: 1,
services: ['auth'],
generatedAt: '2024-01-01T00:00:00.000Z'
};
const mockContent = `throw new BadRequestException('User already exists with this email', { userId: 123 });`;
mockGlob.mockResolvedValue(['auth/src/services/auth.service.ts']);
mockFs.readJson.mockResolvedValue(mockKeysData);
(mockFs.readFile as any).mockResolvedValue(mockContent);
(mockFs.writeFile as any).mockResolvedValue(undefined);
await replacer.replace({
path: '.',
keysFile: 'translation-keys.json',
backup: true,
dryRun: false,
verbose: false
});
expect(mockFs.writeFile).toHaveBeenCalledWith(
'auth/src/services/auth.service.ts',
expect.stringContaining('this.translate(\'AUTH.AUTH_SERVICE.EXCEPTION.USER_ALREADY_EXISTS\')')
);
});
it('should handle return object with additional properties', async () => {
const mockKeys = [
{
key: 'AUTH.AUTH_SERVICE.RETURN_MESSAGE.VALIDATION_FAILED',
text: 'Validation failed',
type: 'RETURN_MESSAGE',
file: 'auth/src/services/auth.service.ts',
line: 15
}
];
const mockKeysData = {
keys: mockKeys,
totalKeys: 1,
services: ['auth'],
generatedAt: '2024-01-01T00:00:00.000Z'
};
const mockContent = `return { message: 'Validation failed', code: 'VALIDATION_ERROR' };`;
mockGlob.mockResolvedValue(['auth/src/services/auth.service.ts']);
mockFs.readJson.mockResolvedValue(mockKeysData);
(mockFs.readFile as any).mockResolvedValue(mockContent);
(mockFs.writeFile as any).mockResolvedValue(undefined);
await replacer.replace({
path: '.',
keysFile: 'translation-keys.json',
backup: true,
dryRun: false,
verbose: false
});
expect(mockFs.writeFile).toHaveBeenCalledWith(
'auth/src/services/auth.service.ts',
expect.stringContaining('this.translate(\'AUTH.AUTH_SERVICE.RETURN_MESSAGE.VALIDATION_FAILED\')')
);
});
it('should handle complex nested structures', async () => {
const mockKeys = [
{
key: 'AUTH.AUTH_SERVICE.EXCEPTION.USER_ALREADY_EXISTS',
text: 'User already exists with this email',
type: 'EXCEPTION',
file: 'auth/src/services/auth.service.ts',
line: 10
},
{
key: 'AUTH.AUTH_SERVICE.RETURN_MESSAGE.VALIDATION_FAILED',
text: 'Validation failed',
type: 'RETURN_MESSAGE',
file: 'auth/src/services/auth.service.ts',
line: 15
}
];
const mockKeysData = {
keys: mockKeys,
totalKeys: 2,
services: ['auth'],
generatedAt: '2024-01-01T00:00:00.000Z'
};
const mockContent = `
if (error) {
throw new BadRequestException('User already exists with this email');
}
return {
success: true,
message: 'Validation failed'
};
`;
mockGlob.mockResolvedValue(['auth/src/services/auth.service.ts']);
mockFs.readJson.mockResolvedValue(mockKeysData);
(mockFs.readFile as any).mockResolvedValue(mockContent);
(mockFs.writeFile as any).mockResolvedValue(undefined);
await replacer.replace({
path: '.',
keysFile: 'translation-keys.json',
backup: true,
dryRun: false,
verbose: false
});
const writtenContent = (mockFs.writeFile as any).mock.calls[1][1]; // Check the actual file, not backup
expect(writtenContent).toContain('this.translate(\'AUTH.AUTH_SERVICE.EXCEPTION.USER_ALREADY_EXISTS\')');
expect(writtenContent).toContain('this.translate(\'AUTH.AUTH_SERVICE.RETURN_MESSAGE.VALIDATION_FAILED\')');
});
});
});