300 lines
15 KiB
JavaScript
300 lines
15 KiB
JavaScript
import { readFile } from "node:fs/promises";
|
|
import { test } from "node:test";
|
|
import assert from "node:assert/strict";
|
|
|
|
function valueAt(object, path) {
|
|
return path.split(".").reduce((value, key) => value?.[key], object);
|
|
}
|
|
|
|
function assertNonEmptyString(object, path) {
|
|
const value = valueAt(object, path);
|
|
assert.equal(typeof value, "string", `${path} must be a string`);
|
|
assert.ok(value.trim(), `${path} must not be empty`);
|
|
}
|
|
|
|
function assertStringArray(object, path, minLength = 1) {
|
|
const value = valueAt(object, path);
|
|
assert.ok(Array.isArray(value), `${path} must be an array`);
|
|
assert.ok(value.length >= minLength, `${path} must contain at least ${minLength} item(s)`);
|
|
value.forEach((item, index) => {
|
|
assert.equal(typeof item, "string", `${path}[${index}] must be a string`);
|
|
assert.ok(item.trim(), `${path}[${index}] must not be empty`);
|
|
});
|
|
}
|
|
|
|
function validateSiteContent(site) {
|
|
[
|
|
"brand.name",
|
|
"brand.homeAriaLabel",
|
|
"navigation.home",
|
|
"navigation.toolboxes",
|
|
"navigation.games",
|
|
"navigation.mobileGames",
|
|
"sidebar.badge",
|
|
"sidebar.note",
|
|
"topbar.dashboard",
|
|
"topbar.toolbox",
|
|
"topbar.games",
|
|
"gamesPage.eyebrow",
|
|
"gamesPage.title",
|
|
"gamesPage.description",
|
|
"gamesPage.emptyTitle",
|
|
"gamesPage.emptyText",
|
|
"home.hero.eyebrow",
|
|
"home.hero.title",
|
|
"home.hero.description",
|
|
"home.hero.primaryAction",
|
|
"home.hero.secondaryAction",
|
|
"home.stats.toolboxSingular",
|
|
"home.stats.toolboxPlural",
|
|
"home.stats.toolSingular",
|
|
"home.stats.toolPlural",
|
|
"home.origin.eyebrow",
|
|
"home.origin.title",
|
|
"home.origin.visualAlt",
|
|
"home.origin.dialogueAriaLabel",
|
|
"home.origin.caption",
|
|
"home.legal.copyright",
|
|
"home.legal.disclaimer",
|
|
"toolboxes.eyebrow",
|
|
"toolboxes.title",
|
|
"toolboxes.newButton",
|
|
"toolboxes.importAll",
|
|
"toolboxes.exportAll",
|
|
"toolboxes.importOne",
|
|
"toolboxes.summary",
|
|
"toolboxes.storageHelp.title",
|
|
"toolboxes.storageHelp.text",
|
|
"toolboxes.emptyTitle",
|
|
"toolboxes.emptyText"
|
|
].forEach((path) => assertNonEmptyString(site, path));
|
|
|
|
assertStringArray(site, "toolboxes.storageHelp.items", 3);
|
|
|
|
const lines = valueAt(site, "home.origin.lines");
|
|
assert.ok(Array.isArray(lines), "home.origin.lines must be an array");
|
|
assert.ok(lines.length >= 2, "home.origin.lines must contain at least 2 items");
|
|
lines.forEach((line, index) => {
|
|
assert.ok(["user", "app"].includes(line?.speaker), `home.origin.lines[${index}].speaker must be "user" or "app"`);
|
|
assert.equal(typeof line?.text, "string", `home.origin.lines[${index}].text must be a string`);
|
|
assert.ok(line.text.trim(), `home.origin.lines[${index}].text must not be empty`);
|
|
});
|
|
}
|
|
|
|
test("vite entrypoint loads the react application", async () => {
|
|
const html = await readFile("website/index.html", "utf8");
|
|
|
|
assert.match(html, /<div id="app"><\/div>/);
|
|
assert.match(html, /\/favicon\.ico/);
|
|
assert.match(html, /\/src\/main\.jsx/);
|
|
});
|
|
|
|
test("react application defines the expected local toolbox primitives", async () => {
|
|
const source = await readFile("website/src/main.jsx", "utf8");
|
|
const styles = await readFile("website/src/styles/main.scss", "utf8");
|
|
const styleNebula = await readFile("website/src/styles/_nebula.scss", "utf8");
|
|
const styleTokens = await readFile("website/src/styles/_tokens.scss", "utf8");
|
|
const styleBase = await readFile("website/src/styles/_base.scss", "utf8");
|
|
const styleHome = await readFile("website/src/styles/_home.scss", "utf8");
|
|
const styleToolboxes = await readFile("website/src/styles/_toolboxes.scss", "utf8");
|
|
const styleIcons = await readFile("website/src/styles/_icons.scss", "utf8");
|
|
const styleMhwilds = await readFile("website/src/styles/_mhwilds.scss", "utf8");
|
|
const styleResponsive = await readFile("website/src/styles/_responsive.scss", "utf8");
|
|
const iconComponent = await readFile("website/src/components/Icon.jsx", "utf8");
|
|
const gamesPage = await readFile("website/src/features/games/GamesPage.jsx", "utf8");
|
|
const gameRoute = await readFile("website/src/features/games/GameRoute.jsx", "utf8");
|
|
const mhwildsPage = await readFile("website/src/features/games/mhwilds/MhwildsPage.jsx", "utf8");
|
|
const mhwildsListing = await readFile("website/src/features/games/mhwilds/MhwildsListing.jsx", "utf8");
|
|
const mhwildsFilters = await readFile("website/src/features/games/mhwilds/MhwildsFilters.jsx", "utf8");
|
|
const monsterCard = await readFile("website/src/features/games/mhwilds/cards/MonsterCard.jsx", "utf8");
|
|
const endemicCard = await readFile("website/src/features/games/mhwilds/cards/EndemicCard.jsx", "utf8");
|
|
const damageTable = await readFile("website/src/features/games/mhwilds/cards/DamageTable.jsx", "utf8");
|
|
const moduleRegistry = await readFile("website/src/features/toolboxes/modules/index.jsx", "utf8");
|
|
const notepadModule = await readFile("website/src/features/toolboxes/modules/NotepadModule.jsx", "utf8");
|
|
const checklistModule = await readFile("website/src/features/toolboxes/modules/ChecklistModule.jsx", "utf8");
|
|
const screenshotsModule = await readFile("website/src/features/toolboxes/modules/ScreenshotsModule.jsx", "utf8");
|
|
const linksModule = await readFile("website/src/features/toolboxes/modules/LinksModule.jsx", "utf8");
|
|
const countersModule = await readFile("website/src/features/toolboxes/modules/CountersModule.jsx", "utf8");
|
|
|
|
assert.match(source, /sokkog:toolboxes/);
|
|
assert.match(source, /sokkog:game-toolbox-links/);
|
|
assert.match(source, /styles\/main\.scss/);
|
|
assert.match(styles, /@use "tokens"/);
|
|
assert.match(styles, /@use "nebula"/);
|
|
assert.match(styles, /@use "toolboxes"/);
|
|
assert.match(styles, /@use "mhwilds"/);
|
|
assert.match(styleTokens, /--gradient-brand/);
|
|
assert.match(styleBase, /\.card-icon-button,\n\.drawer-action-button/);
|
|
assert.match(styleNebula, /@function star-field/);
|
|
assert.match(styleNebula, /body::after/);
|
|
assert.match(styleBase, /\.import-icon-button/);
|
|
assert.match(styleHome, /\.dialogue-line i/);
|
|
assert.match(styleHome, /font-style: italic/);
|
|
assert.match(styleToolboxes, /\.module-icon-abacus/);
|
|
assert.match(styleToolboxes, /\.toolbox-icon-picker/);
|
|
assert.match(styleToolboxes, /\.toolbox-game-icon/);
|
|
assert.match(styleToolboxes, /\.toolbox-actions-row/);
|
|
assert.match(styleIcons, /ui-icon-drag/);
|
|
assert.match(styleIcons, /ui-icon-dropdown/);
|
|
assert.match(styleIcons, /ui-icon-flip/);
|
|
assert.match(styleIcons, /ui-icon-add/);
|
|
assert.match(styleMhwilds, /\.mhwilds-card/);
|
|
assert.match(styleMhwilds, /\.mhwilds-flip-indicator/);
|
|
assert.match(styleResponsive, /@media \(max-width: 760px\)/);
|
|
assert.match(source, /features\/toolboxes\/modules\/index\.jsx/);
|
|
assert.match(source, /features\/games\/GamesPage\.jsx/);
|
|
assert.match(source, /features\/games\/GameRoute\.jsx/);
|
|
assert.match(iconComponent, /export function Icon/);
|
|
assert.match(gamesPage, /export function GamesPage/);
|
|
assert.match(gamesPage, /siteContent\.gamesPage/);
|
|
assert.match(gamesPage, /game-card-cover-link/);
|
|
assert.match(gameRoute, /export function GameRoute/);
|
|
assert.match(gameRoute, /MhwildsPage/);
|
|
assert.match(mhwildsPage, /export function MhwildsPage/);
|
|
assert.match(mhwildsListing, /export function MhwildsListing/);
|
|
assert.match(mhwildsFilters, /export function MhwildsFilters/);
|
|
assert.match(monsterCard, /export function MonsterCard/);
|
|
assert.match(monsterCard, /ui-icon-flip/);
|
|
assert.match(endemicCard, /export function EndemicCard/);
|
|
assert.match(damageTable, /rowIndex/);
|
|
assert.match(moduleRegistry, /export const TOOLBOX_MODULES/);
|
|
assert.match(moduleRegistry, /export function AddToolControls/);
|
|
assert.match(moduleRegistry, /export function ToolboxModules/);
|
|
assert.match(moduleRegistry, /MODULE_COMPONENTS/);
|
|
assert.match(moduleRegistry, /notepad:/);
|
|
assert.match(moduleRegistry, /checklist:/);
|
|
assert.match(moduleRegistry, /screenshots:/);
|
|
assert.match(moduleRegistry, /links:/);
|
|
assert.match(moduleRegistry, /counters:/);
|
|
assert.match(moduleRegistry, /editable: false/);
|
|
assert.match(moduleRegistry, /module-edit-button/);
|
|
assert.match(notepadModule, /export function NotepadModule/);
|
|
assert.match(checklistModule, /export function ChecklistModule/);
|
|
assert.match(screenshotsModule, /export function ScreenshotsModule/);
|
|
assert.match(linksModule, /export function LinksModule/);
|
|
assert.match(countersModule, /export function CountersModule/);
|
|
assert.match(checklistModule, /editing &&/);
|
|
assert.match(screenshotsModule, /editing &&/);
|
|
assert.match(linksModule, /editing &&/);
|
|
assert.match(countersModule, /editing &&/);
|
|
assert.match(countersModule, /counter-actions/);
|
|
assert.match(linksModule, /normalizeUrl/);
|
|
assert.match(linksModule, /copyText/);
|
|
assert.match(source, /DEFAULT_SITE_CONTENT/);
|
|
assert.match(source, /\/data\/site\.json/);
|
|
assert.match(source, /APP_STORAGE_LIMIT_BYTES/);
|
|
assert.match(source, /DEFAULT_TOOLBOX_ICON/);
|
|
assert.match(source, /TOOLBOX_ICON_FILES/);
|
|
assert.match(source, /function normalizeToolboxIcon/);
|
|
assert.match(source, /function ToolboxIconPicker/);
|
|
assert.match(source, /function ToolboxGameIcon/);
|
|
assert.match(source, /function StorageQuota/);
|
|
assert.match(source, /function writeStorageValue/);
|
|
assert.match(source, /role="progressbar"/);
|
|
assert.match(source, /function createGlobalExportPayload/);
|
|
assert.match(source, /async function importAllToolboxes/);
|
|
assert.match(source, /import-icon-button/);
|
|
assert.match(source, /function LinkToolboxModal/);
|
|
assert.doesNotMatch(source, /description: _description/);
|
|
assert.match(source, /function CreateToolboxModal/);
|
|
assert.match(source, /const getToolboxGameId/);
|
|
assert.match(source, /dragon\.png/);
|
|
assert.match(source, /value\.split\("\\n"\)/);
|
|
assert.match(source, /<br key=/);
|
|
assert.match(source, /getToolboxGame\(toolbox\)/);
|
|
assert.match(source, /toolbox-card-cover-link/);
|
|
assert.match(source, /toolbox-icon-cover/);
|
|
assert.match(source, /moduleColumns/);
|
|
assert.match(source, /function ToolboxView/);
|
|
assert.match(moduleRegistry, /onDragStart/);
|
|
assert.match(moduleRegistry, /onPointerDown/);
|
|
assert.match(moduleRegistry, /setPointerCapture/);
|
|
assert.match(moduleRegistry, /elementFromPoint/);
|
|
assert.match(moduleRegistry, /tool-add-card/);
|
|
assert.match(moduleRegistry, /tool-quick-add-button/);
|
|
assert.match(moduleRegistry, /tool-add-quick-toggle/);
|
|
assert.match(moduleRegistry, /Icon name="dropdown"/);
|
|
assert.match(moduleRegistry, /aria-expanded=\{quickOpen\}/);
|
|
assert.doesNotMatch(source, /<select value="" onChange=\{\(event\) => addModule/);
|
|
assert.match(source, /qtyTarget/);
|
|
assert.match(source, /qtyCurrent/);
|
|
assert.match(source, /normalizeLinksData/);
|
|
assert.match(source, /normalizeCountersData/);
|
|
assert.match(checklistModule, /ChecklistItem/);
|
|
assert.match(screenshotsModule, /clipboardData/);
|
|
assert.match(source, /async function addScreenshotFiles/);
|
|
assert.match(source, /function ScreenshotViewer/);
|
|
assert.match(source, /function openImageInNewTab/);
|
|
assert.match(source, /<Icon name="zoom"/);
|
|
assert.match(screenshotsModule, /dataTransfer\.files/);
|
|
});
|
|
|
|
test("site content json is complete", async () => {
|
|
const site = JSON.parse(await readFile("website/public/data/site.json", "utf8"));
|
|
validateSiteContent(site);
|
|
});
|
|
|
|
test("mhwilds data and assets are available", async () => {
|
|
const site = JSON.parse(await readFile("website/public/data/site.json", "utf8"));
|
|
const games = JSON.parse(await readFile("website/public/data/games.json", "utf8"));
|
|
const monsters = JSON.parse(await readFile("website/public/data/mhwilds/monsters.json", "utf8"));
|
|
const endemicLife = JSON.parse(await readFile("website/public/data/mhwilds/endemic_life.json", "utf8"));
|
|
const translations = JSON.parse(await readFile("website/public/data/mhwilds/i18n/fr.json", "utf8"));
|
|
const listingSource = await readFile("website/src/features/games/mhwilds/MhwildsListing.jsx", "utf8");
|
|
|
|
assert.equal(site.brand.name, "Sokko G");
|
|
assert.equal(site.home.hero.title, "Sokko G");
|
|
assert.equal(site.gamesPage.title, "Jeux disponibles");
|
|
assert.match(site.toolboxes.summary, /espaces modulaires/);
|
|
assert.ok(site.toolboxes.storageHelp.items.length >= 3);
|
|
assert.equal(games.games[0].id, "mhwilds");
|
|
assert.equal(games.games[0].eyebrow, "Guide de jeu");
|
|
assert.match(games.games[0].images.cardCover, /games\/mhwilds\/card-cover\.png/);
|
|
assert.match(games.games[0].images.heroBg, /games\/mhwilds\/hero-bg\.png/);
|
|
assert.ok(monsters.monsters.length > 0);
|
|
assert.ok(endemicLife.endemicLife.length > 0);
|
|
assert.equal(translations.monsters, "monstres");
|
|
assert.match(listingSource, /function MhwildsListing/);
|
|
const monsterCardSource = await readFile("website/src/features/games/mhwilds/cards/MonsterCard.jsx", "utf8");
|
|
assert.match(monsterCardSource, /monster-card/);
|
|
await readFile("website/public/static/img/mhwilds/chatacabra.png");
|
|
await readFile("website/public/static/img/games/mhwilds/card-cover.png");
|
|
await readFile("website/public/static/img/games/mhwilds/hero-bg.png");
|
|
});
|
|
|
|
test("server and vite support local env configuration", async () => {
|
|
const server = await readFile("server.mjs", "utf8");
|
|
const viteConfig = await readFile("vite.config.js", "utf8");
|
|
const envExample = await readFile(".env.example", "utf8");
|
|
|
|
assert.match(server, /function loadEnvFile/);
|
|
assert.match(server, /process\.env\.PORT/);
|
|
assert.match(viteConfig, /function loadEnvFile/);
|
|
assert.match(viteConfig, /process\.env\.PORT/);
|
|
assert.match(envExample, /PORT=5173/);
|
|
});
|
|
|
|
test("legacy svg icons are available for reuse", async () => {
|
|
const manifest = JSON.parse(await readFile("website/public/data/toolbox-icons.json", "utf8"));
|
|
|
|
assert.ok(manifest.icons.length >= 4);
|
|
await readFile("website/public/static/icons/home.svg", "utf8");
|
|
await readFile("website/public/static/icons/add.svg", "utf8");
|
|
await readFile("website/public/static/icons/checklist.svg", "utf8");
|
|
await readFile("website/public/static/icons/close.svg", "utf8");
|
|
await readFile("website/public/static/icons/columns.svg", "utf8");
|
|
await readFile("website/public/static/icons/copy.svg", "utf8");
|
|
await readFile("website/public/static/icons/abacus.svg", "utf8");
|
|
await readFile("website/public/static/icons/export.svg", "utf8");
|
|
await readFile("website/public/static/icons/notepad.svg", "utf8");
|
|
await readFile("website/public/static/icons/picture.svg", "utf8");
|
|
await readFile("website/public/static/icons/rows.svg", "utf8");
|
|
await readFile("website/public/static/icons/rubber.svg", "utf8");
|
|
await readFile("website/public/static/icons/trashcan.svg", "utf8");
|
|
await readFile("website/public/static/icons/zoom.svg", "utf8");
|
|
await readFile("website/public/favicon.ico");
|
|
await readFile("website/public/static/img/dragon.png");
|
|
await readFile("website/public/static/img/toolbox-icons/toolbox.png");
|
|
await readFile("website/public/static/img/toolbox-icons/sword.png");
|
|
await readFile("website/public/static/img/toolbox-icons/puzzle.png");
|
|
});
|