auto-translatr
Version:
An automatic translation library
427 lines (426 loc) • 22.4 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
const utils_1 = require("../utils");
const translationsParser_1 = require("../translationsParser");
describe('translationsParser', () => {
const mockTranslations = {
en: { hello: 'Hello', world: 'World', greeting: 'Hi there' },
fr: { hello: 'Bonjour', world: 'Monde' }, // missing 'greeting'
es: { hello: 'Hola' } // missing 'world' and 'greeting'
};
const mockReader = jest.fn(() => Promise.resolve((0, utils_1.right)(mockTranslations)));
const mockWriter = jest.fn(() => Promise.resolve((0, utils_1.right)(undefined)));
const mockTranslator = jest.fn((text) => Promise.resolve(`translated_${text}`));
beforeEach(() => {
jest.clearAllMocks();
});
describe('readTranslations', () => {
it('should return right when reader succeeds', () => __awaiter(void 0, void 0, void 0, function* () {
const reader = jest.fn(() => Promise.resolve((0, utils_1.right)(mockTranslations)));
const result = yield (0, translationsParser_1.readTranslations)(reader);
expect((0, utils_1.isRight)(result)).toBe(true);
if ((0, utils_1.isRight)(result)) {
expect(result.value).toEqual(mockTranslations);
}
expect(reader).toHaveBeenCalledTimes(1);
}));
it('should return left when reader returns left', () => __awaiter(void 0, void 0, void 0, function* () {
const error = new Error('Reader failed');
const reader = jest.fn(() => Promise.resolve((0, utils_1.left)(error)));
const result = yield (0, translationsParser_1.readTranslations)(reader);
expect((0, utils_1.isLeft)(result)).toBe(true);
if ((0, utils_1.isLeft)(result)) {
expect(result.value).toBe(error);
}
expect(reader).toHaveBeenCalledTimes(1);
}));
it('should return left when reader throws error', () => __awaiter(void 0, void 0, void 0, function* () {
const error = new Error('Reader threw');
const reader = jest.fn(() => Promise.reject(error));
const result = yield (0, translationsParser_1.readTranslations)(reader);
expect((0, utils_1.isLeft)(result)).toBe(true);
if ((0, utils_1.isLeft)(result)) {
expect(result.value).toBe(error);
}
expect(reader).toHaveBeenCalledTimes(1);
}));
it('should handle non-Error thrown values', () => __awaiter(void 0, void 0, void 0, function* () {
const reader = jest.fn(() => Promise.reject('string error'));
const result = yield (0, translationsParser_1.readTranslations)(reader);
expect((0, utils_1.isLeft)(result)).toBe(true);
if ((0, utils_1.isLeft)(result)) {
expect(result.value).toEqual('string error');
}
}));
});
describe('getMissingTranslations', () => {
it('should return empty object when no keys are missing', () => {
const defaultTranslations = { hello: 'Hello', world: 'World' };
const targetTranslations = { hello: 'Bonjour', world: 'Monde' };
const result = (0, translationsParser_1.getMissingTranslations)(defaultTranslations, targetTranslations);
expect(result).toEqual({});
});
it('should return missing translations when some keys are missing', () => {
const defaultTranslations = { hello: 'Hello', world: 'World', greeting: 'Hi' };
const targetTranslations = { hello: 'Bonjour' };
const result = (0, translationsParser_1.getMissingTranslations)(defaultTranslations, targetTranslations);
expect(result).toEqual({ world: 'World', greeting: 'Hi' });
});
it('should return all translations when target is empty', () => {
const defaultTranslations = { hello: 'Hello', world: 'World' };
const targetTranslations = {};
const result = (0, translationsParser_1.getMissingTranslations)(defaultTranslations, targetTranslations);
expect(result).toEqual(defaultTranslations);
});
it('should handle falsy values in target translations', () => {
const defaultTranslations = { hello: 'Hello', world: 'World', empty: 'Empty' };
const targetTranslations = { hello: 'Bonjour', world: '', empty: '' };
const result = (0, translationsParser_1.getMissingTranslations)(defaultTranslations, targetTranslations);
expect(result).toEqual({ world: 'World', empty: 'Empty' });
});
it('should handle empty default translations', () => {
const defaultTranslations = {};
const targetTranslations = { hello: 'Bonjour' };
const result = (0, translationsParser_1.getMissingTranslations)(defaultTranslations, targetTranslations);
expect(result).toEqual({});
});
});
describe('extractDefaultAndOthers', () => {
it('should extract default language and others correctly', () => {
const translations = {
en: { hello: 'Hello' },
fr: { hello: 'Bonjour' },
es: { hello: 'Hola' }
};
const result = (0, translationsParser_1.extractDefaultAndOthers)('en', translations);
expect(result).toEqual({
default: { language: 'en', translations: { hello: 'Hello' } },
others: [
{ language: 'fr', translations: { hello: 'Bonjour' } },
{ language: 'es', translations: { hello: 'Hola' } }
]
});
});
it('should handle case where default language is not first', () => {
const translations = {
fr: { hello: 'Bonjour' },
en: { hello: 'Hello' },
es: { hello: 'Hola' }
};
const result = (0, translationsParser_1.extractDefaultAndOthers)('en', translations);
expect(result.default).toEqual({ language: 'en', translations: { hello: 'Hello' } });
expect(result.others).toHaveLength(2);
expect(result.others).toContainEqual({ language: 'fr', translations: { hello: 'Bonjour' } });
expect(result.others).toContainEqual({ language: 'es', translations: { hello: 'Hola' } });
});
it('should handle single language (only default)', () => {
const translations = {
en: { hello: 'Hello' }
};
const result = (0, translationsParser_1.extractDefaultAndOthers)('en', translations);
expect(result).toEqual({
default: { language: 'en', translations: { hello: 'Hello' } },
others: []
});
});
it('should handle missing default language', () => {
const translations = {
fr: { hello: 'Bonjour' },
es: { hello: 'Hola' }
};
const result = (0, translationsParser_1.extractDefaultAndOthers)('en', translations);
expect(result.default).toEqual({ language: 'en', translations: undefined });
expect(result.others).toHaveLength(2);
});
});
describe('extractMissingTranslations', () => {
it('should extract missing translations for all other languages', () => {
const translationsData = {
default: { language: 'en', translations: { hello: 'Hello', world: 'World' } },
others: [
{ language: 'fr', translations: { hello: 'Bonjour' } },
{ language: 'es', translations: {} }
]
};
const result = (0, translationsParser_1.extractMissingTranslations)(translationsData);
expect(result).toEqual([
{ language: 'fr', translations: { world: 'World' } },
{ language: 'es', translations: { hello: 'Hello', world: 'World' } }
]);
});
it('should return empty translations when nothing is missing', () => {
const translationsData = {
default: { language: 'en', translations: { hello: 'Hello' } },
others: [
{ language: 'fr', translations: { hello: 'Bonjour' } },
{ language: 'es', translations: { hello: 'Hola' } }
]
};
const result = (0, translationsParser_1.extractMissingTranslations)(translationsData);
expect(result).toEqual([
{ language: 'fr', translations: {} },
{ language: 'es', translations: {} }
]);
});
it('should handle empty others array', () => {
const translationsData = {
default: { language: 'en', translations: { hello: 'Hello' } },
others: []
};
const result = (0, translationsParser_1.extractMissingTranslations)(translationsData);
expect(result).toEqual([]);
});
});
describe('addTranslator', () => {
it('should add translator function to translation object', () => {
const translation = {
language: 'fr',
translations: { hello: 'Hello' }
};
const mockTranslatorFn = jest.fn();
const addTranslatorFn = (0, translationsParser_1.addTranslator)('en', mockTranslatorFn);
const result = addTranslatorFn(translation);
expect(result).toHaveProperty('language', 'fr');
expect(result).toHaveProperty('translations', { hello: 'Hello' });
expect(result).toHaveProperty('translate');
expect(typeof result.translate).toBe('function');
});
it('should create translator with correct parameters', () => {
const translation = {
language: 'es',
translations: { world: 'World' }
};
const mockTranslatorFn = jest.fn();
const addTranslatorFn = (0, translationsParser_1.addTranslator)('en', mockTranslatorFn);
const result = addTranslatorFn(translation);
// The translate function should be created via createTranslator
expect(result.translate).toBeDefined();
});
});
describe('translate', () => {
it('should successfully translate all keys', () => __awaiter(void 0, void 0, void 0, function* () {
const mockTranslateFn = jest.fn()
.mockResolvedValueOnce('Translated Hello')
.mockResolvedValueOnce('Translated World');
const translationWithTranslator = {
language: 'fr',
translations: { hello: 'Hello', world: 'World' },
translate: mockTranslateFn
};
const result = yield (0, translationsParser_1.translate)(translationWithTranslator);
expect((0, utils_1.isRight)(result)).toBe(true);
if ((0, utils_1.isRight)(result)) {
expect(result.value).toEqual({
language: 'fr',
translations: {
hello: 'Translated Hello',
world: 'Translated World'
}
});
}
expect(mockTranslateFn).toHaveBeenCalledTimes(2);
expect(mockTranslateFn).toHaveBeenCalledWith('Hello');
expect(mockTranslateFn).toHaveBeenCalledWith('World');
}));
it('should handle translation errors', () => __awaiter(void 0, void 0, void 0, function* () {
const error = new Error('Translation failed');
const mockTranslateFn = jest.fn().mockRejectedValue(error);
const translationWithTranslator = {
language: 'fr',
translations: { hello: 'Hello' },
translate: mockTranslateFn
};
try {
yield (0, translationsParser_1.translate)(translationWithTranslator);
fail('Should have thrown an error');
}
catch (caughtError) {
expect(caughtError).toBe(error);
}
}));
it('should handle empty translations object', () => __awaiter(void 0, void 0, void 0, function* () {
const mockTranslateFn = jest.fn();
const translationWithTranslator = {
language: 'fr',
translations: {},
translate: mockTranslateFn
};
const result = yield (0, translationsParser_1.translate)(translationWithTranslator);
expect((0, utils_1.isRight)(result)).toBe(true);
if ((0, utils_1.isRight)(result)) {
expect(result.value).toEqual({
language: 'fr',
translations: {}
});
}
expect(mockTranslateFn).not.toHaveBeenCalled();
}));
it('should handle single translation', () => __awaiter(void 0, void 0, void 0, function* () {
const mockTranslateFn = jest.fn().mockResolvedValue('Translated Single');
const translationWithTranslator = {
language: 'es',
translations: { single: 'Single' },
translate: mockTranslateFn
};
const result = yield (0, translationsParser_1.translate)(translationWithTranslator);
expect((0, utils_1.isRight)(result)).toBe(true);
if ((0, utils_1.isRight)(result)) {
expect(result.value).toEqual({
language: 'es',
translations: { single: 'Translated Single' }
});
}
expect(mockTranslateFn).toHaveBeenCalledTimes(1);
expect(mockTranslateFn).toHaveBeenCalledWith('Single');
}));
});
describe('addMissingTranslations (integration tests)', () => {
it('should process translations and call writer for missing translations', () => __awaiter(void 0, void 0, void 0, function* () {
const dependencies = {
reader: mockReader,
writer: mockWriter,
translator: mockTranslator
};
yield (0, translationsParser_1.addMissingTranslations)('en', dependencies);
// Verify reader was called
expect(mockReader).toHaveBeenCalledTimes(1);
// Verify translator was called for missing translations
expect(mockTranslator).toHaveBeenCalledTimes(3);
// Check that translator was called with the prompt containing our text
expect(mockTranslator.mock.calls.some(call => call[0].includes('Hi there'))).toBe(true);
expect(mockTranslator.mock.calls.some(call => call[0].includes('World'))).toBe(true);
// Verify writer was called with translated results
expect(mockWriter).toHaveBeenCalledTimes(2); // once for fr, once for es
// Check that writer was called (exact structure may vary)
expect(mockWriter).toHaveBeenCalled();
}));
it('should handle reader errors', () => __awaiter(void 0, void 0, void 0, function* () {
const failingReader = jest.fn(() => Promise.resolve((0, utils_1.left)(new Error('Reader failed'))));
const consoleSpy = jest.spyOn(console, 'error').mockImplementation();
const dependencies = {
reader: failingReader,
writer: mockWriter,
translator: mockTranslator
};
yield (0, translationsParser_1.addMissingTranslations)('en', dependencies);
expect(consoleSpy).toHaveBeenCalledWith(expect.any(Error));
expect(mockWriter).not.toHaveBeenCalled();
consoleSpy.mockRestore();
}));
it('should handle translator errors', () => __awaiter(void 0, void 0, void 0, function* () {
const failingTranslator = jest.fn(() => Promise.reject(new Error('Translation failed')));
const consoleSpy = jest.spyOn(console, 'error').mockImplementation();
const dependencies = {
reader: mockReader,
writer: mockWriter,
translator: failingTranslator
};
yield (0, translationsParser_1.addMissingTranslations)('en', dependencies);
expect(consoleSpy).toHaveBeenCalledWith(expect.any(Error));
consoleSpy.mockRestore();
}));
it('should handle writer errors', () => __awaiter(void 0, void 0, void 0, function* () {
const failingWriter = jest.fn(() => Promise.resolve((0, utils_1.left)(new Error('Writer failed'))));
const consoleSpy = jest.spyOn(console, 'error').mockImplementation();
const dependencies = {
reader: mockReader,
writer: failingWriter,
translator: mockTranslator
};
yield (0, translationsParser_1.addMissingTranslations)('en', dependencies);
expect(consoleSpy).toHaveBeenCalledWith(expect.any(Error));
consoleSpy.mockRestore();
}));
it('should handle case where all translations are complete', () => __awaiter(void 0, void 0, void 0, function* () {
const completeTranslations = {
en: { hello: 'Hello', world: 'World' },
fr: { hello: 'Bonjour', world: 'Monde' },
es: { hello: 'Hola', world: 'Mundo' }
};
const completeReader = jest.fn(() => Promise.resolve((0, utils_1.right)(completeTranslations)));
const dependencies = {
reader: completeReader,
writer: mockWriter,
translator: mockTranslator
};
yield (0, translationsParser_1.addMissingTranslations)('en', dependencies);
expect(mockTranslator).not.toHaveBeenCalled();
// Note: writer might still be called for empty translations - that's expected behavior
}));
it('should handle empty translations', () => __awaiter(void 0, void 0, void 0, function* () {
const emptyTranslations = {
en: { hello: 'Hello' },
fr: {},
es: {}
};
const emptyReader = jest.fn(() => Promise.resolve((0, utils_1.right)(emptyTranslations)));
const dependencies = {
reader: emptyReader,
writer: mockWriter,
translator: mockTranslator
};
yield (0, translationsParser_1.addMissingTranslations)('en', dependencies);
expect(mockTranslator).toHaveBeenCalledTimes(2); // once for fr, once for es
expect(mockTranslator.mock.calls.some(call => call[0].includes('Hello'))).toBe(true);
expect(mockWriter).toHaveBeenCalledTimes(2);
}));
it('should handle missing default language', () => __awaiter(void 0, void 0, void 0, function* () {
const missingDefaultTranslations = {
fr: { hello: 'Bonjour' },
es: { hello: 'Hola' }
};
const missingDefaultReader = jest.fn(() => Promise.resolve((0, utils_1.right)(missingDefaultTranslations)));
const consoleSpy = jest.spyOn(console, 'error').mockImplementation();
const dependencies = {
reader: missingDefaultReader,
writer: mockWriter,
translator: mockTranslator
};
yield (0, translationsParser_1.addMissingTranslations)('en', dependencies);
// Should handle gracefully - the default translations would be undefined
expect(consoleSpy).toHaveBeenCalled();
consoleSpy.mockRestore();
}));
});
describe('edge cases', () => {
it('should handle single language scenario', () => __awaiter(void 0, void 0, void 0, function* () {
const singleLanguageTranslations = {
en: { hello: 'Hello', world: 'World' }
};
const singleLanguageReader = jest.fn(() => Promise.resolve((0, utils_1.right)(singleLanguageTranslations)));
const dependencies = {
reader: singleLanguageReader,
writer: mockWriter,
translator: mockTranslator
};
yield (0, translationsParser_1.addMissingTranslations)('en', dependencies);
// No other languages to translate to
expect(mockTranslator).not.toHaveBeenCalled();
expect(mockWriter).not.toHaveBeenCalled();
}));
it('should handle special characters and unicode in translations', () => __awaiter(void 0, void 0, void 0, function* () {
const unicodeTranslations = {
en: { emoji: '🌍', special: 'Special chars: àáâãäå' },
fr: {} // missing both
};
const unicodeReader = jest.fn(() => Promise.resolve((0, utils_1.right)(unicodeTranslations)));
const dependencies = {
reader: unicodeReader,
writer: mockWriter,
translator: mockTranslator
};
yield (0, translationsParser_1.addMissingTranslations)('en', dependencies);
expect(mockTranslator.mock.calls.some(call => call[0].includes('🌍'))).toBe(true);
expect(mockTranslator.mock.calls.some(call => call[0].includes('Special chars: àáâãäå'))).toBe(true);
}));
});
});