generative-a11y

Testing integrations

Test patterns for adapters, runtime behavior, browser delivery, and hands-on screen-reader checks.

Test each layer separately

A runtime transcript shows what core prepared. A DOM test shows how the page changed. Test with a real screen reader to confirm what it speaks.

LayerCheckDoes not confirm
AdapterEvents and stable IDsAnnouncements
CorePrepared updates, timing, suppression, cancellationBrowser behavior
DOMBrowser results and page updatesScreen-reader speech
BrowserKeyboard, landmarks, focus, live regionsEvery screen reader and browser combination
Hands-on screen-reader testOne user workflowBehavior in every environment

Test every supported browser engine

In a checkout of this repository, browser fixtures run in Chromium, Firefox, and WebKit. They check keyboard use, stable focus, page landmarks, live regions, and browser delivery. Run these commands from the repository root:

Terminal
pnpm test:browser:install
pnpm test:browser

Test a realistic app workflow

Fixtures cover streaming text, tool updates, stopping, retrying, and browser announcements. Each assertion names the behavior it checks.

  • Run the same scenarios in all three browser engines.
  • Check that streaming and status updates do not move focus.
  • Connect each app event to the runtime update and the resulting page change.
  • Keep controls and visible status usable without the library.

Record hands-on screen-reader tests

For each release, record the date, browser, operating system, screen reader and version, workflow, expected result, actual result, and outcome. Repository validation checks every required field.

Evidence and testing note

One test describes one setup and workflow. It does not guarantee the same result with every screen reader and browser.

Use ManualClock for time-dependent behavior

Advance the test clock instead of waiting for real timers. Check diagnostics when the runtime intentionally skips an update.

streaming.test.ts
import { expect, test } from "vitest";
import { createRecorder } from "@generative-a11y/core";

test("stopping discards buffered response text", () => {
  const recorder = createRecorder({ preset: "balanced" });
  recorder.runtime.dispatch({ type: "response.started", responseId: "r1" });
  recorder.runtime.dispatch({
    type: "response.text.delta",
    responseId: "r1",
    delta: "Unfinished",
  });
  recorder.runtime.dispatch({ type: "response.interrupted", responseId: "r1" });
  recorder.clock.runUntilIdle();

  expect(recorder.transcript().map(({ text }) => text)).toEqual([
    "Response stopped.",
  ]);
  recorder.runtime.dispose();
});