Conditions & if / else
A condition guards work: one step, or a whole group with else and else if arms. So a session can take one of several paths instead of being split into separate files you choose between by hand.
Two shapes, one vocabulary:
| Guards | Reach it from | |
|---|---|---|
| Run only if | a single step | right-click a step |
| if / else block | a contiguous range | right-click a selection |
Guard one step
Right-click a step and choose Wrap in an if / else…, or set a guard on the step itself. Trq asks you to tap the element the condition is about, then opens the dialog.
A guarded step that doesn't run is marked skipped, not failed, and the row says which condition skipped it.
Guard a group
- Select the steps — click one, or Shift-click a range.
- Right-click and choose Wrap N steps in an if / else…
- Tap the element on the device, then build the condition.
Three rows appear: an if marker, your steps indented beneath it, and an end. Steps between them run only when the condition holds; anything after end always runs.
Right-click the if marker for the rest:
| Menu item | What it does |
|---|---|
| Edit condition… | Change what this arm asks |
| Add else if… | Another condition, tried in order, before any else |
| Add else | A final arm, for when nothing above matched |
| Remove the if / else (keep steps) | Delete the markers; the steps stay, unguarded |
Arms are tried top to bottom and at most one runs. Once one matches, the rest are skipped without being evaluated at all.
Once an else exists both Add else options switch off — an arm after the catch-all could never be reached. The menu says so rather than just greying out.
Reach these from any marker in the block. "Add an else" is an action on the condition, so right-clicking its end does what right-clicking its if does.
What a condition can ask
The dialog has one structural choice — On the screen or On a variable — and then asks only what that kind needs.
On the screen
Pick an element, then choose a check:
| Check | Means |
|---|---|
| is visible | On screen — not scrolled away or covered |
| exists | In the accessibility tree, seen or not |
| is enabled | Not greyed out |
| is checked | A checkbox or radio |
| is selected | A tab or chip — not the same as checked |
| can scroll | The element scrolls its own content |
| text is | Exact match |
| text contains | Case-insensitive |
is visible and exists are different questions, and the gap between them matters more than it sounds. An element can sit in the tree, enabled, matching your selector, and still be scrolled out of its container or hidden behind a bottom sheet. exists is true then; is visible is not.
is selected is not is checked. A selected tab is not a ticked box, and Android reports them through different accessors.
An element condition waits up to 2 seconds for its answer, because a screen may still be painting. Expiry is the answer — unlike a step's find timeout, where expiry is a failure.
On a variable
Compare a captured {{var.NAME}} against a value: equals, contains, matches, is less than, is set, and so on. Captures from earlier steps are offered as chips, because a typo in a variable name evaluates to "never captured" forever.
This is the same vocabulary and the same comparison the API Tests tab uses, so equals is numeric-tolerant here too — "10" equals "10.0".
A variable condition does no waiting. The capture already happened, so a variable can't become true by waiting; it resolves in about zero milliseconds.
Either side can be a {{var.x}}, an {{env.X}}, a {{calc(...)}} or a plain literal. Selector values are templated too, so "run only if the row for {{var.accountName}} is visible" works.
Inverting
Invert — run when this is NOT true flips the verdict. The dialog previews the sentence that will land on the row, which is the quickest way to catch a condition that reads as the opposite of what you meant.
Inversion applies to the verdict, after the wait. "Run only if the error is not visible" waits for the error to appear and runs only if it never does — not merely if it hasn't appeared yet.
What a run looks like
Steps in an arm that didn't run are marked skipped, with the condition that skipped them. Nothing is sent to the device for them.
Markers aren't steps. They carry no timing, no screenshot, and never appear in a report as passing or failing — a session with nine steps and two blocks still reports nine. Their rows show the role (if, else if, else, end) and the condition, in grey, with the guarded steps indented one level.
While you're recording
A condition doesn't run while you record — recording captures what you do, and you are the condition. Guards and arms are evaluated on Play, in Debug (step through a branch exactly as it runs), and from the CLI.
Nesting
Blocks nest, and a block inside a skipped arm is never evaluated — its condition would be asking the screen about something the skipped steps never did.
An example
Skip a first-run tour only when it's actually showing:
1 launch com.example.app
if Skip tour is visible
2 tap Skip tour
end
3 tap Accounts
if {{var.plan}} equals pro
4 assert Portfolio is visible
else if Upgrade banner is visible
5 tap Dismiss
else
6 assert Accounts is visible
end
7 tap Settings
Steps 1, 3 and 7 always run. Step 2 runs only on a first launch. Exactly one of the three arms runs between 3 and 7.
Half-authored blocks
An if with no condition takes its else arm rather than guessing, so a marker you haven't finished never silently runs its steps. A stray end is ignored rather than failing the session — a session caught mid-edit still plays, and refusing over one orphaned marker would be the worse failure.
Deleting any marker removes its partners too. Leaving them behind would make the list unreadable — but the steps between them are untouched, because removing a condition should un-guard your work, not destroy it.
See also
- Variables — capturing the values a condition reads
- Reliability & waits — the other layers that keep a replay stable
- if / else blocks for web and for API — the same feature, same structure