Check dialog, tabs and form-error keyboard journeys in your existing Playwright tests. Declare the expected focus order; KeyTrail presses keys, checks the result and attaches a report showing the first mismatch and the observations that could not run.
Dialog API · Tabs API · Form errors API · CI guard · Read reports · Limits · Performance · Release checklist
Status: dialogs, tabs and form errors, 0.1.0-alpha.5 source/package candidate. No npm
publication or GitHub release yet. This milestone implements custom ARIA dialogs, fixed tab lists and
declared form-error recovery, plus a CI reporter that requires complete results for every discovered journey test and attempt.
Tab to trigger → Enter → initial focus → Tab cycle → Shift+Tab cycle → Escape
↓
focus returns to trigger
If the dialog closes but leaves focus on the page body:
D07 FOCUS_MISMATCH expected=open-profile observed=body
The corrected component passes the same contract. The runnable example includes both versions; its intentionally broken fixture is our teaching example, not an upstream bug.
Node.js 24 and pnpm 11.25.0 are used for development. The tested Playwright peer version is 1.64.1, with its Chromium 156.0.8078.4. Compatibility.
git clone https://github.com/PerchAnton/keytrail.git
cd keytrail
pnpm install --frozen-lockfile --ignore-scripts
pnpm --dir examples/react-portal install --frozen-lockfile --ignore-scripts
pnpm exec playwright install chromium
pnpm build
pnpm test:packageOn Linux, install Chromium's system dependencies with
pnpm exec playwright install --with-deps chromium when needed.
The package check installs the actual archive into two fresh offline consumers.
It checks ESM/CJS imports and types, then runs the vanilla dialog, tabs and form
examples and the React portal example. Corrected journeys also run with the
installed CI guard; a separate defective React process must exit 1 while preserving
its HTML/JUnit reports and incomplete attachment. Dependency installation and the
React lockfile preparation use the registry. The actual consumer installation then
uses --offline --frozen-lockfile in a separate empty directory; browser tests need
only the local fixture server.
It leaves artifacts/keytrail-0.1.0-alpha.5.tgz, a verification record and SHA256SUMS.
Install that archive into an existing Playwright project:
npm install --save-dev /absolute/path/to/keytrail-0.1.0-alpha.5.tgz @playwright/test@1.64.1The React example uses the same dialog contract in StrictMode, with the dialog mounted outside the app root. Compare correct behavior, wrong initial focus and lost return focus, then inspect the first mismatch:
pnpm exec playwright show-report artifacts/consumers/react-portal/reports/teaching/htmlSee reading reports for the deliberately failing process and its
not_run observations. React remains an example dependency, not a library dependency.
import { test } from '@playwright/test';
import { assertDialogJourney } from 'keytrail';
test('profile dialog', async ({ page }, testInfo) => {
await page.goto('/profile'); // Your application and prepared synthetic state.
const trigger = { id: 'open-profile', locator: page.getByRole('button', { name: 'Edit profile' }) };
const name = { id: 'display-name', locator: page.getByLabel('Display name') };
const close = { id: 'close-profile', locator: page.getByRole('button', { name: 'Close' }) };
await assertDialogJourney(page, testInfo, {
id: 'profile-dialog', trigger,
dialog: { id: 'profile', locator: page.getByRole('dialog') },
openKey: 'Enter', initialFocus: name, tabOrder: [name, close],
closeKey: 'Escape', returnFocus: trigger,
});
});Use one journey per test, allow time for setup and reporting (the example uses 45 s),
and let JourneyAssertionError fail application tests. Do not catch and ignore it.
The standalone example provides the actual HTML and configuration;
/profile above is an application route, not a service supplied by this package.
assertTabsJourney checks horizontal/vertical arrow navigation, wraparound, automatic
or manual activation, optional Home/End and the final Tab exit. Manual activation
checks that focus moves while selection stays unchanged until Enter or Space.
See the Tabs API and flow diagram and the runnable manual-tabs example.
assertFormErrorsJourney checks the invalid submit, error text and field relationships,
the declared focus policy, summary links and keyboard correction. It confirms that all
declared errors clear, without including typed values or message text in its own JSON.
Invalid submit → messages + IDREF → focus → optional summary links
→ keyboard correction → errors cleared
See the Form errors API and flow diagram and the working/broken email example.
Add the reporter last and run the named project as a required CI job:
reporter: [['list'], ['keytrail/ci-guard', { projectName: 'journey' }]],
projects: [{ name: 'journey', testMatch: '**/*.journey.spec.ts' }],Skipped tests, missing or incomplete reports, and failed attempts that later pass on retry fail the guard. A successful test without a successful journey is insufficient. See setup, diagnostics, boundaries and the flow diagram.
| Status | Meaning |
|---|---|
pass |
The declared expectation was observed. |
fail |
An expectation did not match, such as the return focus. |
blocked |
A limit or unsupported/ambiguous context prevented a reliable check. |
not_run |
A planned observation was not attempted after an earlier interruption. |
journey-result.json is attached to Playwright's report. Every planned observation
remains in the result. A failure takes precedence over blocking; coverage.complete
still remains false if any checks are blocked or not run. Counts refer to individual
observations, not broad accessibility rules. JSON schema.
Evaluate KeyTrail when several dialogs, tab lists or forms need the same declared keyboard checks and consistent diagnostics. Keep ordinary Playwright assertions when a small native test is clearer or your component needs behavior outside the supported contract.
This is a behavioral test helper. Passing does not establish WCAG conformance, screen-reader behavior, pointer blocking or complete accessibility. It does not replace axe-core, manual testing or your component tests. It has no dependency on our separate interaction-check, edit-session or RefMint projects.
pnpm check
pnpm exec playwright test --workers=1
pnpm exec playwright show-reportTests cover the specified correct/defective dialogs, detached and replaced nodes, unsupported contexts, bounded entry, scenario deadlines and safe own reports. The verification record separates completed checks from the full 0.1 acceptance criteria. The performance protocol compares equivalent fixed workloads. The full-suite verifier records repeatability and attachment isolation. This checkpoint has 45 full sequential rounds (6,885 executions), accepted by the maintainer, plus a matching 153-test four-worker run. The original 100-round target was not completed. Real-world adoption has not been established.
All three synthetic workloads meet the 20% target on the recorded runner. Each has 30 measured pairs after five warmups, with the same actions and semantic checks. Manual tabs include required observation windows. See method, limitations and raw samples; this is not a general speed or accessibility claim.
Original code, rules and examples are licensed under Apache-2.0.