Skip to content

BROWSER INTERACTIONS · LESSON 18 OF 24

Keyboard and accessibility-focused checks

Verify labels, keyboard submission and focus behavior.

  • keyboard
  • accessible names
  • focus
  • accessibility

Your runnable checkpoint

Extract into a new practice folder. Each download contains tests, configuration, the complete local practice app and an offline lesson. Use Node.js 22 or 24. Each checkpoint is independent.

Install dependencies and browser binaries using the commands below. The test runner starts the practice server on port 4174. Tests target localhost, never the production EduPoly site.

1. Understand the concepts

Role and label locators encourage accessible markup, but passing those tests is not a complete accessibility audit. Keyboard behavior, focus order, contrast, screen-reader announcements and usability still need appropriate review.

Test a meaningful keyboard journey. Filling the title and pressing Enter should submit the form, add the row and return focus to the title input. This catches regressions that a mouse-only test may miss.

The lab uses native controls and visible labels. A custom component may require additional checks for ARIA roles, expanded state and keyboard interaction patterns. Do not add ARIA merely to make a locator pass if the component behavior is wrong.

Automated accessibility tools can detect some violations, but manual keyboard and assistive-technology checks remain useful. State the scope of your evidence rather than claiming full compliance from one automated scan.

2. Build and run

These are complete test files and configuration. The download also includes app/ and server.mjs; preserve their paths. Stop any manually started practice server before running the suite.

Terminal
npm install
npx playwright install chromium
npm run check
npm test

tests/lesson.spec.ts

tests/lesson.spec.ts
import { test, expect } from '@playwright/test';
test('submit a task using the keyboard', async ({ page }) => {
  await page.goto('/');
  const title = page.getByLabel('Task title', { exact: true });
  await title.fill('Keyboard task');
  await title.press('Enter');
  await expect(page.getByTestId('task-row')).toContainText('Keyboard task');
  await expect(title).toBeFocused();
  await expect(title).toHaveValue('');
});

playwright.config.ts

playwright.config.ts
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
  testDir: './tests',
  fullyParallel: true,
  forbidOnly: !!process.env.CI,
  retries: process.env.CI ? 1 : 0,
  workers: process.env.CI ? 2 : undefined,
  timeout: 30000,
  expect: { timeout: 5000 },
  reporter: [['list'], ['html', { open: 'never' }]],
  use: { baseURL: 'http://127.0.0.1:4174', trace: 'retain-on-failure', screenshot: 'only-on-failure' },
  projects: [{ name: 'chromium', use: { ...devices['Desktop Chrome'] } }],
  webServer: { command: 'node server.mjs', url: 'http://127.0.0.1:4174', reuseExistingServer: false, timeout: 15000 }
});

package.json

package.json
{
  "name": "edupoly-playwright-keyboard-and-accessibility-checks",
  "version": "1.0.0",
  "private": true,
  "scripts": {
    "test": "playwright test",
    "test:headed": "playwright test --headed",
    "test:ui": "playwright test --ui",
    "check": "tsc --noEmit",
    "report": "playwright show-report",
    "serve": "node server.mjs"
  },
  "devDependencies": {
    "@playwright/test": "1.56.1",
    "typescript": "5.9.3",
    "@types/node": "22.20.5"
  }
}

tsconfig.json

tsconfig.json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "CommonJS",
    "moduleResolution": "Node",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "types": [
      "node"
    ],
    "noEmit": true
  },
  "include": [
    "tests/**/*.ts",
    "pages/**/*.ts",
    "playwright.config.ts"
  ]
}

.gitignore

.gitignore
node_modules/
playwright-report/
test-results/
playwright/.auth/
.env

3. Trace the code and check the result

  1. press exercises a keyboard event rather than clicking the button.
  2. toBeFocused checks the resulting focus state.
  3. The test proves this keyboard path, not overall accessibility compliance.

Verify your result

  • Enter submits the form.
  • Focus returns to the cleared title input.

4. Practice independently

Check that the Add task button has a useful accessible name.

Show the explanation

Use await expect(page.getByRole("button", { name: "Add task", exact: true })).toHaveAccessibleName("Add task"). Also inspect the focus indicator manually.

When something goes wrong

If a browser executable is missing, run npx playwright install chromium in the project folder. If port 4174 is occupied, stop your manual practice server. Read assertion call logs for mismatched locators or expectations. Use npm run report to inspect failures; do not add fixed sleeps or broad resets to hide them.

Read the related official Playwright documentation ↗