[fix](trx-frontend-http): stop the decode history replay giving up at 20s
Reloading a second time sometimes showed history the first load did not, and the safety valve is why: it called one function that both released the buffered live decodes and tore the history worker down, so any load where the replay had not finished inside twenty seconds — a large backlog, a cold cache, a slow link — dropped whatever had not arrived, without a word. A reload got another go at it, and the second one is faster because everything is cached by then. Those are two separate things now. At the timeout the live decodes are released so the panels are not held back, the replay carries on, and the progress says so. The fallback's error path retries once and then says "Decode history unavailable" rather than leaving the operator to guess whether there was anything to see. The progress is no longer a scrim. It was fixed to the whole viewport with a wash over the page — the waterfall, the decode panels, all of it — for the length of the replay, which is exactly when there is something worth watching. It is a corner card with a bar: indeterminate while the payload is on the wire, then filling as N of M messages replay. None of this was reachable from a test. /decode/history answers in CBOR and the worker reads the body as CBOR unconditionally, but the fixture served JSON, so every browser run had been exercising the client's retry path and never its history path. It encodes CBOR now, including the 64-bit form the millisecond timestamps need, and decode-flow serves 1200 records and holds the client to restoring all of them on the first load, showing progress while it does, and never covering the page with it. Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
@@ -97,3 +97,70 @@ try {
|
||||
await browser.close();
|
||||
await fixture.close();
|
||||
}
|
||||
|
||||
// Stored history, which is what is on screen a second after a page load. The
|
||||
// endpoint answers in CBOR, so the fixture speaks CBOR: serving anything else
|
||||
// left the client on its retry path and the history path untested.
|
||||
const HISTORY_AIS = 900;
|
||||
const HISTORY_APRS = 300;
|
||||
const historyFixture = await startWebFixture({
|
||||
spectrum: true,
|
||||
mode: "AIS",
|
||||
history: {
|
||||
ais: Array.from({ length: HISTORY_AIS }, (_, index) => ({
|
||||
mmsi: 244660000 + index, lat: 52.3 + index * 0.001, lon: 4.8,
|
||||
vessel_name: `HISTORIC ${index}`, channel: "A", message_type: 1,
|
||||
rig_id: "rig-a", ts_ms: Date.now() - (index + 1) * 1000,
|
||||
})),
|
||||
aprs: Array.from({ length: HISTORY_APRS }, (_, index) => ({
|
||||
src_call: `SP2SJG-${index % 15}`, dest_call: "APRS", path: "WIDE1-1",
|
||||
info: `history ${index}`, packet_type: "position", crc_ok: true,
|
||||
lat: 54.3, lon: 18.6, rig_id: "rig-a", ts_ms: Date.now() - (index + 1) * 1000,
|
||||
})),
|
||||
},
|
||||
});
|
||||
const replay = await startBrowser(chromium);
|
||||
|
||||
try {
|
||||
await replay.page.setViewportSize({ width: 1400, height: 900 });
|
||||
await replay.page.goto(`${historyFixture.origin}/digital-modes`, { waitUntil: "domcontentloaded" });
|
||||
await replay.page.locator("#tab-digital-modes").waitFor({ state: "visible" });
|
||||
|
||||
// While it loads, the operator can still see the radio. This used to be a
|
||||
// full-screen scrim over everything for as long as the replay ran.
|
||||
const samples = [];
|
||||
for (let attempt = 0; attempt < 30; attempt++) {
|
||||
samples.push(await replay.page.evaluate(() => {
|
||||
const element = document.getElementById("decode-history-overlay");
|
||||
if (!element || element.classList.contains("is-hidden")) return null;
|
||||
const rect = element.getBoundingClientRect();
|
||||
return {
|
||||
width: Math.round(rect.width),
|
||||
coversCentre: document.elementFromPoint(700, 450)?.id === "decode-history-overlay",
|
||||
};
|
||||
}));
|
||||
await replay.page.waitForTimeout(100);
|
||||
}
|
||||
const shown = samples.filter(Boolean);
|
||||
assert.ok(shown.length > 0, "no progress was shown while the history loaded");
|
||||
for (const sample of shown) {
|
||||
assert.ok(sample.width < 700, `the progress covers ${sample.width}px of a 1400px page`);
|
||||
assert.equal(sample.coversCentre, false, "the progress sits over the page");
|
||||
}
|
||||
|
||||
// And all of it arrives, on the first load.
|
||||
await replay.page.waitForTimeout(2000);
|
||||
const restored = await replay.page.evaluate(() => ({
|
||||
ais: document.getElementById("ais-messages")?.children.length ?? 0,
|
||||
aprs: document.getElementById("aprs-packets")?.children.length ?? 0,
|
||||
progressHidden: document.getElementById("decode-history-overlay").classList.contains("is-hidden"),
|
||||
}));
|
||||
assert.equal(restored.ais, HISTORY_AIS, `restored ${restored.ais} of ${HISTORY_AIS} AIS records`);
|
||||
assert.equal(restored.aprs, HISTORY_APRS, `restored ${restored.aprs} of ${HISTORY_APRS} APRS records`);
|
||||
assert.equal(restored.progressHidden, true, "the progress stayed up after the replay finished");
|
||||
|
||||
assert.deepEqual(replay.runtimeErrors, []);
|
||||
} finally {
|
||||
await replay.browser.close();
|
||||
await historyFixture.close();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user