Files
SpineViewer/SpineThumbnailGenerateServer/tests/compound-compat.test.js
T

191 lines
6.5 KiB
JavaScript

'use strict';
const assert = require('node:assert/strict');
const fs = require('node:fs');
const os = require('node:os');
const path = require('node:path');
const compat = require('../js/compound-compat');
const utils = require('../js/spine-utils');
const pluginPath = path.join(__dirname, '..', 'js', 'plugin.js');
const pluginSource = fs.readFileSync(pluginPath, 'utf8');
const pageSource = fs.readFileSync(path.join(__dirname, '..', 'index.html'), 'utf8');
const releaseRoot = path.join(__dirname, '..', '..', 'release', 'UNIVERSAL_SpineThumbnailGenerateServer');
const planned = compat.planChange(
{ entryName: 'hero.atlas.txt' },
{ name: 'hero.atlas', ext: 'txt' },
utils.splitCompoundName,
);
assert.deepEqual(planned, {
sourceName: 'hero.atlas.txt',
currentName: 'hero.atlas',
currentExt: 'txt',
currentFileName: 'hero.atlas.txt',
newName: 'hero',
newExt: 'atlas.txt',
newFileName: 'hero.atlas.txt',
});
assert.deepEqual(compat.planChange(
{ entryName: 'hero.skel.bytes' },
{ name: 'hero.skel', ext: 'bytes' },
utils.splitCompoundName,
), {
sourceName: 'hero.skel.bytes',
currentName: 'hero.skel',
currentExt: 'bytes',
currentFileName: 'hero.skel.bytes',
newName: 'hero',
newExt: 'skel.bytes',
newFileName: 'hero.skel.bytes',
});
assert.equal(compat.planChange(
{ entryName: 'hero.atlas.txt' },
{ name: 'hero', ext: 'atlas.txt' },
utils.splitCompoundName,
), null);
assert.equal(compat.planChange(
{ entryName: 'hero.atlas' },
{ name: 'hero', ext: 'atlas' },
utils.splitCompoundName,
), null);
assert.equal(compat.planChange(
{ entryName: 'hero.atlas.txt' },
{ name: 'renamed', ext: 'txt' },
utils.splitCompoundName,
).reason, 'sourceMismatch');
assert.deepEqual(utils.splitCompoundName('Hero.ATLAS.TXT'), {
name: 'Hero',
ext: 'ATLAS.TXT',
});
assert.ok(
pluginSource.indexOf('eagleItem.customThumbnail === true') <
pluginSource.indexOf('const plan = compoundCompat.planChange'),
'custom thumbnails must be skipped before compatibility changes',
);
assert.match(pluginSource, /await prepareCompoundCompatibility\(ready\)[\s\S]*queue\.push/);
assert.match(pluginSource, /rollbackCompoundChange/);
assert.match(pluginSource, /name=.*ext=/);
assert.match(pluginSource, /function showCompoundConfirmation[\s\S]*input\.checked = true[\s\S]*function fieldsText/);
assert.match(pluginSource, /const selectedIds = new Set\(await showCompoundConfirmation\(changes\)\)/);
assert.match(pluginSource, /if \(location\.textContent\) content\.append\(location\)/);
assert.match(pluginSource, /if \(location\.textContent\) details\.append\(location\)/);
assert.match(pageSource, /id="compatDialog"/);
assert.match(pageSource, /id="compatConfirm"/);
assert.match(pageSource, /id="compatSkip"/);
assert.match(pageSource, /id="restoreNames"/);
assert.match(pageSource, /id="restoreDialog"/);
assert.match(pageSource, /id="restoreList"/);
assert.match(pageSource, /id="restoreApply"[^>]*disabled/);
assert.match(pluginSource, /input\.checked = canRestore/);
assert.match(pluginSource, /compoundCompat\.inspectBackups/);
assert.match(pluginSource, /compoundCompat\.restoreBackups\([\s\S]*selected,/);
assert.doesNotMatch(pluginSource, /window\.confirm\(t\('thumb\.restoreConfirm'/);
for (const relative of [
'index.html',
'js/compound-compat.js',
'js/instance-coordinator.js',
'js/spine-utils.js',
]) {
assert.equal(
fs.readFileSync(path.join(__dirname, '..', relative), 'utf8'),
fs.readFileSync(path.join(releaseRoot, relative), 'utf8'),
relative + ' must match the release copy',
);
}
for (const file of fs.readdirSync(path.join(__dirname, '..', '_locales'))) {
const relative = path.join('_locales', file);
assert.equal(
fs.readFileSync(path.join(__dirname, '..', relative), 'utf8'),
fs.readFileSync(path.join(releaseRoot, relative), 'utf8'),
relative + ' must match the release copy',
);
}
assert.equal(
pluginSource.replace('const SHOW_DEV_VERSION = true;', 'const SHOW_DEV_VERSION = false;'),
fs.readFileSync(path.join(releaseRoot, 'js', 'plugin.js'), 'utf8'),
'plugin.js may differ only by the development-version switch',
);
(async () => {
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'spine-compound-compat-'));
try {
const userData = path.join(root, '用户 data');
fs.mkdirSync(userData);
const store = compat.createStore(userData);
assert.equal(
store.file,
path.join(userData, 'SpineThumbnailGenerateServer', 'compound-name-backups.json'),
);
const original = {
itemId: 'one',
sourceName: 'hero.atlas.txt',
folderPath: 'Assets/Heroes',
currentName: 'hero.atlas',
currentExt: 'txt',
newName: 'hero',
newExt: 'atlas.txt',
};
store.remember(original);
store.remember({ ...original, newName: 'changed-again', newExt: 'txt' });
assert.equal(store.list().length, 1);
assert.equal(store.list()[0].originalName, 'hero.atlas');
const items = new Map([
['one', { name: 'hero', ext: 'atlas.txt' }],
['changed', { name: 'user-renamed', ext: 'atlas.txt' }],
['failed', { id: 'failed', name: 'failed', ext: 'atlas.txt' }],
]);
for (const itemId of ['changed', 'failed']) {
store.remember({
itemId,
sourceName: itemId + '.atlas.txt',
currentName: itemId + '.atlas',
currentExt: 'txt',
newName: itemId,
newExt: 'atlas.txt',
});
}
for (const itemId of ['ondisk', 'gone']) {
store.remember({
itemId,
sourceName: itemId + '.skel.bytes',
currentName: itemId + '.skel',
currentExt: 'bytes',
newName: itemId,
newExt: 'skel.bytes',
});
}
const inspected = await compat.inspectBackups(
store,
async (itemId) => items.get(itemId),
async (entry) => entry.itemId === 'ondisk',
);
assert.deepEqual(Object.fromEntries(inspected.map((row) => [row.entry.itemId, row.status])), {
one: 'ready',
changed: 'changed',
failed: 'ready',
ondisk: 'pluginApplied',
gone: 'missing',
});
const restored = await compat.restoreBackups(
store,
['one', 'changed', 'failed'],
async (itemId) => items.get(itemId),
async (item) => {
if (item.id === 'failed') throw new Error('save failed');
},
);
assert.deepEqual(restored, { total: 3, success: 1, skipped: 1, failed: 1 });
assert.deepEqual(items.get('one'), { name: 'hero.atlas', ext: 'txt' });
assert.deepEqual(items.get('changed'), { name: 'user-renamed', ext: 'atlas.txt' });
assert.equal(store.list().length, 4);
} finally {
fs.rmSync(root, { recursive: true, force: true });
}
console.log('PASS: compound suffix changes require reviewable plans and preserve restorable originals');
})().catch((error) => {
console.error(error);
process.exitCode = 1;
});