AI Tools Compared

AI tools in 2026 generate Playwright keyboard navigation tests by analyzing your component structure and producing test cases that verify Tab order through interactive elements, Escape key closing modal behavior, and Arrow keys navigating dropdown options. Providing clear component documentation and specific keyboard interaction requirements upfront produces immediately usable tests that cover both basic navigation and edge cases like focus trapping and Shift+Tab backward navigation.

Why Keyboard Navigation Testing Matters

Keyboard-only users navigate websites using Tab, Enter, Space, Arrow keys, and Escape. Without proper testing, interactive elements become inaccessible. Playwright’s testing framework combined with AI assistance makes generating keyboard tests straightforward.

Getting Started with AI-Generated Tests

Prerequisites

Ensure you have Playwright installed:

npm init playwright@latest

Generating Test Cases with AI

When prompting AI to generate keyboard navigation tests, provide context about your application’s interactive elements. Include the HTML structure or component details.

Effective AI Prompt Example:

Generate Playwright tests for keyboard navigation on a modal component.
Tests should verify:
1. Tab moves focus through all interactive elements
2. Enter/Space activates buttons
3. Escape closes the modal
4. Focus returns to trigger element after close
5. Arrow keys navigate within the modal

Generated Test Example

AI produces tests similar to this:

import { test, expect } from '@playwright/test';

test.describe('Modal Keyboard Navigation', () => {
  test.beforeEach(async ({ page }) => {
    await page.goto('/components/modal');
  });

  test('Tab moves focus through all interactive elements', async ({ page }) => {
    await page.click('#open-modal');
    const modal = page.locator('.modal');
    await expect(modal).toBeVisible();

    const closeButton = page.locator('.modal .close-btn');
    const confirmButton = page.locator('.modal .confirm-btn');
    const cancelButton = page.locator('.modal .cancel-btn');

    await expect(closeButton).toBeFocused();
    await page.keyboard.press('Tab');
    await expect(confirmButton).toBeFocused();
    await page.keyboard.press('Tab');
    await expect(cancelButton).toBeFocused();
  });

  test('Escape closes the modal', async ({ page }) => {
    await page.click('#open-modal');
    const modal = page.locator('.modal');
    await expect(modal).toBeVisible();

    await page.keyboard.press('Escape');
    await expect(modal).not.toBeVisible();
  });

  test('Enter activates the confirm button', async ({ page }) => {
    await page.click('#open-modal');
    await page.keyboard.press('Tab'); // Focus close button
    await page.keyboard.press('Tab'); // Focus confirm button
    await page.keyboard.press('Enter');

    // Verify action was triggered
    await expect(page.locator('.modal')).not.toBeVisible();
  });
});

Testing Complex Navigation Patterns

AI can generate tests for dropdown keyboard interactions:

test('Arrow keys navigate dropdown options', async ({ page }) => {
  const dropdown = page.locator('.custom-dropdown');
  await dropdown.click();

  const options = page.locator('.dropdown-option');
  await expect(options.first()).toBeFocused();

  await page.keyboard.press('ArrowDown');
  await expect(options.nth(1)).toBeFocused();

  await page.keyboard.press('ArrowUp');
  await expect(options.first()).toBeFocused();

  await page.keyboard.press('Enter');
  await expect(dropdown).toHaveText('Selected Option');
});

Form Navigation

Generate form tests with AI assistance:

test('Form fields keyboard navigation', async ({ page }) => {
  await page.goto('/forms/new-user');

  // Tab through form fields
  await expect(page.locator('#username')).toBeFocused();
  await page.keyboard.press('Tab');
  await expect(page.locator('#email')).toBeFocused();
  await page.keyboard.press('Tab');
  await expect(page.locator('#password')).toBeFocused();

  // Fill using keyboard only
  await page.keyboard.type('testuser');
  await page.keyboard.press('Tab');
  await page.keyboard.type('test@example.com');
  await page.keyboard.press('Tab');
  await page.keyboard.press('Enter');
});

Improving AI Test Quality

Provide Sufficient Context

AI generates better tests when you include:

Review and Refine

AI-generated tests require human review:

  1. Verify focus order matches visual layout

  2. Check skip links are properly tested

  3. Ensure roving tabindex works correctly

  4. Validate modal trap focus behavior

Customizing for Your Framework

React, Vue, and Angular handle keyboard events differently. Specify your framework when prompting AI:

Generate Playwright keyboard tests for a React auto-complete component
using react-keyboard-event-handler patterns.

Automating Test Generation Workflow

Create a script to generate tests from component documentation:

// generate-keyboard-tests.js
const { chromium } = require('playwright');

async function generateTests(componentHtml) {
  const prompt = `Given this HTML structure, generate Playwright
    keyboard navigation tests:\n\n${componentHtml}`;

  // Send to AI API and return generated tests
  const response = await aiClient.complete(prompt);
  return response.tests;
}

module.exports = { generateTests };

Run the generator:

node generate-keyboard-tests.js --component modal --output tests/

Common Pitfalls to Avoid

Measuring Test Coverage

Track keyboard navigation coverage:

test('keyboard navigation coverage report', async ({ page }) => {
  const keyboardEvents = [];
  page.on('keyboard', event => keyboardEvents.push(event));

  // Perform keyboard navigation
  await page.goto('/interactive-page');
  await page.keyboard.press('Tab');
  await page.keyboard.press('Tab');
  await page.keyboard.press('Enter');

  console.log('Keyboard events:', keyboardEvents);
});

Testing ARIA Roles and Focus Management Together

Keyboard navigation tests become significantly more valuable when combined with ARIA role verification. A component that moves focus correctly but uses wrong ARIA roles still fails accessibility audits.

Playwright’s accessibility tree lets you assert ARIA roles alongside keyboard interactions:

test('Dialog has correct ARIA roles and keyboard focus', async ({ page }) => {
  await page.goto('/components/dialog');
  await page.click('#open-dialog-btn');

  const dialog = page.locator('[role="dialog"]');
  await expect(dialog).toBeVisible();
  await expect(dialog).toHaveAttribute('aria-modal', 'true');
  await expect(dialog).toHaveAttribute('aria-labelledby');

  // Verify focus is inside the dialog
  const focusedElement = await page.evaluate(() => document.activeElement?.tagName);
  expect(['BUTTON', 'INPUT', 'A'].includes(focusedElement!)).toBe(true);

  // Escape should close and return focus to trigger
  await page.keyboard.press('Escape');
  await expect(dialog).not.toBeVisible();
  await expect(page.locator('#open-dialog-btn')).toBeFocused();
});

Ask the AI to generate combined ARIA and keyboard tests by providing your component’s ARIA specification alongside the HTML structure. This produces tests that verify both the mechanical keyboard behavior and the semantic meaning — a much higher confidence bar than either test type alone.

Testing Focus Trapping in Modals

Focus trapping — where Tab cycles through elements inside a modal rather than leaving it — is one of the most commonly implemented and most commonly broken accessibility patterns. Playwright makes it straightforward to test:

test('Modal traps focus within container', async ({ page }) => {
  await page.goto('/components/modal');
  await page.click('#open-modal');

  const modal = page.locator('.modal');
  await expect(modal).toBeVisible();

  // Get all focusable elements in the modal
  const focusableSelectors = 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])';
  const focusableElements = modal.locator(focusableSelectors);
  const count = await focusableElements.count();

  // Tab through all elements — the last one should cycle back to the first
  for (let i = 0; i < count; i++) {
    await page.keyboard.press('Tab');
  }

  // After count+1 tabs, focus should be back on the first focusable element
  await page.keyboard.press('Tab');
  await expect(focusableElements.first()).toBeFocused();

  // Shift+Tab from first element should go to last (backward trap)
  await page.keyboard.press('Shift+Tab');
  await expect(focusableElements.last()).toBeFocused();
});

When prompting AI to generate focus trap tests, specify the exact number and type of focusable elements in your modal. AI-generated tests that assume a fixed element count break when the component changes. Better to have the test dynamically count focusable elements as shown above.

Integrating Keyboard Tests into Your CI Pipeline

Keyboard navigation tests slow down fast unit test suites because they require a real browser. Structure your test configuration to run keyboard tests in parallel and separate from your unit tests:

// playwright.config.ts
import { defineConfig } from '@playwright/test';

export default defineConfig({
  projects: [
    {
      name: 'keyboard-nav',
      testMatch: '**/*keyboard*.spec.ts',
      use: { browserName: 'chromium' },
    },
    {
      name: 'keyboard-nav-firefox',
      testMatch: '**/*keyboard*.spec.ts',
      use: { browserName: 'firefox' },
    },
  ],
  workers: 4,
  retries: 1,
});

Run keyboard navigation tests across at least Chromium and Firefox. Keyboard event handling differs between browsers — a test that passes in Chromium may fail in Firefox due to subtle differences in focus management behavior. This cross-browser gap is where many accessibility regressions hide.

Add the keyboard test suite to your pull request checks with a required status check. Make regressions visible to reviewers by including a brief summary of keyboard test failures in your PR description template.

Built by theluckystrike — More at zovo.one