Checks: expect
The expect table: every check a step can make, and how each comparison reads.
expectretries until it holds or its timeout (5 s,--timeout-mson the line or the run) runs out;exit,stdout,stderr,status,body,jsonandheaderread the lastexecorhttpanswer once.iscompares numbers as numbers (1is1.0). A glob (request,download) has*for any run of characters and matches the whole URL or file name:
| expect | Checks |
|---|---|
url/title is (or ==), contains, starts-with, ends-with, matches <value> | the active tab |
text "<t>", no text "<t>" [--within <target>] | visible text, frames included, or inside an element |
<target> visible, hidden, enabled, disabled, checked, unchecked, focused, editable, empty | an element’s state |
<target> text, value, attr <name>, style <property> + a comparison | an element’s text, value, attribute or computed style |
--selector <css> count is, >, >=, <, <= <n> | how many match (exactly one --selector) |
${var} + a comparison | a saved value |
js "<expression>" (or eval) [+ a comparison] | a script in the page: truthy, or its value compared |
js --file <script.js> [+ a comparison] | the same, from a file next to the .bt (an expression, or a function body that returns its answer): for a check across rows or anything too long to quote |
request <METHOD> <url glob> [status <n>], no request <METHOD> <url glob> | a request the page made in this scenario (* in the URL matches anything) |
request … json <$.path> + a comparison | a value in the last matching response’s JSON: $, .key, [0], ["key"] ($.items[0].sku) |
request … body + a comparison | the last matching response’s body, as text (body contains "added to cart") |
request … count + a comparison | how many matching requests |
cookie <name> [+ a comparison], no cookie <name>, cookie <name> absent | a cookie of the scenario’s browser context |
storage local|session <key> [+ a comparison | absent] | Local or Session Storage |
download <file glob>, no download … | a finished download (then ${download.path}, ${download.name}: the latest match). Each scenario’s downloads go to a directory of its own beside its evidence (.bitti/runs/<time>/<scenario>/downloads, kept only when something came), so a name never carries over from another scenario; a second file of the same name in one scenario arrives as name (2).ext |
download <file glob> compare <expected file> [--key <columns>] [compara flags…] | the download is the same table as the expected file beside the .bt (compara on the PATH, or BITTI_COMPARA): CSV, NDJSON or Parquet, either side; compara’s flags (-i, --trim, --tolerance) pass through. A difference fails the step with compara’s counts and the reports it kept beside the scenario’s downloads: --report html,json,rows (the HTML report, the JSON summary of counts and column statistics, CSVs of the changed, added and removed rows) or none; the run’s --compare-report (compare_report in bitti.toml, default html) when the step doesn’t say, and compara’s own -o, --json or --export-dir win |
console + a comparison, no console "<t>" | a console message (any level) |
no errors | no page exceptions or console errors |
accessible [--selector <css>] [--tags wcag2a,wcag2aa,…] [--ignore <rule>,…] | axe-core (built in, through navigera’s a11y, in a world of its own: the page never sees a window.axe) finds no violation in the page, or under the selector: WCAG 2.0 and 2.1 A and AA by default; --tags replaces that set (--tags wcag2a,wcag2aa,best-practice to add axe’s best practices); a failure names each rule, its impact, the first elements and what to fix |
lcp, fcp, ttfb, load, inp <, <=, >, >= <time> (2.5s, 800ms; a bare number is ms); cls + a comparison and a score (0.1) | a performance budget for the current page, as the browser measured it (navigera’s vitals): largest and first contentful paint, time to first byte, the load event (ms from the navigation’s start); the layout shift score (its largest session window, shifts after input left out); interaction to next paint, the slowest of the scenario’s clicks and key presses on this page so far (put it after them). A miss names the element behind it (3120 (#hero)). Not measured yet (the load event still to come, no interaction yet) is retried. Chrome keeps only interactions of 104 ms or more, so inp < 200ms passes when every one was quicker, and a budget under 104 ms is refused. Run under set cpu 4 and set network fast-3g to measure as a mid-tier phone |
exit [is] <n>, stdout/stderr + a comparison | what the last exec gave |
status [is] <n>, body + a comparison, json <$.path> [+ a comparison], header <name> [+ a comparison] | the last http answer; a JSON object or array compared with is is compared as JSON (key order and 1 vs 1.0 don’t matter), with contains as a part of it (json $ contains '{"role": "shopper"}': the keys it names, at any depth; for an array, items that some item matches) |
json <$.path> count + a comparison | how many items the array (or keys the object) at the path has |
json [<$.path>] schema <file.json>[#<pointer>] | the answer (or the value at the path) is valid against a JSON Schema beside the .bt, or one inside a larger document (openapi.json#/components/schemas/Item: an OpenAPI 3 document in JSON, its $refs read in the whole document); a failure lists where it isn’t ($[1].price: expected number, got string "5"). See API tests for what it reads |
json [<$.path>] snapshot <name> [--ignore <$.path>,…] | the answer (or the value at the path) is what its baseline beside the .bt holds, <file>.bt.snapshots/<name>.json, compared as JSON; --ignore $.id,$.items[*].created leaves out what changes from run to run. With no baseline yet the run writes it and the step fails, so a person reads it first; --update-snapshots writes or replaces it. A difference names each path ($.total: 38, was 36) |
time <, <=, >, >= <time> (300ms, 1.5s) | how long the last http exchange took, redirects included |
ax [<target>] matches + the tree indented under it | the page’s (or the target’s) accessibility tree, as navigera ax prints it, holds those lines: partially (button matches button "Place order") and in order, a line indented under another inside what that one matched, refs ignored, /regex/ (spaces allowed) for a name or a value |
screenshot <name> [<target>] [--full-page] [--threshold 0.2] [--max-diff-pixels <n> | --max-diff-ratio <r>] [--mask <css>]… | the viewport (or the element, or the whole page) as its baseline shows it, pixel by pixel; see below |
not … | any of them, negated |
Made from docs/format.md.