// The room page's markup (`node --test web/room/markup.test.mjs`): every element it opens is // closed, in order, so no section nests inside another by accident; and each mode's section // holds its own controls and nothing of another's. A section left open nests everything after it, // so a section the page hides for one mode would hide the Log and the binding table with it. import test from "node:test"; import assert from "node:assert/strict"; import fs from "node:fs"; import { fileURLToPath } from "node:url"; const html = fs.readFileSync(fileURLToPath(new URL("./index.html", import.meta.url)), "utf8"); // HTML's void elements, which have no end tag. const VOID = new Set(["area", "base", "br", "col", "embed", "hr", "img", "input", "link", "meta", "source", "track", "wbr"]); // The elements of `src` by `id`, each with the ids of the elements it is inside, outermost first; // and every tag that does not balance. function parse(src) { const body = src.replace(//g, "").replace(/<(script|style)\b[^>]*>[\s\S]*?<\/\1\s*>/gi, "").replace(/]*>/i, ""); const stack = []; const ids = new Map(); const errors = []; const tag = /<(\/?)([a-zA-Z][a-zA-Z0-9-]*)\b([^>]*)>/g; let m; while ((m = tag.exec(body))) { const [, close, raw, attrs] = m; const name = raw.toLowerCase(); if (close) { const top = stack.pop(); if (!top || top.name !== name) errors.push(` closes <${top ? top.name : "nothing"}>`); continue; } if (VOID.has(name) || /\/\s*$/.test(attrs)) continue; const id = (attrs.match(/\bid="([^"]*)"/) || [])[1]; if (id) ids.set(id, stack.map((e) => e.id).filter(Boolean)); stack.push({ name, id }); } for (const e of stack) errors.push(`<${e.name}${e.id ? ` id="${e.id}"` : ""}> is never closed`); return { ids, errors }; } const { ids, errors } = parse(html); test("every element the room page opens is closed, in order", () => { assert.deepEqual(errors, []); }); test("the Log, the binding table and the co-watch section are outside the co-performance section", () => { for (const id of ["log", "binding-debug", "cowatch", "cowatch-media", "cw-sync"]) { assert.ok(ids.has(id), `#${id} is on the page`); assert.ok(!ids.get(id).includes("co"), `#${id} is inside ${ids.get(id).map((x) => `#${x}`).join(" > ")}`); } }); test("the remote desktop's controls are in its own section, outside the others", () => { for (const id of ["remote-canvas", "remote-request", "remote-release", "remote-state"]) { assert.ok(ids.get(id).includes("remote"), `#${id} is inside #remote`); assert.ok(!ids.get(id).includes("co") && !ids.get(id).includes("cowatch"), `#${id} is inside another mode's section`); } }); test("each mode's controls are inside its own section", () => { for (const id of ["co-lead", "co-input", "co-turns", "co-fidelity"]) assert.ok(ids.get(id).includes("co"), `#${id} is inside #co`); for (const id of ["cw-play", "cw-pause", "cw-rate", "cw-sync"]) assert.ok(ids.get(id).includes("cowatch") && !ids.get(id).includes("co"), `#${id} is inside #cowatch alone`); });