Skip to content

test: drive useFocusTrap's Shift+Tab wrap and other-key return end to end - #1081

Merged
castrojo merged 1 commit into
mainfrom
quality/test-focus-trap-keyboard-e2e
Oct 6, 2026
Merged

castrojo merged 1 commit into
mainfrom
quality/test-focus-trap-keyboard-e2e

Conversation

@hivecommons-hive

Copy link
Copy Markdown
Contributor

Test Improvement

Adds two end-to-end cases to tests/e2e/community-people.spec.js — the spec
that already owns the PersonDialog lightbox — driving two arms of
src/components/hooks/useFocusTrap.js that no spec in tests/e2e/ had ever
executed in a browser:

new case arm it drives
Shift+Tab from the first focusable element wraps to the last useFocusTrap.js:38-40 — if (event.shiftKey && document.activeElement === first) { event.preventDefault(); last.focus(); }. No e2e spec pressed Shift+Tab at all, so forward tabbing short-circuited on event.shiftKey and the comparison was never evaluated.
a key that is neither Escape nor Tab leaves the dialog alone useFocusTrap.js:33 — if (event.key !== 'Tab') return;. The handler is bound to document and sees every keystroke on the page, but nothing asserted that an ordinary key leaves the dialog visible and focus unmoved.

tests/e2e/accessibility.spec.js cannot reach either arm: axe-core scans
static markup and never drives the keyboard. A dialog that leaked focus to the
page behind it on Shift+Tab — WCAG 2.1 2.1.2 No Keyboard Trap / 2.4.3
Focus Order
— would ship green today.

No production code changes; one test file.

Evidence

  • Unit (npm run test:unit:coverage, TZ=UTC node tests/tools/coverage-report.mjs,
    node v26.10.0, local, base 900592b): src/components/hooks/useFocusTrap.js
    100.00% lines / 100.00% regions. This is an e2e-only gap, so the rules put
    it at medium priority rather than high.

  • E2E (local npm run build:e2e:coverage, e2e-coverage-run.mjs init,
    npm run test:e2e:coverage with E2E_COVERAGE_DIR/E2E_COVERAGE_RUN_ID,
    seal --status passed, e2e-coverage-report.mjs --input <dir> --build build):

    useFocusTrap.js regions src files
    base 900592b, 298 passed 62.96% 466 / 377 / 80.90%
    this branch, 300 passed 64.29% 467 / 379 / 81.16%

    The base figure reproduces CI job End-to-end coverage (workflow
    Validate repository, run
    37164361552) at
    468 / 379 / 80.98% to within one region. npm run test:unit:coverage:check
    and npm run check:format both pass on this branch.

A caveat worth stating plainly

The two arms are now provably executed — Shift+Tab landing focus on the
last focusable element can only happen if last.focus() ran — yet the region
keys 33:31:33:38, 38:24:40:21 and 40:18:43:22 stay at zero in the report,
while a newly emitted key covering the same source (33:6:33:38, count 3)
appears beside them. That is the drifted-span defect already recorded in #1079
and #1066, observed here on a third component; it is why the gate moves by
+2 regions rather than by the five lines involved. It is not a reason to
withhold the tests, and this PR deliberately does not touch
tests/tools/e2e-coverage-report.mjs, which #1040, #1051, #1070 and #1072 are
already rewriting.

Coordination

This PR claims exactly one file, tests/e2e/community-people.spec.js, and one
production surface, src/components/hooks/useFocusTrap.js. Neither is touched
by any open PR: #1058 claims tests/e2e/interactions.spec.js, #1060
tests/e2e/member-directory.spec.js, #1075
tests/e2e/member-directory-freshness.spec.js, #1054
tests/e2e/data-fixtures.spec.js (and src/lib/profile-links.mjs), and #1034 /
#1078 tests/e2e-data-fixtures.test.mjs. Branch cut from a fresh
origin/main at 900592b.

Related Issue

Closes #1080


Filed by quality agent (hold-gated mode). Human review required.

— hive: agent=quality backend=copilot model=claude-opus-5 copilot=1.0.88

… end

tests/e2e/community-people.spec.js owns the PersonDialog lightbox, which
mounts src/components/hooks/useFocusTrap.js. Two of that hook's arms were
never executed in a browser by any spec in tests/e2e/:

  * useFocusTrap.js:38-40, the backwards wrap. No e2e spec pressed
    Shift+Tab at all, so forward tabbing short-circuited on event.shiftKey
    and never evaluated document.activeElement === first. A dialog that
    leaked focus to the page behind it on Shift+Tab is a WCAG 2.1.2 /
    2.4.3 defect that shipped green.

  * useFocusTrap.js:33, the early return for every key that is neither
    Escape nor Tab. The listener is on document, so it observes every
    keystroke on the page; nothing asserted that an ordinary key leaves
    the dialog and its focus alone.

tests/e2e/accessibility.spec.js cannot reach either: axe-core scans
static markup and does not drive the keyboard.

Both components report 100% unit line and region coverage, so this is an
e2e-only gap. End-to-end regions for useFocusTrap.js move from 62.96% to
64.29%, and src files from 466/377/80.90% to 467/379/81.16%.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: quality <quality@hive.kubestellar.io>
@hivecommons-hive hivecommons-hive Bot added the hold label Oct 5, 2026
@hivecommons-hive

Copy link
Copy Markdown
Contributor Author

Important

Held for human review by the hive's ACMM level gate.

This PR was opened by the "quality" agent while Hive policy required a human checkpoint for that agent. Non-outreach agents are held at ACMM L3–L5; the outreach agent is always held because it publishes project-facing communication.

Hive will automatically remove the hold label once current policy no longer requires a level hold for "quality". If this is an outreach PR, a human must review it and remove the label.

@castrojo
castrojo added this pull request to the merge queue Oct 6, 2026
Merged via the queue into main with commit 4b3b08b Oct 6, 2026
7 checks passed
@mrbobbytables
mrbobbytables deleted the quality/test-focus-trap-keyboard-e2e branch October 7, 2026 15:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[quality] useFocusTrap's Shift+Tab wrap and non-Tab/non-Escape key arms are never driven end to end

1 participant