@codefusion-cc/testing
v0.1.1
Published
What the apps' tests share: Playwright checks that no control nobody can see takes clicks and that every Tab lands where it shows
Maintainers
Readme
@codefusion-cc/testing
What the apps' tests share. Today: two Playwright checks for a kind of bug no unit test and no axe scan
catches, UI that nobody can see but that still takes clicks or focus. A closed menu hidden only by
opacity: 0 keeps catching the clicks that land on it; a closed drawer moved off the screen keeps its links
in the Tab order; a collapsed section keeps its fields.
npm install -D @codefusion-cc/testingIn a Playwright test
import { expect, test } from '@playwright/test'
import { expectNoInvisibleClickTargets, expectVisibleTabStops } from '@codefusion-cc/testing/playwright'
test('the sidebar hides what it closes', async ({ page }) => {
await page.goto('/')
await page.getByRole('button', { name: 'Appearance' }).click()
await page.keyboard.press('Escape')
// No control nobody can see takes a click anywhere in the viewport.
await expectNoInvisibleClickTargets(page)
// Every Tab from the top of the page lands where it shows; the stops come back for more checks.
const stops = await expectVisibleTabStops(page)
expect(stops.length).toBeGreaterThan(0)
})Run them on each screen and in each state that opens or closes something: a menu, a drawer, a dialog, a section. Both wait for the page's animations and transitions to finish before they look.
What counts
- Nobody can see an element whose effective opacity (its own times its ancestors') is below 0.05.
findInvisibleClickTargetslooks at interactive elements (links, buttons, form controls,tabindex, interactive ARIA roles,onclick, and elements that give themselvescursor: pointer) in the viewport, including open shadow roots, and reports each one the browser's hit testing gives a click to. Something visible on top of it,display: none,visibility: hiddenandpointer-events: noneall make it fine. A native input, select or textarea made transparent over the visible label or wrapper it stands for (a styled checkbox, a file picker) is fine where that label or wrapper is.walkTabStopspresses Tab from the top of the page until focus leaves the page or comes back to an element it was on (the end, or a focus trap), and gives each stop its problems:transparent: nobody can see it;no-size: 2px or less across, like a visually hidden element (a skip link that shows on focus is fine);offscreen: outside the viewport, or clipped away by an ancestor that hides its overflow;covered: something else is on top of it, like a modal's backdrop.
expectNoInvisibleClickTargets and expectVisibleTabStops throw a message listing what they found:
Clicks land on 3 elements nobody can see:
div.sidebar > div.dropdown > div.dropdown-content is transparent, and takes clicks on:
div.dropdown > div.dropdown-content > button "Graphite" at (40, 316)
…
Focus lands where nobody sees it on 1 of 24 tab stops:
Tab 7: nav.drawer > a "Logs" (offscreen)Options
ignore: CSS selectors of elements (with their contents) to leave alone, for an exception the check cannot tell from a bug; say why next to it.settleTimeout: how long to wait for animations to finish, in ms (default 3000). Endless ones, like a spinner, are never waited for.maxStops(tab walk): a walk that has not ended after this many stops throws (default 1000).
Limits
- Only what is in the viewport is checked for clicks; scroll or resize to check the rest.
- Stops inside an iframe are not walked.
- WebKit on macOS skips links on Tab unless its preference says otherwise: walk in Chromium or Firefox.
