swarm-ui: test the rain arithmetic and the swarm name loader
The rain's column maths and fade colour move from MatrixRain into shell/rain.ts, and the request-once swarm name loader from useSwarmName into shell/swarm-name.ts. Both are plain functions with their input passed in, so tests cover them; the components keep the DOM and canvas work.
This commit is contained in:
parent
01e58219cf
commit
e32b4172e4
6 changed files with 237 additions and 42 deletions
69
frontend/packages/swarm-ui/test/rain.test.mjs
Normal file
69
frontend/packages/swarm-ui/test/rain.test.mjs
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
// The digital rain's arithmetic: columns, how they start and step, the glyph
|
||||
// drawn, and the fade colour. The random source is passed in, so each case is
|
||||
// exact. Run by `npm test`.
|
||||
import assert from "node:assert/strict";
|
||||
import { test } from "node:test";
|
||||
import {
|
||||
CELL,
|
||||
GLYPHS,
|
||||
columnsFor,
|
||||
fadeColorOf,
|
||||
glyphFor,
|
||||
startDrops,
|
||||
stepDrops,
|
||||
} from "../src/shell/rain.ts";
|
||||
|
||||
const always = (n) => () => n;
|
||||
|
||||
test("a canvas gets one column per cell, rounded up, and at least one", () => {
|
||||
assert.equal(columnsFor(CELL * 10), 10);
|
||||
assert.equal(columnsFor(CELL * 10 + 1), 11);
|
||||
assert.equal(columnsFor(0), 1);
|
||||
});
|
||||
|
||||
test("columns start above the top, spread over 40 cells", () => {
|
||||
assert.deepEqual(
|
||||
startDrops(3, always(0)).map((row) => row === 0),
|
||||
[true, true, true],
|
||||
);
|
||||
assert.deepEqual(startDrops(2, always(0.5)), [-20, -20]);
|
||||
for (const row of startDrops(200, Math.random)) {
|
||||
assert.ok(row <= 0 && row >= -40, String(row));
|
||||
}
|
||||
});
|
||||
|
||||
test("a step moves every column down one row", () => {
|
||||
assert.deepEqual(stepDrops([-3, 0, 5], 10_000, always(0)), [-2, 1, 6]);
|
||||
});
|
||||
|
||||
test("a column past the bottom restarts only on a high enough roll", () => {
|
||||
const past = [Math.ceil(100 / CELL) + 1];
|
||||
assert.deepEqual(stepDrops(past, 100, always(0.99)), [1]);
|
||||
assert.deepEqual(stepDrops(past, 100, always(0.5)), [past[0] + 1]);
|
||||
});
|
||||
|
||||
test("a column still on the canvas never restarts", () => {
|
||||
assert.deepEqual(stepDrops([2], 100, always(0.99)), [3]);
|
||||
});
|
||||
|
||||
test("a step does not change the array it was given", () => {
|
||||
const drops = [1, 2];
|
||||
stepDrops(drops, 100, always(0.99));
|
||||
assert.deepEqual(drops, [1, 2]);
|
||||
});
|
||||
|
||||
test("the glyph comes from the set, first and last at the ends of the roll", () => {
|
||||
assert.equal(glyphFor(always(0)), GLYPHS[0]);
|
||||
assert.equal(glyphFor(always(0.999999)), GLYPHS[GLYPHS.length - 1]);
|
||||
});
|
||||
|
||||
test("the fade colour keeps the background's channels at a low alpha", () => {
|
||||
assert.equal(fadeColorOf("rgb(30, 30, 46)"), "rgba(30, 30, 46, 0.09)");
|
||||
assert.equal(fadeColorOf("rgb(239,241,245)"), "rgba(239, 241, 245, 0.09)");
|
||||
assert.equal(fadeColorOf("rgba(1, 2, 3, 0.5)"), "rgba(1, 2, 3, 0.09)");
|
||||
});
|
||||
|
||||
test("a colour with no channels fades from the default background", () => {
|
||||
assert.equal(fadeColorOf("transparent"), "rgba(30, 30, 46, 0.09)");
|
||||
assert.equal(fadeColorOf(""), "rgba(30, 30, 46, 0.09)");
|
||||
});
|
||||
60
frontend/packages/swarm-ui/test/swarm-name.test.mjs
Normal file
60
frontend/packages/swarm-ui/test/swarm-name.test.mjs
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
// The swarm name loader: one request however many callers, and a name that is
|
||||
// absent, empty, or failed to load all answer null. Run by `npm test`.
|
||||
import assert from "node:assert/strict";
|
||||
import { test } from "node:test";
|
||||
import { createSwarmNameLoader } from "../src/shell/swarm-name.ts";
|
||||
|
||||
// A fetch that answers `body` as an ok response, counting its calls.
|
||||
function fetching(body, ok = true) {
|
||||
const f = async (url) => {
|
||||
f.calls.push(url);
|
||||
return { ok, json: async () => body };
|
||||
};
|
||||
f.calls = [];
|
||||
return f;
|
||||
}
|
||||
|
||||
test("the loader answers the configured name", async () => {
|
||||
const f = fetching({ name: "constellation" });
|
||||
assert.equal(await createSwarmNameLoader(f)(), "constellation");
|
||||
assert.deepEqual(f.calls, ["/api/swarm"]);
|
||||
});
|
||||
|
||||
test("every caller shares one request", async () => {
|
||||
const f = fetching({ name: "constellation" });
|
||||
const load = createSwarmNameLoader(f);
|
||||
const names = await Promise.all([load(), load(), load()]);
|
||||
assert.deepEqual(names, ["constellation", "constellation", "constellation"]);
|
||||
assert.equal(f.calls.length, 1);
|
||||
});
|
||||
|
||||
test("a null, empty or missing name answers null", async () => {
|
||||
for (const body of [{ name: null }, { name: "" }, {}]) {
|
||||
assert.equal(await createSwarmNameLoader(fetching(body))(), null);
|
||||
}
|
||||
});
|
||||
|
||||
test("an error response answers null", async () => {
|
||||
assert.equal(
|
||||
await createSwarmNameLoader(fetching({ name: "x" }, false))(),
|
||||
null,
|
||||
);
|
||||
});
|
||||
|
||||
test("a failed request answers null rather than throwing", async () => {
|
||||
const load = createSwarmNameLoader(async () => {
|
||||
throw new Error("offline");
|
||||
});
|
||||
assert.equal(await load(), null);
|
||||
});
|
||||
|
||||
test("a failed request is remembered, not retried by later callers", async () => {
|
||||
let calls = 0;
|
||||
const load = createSwarmNameLoader(async () => {
|
||||
calls += 1;
|
||||
throw new Error("offline");
|
||||
});
|
||||
await load();
|
||||
await load();
|
||||
assert.equal(calls, 1);
|
||||
});
|
||||
Loading…
Reference in a new issue