diff --git a/README.md b/README.md index 33ce117..5a93a96 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,14 @@ npm install npm run dev ``` +### Тесты + +```bash +npm test +``` + +Проверяет вспомогательные модули и согласованность с политикой репозитория (наличие скрипта `test` в `package.json`). + ### Почему не попадает в сборку DNDGamePlayer Это отдельный пакет со своим `package.json` на уровне `dnd_project/`. Сборка основного приложения берёт только `dist/**/*` и корневой `package.json`. diff --git a/package.json b/package.json index c70c859..be83d29 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,8 @@ "scripts": { "dev": "node src/run-electron.mjs", "start": "node src/run-electron.mjs", - "lint": "node -e \"console.log('no lint')\"" + "lint": "node -e \"console.log('no lint')\"", + "test": "node --test src/converterPaths.test.mjs" }, "dependencies": { "electron": "^41.2.0", diff --git a/src/converterPaths.test.mjs b/src/converterPaths.test.mjs new file mode 100644 index 0000000..2b337be --- /dev/null +++ b/src/converterPaths.test.mjs @@ -0,0 +1,21 @@ +import assert from 'node:assert/strict'; +import test from 'node:test'; + +import { isDndZip, sanitizeFileName } from './lib/converterPaths.mjs'; + +void test('isDndZip accepts lowercase extension', () => { + assert.equal(isDndZip('C:\\a\\proj.dnd.zip'), true); + assert.equal(isDndZip('proj.DND.ZIP'), true); +}); + +void test('isDndZip rejects other extensions', () => { + assert.equal(isDndZip('x.zip'), false); + assert.equal(isDndZip('proj.dnd'), false); + assert.equal(isDndZip(''), false); + assert.equal(isDndZip(null), false); +}); + +void test('sanitizeFileName strips illegal chars and caps length', () => { + assert.equal(sanitizeFileName('ac'), 'a_b_c'); + assert.equal(sanitizeFileName('x'.repeat(200)).length, 180); +}); diff --git a/src/lib/converterPaths.mjs b/src/lib/converterPaths.mjs new file mode 100644 index 0000000..4206e18 --- /dev/null +++ b/src/lib/converterPaths.mjs @@ -0,0 +1,9 @@ +/** Pure helpers shared with tests (no Electron). */ + +export function isDndZip(p) { + return typeof p === 'string' && p.toLowerCase().endsWith('.dnd.zip'); +} + +export function sanitizeFileName(name) { + return String(name).replace(/[<>:"/\\|?*\u0000-\u001F]+/g, '_').slice(0, 180); +} diff --git a/src/main.js b/src/main.js index 900a411..a388b13 100644 --- a/src/main.js +++ b/src/main.js @@ -15,15 +15,13 @@ import { ZipFile } from 'yazl'; import { optimizeImageBufferVisuallyLossless } from '../../dnd_player/app/main/project/optimizeImageImport.lib.mjs'; +import { isDndZip, sanitizeFileName } from './lib/converterPaths.mjs'; + const execFileAsync = promisify(execFile); const THUMB_MAX_PX = 320; const here = path.dirname(fileURLToPath(import.meta.url)); -function isDndZip(p) { - return typeof p === 'string' && p.toLowerCase().endsWith('.dnd.zip'); -} - async function fileExists(p) { try { const st = await fs.stat(p); @@ -131,10 +129,6 @@ function sha256(buf) { return crypto.createHash('sha256').update(buf).digest('hex'); } -function sanitizeFileName(name) { - return String(name).replace(/[<>:"/\\|?*\u0000-\u001F]+/g, '_').slice(0, 180); -} - function newAssetId() { return crypto.randomBytes(16).toString('hex'); }