Skip to content
15 changes: 5 additions & 10 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
"javascript",
"typescript"
],
"eslint.runtime": "/opt/homebrew/bin/node",
"eslint.format.enable": true,
"eslint.nodePath": "${workspaceFolder}/node_modules",
"eslint.workingDirectories": [
Expand All @@ -29,19 +28,15 @@
"editor.defaultFormatter": "vscode.typescript-language-features"
},
"terminal.integrated.shellIntegration.decorationsEnabled": "never",
"vitest.nodeExecutable": "/opt/homebrew/bin/node",
"[json]": {
"editor.defaultFormatter": "vscode.json-language-features"
},
"[jsonc]": {
"editor.defaultFormatter": "vscode.json-language-features"
},
"chat.mcp.serverSampling": {
"XcodeBuildMCP/.vscode/mcp.json: XcodeBuildMCP-Dev": {
"allowedDuringChat": true,
"allowedModels": [
"copilot/gpt-5.2"
]
}
},
"vitest.shellType": "child_process",
"vitest.nodeExecutable": "/usr/bin/env",
"vitest.nodeExecArgs": [
"node"
]
}
126 changes: 121 additions & 5 deletions src/mcp/tools/logging/__tests__/start_sim_log_cap.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ describe('start_sim_log_cap plugin', () => {

it('should have correct description', () => {
expect(plugin.description).toBe(
'Starts capturing logs from a specified simulator. Returns a session ID. By default, captures only structured logs.',
"Starts capturing logs from a specified simulator. Returns a session ID. Use subsystemFilter to control what logs are captured: 'app' (default), 'all' (everything), 'swiftui' (includes Self._printChanges()), or custom subsystems.",
);
});

Expand All @@ -42,6 +42,41 @@ describe('start_sim_log_cap plugin', () => {
);
});

it('should validate schema with subsystemFilter parameter', () => {
const schema = z.object(plugin.schema);
// Valid enum values
expect(
schema.safeParse({ bundleId: 'com.example.app', subsystemFilter: 'app' }).success,
).toBe(true);
expect(
schema.safeParse({ bundleId: 'com.example.app', subsystemFilter: 'all' }).success,
).toBe(true);
expect(
schema.safeParse({ bundleId: 'com.example.app', subsystemFilter: 'swiftui' }).success,
).toBe(true);
// Valid array of subsystems
expect(
schema.safeParse({ bundleId: 'com.example.app', subsystemFilter: ['com.apple.UIKit'] })
.success,
).toBe(true);
expect(
schema.safeParse({
bundleId: 'com.example.app',
subsystemFilter: ['com.apple.UIKit', 'com.apple.CoreData'],
}).success,
).toBe(true);
// Invalid values
expect(schema.safeParse({ bundleId: 'com.example.app', subsystemFilter: [] }).success).toBe(
false,
);
expect(
schema.safeParse({ bundleId: 'com.example.app', subsystemFilter: 'invalid' }).success,
).toBe(false);
expect(schema.safeParse({ bundleId: 'com.example.app', subsystemFilter: 123 }).success).toBe(
false,
);
});

it('should reject invalid schema parameters', () => {
const schema = z.object(plugin.schema);
expect(schema.safeParse({ bundleId: null }).success).toBe(false);
Expand Down Expand Up @@ -79,6 +114,7 @@ describe('start_sim_log_cap plugin', () => {
{
simulatorId: 'test-uuid',
bundleId: 'com.example.app',
subsystemFilter: 'app',
},
mockExecutor,
logCaptureStub,
Expand All @@ -103,15 +139,93 @@ describe('start_sim_log_cap plugin', () => {
{
simulatorId: 'test-uuid',
bundleId: 'com.example.app',
subsystemFilter: 'app',
},
mockExecutor,
logCaptureStub,
);

expect(result.isError).toBeUndefined();
expect(result.content[0].text).toBe(
"Log capture started successfully. Session ID: test-uuid-123.\n\nNote: Only structured logs are being captured.\n\nNext Steps:\n1. Interact with your simulator and app.\n2. Use 'stop_sim_log_cap' with session ID 'test-uuid-123' to stop capture and retrieve logs.",
"Log capture started successfully. Session ID: test-uuid-123.\n\nOnly structured logs from the app subsystem are being captured.\n\nNext Steps:\n1. Interact with your simulator and app.\n2. Use 'stop_sim_log_cap' with session ID 'test-uuid-123' to stop capture and retrieve logs.",
);
});

it('should indicate swiftui capture when subsystemFilter is swiftui', async () => {
const mockExecutor = createMockExecutor({ success: true, output: '' });
const logCaptureStub = (params: any, executor: any) => {
return Promise.resolve({
sessionId: 'test-uuid-123',
logFilePath: '/tmp/test.log',
processes: [],
error: undefined,
});
};

const result = await start_sim_log_capLogic(
{
simulatorId: 'test-uuid',
bundleId: 'com.example.app',
subsystemFilter: 'swiftui',
},
mockExecutor,
logCaptureStub,
);

expect(result.isError).toBeUndefined();
expect(result.content[0].text).toContain('SwiftUI logs');
expect(result.content[0].text).toContain('Self._printChanges()');
});

it('should indicate all logs capture when subsystemFilter is all', async () => {
const mockExecutor = createMockExecutor({ success: true, output: '' });
const logCaptureStub = (params: any, executor: any) => {
return Promise.resolve({
sessionId: 'test-uuid-123',
logFilePath: '/tmp/test.log',
processes: [],
error: undefined,
});
};

const result = await start_sim_log_capLogic(
{
simulatorId: 'test-uuid',
bundleId: 'com.example.app',
subsystemFilter: 'all',
},
mockExecutor,
logCaptureStub,
);

expect(result.isError).toBeUndefined();
expect(result.content[0].text).toContain('all system logs');
});

it('should indicate custom subsystems when array is provided', async () => {
const mockExecutor = createMockExecutor({ success: true, output: '' });
const logCaptureStub = (params: any, executor: any) => {
return Promise.resolve({
sessionId: 'test-uuid-123',
logFilePath: '/tmp/test.log',
processes: [],
error: undefined,
});
};

const result = await start_sim_log_capLogic(
{
simulatorId: 'test-uuid',
bundleId: 'com.example.app',
subsystemFilter: ['com.apple.UIKit', 'com.apple.CoreData'],
},
mockExecutor,
logCaptureStub,
);

expect(result.isError).toBeUndefined();
expect(result.content[0].text).toContain('com.apple.UIKit');
expect(result.content[0].text).toContain('com.apple.CoreData');
});

it('should indicate console capture when captureConsole is true', async () => {
Expand All @@ -130,14 +244,14 @@ describe('start_sim_log_cap plugin', () => {
simulatorId: 'test-uuid',
bundleId: 'com.example.app',
captureConsole: true,
subsystemFilter: 'app',
},
mockExecutor,
logCaptureStub,
);

expect(result.content[0].text).toBe(
"Log capture started successfully. Session ID: test-uuid-123.\n\nNote: Your app was relaunched to capture console output.\n\nNext Steps:\n1. Interact with your simulator and app.\n2. Use 'stop_sim_log_cap' with session ID 'test-uuid-123' to stop capture and retrieve logs.",
);
expect(result.content[0].text).toContain('Your app was relaunched to capture console output');
expect(result.content[0].text).toContain('test-uuid-123');
});

it('should create correct spawn commands for console capture', async () => {
Expand Down Expand Up @@ -190,6 +304,7 @@ describe('start_sim_log_cap plugin', () => {
simulatorId: 'test-uuid',
bundleId: 'com.example.app',
captureConsole: true,
subsystemFilter: 'app',
},
mockExecutor,
logCaptureStub,
Expand Down Expand Up @@ -259,6 +374,7 @@ describe('start_sim_log_cap plugin', () => {
simulatorId: 'test-uuid',
bundleId: 'com.example.app',
captureConsole: false,
subsystemFilter: 'app',
},
mockExecutor,
logCaptureStub,
Expand Down
Loading
Loading