Skip to main content
Glama
RunCommandTests.swift9.39 kB
import Foundation import PeekabooCore import Testing @testable import PeekabooCLI #if !PEEKABOO_SKIP_AUTOMATION @Suite( "RunCommand CLI Harness Tests", .serialized, .tags(.safe), .enabled(if: CLITestEnvironment.runAutomationRead) ) struct RunCommandCLIHarnessTests { @Test("run command executes scripts via process service") func runCommandOutputsJSON() async throws { let scriptPath = "/tmp/test-script.peekaboo.json" let script = PeekabooScript( description: "Sample script", steps: [ ScriptStep(stepId: "step1", comment: "Capture UI", command: "see", params: nil), ScriptStep(stepId: "step2", comment: "Click login", command: "click", params: nil), ] ) let stepResults = [ StepResult( stepId: "step1", stepNumber: 1, command: "see", success: true, output: .success("Captured"), error: nil, executionTime: 0.5 ), StepResult( stepId: "step2", stepNumber: 2, command: "click", success: true, output: .success("Clicked"), error: nil, executionTime: 0.3 ), ] let process = StubProcessService() process.scriptsByPath[scriptPath] = script process.nextExecuteScriptResults = stepResults let services = await self.makeServices(process: process) let result = try await InProcessCommandRunner.run([ "run", scriptPath, "--json-output", ], services: services) #expect(result.exitStatus == 0) let data = try #require(result.stdout.data(using: .utf8)) let payload = try JSONDecoder().decode(ScriptExecutionResult.self, from: data) #expect(payload.totalSteps == 2) #expect(payload.success) #expect(process.loadScriptCalls.count == 1) #expect(process.executeScriptCalls.count == 1) } @Test("run command writes output file") func runCommandWritesOutputFile() async throws { let scriptPath = "/tmp/output-script.peekaboo.json" let script = PeekabooScript(description: "Write output", steps: []) let process = StubProcessService() process.scriptsByPath[scriptPath] = script process.nextExecuteScriptResults = [] let outputURL = FileManager.default.temporaryDirectory .appendingPathComponent("run-results-\(UUID().uuidString).json") defer { try? FileManager.default.removeItem(at: outputURL) } let services = await self.makeServices(process: process) let result = try await InProcessCommandRunner.run([ "run", scriptPath, "--output", outputURL.path, ], services: services) #expect(result.exitStatus == 0) #expect(FileManager.default.fileExists(atPath: outputURL.path)) let data = try Data(contentsOf: outputURL) let payload = try JSONDecoder().decode(ScriptExecutionResult.self, from: data) #expect(payload.scriptPath == scriptPath) } @Test("run command exits with failure when a step fails") func runCommandFailure() async throws { let scriptPath = "/tmp/failing-script.peekaboo.json" let script = PeekabooScript(description: "Failing script", steps: [ ScriptStep(stepId: "fail", comment: nil, command: "click", params: nil), ]) let failingStep = StepResult( stepId: "fail", stepNumber: 1, command: "click", success: false, output: nil, error: "Element not found", executionTime: 0.2 ) let process = StubProcessService() process.scriptsByPath[scriptPath] = script process.nextExecuteScriptResults = [failingStep] let services = await self.makeServices(process: process) let result = try await InProcessCommandRunner.run(["run", scriptPath], services: services) #expect(result.exitStatus != 0) let output = result.stdout + result.stderr #expect(output.contains("❌ Script failed") || output.contains("❌ Error")) } @MainActor private func makeServices(process: StubProcessService) -> PeekabooServices { TestServicesFactory.makePeekabooServices(process: process) } } #endif @Suite("RunCommand Data Tests", .serialized, .tags(.unit)) struct RunCommandDataTests { @Test("Run command parses script path") func parseScriptPath() throws { let command = try RunCommand.parse(["/path/to/script.peekaboo.json"]) #expect(command.scriptPath == "/path/to/script.peekaboo.json") #expect(command.output == nil) #expect(command.noFailFast == false) } @Test("Run command parses all options") func parseAllOptions() throws { let command = try RunCommand.parse([ "/tmp/automation.peekaboo.json", "--output", "results.json", "--no-fail-fast", ]) #expect(command.scriptPath == "/tmp/automation.peekaboo.json") #expect(command.output == "results.json") #expect(command.noFailFast == true) } @Test("Run command requires script path") func requiresScriptPath() { #expect(throws: (any Error).self) { try CLIOutputCapture.suppressStderr { _ = try RunCommand.parse([]) } } } @Test("Script structure validation") func scriptStructure() { let steps = [ TestScriptStep( stepId: "step1", comment: "Capture Safari UI", command: "see", params: ["app": "Safari"] ), TestScriptStep( stepId: "step2", comment: "Click login button", command: "click", params: ["query": "Login"] ), TestScriptStep( stepId: "step3", comment: nil, command: "type", params: ["text": "user@example.com", "on": "T1"] ), ] let script = TestPeekabooScript( description: "Automates the login flow", steps: steps ) #expect(script.description == "Automates the login flow") #expect(script.steps.count == 3) #expect(script.steps[0].command == "see") #expect(script.steps[0].params?["app"] == "Safari") #expect(script.steps[2].comment == nil) } @Test("Run result structure") func runResultStructure() { let stepResults = [ StepResult( stepId: "step1", stepNumber: 1, command: "see", success: true, output: .success("Step completed successfully"), error: nil, executionTime: 1.5 ), StepResult( stepId: "step2", stepNumber: 2, command: "click", success: false, output: nil, error: "Element not found", executionTime: 2.0 ), ] let result = ScriptExecutionResult( success: false, scriptPath: "/tmp/test.peekaboo.json", description: "Test script", totalSteps: 5, completedSteps: 1, failedSteps: 1, executionTime: 12.5, steps: stepResults ) #expect(result.success == false) #expect(result.scriptPath == "/tmp/test.peekaboo.json") #expect(result.totalSteps == 5) #expect(result.completedSteps == 1) #expect(result.failedSteps == 1) #expect(result.executionTime == 12.5) #expect(result.steps.count == 2) #expect(result.steps[1].error == "Element not found") } @Test("Script JSON parsing") func scriptJSONParsing() throws { let jsonString = """ { "description": "A test automation script", "steps": [ { "stepId": "step1", "command": "see", "params": { "app": "Finder" } }, { "stepId": "step2", "command": "sleep", "params": { "duration": "1000" }, "comment": "Wait for UI to settle" } ] } """ let jsonData = Data(jsonString.utf8) let script = try JSONDecoder().decode(TestPeekabooScript.self, from: jsonData) #expect(script.description == "A test automation script") #expect(script.steps.count == 2) #expect(script.steps[1].comment == "Wait for UI to settle") } } // MARK: - Test Helper Types struct TestPeekabooScript: Codable { let description: String? let steps: [TestScriptStep] } struct TestScriptStep: Codable { let stepId: String let comment: String? let command: String let params: [String: String]? }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/steipete/Peekaboo'

If you have feedback or need assistance with the MCP directory API, please join our Discord server