should states
have, value, be enabled, be visible, count, and related assertion chains
Element should chains use have to scope a quoted label, then optional value, then one or more state (or compare) tasks.
Pattern
Then I should have "<label>" [by …] [value "<expected>"] <state…>Requirements
| Item | Contract |
|---|---|
| Element / control shape | Most have states resolve the label via general resolve (fill → click → checkbox). Text should see is any visible text (not limited to buttons). |
| How the label binds | Optional by … on the primary label. For values … sum equal / sorted with container, find hint applies to the container only |
| Visibility | be visible / not be visible must resolve the node first. If the app unmounts the node, you get not-found — that is not a successful not be visible |
| Counts | Visible children under the container: ul/ol → li; table → rows; role=list / grid → listitem/row; default → direct div children (direct / nested / descendant modes) |
| Sum / sorted | Web only. Sum may hover-harvest tooltips; sorted uses visible DOM order only (no hover-sweep) |
attribute … value | Assert after resolve — not a find hint |
| Downloads / clipboard / a11y / screenshot | No element resolve (web session diagnostics) |
When it fails
- Timeout / not found on
have— wrong shape or label (same as click/fill). not be visibleon a removed node → element not found.- Sum/sorted on Appium → unsupported.
See also UI contracts and should.
State tasks
| State | Example |
|---|---|
be enabled | Then I should have "username" be enabled |
be disabled | Then I should have "locked-field" be disabled |
not be blocked | Then I should have "locked-field" not be blocked |
be checked | Then I should have "terms" be checked |
not be checked | Then I should have "terms" not be checked |
be indeterminate | Then I should have "select-all" be indeterminate |
be visible | Then I should have "spinner" be visible |
not be visible | Then I should have "modal" not be visible |
be indeterminate is true when aria-checked="mixed" or the DOM indeterminate property is true. It does not change be checked / not be checked.
Value capture
Then I should have "username" value "chamodh" be enabled
Then I should have "email" value "a@b.com" have "confirm" value "a@b.com" be equalvalueasserts an expected field value (string-exact).be equalcompares two or more capturedvaluebindings in the same step.
Counts and attributes
Then I should have "products" count "3"
Then I should have "products" nested count "2"
Then I should have "products" descendant count "5"
Then I should have "home-link" attribute "href" value "/home"Numeric compares
Then I should have "cart-total" be greater than "100"
Then I should have "stock" be less than "5"
Then I should have "quantity" be at least "1"
Then I should have "discount" be at most "50"
Then I should have "products" count be greater than "0"Number-only extraction (US commas) and float epsilon apply. Exact value / be equal stay string-exact.
Sum equal (web only)
Then I should have "products" by id values "price" sum equal "order-total"
Then I should have "chart" by id values "*" sum equal "total"Qualifier values is distinct from value. Field "*" means the primary/sole number per sample.
Be sorted (web only)
Then I should have "result-dates" be sorted ascending
Then I should have "prices" as number be sorted descending
Then I should have "slots" as time be sorted ascending
Then I should have "orders" values "Date" as date be sorted ascending
Then I should have "product-grid" by id values "price" as number be sorted ascendingPairwise adjacent order on visible texts (DOM/Nth). Optional find hint on the container/list label only. No hover-sweep. Auto-detect kind when as … is omitted. Explicit as date: ambiguous slash (01/02/2024) uses US default. Equals are OK.
Downloads
Then I should have downloaded "report.csv"
Then I should have downloads "a.csv,b.pdf"have downloaded— asserts the last download basename after a prior click that triggered a download.have downloads— asserts an order-insensitive set of download basenames for the scenario (comma-separated payload). Web only.
Clipboard, diagnostics, screenshot, a11y (web only)
Then I should have clipboard value "hello"
Then I should have no console errors
Then I should have no page errors
Then I should match screenshot "checkout"
Then I should have no a11y violations| Qualifier | Meaning |
|---|---|
have clipboard value | Clipboard text equals the quoted value (pair with copy) |
have no console errors | Fail if console.error messages were collected during the scenario (cleared on navigate) |
have no page errors | Fail if page errors were collected during the scenario (cleared on navigate) |
match screenshot | Full-page PNG vs {stk-dir}/screenshots/baselines/{basename}.png (~0.2% threshold; STK_UPDATE_SCREENSHOT_BASELINES=1 writes/updates) |
have no a11y violations | Inject embedded axe-core; fail on critical or serious only |