import { test } from 'node:test'; import assert from 'node:assert/strict'; import { compare } from './translation-upgrade.mjs'; import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; import { spawnSync } from 'node:child_process'; import { fileURLToPath } from 'node:url'; test('추가와 변경 및 삭제를 구분하고 기존 번역을 검토 후보로 보존한다', () => { const result = compare({ same: 'Hello', changed: 'Old', gone: 'Gone' }, { same: '안녕하세요', changed: '이전', gone: '삭제' }, { same: 'Hello', changed: 'New', added: 'Add' }); assert.deepEqual(result.map(x => x.status), ['reusable', 'source-changed', 'added', 'removed']); assert.equal(result[1].korean, '이전'); }); test('자리표시자 누락과 미번역을 자동 재사용에서 제외한다', () => { const en = { a: 'Hi {{name}}', b: 'Name', c: 'Value', d: 'Brand' }; assert.deepEqual(compare(en, { a: '안녕', b: '', d: 'Brand' }, en).map(x => x.status), ['placeholder-mismatch', 'missing', 'missing', 'review-identical']); }); test('점이 포함된 키와 중첩 키 및 배열을 구분한다', () => { const en = { 'a.b': 'One', a: { b: 'Two' }, list: ['Three'] }; const ko = { 'a.b': '하나', a: { b: '둘' }, list: ['셋'] }; assert.equal(compare(en, ko, en).filter(x => x.status === 'reusable').length, 3); }); test('파일 비교는 변경과 삭제 및 추가를 구분하고 기존 보고서를 덮어쓰지 않는다', () => { const temp = fs.mkdtempSync(path.join(os.tmpdir(), 'trilium-translation-')); try { const old = path.join(temp, 'old'), next = path.join(temp, 'next'); fs.mkdirSync(old); fs.mkdirSync(next); fs.writeFileSync(path.join(old, 'main.cjs'), 'old'); fs.writeFileSync(path.join(next, 'main.cjs'), 'next'); fs.mkdirSync(path.join(old, 'assets/db'), { recursive: true }); fs.writeFileSync(path.join(old, 'assets/db/demo.zip'), 'old-demo'); fs.mkdirSync(path.join(next, 'public/src'), { recursive: true }); fs.writeFileSync(path.join(next, 'public/src/ko.js'), 'next-ko'); const output = path.join(temp, 'report.json'); const tool = fileURLToPath(new URL('./translation-files.mjs', import.meta.url)); const run = () => spawnSync(process.execPath, [tool, old, next, output], { encoding: 'utf8' }); assert.equal(run().status, 0); const saved = fs.readFileSync(output, 'utf8'); const entries = JSON.parse(saved).files; assert.equal(entries.find(x => x.file === 'main.cjs').status, 'upstream-changed'); assert.equal(entries.find(x => x.file === 'assets/db/demo.zip').status, 'removed-or-moved'); assert.equal(entries.find(x => x.file === 'public/src/ko.js').status, 'new-upstream-path'); assert.notEqual(run().status, 0); assert.equal(fs.readFileSync(output, 'utf8'), saved); } finally { // 이 검사에서 만든 임시 폴더만 정리한다. 사용자 자료 경로는 받지 않는다. fs.rmSync(temp, { recursive: true, force: true }); } });