/** * The state matrix, once, for every adapter. * * `inspectWidgetState` was split so the judgement is shared and only the *driving* is per-adapter — * One adapter sets a property, another pushes a signal, a third sets an attribute, and no helper * can do all three honestly. Every adapter runs its own matrix over this one collector: a matrix * that exists for a single adapter is how a defect fixed in one and missing in the rest passes for * closed. * * This is the shared half. An adapter supplies a driver; it inherits the traversal, the report and * the divergence bookkeeping. Assertions stay with the caller, because the three suites run under * two different test runners. */ import { type MdyWidgetState } from "../widget-states.js"; import type { MdyWidgetKind } from "../catalog.js"; import type { MdyDomPartMap } from "./dom-tests.js"; /** One widget, mounted by an adapter and drivable into a state. */ export interface MdyStateFixture { readonly root: Element; /** Where each contract part is, as `inspectWidgetDom` takes it. */ parts(): MdyDomPartMap; /** The focusable, operable control, when the adapter can name it. */ control?(): Element | null; /** * The value the field currently holds, as the form reports it. * * The state matrix reads the DOM and does not need this; a canonical observation compares the * value alongside the shape, and reading it from the DOM would ask each renderer to agree about * how it *displays* a value rather than which one it holds. */ value?(): unknown; /** * Send a key to the widget, wherever focus currently is. * * The adapter owns this because only it knows where its overlay puts focus, and a key dispatched * at a guessed element tests the guess rather than the widget. Returns false when there is * nothing to send it to. */ press?(key: string): boolean; /** * Put the widget in `state`. Return false when the public API offers no way to reach it — that is * a finding in itself and is reported rather than skipped silently. */ drive(state: MdyWidgetState): boolean | Promise; /** Let the adapter's rendering settle. Asserting before this reads every state as its previous * value, which is indistinguishable from a renderer that ignored the change. */ settle(): Promise | void; dispose(): void; } export interface MdyStateMatrixOptions { /** Which kinds this adapter renders. */ readonly kinds: readonly MdyWidgetKind[]; /** Mount one widget of a kind, ready to drive. */ mount(kind: MdyWidgetKind): MdyStateFixture | Promise; } export interface MdyStateMatrixRow { readonly kind: MdyWidgetKind; readonly state: MdyWidgetState; readonly codes: readonly string[]; readonly messages: readonly string[]; } export interface MdyStateMatrixResult { readonly rows: readonly MdyStateMatrixRow[]; /** `kind × state` pairs the adapter's public API cannot reach. */ readonly undrivable: readonly string[]; /** Kinds exposing ARIA for a state they do not declare, as `kind` names. */ readonly unsupportedAria: readonly string[]; /** Every divergence, keyed `kind × state`, ready to compare against a ledger. */ readonly observed: Readonly>; /** How many pairs were actually asserted. */ readonly asserted: number; /** How many the adapter should have covered, drivable or not. */ readonly expected: number; report(label: string): string; } /** Run every declared state of every kind this adapter renders. */ export declare function collectStateMatrix(options: MdyStateMatrixOptions): Promise; /** Normalise a ledger for comparison against {@link MdyStateMatrixResult.observed}. */ export declare function normalizeStateLedger(ledger: Readonly>): Readonly>;