search ESC

Searching…

No results for "".

Type at least 2 characters to search.

Docs

dusk:tap

Synthesise a tap at the widget identified by a snapshot ref token. dusk:tap is the primary action command: every interactive flow in an agent-driven test starts with a dusk:snap (to obtain eN refs) followed by one or more dusk:tap --ref= calls.

The handler routes through the actionability gate (enabled, zero-rect, off-viewport) before the pointer event leaves the VM. A gate failure throws DuskActionabilityException and surfaces as a structured error; the agent re-snaps or re-finds rather than silently retrying.


Table of contents


Synopsis

dart run fluttersdk_dusk dusk:tap --ref=
                                   [--includeSnapshot]
                                   [--[no-]checkStable]
                                   [--[no-]checkReceivesEvents]
                                   [--until=]
                                   [--untilTimeoutMs=]

dusk:tap requires a running Flutter session (CommandBoot.connected). It dials the VM Service URI, calls ext.dusk.tap, and prints either a one-line success or the post-tap JSON depending on --includeSnapshot / --until.

The pointer is dispatched at the target's LIVE center: the handler re-resolves the element's current bounding rect immediately after the actionability gate passes (falling back to the cached snapshot rect only for slivers / detached render objects), so a tap still lands on a button whose host rebuilt it into a shifted slot between dusk:snap and dusk:tap.


Arguments

Option Type Default Required Description
--ref string (none) yes (mandatory: true) Snapshot ref token (e.g. e1 from a prior dusk:snap, or q1 from a prior dusk:find). Empty values trigger a CLI-side error with exit code 1 before the VM Service call is made.
--includeSnapshot flag false no Embed the post-tap snapshot YAML in the response. Useful when the tap is expected to trigger a navigation or modal and the next agent step needs the fresh tree.
--checkStable flag true no Run the Stable (2-frame rect-unchanged) actionability gate. Disable when targeting an animating widget that intentionally rebuilds across frames.
--checkReceivesEvents flag true no Run the Receives-Events (front-most hit-test) actionability gate. Disable when targeting a widget that is intentionally occluded by an overlay you also want to interact with.
--until string (none) no After the tap settles, poll the live element tree (same loop as dusk:wait) for a Text whose data equals this value, up to the --untilTimeoutMs timeout (default 3000ms), and add an untilMatched boolean to the response reporting whether it appeared. Use to confirm a navigation / state change in one call instead of a separate dusk:wait. Off by default, which keeps the response shape unchanged. Setting --until always prints the JSON envelope (so untilMatched is visible) regardless of --includeSnapshot.
--untilTimeoutMs int 3000 no Timeout in milliseconds for the --until poll. Ignored when --until is not set.

The two check* flags default to true. Disable them with the inverted form (--no-checkStable, --no-checkReceivesEvents) when the target genuinely should not be subject to that precondition.


Returns

dusk:tap returns an integer exit code via Future:

Exit code Meaning
0 Tap synthesised. The handler emits either Tapped (default) or the full JSON envelope when --includeSnapshot is set.
1 --ref was missing or empty (CLI-side guard fires before the VM Service call).
non-zero VM Service handler returned ServiceExtensionResponse.error. Common causes: ref not found in the registry, actionability gate failure, no running app at the recorded URI.

Success envelope (default, --includeSnapshot off):

[ok]      Tapped e2

When the effect block reports no change, the one-line form says so rather than claiming a plain success:

[ok]      Tapped e2 (no observable change)

Success envelope (--includeSnapshot on):

{
  "snapshot": "[ref=e1] role=text label=\"Welcome\" ...",
  "ref": "e2"
}

The effect block, on every response:

{
  "ref": "e2",
  "effect": { "kind": "treeChanged", "changed": true }
}

changed is true when the target's route or its own semantics subtree differed after the tap, false when nothing observable changed. The signal is deliberately scoped to the TARGET rather than the whole tree, so unrelated background churn does not read as "something happened", and a counter button whose own label increments does.

changed: false is the classic false success: a button the gate accepted, whose onTap never fired. It was an opt-in --verify flag until it became clear that the agents who most needed it were the ones who did not know to ask for it.

Error envelope (actionability gate failure):

The message format is Widget ref= is not actionable: where is one of defunct (...), not enabled, zero rect, off-viewport (rect=..., viewport=...), not stable (rect changed by Xpx), or obscured by other widget (top=...). The agent branches by substring-matching on ; the full vocabulary is documented under Reference: Actionability gate.


Actionability gate

The actionability gate runs six preconditions in order: defunct (Step 0, preflight: render-object still attached), enabled (Tristate.isFalse fails), zero-rect (zero width or height), off-viewport (auto-showOnScreen first when a Scrollable ancestor exists), stable (2-frame rect drift ≤ 0.5px; opt out via --no-checkStable), and receives-events (hit-test path includes the target or descendant; opt out via --no-checkReceivesEvents). The full reference lives at Reference: Actionability gate.

Two further checks layer on top when their CLI flags are enabled:

  • checkStable ; the rect is unchanged across two consecutive frames.
  • checkReceivesEvents ; the hit-test at the rect center resolves to the target widget (i.e. no overlay is intercepting the tap).

See Reference: Actionability gate for the full ordering rationale and how new preconditions can be appended without breaking pinned agent prompts.


Examples

1. Tap a button by its snapshot ref

dart run fluttersdk_dusk dusk:snap > /tmp/snap.yaml
# locate the button's eN in /tmp/snap.yaml
dart run fluttersdk_dusk dusk:tap --ref=e2

Expected output (illustrative):

[ok]      Tapped e2

2. Tap and capture the post-tap tree in one round trip

dart run fluttersdk_dusk dusk:tap --ref=e2 --includeSnapshot

Expected output (illustrative; abbreviated):

{"snapshot":"[ref=e1] role=text label=\"Detail page\" ...","ref":"e2"}

3. Tap an intentionally-animating widget

dart run fluttersdk_dusk dusk:tap --ref=e5 --no-checkStable

Skips the 2-frame stable check for an animating loader / shimmer / spinner that the agent legitimately wants to interact with.

4. Read whether the tap had an observable effect

dart run fluttersdk_dusk dusk:tap --ref=e2 --includeSnapshot

Expected output (illustrative):

{"ref":"e2","effect":{"kind":"treeChanged","changed":true},"snapshot":"..."}

No flag is needed: the block is on every response. Use it as a post-condition assertion in agent-driven flows, and read it before concluding the app is broken.

5. Tap and wait for the expected text to appear

dart run fluttersdk_dusk dusk:tap --ref=e2 --until="Welcome back"

Expected output (illustrative):

{"ref":"e2","untilMatched":true}

After the tap, the handler polls the element tree for a Text("Welcome back") up to 3000ms. untilMatched:false means the text never appeared within the window (the tap did not produce the expected navigation / state change). This folds a dusk:wait --text=... into the tap call so the agent confirms the outcome in one round trip.


See also

  • dusk:snap: produce the eN refs that dusk:tap consumes.
  • dusk:find: mint a re-resolvable qN handle that survives rebuilds; pass it as --ref=q1.
  • dusk:observe: structured candidate list when the agent needs more than the raw Semantics tree.
  • Reference: Actionability gate: full ordering and message format the agent branches on.