Scope the profile-menu assertion to the menu itself
This commit was merged in pull request #50.
This commit is contained in:
@@ -39,7 +39,9 @@ test("app shell renders sidebar, topbar and the content slot", async () => {
|
||||
// Sign out is a CSRF-guarded POST form (state change, not a GET link), carrying the token.
|
||||
assert.match(html, /<form class="menu-item-form" method="post" action="\/logout">/);
|
||||
assert.match(html, /<input type="hidden" name="_csrf" value="tok\.sig" \/>/);
|
||||
assert.doesNotMatch(html, /<button class="menu-item" type="button"/); // every item in the menu goes somewhere
|
||||
// …and it is the profile menu's only control: nothing dead sits beside it.
|
||||
const profileMenu = html.slice(html.indexOf('<div class="menu-pop'), html.indexOf("</details>"));
|
||||
assert.deepEqual(profileMenu.match(/<button/g), ["<button"]);
|
||||
|
||||
// Branding, document title, and the inlined icon sprite (so <use> resolves).
|
||||
assert.match(html, /Acme Console/);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
## Unfinnished work
|
||||
|
||||
- [ ] The little menues, like when choosing language or clicking my username, they do not dissapear when clicking outside them, I must click the original trigger or choose something. See if there are more modern ways of handling this with HTML and CSS. I think there is a modal-thing or something?
|
||||
- [ ] Decide whether the profile dropdown still earns a dropdown. With the dead Profile link gone it holds one item, Sign out, behind a click — and its "Signed in as X" head only repeats the name and email the trigger already shows. Either put Sign out in the footer directly, or give the menu a second reason to exist. Overlaps the outside-click item above. Raised by review 2026-08-05.
|
||||
- [ ] When copy+paste the verification code from the email, it doesn't work because it does not trim whitechars around the code in the form. It should trim automatically.
|
||||
- [ ] Guard against the double-clicked submit, without client-side JavaScript. The README's non-technical persona double-clicks a button that doesn't respond instantly, so a second identical POST is an expected event, not misuse — today it creates two users, mints two recovery codes, or registers two OAuth2 clients. Constraints: HTML/CSS only (no client JS — priority: zero-JS spine), and it must not break an action that is *legitimately* repeatable (an increase-by-one button is not a duplicate, it is two increments). Sketch to evaluate: a CSS-only affordance so the second click has nothing to hit (`:active`/`:focus` state, or the submit visually and semantically settling), paired with the host recognising a duplicate on the server — same session, same route, same payload, within a short window — and then logging it and dropping the second rather than replaying it. Open questions: what identifies "the same submission" (a one-time token minted into each rendered form is stronger than hashing the payload, and the CSRF plumbing already mints per-request tokens), how long the window is, where the record lives given the app is stateless (in-memory like the revoke denylist, or push it to the upstream the plugin already writes to), and how a plugin declares a route as repeatable — an opt-out on the route, or opt-in per form. Raised 2026-08-04 with the personas.
|
||||
- [ ] Decide the caching contract for rendered pages. Responses now carry `Vary: Accept-Language` (they content-negotiate), but nothing sets `Cache-Control` — so a shared cache in front of the app has no instruction, and a signed-in page is not marked `private`. Pre-existing, surfaced by the i18n review 2026-08-03: either set the headers deliberately (public pages cacheable, gated pages `private, no-store`) or record in AGENTS.md that the reverse proxy owns this.
|
||||
|
||||
Reference in New Issue
Block a user