Skip to content

Commit f32bd2e

Browse files
cameroncookecodex
andcommitted
fix(ui-automation): Address Warden contract feedback
Preserve zero-value compatibility for gesture duration and delta validation while keeping negative values rejected. Remove internal runtime snapshot wording from the public swipe manifest. Keep touch success text stable when the optional event is omitted and simplify runtime snapshot control flow. Co-Authored-By: Codex <noreply@openai.com>
1 parent 6cc93b2 commit f32bd2e

5 files changed

Lines changed: 30 additions & 30 deletions

File tree

manifests/tools/swipe.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ module: mcp/tools/ui-automation/swipe
33
names:
44
mcp: swipe
55
cli: swipe
6-
description: Swipe within a UI element by withinElementRef and direction from a current rs/1 runtime snapshot.
6+
description: Swipe within a scrollable UI element using an element reference from the latest runtime snapshot.
77
outputSchema:
88
schema: xcodebuildmcp.output.ui-action-result
99
version: "2"

src/mcp/tools/ui-automation/__tests__/gesture.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ describe('Gesture Plugin', () => {
4040
expect(schemaObj.safeParse({ preset: 'scroll-up', screenWidth: 2001 }).success).toBe(false);
4141
expect(schemaObj.safeParse({ preset: 'scroll-up', screenHeight: 3001 }).success).toBe(false);
4242
expect(schemaObj.safeParse({ preset: 'scroll-up', duration: -1 }).success).toBe(false);
43-
expect(schemaObj.safeParse({ preset: 'scroll-up', duration: 0 }).success).toBe(false);
43+
expect(schemaObj.safeParse({ preset: 'scroll-up', duration: 0 }).success).toBe(true);
44+
expect(schemaObj.safeParse({ preset: 'scroll-up', delta: 0 }).success).toBe(true);
4445
expect(schemaObj.safeParse({ preset: 'scroll-up', delta: 201 }).success).toBe(false);
4546

4647
const withSimId = schemaObj.safeParse({

src/mcp/tools/ui-automation/gesture.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,13 @@ const gestureSchema = z.object({
6767
),
6868
duration: z
6969
.number()
70-
.positive({ message: 'Duration must be greater than 0 seconds' })
70+
.min(0, { message: 'Duration must be non-negative' })
7171
.max(10, { message: 'Duration must be at most 10 seconds' })
7272
.optional()
7373
.describe('Duration of the gesture in seconds.'),
7474
delta: z
7575
.number()
76-
.positive({ message: 'Delta must be greater than 0' })
76+
.min(0, { message: 'Delta must be non-negative' })
7777
.max(200, { message: 'Delta must be at most 200' })
7878
.optional()
7979
.describe('Distance to move in pixels.'),

src/mcp/tools/ui-automation/shared/runtime-snapshot.ts

Lines changed: 24 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -533,21 +533,17 @@ export function extractAccessibilityHierarchy(responseText: string): Accessibili
533533
throw new RuntimeSnapshotParseError(`AXe describe-ui returned invalid JSON: ${message}`);
534534
}
535535

536-
const hierarchy = (() => {
537-
if (Array.isArray(parsed)) {
538-
return parsed as AccessibilityNode[];
539-
}
540-
541-
if (isRecord(parsed) && Array.isArray(parsed.elements)) {
542-
return parsed.elements as AccessibilityNode[];
543-
}
536+
if (Array.isArray(parsed)) {
537+
return parsed as AccessibilityNode[];
538+
}
544539

545-
throw new RuntimeSnapshotParseError(
546-
'AXe describe-ui did not return an accessibility element array.',
547-
);
548-
})();
540+
if (isRecord(parsed) && Array.isArray(parsed.elements)) {
541+
return parsed.elements as AccessibilityNode[];
542+
}
549543

550-
return hierarchy;
544+
throw new RuntimeSnapshotParseError(
545+
'AXe describe-ui did not return an accessibility element array.',
546+
);
551547
}
552548

553549
export function createRuntimeSnapshotRecord(params: {
@@ -728,18 +724,21 @@ export function getRuntimeElementSwipePoints(
728724
const top = Math.round(frame.y + verticalInset);
729725
const bottom = Math.round(frame.y + frame.height - verticalInset);
730726

731-
const points = ((): { from: Point; to: Point } => {
732-
switch (direction) {
733-
case 'up':
734-
return { from: { x: center.x, y: bottom }, to: { x: center.x, y: top } };
735-
case 'down':
736-
return { from: { x: center.x, y: top }, to: { x: center.x, y: bottom } };
737-
case 'left':
738-
return { from: { x: right, y: center.y }, to: { x: left, y: center.y } };
739-
case 'right':
740-
return { from: { x: left, y: center.y }, to: { x: right, y: center.y } };
741-
}
742-
})();
727+
let points: { from: Point; to: Point };
728+
switch (direction) {
729+
case 'up':
730+
points = { from: { x: center.x, y: bottom }, to: { x: center.x, y: top } };
731+
break;
732+
case 'down':
733+
points = { from: { x: center.x, y: top }, to: { x: center.x, y: bottom } };
734+
break;
735+
case 'left':
736+
points = { from: { x: right, y: center.y }, to: { x: left, y: center.y } };
737+
break;
738+
case 'right':
739+
points = { from: { x: left, y: center.y }, to: { x: right, y: center.y } };
740+
break;
741+
}
743742

744743
if (isDegenerateSwipe(points.from, points.to)) {
745744
return {

src/utils/renderers/domain-result-text.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2376,7 +2376,7 @@ function createSpecialCaseItems(
23762376
break;
23772377
}
23782378
case 'touch':
2379-
successMessage = `Touch event (${result.action.event}) on elementRef ${result.action.elementRef} executed successfully.`;
2379+
successMessage = `Touch event (${result.action.event ?? 'touch'}) on elementRef ${result.action.elementRef} executed successfully.`;
23802380
break;
23812381
case 'long-press':
23822382
successMessage = `Long press on elementRef ${result.action.elementRef} for ${result.action.durationMs}ms simulated successfully.`;

0 commit comments

Comments
 (0)