Summary reads --/-- after an interval change, not 0/26 in red (closes #140)
check / check (push) Waiting to run
check / check (push) Waiting to run
The interval menu resets every host to pending and updates the summary, which counted the WAN hosts with a latency recorded, none, and showed 0/26 in red until the second round after the change ended. While every WAN host still waits for its first check, the Reachable slot now reads --/-- in white, as at page load. updateSummary is exported for a unit test that resets the hosts as the interval menu does; a second test checks that the slot still reads 0/26 in red once every check failed. Model: opus-5-5
This commit is contained in:
@@ -21,6 +21,14 @@ Run `make frontend-viewport-test` in CI as its own step; it is not part of
|
|||||||
|
|
||||||
# Completed Steps
|
# Completed Steps
|
||||||
|
|
||||||
|
- 2026-10-18: after an interval change, the summary's Reachable slot reads
|
||||||
|
`--/--` in white, as at page load, until the second round since the change
|
||||||
|
ends ([#140](https://git.eeqj.de/sneak/netwatch/issues/140)). It used to read
|
||||||
|
0/26 in red from the change on, as no WAN host had a check recorded, for up to
|
||||||
|
two intervals with the network fine. The summary update now writes `--/--`
|
||||||
|
while every WAN host still waits for its first check. A unit test resets the
|
||||||
|
hosts as the interval menu does and checks the slot; another checks that it
|
||||||
|
reads 0/26 in red once every WAN host's check has failed
|
||||||
- 2026-10-14: stopping the server waits for a report file write already under
|
- 2026-10-14: stopping the server waits for a report file write already under
|
||||||
way, the periodic flush's or the one `Append` starts when 10 MiB are waiting,
|
way, the periodic flush's or the one `Append` starts when 10 MiB are waiting,
|
||||||
before the final flush
|
before the final flush
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import {
|
|||||||
measureLatency,
|
measureLatency,
|
||||||
Reporter,
|
Reporter,
|
||||||
tick,
|
tick,
|
||||||
|
updateSummary,
|
||||||
} from "../../web/src/main.js";
|
} from "../../web/src/main.js";
|
||||||
|
|
||||||
// There is no page here, so the tests stand in for it. The debug log looks
|
// There is no page here, so the tests stand in for it. The debug log looks
|
||||||
@@ -654,6 +655,52 @@ test("the summary's min, max, median and average latency over the WAN targets' l
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// In the next two tests each element of the summary that updateSummary
|
||||||
|
// writes into is a plain object, made the first time it is looked up and
|
||||||
|
// kept in elements under "#" and its id.
|
||||||
|
|
||||||
|
// The interval menu resets each target and counts the rounds from 0 again,
|
||||||
|
// then updates the summary, before any check since the change is recorded.
|
||||||
|
test("after an interval change, the summary's Reachable slot reads --/-- in white, as at page load", (t) => {
|
||||||
|
t.mock.method(
|
||||||
|
globalThis.document,
|
||||||
|
"getElementById",
|
||||||
|
(id) => (elements[`#${id}`] ??= {}),
|
||||||
|
);
|
||||||
|
const state = new AppState([]);
|
||||||
|
for (const host of state.wan) {
|
||||||
|
host.pushSample(Date.now(), { latency: 30, error: null });
|
||||||
|
}
|
||||||
|
state.tickCount = 5;
|
||||||
|
|
||||||
|
for (const host of state.allHosts) host.reset();
|
||||||
|
state.tickCount = 0;
|
||||||
|
updateSummary(state);
|
||||||
|
|
||||||
|
assert.equal(elements["#summary-reachable"].textContent, "--/--");
|
||||||
|
assert.equal(elements["#summary-reachable"].className, "text-white");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("once every WAN target's check has failed, the summary's Reachable slot reads 0 of them, in red", (t) => {
|
||||||
|
t.mock.method(
|
||||||
|
globalThis.document,
|
||||||
|
"getElementById",
|
||||||
|
(id) => (elements[`#${id}`] ??= {}),
|
||||||
|
);
|
||||||
|
const state = new AppState([]);
|
||||||
|
for (const host of state.wan) {
|
||||||
|
host.pushSample(Date.now(), { latency: null, error: "timeout" });
|
||||||
|
}
|
||||||
|
|
||||||
|
updateSummary(state);
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
elements["#summary-reachable"].textContent,
|
||||||
|
`0/${state.wan.length}`,
|
||||||
|
);
|
||||||
|
assert.equal(elements["#summary-reachable"].className, "text-red-400");
|
||||||
|
});
|
||||||
|
|
||||||
// An app state in which, of the WAN targets, the first timedOut timed out,
|
// An app state in which, of the WAN targets, the first timedOut timed out,
|
||||||
// the next unreachable were found unreachable, the next answered answered
|
// the next unreachable were found unreachable, the next answered answered
|
||||||
// after latency ms, and the rest have not been checked yet.
|
// after latency ms, and the rest have not been checked yet.
|
||||||
|
|||||||
+15
-8
@@ -995,7 +995,7 @@ function updateHostRow(host, index) {
|
|||||||
SparklineRenderer.draw(canvas, host.history);
|
SparklineRenderer.draw(canvas, host.history);
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateSummary(state) {
|
export function updateSummary(state) {
|
||||||
const stats = state.wanStats();
|
const stats = state.wanStats();
|
||||||
const hstats = state.wanHistoryStats();
|
const hstats = state.wanHistoryStats();
|
||||||
|
|
||||||
@@ -1014,13 +1014,20 @@ function updateSummary(state) {
|
|||||||
const hmaxUnitEl = document.getElementById("summary-hmax-unit");
|
const hmaxUnitEl = document.getElementById("summary-hmax-unit");
|
||||||
if (!reachableEl) return;
|
if (!reachableEl) return;
|
||||||
|
|
||||||
reachableEl.textContent = `${stats.reachable}/${stats.total}`;
|
// Before any WAN host's check is recorded, as after an interval change,
|
||||||
reachableEl.className =
|
// no host has answered or failed, so the slot reads as at page load.
|
||||||
stats.reachable === stats.total
|
if (state.wan.every((h) => h.status === "pending")) {
|
||||||
? "text-green-400"
|
reachableEl.textContent = "--/--";
|
||||||
: stats.reachable === 0
|
reachableEl.className = "text-white";
|
||||||
? "text-red-400"
|
} else {
|
||||||
: "text-yellow-400";
|
reachableEl.textContent = `${stats.reachable}/${stats.total}`;
|
||||||
|
reachableEl.className =
|
||||||
|
stats.reachable === stats.total
|
||||||
|
? "text-green-400"
|
||||||
|
: stats.reachable === 0
|
||||||
|
? "text-red-400"
|
||||||
|
: "text-yellow-400";
|
||||||
|
}
|
||||||
|
|
||||||
if (stats.min !== null) {
|
if (stats.min !== null) {
|
||||||
minEl.textContent = `${stats.min}`;
|
minEl.textContent = `${stats.min}`;
|
||||||
|
|||||||
Reference in New Issue
Block a user