From 70c4e35829f69b63f06509e3390a01120f56d360 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Tue, 7 Jul 2026 13:44:51 +0200 Subject: [PATCH 1/2] fix: omit interaction uptime from wire responses --- src/daemon/handlers/interaction-touch-response.ts | 1 + .../interaction-response-shape.contract.test.ts | 14 +++++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/daemon/handlers/interaction-touch-response.ts b/src/daemon/handlers/interaction-touch-response.ts index 4bf01317f..8888287a8 100644 --- a/src/daemon/handlers/interaction-touch-response.ts +++ b/src/daemon/handlers/interaction-touch-response.ts @@ -183,6 +183,7 @@ function shouldKeepWireBackendField(key: string, value: unknown): boolean { switch (key) { case 'gestureStartUptimeMs': case 'gestureEndUptimeMs': + case 'currentUptimeMs': case 'sequenceResults': return false; case 'count': diff --git a/test/integration/interaction-contract/interaction-response-shape.contract.test.ts b/test/integration/interaction-contract/interaction-response-shape.contract.test.ts index 328e2b7a0..5b5c7a429 100644 --- a/test/integration/interaction-contract/interaction-response-shape.contract.test.ts +++ b/test/integration/interaction-contract/interaction-response-shape.contract.test.ts @@ -40,9 +40,20 @@ const SELECTOR_KEYS = [ const POINT_KEYS = ['message', 'targetKind', 'x', 'y'] as const; +const NOISY_DEFAULT_TAP_RESULT = { + count: 1, + currentUptimeMs: 1_418_719_052.9, + doubleTap: false, + gestureEndUptimeMs: 1_418_719_052.338875, + gestureStartUptimeMs: 1_418_718_741.4275, + holdMs: 0, + intervalMs: 0, + jitterPx: 0, +}; + test('interaction response shape: press @ref uses the canonical ref envelope', async () => { await withIosContractDaemon( - [runnerSnapshotEntry(RUNNER_CONTINUE_NODES), runnerTapEntry({})], + [runnerSnapshotEntry(RUNNER_CONTINUE_NODES), runnerTapEntry(NOISY_DEFAULT_TAP_RESULT)], async (daemon, transcript) => { assertRpcOk(await daemon.callCommand('snapshot', [], { snapshotInteractiveOnly: true })); @@ -317,6 +328,7 @@ function assertExactKeys(data: WireData, expectedKeys: readonly string[]): void function assertNoWireNoise(data: WireData): void { for (const key of [ 'count', + 'currentUptimeMs', 'doubleTap', 'gestureEndUptimeMs', 'gestureStartUptimeMs', From 70b861e6f582be1b9b25d2f5f285af30f9c70f69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Tue, 7 Jul 2026 14:16:14 +0200 Subject: [PATCH 2/2] fix: simplify interaction wire sanitizer --- .../handlers/interaction-touch-response.ts | 35 ++++++++++--------- ...nteraction-response-shape.contract.test.ts | 23 ++++++------ 2 files changed, 31 insertions(+), 27 deletions(-) diff --git a/src/daemon/handlers/interaction-touch-response.ts b/src/daemon/handlers/interaction-touch-response.ts index 8888287a8..305069e33 100644 --- a/src/daemon/handlers/interaction-touch-response.ts +++ b/src/daemon/handlers/interaction-touch-response.ts @@ -179,24 +179,25 @@ function sanitizeWireBackendData( return Object.keys(sanitized).length > 0 ? sanitized : undefined; } +const OMITTED_WIRE_BACKEND_FIELDS = new Set([ + 'gestureStartUptimeMs', + 'gestureEndUptimeMs', + 'currentUptimeMs', + 'sequenceResults', +]); + +const DEFAULT_WIRE_BACKEND_FIELD_VALUES = new Map([ + ['count', 1], + ['intervalMs', 0], + ['holdMs', 0], + ['jitterPx', 0], + ['doubleTap', false], +]); + function shouldKeepWireBackendField(key: string, value: unknown): boolean { - switch (key) { - case 'gestureStartUptimeMs': - case 'gestureEndUptimeMs': - case 'currentUptimeMs': - case 'sequenceResults': - return false; - case 'count': - return value !== 1; - case 'intervalMs': - case 'holdMs': - case 'jitterPx': - return value !== 0; - case 'doubleTap': - return value !== false; - default: - return true; - } + if (OMITTED_WIRE_BACKEND_FIELDS.has(key)) return false; + if (!DEFAULT_WIRE_BACKEND_FIELD_VALUES.has(key)) return true; + return value !== DEFAULT_WIRE_BACKEND_FIELD_VALUES.get(key); } function buildTouchMessage( diff --git a/test/integration/interaction-contract/interaction-response-shape.contract.test.ts b/test/integration/interaction-contract/interaction-response-shape.contract.test.ts index 5b5c7a429..be40cf663 100644 --- a/test/integration/interaction-contract/interaction-response-shape.contract.test.ts +++ b/test/integration/interaction-contract/interaction-response-shape.contract.test.ts @@ -128,17 +128,20 @@ test('interaction response shape: press selector uses the canonical selector env }); test('interaction response shape: fill selector uses the canonical selector envelope', async () => { - await withIosContractDaemon([runnerTypeEntry({ x: 200, y: 322 })], async (daemon, transcript) => { - const data = assertRpcOk(await daemon.callCommand('fill', ['label=Continue', 'Hello'])); - assertRunnerRequest(transcript, 'ios.runner.type', selectorTypeRequest('Hello')); + await withIosContractDaemon( + [runnerTypeEntry({ ...NOISY_DEFAULT_TAP_RESULT, x: 200, y: 322 })], + async (daemon, transcript) => { + const data = assertRpcOk(await daemon.callCommand('fill', ['label=Continue', 'Hello'])); + assertRunnerRequest(transcript, 'ios.runner.type', selectorTypeRequest('Hello')); - assertCanonicalDirectSelector(data, { - message: 'Filled 5 chars', - x: 200, - y: 322, - extra: { delayMs: 0, text: 'Hello' }, - }); - }); + assertCanonicalDirectSelector(data, { + message: 'Filled 5 chars', + x: 200, + y: 322, + extra: { delayMs: 0, text: 'Hello' }, + }); + }, + ); }); test('interaction response shape: longpress selector uses the canonical selector envelope', async () => {