Skip to content

refactor(daemon): relocate the interaction touch tests by what they assert - #3672

Merged
thymikee merged 13 commits into
refactor/daemon-session-lifecycle-testsfrom
refactor/daemon-interaction-touch-tests
Oct 15, 2026
Merged

thymikee merged 13 commits into
refactor/daemon-session-lifecycle-testsfrom
refactor/daemon-interaction-touch-tests

Conversation

@thymikee

@thymikee thymikee commented Oct 15, 2026 •

Copy link
Copy Markdown
Member

Summary

Batch interaction-touch of the daemon test redesign (no test exports of any kind). The 16 root tests (plus the interaction-touch-runner.ts helper and the root-level interaction-touch-android-readiness-spawns sibling) that drove handleInteractionCommands with the real command surface are placed by what they assert. Daemon halves (a) now run against package fakes; assertions about what the real src/commands/interaction/** code does (c) moved to tests beside that code. snapshot-interactor-capture mocks are gone: captures go through installInteractorSnapshotResolution() (the InteractorResolution seam). Stacked on #3670.

New package fixtures, all unexported: interactor-resolution-fixture.ts, touch-command-surface-fake.ts, touch-interaction-runner.ts.

a / c split and test-count parity

Before = exact count at the old path (vitest JSON reporter); after = run count per destination (all under packages/daemon/src/execution/ unless noted).

Source (before) Daemon half (a) Command half (c), src/commands/interaction/runtime/
interaction-touch (12) interaction/internal/interaction-touch-routing.test.ts (12) none
interaction-touch-press-admission (13) .../interaction-touch-press-admission.test.ts (12) interactions-preresolved-target.test.ts (the #1654 provenance-mismatch case)
interaction-touch-press (9) .../interaction-touch-press.test.ts (5) interactions-target-promotion.test.ts (4), interactions-preresolved-target.test.ts (5, shared)
interaction-touch-fill (6) .../interaction-touch-fill.test.ts (4; +1 dispatch-and-record) interactions-ref-frame-evidence.test.ts (off-screen and invalid bounds), interactions-target-promotion.test.ts (editable node)
interaction-touch-runtime (7) .../interaction-touch-runtime.test.ts (3) interactions-ref-frame-evidence.test.ts (5, shared); the missing-rect refusal is already pinned by ref-target-resolution.test.ts
interaction-touch-response (6) .../interaction-touch-response-evidence.test.ts (6) digest/changed-from-before stays in interactions.test.ts
interaction-touch-payload (7) .../interaction-touch-payload.test.ts (7) none
interaction-touch-prepare (5) .../interaction-touch-prepare.test.ts (5) none
interaction-touch-android-freshness (4) .../interaction-touch-android-freshness.test.ts (4) none
interaction-touch-android-readiness (7) .../interaction-touch-android-readiness.test.ts (7) none
root interaction-touch-android-readiness-spawns (1) .../interaction-touch-android-readiness-spawns.test.ts (1; real createAndroidObservationAdapter over a counting runAdb host port: six dumpsys calls for three taps) none
interaction-touch-direct-ios-eligibility (4) .../interaction-touch-direct-ios-eligibility.test.ts (4) none
interaction-touch-direct-ios (6) .../interaction-touch-direct-ios.test.ts (6, incl. the maestro-direct disclosure row loop) none
interaction-ios-tap-outcome (22) .../interaction-ios-tap-outcome.test.ts (22) none
interaction-gesture-drag (2) .../interaction-gesture-drag.test.ts (2) drag endpoint disclosure already pinned by gestures.test.ts
interaction-get (16) execution/selector-runtime-get-reads.test.ts (9), execution/selector-runtime-is-reads.test.ts (5) selector-is-visibility.test.ts (2), selector-read-disambiguation.test.ts (1)

127 tests before; 131 after (the added ones are the fill dispatch-and-record case, the disambiguation case, and splits). The interaction-touch.test.ts original was deleted with its package copy (12 = 12); nothing was dropped without a named owner.

Selection

All destinations match packages/*/src/**/*.test.ts or src/**/*.test.ts, so unit-core selects them (pnpm test:unit, test:coverage); the sources were already in unit-core. check:affected --run runs the vitest related leg over them.

vi.mock (each reads what its mock provided or recorded)

  • interaction-touch-android-readiness.test.ts, platform-android/mechanics: the mocked getAndroidAppState result is read by "tap likely escaped the app", "foregrounded com.android.settings" and the permission-dialog warning.
  • same file, ../../android-system-dialog.ts: its spy drives "a ref action aborts with the shared ref_frame_expired rejection after Android dialog recovery mutates" (details.reason === 'ref_frame_expired', mockTapPoint not called).
  • interaction-touch-payload.test.ts, platform-android/mechanics: getAndroidScreenSize is read by toHaveBeenCalledTimes(1), not.toHaveBeenCalled() and the 1344×2992 reference-size assertions.
  • interaction-touch-android-freshness.test.ts, platform-android/mechanics: a benign default that keeps the readiness check from spawning adb; the response.ok assertions depend on it. No assertion reads its recordings.
  • selector-runtime-is-reads.test.ts, platform-android/mechanics: mockGetAndroidAppState is read by the permission-dialog blocker test (foregroundPackage in details).
  • selector-runtime-get-reads.test.ts, platform-apple/runner/operations: a negative guard. expect(mockRunAppleRunnerCommand).not.toHaveBeenCalled() and the querySelector not.toHaveBeenCalledWith fail if the daemon starts reaching the runner for a get again, instead of silently running it. It passes today because the daemon no longer queries the runner.

Subpath exports

Package Before (#3670) After
daemon 85 81

Deleted: __tests__/ios-comparison-key-fixture, __tests__/replay-runtime/replay-command-fixture, interaction/internal/interaction-ios-tap-outcome, interaction/internal/interaction-touch-android-readiness. No other package changed; no new subpath, barrel or export *.

Notes

  • The touch fake's target resolution is a deliberate minimum (points resolve to themselves; a @ref is the centre of its node in refFrameSnapshot/snapshot; a selector is the first matching node of one fresh capture; preresolvedTarget is honored). It has no promotion, occlusion, off-screen, bounds, label-recovery or verify-evidence logic. Those are asserted as (c) tests against the real runtime, so its default answers do not stand in for command behavior.
  • Real-surface composition legs removed from the daemon tests are covered by test/integration/interaction-contract/* and provider-scenarios/press-selector-resolution.test.ts; I have not confirmed those assert the same cases one for one.
  • One-line path repoints for registries that named moved tests: packages/contracts/src/dispatch-disclosure.fixtures.ts (maestro-direct. owner) and packages/test-kit/src/boundary-fault-matrix.ts.
  • (c) tests are additive files in src/commands/interaction/runtime/, outside packages/daemon; say if any belongs to another worker.

Validation

check:quick, check:layering, check:fallow and check:gate-manifest pass on this head; check:affected --run passes (including test-file-size-ratchet and the eager-closure suite).

🤖 Generated with Claude Code

View guided diff Turn on auto-fix

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 33 files

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

Comment thread src/commands/interaction/runtime/selector-is-visibility.test.ts
Comment thread src/commands/interaction/runtime/interactions-ref-frame-evidence.test.ts Outdated
Comment thread packages/daemon/src/__tests__/support/touch-command-surface-fake.ts Outdated
Comment thread packages/daemon/src/__tests__/support/touch-command-surface-fake.ts Outdated
Comment thread packages/daemon/src/execution/selector-runtime-is-reads.test.ts
@thymikee
thymikee force-pushed the refactor/daemon-interaction-touch-tests branch from 44dcc5d to cff2d79 Compare October 15, 2026 14:22
@thymikee
thymikee force-pushed the refactor/daemon-interaction-touch-tests branch from cff2d79 to 5b442e7 Compare October 15, 2026 14:35
@thymikee
thymikee force-pushed the refactor/daemon-interaction-touch-tests branch from 5b442e7 to 8bedf50 Compare October 15, 2026 14:36
@thymikee

Copy link
Copy Markdown
Member Author

Thanks for the careful move. At 8bedf50 the new touch-interaction-runner.ts re-declares contextFromFlags (https://github.com/callstack/agent-device/blob/8bedf50/packages/daemon/src/__tests__/support/touch-interaction-runner.ts#L8), which already exists as an export in https://github.com/callstack/agent-device/blob/8bedf50/packages/daemon/src/execution/interaction/internal/__tests__/interaction-touch-fixtures.ts#L65. The deleted runner imported that export, and the new one already imports the sibling fixture, so nothing blocks reuse. With two copies, a new request flag added to one will make runInteraction and the sibling suites diverge without any failure. Please keep one projection: import it from interaction-touch-fixtures.ts, or move it to support/ and have both import it.

Not blocking, and fine to take or leave: some new test names do not match the module beside them (interaction-touch-routing has no sibling module, interaction-touch-direct-ios-eligibility sits away from its module, and interaction-touch-android-readiness-spawns now counts runAdb calls and spawns nothing), and several daemon-half assertions read values the touch-command-surface fake invents (the always-pass is, the fixed READ_CAPTURE_OPTIONS, the made-up verify evidence), so the test headers should say which are fake-supplied and covered by the real-surface tests, and the as never casts could use the typed return shapes.

Of the open inline threads, none still apply: all nine fixed-at-head or outdated ones can be resolved (#3672 (comment), #3672 (comment), #3672 (comment), #3672 (comment), #3672 (comment), #3672 (comment), #3672 (comment), #3672 (comment), #3672 (comment)), and the fill-contrast thread is benign because the stale-ref case asserts the fill point is not called (#3672 (comment)).

CI has not run yet: all 13 remaining checks are queued, with no failures. I did not run the tests myself. Before merge, please remove the duplicate contextFromFlags and wait for Typecheck, Repo Guards, Coverage and Integration to pass on the new head.

@thymikee
thymikee added this pull request to stack #3681 October 15, 2026 15:46
@github-actions

github-actions Bot commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Size Report

Metric Base Current Diff
Installed (including dependencies) 5.24 MB 5.24 MB 0 B
Package (unpacked) 5.24 MB 5.24 MB 0 B
Package (download) 1.57 MB 1.57 MB -3 B

Startup median (7 runs, lower is better):

Scenario Base Current Diff
CLI --version 27.7 ms 28.2 ms +0.5 ms
CLI --help 78.8 ms 80.7 ms +1.9 ms

thymikee and others added 12 commits October 15, 2026 17:59
…mand surface fake

The interactor-resolution fixture replaces the snapshot-interactor-capture module mock: captures
reach one spy through the installed InteractorResolution. The touch surface fake dispatches resolved
targets through the runtime config's backend and reads the session through its store, so daemon
suites pin admission, bound-operation wiring, response shaping, and capture wiring without the
real command family.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nteraction handler

interaction-touch.test.ts is (a) daemon logic: router claims press/click/longpress/hover/fill,
single inspect/bind, macOS surface refusals, hover capability refusal. All 12 tests move to
execution/interaction/internal/interaction-touch-routing.test.ts on the touch surface fake.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t each asserts

interaction-touch-press-admission.test.ts: (a) 12 tests move to the daemon package on the touch
surface fake; the #1654 provenance-mismatch test is (c) and moves to
src/commands/interaction/runtime/interactions-preresolved-target.test.ts.

interaction-touch-press.test.ts: (a) secondary-button projection, the find pre-resolved wiring
(adopt, control, no lookup) and read-after-press capture stay in the daemon package; the macOS
menubar wrapper promotion, hittable-ancestor promotion, full-screen non-promotion (interactions-
target-promotion.test.ts) and the pre-resolved adopt/control/no-lookup/shared-guard cases
(interactions-preresolved-target.test.ts) are (c) and run against the real command runtime.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…asserts

interaction-touch-fill.test.ts: (a) expired-frame rejection, pinned stale/current ref, and the
find-internal fill without a stale-ref warning move to the daemon package (plus one new test that
the resolved point, typing delay and recorded ref reach the bound fill). The off-screen refusal,
invalid-bounds refusal and editable-node retention are (c): interactions-ref-frame-evidence.test.ts
and interactions-target-promotion.test.ts.

interaction-touch-runtime.test.ts: (a) the resolved-point tap/record test and the #1542 rescue
hook wiring (hook present iff the owner advertises querySelector) move to the daemon package; the
invalid-bounds, off-screen refusal and in-frame label recovery cases are (c) in
interactions-ref-frame-evidence.test.ts (the missing-rect refusal is already
ref-target-resolution.test.ts).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ide their modules

interaction-touch-response.test.ts (6), interaction-touch-payload.test.ts (7) and
interaction-touch-prepare.test.ts (5) are (a) daemon logic: evidence rides the response allowlist
only when --verify ran, recording/visualization payloads and reference frames, and the settle
observation capture the request admitted. They move to execution/interaction/internal/ on the touch
surface fake, the interactor-resolution fixture and daemonPlatformServicesFixture. The --verify
evidence computation itself stays covered by the real-runtime tests in
src/commands/interaction/runtime/interactions.test.ts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ide their modules

interaction-touch-android-freshness.test.ts (4) and interaction-touch-android-readiness.test.ts
(7) are (a) daemon logic: the freshness refresh may not retarget an admitted ref, the escape and
permission-prompt outcomes, and recovery aborting an admitted ref. They move to
execution/interaction/internal/. The readiness suite now mocks the exact android-system-dialog
module the handler imports.

interaction-touch-android-readiness-spawns.test.ts is (a): the two-checks-per-tap dumpsys budget is
pinned through the real Android observation adapter over a counting runAdb host port instead of
the root adb provider and host binding.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…press handler

interaction-touch-direct-ios.test.ts (6) and interaction-touch-direct-ios-eligibility.test.ts (4)
are (a) daemon logic: the Maestro-only direct selector route, its error passthrough, the fused
ref-frame seam, and the maestro-direct dispatch-disclosure rows. They move to
execution/interaction/internal/. The dispatch-disclosure driver owner and the boundary-fault-matrix
evidence path follow the moved driver file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ir modules

interaction-ios-tap-outcome.test.ts (22) and interaction-gesture-drag.test.ts (2) are (a) daemon
logic: same-scope corroboration of a failed iOS tap, the recorded drag with both endpoint
identities, one inspect/bind per drag, and frame expiry. They move to
execution/interaction/internal/ on the touch surface fake (which now answers a target-authored
drag through one backend plan). The drag endpoint resolution and disclosure stay covered by
src/commands/interaction/runtime/gestures.test.ts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
interaction-get.test.ts: (a) the 9 get tests (bound read expansion, no live read, retired direct
selector, frame-tree @ref reads and their warnings) move to execution/selector-runtime-get-reads
.test.ts and the 5 is tests that pin capture wiring and the Android foreground blocker move to
execution/selector-runtime-is-reads.test.ts. The two predicate-semantics tests (is visible
inheriting ancestor viewport visibility, is visible failing outside the viewport) are (c) in
src/commands/interaction/runtime/selector-is-visibility.test.ts, and the same-label disambiguation
read is (c) in selector-read-disambiguation.test.ts. Nothing imports the root
interaction-touch-runner helper any more, so it is deleted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…h fake's drag and ref routes follow production

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nd say which fake values are fake-supplied

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@thymikee
thymikee force-pushed the refactor/daemon-interaction-touch-tests branch from 8bedf50 to f311725 Compare October 15, 2026 16:00
@thymikee

Copy link
Copy Markdown
Member Author

Fixed in f311725. touch-interaction-runner.ts imports the one contextFromFlags from interaction-touch-fixtures.ts instead of re-declaring it. The only other copy, in interaction-ios-tap-outcome.test.ts, is a deliberate superset (it adds snapshotPreferredBackend for the corroboration capture), so I left it. The not-blocking asks: the header of touch-command-surface-fake.ts now lists the values the fake supplies itself (is always passes with one match, reads capture with READ_CAPTURE_OPTIONS, --verify evidence is a fake-digest-<n> placeholder) and names src/commands/interaction/runtime/ as where the real values are asserted. I took no action on the test names or the as never casts; both are cosmetic and I'd rather not widen this PR.

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 2 files (changes from recent commits).

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/daemon/src/__tests__/support/touch-command-surface-fake.ts">

<violation number="1" location="packages/daemon/src/__tests__/support/touch-command-surface-fake.ts:45">
P3: This overstates capture behavior: `get` on a point or `@ref` does not capture a snapshot. Specify that selector reads use `READ_CAPTURE_OPTIONS` so the documentation does not imply capture wiring for every read.</violation>
</file>

Comment thread packages/daemon/src/__tests__/support/touch-command-surface-fake.ts Outdated
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@thymikee

Copy link
Copy Markdown
Member Author

This PR is ready at 75f4e4c. The two points from the earlier review (#3672 (comment)) are fixed: the shared runner import and the fake's header comments now match the code. Not blocking: packages/daemon/src/__tests__/support/touch-interaction-runner.ts line 3 imports its projection from a feature-local __tests__/ fixture, which points the dependency the wrong way, and if a third consumer appears outside execution/interaction, moving contextFromFlags into __tests__/support would fix it, but you can take or leave this.

The one earlier thread on the fake's header (#3672 (comment)) no longer applies. The header now says that only selector reads (get, is) capture, once, and that point and @ref reads capture nothing, which matches the fake. You can resolve it.

I did not run the test suites. This is a test relocation, so the moved assertions are the evidence. I did not re-diff the full 33-file move, because the earlier review covered it and this change touches only the runner import and two header comments. CI has not run on this head yet, and all checks are queued with none failed. Before merge, CI needs to go green on 75f4e4c, and #3670 needs to land first so this PR's base is main.

@thymikee thymikee added the ready-for-human Valid work that needs human implementation, judgment, or maintainer merge label Oct 15, 2026
@thymikee
thymikee merged commit 9c06cd2 into main Oct 15, 2026
20 checks passed
@thymikee
thymikee deleted the refactor/daemon-interaction-touch-tests branch October 15, 2026 16:50
@github-actions

Copy link
Copy Markdown
Contributor
PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-10-15 17:13 UTC

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-for-human Valid work that needs human implementation, judgment, or maintainer merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant