ScrollCommandTests.swift•5.88 kB
import CoreGraphics
import Foundation
import PeekabooCore
import PeekabooFoundation
import Testing
@testable import PeekabooCLI
#if !PEEKABOO_SKIP_AUTOMATION
@Suite(
"ScrollCommand Tests",
.serialized,
.tags(.safe),
.enabled(if: CLITestEnvironment.runAutomationRead)
)
struct ScrollCommandTests {
@Test("scroll --help surfaces command documentation")
func scrollHelp() async throws {
let context = await self.makeContext()
let result = try await self.runScroll(arguments: ["--help"], context: context)
#expect(result.exitStatus == 0)
let output = self.output(from: result)
#expect(output.contains("Scroll the mouse wheel in any direction"))
}
@Test("Scroll command requires a direction")
func requiresDirection() async throws {
let context = await self.makeContext()
let result = try await self.runScroll(arguments: [], context: context)
#expect(result.exitStatus != 0)
let output = self.output(from: result)
#expect(output.contains("--direction"))
let scrollCalls = await self.automationState(context) { $0.scrollCalls }
#expect(scrollCalls.isEmpty)
}
@Test("Scroll forwards parameters to automation service")
func forwardsParameters() async throws {
let context = await self.makeContext()
let result = try await self.runScroll(
arguments: [
"--direction", "down",
"--amount", "5",
"--delay", "10",
"--smooth",
"--session", "session-42",
"--on", "B1",
"--json-output",
],
context: context
)
#expect(result.exitStatus == 0)
let scrollCalls = await self.automationState(context) { $0.scrollCalls }
let call = try #require(scrollCalls.first)
#expect(call.request.direction == .down)
#expect(call.request.amount == 5)
#expect(call.request.delay == 10)
#expect(call.request.smooth == true)
#expect(call.request.target == "B1")
#expect(call.request.sessionId == "session-42")
let payloadData = try #require(self.output(from: result).data(using: .utf8))
let payload = try JSONDecoder().decode(ScrollResult.self, from: payloadData)
#expect(payload.success)
#expect(payload.direction == "down")
#expect(payload.amount == 5)
}
@Test("Scroll without session still executes")
func executesWithoutSession() async throws {
let context = await self.makeContext()
let result = try await self.runScroll(
arguments: ["--direction", "up", "--amount", "2"],
context: context
)
#expect(result.exitStatus == 0)
let scrollCalls = await self.automationState(context) { $0.scrollCalls }
#expect(scrollCalls.count == 1)
let call = try #require(scrollCalls.first)
#expect(call.request.sessionId == nil)
#expect(call.request.amount == 2)
}
@Test("Smooth scrolling adjusts total ticks in JSON output")
func smoothScrollingIncreasesTicks() async throws {
let context = await self.makeContext()
let result = try await self.runScroll(
arguments: ["--direction", "down", "--amount", "4", "--smooth", "--json-output"],
context: context
)
#expect(result.exitStatus == 0)
let payloadData = try #require(self.output(from: result).data(using: .utf8))
let payload = try JSONDecoder().decode(ScrollResult.self, from: payloadData)
#expect(payload.totalTicks == 12) // 4 * 3 when smooth
}
@Test("Direction validation accepts common values", arguments: [
"up", "down", "left", "right",
])
func directionValidation(value: String) async throws {
let context = await self.makeContext()
let result = try await self.runScroll(arguments: ["--direction", value], context: context)
#expect(result.exitStatus == 0)
}
// MARK: - Helpers
private func runScroll(
arguments: [String],
context: TestServicesFactory.AutomationTestContext
) async throws -> CommandRunResult {
try await InProcessCommandRunner.run(["scroll"] + arguments, services: context.services)
}
private func output(from result: CommandRunResult) -> String {
result.stdout.isEmpty ? result.stderr : result.stdout
}
private func makeContext(
configure: (@MainActor (StubAutomationService, StubSessionManager) -> Void)? = nil
) async -> TestServicesFactory.AutomationTestContext {
await MainActor.run {
let context = TestServicesFactory.makeAutomationTestContext()
configure?(context.automation, context.sessions)
return context
}
}
private func automationState<T: Sendable>(
_ context: TestServicesFactory.AutomationTestContext,
_ operation: @MainActor (StubAutomationService) -> T
) async -> T {
await MainActor.run {
operation(context.automation)
}
}
}
#endif
@Suite("ScrollCommand Result Structures", .tags(.safe))
struct ScrollCommandResultStructTests {
@Test("Scroll result structure maintains fields")
func scrollResultStructure() {
let result = ScrollResult(
success: true,
direction: "down",
amount: 5,
location: ["x": 500.0, "y": 300.0],
totalTicks: 5,
executionTime: 0.15
)
#expect(result.success == true)
#expect(result.direction == "down")
#expect(result.amount == 5)
#expect(result.location["x"] == 500.0)
#expect(result.location["y"] == 300.0)
#expect(result.totalTicks == 5)
#expect(result.executionTime == 0.15)
}
}