Appendix A. ABNF Grammar for Steps
Permalink to Appendix A. ABNF Grammar for StepsThis appendix is normative.
; A step entry is a YAML mapping with one key. After the YAML layer, what
; remains for the parser is a (keyword, rest) pair. Each keyword is bound to
; its own argument grammar: a step is well-formed only when the keyword and
; the rest belong to the same alternative below.
step = "locate:" SP locate-rest
/ "focus:" SP focus-rest
/ "enter:" SP enter-rest
/ "select:" SP select-rest
/ "deselect:" SP deselect-rest
/ "activate:" SP activate-rest
/ "toggle:" SP toggle-rest
/ "verify:" SP verify-rest
/ "wait:" SP wait-rest
/ "wait_for:" SP wait-for-rest
/ "navigate:" SP navigate-rest
/ "screenshot:" SP screenshot-rest
/ "keyboard:" SP keyboard-rest
/ "type:" SP type-rest
/ "scroll:" SP scroll-rest
/ "hover:" SP hover-rest
/ "hover_out:" SP hover-out-rest
/ "note:" SP note-rest
/ "audit:" SP audit-rest
/ "contrast:" SP contrast-rest
/ "lang_check:" SP lang-check-rest
/ "read_image:" SP read-image-rest
/ "sr_says:" SP sr-says-rest
/ "viewport:" SP viewport-rest
/ "anchor:" SP anchor-rest
; The keyword and rest sets, named for the step-descriptor in
; `sr_says ... after` (which takes a bare keyword) and for the §5.1 rule that
; rejects a keyword outside the set. They do not license a step on their own:
; `focus: 2000` pairs a keyword with another keyword's rest and is not
; derivable from `step`.
keyword = "locate" / "focus" / "enter" / "select" / "deselect"
/ "activate" / "toggle" / "verify" / "wait" / "wait_for"
/ "navigate" / "screenshot" / "keyboard" / "type" / "scroll"
/ "hover" / "hover_out" / "note" / "audit" / "contrast"
/ "lang_check" / "read_image" / "sr_says" / "viewport"
/ "anchor"
rest = locate-rest / focus-rest / enter-rest / select-rest
/ deselect-rest / activate-rest / toggle-rest / verify-rest
/ wait-rest / wait-for-rest / navigate-rest / screenshot-rest
/ keyboard-rest / type-rest / scroll-rest / hover-rest
/ hover-out-rest / note-rest / audit-rest / contrast-rest
/ lang-check-rest / read-image-rest / sr-says-rest
/ viewport-rest / anchor-rest
; --- Targets ---
;
; The accessible name is optional for role-based targets (curated tokens
; and the `role "X"` form). `field`, `text`, `id`, and `data-*` still
; require their argument. `within` and `inside` are synonyms.
target = role-and-name [ SP scope-kw SP target ]
scope-kw = "within" / "inside"
role-and-name = curated-role [ SP [ "contains" SP ] qstring ]
/ "role" SP qstring [ SP "name" SP [ "contains" SP ] qstring ]
/ "field" SP [ "contains" SP ] qstring
/ "text" SP [ "contains" SP ] qstring
/ "id" SP qstring
/ data-role SP qstring
/ "self"
curated-role = ; ARIA 1.2 widget, composite, structure, landmark,
; live-region, and window roles (excluding `field` and
; `text`, which appear above with required names).
"link" / "button" / "checkbox" / "radio" / "switch"
/ "option" / "select" / "combobox" / "searchbox" / "textbox"
/ "slider" / "spinbutton" / "progressbar" / "meter"
/ "scrollbar" / "separator" / "heading" / "image" / "dialog"
/ "alertdialog" / "tab" / "tabpanel" / "treeitem" / "menu"
/ "menubar" / "menuitem" / "radiogroup" / "tablist"
/ "listbox" / "tree" / "treegrid" / "grid" / "article"
/ "feed" / "figure" / "group" / "list" / "listitem"
/ "table" / "row" / "rowgroup" / "cell" / "gridcell"
/ "columnheader" / "rowheader" / "toolbar" / "tooltip"
/ "banner" / "navigation" / "main" / "complementary"
/ "contentinfo" / "region" / "search" / "form" / "alert"
/ "log" / "status"
data-role = "data-" 1*data-char
data-char = ALPHA / DIGIT / "-" / "_"
; The `self` token is only resolvable inside a `scope.for_each` body.
; A processor MUST raise a runtime error if `self` is used elsewhere.
; --- scope.for_each grammar ---
;
; The accessible name is optional. The expression may itself be scoped
; with `within`/`inside` to limit iteration to descendants of a parent.
for-each-expr = "criteria" SP 1*VCHAR
/ target
; A quoted string may hold any Unicode scalar value other than DQUOTE, the
; backslash and the control characters, C0 and DEL (§5.3). The processor
; preserves the code
; points as authored and applies no normalisation.
qstring = DQUOTE *( escape / passthrough / qchar ) DQUOTE
qchar = %x20-21 / %x23-5B / %x5D-7E / %x80-D7FF / %xE000-10FFFF
escape = %x5C DQUOTE / %x5C %x5C ; \" and \\ denote one character
; A backslash before any other character denotes both characters literally:
; "a\nb" is a, backslash, n, b — not a newline.
passthrough = %x5C qchar
; --- Per-keyword grammars ---
locate-rest = target [ SP "level" SP 1*DIGIT ]
focus-rest = target
enter-rest = target SP "value" SP qstring
*( SP enter-mod )
enter-mod = "type_slowly" SP boolean
/ "clear_first" SP boolean
; `force` is only valid on `option` targets, and suppresses the post-click
; aria-selected assertion (§5.6.4).
select-rest = target [ SP "option" SP qstring ] [ SP "force" SP boolean ]
deselect-rest = target
; `force` and `via keyboard` are mutually exclusive (§5.6.7).
activate-rest = target *( SP activate-mod )
activate-mod = "via" SP "keyboard"
/ "new_tab" SP boolean
/ "force" SP boolean
; The attribute clause takes NO predicate: toggle asserts the value changed,
; it does not compare it to anything.
toggle-rest = target [ SP "attribute" SP qstring ]
verify-rest = "url" SP qstring
/ "title" SP qstring
/ "download" SP qstring
/ "live_region" [ SP live-qualifier ] SP qstring
/ "alert" SP qstring
/ "field_error" SP ( qstring / role-and-name )
/ "text" SP [ "contains" SP ] qstring
/ "heading" SP qstring [ SP scope-kw SP target ]
[ SP "level" SP 1*DIGIT ]
/ iter-summary-rest
/ state-verify SP target
/ "count" SP target SP "is" SP 1*DIGIT
/ target SP "has_value" SP qstring
/ target SP "attribute" SP qstring SP attr-predicate
/ target
state-verify = "visible" / "hidden" / "enabled" / "disabled"
/ "checked" / "unchecked" / "focus"
; --- Attribute predicates ---
attr-predicate = "is" SP qstring
/ "is_or_absent" SP qstring
/ "present"
/ "absent"
/ "starts_with" SP qstring
/ "matches" SP qstring
/ "references_existing_id"
/ "within_range_of" SP qstring SP qstring
; --- iteration_summary terminal verify ---
live-qualifier = "role" SP qstring / "politeness" SP politeness
politeness = %s"polite" / %s"assertive"
iter-summary-rest = "iteration_summary." iter-summary-field SP "is" SP 1*DIGIT
iter-summary-field = "failed" / "passed" / "total" / "inapplicable"
anchor-rest = 1*( ALPHA / DIGIT / "_" / "-" )
wait-rest = duration
duration = 1*DIGIT [ "ms" / "s" ]
wait-for-rest = target
navigate-rest = qstring / 1*char
screenshot-rest = DQUOTE screenshot-name DQUOTE / screenshot-name
screenshot-name = 1*( ALPHA / DIGIT / "." / "_" / "-" )
; A screenshot name labels one capture beneath `screenshot_dir`; it is not a
; path. The quoted spelling carries the same restriction as the bare one --
; the quotes are not part of the name. `.` and `..` match `screenshot-name`
; but MUST also be rejected (§5.6.12), which the grammar cannot state.
; Every key-spec MUST name a key the processor can press. A token that does not
; is rejected at parse time rather than compiled to a press that fails at run
; time; `type` is the verb for literal text.
keyboard-rest = key-spec *( SP key-spec )
key-spec = 1*( ALPHA / DIGIT / "+" )
type-rest = 1*char
scroll-rest = [ "to" SP ] target
hover-rest = target
hover-out-rest = target
note-rest = qstring / 1*char
audit-rest = "page" [ SP "level" SP audit-level ]
/ target [ SP "level" SP audit-level ]
audit-level = DQUOTE ( "A" / "AA" / "AAA" ) DQUOTE
contrast-rest = target [ SP "level" SP contrast-level ]
contrast-level = DQUOTE ( "AA" / "AAA" ) DQUOTE
lang-check-rest = "page" / target
read-image-rest = target [ SP read-image-expectation ]
[ SP "lang" SP qstring ]
read-image-expectation = "has_text" SP qstring
/ "matches" SP qstring
sr-says-rest = sr-says-payload
[ SP "after" SP step-descriptor ]
[ SP "within" SP sr-says-duration ]
sr-says-payload = sr-says-expectation
/ "role" SP qstring SP sr-says-expectation
sr-says-expectation = qstring / "matches" SP qstring
sr-says-duration = duration
viewport-rest = viewport-preset / viewport-size
viewport-preset = "mobile" / "tablet" / "desktop" / "reflow"
viewport-size = 1*DIGIT "x" 1*DIGIT
step-descriptor = keyword [ SP target ]
; --- Lexical primitives ---
boolean = "true" / "false"
SP = %x20
DQUOTE = %x22
ALPHA = %x41-5A / %x61-7A
DIGIT = %x30-39
char = ALPHA / DIGIT / SP / "-" / "_" / "/" / "." / ":" / "?" / "&" / "=" / "%"
The grammar above is the post-tokenization shape; YAML parsing handles the outer
layer. The reference implementation's parser is hand-written, and there are five
places where a hand-written parser is easily more permissive than this grammar.
A conforming processor MUST reject all five: an unrecognized modifier, which is
an error rather than a warning (§7.5); a boolean other
than true or false, which MUST NOT be coerced to false; a "via" argument
other than keyboard; a modifier belonging to one keyword after another
keyword's target, since the per-keyword productions above (locate-rest,
enter-mod, activate-mod, …) apply to every modifier and not to force
alone; and input following wait-rest, wait-for-rest, scroll-rest or
lang-check-rest, which are closed productions with nothing optional left at
the end even though those four verbs never reach a modifier list.