Package limn.testing.a11y
@NullMarked
package limn.testing.a11y
What a widget of each accessibility shape owes a screen reader, as named cases a test runs.
Each shape has a contract and a subject: ToggleContract over a
ToggleSubject, ValueContract over a
ValueSubject, and the same for text, rows, grids, menus, a leaf action
and a popup owner. The subject builds the widget fresh for every case and answers through the
widget's API what the published tree cannot say; the contract's cases returns
ContractCases, each a name and a check, which a test turns into its
framework's dynamic tests in one line. The toolkit's own components are held to these same
contracts.
-
ClassDescriptionOne named check of a per-shape contract: a rule the shape holds every widget of that shape to, with the decision it comes from in its name, run over a fresh widget each time.The check; it throws
AssertionErrorwhen the rule does not hold.What every widget of theGRIDshape owes a reader beyond what its rows do, as named cases over aGridSubject: the container is of theGRIDshape and carries aTableFacetwhose column count is the header's; the four invariants hold, unfocused and focused; the header cells sit at row −1, one per column, in order, each carrying the direction its column is sorted in andPRESSexactly where the column sorts;PRESSon a sortable header sorts by it and the facet says so, and a second press changes the direction the facet reports; a data cell is aCELLat a row and a column inside the counts;FOCUSon a cell that offers it moves the cursor onto that cell, which is then the one active node, and selects nothing; a header cell keeps its id across a sort.A widget of theGRIDshape asGridContractdrives it: a table or a calendar, whose container carries aTableFacet, whose header cells sit at row −1 and whose data cells carry their place.What every widget of theLEAF_ACTIONshape owes a reader, as named cases over aLeafActionSubject: it is a leaf that offersPRESS; the verb reaches the widget's own handler exactly once; a disabled leaf withdraws the verb and a press reaches nothing; and the node keeps its id across a press.A widget of theLEAF_ACTIONshape as the leaf contract sees it: a pressable leaf the contract can build fresh and count the presses of through the widget's own handler.What every widget of theMENUshape owes a reader, as named cases over aMenuSubject: a menu bar and a popup menu under the same rules, which until this were tested in each widget's own file.A widget of theMENUshape asMenuContractdrives it: a menu bar, whose rows are its titles, or a popup menu, whose rows are the root column's.What a row is, which decides the verbs the contract expects on it.What every widget of thePOPUP_OWNERshape owes a reader, as named cases over aPopupOwnerSubject: it saysHAS_POPUPbefore anything has happened (a disclosure, which opens in place, says it nowhere); its open state is the facet's and the API's; closed, it offersEXPANDand notCOLLAPSE;EXPANDopens it and something appears in the tree, described where it lives; open, it never offersEXPAND, and offersCOLLAPSEunless a layer above owns the input, in which case it offers nothing; a secondEXPANDchanges nothing; closing through the API bringsEXPANDback; and the node keeps its id across the round trip.A widget of thePOPUP_OWNERshape as the popup-owner contract sees it: something that opens a popup, which the contract can build fresh, closed, and read the open state of through the widget's own API.What every widget of theROWSshape owes a reader, as named cases over aRowsSubject.A widget of theROWSshape as the rows contract sees it: something that publishes a selection and members of it, which the contract can build fresh, read the selection and the cursor of through the widget's own API, and ask three things about that the published tree cannot say.How the widget reaches a row outside its box.What every widget of theTEXTshape owes a reader, as named cases over aTextSubject: the facet's string is the API's;SET_TEXTwith a text replaces it and with anything else changes nothing;SET_CARETwith a collapsed range moves the caret andSET_SELECTIONwith a range selects it, each read back off the facet; and the node keeps its id as the text changes.A widget of theTEXTshape as the text contract sees it: an editable text the contract can build fresh, holding a short string, and read back through the widget's own API.What every widget of theTOGGLEshape owes a reader, as named cases over aToggleSubject: the facet is the API's state and the verb is offered exactly where the flip is;TOGGLEflips it, heard once by the widget's own listener and announced, where announced, from the user, and flips it back; a disabled toggle withdraws the verb and moves for nothing; and the node keeps its id as it flips.A widget of theTOGGLEshape as the toggle contract sees it: something that publishes one node that is on or off, which the contract can build fresh, read the state of through the widget's own API, and ask whether the flip is on offer.What every widget of theVALUEshape owes a reader, as named cases over aValueSubject: the number, its bounds and its step are the API's;INCREMENTandDECREMENTmove by the step the facet names, clamped at the bounds, each heard once by the widget's own listener and announced, where announced, from the user;SET_VALUEtakes a finite number and moves nothing for anything else; a read-only value offers no verb and moves for none; a disabled one withdraws its verbs and moves for none; and the node keeps its id as the number moves.A widget of theVALUEshape as the value contract sees it: something that publishes one number in a range, which the contract can build fresh, read the number of through the widget's own API, and ask whether the number can be set at all.