Skip to content
PerchAntonPublic

About

Test keyboard journeys and focus transitions in Playwright. Dialog-first accessibility behavior checks.

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

KeyTrail

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.

CI 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.

Try the packaged example

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:package

On 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.1

Try the React portal

The 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/html

See reading reports for the deliberately failing process and its not_run observations. React remains an example dependency, not a library dependency.

Write a contract

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.

Test a tab list

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.

Test form error recovery

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.

Require complete results in CI

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.

What the result means

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.

When to use it

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.

Development and evidence

pnpm check
pnpm exec playwright test --workers=1
pnpm exec playwright show-report

Tests 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.

Measured overhead

KeyTrail overhead against equivalent native Playwright checks

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.

About

Test keyboard journeys and focus transitions in Playwright. Dialog-first accessibility behavior checks.

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages