Assertions
Prowl supports two kinds of assertions: inline assertions (mid-flow checks as steps) and hunt-level assertions (evaluated after all steps complete).
Inline Assertions
Use assert steps anywhere in your hunt for mid-flow checks. The hunt fails immediately if an inline assertion fails.
visible
Assert that text is visible on the page.
- assert:
visible: "Welcome back"
visible and notVisible accept either plain text or a CSS selector. Prose values — including those with punctuation like "Name:" or a sentence ending in . — are matched as text. Values with a clear selector signature (a leading ., #, or [, an attribute bracket, or an engine prefix like css= / xpath= / text=) are matched as selectors, e.g. visible: "img[alt='Logo']".
notVisible
Assert that text is NOT visible on the page.
- assert:
notVisible: "Error"
urlIncludes
Assert the current URL contains a substring.
- assert:
urlIncludes: "/dashboard"
urlEquals
Assert the current URL matches exactly.
- assert:
urlEquals: "https://example.com/dashboard"
Hunt-Level Assertions
These run after all steps complete. Define them in the assertions block at the top level of your hunt YAML. Assertions run even when a step failed.
name: smoke-test
steps:
- navigate: "/"
assertions:
- selectorExists: "h1"
- noConsoleErrors: true
- noNetworkErrors: true
As of 0.1.7, hunt-level assertions run on the experimental macOS, Android, and iOS targets too — they are no longer silently skipped. The selectorExists / selectorNotExists types resolve their selector against the app (and pass through guardrails.forbiddenSelectors first, just like step selectors). The web-only types — urlIncludes, urlEquals, noConsoleErrors, noNetworkErrors — are reported per-assertion with a skipped status (shown as [SKIPPED] in summary.md, skipped in result.json, and <skipped/> in JUnit) rather than dropped or errored. A console warning names the target for any web-only assertion a hunt explicitly authored.
selectorExists
Assert that an element matching the selector exists on the page.
assertions:
- selectorExists: "h1"
- selectorExists: "[data-testid='main-content']"
selectorNotExists
Assert that no element matching the selector exists on the page.
assertions:
- selectorNotExists: ".error-banner"
- selectorNotExists: "[data-testid='error-message']"
urlIncludes / urlEquals
Same as inline versions, but evaluated after all steps complete.
assertions:
- urlIncludes: "/dashboard"
- urlEquals: "https://example.com/"
noConsoleErrors
Fail if any console.error messages were logged during the hunt.
assertions:
- noConsoleErrors: true
noNetworkErrors
Fail if any HTTP responses with status >= 400 were received during the hunt.
assertions:
- noNetworkErrors: true
Use networkIgnorePatterns in your config to exclude known noisy endpoints (analytics, third-party scripts) from network error checks.
# .prowl/config.yml
assertions:
networkIgnorePatterns:
- "analytics.google.com"
- "hotjar.com"
AI-Powered Assertions
For checks that are hard to express as a selector — layout, rendered content, visual state — the assertWithAI step (0.1.6) screenshots the page and asks a vision LLM to verdict a natural-language claim. It runs on your own API key (BYOK), degrades to a non-fatal skip when no key is configured, and is a deliberate, non-deterministic exception to Prowl's determinism principle. See the step reference for the shape, config, and caveats.