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-testid → Element.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 |
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 |
required |
resolve
|
Callable[[str], Component]
|
Maps a component name to its |
required |
max_depth
|
int
|
The deepest |
25
|
Raises:
| Type | Description |
|---|---|
ValueError
|
A required param is missing, an unknown param is passed, a |
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 |
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 |
required |
Returns:
| Type | Description |
|---|---|
Path
|
The resolved real path, ready to read. |
Raises:
| Type | Description |
|---|---|
ValueError
|
The ref is absolute, escapes the root via |
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 — |
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 |
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
|
required |
exclude
|
list[str]
|
A scenario carrying any of these tags is dropped. |
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 ( |
required |
Raises:
| Type | Description |
|---|---|
UncoveredSystemAlertLocale
|
the table has no entry for that language. |