@@ -49,7 +49,7 @@ describe('list_sims tool', () => {
4949
5050 describe ( 'Handler Behavior (Complete Literal Returns)' , ( ) => {
5151 it ( 'should handle successful simulator listing' , async ( ) => {
52- const mockOutput = JSON . stringify ( {
52+ const mockJsonOutput = JSON . stringify ( {
5353 devices : {
5454 'iOS 17.0' : [
5555 {
@@ -62,31 +62,51 @@ describe('list_sims tool', () => {
6262 } ,
6363 } ) ;
6464
65- const mockExecutor = createMockExecutor ( {
66- success : true ,
67- output : mockOutput ,
68- error : undefined ,
69- process : { pid : 12345 } ,
70- } ) ;
65+ const mockTextOutput = `== Devices ==
66+ -- iOS 17.0 --
67+ iPhone 15 (test-uuid-123) (Shutdown)` ;
7168
72- // Track calls manually
73- const wrappedExecutor = async (
69+ // Create a mock executor that returns different outputs based on command
70+ const mockExecutor = async (
7471 command : string [ ] ,
7572 logPrefix ?: string ,
7673 useShell ?: boolean ,
7774 env ?: Record < string , string > ,
7875 ) => {
7976 callHistory . push ( { command, logPrefix, useShell, env } ) ;
80- return mockExecutor ( command , logPrefix , useShell , env ) ;
77+
78+ // Return JSON output for JSON command
79+ if ( command . includes ( '--json' ) ) {
80+ return {
81+ success : true ,
82+ output : mockJsonOutput ,
83+ error : undefined ,
84+ process : { pid : 12345 } ,
85+ } ;
86+ }
87+
88+ // Return text output for text command
89+ return {
90+ success : true ,
91+ output : mockTextOutput ,
92+ error : undefined ,
93+ process : { pid : 12345 } ,
94+ } ;
8195 } ;
8296
83- const result = await list_simsLogic ( { enabled : true } , wrappedExecutor ) ;
97+ const result = await list_simsLogic ( { enabled : true } , mockExecutor ) ;
8498
85- // Verify command was called correctly
86- expect ( callHistory ) . toHaveLength ( 1 ) ;
99+ // Verify both commands were called
100+ expect ( callHistory ) . toHaveLength ( 2 ) ;
87101 expect ( callHistory [ 0 ] ) . toEqual ( {
88- command : [ 'xcrun' , 'simctl' , 'list' , 'devices' , 'available' , '--json' ] ,
89- logPrefix : 'List Simulators' ,
102+ command : [ 'xcrun' , 'simctl' , 'list' , 'devices' , '--json' ] ,
103+ logPrefix : 'List Simulators (JSON)' ,
104+ useShell : true ,
105+ env : undefined ,
106+ } ) ;
107+ expect ( callHistory [ 1 ] ) . toEqual ( {
108+ command : [ 'xcrun' , 'simctl' , 'list' , 'devices' ] ,
109+ logPrefix : 'List Simulators (Text)' ,
90110 useShell : true ,
91111 env : undefined ,
92112 } ) ;
@@ -111,7 +131,7 @@ Next Steps:
111131 } ) ;
112132
113133 it ( 'should handle successful listing with booted simulator' , async ( ) => {
114- const mockOutput = JSON . stringify ( {
134+ const mockJsonOutput = JSON . stringify ( {
115135 devices : {
116136 'iOS 17.0' : [
117137 {
@@ -124,12 +144,26 @@ Next Steps:
124144 } ,
125145 } ) ;
126146
127- const mockExecutor = createMockExecutor ( {
128- success : true ,
129- output : mockOutput ,
130- error : undefined ,
131- process : { pid : 12345 } ,
132- } ) ;
147+ const mockTextOutput = `== Devices ==
148+ -- iOS 17.0 --
149+ iPhone 15 (test-uuid-123) (Booted)` ;
150+
151+ const mockExecutor = async ( command : string [ ] ) => {
152+ if ( command . includes ( '--json' ) ) {
153+ return {
154+ success : true ,
155+ output : mockJsonOutput ,
156+ error : undefined ,
157+ process : { pid : 12345 } ,
158+ } ;
159+ }
160+ return {
161+ success : true ,
162+ output : mockTextOutput ,
163+ error : undefined ,
164+ process : { pid : 12345 } ,
165+ } ;
166+ } ;
133167
134168 const result = await list_simsLogic ( { enabled : true } , mockExecutor ) ;
135169
@@ -142,6 +176,68 @@ Next Steps:
142176iOS 17.0:
143177- iPhone 15 (test-uuid-123) [Booted]
144178
179+ Next Steps:
180+ 1. Boot a simulator: boot_sim({ simulatorUuid: 'UUID_FROM_ABOVE' })
181+ 2. Open the simulator UI: open_sim({})
182+ 3. Build for simulator: build_sim({ scheme: 'YOUR_SCHEME', simulatorId: 'UUID_FROM_ABOVE' })
183+ 4. Get app path: get_sim_app_path({ scheme: 'YOUR_SCHEME', platform: 'iOS Simulator', simulatorId: 'UUID_FROM_ABOVE' })` ,
184+ } ,
185+ ] ,
186+ } ) ;
187+ } ) ;
188+
189+ it ( 'should merge devices from text that are missing from JSON' , async ( ) => {
190+ const mockJsonOutput = JSON . stringify ( {
191+ devices : {
192+ 'iOS 18.6' : [
193+ {
194+ name : 'iPhone 15' ,
195+ udid : 'json-uuid-123' ,
196+ isAvailable : true ,
197+ state : 'Shutdown' ,
198+ } ,
199+ ] ,
200+ } ,
201+ } ) ;
202+
203+ const mockTextOutput = `== Devices ==
204+ -- iOS 18.6 --
205+ iPhone 15 (json-uuid-123) (Shutdown)
206+ -- iOS 26.0 --
207+ iPhone 17 Pro (text-uuid-456) (Shutdown)` ;
208+
209+ const mockExecutor = async ( command : string [ ] ) => {
210+ if ( command . includes ( '--json' ) ) {
211+ return {
212+ success : true ,
213+ output : mockJsonOutput ,
214+ error : undefined ,
215+ process : { pid : 12345 } ,
216+ } ;
217+ }
218+ return {
219+ success : true ,
220+ output : mockTextOutput ,
221+ error : undefined ,
222+ process : { pid : 12345 } ,
223+ } ;
224+ } ;
225+
226+ const result = await list_simsLogic ( { enabled : true } , mockExecutor ) ;
227+
228+ // Should contain both iOS 18.6 from JSON and iOS 26.0 from text
229+ expect ( result ) . toEqual ( {
230+ content : [
231+ {
232+ type : 'text' ,
233+ text : `Available iOS Simulators:
234+
235+ iOS 18.6:
236+ - iPhone 15 (json-uuid-123)
237+
238+ iOS 26.0:
239+ - iPhone 17 Pro (text-uuid-456)
240+
145241Next Steps:
1462421. Boot a simulator: boot_sim({ simulatorUuid: 'UUID_FROM_ABOVE' })
1472432. Open the simulator UI: open_sim({})
@@ -172,21 +268,48 @@ Next Steps:
172268 } ) ;
173269 } ) ;
174270
175- it ( 'should handle JSON parse failure' , async ( ) => {
176- const mockExecutor = createMockExecutor ( {
177- success : true ,
178- output : 'invalid json' ,
179- error : undefined ,
180- process : { pid : 12345 } ,
181- } ) ;
271+ it ( 'should handle JSON parse failure and fall back to text parsing' , async ( ) => {
272+ const mockTextOutput = `== Devices ==
273+ -- iOS 17.0 --
274+ iPhone 15 (test-uuid-456) (Shutdown)` ;
275+
276+ const mockExecutor = async ( command : string [ ] ) => {
277+ // JSON command returns invalid JSON
278+ if ( command . includes ( '--json' ) ) {
279+ return {
280+ success : true ,
281+ output : 'invalid json' ,
282+ error : undefined ,
283+ process : { pid : 12345 } ,
284+ } ;
285+ }
286+
287+ // Text command returns valid text output
288+ return {
289+ success : true ,
290+ output : mockTextOutput ,
291+ error : undefined ,
292+ process : { pid : 12345 } ,
293+ } ;
294+ } ;
182295
183296 const result = await list_simsLogic ( { enabled : true } , mockExecutor ) ;
184297
298+ // Should fall back to text parsing and extract devices
185299 expect ( result ) . toEqual ( {
186300 content : [
187301 {
188302 type : 'text' ,
189- text : 'invalid json' ,
303+ text : `Available iOS Simulators:
304+
305+ iOS 17.0:
306+ - iPhone 15 (test-uuid-456)
307+
308+ Next Steps:
309+ 1. Boot a simulator: boot_sim({ simulatorUuid: 'UUID_FROM_ABOVE' })
310+ 2. Open the simulator UI: open_sim({})
311+ 3. Build for simulator: build_sim({ scheme: 'YOUR_SCHEME', simulatorId: 'UUID_FROM_ABOVE' })
312+ 4. Get app path: get_sim_app_path({ scheme: 'YOUR_SCHEME', platform: 'iOS Simulator', simulatorId: 'UUID_FROM_ABOVE' })` ,
190313 } ,
191314 ] ,
192315 } ) ;
0 commit comments