mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2026-10-06 21:46:44 +00:00
test: align Audit comparison expectations and backup i18n fixtures
This commit is contained in:
@@ -2,38 +2,8 @@
|
||||
// No component imports, React effects, API calls or host-management execution.
|
||||
// Run from the repository root: node tests/test_backup_archives_empty.cjs
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const { createRequire } = require('node:module');
|
||||
const app = path.resolve(__dirname, '../AppImage');
|
||||
const ts = createRequire(path.join(app, 'package.json'))('typescript');
|
||||
const read = file => fs.readFileSync(path.join(app, file), 'utf8');
|
||||
const { ts, read, compile, printer, catalogs, translator } = require('./backup_i18n_seam.cjs');
|
||||
const parse = file => ts.createSourceFile(file, read(file), ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX);
|
||||
const compile = text => ts.transpileModule(text, {
|
||||
compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020, jsx: ts.JsxEmit.React },
|
||||
}).outputText;
|
||||
const printer = ts.createPrinter();
|
||||
const provider = parse('lib/i18n/provider.tsx');
|
||||
const pure = provider.statements.filter(node => ts.isFunctionDeclaration(node) &&
|
||||
['getMessage', 'interpolate'].includes(node.name?.text));
|
||||
assert.equal(pure.length, 2);
|
||||
let lookup;
|
||||
function visitLookup(node) {
|
||||
if (ts.isVariableDeclaration(node) && node.name.getText(provider) === 't' &&
|
||||
node.initializer && ts.isCallExpression(node.initializer) &&
|
||||
node.initializer.expression.getText(provider) === 'useCallback') lookup = node.initializer.arguments[0];
|
||||
ts.forEachChild(node, visitLookup);
|
||||
}
|
||||
visitLookup(provider);
|
||||
assert.ok(lookup, 'real provider lookup callback');
|
||||
const lookupCode = pure.map(node => printer.printNode(ts.EmitHint.Unspecified, node, provider)).join('\n') +
|
||||
'\nconst t = ' + printer.printNode(ts.EmitHint.Expression, lookup, provider) + ';';
|
||||
const catalogs = Object.fromEntries(fs.readdirSync(path.join(app, 'messages')).filter(locale =>
|
||||
fs.existsSync(path.join(app, 'messages', locale, 'common.json'))).map(locale =>
|
||||
[locale, JSON.parse(read(`messages/${locale}/common.json`))]));
|
||||
// Compile only trusted, checked-out source; no catalog/input strings enter code.
|
||||
const translator = (language, messages = catalogs) =>
|
||||
new Function('MESSAGE_CATALOG', 'language', compile(lookupCode) + '\nreturn t;')(messages, language);
|
||||
|
||||
const source = parse('components/host-backup.tsx');
|
||||
let branch;
|
||||
@@ -68,16 +38,22 @@ const text = (locale = 'en', overrides = {}, messages = catalogs) => {
|
||||
state.archivesResp, state.remoteArchivesResp, state.unifiedArchives);
|
||||
};
|
||||
|
||||
assert.equal(text(), 'No backup archives found.');
|
||||
assert.equal(text('it'), 'No backup archives found.', 'Italian catalog uses English fallback');
|
||||
// Keep fallback coverage even after all shipped locales gain the new key.
|
||||
assert.equal(text('missing', {}, { ...catalogs, missing: {} }), 'No backup archives found.');
|
||||
for (const locale of Object.keys(catalogs)) {
|
||||
// Existing catalogs intentionally have no new key; actual provider falls back.
|
||||
if (catalogs[locale].backup?.archives?.emptyMessage === undefined) {
|
||||
assert.equal(text(locale), 'No backup archives found.', `${locale}: missing-key fallback`);
|
||||
}
|
||||
const emptyMessage = 'No backup archives found.';
|
||||
assert.equal(catalogs.en.backup.archives.emptyMessage, emptyMessage);
|
||||
assert.equal(text(), emptyMessage);
|
||||
// Missing-key fallback is unconditional: it must survive later catalog generation.
|
||||
assert.equal(text('missing', {}, { ...catalogs, missing: {} }), emptyMessage);
|
||||
const synthetic = { ...catalogs, fixture: { backup: { archives: { emptyMessage: 'Fixture: no archives.' } } } };
|
||||
assert.equal(text('fixture', {}, synthetic), 'Fixture: no archives.');
|
||||
const locales = Object.keys(catalogs);
|
||||
assert.ok(locales.length > 1, 'shipped multilingual catalogs discovered');
|
||||
for (const locale of locales) {
|
||||
const shipped = catalogs[locale].backup?.archives?.emptyMessage;
|
||||
assert.equal(text(locale), shipped ?? emptyMessage, `${locale}: actual shipped locale or English fallback`);
|
||||
}
|
||||
assert.ok(locales.some(locale => locale !== 'en' &&
|
||||
typeof catalogs[locale].backup?.archives?.emptyMessage === 'string' &&
|
||||
catalogs[locale].backup.archives.emptyMessage !== emptyMessage), 'localized shipped archive message exercised');
|
||||
for (const count of [1, 3]) assert.equal(text('en', { unifiedArchives: Array(count).fill({}) }), 'NONEMPTY_ARCHIVES');
|
||||
assert.equal(text('en', { archivesResp: undefined, remoteArchivesResp: undefined }), translator('en')('backup.common.loading'));
|
||||
assert.equal(text('en', { archivesErr: true, remoteArchivesErr: true }), translator('en')('backup.archives.loadFailed'));
|
||||
@@ -86,6 +62,4 @@ assert.equal(text('en', { archivesErr: true, archivesResp: undefined }), 'No bac
|
||||
assert.equal(text('en', { remoteArchivesErr: true, remoteArchivesResp: undefined }), 'No backup archives found.');
|
||||
assert.equal(translator('en')('backup.manual.run'), 'Run');
|
||||
assert.ok(read('components/host-backup.tsx').includes('{t("backup.manual.run")}'), 'manual action remains');
|
||||
console.log('PASS: actual archives JSX seam, zero/one/multiple, loading/errors, locale fallback, Run unchanged');
|
||||
// Export only test seams for separate, unapplied linguistic proposal checks.
|
||||
module.exports = { text, translator, catalogs };
|
||||
console.log('PASS: actual archives JSX seam, zero/one/multiple, loading/errors, shipped locales and synthetic fallback/translation, Run unchanged');
|
||||
|
||||
Reference in New Issue
Block a user