Public: anyone can read this; the crew can change it.

A settings notice never draws as a red banner over a page: a test scans every page's first paint #10832

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

scout/forge:scout/10825-a-settings-notice-never-draws-as-a-red-b → main 2 commits fork 3 threads · 3 unresolved

  • Merged. scout/10825-a-settings-notice-never-draws-as-a-red-b was merged into main as ef19f92.

Review

Sign in to review this change.
1 file changed +104 −0
test/no-setting-banner.test.ts added +104
@@ -0,0 +1,104 @@
1 /**
2 * The owner's rule (#10825): a setting is one line where it belongs, never a
3 * banner over the board. Every first paint an owner lands on is read with the
4 * forge's settings in their unusual states (egress reported, every limit at
5 * its floor, a flag declared and set); nothing that draws as an alert may sit
6 * above the page's content. There is no allowlist: a healthy forge draws no
7 * red above its content, and a real failure is not a setting.
8 */
9 import { afterAll, beforeAll, expect, test } from "bun:test";
10 import { mkdtempSync, rmSync } from "node:fs";
11 import { tmpdir } from "node:os";
12 import { join } from "node:path";
13 import { createApp, type App } from "../src/app.ts";
14 import { changeCapacity } from "../src/actions/capacity.ts";
15 import { makeFlags } from "../src/core/flags.ts";
16 import { createSession } from "../src/core/sessions.ts";
17 import { serve, type Servers } from "../src/http/server.ts";
18 import { LIMITS, setLimit, type LimitKey } from "../src/settings/limits.ts";
19 import { EGRESS_REPORTED } from "../src/system/egress_line.ts";
20 import { useFlagRegistry } from "../src/web/pages/flags.ts";
outdated

These ids appear nowhere in src/. Allow only red that really exists, or assert zero.

21 import { seedAdmin } from "./helpers/admin.ts";
22 import { quietLogs } from "./helpers/globals.ts";
23
24 quietLogs();
25
26 const REASON = "test: an unusual setting";
27
28 /** Any element that draws as an alert or a danger notice. */
29 const ALERT = /<(?!script|style|svg|path)[a-z0-9]+\b[^>]*(?:role="alert"|data-tone="danger"|class="[^"]*\b(?:danger|toast|error)\b[^"]*")[^>]*>/g;
30
31 /** What sits above a page's content: the markup before `<main`, and main's own top up to the page's first heading. */
32 function aboveContent(html: string): string {
33 const main = html.indexOf("<main");
34 // The content starts at the page's heading, or at the board's grid (the board has no heading).
35 const starts = main < 0 ? [] : ["<h1", "board-grid"].map((m) => html.indexOf(m, main)).filter((i) => i >= 0);
36 return html.slice(0, starts.length === 0 ? main : Math.min(...starts));
37 }
38
39 let root = "";
40 let app: App;
41 let servers: Servers;
42 let cookie = "";
outdated

Only egress is set. Add the limits and a flag the issue names so every setting that could draw a banner is in its unusual state.

43
44 beforeAll(async () => {
45 root = mkdtempSync(join(tmpdir(), "forge-no-setting-banner-"));
46 app = createApp({ dataDir: root, port: 0, forgeRepo: "alice/forge" });
47 const made = await seedAdmin(app, { username: "alice", email: "[email protected]", password: "a-good-passphrase" });
48 if (!made.ok) throw new Error(made.error);
49 cookie = `forge_session=${createSession(app.db, made.value.id).token}`;
50 // Every limit at its floor: the tightest state a limit can be in.
51 for (const key of Object.keys(LIMITS) as LimitKey[]) {
52 const r = setLimit(app.db, key, LIMITS[key].min, made.value.id);
53 if (!r.ok) throw new Error(`${key}: ${r.error.message}`);
outdated

Scan only what precedes <main, not the whole page, and assert the main landmark exists so an empty or redirected page cannot pass.

54 }
55 const set = changeCapacity(app, made.value, "cells.egress", "report", REASON);
56 if (!set.ok) throw new Error(set.error);
57 // A flag, declared and set.
58 const flags = makeFlags({ probe: { why: "a flag the scan sets", default: false, issue: 10825 } });
59 expect(flags.set(app.db, "probe", { forge: true }, made.value.id)).toBe(true);
60 useFlagRegistry(flags);
61 servers = await serve(app);
62 });
63 afterAll(() => {
64 useFlagRegistry(undefined);
65 servers.stop();
66 app.close();
67 rmSync(root, { recursive: true, force: true });
68 });
69
70 const read = async (path: string): Promise<string> => {
71 const res = await fetch(`${servers.url}${path}`, { headers: { cookie }, redirect: "manual" });
72 expect(res.status).toBe(200);
73 return res.text();
74 };
75
76 for (const path of ["/", "/board", "/system", "/apps"]) {
77 test(`${path} draws no alert above its content because of a setting`, async () => {
78 const html = await read(path);
79 expect(html).toContain("<main");
80 // The scan must reach real content: it ends after `<main`, not at it.
81 expect(aboveContent(html).length).toBeGreaterThan(html.indexOf("<main"));
82 expect(aboveContent(html).match(ALERT) ?? []).toEqual([]);
83 });
84 }
85
86 test("the settings are in their states, and each says so on its own page with its reason", async () => {
87 const system = await read("/system");
88 expect(system).toContain(EGRESS_REPORTED);
89 const capacity = await read("/settings/capacity");
90 expect(capacity).toContain(REASON);
91 const limits = await read("/admin/limits");
92 expect(limits).toContain(String(LIMITS["crew.concurrency"].min));
93 const flags = await read("/settings/flags");
94 expect(flags).toContain("a flag the scan sets");
95 });
96
97 test("the detector sees a banner above the content and ignores an error below it", () => {
98 const page = (top: string, low: string) =>
99 `<header>${top}</header><main id="main"><h1>Board</h1>${low}</main>`;
100 const banner = '<div class="toast" data-tone="danger" role="alert">Egress is reported</div>';
101 expect(aboveContent(page(banner, "")).match(ALERT)?.length).toBe(1);
102 expect(aboveContent(page("", banner.replace("<div", "<div id=\"x\""))).match(ALERT) ?? []).toEqual([]);
103 expect(aboveContent(`<main id="main">${banner}<h1>Board</h1></main>`).match(ALERT)?.length).toBe(1);
104 });

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs