Files
SpineViewer/tests/viewer-i18n.test.js
T
tianmo bb73cc9227 1.1.1版本审核修复
[spine-utils.js (line 35)](/Users/tianmokeji/Desktop/特效/SpineViewer/SpineViewer/js/spine-utils.js:35) 统一拦截绝对路径、..、Windows 盘符、UNC、URI、控制字符和符号链接越界。
跨素材贴图只能使用已枚举、验证且位于 Eagle 素材目录内的文件。
[spine-render.js (line 62)](/Users/tianmokeji/Desktop/特效/SpineViewer/SpineViewer/js/spine-render.js:62) 已删除未经验证的路径备用拼接。
自动缩略图只写当前回调提供的 dest,不再调用接口改写同组素材。
已删除后台整组补刷进程及 relatedDataPaths。
[viewer/spine.html (line 482)](/Users/tianmokeji/Desktop/特效/SpineViewer/SpineViewer/viewer/spine.html:482) 的手动缩略图只针对当前素材,并核对素材 ID、文件名和扩展名。
当前素材已有自定义缩略图时,会提示替换后无法自动恢复并要求确认。
手动生成的中间文件改存系统临时目录,API 失败时不再直接写 Eagle 素材目录。
八种语言文案及 README 已同步。
处理历史日志遗留问题
2026-09-17 13:38:22 +08:00

44 lines
2.5 KiB
JavaScript

'use strict';
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const vm = require('node:vm');
const { createRequire } = require('node:module');
const roots = ['.', 'release/UNIVERSAL_SpineViewer'];
for (const root of roots) {
const html = fs.readFileSync(path.join(root, 'viewer/spine.html'), 'utf8');
const script = html.match(/<script>([\s\S]*?)<\/script>/)[1];
new vm.Script(script);
const i18n = script.slice(script.indexOf('\tconst I18N_LANGUAGES'), script.indexOf('\tapplyStaticI18n();'));
const markers = script.slice(script.indexOf('\tfunction renderKeyframeMarkers('), script.indexOf('\tfunction seekTo('));
const manifest = JSON.parse(fs.readFileSync(path.join(root, 'manifest.json'), 'utf8'));
for (const lang of manifest.languages) {
const table = JSON.parse(fs.readFileSync(path.join(root, '_locales', lang + '.json'), 'utf8'));
for (const match of html.matchAll(/(?:\bt\(['"]|data-i18n(?:-title|-aria-label)?=")([^'"]+)/g)) {
assert.equal(typeof match[1].split('.').reduce((obj, key) => obj && obj[key], table), 'string', `${root}/${lang}: ${match[1]}`);
}
const children = [];
const context = vm.createContext({
require: createRequire(path.resolve(root, 'viewer/spine.html')), path, fs,
// Simulate Eagle supplying a different directory than the HTML page.
__dirname: path.resolve(root, 'viewer/runtime'), lang, log() {},
document: { documentElement: {}, createElement: () => ({ style: {}, setAttribute(key, value) { this[key] = value; } }) },
keyframesEl: { innerHTML: '', appendChild: el => children.push(el) },
isStaticAnimation: false, duration: 2,
collectKeyframeTimes: () => [0, 1, 2, 3],
formatSeconds: value => `${value}s`, frameOf: value => value * 30
});
vm.runInContext(i18n + '\n' + markers + '\nrenderKeyframeMarkers({});', context);
assert.equal(vm.runInContext("t('viewer.skin')", context), table.viewer.skin);
assert.equal(children.length, 3);
assert.equal(children[1].style.left, '50%');
assert.equal(children[1].title, table.viewer.keyframeTitle.replace('{time}', '1s').replace('{frame}', '30'));
assert.equal(children[1]['aria-label'], children[1].title);
children.length = 0;
context.isStaticAnimation = true;
vm.runInContext('renderKeyframeMarkers({});', context);
assert.equal(children[0].title, table.viewer.staticPoseTitle);
}
}
console.log('PASS: source and universal release; eight languages; animated/static keyframe labels and translation keys');