コンテンツにスキップ

Scenario schema

The scenario model (steps, assertions, preconditions) and the loaders that parse a *.yaml, expand its component / data references, and serialize it back.

bajutsu.common.scenario

Scenario spec — the structure normalized from natural language.

The package keeps the data shapes (models), parsing (load), compile-time expansion (expand), tag selection (select), and serialization (serialize) in separate modules so a change to one rarely touches the others (BE-0043). The full public API is re-exported here, so from bajutsu.common.scenario import Scenario, load_scenarios, ... keeps working unchanged.

AfterRule

Bases: _Model

One after entry (BE-0392): the teardown steps to run for one scenario outcome.

on names the outcome this entry answers — always, or the scenario's own machine-checked verdict (success / error). success/error extend the word capturePolicy's Trigger.result already spells for a failed outcome (there one step's, here the whole scenario's) rather than inventing a second vocabulary; always is the one addition, for cleanup that does not depend on the outcome at all. More than one entry may carry the same on and they compose in declaration order, the same way two capturePolicy rules may share a trigger. The steps share the enclosing scenario's vars.*, so an entry can delete the very record an earlier http step's saveBody captured.

Assertion

Bases: _Model

One machine check. Exactly one kind may be set.

Background

Bases: _Model

Send the app to the background, as pressing the Home button does.

Backgrounds without terminating (SpringBoard is brought to the front), so the app's state survives for a later foreground.

CaptureRule

Bases: _Model

A capturePolicy rule — capture the artifacts in capture when its on trigger fires.

ClearClipboard

Bases: _Model

Clear the Simulator's pasteboard.

ClearKeychain

Bases: _Model

Reset the Simulator's keychain (saved passwords, certificates).

ClearStatusBar

Bases: _Model

Remove any status bar overrides (restore the live status bar).

ClipboardMatch

Bases: _Model

clipboard: verify what the app copied to the pasteboard — exactly one of equals / matches.

Read off the device (simctl pbpaste), so it needs the per-device control channel and is unavailable on the fake driver / in parallel runs.

Component

Bases: _Model

A reusable, parameterized sequence of steps.

params are the names a caller must supply via use: { with: {...} }; the steps reference them as ${params.<name>}.

CountMatch

Bases: _Model

count: exactly one of equals / atLeast / atMost.

CountOp

Bases: _Model

A count comparison with no element selector — exactly one of equals / atLeast / atMost.

The element-free counterpart to CountMatch, for aggregating over the network timeline (e.g. an event's multiplicity) rather than over screen elements.

DatetimeValue

Bases: _Model

generate: { datetime: … } — the current time as text, optionally shifted and zoned.

format is a strftime pattern (ISO 8601 when omitted); the four offset* fields are signed and additive, so a value an hour before tomorrow is offsetDays: 1, offsetHours: -1. timezone is an IANA name; the default is UTC, so a scenario matching a date the app renders in the device's own zone must name that zone (pinning the device's zone is BE-0158's concern).

Drag

Bases: _Model

drag action — a real pointer drag of an element (on) in a direction (BE-0227).

Where swipe's directional form scrolls (revealing off-screen content), drag grabs the element and moves it — a resize divider, a slider thumb, a reorder handle, a map inside a canvas: any control you drag rather than scroll. amount sets how far to travel as a fraction of the screen (0 < amount ≤ 1); omitted, a small default distance is used. It matters on the web backend, where a directional swipe is a wheel scroll (which does not move a grabbed element) but a drag is a genuine pointer drag (move → down → move → up). On iOS / Android a real OS drag both scrolls and moves handles, so swipe's directional form and drag coincide there.

Email

Bases: _Model

email — poll a mailbox until a matching message arrives, extract a value into ${vars.*}.

match selects the awaited message, extract pulls the value, and timeout (seconds, required) bounds the poll — a condition wait, never a fixed sleep (BE-0046). The mailbox endpoint lives in config (targets.<name>.mailbox), so the scenario stays app-agnostic and credential-free.

EmailExtract

Bases: _Model

Pull a value from the matched message body into ${vars.<var>} via a regex.

bodyMatches is a regex; its first capturing group (or the whole match, if it has none) is the value written to var. A matched message whose body the regex does not hit fails the step.

EmailMatch

Bases: _Model

Which message email waits for: recipient and/or subject, AND-ed. At least one is required.

EventMatch

Bases: _EndpointMatch

An analytics / telemetry event the app sent (BE-0048).

Matched over the captured request timeline by endpoint (url / urlMatches / path / pathMatches / method, AND-ed, same meaning as RequestMatch) and structured request-body fields (body: each given key must be present in the JSON request body and equal — compared as text — the given value). count is the expected multiplicity (default: at least one). At least one of an endpoint criterion or body is required, so an event always pins something.

ExcludeRegion

Bases: _Model

A rectangular region to ignore during visual comparison (e.g. status bar, clock).

Coordinates are in screenshot pixels — a fixed box, stable only while the layout is. Use SelectorRegion to mask by element instead, which survives reflow and resolution changes.

Exists

Bases: _Model

exists: { <selector>, negate? } (selector inline, optional negate).

Extract

Bases: _Model

Capture a UI element's property into a runtime variable (vars.*).

ForEach

Bases: _Model

Iterate over elements matching a selector.

Each element's identifier is stored as vars.<as> and the nested steps are executed.

Foreground

Bases: _Model

Resume a backgrounded app to the foreground (simctl launch, without terminating it).

The other half of background. It adds no settle sleep — any wait after resuming is the scenario's own condition wait.

Generate

Bases: _Model

generate — compute a random or current-datetime value into ${vars.*} (BE-0377).

Exactly one generator kind produces the value, and into.var names the slot a later type / assert reads it from — the same placement totp uses. Local and deterministic: a generator draw or a clock read, never a model and never the network. Only the value varies between runs; a step the validator accepted always executes and always succeeds.

GoldenMatch

Bases: _Model

golden: compare the live element tree against a recorded golden file (BE-0006).

The path is resolved against the golden context's base directory. The comparison is field-level per BE-0006 rules: exact on identity/state, set-equal on traits, tolerant (sanity only) on frame geometry.

Gone

Bases: _Model

until: { gone: <Selector> } — wait until a selector no longer matches any element.

HandleSystemAlert

Bases: _Model

handleSystemAlert action — tap a button on an iOS SpringBoard permission prompt (BE-0316).

A permission alert lives outside the app's accessibility tree (SpringBoard owns it), so it carries no app-assigned identifier, trait set, or value — only its visible text. sel therefore accepts the label-based fields alone (label / labelMatches / index) and rejects the rest at parse time. timeout bounds the condition wait for the prompt, required exactly as wait's is.

Two ways to name the button, exactly one per step: handleSystemAlert: { sel: { label: "Allow" }, timeout: 10 } handleSystemAlert: { prompt: notifications, choice: grant, timeout: 10 }

The second form states the intent and lets the run resolve the label from the scenario's locale (BE-0320), for the prompts a permissions preset cannot pre-answer — notification authorization, App Tracking Transparency, and the cross-process paste consent (BE-0369). It is worth reaching for because the literal text is easy to get subtly wrong: English's own deny button spells its apostrophe typographically, not as the ASCII character a hand-typed label carries. Every other alert keeps naming its button through sel, unchanged.

resolved(locale)

This step with prompt/choice turned into the sel the locale's SpringBoard renders.

A sel form returns unchanged, so the resolution is a no-op for every alert outside the prompts the lookup covers.

Raises:

Type Description
UncoveredSystemAlertLocale

the lookup has no labels for the locale's language — the step fails loudly rather than tapping a guessed button.

HttpRequest

Bases: _Model

Issue an HTTP request (for test-data setup, webhook triggers, API calls).

The response status is checked against status (if given); a mismatch fails the step. saveBody stores the response body text as vars.<saveBody> for subsequent ${vars.*} interpolation.

If

Bases: _Model

Conditional execution.

Evaluate an assertion as the condition, then run then steps if it passes or else steps otherwise.

Interrupt

Bases: _Model

A handler for an interstitial screen that can appear at an unpredictable point (BE-0314).

condition is the same assertion the if step evaluates; the runner checks it opportunistically against trees it has already fetched (a wait's poll tick, an act step's pre-action read), wherever in the step sequence the screen happens to surface, and runs steps to clear it when it matches. The steps share the enclosing scenario's vars.*, the same as if's branches do.

LongPress

Bases: _Model

longPress action — press and hold a selector for duration seconds.

Manual

Bases: _Model

manual action — a human-takeover marker recorded during record (BE-0185).

Emitted when a blocker is an operation the AI cannot perform (a CAPTCHA, a biometric prompt, a repeatedly unresolvable gesture): the human operated the device live and record recorded this marker of the observed transition, not the raw gesture. It has no deterministic run-time equivalent, so at run time it fails loudly with label — never a silent pass, never a hang (directives 1 and 2). bypass, when set, names the deterministic bridge an author can wire to make the step replayable (a test-build flag, a device-control / device-state primitive, BE-0035 / BE-0052); None means no such bridge exists (a real CAPTCHA).

Mock

Bases: _Model

A deterministic network stub.

When an outgoing request matches match, BajutsuKit returns respond instead of hitting the network (so tests don't depend on a live server). match reuses the request matcher's request-side fields (method / url / urlMatches / path / pathMatches / bodyMatches); status / count do not apply here.

MockResponse

Bases: _Model

The canned response a mock returns (defaults to an empty 200).

Network

Bases: _Model

Per-scenario network settings.

filter scopes which observed requests are interleaved into the report's Steps timeline.

NetworkFilter

Bases: _Model

Which observed requests to interleave into the report's Steps timeline.

With domains set, only exchanges whose URL host matches one of them — exactly or as a parent suffix (example.com matches api.example.com) — appear in Steps; empty / unset shows every captured exchange. The Network tab always lists them all.

OverrideStatusBar

Bases: _Model

Override the Simulator's status bar for deterministic screenshots.

All fields are optional; only the provided fields are overridden.

Pinch

Bases: _Model

Two-finger magnify. scale > 1 zooms in, 0 < scale < 1 zooms out.

Preconditions

Bases: _Model

Per-test environment setup.

resolved_locale(target_locale)

The locale this scenario runs under: its own override, else the target config's locale.

The one place the precedence lives, so everything that acts on it agrees — the app's launch arguments, the Simulator's own system language, and the system-alert label lookup that predicts what SpringBoard renders (BE-0320). Takes the target's value rather than the whole config, keeping the scenario schema a portable inner contract.

Push

Bases: _Model

Deliver a simulated push notification (simctl push) to the app under test.

Carries this APNs payload, e.g. {"aps": {"alert": "..."}}.

RandomValue

Bases: _Model

generate: { random: … } — exactly one generator kind (BE-0377).

Redact

Bases: _Model

Redaction config — element labels, network headers, and JSON fields to scrub from evidence.

Each list names items that are zeroed out before evidence is written to the report. A standard set of credential-bearing headers (authorization, cookie, set-cookie, …) is masked by default (BE-0130); unmask_headers is the explicit, visible opt-out that releases a specific default — turning off protection is never the mere absence of redact:.

Two element-level defaults join that header set (BE-0331) and take the same shape of opt-out: a field the platform itself marks secret, and a field whose identifier or label names a credential. Both cover cases a caller should never have to configure, so each releases only through its own explicit flag.

Relaunch

Bases: _Model

relaunch action — restart the app process, optionally overriding its launch env/args.

RequestMatch

Bases: _EndpointMatch

Network-traffic matcher, shared by the request assertion and until: { request: ... }.

The fields (method / url / urlMatches / path / pathMatches / status / bodyMatches) are AND-ed; count is how many exchanges matched — exact for the assertion, a lower bound for the wait. The endpoint can be pinned by url (exact full URL) or urlMatches (regex/substring; query strings live here), or just the path; bodyMatches checks the request body. At least one match field is required.

ResponseSchemaMatch

Bases: _Model

Validate a captured response body against a stored JSON Schema (BE-0048).

request selects the exchange whose response is checked (reusing the request matcher); schema is the schema file, resolved against the app's schemas dir. schema_path carries the value (the field is aliased schema to avoid shadowing pydantic's own schema attribute).

Rotate

Bases: _Model

Two-finger rotation. radians > 0 rotates clockwise.

Scenario

Bases: _Model

One scenario.

ScenarioFile

Bases: _Model

A scenario file: an optional file-level description plus the scenarios it defines.

Two on-disk forms are accepted: the bare list of scenarios (no file description), or a {description: "...", scenarios: [...]} mapping.

Scroll

Bases: _Model

scroll action — scroll a region until to is on-screen, or fail at a bound (BE-0326).

Where swipe's directional form is a single gesture, scroll is a bounded condition wait: it scrolls one non-inertial step, re-queries, and stops the moment to resolves with its frame's center inside the viewport — the point a following coordinate tap would aim at. It fails deterministically when it spends maxScrolls steps, or as soon as a step no longer changes the scrolled region's subtree (the region has bottomed out and the target is not there).

direction names the direction the content scrolls, so down reveals items below the fold — the inverse of swipe, whose direction is the finger's. within scopes the gesture (and the end-of-content comparison) to one scrollable container; omitted, the whole screen scrolls.

amount sets how far one step travels, as a fraction of the viewport (0 < amount ≤ 1) — the same unit swipe and drag take — for a screen whose content the default step size overshoots or creeps across. Omitted, the loop keeps its own default. It sets only where the loop starts: the overshoot recovery still shrinks the step toward its own fixed floor from wherever amount put it (BE-0400).

SelectOption

Bases: _Model

selectOption action — set a native <select> to the option with the given value.

Web-only: a <select> lives in the DOM but has no native counterpart on iOS / Android, so those backends refuse it (UnsupportedAction). option matches an option's value (not its visible label), mirroring the value assertion — which reads the <select>'s current value — so a picked option is directly assertable. A <select>'s dropdown is not in the DOM, so this is how a web <select> (e.g. the BE-0191 theme picker) is switched deterministically rather than by a coordinate click.

Selector

Bases: _Model

How to address an element. Provided fields are combined with AND.

id / idMatches accept a single value or a list of candidates; a list matches an element whose identifier equals (or glob-matches) any candidate — an OR. This lets one shared scenario carry every platform's form of an id (id: [stable.refresh, stable_refresh]), so it runs unchanged where the native id syntax differs — e.g. Android's android:id, which allows neither . nor -, surfaces stable.refresh as stable_refresh (BE-0221). Ambiguity is unchanged: 2+ matching elements on screen still fail fast.

List the canonical (dotted SPEC) form first. OR matching is order-independent, but single-id consumers take the first candidate as the representative — first_id(), audit.referenced_ids coverage bucketing (namespace_of splits on .), and the XCUITest / Playwright codegen emitters. Leading with the dotted SPEC id keeps their output correct; an underscore-first list would still resolve at runtime but skew coverage and generate the non-portable id.

as_selector()

Convert to the TypedDict consumed by base.resolve_unique.

first_id()

The primary id candidate (the first when id is a list), or None (BE-0221).

For single-id consumers — triage rename suggestions, capturePolicy on: matching, the WebView host — that want one representative id rather than the whole OR set. Selectors list the canonical (dotted SPEC) form first (see the class docstring), so "first" is the portable id.

SelectorRegion

Bases: _Model

An element to ignore during visual comparison, addressed by selector (BE-0171).

Resolved to the element's frame at evaluation time and masked exactly as an ExcludeRegion rectangle is. Robust where a pixel box is not: it follows the element across reflow, resolution, and locale changes. A selector matching nothing is a masking no-op (nothing on screen to hide); an ambiguous one fails, like every other selector resolution (prime directive 2).

SetClipboard

Bases: _Model

Seed the Simulator's pasteboard with text (simctl pbcopy), for paste flows.

SetLocation

Bases: _Model

Override the simulated device's GPS location (simctl location set).

SetPickerValue

Bases: _Model

setPickerValue action — move a picker wheel to the row whose value is value.

iOS-only: a wheel-style UIPickerView / UIDatePicker exposes no separately addressable row, so no coordinate drag can guarantee stopping on one — XCUITest's own adjust(toPickerWheelValue:) acts on the resolved element instead, which is what makes this deterministic (BE-0356). Other backends have no such control and refuse it (UnsupportedAction).

sel addresses exactly one wheel. A multi-component picker (a year wheel beside a month wheel) exposes each component as its own pickerWheel element, disambiguated by the within / traits / index fields every selector already carries, so value stays a plain string and each component is set by its own step.

Step

Bases: _Model

One action plus optional modifiers (capture / name / extract).

Swipe

Bases: _Model

swipe action — by direction on an element (on), or between two points (from/to).

amount (only with on/direction) sets how far to travel as a fraction of the screen (0 < amount ≤ 1): ~0.2 nudges, ~0.5 scrolls half a screen, ~0.9 nearly a full one. Omitted, a small default distance is used — so the caller can dial the scroll to the instruction.

SystemAlertHandling

Bases: _Model

Per-scenario control of the reactive system-alert guard.

Handling of OS prompts (e.g. a notification or App Tracking Transparency request) that the app-scoped accessibility tree cannot see or tap, fired reactively when a step (or expect) is blocked or a guarded wait finds an alert. The guard is ON by default. On the iOS XCUITest backend it clears the prompt deterministically and natively (BE-0315), reusing BE-0316's SpringBoard query + tap — no screenshot and no model round trip; where the native path cannot act it falls back to the vision guard (a screenshot the locator reads). This is the reactive counterpart to the proactive handleSystemAlert step (BE-0316): the step taps a named button at an author-chosen point, this guard clears prompts automatically wherever they surface.

Each key reaches exactly one of those two paths (BE-0401): rules and labels steer the native path, visionInstruction steers the fallback. On-disk forms — the bare boolean carries on and off, so a mapping always means on: systemAlertHandling: false — disable the guard for this scenario systemAlertHandling: { rules: [{ prompt: notifications, choice: grant }] } — answer a named prompt by its own choice, regardless of which label it shares with another systemAlertHandling: { labels: ["Allow", "OK"] } — tap the first of these labels present on the alert systemAlertHandling: { visionInstruction: "tap Allow" } — free text only the vision fallback reads Within the native path a rule names a prompt and a label names a button, so rules is consulted first; an alert whose prompt no rule names falls through to labels, and to the built-in dismissive labels when no layer supplies any.

SystemAlertRule

Bases: _Model

One entry of systemAlertHandling.rules: the choice to make on one named prompt.

prompt and choice reuse the vocabulary the proactive handleSystemAlert step already takes (its prompt/choice form) instead of a literal button label, so the same rule grants or denies the prompt under any locale bajutsu.common.scenario.system_alerts covers. The reactive guard identifies which alert is on screen from this prompt's own two labels — not from an ordering trick over labels, which cannot record which answer belongs to which prompt (see SystemAlertHandling below).

TapPoint

Bases: _Model

tapPoint action — tap a screen location by normalized coordinates (0..1), not a selector.

The bottom rung of the stability ladder (DESIGN §5), for a control the accessibility tree does not expose as an addressable element — most notably a tab-bar tab on an app with no accessibility ids, which iOS's accessibility tree collapses into one opaque group. record's agent locates it in the screenshot and emits its center here; run replays it against the current screen size. x/y are fractions of the app window (top-left origin), so the tap survives a resolution change a raw-pixel tap would not — but it is still coordinate-based and unverifiable by selector, so prefer a real selector whenever the element is addressable.

TextMatch

Bases: _Model

value / label: exactly one of equals / contains / matches.

Totp

Bases: _Model

totp — generate an RFC 6238 time-based one-time password into ${vars.*} (BE-0046).

secret is the shared base32 key (commonly ${secrets.*}); the current code is written to into.var for a later type / assert to consume. Local and deterministic — no LLM, no network, no scripting escape hatch.

Trigger

Bases: _Model

A capturePolicy trigger that fires its CaptureRule when a condition holds.

The condition is exactly one of action / event / result; idMatches narrows an action trigger to a specific element ID.

TypeText

Bases: _Model

type action — enter text, optionally into a selector and optionally submitting after.

Use

Bases: _Model

Invoke a reusable component, substituting its declared params with with.

The use step is expanded away (replaced by the component's steps) before the run, so it is a compile-time macro, not a runtime action — determinism is unaffected.

VarTarget

Bases: _Model

into: { var: <name> } — the ${vars.<name>} slot a step writes its produced value to.

VisualMatch

Bases: _Model

Visual regression assertion — compare a screenshot to a baseline image.

By default the whole screen is compared. element (BE-0171) scopes the comparison to one element's frame: the screenshot is cropped to it and the baseline is that crop, so unrelated on-screen changes no longer churn the baseline.

Wait

Bases: _Model

wait step — block until a selector appears (for) or a condition holds (until).

Bounded by timeout; always a condition wait, never a fixed sleep.

WaitRequest

Bases: _Model

until: { request: <RequestMatch> } — wait until a matching network exchange has been observed.

Requires the run's network collector to be active.

Web

Bases: _Model

Enter the web context: resolve a native WebView host, then run inner steps against its DOM.

The within selector resolves natively to exactly one WKWebView element; inner steps address the normalized DOM (data-testidElement.identifier), not the native a11y tree.

UncoveredSystemAlertLocale

Bases: ValueError

A prompt / choice pair was asked for under a language the table does not cover.

Raised rather than guessed at: a wrong label would tap nothing (or, worse, the other button), and BE-0320 exists to remove exactly that kind of accident.

apply_setups(scenarios, default_setup, resolve)

Prepend each scenario's reusable setup prelude, in place.

A scenario's setup precondition (falling back to the app/config default) names a reusable prelude; those steps run before the scenario's own, so a shared login / navigation flow is written once and reused. The same reference is resolved at most once.

Parameters:

Name Type Description Default
scenarios list[Scenario]

The scenarios to prepend setups to; their steps are rewritten in place.

required
default_setup str | None

The setup reference used when a scenario declares none. None means none.

required
resolve Callable[[str], list[Step]]

Maps a setup reference to its list of steps (e.g. by loading a shared file).

required

declared_name(run_scenario)

The name a suite declares for the scenario a run recorded as run_scenario.

Strips the row suffix _row_name appends; a name carrying none is returned unchanged, so a scenario that is not data-driven passes through.

expand_components(scenarios, resolve, max_depth=25)

Replace every use step with the referenced component's steps, recursively and in place.

Pure compile-time expansion: a component may itself use another, and after this no use steps remain, so the run loop is unaffected.

Parameters:

Name Type Description Default
scenarios list[Scenario]

The scenarios to expand; their steps, their before / after lifecycle steps, and every interrupts entry's recovery steps are rewritten in place.

required
resolve Callable[[str], Component]

Maps a component name to its Component (e.g. by loading a shared file).

required
max_depth int

The deepest use nesting allowed before giving up on a runaway chain.

25

Raises:

Type Description
ValueError

A required param is missing, an unknown param is passed, a ${params.*} token references an undeclared param, a reference cycle is detected, or nesting exceeds max_depth.

expand_data(scenarios, resolve_csv)

Expand each data-driven scenario into one scenario per data row.

${row.<col>} tokens are substituted per row. A scenario with neither data nor dataFile passes through unchanged. Each derived scenario keeps the original's preconditions (erase default intact), so every row runs in its own clean environment — isolation is preserved.

Parameters:

Name Type Description Default
scenarios list[Scenario]

The scenarios to expand.

required
resolve_csv Callable[[str], list[dict[str, str]]]

Loads a dataFile reference into a list of {column: value} rows.

required

Returns:

Type Description
list[Scenario]

The scenarios with every data-driven one replaced by its per-row instances, in order.

read_csv(text)

Parse CSV text into a list of {column: value} row dicts (header row required).

load_component(text)

Parse a YAML string (a single component mapping) into a validated Component.

load_scenario_file(text)

Parse a scenario file: a list of scenarios, or a {description, scenarios} mapping.

Raises:

Type Description
ValueError

The top level is neither a list nor a mapping (§6.1), or the file declares a schema version newer than this bajutsu supports (BE-0119).

load_scenarios(text)

Parse a scenario file into validated Scenario objects (any file-level description dropped).

contained_ref(root, base, ref)

Resolve a scenario ref (base / ref) and require its real path to stay within root.

The one containment choke point for a scenario's component / data refs (BE-0174): every device-free resolver routes ref resolution through here, so a scenario cannot make the loader read a file outside its suite. resolve follows symlinks, so an out-of-root link is caught the same as a .. chain or an absolute path — the three ways a ref leaves the tree. On rejection the error names only the offending ref, never the target's contents, so the check happens before the read and closes the leak as well as the read.

Parameters:

Name Type Description Default
root Path

The suite root the ref must stay within (the scenarios dir the load started from).

required
base Path

The directory refs resolve against (the referring scenario file's directory).

required
ref str

The use: component or dataFile reference to resolve.

required

Returns:

Type Description
Path

The resolved real path, ready to read.

Raises:

Type Description
ValueError

The ref is absolute, escapes the root via .., or symlinks outside it.

load_expanded_scenarios(path, root=None)

Load a scenario file and expand its components + data rows, resolving refs relative to the file.

Parameters:

Name Type Description Default
path Path

The scenario file to load.

required
root Path | None

The suite root every component / data ref must stay within (BE-0174). Defaults to the scenario file's own directory, so a single-file load confines refs to that directory; a suite loader passes the shared scenarios dir instead.

None

Raises:

Type Description
OSError

The scenario file or a referenced component / CSV cannot be read.

ValueError

The content is invalid, the YAML does not parse — _parse_yaml_named normalizes a yaml.YAMLError into a ValueError naming the offending file (the scenario or a referenced component), so its callers' except (OSError, ValueError) guard a malformed file as cleanly as a structurally-invalid one (BE-0150) — or a ref resolves outside root (contained_ref).

load_scenarios_dir(scenarios_dir)

Every expanded scenario in scenarios_dir's *.yaml files, sorted by filename.

The device-free suite loader shared by coverage on the CLI and in the serve Web UI, so both read a target's suite identically. The scenarios dir is the containment root passed to each file's load, so a scenario's refs stay inside the suite (BE-0174).

Raises:

Type Description
(OSError, ValueError)

as load_expanded_scenarios — an unreadable or invalid file, or a ref that escapes the suite root.

select_scenarios(scenarios, include, exclude)

Filter scenarios by tag, preserving order.

Pure metadata filtering — never mutates or reorders.

Parameters:

Name Type Description Default
scenarios list[Scenario]

The scenarios to filter.

required
include list[str]

A scenario is kept only if it carries at least one of these tags; an empty include keeps everything.

required
exclude list[str]

A scenario carrying any of these tags is dropped. exclude wins over include.

required

Returns:

Type Description
list[Scenario]

The kept scenarios, in their original order.

dump_block(items)

Serialize models as a - … YAML sequence block — one pruned, alias-keyed item each.

Alias keying matches scenario_dict, but a scoped block also drops default-valued fields (exclude_defaults), so a single spliced step / assertion stays as terse as the author wrote it rather than sprouting submit: false and other model defaults. Used by the Author editor's scoped round-trip edits (BE-0261) to re-serialize just the changed step / expect block.

dump_mocks(mocks)

Serialize a scenario's mocks to the compact JSON BajutsuKit reads from BAJUTSU_MOCKS.

Alias keys, omitting unset fields.

dump_scenario_file(scenarios, description=None)

Serialize a scenario file.

With a file-level description, emits the {description, scenarios} mapping form; otherwise the bare list (round-trips through load_scenario_file).

dump_scenarios(scenarios)

Serialize scenarios back to YAML (round-trips through load_scenarios).

redact_totp_secrets(scenario)

A copy of scenario with literal totp.secret seeds masked, for on-disk evidence (BE-0152).

The executed scenario is snapshotted into the run's artifacts; a literal base32 seed there is durable credential material, so it is replaced with a placeholder before the snapshot is written. A ${secrets.*} reference is left intact (its resolved value never reaches the snapshot — BE-0032). Round-trips through the model so the result stays a valid scenario.

exclude_defaults is what keeps that round-trip total: a field validator may reject a value the author can never write but a dump still emits — systemAlertHandling.labels rejects [], which is its own default_factory output and which exclude_none does not drop — so re-validating a model's own dump would fail on a policy that named no button (BE-0401). Excluding default-valued fields hands model_validate only what was declared, and re-validation restores each default, so the returned model is field-for-field the input.

scenario_dict(scenario)

A pruned, alias-keyed dict of one scenario (for the rich report view).

Drops default-valued fields for the same reason redact_totp_secrets does: a model dump that emits every default is not reloadable, because a validator reading model_fields_set cannot tell a default the dump added from a value the author wrote. VisualMatch._engine_fields is the case — it rejects colorTolerance / antialiasing alongside compare: exact, and both carry non-None defaults that exclude_none keeps — so the scenario.yaml written beside a run's results failed to reload, against dump_scenarios' own round-trip contract. Excluding them also keeps the snapshot as terse as the author wrote it, which is what dump_block already does.

covered_languages(prompt)

The language subtags this table covers for prompt, sorted — the documented, testable surface.

system_alert_label(prompt, choice, locale)

The button label SpringBoard renders for prompt's choice under locale.

Parameters:

Name Type Description Default
prompt SystemAlertPrompt

Which of the covered OS prompts the step is answering.

required
choice SystemAlertChoice

What the author means by the tap, rather than which button says it.

required
locale str

The scenario's resolved locale (Preconditions.resolved_locale); only its language subtag selects the labels, since SpringBoard localizes by language, not by region.

required

Raises:

Type Description
UncoveredSystemAlertLocale

the table has no entry for that language.