Skip to content

Commit fe383db

Browse files
thymikeeclaude
andcommitted
refactor: migrate interaction + snapshot chains from test-only DI to vitest mocking
Remove ~25 DI parameters threaded through the interaction and snapshot handler chains that existed solely for testability. Functions now import their dependencies directly; tests use vi.mock to intercept them. Interaction chain: dispatch, readAndroidScreenSize removed from interaction.ts, interaction-touch.ts, interaction-common.ts, interaction-read.ts, interaction-selector.ts, interaction-snapshot.ts, interaction-get.ts, interaction-is.ts, interaction-scroll.ts, find.ts. Snapshot chain: dispatchSnapshotCommand, runnerCommand, sessionlessRunnerCleanup removed from snapshot.ts, snapshot-wait.ts, snapshot-alert.ts, snapshot-capture.ts. Old node:test files replaced with vitest equivalents using vi.mock. Closes #282 Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
1 parent efb45ec commit fe383db

16 files changed

Lines changed: 555 additions & 658 deletions

src/daemon/handlers/__tests__/interaction.test.ts renamed to src/daemon/handlers/__tests__/interaction.vitest.ts

Lines changed: 354 additions & 383 deletions
Large diffs are not rendered by default.

src/daemon/handlers/__tests__/snapshot-handler.test.ts renamed to src/daemon/handlers/__tests__/snapshot-handler.vitest.ts

Lines changed: 170 additions & 156 deletions
Large diffs are not rendered by default.

‎src/daemon/handlers/find.ts‎

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@ export async function handleFindCommands(params: {
6262
return { nodes: lastNodes };
6363
}
6464
const { snapshot } = await captureSnapshot({
65-
dispatchSnapshotCommand: dispatchCommand,
6665
device,
6766
session,
6867
flags: {
@@ -167,7 +166,6 @@ export async function handleFindCommands(params: {
167166
surface: session?.surface,
168167
contextFromFlags: (flags, appBundleId, traceLogPath) =>
169168
contextFromFlags(logPath, flags, appBundleId, traceLogPath),
170-
dispatch: dispatchCommand,
171169
});
172170
if (session) {
173171
sessionStore.recordAction(session, {

‎src/daemon/handlers/interaction-common.ts‎

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ export type InteractionHandlerParams = {
1515
sessionName: string;
1616
sessionStore: SessionStore;
1717
contextFromFlags: ContextFromFlags;
18-
dispatch: typeof dispatchCommand;
1918
};
2019

2120
export function buildTouchVisualizationResult(params: {
@@ -42,7 +41,6 @@ export async function dispatchRecordedTouchInteraction(params: {
4241
requestPositionals: string[];
4342
flags: CommandFlags | undefined;
4443
contextFromFlags: ContextFromFlags;
45-
dispatch: typeof dispatchCommand;
4644
interactionCommand: string;
4745
interactionPositionals: string[];
4846
outPath: string | undefined;
@@ -63,7 +61,6 @@ export async function dispatchRecordedTouchInteraction(params: {
6361
requestPositionals,
6462
flags,
6563
contextFromFlags,
66-
dispatch,
6764
interactionCommand,
6865
interactionPositionals,
6966
outPath,
@@ -73,7 +70,6 @@ export async function dispatchRecordedTouchInteraction(params: {
7370
session,
7471
flags,
7572
contextFromFlags,
76-
dispatch,
7773
command: interactionCommand,
7874
positionals: interactionPositionals,
7975
outPath,
@@ -96,7 +92,6 @@ async function dispatchInteractionCommand(params: {
9692
session: SessionState;
9793
flags: CommandFlags | undefined;
9894
contextFromFlags: ContextFromFlags;
99-
dispatch: typeof dispatchCommand;
10095
command: string;
10196
positionals: string[];
10297
outPath: string | undefined;
@@ -105,12 +100,12 @@ async function dispatchInteractionCommand(params: {
105100
actionStartedAt: number;
106101
actionFinishedAt: number;
107102
}> {
108-
const { session, flags, contextFromFlags, dispatch, command, positionals, outPath } = params;
103+
const { session, flags, contextFromFlags, command, positionals, outPath } = params;
109104
const actionStartedAt = Date.now();
110105
const dispatchContext = {
111106
...contextFromFlags(flags, session.appBundleId, session.trace?.outPath),
112107
};
113-
const rawData = await dispatch(session.device, command, positionals, outPath, dispatchContext);
108+
const rawData = await dispatchCommand(session.device, command, positionals, outPath, dispatchContext);
114109
const actionFinishedAt = Date.now();
115110
const data = rawData && typeof rawData === 'object' ? rawData : undefined;
116111
return { data, actionStartedAt, actionFinishedAt };

‎src/daemon/handlers/interaction-get.ts‎

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { resolveRefTarget } from './interaction-targeting.ts';
88
import { resolveSelectorTarget } from './interaction-selector.ts';
99

1010
export async function handleGetCommand(params: InteractionHandlerParams): Promise<DaemonResponse> {
11-
const { req, sessionName, sessionStore, contextFromFlags, dispatch } = params;
11+
const { req, sessionName, sessionStore, contextFromFlags } = params;
1212
const sub = req.positionals?.[0];
1313
if (sub !== 'text' && sub !== 'attrs') {
1414
return {
@@ -65,7 +65,6 @@ export async function handleGetCommand(params: InteractionHandlerParams): Promis
6565
traceOutPath: session.trace?.outPath,
6666
surface: session.surface,
6767
contextFromFlags,
68-
dispatch,
6968
});
7069
sessionStore.recordAction(session, {
7170
command: req.command,
@@ -94,7 +93,6 @@ export async function handleGetCommand(params: InteractionHandlerParams): Promis
9493
requireRect: false,
9594
requireUnique: true,
9695
disambiguateAmbiguous: sub === 'text',
97-
dispatch,
9896
});
9997
if (!resolvedSelectorTarget.ok) return resolvedSelectorTarget.response;
10098
const { resolved } = resolvedSelectorTarget;
@@ -119,7 +117,6 @@ export async function handleGetCommand(params: InteractionHandlerParams): Promis
119117
traceOutPath: session.trace?.outPath,
120118
surface: session.surface,
121119
contextFromFlags,
122-
dispatch,
123120
});
124121
sessionStore.recordAction(session, {
125122
command: req.command,

‎src/daemon/handlers/interaction-is.ts‎

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { captureSnapshotForSession } from './interaction-snapshot.ts';
1212
import { resolveSelectorTarget } from './interaction-selector.ts';
1313

1414
export async function handleIsCommand(params: InteractionHandlerParams): Promise<DaemonResponse> {
15-
const { req, sessionName, sessionStore, contextFromFlags, dispatch } = params;
15+
const { req, sessionName, sessionStore, contextFromFlags } = params;
1616
const predicate = (req.positionals?.[0] ?? '').toLowerCase();
1717
if (!isSupportedPredicate(predicate)) {
1818
return {
@@ -73,7 +73,6 @@ export async function handleIsCommand(params: InteractionHandlerParams): Promise
7373
sessionStore,
7474
contextFromFlags,
7575
{ interactiveOnly: false },
76-
dispatch,
7776
);
7877
const matched = findSelectorChainMatch(snapshot.nodes, chain, {
7978
platform: session.device.platform,
@@ -116,7 +115,6 @@ export async function handleIsCommand(params: InteractionHandlerParams): Promise
116115
requireRect: false,
117116
requireUnique: true,
118117
disambiguateAmbiguous: false,
119-
dispatch,
120118
});
121119
if (!resolvedSelectorTarget.ok) return resolvedSelectorTarget.response;
122120
const { resolved } = resolvedSelectorTarget;

‎src/daemon/handlers/interaction-read.ts‎

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,16 @@ export async function readTextForNode(params: {
1414
traceOutPath?: string;
1515
surface?: SessionState['surface'];
1616
contextFromFlags: ContextFromFlags;
17-
dispatch: typeof dispatchCommand;
1817
}): Promise<string> {
19-
const { device, node, flags, appBundleId, traceOutPath, surface, contextFromFlags, dispatch } =
20-
params;
18+
const { device, node, flags, appBundleId, traceOutPath, surface, contextFromFlags } = params;
2119
const fallbackText = extractNodeReadText(node);
2220
const center = resolveRectCenter(node.rect);
2321
if (!center) {
2422
return fallbackText;
2523
}
2624

2725
try {
28-
const rawData = await dispatch(
26+
const rawData = await dispatchCommand(
2927
device,
3028
'read',
3129
[String(center.x), String(center.y)],

‎src/daemon/handlers/interaction-scroll.ts‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { isCommandSupportedOnDevice } from '../../core/capabilities.ts';
2+
import { dispatchCommand } from '../../core/dispatch.ts';
23
import { buildSelectorChainForNode } from '../selectors.ts';
34
import { resolveRefLabel } from '../snapshot-processing.ts';
45
import { buildScrollIntoViewPlan, resolveViewportRect } from '../scroll-planner.ts';
@@ -10,7 +11,7 @@ import { resolveRefTarget } from './interaction-targeting.ts';
1011
export async function handleScrollIntoViewCommand(
1112
params: InteractionHandlerParams,
1213
): Promise<DaemonResponse | null> {
13-
const { req, sessionName, sessionStore, contextFromFlags, dispatch } = params;
14+
const { req, sessionName, sessionStore, contextFromFlags } = params;
1415
const session = sessionStore.get(sessionName);
1516
if (!session) {
1617
return {
@@ -75,7 +76,7 @@ export async function handleScrollIntoViewCommand(
7576
});
7677
return { ok: true, data: { ref, attempts: 0, alreadyVisible: true } };
7778
}
78-
const data = await dispatch(
79+
const data = await dispatchCommand(
7980
session.device,
8081
'swipe',
8182
[String(plan.x), String(plan.startY), String(plan.x), String(plan.endY), '16'],

‎src/daemon/handlers/interaction-selector.ts‎

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import type { DaemonResponse, SessionState } from '../types.ts';
44
import type { SessionStore } from '../session-store.ts';
55
import { captureSnapshotForSession } from './interaction-snapshot.ts';
66
import type { ContextFromFlags } from './interaction-common.ts';
7-
import { dispatchCommand, type CommandFlags } from '../../core/dispatch.ts';
7+
import type { CommandFlags } from '../../core/dispatch.ts';
88

99
export async function resolveSelectorTarget(params: {
1010
command: string;
@@ -17,7 +17,6 @@ export async function resolveSelectorTarget(params: {
1717
requireRect: boolean;
1818
requireUnique: boolean;
1919
disambiguateAmbiguous: boolean;
20-
dispatch: typeof dispatchCommand;
2120
}): Promise<
2221
| {
2322
ok: true;
@@ -38,7 +37,6 @@ export async function resolveSelectorTarget(params: {
3837
requireRect,
3938
requireUnique,
4039
disambiguateAmbiguous,
41-
dispatch,
4240
} = params;
4341
const chain = parseSelectorChain(selectorExpression);
4442
const snapshot = await captureSnapshotForSession(
@@ -47,7 +45,6 @@ export async function resolveSelectorTarget(params: {
4745
sessionStore,
4846
contextFromFlags,
4947
{ interactiveOnly },
50-
dispatch,
5148
);
5249
const resolved = await withDiagnosticTimer(
5350
'selector_resolve',

‎src/daemon/handlers/interaction-snapshot.ts‎

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { dispatchCommand, type CommandFlags } from '../../core/dispatch.ts';
1+
import type { CommandFlags } from '../../core/dispatch.ts';
22
import type { SessionStore } from '../session-store.ts';
33
import type { SessionState } from '../types.ts';
44
import type { SnapshotState } from '../../utils/snapshot.ts';
@@ -11,7 +11,6 @@ export async function captureSnapshotForSession(
1111
sessionStore: SessionStore,
1212
contextFromFlags: ContextFromFlags,
1313
options: { interactiveOnly: boolean },
14-
dispatch: typeof dispatchCommand = dispatchCommand,
1514
): Promise<SnapshotState> {
1615
const effectiveFlags = {
1716
...(flags ?? {}),
@@ -24,7 +23,6 @@ export async function captureSnapshotForSession(
2423
session.trace?.outPath,
2524
);
2625
const { snapshot } = await captureSnapshot({
27-
dispatchSnapshotCommand: dispatch,
2826
device: session.device,
2927
session,
3028
flags: effectiveFlags,

0 commit comments

Comments
 (0)