Home / Services / Playwright accessibility testing

Playwright accessibility testing, inside the suite you already run

A control with no accessible name, and a contrast ratio that fails, become failing tests in the specs your team already reviews, before either one ships.

Short answer

We wire automated accessibility checks into your Playwright suite: axe scans in the specs you already have, role-based locators that fail when an element has no accessible name, and one shared configuration. They run on every pull request. We do not certify WCAG conformance and do not issue a VPAT — a scan finds what a machine can decide.

If you do not yet know what is in the suite these checks would go into, the audit reads it and answers what should happen first. It is optional, it is billed by the hour, and the minimum of one full-time engineer for one month does not apply to it.

Who this is for

Somebody has handed your team an accessibility requirement, and it has landed on the test suite because that is where checks live.

  • A procurement questionnaire with an accessibility page in it, due before a renewal, and nobody in the company owns the answer.
  • A designer or a developer who keeps finding the same missing labels by hand, one screen at a time, after the code has shipped.
  • A team that ran axe once, got three hundred violations, closed the tab, and has not opened it since.

If there is no Playwright suite yet, these checks have nowhere to live. Building one is Playwright test automation, and this work goes on top of it.

Who it is not for. A team that needs a conformance report to hand to a customer or a regulator is not buying this. That report is written by people who assess a product against the standard: accessibility audit specialists, and people who operate the product with assistive technology. We do not do that work, and it is better to hear it now than after a scan comes back green and somebody reads the green as an answer.

What you get

axe scans in the specs you already have

The scan runs inside the spec structure your suite already has, against the states those specs already reach. It is not a separate scanning job with its own schedule, its own dashboard and nobody's name on it.

One configuration every spec shares

A fixture that builds the scan the same way everywhere: the tag list, the exclusions, and any rule that has been switched off. Turning a rule on or off is one edit in one file and one review, instead of thirty specs that have drifted apart.

Role and name assertions in the suite

Specs written so a control with no accessible name fails the test that touches it. These assertions need no library: Playwright ships matchers for roles, accessible names, descriptions and error messages, and they sit beside your ordinary expectations.

A CI position, written down

What fails a build, what is recorded without failing one, where a run's evidence lands so the failure message stays readable, and who opens a red run at 9am. You decide each of those; we write them into the config and the pipeline so the decision sits in the repository.

The existing violations, as a list

Every violation the first full run returns, written down as an item: the rule id, the element it fired on, a ticket number and an owner. A list somebody can work through, which a count is not.

How it works

  1. Scoping

    We read two things: the suite, and the requirement you were handed. Which specs exist, which states of the product they reach today, and what the contract clause or the questionnaire asks for. You give us repository access, that clause in its own words, and one engineer who can say which flows a customer would notice.

  2. One configuration, one flow

    The fixture and the first scanned flow go through your pull request process. The tag list and the first exclusions get argued over a working test, which is a shorter argument than the same one held in a document.

  3. Across the suite

    Scans are added to the specs that already reach the states worth scanning, with the role and name assertions beside them. The first full run returns everything the application carried before anyone was looking, and that is when the list gets written and its items get owners. Your side pays in review time: each batch is a pull request and it needs a reviewer who knows what the screen is for.

  4. Handover

    The fixture, the list and the CI position are in your repository and your team can change any of them without us. They come back with the part of your requirement none of this reaches, named item by item, so nobody reports coverage they do not have.

What this looks like in the repository

Two blocks. Both were written against Playwright 1.62 and run on the 1.62.1 an unpinned install resolved to, with @axe-core/playwright 4.13.0 pulling axe-core 4.13.0 underneath it.

The first is a spec that already existed: it walks a customer to checkout, asserts the order was placed, and then scans the state it reached. The scan is three lines at the end of a test that is there for another reason.

import { test, expect } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';

test('a customer can place an order from the checkout page', async ({ page }) => {
  await page.goto('/index.html');
  await page.getByRole('link', { name: 'Checkout' }).click();
  await page.getByRole('button', { name: 'Place order' }).click();

  await expect(page.getByText('Order EX-8891 placed')).toBeVisible();

  const results = await new AxeBuilder({ page })
    .withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa'])
    .analyze();

  expect(results.violations).toEqual([]);
});
Playwright 1.62.1 · @axe-core/playwright 4.13.0 · TypeScript · tests/checkout.spec.ts · fails with two violations against the demo checkout below

The demo is a checkout page with two planted defects and the run reports both: button-name, for an icon button drawn as an SVG with no label on it, and color-contrast, for a delivery estimate set in grey on white. One is a missing attribute, the other is arithmetic, and that is the kind of thing a machine decides. How the package is wired in, with the result object taken apart, is the article on running axe in a Playwright test.

The second block needs no library at all:

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

test('the checkout page exposes its summary and its controls by name', async ({ page }) => {
  await page.goto('/checkout.html');

  const summary = page.locator('.summary');

  await expect(summary).toHaveRole('region');
  await expect(summary).toHaveAccessibleName('Order summary');

  const edit = page.locator('.toolbar button').first();

  await expect(edit).toHaveAccessibleName('Edit delivery address');
});
Playwright 1.62.1 · TypeScript · tests/checkout-names.spec.ts · passes

Both locators find their element by where it sits in the markup, so each assertion checks a property instead of repeating its own search term. Swap that section for a div in a redesign and the panel stops being a named region, which fails here on the commit that did it.

The signal you already own

A suite written with role-based locators is already doing part of this work, and nobody decided that it should. A call like getByRole('button', { name: 'Place order' }) goes through the browser's accessible name computation to find its element, so a call that finds nothing is usually reporting a control nobody could have asked for by name either. The suite says so as a red test on the pull request that broke it, months before a scan would have been run and further still before an audit.

Playwright's documentation draws the boundary in the same paragraph that recommends the locators: "Note that role locators do not replace accessibility audits and conformance tests, but rather give early feedback about the ARIA guidelines." A scan reads the whole rendered state. A locator reads the one control it was looking for.

So the first thing we read in your repository is how the specs find things. A suite already written in role locators has half of this convention in it, and most of the work left is the scans. A suite of CSS paths and ids has a conversation about the specs to have first. The locator family, and how to choose inside it, is argued at length.

Where this stops

This is the automated layer of accessibility work and it is not the whole of it, so the boundary gets drawn item by item.

  • We do not certify conformance. No VPAT, no accessibility conformance report, no accessibility statement, no letter anybody can forward. We deliver checks in a suite and the evidence of what they found.
  • We do not do keyboard walkthroughs, screen-reader passes or assessment against WCAG. Firm86 builds and runs the automated layer, and that is the whole offer. Those three are work for an accessibility specialist and for people who use the product with assistive technology.
  • Playwright does not drive a screen reader. What the tooling reads is the accessibility tree the browser exposes. What a screen reader user hears is that tree after a particular reader, in a particular mode, in a particular browser, has interpreted it. No assertion we write reaches the second thing.
  • No legal advice, on this page or in anything we hand you. We do not tell you what a law or a standard requires of your product. axe carries section508 and EN-301-549 tags beside the WCAG ones and a run can be scoped to them; naming a tag decides which rules execute and finds nothing about your obligations.
  • Browsers only. Nothing here covers a native iOS or Android application, VoiceOver or TalkBack on a real device, or a store build. Playwright emulates a mobile browser: a viewport, a user agent, touch input. A responsive web app is covered at a phone-sized screen, and the app itself is not.
  • Not a load test and not a security review. Neither is offered here in any framework.

A scan tells you about the failures a machine can decide on its own, on the states somebody navigated to, at the moment it ran. Whether the product works for the person it excluded is not a question it was asked.

What it costs

Nothing about the terms changes because the subject is accessibility. Engineers are billed hourly, from $50 an hour, depending on where the engineer sits. The minimum engagement is one full-time engineer for one month, and this work takes no exemption from it.

Scope is counted in states of the product: how many a scan has to reach, and how many of those a spec already reaches without new work. How much of the suite is written in role locators moves it, and so does the number of pipelines running any of it. The first full run is a line of its own, because a hundred items have to be read, ticketed and given an owner, one at a time, by a person.

Read first

Questions

Does this make our product WCAG compliant?

No. We do not certify conformance and we do not issue a VPAT. The W3C's Web Accessibility Initiative, who publish WCAG, put the limit as a category and not as a quantity: "Tools cannot check all accessibility aspects automatically. Human judgement is required", and "Web accessibility evaluation tools can not determine accessibility, they can only assist in doing so." You are buying a suite that decides the failures a machine can decide, on the states it scans, every time the suite runs. Whether the product is usable by the person it excluded is a different question, and nothing in the suite answers it.

Do you issue a VPAT or an accessibility conformance report?

No, and we do not write an accessibility statement or a letter you can forward to a customer either. You get checks in your repository, the configuration they run under, and the evidence of what they found on a given run. A conformance report is written by people who assess a product against the standard: accessibility audit specialists, and people who operate the product with assistive technology. If your requirement needs one, buy it from them.

We already use getByRole everywhere. Do we still need axe?

Yes, because the two answer different questions. A role locator reaches an element through the accessible name computation, so a locator that comes up empty is usually reporting a control a user could not have referred to either: one element, at the moment it changed. An axe run reads the whole rendered state at once, including the contrast arithmetic and the ARIA attributes no spec touches. Playwright's documentation draws the line itself: "Note that role locators do not replace accessibility audits and conformance tests, but rather give early feedback about the ARIA guidelines."

We have three hundred existing violations. Does the build go red on day one?

Not unless you decide it should. Failing on all three hundred teaches the team to skip the check; never failing at all teaches them to ignore it. The setting between the two is a red build for anything new, with the three hundred sitting in one file where every entry carries a rule id, a ticket number and a name, so somebody is accountable for how long the file is. That is a recommendation about your configuration and not a policy of ours. You take the position; we wire up the one you take.

Which WCAG version and level do you test against?

Whichever one your requirement names, and that answer has to come from you. A tag list filters axe's rules and does nothing else: the four the documentation names, wcag2a, wcag2aa, wcag21a and wcag21aa, run the rules carrying those tags, and axe ships section508 and EN-301-549 tags beside them, so a run can be scoped to those as well. Which of them applies to your product is not something a tag list can tell you. That comes from your contract, your customer or your lawyer, and it is the input. Send us the clause you were handed and we will scope the run to it.

What does the requirement you were handed actually say?

Paste the clause, the procurement question or the customer email, in the words it arrived in, and say who is waiting on the answer. That is enough for us to tell you which part of it a suite can carry, which part only a person working through the product will find, and what the first configuration would be scoped to. We do not need an audit to start. If the suite the checks would go into does not exist yet, say that instead, because it changes the order of the work.