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

Errors on the site pages are §5's field error, not .error-note #5092

Merged opened this

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

Merged

scout/forge:scout/5071-errors-on-the-site-pages-are-5-s-field-e → main 4 commits fork

  • Merged. scout/5071-errors-on-the-site-pages-are-5-s-field-e was merged into main as 9167603.

Review

Sign in to review this change.
2 files changed +52 −1
src/web/pages/site.ts +1 −1
@@ -359,7 +359,7 @@ function authForm(
359359 A passkey signs you in with a fingerprint, face or device PIN; there
360360 is no password to remember.
361361 </p>
362 ${passkeyNote("passkey-signup-note", errors.password)}
362 ${passkeyNote("passkey-signup-note")}
363363 <noscript><p class="muted small">Signing up needs JavaScript for the passkey.</p></noscript>`,
364364 )}
365365 ${when(
test/site-field-errors.test.ts added +51
@@ -0,0 +1,51 @@
1 /**
2 * The site pages' errors are §5's field error, not `.error-note` (#5071): the
3 * passkey-only sign-up's refusal sits in a `.field` as `.error`, and the notes
4 * the passkey script fills name `error` as the class a failure takes.
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 { serve, type Servers } from "../src/http/server.ts";
12
13 let app: App;
14 let servers: Servers;
15 let dir: string;
16 let base: string;
17
18 beforeAll(async () => {
19 dir = await mkdtemp(join(tmpdir(), "forge-site-errors-"));
20 app = createApp({ dataDir: dir, port: 0, passkeyOnlySignup: true });
21 servers = await serve(app);
22 base = servers.url;
23 });
24
25 afterAll(async () => {
26 servers.stop();
27 app.close();
28 await rm(dir, { recursive: true, force: true });
29 });
30
31 test("the sign-in note's failure class is the field error", async () => {
32 const page = await (await fetch(`${base}/signin`)).text();
33 expect(page).not.toContain("error-note");
34 expect(page).toMatch(/<div class="field"><span [^>]*id="passkey-signin-note" data-fail="error"/);
35 });
36
37 test("the sign-up note waits in a field; a refused password form shows its error at the form's head", async () => {
38 const page = await (await fetch(`${base}/signup`)).text();
39 expect(page).not.toContain("error-note");
40 expect(page).toMatch(/<div class="field"><span [^>]*id="passkey-signup-note" data-fail="error" role="status">/);
41
42 const res = await fetch(`${base}/signup`, {
43 method: "POST",
44 headers: { "content-type": "application/x-www-form-urlencoded", origin: base },
45 body: new URLSearchParams({ username: "pw", email: "", password: "a-long-passphrase" }),
46 });
47 const refused = await res.text();
48 expect(res.status).toBe(422);
49 expect(refused).not.toContain("error-note");
50 expect(refused).toMatch(/<p class="error mb" role="alert">New accounts sign up with a passkey/);
51 });

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs