Something went wrong. Try again.
Web frontend and supporting services for lance.blue
Something went wrong. Try again.
8.1 kB · 194 lines
JavaScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195/** * The second stamp says two words, in two places, and one of those places * cannot import the file that defines it. * * soon-stamp.ts builds the ones on the front page's facts. Base.astro's bar * writes the same span by hand, because the blog's pages never run the app. * Nothing errors when they drift: the page still renders, with two different * promises on it, or with a stamp that has lost its styling and is a bare * "Coming soon" sitting in the middle of the masthead. * * The other half of this is that the mark and the words have to stay together * with what they are marking. A fact that stops being a promise should lose * its stamp, and a destination that gets built should lose its own — but a * stamp that goes missing while the thing it marked is still unbuilt looks * exactly like a feature that shipped. * * 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";
const web = join(new URL(".", import.meta.url).pathname, "..");const read = (path) => readFile(join(web, path), "utf8");
const stamp = await read("src/soon-stamp.ts");const layout = await read("src/layouts/Base.astro");const home = await read("src/screens/home.ts");const matches = await read("src/screens/matches.ts");const pilot = await read("src/screens/pilot.ts");const css = await read("src/styles.css");
const words = stamp.match(/SOON = "([^"]+)"/)?.[1];assert.ok(words, "soon-stamp.ts no longer declares SOON as a literal");
test("the bar's hand-written stamp says what soon-stamp.ts says", () => { // Loose about the whitespace around the words: the span is wide enough that // the formatter breaks it across lines, and where it chooses to break is not // what this is about. assert.match( layout, new RegExp(`class="stamp stamp-soon stamp-\\w+"[^>]*>\\s*${words}\\s*<`), `Base.astro does not stamp ${words}`, );});
test("every place that stamps has a rule to draw it with", () => { // soon-stamp.ts builds its class from the variant, so the names are in the // union rather than in any string a search would find; Base.astro's is // written out. A variant with no rule is a bare word sitting on a card. const variants = new Set([ ...[...stamp.matchAll(/"(fact|mode|tile|seat)"/g)].map((m) => m[1]), ...[...layout.matchAll(/stamp-soon stamp-(\w+)"/g)].map((m) => m[1]), ]); assert.ok(variants.size >= 5, "the variants stopped being found at all");
assert.ok(css.includes(".stamp-soon {"), "styles.css has no .stamp-soon"); for (const variant of variants) { assert.ok( new RegExp(`\\.stamp-${variant}[\\s,{]`).test(css), `styles.css has no .stamp-${variant}`, ); }});
test("the bar's stamp is in the flow, so it cannot cover a label", () => { // The one this file exists to hold. The bar's stamp used to be absolutely // positioned and rotated across its label: it covered LEADERBOARDS and // OBSERVE at every width, and where the bar wrapped the two marks came down // on ABOUT and UPDATES on the row underneath. Four illegible labels, and // nothing failed. Out of the flow is what let it land anywhere, so that is // what is checked rather than the angle it was turned to. const rule = css.slice( css.indexOf(".stamp-nav {"), css.indexOf("}", css.indexOf(".stamp-nav {")), ); assert.notEqual(rule.length, 0, ".stamp-nav lost its rule"); assert.doesNotMatch( rule, /position:\s*(absolute|fixed)/, "the bar's stamp is out of the flow again, and can land on a label", ); assert.doesNotMatch( rule, /translate\(/, "the bar's stamp is being moved off the place the flow put it", );});
test("the bar's stamp is hidden from a screen reader, and the label is not", () => { // Two marks and one sentence: the stamp is an angle and a colour, and the // parenthesis is what the words actually reach a reader as. A stamp that // stopped being aria-hidden would have the bar read "Hangar coming soon not // ready yet". const span = layout.slice( layout.indexOf('<span class="soon"'), layout.indexOf("</span>", layout.indexOf("not ready yet")), ); assert.match( span, /class="stamp stamp-soon[^"]*"\s+aria-hidden="true"/, "the bar's stamp is announced as well as the parenthesis after it", ); assert.match(span, /class="soon-label"/, "the bar's label is not in a span");});
// --- what the front page is still promising -------------------------------//// Both of these are prose the site says in the present tense about something// it cannot do. They are checked by the sentence rather than by a count, so// building the thing and rewriting the fact is what removes the case, and// quietly dropping the mark off a fact that still reads this way does not.
const PROMISES = ["Design your forces", "Earn commendations"];
for (const promise of PROMISES) { test(`the fact that says "${promise}" is stamped`, () => { const at = home.indexOf(promise); assert.notEqual(at, -1, `no fact on the front page says "${promise}"`); // The flag is the last argument of the fact() call, so it is somewhere // between the sentence and whatever starts the next fact — or the end of // the strip, for the last one. const after = home.slice(at); const ends = after.search(/\n {4}fact\(|\n {2}\]\);/); assert.match( ends === -1 ? after : after.slice(0, ends), /\btrue\b/, `the front page states "${promise}" as a fact of the site today`, ); });}
test("a stamped fact is marked for a screen reader too", () => { assert.match(home, /soonStamp\("fact"\)/, "the facts strip lost its stamp"); assert.match( home, /notReadyNote\(\)/, "a stamped fact is marked by the stamp alone, which is nothing to a " + "screen reader", );});
test("the note the stamps cannot give a screen reader says one thing", () => { // Two screens call notReadyNote() and Base.astro writes the words by hand, // the same split the stamp itself has. Drift here is one part of the site // announcing something different from the rest. const words = stamp.match(/textContent: " \(([^"]+)\)"/)?.[1]; assert.ok(words, "soon-stamp.ts no longer spells the note out"); assert.ok( layout.includes(`(${words})`), `Base.astro does not say "(${words})"`, );});
// --- the Modes on Play that cannot be started -----------------------------//// futureModeCard is the one that stamps; modeCard, beside it, is a real button.// Building a Mode with the wrong one of the two is silent either way round: an// unbuilt Mode that looks live, or a working Mode stamped as if it were not.
test("a Mode with nothing behind it is stamped, and a live one is not", () => { const future = matches.slice( matches.indexOf("function futureModeCard"), matches.indexOf("export function matchesScreen"), ); assert.match(future, /soonStamp\("mode"\)/, "futureModeCard lost its stamp"); assert.match( future, /notReadyNote\(\)/, "an unbuilt Mode is marked by the stamp alone, which is nothing to a " + "screen reader", );
const live = matches.slice( matches.indexOf("function modeCard"), matches.indexOf("function futureModeCard"), ); assert.doesNotMatch(live, /soonStamp/, "a Mode that works is stamped soon");});
// --- the figures on the pilot card that nothing reports --------------------
test("a stat with no reader is stamped, and one with a reader is not", () => { assert.match(pilot, /soonStamp\("tile"\)/, "the unread stats lost the stamp"); // The stamp goes on inside `if (!stat.read)`. Outside it, every tile on the // card would carry one, including the two that count something real. const guarded = pilot.slice( pilot.indexOf("if (!stat.read)"), pilot.indexOf("return box;", pilot.indexOf("if (!stat.read)")), ); assert.match(guarded, /soonStamp\("tile"\)/, "the stamp is not guarded"); assert.match(guarded, /notReadyNote\(\)/, "the caption carries no note");});