UCDL Accessibility Use Case Definition Language
Contents

Accessibility Use Case Definition Language (UCDL) and Runner

4. Use Case Document Format

This section is normative.

4.1 File extension and encoding

Use case documents MUST be encoded as UTF-8 YAML 1.2 ([YAML]). The RECOMMENDED file extension is .uc.yaml. Processors that operate on directories SHOULD discover use case documents by globbing for **/*.uc.yaml.

Each document is a single YAML mapping with the following keys.

Key Type Required Description
id non-empty string yes Unique identifier within a project.
title non-empty string yes Human-readable title.
type enum: positive, negative yes Intent of the case. Has no effect on resolution; see below.
description string no Free-form description.
extends string (id of another use case) no Inherits from the referenced case. See §7.3.
preconditions array of strings no (defaults to []) Human-readable preconditions; carried into reports.
start_location string (URL) yes for non-extension cases URL navigated to before step 1.
expected_result string yes for non-extension cases Statement of the expected outcome; carried into reports.
data mapping no (defaults to {}) Template variables. Values MAY be nested objects.
scope object no Iteration scope. See §5.8.
steps array of step entries yes for non-extension cases without steps_override Ordered list. See §5.
steps_override object no Used in extension cases. See §7.3.
before array of step entries no Setup steps run before steps. See §4.6.

A single document defines exactly one use case. A directory MAY contain any number of documents.

A non-extension case in the table above is a document with no extends field. The conditional requirements key on the presence of extends, not on type: Appendix C.2 is type: negative and inherits its start_location from its parent. type records what the case is for — a happy path or an error path — and does not change how the document is resolved. A document with extends MAY omit all three conditional fields and inherit them (§7.3).

A document without extends that omits one of them is not a conforming document, and a processor MUST reject it. Accepting it produces a case that resolves to an empty start_location and zero steps and then scores pass, because nothing in it failed — "pass" meaning nothing was evaluated.

A processor MUST also reject a top-level field this specification does not define, rather than ignoring it. A misspelled steps_overide: is otherwise dropped in silence, and the document reads as an extension case while running as its parent.

4.3 The steps_override object

Used in extension cases to replace parent steps from a given point onward.

Key Type Required Description
from_anchor string one of these two Name of an anchor: step in the parent to splice at. Preferred.
from_step positive integer one of these two 1-based index in the parent's resolved step list.
condition string no Why the extension branches here. Carried into reports.
steps array of step entries yes Replacement steps. Either spelling of §4.5.

Exactly one of from_anchor and from_step MUST be supplied; both, or neither, is a parse error.

from_anchor is preferred because from_step couples a child to a position in someone else's file. Inserting one step into a parent re-targets every child that extends it, and nothing errors: the override still applies cleanly, just at the wrong point, and the extension tests something nobody wrote. A label survives edits to the surrounding flow.

An unresolvable from_anchor MUST be an error naming the anchors the parent does define. The from_step value MUST NOT exceed parent.steps.length + 1.

Anchors do not occupy a step index (§5.6.20), so a parent MAY be annotated with anchors before any of its children are converted, without re-targeting the ones still using from_step.

data is a mapping of names to values. Values MAY be nested mappings, in which case dot notation is used to address them from steps (see §7.4). Values MAY be strings, numbers, booleans, or null; arrays are permitted but addressing into them is not defined.

A step entry is a YAML mapping with exactly one key. The key is the step keyword. The value is a string conforming to the grammar for that keyword in §5.

steps:
  - locate: button "Sign Up"
  - activate: button "Sign Up"
  - verify: url "/welcome"

A value that YAML resolves to a number rather than a string — wait: 2000 unquoted — is accepted: the processor converts it to its decimal string form and parses it as if it had been quoted.

A step entry with zero keys, more than one key, or a key that is not a recognized keyword MUST cause the document to be rejected.

A bare string keyword: rest is the second spelling of the same entry, and a processor MUST accept it wherever it accepts the mapping form. It is normalised to the mapping form before parsing — split at the first colon, the keyword trimmed, one matching pair of surrounding quotes stripped from the rest, as YAML would have done for a mapping value — so the two spellings cannot parse differently. All three step lists — steps, before (§4.6) and steps_override.steps (§4.3) — accept both. The string form is useful where the mapping form's YAML quoting gets in the way, as it does for a keyboard: 'Escape' override; the mapping form is the one this document uses in its examples.

before is an OPTIONAL array of steps that run after navigation to start_location and before the first entry in steps. Entries use the step language of §5 in either spelling of §4.5, as steps_override.steps entries do.

The block performs setup the case depends on but is not itself asserting — a log-in, dismissing a consent gate, seeding application state. It complements preconditions, which records such requirements as prose for the report: preconditions states what must be true, before makes it so.

A processor MUST observe the following:

  1. Entries run in document order, and the block halts at the first failing step; entries after it are recorded with status skip rather than executed. The halt is unconditional: continue_on_failure (§12.1) does not apply to the block, whatever it is set to. Gathering further diagnostics from a case whose setup did not complete has no value, because nothing after it can be trusted.
  2. If any entry fails, the main steps MUST NOT run and the use case scores error rather than fail (§11) — the case never reached the behaviour it exists to judge.
  3. Results of the block are kept separate from the main step results and MUST NOT be merged into them, so a report cannot present setup work as though it were the behaviour under test.
  4. Template variables are interpolated as in steps (§7.4).
  5. Extension cases inherit the block, or replace it by declaring their own. See §7.3.

The block is part of the document like any other, so §8.3 applies to it: both execution modes MUST reach the same outcome for a document that declares one.

Codegen has no equivalent of the error score — a generated test either passes or fails. A processor generating code for a document that declares a before block MUST therefore emit the block's entries ahead of the steps under test, such that the first failing entry fails the generated test and the steps under test do not run. That is the closest faithful mapping of rules 1 and 2 above. A processor SHOULD keep the emitted entries distinguishable from the steps under test, so a reader of the generated test's output can tell a setup failure from a failure of the behaviour under test; the reference implementation groups them in a Playwright test.step named Setup (before).

Emitting the block MUST NOT renumber the steps under test (§10.4).