UNPKG

auto-translatr

Version:
427 lines (426 loc) 22.4 kB
"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); })); }); });