// Guards the content packs: every theme and language must be complete, so a // missing translation is caught by `npm test` instead of showing a raw key. import assert from 'node:assert/strict'; import { readdirSync } from 'node:fs'; import { test } from 'node:test'; import { flatKeys, loadContext, PUBLIC_DIR, readJson } from './helpers.js'; const ctx = loadContext(); const codes = (dir) => readdirSync(PUBLIC_DIR + dir) .filter((f) => f.endsWith('.json')) .map((f) => f.slice(0, -5)); const themes = readdirSync(PUBLIC_DIR + 'themes', { withFileTypes: true }) .filter((e) => e.isDirectory()) .map((e) => e.name); test('core locales define the same keys as English', () => { const reference = flatKeys(readJson('locales/en.json')).sort(); for (const code of codes('locales')) { assert.deepEqual(flatKeys(readJson(`locales/${code}.json`)).sort(), reference, `locales/${code}.json`); } }); test('every effect type has a core text', () => { const keys = new Set(flatKeys(readJson('locales/en.json'))); for (const u of ctx.upgrades) assert.ok(keys.has(`effect.${u.effect.type}`), u.effect.type); }); test('there is at least one theme, each with a manifest, a stylesheet and English texts', () => { assert.ok(themes.length >= 1); for (const id of themes) { assert.ok(readdirSync(`${PUBLIC_DIR}themes/${id}`).includes('theme.css'), `${id}/theme.css`); assert.ok(codes(`themes/${id}/locales`).includes('en'), `${id} needs an en locale`); assert.ok(readJson(`themes/${id}/theme.json`)); } }); test('every theme names every resource, generator and upgrade, in every language', () => { const required = [ 'theme.name', 'theme.title', 'theme.res.units', 'theme.res.unit', 'theme.res.funds', 'theme.res.material', 'theme.fmt.funds', 'theme.action.make', 'theme.action.buyMaterial', ...ctx.generators.flatMap((g) => [`theme.gen.${g.id}.name`, `theme.gen.${g.id}.desc`]), ...ctx.upgrades.map((u) => `theme.upg.${u.id}`), ]; for (const id of themes) { const reference = flatKeys(readJson(`themes/${id}/locales/en.json`)).sort(); assert.deepEqual(reference.filter((k) => required.includes(k)).sort(), [...required].sort(), `${id}/en`); for (const code of codes(`themes/${id}/locales`)) { const keys = flatKeys(readJson(`themes/${id}/locales/${code}.json`)).sort(); assert.deepEqual(keys, reference, `${id}/${code} differs from ${id}/en`); } } }); test('every theme is translated in every core language', () => { for (const id of themes) { for (const code of codes('locales')) { assert.ok(codes(`themes/${id}/locales`).includes(code), `${id} has no "${code}" texts`); } } });