Watch
0
0
Fork
You've already forked hyperhive
0
hyperhive/frontend/packages/swarm-ui/test/poller.test.mjs
iris 47c447409b swarm-ui: the refresh picker shows a spinner in place of its clock while its page is fetching
usePolled reports when a request is in flight (through a new onBusy of the
poller) and holds it on for at least 0.4 seconds, so a quick answer still
reads as a refresh. The picker swaps its clock for the spinner, in the same
box so its width stays, and the spinner stands still where motion is off.
Every page with a picker passes it; a page with several polled requests shows
it while any of them runs. The Jobs page fetches through shared components
and has none yet.
2026-11-05 23:09:07 +01:00

116 lines
3.5 KiB
JavaScript

// The request discipline of a polled page. 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 { createPoller } from "../src/ui/polled/poller.ts";
// A fetch whose answers the test settles by hand, in any order.
function manualFetch() {
const calls = [];
const fetch = (signal) =>
new Promise((resolve, reject) => {
calls.push({ signal, resolve, reject });
});
return { calls, fetch };
}
// Lets the promise callbacks of a settled request run.
const flush = () => new Promise((resolve) => setImmediate(resolve));
function collect() {
const seen = { data: [], error: [] };
return {
seen,
onData: (d) => seen.data.push(d),
onError: (e) => seen.error.push(e),
};
}
test("a tick while a request is in flight asks for nothing", async () => {
const { calls, fetch } = manualFetch();
const sink = collect();
const poller = createPoller({ fetch, ...sink });
poller.tick();
poller.tick();
assert.equal(calls.length, 1);
calls[0].resolve("one");
await flush();
assert.deepEqual(sink.seen.data, ["one"]);
poller.tick();
assert.equal(calls.length, 2);
});
test("a refetch drops the request in flight, so an older answer never lands", async () => {
const { calls, fetch } = manualFetch();
const sink = collect();
const poller = createPoller({ fetch, ...sink });
poller.tick();
poller.refetch();
assert.equal(calls.length, 2);
assert.equal(calls[0].signal.aborted, true);
calls[1].resolve("new");
calls[0].resolve("old");
await flush();
assert.deepEqual(sink.seen.data, ["new"]);
});
test("a stopped poller aborts the request and ignores its answer", async () => {
const { calls, fetch } = manualFetch();
const sink = collect();
const poller = createPoller({ fetch, ...sink });
poller.tick();
poller.stop();
assert.equal(calls[0].signal.aborted, true);
calls[0].resolve("late");
await flush();
assert.deepEqual(sink.seen.data, []);
poller.tick();
assert.equal(calls.length, 2);
});
test("a failed request reports the error and frees the next tick", async () => {
const { calls, fetch } = manualFetch();
const sink = collect();
const poller = createPoller({ fetch, ...sink });
poller.tick();
const boom = new Error("boom");
calls[0].reject(boom);
await flush();
assert.deepEqual(sink.seen.error, [boom]);
poller.tick();
assert.equal(calls.length, 2);
});
test("the answer of a dropped request that fails is not reported", async () => {
const { calls, fetch } = manualFetch();
const sink = collect();
const poller = createPoller({ fetch, ...sink });
poller.tick();
poller.refetch();
calls[0].reject(new Error("aborted"));
await flush();
assert.deepEqual(sink.seen.error, []);
});
test("onBusy follows a request in flight, however it ends", async () => {
const { calls, fetch } = manualFetch();
const sink = collect();
const busy = [];
const poller = createPoller({ fetch, ...sink, onBusy: (b) => busy.push(b) });
poller.tick();
poller.tick();
assert.deepEqual(busy, [true]);
calls[0].resolve("one");
await flush();
assert.deepEqual(busy, [true, false]);
poller.tick();
calls[1].reject(new Error("boom"));
await flush();
assert.deepEqual(busy, [true, false, true, false]);
poller.tick();
poller.stop();
assert.deepEqual(busy, [true, false, true, false, true, false]);
poller.stop();
assert.equal(busy.length, 6);
});