1- /**
2- * Tests for snapshot_ui tool plugin
3- */
4-
51import { describe , it , expect } from 'vitest' ;
62import * as z from 'zod' ;
73import { createMockExecutor , createNoopExecutor } from '../../../../test-utils/mock-executors.ts' ;
84import type { CommandExecutor } from '../../../../utils/execution/index.ts' ;
95import { schema , handler , snapshot_uiLogic } from '../snapshot_ui.ts' ;
106import { AXE_NOT_AVAILABLE_MESSAGE } from '../../../../utils/axe-helpers.ts' ;
7+ import { allText , createMockToolHandlerContext } from '../../../../test-utils/test-helpers.ts' ;
8+
9+ const runLogic = async ( logic : ( ) => Promise < unknown > ) => {
10+ const { result, run } = createMockToolHandlerContext ( ) ;
11+ const response = await run ( logic ) ;
12+
13+ if (
14+ response &&
15+ typeof response === 'object' &&
16+ 'content' in ( response as Record < string , unknown > )
17+ ) {
18+ return response as {
19+ content : Array < { type : string ; text ?: string ; data ?: string ; mimeType ?: string } > ;
20+ isError ?: boolean ;
21+ nextStepParams ?: unknown ;
22+ } ;
23+ }
24+
25+ const text = result . text ( ) ;
26+ const textContent = text . length > 0 ? [ { type : 'text' as const , text } ] : [ ] ;
27+ const imageContent = result . attachments . map ( ( attachment ) => ( {
28+ type : 'image' as const ,
29+ data : attachment . data ,
30+ mimeType : attachment . mimeType ,
31+ } ) ) ;
32+
33+ return {
34+ content : [ ...textContent , ...imageContent ] ,
35+ isError : result . isError ( ) ? true : undefined ,
36+ nextStepParams : result . nextStepParams ,
37+ attachments : result . attachments ,
38+ text,
39+ } ;
40+ } ;
1141
1242describe ( 'Snapshot UI Plugin' , ( ) => {
1343 describe ( 'Export Field Validation (Literal)' , ( ) => {
@@ -33,8 +63,8 @@ describe('Snapshot UI Plugin', () => {
3363 const result = await handler ( { } ) ;
3464
3565 expect ( result . isError ) . toBe ( true ) ;
36- expect ( result . content [ 0 ] . text ) . toContain ( 'Missing required session defaults' ) ;
37- expect ( result . content [ 0 ] . text ) . toContain ( 'simulatorId is required' ) ;
66+ expect ( allText ( result ) ) . toContain ( 'Missing required session defaults' ) ;
67+ expect ( allText ( result ) ) . toContain ( 'simulatorId is required' ) ;
3868 } ) ;
3969
4070 it ( 'should handle invalid simulatorId format via schema validation' , async ( ) => {
@@ -44,8 +74,8 @@ describe('Snapshot UI Plugin', () => {
4474 } ) ;
4575
4676 expect ( result . isError ) . toBe ( true ) ;
47- expect ( result . content [ 0 ] . text ) . toContain ( 'Parameter validation failed' ) ;
48- expect ( result . content [ 0 ] . text ) . toContain ( 'Invalid Simulator UUID format' ) ;
77+ expect ( allText ( result ) ) . toContain ( 'Parameter validation failed' ) ;
78+ expect ( allText ( result ) ) . toContain ( 'Invalid Simulator UUID format' ) ;
4979 } ) ;
5080
5181 it ( 'should return success for valid snapshot_ui execution' , async ( ) => {
@@ -63,10 +93,6 @@ describe('Snapshot UI Plugin', () => {
6393 const mockAxeHelpers = {
6494 getAxePath : ( ) => '/usr/local/bin/axe' ,
6595 getBundledAxeEnvironment : ( ) => ( { } ) ,
66- createAxeNotAvailableResponse : ( ) => ( {
67- content : [ { type : 'text' as const , text : 'axe not available' } ] ,
68- isError : true ,
69- } ) ,
7096 } ;
7197
7298 // Wrap executor to track calls
@@ -76,12 +102,14 @@ describe('Snapshot UI Plugin', () => {
76102 return mockExecutor ( ...args ) ;
77103 } ;
78104
79- const result = await snapshot_uiLogic (
80- {
81- simulatorId : '12345678-1234-4234-8234-123456789012' ,
82- } ,
83- trackingExecutor ,
84- mockAxeHelpers ,
105+ const result = await runLogic ( ( ) =>
106+ snapshot_uiLogic (
107+ {
108+ simulatorId : '12345678-1234-4234-8234-123456789012' ,
109+ } ,
110+ trackingExecutor ,
111+ mockAxeHelpers ,
112+ ) ,
85113 ) ;
86114
87115 expect ( executorCalls [ 0 ] ) . toEqual ( [
@@ -91,22 +119,17 @@ describe('Snapshot UI Plugin', () => {
91119 { env : { } } ,
92120 ] ) ;
93121
94- expect ( result ) . toEqual ( {
95- content : [
96- {
97- type : 'text' as const ,
98- text : 'Accessibility hierarchy retrieved successfully:\n```json\n{"elements": [{"type": "Button", "frame": {"x": 100, "y": 200, "width": 50, "height": 30}}]}\n```' ,
99- } ,
100- {
101- type : 'text' as const ,
102- text : 'Tips:\n- Use frame coordinates for tap/swipe (center: x+width/2, y+height/2)\n- If a debugger is attached, ensure the app is running (not stopped on breakpoints)\n- Screenshots are for visual verification only' ,
103- } ,
104- ] ,
105- nextStepParams : {
106- snapshot_ui : { simulatorId : '12345678-1234-4234-8234-123456789012' } ,
107- tap : { simulatorId : '12345678-1234-4234-8234-123456789012' , x : 0 , y : 0 } ,
108- screenshot : { simulatorId : '12345678-1234-4234-8234-123456789012' } ,
109- } ,
122+ expect ( result . isError ) . toBeFalsy ( ) ;
123+ const text = allText ( result ) ;
124+ expect ( text ) . toContain ( 'Accessibility hierarchy retrieved successfully.' ) ;
125+ expect ( text ) . toContain (
126+ '{"elements": [{"type": "Button", "frame": {"x": 100, "y": 200, "width": 50, "height": 30}}]}' ,
127+ ) ;
128+ expect ( text ) . toContain ( 'Use frame coordinates for tap/swipe' ) ;
129+ expect ( result . nextStepParams ) . toEqual ( {
130+ snapshot_ui : { simulatorId : '12345678-1234-4234-8234-123456789012' } ,
131+ tap : { simulatorId : '12345678-1234-4234-8234-123456789012' , x : 0 , y : 0 } ,
132+ screenshot : { simulatorId : '12345678-1234-4234-8234-123456789012' } ,
110133 } ) ;
111134 } ) ;
112135
@@ -115,34 +138,20 @@ describe('Snapshot UI Plugin', () => {
115138 const mockAxeHelpers = {
116139 getAxePath : ( ) => null ,
117140 getBundledAxeEnvironment : ( ) => ( { } ) ,
118- createAxeNotAvailableResponse : ( ) => ( {
119- content : [
120- {
121- type : 'text' as const ,
122- text : AXE_NOT_AVAILABLE_MESSAGE ,
123- } ,
124- ] ,
125- isError : true ,
126- } ) ,
127141 } ;
128142
129- const result = await snapshot_uiLogic (
130- {
131- simulatorId : '12345678-1234-4234-8234-123456789012' ,
132- } ,
133- createNoopExecutor ( ) ,
134- mockAxeHelpers ,
135- ) ;
136-
137- expect ( result ) . toEqual ( {
138- content : [
143+ const result = await runLogic ( ( ) =>
144+ snapshot_uiLogic (
139145 {
140- type : 'text' as const ,
141- text : AXE_NOT_AVAILABLE_MESSAGE ,
146+ simulatorId : '12345678-1234-4234-8234-123456789012' ,
142147 } ,
143- ] ,
144- isError : true ,
145- } ) ;
148+ createNoopExecutor ( ) ,
149+ mockAxeHelpers ,
150+ ) ,
151+ ) ;
152+
153+ expect ( result . isError ) . toBe ( true ) ;
154+ expect ( allText ( result ) ) . toContain ( AXE_NOT_AVAILABLE_MESSAGE ) ;
146155 } ) ;
147156
148157 it ( 'should handle AxeError from failed command execution' , async ( ) => {
@@ -157,29 +166,22 @@ describe('Snapshot UI Plugin', () => {
157166 const mockAxeHelpers = {
158167 getAxePath : ( ) => '/usr/local/bin/axe' ,
159168 getBundledAxeEnvironment : ( ) => ( { } ) ,
160- createAxeNotAvailableResponse : ( ) => ( {
161- content : [ { type : 'text' as const , text : 'axe not available' } ] ,
162- isError : true ,
163- } ) ,
164169 } ;
165170
166- const result = await snapshot_uiLogic (
167- {
168- simulatorId : '12345678-1234-4234-8234-123456789012' ,
169- } ,
170- mockExecutor ,
171- mockAxeHelpers ,
172- ) ;
173-
174- expect ( result ) . toEqual ( {
175- content : [
171+ const result = await runLogic ( ( ) =>
172+ snapshot_uiLogic (
176173 {
177- type : 'text' as const ,
178- text : "Error: Failed to get accessibility hierarchy: axe command 'describe-ui' failed.\nDetails: axe command failed" ,
174+ simulatorId : '12345678-1234-4234-8234-123456789012' ,
179175 } ,
180- ] ,
181- isError : true ,
182- } ) ;
176+ mockExecutor ,
177+ mockAxeHelpers ,
178+ ) ,
179+ ) ;
180+
181+ expect ( result . isError ) . toBe ( true ) ;
182+ expect ( allText ( result ) ) . toContain (
183+ "Failed to get accessibility hierarchy: axe command 'describe-ui' failed." ,
184+ ) ;
183185 } ) ;
184186
185187 it ( 'should handle SystemError from command execution' , async ( ) => {
@@ -189,31 +191,19 @@ describe('Snapshot UI Plugin', () => {
189191 const mockAxeHelpers = {
190192 getAxePath : ( ) => '/usr/local/bin/axe' ,
191193 getBundledAxeEnvironment : ( ) => ( { } ) ,
192- createAxeNotAvailableResponse : ( ) => ( {
193- content : [ { type : 'text' as const , text : 'axe not available' } ] ,
194- isError : true ,
195- } ) ,
196194 } ;
197195
198- const result = await snapshot_uiLogic (
199- {
200- simulatorId : '12345678-1234-4234-8234-123456789012' ,
201- } ,
202- mockExecutor ,
203- mockAxeHelpers ,
204- ) ;
205-
206- expect ( result ) . toEqual ( {
207- content : [
196+ const result = await runLogic ( ( ) =>
197+ snapshot_uiLogic (
208198 {
209- type : 'text' as const ,
210- text : expect . stringContaining (
211- 'Error: System error executing axe: Failed to execute axe command: ENOENT: no such file or directory' ,
212- ) ,
199+ simulatorId : '12345678-1234-4234-8234-123456789012' ,
213200 } ,
214- ] ,
215- isError : true ,
216- } ) ;
201+ mockExecutor ,
202+ mockAxeHelpers ,
203+ ) ,
204+ ) ;
205+
206+ expect ( result . isError ) . toBe ( true ) ;
217207 } ) ;
218208
219209 it ( 'should handle unexpected Error objects' , async ( ) => {
@@ -223,31 +213,19 @@ describe('Snapshot UI Plugin', () => {
223213 const mockAxeHelpers = {
224214 getAxePath : ( ) => '/usr/local/bin/axe' ,
225215 getBundledAxeEnvironment : ( ) => ( { } ) ,
226- createAxeNotAvailableResponse : ( ) => ( {
227- content : [ { type : 'text' as const , text : 'axe not available' } ] ,
228- isError : true ,
229- } ) ,
230216 } ;
231217
232- const result = await snapshot_uiLogic (
233- {
234- simulatorId : '12345678-1234-4234-8234-123456789012' ,
235- } ,
236- mockExecutor ,
237- mockAxeHelpers ,
238- ) ;
239-
240- expect ( result ) . toEqual ( {
241- content : [
218+ const result = await runLogic ( ( ) =>
219+ snapshot_uiLogic (
242220 {
243- type : 'text' as const ,
244- text : expect . stringContaining (
245- 'Error: System error executing axe: Failed to execute axe command: Unexpected error' ,
246- ) ,
221+ simulatorId : '12345678-1234-4234-8234-123456789012' ,
247222 } ,
248- ] ,
249- isError : true ,
250- } ) ;
223+ mockExecutor ,
224+ mockAxeHelpers ,
225+ ) ,
226+ ) ;
227+
228+ expect ( result . isError ) . toBe ( true ) ;
251229 } ) ;
252230
253231 it ( 'should handle unexpected string errors' , async ( ) => {
@@ -257,29 +235,22 @@ describe('Snapshot UI Plugin', () => {
257235 const mockAxeHelpers = {
258236 getAxePath : ( ) => '/usr/local/bin/axe' ,
259237 getBundledAxeEnvironment : ( ) => ( { } ) ,
260- createAxeNotAvailableResponse : ( ) => ( {
261- content : [ { type : 'text' as const , text : 'axe not available' } ] ,
262- isError : true ,
263- } ) ,
264238 } ;
265239
266- const result = await snapshot_uiLogic (
267- {
268- simulatorId : '12345678-1234-4234-8234-123456789012' ,
269- } ,
270- mockExecutor ,
271- mockAxeHelpers ,
272- ) ;
273-
274- expect ( result ) . toEqual ( {
275- content : [
240+ const result = await runLogic ( ( ) =>
241+ snapshot_uiLogic (
276242 {
277- type : 'text' as const ,
278- text : 'Error: System error executing axe: Failed to execute axe command: String error' ,
243+ simulatorId : '12345678-1234-4234-8234-123456789012' ,
279244 } ,
280- ] ,
281- isError : true ,
282- } ) ;
245+ mockExecutor ,
246+ mockAxeHelpers ,
247+ ) ,
248+ ) ;
249+
250+ expect ( result . isError ) . toBe ( true ) ;
251+ expect ( allText ( result ) ) . toContain (
252+ 'System error executing axe: Failed to execute axe command: String error' ,
253+ ) ;
283254 } ) ;
284255 } ) ;
285256} ) ;
0 commit comments