auto-translatr
Version:
An automatic translation library
434 lines (433 loc) • 18.7 kB
JavaScript
;
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;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const settingsReader_1 = require("../settingsReader");
const utils_1 = require("../utils");
// Mock console methods
const originalConsoleLog = console.log;
const originalConsoleWarn = console.warn;
const originalConsoleError = console.error;
beforeEach(() => {
console.log = jest.fn();
console.warn = jest.fn();
console.error = jest.fn();
// Clear all mocks before each test
jest.clearAllMocks();
jest.restoreAllMocks();
});
afterEach(() => {
console.log = originalConsoleLog;
console.warn = originalConsoleWarn;
console.error = originalConsoleError;
// Restore all mocks after each test
jest.restoreAllMocks();
});
describe('settingsReader', () => {
const testDir = path.join(__dirname, 'test-settings');
const settingsFileName = 'auto-translate.settings.json';
const settingsFilePath = path.join(testDir, settingsFileName);
const originalEnv = process.env.OPENAI_API_KEY;
beforeEach(() => {
// Create test directory
if (fs.existsSync(testDir)) {
try {
fs.rmSync(testDir, { recursive: true, force: true });
}
catch (error) {
// Ignore cleanup errors
}
}
fs.mkdirSync(testDir, { recursive: true });
// Clean environment
delete process.env.OPENAI_API_KEY;
});
afterEach(() => {
// Clean up test directory with retry logic
let retries = 3;
while (retries > 0) {
try {
if (fs.existsSync(testDir)) {
fs.rmSync(testDir, { recursive: true, force: true });
}
break;
}
catch (error) {
retries--;
if (retries === 0) {
console.warn('Could not clean up test directory:', error);
}
}
}
// Clean up any settings files in current directory
try {
const currentDirSettingsFile = path.join(process.cwd(), settingsFileName);
if (fs.existsSync(currentDirSettingsFile)) {
fs.rmSync(currentDirSettingsFile);
}
}
catch (error) {
// Ignore cleanup errors
}
// Restore environment
if (originalEnv) {
process.env.OPENAI_API_KEY = originalEnv;
}
else {
delete process.env.OPENAI_API_KEY;
}
});
describe('getDefaultSettings', () => {
it('should return default settings without environment variable', () => {
const settings = (0, settingsReader_1.getDefaultSettings)();
expect(settings).toEqual({
default: 'en',
supported: ['en', 'fr', 'de'],
openai: {
model: 'gpt-4o-mini',
apiKey: ''
}
});
});
it('should include environment variable for API key when available', () => {
process.env.OPENAI_API_KEY = 'test-api-key';
const settings = (0, settingsReader_1.getDefaultSettings)();
expect(settings).toEqual({
default: 'en',
supported: ['en', 'fr', 'de'],
openai: {
model: 'gpt-4o-mini',
apiKey: 'test-api-key'
}
});
});
});
describe('readSettingsFile', () => {
const originalCwd = process.cwd();
afterEach(() => {
process.chdir(originalCwd);
});
it('should return Left when settings file does not exist', () => {
process.chdir(testDir);
const result = (0, settingsReader_1.readSettingsFile)();
expect((0, utils_1.isLeft)(result)).toBe(true);
if ((0, utils_1.isLeft)(result)) {
expect(result.value.message).toBe('Settings file auto-translate.settings.json not found');
}
});
it('should return Right with settings when file exists and is valid', () => {
const validSettings = {
default: 'es',
supported: ['es', 'en', 'pt'],
openai: {
model: 'gpt-4',
apiKey: 'file-api-key'
}
};
process.chdir(testDir);
fs.writeFileSync(settingsFilePath, JSON.stringify(validSettings, null, 2));
const result = (0, settingsReader_1.readSettingsFile)();
expect((0, utils_1.isRight)(result)).toBe(true);
if ((0, utils_1.isRight)(result)) {
expect(result.value).toEqual(validSettings);
}
});
it('should automatically add default language to supported array when missing', () => {
const settingsWithMissingDefault = {
default: 'ja',
supported: ['en', 'fr'],
openai: {
model: 'gpt-4',
apiKey: 'test-key'
}
};
process.chdir(testDir);
fs.writeFileSync(settingsFilePath, JSON.stringify(settingsWithMissingDefault, null, 2));
const result = (0, settingsReader_1.readSettingsFile)();
expect((0, utils_1.isRight)(result)).toBe(true);
if ((0, utils_1.isRight)(result)) {
expect(result.value.supported).toEqual(['ja', 'en', 'fr']);
expect(result.value.default).toBe('ja');
}
// Should log a warning
expect(console.warn).toHaveBeenCalledWith("⚠️ Default language 'ja' not found in supported languages. Adding it automatically.");
});
it('should merge environment API key when file has empty API key', () => {
process.env.OPENAI_API_KEY = 'env-api-key';
const settingsWithoutApiKey = {
default: 'fr',
supported: ['fr', 'en'],
openai: {
model: 'gpt-3.5-turbo',
apiKey: ''
}
};
process.chdir(testDir);
fs.writeFileSync(settingsFilePath, JSON.stringify(settingsWithoutApiKey, null, 2));
const result = (0, settingsReader_1.readSettingsFile)();
expect((0, utils_1.isRight)(result)).toBe(true);
if ((0, utils_1.isRight)(result)) {
expect(result.value.openai.apiKey).toBe('env-api-key');
}
});
it('should prefer file API key over environment when file has non-empty API key', () => {
process.env.OPENAI_API_KEY = 'env-api-key';
const settingsWithApiKey = {
default: 'de',
supported: ['de', 'en'],
openai: {
model: 'gpt-4',
apiKey: 'file-api-key'
}
};
process.chdir(testDir);
fs.writeFileSync(settingsFilePath, JSON.stringify(settingsWithApiKey, null, 2));
const result = (0, settingsReader_1.readSettingsFile)();
expect((0, utils_1.isRight)(result)).toBe(true);
if ((0, utils_1.isRight)(result)) {
expect(result.value.openai.apiKey).toBe('file-api-key');
}
});
it('should return Left when settings file has invalid JSON', () => {
process.chdir(testDir);
fs.writeFileSync(settingsFilePath, '{ invalid json }');
const result = (0, settingsReader_1.readSettingsFile)();
expect((0, utils_1.isLeft)(result)).toBe(true);
if ((0, utils_1.isLeft)(result)) {
expect(result.value).toBeInstanceOf(Error);
}
});
it('should return Left when default field is missing', () => {
const settingsWithoutDefault = {
supported: ['en', 'fr'],
openai: {
model: 'gpt-4',
apiKey: 'test-key'
}
};
process.chdir(testDir);
fs.writeFileSync(settingsFilePath, JSON.stringify(settingsWithoutDefault, null, 2));
const result = (0, settingsReader_1.readSettingsFile)();
expect((0, utils_1.isLeft)(result)).toBe(true);
if ((0, utils_1.isLeft)(result)) {
expect(result.value.message).toBe('Settings file is missing required field: default');
}
});
it('should return Left when supported field is missing', () => {
const settingsWithoutSupported = {
default: 'en',
openai: {
model: 'gpt-4',
apiKey: 'test-key'
}
};
process.chdir(testDir);
fs.writeFileSync(settingsFilePath, JSON.stringify(settingsWithoutSupported, null, 2));
const result = (0, settingsReader_1.readSettingsFile)();
expect((0, utils_1.isLeft)(result)).toBe(true);
if ((0, utils_1.isLeft)(result)) {
expect(result.value.message).toBe('Settings file is missing required field: supported (must be a non-empty array)');
}
});
it('should return Left when supported field is empty array', () => {
const settingsWithEmptySupported = {
default: 'en',
supported: [],
openai: {
model: 'gpt-4',
apiKey: 'test-key'
}
};
process.chdir(testDir);
fs.writeFileSync(settingsFilePath, JSON.stringify(settingsWithEmptySupported, null, 2));
const result = (0, settingsReader_1.readSettingsFile)();
expect((0, utils_1.isLeft)(result)).toBe(true);
if ((0, utils_1.isLeft)(result)) {
expect(result.value.message).toBe('Settings file is missing required field: supported (must be a non-empty array)');
}
});
it('should return Left when openai.model field is missing', () => {
const settingsWithoutModel = {
default: 'en',
supported: ['en', 'fr'],
openai: {
apiKey: 'test-key'
}
};
process.chdir(testDir);
fs.writeFileSync(settingsFilePath, JSON.stringify(settingsWithoutModel, null, 2));
const result = (0, settingsReader_1.readSettingsFile)();
expect((0, utils_1.isLeft)(result)).toBe(true);
if ((0, utils_1.isLeft)(result)) {
expect(result.value.message).toBe('Settings file is missing required field: openai.model');
}
});
});
describe('createDefaultSettingsFile', () => {
const originalCwd = process.cwd();
afterEach(() => {
process.chdir(originalCwd);
});
it('should create settings file with default values', () => {
process.chdir(testDir);
const result = (0, settingsReader_1.createDefaultSettingsFile)();
expect((0, utils_1.isRight)(result)).toBe(true);
expect(fs.existsSync(settingsFilePath)).toBe(true);
const fileContent = JSON.parse(fs.readFileSync(settingsFilePath, 'utf-8'));
expect(fileContent).toEqual({
default: 'en',
supported: ['en', 'fr', 'de'],
openai: {
model: 'gpt-4o-mini',
apiKey: '' // Should be empty in file for security
}
});
if ((0, utils_1.isRight)(result)) {
// But should return settings with environment API key if available
expect(result.value.openai.apiKey).toBe('');
}
});
it('should create settings file and return settings with environment API key', () => {
process.env.OPENAI_API_KEY = 'test-env-key';
process.chdir(testDir);
const result = (0, settingsReader_1.createDefaultSettingsFile)();
expect((0, utils_1.isRight)(result)).toBe(true);
// File should still have empty API key for security
const fileContent = JSON.parse(fs.readFileSync(settingsFilePath, 'utf-8'));
expect(fileContent.openai.apiKey).toBe('');
// But returned settings should have environment API key
if ((0, utils_1.isRight)(result)) {
expect(result.value.openai.apiKey).toBe('test-env-key');
}
});
it('should log creation messages', () => {
process.chdir(testDir);
(0, settingsReader_1.createDefaultSettingsFile)();
expect(console.log).toHaveBeenCalledWith('📄 Created auto-translate.settings.json with default settings');
expect(console.log).toHaveBeenCalledWith('💡 Please set your OpenAI API key in the file or as OPENAI_API_KEY environment variable');
});
});
describe('loadSettings', () => {
const originalCwd = process.cwd();
afterEach(() => {
process.chdir(originalCwd);
});
it('should return existing settings when file exists', () => {
const existingSettings = {
default: 'ja',
supported: ['ja', 'en', 'ko'],
openai: {
model: 'gpt-4-turbo',
apiKey: 'existing-key'
}
};
process.chdir(testDir);
fs.writeFileSync(settingsFilePath, JSON.stringify(existingSettings, null, 2));
const result = (0, settingsReader_1.loadSettings)();
expect((0, utils_1.isRight)(result)).toBe(true);
if ((0, utils_1.isRight)(result)) {
expect(result.value).toEqual(existingSettings);
}
expect(console.log).toHaveBeenCalledWith('⚙️ Loaded settings from auto-translate.settings.json');
});
it('should create default settings when file does not exist', () => {
process.chdir(testDir);
const result = (0, settingsReader_1.loadSettings)();
expect((0, utils_1.isRight)(result)).toBe(true);
expect(fs.existsSync(settingsFilePath)).toBe(true);
if ((0, utils_1.isRight)(result)) {
expect(result.value).toEqual({
default: 'en',
supported: ['en', 'fr', 'de'],
openai: {
model: 'gpt-4o-mini',
apiKey: ''
}
});
}
expect(console.log).toHaveBeenCalledWith('⚙️ Settings file auto-translate.settings.json not found, creating default settings...');
});
it('should auto-fix settings when default language is missing from supported array', () => {
const settingsWithMissingDefault = {
default: 'pt',
supported: ['en', 'fr'],
openai: {
model: 'gpt-4',
apiKey: 'test-key'
}
};
process.chdir(testDir);
fs.writeFileSync(settingsFilePath, JSON.stringify(settingsWithMissingDefault, null, 2));
const result = (0, settingsReader_1.loadSettings)();
expect((0, utils_1.isRight)(result)).toBe(true);
if ((0, utils_1.isRight)(result)) {
expect(result.value.supported).toEqual(['pt', 'en', 'fr']);
expect(result.value.default).toBe('pt');
}
expect(console.warn).toHaveBeenCalledWith("⚠️ Default language 'pt' not found in supported languages. Adding it automatically.");
});
});
describe('Settings type validation', () => {
it('should handle settings with all required fields', () => {
const validSettings = {
default: 'pt',
supported: ['pt', 'es', 'en'],
openai: {
model: 'gpt-4',
apiKey: 'test-key'
}
};
const originalCwd = process.cwd();
process.chdir(testDir);
try {
fs.writeFileSync(settingsFilePath, JSON.stringify(validSettings, null, 2));
const result = (0, settingsReader_1.readSettingsFile)();
expect((0, utils_1.isRight)(result)).toBe(true);
if ((0, utils_1.isRight)(result)) {
expect(result.value.default).toBe('pt');
expect(result.value.supported).toContain('pt');
expect(result.value.supported).toContain('es');
expect(result.value.supported).toContain('en');
expect(result.value.openai.model).toBe('gpt-4');
expect(result.value.openai.apiKey).toBe('test-key');
}
}
finally {
process.chdir(originalCwd);
}
});
});
});