Watch
0
0
Fork
You've already forked hyperhive
0
hyperhive/frontend/packages/swarm-ui/test/messages.test.mjs
iris bdeec3f744 swarm-ui: key message rows on an arrival id and follow the tail on it
At the 2000-message cap the count stops changing, so the page stopped
following the newest message, and every row's index key shifted on each
arrival, remounting all of them. Each message now gets an id when it
arrives; the rows key on it and the scroll follows the newest id. Rows
are memoised on the message, and the events of one frame join the list
in a single update.
2026-10-18 18:39:11 +02:00

128 lines
4.1 KiB
JavaScript

// The messages page's logic: the stream URL, the filter, the cap, and how a
// message becomes a terminal row. Run by `npm test`, which Node runs with its
// built-in test runner and type stripping, so the `.ts` module is imported as
// it is.
import assert from "node:assert/strict";
import { test } from "node:test";
import {
clock,
filterMessages,
messageRow,
messagesUrl,
newest,
numbered,
snippet,
} from "../src/pages/messages/messages.ts";
const msg = (from, to, body = "hi", extra = {}) => ({
ts: "2026-10-18T12:00:00Z",
from,
to,
body,
...extra,
});
test("the stream URL carries the history window", () => {
assert.equal(messagesUrl(60), "/api/messages/stream?since_minutes=60");
assert.equal(messagesUrl(0), "/api/messages/stream?since_minutes=0");
});
test("a blank filter keeps every message", () => {
const all = [msg("a", "b"), msg("c", "d")];
assert.deepEqual(filterMessages(all, ""), all);
assert.deepEqual(filterMessages(all, " "), all);
});
test("the filter matches sender or recipient, ignoring case", () => {
const all = [msg("atlas", "iris"), msg("iris", "coder"), msg("a", "b")];
assert.deepEqual(
filterMessages(all, "IRIS").map((m) => `${m.from}>${m.to}`),
["atlas>iris", "iris>coder"],
);
assert.deepEqual(filterMessages(all, "cod").length, 1);
assert.deepEqual(filterMessages(all, "nobody"), []);
});
test("the filter does not search the body", () => {
assert.deepEqual(filterMessages([msg("a", "b", "talk to iris")], "iris"), []);
});
test("the cap keeps the newest messages and leaves a short list alone", () => {
const all = [1, 2, 3, 4, 5].map((n) => msg("a", "b", String(n)));
assert.deepEqual(
newest(all, 3).map((m) => m.body),
["3", "4", "5"],
);
assert.equal(newest(all, 5), all);
assert.equal(newest(all, 10), all);
});
test("arrivals are numbered in order from the id given", () => {
const batch = numbered([msg("a", "b", "x"), msg("c", "d", "y")], 7);
assert.deepEqual(
batch.map((m) => [m.id, m.body]),
[
[7, "x"],
[8, "y"],
],
);
assert.deepEqual(numbered([], 3), []);
});
test("in a full list the newest id still changes with every arrival", () => {
// The page follows the tail on the newest id, because the count stops
// moving once the cap drops one message per arrival.
let list = numbered(
[1, 2, 3].map((n) => msg("a", "b", String(n))),
0,
);
const seen = new Set();
for (let id = 3; id < 8; id++) {
list = newest([...list, ...numbered([msg("a", "b", "n")], id)], 3);
assert.equal(list.length, 3);
seen.add(list[list.length - 1].id);
}
assert.equal(seen.size, 5);
});
test("a number-tagged message keeps its id through the filter and the cap", () => {
const all = numbered([msg("atlas", "iris"), msg("a", "b")], 40);
assert.deepEqual(
filterMessages(all, "atlas").map((m) => m.id),
[40],
);
assert.deepEqual(
newest(all, 1).map((m) => m.id),
[41],
);
});
test("a snippet is the first non-blank line, cut with an ellipsis", () => {
assert.equal(snippet("\n\n hello there \nsecond", 80), "hello there");
assert.equal(snippet("abcdefghij", 5), "abcd…");
assert.equal(snippet("abcde", 5), "abcde");
assert.equal(snippet(" \n ", 5), "");
});
test("the clock reads hours, minutes and seconds, and passes a non-date through", () => {
assert.match(clock("2026-10-18T12:00:05Z"), /^\d\d:\d\d:\d\d$/);
assert.equal(clock("not a date"), "not a date");
});
test("a message becomes an info row with its parties in the summary", () => {
const row = messageRow(msg("atlas", "iris", "ready\nmore"), "k1");
assert.equal(row.key, "k1");
assert.equal(row.level, "info");
assert.equal(row.body, "ready\nmore");
assert.equal(row.fromHistory, false);
assert.match(row.summary, /atlas → iris — ready$/);
});
test("the body format rides on the row, and an empty body leaves no dash", () => {
assert.equal(
messageRow(msg("a", "b", "x", { body_format: "diff" }), "k").body_format,
"diff",
);
assert.equal(messageRow(msg("a", "b", "x"), "k").body_format, undefined);
assert.doesNotMatch(messageRow(msg("a", "b", ""), "k").summary, / — /);
});