67 lines
3.2 KiB
JavaScript
67 lines
3.2 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import { readdirSync, readFileSync } from 'node:fs';
|
|
import { test } from 'node:test';
|
|
import { fileURLToPath } from 'node:url';
|
|
import { loadContext, PUBLIC_DIR } from './helpers.js';
|
|
import { README, withIconList } from '../scripts/icons.mjs';
|
|
import { allIconKeys, assetsSkeleton, iconGroups, THEME_IDS } from '../scripts/icon-keys.js';
|
|
|
|
const ctx = loadContext();
|
|
|
|
test('the icon list in the README is up to date (run: npm run icons -- --write)', () => {
|
|
const readme = readFileSync(README, 'utf8').replaceAll('\r\n', '\n');
|
|
assert.equal(readme, withIconList(readme, ctx));
|
|
});
|
|
|
|
test('the README lists every single icon key, once per theme row', () => {
|
|
const readme = readFileSync(README, 'utf8');
|
|
const keys = allIconKeys(ctx);
|
|
assert.equal(new Set(keys).size, keys.length, 'icon keys are unique');
|
|
for (const key of keys) assert.ok(readme.includes(`| \`${key}\` |`), key);
|
|
});
|
|
|
|
test('every theme names every themed icon (nothing falls back to a raw key)', () => {
|
|
const full = readFileSync(README, 'utf8');
|
|
const generated = full.slice(full.indexOf('<!-- icons:start'), full.indexOf('<!-- icons:end'));
|
|
assert.ok(generated.length > 5000, 'the generated list is there');
|
|
assert.ok(!/\| [a-z]+(\.[a-z_0-9]+)+ \|/.test(generated.replace(/`[^`]*`/g, '')), 'no unresolved text key in a table');
|
|
assert.ok(!generated.includes('{@'), 'embedded theme words are resolved');
|
|
assert.ok(!/\{target\}|\{value\}/.test(generated), 'placeholders are resolved');
|
|
assert.ok(THEME_IDS.length >= 5);
|
|
});
|
|
|
|
test('the interface only uses icon keys that are documented', () => {
|
|
const documented = new Set(iconGroups(ctx).map((group) => group.id));
|
|
const used = new Set();
|
|
const walk = (dir) => {
|
|
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
const path = `${dir}/${entry.name}`;
|
|
if (entry.isDirectory()) walk(path);
|
|
else if (entry.name.endsWith('.js')) {
|
|
// icon('gen.x') and icon(`inv.${id}`) calls, outside the helper's own definition
|
|
if (path.endsWith('ui/dom.js')) continue;
|
|
for (const m of readFileSync(path, 'utf8').matchAll(/\bicon\(\s*[`'"]([a-z]+)\./g)) used.add(m[1]);
|
|
}
|
|
}
|
|
};
|
|
walk(`${PUBLIC_DIR}js`);
|
|
for (const m of readFileSync(`${PUBLIC_DIR}index.html`, 'utf8').matchAll(/data-icon="([a-z]+)\./g)) used.add(m[1]);
|
|
|
|
assert.ok(used.size >= 7, 'the scan found the icons');
|
|
const undocumented = [...used].filter((prefix) => !documented.has(prefix));
|
|
assert.deepEqual(undocumented, [], `icons used but not listed in the README: ${undocumented.join(', ')}`);
|
|
});
|
|
|
|
test('the generated manifest covers every key with a real file name', () => {
|
|
const assets = assetsSkeleton(ctx);
|
|
assert.deepEqual(Object.keys(assets), allIconKeys(ctx));
|
|
assert.equal(assets['gen.gen1'], 'img/gen-gen1.svg');
|
|
assert.ok(Object.values(assets).every((file) => /^img\/[a-z0-9_-]+\.svg$/.test(file)));
|
|
});
|
|
|
|
test('the paths the game reads from theme.json are the same keys', () => {
|
|
// dom.js looks assets up by the exact key, so a skeleton entry must resolve as-is.
|
|
const domSource = readFileSync(fileURLToPath(new URL('../public/js/ui/dom.js', import.meta.url)), 'utf8');
|
|
assert.ok(domSource.includes('assets[key]'));
|
|
});
|