diff --git a/README.md b/README.md index 04ba350..0b63a7d 100644 --- a/README.md +++ b/README.md @@ -113,19 +113,19 @@ half second. As soon as one answers, a new round starts. ### Rows, colors and controls A figure and its line in the graph take the color of the legend in the footer, -from green under 50 ms to red from 500 ms up; gray is no answer. The graph's -axis ends at 1000 ms; a higher latency is drawn at the top, and the figure still -shows its real value. +from green under 50 ms to red from 500 ms up; gray is no answer. The dot beside +the host's name takes the same color. The graph's axis ends at 1000 ms; a higher +latency is drawn at the top, and the figure still shows its real value. From time to time, the page sorts the WAN rows by their latest latency: fastest first, failed last. The pin button at the left of a row keeps the row at the top; pinned rows are sorted by name. `datavi.be` is pinned at start. `Pause` stops the checks: the graphs keep scrolling with a gap for each paused -round, and the figures turn gray. The `Interval` menu sets the time between -rounds: 1, 2, 3, 5, 10, 15, 30 or 60 seconds. Changing it clears the history and -starts a new round at once. The `Debug log` checkbox in the footer opens a panel -with the page's last 1000 log lines. +round, and the figures and dots turn gray. The `Interval` menu sets the time +between rounds: 1, 2, 3, 5, 10, 15, 30 or 60 seconds. Changing it clears the +history and starts a new round at once. The `Debug log` checkbox in the footer +opens a panel with the page's last 1000 log lines. ## Reporting diff --git a/test/unit/main.test.js b/test/unit/main.test.js index d265b44..15ce6d1 100644 --- a/test/unit/main.test.js +++ b/test/unit/main.test.js @@ -28,8 +28,8 @@ import { // a host's row that tick or greyOutUI draws into is a plain object, made // the first time a test looks it up and kept in elements under its // selector until the next test starts. As on a page, writing its text -// replaces its markup; the status dot greyOutUI looks for in it is not -// there. Drawing a sparkline does nothing; it looks for the pixel ratio on +// replaces its markup; setting a style keeps it in the element's style +// object. Drawing a sparkline does nothing; it looks for the pixel ratio on // window and finds none. The commit the page's footer names, which // vite.config.js fills in when it builds the page, is blank. In each test, // console.error and console.info print nothing and keep what they are @@ -63,7 +63,7 @@ globalThis.document = { querySelector: (selector) => (elements[selector] ??= { getContext: () => canvasContext, - querySelector: () => null, + style: {}, set textContent(text) { this.innerHTML = text; }, @@ -83,6 +83,12 @@ function statusText(state, host) { return elements[`.status-text[data-host="${index}"]`]?.innerHTML; } +// The color last given to the dot beside host's name. +function statusDot(state, host) { + const index = state.allHosts.indexOf(host); + return elements[`.status-dot[data-host="${index}"]`]?.style.backgroundColor; +} + // What this test wrote to the browser console with console[method]. function consoleLines(method) { return console[method].mock.calls.map((call) => call.arguments[0]); @@ -683,6 +689,37 @@ test("a check that timed out or found its target unreachable has a grey figure a assert.equal(latencyHex(null), "#6b7280"); }); +test("a check that answers after 488ms turns the dot beside its target's name orange", async (t) => { + const { state, target } = stateWithOneTarget(); + mockTargets(t, () => 488); + const round = tick(state, new AbortController().signal); + t.mock.timers.tick(488); + assert.notEqual(await settled(round), "still waiting"); + assert.equal(statusDot(state, target), "#f97316"); +}); + +test("a check that fails after one that answered turns the dot beside its target's name gray", async (t) => { + const { state, target } = stateWithOneTarget(); + target.pushSample(Date.now(), { latency: 8, error: null }); + mockTargets(t, () => 23, target.url); + const round = tick(state, new AbortController().signal); + t.mock.timers.tick(23); + assert.notEqual(await settled(round), "still waiting"); + assert.equal(statusDot(state, target), "#6b7280"); +}); + +test("pausing turns the dot beside a target's name gray", async (t) => { + const { state, target } = stateWithOneTarget(); + mockTargets(t, () => 8); + const round = tick(state, new AbortController().signal); + t.mock.timers.tick(8); + assert.notEqual(await settled(round), "still waiting"); + assert.equal(statusDot(state, target), "#22c55e"); + state.paused = true; + greyOutUI(state); + assert.equal(statusDot(state, target), "#6b7280"); +}); + // buildUI draws the page into #app, here a plain object. Each entry in the // footer's color legend is a colored dot and the latencies it stands for, // with < and > written as < and >. A latency takes the color of the diff --git a/web/src/main.js b/web/src/main.js index 85d16f1..a0b1da9 100644 --- a/web/src/main.js +++ b/web/src/main.js @@ -30,8 +30,8 @@ export const CONFIG = { return [0, 1, 2, 3, 4, 5].map((i) => Math.round((d * i) / 5)); }, canvasHeight: 96, - // A latency figure and its sparkline take the color of the first entry - // whose limit, in ms, the latency is below. + // A latency figure, its row's dot and its sparkline take the color of + // the first entry whose limit, in ms, the latency is below. latencyColors: [ { below: 50, hex: "#22c55e", className: "text-green-500" }, { below: 100, hex: "#84cc16", className: "text-lime-500" }, @@ -819,7 +819,7 @@ export function hostRowHTML(host, index, showPin = true) { ${pinBtn}
-
+
${escapeHTML(host.name)}
@@ -982,8 +982,10 @@ function updateHostRow(host, index) { const canvas = document.querySelector( `.sparkline-canvas[data-host="${index}"]`, ); - if (!latencyEl || !statusEl || !canvas) return; + const dot = document.querySelector(`.status-dot[data-host="${index}"]`); + if (!latencyEl || !statusEl || !canvas || !dot) return; + dot.style.backgroundColor = latencyHex(host.lastLatency); if (host.lastLatency !== null) { const cls = latencyClass(host.lastLatency, host.status); latencyEl.innerHTML = `${host.lastLatency}ms`; @@ -1420,12 +1422,8 @@ export function greyOutUI(state) { statusEl.textContent = "paused"; statusEl.className = `${STATUS_TEXT_CLASS} text-gray-500`; } - // Grey out the status dot - const row = document.querySelector(`.host-row[data-index="${i}"]`); - if (row) { - const dot = row.querySelector(".rounded-full"); - if (dot) dot.style.backgroundColor = "#6b7280"; - } + const dot = document.querySelector(`.status-dot[data-host="${i}"]`); + if (dot) dot.style.backgroundColor = latencyHex(null); }); // Grey out summary current stats