Skip to content

Commit 30bff35

Browse files
committed
fix: stop double-listing region warnings; share page header
Address review round 4 (CRF-32..35): - CRF-32: the CLI folds region/node warnings into the DERP section `warnings`, so the region walk listed them twice. Drop the region-warning walk (keep region errors, which are not aggregated) and remove the now-redundant region `warnings` field from the schema and shared type. - CRF-33: add a test for the `derp.error` severity fold (the existing case set `netcheck_err`, short-circuiting the `|| d.error` operand). - CRF-34: extract `pageHeader` into @repo/webview-shared/dom.ts, used by both vanilla webviews (the last header duplicate after the dom.ts dedup). - CRF-35: document that section `severity` is a parse-time rollup. Kept the error fold inlined per earlier maintainer preference (trivial ternary).
1 parent 6a4fbee commit 30bff35

9 files changed

Lines changed: 53 additions & 49 deletions

File tree

packages/netcheck/src/health.ts

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -69,24 +69,15 @@ export function collectIssues(report: NetcheckReport): Issue[] {
6969
}
7070
for (const [key, region] of Object.entries(report.derp.regions)) {
7171
const name = regionName(region, Number(key));
72+
// Region warnings are already in the section list; list only errors.
7273
if (region.error) {
7374
errors.push({ kind: "error", message: `${name}: ${region.error}` });
74-
}
75-
for (const warning of region.warnings ?? []) {
76-
warnings.push({
77-
kind: "warning",
78-
message: `${name}: ${warning.message}`,
79-
...(warning.code ? { code: warning.code } : {}),
75+
} else if (region.severity === "error") {
76+
errors.push({
77+
kind: "error",
78+
message: `${name}: a node failed its health check`,
8079
});
8180
}
82-
// A node can be unhealthy with no message of its own (region severity
83-
// already reflects it). Synthesize one so the banner isn't alone.
84-
const hasMessage = Boolean(region.error) || Boolean(region.warnings?.length);
85-
if (!hasMessage && region.severity === "error") {
86-
errors.push({ kind: "error", message: `${name}: a node failed its health check` });
87-
} else if (!hasMessage && region.severity === "warning") {
88-
warnings.push({ kind: "warning", message: `${name}: a node reported a warning` });
89-
}
9081
}
9182
addSection(report.interfaces);
9283
return [...errors, ...warnings];

packages/netcheck/src/page.ts

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
type NetcheckReport,
66
type NetcheckSeverity,
77
} from "@repo/shared";
8-
import { emptyMessage, viewJsonAction } from "@repo/webview-shared";
8+
import { emptyMessage, pageHeader, viewJsonAction } from "@repo/webview-shared";
99

1010
import { buildConnectivityItems } from "./connectivity";
1111
import { formatLatency, formatTriState } from "./format";
@@ -22,7 +22,10 @@ export function renderPage(
2222
{ host, report }: NetcheckData,
2323
onViewJson: () => void,
2424
): HTMLElement[] {
25-
const children = [renderHeading(host), renderBanner(report)];
25+
const children = [
26+
pageHeader("Network Check", host, "deployment-host"),
27+
renderBanner(report),
28+
];
2629

2730
const issues = collectIssues(report);
2831
if (issues.length > 0) {
@@ -40,15 +43,6 @@ export function renderPage(
4043
return children;
4144
}
4245

43-
function renderHeading(host: string): HTMLElement {
44-
const header = el("header", "page-header");
45-
header.append(
46-
el("p", "eyebrow", "Network Check"),
47-
el("h1", "deployment-host", host),
48-
);
49-
return header;
50-
}
51-
5246
function renderBanner(report: NetcheckReport): HTMLElement {
5347
const severity = overallNetcheckSeverity(report);
5448
const banner = el("div", `status-banner severity-${severity}`);

packages/shared/src/netcheck/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export type NetcheckSeverity = "ok" | "warning" | "error";
99

1010
/** Health fields shared by the DERP and interfaces sections of the report. */
1111
export interface NetcheckSectionHealth {
12+
/** Parse-time rollup: errors (and, for DERP, probe/region severity) folded in. */
1213
severity: NetcheckSeverity;
1314
warnings: Array<{ code: string; message: string }>;
1415
error?: string | null;
@@ -24,7 +25,6 @@ interface NetcheckNodeReport {
2425
export interface NetcheckRegionReport {
2526
severity: NetcheckSeverity;
2627
error?: string | null;
27-
warnings?: Array<{ code: string; message: string }>;
2828
region?: {
2929
RegionID: number;
3030
RegionName: string;

packages/speedtest/src/index.ts

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { SpeedtestApi, type SpeedtestResult, toError } from "@repo/shared";
22
import {
33
emptyMessage,
44
errorMessage,
5+
pageHeader,
56
sendCommand,
67
subscribeNotifications,
78
viewJsonAction,
@@ -54,7 +55,7 @@ function renderPage(
5455
}
5556

5657
root.innerHTML = "";
57-
root.appendChild(renderHeading(workspaceId));
58+
root.appendChild(pageHeader("Speed Test", workspaceId, "workspace-id"));
5859
root.appendChild(renderSummary(data));
5960

6061
const samples = toChartSamples(data.intervals);
@@ -70,22 +71,6 @@ function renderPage(
7071
return chart.cleanup;
7172
}
7273

73-
function renderHeading(workspaceId: string): HTMLElement {
74-
const header = document.createElement("header");
75-
header.className = "page-header";
76-
77-
const eyebrow = document.createElement("p");
78-
eyebrow.className = "eyebrow";
79-
eyebrow.textContent = "Speed Test";
80-
81-
const heading = document.createElement("h1");
82-
heading.className = "workspace-id";
83-
heading.textContent = workspaceId;
84-
85-
header.append(eyebrow, heading);
86-
return header;
87-
}
88-
8974
function renderSummary(data: SpeedtestResult): HTMLElement {
9075
const summary = document.createElement("div");
9176
summary.className = "summary";

packages/webview-shared/src/dom.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,23 @@
11
/** DOM builders shared by the vanilla webviews. */
22

3+
/** A page header: an eyebrow label above a title with the given class. */
4+
export function pageHeader(
5+
eyebrow: string,
6+
title: string,
7+
titleClass: string,
8+
): HTMLElement {
9+
const header = document.createElement("header");
10+
header.className = "page-header";
11+
const eyebrowEl = document.createElement("p");
12+
eyebrowEl.className = "eyebrow";
13+
eyebrowEl.textContent = eyebrow;
14+
const titleEl = document.createElement("h1");
15+
titleEl.className = titleClass;
16+
titleEl.textContent = title;
17+
header.append(eyebrowEl, titleEl);
18+
return header;
19+
}
20+
321
/** An action bar with a "View JSON" button wired to `onClick`. */
422
export function viewJsonAction(onClick: () => void): HTMLElement {
523
const actions = document.createElement("div");

packages/webview-shared/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,4 @@ export {
1616
} from "./ipc";
1717

1818
// DOM builders shared by the vanilla webviews.
19-
export { viewJsonAction, emptyMessage, errorMessage } from "./dom";
19+
export { pageHeader, viewJsonAction, emptyMessage, errorMessage } from "./dom";

src/webviews/netcheck/types.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,6 @@ const RegionReportSchema = z
8080
.object({
8181
severity: SeveritySchema,
8282
error: z.string().nullish(),
83-
warnings: WarningsSchema,
8483
region: z
8584
.object({
8685
RegionID: z.number(),

test/unit/webviews/netcheck/types.test.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,17 @@ describe("parseNetcheckReport", () => {
100100
expect(report.interfaces.severity).toBe("error");
101101
});
102102

103+
it("escalates derp severity from a section error alone", () => {
104+
const r = structuredClone(validReport) as {
105+
derp: { severity: string; error?: string };
106+
};
107+
r.derp.severity = "ok";
108+
r.derp.error = "DERP map unreachable";
109+
110+
const report = parseNetcheckReport(JSON.stringify(r));
111+
expect(report.derp.severity).toBe("error");
112+
});
113+
103114
it("throws ZodError when a required field is missing", () => {
104115
const missingSeverity = structuredClone(validReport) as {
105116
derp: { severity?: string };

test/webview/netcheck/health.test.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,15 +49,17 @@ describe("collectIssues", () => {
4949
]);
5050
});
5151

52-
it("includes per-region errors and warnings, prefixed with the region name", () => {
52+
it("lists region errors prefixed, and section warnings only once", () => {
5353
const issues = collectIssues(
5454
report({
5555
derp: {
56+
severity: "error",
57+
// The CLI folds region/node warnings into the section list.
58+
warnings: [{ code: "ERLY", message: "high latency" }],
5659
regions: {
5760
"5": {
5861
severity: "error",
5962
error: "region unreachable",
60-
warnings: [{ code: "ERLY", message: "high latency" }],
6163
region: {
6264
RegionID: 5,
6365
RegionName: "Tokyo",
@@ -72,7 +74,7 @@ describe("collectIssues", () => {
7274

7375
expect(issues).toEqual([
7476
{ kind: "error", message: "Tokyo: region unreachable" },
75-
{ kind: "warning", code: "ERLY", message: "Tokyo: high latency" },
77+
{ kind: "warning", code: "ERLY", message: "high latency" },
7678
]);
7779
});
7880

@@ -83,7 +85,11 @@ describe("collectIssues", () => {
8385
regions: {
8486
"3": {
8587
severity: "error",
86-
region: { RegionID: 3, RegionName: "Frankfurt", EmbeddedRelay: true },
88+
region: {
89+
RegionID: 3,
90+
RegionName: "Frankfurt",
91+
EmbeddedRelay: true,
92+
},
8793
node_reports: [
8894
{
8995
can_exchange_messages: false,

0 commit comments

Comments
 (0)