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

j/k, enter, esc and ? on capacity, with test/keys-<page>.test.ts #10812

Merged opened this

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

Merged

scout/forge:scout/10800-j-k-enter-esc-and-on-capacity-with-test → main 1 commit fork

  • Merged. scout/10800-j-k-enter-esc-and-on-capacity-with-test was merged into main as f3a19a1.

Review

Sign in to review this change.
1 file changed +79 −0
test/keys-capacity.test.ts added +79
@@ -0,0 +1,79 @@
1 /**
2 * The real /settings/capacity page and the shell's keys. Its j/k rows are the
3 * settings sections (chrome `settingsLayout`); the setting rows are forms
4 * (Save, Dry run, Put back) with nowhere to open, so they are not rows.
5 */
6 import { afterAll, beforeAll, expect, test } from "bun:test";
7 import { mkdtemp, rm } from "node:fs/promises";
8 import { tmpdir } from "node:os";
9 import { join } from "node:path";
10 import { createApp, type App } from "../src/app.ts";
11 import { forgeHttp, type ForgeHttp } from "../src/http/server.ts";
12 import { createSession } from "../src/core/sessions.ts";
13 import { seedAdmin } from "./helpers/admin.ts";
14 import { quietLogs } from "./helpers/globals.ts";
15 import { mainOf, shellPage } from "./helpers/shell-page.ts";
16
17 quietLogs();
18
19 let dir = "";
20 let app: App;
21 let http: ForgeHttp;
22 let html = "";
23
24 beforeAll(async () => {
25 dir = await mkdtemp(join(tmpdir(), "forge-keys-capacity-"));
26 app = createApp({ dataDir: dir, port: 0 });
27 http = forgeHttp(app);
28 const admin = await seedAdmin(app, { username: "alice", email: "[email protected]", password: "correct-horse-battery" });
29 if (!admin.ok) throw new Error(admin.error);
30 const cookie = `forge_session=${createSession(app.db, admin.value.id).token}`;
31 const res = await http.handle(new Request("http://forge.test/settings/capacity", { headers: { cookie } }));
32 expect(res.status).toBe(200);
33 html = mainOf(await res.text());
34 });
35 afterAll(async () => {
36 app.close();
37 await rm(dir, { recursive: true, force: true });
38 });
39
40 test("the sections are the only rows; the setting forms are not", () => {
41 expect(html).toContain('class="row-fields"');
42 const settings = html.slice(html.indexOf('class="list setting-list"'));
43 expect(settings).not.toContain("data-nav");
44 expect(html).toContain("data-nav");
45 });
46
47 test("j/k walk the sections from the current one, Enter opens, Esc has nowhere to go, fields keep their keys", async () => {
48 const p = await shellPage("/settings/capacity", html + "<input id=f>");
49 const rows = [...p.doc.querySelectorAll("[data-nav]")] as any[];
50 const at = rows.findIndex((r) => r.getAttribute("aria-current") === "page");
51 expect(at).toBeGreaterThanOrEqual(0);
52
53 p.key("j");
54 expect(p.doc.activeElement).toBe(rows[Math.min(at + 1, rows.length - 1)]);
55 const target = p.doc.activeElement.getAttribute("href");
56 p.key("k");
57 expect(p.doc.activeElement).toBe(rows[at]);
58 p.key("j");
59 p.key("Enter");
60 expect(p.clicks).toEqual([target]);
61 expect(JSON.parse(p.win.sessionStorage.getItem("nav-from")!)).toMatchObject({ list: "/settings/capacity", href: target });
62
63 const f = p.doc.getElementById("f") as any;
64 f.focus();
65 const before = p.clicks.length;
66 p.key("j", f);
67 p.key("Enter", f);
68 expect(p.doc.activeElement).toBe(f);
69 expect(p.clicks.length).toBe(before);
70
71 const same = await shellPage("/settings/capacity", html, { "nav-from": JSON.stringify({ list: "/settings/capacity", to: "/settings/capacity", href: target }) });
72 same.key("Escape");
73 expect(same.assigned).toEqual([]);
74 });
75
76 test("`?` opens the help from this page", async () => {
77 const p = await shellPage("/settings/capacity", html);
78 expect(p.key("?").defaultPrevented).toBe(true);
79 });

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs