Watch
0
0
Fork
You've already forked hyperhive
0
hyperhive/frontend/packages/swarm-ui/test/rain.test.mjs
iris 83b5f0bd89 swarm-ui: the start page's rain follows the app's motion setting
The rain read only the OS reduced-motion query, once, at mount. The settings
menu's motion override (data-motion on the document) wins over the OS in
either direction everywhere else, and the shell applies it from an effect
that runs after this one. Whether the rain may move is now rainAllowed(), and
the component re-evaluates it on mount, on every document attribute change
and on the media query's change, starting or stopping the interval and
clearing the canvas.
2026-10-14 18:36:52 +02:00

86 lines
2.9 KiB
JavaScript

// 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,
rainAllowed,
startDrops,
stepDrops,
} from "../src/shell/rain.ts";
const always = (n) => () => n;
test("the app's reduce setting stops the rain whatever the OS says", () => {
assert.equal(rainAllowed("reduce", false), false);
assert.equal(rainAllowed("reduce", true), false);
});
test("the app's allow setting runs the rain even on a reduce-motion OS", () => {
assert.equal(rainAllowed("allow", true), true);
assert.equal(rainAllowed("allow", false), true);
});
test("with no app setting the rain follows the OS", () => {
assert.equal(rainAllowed(undefined, false), true);
assert.equal(rainAllowed(undefined, true), false);
assert.equal(rainAllowed("", true), false);
});
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)");
});