Context
The Badge is the user's primary interaction surface on the web: it detects the field, attaches a shadow-DOM panel, generates the password, writes it to the input, handles save/rotate banners. It is 938 lines and has zero unit tests; only detect.ts (the field-finder) is unit-tested.
Problem / Observation
- extension/src/content/Badge.tsx (938 lines):
writeInput, showSaveBanner, showRotateBanner, attachBadge, Badge component lifecycle, UnlockForm, ReadyView, focus/key handlers.
- tests/detect.test.ts exists and is solid.
tests/e2e/content.spec.ts covers a few end-to-end smokes via Playwright but doesn't drive the panel through every state machine transition.
- No vitest test of
writeInput's React-controlled-component invariants (calling the prototype setter, firing both input and change events) — a regression here breaks every controlled-input login form on the web silently.
- No test of the
findHost traversal that walks shadow boundaries in click-outside handling.
- No test of the rotate banner's heuristic for which input is current vs. new (hint matching on
autocomplete, name, id containing "current" or "old").
Proposed approach
Add tests/badge.test.ts (or split into tests/badge-*.test.ts) with happy-dom fixtures. Specifically:
writeInput calls the prototype setter AND fires input + change events (verify with spies).
showSaveBanner removes any previous banner before adding a new one (DOM assertion).
- The rotate banner's "current vs new" split: given two password inputs, one with
name="currentPassword" and one with id="newPassword", the old value is written to the first and the new value to the second.
findHost returns the host element when given a node nested 3 shadow roots deep (mirrors what happens with iframe + shadow root sites).
Acceptance criteria
Context
The Badge is the user's primary interaction surface on the web: it detects the field, attaches a shadow-DOM panel, generates the password, writes it to the input, handles save/rotate banners. It is 938 lines and has zero unit tests; only
detect.ts(the field-finder) is unit-tested.Problem / Observation
writeInput,showSaveBanner,showRotateBanner,attachBadge,Badgecomponent lifecycle,UnlockForm,ReadyView, focus/key handlers.tests/e2e/content.spec.tscovers a few end-to-end smokes via Playwright but doesn't drive the panel through every state machine transition.writeInput's React-controlled-component invariants (calling the prototype setter, firing bothinputandchangeevents) — a regression here breaks every controlled-input login form on the web silently.findHosttraversal that walks shadow boundaries in click-outside handling.autocomplete,name,idcontaining "current" or "old").Proposed approach
Add
tests/badge.test.ts(or split intotests/badge-*.test.ts) with happy-dom fixtures. Specifically:writeInputcalls the prototype setter AND firesinput+changeevents (verify with spies).showSaveBannerremoves any previous banner before adding a new one (DOM assertion).name="currentPassword"and one withid="newPassword", the old value is written to the first and the new value to the second.findHostreturns the host element when given a node nested 3 shadow roots deep (mirrors what happens with iframe + shadow root sites).Acceptance criteria
src/content/Badge.tsxabove 50% line coverage (currently ~0%).