Assertions
Assertions are made with the expect function from FlowContext. It takes a selector and returns a PromiseExpectation with chainable assertion methods.
const { expect } = ctx;
await expect({ testID: "welcome-banner" }).toBeVisible();expect(selector)
Section titled “expect(selector)”expect: (selector: Selector) => PromiseExpectation;Returns a PromiseExpectation bound to the given selector.
Core visibility, text, and state assertions
Section titled “Core visibility, text, and state assertions”| Method | Signature | Description |
|---|---|---|
toBeVisible | (opts?: WaitOptions) => Promise<void> | Assert that the element exists and is visible |
toBeHidden | (opts?: WaitOptions) => Promise<void> | Assert that the element is missing or hidden |
toHaveText | (expected: string, opts?: WaitOptions) => Promise<void> | Assert an exact text match |
toContainText | (expected: string, opts?: WaitOptions) => Promise<void> | Assert a case-insensitive substring match |
toMatchText | (pattern: RegExp, opts?: WaitOptions) => Promise<void> | Assert a regular-expression text match |
toHaveValue | (expected: string | number, opts?: WaitOptions) => Promise<void> | Assert an exact form value match |
toHaveAttribute | (name: string, value?: string, opts?: WaitOptions) => Promise<void> | Assert that an attribute exists or matches a value |
toBeEnabled | (opts?: WaitOptions) => Promise<void> | Assert that the element is enabled |
toBeDisabled | (opts?: WaitOptions) => Promise<void> | Assert that the element is disabled |
await expect({ testID: "success-message" }).toBeVisible();await expect({ text: "Welcome" }).toBeVisible({ timeout: 8000 });await expect({ testID: "user-greeting" }).toHaveText("Hello, Alice");await expect({ role: "status" }).toContainText("saved");await expect({ testID: "price" }).toMatchText(/^\$\d+\.\d{2}$/);await expect({ testID: "email" }).toHaveValue("demo@example.com");await expect({ testID: "primary-button" }).toHaveAttribute("aria-busy", "false");await expect({ testID: "submit" }).toBeEnabled();Use toHaveText() for exact text, toContainText() for partial copy, and toMatchText() when you need more flexible matching.
Visual regression
Section titled “Visual regression”toMatchScreenshot( name: string, options?: { threshold?: number; maxDiffPixelRatio?: number; },): Promise<void>Capture the current element and compare it against a named baseline image.
await expect({ testID: "avatar" }).toMatchScreenshot("profile-avatar");await expect({ testID: "receipt" }).toMatchScreenshot("receipt", { threshold: 0.2, maxDiffPixelRatio: 0.01,});Create or refresh baselines with spana test --update-baselines. For suite-wide defaults, configure visualRegression in spana.config.ts.
Accessibility assertions
Section titled “Accessibility assertions”| Method | Signature | Description |
|---|---|---|
toPassAccessibilityAudit | (options?: { severity?: "critical" | "serious" | "moderate" | "minor"; rules?: string[]; exclude?: Selector[] }) => Promise<void> | Run an axe-core audit of the current web page |
toHaveAccessibilityLabel | (expected?: string) => Promise<void> | Assert that an element has an accessibility label, optionally with an exact value |
toBeFocusable | () => Promise<void> | Assert that the element can receive focus |
toHaveRole | (expected: string) => Promise<void> | Assert a semantic role such as button or link |
toHaveMinTouchTarget | (size?: number) => Promise<void> | Assert a minimum touch target size, default 44 |
await expect("body").toPassAccessibilityAudit({ severity: "serious", rules: ["color-contrast", "button-name"],});
await expect({ testID: "login-button" }).toHaveAccessibilityLabel("Log in");await expect({ testID: "login-button" }).toHaveRole("button");await expect({ testID: "login-button" }).toBeFocusable();await expect({ testID: "login-button" }).toHaveMinTouchTarget(48);toPassAccessibilityAudit() is web-only. The other accessibility assertions are element-level checks that work across supported platforms.
Auto-wait behavior
Section titled “Auto-wait behavior”All assertions auto-wait. spana does not require manual sleep() calls. The assertion loop works as follows:
- Dump the element hierarchy from the platform driver.
- Search the tree for the selector.
- Check the assertion condition.
- If the condition is not met, wait and retry using adaptive polling.
- If
timeoutis exceeded, fail with a descriptive error including the selector and last known state.
WaitOptions lets you tune assertion behavior:
type WaitOptions = { timeout?: number; pollInterval?: number; initialPollInterval?: number; settleTimeout?: number;};timeout: maximum time to wait before failingpollInterval: maximum poll interval once backoff ramps upinitialPollInterval: first poll interval before backoff growssettleTimeout: how long the condition must remain stable before passing
settleTimeout is useful for transitions and animations where the element briefly appears and disappears.
Timeout configuration
Section titled “Timeout configuration”Timeouts are resolved in this order (highest priority first):
opts.timeoutpassed directly to the assertion methodflow(..., { defaults: { waitTimeout } })on the containing flowdefaults.waitTimeoutinspana.config.ts- Built-in default: 5000 ms
// Global default in configexport default defineConfig({ defaults: { waitTimeout: 8000 },});
// Per-flow override for waits and pollingexport default flow( "slow screen test", { defaults: { waitTimeout: 12000, pollInterval: 250, settleTimeout: 300, }, }, async ({ expect }) => { // Per-assertion override await expect({ testID: "result" }).toBeVisible({ timeout: 15000 }); },);FlowConfig.timeout is a separate overall flow timeout. It does not replace assertion wait settings.