ProbeScript Syntax
ProbeScript is a natural language test syntax with indent-based blocks (like Python). Test files use the .probe extension.
Every test starts with test followed by a quoted name, with steps indented below:
test "user sees welcome screen" open the app wait 3 seconds see "Welcome" don't see "Error"Add tags with @ after the test declaration:
test "critical login flow" @smoke @critical open the app tap "Sign In" see "Dashboard"Run tagged tests with probe test tests/ --tag smoke.
Selectors
Section titled “Selectors”ProbeScript supports multiple strategies for identifying widgets:
| Selector | Syntax | Example |
|---|---|---|
| Text match | "text" | tap "Submit" |
| Widget key | #keyName | tap #loginButton |
| Widget type | <TypeName> | tap <ElevatedButton> |
| Ordinal | 1st "Item", 2nd "Item" | tap 2nd "Add" |
| Positional | "text" in "Container" | tap "Edit" in "Settings" |
Text Input
Section titled “Text Input”type "hello@world.com" into "Email"type "secret123" into the "Password" fieldAssertions
Section titled “Assertions”see "Dashboard" # text is visibledon't see "Error" # text is NOT visiblesee 3 "Item" # exactly 3 matchessee "Submit" is enabled # widget statesee "Terms" is checked # checkbox statesee "Price" contains "$9.99" # partial text matchGestures
Section titled “Gestures”tap "Button"double tap "Image"long press "Item"swipe leftswipe up on "Card"scroll downscroll up on "ListView"drag "Item A" to "Item B"Wait Commands
Section titled “Wait Commands”wait 5 secondswait until "Dashboard" appearswait until "Loading" disappearswait for the page to loadwait for network idleConditionals
Section titled “Conditionals”if "Accept Cookies" appears tap "Accept Cookies"With an else branch:
if "Welcome Back" appears tap "Continue"else tap "Sign In"repeat 3 times swipe left wait 1 secondDart Escape Hatch
Section titled “Dart Escape Hatch”For anything ProbeScript doesn’t cover natively, use a dart: block:
dart: final prefs = await SharedPreferences.getInstance(); await prefs.clear();HTTP Mocking
Section titled “HTTP Mocking”when the app calls POST "/api/auth/login" respond with 503 and body "{ \"error\": \"Service Unavailable\" }"Utility Commands
Section titled “Utility Commands”take screenshot "checkout_page" # save PNG to screenshots folderdump tree # dump widget tree for debuggingsave logs # save app logsgo back # device back buttonrotate landscape # rotate devicelog "checkpoint reached" # print to test outputpause # 1-second pauseApp Lifecycle
Section titled “App Lifecycle”clear app data # wipe data and relaunchrestart the app # force-stop and relaunch (preserves data)kill the app # force-stop only (no relaunch)open the app # launch the app (CLI-side) and reconnectClipboard
Section titled “Clipboard”copy "user@example.com" to clipboardpaste from clipboard # stores result in <clipboard> variabletype "<clipboard>" into "Email" # use the pasted valueDevice Location
Section titled “Device Location”set location 37.7749, -122.4194 # set GPS coordinates (lat, lng)External Browser
Section titled “External Browser”verify external browser opened # assert url_launcher was calledHTTP Calls
Section titled “HTTP Calls”Make real HTTP requests to APIs (runs on the CLI, not the device):
call GET "https://api.example.com/health"call POST "https://api.example.com/seed" with body "{\"env\":\"test\"}"call PUT "https://api.example.com/users/1" with body "{\"name\":\"updated\"}"call DELETE "https://api.example.com/sessions"Responses are stored in variables:
<response.status>— HTTP status code (e.g.,200)<response.body>— response body as a string
Data Generators
Section titled “Data Generators”Generate random data for form-heavy tests:
type "<random.email>" into "Email" # e.g., user_x7k2m@test.probetype "<random.name>" into "Name" # e.g., Alice Johnsontype "<random.phone>" into "Phone" # e.g., +1-555-042-7831type "<random.uuid>" into "Reference" # UUID v4type "<random.number(1,100)>" into "Age" # random int in rangetype "<random.text(8)>" into "Code" # random alphanumeric stringPermissions
Section titled “Permissions”allow permission "notifications"deny permission "camera"grant all permissionsrevoke all permissionsSee App Lifecycle for details on how these work across platforms.