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

Settings › Cloud accounts, the sealed provider credentials, speaks the home page's language #9879

Merged opened this

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

Merged

mason/forge:mason/9878-settings-cloud-accounts-the-sealed-provi → main 2 commits fork

  • Merged. mason/9878-settings-cloud-accounts-the-sealed-provi was merged into main as 4ba4dbe.

Review

Sign in to review this change.
3 files changed +34 −9
docs/design/areas/settings.md +2 −2
@@ -105,8 +105,8 @@ the row at every width, so nothing squeezes beside the title on a 390 px phone.
105105
106106 Settings › Cloud (`src/web/pages/cloud.ts`) adds no component class. As on the home page it is headings over rows: Cloud accounts,
107107 Add an account and Every read are each a `<section class="settings-block">` with an `<h2>`; each account is a `row()` in
108 `<ul class="list setting-list">`; with none, one plain sentence says how to add one. `/settings/cloud/audit` is unchanged.
109 `test/cloud-page-shape.test.ts` holds the shape.
108 `<ul class="list setting-list">`; with none, one plain sentence says how to add one. `/settings/cloud/audit` is one `settings-block`, Today's vendor calls,
109 its rows in a `.list` or one plain sentence when there are none (#9878). `test/cloud-page-shape.test.ts` holds the shape.
110110
111111 Settings › Telemetry (`telemetryPage()` in `src/web/pages/telemetry.ts`, #9806) adds no class: two `<section class="settings-block">` blocks, Telemetry grants (one plain paragraph, then each agent a `row()` with its Grant/Revoke form in a `.setting-list`) and Reads; an empty list is one plain sentence. Pinned by `test/telemetry-blocks.test.ts`.
112112
src/web/pages/cloud.ts +7 −7
@@ -30,7 +30,7 @@ import { notFoundPage, settingsLayout, signInFirst } from "../chrome.ts";
3030 import { field, readForm } from "../form.ts";
3131 import { html, type Html } from "../html.ts";
3232 import { flashTo, page } from "../layout.ts";
33 import { btn, card, confirmAction, icon, row, time } from "../ui.ts";
33 import { btn, confirmAction, icon, row, time } from "../ui.ts";
3434 import { accountSections } from "./origins.ts";
3535
3636 const BACK = "/settings/cloud";
@@ -223,9 +223,10 @@ export function cloudAuditPage(ctx: Ctx): Response {
223223 body: html`<div class="page-head"><h1>Cloud audit</h1></div>
224224 ${settingsLayout(
225225 accountSections("cloud", { admin: true }),
226 card(
227 rows.length === 0
228 ? html`<p class="muted small card-body">No vendor call today. Rows are kept 90 days.</p>`
226 html`<section class="settings-block">
227 <h2>Today's vendor calls</h2>
228 ${rows.length === 0
229 ? html`<p class="muted small">No vendor call today. Rows are kept 90 days.</p>`
229230 : html`<ul class="list">
230231 ${rows.map((r) =>
231232 row({
@@ -235,9 +236,8 @@ export function cloudAuditPage(ctx: Ctx): Response {
235236 meta: html`<span class="mono">${r.target}</span> · ${r.bytesOut} bytes out · ${time(r.at)}`,
236237 }),
237238 )}
238 </ul>`,
239 { head: html`${icon("eye")}<h2>Today's vendor calls</h2>`, pad: false },
240 ),
239 </ul>`}
240 </section>`,
241241 "Account settings",
242242 )}`,
243243 }));
test/cloud-page-shape.test.ts +25
@@ -46,3 +46,28 @@ test("an admin sees headed sections and no card; with no account one sentence sa
4646 expect(main).toContain("No cloud account is sealed yet. Add one below");
4747 expect(main).toContain('action="/settings/cloud"');
4848 });
49
50 test("today's vendor calls are a headed block, no card; with none one sentence says so (#9878)", async () => {
51 const page = await (await fetch(`${servers.url}/settings/cloud/audit`, { headers: { cookie } })).text();
52 const main = page.slice(page.indexOf("<main"), page.indexOf("</main>"));
53 expect(main).toContain('<section class="settings-block">');
54 expect(main).toContain("<h2>Today's vendor calls</h2>");
55 expect(main).not.toContain('class="card');
56 expect(main).not.toContain("card-body");
57 expect(main).toContain("No vendor call today. Rows are kept 90 days.");
58 });
59
60 test("a signed-in non-admin gets the forge's one 404 for the vendor calls", async () => {
61 const made = await createUser(app.db, { origin: "seed", trusted: true, username: "bob", email: "[email protected]", password: "a-good-passphrase" });
62 if (!made.ok) throw new Error(made.error);
63 const r = await fetch(`${servers.url}/signin`, {
64 method: "POST",
65 redirect: "manual",
66 headers: { origin: servers.url },
67 body: new URLSearchParams({ username: "bob", password: "a-good-passphrase" }),
68 });
69 const bob = (r.headers.get("set-cookie") ?? "").split(";")[0] ?? "";
70 const res = await fetch(`${servers.url}/settings/cloud/audit`, { headers: { cookie: bob } });
71 expect(res.status).toBe(404);
72 expect(await res.text()).not.toContain("vendor call");
73 });

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs