Skip to content

Commit 1f8edcf

Browse files
committed
refactor: improve text parser robustness and test coverage
- Replace fixed-width indentation (\s{4}) with flexible indentation (\s+) - Capture unavailable suffix in regex group instead of line.includes() - Add test for merge logic with text-only devices (core bug fix scenario) - Update resource test to handle new fallback behavior Addresses CodeRabbit review feedback on PR #118
1 parent 666ba0c commit 1f8edcf

3 files changed

Lines changed: 91 additions & 9 deletions

File tree

src/mcp/resources/__tests__/simulators.test.ts

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -65,16 +65,36 @@ describe('simulators resource', () => {
6565
expect(result.contents[0].text).toContain('Command failed');
6666
});
6767

68-
it('should handle JSON parsing errors', async () => {
69-
const mockExecutor = createMockExecutor({
70-
success: true,
71-
output: 'invalid json',
72-
});
68+
it('should handle JSON parsing errors and fall back to text parsing', async () => {
69+
const mockTextOutput = `== Devices ==
70+
-- iOS 17.0 --
71+
iPhone 15 (test-uuid-123) (Shutdown)`;
72+
73+
const mockExecutor = async (command: string[]) => {
74+
// JSON command returns invalid JSON
75+
if (command.includes('--json')) {
76+
return {
77+
success: true,
78+
output: 'invalid json',
79+
error: undefined,
80+
process: { pid: 12345 },
81+
};
82+
}
83+
84+
// Text command returns valid text output
85+
return {
86+
success: true,
87+
output: mockTextOutput,
88+
error: undefined,
89+
process: { pid: 12345 },
90+
};
91+
};
7392

7493
const result = await simulatorsResourceLogic(mockExecutor);
7594

7695
expect(result.contents).toHaveLength(1);
77-
expect(result.contents[0].text).toBe('invalid json');
96+
expect(result.contents[0].text).toContain('iPhone 15 (test-uuid-123)');
97+
expect(result.contents[0].text).toContain('iOS 17.0');
7898
});
7999

80100
it('should handle spawn errors', async () => {

src/mcp/tools/simulator/__tests__/list_sims.test.ts

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,68 @@ Next Steps:
176176
iOS 17.0:
177177
- iPhone 15 (test-uuid-123) [Booted]
178178
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+
179241
Next Steps:
180242
1. Boot a simulator: boot_sim({ simulatorUuid: 'UUID_FROM_ABOVE' })
181243
2. Open the simulator UI: open_sim({})

src/mcp/tools/simulator/list_sims.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,11 @@ function parseTextOutput(textOutput: string): SimulatorDevice[] {
4242
// Match device lines like " iPhone 17 Pro (UUID) (Booted)"
4343
// UUID pattern is flexible to handle test UUIDs like "test-uuid-123"
4444
const deviceMatch = line.match(
45-
/^\s{4}(.+?)\s+\(([^)]+)\)\s+\((Booted|Shutdown|Booting|Shutting Down)\)(?:\s+\(unavailable.*\))?$/i,
45+
/^\s+(.+?)\s+\(([^)]+)\)\s+\((Booted|Shutdown|Booting|Shutting Down)\)(\s+\(unavailable.*\))?$/i,
4646
);
4747
if (deviceMatch && currentRuntime) {
48-
const [, name, udid, state] = deviceMatch;
49-
const isUnavailable = line.includes('unavailable');
48+
const [, name, udid, state, unavailableSuffix] = deviceMatch;
49+
const isUnavailable = Boolean(unavailableSuffix);
5050
if (!isUnavailable) {
5151
devices.push({
5252
name: name.trim(),

0 commit comments

Comments
 (0)