Something went wrong. Try again.
Web frontend and supporting services for lance.blue
Something went wrong. Try again.
5.7 kB · 140 lines
JavaScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141/** * The camo picker, when there is nothing to pick. * * A player with no camo gets a sentence saying so and a tile that goes to the * editor. Both halves are easy to lose by accident: the empty branch is three * lines long and returns early, and the editor's address is a hash that any * refactor could write down again by hand and then forget when the route moves. * * `camo-picker.ts` is shared between challenge.ts and lobby.ts (its own * module doc says why); this reads the shared module rather than either * screen, since the picker itself is what these checks are about, not * either place it is mounted. * * Structural, like the editor's own checks. There is no DOM under `npm test`, * and nothing here fails at runtime — a picker that offers no way out still * renders, and a link to a hash that means nothing lands the player on Home. * * Run with `npm test`. */import { test } from "node:test";import assert from "node:assert/strict";import { readFile } from "node:fs/promises";import { join } from "node:path";import { fileURLToPath } from "node:url";
import { DESTINATIONS } from "../src/destinations.ts";
const web = fileURLToPath(new URL("../", import.meta.url));const source = await readFile(join(web, "src/screens/camo-picker.ts"), "utf8");
test("an empty collection is offered the editor", () => { const branch = source.match(/if \(!camos\.length\) \{([\s\S]*?)\n {6}\}/); assert.ok(branch, "the empty-collection branch moved or was renamed"); assert.match( branch[1], /makeOne\(\)/, "a player with no camo is told so and left there; nothing on the screen " + "says where a camo is made", );});
test("the editor link is the masthead's camo destination", () => { const camo = DESTINATIONS.find((d) => d.id === "camo"); assert.ok(camo, "there is no camo destination for the picker to link to"); assert.match( source, /DESTINATIONS\.find\(\(d\) => d\.id === "camo"\)/, "the picker should read the editor's address off DESTINATIONS", ); assert.doesNotMatch( source, /["'`][^"'`]*#\/?camo/, `the editor's address is written down here as well as in destinations.ts ` + `(${camo.href}); one of the two will be wrong the day the route moves`, );});
test("the roll's die is drawn rather than typed", async () => { // U+2684 is not in the site's typeface and nothing in the fallback chain // carries it, so the die came out as the browser's missing-character box in // the corner of the Random tile - which reads as a picture that failed. assert.doesNotMatch( source, /\u2684/, "the die is a character again, and no font the site loads has it", ); assert.match(source, /function dieMark\(\): SVGSVGElement/); const css = await readFile(join(web, "src/styles.css"), "utf8"); assert.match( css, /\.camo-die-mark,\s*\.camo-make-mark svg \{\s*width: 1em;/, "the drawn die has no size, so it lands at the SVG default and is a box", ); // The tile's picture before there is one to show: a `display` in the // stylesheet beats the `hidden` attribute, and a srcless <img> is drawn as // the broken-picture mark. assert.match(css, /\.camo-choice img\[hidden\] \{\s*display: none;/);});
test("a tile is one size whatever its camo is called", async () => { // The tiles used to be as wide as their names, so "pink" and "i just liked // this one" were different shapes and no two rows lined up. const css = await readFile(join(web, "src/styles.css"), "utf8"); assert.match( css, /grid-template-columns: repeat\(auto-fill, var\(--camo-tile\)\);/, "the strip is not a grid of one tile width any more", ); assert.match( css, /\.camo-choice \{[\s\S]*?width: var\(--camo-tile\);/, "a tile sizes itself again, so a long name widens it", ); assert.match( css, /\.choice\.camo-pick \{[\s\S]*?align-items: stretch;/, "`.choice` sets flex-start, so without this the grid is one column wide", ); // The whole name is still readable, since the tile can only show some of it. assert.match(source, /className: "camo-choice",\s*title: label,/);});
test("a long collection is paged rather than poured into the strip", () => { assert.match(source, /const PAGE_SIZE = \d+;/); assert.match( source, /strip\.replaceChildren\(\.\.\.fixed, \.\.\.\(pages\[page\] \?\? \[\]\)\)/, "the strip no longer draws one page at a time", ); // None, the roll and the editor link are not paged: they are the choices // that need no collection behind them. assert.match(source, /fixed\.push\(plain\)/); assert.match(source, /fixed\.push\(wrap\)/); assert.match(source, /fixed\.push\(link\)/); // Opened on the page the selection is on, or a default on page three would // be worn by a strip with no active tile in it. assert.match(source, /showPage\(Math\.floor\(start \/ PAGE_SIZE\)\)/);});
test("every screen that offers camo opens the same swatch and dialog", async () => { // Three screens choose camo - the hangar's force, the challenge's seat and // the lobby's - and a strip on one of them and a dialog on the others is // three answers to one question. The swatch is the answer. for (const screen of ["hangar", "challenge", "lobby"]) { const source = await readFile( join(web, `src/screens/${screen}.ts`), "utf8", ); assert.match( source, /camoModal\([\s\S]{0,40}"swatch"\)/, `${screen}.ts does not open the picker as a swatch`, ); assert.doesNotMatch( source, /\bcamo\.node\b/, `${screen}.ts mounts the strip itself rather than opening the dialog`, ); }});