[feat](trx-frontend-http): make the APRS list a log, and read the payloads
A frame was a card five lines tall — timestamp, a meta line, the information field as it arrived on the air, three buttons, and a Details panel repeating the four things already on the row — so five frames filled the panel and the payload was left to be decoded by eye. A frame is one line now: time, station, type, and what the frame says. It opens in place for the path, the CRC, the raw field, its bytes and the actions. Twenty-one frames fit where five did. And the information field is read rather than echoed. Weather reports give temperature, wind, humidity and pressure; telemetry gives its sequence and channels; a message gives its addressee and text; a position gives the fix, course and speed, and the comment the station wrote. Anything that cannot be summarised falls back to the raw field, which is in the expanded view either way. HF APRS had a copy of the same forty lines of markup, differing by one badge, so both now build their rows from one function in the shared module — the CSS is shared between them and this would have broken it otherwise. Both headers lose their three summary cards for a line of counts beside the filters, which frees another fifth of the panel. Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
@@ -164,3 +164,71 @@ try {
|
||||
await replay.browser.close();
|
||||
await historyFixture.close();
|
||||
}
|
||||
|
||||
// The APRS list: one line per frame, with the information field read for the
|
||||
// operator rather than shown as it arrives on the air.
|
||||
const APRS_FRAMES = [
|
||||
{ packet_type: "weather", info: "_10090556c220s004g005t077r000p000P000h50b09900" },
|
||||
{ packet_type: "message", info: ":SP2SJG-9 :Hello from the field{01" },
|
||||
{ packet_type: "telemetry", info: "T#005,199,000,255,073,123,01101001" },
|
||||
{ packet_type: "position", info: "!5421.30N/01839.20E>Test beacon 73", lat: 54.35, lon: 18.65 },
|
||||
].map((frame, index) => ({
|
||||
src_call: `SP2SJG-${index}`, dest_call: "APRS", path: "WIDE1-1", crc_ok: true,
|
||||
symbol_table: "/", symbol_code: ">", rig_id: "rig-a", ts_ms: Date.now() - index * 1000,
|
||||
...frame,
|
||||
}));
|
||||
|
||||
const aprsFixture = await startWebFixture({ spectrum: true, history: { aprs: APRS_FRAMES } });
|
||||
const aprs = await startBrowser(chromium);
|
||||
|
||||
try {
|
||||
await aprs.page.setViewportSize({ width: 1400, height: 900 });
|
||||
await aprs.page.goto(`${aprsFixture.origin}/digital-modes`, { waitUntil: "domcontentloaded" });
|
||||
await aprs.page.locator("#tab-digital-modes").waitFor({ state: "visible" });
|
||||
await aprs.page.waitForTimeout(2000);
|
||||
await aprs.page.locator('.sub-tab[data-subtab="aprs"]').click();
|
||||
await aprs.page.waitForTimeout(400);
|
||||
|
||||
const rows = await aprs.page.evaluate(() =>
|
||||
[...document.querySelectorAll("#aprs-packets .aprs-packet")].map((row) => ({
|
||||
tag: row.tagName,
|
||||
height: Math.round(row.getBoundingClientRect().height),
|
||||
type: row.querySelector(".aprs-badge-type")?.textContent?.trim() ?? "",
|
||||
summary: row.querySelector(".aprs-line-summary")?.textContent?.trim() ?? "",
|
||||
})));
|
||||
// Newest first, so find each by the type it carries rather than by position.
|
||||
const summaryOf = (type) => rows.find((row) => row.type === type)?.summary ?? "";
|
||||
assert.equal(rows.length, APRS_FRAMES.length, `rendered ${rows.length} frames`);
|
||||
for (const row of rows) {
|
||||
assert.equal(row.tag, "DETAILS", "a frame is not expandable in place");
|
||||
assert.ok(row.height < 44, `a frame is ${row.height}px tall; it used to be a card of about 140`);
|
||||
}
|
||||
// Each payload read rather than echoed: 25 °C from t077, the addressee from
|
||||
// a message, the sequence from telemetry, the fix from a position report.
|
||||
assert.match(summaryOf("Weather"), /25 °C/, `weather summary: ${summaryOf("Weather")}`);
|
||||
assert.match(summaryOf("Weather"), /990\.0 hPa/, `weather summary: ${summaryOf("Weather")}`);
|
||||
assert.match(summaryOf("Message"), /→ SP2SJG-9: Hello from the field/, `message summary: ${summaryOf("Message")}`);
|
||||
assert.match(summaryOf("Telemetry"), /^#005/, `telemetry summary: ${summaryOf("Telemetry")}`);
|
||||
assert.match(summaryOf("Position"), /54\.3500, 18\.6500/, `position summary: ${summaryOf("Position")}`);
|
||||
|
||||
// The frame as it arrived is still there, one click away.
|
||||
await aprs.page.locator("#aprs-packets .aprs-packet", { hasText: "25 °C" })
|
||||
.locator(".aprs-line").first().click();
|
||||
await aprs.page.waitForTimeout(200);
|
||||
const expanded = await aprs.page.evaluate(() => {
|
||||
const row = document.querySelector("#aprs-packets .aprs-packet[open]");
|
||||
return {
|
||||
open: row.hasAttribute("open"),
|
||||
raw: row.querySelector(".aprs-expanded-raw")?.textContent?.trim() ?? "",
|
||||
meta: row.querySelector(".aprs-expanded-meta")?.textContent ?? "",
|
||||
};
|
||||
});
|
||||
assert.equal(expanded.open, true, "the frame did not open");
|
||||
assert.match(expanded.raw, /^_10090556c220s004g005t077/, `raw frame: ${expanded.raw}`);
|
||||
assert.match(expanded.meta, /WIDE1-1/, `expanded meta: ${expanded.meta}`);
|
||||
|
||||
assert.deepEqual(aprs.runtimeErrors, []);
|
||||
} finally {
|
||||
await aprs.browser.close();
|
||||
await aprsFixture.close();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user