UNPKG

auto-translatr

Version:
288 lines (287 loc) 15 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); 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 fs = __importStar(require("fs")); const translationsReader_1 = require("../translationsReader"); const utils_1 = require("../utils"); const path = __importStar(require("path")); jest.mock('fs'); const mockedFs = fs; const i18nFolder = 'i18n'; const CWD = '/fake/project/root'; // Helper to get expected file path const getTranslationFilePath = (language) => { return path.join(CWD, i18nFolder, `${language}.json`); }; describe('translationsReader', () => { beforeEach(() => { jest.clearAllMocks(); // Mock process.cwd() jest.spyOn(process, 'cwd').mockReturnValue(CWD); // Set up default mock implementations mockedFs.existsSync.mockReturnValue(false); mockedFs.readFileSync.mockReturnValue('{}'); mockedFs.writeFileSync.mockReturnValue(undefined); mockedFs.mkdirSync.mockReturnValue(undefined); }); describe('readAllTranslations', () => { it('should read all existing translation files correctly', () => { const supportedLanguages = ['en', 'fr']; const enTranslations = { hello: 'Hello' }; const frTranslations = { hello: 'Bonjour' }; mockedFs.existsSync.mockImplementation((p) => { return p === getTranslationFilePath('en') || p === getTranslationFilePath('fr'); }); mockedFs.readFileSync.mockImplementation((p) => { if (p === getTranslationFilePath('en')) { return JSON.stringify(enTranslations); } if (p === getTranslationFilePath('fr')) { return JSON.stringify(frTranslations); } return '{}'; }); const result = (0, translationsReader_1.readAllTranslations)(supportedLanguages); expect((0, utils_1.isRight)(result)).toBe(true); if ((0, utils_1.isRight)(result)) { expect(result.value).toEqual({ en: enTranslations, fr: frTranslations, }); } expect(mockedFs.existsSync).toHaveBeenCalledTimes(2); expect(mockedFs.readFileSync).toHaveBeenCalledTimes(2); }); it('should return an empty object for languages with no translation file', () => { const supportedLanguages = ['en', 'es']; const enTranslations = { hello: 'Hello' }; mockedFs.existsSync.mockImplementation((p) => p === getTranslationFilePath('en')); mockedFs.readFileSync.mockImplementation((p) => { if (p === getTranslationFilePath('en')) { return JSON.stringify(enTranslations); } return '{}'; }); const result = (0, translationsReader_1.readAllTranslations)(supportedLanguages); expect((0, utils_1.isRight)(result)).toBe(true); if ((0, utils_1.isRight)(result)) { expect(result.value).toEqual({ en: enTranslations, es: {}, }); } }); it('should return an empty object if no languages are provided', () => { const result = (0, translationsReader_1.readAllTranslations)([]); expect((0, utils_1.isRight)(result)).toBe(true); if ((0, utils_1.isRight)(result)) { expect(result.value).toEqual({}); } expect(mockedFs.existsSync).not.toHaveBeenCalled(); }); it('should handle malformed JSON gracefully and continue with other languages', () => { const supportedLanguages = ['en', 'fr']; const consoleSpy = jest.spyOn(console, 'warn').mockImplementation(); mockedFs.existsSync.mockReturnValue(true); mockedFs.readFileSync.mockImplementation((p) => { if (p === getTranslationFilePath('en')) { return '{"hello": "Hello",}'; // Invalid JSON } if (p === getTranslationFilePath('fr')) { return '{"hello": "Bonjour"}'; // Valid JSON } return '{}'; }); const result = (0, translationsReader_1.readAllTranslations)(supportedLanguages); expect((0, utils_1.isRight)(result)).toBe(true); if ((0, utils_1.isRight)(result)) { expect(result.value).toEqual({ en: {}, // Should be empty due to JSON parse error fr: { hello: 'Bonjour' }, }); } expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining("Warning: Could not read translation file for language 'en'")); consoleSpy.mockRestore(); }); it('should handle file system errors gracefully for individual files', () => { const supportedLanguages = ['en', 'fr']; const consoleSpy = jest.spyOn(console, 'warn').mockImplementation(); mockedFs.existsSync.mockImplementation((p) => { if (p === getTranslationFilePath('en')) { throw new Error('File system error'); } return p === getTranslationFilePath('fr'); }); mockedFs.readFileSync.mockImplementation((p) => { if (p === getTranslationFilePath('fr')) { return '{"hello": "Bonjour"}'; } return '{}'; }); const result = (0, translationsReader_1.readAllTranslations)(supportedLanguages); expect((0, utils_1.isRight)(result)).toBe(true); if ((0, utils_1.isRight)(result)) { expect(result.value).toEqual({ en: {}, // Should be empty due to file system error fr: { hello: 'Bonjour' }, }); } expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining("Warning: Could not read translation file for language 'en'")); consoleSpy.mockRestore(); }); it('should handle edge cases gracefully', () => { // Test with a very large number of languages to ensure robustness const manyLanguages = Array.from({ length: 100 }, (_, i) => `lang${i}`); mockedFs.existsSync.mockReturnValue(false); // No files exist const result = (0, translationsReader_1.readAllTranslations)(manyLanguages); expect((0, utils_1.isRight)(result)).toBe(true); if ((0, utils_1.isRight)(result)) { expect(Object.keys(result.value)).toHaveLength(100); expect(Object.values(result.value).every(translations => Object.keys(translations).length === 0)).toBe(true); } }); }); describe('writeLanguageFile', () => { it('should write a new file when it does not exist', () => { const lang = 'de'; const translations = { welcome: 'Willkommen' }; const filePath = getTranslationFilePath(lang); // Directory doesn't exist, file doesn't exist mockedFs.existsSync.mockReturnValue(false); const result = (0, translationsReader_1.writeLanguageFile)(lang, translations); expect((0, utils_1.isRight)(result)).toBe(true); expect(mockedFs.mkdirSync).toHaveBeenCalledWith(path.dirname(filePath), { recursive: true }); expect(mockedFs.writeFileSync).toHaveBeenCalledWith(filePath, JSON.stringify(translations, null, 2), 'utf-8'); }); it('should overwrite an existing file when append is false', () => { const lang = 'en'; const translations = { bye: 'Goodbye' }; const filePath = getTranslationFilePath(lang); // Directory exists, file exists, but we're not appending mockedFs.existsSync.mockImplementation((p) => { return p === path.dirname(filePath); // directory exists }); const result = (0, translationsReader_1.writeLanguageFile)(lang, translations, { append: false }); expect((0, utils_1.isRight)(result)).toBe(true); // readFileSync should NOT be called when overwriting (append: false) expect(mockedFs.readFileSync).not.toHaveBeenCalled(); expect(mockedFs.writeFileSync).toHaveBeenCalledWith(filePath, JSON.stringify(translations, null, 2), 'utf-8'); }); it('should append to an existing file when append is true', () => { const lang = 'en'; const newTranslations = { bye: 'Goodbye' }; const filePath = getTranslationFilePath(lang); const existingTranslations = { hello: 'Hello' }; const mergedTranslations = Object.assign(Object.assign({}, existingTranslations), newTranslations); // Directory and file both exist mockedFs.existsSync.mockReturnValue(true); mockedFs.readFileSync.mockReturnValue(JSON.stringify(existingTranslations)); const result = (0, translationsReader_1.writeLanguageFile)(lang, newTranslations, { append: true }); expect((0, utils_1.isRight)(result)).toBe(true); expect(mockedFs.readFileSync).toHaveBeenCalledWith(filePath, 'utf-8'); expect(mockedFs.writeFileSync).toHaveBeenCalledWith(filePath, JSON.stringify(mergedTranslations, null, 2), 'utf-8'); }); it('should return a left with an error if writing fails', () => { const lang = 'fr'; const translations = { test: 'Test' }; const error = new Error('Disk full'); mockedFs.existsSync.mockReturnValue(false); mockedFs.writeFileSync.mockImplementation(() => { throw error; }); const result = (0, translationsReader_1.writeLanguageFile)(lang, translations); expect((0, utils_1.isLeft)(result)).toBe(true); if ((0, utils_1.isLeft)(result)) { expect(result.value).toBe(error); } }); it('should return left if reading existing file for append fails', () => { const lang = 'en'; const translations = { test: 'Test' }; mockedFs.existsSync.mockReturnValue(true); mockedFs.readFileSync.mockReturnValue('{"malformed": json,}'); // Invalid JSON const result = (0, translationsReader_1.writeLanguageFile)(lang, translations, { append: true }); expect((0, utils_1.isLeft)(result)).toBe(true); if ((0, utils_1.isLeft)(result)) { expect(result.value.message).toContain(`Failed to read existing translation file for '${lang}'`); } }); }); describe('writeLanguageFiles', () => { it('should write all language files successfully', () => __awaiter(void 0, void 0, void 0, function* () { const translations = { en: { hello: 'Hello' }, fr: { hello: 'Bonjour' }, }; // Setup mocks for successful writes mockedFs.existsSync.mockReturnValue(false); // directories don't exist const result = yield (0, translationsReader_1.writeLanguageFiles)(translations, { append: false }); expect((0, utils_1.isRight)(result)).toBe(true); expect(mockedFs.writeFileSync).toHaveBeenCalledTimes(2); expect(mockedFs.writeFileSync).toHaveBeenCalledWith(getTranslationFilePath('en'), JSON.stringify(translations.en, null, 2), 'utf-8'); expect(mockedFs.writeFileSync).toHaveBeenCalledWith(getTranslationFilePath('fr'), JSON.stringify(translations.fr, null, 2), 'utf-8'); })); it('should return a left if any file write fails', () => __awaiter(void 0, void 0, void 0, function* () { const translations = { en: { hello: 'Hello' }, fr: { hello: 'Bonjour' }, }; const error = new Error('Write error'); mockedFs.existsSync.mockReturnValue(false); mockedFs.writeFileSync.mockImplementation((p) => { if (p.includes('fr.json')) { throw error; } }); const result = yield (0, translationsReader_1.writeLanguageFiles)(translations, { append: false }); expect((0, utils_1.isLeft)(result)).toBe(true); if ((0, utils_1.isLeft)(result)) { expect(result.value).toBe(error); } // It should have tried to write 'en' successfully and then failed on 'fr' expect(mockedFs.writeFileSync).toHaveBeenCalledWith(getTranslationFilePath('en'), JSON.stringify(translations.en, null, 2), 'utf-8'); expect(mockedFs.writeFileSync).toHaveBeenCalledTimes(2); })); }); });