/**
* The axe accessibility scan, and the shape lookout keeps of what it says.
*
* Scoped to the element when one is given. The color-contrast rule is off by
* default: computed-style contrast over translucent/washed surfaces
* false-positives heavily; the AI judge sees real pixels instead.
* --axe-contrast turns it on.
*
* axe reports far more than a rule id: the standard the rule belongs to, and
* for every failing element its markup and the sentence each check wrote. A
* ticket that says "the `
` reading 'Recent activity'" is one an agent can
* act on; one that says `main > section:nth-of-type(2) > h5` is one it has to
* go and resolve first. The markup is summarised at capture, never stored
* whole: it can carry a row of somebody's data, and the folder it ends up in
* is committed with the project.
*/
import type { Page } from "playwright";
import type { DeterministicFinding } from "../types.js";
/** What lookout keeps of one failing element's markup. */
export interface ElementSummary {
tag: string;
/** A few naming attributes; nothing that could carry a value or a payload. */
attrs: Record;
/** Visible text, whitespace collapsed, at most 80 characters. */
text: string;
}
/**
* The tag, a few naming attributes, and the visible text. Everything else in
* the markup (values, sources, styles, handlers, other data attributes, nested
* elements) is dropped here, before it is written anywhere.
*/
export declare function summariseHtml(html: string): ElementSummary;
/** The slice of an axe violation lookout reads, typed structurally so the mapper needs no axe types. */
export interface AxeViolation {
id: string;
impact?: string | null;
help: string;
helpUrl: string;
description: string;
tags: string[];
nodes: {
target: unknown[];
html: string;
impact?: string | null;
failureSummary?: string;
any: {
message: string;
}[];
all: {
message: string;
}[];
none: {
message: string;
}[];
}[];
}
/** One violation as a finding: the message names the rule, the record keeps everything a fixer needs. */
export declare function axeFinding(v: AxeViolation): DeterministicFinding;
export declare function runAxe(page: Page, includeSelector: string | null, opts: {
contrast: boolean;
}): Promise;
/**
* What the scan has already filed on a route: rule id to the selectors of
* the elements it fired on. The scan runs at every form factor, and a
* violation the desktop layout showed is not news at tablet or phone; what
* is news is the node only the narrower layout shows (a hamburger button
* with no name, content a media query pushed off screen). Kept per scheme,
* because the walk is schemes outside and form factors inside.
*/
export type AxeSeen = Map>;
/** Add every (rule, node) pair a scan reported. */
export declare function rememberAxe(findings: readonly DeterministicFinding[], seen: AxeSeen): void;
/**
* The findings a narrower form factor adds: a violation is kept only when at
* least one of its nodes is new for its rule, and the kept copy names only
* those nodes, so a phone-only finding reads as what it is. The dedupe works
* on the nodes the finding kept (MAX_NODES), which is the same lossy cut the
* record has always made.
*/
export declare function newAxeFindings(findings: readonly DeterministicFinding[], seen: AxeSeen): DeterministicFinding[];
/**
* The scan for one shot. With `seen`, the route's memory for this scheme: the
* findings come back narrowed to what this form factor adds, and the memory
* grows by everything the scan saw. Without it, every violation, every time.
*/
export declare function axeForShot(page: Page, includeSelector: string | null, opts: {
contrast: boolean;
seen: AxeSeen | null;
}): Promise;
/**
* How big a control is, measured at the width where it matters.
*
* axe ships `target-size` disabled, so the ordinary scan never runs it at any
* form factor, however widely that scan reaches. Selecting it by name here is
* the only thing that makes the one rule whose whole subject is touch run at
* all. Meanwhile the visibility panel was being asked to judge "touch targets
* too small to hit reliably" by eye, while the rubric rightly forbids it the
* measurement that would make such a finding actionable.
*
* Selecting the rule by name runs it whether or not it ships enabled, which is
* the point: this is a deliberate opt-in to one rule, not a widening of the
* scan. It also accounts for spacing, so a small control with room around it
* passes and a small control crowded by its neighbours does not.
*/
export declare function runTargetSize(page: Page, includeSelector: string | null): Promise;