diff --git a/CHANGELOG.md b/CHANGELOG.md index d62bdce35..3b47e26f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,11 @@ - Added `xcodebuildmcp purge` to report and explicitly clean XcodeBuildMCP-managed workspace storage, including opt-in DerivedData cleanup with dry-run and confirmation safeguards. - Added `extraArgs` as a first-class session-default value. Repo config or runtime defaults can now carry common `xcodebuild` flags (for example `-skipPackagePluginValidation` or `-disableAutomaticPackageResolution`) so they don't need repeating on every build or test call. Per-call `extraArgs` replace matching configured flags or build settings and append after non-matching defaults, while an explicit empty array (`extraArgs: []`) clears the defaults for a single call. The session management tools show, set, sync, and clear `extraArgs` alongside the other defaults. +- Added reusable test preparation to the existing simulator, device, and macOS build tools. Builds can now return a portable `.xctestproducts` artifact, and test tools can run that artifact—or an advanced `.xctestrun` input—without rebuilding before producing a new `.xcresult` ([#450](https://github.com/getsentry/XcodeBuildMCP/issues/450)). + +### Changed + +- Tool manifests can now conditionally expose static next steps using handler-activated condition keys, while runtime-generated parameters continue to flow through the existing next-step parameter contract. ### Fixed @@ -694,4 +699,3 @@ Please note that the UI automation features are an early preview and currently i ## [v1.0.1] - 2025-04-02 - Initial release of XcodeBuildMCP - Basic support for building iOS and macOS applications - diff --git a/README.md b/README.md index 0bc513ff4..46914c2e8 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,12 @@ xcodebuildmcp tools # Build for simulator xcodebuildmcp simulator build --scheme MyApp --project-path ./MyApp.xcodeproj + +# Prepare portable test products without running tests +xcodebuildmcp simulator build --scheme MyApp --project-path ./MyApp.xcodeproj --simulator-name "iPhone 17" --build-for-testing --test-products-path ./MyApp.xctestproducts + +# Run previously prepared test products +xcodebuildmcp simulator test --test-products-path ./MyApp.xctestproducts --simulator-name "iPhone 17" ``` Check for updates and upgrade in place: diff --git a/example_projects/iOS_Calculator/CalculatorApp/CalculatorApp.swift b/example_projects/iOS_Calculator/CalculatorApp/CalculatorApp.swift index 6125b83cd..ff5d4d55e 100644 --- a/example_projects/iOS_Calculator/CalculatorApp/CalculatorApp.swift +++ b/example_projects/iOS_Calculator/CalculatorApp/CalculatorApp.swift @@ -3,6 +3,7 @@ import OSLog import CalculatorAppFeature private let logger = Logger(subsystem: "io.sentry.calculatorapp", category: "lifecycle") +private let snapshotScrollSurfaceArgument = "--snapshot-scroll-surface" @main struct CalculatorApp: App { @@ -10,7 +11,11 @@ struct CalculatorApp: App { var body: some Scene { WindowGroup { - ContentView() + if ProcessInfo.processInfo.arguments.contains(snapshotScrollSurfaceArgument) { + SnapshotScrollSurface() + } else { + ContentView() + } } .onChange(of: scenePhase) { _, newPhase in switch newPhase { @@ -25,6 +30,21 @@ struct CalculatorApp: App { } } +private struct SnapshotScrollSurface: View { + var body: some View { + ScrollView { + LazyVStack(spacing: 16) { + ForEach(0..<30, id: \.self) { index in + Text("Snapshot row \(index)") + .frame(maxWidth: .infinity, minHeight: 44) + } + } + .padding() + } + .accessibilityIdentifier("snapshot-scroll-surface") + } +} + #Preview { ContentView() } diff --git a/manifests/tools/batch.yaml b/manifests/tools/batch.yaml index 699eab740..ca7297816 100644 --- a/manifests/tools/batch.yaml +++ b/manifests/tools/batch.yaml @@ -15,3 +15,8 @@ annotations: readOnlyHint: true destructiveHint: false openWorldHint: false +nextSteps: + - label: Refresh after UI action + toolId: snapshot_ui + when: success + condition: ui_action_needs_refresh diff --git a/manifests/tools/build_device.yaml b/manifests/tools/build_device.yaml index 8f1c716a3..85a040228 100644 --- a/manifests/tools/build_device.yaml +++ b/manifests/tools/build_device.yaml @@ -6,7 +6,7 @@ names: description: Build for device. outputSchema: schema: xcodebuildmcp.output.build-result - version: "2" + version: '3' predicates: - hideWhenXcodeAgentMode annotations: @@ -19,3 +19,8 @@ nextSteps: toolId: get_device_app_path priority: 1 when: success + condition: app_build_succeeded + - label: Run prepared tests + toolId: test_device + when: success + condition: prepared_tests_available diff --git a/manifests/tools/build_macos.yaml b/manifests/tools/build_macos.yaml index 51c4f4f24..04bb08d2c 100644 --- a/manifests/tools/build_macos.yaml +++ b/manifests/tools/build_macos.yaml @@ -6,7 +6,7 @@ names: description: Build macOS app. outputSchema: schema: xcodebuildmcp.output.build-result - version: "2" + version: '3' predicates: - hideWhenXcodeAgentMode annotations: @@ -19,3 +19,8 @@ nextSteps: toolId: get_mac_app_path priority: 1 when: success + condition: app_build_succeeded + - label: Run prepared tests + toolId: test_macos + when: success + condition: prepared_tests_available diff --git a/manifests/tools/build_sim.yaml b/manifests/tools/build_sim.yaml index e7305ee09..43b265578 100644 --- a/manifests/tools/build_sim.yaml +++ b/manifests/tools/build_sim.yaml @@ -6,7 +6,7 @@ names: description: Build for iOS sim (compile-only, no launch). outputSchema: schema: xcodebuildmcp.output.build-result - version: "2" + version: '3' predicates: - hideWhenXcodeAgentMode annotations: @@ -19,3 +19,8 @@ nextSteps: toolId: get_sim_app_path priority: 1 when: success + condition: app_build_succeeded + - label: Run prepared tests + toolId: test_sim + when: success + condition: prepared_tests_available diff --git a/manifests/tools/button.yaml b/manifests/tools/button.yaml index 426659dba..13d1d5dbe 100644 --- a/manifests/tools/button.yaml +++ b/manifests/tools/button.yaml @@ -14,3 +14,8 @@ annotations: readOnlyHint: true destructiveHint: false openWorldHint: false +nextSteps: + - label: Refresh after UI action + toolId: snapshot_ui + when: success + condition: ui_action_needs_refresh diff --git a/manifests/tools/drag.yaml b/manifests/tools/drag.yaml index 3ed9078dc..d96135d87 100644 --- a/manifests/tools/drag.yaml +++ b/manifests/tools/drag.yaml @@ -15,3 +15,8 @@ annotations: readOnlyHint: true destructiveHint: false openWorldHint: false +nextSteps: + - label: Refresh after UI action + toolId: snapshot_ui + when: success + condition: ui_action_needs_refresh diff --git a/manifests/tools/gesture.yaml b/manifests/tools/gesture.yaml index ad9a2f5f8..f14b27790 100644 --- a/manifests/tools/gesture.yaml +++ b/manifests/tools/gesture.yaml @@ -14,3 +14,8 @@ annotations: readOnlyHint: true destructiveHint: false openWorldHint: false +nextSteps: + - label: Refresh after UI action + toolId: snapshot_ui + when: success + condition: ui_action_needs_refresh diff --git a/manifests/tools/key_press.yaml b/manifests/tools/key_press.yaml index 56336773e..11cff5eab 100644 --- a/manifests/tools/key_press.yaml +++ b/manifests/tools/key_press.yaml @@ -14,3 +14,8 @@ annotations: readOnlyHint: true destructiveHint: false openWorldHint: false +nextSteps: + - label: Refresh after UI action + toolId: snapshot_ui + when: success + condition: ui_action_needs_refresh diff --git a/manifests/tools/key_sequence.yaml b/manifests/tools/key_sequence.yaml index 8550b6396..ab45e36e3 100644 --- a/manifests/tools/key_sequence.yaml +++ b/manifests/tools/key_sequence.yaml @@ -14,3 +14,8 @@ annotations: readOnlyHint: true destructiveHint: false openWorldHint: false +nextSteps: + - label: Refresh after UI action + toolId: snapshot_ui + when: success + condition: ui_action_needs_refresh diff --git a/manifests/tools/long_press.yaml b/manifests/tools/long_press.yaml index 7aee1112e..e7360e747 100644 --- a/manifests/tools/long_press.yaml +++ b/manifests/tools/long_press.yaml @@ -14,3 +14,8 @@ annotations: readOnlyHint: true destructiveHint: false openWorldHint: false +nextSteps: + - label: Refresh after UI action + toolId: snapshot_ui + when: success + condition: ui_action_needs_refresh diff --git a/manifests/tools/swipe.yaml b/manifests/tools/swipe.yaml index 5643adbe2..851c6562d 100644 --- a/manifests/tools/swipe.yaml +++ b/manifests/tools/swipe.yaml @@ -18,3 +18,8 @@ annotations: readOnlyHint: true destructiveHint: false openWorldHint: false +nextSteps: + - label: Refresh after UI action + toolId: snapshot_ui + when: success + condition: ui_action_needs_refresh diff --git a/manifests/tools/tap.yaml b/manifests/tools/tap.yaml index d05be833e..9ffc8bb6c 100644 --- a/manifests/tools/tap.yaml +++ b/manifests/tools/tap.yaml @@ -15,3 +15,8 @@ annotations: readOnlyHint: true destructiveHint: false openWorldHint: false +nextSteps: + - label: Refresh after UI action + toolId: snapshot_ui + when: success + condition: ui_action_needs_refresh diff --git a/manifests/tools/test_device.yaml b/manifests/tools/test_device.yaml index 3f0562f1d..7f5677f7f 100644 --- a/manifests/tools/test_device.yaml +++ b/manifests/tools/test_device.yaml @@ -6,7 +6,7 @@ names: description: Test on device. outputSchema: schema: xcodebuildmcp.output.test-result - version: "2" + version: "3" predicates: - hideWhenXcodeAgentMode annotations: diff --git a/manifests/tools/test_macos.yaml b/manifests/tools/test_macos.yaml index ef5db1325..6100ebe37 100644 --- a/manifests/tools/test_macos.yaml +++ b/manifests/tools/test_macos.yaml @@ -6,7 +6,7 @@ names: description: Test macOS target. outputSchema: schema: xcodebuildmcp.output.test-result - version: "2" + version: "3" predicates: - hideWhenXcodeAgentMode annotations: diff --git a/manifests/tools/test_sim.yaml b/manifests/tools/test_sim.yaml index d15375220..1e2e527a9 100644 --- a/manifests/tools/test_sim.yaml +++ b/manifests/tools/test_sim.yaml @@ -6,7 +6,7 @@ names: description: Test on iOS sim. outputSchema: schema: xcodebuildmcp.output.test-result - version: "2" + version: "3" predicates: - hideWhenXcodeAgentMode annotations: diff --git a/manifests/tools/touch.yaml b/manifests/tools/touch.yaml index 3849c5bc8..e08aacb40 100644 --- a/manifests/tools/touch.yaml +++ b/manifests/tools/touch.yaml @@ -14,3 +14,8 @@ annotations: readOnlyHint: true destructiveHint: false openWorldHint: false +nextSteps: + - label: Refresh after UI action + toolId: snapshot_ui + when: success + condition: ui_action_needs_refresh diff --git a/manifests/tools/type_text.yaml b/manifests/tools/type_text.yaml index c2cd64ec7..1cc50c2db 100644 --- a/manifests/tools/type_text.yaml +++ b/manifests/tools/type_text.yaml @@ -15,3 +15,8 @@ annotations: readOnlyHint: true destructiveHint: false openWorldHint: false +nextSteps: + - label: Refresh after UI action + toolId: snapshot_ui + when: success + condition: ui_action_needs_refresh diff --git a/package.json b/package.json index 7872e1c4b..235f8fc3f 100644 --- a/package.json +++ b/package.json @@ -48,10 +48,10 @@ "knip": "knip", "test": "vitest run", "test:schema-fixtures": "vitest run src/snapshot-tests/__tests__/json-fixture-schema.test.ts", - "test:snapshot": "npm run build && node build/cli.js daemon stop 2>/dev/null; vitest run --config vitest.snapshot.config.ts", + "test:snapshot": "npm run build && vitest run --config vitest.snapshot.config.ts", "test:snapshots": "npm run test:snapshot", - "test:snapshot:device": "npm run build && node build/cli.js daemon stop 2>/dev/null; vitest run --config vitest.snapshot.config.ts src/snapshot-tests/__tests__/device.snapshot.test.ts && vitest run --config vitest.snapshot.config.ts src/snapshot-tests/__tests__/cli-json-fixture-parity.snapshot.test.ts src/snapshot-tests/__tests__/mcp-json-fixture-parity.snapshot.test.ts -t 'device workflow'", - "test:snapshot:update": "npm run build && node build/cli.js daemon stop 2>/dev/null; UPDATE_SNAPSHOTS=1 vitest run --config vitest.snapshot.config.ts", + "test:snapshot:device": "npm run build && vitest run --config vitest.snapshot.config.ts src/snapshot-tests/__tests__/device.snapshot.test.ts && vitest run --config vitest.snapshot.config.ts src/snapshot-tests/__tests__/cli-json.snapshot.test.ts src/snapshot-tests/__tests__/mcp-json.snapshot.test.ts -t 'device workflow'", + "test:snapshot:update": "npm run build && UPDATE_SNAPSHOTS=1 vitest run --config vitest.snapshot.config.ts", "test:snapshots:update": "npm run test:snapshot:update", "test:smoke": "npm run build && vitest run --config vitest.smoke.config.ts", "test:watch": "vitest", diff --git a/schemas/structured-output/xcodebuildmcp.output.build-result/3.schema.json b/schemas/structured-output/xcodebuildmcp.output.build-result/3.schema.json new file mode 100644 index 000000000..a1ac882a9 --- /dev/null +++ b/schemas/structured-output/xcodebuildmcp.output.build-result/3.schema.json @@ -0,0 +1,122 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://xcodebuildmcp.com/schemas/structured-output/xcodebuildmcp.output.build-result/3.schema.json", + "type": "object", + "additionalProperties": false, + "allOf": [ + { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/errorConsistency" + } + ], + "$defs": { + "buildInvocationRequest": { + "type": "object", + "additionalProperties": false, + "properties": { + "buildForTesting": { "type": "boolean" }, + "scheme": { "type": "string" }, + "workspacePath": { "type": "string" }, + "projectPath": { "type": "string" }, + "packagePath": { "type": "string" }, + "targetName": { "type": "string" }, + "configuration": { "type": "string" }, + "platform": { "type": "string" }, + "target": { + "enum": ["simulator", "device", "macos", "swift-package"] + }, + "simulatorName": { "type": "string" }, + "simulatorId": { "type": "string" }, + "deviceId": { "type": "string" }, + "executableName": { "type": "string" }, + "arch": { "type": "string" }, + "derivedDataPath": { "type": "string" }, + "testProductsPath": { "type": "string" }, + "xctestrunPath": { "type": "string" }, + "onlyTesting": { + "type": "array", + "items": { "type": "string" } + }, + "skipTesting": { + "type": "array", + "items": { "type": "string" } + } + } + } + }, + "properties": { + "schema": { + "const": "xcodebuildmcp.output.build-result" + }, + "schemaVersion": { + "const": "3" + }, + "didError": { + "type": "boolean" + }, + "error": { + "type": ["string", "null"] + }, + "data": { + "anyOf": [ + { + "type": "object", + "additionalProperties": false, + "properties": { + "request": { + "$ref": "#/$defs/buildInvocationRequest" + }, + "summary": { + "type": "object", + "additionalProperties": false, + "properties": { + "status": { + "enum": ["SUCCEEDED", "FAILED"] + }, + "durationMs": { + "type": "integer", + "minimum": 0 + }, + "target": { + "enum": ["simulator", "device", "macos", "swift-package"] + } + }, + "required": ["status"] + }, + "artifacts": { + "type": "object", + "additionalProperties": false, + "properties": { + "appPath": { "type": "string" }, + "bundleId": { "type": "string" }, + "buildLogPath": { "type": "string" }, + "packagePath": { "type": "string" }, + "workspacePath": { "type": "string" }, + "scheme": { "type": "string" }, + "configuration": { "type": "string" }, + "platform": { "type": "string" }, + "testProductsPath": { "type": "string" }, + "xctestrunPaths": { + "type": "array", + "items": { "type": "string" } + } + }, + "required": [], + "minProperties": 1 + }, + "diagnostics": { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/basicDiagnostics" + } + }, + "required": ["summary", "artifacts", "diagnostics"] + }, + { + "type": "null" + } + ] + }, + "nextSteps": { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/nextSteps" + } + }, + "required": ["schema", "schemaVersion", "didError", "error", "data"] +} diff --git a/schemas/structured-output/xcodebuildmcp.output.test-result/3.schema.json b/schemas/structured-output/xcodebuildmcp.output.test-result/3.schema.json new file mode 100644 index 000000000..4cc8d3fd2 --- /dev/null +++ b/schemas/structured-output/xcodebuildmcp.output.test-result/3.schema.json @@ -0,0 +1,148 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://xcodebuildmcp.com/schemas/structured-output/xcodebuildmcp.output.test-result/3.schema.json", + "type": "object", + "additionalProperties": false, + "allOf": [ + { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/errorConsistency" + } + ], + "$defs": { + "buildInvocationRequest": { + "type": "object", + "additionalProperties": false, + "properties": { + "buildForTesting": { "type": "boolean" }, + "scheme": { "type": "string" }, + "workspacePath": { "type": "string" }, + "projectPath": { "type": "string" }, + "packagePath": { "type": "string" }, + "targetName": { "type": "string" }, + "configuration": { "type": "string" }, + "platform": { "type": "string" }, + "target": { + "enum": ["simulator", "device", "macos", "swift-package"] + }, + "simulatorName": { "type": "string" }, + "simulatorId": { "type": "string" }, + "deviceId": { "type": "string" }, + "executableName": { "type": "string" }, + "arch": { "type": "string" }, + "derivedDataPath": { "type": "string" }, + "testProductsPath": { "type": "string" }, + "xctestrunPath": { "type": "string" }, + "onlyTesting": { + "type": "array", + "items": { "type": "string" } + }, + "skipTesting": { + "type": "array", + "items": { "type": "string" } + } + } + } + }, + "properties": { + "schema": { + "const": "xcodebuildmcp.output.test-result" + }, + "schemaVersion": { + "const": "3" + }, + "didError": { + "type": "boolean" + }, + "error": { + "type": ["string", "null"] + }, + "data": { + "type": "object", + "additionalProperties": false, + "properties": { + "request": { + "$ref": "#/$defs/buildInvocationRequest" + }, + "summary": { + "type": "object", + "additionalProperties": false, + "properties": { + "status": { + "enum": ["SUCCEEDED", "FAILED"] + }, + "durationMs": { + "type": "integer", + "minimum": 0 + }, + "target": { + "enum": ["simulator", "device", "macos", "swift-package"] + }, + "counts": { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/counts" + } + }, + "required": ["status"] + }, + "artifacts": { + "type": "object", + "additionalProperties": false, + "properties": { + "deviceId": { "type": "string" }, + "buildLogPath": { "type": "string" }, + "packagePath": { "type": "string" }, + "xcresultPath": { "type": "string" }, + "testProductsPath": { "type": "string" }, + "xctestrunPath": { "type": "string" } + }, + "required": [], + "minProperties": 1 + }, + "diagnostics": { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/testDiagnostics" + }, + "tests": { + "type": "object", + "additionalProperties": false, + "properties": { + "selected": { + "type": "array", + "items": { "type": "string" } + }, + "discovered": { + "type": "object", + "additionalProperties": false, + "properties": { + "total": { "type": "integer", "minimum": 0 }, + "items": { + "type": "array", + "items": { "type": "string" } + } + }, + "required": ["total", "items"] + } + }, + "required": [] + }, + "testCases": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "suite": { "type": "string" }, + "test": { "type": "string" }, + "status": { "enum": ["passed", "failed", "skipped"] }, + "durationMs": { "type": "integer", "minimum": 0 } + }, + "required": ["test", "status"] + } + } + }, + "required": ["summary", "artifacts", "diagnostics"] + }, + "nextSteps": { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/nextSteps" + } + }, + "required": ["schema", "schemaVersion", "didError", "error", "data"] +} diff --git a/src/cli/commands/purge-ui.ts b/src/cli/commands/purge-ui.ts index 78bf51ecf..8feb7d742 100644 --- a/src/cli/commands/purge-ui.ts +++ b/src/cli/commands/purge-ui.ts @@ -14,6 +14,7 @@ const CLASS_LABELS: Record = { derivedData: 'DerivedData', logs: 'Logs', resultBundles: 'Result bundles', + testProducts: 'Test products', stateTransients: 'State transients', locks: 'Locks', }; @@ -345,5 +346,5 @@ export function executionToJson(result: PurgeStorageExecutionResult): object { } export function defaultClassKeys(): PurgeStorageDeletableClass[] { - return ['logs', 'resultBundles', 'stateTransients']; + return ['logs', 'resultBundles', 'testProducts', 'stateTransients']; } diff --git a/src/cli/commands/purge.ts b/src/cli/commands/purge.ts index e76cc75d8..99910aa39 100644 --- a/src/cli/commands/purge.ts +++ b/src/cli/commands/purge.ts @@ -348,7 +348,7 @@ export function registerPurgeCommand(app: Argv, opts: { currentWorkspaceKey: str .option('classes', { type: 'string', describe: - 'Comma-separated storage classes: derivedData,logs,resultBundles,stateTransients. "all" excludes DerivedData.', + 'Comma-separated storage classes: derivedData,logs,resultBundles,testProducts,stateTransients. "all" excludes DerivedData.', }) .option('older-than', { type: 'string', diff --git a/src/core/__tests__/structured-output-schema.test.ts b/src/core/__tests__/structured-output-schema.test.ts index e999df86f..a415ab3af 100644 --- a/src/core/__tests__/structured-output-schema.test.ts +++ b/src/core/__tests__/structured-output-schema.test.ts @@ -211,6 +211,54 @@ describe('structured output schema bundling', () => { ).toBe(true); }); + it('accepts prepared test artifacts only in build and test result v3', () => { + const ajv = new Ajv2020({ allErrors: true, strict: true, validateSchema: true }); + const buildV2 = ajv.compile( + getMcpOutputSchema({ schema: 'xcodebuildmcp.output.build-result', version: '2' }), + ); + const buildV3 = ajv.compile( + getMcpOutputSchema({ schema: 'xcodebuildmcp.output.build-result', version: '3' }), + ); + const testV3 = ajv.compile( + getMcpOutputSchema({ schema: 'xcodebuildmcp.output.test-result', version: '3' }), + ); + const buildEnvelope = { + schema: 'xcodebuildmcp.output.build-result', + schemaVersion: '3', + didError: false, + error: null, + data: { + request: { buildForTesting: true, scheme: 'CalculatorApp' }, + summary: { status: 'SUCCEEDED', target: 'simulator' }, + artifacts: { + testProductsPath: 'artifacts/CalculatorApp.xctestproducts', + xctestrunPaths: ['artifacts/CalculatorApp.xctestproducts/CalculatorApp.xctestrun'], + }, + diagnostics: { warnings: [], errors: [] }, + }, + }; + + expect(buildV2({ ...buildEnvelope, schemaVersion: '2' })).toBe(false); + expect(buildV3(buildEnvelope)).toBe(true); + expect( + testV3({ + schema: 'xcodebuildmcp.output.test-result', + schemaVersion: '3', + didError: false, + error: null, + data: { + request: { testProductsPath: 'artifacts/CalculatorApp.xctestproducts' }, + summary: { status: 'SUCCEEDED', target: 'simulator' }, + artifacts: { + testProductsPath: 'artifacts/CalculatorApp.xctestproducts', + xcresultPath: 'artifacts/test.xcresult', + }, + diagnostics: { warnings: [], errors: [], testFailures: [] }, + }, + }), + ).toBe(true); + }); + it('accepts video recording capture payloads in the bumped capture contract', () => { const schema = getMcpOutputSchema({ schema: 'xcodebuildmcp.output.capture-result', diff --git a/src/core/manifest/__tests__/schema.test.ts b/src/core/manifest/__tests__/schema.test.ts index 528339bd3..232cd474c 100644 --- a/src/core/manifest/__tests__/schema.test.ts +++ b/src/core/manifest/__tests__/schema.test.ts @@ -98,6 +98,43 @@ describe('schema', () => { }); }); + it('parses a custom next-step condition key', () => { + const result = toolManifestEntrySchema.safeParse({ + id: 'build_sim', + module: 'mcp/tools/simulator/build_sim', + names: { mcp: 'build_sim' }, + nextSteps: [ + { + label: 'Run prepared tests', + toolId: 'test_sim', + when: 'success', + condition: 'prepared_tests_available', + }, + ], + }); + + expect(result.success).toBe(true); + if (!result.success) throw new Error('Expected custom next-step condition to parse'); + expect(result.data.nextSteps[0]).toMatchObject({ + when: 'success', + condition: 'prepared_tests_available', + }); + }); + + it.each(['', 'PreparedTests', 'prepared-tests', 'prepared tests'])( + 'rejects invalid next-step condition key %j', + (condition) => { + const result = toolManifestEntrySchema.safeParse({ + id: 'build_sim', + module: 'mcp/tools/simulator/build_sim', + names: { mcp: 'build_sim' }, + nextSteps: [{ label: 'Run prepared tests', condition }], + }); + + expect(result.success).toBe(false); + }, + ); + it('rejects invalid output schema metadata', () => { const result = toolManifestEntrySchema.safeParse({ id: 'list_sims', diff --git a/src/core/manifest/schema.ts b/src/core/manifest/schema.ts index d75be3219..9a5be2769 100644 --- a/src/core/manifest/schema.ts +++ b/src/core/manifest/schema.ts @@ -73,6 +73,10 @@ export const manifestNextStepTemplateSchema = z params: z.record(z.string(), z.union([z.string(), z.number(), z.boolean()])).default({}), priority: z.number().optional(), when: z.enum(['always', 'success', 'failure']).default('always'), + condition: z + .string() + .regex(/^[a-z][a-z0-9_]*$/) + .optional(), }) .strict(); diff --git a/src/daemon/__tests__/conditional-next-steps.test.ts b/src/daemon/__tests__/conditional-next-steps.test.ts new file mode 100644 index 000000000..8a974fd66 --- /dev/null +++ b/src/daemon/__tests__/conditional-next-steps.test.ts @@ -0,0 +1,128 @@ +import { mkdtemp, rm } from 'node:fs/promises'; +import path from 'node:path'; +import { tmpdir } from 'node:os'; +import type net from 'node:net'; +import { afterEach, describe, expect, it } from 'vitest'; +import { createRenderSession } from '../../rendering/render.ts'; +import { createToolCatalog } from '../../runtime/tool-catalog.ts'; +import { DefaultToolInvoker } from '../../runtime/tool-invoker.ts'; +import type { ToolDefinition } from '../../runtime/types.ts'; +import { startDaemonServer } from '../daemon-server.ts'; + +function createTool(overrides: Partial = {}): ToolDefinition { + return { + id: 'source', + cliName: 'source', + mcpName: 'source', + workflow: 'simulator', + cliSchema: {}, + mcpSchema: {}, + stateful: true, + handler: async () => {}, + ...overrides, + }; +} + +async function createSocketPath(): Promise { + const directory = await mkdtemp(path.join(tmpdir(), 'xcodebuildmcp-daemon-conditions-')); + return path.join(directory, 'daemon.sock'); +} + +async function listen(server: net.Server, socketPath: string): Promise { + await new Promise((resolve, reject) => { + server.once('error', reject); + server.listen(socketPath, () => { + server.off('error', reject); + resolve(); + }); + }); +} + +describe('daemon conditional next steps', () => { + const cleanupPaths: string[] = []; + const cleanupServers: net.Server[] = []; + + afterEach(async () => { + await Promise.all( + cleanupServers.splice(0).map( + (server) => + new Promise((resolve) => { + server.close(() => resolve()); + }), + ), + ); + await Promise.all( + cleanupPaths.splice(0).map(async (socketPath) => { + await rm(path.dirname(socketPath), { recursive: true, force: true }); + }), + ); + }); + + it('selects a manifest template using condition metadata returned by the daemon', async () => { + const daemonSource = createTool({ + handler: async (_args, ctx) => { + ctx.nextStepConditionKeys = ['prepared_tests_available']; + ctx.nextStepParams = { + test_sim: { testProductsPath: '/tmp/App.xctestproducts' }, + }; + ctx.structuredOutput = { + schema: 'xcodebuildmcp.output.simulator-list', + schemaVersion: '1', + result: { + kind: 'simulator-list', + didError: false, + error: null, + simulators: [], + }, + }; + }, + }); + const clientSource = createTool({ + nextStepTemplates: [ + { + label: 'Run prepared tests', + toolId: 'test_sim', + when: 'success', + condition: 'prepared_tests_available', + }, + ], + }); + const testTool = createTool({ + id: 'test_sim', + cliName: 'test', + mcpName: 'test_sim', + stateful: false, + }); + const socketPath = await createSocketPath(); + cleanupPaths.push(socketPath); + + const server = startDaemonServer({ + socketPath, + startedAt: new Date().toISOString(), + enabledWorkflows: ['simulator'], + catalog: createToolCatalog([daemonSource, testTool]), + workspaceRoot: '/repo', + workspaceKey: 'repo-key', + xcodeIdeWorkflowEnabled: false, + requestShutdown: () => {}, + }); + cleanupServers.push(server); + await listen(server, socketPath); + + const session = createRenderSession('text'); + const invoker = new DefaultToolInvoker(createToolCatalog([clientSource, testTool])); + await invoker.invoke('source', {}, { runtime: 'cli', renderSession: session, socketPath }); + + expect(session.getNextSteps?.()).toEqual([ + { + tool: 'test_sim', + cliTool: 'test', + workflow: 'simulator', + label: 'Run prepared tests', + params: { testProductsPath: '/tmp/App.xctestproducts' }, + priority: undefined, + when: 'success', + }, + ]); + }); +}); diff --git a/src/daemon/__tests__/tool-invoke-streaming.test.ts b/src/daemon/__tests__/tool-invoke-streaming.test.ts index 50d1f104f..f1e11a0f6 100644 --- a/src/daemon/__tests__/tool-invoke-streaming.test.ts +++ b/src/daemon/__tests__/tool-invoke-streaming.test.ts @@ -93,6 +93,7 @@ describe('daemon tool.invoke streaming', () => { stream: 'stderr', line: 'Build Log: /tmp/build.log', }); + ctx.nextStepConditionKeys = ['build_artifact_available']; ctx.nextSteps = [{ label: 'Open the build log' }]; ctx.structuredOutput = { schema: 'xcodebuildmcp.output.simulator-list', @@ -147,6 +148,7 @@ describe('daemon tool.invoke streaming', () => { simulators: [], }, }, + nextStepConditionKeys: ['build_artifact_available'], nextSteps: [{ label: 'Open the build log' }], }); }); diff --git a/src/daemon/daemon-server.ts b/src/daemon/daemon-server.ts index b9c81869f..bdd0d1dad 100644 --- a/src/daemon/daemon-server.ts +++ b/src/daemon/daemon-server.ts @@ -221,6 +221,7 @@ export function startDaemonServer(ctx: DaemonServerContext): net.Server { result: { structuredOutput: handlerContext.structuredOutput ?? null, nextStepParams: handlerContext.nextStepParams, + nextStepConditionKeys: handlerContext.nextStepConditionKeys, nextSteps: handlerContext.nextSteps, }, }; diff --git a/src/daemon/protocol.ts b/src/daemon/protocol.ts index d3c38e920..976640504 100644 --- a/src/daemon/protocol.ts +++ b/src/daemon/protocol.ts @@ -2,7 +2,7 @@ import type { ToolAnnotations } from '@modelcontextprotocol/sdk/types.js'; import type { StructuredToolOutput } from '../rendering/types.ts'; import type { NextStep, NextStepParamsMap } from '../types/common.ts'; import type { AnyFragment } from '../types/domain-fragments.ts'; -export const DAEMON_PROTOCOL_VERSION = 7 as const; +export const DAEMON_PROTOCOL_VERSION = 8 as const; export type DaemonMethod = | 'daemon.status' @@ -49,6 +49,7 @@ export type ToolInvokeProgressStream = { kind: 'fragment'; fragment: AnyFragment export interface ToolInvokeResult { structuredOutput: StructuredToolOutput | null; nextStepParams?: NextStepParamsMap; + nextStepConditionKeys?: string[]; nextSteps?: NextStep[]; } @@ -68,6 +69,7 @@ export interface DaemonToolResult { structuredOutput: StructuredToolOutput | null; isError: boolean; nextStepParams?: NextStepParamsMap; + nextStepConditionKeys?: string[]; nextSteps?: NextStep[]; } diff --git a/src/mcp/tools/device/__tests__/build_device.test.ts b/src/mcp/tools/device/__tests__/build_device.test.ts index 10b7a6a11..765f838bd 100644 --- a/src/mcp/tools/device/__tests__/build_device.test.ts +++ b/src/mcp/tools/device/__tests__/build_device.test.ts @@ -49,7 +49,37 @@ describe('build_device plugin', () => { expect(schemaObj.safeParse({ platform: 'macOS' }).success).toBe(false); const schemaKeys = Object.keys(schema).sort(); - expect(schemaKeys).toEqual(['extraArgs', 'platform']); + expect(schemaKeys).toEqual([ + 'buildForTesting', + 'deviceId', + 'extraArgs', + 'platform', + 'testProductsPath', + ]); + }); + + it('should reject testProductsPath without buildForTesting', async () => { + const result = await callHandler(handler, { + projectPath: '/path/to/MyProject.xcodeproj', + scheme: 'MyScheme', + testProductsPath: '/tmp/MyApp.xctestproducts', + }); + + expect(result.isError).toBe(true); + expect(result.content[0].text).toContain( + 'testProductsPath requires buildForTesting to be true', + ); + }); + + it('should reject deviceId without buildForTesting', async () => { + const result = await callHandler(handler, { + projectPath: '/path/to/MyProject.xcodeproj', + scheme: 'MyScheme', + deviceId: 'DEVICE-UDID', + }); + + expect(result.isError).toBe(true); + expect(result.content[0].text).toContain('deviceId requires buildForTesting to be true'); }); }); @@ -272,6 +302,7 @@ describe('build_device plugin', () => { scheme: 'MyScheme', derivedDataPath: '/tmp/derived-data', }); + expect(result.nextStepConditionKeys).toEqual(['app_build_succeeded']); }); it('should return exact build failure response', async () => { @@ -358,5 +389,62 @@ describe('build_device plugin', () => { }), ); }); + + it('should prepare reusable test products for a selected device', async () => { + const spy = createSpyExecutor(); + const testProductsPath = '/tmp/MyApp Tests.xctestproducts'; + + const { result } = await runToolLogic(() => + buildDeviceLogic( + { + projectPath: '/path/to/MyProject.xcodeproj', + scheme: 'MyScheme', + platform: 'iOS', + deviceId: 'DEVICE-UDID', + buildForTesting: true, + testProductsPath, + }, + spy.executor, + ), + ); + + expect(spy.commandCalls).toHaveLength(1); + expect(spy.commandCalls[0].args).toContain('platform=iOS,id=DEVICE-UDID'); + expect(spy.commandCalls[0].args.slice(-3)).toEqual([ + '-testProductsPath', + testProductsPath, + 'build-for-testing', + ]); + expect(spy.commandCalls[0].logPrefix).toBe('iOS Device Build for Testing'); + expect(result.nextStepParams).toEqual({ + test_device: { + testProductsPath, + deviceId: 'DEVICE-UDID', + platform: 'iOS', + }, + }); + expect(result.nextStepConditionKeys).toEqual(['prepared_tests_available']); + }); + + it('should not suggest running generic device test products without a device', async () => { + const spy = createSpyExecutor(); + const testProductsPath = '/tmp/MyApp Tests.xctestproducts'; + + const { result } = await runToolLogic(() => + buildDeviceLogic( + { + projectPath: '/path/to/MyProject.xcodeproj', + scheme: 'MyScheme', + buildForTesting: true, + testProductsPath, + }, + spy.executor, + ), + ); + + expect(spy.commandCalls[0].args).toContain('generic/platform=iOS'); + expect(result.nextStepParams).toBeUndefined(); + expect(result.nextStepConditionKeys).toBeUndefined(); + }); }); }); diff --git a/src/mcp/tools/device/__tests__/test_device.test.ts b/src/mcp/tools/device/__tests__/test_device.test.ts index f007bfd7e..9c7cb833f 100644 --- a/src/mcp/tools/device/__tests__/test_device.test.ts +++ b/src/mcp/tools/device/__tests__/test_device.test.ts @@ -71,7 +71,14 @@ describe('test_device plugin', () => { ); const schemaKeys = Object.keys(schema).sort(); - expect(schemaKeys).toEqual(['extraArgs', 'platform', 'progress', 'testRunnerEnv']); + expect(schemaKeys).toEqual([ + 'extraArgs', + 'platform', + 'progress', + 'testProductsPath', + 'testRunnerEnv', + 'xctestrunPath', + ]); }); it('should validate XOR between projectPath and workspacePath', async () => { @@ -112,7 +119,7 @@ describe('test_device plugin', () => { expect(result.isError).toBe(true); expect(result.content[0].text).toContain('Missing required session defaults'); - expect(result.content[0].text).toContain('Provide scheme and deviceId'); + expect(result.content[0].text).toContain('Provide deviceId'); }); it('should require project or workspace when defaults provide scheme and device', async () => { @@ -121,7 +128,7 @@ describe('test_device plugin', () => { const result = await callHandler(handler, {}); expect(result.isError).toBe(true); - expect(result.content[0].text).toContain('Provide a project or workspace'); + expect(result.content[0].text).toContain('Either projectPath or workspacePath is required'); }); it('should reject mutually exclusive project inputs when defaults satisfy requirements', async () => { @@ -155,7 +162,7 @@ describe('test_device plugin', () => { mockFs(), ); - expect(spy.commandCalls).toHaveLength(1); + expect(spy.commandCalls).toHaveLength(2); expect(spy.commandCalls[0].args).toEqual([ 'xcodebuild', '-project', @@ -171,9 +178,21 @@ describe('test_device plugin', () => { 'never', '-derivedDataPath', computeScopedDerivedDataPath('/path/to/project.xcodeproj'), + '-testProductsPath', + expect.stringContaining('/test-products/test_device_'), + 'build-for-testing', + ]); + expect(spy.commandCalls[1].args).toEqual([ + 'xcodebuild', + '-testProductsPath', + expect.stringContaining('/test-products/test_device_'), + '-destination', + 'platform=iOS,id=test-device-123', + '-collect-test-diagnostics', + 'never', '-resultBundlePath', expect.stringContaining('/result-bundles/test_device_'), - 'test', + 'test-without-building', ]); }); }); diff --git a/src/mcp/tools/device/build_device.ts b/src/mcp/tools/device/build_device.ts index e3921d244..78760b0ba 100644 --- a/src/mcp/tools/device/build_device.ts +++ b/src/mcp/tools/device/build_device.ts @@ -27,11 +27,54 @@ import { setXcodebuildStructuredOutput, } from '../../../utils/xcodebuild-domain-results.ts'; import type { BuildInvocationRequest } from '../../../types/domain-fragments.ts'; +import { displayPath } from '../../../utils/build-preflight.ts'; import { resolveEffectiveDerivedDataPath } from '../../../utils/derived-data-path.ts'; +import { resolvePathFromCwd } from '../../../utils/path.ts'; +import { + createDefaultTestProductsPath, + findXctestrunPaths, + markTestProductsPathCompleted, +} from '../../../utils/test-products-path.ts'; import { createBuildInvocationFragment } from '../../../utils/xcodebuild-pipeline.ts'; -function createBuildDeviceRequest(params: BuildDeviceParams): BuildInvocationRequest { +interface PreparedBuildDeviceExecution { + buildAction: 'build' | 'build-for-testing'; + invocationRequest: BuildInvocationRequest; + isManagedTestProductsPath: boolean; + logLabel: 'Build' | 'Build for Testing'; + sharedBuildParams: BuildDeviceParams; + testProductsPath?: string; +} + +function prepareBuildDeviceExecution(params: BuildDeviceParams): PreparedBuildDeviceExecution { + const buildForTesting = params.buildForTesting ?? false; + const isManagedTestProductsPath = buildForTesting && params.testProductsPath === undefined; + const testProductsPath = buildForTesting + ? (resolvePathFromCwd(params.testProductsPath) ?? createDefaultTestProductsPath('build_device')) + : undefined; + const sharedBuildParams = testProductsPath + ? { + ...params, + extraArgs: [...(params.extraArgs ?? []), '-testProductsPath', testProductsPath], + } + : params; + return { + buildAction: buildForTesting ? 'build-for-testing' : 'build', + invocationRequest: createBuildDeviceRequest(params, testProductsPath), + isManagedTestProductsPath, + logLabel: buildForTesting ? 'Build for Testing' : 'Build', + sharedBuildParams, + testProductsPath, + }; +} + +function createBuildDeviceRequest( + params: BuildDeviceParams, + testProductsPath?: string, +): BuildInvocationRequest { + return { + ...(params.buildForTesting ? { buildForTesting: true } : {}), scheme: params.scheme, workspacePath: params.workspacePath, projectPath: params.projectPath, @@ -39,6 +82,8 @@ function createBuildDeviceRequest(params: BuildDeviceParams): BuildInvocationReq configuration: params.configuration, platform: String(mapDevicePlatform(params.platform)), target: 'device', + ...(params.buildForTesting && params.deviceId ? { deviceId: params.deviceId } : {}), + ...(testProductsPath ? { testProductsPath: displayPath(testProductsPath) } : {}), }; } @@ -51,11 +96,26 @@ const baseSchemaObject = z.object({ derivedDataPath: z.string().optional(), extraArgs: z.array(z.string()).optional(), preferXcodebuild: z.boolean().optional(), + deviceId: z.string().optional().describe('UDID of the destination device'), + buildForTesting: z + .boolean() + .optional() + .describe('Build reusable test products without running tests (default: false)'), + testProductsPath: z + .string() + .optional() + .describe('Output path for the .xctestproducts bundle when buildForTesting is true'), }); const buildDeviceSchema = z.preprocess( nullifyEmptyStrings, - withProjectOrWorkspace(baseSchemaObject), + withProjectOrWorkspace(baseSchemaObject) + .refine((params) => params.testProductsPath === undefined || params.buildForTesting === true, { + message: 'testProductsPath requires buildForTesting to be true', + }) + .refine((params) => params.deviceId === undefined || params.buildForTesting === true, { + message: 'deviceId requires buildForTesting to be true', + }), ); export type BuildDeviceParams = z.infer; @@ -72,37 +132,50 @@ const publicSchemaObject = baseSchemaObject.omit({ export function createBuildDeviceExecutor( executor: CommandExecutor, + prepared?: PreparedBuildDeviceExecution, ): StreamingExecutor { return async (params, ctx) => { + const resolved = prepared ?? prepareBuildDeviceExecution(params); const platform = mapDevicePlatform(params.platform); - const processedParams = { - ...params, - configuration: params.configuration, - }; const started = createDomainStreamingPipeline('build_device', 'BUILD', ctx, 'build-result'); const buildResult = await executeXcodeBuildCommand( - processedParams, + resolved.sharedBuildParams, { platform, - logPrefix: `${platform} Device Build`, + logPrefix: `${platform} Device ${resolved.logLabel}`, + deviceId: params.buildForTesting ? params.deviceId : undefined, }, params.preferXcodebuild ?? false, - 'build', + resolved.buildAction, executor, undefined, started.pipeline, ); + const succeeded = !buildResult.isError; + + if (resolved.isManagedTestProductsPath) { + markTestProductsPathCompleted(resolved.testProductsPath); + } + + const xctestrunPaths = + succeeded && resolved.testProductsPath + ? await findXctestrunPaths(resolved.testProductsPath) + : []; return createBuildDomainResult({ started, - succeeded: !buildResult.isError, + succeeded, target: 'device', artifacts: { - buildLogPath: started.pipeline.logPath, + buildLogPath: displayPath(started.pipeline.logPath), + ...(succeeded && resolved.testProductsPath + ? { testProductsPath: displayPath(resolved.testProductsPath) } + : {}), + ...(xctestrunPaths.length > 0 ? { xctestrunPaths: xctestrunPaths.map(displayPath) } : {}), }, fallbackErrorMessages: collectFallbackErrorMessages(started, [], buildResult.content), - request: createBuildDeviceRequest(params), + request: resolved.invocationRequest, }); }; } @@ -112,18 +185,26 @@ export async function buildDeviceLogic( executor: CommandExecutor, ): Promise { const ctx = getHandlerContext(); - const invocationRequest = createBuildDeviceRequest(params); + const prepared = prepareBuildDeviceExecution(params); - ctx.emit(createBuildInvocationFragment('build-result', 'BUILD', invocationRequest)); + ctx.emit(createBuildInvocationFragment('build-result', 'BUILD', prepared.invocationRequest)); const executionContext = createStreamingExecutionContext(ctx); - const executeBuildDevice = createBuildDeviceExecutor(executor); + const executeBuildDevice = createBuildDeviceExecutor(executor, prepared); const result = await executeBuildDevice(params, executionContext); - setXcodebuildStructuredOutput(ctx, 'build-result', result); + setXcodebuildStructuredOutput(ctx, 'build-result', result, '3'); if (!result.didError) { - ctx.nextStepParams = { - get_device_app_path: { + if (prepared.testProductsPath && params.deviceId) { + const nextParams = { + testProductsPath: displayPath(prepared.testProductsPath), + deviceId: params.deviceId, + ...(params.platform ? { platform: String(mapDevicePlatform(params.platform)) } : {}), + }; + ctx.nextStepParams = { test_device: nextParams }; + ctx.nextStepConditionKeys = ['prepared_tests_available']; + } else if (!prepared.testProductsPath) { + const nextParams = { scheme: params.scheme, ...(params.derivedDataPath !== undefined ? { derivedDataPath: params.derivedDataPath } @@ -131,8 +212,10 @@ export async function buildDeviceLogic( ...(params.platform !== undefined ? { platform: String(mapDevicePlatform(params.platform)) } : {}), - }, - }; + }; + ctx.nextStepParams = { get_device_app_path: nextParams }; + ctx.nextStepConditionKeys = ['app_build_succeeded']; + } } } @@ -145,6 +228,7 @@ export const handler = createSessionAwareTool({ internalSchema: toInternalSchema(buildDeviceSchema), logicFunction: buildDeviceLogic, getExecutor: getDefaultCommandExecutor, + clearSessionDeviceIdUnlessPrepared: true, requirements: [ { allOf: ['scheme'], message: 'scheme is required' }, { oneOf: ['projectPath', 'workspacePath'], message: 'Provide a project or workspace' }, diff --git a/src/mcp/tools/device/test_device.ts b/src/mcp/tools/device/test_device.ts index c5582a378..eb948ea55 100644 --- a/src/mcp/tools/device/test_device.ts +++ b/src/mcp/tools/device/test_device.ts @@ -21,7 +21,12 @@ import { getSessionAwareToolSchemaShape, toInternalSchema, } from '../../../utils/typed-tool-factory.ts'; -import { nullifyEmptyStrings, withProjectOrWorkspace } from '../../../utils/schema-helpers.ts'; +import { nullifyEmptyStrings } from '../../../utils/schema-helpers.ts'; +import { + hasPreparedTestSource, + TEST_SOURCE_EXCLUSIVE_GROUPS, + withProjectWorkspaceOrTestArtifact, +} from '../../../utils/test-source.ts'; import { resolveTestPreflight, type TestPreflightResult } from '../../../utils/test-preflight.ts'; import { getHandlerContext } from '../../../utils/typed-tool-factory.ts'; import { @@ -31,11 +36,20 @@ import { import type { BuildInvocationRequest } from '../../../types/domain-fragments.ts'; import { resolveEffectiveDerivedDataPath } from '../../../utils/derived-data-path.ts'; import { createBuildInvocationFragment } from '../../../utils/xcodebuild-pipeline.ts'; +import { displayPath } from '../../../utils/build-preflight.ts'; const baseSchemaObject = z.object({ projectPath: z.string().optional().describe('Path to the .xcodeproj file'), workspacePath: z.string().optional().describe('Path to the .xcworkspace file'), - scheme: z.string().describe('The scheme to test'), + scheme: z.string().optional().describe('The scheme to test in source mode'), + testProductsPath: z + .string() + .optional() + .describe('Path to a prepared .xctestproducts package. Cannot be combined with source inputs'), + xctestrunPath: z + .string() + .optional() + .describe('Path to a prepared .xctestrun file. Cannot be combined with source inputs'), deviceId: z.string().describe('UDID of the device (obtained from list_devices)'), configuration: z.string().optional().describe('Build configuration (Debug, Release)'), derivedDataPath: z.string().optional(), @@ -56,7 +70,7 @@ const baseSchemaObject = z.object({ const testDeviceSchema = z.preprocess( nullifyEmptyStrings, - withProjectOrWorkspace(baseSchemaObject), + withProjectWorkspaceOrTestArtifact(baseSchemaObject), ); export type TestDeviceParams = z.infer; @@ -83,19 +97,22 @@ async function prepareTestDeviceExecution( params: TestDeviceParams, fileSystemExecutor: FileSystemExecutor, ): Promise { - const configuration = params.configuration; + const preparedTestSource = hasPreparedTestSource(params); + const configuration = preparedTestSource ? undefined : params.configuration; const platform = mapDevicePlatform(params.platform); - const preflight = await resolveTestPreflight( - { - projectPath: params.projectPath, - workspacePath: params.workspacePath, - scheme: params.scheme, - configuration, - extraArgs: params.extraArgs, - destinationName: params.deviceId, - }, - fileSystemExecutor, - ); + const preflight = preparedTestSource + ? null + : await resolveTestPreflight( + { + projectPath: params.projectPath, + workspacePath: params.workspacePath, + scheme: params.scheme!, + configuration, + extraArgs: params.extraArgs, + destinationName: params.deviceId, + }, + fileSystemExecutor, + ); return { configuration, @@ -105,11 +122,13 @@ async function prepareTestDeviceExecution( scheme: params.scheme, workspacePath: params.workspacePath, projectPath: params.projectPath, - derivedDataPath: resolveEffectiveDerivedDataPath(params), + derivedDataPath: preparedTestSource ? undefined : resolveEffectiveDerivedDataPath(params), configuration, platform: String(platform), deviceId: params.deviceId, target: 'device' as const, + testProductsPath: params.testProductsPath ? displayPath(params.testProductsPath) : undefined, + xctestrunPath: params.xctestrunPath ? displayPath(params.xctestrunPath) : undefined, onlyTesting: preflight?.selectors.onlyTesting.map((selector) => selector.raw), skipTesting: preflight?.selectors.skipTesting.map((selector) => selector.raw), } satisfies BuildInvocationRequest, @@ -144,6 +163,8 @@ export function createTestDeviceExecutor( useLatestOS: false, testRunnerEnv: params.testRunnerEnv, progress: params.progress, + testProductsPath: params.testProductsPath, + xctestrunPath: params.xctestrunPath, }, ctx, ); @@ -163,7 +184,7 @@ export async function testDeviceLogic( const executeTestDevice = createTestDeviceExecutor(executor, fileSystemExecutor, prepared); const result = await executeTestDevice(params, executionContext); - setXcodebuildStructuredOutput(ctx, 'test-result', result); + setXcodebuildStructuredOutput(ctx, 'test-result', result, '3'); } export const schema = getSessionAwareToolSchemaShape({ @@ -176,9 +197,6 @@ export const handler = createSessionAwareTool({ logicFunction: (params, executor) => testDeviceLogic(params, executor, getDefaultFileSystemExecutor()), getExecutor: getDefaultCommandExecutor, - requirements: [ - { allOf: ['scheme', 'deviceId'], message: 'Provide scheme and deviceId' }, - { oneOf: ['projectPath', 'workspacePath'], message: 'Provide a project or workspace' }, - ], - exclusivePairs: [['projectPath', 'workspacePath']], + requirements: [{ allOf: ['deviceId'], message: 'Provide deviceId' }], + exclusivePairs: [...TEST_SOURCE_EXCLUSIVE_GROUPS, ['projectPath', 'workspacePath']], }); diff --git a/src/mcp/tools/macos/__tests__/build_macos.test.ts b/src/mcp/tools/macos/__tests__/build_macos.test.ts index ef24f5509..5cb94ac1a 100644 --- a/src/mcp/tools/macos/__tests__/build_macos.test.ts +++ b/src/mcp/tools/macos/__tests__/build_macos.test.ts @@ -51,7 +51,20 @@ describe('build_macos plugin', () => { expect(zodSchema.safeParse({ preferXcodebuild: true }).success).toBe(false); const schemaKeys = Object.keys(schema).sort(); - expect(schemaKeys).toEqual(['extraArgs']); + expect(schemaKeys).toEqual(['buildForTesting', 'extraArgs', 'testProductsPath']); + }); + + it('should reject testProductsPath without buildForTesting', async () => { + const result = await callHandler(handler, { + projectPath: '/path/to/MyProject.xcodeproj', + scheme: 'MyScheme', + testProductsPath: '/tmp/MyApp.xctestproducts', + }); + + expect(result.isError).toBe(true); + expect(result.content[0].text).toContain( + 'testProductsPath requires buildForTesting to be true', + ); }); }); @@ -152,6 +165,7 @@ describe('build_macos plugin', () => { derivedDataPath: '/path/to/derived-data', }, }); + expect(result.nextStepConditionKeys).toEqual(['app_build_succeeded']); }); it('should return exact exception handling response', async () => { @@ -367,6 +381,35 @@ describe('build_macos plugin', () => { 'build', ]); }); + + it('should prepare reusable macOS test products without app inspection', async () => { + const calls: string[][] = []; + const testProductsPath = '/tmp/MyApp Tests.xctestproducts'; + const executor = createMockExecutor({ + success: true, + output: 'BUILD SUCCEEDED', + onExecute: (command) => calls.push(command), + }); + + const { result } = await runBuildMacOS( + { + projectPath: '/path/to/project.xcodeproj', + scheme: 'MyScheme', + buildForTesting: true, + testProductsPath, + }, + executor, + ); + + expect(calls).toHaveLength(1); + expect(calls[0].slice(-3)).toEqual([ + '-testProductsPath', + testProductsPath, + 'build-for-testing', + ]); + expect(result.nextStepParams).toEqual({ test_macos: { testProductsPath } }); + expect(result.nextStepConditionKeys).toEqual(['prepared_tests_available']); + }); }); describe('XOR Validation', () => { diff --git a/src/mcp/tools/macos/__tests__/test_macos.test.ts b/src/mcp/tools/macos/__tests__/test_macos.test.ts index 84e95e950..8c79fdca3 100644 --- a/src/mcp/tools/macos/__tests__/test_macos.test.ts +++ b/src/mcp/tools/macos/__tests__/test_macos.test.ts @@ -54,7 +54,9 @@ describe('test_macos plugin (unified)', () => { expect(zodSchema.safeParse({ testRunnerEnv: { FOO: 123 } }).success).toBe(false); const schemaKeys = Object.keys(schema).sort(); - expect(schemaKeys).toEqual(['extraArgs', 'progress', 'testRunnerEnv'].sort()); + expect(schemaKeys).toEqual( + ['extraArgs', 'progress', 'testProductsPath', 'testRunnerEnv', 'xctestrunPath'].sort(), + ); }); }); @@ -72,7 +74,7 @@ describe('test_macos plugin (unified)', () => { const result = await callHandler(handler, {}); expect(result.isError).toBe(true); - expect(result.content[0].text).toContain('Provide a project or workspace'); + expect(result.content[0].text).toContain('Either projectPath or workspacePath is required'); }); it('should reject when both projectPath and workspacePath provided explicitly', async () => { @@ -95,7 +97,7 @@ describe('test_macos plugin (unified)', () => { }); expect(result.isError).toBe(true); - expect(result.content[0].text).toContain('Provide a project or workspace'); + expect(result.content[0].text).toContain('Either projectPath or workspacePath is required'); }); it('should validate that both projectPath and workspacePath cannot be provided', async () => { @@ -251,16 +253,16 @@ describe('test_macos plugin (unified)', () => { }); it('should return pending response on successful test', async () => { - const commandCalls: { command: string[]; logPrefix?: string }[] = []; + const commandCalls: { command: string[]; logPrefix?: string; cwd?: string }[] = []; const mockExecutor = async ( command: string[], logPrefix?: string, _useShell?: boolean, - _opts?: { env?: Record }, + opts?: { env?: Record; cwd?: string }, _detached?: boolean, ) => { - commandCalls.push({ command, logPrefix }); + commandCalls.push({ command, logPrefix, cwd: opts?.cwd }); return createMockCommandResponse({ success: true, output: 'Test Succeeded', @@ -278,14 +280,19 @@ describe('test_macos plugin (unified)', () => { mockFs(), ); - expect(commandCalls).toHaveLength(1); + expect(commandCalls).toHaveLength(2); expect(commandCalls[0].command).toContain('xcodebuild'); expect(commandCalls[0].command).toContain('-workspace'); expect(commandCalls[0].command).toContain('/path/to/MyProject.xcworkspace'); expect(commandCalls[0].command).toContain('-scheme'); expect(commandCalls[0].command).toContain('MyScheme'); - expect(commandCalls[0].command).toContain('test'); + expect(commandCalls[0].command).toContain('build-for-testing'); expect(commandCalls[0].logPrefix).toBe('Test Run'); + expect(commandCalls[1].command).toContain('-testProductsPath'); + expect(commandCalls[1].command).not.toContain('-workspace'); + expect(commandCalls[1].command).not.toContain('-scheme'); + expect(commandCalls[1].command).toContain('test-without-building'); + expect(commandCalls.map((call) => call.cwd)).toEqual(['/path/to', '/path/to']); expectPendingBuildResponse(result); expect(result.isError()).toBeFalsy(); @@ -383,23 +390,23 @@ describe('test_macos plugin (unified)', () => { expect(result.isError()).toBe(true); }); - it('should return error response when executor throws an exception', async () => { + it('should propagate executor exceptions as runtime errors', async () => { const mockExecutor = createMockExecutor({ success: false, error: '', shouldThrow: new Error('Network error'), }); - const { result } = await runTestMacosLogic( - { - workspacePath: '/path/to/MyProject.xcworkspace', - scheme: 'MyScheme', - }, - mockExecutor, - mockFs(), - ); - - expect(result.isError()).toBe(true); + await expect( + runTestMacosLogic( + { + workspacePath: '/path/to/MyProject.xcworkspace', + scheme: 'MyScheme', + }, + mockExecutor, + mockFs(), + ), + ).rejects.toThrow('Network error'); }); }); }); diff --git a/src/mcp/tools/macos/build_macos.ts b/src/mcp/tools/macos/build_macos.ts index cadf3382d..e19a72222 100644 --- a/src/mcp/tools/macos/build_macos.ts +++ b/src/mcp/tools/macos/build_macos.ts @@ -22,11 +22,54 @@ import { setXcodebuildStructuredOutput, } from '../../../utils/xcodebuild-domain-results.ts'; import type { BuildInvocationRequest } from '../../../types/domain-fragments.ts'; +import { displayPath } from '../../../utils/build-preflight.ts'; import { resolveEffectiveDerivedDataPath } from '../../../utils/derived-data-path.ts'; +import { resolvePathFromCwd } from '../../../utils/path.ts'; +import { + createDefaultTestProductsPath, + findXctestrunPaths, + markTestProductsPathCompleted, +} from '../../../utils/test-products-path.ts'; import { createBuildInvocationFragment } from '../../../utils/xcodebuild-pipeline.ts'; -function createBuildMacOSRequest(params: BuildMacOSParams): BuildInvocationRequest { +interface PreparedBuildMacOSExecution { + buildAction: 'build' | 'build-for-testing'; + invocationRequest: BuildInvocationRequest; + isManagedTestProductsPath: boolean; + logLabel: 'Build' | 'Build for Testing'; + sharedBuildParams: BuildMacOSParams; + testProductsPath?: string; +} + +function prepareBuildMacOSExecution(params: BuildMacOSParams): PreparedBuildMacOSExecution { + const buildForTesting = params.buildForTesting ?? false; + const isManagedTestProductsPath = buildForTesting && params.testProductsPath === undefined; + const testProductsPath = buildForTesting + ? (resolvePathFromCwd(params.testProductsPath) ?? createDefaultTestProductsPath('build_macos')) + : undefined; + const sharedBuildParams = testProductsPath + ? { + ...params, + extraArgs: [...(params.extraArgs ?? []), '-testProductsPath', testProductsPath], + } + : params; + + return { + buildAction: buildForTesting ? 'build-for-testing' : 'build', + invocationRequest: createBuildMacOSRequest(params, testProductsPath), + isManagedTestProductsPath, + logLabel: buildForTesting ? 'Build for Testing' : 'Build', + sharedBuildParams, + testProductsPath, + }; +} + +function createBuildMacOSRequest( + params: BuildMacOSParams, + testProductsPath?: string, +): BuildInvocationRequest { return { + ...(params.buildForTesting ? { buildForTesting: true } : {}), scheme: params.scheme, workspacePath: params.workspacePath, projectPath: params.projectPath, @@ -35,6 +78,7 @@ function createBuildMacOSRequest(params: BuildMacOSParams): BuildInvocationReque platform: 'macOS', arch: params.arch, target: 'macos', + ...(testProductsPath ? { testProductsPath: displayPath(testProductsPath) } : {}), }; } @@ -50,6 +94,14 @@ const baseSchemaObject = z.object({ .describe('Architecture to build for (arm64 or x86_64). For macOS only.'), extraArgs: z.array(z.string()).optional(), preferXcodebuild: z.boolean().optional(), + buildForTesting: z + .boolean() + .optional() + .describe('Build reusable test products without running tests (default: false)'), + testProductsPath: z + .string() + .optional() + .describe('Output path for the .xctestproducts bundle when buildForTesting is true'), }); const publicSchemaObject = baseSchemaObject.omit({ @@ -64,7 +116,10 @@ const publicSchemaObject = baseSchemaObject.omit({ const buildMacOSSchema = z.preprocess( nullifyEmptyStrings, - withProjectOrWorkspace(baseSchemaObject), + withProjectOrWorkspace(baseSchemaObject).refine( + (params) => params.testProductsPath === undefined || params.buildForTesting === true, + { message: 'testProductsPath requires buildForTesting to be true' }, + ), ); export type BuildMacOSParams = z.infer; @@ -72,26 +127,28 @@ type BuildMacOSResult = BuildResultDomainResult; export function createBuildMacOSExecutor( executor: CommandExecutor, + prepared?: PreparedBuildMacOSExecution, ): StreamingExecutor { return async (params, ctx) => { + const resolved = prepared ?? prepareBuildMacOSExecution(params); const configuration = params.configuration; const started = createDomainStreamingPipeline('build_macos', 'BUILD', ctx, 'build-result'); const buildResult = await executeXcodeBuildCommand( - { ...params, configuration }, + { ...resolved.sharedBuildParams, configuration }, { platform: XcodePlatform.macOS, arch: params.arch, - logPrefix: 'macOS Build', + logPrefix: `macOS ${resolved.logLabel}`, }, params.preferXcodebuild ?? false, - 'build', + resolved.buildAction, executor, undefined, started.pipeline, ); let bundleId: string | undefined; - if (!buildResult.isError) { + if (!buildResult.isError && !params.buildForTesting) { try { const appPath = await resolveAppPathFromBuildSettings( { @@ -118,17 +175,31 @@ export function createBuildMacOSExecutor( // bundle ID is informational only } } + const succeeded = !buildResult.isError; + + if (resolved.isManagedTestProductsPath) { + markTestProductsPathCompleted(resolved.testProductsPath); + } + + const xctestrunPaths = + succeeded && resolved.testProductsPath + ? await findXctestrunPaths(resolved.testProductsPath) + : []; return createBuildDomainResult({ started, - succeeded: !buildResult.isError, + succeeded, target: 'macos', artifacts: { ...(bundleId ? { bundleId } : {}), - buildLogPath: started.pipeline.logPath, + buildLogPath: displayPath(started.pipeline.logPath), + ...(succeeded && resolved.testProductsPath + ? { testProductsPath: displayPath(resolved.testProductsPath) } + : {}), + ...(xctestrunPaths.length > 0 ? { xctestrunPaths: xctestrunPaths.map(displayPath) } : {}), }, fallbackErrorMessages: collectFallbackErrorMessages(started, [], buildResult.content), - request: createBuildMacOSRequest(params), + request: resolved.invocationRequest, }); }; } @@ -138,26 +209,32 @@ export async function buildMacOSLogic( executor: CommandExecutor, ): Promise { const ctx = getHandlerContext(); - const invocationRequest = createBuildMacOSRequest(params); + const prepared = prepareBuildMacOSExecution(params); log('info', `Starting macOS build for scheme ${params.scheme}`); - ctx.emit(createBuildInvocationFragment('build-result', 'BUILD', invocationRequest)); + ctx.emit(createBuildInvocationFragment('build-result', 'BUILD', prepared.invocationRequest)); const executionContext = createStreamingExecutionContext(ctx); - const executeBuildMacOS = createBuildMacOSExecutor(executor); + const executeBuildMacOS = createBuildMacOSExecutor(executor, prepared); const result = await executeBuildMacOS(params, executionContext); - setXcodebuildStructuredOutput(ctx, 'build-result', result); + setXcodebuildStructuredOutput(ctx, 'build-result', result, '3'); if (!result.didError) { - ctx.nextStepParams = { - get_mac_app_path: { + if (prepared.testProductsPath) { + const nextParams = { testProductsPath: displayPath(prepared.testProductsPath) }; + ctx.nextStepParams = { test_macos: nextParams }; + ctx.nextStepConditionKeys = ['prepared_tests_available']; + } else { + const nextParams = { scheme: params.scheme, ...(params.derivedDataPath !== undefined ? { derivedDataPath: params.derivedDataPath } : {}), - }, - }; + }; + ctx.nextStepParams = { get_mac_app_path: nextParams }; + ctx.nextStepConditionKeys = ['app_build_succeeded']; + } } } diff --git a/src/mcp/tools/macos/test_macos.ts b/src/mcp/tools/macos/test_macos.ts index a0bf1117f..94e6448de 100644 --- a/src/mcp/tools/macos/test_macos.ts +++ b/src/mcp/tools/macos/test_macos.ts @@ -20,7 +20,12 @@ import { getSessionAwareToolSchemaShape, toInternalSchema, } from '../../../utils/typed-tool-factory.ts'; -import { nullifyEmptyStrings, withProjectOrWorkspace } from '../../../utils/schema-helpers.ts'; +import { nullifyEmptyStrings } from '../../../utils/schema-helpers.ts'; +import { + hasPreparedTestSource, + TEST_SOURCE_EXCLUSIVE_GROUPS, + withProjectWorkspaceOrTestArtifact, +} from '../../../utils/test-source.ts'; import { resolveTestPreflight, type TestPreflightResult } from '../../../utils/test-preflight.ts'; import { getHandlerContext } from '../../../utils/typed-tool-factory.ts'; import { @@ -30,11 +35,20 @@ import { import type { BuildInvocationRequest } from '../../../types/domain-fragments.ts'; import { resolveEffectiveDerivedDataPath } from '../../../utils/derived-data-path.ts'; import { createBuildInvocationFragment } from '../../../utils/xcodebuild-pipeline.ts'; +import { displayPath } from '../../../utils/build-preflight.ts'; const baseSchemaObject = z.object({ projectPath: z.string().optional().describe('Path to the .xcodeproj file'), workspacePath: z.string().optional().describe('Path to the .xcworkspace file'), - scheme: z.string().describe('The scheme to use'), + scheme: z.string().optional().describe('The scheme to use in source mode'), + testProductsPath: z + .string() + .optional() + .describe('Path to a prepared .xctestproducts package. Cannot be combined with source inputs'), + xctestrunPath: z + .string() + .optional() + .describe('Path to a prepared .xctestrun file. Cannot be combined with source inputs'), configuration: z.string().optional().describe('Build configuration (Debug, Release, etc.)'), derivedDataPath: z.string().optional(), extraArgs: z.array(z.string()).optional(), @@ -60,7 +74,10 @@ const publicSchemaObject = baseSchemaObject.omit({ preferXcodebuild: true, } as const); -const testMacosSchema = z.preprocess(nullifyEmptyStrings, withProjectOrWorkspace(baseSchemaObject)); +const testMacosSchema = z.preprocess( + nullifyEmptyStrings, + withProjectWorkspaceOrTestArtifact(baseSchemaObject), +); export type TestMacosParams = z.infer; type TestMacosResult = TestResultDomainResult; @@ -75,18 +92,21 @@ async function prepareTestMacosExecution( params: TestMacosParams, fileSystemExecutor: FileSystemExecutor, ): Promise { - const configuration = params.configuration; - const preflight = await resolveTestPreflight( - { - projectPath: params.projectPath, - workspacePath: params.workspacePath, - scheme: params.scheme, - configuration, - extraArgs: params.extraArgs, - destinationName: 'macOS', - }, - fileSystemExecutor, - ); + const preparedTestSource = hasPreparedTestSource(params); + const configuration = preparedTestSource ? undefined : params.configuration; + const preflight = preparedTestSource + ? null + : await resolveTestPreflight( + { + projectPath: params.projectPath, + workspacePath: params.workspacePath, + scheme: params.scheme!, + configuration, + extraArgs: params.extraArgs, + destinationName: 'macOS', + }, + fileSystemExecutor, + ); return { configuration, @@ -95,9 +115,11 @@ async function prepareTestMacosExecution( scheme: params.scheme, workspacePath: params.workspacePath, projectPath: params.projectPath, - derivedDataPath: resolveEffectiveDerivedDataPath(params), + derivedDataPath: preparedTestSource ? undefined : resolveEffectiveDerivedDataPath(params), configuration, platform: 'macOS', + testProductsPath: params.testProductsPath ? displayPath(params.testProductsPath) : undefined, + xctestrunPath: params.xctestrunPath ? displayPath(params.xctestrunPath) : undefined, onlyTesting: preflight?.selectors.onlyTesting.map((selector) => selector.raw), skipTesting: preflight?.selectors.skipTesting.map((selector) => selector.raw), }, @@ -130,6 +152,8 @@ export function createTestMacOSExecutor( platform: XcodePlatform.macOS, testRunnerEnv: params.testRunnerEnv, progress: params.progress, + testProductsPath: params.testProductsPath, + xctestrunPath: params.xctestrunPath, }, ctx, ); @@ -149,7 +173,7 @@ export async function testMacosLogic( const executeTestMacOS = createTestMacOSExecutor(executor, fileSystemExecutor, prepared); const result = await executeTestMacOS(params, executionContext); - setXcodebuildStructuredOutput(ctx, 'test-result', result); + setXcodebuildStructuredOutput(ctx, 'test-result', result, '3'); } export const schema = getSessionAwareToolSchemaShape({ @@ -162,9 +186,5 @@ export const handler = createSessionAwareTool({ logicFunction: (params, executor) => testMacosLogic(params, executor, getDefaultFileSystemExecutor()), getExecutor: getDefaultCommandExecutor, - requirements: [ - { allOf: ['scheme'], message: 'scheme is required' }, - { oneOf: ['projectPath', 'workspacePath'], message: 'Provide a project or workspace' }, - ], - exclusivePairs: [['projectPath', 'workspacePath']], + exclusivePairs: [...TEST_SOURCE_EXCLUSIVE_GROUPS, ['projectPath', 'workspacePath']], }); diff --git a/src/mcp/tools/simulator/__tests__/build_sim.test.ts b/src/mcp/tools/simulator/__tests__/build_sim.test.ts index 38f4f927f..4748d6de8 100644 --- a/src/mcp/tools/simulator/__tests__/build_sim.test.ts +++ b/src/mcp/tools/simulator/__tests__/build_sim.test.ts @@ -12,7 +12,7 @@ import { } from '../../../../test-utils/test-helpers.ts'; import { sessionStore } from '../../../../utils/session-store.ts'; -import { schema, handler, build_simLogic } from '../build_sim.ts'; +import { schema, handler, build_simLogic, buildSimulatorSchema } from '../build_sim.ts'; const runBuildSimLogic = ( params: Parameters[0], @@ -43,6 +43,23 @@ describe('build_sim tool', () => { expect(schemaObj.safeParse({ derivedDataPath: '/path/to/derived' }).success).toBe(false); expect(schemaObj.safeParse({ extraArgs: [123] }).success).toBe(false); expect(schemaObj.safeParse({ preferXcodebuild: false }).success).toBe(false); + expect( + schemaObj.safeParse({ + buildForTesting: true, + testProductsPath: '/tmp/MyApp.xctestproducts', + }).success, + ).toBe(true); + }); + + it('should reject testProductsPath without buildForTesting', () => { + const result = buildSimulatorSchema.safeParse({ + projectPath: '/path/to/MyProject.xcodeproj', + scheme: 'MyScheme', + simulatorName: 'iPhone 17', + testProductsPath: '/tmp/MyApp.xctestproducts', + }); + + expect(result.success).toBe(false); }); }); @@ -392,6 +409,31 @@ describe('build_sim tool', () => { 'watchOS Simulator Build', ); }); + + it('should prepare reusable simulator test products', async () => { + const callHistory: Array<{ command: string[]; logPrefix?: string }> = []; + const testProductsPath = '/tmp/MyApp Tests.xctestproducts'; + + const { result } = await runBuildSimLogic( + { + projectPath: '/path/to/MyProject.xcodeproj', + scheme: 'MyScheme', + simulatorName: 'iPhone 17', + buildForTesting: true, + testProductsPath, + }, + createTrackingExecutor(callHistory), + ); + + expect(callHistory).toHaveLength(2); + expect(callHistory[1].command.slice(-3)).toEqual([ + '-testProductsPath', + testProductsPath, + 'build-for-testing', + ]); + expect(callHistory[1].logPrefix).toBe('iOS Simulator Build for Testing'); + expect(result.nextStepParams).toBeUndefined(); + }); }); describe('Response Processing', () => { @@ -433,6 +475,7 @@ describe('build_sim tool', () => { expect(result.nextStepParams?.get_sim_app_path).toMatchObject({ derivedDataPath: '/path/to/derived', }); + expect(result.nextStepConditionKeys).toEqual(['app_build_succeeded']); }); it('should handle build failure', async () => { @@ -528,6 +571,27 @@ describe('build_sim tool', () => { expect(result.isError()).toBeFalsy(); expectPendingBuildResponse(result, 'get_sim_app_path'); }); + + it('should suggest running prepared simulator tests after success', async () => { + const mockExecutor = createMockExecutor({ success: true, output: 'BUILD SUCCEEDED' }); + const testProductsPath = '/tmp/MyApp Tests.xctestproducts'; + + const { result } = await runBuildSimLogic( + { + workspacePath: '/path/to/workspace', + scheme: 'MyScheme', + simulatorName: 'iPhone 17', + buildForTesting: true, + testProductsPath, + }, + mockExecutor, + ); + + expect(result.nextStepParams).toEqual({ + test_sim: { testProductsPath, simulatorName: 'iPhone 17' }, + }); + expect(result.nextStepConditionKeys).toEqual(['prepared_tests_available']); + }); }); describe('Error Handling', () => { diff --git a/src/mcp/tools/simulator/__tests__/test_sim.test.ts b/src/mcp/tools/simulator/__tests__/test_sim.test.ts index ce3d6abe9..ca2a5b775 100644 --- a/src/mcp/tools/simulator/__tests__/test_sim.test.ts +++ b/src/mcp/tools/simulator/__tests__/test_sim.test.ts @@ -35,25 +35,27 @@ describe('test_sim tool', () => { expect(schemaObj.safeParse({ testRunnerEnv: { FOO: 123 } }).success).toBe(false); const schemaKeys = Object.keys(schema).sort(); - expect(schemaKeys).toEqual(['extraArgs', 'progress', 'testRunnerEnv'].sort()); + expect(schemaKeys).toEqual( + ['extraArgs', 'progress', 'testProductsPath', 'testRunnerEnv', 'xctestrunPath'].sort(), + ); }); }); describe('Handler Requirements', () => { - it('should require scheme when not provided', async () => { + it('should require a simulator destination before source validation', async () => { const result = await callHandler(handler, {}); expect(result.isError).toBe(true); - expect(result.content[0].text).toContain('scheme is required'); + expect(result.content[0].text).toContain('Provide simulatorId or simulatorName'); }); it('should require project or workspace when scheme default exists', async () => { sessionStore.setDefaults({ scheme: 'MyScheme' }); - const result = await callHandler(handler, {}); + const result = await callHandler(handler, { simulatorId: 'SIM-UUID' }); expect(result.isError).toBe(true); - expect(result.content[0].text).toContain('Provide a project or workspace'); + expect(result.content[0].text).toContain('Either projectPath or workspacePath is required'); }); it('should require simulator identifier when scheme and project defaults exist', async () => { diff --git a/src/mcp/tools/simulator/build_sim.ts b/src/mcp/tools/simulator/build_sim.ts index e28136fe7..42f62fed4 100644 --- a/src/mcp/tools/simulator/build_sim.ts +++ b/src/mcp/tools/simulator/build_sim.ts @@ -33,7 +33,14 @@ import { setXcodebuildStructuredOutput, } from '../../../utils/xcodebuild-domain-results.ts'; import type { BuildInvocationRequest } from '../../../types/domain-fragments.ts'; +import { displayPath } from '../../../utils/build-preflight.ts'; import { resolveEffectiveDerivedDataPath } from '../../../utils/derived-data-path.ts'; +import { resolvePathFromCwd } from '../../../utils/path.ts'; +import { + createDefaultTestProductsPath, + findXctestrunPaths, + markTestProductsPathCompleted, +} from '../../../utils/test-products-path.ts'; import { createBuildInvocationFragment } from '../../../utils/xcodebuild-pipeline.ts'; const baseOptions = { @@ -58,6 +65,14 @@ const baseOptions = { .optional() .describe('Whether to use the latest OS version for the named simulator'), preferXcodebuild: z.boolean().optional(), + buildForTesting: z + .boolean() + .optional() + .describe('Build reusable test products without running tests (default: false)'), + testProductsPath: z + .string() + .optional() + .describe('Output path for the .xctestproducts bundle when buildForTesting is true'), }; const baseSchemaObject = z.object({ @@ -72,15 +87,19 @@ const baseSchemaObject = z.object({ ...baseOptions, }); -const buildSimulatorSchema = z.preprocess( +export const buildSimulatorSchema = z.preprocess( nullifyEmptyStrings, - withSimulatorIdOrName(withProjectOrWorkspace(baseSchemaObject)), + withSimulatorIdOrName(withProjectOrWorkspace(baseSchemaObject)).refine( + (params) => params.testProductsPath === undefined || params.buildForTesting === true, + { message: 'testProductsPath requires buildForTesting to be true' }, + ), ); export type BuildSimulatorParams = z.infer; type BuildSimulatorResult = BuildResultDomainResult; -interface PreparedBuildSimExecution { +export interface PreparedBuildSimExecution { + buildAction: 'build' | 'build-for-testing'; configuration?: string; detectedPlatform: InferPlatformResult['platform']; platformName: string; @@ -93,10 +112,12 @@ interface PreparedBuildSimExecution { logPrefix: string; }; invocationRequest: BuildInvocationRequest; + isManagedTestProductsPath: boolean; + testProductsPath?: string; warningMessage?: string; } -async function prepareBuildSimExecution( +export async function prepareBuildSimExecution( params: BuildSimulatorParams, executor: CommandExecutor, ): Promise { @@ -114,20 +135,34 @@ async function prepareBuildSimExecution( ); const detectedPlatform = inferred.platform; const platformName = detectedPlatform.replace(' Simulator', ''); + const buildForTesting = params.buildForTesting ?? false; + const isManagedTestProductsPath = buildForTesting && params.testProductsPath === undefined; + const testProductsPath = buildForTesting + ? (resolvePathFromCwd(params.testProductsPath) ?? createDefaultTestProductsPath('build_sim')) + : undefined; + const sharedBuildParams = testProductsPath + ? { + ...params, + configuration, + extraArgs: [...(params.extraArgs ?? []), '-testProductsPath', testProductsPath], + } + : { ...params, configuration }; return { + buildAction: buildForTesting ? 'build-for-testing' : 'build', configuration, detectedPlatform, platformName, - sharedBuildParams: { ...params, configuration }, + sharedBuildParams, platformOptions: { platform: detectedPlatform, simulatorName: params.simulatorName, simulatorId: params.simulatorId, useLatestOS: params.simulatorId ? false : useLatestOS, - logPrefix: `${platformName} Simulator Build`, + logPrefix: `${platformName} Simulator ${buildForTesting ? 'Build for Testing' : 'Build'}`, }, invocationRequest: { + ...(buildForTesting ? { buildForTesting: true } : {}), scheme: params.scheme, workspacePath: params.workspacePath, projectPath: params.projectPath, @@ -136,7 +171,10 @@ async function prepareBuildSimExecution( platform: detectedPlatform, simulatorName: params.simulatorName, simulatorId: params.simulatorId, + ...(testProductsPath ? { testProductsPath: displayPath(testProductsPath) } : {}), }, + isManagedTestProductsPath, + testProductsPath, warningMessage: params.simulatorId && params.useLatestOS !== undefined ? 'useLatestOS parameter is ignored when using simulatorId (UUID implies exact device/OS)' @@ -177,18 +215,32 @@ export function createBuildSimExecutor( resolved.sharedBuildParams, resolved.platformOptions, params.preferXcodebuild ?? false, - 'build', + resolved.buildAction, executor, undefined, started.pipeline, ); + const succeeded = !buildResult.isError; + + if (resolved.isManagedTestProductsPath) { + markTestProductsPathCompleted(resolved.testProductsPath); + } + + const xctestrunPaths = + succeeded && resolved.testProductsPath + ? await findXctestrunPaths(resolved.testProductsPath) + : []; return createBuildDomainResult({ started, - succeeded: !buildResult.isError, + succeeded, target: 'simulator', artifacts: { - buildLogPath: started.pipeline.logPath, + buildLogPath: displayPath(started.pipeline.logPath), + ...(succeeded && resolved.testProductsPath + ? { testProductsPath: displayPath(resolved.testProductsPath) } + : {}), + ...(xctestrunPaths.length > 0 ? { xctestrunPaths: xctestrunPaths.map(displayPath) } : {}), }, fallbackErrorMessages: collectFallbackErrorMessages(started, [], buildResult.content), request: resolved.invocationRequest, @@ -208,11 +260,20 @@ export async function build_simLogic( const executeBuildSim = createBuildSimExecutor(executor, prepared); const result = await executeBuildSim(params, executionContext); - setXcodebuildStructuredOutput(ctx, 'build-result', result); + setXcodebuildStructuredOutput(ctx, 'build-result', result, '3'); if (!result.didError) { - ctx.nextStepParams = { - get_sim_app_path: { + if (prepared.testProductsPath) { + const nextParams = { + testProductsPath: displayPath(prepared.testProductsPath), + ...(params.simulatorId + ? { simulatorId: params.simulatorId } + : { simulatorName: params.simulatorName ?? '' }), + }; + ctx.nextStepParams = { test_sim: nextParams }; + ctx.nextStepConditionKeys = ['prepared_tests_available']; + } else { + const nextParams = { ...(params.simulatorId ? { simulatorId: params.simulatorId } : { simulatorName: params.simulatorName ?? '' }), @@ -221,8 +282,10 @@ export async function build_simLogic( ...(params.derivedDataPath !== undefined ? { derivedDataPath: params.derivedDataPath } : {}), - }, - }; + }; + ctx.nextStepParams = { get_sim_app_path: nextParams }; + ctx.nextStepConditionKeys = ['app_build_succeeded']; + } } } diff --git a/src/mcp/tools/simulator/test_sim.ts b/src/mcp/tools/simulator/test_sim.ts index e50516f2d..3c7886b23 100644 --- a/src/mcp/tools/simulator/test_sim.ts +++ b/src/mcp/tools/simulator/test_sim.ts @@ -16,11 +16,12 @@ import { getDefaultCommandExecutor, getDefaultFileSystemExecutor, } from '../../../utils/execution/index.ts'; +import { nullifyEmptyStrings, withSimulatorIdOrName } from '../../../utils/schema-helpers.ts'; import { - nullifyEmptyStrings, - withProjectOrWorkspace, - withSimulatorIdOrName, -} from '../../../utils/schema-helpers.ts'; + hasPreparedTestSource, + TEST_SOURCE_EXCLUSIVE_GROUPS, + withProjectWorkspaceOrTestArtifact, +} from '../../../utils/test-source.ts'; import { createSessionAwareTool, getSessionAwareToolSchemaShape, @@ -39,6 +40,7 @@ import { import type { BuildInvocationRequest } from '../../../types/domain-fragments.ts'; import { resolveEffectiveDerivedDataPath } from '../../../utils/derived-data-path.ts'; import { createBuildInvocationFragment } from '../../../utils/xcodebuild-pipeline.ts'; +import { displayPath } from '../../../utils/build-preflight.ts'; const baseSchemaObject = z.object({ projectPath: z @@ -49,7 +51,15 @@ const baseSchemaObject = z.object({ .string() .optional() .describe('Path to .xcworkspace file. Provide EITHER this OR projectPath, not both'), - scheme: z.string().describe('The scheme to use (Required)'), + scheme: z.string().optional().describe('The scheme to use in source mode'), + testProductsPath: z + .string() + .optional() + .describe('Path to a prepared .xctestproducts package. Cannot be combined with source inputs'), + xctestrunPath: z + .string() + .optional() + .describe('Path to a prepared .xctestrun file. Cannot be combined with source inputs'), simulatorId: z .string() .optional() @@ -84,7 +94,7 @@ const baseSchemaObject = z.object({ const testSimulatorSchema = z.preprocess( nullifyEmptyStrings, - withSimulatorIdOrName(withProjectOrWorkspace(baseSchemaObject)), + withSimulatorIdOrName(withProjectWorkspaceOrTestArtifact(baseSchemaObject)), ); type TestSimulatorParams = z.infer; @@ -105,7 +115,8 @@ async function prepareTestSimExecution( executor: CommandExecutor, fileSystemExecutor: FileSystemExecutor, ): Promise { - const configuration = params.configuration; + const preparedTestSource = hasPreparedTestSource(params); + const configuration = preparedTestSource ? undefined : params.configuration; const inferred = await inferPlatform( { projectPath: params.projectPath, @@ -137,11 +148,15 @@ async function prepareTestSimExecution( scheme: params.scheme, workspacePath: params.workspacePath, projectPath: params.projectPath, - derivedDataPath: resolveEffectiveDerivedDataPath(params), + derivedDataPath: preparedTestSource ? undefined : resolveEffectiveDerivedDataPath(params), configuration, platform: inferred.platform, simulatorName: params.simulatorName, simulatorId: params.simulatorId, + testProductsPath: params.testProductsPath + ? displayPath(params.testProductsPath) + : undefined, + xctestrunPath: params.xctestrunPath ? displayPath(params.xctestrunPath) : undefined, }, warningMessage: params.simulatorId && params.useLatestOS !== undefined @@ -151,17 +166,19 @@ async function prepareTestSimExecution( } const destinationName = params.simulatorName ?? simulatorResolution.simulatorName; - const preflight = await resolveTestPreflight( - { - projectPath: params.projectPath, - workspacePath: params.workspacePath, - scheme: params.scheme, - configuration, - extraArgs: params.extraArgs, - destinationName, - }, - fileSystemExecutor, - ); + const preflight = preparedTestSource + ? null + : await resolveTestPreflight( + { + projectPath: params.projectPath, + workspacePath: params.workspacePath, + scheme: params.scheme!, + configuration, + extraArgs: params.extraArgs, + destinationName, + }, + fileSystemExecutor, + ); return { configuration, @@ -172,11 +189,13 @@ async function prepareTestSimExecution( scheme: params.scheme, workspacePath: params.workspacePath, projectPath: params.projectPath, - derivedDataPath: resolveEffectiveDerivedDataPath(params), + derivedDataPath: preparedTestSource ? undefined : resolveEffectiveDerivedDataPath(params), configuration, platform: inferred.platform, simulatorName: params.simulatorName, simulatorId: params.simulatorId, + testProductsPath: params.testProductsPath ? displayPath(params.testProductsPath) : undefined, + xctestrunPath: params.xctestrunPath ? displayPath(params.xctestrunPath) : undefined, onlyTesting: preflight?.selectors.onlyTesting.map((selector) => selector.raw), skipTesting: preflight?.selectors.skipTesting.map((selector) => selector.raw), }, @@ -212,7 +231,7 @@ export function createTestSimExecutor( succeeded: false, target: 'simulator', artifacts: { - buildLogPath: started.pipeline.logPath, + buildLogPath: displayPath(started.pipeline.logPath), }, fallbackErrorMessages: [ resolved.resolutionError ?? 'Failed to resolve simulator identifier for test execution.', @@ -243,6 +262,8 @@ export function createTestSimExecutor( platform: resolved.platform, testRunnerEnv: params.testRunnerEnv, progress: params.progress, + testProductsPath: params.testProductsPath, + xctestrunPath: params.xctestrunPath, }, ctx, ); @@ -262,7 +283,7 @@ export async function test_simLogic( const executeTestSim = createTestSimExecutor(executor, fileSystemExecutor, prepared); const result = await executeTestSim(params, executionContext); - setXcodebuildStructuredOutput(ctx, 'test-result', result); + setXcodebuildStructuredOutput(ctx, 'test-result', result, '3'); } const publicSchemaObject = baseSchemaObject.omit({ @@ -288,11 +309,10 @@ export const handler = createSessionAwareTool({ test_simLogic(params, executor, getDefaultFileSystemExecutor()), getExecutor: getDefaultCommandExecutor, requirements: [ - { allOf: ['scheme'], message: 'scheme is required' }, - { oneOf: ['projectPath', 'workspacePath'], message: 'Provide a project or workspace' }, { oneOf: ['simulatorId', 'simulatorName'], message: 'Provide simulatorId or simulatorName' }, ], exclusivePairs: [ + ...TEST_SOURCE_EXCLUSIVE_GROUPS, ['projectPath', 'workspacePath'], ['simulatorId', 'simulatorName'], ], diff --git a/src/mcp/tools/ui-automation/__tests__/manifest-next-step-condition.test.ts b/src/mcp/tools/ui-automation/__tests__/manifest-next-step-condition.test.ts new file mode 100644 index 000000000..db4eb103c --- /dev/null +++ b/src/mcp/tools/ui-automation/__tests__/manifest-next-step-condition.test.ts @@ -0,0 +1,37 @@ +import { describe, expect, it } from 'vitest'; +import { createMockToolHandlerContext } from '../../../../test-utils/test-helpers.ts'; +import { + createUiActionFailureResult, + createUiActionSuccessResult, + setUiActionStructuredOutput, +} from '../shared/domain-result.ts'; + +describe('UI action manifest next-step condition', () => { + it('activates the refresh template when no runtime snapshot is available', () => { + const result = createUiActionSuccessResult({ type: 'button', button: 'home' }, 'SIMULATOR-ID'); + const { ctx } = createMockToolHandlerContext(); + + setUiActionStructuredOutput(ctx, result); + + expect(ctx.nextSteps).toBeUndefined(); + expect(ctx.nextStepConditionKeys).toEqual(['ui_action_needs_refresh']); + expect(ctx.nextStepParams).toEqual({ + snapshot_ui: { simulatorId: 'SIMULATOR-ID' }, + }); + }); + + it('does not activate the refresh template after a failed action', () => { + const result = createUiActionFailureResult( + { type: 'button', button: 'home' }, + 'SIMULATOR-ID', + 'Action failed', + ); + const { ctx } = createMockToolHandlerContext(); + + setUiActionStructuredOutput(ctx, result); + + expect(ctx.nextSteps).toBeUndefined(); + expect(ctx.nextStepConditionKeys).toBeUndefined(); + expect(ctx.nextStepParams).toBeUndefined(); + }); +}); diff --git a/src/mcp/tools/ui-automation/shared/domain-result.ts b/src/mcp/tools/ui-automation/shared/domain-result.ts index 469f24c20..9994e1a80 100644 --- a/src/mcp/tools/ui-automation/shared/domain-result.ts +++ b/src/mcp/tools/ui-automation/shared/domain-result.ts @@ -1,5 +1,4 @@ import type { RenderHints, ToolHandlerContext } from '../../../../rendering/types.ts'; -import type { NextStep } from '../../../../types/common.ts'; import type { BasicDiagnostics, CapturePayload, @@ -51,20 +50,6 @@ function compact(values: Array): string[] { return values.filter((value): value is string => typeof value === 'string' && value.length > 0); } -function createUiActionSuccessNextSteps(result: UiActionResultDomainResult): NextStep[] { - if (result.didError) { - return []; - } - - return [ - { - label: 'Refresh after UI action', - tool: 'snapshot_ui', - params: { simulatorId: result.artifacts.simulatorId }, - }, - ]; -} - function getUiActionTargetRef(action: UiAction): string | null { switch (action.type) { case 'tap': @@ -326,7 +311,10 @@ export function setUiActionStructuredOutput( schema: UI_ACTION_SCHEMA, schemaVersion: '2', }; - ctx.nextSteps = createUiActionSuccessNextSteps(result); + if (!result.didError) { + ctx.nextStepParams = { snapshot_ui: { simulatorId: result.artifacts.simulatorId } }; + ctx.nextStepConditionKeys = ['ui_action_needs_refresh']; + } } export function setCaptureStructuredOutput( diff --git a/src/rendering/types.ts b/src/rendering/types.ts index 1b3819d4e..07c395d3c 100644 --- a/src/rendering/types.ts +++ b/src/rendering/types.ts @@ -40,6 +40,7 @@ export interface ToolHandlerContext { emit: (fragment: AnyFragment) => void; attach: (image: ImageAttachment) => void; nextStepParams?: NextStepParamsMap; + nextStepConditionKeys?: string[]; nextSteps?: NextStep[]; structuredOutput?: StructuredToolOutput; } diff --git a/src/runtime/__tests__/conditional-next-steps.test.ts b/src/runtime/__tests__/conditional-next-steps.test.ts new file mode 100644 index 000000000..1ee8f6857 --- /dev/null +++ b/src/runtime/__tests__/conditional-next-steps.test.ts @@ -0,0 +1,124 @@ +import { describe, expect, it } from 'vitest'; +import { createRenderSession } from '../../rendering/render.ts'; +import type { ToolHandlerContext } from '../../rendering/types.ts'; +import { createToolCatalog } from '../tool-catalog.ts'; +import { postProcessSession } from '../tool-invoker.ts'; +import type { ToolDefinition } from '../types.ts'; +import { createStructuredErrorOutput } from '../../utils/structured-error.ts'; + +function createTool(overrides: Partial = {}): ToolDefinition { + return { + id: 'source', + cliName: 'source', + mcpName: 'source', + workflow: 'simulator', + cliSchema: {}, + mcpSchema: {}, + stateful: false, + handler: async () => {}, + ...overrides, + }; +} + +function createContext(overrides: Partial = {}): ToolHandlerContext { + return { + emit: () => {}, + attach: () => {}, + ...overrides, + }; +} + +describe('conditional manifest next steps', () => { + const getAppPath = createTool({ + id: 'get_app_path', + cliName: 'get-app-path', + mcpName: 'get_app_path', + }); + const testTool = createTool({ + id: 'test_sim', + cliName: 'test', + mcpName: 'test_sim', + }); + const source = createTool({ + nextStepTemplates: [ + { + label: 'Get app path', + toolId: 'get_app_path', + when: 'success', + condition: 'app_build_succeeded', + }, + { + label: 'Run prepared tests', + toolId: 'test_sim', + when: 'success', + condition: 'prepared_tests_available', + }, + ], + }); + const catalog = createToolCatalog([source, getAppPath, testTool]); + + it('selects an active condition and merges its runtime parameters', () => { + const session = createRenderSession('text'); + + postProcessSession({ + tool: source, + session, + catalog, + runtime: 'mcp', + ctx: createContext({ + nextStepConditionKeys: ['prepared_tests_available'], + nextStepParams: { + test_sim: { testProductsPath: '/tmp/App.xctestproducts' }, + }, + }), + }); + + expect(session.getNextSteps?.()).toEqual([ + { + tool: 'test_sim', + cliTool: 'test', + workflow: 'simulator', + label: 'Run prepared tests', + params: { testProductsPath: '/tmp/App.xctestproducts' }, + priority: undefined, + when: 'success', + }, + ]); + expect(session.getNextSteps?.()[0]).not.toHaveProperty('condition'); + }); + + it('does not render templates whose condition is inactive', () => { + const session = createRenderSession('text'); + + postProcessSession({ + tool: source, + session, + catalog, + runtime: 'mcp', + ctx: createContext(), + }); + + expect(session.getNextSteps?.()).toEqual([]); + }); + + it('requires both the status and custom condition to match', () => { + const session = createRenderSession('text'); + session.setStructuredOutput?.( + createStructuredErrorOutput({ + category: 'runtime', + code: 'BUILD_FAILED', + message: 'Build failed', + }), + ); + + postProcessSession({ + tool: source, + session, + catalog, + runtime: 'mcp', + ctx: createContext({ nextStepConditionKeys: ['app_build_succeeded'] }), + }); + + expect(session.getNextSteps?.()).toEqual([]); + }); +}); diff --git a/src/runtime/tool-invoker.ts b/src/runtime/tool-invoker.ts index 1f82f2abe..2c0024c43 100644 --- a/src/runtime/tool-invoker.ts +++ b/src/runtime/tool-invoker.ts @@ -23,6 +23,7 @@ import { createStructuredErrorOutput } from '../utils/structured-error.ts'; type BuiltTemplateNextStep = { step: NextStep; templateToolId?: string; + condition?: string; }; function emitExplicitRuntimeError(params: { @@ -61,6 +62,7 @@ function buildTemplateNextSteps( priority: template.priority, when: template.when, }, + condition: template.condition, }); continue; } @@ -79,6 +81,7 @@ function buildTemplateNextSteps( when: template.when, }, templateToolId: template.toolId, + condition: template.condition, }); } @@ -222,6 +225,7 @@ export function postProcessSession(params: { const isError = session.isError(); const nextStepParams = ctx.nextStepParams; + const nextStepConditionKeys = new Set(ctx.nextStepConditionKeys ?? []); const handlerNextSteps = ctx.nextSteps; const suppressNextStepsForStructuredFailure = isError && isStructuredXcodebuildFailureSession(session); @@ -241,9 +245,9 @@ export function postProcessSession(params: { const allTemplateSteps = buildTemplateNextSteps(tool, catalog); const templateSteps = allTemplateSteps.filter((t) => { const when = t.step.when ?? 'always'; - if (when === 'success') return !isError; - if (when === 'failure') return isError; - return true; + const statusMatches = when === 'success' ? !isError : when === 'failure' ? isError : true; + const conditionMatches = !t.condition || nextStepConditionKeys.has(t.condition); + return statusMatches && conditionMatches; }); let finalSteps: NextStep[]; @@ -502,6 +506,7 @@ export class DefaultToolInvoker implements ToolInvoker { }, attach: (image) => opts.renderSession!.attach(image), nextStepParams: daemonResult.nextStepParams, + nextStepConditionKeys: daemonResult.nextStepConditionKeys, nextSteps: daemonResult.nextSteps, structuredOutput, }; @@ -547,6 +552,7 @@ export class DefaultToolInvoker implements ToolInvoker { }, attach: (image) => session.attach(image), nextStepParams: daemonResult.nextStepParams, + nextStepConditionKeys: daemonResult.nextStepConditionKeys, nextSteps: daemonResult.nextSteps, structuredOutput: daemonResult.structuredOutput ?? undefined, }; diff --git a/src/runtime/types.ts b/src/runtime/types.ts index 0b7e9eb16..b382a3475 100644 --- a/src/runtime/types.ts +++ b/src/runtime/types.ts @@ -14,6 +14,7 @@ export interface NextStepTemplate { params?: Record; priority?: number; when?: 'always' | 'success' | 'failure'; + condition?: string; } export type RuntimeKind = 'cli' | 'daemon' | 'mcp'; diff --git a/src/snapshot-tests/__fixtures__/cli/json/coverage/get-coverage-report--success.json b/src/snapshot-tests/__fixtures__/cli/json/coverage/get-coverage-report--success.json index 587947ba3..2ecc445e4 100644 --- a/src/snapshot-tests/__fixtures__/cli/json/coverage/get-coverage-report--success.json +++ b/src/snapshot-tests/__fixtures__/cli/json/coverage/get-coverage-report--success.json @@ -6,8 +6,8 @@ "data": { "summary": { "status": "SUCCEEDED", - "coveragePct": 94.9, - "coveredLines": 371, + "coveragePct": 4.6, + "coveredLines": 18, "executableLines": 391 }, "coverageScope": "report", @@ -18,8 +18,8 @@ "targets": [ { "name": "CalculatorAppTests.xctest", - "coveragePct": 94.9, - "coveredLines": 371, + "coveragePct": 4.6, + "coveredLines": 18, "executableLines": 391 } ] diff --git a/src/snapshot-tests/__fixtures__/cli/json/coverage/get-file-coverage--success.json b/src/snapshot-tests/__fixtures__/cli/json/coverage/get-file-coverage--success.json index ecc52c88c..34594ab0e 100644 --- a/src/snapshot-tests/__fixtures__/cli/json/coverage/get-file-coverage--success.json +++ b/src/snapshot-tests/__fixtures__/cli/json/coverage/get-file-coverage--success.json @@ -6,8 +6,8 @@ "data": { "summary": { "status": "SUCCEEDED", - "coveragePct": 83.1, - "coveredLines": 157, + "coveragePct": 55, + "coveredLines": 104, "executableLines": 189 }, "coverageScope": "file", @@ -24,12 +24,54 @@ "coveredLines": 0, "executableLines": 16 }, + { + "line": 63, + "name": "CalculatorService.inputDecimal()", + "coveredLines": 0, + "executableLines": 14 + }, + { + "line": 148, + "name": "CalculatorService.clear()", + "coveredLines": 0, + "executableLines": 10 + }, + { + "line": 134, + "name": "CalculatorService.toggleSign()", + "coveredLines": 0, + "executableLines": 6 + }, + { + "line": 141, + "name": "CalculatorService.percentage()", + "coveredLines": 0, + "executableLines": 6 + }, + { + "line": 178, + "name": "CalculatorService.setError(_:)", + "coveredLines": 0, + "executableLines": 5 + }, { "line": 58, "name": "implicit closure #2 in CalculatorService.inputNumber(_:)", "coveredLines": 0, "executableLines": 1 }, + { + "line": 68, + "name": "implicit closure #1 in CalculatorService.inputDecimal()", + "coveredLines": 0, + "executableLines": 1 + }, + { + "line": 94, + "name": "implicit closure #1 in CalculatorService.calculate()", + "coveredLines": 0, + "executableLines": 1 + }, { "line": 98, "name": "implicit closure #3 in CalculatorService.calculate()", @@ -54,11 +96,41 @@ "coveredLines": 0, "executableLines": 1 }, + { + "line": 209, + "name": "implicit closure #3 in CalculatorService.formatNumber(_:)", + "coveredLines": 0, + "executableLines": 1 + }, { "line": 214, "name": "implicit closure #4 in CalculatorService.formatNumber(_:)", "coveredLines": 0, "executableLines": 1 + }, + { + "line": 221, + "name": "CalculatorService.currentValue.getter", + "coveredLines": 0, + "executableLines": 1 + }, + { + "line": 222, + "name": "CalculatorService.previousValue.getter", + "coveredLines": 0, + "executableLines": 1 + }, + { + "line": 223, + "name": "CalculatorService.currentOperation.getter", + "coveredLines": 0, + "executableLines": 1 + }, + { + "line": 224, + "name": "CalculatorService.willResetDisplay.getter", + "coveredLines": 0, + "executableLines": 1 } ], "partialCoverage": [ @@ -69,32 +141,39 @@ "coveredLines": 8, "executableLines": 10 }, - { - "line": 195, - "name": "CalculatorService.formatNumber(_:)", - "coveragePct": 85.7, - "coveredLines": 18, - "executableLines": 21 - }, { "line": 93, "name": "CalculatorService.calculate()", - "coveragePct": 89.5, - "coveredLines": 34, + "coveragePct": 84.2, + "coveredLines": 32, "executableLines": 38 }, { - "line": 63, - "name": "CalculatorService.inputDecimal()", - "coveragePct": 92.9, - "coveredLines": 13, + "line": 47, + "name": "CalculatorService.inputNumber(_:)", + "coveragePct": 85.7, + "coveredLines": 12, "executableLines": 14 + }, + { + "line": 78, + "name": "CalculatorService.setOperation(_:)", + "coveragePct": 85.7, + "coveredLines": 12, + "executableLines": 14 + }, + { + "line": 195, + "name": "CalculatorService.formatNumber(_:)", + "coveragePct": 85.7, + "coveredLines": 18, + "executableLines": 21 } ], - "fullCoverageCount": 28, - "notCoveredFunctionCount": 7, - "notCoveredLineCount": 22, - "partialCoverageFunctionCount": 4 + "fullCoverageCount": 15, + "notCoveredFunctionCount": 19, + "notCoveredLineCount": 70, + "partialCoverageFunctionCount": 5 } }, "nextSteps": [ diff --git a/src/snapshot-tests/__fixtures__/cli/json/debugging/lldb-command--success.json b/src/snapshot-tests/__fixtures__/cli/json/debugging/lldb-command--success.json index bc3e32531..98ef44cef 100644 --- a/src/snapshot-tests/__fixtures__/cli/json/debugging/lldb-command--success.json +++ b/src/snapshot-tests/__fixtures__/cli/json/debugging/lldb-command--success.json @@ -6,12 +6,7 @@ "data": { "command": "breakpoint list", "outputLines": [ - "Current breakpoints:", - "1: file = 'ContentView.swift', line = 42, exact_match = 0, locations = ", - " Names:", - " dap", - "", - " 1.1: where = CalculatorApp.debug.dylib`closure #1 in closure #1 in closure #1 in ContentView.body.getter + at ContentView.swift:42:31, address = , resolved, hit count = 0" + "No breakpoints currently set." ] } } diff --git a/src/snapshot-tests/__fixtures__/cli/json/device/build--error-compiler.json b/src/snapshot-tests/__fixtures__/cli/json/device/build--error-compiler.json index 2713f592f..7ed092d46 100644 --- a/src/snapshot-tests/__fixtures__/cli/json/device/build--error-compiler.json +++ b/src/snapshot-tests/__fixtures__/cli/json/device/build--error-compiler.json @@ -1,14 +1,13 @@ { "schema": "xcodebuildmcp.output.build-result", - "schemaVersion": "2", + "schemaVersion": "3", "didError": true, "error": "Build failed", "data": { "request": { "scheme": "CalculatorApp", "workspacePath": "example_projects/iOS_Calculator/CalculatorApp.xcworkspace", - "derivedDataPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/CalculatorApp-", - "configuration": "Debug", + "derivedDataPath": "", "platform": "iOS", "target": "device" }, @@ -25,7 +24,7 @@ "errors": [ { "message": "cannot convert value of type 'String' to specified type 'Int'", - "location": "/example_projects/iOS_Calculator/CalculatorApp/CalculatorApp.swift:33" + "location": "/example_projects/iOS_Calculator/CalculatorApp/CalculatorApp.swift:53" } ] } diff --git a/src/snapshot-tests/__fixtures__/cli/json/device/build--error-prepared-tests-wrong-scheme.json b/src/snapshot-tests/__fixtures__/cli/json/device/build--error-prepared-tests-wrong-scheme.json new file mode 100644 index 000000000..678e6cbfb --- /dev/null +++ b/src/snapshot-tests/__fixtures__/cli/json/device/build--error-prepared-tests-wrong-scheme.json @@ -0,0 +1,33 @@ +{ + "schema": "xcodebuildmcp.output.build-result", + "schemaVersion": "3", + "didError": true, + "error": "Build failed", + "data": { + "request": { + "buildForTesting": true, + "scheme": "NONEXISTENT", + "workspacePath": "example_projects/iOS_Calculator/CalculatorApp.xcworkspace", + "derivedDataPath": "", + "platform": "iOS", + "target": "device", + "testProductsPath": "/Invalid CalculatorApp Tests.xctestproducts" + }, + "summary": { + "status": "FAILED", + "durationMs": 1234, + "target": "device" + }, + "artifacts": { + "buildLogPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/build_device__pid.log" + }, + "diagnostics": { + "warnings": [], + "errors": [ + { + "message": "The workspace named \"CalculatorApp\" does not contain a scheme named \"NONEXISTENT\". The \"-list\" option can be used to find the names of the schemes in the workspace." + } + ] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/cli/json/device/build--error-wrong-scheme.json b/src/snapshot-tests/__fixtures__/cli/json/device/build--error-wrong-scheme.json index dbc1d4175..258722b42 100644 --- a/src/snapshot-tests/__fixtures__/cli/json/device/build--error-wrong-scheme.json +++ b/src/snapshot-tests/__fixtures__/cli/json/device/build--error-wrong-scheme.json @@ -1,14 +1,13 @@ { "schema": "xcodebuildmcp.output.build-result", - "schemaVersion": "2", + "schemaVersion": "3", "didError": true, "error": "Build failed", "data": { "request": { "scheme": "NONEXISTENT", "workspacePath": "example_projects/iOS_Calculator/CalculatorApp.xcworkspace", - "derivedDataPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/CalculatorApp-", - "configuration": "Debug", + "derivedDataPath": "", "platform": "iOS", "target": "device" }, diff --git a/src/snapshot-tests/__fixtures__/cli/json/device/build--success-prepared-tests-generic.json b/src/snapshot-tests/__fixtures__/cli/json/device/build--success-prepared-tests-generic.json new file mode 100644 index 000000000..31ecf6e1b --- /dev/null +++ b/src/snapshot-tests/__fixtures__/cli/json/device/build--success-prepared-tests-generic.json @@ -0,0 +1,33 @@ +{ + "schema": "xcodebuildmcp.output.build-result", + "schemaVersion": "3", + "didError": false, + "error": null, + "data": { + "request": { + "buildForTesting": true, + "scheme": "CalculatorApp", + "workspacePath": "example_projects/iOS_Calculator/CalculatorApp.xcworkspace", + "derivedDataPath": "", + "platform": "iOS", + "target": "device", + "testProductsPath": "/Generic CalculatorApp Tests.xctestproducts" + }, + "summary": { + "status": "SUCCEEDED", + "durationMs": 1234, + "target": "device" + }, + "artifacts": { + "buildLogPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/build_device__pid.log", + "testProductsPath": "/Generic CalculatorApp Tests.xctestproducts", + "xctestrunPaths": [ + "/Generic CalculatorApp Tests.xctestproducts/Tests/0/CalculatorApp.xctestrun" + ] + }, + "diagnostics": { + "warnings": [], + "errors": [] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/cli/json/device/build--success.json b/src/snapshot-tests/__fixtures__/cli/json/device/build--success.json index f4e221ecb..91c6c1582 100644 --- a/src/snapshot-tests/__fixtures__/cli/json/device/build--success.json +++ b/src/snapshot-tests/__fixtures__/cli/json/device/build--success.json @@ -1,14 +1,13 @@ { "schema": "xcodebuildmcp.output.build-result", - "schemaVersion": "2", + "schemaVersion": "3", "didError": false, "error": null, "data": { "request": { "scheme": "CalculatorApp", "workspacePath": "example_projects/iOS_Calculator/CalculatorApp.xcworkspace", - "derivedDataPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/CalculatorApp-", - "configuration": "Debug", + "derivedDataPath": "", "platform": "iOS", "target": "device" }, @@ -26,6 +25,6 @@ } }, "nextSteps": [ - "Get built device app path: xcodebuildmcp device get-app-path --scheme CalculatorApp" + "Get built device app path: xcodebuildmcp device get-app-path --scheme CalculatorApp --derived-data-path " ] } diff --git a/src/snapshot-tests/__fixtures__/cli/json/device/build-and-run--error-compiler.json b/src/snapshot-tests/__fixtures__/cli/json/device/build-and-run--error-compiler.json index c4ddb1575..1e8e040b8 100644 --- a/src/snapshot-tests/__fixtures__/cli/json/device/build-and-run--error-compiler.json +++ b/src/snapshot-tests/__fixtures__/cli/json/device/build-and-run--error-compiler.json @@ -27,7 +27,7 @@ "errors": [ { "message": "cannot convert value of type 'String' to specified type 'Int'", - "location": "/example_projects/iOS_Calculator/CalculatorApp/CalculatorApp.swift:33" + "location": "/example_projects/iOS_Calculator/CalculatorApp/CalculatorApp.swift:53" } ] } diff --git a/src/snapshot-tests/__fixtures__/cli/json/device/get-app-path--error-wrong-scheme.json b/src/snapshot-tests/__fixtures__/cli/json/device/get-app-path--error-wrong-scheme.json index 75a3a6c60..fdd1b8750 100644 --- a/src/snapshot-tests/__fixtures__/cli/json/device/get-app-path--error-wrong-scheme.json +++ b/src/snapshot-tests/__fixtures__/cli/json/device/get-app-path--error-wrong-scheme.json @@ -7,7 +7,6 @@ "request": { "scheme": "NONEXISTENT", "workspacePath": "example_projects/iOS_Calculator/CalculatorApp.xcworkspace", - "configuration": "Debug", "platform": "iOS" }, "summary": { diff --git a/src/snapshot-tests/__fixtures__/cli/json/device/get-app-path--success.json b/src/snapshot-tests/__fixtures__/cli/json/device/get-app-path--success.json index 2ad03d5b5..26b81b162 100644 --- a/src/snapshot-tests/__fixtures__/cli/json/device/get-app-path--success.json +++ b/src/snapshot-tests/__fixtures__/cli/json/device/get-app-path--success.json @@ -7,7 +7,6 @@ "request": { "scheme": "CalculatorApp", "workspacePath": "example_projects/iOS_Calculator/CalculatorApp.xcworkspace", - "configuration": "Debug", "platform": "iOS" }, "summary": { @@ -15,12 +14,12 @@ "target": "device" }, "artifacts": { - "appPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/CalculatorApp-/Build/Products/Debug-iphoneos/CalculatorApp.app" + "appPath": "/Build/Products/Debug-iphoneos/CalculatorApp.app" } }, "nextSteps": [ - "Get bundle ID: xcodebuildmcp device get-app-bundle-id --app-path ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/CalculatorApp-/Build/Products/Debug-iphoneos/CalculatorApp.app", - "Install app on device: xcodebuildmcp device install --device-id DEVICE_UDID --app-path ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/CalculatorApp-/Build/Products/Debug-iphoneos/CalculatorApp.app", + "Get bundle ID: xcodebuildmcp device get-app-bundle-id --app-path /Build/Products/Debug-iphoneos/CalculatorApp.app", + "Install app on device: xcodebuildmcp device install --device-id DEVICE_UDID --app-path /Build/Products/Debug-iphoneos/CalculatorApp.app", "Launch app on device: xcodebuildmcp device launch --device-id DEVICE_UDID --bundle-id BUNDLE_ID" ] } diff --git a/src/snapshot-tests/__fixtures__/cli/json/device/test--error-compiler.json b/src/snapshot-tests/__fixtures__/cli/json/device/test--error-compiler.json index be5d20863..a50312551 100644 --- a/src/snapshot-tests/__fixtures__/cli/json/device/test--error-compiler.json +++ b/src/snapshot-tests/__fixtures__/cli/json/device/test--error-compiler.json @@ -1,6 +1,6 @@ { "schema": "xcodebuildmcp.output.test-result", - "schemaVersion": "2", + "schemaVersion": "3", "didError": true, "error": "Tests failed", "data": { @@ -48,7 +48,7 @@ "errors": [ { "message": "cannot convert value of type 'String' to specified type 'Int'", - "location": "/example_projects/iOS_Calculator/CalculatorApp/CalculatorApp.swift:33" + "location": "/example_projects/iOS_Calculator/CalculatorApp/CalculatorApp.swift:53" } ], "testFailures": [] diff --git a/src/snapshot-tests/__fixtures__/cli/json/device/test--failure.json b/src/snapshot-tests/__fixtures__/cli/json/device/test--failure.json index 3d943d75c..a0256775c 100644 --- a/src/snapshot-tests/__fixtures__/cli/json/device/test--failure.json +++ b/src/snapshot-tests/__fixtures__/cli/json/device/test--failure.json @@ -1,6 +1,6 @@ { "schema": "xcodebuildmcp.output.test-result", - "schemaVersion": "2", + "schemaVersion": "3", "didError": true, "error": "Tests failed", "data": { diff --git a/src/snapshot-tests/__fixtures__/cli/json/device/test--success.json b/src/snapshot-tests/__fixtures__/cli/json/device/test--success.json index fa8687bb8..deb900d65 100644 --- a/src/snapshot-tests/__fixtures__/cli/json/device/test--success.json +++ b/src/snapshot-tests/__fixtures__/cli/json/device/test--success.json @@ -1,6 +1,6 @@ { "schema": "xcodebuildmcp.output.test-result", - "schemaVersion": "2", + "schemaVersion": "3", "didError": false, "error": null, "data": { diff --git a/src/snapshot-tests/__fixtures__/cli/json/macos/build--error-compiler.json b/src/snapshot-tests/__fixtures__/cli/json/macos/build--error-compiler.json index 5d0b0e411..649a3d57a 100644 --- a/src/snapshot-tests/__fixtures__/cli/json/macos/build--error-compiler.json +++ b/src/snapshot-tests/__fixtures__/cli/json/macos/build--error-compiler.json @@ -1,14 +1,13 @@ { "schema": "xcodebuildmcp.output.build-result", - "schemaVersion": "2", + "schemaVersion": "3", "didError": true, "error": "Build failed", "data": { "request": { "scheme": "MCPTest", "projectPath": "example_projects/macOS/MCPTest.xcodeproj", - "derivedDataPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/MCPTest-", - "configuration": "Debug", + "derivedDataPath": "/DerivedData", "platform": "macOS", "target": "macos" }, diff --git a/src/snapshot-tests/__fixtures__/cli/json/macos/build--error-prepared-tests-wrong-scheme.json b/src/snapshot-tests/__fixtures__/cli/json/macos/build--error-prepared-tests-wrong-scheme.json new file mode 100644 index 000000000..9c3a0b940 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/cli/json/macos/build--error-prepared-tests-wrong-scheme.json @@ -0,0 +1,33 @@ +{ + "schema": "xcodebuildmcp.output.build-result", + "schemaVersion": "3", + "didError": true, + "error": "Build failed", + "data": { + "request": { + "buildForTesting": true, + "scheme": "NONEXISTENT", + "projectPath": "example_projects/macOS/MCPTest.xcodeproj", + "derivedDataPath": "/DerivedData", + "platform": "macOS", + "target": "macos", + "testProductsPath": "/Invalid MCPTest Tests.xctestproducts" + }, + "summary": { + "status": "FAILED", + "durationMs": 1234, + "target": "macos" + }, + "artifacts": { + "buildLogPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/build_macos__pid.log" + }, + "diagnostics": { + "warnings": [], + "errors": [ + { + "message": "The project named \"MCPTest\" does not contain a scheme named \"NONEXISTENT\". The \"-list\" option can be used to find the names of the schemes in the project." + } + ] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/cli/json/macos/build--error-wrong-scheme.json b/src/snapshot-tests/__fixtures__/cli/json/macos/build--error-wrong-scheme.json index 923b347af..8d5465013 100644 --- a/src/snapshot-tests/__fixtures__/cli/json/macos/build--error-wrong-scheme.json +++ b/src/snapshot-tests/__fixtures__/cli/json/macos/build--error-wrong-scheme.json @@ -1,14 +1,13 @@ { "schema": "xcodebuildmcp.output.build-result", - "schemaVersion": "2", + "schemaVersion": "3", "didError": true, "error": "Build failed", "data": { "request": { "scheme": "NONEXISTENT", "projectPath": "example_projects/macOS/MCPTest.xcodeproj", - "derivedDataPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/MCPTest-", - "configuration": "Debug", + "derivedDataPath": "/DerivedData", "platform": "macOS", "target": "macos" }, diff --git a/src/snapshot-tests/__fixtures__/cli/json/macos/build--success-prepared-tests.json b/src/snapshot-tests/__fixtures__/cli/json/macos/build--success-prepared-tests.json new file mode 100644 index 000000000..713eefed3 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/cli/json/macos/build--success-prepared-tests.json @@ -0,0 +1,36 @@ +{ + "schema": "xcodebuildmcp.output.build-result", + "schemaVersion": "3", + "didError": false, + "error": null, + "data": { + "request": { + "buildForTesting": true, + "scheme": "MCPTest", + "projectPath": "example_projects/macOS/MCPTest.xcodeproj", + "derivedDataPath": "/DerivedData", + "platform": "macOS", + "target": "macos", + "testProductsPath": "/MCPTest Tests.xctestproducts" + }, + "summary": { + "status": "SUCCEEDED", + "durationMs": 1234, + "target": "macos" + }, + "artifacts": { + "buildLogPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/build_macos__pid.log", + "testProductsPath": "/MCPTest Tests.xctestproducts", + "xctestrunPaths": [ + "/MCPTest Tests.xctestproducts/Tests/0/MCPTest.xctestrun" + ] + }, + "diagnostics": { + "warnings": [], + "errors": [] + } + }, + "nextSteps": [ + "Run prepared tests: xcodebuildmcp macos test --test-products-path '/MCPTest Tests.xctestproducts'" + ] +} diff --git a/src/snapshot-tests/__fixtures__/cli/json/macos/build--success.json b/src/snapshot-tests/__fixtures__/cli/json/macos/build--success.json index 32d13aff1..332c4d5ec 100644 --- a/src/snapshot-tests/__fixtures__/cli/json/macos/build--success.json +++ b/src/snapshot-tests/__fixtures__/cli/json/macos/build--success.json @@ -1,14 +1,13 @@ { "schema": "xcodebuildmcp.output.build-result", - "schemaVersion": "2", + "schemaVersion": "3", "didError": false, "error": null, "data": { "request": { "scheme": "MCPTest", "projectPath": "example_projects/macOS/MCPTest.xcodeproj", - "derivedDataPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/MCPTest-", - "configuration": "Debug", + "derivedDataPath": "/DerivedData", "platform": "macOS", "target": "macos" }, @@ -27,6 +26,6 @@ } }, "nextSteps": [ - "Get built macOS app path: xcodebuildmcp macos get-app-path --scheme MCPTest" + "Get built macOS app path: xcodebuildmcp macos get-app-path --scheme MCPTest --derived-data-path /DerivedData" ] } diff --git a/src/snapshot-tests/__fixtures__/cli/json/macos/build-and-run--error-compiler.json b/src/snapshot-tests/__fixtures__/cli/json/macos/build-and-run--error-compiler.json index 7ab611eab..5770a4ef0 100644 --- a/src/snapshot-tests/__fixtures__/cli/json/macos/build-and-run--error-compiler.json +++ b/src/snapshot-tests/__fixtures__/cli/json/macos/build-and-run--error-compiler.json @@ -7,8 +7,7 @@ "request": { "scheme": "MCPTest", "projectPath": "example_projects/macOS/MCPTest.xcodeproj", - "derivedDataPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/MCPTest-", - "configuration": "Debug", + "derivedDataPath": "/DerivedData", "platform": "macOS", "target": "macos" }, diff --git a/src/snapshot-tests/__fixtures__/cli/json/macos/build-and-run--error-wrong-scheme.json b/src/snapshot-tests/__fixtures__/cli/json/macos/build-and-run--error-wrong-scheme.json index 6c0e76acd..f2bd53240 100644 --- a/src/snapshot-tests/__fixtures__/cli/json/macos/build-and-run--error-wrong-scheme.json +++ b/src/snapshot-tests/__fixtures__/cli/json/macos/build-and-run--error-wrong-scheme.json @@ -7,8 +7,7 @@ "request": { "scheme": "NONEXISTENT", "projectPath": "example_projects/macOS/MCPTest.xcodeproj", - "derivedDataPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/MCPTest-", - "configuration": "Debug", + "derivedDataPath": "/DerivedData", "platform": "macOS", "target": "macos" }, diff --git a/src/snapshot-tests/__fixtures__/cli/json/macos/build-and-run--success.json b/src/snapshot-tests/__fixtures__/cli/json/macos/build-and-run--success.json index 42c291daf..ea8e076b0 100644 --- a/src/snapshot-tests/__fixtures__/cli/json/macos/build-and-run--success.json +++ b/src/snapshot-tests/__fixtures__/cli/json/macos/build-and-run--success.json @@ -7,8 +7,7 @@ "request": { "scheme": "MCPTest", "projectPath": "example_projects/macOS/MCPTest.xcodeproj", - "derivedDataPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/MCPTest-", - "configuration": "Debug", + "derivedDataPath": "/DerivedData", "platform": "macOS", "target": "macos" }, @@ -18,7 +17,7 @@ "target": "macos" }, "artifacts": { - "appPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/MCPTest-/Build/Products/Debug/MCPTest.app", + "appPath": "/DerivedData/Build/Products/Debug/MCPTest.app", "bundleId": "io.sentry.MCPTest.macOS", "processId": 99999, "buildLogPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/build_run_macos__pid.log" diff --git a/src/snapshot-tests/__fixtures__/cli/json/macos/get-app-path--error-wrong-scheme.json b/src/snapshot-tests/__fixtures__/cli/json/macos/get-app-path--error-wrong-scheme.json index 6fffee19b..80c604d24 100644 --- a/src/snapshot-tests/__fixtures__/cli/json/macos/get-app-path--error-wrong-scheme.json +++ b/src/snapshot-tests/__fixtures__/cli/json/macos/get-app-path--error-wrong-scheme.json @@ -7,7 +7,6 @@ "request": { "scheme": "NONEXISTENT", "projectPath": "example_projects/macOS/MCPTest.xcodeproj", - "configuration": "Debug", "platform": "macOS" }, "summary": { diff --git a/src/snapshot-tests/__fixtures__/cli/json/macos/get-app-path--success.json b/src/snapshot-tests/__fixtures__/cli/json/macos/get-app-path--success.json index 184f86869..e072989c4 100644 --- a/src/snapshot-tests/__fixtures__/cli/json/macos/get-app-path--success.json +++ b/src/snapshot-tests/__fixtures__/cli/json/macos/get-app-path--success.json @@ -7,7 +7,6 @@ "request": { "scheme": "MCPTest", "projectPath": "example_projects/macOS/MCPTest.xcodeproj", - "configuration": "Debug", "platform": "macOS" }, "summary": { @@ -15,11 +14,11 @@ "target": "macos" }, "artifacts": { - "appPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/MCPTest-/Build/Products/Debug/MCPTest.app" + "appPath": "/DerivedData/Build/Products/Debug/MCPTest.app" } }, "nextSteps": [ - "Get bundle ID: xcodebuildmcp macos get-macos-bundle-id --app-path ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/MCPTest-/Build/Products/Debug/MCPTest.app", - "Launch app: xcodebuildmcp macos launch --app-path ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/MCPTest-/Build/Products/Debug/MCPTest.app" + "Get bundle ID: xcodebuildmcp macos get-macos-bundle-id --app-path /DerivedData/Build/Products/Debug/MCPTest.app", + "Launch app: xcodebuildmcp macos launch --app-path /DerivedData/Build/Products/Debug/MCPTest.app" ] } diff --git a/src/snapshot-tests/__fixtures__/cli/json/macos/get-macos-bundle-id--error-missing-app.json b/src/snapshot-tests/__fixtures__/cli/json/macos/get-macos-bundle-id--error-missing-app.json index 590082ed6..26b8d253b 100644 --- a/src/snapshot-tests/__fixtures__/cli/json/macos/get-macos-bundle-id--error-missing-app.json +++ b/src/snapshot-tests/__fixtures__/cli/json/macos/get-macos-bundle-id--error-missing-app.json @@ -5,13 +5,13 @@ "error": "Failed to get macOS bundle ID.", "data": { "artifacts": { - "appPath": "/nonexistent/path/Fake.app" + "appPath": "/missing.app" }, "diagnostics": { "warnings": [], "errors": [ { - "message": "File not found: '/nonexistent/path/Fake.app'. Please check the path and try again." + "message": "File not found: '/missing.app'. Please check the path and try again." } ] } diff --git a/src/snapshot-tests/__fixtures__/cli/json/macos/launch--success.json b/src/snapshot-tests/__fixtures__/cli/json/macos/launch--success.json index 05656efca..8765f555b 100644 --- a/src/snapshot-tests/__fixtures__/cli/json/macos/launch--success.json +++ b/src/snapshot-tests/__fixtures__/cli/json/macos/launch--success.json @@ -8,7 +8,7 @@ "status": "SUCCEEDED" }, "artifacts": { - "appPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/MCPTest-/Build/Products/Debug/MCPTest.app", + "appPath": "/DerivedData/Build/Products/Debug/MCPTest.app", "bundleId": "io.sentry.MCPTest.macOS", "processId": 99999 }, diff --git a/src/snapshot-tests/__fixtures__/cli/json/macos/stop--success.json b/src/snapshot-tests/__fixtures__/cli/json/macos/stop--success.json index 7c3ed4c45..13933bed8 100644 --- a/src/snapshot-tests/__fixtures__/cli/json/macos/stop--success.json +++ b/src/snapshot-tests/__fixtures__/cli/json/macos/stop--success.json @@ -8,7 +8,8 @@ "status": "SUCCEEDED" }, "artifacts": { - "appName": "MCPTest" + "processId": 99999, + "appName": "PID " }, "diagnostics": { "warnings": [], diff --git a/src/snapshot-tests/__fixtures__/cli/json/macos/test--error-compiler.json b/src/snapshot-tests/__fixtures__/cli/json/macos/test--error-compiler.json index 6fdabe48c..d19c4fb86 100644 --- a/src/snapshot-tests/__fixtures__/cli/json/macos/test--error-compiler.json +++ b/src/snapshot-tests/__fixtures__/cli/json/macos/test--error-compiler.json @@ -1,14 +1,13 @@ { "schema": "xcodebuildmcp.output.test-result", - "schemaVersion": "2", + "schemaVersion": "3", "didError": true, "error": "Tests failed", "data": { "request": { "scheme": "MCPTest", "projectPath": "example_projects/macOS/MCPTest.xcodeproj", - "derivedDataPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/MCPTest-", - "configuration": "Debug", + "derivedDataPath": "/DerivedData", "platform": "macOS", "onlyTesting": [ "MCPTestTests/MCPTestTests/appNameIsCorrect()", @@ -19,16 +18,10 @@ "summary": { "status": "FAILED", "durationMs": 1234, - "counts": { - "passed": 0, - "failed": 0, - "skipped": 0 - }, "target": "macos" }, "artifacts": { - "buildLogPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/test_macos__pid.log", - "xcresultPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/result-bundles/test_macos__pid.xcresult" + "buildLogPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/test_macos__pid.log" }, "tests": { "selected": [ diff --git a/src/snapshot-tests/__fixtures__/cli/json/macos/test--error-wrong-scheme.json b/src/snapshot-tests/__fixtures__/cli/json/macos/test--error-wrong-scheme.json index a3a436aee..ddc6f3ac1 100644 --- a/src/snapshot-tests/__fixtures__/cli/json/macos/test--error-wrong-scheme.json +++ b/src/snapshot-tests/__fixtures__/cli/json/macos/test--error-wrong-scheme.json @@ -1,14 +1,13 @@ { "schema": "xcodebuildmcp.output.test-result", - "schemaVersion": "2", + "schemaVersion": "3", "didError": true, "error": "Tests failed", "data": { "request": { "scheme": "NONEXISTENT", "projectPath": "example_projects/macOS/MCPTest.xcodeproj", - "derivedDataPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/MCPTest-", - "configuration": "Debug", + "derivedDataPath": "/DerivedData", "platform": "macOS", "onlyTesting": [], "skipTesting": [] @@ -16,16 +15,10 @@ "summary": { "status": "FAILED", "durationMs": 1234, - "counts": { - "passed": 0, - "failed": 0, - "skipped": 0 - }, "target": "macos" }, "artifacts": { - "buildLogPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/test_macos__pid.log", - "xcresultPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/result-bundles/test_macos__pid.xcresult" + "buildLogPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/test_macos__pid.log" }, "diagnostics": { "warnings": [], diff --git a/src/snapshot-tests/__fixtures__/cli/json/macos/test--failure.json b/src/snapshot-tests/__fixtures__/cli/json/macos/test--failure.json index 0f0e5083c..4aff7746b 100644 --- a/src/snapshot-tests/__fixtures__/cli/json/macos/test--failure.json +++ b/src/snapshot-tests/__fixtures__/cli/json/macos/test--failure.json @@ -1,14 +1,13 @@ { "schema": "xcodebuildmcp.output.test-result", - "schemaVersion": "2", + "schemaVersion": "3", "didError": true, "error": "Tests failed", "data": { "request": { "scheme": "MCPTest", "projectPath": "example_projects/macOS/MCPTest.xcodeproj", - "derivedDataPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/MCPTest-", - "configuration": "Debug", + "derivedDataPath": "/DerivedData", "platform": "macOS", "onlyTesting": [], "skipTesting": [] @@ -25,7 +24,8 @@ }, "artifacts": { "buildLogPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/test_macos__pid.log", - "xcresultPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/result-bundles/test_macos__pid.xcresult" + "xcresultPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/result-bundles/test_macos__pid.xcresult", + "testProductsPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/test-products/test_macos__pid.xctestproducts" }, "tests": { "discovered": { @@ -42,17 +42,17 @@ "warnings": [], "errors": [], "testFailures": [ - { - "suite": "MCPTestsXCTests", - "test": "testDeliberateFailure()", - "message": "XCTAssertTrue failed - This test is designed to fail for snapshot testing", - "location": "MCPTestsXCTests.swift:11" - }, { "suite": "MCPTestTests", "test": "deliberateFailure()", "message": "Expectation failed: 1 == 2: This test is designed to fail for snapshot testing", "location": "MCPTestTests.swift:11" + }, + { + "suite": "MCPTestsXCTests", + "test": "testDeliberateFailure()", + "message": "XCTAssertTrue failed - This test is designed to fail for snapshot testing", + "location": "MCPTestsXCTests.swift:11" } ] }, diff --git a/src/snapshot-tests/__fixtures__/cli/json/macos/test--success.json b/src/snapshot-tests/__fixtures__/cli/json/macos/test--success.json index b09d39b4d..9b3342d58 100644 --- a/src/snapshot-tests/__fixtures__/cli/json/macos/test--success.json +++ b/src/snapshot-tests/__fixtures__/cli/json/macos/test--success.json @@ -1,14 +1,13 @@ { "schema": "xcodebuildmcp.output.test-result", - "schemaVersion": "2", + "schemaVersion": "3", "didError": false, "error": null, "data": { "request": { "scheme": "MCPTest", "projectPath": "example_projects/macOS/MCPTest.xcodeproj", - "derivedDataPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/MCPTest-", - "configuration": "Debug", + "derivedDataPath": "/DerivedData", "platform": "macOS", "onlyTesting": [ "MCPTestTests/MCPTestTests/appNameIsCorrect()", @@ -28,7 +27,8 @@ }, "artifacts": { "buildLogPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/test_macos__pid.log", - "xcresultPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/result-bundles/test_macos__pid.xcresult" + "xcresultPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/result-bundles/test_macos__pid.xcresult", + "testProductsPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/test-products/test_macos__pid.xctestproducts" }, "tests": { "selected": [ diff --git a/src/snapshot-tests/__fixtures__/cli/json/simulator-management/list--success.json b/src/snapshot-tests/__fixtures__/cli/json/simulator-management/list--success.json index 1135f65d1..fffe57c30 100644 --- a/src/snapshot-tests/__fixtures__/cli/json/simulator-management/list--success.json +++ b/src/snapshot-tests/__fixtures__/cli/json/simulator-management/list--success.json @@ -5,6 +5,272 @@ "error": null, "data": { "simulators": [ + { + "name": "Apple Watch Series 11 (46mm)", + "simulatorId": "", + "state": "", + "isAvailable": true, + "runtime": "watchOS " + }, + { + "name": "Apple Watch Series 11 (42mm)", + "simulatorId": "", + "state": "", + "isAvailable": true, + "runtime": "watchOS " + }, + { + "name": "Apple Watch Ultra 3 (49mm)", + "simulatorId": "", + "state": "", + "isAvailable": true, + "runtime": "watchOS " + }, + { + "name": "Apple Watch SE 3 (44mm)", + "simulatorId": "", + "state": "", + "isAvailable": true, + "runtime": "watchOS " + }, + { + "name": "Apple Watch SE 3 (40mm)", + "simulatorId": "", + "state": "", + "isAvailable": true, + "runtime": "watchOS " + }, + { + "name": "iPhone 17 Pro", + "simulatorId": "", + "state": "", + "isAvailable": false, + "runtime": "iOS " + }, + { + "name": "iPhone 17 Pro Max", + "simulatorId": "", + "state": "", + "isAvailable": false, + "runtime": "iOS " + }, + { + "name": "iPhone 17e", + "simulatorId": "", + "state": "", + "isAvailable": false, + "runtime": "iOS " + }, + { + "name": "iPhone Air", + "simulatorId": "", + "state": "", + "isAvailable": false, + "runtime": "iOS " + }, + { + "name": "iPhone 17", + "simulatorId": "", + "state": "", + "isAvailable": false, + "runtime": "iOS " + }, + { + "name": "iPad Pro 13-inch (M5)", + "simulatorId": "", + "state": "", + "isAvailable": false, + "runtime": "iOS " + }, + { + "name": "iPad Pro 11-inch (M5)", + "simulatorId": "", + "state": "", + "isAvailable": false, + "runtime": "iOS " + }, + { + "name": "iPad mini (A17 Pro)", + "simulatorId": "", + "state": "", + "isAvailable": false, + "runtime": "iOS " + }, + { + "name": "iPad Air 13-inch (M4)", + "simulatorId": "", + "state": "", + "isAvailable": false, + "runtime": "iOS " + }, + { + "name": "iPad Air 11-inch (M4)", + "simulatorId": "", + "state": "", + "isAvailable": false, + "runtime": "iOS " + }, + { + "name": "iPad (A16)", + "simulatorId": "", + "state": "", + "isAvailable": false, + "runtime": "iOS " + }, + { + "name": "iPhone 17 Pro", + "simulatorId": "", + "state": "", + "isAvailable": true, + "runtime": "iOS " + }, + { + "name": "iPhone 17 Pro", + "simulatorId": "", + "state": "", + "isAvailable": true, + "runtime": "iOS " + }, + { + "name": "iPhone 17 Pro Max", + "simulatorId": "", + "state": "", + "isAvailable": true, + "runtime": "iOS " + }, + { + "name": "iPhone 17e", + "simulatorId": "", + "state": "", + "isAvailable": true, + "runtime": "iOS " + }, + { + "name": "iPhone Air", + "simulatorId": "", + "state": "", + "isAvailable": true, + "runtime": "iOS " + }, + { + "name": "iPhone 17", + "simulatorId": "", + "state": "", + "isAvailable": true, + "runtime": "iOS " + }, + { + "name": "iPad Pro 13-inch (M5)", + "simulatorId": "", + "state": "", + "isAvailable": true, + "runtime": "iOS " + }, + { + "name": "iPad Pro 11-inch (M5)", + "simulatorId": "", + "state": "", + "isAvailable": true, + "runtime": "iOS " + }, + { + "name": "iPad mini (A17 Pro)", + "simulatorId": "", + "state": "", + "isAvailable": true, + "runtime": "iOS " + }, + { + "name": "iPad Air 13-inch (M4)", + "simulatorId": "", + "state": "", + "isAvailable": true, + "runtime": "iOS " + }, + { + "name": "iPad Air 11-inch (M4)", + "simulatorId": "", + "state": "", + "isAvailable": true, + "runtime": "iOS " + }, + { + "name": "iPad (A16)", + "simulatorId": "", + "state": "", + "isAvailable": true, + "runtime": "iOS " + }, + { + "name": "Apple Watch Series 11 (46mm)", + "simulatorId": "", + "state": "", + "isAvailable": true, + "runtime": "watchOS " + }, + { + "name": "Apple Watch Series 11 (42mm)", + "simulatorId": "", + "state": "", + "isAvailable": true, + "runtime": "watchOS " + }, + { + "name": "Apple Watch Ultra 3 (49mm)", + "simulatorId": "", + "state": "", + "isAvailable": true, + "runtime": "watchOS " + }, + { + "name": "Apple Watch SE 3 (44mm)", + "simulatorId": "", + "state": "", + "isAvailable": true, + "runtime": "watchOS " + }, + { + "name": "Apple Watch SE 3 (40mm)", + "simulatorId": "", + "state": "", + "isAvailable": true, + "runtime": "watchOS " + }, + { + "name": "Apple Watch Series 11 (46mm)", + "simulatorId": "", + "state": "", + "isAvailable": false, + "runtime": "watchOS " + }, + { + "name": "Apple Watch Series 11 (42mm)", + "simulatorId": "", + "state": "", + "isAvailable": false, + "runtime": "watchOS " + }, + { + "name": "Apple Watch Ultra 3 (49mm)", + "simulatorId": "", + "state": "", + "isAvailable": false, + "runtime": "watchOS " + }, + { + "name": "Apple Watch SE 3 (44mm)", + "simulatorId": "", + "state": "", + "isAvailable": false, + "runtime": "watchOS " + }, + { + "name": "Apple Watch SE 3 (40mm)", + "simulatorId": "", + "state": "", + "isAvailable": false, + "runtime": "watchOS " + }, { "name": "iPhone 17 Pro", "simulatorId": "", diff --git a/src/snapshot-tests/__fixtures__/cli/json/simulator/build--error-compiler.json b/src/snapshot-tests/__fixtures__/cli/json/simulator/build--error-compiler.json index d86866109..729e2d4b1 100644 --- a/src/snapshot-tests/__fixtures__/cli/json/simulator/build--error-compiler.json +++ b/src/snapshot-tests/__fixtures__/cli/json/simulator/build--error-compiler.json @@ -1,16 +1,15 @@ { "schema": "xcodebuildmcp.output.build-result", - "schemaVersion": "2", + "schemaVersion": "3", "didError": true, "error": "Build failed", "data": { "request": { "scheme": "CalculatorApp", "workspacePath": "example_projects/iOS_Calculator/CalculatorApp.xcworkspace", - "derivedDataPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/CalculatorApp-", - "configuration": "Debug", + "derivedDataPath": "", "platform": "iOS Simulator", - "simulatorName": "iPhone 17" + "simulatorId": "" }, "summary": { "status": "FAILED", @@ -25,7 +24,7 @@ "errors": [ { "message": "cannot convert value of type 'String' to specified type 'Int'", - "location": "/example_projects/iOS_Calculator/CalculatorApp/CalculatorApp.swift:33" + "location": "/example_projects/iOS_Calculator/CalculatorApp/CalculatorApp.swift:53" } ] } diff --git a/src/snapshot-tests/__fixtures__/cli/json/simulator/build--error-prepared-tests-wrong-scheme.json b/src/snapshot-tests/__fixtures__/cli/json/simulator/build--error-prepared-tests-wrong-scheme.json new file mode 100644 index 000000000..41002606b --- /dev/null +++ b/src/snapshot-tests/__fixtures__/cli/json/simulator/build--error-prepared-tests-wrong-scheme.json @@ -0,0 +1,33 @@ +{ + "schema": "xcodebuildmcp.output.build-result", + "schemaVersion": "3", + "didError": true, + "error": "Build failed", + "data": { + "request": { + "buildForTesting": true, + "scheme": "NONEXISTENT", + "workspacePath": "example_projects/iOS_Calculator/CalculatorApp.xcworkspace", + "derivedDataPath": "", + "platform": "iOS Simulator", + "simulatorId": "", + "testProductsPath": "/Invalid CalculatorApp Tests.xctestproducts" + }, + "summary": { + "status": "FAILED", + "durationMs": 1234, + "target": "simulator" + }, + "artifacts": { + "buildLogPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/build_sim__pid.log" + }, + "diagnostics": { + "warnings": [], + "errors": [ + { + "message": "The workspace named \"CalculatorApp\" does not contain a scheme named \"NONEXISTENT\". The \"-list\" option can be used to find the names of the schemes in the workspace." + } + ] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/cli/json/simulator/build--error-wrong-scheme.json b/src/snapshot-tests/__fixtures__/cli/json/simulator/build--error-wrong-scheme.json index cb912b99e..1c4d27f55 100644 --- a/src/snapshot-tests/__fixtures__/cli/json/simulator/build--error-wrong-scheme.json +++ b/src/snapshot-tests/__fixtures__/cli/json/simulator/build--error-wrong-scheme.json @@ -1,16 +1,15 @@ { "schema": "xcodebuildmcp.output.build-result", - "schemaVersion": "2", + "schemaVersion": "3", "didError": true, "error": "Build failed", "data": { "request": { "scheme": "NONEXISTENT", "workspacePath": "example_projects/iOS_Calculator/CalculatorApp.xcworkspace", - "derivedDataPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/CalculatorApp-", - "configuration": "Debug", + "derivedDataPath": "", "platform": "iOS Simulator", - "simulatorName": "iPhone 17" + "simulatorId": "" }, "summary": { "status": "FAILED", diff --git a/src/snapshot-tests/__fixtures__/cli/json/simulator/build--success-prepared-tests.json b/src/snapshot-tests/__fixtures__/cli/json/simulator/build--success-prepared-tests.json new file mode 100644 index 000000000..b91950011 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/cli/json/simulator/build--success-prepared-tests.json @@ -0,0 +1,36 @@ +{ + "schema": "xcodebuildmcp.output.build-result", + "schemaVersion": "3", + "didError": false, + "error": null, + "data": { + "request": { + "buildForTesting": true, + "scheme": "CalculatorApp", + "workspacePath": "example_projects/iOS_Calculator/CalculatorApp.xcworkspace", + "derivedDataPath": "", + "platform": "iOS Simulator", + "simulatorId": "", + "testProductsPath": "/CalculatorApp Tests.xctestproducts" + }, + "summary": { + "status": "SUCCEEDED", + "durationMs": 1234, + "target": "simulator" + }, + "artifacts": { + "buildLogPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/build_sim__pid.log", + "testProductsPath": "/CalculatorApp Tests.xctestproducts", + "xctestrunPaths": [ + "/CalculatorApp Tests.xctestproducts/Tests/0/CalculatorApp.xctestrun" + ] + }, + "diagnostics": { + "warnings": [], + "errors": [] + } + }, + "nextSteps": [ + "Run prepared tests: xcodebuildmcp simulator test --test-products-path '/CalculatorApp Tests.xctestproducts' --simulator-id " + ] +} diff --git a/src/snapshot-tests/__fixtures__/cli/json/simulator/build--success.json b/src/snapshot-tests/__fixtures__/cli/json/simulator/build--success.json index a4d7bbeb2..211f4f4da 100644 --- a/src/snapshot-tests/__fixtures__/cli/json/simulator/build--success.json +++ b/src/snapshot-tests/__fixtures__/cli/json/simulator/build--success.json @@ -1,16 +1,15 @@ { "schema": "xcodebuildmcp.output.build-result", - "schemaVersion": "2", + "schemaVersion": "3", "didError": false, "error": null, "data": { "request": { "scheme": "CalculatorApp", "workspacePath": "example_projects/iOS_Calculator/CalculatorApp.xcworkspace", - "derivedDataPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/CalculatorApp-", - "configuration": "Debug", + "derivedDataPath": "", "platform": "iOS Simulator", - "simulatorName": "iPhone 17" + "simulatorId": "" }, "summary": { "status": "SUCCEEDED", @@ -26,6 +25,6 @@ } }, "nextSteps": [ - "Get built app path in simulator derived data: xcodebuildmcp simulator get-app-path --simulator-name 'iPhone 17' --scheme CalculatorApp --platform 'iOS Simulator'" + "Get built app path in simulator derived data: xcodebuildmcp simulator get-app-path --simulator-id --scheme CalculatorApp --platform 'iOS Simulator' --derived-data-path " ] } diff --git a/src/snapshot-tests/__fixtures__/cli/json/simulator/build-and-run--error-compiler.json b/src/snapshot-tests/__fixtures__/cli/json/simulator/build-and-run--error-compiler.json index 437ba453d..ce90e77da 100644 --- a/src/snapshot-tests/__fixtures__/cli/json/simulator/build-and-run--error-compiler.json +++ b/src/snapshot-tests/__fixtures__/cli/json/simulator/build-and-run--error-compiler.json @@ -7,10 +7,9 @@ "request": { "scheme": "CalculatorApp", "workspacePath": "example_projects/iOS_Calculator/CalculatorApp.xcworkspace", - "derivedDataPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/CalculatorApp-", - "configuration": "Debug", + "derivedDataPath": "", "platform": "iOS Simulator", - "simulatorName": "iPhone 17" + "simulatorId": "" }, "summary": { "status": "FAILED", @@ -25,7 +24,7 @@ "errors": [ { "message": "cannot convert value of type 'String' to specified type 'Int'", - "location": "/example_projects/iOS_Calculator/CalculatorApp/CalculatorApp.swift:33" + "location": "/example_projects/iOS_Calculator/CalculatorApp/CalculatorApp.swift:53" } ] } diff --git a/src/snapshot-tests/__fixtures__/cli/json/simulator/build-and-run--error-wrong-scheme.json b/src/snapshot-tests/__fixtures__/cli/json/simulator/build-and-run--error-wrong-scheme.json index 4398d22e0..5d918f52b 100644 --- a/src/snapshot-tests/__fixtures__/cli/json/simulator/build-and-run--error-wrong-scheme.json +++ b/src/snapshot-tests/__fixtures__/cli/json/simulator/build-and-run--error-wrong-scheme.json @@ -7,10 +7,9 @@ "request": { "scheme": "NONEXISTENT", "workspacePath": "example_projects/iOS_Calculator/CalculatorApp.xcworkspace", - "derivedDataPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/CalculatorApp-", - "configuration": "Debug", + "derivedDataPath": "", "platform": "iOS Simulator", - "simulatorName": "iPhone 17" + "simulatorId": "" }, "summary": { "status": "FAILED", diff --git a/src/snapshot-tests/__fixtures__/cli/json/simulator/build-and-run--success.json b/src/snapshot-tests/__fixtures__/cli/json/simulator/build-and-run--success.json index b11f85670..add9ef07d 100644 --- a/src/snapshot-tests/__fixtures__/cli/json/simulator/build-and-run--success.json +++ b/src/snapshot-tests/__fixtures__/cli/json/simulator/build-and-run--success.json @@ -7,10 +7,9 @@ "request": { "scheme": "CalculatorApp", "workspacePath": "example_projects/iOS_Calculator/CalculatorApp.xcworkspace", - "derivedDataPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/CalculatorApp-", - "configuration": "Debug", + "derivedDataPath": "", "platform": "iOS Simulator", - "simulatorName": "iPhone 17" + "simulatorId": "" }, "summary": { "status": "SUCCEEDED", @@ -18,7 +17,7 @@ "target": "simulator" }, "artifacts": { - "appPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/CalculatorApp-/Build/Products/Debug-iphonesimulator/CalculatorApp.app", + "appPath": "/Build/Products/Debug-iphonesimulator/CalculatorApp.app", "bundleId": "io.sentry.calculatorapp", "processId": 99999, "simulatorId": "", diff --git a/src/snapshot-tests/__fixtures__/cli/json/simulator/get-app-path--error-wrong-scheme.json b/src/snapshot-tests/__fixtures__/cli/json/simulator/get-app-path--error-wrong-scheme.json index f0c0c75bb..d5e760dec 100644 --- a/src/snapshot-tests/__fixtures__/cli/json/simulator/get-app-path--error-wrong-scheme.json +++ b/src/snapshot-tests/__fixtures__/cli/json/simulator/get-app-path--error-wrong-scheme.json @@ -7,9 +7,8 @@ "request": { "scheme": "NONEXISTENT", "workspacePath": "example_projects/iOS_Calculator/CalculatorApp.xcworkspace", - "configuration": "Debug", "platform": "iOS Simulator", - "simulator": "iPhone 17" + "simulator": "" }, "summary": { "status": "FAILED", diff --git a/src/snapshot-tests/__fixtures__/cli/json/simulator/get-app-path--success.json b/src/snapshot-tests/__fixtures__/cli/json/simulator/get-app-path--success.json index 9e970484c..79055c835 100644 --- a/src/snapshot-tests/__fixtures__/cli/json/simulator/get-app-path--success.json +++ b/src/snapshot-tests/__fixtures__/cli/json/simulator/get-app-path--success.json @@ -7,9 +7,8 @@ "request": { "scheme": "CalculatorApp", "workspacePath": "example_projects/iOS_Calculator/CalculatorApp.xcworkspace", - "configuration": "Debug", "platform": "iOS Simulator", - "simulator": "iPhone 17" + "simulator": "" }, "summary": { "status": "SUCCEEDED", @@ -17,13 +16,13 @@ "durationMs": 1234 }, "artifacts": { - "appPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/CalculatorApp-/Build/Products/Debug-iphonesimulator/CalculatorApp.app" + "appPath": "/Build/Products/Debug-iphonesimulator/CalculatorApp.app" } }, "nextSteps": [ - "Get bundle ID: xcodebuildmcp simulator get-app-bundle-id --app-path ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/CalculatorApp-/Build/Products/Debug-iphonesimulator/CalculatorApp.app", + "Get bundle ID: xcodebuildmcp simulator get-app-bundle-id --app-path /Build/Products/Debug-iphonesimulator/CalculatorApp.app", "Boot simulator: xcodebuildmcp simulator boot --simulator-id SIMULATOR_UUID", - "Install app: xcodebuildmcp simulator install --simulator-id SIMULATOR_UUID --app-path ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/CalculatorApp-/Build/Products/Debug-iphonesimulator/CalculatorApp.app", + "Install app: xcodebuildmcp simulator install --simulator-id SIMULATOR_UUID --app-path /Build/Products/Debug-iphonesimulator/CalculatorApp.app", "Launch app: xcodebuildmcp simulator launch-app --simulator-id SIMULATOR_UUID --bundle-id BUNDLE_ID" ] } diff --git a/src/snapshot-tests/__fixtures__/cli/json/simulator/install--success.json b/src/snapshot-tests/__fixtures__/cli/json/simulator/install--success.json index fecef3d42..0d101cdaa 100644 --- a/src/snapshot-tests/__fixtures__/cli/json/simulator/install--success.json +++ b/src/snapshot-tests/__fixtures__/cli/json/simulator/install--success.json @@ -9,7 +9,7 @@ }, "artifacts": { "simulatorId": "", - "appPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/CalculatorApp-/Build/Products/Debug-iphonesimulator/CalculatorApp.app" + "appPath": "/Build/Products/Debug-iphonesimulator/CalculatorApp.app" }, "diagnostics": { "warnings": [], diff --git a/src/snapshot-tests/__fixtures__/cli/json/simulator/test--error-compiler.json b/src/snapshot-tests/__fixtures__/cli/json/simulator/test--error-compiler.json index 98648c37e..eda10eb91 100644 --- a/src/snapshot-tests/__fixtures__/cli/json/simulator/test--error-compiler.json +++ b/src/snapshot-tests/__fixtures__/cli/json/simulator/test--error-compiler.json @@ -1,16 +1,15 @@ { "schema": "xcodebuildmcp.output.test-result", - "schemaVersion": "2", + "schemaVersion": "3", "didError": true, "error": "Tests failed", "data": { "request": { "scheme": "CalculatorApp", "workspacePath": "example_projects/iOS_Calculator/CalculatorApp.xcworkspace", - "derivedDataPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/CalculatorApp-", - "configuration": "Debug", + "derivedDataPath": "", "platform": "iOS Simulator", - "simulatorName": "iPhone 17", + "simulatorId": "", "onlyTesting": [ "CalculatorAppTests/CalculatorAppTests/testAddition" ], @@ -40,7 +39,7 @@ "errors": [ { "message": "cannot convert value of type 'String' to specified type 'Int'", - "location": "/example_projects/iOS_Calculator/CalculatorApp/CalculatorApp.swift:33" + "location": "/example_projects/iOS_Calculator/CalculatorApp/CalculatorApp.swift:53" } ], "testFailures": [] diff --git a/src/snapshot-tests/__fixtures__/cli/json/simulator/test--error-wrong-scheme.json b/src/snapshot-tests/__fixtures__/cli/json/simulator/test--error-wrong-scheme.json index b4ffc4ae7..4e0fe13e7 100644 --- a/src/snapshot-tests/__fixtures__/cli/json/simulator/test--error-wrong-scheme.json +++ b/src/snapshot-tests/__fixtures__/cli/json/simulator/test--error-wrong-scheme.json @@ -1,16 +1,15 @@ { "schema": "xcodebuildmcp.output.test-result", - "schemaVersion": "2", + "schemaVersion": "3", "didError": true, "error": "Tests failed", "data": { "request": { "scheme": "NONEXISTENT", "workspacePath": "example_projects/iOS_Calculator/CalculatorApp.xcworkspace", - "derivedDataPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/CalculatorApp-", - "configuration": "Debug", + "derivedDataPath": "", "platform": "iOS Simulator", - "simulatorName": "iPhone 17", + "simulatorId": "", "onlyTesting": [], "skipTesting": [] }, diff --git a/src/snapshot-tests/__fixtures__/cli/json/simulator/test--failure.json b/src/snapshot-tests/__fixtures__/cli/json/simulator/test--failure.json index e56bfefca..af4f82629 100644 --- a/src/snapshot-tests/__fixtures__/cli/json/simulator/test--failure.json +++ b/src/snapshot-tests/__fixtures__/cli/json/simulator/test--failure.json @@ -1,16 +1,15 @@ { "schema": "xcodebuildmcp.output.test-result", - "schemaVersion": "2", + "schemaVersion": "3", "didError": true, "error": "Tests failed", "data": { "request": { "scheme": "CalculatorApp", "workspacePath": "example_projects/iOS_Calculator/CalculatorApp.xcworkspace", - "derivedDataPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/CalculatorApp-", - "configuration": "Debug", + "derivedDataPath": "", "platform": "iOS Simulator", - "simulatorName": "iPhone 17", + "simulatorId": "", "onlyTesting": [], "skipTesting": [] }, @@ -26,7 +25,8 @@ }, "artifacts": { "buildLogPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/test_sim__pid.log", - "xcresultPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/result-bundles/test_sim__pid.xcresult" + "xcresultPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/result-bundles/test_sim__pid.xcresult", + "testProductsPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/test-products/test_sim__pid.xctestproducts" }, "tests": { "discovered": { diff --git a/src/snapshot-tests/__fixtures__/cli/json/simulator/test--success.json b/src/snapshot-tests/__fixtures__/cli/json/simulator/test--success.json index 68d45018e..85b606059 100644 --- a/src/snapshot-tests/__fixtures__/cli/json/simulator/test--success.json +++ b/src/snapshot-tests/__fixtures__/cli/json/simulator/test--success.json @@ -1,16 +1,15 @@ { "schema": "xcodebuildmcp.output.test-result", - "schemaVersion": "2", + "schemaVersion": "3", "didError": false, "error": null, "data": { "request": { "scheme": "CalculatorApp", "workspacePath": "example_projects/iOS_Calculator/CalculatorApp.xcworkspace", - "derivedDataPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/CalculatorApp-", - "configuration": "Debug", + "derivedDataPath": "", "platform": "iOS Simulator", - "simulatorName": "iPhone 17", + "simulatorId": "", "onlyTesting": [ "CalculatorAppTests/CalculatorAppTests/testAddition" ], @@ -28,7 +27,8 @@ }, "artifacts": { "buildLogPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/test_sim__pid.log", - "xcresultPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/result-bundles/test_sim__pid.xcresult" + "xcresultPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/result-bundles/test_sim__pid.xcresult", + "testProductsPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/test-products/test_sim__pid.xctestproducts" }, "tests": { "selected": [ diff --git a/src/snapshot-tests/__fixtures__/cli/json/swift-package/build--error-bad-path.json b/src/snapshot-tests/__fixtures__/cli/json/swift-package/build--error-bad-path.json index 67daafb8c..293ba8c5a 100644 --- a/src/snapshot-tests/__fixtures__/cli/json/swift-package/build--error-bad-path.json +++ b/src/snapshot-tests/__fixtures__/cli/json/swift-package/build--error-bad-path.json @@ -5,7 +5,7 @@ "error": "Build failed", "data": { "request": { - "packagePath": "/example_projects/NONEXISTENT", + "packagePath": "/spm/NONEXISTENT", "target": "swift-package" }, "summary": { @@ -14,14 +14,14 @@ "target": "swift-package" }, "artifacts": { - "packagePath": "/example_projects/NONEXISTENT", + "packagePath": "/spm/NONEXISTENT", "buildLogPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/build_spm__pid.log" }, "diagnostics": { "warnings": [], "errors": [ { - "message": "chdir error: No such file or directory (2): /example_projects/NONEXISTENT" + "message": "chdir error: No such file or directory (2): /spm/NONEXISTENT" } ] } diff --git a/src/snapshot-tests/__fixtures__/cli/json/swift-package/build--success.json b/src/snapshot-tests/__fixtures__/cli/json/swift-package/build--success.json index d993cf664..2b7d310cd 100644 --- a/src/snapshot-tests/__fixtures__/cli/json/swift-package/build--success.json +++ b/src/snapshot-tests/__fixtures__/cli/json/swift-package/build--success.json @@ -5,7 +5,7 @@ "error": null, "data": { "request": { - "packagePath": "/example_projects/spm", + "packagePath": "/spm", "target": "swift-package" }, "summary": { @@ -14,7 +14,7 @@ "target": "swift-package" }, "artifacts": { - "packagePath": "/example_projects/spm", + "packagePath": "/spm", "buildLogPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/build_spm__pid.log" }, "diagnostics": { diff --git a/src/snapshot-tests/__fixtures__/cli/json/swift-package/clean--error-bad-path.json b/src/snapshot-tests/__fixtures__/cli/json/swift-package/clean--error-bad-path.json index 7f29e308a..893061af3 100644 --- a/src/snapshot-tests/__fixtures__/cli/json/swift-package/clean--error-bad-path.json +++ b/src/snapshot-tests/__fixtures__/cli/json/swift-package/clean--error-bad-path.json @@ -9,13 +9,13 @@ "target": "swift-package" }, "artifacts": { - "packagePath": "/example_projects/NONEXISTENT" + "packagePath": "/spm/NONEXISTENT" }, "diagnostics": { "warnings": [], "errors": [ { - "message": "chdir error: No such file or directory (2): /example_projects/NONEXISTENT" + "message": "chdir error: No such file or directory (2): /spm/NONEXISTENT" } ] } diff --git a/src/snapshot-tests/__fixtures__/cli/json/swift-package/clean--success.json b/src/snapshot-tests/__fixtures__/cli/json/swift-package/clean--success.json index 75b69af0e..1461bac35 100644 --- a/src/snapshot-tests/__fixtures__/cli/json/swift-package/clean--success.json +++ b/src/snapshot-tests/__fixtures__/cli/json/swift-package/clean--success.json @@ -9,7 +9,7 @@ "target": "swift-package" }, "artifacts": { - "packagePath": "/example_projects/spm" + "packagePath": "/spm" }, "diagnostics": { "warnings": [], diff --git a/src/snapshot-tests/__fixtures__/cli/json/swift-package/list--success.json b/src/snapshot-tests/__fixtures__/cli/json/swift-package/list--success.json index 82f194fc5..018c30d24 100644 --- a/src/snapshot-tests/__fixtures__/cli/json/swift-package/list--success.json +++ b/src/snapshot-tests/__fixtures__/cli/json/swift-package/list--success.json @@ -13,7 +13,7 @@ "processId": 99999, "uptimeSeconds": 3600, "artifacts": { - "packagePath": "/example_projects/spm" + "packagePath": "/spm" } } ] diff --git a/src/snapshot-tests/__fixtures__/cli/json/swift-package/run--error-bad-executable.json b/src/snapshot-tests/__fixtures__/cli/json/swift-package/run--error-bad-executable.json index 48b7983e7..745fc8277 100644 --- a/src/snapshot-tests/__fixtures__/cli/json/swift-package/run--error-bad-executable.json +++ b/src/snapshot-tests/__fixtures__/cli/json/swift-package/run--error-bad-executable.json @@ -5,7 +5,7 @@ "error": "Build failed", "data": { "request": { - "packagePath": "/example_projects/spm", + "packagePath": "/spm", "executableName": "nonexistent-executable", "target": "swift-package" }, @@ -15,7 +15,7 @@ "target": "swift-package" }, "artifacts": { - "packagePath": "/example_projects/spm", + "packagePath": "/spm", "buildLogPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/build_run_spm__pid.log" }, "diagnostics": { diff --git a/src/snapshot-tests/__fixtures__/cli/json/swift-package/run--success.json b/src/snapshot-tests/__fixtures__/cli/json/swift-package/run--success.json index 2d6aafe5b..715465524 100644 --- a/src/snapshot-tests/__fixtures__/cli/json/swift-package/run--success.json +++ b/src/snapshot-tests/__fixtures__/cli/json/swift-package/run--success.json @@ -5,7 +5,7 @@ "error": null, "data": { "request": { - "packagePath": "/example_projects/spm", + "packagePath": "/spm", "executableName": "spm", "target": "swift-package" }, @@ -15,8 +15,8 @@ "target": "swift-package" }, "artifacts": { - "packagePath": "/example_projects/spm", - "executablePath": "/example_projects/spm/.build/arm64-apple-macosx/debug/spm", + "packagePath": "/spm", + "executablePath": "/spm/.build//debug/spm", "processId": 99999, "buildLogPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/build_run_spm__pid.log" }, @@ -25,6 +25,13 @@ "Hello, world!" ], "stderr": [ + "Fetching https://github.com/apple/swift-argument-parser.git from cache", + "Fetched https://github.com/apple/swift-argument-parser.git from cache ()", + "Computing version for https://github.com/apple/swift-argument-parser.git", + "Computed https://github.com/apple/swift-argument-parser.git at 1.5.1 ()", + "Computed https://github.com/apple/swift-argument-parser.git at 1.5.1 ()", + "Creating working copy for https://github.com/apple/swift-argument-parser.git", + "Working copy of https://github.com/apple/swift-argument-parser.git resolved at 1.5.1", "Building for debugging...", "[] Applying spm", "[] Compiling spm main.swift", diff --git a/src/snapshot-tests/__fixtures__/cli/json/swift-package/test--error-bad-path.json b/src/snapshot-tests/__fixtures__/cli/json/swift-package/test--error-bad-path.json index 5c87db045..98a6d8e6a 100644 --- a/src/snapshot-tests/__fixtures__/cli/json/swift-package/test--error-bad-path.json +++ b/src/snapshot-tests/__fixtures__/cli/json/swift-package/test--error-bad-path.json @@ -17,13 +17,13 @@ }, "artifacts": { "buildLogPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/swift_package_test__pid.log", - "packagePath": "/example_projects/NONEXISTENT" + "packagePath": "/spm/NONEXISTENT" }, "diagnostics": { "warnings": [], "errors": [ { - "message": "chdir error: No such file or directory (2): /example_projects/NONEXISTENT" + "message": "chdir error: No such file or directory (2): /spm/NONEXISTENT" } ], "testFailures": [] diff --git a/src/snapshot-tests/__fixtures__/cli/json/swift-package/test--failure.json b/src/snapshot-tests/__fixtures__/cli/json/swift-package/test--failure.json index e060a3b35..45c8b0de3 100644 --- a/src/snapshot-tests/__fixtures__/cli/json/swift-package/test--failure.json +++ b/src/snapshot-tests/__fixtures__/cli/json/swift-package/test--failure.json @@ -27,17 +27,17 @@ "warnings": [], "errors": [], "testFailures": [ - { - "suite": "CalculatorAppTests", - "test": "testCalculatorServiceFailure", - "message": "XCTAssertEqual failed: (\"0\") is not equal to (\"999\") - This test should fail - display should be 0, not 999", - "location": "/example_projects/spm/Tests/TestLibTests/SimpleTests.swift:49" - }, { "suite": "(Unknown Suite)", "test": "test", "message": "Expectation failed: Bool(false)\nTest failed", "location": "SimpleTests.swift:57" + }, + { + "suite": "CalculatorAppTests", + "test": "testCalculatorServiceFailure", + "message": "XCTAssertEqual failed: (\"0\") is not equal to (\"999\") - This test should fail - display should be 0, not 999", + "location": "/spm/Tests/TestLibTests/SimpleTests.swift:49" } ] }, diff --git a/src/snapshot-tests/__fixtures__/cli/json/ui-automation/gesture--success.json b/src/snapshot-tests/__fixtures__/cli/json/ui-automation/gesture--success.json index db2d0a23b..227ce80eb 100644 --- a/src/snapshot-tests/__fixtures__/cli/json/ui-automation/gesture--success.json +++ b/src/snapshot-tests/__fixtures__/cli/json/ui-automation/gesture--success.json @@ -19,7 +19,7 @@ "rs": "1", "screenHash": "", "seq": 1, - "count": 28, + "count": 99999, "targets": [ "|tap|button|7||", "|tap|button|8||", diff --git a/src/snapshot-tests/__fixtures__/cli/json/ui-automation/key-press--success.json b/src/snapshot-tests/__fixtures__/cli/json/ui-automation/key-press--success.json index 68b0d0977..13bf57018 100644 --- a/src/snapshot-tests/__fixtures__/cli/json/ui-automation/key-press--success.json +++ b/src/snapshot-tests/__fixtures__/cli/json/ui-automation/key-press--success.json @@ -19,7 +19,7 @@ "rs": "1", "screenHash": "", "seq": 1, - "count": 28, + "count": 99999, "targets": [ "|tap|button|7||", "|tap|button|8||", diff --git a/src/snapshot-tests/__fixtures__/cli/json/ui-automation/key-sequence--success.json b/src/snapshot-tests/__fixtures__/cli/json/ui-automation/key-sequence--success.json index 2ea33f45f..fc87abf89 100644 --- a/src/snapshot-tests/__fixtures__/cli/json/ui-automation/key-sequence--success.json +++ b/src/snapshot-tests/__fixtures__/cli/json/ui-automation/key-sequence--success.json @@ -23,7 +23,7 @@ "rs": "1", "screenHash": "", "seq": 1, - "count": 28, + "count": 99999, "targets": [ "|tap|button|7||", "|tap|button|8||", diff --git a/src/snapshot-tests/__fixtures__/cli/json/ui-automation/long-press--success.json b/src/snapshot-tests/__fixtures__/cli/json/ui-automation/long-press--success.json index ca0589497..4e19691ff 100644 --- a/src/snapshot-tests/__fixtures__/cli/json/ui-automation/long-press--success.json +++ b/src/snapshot-tests/__fixtures__/cli/json/ui-automation/long-press--success.json @@ -22,7 +22,7 @@ "rs": "1", "screenHash": "", "seq": 1, - "count": 28, + "count": 99999, "targets": [ "|tap|button|7||", "|tap|button|8||", diff --git a/src/snapshot-tests/__fixtures__/cli/json/ui-automation/snapshot-ui--success.json b/src/snapshot-tests/__fixtures__/cli/json/ui-automation/snapshot-ui--success.json index 24b131aea..35c92730e 100644 --- a/src/snapshot-tests/__fixtures__/cli/json/ui-automation/snapshot-ui--success.json +++ b/src/snapshot-tests/__fixtures__/cli/json/ui-automation/snapshot-ui--success.json @@ -15,7 +15,7 @@ "rs": "1", "screenHash": "", "seq": 1, - "count": 28, + "count": 99999, "targets": [ "|tap|button|7||", "|tap|button|8||", diff --git a/src/snapshot-tests/__fixtures__/cli/json/ui-automation/swipe--error-not-actionable.json b/src/snapshot-tests/__fixtures__/cli/json/ui-automation/swipe--error-not-actionable.json index e3071ffbc..978744a3b 100644 --- a/src/snapshot-tests/__fixtures__/cli/json/ui-automation/swipe--error-not-actionable.json +++ b/src/snapshot-tests/__fixtures__/cli/json/ui-automation/swipe--error-not-actionable.json @@ -2,7 +2,7 @@ "schema": "xcodebuildmcp.output.ui-action-result", "schemaVersion": "2", "didError": true, - "error": "Element ref 'e3' does not support 'swipeWithin'.", + "error": "Element ref '' does not support 'swipeWithin'.", "data": { "summary": { "status": "FAILED" @@ -17,7 +17,7 @@ }, "uiError": { "code": "TARGET_NOT_ACTIONABLE", - "message": "Element ref 'e3' does not support 'swipeWithin'.", + "message": "Element ref '' does not support 'swipeWithin'.", "recoveryHint": "Choose an elementRef that lists the required action, or refresh with snapshot_ui.", "elementRef": "", "candidates": [], diff --git a/src/snapshot-tests/__fixtures__/cli/json/ui-automation/swipe--success.json b/src/snapshot-tests/__fixtures__/cli/json/ui-automation/swipe--success.json index 5c40383a0..9b6d1fec7 100644 --- a/src/snapshot-tests/__fixtures__/cli/json/ui-automation/swipe--success.json +++ b/src/snapshot-tests/__fixtures__/cli/json/ui-automation/swipe--success.json @@ -13,11 +13,11 @@ "direction": "up", "from": { "x": 201, - "y": 743 + "y": 468 }, "to": { "x": 201, - "y": 131 + "y": 406 } }, "artifacts": { @@ -28,45 +28,31 @@ "rs": "1", "screenHash": "", "seq": 1, - "count": 144, - "targets": [ - "|tap|button|Home Screen & App Library||com.apple.settings.homeScreen", - "|typeText|text-field||Search|", - "|tap|button|Camera||com.apple.settings.camera", - "|tap|button|Search||com.apple.settings.search", - "|tap|button|StandBy||com.apple.settings.standBy", - "|tap|button|Screen Time||com.apple.settings.screenTime", - "|tap|button|Passcode||com.apple.settings.passcodeAndBiometrics", - "|tap|button|Privacy & Security||com.apple.settings.privacyAndSecurity", - "|tap|button|Game Center||com.apple.settings.gameCenter", - "|tap|button|iCloud||com.apple.settings.iCloud", - "|tap|button|Apps||com.apple.settings.apps", - "|tap|button|Developer||com.apple.settings.developer", - "|tap|button|Dictate||Dictate", - "|tap|button|Dictate||Dictate" - ], + "count": 99999, + "targets": [], "scroll": [ - "|swipe|application|Settings||" + "|swipe|scroll-view|||snapshot-scroll-surface" ], "text": [ - "|text|text|Apple Intelligence & Siri||", - "|text|text|Camera||", - "|text|text|Home Screen & App Library||", - "|text|text|Search||", - "|text|text|StandBy||", - "|text|text|Screen Time||", - "|text|text|Passcode||", - "|text|text|Privacy & Security||", - "|text|text|Game Center||", - "|text|text|iCloud||", - "|text|text|Apps||", - "|text|text|Developer||" + "|text|text|Snapshot row 0||", + "|text|text|Snapshot row 1||", + "|text|text|Snapshot row 2||", + "|text|text|Snapshot row 3||", + "|text|text|Snapshot row 4||", + "|text|text|Snapshot row 5||", + "|text|text|Snapshot row 6||", + "|text|text|Snapshot row 7||", + "|text|text|Snapshot row 8||", + "|text|text|Snapshot row 9||", + "|text|text|Snapshot row 10||", + "|text|text|Snapshot row 11||", + "|text|text|Snapshot row 12||", + "|text|text|Snapshot row 13||" ], "udid": "" } }, "nextSteps": [ - "Batch same-screen taps: xcodebuildmcp ui-automation batch --json '{\"simulatorId\":\"\",\"steps\":[{\"action\":\"tap\",\"elementRef\":\"\"},{\"action\":\"tap\",\"elementRef\":\"\"}]}'", - "Tap an elementRef: xcodebuildmcp ui-automation tap --simulator-id --element-ref " + "" ] } diff --git a/src/snapshot-tests/__fixtures__/cli/json/ui-automation/tap--success-verbose.json b/src/snapshot-tests/__fixtures__/cli/json/ui-automation/tap--success-verbose.json index c8eb7775f..0403ab758 100644 --- a/src/snapshot-tests/__fixtures__/cli/json/ui-automation/tap--success-verbose.json +++ b/src/snapshot-tests/__fixtures__/cli/json/ui-automation/tap--success-verbose.json @@ -149,7 +149,7 @@ { "ref": "e8", "role": "text", - "label": "7", + "label": "0", "frame": { "x": 0, "y": 0, @@ -608,12 +608,12 @@ { "action": "longPress", "elementRef": "e8", - "label": "7" + "label": "0" }, { "action": "touch", "elementRef": "e8", - "label": "7" + "label": "0" }, { "action": "longPress", diff --git a/src/snapshot-tests/__fixtures__/cli/json/ui-automation/tap--success.json b/src/snapshot-tests/__fixtures__/cli/json/ui-automation/tap--success.json index 8b17e5398..369ece2af 100644 --- a/src/snapshot-tests/__fixtures__/cli/json/ui-automation/tap--success.json +++ b/src/snapshot-tests/__fixtures__/cli/json/ui-automation/tap--success.json @@ -21,7 +21,7 @@ "rs": "1", "screenHash": "", "seq": 1, - "count": 28, + "count": 99999, "targets": [ "|tap|button|7||", "|tap|button|8||", @@ -45,7 +45,7 @@ ], "scroll": [], "text": [ - "|text|text|7||" + "|text|text|0||" ], "udid": "" } diff --git a/src/snapshot-tests/__fixtures__/cli/json/ui-automation/touch--success.json b/src/snapshot-tests/__fixtures__/cli/json/ui-automation/touch--success.json index 824f4e4a3..87bbaa6fa 100644 --- a/src/snapshot-tests/__fixtures__/cli/json/ui-automation/touch--success.json +++ b/src/snapshot-tests/__fixtures__/cli/json/ui-automation/touch--success.json @@ -22,7 +22,7 @@ "rs": "1", "screenHash": "", "seq": 1, - "count": 28, + "count": 99999, "targets": [ "|tap|button|7||", "|tap|button|8||", diff --git a/src/snapshot-tests/__fixtures__/cli/json/ui-automation/type-text--error-not-actionable.json b/src/snapshot-tests/__fixtures__/cli/json/ui-automation/type-text--error-not-actionable.json index 69ac8a784..caf6f8a68 100644 --- a/src/snapshot-tests/__fixtures__/cli/json/ui-automation/type-text--error-not-actionable.json +++ b/src/snapshot-tests/__fixtures__/cli/json/ui-automation/type-text--error-not-actionable.json @@ -2,7 +2,7 @@ "schema": "xcodebuildmcp.output.ui-action-result", "schemaVersion": "2", "didError": true, - "error": "Element ref 'e3' does not support 'typeText'.", + "error": "Element ref '' does not support 'typeText'.", "data": { "summary": { "status": "FAILED" @@ -17,7 +17,7 @@ }, "uiError": { "code": "TARGET_NOT_ACTIONABLE", - "message": "Element ref 'e3' does not support 'typeText'.", + "message": "Element ref '' does not support 'typeText'.", "recoveryHint": "Choose an elementRef that lists the required action, or refresh with snapshot_ui.", "elementRef": "", "candidates": [], diff --git a/src/snapshot-tests/__fixtures__/cli/json/ui-automation/wait-for-ui--success.json b/src/snapshot-tests/__fixtures__/cli/json/ui-automation/wait-for-ui--success.json index 9b787a02c..db94390a5 100644 --- a/src/snapshot-tests/__fixtures__/cli/json/ui-automation/wait-for-ui--success.json +++ b/src/snapshot-tests/__fixtures__/cli/json/ui-automation/wait-for-ui--success.json @@ -15,7 +15,7 @@ "rs": "1", "screenHash": "", "seq": 1, - "count": 28, + "count": 99999, "targets": [ "|tap|button|7||", "|tap|button|8||", diff --git a/src/snapshot-tests/__fixtures__/cli/text/coverage/get-coverage-report--success.txt b/src/snapshot-tests/__fixtures__/cli/text/coverage/get-coverage-report--success.txt index 07df5e959..f063b0b6c 100644 --- a/src/snapshot-tests/__fixtures__/cli/text/coverage/get-coverage-report--success.txt +++ b/src/snapshot-tests/__fixtures__/cli/text/coverage/get-coverage-report--success.txt @@ -4,10 +4,10 @@ xcresult: /TestResults.xcresult Target Filter: CalculatorAppTests -ℹ️ Overall: 94.9% (371/391 lines) +ℹ️ Overall: 4.6% (18/391 lines) Targets - CalculatorAppTests.xctest: 94.9% (371/391 lines) + CalculatorAppTests.xctest: 4.6% (18/391 lines) Next steps: 1. View file-level coverage: xcodebuildmcp coverage get-file-coverage --xcresult-path /TestResults.xcresult diff --git a/src/snapshot-tests/__fixtures__/cli/text/coverage/get-file-coverage--success.txt b/src/snapshot-tests/__fixtures__/cli/text/coverage/get-file-coverage--success.txt index b6e55da8b..4628cb641 100644 --- a/src/snapshot-tests/__fixtures__/cli/text/coverage/get-file-coverage--success.txt +++ b/src/snapshot-tests/__fixtures__/cli/text/coverage/get-file-coverage--success.txt @@ -6,24 +6,37 @@ File: example_projects/iOS_Calculator/CalculatorAppPackage/Sources/CalculatorAppFeature/CalculatorService.swift -ℹ️ Coverage: 83.1% (157/189 lines) +ℹ️ Coverage: 55.0% (104/189 lines) -🔴 Not Covered (7 functions, 22 lines) +🔴 Not Covered (19 functions, 70 lines) L159 CalculatorService.deleteLastDigit() -- 0/16 lines + L63 CalculatorService.inputDecimal() -- 0/14 lines + L148 CalculatorService.clear() -- 0/10 lines + L134 CalculatorService.toggleSign() -- 0/6 lines + L141 CalculatorService.percentage() -- 0/6 lines + L178 CalculatorService.setError(_:) -- 0/5 lines L58 implicit closure #2 in CalculatorService.inputNumber(_:) -- 0/1 lines + L68 implicit closure #1 in CalculatorService.inputDecimal() -- 0/1 lines + L94 implicit closure #1 in CalculatorService.calculate() -- 0/1 lines L98 implicit closure #3 in CalculatorService.calculate() -- 0/1 lines L99 implicit closure #4 in CalculatorService.calculate() -- 0/1 lines L162 implicit closure #1 in CalculatorService.deleteLastDigit() -- 0/1 lines L172 implicit closure #2 in CalculatorService.deleteLastDigit() -- 0/1 lines + L209 implicit closure #3 in CalculatorService.formatNumber(_:) -- 0/1 lines L214 implicit closure #4 in CalculatorService.formatNumber(_:) -- 0/1 lines + L221 CalculatorService.currentValue.getter -- 0/1 lines + L222 CalculatorService.previousValue.getter -- 0/1 lines + L223 CalculatorService.currentOperation.getter -- 0/1 lines + L224 CalculatorService.willResetDisplay.getter -- 0/1 lines -🟡 Partial Coverage (4 functions) +🟡 Partial Coverage (5 functions) L184 CalculatorService.updateExpressionDisplay() -- 80.0% (8/10 lines) + L93 CalculatorService.calculate() -- 84.2% (32/38 lines) + L47 CalculatorService.inputNumber(_:) -- 85.7% (12/14 lines) + L78 CalculatorService.setOperation(_:) -- 85.7% (12/14 lines) L195 CalculatorService.formatNumber(_:) -- 85.7% (18/21 lines) - L93 CalculatorService.calculate() -- 89.5% (34/38 lines) - L63 CalculatorService.inputDecimal() -- 92.9% (13/14 lines) -🟢 Full Coverage (28 functions) -- all at 100% +🟢 Full Coverage (15 functions) -- all at 100% Next steps: 1. View overall coverage: xcodebuildmcp coverage get-coverage-report --xcresult-path /TestResults.xcresult diff --git a/src/snapshot-tests/__fixtures__/cli/text/debugging/lldb-command--success.txt b/src/snapshot-tests/__fixtures__/cli/text/debugging/lldb-command--success.txt index 94055091f..c796dafd6 100644 --- a/src/snapshot-tests/__fixtures__/cli/text/debugging/lldb-command--success.txt +++ b/src/snapshot-tests/__fixtures__/cli/text/debugging/lldb-command--success.txt @@ -6,9 +6,4 @@ ✅ Command executed Output: - Current breakpoints: - 1: file = 'ContentView.swift', line = 42, exact_match = 0, locations = - Names: - dap - - 1.1: where = CalculatorApp.debug.dylib`closure #1 in closure #1 in closure #1 in ContentView.body.getter + at ContentView.swift:42:31, address = , resolved, hit count = 0 + No breakpoints currently set. diff --git a/src/snapshot-tests/__fixtures__/cli/text/device/build--error-compiler.txt b/src/snapshot-tests/__fixtures__/cli/text/device/build--error-compiler.txt index 57f7369c3..be5bba0b6 100644 --- a/src/snapshot-tests/__fixtures__/cli/text/device/build--error-compiler.txt +++ b/src/snapshot-tests/__fixtures__/cli/text/device/build--error-compiler.txt @@ -3,14 +3,13 @@ Scheme: CalculatorApp Workspace: example_projects/iOS_Calculator/CalculatorApp.xcworkspace - Configuration: Debug Platform: iOS - Derived Data: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/CalculatorApp- + Derived Data: Compiler Errors (1): ✗ cannot convert value of type 'String' to specified type 'Int' - example_projects/iOS_Calculator/CalculatorApp/CalculatorApp.swift:33:42 + /example_projects/iOS_Calculator/CalculatorApp/CalculatorApp.swift:53:42 ❌ Build failed. (⏱️ ) └ Files: diff --git a/src/snapshot-tests/__fixtures__/cli/text/device/build--error-prepared-tests-wrong-scheme.txt b/src/snapshot-tests/__fixtures__/cli/text/device/build--error-prepared-tests-wrong-scheme.txt new file mode 100644 index 000000000..1eb285701 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/cli/text/device/build--error-prepared-tests-wrong-scheme.txt @@ -0,0 +1,15 @@ + +🔨 Build + + Scheme: NONEXISTENT + Workspace: example_projects/iOS_Calculator/CalculatorApp.xcworkspace + Platform: iOS + Derived Data: + +Errors (1): + + ✗ The workspace named "CalculatorApp" does not contain a scheme named "NONEXISTENT". The "-list" option can be used to find the names of the schemes in the workspace. + +❌ Build failed. (⏱️ ) + └ Files: + └ Build Logs: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/build_device__pid.log diff --git a/src/snapshot-tests/__fixtures__/cli/text/device/build--error-wrong-scheme.txt b/src/snapshot-tests/__fixtures__/cli/text/device/build--error-wrong-scheme.txt index b872bf0f5..1eb285701 100644 --- a/src/snapshot-tests/__fixtures__/cli/text/device/build--error-wrong-scheme.txt +++ b/src/snapshot-tests/__fixtures__/cli/text/device/build--error-wrong-scheme.txt @@ -3,9 +3,8 @@ Scheme: NONEXISTENT Workspace: example_projects/iOS_Calculator/CalculatorApp.xcworkspace - Configuration: Debug Platform: iOS - Derived Data: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/CalculatorApp- + Derived Data: Errors (1): diff --git a/src/snapshot-tests/__fixtures__/cli/text/device/build--success-prepared-tests-generic.txt b/src/snapshot-tests/__fixtures__/cli/text/device/build--success-prepared-tests-generic.txt new file mode 100644 index 000000000..07075b7ca --- /dev/null +++ b/src/snapshot-tests/__fixtures__/cli/text/device/build--success-prepared-tests-generic.txt @@ -0,0 +1,13 @@ + +🔨 Build + + Scheme: CalculatorApp + Workspace: example_projects/iOS_Calculator/CalculatorApp.xcworkspace + Platform: iOS + Derived Data: + +✅ Build succeeded. (⏱️ ) + └ Files: + ├ Build Logs: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/build_device__pid.log + ├ Test Products: /Generic CalculatorApp Tests.xctestproducts + └ XCTest Run: /Generic CalculatorApp Tests.xctestproducts/Tests/0/CalculatorApp.xctestrun diff --git a/src/snapshot-tests/__fixtures__/cli/text/device/build--success.txt b/src/snapshot-tests/__fixtures__/cli/text/device/build--success.txt index 17613ffd6..2175cbc9b 100644 --- a/src/snapshot-tests/__fixtures__/cli/text/device/build--success.txt +++ b/src/snapshot-tests/__fixtures__/cli/text/device/build--success.txt @@ -3,13 +3,12 @@ Scheme: CalculatorApp Workspace: example_projects/iOS_Calculator/CalculatorApp.xcworkspace - Configuration: Debug Platform: iOS - Derived Data: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/CalculatorApp- + Derived Data: ✅ Build succeeded. (⏱️ ) └ Files: └ Build Logs: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/build_device__pid.log Next steps: -1. Get built device app path: xcodebuildmcp device get-app-path --scheme CalculatorApp +1. Get built device app path: xcodebuildmcp device get-app-path --scheme CalculatorApp --derived-data-path diff --git a/src/snapshot-tests/__fixtures__/cli/text/device/build-and-run--error-compiler.txt b/src/snapshot-tests/__fixtures__/cli/text/device/build-and-run--error-compiler.txt index 044ddf736..b6699d2e9 100644 --- a/src/snapshot-tests/__fixtures__/cli/text/device/build-and-run--error-compiler.txt +++ b/src/snapshot-tests/__fixtures__/cli/text/device/build-and-run--error-compiler.txt @@ -3,15 +3,14 @@ Scheme: CalculatorApp Workspace: example_projects/iOS_Calculator/CalculatorApp.xcworkspace - Configuration: Debug Platform: iOS - Device: () - Derived Data: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/CalculatorApp- + Device: + Derived Data: Compiler Errors (1): ✗ cannot convert value of type 'String' to specified type 'Int' - example_projects/iOS_Calculator/CalculatorApp/CalculatorApp.swift:33:42 + /example_projects/iOS_Calculator/CalculatorApp/CalculatorApp.swift:53:42 ❌ Build failed. (⏱️ ) └ Files: diff --git a/src/snapshot-tests/__fixtures__/cli/text/device/build-and-run--error-wrong-scheme.txt b/src/snapshot-tests/__fixtures__/cli/text/device/build-and-run--error-wrong-scheme.txt index 66ad3c609..ded0e6345 100644 --- a/src/snapshot-tests/__fixtures__/cli/text/device/build-and-run--error-wrong-scheme.txt +++ b/src/snapshot-tests/__fixtures__/cli/text/device/build-and-run--error-wrong-scheme.txt @@ -3,10 +3,9 @@ Scheme: NONEXISTENT Workspace: example_projects/iOS_Calculator/CalculatorApp.xcworkspace - Configuration: Debug Platform: iOS - Device: () - Derived Data: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/CalculatorApp- + Device: + Derived Data: Errors (1): diff --git a/src/snapshot-tests/__fixtures__/cli/text/device/build-and-run--success.txt b/src/snapshot-tests/__fixtures__/cli/text/device/build-and-run--success.txt index 64e22805e..1bd7daa7d 100644 --- a/src/snapshot-tests/__fixtures__/cli/text/device/build-and-run--success.txt +++ b/src/snapshot-tests/__fixtures__/cli/text/device/build-and-run--success.txt @@ -3,10 +3,9 @@ Scheme: CalculatorApp Workspace: example_projects/iOS_Calculator/CalculatorApp.xcworkspace - Configuration: Debug Platform: iOS - Device: () - Derived Data: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/CalculatorApp- + Device: + Derived Data: ℹ️ Resolving app path ✅ Resolving app path @@ -19,7 +18,7 @@ ├ Bundle ID: io.sentry.calculatorapp ├ Process ID: └ Files: - ├ App Path: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/CalculatorApp-/Build/Products/Debug-iphoneos/CalculatorApp.app + ├ App Path: /Build/Products/Debug-iphoneos/CalculatorApp.app └ Build Logs: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/build_run_device__pid.log Next steps: diff --git a/src/snapshot-tests/__fixtures__/cli/text/device/get-app-path--error-wrong-scheme.txt b/src/snapshot-tests/__fixtures__/cli/text/device/get-app-path--error-wrong-scheme.txt index 6817789b7..0b2761ee1 100644 --- a/src/snapshot-tests/__fixtures__/cli/text/device/get-app-path--error-wrong-scheme.txt +++ b/src/snapshot-tests/__fixtures__/cli/text/device/get-app-path--error-wrong-scheme.txt @@ -3,7 +3,6 @@ Scheme: NONEXISTENT Workspace: example_projects/iOS_Calculator/CalculatorApp.xcworkspace - Configuration: Debug Platform: iOS Errors (1): diff --git a/src/snapshot-tests/__fixtures__/cli/text/device/get-app-path--success.txt b/src/snapshot-tests/__fixtures__/cli/text/device/get-app-path--success.txt index a6e0241c6..38ecc5634 100644 --- a/src/snapshot-tests/__fixtures__/cli/text/device/get-app-path--success.txt +++ b/src/snapshot-tests/__fixtures__/cli/text/device/get-app-path--success.txt @@ -3,14 +3,13 @@ Scheme: CalculatorApp Workspace: example_projects/iOS_Calculator/CalculatorApp.xcworkspace - Configuration: Debug Platform: iOS ✅ Success └ Files: - └ App Path: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/CalculatorApp-/Build/Products/Debug-iphoneos/CalculatorApp.app + └ App Path: /Build/Products/Debug-iphoneos/CalculatorApp.app Next steps: -1. Get bundle ID: xcodebuildmcp device get-app-bundle-id --app-path ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/CalculatorApp-/Build/Products/Debug-iphoneos/CalculatorApp.app -2. Install app on device: xcodebuildmcp device install --device-id DEVICE_UDID --app-path ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/CalculatorApp-/Build/Products/Debug-iphoneos/CalculatorApp.app +1. Get bundle ID: xcodebuildmcp device get-app-bundle-id --app-path /Build/Products/Debug-iphoneos/CalculatorApp.app +2. Install app on device: xcodebuildmcp device install --device-id DEVICE_UDID --app-path /Build/Products/Debug-iphoneos/CalculatorApp.app 3. Launch app on device: xcodebuildmcp device launch --device-id DEVICE_UDID --bundle-id BUNDLE_ID diff --git a/src/snapshot-tests/__fixtures__/cli/text/device/install--success.txt b/src/snapshot-tests/__fixtures__/cli/text/device/install--success.txt index 425f11a28..766a644b4 100644 --- a/src/snapshot-tests/__fixtures__/cli/text/device/install--success.txt +++ b/src/snapshot-tests/__fixtures__/cli/text/device/install--success.txt @@ -1,7 +1,7 @@ 📦 Install App - Device: () - App: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/CalculatorApp-/Build/Products/Debug-iphoneos/CalculatorApp.app + Device: + App: /Build/Products/Debug-iphoneos/CalculatorApp.app ✅ App installed successfully. diff --git a/src/snapshot-tests/__fixtures__/cli/text/device/launch--success.txt b/src/snapshot-tests/__fixtures__/cli/text/device/launch--success.txt index f1366bc66..6cef69335 100644 --- a/src/snapshot-tests/__fixtures__/cli/text/device/launch--success.txt +++ b/src/snapshot-tests/__fixtures__/cli/text/device/launch--success.txt @@ -1,7 +1,7 @@ 🚀 Launch App - Device: () + Device: Bundle ID: io.sentry.calculatorapp ✅ App launched successfully. diff --git a/src/snapshot-tests/__fixtures__/cli/text/device/stop--success.txt b/src/snapshot-tests/__fixtures__/cli/text/device/stop--success.txt index 2fa577551..9703b2e57 100644 --- a/src/snapshot-tests/__fixtures__/cli/text/device/stop--success.txt +++ b/src/snapshot-tests/__fixtures__/cli/text/device/stop--success.txt @@ -1,7 +1,7 @@ 🛑 Stop App - Device: () + Device: PID: ✅ App stopped successfully diff --git a/src/snapshot-tests/__fixtures__/cli/text/device/test--error-compiler.txt b/src/snapshot-tests/__fixtures__/cli/text/device/test--error-compiler.txt index ddbc13588..08b6c58c2 100644 --- a/src/snapshot-tests/__fixtures__/cli/text/device/test--error-compiler.txt +++ b/src/snapshot-tests/__fixtures__/cli/text/device/test--error-compiler.txt @@ -3,10 +3,9 @@ Scheme: CalculatorApp Workspace: example_projects/iOS_Calculator/CalculatorApp.xcworkspace - Configuration: Debug Platform: iOS - Device: () - Derived Data: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/CalculatorApp- + Device: + Derived Data: Selective Testing: CalculatorAppTests/CalculatorAppTests/testAddition @@ -16,9 +15,8 @@ Discovered 1 test(s): Compiler Errors (1): ✗ cannot convert value of type 'String' to specified type 'Int' - example_projects/iOS_Calculator/CalculatorApp/CalculatorApp.swift:33:42 + /example_projects/iOS_Calculator/CalculatorApp/CalculatorApp.swift:53:42 ❌ Test failed. (⏱️ ) └ Files: - ├ Result Bundle: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/result-bundles/test_device__pid.xcresult └ Build Logs: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/test_device__pid.log diff --git a/src/snapshot-tests/__fixtures__/cli/text/device/test--failure.txt b/src/snapshot-tests/__fixtures__/cli/text/device/test--failure.txt index cbd595b3f..443c5ab8e 100644 --- a/src/snapshot-tests/__fixtures__/cli/text/device/test--failure.txt +++ b/src/snapshot-tests/__fixtures__/cli/text/device/test--failure.txt @@ -3,10 +3,9 @@ Scheme: CalculatorApp Workspace: example_projects/iOS_Calculator/CalculatorApp.xcworkspace - Configuration: Debug Platform: iOS - Device: () - Derived Data: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/CalculatorApp- + Device: + Derived Data: Discovered 57 test(s): CalculatorAppFeatureTests/CalculatorBasicTests/testClear @@ -43,14 +42,13 @@ Running tests (23 completed, 2 failures, 0 skipped) CalculatorAppTests ✗ testCalculatorServiceFailure: - XCTAssertEqual failed: ("0") is not equal to ("999") - This test should fail - display should be 0, not 999 - example_projects/iOS_Calculator/CalculatorAppTests/CalculatorAppTests.swift:52 - + /example_projects/iOS_Calculator/CalculatorAppTests/CalculatorAppTests.swift:52 IntentionalFailureTests ✗ test: - XCTAssertTrue failed - This test should fail to verify error reporting - example_projects/iOS_Calculator/CalculatorAppTests/CalculatorAppTests.swift:286 - + /example_projects/iOS_Calculator/CalculatorAppTests/CalculatorAppTests.swift:286 ❌ tests failed, passed, skipped (⏱️ ) └ Files: ├ Result Bundle: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/result-bundles/test_device__pid.xcresult - └ Build Logs: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/test_device__pid.log + ├ Build Logs: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/test_device__pid.log + └ Test Products: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/test-products/test_device__pid.xctestproducts diff --git a/src/snapshot-tests/__fixtures__/cli/text/device/test--success.txt b/src/snapshot-tests/__fixtures__/cli/text/device/test--success.txt index ae68d6925..7b949b3cc 100644 --- a/src/snapshot-tests/__fixtures__/cli/text/device/test--success.txt +++ b/src/snapshot-tests/__fixtures__/cli/text/device/test--success.txt @@ -3,10 +3,9 @@ Scheme: CalculatorApp Workspace: example_projects/iOS_Calculator/CalculatorApp.xcworkspace - Configuration: Debug Platform: iOS - Device: () - Derived Data: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/CalculatorApp- + Device: + Derived Data: Selective Testing: CalculatorAppTests/CalculatorAppTests/testAddition @@ -17,4 +16,5 @@ Running tests (1 completed, 0 failures, 0 skipped) ✅ 1 test passed, 0 failed, 0 skipped (⏱️ ) └ Files: ├ Result Bundle: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/result-bundles/test_device__pid.xcresult - └ Build Logs: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/test_device__pid.log + ├ Build Logs: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/test_device__pid.log + └ Test Products: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/test-products/test_device__pid.xctestproducts diff --git a/src/snapshot-tests/__fixtures__/cli/text/macos/build--error-compiler.txt b/src/snapshot-tests/__fixtures__/cli/text/macos/build--error-compiler.txt index 8c97dd1d8..704b60362 100644 --- a/src/snapshot-tests/__fixtures__/cli/text/macos/build--error-compiler.txt +++ b/src/snapshot-tests/__fixtures__/cli/text/macos/build--error-compiler.txt @@ -3,14 +3,13 @@ Scheme: MCPTest Project: example_projects/macOS/MCPTest.xcodeproj - Configuration: Debug Platform: macOS - Derived Data: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/MCPTest- + Derived Data: /DerivedData Compiler Errors (1): ✗ cannot convert value of type 'String' to specified type 'Int' - example_projects/macOS/MCPTest/MCPTestApp.swift:20:42 + /example_projects/macOS/MCPTest/MCPTestApp.swift:20:42 ❌ Build failed. (⏱️ ) └ Files: diff --git a/src/snapshot-tests/__fixtures__/cli/text/macos/build--error-prepared-tests-wrong-scheme.txt b/src/snapshot-tests/__fixtures__/cli/text/macos/build--error-prepared-tests-wrong-scheme.txt new file mode 100644 index 000000000..adcd3c00a --- /dev/null +++ b/src/snapshot-tests/__fixtures__/cli/text/macos/build--error-prepared-tests-wrong-scheme.txt @@ -0,0 +1,15 @@ + +🔨 Build + + Scheme: NONEXISTENT + Project: example_projects/macOS/MCPTest.xcodeproj + Platform: macOS + Derived Data: /DerivedData + +Errors (1): + + ✗ The project named "MCPTest" does not contain a scheme named "NONEXISTENT". The "-list" option can be used to find the names of the schemes in the project. + +❌ Build failed. (⏱️ ) + └ Files: + └ Build Logs: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/build_macos__pid.log diff --git a/src/snapshot-tests/__fixtures__/cli/text/macos/build--error-wrong-scheme.txt b/src/snapshot-tests/__fixtures__/cli/text/macos/build--error-wrong-scheme.txt index 0d7ef994c..adcd3c00a 100644 --- a/src/snapshot-tests/__fixtures__/cli/text/macos/build--error-wrong-scheme.txt +++ b/src/snapshot-tests/__fixtures__/cli/text/macos/build--error-wrong-scheme.txt @@ -3,9 +3,8 @@ Scheme: NONEXISTENT Project: example_projects/macOS/MCPTest.xcodeproj - Configuration: Debug Platform: macOS - Derived Data: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/MCPTest- + Derived Data: /DerivedData Errors (1): diff --git a/src/snapshot-tests/__fixtures__/cli/text/macos/build--success-prepared-tests.txt b/src/snapshot-tests/__fixtures__/cli/text/macos/build--success-prepared-tests.txt new file mode 100644 index 000000000..64a5f1d08 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/cli/text/macos/build--success-prepared-tests.txt @@ -0,0 +1,16 @@ + +🔨 Build + + Scheme: MCPTest + Project: example_projects/macOS/MCPTest.xcodeproj + Platform: macOS + Derived Data: /DerivedData + +✅ Build succeeded. (⏱️ ) + └ Files: + ├ Build Logs: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/build_macos__pid.log + ├ Test Products: /MCPTest Tests.xctestproducts + └ XCTest Run: /MCPTest Tests.xctestproducts/Tests/0/MCPTest.xctestrun + +Next steps: +1. Run prepared tests: xcodebuildmcp macos test --test-products-path '/MCPTest Tests.xctestproducts' diff --git a/src/snapshot-tests/__fixtures__/cli/text/macos/build--success.txt b/src/snapshot-tests/__fixtures__/cli/text/macos/build--success.txt index 3d74d0c2c..d835522ed 100644 --- a/src/snapshot-tests/__fixtures__/cli/text/macos/build--success.txt +++ b/src/snapshot-tests/__fixtures__/cli/text/macos/build--success.txt @@ -3,9 +3,8 @@ Scheme: MCPTest Project: example_projects/macOS/MCPTest.xcodeproj - Configuration: Debug Platform: macOS - Derived Data: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/MCPTest- + Derived Data: /DerivedData ✅ Build succeeded. (⏱️ ) ├ Bundle ID: io.sentry.MCPTest.macOS @@ -13,4 +12,4 @@ └ Build Logs: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/build_macos__pid.log Next steps: -1. Get built macOS app path: xcodebuildmcp macos get-app-path --scheme MCPTest +1. Get built macOS app path: xcodebuildmcp macos get-app-path --scheme MCPTest --derived-data-path /DerivedData diff --git a/src/snapshot-tests/__fixtures__/cli/text/macos/build-and-run--error-compiler.txt b/src/snapshot-tests/__fixtures__/cli/text/macos/build-and-run--error-compiler.txt index 6c40977a8..2b11f7acb 100644 --- a/src/snapshot-tests/__fixtures__/cli/text/macos/build-and-run--error-compiler.txt +++ b/src/snapshot-tests/__fixtures__/cli/text/macos/build-and-run--error-compiler.txt @@ -3,14 +3,13 @@ Scheme: MCPTest Project: example_projects/macOS/MCPTest.xcodeproj - Configuration: Debug Platform: macOS - Derived Data: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/MCPTest- + Derived Data: /DerivedData Compiler Errors (1): ✗ cannot convert value of type 'String' to specified type 'Int' - example_projects/macOS/MCPTest/MCPTestApp.swift:20:42 + /example_projects/macOS/MCPTest/MCPTestApp.swift:20:42 ❌ Build failed. (⏱️ ) └ Files: diff --git a/src/snapshot-tests/__fixtures__/cli/text/macos/build-and-run--error-wrong-scheme.txt b/src/snapshot-tests/__fixtures__/cli/text/macos/build-and-run--error-wrong-scheme.txt index 711ff7b31..7450d4a9d 100644 --- a/src/snapshot-tests/__fixtures__/cli/text/macos/build-and-run--error-wrong-scheme.txt +++ b/src/snapshot-tests/__fixtures__/cli/text/macos/build-and-run--error-wrong-scheme.txt @@ -3,9 +3,8 @@ Scheme: NONEXISTENT Project: example_projects/macOS/MCPTest.xcodeproj - Configuration: Debug Platform: macOS - Derived Data: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/MCPTest- + Derived Data: /DerivedData Errors (1): diff --git a/src/snapshot-tests/__fixtures__/cli/text/macos/build-and-run--success.txt b/src/snapshot-tests/__fixtures__/cli/text/macos/build-and-run--success.txt index dc91eca6e..13996a4ab 100644 --- a/src/snapshot-tests/__fixtures__/cli/text/macos/build-and-run--success.txt +++ b/src/snapshot-tests/__fixtures__/cli/text/macos/build-and-run--success.txt @@ -3,9 +3,8 @@ Scheme: MCPTest Project: example_projects/macOS/MCPTest.xcodeproj - Configuration: Debug Platform: macOS - Derived Data: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/MCPTest- + Derived Data: /DerivedData ℹ️ Resolving app path ✅ Resolving app path @@ -17,7 +16,7 @@ ├ Bundle ID: io.sentry.MCPTest.macOS ├ Process ID: └ Files: - ├ App Path: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/MCPTest-/Build/Products/Debug/MCPTest.app + ├ App Path: /DerivedData/Build/Products/Debug/MCPTest.app └ Build Logs: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/build_run_macos__pid.log Next steps: diff --git a/src/snapshot-tests/__fixtures__/cli/text/macos/get-app-path--error-wrong-scheme.txt b/src/snapshot-tests/__fixtures__/cli/text/macos/get-app-path--error-wrong-scheme.txt index 0661e1121..5129bab95 100644 --- a/src/snapshot-tests/__fixtures__/cli/text/macos/get-app-path--error-wrong-scheme.txt +++ b/src/snapshot-tests/__fixtures__/cli/text/macos/get-app-path--error-wrong-scheme.txt @@ -3,7 +3,6 @@ Scheme: NONEXISTENT Project: example_projects/macOS/MCPTest.xcodeproj - Configuration: Debug Platform: macOS Errors (1): diff --git a/src/snapshot-tests/__fixtures__/cli/text/macos/get-app-path--success.txt b/src/snapshot-tests/__fixtures__/cli/text/macos/get-app-path--success.txt index 8a94fdea4..41106695b 100644 --- a/src/snapshot-tests/__fixtures__/cli/text/macos/get-app-path--success.txt +++ b/src/snapshot-tests/__fixtures__/cli/text/macos/get-app-path--success.txt @@ -3,13 +3,12 @@ Scheme: MCPTest Project: example_projects/macOS/MCPTest.xcodeproj - Configuration: Debug Platform: macOS ✅ Success └ Files: - └ App Path: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/MCPTest-/Build/Products/Debug/MCPTest.app + └ App Path: /DerivedData/Build/Products/Debug/MCPTest.app Next steps: -1. Get bundle ID: xcodebuildmcp macos get-macos-bundle-id --app-path ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/MCPTest-/Build/Products/Debug/MCPTest.app -2. Launch app: xcodebuildmcp macos launch --app-path ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/MCPTest-/Build/Products/Debug/MCPTest.app +1. Get bundle ID: xcodebuildmcp macos get-macos-bundle-id --app-path /DerivedData/Build/Products/Debug/MCPTest.app +2. Launch app: xcodebuildmcp macos launch --app-path /DerivedData/Build/Products/Debug/MCPTest.app diff --git a/src/snapshot-tests/__fixtures__/cli/text/macos/get-macos-bundle-id--error-missing-app.txt b/src/snapshot-tests/__fixtures__/cli/text/macos/get-macos-bundle-id--error-missing-app.txt index a7d25d46a..f17496c53 100644 --- a/src/snapshot-tests/__fixtures__/cli/text/macos/get-macos-bundle-id--error-missing-app.txt +++ b/src/snapshot-tests/__fixtures__/cli/text/macos/get-macos-bundle-id--error-missing-app.txt @@ -1,10 +1,10 @@ 🔍 Get macOS Bundle ID - App: /nonexistent/path/Fake.app + App: /missing.app Errors (1): - ✗ File not found: '/nonexistent/path/Fake.app'. Please check the path and try again. + ✗ File not found: '/missing.app'. Please check the path and try again. ❌ Failed to get macOS bundle ID. diff --git a/src/snapshot-tests/__fixtures__/cli/text/macos/launch--success.txt b/src/snapshot-tests/__fixtures__/cli/text/macos/launch--success.txt index 2362ab71b..517f10528 100644 --- a/src/snapshot-tests/__fixtures__/cli/text/macos/launch--success.txt +++ b/src/snapshot-tests/__fixtures__/cli/text/macos/launch--success.txt @@ -1,7 +1,7 @@ 🚀 Launch macOS App - App: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/MCPTest-/Build/Products/Debug/MCPTest.app + App: /DerivedData/Build/Products/Debug/MCPTest.app ✅ App launched successfully ├ Bundle ID: io.sentry.MCPTest.macOS diff --git a/src/snapshot-tests/__fixtures__/cli/text/macos/stop--success.txt b/src/snapshot-tests/__fixtures__/cli/text/macos/stop--success.txt index dffe5e4ef..5bcead8c8 100644 --- a/src/snapshot-tests/__fixtures__/cli/text/macos/stop--success.txt +++ b/src/snapshot-tests/__fixtures__/cli/text/macos/stop--success.txt @@ -1,6 +1,6 @@ 🛑 Stop macOS App - App: MCPTest + App: PID ✅ App stopped successfully diff --git a/src/snapshot-tests/__fixtures__/cli/text/macos/test--error-compiler.txt b/src/snapshot-tests/__fixtures__/cli/text/macos/test--error-compiler.txt index 0a357bdbe..1abcfaf24 100644 --- a/src/snapshot-tests/__fixtures__/cli/text/macos/test--error-compiler.txt +++ b/src/snapshot-tests/__fixtures__/cli/text/macos/test--error-compiler.txt @@ -3,9 +3,8 @@ Scheme: MCPTest Project: example_projects/macOS/MCPTest.xcodeproj - Configuration: Debug Platform: macOS - Derived Data: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/MCPTest- + Derived Data: /DerivedData Selective Testing: MCPTestTests/MCPTestTests/appNameIsCorrect() MCPTestTests/MCPTestsXCTests/testAppNameIsCorrect @@ -17,9 +16,8 @@ Discovered 2 test(s): Compiler Errors (1): ✗ cannot convert value of type 'String' to specified type 'Int' - example_projects/macOS/MCPTest/MCPTestApp.swift:20:42 + /example_projects/macOS/MCPTest/MCPTestApp.swift:20:42 ❌ Test failed. (⏱️ ) └ Files: - ├ Result Bundle: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/result-bundles/test_macos__pid.xcresult └ Build Logs: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/test_macos__pid.log diff --git a/src/snapshot-tests/__fixtures__/cli/text/macos/test--error-wrong-scheme.txt b/src/snapshot-tests/__fixtures__/cli/text/macos/test--error-wrong-scheme.txt index 795e8d84e..0eacd90a1 100644 --- a/src/snapshot-tests/__fixtures__/cli/text/macos/test--error-wrong-scheme.txt +++ b/src/snapshot-tests/__fixtures__/cli/text/macos/test--error-wrong-scheme.txt @@ -3,9 +3,8 @@ Scheme: NONEXISTENT Project: example_projects/macOS/MCPTest.xcodeproj - Configuration: Debug Platform: macOS - Derived Data: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/MCPTest- + Derived Data: /DerivedData Errors (1): @@ -13,5 +12,4 @@ Errors (1): ❌ Test failed. (⏱️ ) └ Files: - ├ Result Bundle: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/result-bundles/test_macos__pid.xcresult └ Build Logs: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/test_macos__pid.log diff --git a/src/snapshot-tests/__fixtures__/cli/text/macos/test--failure.txt b/src/snapshot-tests/__fixtures__/cli/text/macos/test--failure.txt index 2a220f7aa..1abbe25f0 100644 --- a/src/snapshot-tests/__fixtures__/cli/text/macos/test--failure.txt +++ b/src/snapshot-tests/__fixtures__/cli/text/macos/test--failure.txt @@ -3,9 +3,8 @@ Scheme: MCPTest Project: example_projects/macOS/MCPTest.xcodeproj - Configuration: Debug Platform: macOS - Derived Data: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/MCPTest- + Derived Data: /DerivedData Discovered 4 test(s): MCPTestTests/MCPTestTests/appNameIsCorrect @@ -17,17 +16,18 @@ Running tests (2 completed, 1 failure, 0 skipped) Running tests (3 completed, 1 failure, 0 skipped) Running tests (4 completed, 2 failures, 0 skipped) -MCPTestsXCTests - ✗ testDeliberateFailure(): - - XCTAssertTrue failed - This test is designed to fail for snapshot testing - example_projects/macOS/MCPTestTests/MCPTestsXCTests.swift:11 - MCPTestTests ✗ deliberateFailure(): - Expectation failed: 1 == 2: This test is designed to fail for snapshot testing example_projects/macOS/MCPTestTests/MCPTestTests.swift:11 +MCPTestsXCTests + ✗ testDeliberateFailure(): + - XCTAssertTrue failed - This test is designed to fail for snapshot testing + example_projects/macOS/MCPTestTests/MCPTestsXCTests.swift:11 + ❌ tests failed, passed, skipped (⏱️ ) └ Files: ├ Result Bundle: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/result-bundles/test_macos__pid.xcresult - └ Build Logs: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/test_macos__pid.log + ├ Build Logs: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/test_macos__pid.log + └ Test Products: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/test-products/test_macos__pid.xctestproducts diff --git a/src/snapshot-tests/__fixtures__/cli/text/macos/test--success.txt b/src/snapshot-tests/__fixtures__/cli/text/macos/test--success.txt index cb54b9b7a..13c24efba 100644 --- a/src/snapshot-tests/__fixtures__/cli/text/macos/test--success.txt +++ b/src/snapshot-tests/__fixtures__/cli/text/macos/test--success.txt @@ -3,9 +3,8 @@ Scheme: MCPTest Project: example_projects/macOS/MCPTest.xcodeproj - Configuration: Debug Platform: macOS - Derived Data: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/MCPTest- + Derived Data: /DerivedData Selective Testing: MCPTestTests/MCPTestTests/appNameIsCorrect() MCPTestTests/MCPTestsXCTests/testAppNameIsCorrect @@ -19,4 +18,5 @@ Running tests (2 completed, 0 failures, 0 skipped) ✅ 2 tests passed, 0 failed, 0 skipped (⏱️ ) └ Files: ├ Result Bundle: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/result-bundles/test_macos__pid.xcresult - └ Build Logs: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/test_macos__pid.log + ├ Build Logs: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/test_macos__pid.log + └ Test Products: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/test-products/test_macos__pid.xctestproducts diff --git a/src/snapshot-tests/__fixtures__/cli/text/simulator/build--error-compiler.txt b/src/snapshot-tests/__fixtures__/cli/text/simulator/build--error-compiler.txt index bc83057bf..0b1919cf7 100644 --- a/src/snapshot-tests/__fixtures__/cli/text/simulator/build--error-compiler.txt +++ b/src/snapshot-tests/__fixtures__/cli/text/simulator/build--error-compiler.txt @@ -3,15 +3,14 @@ Scheme: CalculatorApp Workspace: example_projects/iOS_Calculator/CalculatorApp.xcworkspace - Configuration: Debug Platform: iOS Simulator - Simulator: iPhone 17 - Derived Data: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/CalculatorApp- + Simulator: + Derived Data: Compiler Errors (1): ✗ cannot convert value of type 'String' to specified type 'Int' - example_projects/iOS_Calculator/CalculatorApp/CalculatorApp.swift:33:42 + /example_projects/iOS_Calculator/CalculatorApp/CalculatorApp.swift:53:42 ❌ Build failed. (⏱️ ) └ Files: diff --git a/src/snapshot-tests/__fixtures__/cli/text/simulator/build--error-prepared-tests-wrong-scheme.txt b/src/snapshot-tests/__fixtures__/cli/text/simulator/build--error-prepared-tests-wrong-scheme.txt new file mode 100644 index 000000000..10c52a6fb --- /dev/null +++ b/src/snapshot-tests/__fixtures__/cli/text/simulator/build--error-prepared-tests-wrong-scheme.txt @@ -0,0 +1,16 @@ + +🔨 Build + + Scheme: NONEXISTENT + Workspace: example_projects/iOS_Calculator/CalculatorApp.xcworkspace + Platform: iOS Simulator + Simulator: + Derived Data: + +Errors (1): + + ✗ The workspace named "CalculatorApp" does not contain a scheme named "NONEXISTENT". The "-list" option can be used to find the names of the schemes in the workspace. + +❌ Build failed. (⏱️ ) + └ Files: + └ Build Logs: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/build_sim__pid.log diff --git a/src/snapshot-tests/__fixtures__/cli/text/simulator/build--error-wrong-scheme.txt b/src/snapshot-tests/__fixtures__/cli/text/simulator/build--error-wrong-scheme.txt index dae2a4233..10c52a6fb 100644 --- a/src/snapshot-tests/__fixtures__/cli/text/simulator/build--error-wrong-scheme.txt +++ b/src/snapshot-tests/__fixtures__/cli/text/simulator/build--error-wrong-scheme.txt @@ -3,10 +3,9 @@ Scheme: NONEXISTENT Workspace: example_projects/iOS_Calculator/CalculatorApp.xcworkspace - Configuration: Debug Platform: iOS Simulator - Simulator: iPhone 17 - Derived Data: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/CalculatorApp- + Simulator: + Derived Data: Errors (1): diff --git a/src/snapshot-tests/__fixtures__/cli/text/simulator/build--success-prepared-tests.txt b/src/snapshot-tests/__fixtures__/cli/text/simulator/build--success-prepared-tests.txt new file mode 100644 index 000000000..dbbeffd80 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/cli/text/simulator/build--success-prepared-tests.txt @@ -0,0 +1,17 @@ + +🔨 Build + + Scheme: CalculatorApp + Workspace: example_projects/iOS_Calculator/CalculatorApp.xcworkspace + Platform: iOS Simulator + Simulator: + Derived Data: + +✅ Build succeeded. (⏱️ ) + └ Files: + ├ Build Logs: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/build_sim__pid.log + ├ Test Products: /CalculatorApp Tests.xctestproducts + └ XCTest Run: /CalculatorApp Tests.xctestproducts/Tests/0/CalculatorApp.xctestrun + +Next steps: +1. Run prepared tests: xcodebuildmcp simulator test --test-products-path '/CalculatorApp Tests.xctestproducts' --simulator-id diff --git a/src/snapshot-tests/__fixtures__/cli/text/simulator/build--success.txt b/src/snapshot-tests/__fixtures__/cli/text/simulator/build--success.txt index 7395ba47e..5189bc531 100644 --- a/src/snapshot-tests/__fixtures__/cli/text/simulator/build--success.txt +++ b/src/snapshot-tests/__fixtures__/cli/text/simulator/build--success.txt @@ -3,14 +3,13 @@ Scheme: CalculatorApp Workspace: example_projects/iOS_Calculator/CalculatorApp.xcworkspace - Configuration: Debug Platform: iOS Simulator - Simulator: iPhone 17 - Derived Data: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/CalculatorApp- + Simulator: + Derived Data: ✅ Build succeeded. (⏱️ ) └ Files: └ Build Logs: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/build_sim__pid.log Next steps: -1. Get built app path in simulator derived data: xcodebuildmcp simulator get-app-path --simulator-name 'iPhone 17' --scheme CalculatorApp --platform 'iOS Simulator' +1. Get built app path in simulator derived data: xcodebuildmcp simulator get-app-path --simulator-id --scheme CalculatorApp --platform 'iOS Simulator' --derived-data-path diff --git a/src/snapshot-tests/__fixtures__/cli/text/simulator/build-and-run--error-compiler.txt b/src/snapshot-tests/__fixtures__/cli/text/simulator/build-and-run--error-compiler.txt index 0d23f830c..73361f216 100644 --- a/src/snapshot-tests/__fixtures__/cli/text/simulator/build-and-run--error-compiler.txt +++ b/src/snapshot-tests/__fixtures__/cli/text/simulator/build-and-run--error-compiler.txt @@ -3,15 +3,14 @@ Scheme: CalculatorApp Workspace: example_projects/iOS_Calculator/CalculatorApp.xcworkspace - Configuration: Debug Platform: iOS Simulator - Simulator: iPhone 17 - Derived Data: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/CalculatorApp- + Simulator: + Derived Data: Compiler Errors (1): ✗ cannot convert value of type 'String' to specified type 'Int' - example_projects/iOS_Calculator/CalculatorApp/CalculatorApp.swift:33:42 + /example_projects/iOS_Calculator/CalculatorApp/CalculatorApp.swift:53:42 ❌ Build failed. (⏱️ ) └ Files: diff --git a/src/snapshot-tests/__fixtures__/cli/text/simulator/build-and-run--error-wrong-scheme.txt b/src/snapshot-tests/__fixtures__/cli/text/simulator/build-and-run--error-wrong-scheme.txt index 21075fecf..9a1c6aa78 100644 --- a/src/snapshot-tests/__fixtures__/cli/text/simulator/build-and-run--error-wrong-scheme.txt +++ b/src/snapshot-tests/__fixtures__/cli/text/simulator/build-and-run--error-wrong-scheme.txt @@ -3,10 +3,9 @@ Scheme: NONEXISTENT Workspace: example_projects/iOS_Calculator/CalculatorApp.xcworkspace - Configuration: Debug Platform: iOS Simulator - Simulator: iPhone 17 - Derived Data: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/CalculatorApp- + Simulator: + Derived Data: Errors (1): diff --git a/src/snapshot-tests/__fixtures__/cli/text/simulator/build-and-run--success.txt b/src/snapshot-tests/__fixtures__/cli/text/simulator/build-and-run--success.txt index 97674114a..e70a09b4f 100644 --- a/src/snapshot-tests/__fixtures__/cli/text/simulator/build-and-run--success.txt +++ b/src/snapshot-tests/__fixtures__/cli/text/simulator/build-and-run--success.txt @@ -3,10 +3,9 @@ Scheme: CalculatorApp Workspace: example_projects/iOS_Calculator/CalculatorApp.xcworkspace - Configuration: Debug Platform: iOS Simulator - Simulator: iPhone 17 - Derived Data: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/CalculatorApp- + Simulator: + Derived Data: ℹ️ Resolving app path ✅ Resolving app path @@ -21,7 +20,7 @@ ├ Bundle ID: io.sentry.calculatorapp ├ Process ID: └ Files: - ├ App Path: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/CalculatorApp-/Build/Products/Debug-iphonesimulator/CalculatorApp.app + ├ App Path: /Build/Products/Debug-iphonesimulator/CalculatorApp.app ├ Build Logs: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/build_run_sim__pid.log ├ Runtime Logs: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/io.sentry.calculatorapp__pid.log └ OSLog: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/io.sentry.calculatorapp_oslog__pid.log diff --git a/src/snapshot-tests/__fixtures__/cli/text/simulator/get-app-path--error-wrong-scheme.txt b/src/snapshot-tests/__fixtures__/cli/text/simulator/get-app-path--error-wrong-scheme.txt index 4ccab0533..070beb687 100644 --- a/src/snapshot-tests/__fixtures__/cli/text/simulator/get-app-path--error-wrong-scheme.txt +++ b/src/snapshot-tests/__fixtures__/cli/text/simulator/get-app-path--error-wrong-scheme.txt @@ -3,9 +3,8 @@ Scheme: NONEXISTENT Workspace: example_projects/iOS_Calculator/CalculatorApp.xcworkspace - Configuration: Debug Platform: iOS Simulator - Simulator: iPhone 17 + Simulator: Errors (1): diff --git a/src/snapshot-tests/__fixtures__/cli/text/simulator/get-app-path--success.txt b/src/snapshot-tests/__fixtures__/cli/text/simulator/get-app-path--success.txt index 7b3473270..74b3c832d 100644 --- a/src/snapshot-tests/__fixtures__/cli/text/simulator/get-app-path--success.txt +++ b/src/snapshot-tests/__fixtures__/cli/text/simulator/get-app-path--success.txt @@ -3,16 +3,15 @@ Scheme: CalculatorApp Workspace: example_projects/iOS_Calculator/CalculatorApp.xcworkspace - Configuration: Debug Platform: iOS Simulator - Simulator: iPhone 17 + Simulator: ✅ Get app path successful (⏱️ ) └ Files: - └ App Path: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/CalculatorApp-/Build/Products/Debug-iphonesimulator/CalculatorApp.app + └ App Path: /Build/Products/Debug-iphonesimulator/CalculatorApp.app Next steps: -1. Get bundle ID: xcodebuildmcp simulator get-app-bundle-id --app-path ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/CalculatorApp-/Build/Products/Debug-iphonesimulator/CalculatorApp.app +1. Get bundle ID: xcodebuildmcp simulator get-app-bundle-id --app-path /Build/Products/Debug-iphonesimulator/CalculatorApp.app 2. Boot simulator: xcodebuildmcp simulator boot --simulator-id SIMULATOR_UUID -3. Install app: xcodebuildmcp simulator install --simulator-id SIMULATOR_UUID --app-path ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/CalculatorApp-/Build/Products/Debug-iphonesimulator/CalculatorApp.app +3. Install app: xcodebuildmcp simulator install --simulator-id SIMULATOR_UUID --app-path /Build/Products/Debug-iphonesimulator/CalculatorApp.app 4. Launch app: xcodebuildmcp simulator launch-app --simulator-id SIMULATOR_UUID --bundle-id BUNDLE_ID diff --git a/src/snapshot-tests/__fixtures__/cli/text/simulator/install--success.txt b/src/snapshot-tests/__fixtures__/cli/text/simulator/install--success.txt index 12875e64a..62d72e37d 100644 --- a/src/snapshot-tests/__fixtures__/cli/text/simulator/install--success.txt +++ b/src/snapshot-tests/__fixtures__/cli/text/simulator/install--success.txt @@ -2,7 +2,7 @@ 📦 Install App Simulator: - App Path: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/CalculatorApp-/Build/Products/Debug-iphonesimulator/CalculatorApp.app + App Path: /Build/Products/Debug-iphonesimulator/CalculatorApp.app ✅ App installed successfully diff --git a/src/snapshot-tests/__fixtures__/cli/text/simulator/test--error-compiler.txt b/src/snapshot-tests/__fixtures__/cli/text/simulator/test--error-compiler.txt index bad0431de..2ba2412cc 100644 --- a/src/snapshot-tests/__fixtures__/cli/text/simulator/test--error-compiler.txt +++ b/src/snapshot-tests/__fixtures__/cli/text/simulator/test--error-compiler.txt @@ -3,10 +3,9 @@ Scheme: CalculatorApp Workspace: example_projects/iOS_Calculator/CalculatorApp.xcworkspace - Configuration: Debug Platform: iOS Simulator - Simulator: iPhone 17 - Derived Data: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/CalculatorApp- + Simulator: + Derived Data: Selective Testing: CalculatorAppTests/CalculatorAppTests/testAddition @@ -16,7 +15,7 @@ Discovered 1 test(s): Compiler Errors (1): ✗ cannot convert value of type 'String' to specified type 'Int' - example_projects/iOS_Calculator/CalculatorApp/CalculatorApp.swift:33:42 + /example_projects/iOS_Calculator/CalculatorApp/CalculatorApp.swift:53:42 ❌ Test failed. (⏱️ ) └ Files: diff --git a/src/snapshot-tests/__fixtures__/cli/text/simulator/test--error-wrong-scheme.txt b/src/snapshot-tests/__fixtures__/cli/text/simulator/test--error-wrong-scheme.txt index 85568ca7f..5a994022d 100644 --- a/src/snapshot-tests/__fixtures__/cli/text/simulator/test--error-wrong-scheme.txt +++ b/src/snapshot-tests/__fixtures__/cli/text/simulator/test--error-wrong-scheme.txt @@ -3,10 +3,9 @@ Scheme: NONEXISTENT Workspace: example_projects/iOS_Calculator/CalculatorApp.xcworkspace - Configuration: Debug Platform: iOS Simulator - Simulator: iPhone 17 - Derived Data: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/CalculatorApp- + Simulator: + Derived Data: Errors (1): diff --git a/src/snapshot-tests/__fixtures__/cli/text/simulator/test--failure.txt b/src/snapshot-tests/__fixtures__/cli/text/simulator/test--failure.txt index 6500f0f76..a9028046a 100644 --- a/src/snapshot-tests/__fixtures__/cli/text/simulator/test--failure.txt +++ b/src/snapshot-tests/__fixtures__/cli/text/simulator/test--failure.txt @@ -3,10 +3,9 @@ Scheme: CalculatorApp Workspace: example_projects/iOS_Calculator/CalculatorApp.xcworkspace - Configuration: Debug Platform: iOS Simulator - Simulator: iPhone 17 - Derived Data: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/CalculatorApp- + Simulator: + Derived Data: Discovered 57 test(s): CalculatorAppFeatureTests/CalculatorBasicTests/testClear @@ -28,14 +27,15 @@ This should fail - display should be 0, not 999 CalculatorAppTests ✗ testCalculatorServiceFailure: - XCTAssertEqual failed: ("0") is not equal to ("999") - This test should fail - display should be 0, not 999 - example_projects/iOS_Calculator/CalculatorAppTests/CalculatorAppTests.swift:52 + /example_projects/iOS_Calculator/CalculatorAppTests/CalculatorAppTests.swift:52 IntentionalFailureTests ✗ test: - XCTAssertTrue failed - This test should fail to verify error reporting - example_projects/iOS_Calculator/CalculatorAppTests/CalculatorAppTests.swift:286 + /example_projects/iOS_Calculator/CalculatorAppTests/CalculatorAppTests.swift:286 ❌ tests failed, passed, skipped (⏱️ ) └ Files: ├ Result Bundle: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/result-bundles/test_sim__pid.xcresult - └ Build Logs: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/test_sim__pid.log + ├ Build Logs: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/test_sim__pid.log + └ Test Products: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/test-products/test_sim__pid.xctestproducts diff --git a/src/snapshot-tests/__fixtures__/cli/text/simulator/test--success.txt b/src/snapshot-tests/__fixtures__/cli/text/simulator/test--success.txt index 6f2c5e1aa..c649bc26b 100644 --- a/src/snapshot-tests/__fixtures__/cli/text/simulator/test--success.txt +++ b/src/snapshot-tests/__fixtures__/cli/text/simulator/test--success.txt @@ -3,10 +3,9 @@ Scheme: CalculatorApp Workspace: example_projects/iOS_Calculator/CalculatorApp.xcworkspace - Configuration: Debug Platform: iOS Simulator - Simulator: iPhone 17 - Derived Data: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/CalculatorApp- + Simulator: + Derived Data: Selective Testing: CalculatorAppTests/CalculatorAppTests/testAddition @@ -17,4 +16,5 @@ Running tests (1 completed, 0 failures, 0 skipped) ✅ 1 test passed, 0 failed, 0 skipped (⏱️ ) └ Files: ├ Result Bundle: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/result-bundles/test_sim__pid.xcresult - └ Build Logs: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/test_sim__pid.log + ├ Build Logs: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/test_sim__pid.log + └ Test Products: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/test-products/test_sim__pid.xctestproducts diff --git a/src/snapshot-tests/__fixtures__/cli/text/swift-package/build--error-bad-path.txt b/src/snapshot-tests/__fixtures__/cli/text/swift-package/build--error-bad-path.txt index bbf70264d..47144859a 100644 --- a/src/snapshot-tests/__fixtures__/cli/text/swift-package/build--error-bad-path.txt +++ b/src/snapshot-tests/__fixtures__/cli/text/swift-package/build--error-bad-path.txt @@ -1,11 +1,11 @@ 📦 Swift Package Build - Package: /example_projects/NONEXISTENT + Package: /spm/NONEXISTENT Errors (1): - ✗ chdir error: No such file or directory (2): /example_projects/NONEXISTENT + ✗ chdir error: No such file or directory (2): /spm/NONEXISTENT ❌ Build failed. (⏱️ ) └ Files: diff --git a/src/snapshot-tests/__fixtures__/cli/text/swift-package/build--success.txt b/src/snapshot-tests/__fixtures__/cli/text/swift-package/build--success.txt index 5a4cd434b..0395cc4c3 100644 --- a/src/snapshot-tests/__fixtures__/cli/text/swift-package/build--success.txt +++ b/src/snapshot-tests/__fixtures__/cli/text/swift-package/build--success.txt @@ -1,7 +1,7 @@ 📦 Swift Package Build - Package: /example_projects/spm + Package: /spm ✅ Build succeeded. (⏱️ ) └ Files: diff --git a/src/snapshot-tests/__fixtures__/cli/text/swift-package/clean--error-bad-path.txt b/src/snapshot-tests/__fixtures__/cli/text/swift-package/clean--error-bad-path.txt index cb4abdeb9..eb91857c9 100644 --- a/src/snapshot-tests/__fixtures__/cli/text/swift-package/clean--error-bad-path.txt +++ b/src/snapshot-tests/__fixtures__/cli/text/swift-package/clean--error-bad-path.txt @@ -1,10 +1,10 @@ 🧹 Swift Package Clean - Package: /example_projects/NONEXISTENT + Package: /spm/NONEXISTENT Errors (1): - ✗ chdir error: No such file or directory (2): /example_projects/NONEXISTENT + ✗ chdir error: No such file or directory (2): /spm/NONEXISTENT ❌ Swift package clean failed. diff --git a/src/snapshot-tests/__fixtures__/cli/text/swift-package/clean--success.txt b/src/snapshot-tests/__fixtures__/cli/text/swift-package/clean--success.txt index a0e316a04..72204ef41 100644 --- a/src/snapshot-tests/__fixtures__/cli/text/swift-package/clean--success.txt +++ b/src/snapshot-tests/__fixtures__/cli/text/swift-package/clean--success.txt @@ -1,6 +1,6 @@ 🧹 Swift Package Clean - Package: /example_projects/spm + Package: /spm ✅ Swift package cleaned successfully diff --git a/src/snapshot-tests/__fixtures__/cli/text/swift-package/list--success.txt b/src/snapshot-tests/__fixtures__/cli/text/swift-package/list--success.txt index 17a38338f..d8f1cd25a 100644 --- a/src/snapshot-tests/__fixtures__/cli/text/swift-package/list--success.txt +++ b/src/snapshot-tests/__fixtures__/cli/text/swift-package/list--success.txt @@ -5,4 +5,4 @@ Running Processes (1): 🟢 spm PID: | Uptime: - Package: /example_projects/spm + Package: /spm diff --git a/src/snapshot-tests/__fixtures__/cli/text/swift-package/run--error-bad-executable.txt b/src/snapshot-tests/__fixtures__/cli/text/swift-package/run--error-bad-executable.txt index 049283385..2b1ea83d5 100644 --- a/src/snapshot-tests/__fixtures__/cli/text/swift-package/run--error-bad-executable.txt +++ b/src/snapshot-tests/__fixtures__/cli/text/swift-package/run--error-bad-executable.txt @@ -1,7 +1,7 @@ 🚀 Swift Package Run - Package: /example_projects/spm + Package: /spm Executable: nonexistent-executable Errors (1): diff --git a/src/snapshot-tests/__fixtures__/cli/text/swift-package/run--success.txt b/src/snapshot-tests/__fixtures__/cli/text/swift-package/run--success.txt index 05d8aae6b..1b320aa7c 100644 --- a/src/snapshot-tests/__fixtures__/cli/text/swift-package/run--success.txt +++ b/src/snapshot-tests/__fixtures__/cli/text/swift-package/run--success.txt @@ -1,14 +1,14 @@ 🚀 Swift Package Run - Package: /example_projects/spm + Package: /spm Executable: spm ✅ Build succeeded. (⏱️ ) ✅ Build & Run complete ├ Process ID: └ Files: - ├ App Path: example_projects/spm/.build/arm64-apple-macosx/debug/spm + ├ App Path: /spm/.build//debug/spm └ Build Logs: ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/build_run_spm__pid.log Output diff --git a/src/snapshot-tests/__fixtures__/cli/text/swift-package/test--error-bad-path.txt b/src/snapshot-tests/__fixtures__/cli/text/swift-package/test--error-bad-path.txt index 750e0865b..2913a1bca 100644 --- a/src/snapshot-tests/__fixtures__/cli/text/swift-package/test--error-bad-path.txt +++ b/src/snapshot-tests/__fixtures__/cli/text/swift-package/test--error-bad-path.txt @@ -8,7 +8,7 @@ Errors (1): - ✗ chdir error: No such file or directory (2): /example_projects/NONEXISTENT + ✗ chdir error: No such file or directory (2): /spm/NONEXISTENT ❌ Test failed. (⏱️ ) └ Files: diff --git a/src/snapshot-tests/__fixtures__/cli/text/swift-package/test--failure.txt b/src/snapshot-tests/__fixtures__/cli/text/swift-package/test--failure.txt index 0516de499..8b20afd20 100644 --- a/src/snapshot-tests/__fixtures__/cli/text/swift-package/test--failure.txt +++ b/src/snapshot-tests/__fixtures__/cli/text/swift-package/test--failure.txt @@ -16,7 +16,7 @@ Running tests (7 completed, 2 failures, 0 skipped) CalculatorAppTests ✗ testCalculatorServiceFailure: - XCTAssertEqual failed: ("0") is not equal to ("999") - This test should fail - display should be 0, not 999 - example_projects/spm/Tests/TestLibTests/SimpleTests.swift:49 + /spm/Tests/TestLibTests/SimpleTests.swift:49 (Unknown Suite) ✗ test: diff --git a/src/snapshot-tests/__fixtures__/cli/text/ui-automation/snapshot-ui--success.txt b/src/snapshot-tests/__fixtures__/cli/text/ui-automation/snapshot-ui--success.txt index 095f30a53..8fc16540b 100644 --- a/src/snapshot-tests/__fixtures__/cli/text/ui-automation/snapshot-ui--success.txt +++ b/src/snapshot-tests/__fixtures__/cli/text/ui-automation/snapshot-ui--success.txt @@ -29,7 +29,7 @@ Tips - Refs are snapshot-specific; after snapshot_ui or wait_for_ui, use refs from the latest output. - Use wait_for_ui for text/assertions or changing UI. -✅ Runtime UI snapshot captured with 28 elements, 19 likely targets, and 0 scroll areas. +✅ Runtime UI snapshot captured with elements, likely targets, and scroll areas. Next steps: 1. Refresh after layout changes: xcodebuildmcp ui-automation snapshot-ui --simulator-id diff --git a/src/snapshot-tests/__fixtures__/cli/text/ui-automation/swipe--error-not-actionable.txt b/src/snapshot-tests/__fixtures__/cli/text/ui-automation/swipe--error-not-actionable.txt index f227e42b4..0ac0a086d 100644 --- a/src/snapshot-tests/__fixtures__/cli/text/ui-automation/swipe--error-not-actionable.txt +++ b/src/snapshot-tests/__fixtures__/cli/text/ui-automation/swipe--error-not-actionable.txt @@ -5,8 +5,8 @@ Recovery Code: TARGET_NOT_ACTIONABLE - Message: Element ref 'e3' does not support 'swipeWithin'. - Element: e3 + Message: Element ref '' does not support 'swipeWithin'. + Element: Hint: Choose an elementRef that lists the required action, or refresh with snapshot_ui. -❌ Element ref 'e3' does not support 'swipeWithin'. +❌ Element ref '' does not support 'swipeWithin'. diff --git a/src/snapshot-tests/__fixtures__/cli/text/ui-automation/swipe--success.txt b/src/snapshot-tests/__fixtures__/cli/text/ui-automation/swipe--success.txt index 4a0a437d1..a6fd918b7 100644 --- a/src/snapshot-tests/__fixtures__/cli/text/ui-automation/swipe--success.txt +++ b/src/snapshot-tests/__fixtures__/cli/text/ui-automation/swipe--success.txt @@ -6,5 +6,4 @@ ✅ Swipe up within elementRef simulated successfully. Next steps: -1. Batch same-screen taps: xcodebuildmcp ui-automation batch --json '{"simulatorId":"","steps":[{"action":"tap","elementRef":""},{"action":"tap","elementRef":""}]}' -2. Tap an elementRef: xcodebuildmcp ui-automation tap --simulator-id --element-ref +1. diff --git a/src/snapshot-tests/__fixtures__/cli/text/ui-automation/type-text--error-not-actionable.txt b/src/snapshot-tests/__fixtures__/cli/text/ui-automation/type-text--error-not-actionable.txt index 27afd2f76..4a16a4a63 100644 --- a/src/snapshot-tests/__fixtures__/cli/text/ui-automation/type-text--error-not-actionable.txt +++ b/src/snapshot-tests/__fixtures__/cli/text/ui-automation/type-text--error-not-actionable.txt @@ -5,8 +5,8 @@ Recovery Code: TARGET_NOT_ACTIONABLE - Message: Element ref 'e3' does not support 'typeText'. - Element: e3 + Message: Element ref '' does not support 'typeText'. + Element: Hint: Choose an elementRef that lists the required action, or refresh with snapshot_ui. -❌ Element ref 'e3' does not support 'typeText'. +❌ Element ref '' does not support 'typeText'. diff --git a/src/snapshot-tests/__fixtures__/cli/text/ui-automation/wait-for-ui--success.txt b/src/snapshot-tests/__fixtures__/cli/text/ui-automation/wait-for-ui--success.txt index 043c407c5..624a65fac 100644 --- a/src/snapshot-tests/__fixtures__/cli/text/ui-automation/wait-for-ui--success.txt +++ b/src/snapshot-tests/__fixtures__/cli/text/ui-automation/wait-for-ui--success.txt @@ -32,7 +32,7 @@ Tips - Refs are snapshot-specific; after snapshot_ui or wait_for_ui, use refs from the latest output. - Use wait_for_ui for text/assertions or changing UI. -✅ Wait completed; runtime UI snapshot refreshed with 28 elements, 19 likely targets, and 0 scroll areas. +✅ Wait completed; runtime UI snapshot refreshed with elements, likely targets, and scroll areas. Next steps: 1. Capture a fresh runtime UI snapshot: xcodebuildmcp ui-automation snapshot-ui --simulator-id diff --git a/src/snapshot-tests/__fixtures__/mcp/json/debugging/lldb-command--success.json b/src/snapshot-tests/__fixtures__/mcp/json/debugging/lldb-command--success.json index bc3e32531..98ef44cef 100644 --- a/src/snapshot-tests/__fixtures__/mcp/json/debugging/lldb-command--success.json +++ b/src/snapshot-tests/__fixtures__/mcp/json/debugging/lldb-command--success.json @@ -6,12 +6,7 @@ "data": { "command": "breakpoint list", "outputLines": [ - "Current breakpoints:", - "1: file = 'ContentView.swift', line = 42, exact_match = 0, locations = ", - " Names:", - " dap", - "", - " 1.1: where = CalculatorApp.debug.dylib`closure #1 in closure #1 in closure #1 in ContentView.body.getter + at ContentView.swift:42:31, address = , resolved, hit count = 0" + "No breakpoints currently set." ] } } diff --git a/src/snapshot-tests/__fixtures__/mcp/json/device/build--error-compiler.json b/src/snapshot-tests/__fixtures__/mcp/json/device/build--error-compiler.json index df6c7eafd..16637efac 100644 --- a/src/snapshot-tests/__fixtures__/mcp/json/device/build--error-compiler.json +++ b/src/snapshot-tests/__fixtures__/mcp/json/device/build--error-compiler.json @@ -1,6 +1,6 @@ { "schema": "xcodebuildmcp.output.build-result", - "schemaVersion": "2", + "schemaVersion": "3", "didError": true, "error": "Build failed", "data": { @@ -17,7 +17,7 @@ "errors": [ { "message": "cannot convert value of type 'String' to specified type 'Int'", - "location": "/example_projects/iOS_Calculator/CalculatorApp/CalculatorApp.swift:33" + "location": "/example_projects/iOS_Calculator/CalculatorApp/CalculatorApp.swift:53" } ] } diff --git a/src/snapshot-tests/__fixtures__/mcp/json/device/build--error-prepared-tests-wrong-scheme.json b/src/snapshot-tests/__fixtures__/mcp/json/device/build--error-prepared-tests-wrong-scheme.json new file mode 100644 index 000000000..d1f2c3ed7 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/mcp/json/device/build--error-prepared-tests-wrong-scheme.json @@ -0,0 +1,24 @@ +{ + "schema": "xcodebuildmcp.output.build-result", + "schemaVersion": "3", + "didError": true, + "error": "Build failed", + "data": { + "summary": { + "status": "FAILED", + "durationMs": 1234, + "target": "device" + }, + "artifacts": { + "buildLogPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/build_device__pid.log" + }, + "diagnostics": { + "warnings": [], + "errors": [ + { + "message": "The workspace named \"CalculatorApp\" does not contain a scheme named \"NONEXISTENT\". The \"-list\" option can be used to find the names of the schemes in the workspace." + } + ] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/mcp/json/device/build--error-wrong-scheme.json b/src/snapshot-tests/__fixtures__/mcp/json/device/build--error-wrong-scheme.json index 0799e0040..d1f2c3ed7 100644 --- a/src/snapshot-tests/__fixtures__/mcp/json/device/build--error-wrong-scheme.json +++ b/src/snapshot-tests/__fixtures__/mcp/json/device/build--error-wrong-scheme.json @@ -1,6 +1,6 @@ { "schema": "xcodebuildmcp.output.build-result", - "schemaVersion": "2", + "schemaVersion": "3", "didError": true, "error": "Build failed", "data": { diff --git a/src/snapshot-tests/__fixtures__/mcp/json/device/build--success-prepared-tests-generic.json b/src/snapshot-tests/__fixtures__/mcp/json/device/build--success-prepared-tests-generic.json new file mode 100644 index 000000000..9a8844bae --- /dev/null +++ b/src/snapshot-tests/__fixtures__/mcp/json/device/build--success-prepared-tests-generic.json @@ -0,0 +1,24 @@ +{ + "schema": "xcodebuildmcp.output.build-result", + "schemaVersion": "3", + "didError": false, + "error": null, + "data": { + "summary": { + "status": "SUCCEEDED", + "durationMs": 1234, + "target": "device" + }, + "artifacts": { + "buildLogPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/build_device__pid.log", + "testProductsPath": "/Generic CalculatorApp Tests.xctestproducts", + "xctestrunPaths": [ + "/Generic CalculatorApp Tests.xctestproducts/Tests/0/CalculatorApp.xctestrun" + ] + }, + "diagnostics": { + "warnings": [], + "errors": [] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/mcp/json/device/build--success.json b/src/snapshot-tests/__fixtures__/mcp/json/device/build--success.json index c29c38cff..017050f3a 100644 --- a/src/snapshot-tests/__fixtures__/mcp/json/device/build--success.json +++ b/src/snapshot-tests/__fixtures__/mcp/json/device/build--success.json @@ -1,6 +1,6 @@ { "schema": "xcodebuildmcp.output.build-result", - "schemaVersion": "2", + "schemaVersion": "3", "didError": false, "error": null, "data": { diff --git a/src/snapshot-tests/__fixtures__/mcp/json/device/build-and-run--error-compiler.json b/src/snapshot-tests/__fixtures__/mcp/json/device/build-and-run--error-compiler.json index 584c06234..8f376f679 100644 --- a/src/snapshot-tests/__fixtures__/mcp/json/device/build-and-run--error-compiler.json +++ b/src/snapshot-tests/__fixtures__/mcp/json/device/build-and-run--error-compiler.json @@ -18,7 +18,7 @@ "errors": [ { "message": "cannot convert value of type 'String' to specified type 'Int'", - "location": "/example_projects/iOS_Calculator/CalculatorApp/CalculatorApp.swift:33" + "location": "/example_projects/iOS_Calculator/CalculatorApp/CalculatorApp.swift:53" } ] } diff --git a/src/snapshot-tests/__fixtures__/mcp/json/device/test--error-compiler.json b/src/snapshot-tests/__fixtures__/mcp/json/device/test--error-compiler.json index 1a2283fbc..7e9253e5e 100644 --- a/src/snapshot-tests/__fixtures__/mcp/json/device/test--error-compiler.json +++ b/src/snapshot-tests/__fixtures__/mcp/json/device/test--error-compiler.json @@ -1,6 +1,6 @@ { "schema": "xcodebuildmcp.output.test-result", - "schemaVersion": "2", + "schemaVersion": "3", "didError": true, "error": "Tests failed", "data": { @@ -35,7 +35,7 @@ "errors": [ { "message": "cannot convert value of type 'String' to specified type 'Int'", - "location": "/example_projects/iOS_Calculator/CalculatorApp/CalculatorApp.swift:33" + "location": "/example_projects/iOS_Calculator/CalculatorApp/CalculatorApp.swift:53" } ], "testFailures": [] diff --git a/src/snapshot-tests/__fixtures__/mcp/json/device/test--failure.json b/src/snapshot-tests/__fixtures__/mcp/json/device/test--failure.json index 4ba8d6390..517f6e8dc 100644 --- a/src/snapshot-tests/__fixtures__/mcp/json/device/test--failure.json +++ b/src/snapshot-tests/__fixtures__/mcp/json/device/test--failure.json @@ -1,6 +1,6 @@ { "schema": "xcodebuildmcp.output.test-result", - "schemaVersion": "2", + "schemaVersion": "3", "didError": true, "error": "Tests failed", "data": { diff --git a/src/snapshot-tests/__fixtures__/mcp/json/device/test--success.json b/src/snapshot-tests/__fixtures__/mcp/json/device/test--success.json index 9f2267bd5..487100a4d 100644 --- a/src/snapshot-tests/__fixtures__/mcp/json/device/test--success.json +++ b/src/snapshot-tests/__fixtures__/mcp/json/device/test--success.json @@ -1,6 +1,6 @@ { "schema": "xcodebuildmcp.output.test-result", - "schemaVersion": "2", + "schemaVersion": "3", "didError": false, "error": null, "data": { diff --git a/src/snapshot-tests/__fixtures__/mcp/json/macos/build--error-compiler.json b/src/snapshot-tests/__fixtures__/mcp/json/macos/build--error-compiler.json index 056b18fa3..444bb5864 100644 --- a/src/snapshot-tests/__fixtures__/mcp/json/macos/build--error-compiler.json +++ b/src/snapshot-tests/__fixtures__/mcp/json/macos/build--error-compiler.json @@ -1,6 +1,6 @@ { "schema": "xcodebuildmcp.output.build-result", - "schemaVersion": "2", + "schemaVersion": "3", "didError": true, "error": "Build failed", "data": { diff --git a/src/snapshot-tests/__fixtures__/mcp/json/macos/build--error-prepared-tests-wrong-scheme.json b/src/snapshot-tests/__fixtures__/mcp/json/macos/build--error-prepared-tests-wrong-scheme.json new file mode 100644 index 000000000..0910b24a1 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/mcp/json/macos/build--error-prepared-tests-wrong-scheme.json @@ -0,0 +1,24 @@ +{ + "schema": "xcodebuildmcp.output.build-result", + "schemaVersion": "3", + "didError": true, + "error": "Build failed", + "data": { + "summary": { + "status": "FAILED", + "durationMs": 1234, + "target": "macos" + }, + "artifacts": { + "buildLogPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/build_macos__pid.log" + }, + "diagnostics": { + "warnings": [], + "errors": [ + { + "message": "The project named \"MCPTest\" does not contain a scheme named \"NONEXISTENT\". The \"-list\" option can be used to find the names of the schemes in the project." + } + ] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/mcp/json/macos/build--error-wrong-scheme.json b/src/snapshot-tests/__fixtures__/mcp/json/macos/build--error-wrong-scheme.json index 8d4556828..0910b24a1 100644 --- a/src/snapshot-tests/__fixtures__/mcp/json/macos/build--error-wrong-scheme.json +++ b/src/snapshot-tests/__fixtures__/mcp/json/macos/build--error-wrong-scheme.json @@ -1,6 +1,6 @@ { "schema": "xcodebuildmcp.output.build-result", - "schemaVersion": "2", + "schemaVersion": "3", "didError": true, "error": "Build failed", "data": { diff --git a/src/snapshot-tests/__fixtures__/mcp/json/macos/build--success-prepared-tests.json b/src/snapshot-tests/__fixtures__/mcp/json/macos/build--success-prepared-tests.json new file mode 100644 index 000000000..bfc39eb75 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/mcp/json/macos/build--success-prepared-tests.json @@ -0,0 +1,27 @@ +{ + "schema": "xcodebuildmcp.output.build-result", + "schemaVersion": "3", + "didError": false, + "error": null, + "data": { + "summary": { + "status": "SUCCEEDED", + "durationMs": 1234, + "target": "macos" + }, + "artifacts": { + "buildLogPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/build_macos__pid.log", + "testProductsPath": "/MCPTest Tests.xctestproducts", + "xctestrunPaths": [ + "/MCPTest Tests.xctestproducts/Tests/0/MCPTest.xctestrun" + ] + }, + "diagnostics": { + "warnings": [], + "errors": [] + } + }, + "nextSteps": [ + "Run prepared tests: test_macos({ testProductsPath: \"/MCPTest Tests.xctestproducts\" })" + ] +} diff --git a/src/snapshot-tests/__fixtures__/mcp/json/macos/build--success.json b/src/snapshot-tests/__fixtures__/mcp/json/macos/build--success.json index 5e7318280..e6a64a9a6 100644 --- a/src/snapshot-tests/__fixtures__/mcp/json/macos/build--success.json +++ b/src/snapshot-tests/__fixtures__/mcp/json/macos/build--success.json @@ -1,6 +1,6 @@ { "schema": "xcodebuildmcp.output.build-result", - "schemaVersion": "2", + "schemaVersion": "3", "didError": false, "error": null, "data": { diff --git a/src/snapshot-tests/__fixtures__/mcp/json/macos/stop--success.json b/src/snapshot-tests/__fixtures__/mcp/json/macos/stop--success.json index 7c3ed4c45..13933bed8 100644 --- a/src/snapshot-tests/__fixtures__/mcp/json/macos/stop--success.json +++ b/src/snapshot-tests/__fixtures__/mcp/json/macos/stop--success.json @@ -8,7 +8,8 @@ "status": "SUCCEEDED" }, "artifacts": { - "appName": "MCPTest" + "processId": 99999, + "appName": "PID " }, "diagnostics": { "warnings": [], diff --git a/src/snapshot-tests/__fixtures__/mcp/json/macos/test--error-compiler.json b/src/snapshot-tests/__fixtures__/mcp/json/macos/test--error-compiler.json index 78c2aa7cd..a070027e3 100644 --- a/src/snapshot-tests/__fixtures__/mcp/json/macos/test--error-compiler.json +++ b/src/snapshot-tests/__fixtures__/mcp/json/macos/test--error-compiler.json @@ -1,22 +1,16 @@ { "schema": "xcodebuildmcp.output.test-result", - "schemaVersion": "2", + "schemaVersion": "3", "didError": true, "error": "Tests failed", "data": { "summary": { "status": "FAILED", "durationMs": 1234, - "counts": { - "passed": 0, - "failed": 0, - "skipped": 0 - }, "target": "macos" }, "artifacts": { - "buildLogPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/test_macos__pid.log", - "xcresultPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/result-bundles/test_macos__pid.xcresult" + "buildLogPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/test_macos__pid.log" }, "tests": { "selected": [ diff --git a/src/snapshot-tests/__fixtures__/mcp/json/macos/test--error-wrong-scheme.json b/src/snapshot-tests/__fixtures__/mcp/json/macos/test--error-wrong-scheme.json index 46da0089b..9ae7221af 100644 --- a/src/snapshot-tests/__fixtures__/mcp/json/macos/test--error-wrong-scheme.json +++ b/src/snapshot-tests/__fixtures__/mcp/json/macos/test--error-wrong-scheme.json @@ -1,22 +1,16 @@ { "schema": "xcodebuildmcp.output.test-result", - "schemaVersion": "2", + "schemaVersion": "3", "didError": true, "error": "Tests failed", "data": { "summary": { "status": "FAILED", "durationMs": 1234, - "counts": { - "passed": 0, - "failed": 0, - "skipped": 0 - }, "target": "macos" }, "artifacts": { - "buildLogPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/test_macos__pid.log", - "xcresultPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/result-bundles/test_macos__pid.xcresult" + "buildLogPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/test_macos__pid.log" }, "diagnostics": { "warnings": [], diff --git a/src/snapshot-tests/__fixtures__/mcp/json/macos/test--failure.json b/src/snapshot-tests/__fixtures__/mcp/json/macos/test--failure.json index 8db733c47..ebccfb4f7 100644 --- a/src/snapshot-tests/__fixtures__/mcp/json/macos/test--failure.json +++ b/src/snapshot-tests/__fixtures__/mcp/json/macos/test--failure.json @@ -1,6 +1,6 @@ { "schema": "xcodebuildmcp.output.test-result", - "schemaVersion": "2", + "schemaVersion": "3", "didError": true, "error": "Tests failed", "data": { @@ -16,7 +16,8 @@ }, "artifacts": { "buildLogPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/test_macos__pid.log", - "xcresultPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/result-bundles/test_macos__pid.xcresult" + "xcresultPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/result-bundles/test_macos__pid.xcresult", + "testProductsPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/test-products/test_macos__pid.xctestproducts" }, "tests": { "discovered": { @@ -33,17 +34,17 @@ "warnings": [], "errors": [], "testFailures": [ - { - "suite": "MCPTestsXCTests", - "test": "testDeliberateFailure()", - "message": "XCTAssertTrue failed - This test is designed to fail for snapshot testing", - "location": "MCPTestsXCTests.swift:11" - }, { "suite": "MCPTestTests", "test": "deliberateFailure()", "message": "Expectation failed: 1 == 2: This test is designed to fail for snapshot testing", "location": "MCPTestTests.swift:11" + }, + { + "suite": "MCPTestsXCTests", + "test": "testDeliberateFailure()", + "message": "XCTAssertTrue failed - This test is designed to fail for snapshot testing", + "location": "MCPTestsXCTests.swift:11" } ] }, diff --git a/src/snapshot-tests/__fixtures__/mcp/json/macos/test--success.json b/src/snapshot-tests/__fixtures__/mcp/json/macos/test--success.json index 031d4f8d0..42262778b 100644 --- a/src/snapshot-tests/__fixtures__/mcp/json/macos/test--success.json +++ b/src/snapshot-tests/__fixtures__/mcp/json/macos/test--success.json @@ -1,6 +1,6 @@ { "schema": "xcodebuildmcp.output.test-result", - "schemaVersion": "2", + "schemaVersion": "3", "didError": false, "error": null, "data": { @@ -16,7 +16,8 @@ }, "artifacts": { "buildLogPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/test_macos__pid.log", - "xcresultPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/result-bundles/test_macos__pid.xcresult" + "xcresultPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/result-bundles/test_macos__pid.xcresult", + "testProductsPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/test-products/test_macos__pid.xctestproducts" }, "tests": { "selected": [ diff --git a/src/snapshot-tests/__fixtures__/mcp/json/simulator/build--error-compiler.json b/src/snapshot-tests/__fixtures__/mcp/json/simulator/build--error-compiler.json index 19d7929c4..81c78b9da 100644 --- a/src/snapshot-tests/__fixtures__/mcp/json/simulator/build--error-compiler.json +++ b/src/snapshot-tests/__fixtures__/mcp/json/simulator/build--error-compiler.json @@ -1,6 +1,6 @@ { "schema": "xcodebuildmcp.output.build-result", - "schemaVersion": "2", + "schemaVersion": "3", "didError": true, "error": "Build failed", "data": { @@ -17,7 +17,7 @@ "errors": [ { "message": "cannot convert value of type 'String' to specified type 'Int'", - "location": "/example_projects/iOS_Calculator/CalculatorApp/CalculatorApp.swift:33" + "location": "/example_projects/iOS_Calculator/CalculatorApp/CalculatorApp.swift:53" } ] } diff --git a/src/snapshot-tests/__fixtures__/mcp/json/simulator/build--error-prepared-tests-wrong-scheme.json b/src/snapshot-tests/__fixtures__/mcp/json/simulator/build--error-prepared-tests-wrong-scheme.json new file mode 100644 index 000000000..2e9725ab5 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/mcp/json/simulator/build--error-prepared-tests-wrong-scheme.json @@ -0,0 +1,24 @@ +{ + "schema": "xcodebuildmcp.output.build-result", + "schemaVersion": "3", + "didError": true, + "error": "Build failed", + "data": { + "summary": { + "status": "FAILED", + "durationMs": 1234, + "target": "simulator" + }, + "artifacts": { + "buildLogPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/build_sim__pid.log" + }, + "diagnostics": { + "warnings": [], + "errors": [ + { + "message": "The workspace named \"CalculatorApp\" does not contain a scheme named \"NONEXISTENT\". The \"-list\" option can be used to find the names of the schemes in the workspace." + } + ] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/mcp/json/simulator/build--error-wrong-scheme.json b/src/snapshot-tests/__fixtures__/mcp/json/simulator/build--error-wrong-scheme.json index c0c6d8f27..2e9725ab5 100644 --- a/src/snapshot-tests/__fixtures__/mcp/json/simulator/build--error-wrong-scheme.json +++ b/src/snapshot-tests/__fixtures__/mcp/json/simulator/build--error-wrong-scheme.json @@ -1,6 +1,6 @@ { "schema": "xcodebuildmcp.output.build-result", - "schemaVersion": "2", + "schemaVersion": "3", "didError": true, "error": "Build failed", "data": { diff --git a/src/snapshot-tests/__fixtures__/mcp/json/simulator/build--success-prepared-tests.json b/src/snapshot-tests/__fixtures__/mcp/json/simulator/build--success-prepared-tests.json new file mode 100644 index 000000000..7ec0d1ab1 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/mcp/json/simulator/build--success-prepared-tests.json @@ -0,0 +1,27 @@ +{ + "schema": "xcodebuildmcp.output.build-result", + "schemaVersion": "3", + "didError": false, + "error": null, + "data": { + "summary": { + "status": "SUCCEEDED", + "durationMs": 1234, + "target": "simulator" + }, + "artifacts": { + "buildLogPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/build_sim__pid.log", + "testProductsPath": "/CalculatorApp Tests.xctestproducts", + "xctestrunPaths": [ + "/CalculatorApp Tests.xctestproducts/Tests/0/CalculatorApp.xctestrun" + ] + }, + "diagnostics": { + "warnings": [], + "errors": [] + } + }, + "nextSteps": [ + "Run prepared tests: test_sim({ testProductsPath: \"/CalculatorApp Tests.xctestproducts\", simulatorName: \"iPhone 17\" })" + ] +} diff --git a/src/snapshot-tests/__fixtures__/mcp/json/simulator/build--success.json b/src/snapshot-tests/__fixtures__/mcp/json/simulator/build--success.json index 79e2ad1cf..564ccfddb 100644 --- a/src/snapshot-tests/__fixtures__/mcp/json/simulator/build--success.json +++ b/src/snapshot-tests/__fixtures__/mcp/json/simulator/build--success.json @@ -1,6 +1,6 @@ { "schema": "xcodebuildmcp.output.build-result", - "schemaVersion": "2", + "schemaVersion": "3", "didError": false, "error": null, "data": { diff --git a/src/snapshot-tests/__fixtures__/mcp/json/simulator/build-and-run--error-compiler.json b/src/snapshot-tests/__fixtures__/mcp/json/simulator/build-and-run--error-compiler.json index c9d9b9601..3a8271dac 100644 --- a/src/snapshot-tests/__fixtures__/mcp/json/simulator/build-and-run--error-compiler.json +++ b/src/snapshot-tests/__fixtures__/mcp/json/simulator/build-and-run--error-compiler.json @@ -17,7 +17,7 @@ "errors": [ { "message": "cannot convert value of type 'String' to specified type 'Int'", - "location": "/example_projects/iOS_Calculator/CalculatorApp/CalculatorApp.swift:33" + "location": "/example_projects/iOS_Calculator/CalculatorApp/CalculatorApp.swift:53" } ] } diff --git a/src/snapshot-tests/__fixtures__/mcp/json/simulator/test--error-compiler.json b/src/snapshot-tests/__fixtures__/mcp/json/simulator/test--error-compiler.json index 8bc0b483f..f76a06537 100644 --- a/src/snapshot-tests/__fixtures__/mcp/json/simulator/test--error-compiler.json +++ b/src/snapshot-tests/__fixtures__/mcp/json/simulator/test--error-compiler.json @@ -1,6 +1,6 @@ { "schema": "xcodebuildmcp.output.test-result", - "schemaVersion": "2", + "schemaVersion": "3", "didError": true, "error": "Tests failed", "data": { @@ -28,7 +28,7 @@ "errors": [ { "message": "cannot convert value of type 'String' to specified type 'Int'", - "location": "/example_projects/iOS_Calculator/CalculatorApp/CalculatorApp.swift:33" + "location": "/example_projects/iOS_Calculator/CalculatorApp/CalculatorApp.swift:53" } ], "testFailures": [] diff --git a/src/snapshot-tests/__fixtures__/mcp/json/simulator/test--error-wrong-scheme.json b/src/snapshot-tests/__fixtures__/mcp/json/simulator/test--error-wrong-scheme.json index 8658e3ec8..1b6fbb54a 100644 --- a/src/snapshot-tests/__fixtures__/mcp/json/simulator/test--error-wrong-scheme.json +++ b/src/snapshot-tests/__fixtures__/mcp/json/simulator/test--error-wrong-scheme.json @@ -1,6 +1,6 @@ { "schema": "xcodebuildmcp.output.test-result", - "schemaVersion": "2", + "schemaVersion": "3", "didError": true, "error": "Tests failed", "data": { diff --git a/src/snapshot-tests/__fixtures__/mcp/json/simulator/test--failure.json b/src/snapshot-tests/__fixtures__/mcp/json/simulator/test--failure.json index d3acd12fc..2d5a4d2dc 100644 --- a/src/snapshot-tests/__fixtures__/mcp/json/simulator/test--failure.json +++ b/src/snapshot-tests/__fixtures__/mcp/json/simulator/test--failure.json @@ -1,6 +1,6 @@ { "schema": "xcodebuildmcp.output.test-result", - "schemaVersion": "2", + "schemaVersion": "3", "didError": true, "error": "Tests failed", "data": { @@ -16,7 +16,8 @@ }, "artifacts": { "buildLogPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/test_sim__pid.log", - "xcresultPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/result-bundles/test_sim__pid.xcresult" + "xcresultPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/result-bundles/test_sim__pid.xcresult", + "testProductsPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/test-products/test_sim__pid.xctestproducts" }, "tests": { "discovered": { diff --git a/src/snapshot-tests/__fixtures__/mcp/json/simulator/test--success.json b/src/snapshot-tests/__fixtures__/mcp/json/simulator/test--success.json index 5dabc5eb5..424b1438f 100644 --- a/src/snapshot-tests/__fixtures__/mcp/json/simulator/test--success.json +++ b/src/snapshot-tests/__fixtures__/mcp/json/simulator/test--success.json @@ -1,6 +1,6 @@ { "schema": "xcodebuildmcp.output.test-result", - "schemaVersion": "2", + "schemaVersion": "3", "didError": false, "error": null, "data": { @@ -16,7 +16,8 @@ }, "artifacts": { "buildLogPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/test_sim__pid.log", - "xcresultPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/result-bundles/test_sim__pid.xcresult" + "xcresultPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/result-bundles/test_sim__pid.xcresult", + "testProductsPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/test-products/test_sim__pid.xctestproducts" }, "tests": { "selected": [ diff --git a/src/snapshot-tests/__fixtures__/mcp/json/swift-package/build--success.json b/src/snapshot-tests/__fixtures__/mcp/json/swift-package/build--success.json index 09e013315..4e4082225 100644 --- a/src/snapshot-tests/__fixtures__/mcp/json/swift-package/build--success.json +++ b/src/snapshot-tests/__fixtures__/mcp/json/swift-package/build--success.json @@ -10,7 +10,7 @@ "target": "swift-package" }, "artifacts": { - "packagePath": "/example_projects/spm", + "packagePath": "/spm", "buildLogPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/build_spm__pid.log" }, "diagnostics": { diff --git a/src/snapshot-tests/__fixtures__/mcp/json/swift-package/clean--success.json b/src/snapshot-tests/__fixtures__/mcp/json/swift-package/clean--success.json index 75b69af0e..1461bac35 100644 --- a/src/snapshot-tests/__fixtures__/mcp/json/swift-package/clean--success.json +++ b/src/snapshot-tests/__fixtures__/mcp/json/swift-package/clean--success.json @@ -9,7 +9,7 @@ "target": "swift-package" }, "artifacts": { - "packagePath": "/example_projects/spm" + "packagePath": "/spm" }, "diagnostics": { "warnings": [], diff --git a/src/snapshot-tests/__fixtures__/mcp/json/swift-package/list--success.json b/src/snapshot-tests/__fixtures__/mcp/json/swift-package/list--success.json index 82f194fc5..018c30d24 100644 --- a/src/snapshot-tests/__fixtures__/mcp/json/swift-package/list--success.json +++ b/src/snapshot-tests/__fixtures__/mcp/json/swift-package/list--success.json @@ -13,7 +13,7 @@ "processId": 99999, "uptimeSeconds": 3600, "artifacts": { - "packagePath": "/example_projects/spm" + "packagePath": "/spm" } } ] diff --git a/src/snapshot-tests/__fixtures__/mcp/json/swift-package/run--error-bad-executable.json b/src/snapshot-tests/__fixtures__/mcp/json/swift-package/run--error-bad-executable.json index 6f3b6f6db..30d3108f1 100644 --- a/src/snapshot-tests/__fixtures__/mcp/json/swift-package/run--error-bad-executable.json +++ b/src/snapshot-tests/__fixtures__/mcp/json/swift-package/run--error-bad-executable.json @@ -10,7 +10,7 @@ "target": "swift-package" }, "artifacts": { - "packagePath": "/example_projects/spm", + "packagePath": "/spm", "buildLogPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/build_run_spm__pid.log" }, "diagnostics": { diff --git a/src/snapshot-tests/__fixtures__/mcp/json/swift-package/run--success.json b/src/snapshot-tests/__fixtures__/mcp/json/swift-package/run--success.json index adba600ec..ce4deaeb2 100644 --- a/src/snapshot-tests/__fixtures__/mcp/json/swift-package/run--success.json +++ b/src/snapshot-tests/__fixtures__/mcp/json/swift-package/run--success.json @@ -10,8 +10,8 @@ "target": "swift-package" }, "artifacts": { - "packagePath": "/example_projects/spm", - "executablePath": "/example_projects/spm/.build/arm64-apple-macosx/debug/spm", + "packagePath": "/spm", + "executablePath": "/spm/.build//debug/spm", "processId": 99999, "buildLogPath": "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/build_run_spm__pid.log" }, diff --git a/src/snapshot-tests/__fixtures__/mcp/json/swift-package/test--failure.json b/src/snapshot-tests/__fixtures__/mcp/json/swift-package/test--failure.json index 8b68f052a..51a6fd82b 100644 --- a/src/snapshot-tests/__fixtures__/mcp/json/swift-package/test--failure.json +++ b/src/snapshot-tests/__fixtures__/mcp/json/swift-package/test--failure.json @@ -25,7 +25,7 @@ "suite": "CalculatorAppTests", "test": "testCalculatorServiceFailure", "message": "XCTAssertEqual failed: (\"0\") is not equal to (\"999\") - This test should fail - display should be 0, not 999", - "location": "/example_projects/spm/Tests/TestLibTests/SimpleTests.swift:49" + "location": "/spm/Tests/TestLibTests/SimpleTests.swift:49" }, { "suite": "(Unknown Suite)", diff --git a/src/snapshot-tests/__fixtures__/mcp/json/ui-automation/gesture--success.json b/src/snapshot-tests/__fixtures__/mcp/json/ui-automation/gesture--success.json index 181ed181e..056c4ab82 100644 --- a/src/snapshot-tests/__fixtures__/mcp/json/ui-automation/gesture--success.json +++ b/src/snapshot-tests/__fixtures__/mcp/json/ui-automation/gesture--success.json @@ -19,7 +19,7 @@ "rs": "1", "screenHash": "", "seq": 1, - "count": 28, + "count": 99999, "targets": [ "|tap|button|7||", "|tap|button|8||", diff --git a/src/snapshot-tests/__fixtures__/mcp/json/ui-automation/key-press--success.json b/src/snapshot-tests/__fixtures__/mcp/json/ui-automation/key-press--success.json index 0e27b7421..01ada3f56 100644 --- a/src/snapshot-tests/__fixtures__/mcp/json/ui-automation/key-press--success.json +++ b/src/snapshot-tests/__fixtures__/mcp/json/ui-automation/key-press--success.json @@ -19,7 +19,7 @@ "rs": "1", "screenHash": "", "seq": 1, - "count": 28, + "count": 99999, "targets": [ "|tap|button|7||", "|tap|button|8||", diff --git a/src/snapshot-tests/__fixtures__/mcp/json/ui-automation/key-sequence--success.json b/src/snapshot-tests/__fixtures__/mcp/json/ui-automation/key-sequence--success.json index 632de6711..cb95e5e9b 100644 --- a/src/snapshot-tests/__fixtures__/mcp/json/ui-automation/key-sequence--success.json +++ b/src/snapshot-tests/__fixtures__/mcp/json/ui-automation/key-sequence--success.json @@ -23,7 +23,7 @@ "rs": "1", "screenHash": "", "seq": 1, - "count": 28, + "count": 99999, "targets": [ "|tap|button|7||", "|tap|button|8||", diff --git a/src/snapshot-tests/__fixtures__/mcp/json/ui-automation/long-press--success.json b/src/snapshot-tests/__fixtures__/mcp/json/ui-automation/long-press--success.json index 3c21e82d0..c8a0e79bc 100644 --- a/src/snapshot-tests/__fixtures__/mcp/json/ui-automation/long-press--success.json +++ b/src/snapshot-tests/__fixtures__/mcp/json/ui-automation/long-press--success.json @@ -22,7 +22,7 @@ "rs": "1", "screenHash": "", "seq": 1, - "count": 28, + "count": 99999, "targets": [ "|tap|button|7||", "|tap|button|8||", diff --git a/src/snapshot-tests/__fixtures__/mcp/json/ui-automation/snapshot-ui--success.json b/src/snapshot-tests/__fixtures__/mcp/json/ui-automation/snapshot-ui--success.json index d0f99b26e..f8189c921 100644 --- a/src/snapshot-tests/__fixtures__/mcp/json/ui-automation/snapshot-ui--success.json +++ b/src/snapshot-tests/__fixtures__/mcp/json/ui-automation/snapshot-ui--success.json @@ -15,7 +15,7 @@ "rs": "1", "screenHash": "", "seq": 1, - "count": 28, + "count": 99999, "targets": [ "|tap|button|7||", "|tap|button|8||", diff --git a/src/snapshot-tests/__fixtures__/mcp/json/ui-automation/swipe--success.json b/src/snapshot-tests/__fixtures__/mcp/json/ui-automation/swipe--success.json index 3ed971670..9b6d1fec7 100644 --- a/src/snapshot-tests/__fixtures__/mcp/json/ui-automation/swipe--success.json +++ b/src/snapshot-tests/__fixtures__/mcp/json/ui-automation/swipe--success.json @@ -13,11 +13,11 @@ "direction": "up", "from": { "x": 201, - "y": 743 + "y": 468 }, "to": { "x": 201, - "y": 131 + "y": 406 } }, "artifacts": { @@ -28,45 +28,31 @@ "rs": "1", "screenHash": "", "seq": 1, - "count": 144, - "targets": [ - "|tap|button|Home Screen & App Library||com.apple.settings.homeScreen", - "|typeText|text-field||Search|", - "|tap|button|Camera||com.apple.settings.camera", - "|tap|button|Search||com.apple.settings.search", - "|tap|button|StandBy||com.apple.settings.standBy", - "|tap|button|Screen Time||com.apple.settings.screenTime", - "|tap|button|Passcode||com.apple.settings.passcodeAndBiometrics", - "|tap|button|Privacy & Security||com.apple.settings.privacyAndSecurity", - "|tap|button|Game Center||com.apple.settings.gameCenter", - "|tap|button|iCloud||com.apple.settings.iCloud", - "|tap|button|Apps||com.apple.settings.apps", - "|tap|button|Developer||com.apple.settings.developer", - "|tap|button|Dictate||Dictate", - "|tap|button|Dictate||Dictate" - ], + "count": 99999, + "targets": [], "scroll": [ - "|swipe|application|Settings||" + "|swipe|scroll-view|||snapshot-scroll-surface" ], "text": [ - "|text|text|Apple Intelligence & Siri||", - "|text|text|Camera||", - "|text|text|Home Screen & App Library||", - "|text|text|Search||", - "|text|text|StandBy||", - "|text|text|Screen Time||", - "|text|text|Passcode||", - "|text|text|Privacy & Security||", - "|text|text|Game Center||", - "|text|text|iCloud||", - "|text|text|Apps||", - "|text|text|Developer||" + "|text|text|Snapshot row 0||", + "|text|text|Snapshot row 1||", + "|text|text|Snapshot row 2||", + "|text|text|Snapshot row 3||", + "|text|text|Snapshot row 4||", + "|text|text|Snapshot row 5||", + "|text|text|Snapshot row 6||", + "|text|text|Snapshot row 7||", + "|text|text|Snapshot row 8||", + "|text|text|Snapshot row 9||", + "|text|text|Snapshot row 10||", + "|text|text|Snapshot row 11||", + "|text|text|Snapshot row 12||", + "|text|text|Snapshot row 13||" ], "udid": "" } }, "nextSteps": [ - "Batch same-screen taps: batch({ simulatorId: \"\", steps: [{\"action\":\"tap\",\"elementRef\":\"\"},{\"action\":\"tap\",\"elementRef\":\"\"}] })", - "Tap an elementRef: tap({ simulatorId: \"\", elementRef: \"\" })" + "" ] } diff --git a/src/snapshot-tests/__fixtures__/mcp/json/ui-automation/tap--success.json b/src/snapshot-tests/__fixtures__/mcp/json/ui-automation/tap--success.json index ef29db861..c668486fa 100644 --- a/src/snapshot-tests/__fixtures__/mcp/json/ui-automation/tap--success.json +++ b/src/snapshot-tests/__fixtures__/mcp/json/ui-automation/tap--success.json @@ -21,7 +21,7 @@ "rs": "1", "screenHash": "", "seq": 1, - "count": 28, + "count": 99999, "targets": [ "|tap|button|7||", "|tap|button|8||", @@ -45,7 +45,7 @@ ], "scroll": [], "text": [ - "|text|text|7||" + "|text|text|0||" ], "udid": "" } diff --git a/src/snapshot-tests/__fixtures__/mcp/json/ui-automation/touch--success.json b/src/snapshot-tests/__fixtures__/mcp/json/ui-automation/touch--success.json index adecbcf3b..98884e7d6 100644 --- a/src/snapshot-tests/__fixtures__/mcp/json/ui-automation/touch--success.json +++ b/src/snapshot-tests/__fixtures__/mcp/json/ui-automation/touch--success.json @@ -22,7 +22,7 @@ "rs": "1", "screenHash": "", "seq": 1, - "count": 28, + "count": 99999, "targets": [ "|tap|button|7||", "|tap|button|8||", diff --git a/src/snapshot-tests/__fixtures__/mcp/json/ui-automation/wait-for-ui--success.json b/src/snapshot-tests/__fixtures__/mcp/json/ui-automation/wait-for-ui--success.json index a7a604e46..8247d3c2f 100644 --- a/src/snapshot-tests/__fixtures__/mcp/json/ui-automation/wait-for-ui--success.json +++ b/src/snapshot-tests/__fixtures__/mcp/json/ui-automation/wait-for-ui--success.json @@ -15,7 +15,7 @@ "rs": "1", "screenHash": "", "seq": 1, - "count": 28, + "count": 99999, "targets": [ "|tap|button|7||", "|tap|button|8||", diff --git a/src/snapshot-tests/__fixtures__/mcp/text/coverage/get-coverage-report--success.txt b/src/snapshot-tests/__fixtures__/mcp/text/coverage/get-coverage-report--success.txt index bae11ffd9..b4ae271a8 100644 --- a/src/snapshot-tests/__fixtures__/mcp/text/coverage/get-coverage-report--success.txt +++ b/src/snapshot-tests/__fixtures__/mcp/text/coverage/get-coverage-report--success.txt @@ -1,10 +1,10 @@ 📊 Coverage Report -ℹ️ Overall: 94.9% (371/391 lines) +ℹ️ Overall: 4.6% (18/391 lines) Targets - CalculatorAppTests.xctest: 94.9% (371/391 lines) + CalculatorAppTests.xctest: 4.6% (18/391 lines) Next steps: 1. View file-level coverage: get_file_coverage({ xcresultPath: "/TestResults.xcresult" }) diff --git a/src/snapshot-tests/__fixtures__/mcp/text/coverage/get-file-coverage--success.txt b/src/snapshot-tests/__fixtures__/mcp/text/coverage/get-file-coverage--success.txt index 2d539e0a3..5742a6109 100644 --- a/src/snapshot-tests/__fixtures__/mcp/text/coverage/get-file-coverage--success.txt +++ b/src/snapshot-tests/__fixtures__/mcp/text/coverage/get-file-coverage--success.txt @@ -3,24 +3,37 @@ File: example_projects/iOS_Calculator/CalculatorAppPackage/Sources/CalculatorAppFeature/CalculatorService.swift -ℹ️ Coverage: 83.1% (157/189 lines) +ℹ️ Coverage: 55.0% (104/189 lines) -🔴 Not Covered (7 functions, 22 lines) +🔴 Not Covered (19 functions, 70 lines) L159 CalculatorService.deleteLastDigit() -- 0/16 lines + L63 CalculatorService.inputDecimal() -- 0/14 lines + L148 CalculatorService.clear() -- 0/10 lines + L134 CalculatorService.toggleSign() -- 0/6 lines + L141 CalculatorService.percentage() -- 0/6 lines + L178 CalculatorService.setError(_:) -- 0/5 lines L58 implicit closure #2 in CalculatorService.inputNumber(_:) -- 0/1 lines + L68 implicit closure #1 in CalculatorService.inputDecimal() -- 0/1 lines + L94 implicit closure #1 in CalculatorService.calculate() -- 0/1 lines L98 implicit closure #3 in CalculatorService.calculate() -- 0/1 lines L99 implicit closure #4 in CalculatorService.calculate() -- 0/1 lines L162 implicit closure #1 in CalculatorService.deleteLastDigit() -- 0/1 lines L172 implicit closure #2 in CalculatorService.deleteLastDigit() -- 0/1 lines + L209 implicit closure #3 in CalculatorService.formatNumber(_:) -- 0/1 lines L214 implicit closure #4 in CalculatorService.formatNumber(_:) -- 0/1 lines + L221 CalculatorService.currentValue.getter -- 0/1 lines + L222 CalculatorService.previousValue.getter -- 0/1 lines + L223 CalculatorService.currentOperation.getter -- 0/1 lines + L224 CalculatorService.willResetDisplay.getter -- 0/1 lines -🟡 Partial Coverage (4 functions) +🟡 Partial Coverage (5 functions) L184 CalculatorService.updateExpressionDisplay() -- 80.0% (8/10 lines) + L93 CalculatorService.calculate() -- 84.2% (32/38 lines) + L47 CalculatorService.inputNumber(_:) -- 85.7% (12/14 lines) + L78 CalculatorService.setOperation(_:) -- 85.7% (12/14 lines) L195 CalculatorService.formatNumber(_:) -- 85.7% (18/21 lines) - L93 CalculatorService.calculate() -- 89.5% (34/38 lines) - L63 CalculatorService.inputDecimal() -- 92.9% (13/14 lines) -🟢 Full Coverage (28 functions) -- all at 100% +🟢 Full Coverage (15 functions) -- all at 100% Next steps: 1. View overall coverage: get_coverage_report({ xcresultPath: "/TestResults.xcresult" }) diff --git a/src/snapshot-tests/__fixtures__/mcp/text/debugging/lldb-command--success.txt b/src/snapshot-tests/__fixtures__/mcp/text/debugging/lldb-command--success.txt index 8894bef41..88183d729 100644 --- a/src/snapshot-tests/__fixtures__/mcp/text/debugging/lldb-command--success.txt +++ b/src/snapshot-tests/__fixtures__/mcp/text/debugging/lldb-command--success.txt @@ -4,9 +4,4 @@ ✅ Command executed Output: - Current breakpoints: - 1: file = 'ContentView.swift', line = 42, exact_match = 0, locations = - Names: - dap - - 1.1: where = CalculatorApp.debug.dylib`closure #1 in closure #1 in closure #1 in ContentView.body.getter + at ContentView.swift:42:31, address = , resolved, hit count = 0 + No breakpoints currently set. diff --git a/src/snapshot-tests/__fixtures__/mcp/text/device/build--error-compiler.txt b/src/snapshot-tests/__fixtures__/mcp/text/device/build--error-compiler.txt index c720ef98e..c31a09d49 100644 --- a/src/snapshot-tests/__fixtures__/mcp/text/device/build--error-compiler.txt +++ b/src/snapshot-tests/__fixtures__/mcp/text/device/build--error-compiler.txt @@ -4,7 +4,7 @@ Errors (1): ✗ cannot convert value of type 'String' to specified type 'Int' - /example_projects/iOS_Calculator/CalculatorApp/CalculatorApp.swift:33 + /example_projects/iOS_Calculator/CalculatorApp/CalculatorApp.swift:53 ❌ Build failed. (⏱️ ) └ Files: diff --git a/src/snapshot-tests/__fixtures__/mcp/text/device/build--error-prepared-tests-wrong-scheme.txt b/src/snapshot-tests/__fixtures__/mcp/text/device/build--error-prepared-tests-wrong-scheme.txt new file mode 100644 index 000000000..a65c33fe1 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/mcp/text/device/build--error-prepared-tests-wrong-scheme.txt @@ -0,0 +1,10 @@ + +🔨 Build + +Errors (1): + + ✗ The workspace named "CalculatorApp" does not contain a scheme named "NONEXISTENT". The "-list" option can be used to find the names of the schemes in the workspace. + +❌ Build failed. (⏱️ ) + └ Files: + └── ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/build_device__pid.log — Build Logs diff --git a/src/snapshot-tests/__fixtures__/mcp/text/device/build--success-prepared-tests-generic.txt b/src/snapshot-tests/__fixtures__/mcp/text/device/build--success-prepared-tests-generic.txt new file mode 100644 index 000000000..9aaa9f513 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/mcp/text/device/build--success-prepared-tests-generic.txt @@ -0,0 +1,8 @@ + +🔨 Build + +✅ Build succeeded. (⏱️ ) + └ Files: + ├── /Generic CalculatorApp Tests.xctestproducts/ — Test Products + │ └── Tests/0/CalculatorApp.xctestrun — XCTest Run + └── ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/build_device__pid.log — Build Logs diff --git a/src/snapshot-tests/__fixtures__/mcp/text/device/build--success.txt b/src/snapshot-tests/__fixtures__/mcp/text/device/build--success.txt index 9f8794f70..50f8c540a 100644 --- a/src/snapshot-tests/__fixtures__/mcp/text/device/build--success.txt +++ b/src/snapshot-tests/__fixtures__/mcp/text/device/build--success.txt @@ -6,4 +6,4 @@ └── ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/build_device__pid.log — Build Logs Next steps: -1. Get built device app path: get_device_app_path({ scheme: "CalculatorApp" }) +1. Get built device app path: get_device_app_path({ scheme: "CalculatorApp", derivedDataPath: "" }) diff --git a/src/snapshot-tests/__fixtures__/mcp/text/device/build-and-run--error-compiler.txt b/src/snapshot-tests/__fixtures__/mcp/text/device/build-and-run--error-compiler.txt index dff811af7..218365c3e 100644 --- a/src/snapshot-tests/__fixtures__/mcp/text/device/build-and-run--error-compiler.txt +++ b/src/snapshot-tests/__fixtures__/mcp/text/device/build-and-run--error-compiler.txt @@ -4,7 +4,7 @@ Errors (1): ✗ cannot convert value of type 'String' to specified type 'Int' - /example_projects/iOS_Calculator/CalculatorApp/CalculatorApp.swift:33 + /example_projects/iOS_Calculator/CalculatorApp/CalculatorApp.swift:53 ❌ Build failed. (⏱️ ) └ Files: diff --git a/src/snapshot-tests/__fixtures__/mcp/text/device/build-and-run--success.txt b/src/snapshot-tests/__fixtures__/mcp/text/device/build-and-run--success.txt index 10d63318d..cd050c55e 100644 --- a/src/snapshot-tests/__fixtures__/mcp/text/device/build-and-run--success.txt +++ b/src/snapshot-tests/__fixtures__/mcp/text/device/build-and-run--success.txt @@ -12,9 +12,8 @@ ├ Bundle ID: io.sentry.calculatorapp ├ Process ID: └ Files: - └── ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/ - ├── DerivedData/CalculatorApp-/Build/Products/Debug-iphoneos/CalculatorApp.app — App Path - └── logs/build_run_device__pid.log — Build Logs + ├── ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/build_run_device__pid.log — Build Logs + └── /Build/Products/Debug-iphoneos/CalculatorApp.app — App Path Next steps: 1. Stop app on device: stop_app_device({ deviceId: "", processId: }) diff --git a/src/snapshot-tests/__fixtures__/mcp/text/device/get-app-path--success.txt b/src/snapshot-tests/__fixtures__/mcp/text/device/get-app-path--success.txt index 9fc79b8fd..bb82a7dcf 100644 --- a/src/snapshot-tests/__fixtures__/mcp/text/device/get-app-path--success.txt +++ b/src/snapshot-tests/__fixtures__/mcp/text/device/get-app-path--success.txt @@ -3,9 +3,9 @@ ✅ Success └ Files: - └── ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/CalculatorApp-/Build/Products/Debug-iphoneos/CalculatorApp.app — App Path + └── /Build/Products/Debug-iphoneos/CalculatorApp.app — App Path Next steps: -1. Get bundle ID: get_app_bundle_id({ appPath: "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/CalculatorApp-/Build/Products/Debug-iphoneos/CalculatorApp.app" }) -2. Install app on device: install_app_device({ deviceId: "DEVICE_UDID", appPath: "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/CalculatorApp-/Build/Products/Debug-iphoneos/CalculatorApp.app" }) +1. Get bundle ID: get_app_bundle_id({ appPath: "/Build/Products/Debug-iphoneos/CalculatorApp.app" }) +2. Install app on device: install_app_device({ deviceId: "DEVICE_UDID", appPath: "/Build/Products/Debug-iphoneos/CalculatorApp.app" }) 3. Launch app on device: launch_app_device({ deviceId: "DEVICE_UDID", bundleId: "BUNDLE_ID" }) diff --git a/src/snapshot-tests/__fixtures__/mcp/text/device/test--error-compiler.txt b/src/snapshot-tests/__fixtures__/mcp/text/device/test--error-compiler.txt index 2bb489471..ff6a7bdf4 100644 --- a/src/snapshot-tests/__fixtures__/mcp/text/device/test--error-compiler.txt +++ b/src/snapshot-tests/__fixtures__/mcp/text/device/test--error-compiler.txt @@ -7,10 +7,8 @@ Discovered 1 test(s): Errors (1): ✗ cannot convert value of type 'String' to specified type 'Int' - /example_projects/iOS_Calculator/CalculatorApp/CalculatorApp.swift:33 + /example_projects/iOS_Calculator/CalculatorApp/CalculatorApp.swift:53 ❌ Test failed. (⏱️ ) └ Files: - └── ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/ - ├── logs/test_device__pid.log — Build Logs - └── result-bundles/test_device__pid.xcresult — Result Bundle + └── ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/test_device__pid.log — Build Logs diff --git a/src/snapshot-tests/__fixtures__/mcp/text/device/test--failure.txt b/src/snapshot-tests/__fixtures__/mcp/text/device/test--failure.txt index d4fe8e5d6..0ab31d224 100644 --- a/src/snapshot-tests/__fixtures__/mcp/text/device/test--failure.txt +++ b/src/snapshot-tests/__fixtures__/mcp/text/device/test--failure.txt @@ -22,4 +22,5 @@ Test Failures (2): └ Files: └── ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/ ├── logs/test_device__pid.log — Build Logs - └── result-bundles/test_device__pid.xcresult — Result Bundle + ├── result-bundles/test_device__pid.xcresult — Result Bundle + └── test-products/test_device__pid.xctestproducts — Test Products diff --git a/src/snapshot-tests/__fixtures__/mcp/text/device/test--success.txt b/src/snapshot-tests/__fixtures__/mcp/text/device/test--success.txt index aeba621eb..69c78ee8d 100644 --- a/src/snapshot-tests/__fixtures__/mcp/text/device/test--success.txt +++ b/src/snapshot-tests/__fixtures__/mcp/text/device/test--success.txt @@ -8,4 +8,5 @@ Discovered 1 test(s): └ Files: └── ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/ ├── logs/test_device__pid.log — Build Logs - └── result-bundles/test_device__pid.xcresult — Result Bundle + ├── result-bundles/test_device__pid.xcresult — Result Bundle + └── test-products/test_device__pid.xctestproducts — Test Products diff --git a/src/snapshot-tests/__fixtures__/mcp/text/macos/build--error-prepared-tests-wrong-scheme.txt b/src/snapshot-tests/__fixtures__/mcp/text/macos/build--error-prepared-tests-wrong-scheme.txt new file mode 100644 index 000000000..0da1f22f8 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/mcp/text/macos/build--error-prepared-tests-wrong-scheme.txt @@ -0,0 +1,10 @@ + +🔨 Build + +Errors (1): + + ✗ The project named "MCPTest" does not contain a scheme named "NONEXISTENT". The "-list" option can be used to find the names of the schemes in the project. + +❌ Build failed. (⏱️ ) + └ Files: + └── ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/build_macos__pid.log — Build Logs diff --git a/src/snapshot-tests/__fixtures__/mcp/text/macos/build--success-prepared-tests.txt b/src/snapshot-tests/__fixtures__/mcp/text/macos/build--success-prepared-tests.txt new file mode 100644 index 000000000..7aae427ba --- /dev/null +++ b/src/snapshot-tests/__fixtures__/mcp/text/macos/build--success-prepared-tests.txt @@ -0,0 +1,11 @@ + +🔨 Build + +✅ Build succeeded. (⏱️ ) + └ Files: + ├── /MCPTest Tests.xctestproducts/ — Test Products + │ └── Tests/0/MCPTest.xctestrun — XCTest Run + └── ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/build_macos__pid.log — Build Logs + +Next steps: +1. Run prepared tests: test_macos({ testProductsPath: "/MCPTest Tests.xctestproducts" }) diff --git a/src/snapshot-tests/__fixtures__/mcp/text/macos/build--success.txt b/src/snapshot-tests/__fixtures__/mcp/text/macos/build--success.txt index 6d3ee03ab..5aba95064 100644 --- a/src/snapshot-tests/__fixtures__/mcp/text/macos/build--success.txt +++ b/src/snapshot-tests/__fixtures__/mcp/text/macos/build--success.txt @@ -7,4 +7,4 @@ └── ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/build_macos__pid.log — Build Logs Next steps: -1. Get built macOS app path: get_mac_app_path({ scheme: "MCPTest" }) +1. Get built macOS app path: get_mac_app_path({ scheme: "MCPTest", derivedDataPath: "/DerivedData" }) diff --git a/src/snapshot-tests/__fixtures__/mcp/text/macos/build-and-run--success.txt b/src/snapshot-tests/__fixtures__/mcp/text/macos/build-and-run--success.txt index f45718388..f695b3d92 100644 --- a/src/snapshot-tests/__fixtures__/mcp/text/macos/build-and-run--success.txt +++ b/src/snapshot-tests/__fixtures__/mcp/text/macos/build-and-run--success.txt @@ -11,9 +11,8 @@ ├ Bundle ID: io.sentry.MCPTest.macOS ├ Process ID: └ Files: - └── ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/ - ├── DerivedData/MCPTest-/Build/Products/Debug/MCPTest.app — App Path - └── logs/build_run_macos__pid.log — Build Logs + ├── ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/build_run_macos__pid.log — Build Logs + └── /DerivedData/Build/Products/Debug/MCPTest.app — App Path Next steps: 1. Interact with the launched app in the foreground diff --git a/src/snapshot-tests/__fixtures__/mcp/text/macos/get-app-path--success.txt b/src/snapshot-tests/__fixtures__/mcp/text/macos/get-app-path--success.txt index 48d88a45c..cd6e80ab1 100644 --- a/src/snapshot-tests/__fixtures__/mcp/text/macos/get-app-path--success.txt +++ b/src/snapshot-tests/__fixtures__/mcp/text/macos/get-app-path--success.txt @@ -3,8 +3,8 @@ ✅ Success └ Files: - └── ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/MCPTest-/Build/Products/Debug/MCPTest.app — App Path + └── /DerivedData/Build/Products/Debug/MCPTest.app — App Path Next steps: -1. Get bundle ID: get_mac_bundle_id({ appPath: "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/MCPTest-/Build/Products/Debug/MCPTest.app" }) -2. Launch app: launch_mac_app({ appPath: "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/MCPTest-/Build/Products/Debug/MCPTest.app" }) +1. Get bundle ID: get_mac_bundle_id({ appPath: "/DerivedData/Build/Products/Debug/MCPTest.app" }) +2. Launch app: launch_mac_app({ appPath: "/DerivedData/Build/Products/Debug/MCPTest.app" }) diff --git a/src/snapshot-tests/__fixtures__/mcp/text/macos/get-macos-bundle-id--error-missing-app.txt b/src/snapshot-tests/__fixtures__/mcp/text/macos/get-macos-bundle-id--error-missing-app.txt index fac62d2f5..16dde937c 100644 --- a/src/snapshot-tests/__fixtures__/mcp/text/macos/get-macos-bundle-id--error-missing-app.txt +++ b/src/snapshot-tests/__fixtures__/mcp/text/macos/get-macos-bundle-id--error-missing-app.txt @@ -3,6 +3,6 @@ Errors (1): - ✗ File not found: '/nonexistent/path/Fake.app'. Please check the path and try again. + ✗ File not found: '/missing.app'. Please check the path and try again. ❌ Failed to get macOS bundle ID. diff --git a/src/snapshot-tests/__fixtures__/mcp/text/macos/stop--success.txt b/src/snapshot-tests/__fixtures__/mcp/text/macos/stop--success.txt index 8703f9407..5bcead8c8 100644 --- a/src/snapshot-tests/__fixtures__/mcp/text/macos/stop--success.txt +++ b/src/snapshot-tests/__fixtures__/mcp/text/macos/stop--success.txt @@ -1,4 +1,6 @@ 🛑 Stop macOS App + App: PID + ✅ App stopped successfully diff --git a/src/snapshot-tests/__fixtures__/mcp/text/macos/test--error-compiler.txt b/src/snapshot-tests/__fixtures__/mcp/text/macos/test--error-compiler.txt index 12a1f9e69..a88ecf3e7 100644 --- a/src/snapshot-tests/__fixtures__/mcp/text/macos/test--error-compiler.txt +++ b/src/snapshot-tests/__fixtures__/mcp/text/macos/test--error-compiler.txt @@ -12,6 +12,4 @@ Errors (1): ❌ Test failed. (⏱️ ) └ Files: - └── ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/ - ├── logs/test_macos__pid.log — Build Logs - └── result-bundles/test_macos__pid.xcresult — Result Bundle + └── ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/test_macos__pid.log — Build Logs diff --git a/src/snapshot-tests/__fixtures__/mcp/text/macos/test--error-wrong-scheme.txt b/src/snapshot-tests/__fixtures__/mcp/text/macos/test--error-wrong-scheme.txt index 28957ac8b..f252f8301 100644 --- a/src/snapshot-tests/__fixtures__/mcp/text/macos/test--error-wrong-scheme.txt +++ b/src/snapshot-tests/__fixtures__/mcp/text/macos/test--error-wrong-scheme.txt @@ -7,6 +7,4 @@ Errors (1): ❌ Test failed. (⏱️ ) └ Files: - └── ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/ - ├── logs/test_macos__pid.log — Build Logs - └── result-bundles/test_macos__pid.xcresult — Result Bundle + └── ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/test_macos__pid.log — Build Logs diff --git a/src/snapshot-tests/__fixtures__/mcp/text/macos/test--failure.txt b/src/snapshot-tests/__fixtures__/mcp/text/macos/test--failure.txt index c7db60d85..f68d9e253 100644 --- a/src/snapshot-tests/__fixtures__/mcp/text/macos/test--failure.txt +++ b/src/snapshot-tests/__fixtures__/mcp/text/macos/test--failure.txt @@ -9,14 +9,15 @@ Discovered 4 test(s): Test Failures (2): - ✗ MCPTestsXCTests / testDeliberateFailure(): XCTAssertTrue failed - This test is designed to fail for snapshot testing - MCPTestsXCTests.swift:11 - ✗ MCPTestTests / deliberateFailure(): Expectation failed: 1 == 2: This test is designed to fail for snapshot testing MCPTestTests.swift:11 + ✗ MCPTestsXCTests / testDeliberateFailure(): XCTAssertTrue failed - This test is designed to fail for snapshot testing + MCPTestsXCTests.swift:11 + ❌ tests failed, passed, skipped (⏱️ ) └ Files: └── ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/ ├── logs/test_macos__pid.log — Build Logs - └── result-bundles/test_macos__pid.xcresult — Result Bundle + ├── result-bundles/test_macos__pid.xcresult — Result Bundle + └── test-products/test_macos__pid.xctestproducts — Test Products diff --git a/src/snapshot-tests/__fixtures__/mcp/text/macos/test--success.txt b/src/snapshot-tests/__fixtures__/mcp/text/macos/test--success.txt index bef7e6229..03ced74f9 100644 --- a/src/snapshot-tests/__fixtures__/mcp/text/macos/test--success.txt +++ b/src/snapshot-tests/__fixtures__/mcp/text/macos/test--success.txt @@ -9,4 +9,5 @@ Discovered 2 test(s): └ Files: └── ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/ ├── logs/test_macos__pid.log — Build Logs - └── result-bundles/test_macos__pid.xcresult — Result Bundle + ├── result-bundles/test_macos__pid.xcresult — Result Bundle + └── test-products/test_macos__pid.xctestproducts — Test Products diff --git a/src/snapshot-tests/__fixtures__/mcp/text/simulator/build--error-compiler.txt b/src/snapshot-tests/__fixtures__/mcp/text/simulator/build--error-compiler.txt index e94f9f3a0..4e43a90c6 100644 --- a/src/snapshot-tests/__fixtures__/mcp/text/simulator/build--error-compiler.txt +++ b/src/snapshot-tests/__fixtures__/mcp/text/simulator/build--error-compiler.txt @@ -4,7 +4,7 @@ Errors (1): ✗ cannot convert value of type 'String' to specified type 'Int' - /example_projects/iOS_Calculator/CalculatorApp/CalculatorApp.swift:33 + /example_projects/iOS_Calculator/CalculatorApp/CalculatorApp.swift:53 ❌ Build failed. (⏱️ ) └ Files: diff --git a/src/snapshot-tests/__fixtures__/mcp/text/simulator/build--error-prepared-tests-wrong-scheme.txt b/src/snapshot-tests/__fixtures__/mcp/text/simulator/build--error-prepared-tests-wrong-scheme.txt new file mode 100644 index 000000000..2bf85ad02 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/mcp/text/simulator/build--error-prepared-tests-wrong-scheme.txt @@ -0,0 +1,10 @@ + +🔨 Build + +Errors (1): + + ✗ The workspace named "CalculatorApp" does not contain a scheme named "NONEXISTENT". The "-list" option can be used to find the names of the schemes in the workspace. + +❌ Build failed. (⏱️ ) + └ Files: + └── ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/build_sim__pid.log — Build Logs diff --git a/src/snapshot-tests/__fixtures__/mcp/text/simulator/build--success-prepared-tests.txt b/src/snapshot-tests/__fixtures__/mcp/text/simulator/build--success-prepared-tests.txt new file mode 100644 index 000000000..2c2708841 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/mcp/text/simulator/build--success-prepared-tests.txt @@ -0,0 +1,11 @@ + +🔨 Build + +✅ Build succeeded. (⏱️ ) + └ Files: + ├── /CalculatorApp Tests.xctestproducts/ — Test Products + │ └── Tests/0/CalculatorApp.xctestrun — XCTest Run + └── ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/build_sim__pid.log — Build Logs + +Next steps: +1. Run prepared tests: test_sim({ testProductsPath: "/CalculatorApp Tests.xctestproducts", simulatorId: "" }) diff --git a/src/snapshot-tests/__fixtures__/mcp/text/simulator/build--success.txt b/src/snapshot-tests/__fixtures__/mcp/text/simulator/build--success.txt index cb5804c08..02cab56e8 100644 --- a/src/snapshot-tests/__fixtures__/mcp/text/simulator/build--success.txt +++ b/src/snapshot-tests/__fixtures__/mcp/text/simulator/build--success.txt @@ -6,4 +6,4 @@ └── ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/build_sim__pid.log — Build Logs Next steps: -1. Get built app path in simulator derived data: get_sim_app_path({ simulatorName: "iPhone 17", scheme: "CalculatorApp", platform: "iOS Simulator" }) +1. Get built app path in simulator derived data: get_sim_app_path({ simulatorId: "", scheme: "CalculatorApp", platform: "iOS Simulator", derivedDataPath: "" }) diff --git a/src/snapshot-tests/__fixtures__/mcp/text/simulator/build-and-run--error-compiler.txt b/src/snapshot-tests/__fixtures__/mcp/text/simulator/build-and-run--error-compiler.txt index 3dcc58bda..7fad89001 100644 --- a/src/snapshot-tests/__fixtures__/mcp/text/simulator/build-and-run--error-compiler.txt +++ b/src/snapshot-tests/__fixtures__/mcp/text/simulator/build-and-run--error-compiler.txt @@ -4,7 +4,7 @@ Errors (1): ✗ cannot convert value of type 'String' to specified type 'Int' - /example_projects/iOS_Calculator/CalculatorApp/CalculatorApp.swift:33 + /example_projects/iOS_Calculator/CalculatorApp/CalculatorApp.swift:53 ❌ Build failed. (⏱️ ) └ Files: diff --git a/src/snapshot-tests/__fixtures__/mcp/text/simulator/build-and-run--success.txt b/src/snapshot-tests/__fixtures__/mcp/text/simulator/build-and-run--success.txt index 354429bd5..015abda20 100644 --- a/src/snapshot-tests/__fixtures__/mcp/text/simulator/build-and-run--success.txt +++ b/src/snapshot-tests/__fixtures__/mcp/text/simulator/build-and-run--success.txt @@ -14,12 +14,11 @@ ├ Bundle ID: io.sentry.calculatorapp ├ Process ID: └ Files: - └── ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/ - ├── DerivedData/CalculatorApp-/Build/Products/Debug-iphonesimulator/CalculatorApp.app — App Path - └── logs/ - ├── build_run_sim__pid.log — Build Logs - ├── io.sentry.calculatorapp__pid.log — Runtime Logs - └── io.sentry.calculatorapp_oslog__pid.log — OSLog + ├── ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/ + │ ├── build_run_sim__pid.log — Build Logs + │ ├── io.sentry.calculatorapp__pid.log — Runtime Logs + │ └── io.sentry.calculatorapp_oslog__pid.log — OSLog + └── /Build/Products/Debug-iphonesimulator/CalculatorApp.app — App Path Next steps: 1. Stop app in simulator: stop_app_sim({ simulatorId: "", bundleId: "io.sentry.calculatorapp" }) diff --git a/src/snapshot-tests/__fixtures__/mcp/text/simulator/get-app-path--success.txt b/src/snapshot-tests/__fixtures__/mcp/text/simulator/get-app-path--success.txt index 7efb25cf8..525822f4f 100644 --- a/src/snapshot-tests/__fixtures__/mcp/text/simulator/get-app-path--success.txt +++ b/src/snapshot-tests/__fixtures__/mcp/text/simulator/get-app-path--success.txt @@ -3,10 +3,10 @@ ✅ Get app path successful (⏱️ ) └ Files: - └── ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/CalculatorApp-/Build/Products/Debug-iphonesimulator/CalculatorApp.app — App Path + └── /Build/Products/Debug-iphonesimulator/CalculatorApp.app — App Path Next steps: -1. Get bundle ID: get_app_bundle_id({ appPath: "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/CalculatorApp-/Build/Products/Debug-iphonesimulator/CalculatorApp.app" }) +1. Get bundle ID: get_app_bundle_id({ appPath: "/Build/Products/Debug-iphonesimulator/CalculatorApp.app" }) 2. Boot simulator: boot_sim({ simulatorId: "SIMULATOR_UUID" }) -3. Install app: install_app_sim({ simulatorId: "SIMULATOR_UUID", appPath: "~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/DerivedData/CalculatorApp-/Build/Products/Debug-iphonesimulator/CalculatorApp.app" }) +3. Install app: install_app_sim({ simulatorId: "SIMULATOR_UUID", appPath: "/Build/Products/Debug-iphonesimulator/CalculatorApp.app" }) 4. Launch app: launch_app_sim({ simulatorId: "SIMULATOR_UUID", bundleId: "BUNDLE_ID" }) diff --git a/src/snapshot-tests/__fixtures__/mcp/text/simulator/test--error-compiler.txt b/src/snapshot-tests/__fixtures__/mcp/text/simulator/test--error-compiler.txt index a9d7a15e7..ea10a5158 100644 --- a/src/snapshot-tests/__fixtures__/mcp/text/simulator/test--error-compiler.txt +++ b/src/snapshot-tests/__fixtures__/mcp/text/simulator/test--error-compiler.txt @@ -7,7 +7,7 @@ Discovered 1 test(s): Errors (1): ✗ cannot convert value of type 'String' to specified type 'Int' - /example_projects/iOS_Calculator/CalculatorApp/CalculatorApp.swift:33 + /example_projects/iOS_Calculator/CalculatorApp/CalculatorApp.swift:53 ❌ Test failed. (⏱️ ) └ Files: diff --git a/src/snapshot-tests/__fixtures__/mcp/text/simulator/test--failure.txt b/src/snapshot-tests/__fixtures__/mcp/text/simulator/test--failure.txt index 6edf231d7..513c42ce0 100644 --- a/src/snapshot-tests/__fixtures__/mcp/text/simulator/test--failure.txt +++ b/src/snapshot-tests/__fixtures__/mcp/text/simulator/test--failure.txt @@ -27,4 +27,5 @@ Test Failures (3): └ Files: └── ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/ ├── logs/test_sim__pid.log — Build Logs - └── result-bundles/test_sim__pid.xcresult — Result Bundle + ├── result-bundles/test_sim__pid.xcresult — Result Bundle + └── test-products/test_sim__pid.xctestproducts — Test Products diff --git a/src/snapshot-tests/__fixtures__/mcp/text/simulator/test--success.txt b/src/snapshot-tests/__fixtures__/mcp/text/simulator/test--success.txt index d32b486b8..7b5337002 100644 --- a/src/snapshot-tests/__fixtures__/mcp/text/simulator/test--success.txt +++ b/src/snapshot-tests/__fixtures__/mcp/text/simulator/test--success.txt @@ -8,4 +8,5 @@ Discovered 1 test(s): └ Files: └── ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/ ├── logs/test_sim__pid.log — Build Logs - └── result-bundles/test_sim__pid.xcresult — Result Bundle + ├── result-bundles/test_sim__pid.xcresult — Result Bundle + └── test-products/test_sim__pid.xctestproducts — Test Products diff --git a/src/snapshot-tests/__fixtures__/mcp/text/swift-package/build--error-bad-path.txt b/src/snapshot-tests/__fixtures__/mcp/text/swift-package/build--error-bad-path.txt index 466e7b1a2..a050d4705 100644 --- a/src/snapshot-tests/__fixtures__/mcp/text/swift-package/build--error-bad-path.txt +++ b/src/snapshot-tests/__fixtures__/mcp/text/swift-package/build--error-bad-path.txt @@ -3,7 +3,7 @@ Errors (1): - ✗ chdir error: No such file or directory (2): /example_projects/NONEXISTENT + ✗ chdir error: No such file or directory (2): /spm/NONEXISTENT ❌ Build failed. (⏱️ ) └ Files: diff --git a/src/snapshot-tests/__fixtures__/mcp/text/swift-package/clean--error-bad-path.txt b/src/snapshot-tests/__fixtures__/mcp/text/swift-package/clean--error-bad-path.txt index 0363983ec..ebe3a0c50 100644 --- a/src/snapshot-tests/__fixtures__/mcp/text/swift-package/clean--error-bad-path.txt +++ b/src/snapshot-tests/__fixtures__/mcp/text/swift-package/clean--error-bad-path.txt @@ -3,6 +3,6 @@ Errors (1): - ✗ chdir error: No such file or directory (2): /example_projects/NONEXISTENT + ✗ chdir error: No such file or directory (2): /spm/NONEXISTENT ❌ Swift package clean failed. diff --git a/src/snapshot-tests/__fixtures__/mcp/text/swift-package/list--success.txt b/src/snapshot-tests/__fixtures__/mcp/text/swift-package/list--success.txt index 17a38338f..d8f1cd25a 100644 --- a/src/snapshot-tests/__fixtures__/mcp/text/swift-package/list--success.txt +++ b/src/snapshot-tests/__fixtures__/mcp/text/swift-package/list--success.txt @@ -5,4 +5,4 @@ Running Processes (1): 🟢 spm PID: | Uptime: - Package: /example_projects/spm + Package: /spm diff --git a/src/snapshot-tests/__fixtures__/mcp/text/swift-package/run--success.txt b/src/snapshot-tests/__fixtures__/mcp/text/swift-package/run--success.txt index 5b38814ec..abfb0ae43 100644 --- a/src/snapshot-tests/__fixtures__/mcp/text/swift-package/run--success.txt +++ b/src/snapshot-tests/__fixtures__/mcp/text/swift-package/run--success.txt @@ -5,8 +5,8 @@ ✅ Build & Run complete ├ Process ID: └ Files: - ├── ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/build_run_spm__pid.log — Build Logs - └── example_projects/spm/.build/arm64-apple-macosx/debug/spm — App Path + ├── /spm/.build//debug/spm — App Path + └── ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/build_run_spm__pid.log — Build Logs Output Hello, world! diff --git a/src/snapshot-tests/__fixtures__/mcp/text/swift-package/test--error-bad-path.txt b/src/snapshot-tests/__fixtures__/mcp/text/swift-package/test--error-bad-path.txt index 2d2fe3081..e941ccfb7 100644 --- a/src/snapshot-tests/__fixtures__/mcp/text/swift-package/test--error-bad-path.txt +++ b/src/snapshot-tests/__fixtures__/mcp/text/swift-package/test--error-bad-path.txt @@ -3,7 +3,7 @@ Errors (1): - ✗ chdir error: No such file or directory (2): /example_projects/NONEXISTENT + ✗ chdir error: No such file or directory (2): /spm/NONEXISTENT ❌ Test failed. (⏱️ ) └ Files: diff --git a/src/snapshot-tests/__fixtures__/mcp/text/swift-package/test--failure.txt b/src/snapshot-tests/__fixtures__/mcp/text/swift-package/test--failure.txt index 821bb760b..6c0f814c9 100644 --- a/src/snapshot-tests/__fixtures__/mcp/text/swift-package/test--failure.txt +++ b/src/snapshot-tests/__fixtures__/mcp/text/swift-package/test--failure.txt @@ -3,13 +3,13 @@ Test Failures (2): - ✗ CalculatorAppTests / testCalculatorServiceFailure: XCTAssertEqual failed: ("0") is not equal to ("999") - This test should fail - display should be 0, not 999 - /example_projects/spm/Tests/TestLibTests/SimpleTests.swift:49 - ✗ (Unknown Suite) / test: Expectation failed: Bool(false) Test failed SimpleTests.swift:57 + ✗ CalculatorAppTests / testCalculatorServiceFailure: XCTAssertEqual failed: ("0") is not equal to ("999") - This test should fail - display should be 0, not 999 + /spm/Tests/TestLibTests/SimpleTests.swift:49 + ❌ tests failed, passed, skipped (⏱️ ) └ Files: └── ~/Library/Developer/XcodeBuildMCP/workspaces/XcodeBuildMCP-/logs/swift_package_test__pid.log — Build Logs diff --git a/src/snapshot-tests/__fixtures__/mcp/text/ui-automation/snapshot-ui--success.txt b/src/snapshot-tests/__fixtures__/mcp/text/ui-automation/snapshot-ui--success.txt index 2ee723a91..2a4af955d 100644 --- a/src/snapshot-tests/__fixtures__/mcp/text/ui-automation/snapshot-ui--success.txt +++ b/src/snapshot-tests/__fixtures__/mcp/text/ui-automation/snapshot-ui--success.txt @@ -27,7 +27,7 @@ Tips - Refs are snapshot-specific; after snapshot_ui or wait_for_ui, use refs from the latest output. - Use wait_for_ui for text/assertions or changing UI. -✅ Runtime UI snapshot captured with 28 elements, 19 likely targets, and 0 scroll areas. +✅ Runtime UI snapshot captured with elements, likely targets, and scroll areas. Next steps: 1. Refresh after layout changes: snapshot_ui({ simulatorId: "" }) diff --git a/src/snapshot-tests/__fixtures__/mcp/text/ui-automation/swipe--error-not-actionable.txt b/src/snapshot-tests/__fixtures__/mcp/text/ui-automation/swipe--error-not-actionable.txt index 51d69da9e..16fa6dbfa 100644 --- a/src/snapshot-tests/__fixtures__/mcp/text/ui-automation/swipe--error-not-actionable.txt +++ b/src/snapshot-tests/__fixtures__/mcp/text/ui-automation/swipe--error-not-actionable.txt @@ -3,8 +3,8 @@ Recovery Code: TARGET_NOT_ACTIONABLE - Message: Element ref 'e3' does not support 'swipeWithin'. - Element: e3 + Message: Element ref '' does not support 'swipeWithin'. + Element: Hint: Choose an elementRef that lists the required action, or refresh with snapshot_ui. -❌ Element ref 'e3' does not support 'swipeWithin'. +❌ Element ref '' does not support 'swipeWithin'. diff --git a/src/snapshot-tests/__fixtures__/mcp/text/ui-automation/swipe--success.txt b/src/snapshot-tests/__fixtures__/mcp/text/ui-automation/swipe--success.txt index 868cf4400..c66def209 100644 --- a/src/snapshot-tests/__fixtures__/mcp/text/ui-automation/swipe--success.txt +++ b/src/snapshot-tests/__fixtures__/mcp/text/ui-automation/swipe--success.txt @@ -4,5 +4,4 @@ ✅ Swipe up within elementRef simulated successfully. Next steps: -1. Batch same-screen taps: batch({ simulatorId: "", steps: [{"action":"tap","elementRef":""},{"action":"tap","elementRef":""}] }) -2. Tap an elementRef: tap({ simulatorId: "", elementRef: "" }) +1. diff --git a/src/snapshot-tests/__fixtures__/mcp/text/ui-automation/type-text--error-not-actionable.txt b/src/snapshot-tests/__fixtures__/mcp/text/ui-automation/type-text--error-not-actionable.txt index 93cf24775..0f1d6359c 100644 --- a/src/snapshot-tests/__fixtures__/mcp/text/ui-automation/type-text--error-not-actionable.txt +++ b/src/snapshot-tests/__fixtures__/mcp/text/ui-automation/type-text--error-not-actionable.txt @@ -3,8 +3,8 @@ Recovery Code: TARGET_NOT_ACTIONABLE - Message: Element ref 'e3' does not support 'typeText'. - Element: e3 + Message: Element ref '' does not support 'typeText'. + Element: Hint: Choose an elementRef that lists the required action, or refresh with snapshot_ui. -❌ Element ref 'e3' does not support 'typeText'. +❌ Element ref '' does not support 'typeText'. diff --git a/src/snapshot-tests/__fixtures__/mcp/text/ui-automation/wait-for-ui--success.txt b/src/snapshot-tests/__fixtures__/mcp/text/ui-automation/wait-for-ui--success.txt index 63baa2d7f..d751f5b53 100644 --- a/src/snapshot-tests/__fixtures__/mcp/text/ui-automation/wait-for-ui--success.txt +++ b/src/snapshot-tests/__fixtures__/mcp/text/ui-automation/wait-for-ui--success.txt @@ -30,7 +30,7 @@ Tips - Refs are snapshot-specific; after snapshot_ui or wait_for_ui, use refs from the latest output. - Use wait_for_ui for text/assertions or changing UI. -✅ Wait completed; runtime UI snapshot refreshed with 28 elements, 19 likely targets, and 0 scroll areas. +✅ Wait completed; runtime UI snapshot refreshed with elements, likely targets, and scroll areas. Next steps: 1. Capture a fresh runtime UI snapshot: snapshot_ui({ simulatorId: "" }) diff --git a/src/snapshot-tests/__tests__/fixture-io.test.ts b/src/snapshot-tests/__tests__/fixture-io.test.ts index cce46a848..ca1983c01 100644 --- a/src/snapshot-tests/__tests__/fixture-io.test.ts +++ b/src/snapshot-tests/__tests__/fixture-io.test.ts @@ -1,7 +1,7 @@ import fs from 'node:fs'; import path from 'node:path'; import { afterEach, describe, expect, it } from 'vitest'; -import { expectMatchesFixture, fixturePathFor } from '../fixture-io.ts'; +import { expectMatchesFixture, expectResultMatchesFixture, fixturePathFor } from '../fixture-io.ts'; const workflow = '__fixture_diff_test__'; const scenario = 'block-diff'; @@ -81,3 +81,113 @@ describe('fixture diff formatting', () => { }).toThrowError(/\+\s+3 same\n\+\s+4 same/); }); }); + +describe('snapshot result outcome validation', () => { + it('rejects an unexpected error before update mode can overwrite a success fixture', () => { + fs.mkdirSync(fixtureDir, { recursive: true }); + fs.writeFileSync(fixturePath, 'known success', 'utf8'); + const previousUpdateMode = process.env.UPDATE_SNAPSHOTS; + process.env.UPDATE_SNAPSHOTS = '1'; + + try { + expect(() => { + expectResultMatchesFixture( + { + text: 'normalized error', + rawText: 'raw setup failure', + isError: true, + outcome: 'domain-error', + }, + 'success', + { runtime: 'cli/text', workflow, scenario }, + ); + }).toThrowError(/expected success, received domain-error[\s\S]*raw setup failure/); + expect(fs.readFileSync(fixturePath, 'utf8')).toBe('known success'); + } finally { + if (previousUpdateMode === undefined) { + delete process.env.UPDATE_SNAPSHOTS; + } else { + process.env.UPDATE_SNAPSHOTS = previousUpdateMode; + } + } + }); + + it('writes a fixture after the expected outcome is confirmed', () => { + const previousUpdateMode = process.env.UPDATE_SNAPSHOTS; + process.env.UPDATE_SNAPSHOTS = '1'; + + try { + expectResultMatchesFixture( + { + text: 'expected error', + rawText: 'raw error', + isError: true, + outcome: 'domain-error', + }, + 'error', + { runtime: 'cli/text', workflow, scenario }, + ); + expect(fs.readFileSync(fixturePath, 'utf8')).toBe('expected error'); + } finally { + if (previousUpdateMode === undefined) { + delete process.env.UPDATE_SNAPSHOTS; + } else { + process.env.UPDATE_SNAPSHOTS = previousUpdateMode; + } + } + }); + + it('allows deterministic validation errors to update error fixtures', () => { + const previousUpdateMode = process.env.UPDATE_SNAPSHOTS; + process.env.UPDATE_SNAPSHOTS = '1'; + + try { + expectResultMatchesFixture( + { + text: 'expected validation error', + rawText: 'MCP input validation error', + isError: true, + outcome: 'validation-error', + }, + 'error', + { runtime: 'cli/text', workflow, scenario }, + ); + expect(fs.readFileSync(fixturePath, 'utf8')).toBe('expected validation error'); + } finally { + if (previousUpdateMode === undefined) { + delete process.env.UPDATE_SNAPSHOTS; + } else { + process.env.UPDATE_SNAPSHOTS = previousUpdateMode; + } + } + }); + + it('rejects infrastructure errors before update mode can overwrite an error fixture', () => { + fs.mkdirSync(fixtureDir, { recursive: true }); + fs.writeFileSync(fixturePath, 'known domain error', 'utf8'); + const previousUpdateMode = process.env.UPDATE_SNAPSHOTS; + process.env.UPDATE_SNAPSHOTS = '1'; + + try { + expect(() => { + expectResultMatchesFixture( + { + text: 'normalized infrastructure error', + rawText: 'MCP transport failed', + isError: true, + outcome: 'infrastructure-error', + }, + 'error', + { runtime: 'mcp/text', workflow, scenario }, + ); + }).toThrowError(/expected error, received infrastructure-error[\s\S]*MCP transport failed/); + expect(fs.readFileSync(fixturePath, 'utf8')).toBe('known domain error'); + } finally { + if (previousUpdateMode === undefined) { + delete process.env.UPDATE_SNAPSHOTS; + } else { + process.env.UPDATE_SNAPSHOTS = previousUpdateMode; + } + } + }); +}); diff --git a/src/snapshot-tests/__tests__/json-harness-error-state.test.ts b/src/snapshot-tests/__tests__/json-harness-error-state.test.ts index 573b66347..7cb44d39a 100644 --- a/src/snapshot-tests/__tests__/json-harness-error-state.test.ts +++ b/src/snapshot-tests/__tests__/json-harness-error-state.test.ts @@ -1,6 +1,10 @@ import { describe, expect, it } from 'vitest'; -import { assertCliSnapshotProcessResult, resolveCliJsonSnapshotErrorState } from '../harness.ts'; -import { resolveMcpSnapshotErrorState } from '../mcp-harness.ts'; +import { + assertCliSnapshotProcessResult, + resolveCliJsonSnapshotErrorState, + resolveCliJsonSnapshotOutcome, +} from '../harness.ts'; +import { resolveMcpSnapshotOutcome } from '../mcp-harness.ts'; import type { StructuredOutputEnvelope } from '../../types/structured-output.ts'; const successEnvelope: StructuredOutputEnvelope = { @@ -57,6 +61,8 @@ describe('JSON snapshot harness error state', () => { it('uses CLI process status and envelope.didError when they agree', () => { expect(resolveCliJsonSnapshotErrorState(0, successEnvelope, 'tool')).toBe(false); expect(resolveCliJsonSnapshotErrorState(1, errorEnvelope, 'tool')).toBe(true); + expect(resolveCliJsonSnapshotOutcome(0, successEnvelope, 'tool')).toBe('success'); + expect(resolveCliJsonSnapshotOutcome(1, errorEnvelope, 'tool')).toBe('domain-error'); }); it('rejects null CLI process status', () => { @@ -75,18 +81,33 @@ describe('JSON snapshot harness error state', () => { }); it('uses MCP transport isError and structuredContent.didError when they agree', () => { - expect(resolveMcpSnapshotErrorState(false, false, 'tool')).toBe(false); - expect(resolveMcpSnapshotErrorState(true, true, 'tool')).toBe(true); + expect(resolveMcpSnapshotOutcome(false, false, 'tool')).toBe('success'); + expect(resolveMcpSnapshotOutcome(true, true, 'tool')).toBe('domain-error'); + }); + + it('classifies an MCP error without a domain envelope as an infrastructure failure', () => { + expect(resolveMcpSnapshotOutcome(true, undefined, 'tool')).toBe('infrastructure-error'); + }); + + it('classifies MCP argument validation separately from infrastructure failures', () => { + expect( + resolveMcpSnapshotOutcome( + true, + undefined, + 'tool', + 'MCP error -32602: Input validation error: Invalid arguments for tool', + ), + ).toBe('validation-error'); }); it('rejects MCP transport isError and structuredContent.didError disagreement', () => { - expect(() => resolveMcpSnapshotErrorState(true, false, 'tool')).toThrow( + expect(() => resolveMcpSnapshotOutcome(true, false, 'tool')).toThrow( 'MCP result.isError (true) disagrees with structuredContent.didError (false)', ); - expect(() => resolveMcpSnapshotErrorState(false, true, 'tool')).toThrow( + expect(() => resolveMcpSnapshotOutcome(false, true, 'tool')).toThrow( 'MCP result.isError (false) disagrees with structuredContent.didError (true)', ); - expect(() => resolveMcpSnapshotErrorState(undefined, true, 'tool')).toThrow( + expect(() => resolveMcpSnapshotOutcome(undefined, true, 'tool')).toThrow( 'MCP result.isError (undefined) disagrees with structuredContent.didError (true)', ); }); diff --git a/src/snapshot-tests/__tests__/json-normalize.test.ts b/src/snapshot-tests/__tests__/json-normalize.test.ts index 5b78a1dd1..e71a6434e 100644 --- a/src/snapshot-tests/__tests__/json-normalize.test.ts +++ b/src/snapshot-tests/__tests__/json-normalize.test.ts @@ -104,7 +104,7 @@ describe('normalizeStructuredEnvelope', () => { }); }); - it('preserves simulator diagnostic test failure order while normalizing volatile Swift Testing suite name', () => { + it('sorts diagnostic test failures while normalizing volatile Swift Testing suite names', () => { const envelope: StructuredOutputEnvelope = { schema: 'xcodebuildmcp.output.test-result', schemaVersion: '2', @@ -113,18 +113,18 @@ describe('normalizeStructuredEnvelope', () => { data: { diagnostics: { testFailures: [ - { - suite: 'Calculator Basic Functionality', - test: 'This test should fail to verify error reporting', - message: 'Expectation failed', - location: 'CalculatorServiceTests.swift:37', - }, { suite: 'CalculatorAppTests', test: 'testCalculatorServiceFailure', message: 'XCTAssertEqual failed', location: '/example_projects/iOS_Calculator/CalculatorAppTests.swift:52', }, + { + suite: 'Calculator Basic Functionality', + test: 'This test should fail to verify error reporting', + message: 'Expectation failed', + location: 'CalculatorServiceTests.swift:37', + }, ], }, }, @@ -156,6 +156,21 @@ describe('normalizeStructuredEnvelope', () => { }); }); + it('normalizes iOS and watchOS simulator runtime versions', () => { + const envelope = { + schema: 'xcodebuildmcp.output.simulators-result', + schemaVersion: '1', + didError: false, + error: null, + data: { simulators: [{ runtime: 'iOS 26.4' }, { runtime: 'watchOS 27.0' }] }, + } as StructuredOutputEnvelope; + + expect(normalizeStructuredEnvelope(envelope)).toEqual({ + ...envelope, + data: { simulators: [{ runtime: 'iOS ' }, { runtime: 'watchOS ' }] }, + }); + }); + it('normalizes UI element refs without hiding action content', () => { const envelope: StructuredOutputEnvelope = { schema: 'xcodebuildmcp.output.ui-action-result', @@ -172,15 +187,15 @@ describe('normalizeStructuredEnvelope', () => { rs: '1', screenHash: 'screen-hash', seq: 7, - count: 2, + count: 99999, targets: ['e48|tap|button|Camera||com.apple.settings.camera'], scroll: ['e1|swipe|application|Calculator||Calculator'], text: ['e42|text|text|12:34||'], }, }, nextSteps: [ - 'Tap: xcodebuildmcp ui-automation tap --element-ref e48', - 'MCP: swipe({ withinElementRef: "e1" })', + 'Take screenshot for verification: screenshot({ simulatorId: "SIMULATOR-1" })', + 'Scroll visible content: swipe({ withinElementRef: "e1" })', ], }; @@ -199,16 +214,13 @@ describe('normalizeStructuredEnvelope', () => { rs: '1', screenHash: '', seq: 1, - count: 2, + count: 99999, targets: ['|tap|button|Camera||com.apple.settings.camera'], scroll: ['|swipe|application|Calculator||Calculator'], text: ['|text|text|