193 lines
6.6 KiB
JavaScript
193 lines
6.6 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, /class="toolbar-actions"[\s\S]*id="clear"[\s\S]*id="restoreNames"/);
|
|
assert.match(pageSource, /id="restoreDialog"/);
|
|
assert.doesNotMatch(pageSource, /<h1\b/);
|
|
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;
|
|
});
|