mirror of
https://github.com/samsonjs/Peekaboo.git
synced 2026-06-30 05:49:29 +00:00
- Fixed all Swift test compilation errors and SwiftLint violations - Enhanced test host app with permission status display and CLI availability checking - Refactored ImageCommand.swift to improve readability and reduce function length - Updated all tests to use proper Swift Testing patterns - Added comprehensive local testing framework for screenshot functionality - Updated documentation with proper test execution instructions - Applied SwiftFormat to all Swift files and achieved zero serious linting issues 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
381 lines
13 KiB
Swift
381 lines
13 KiB
Swift
import ArgumentParser
|
|
import Foundation
|
|
@testable import peekaboo
|
|
import Testing
|
|
|
|
@Suite("ListCommand Tests", .tags(.unit))
|
|
struct ListCommandTests {
|
|
// MARK: - Command Parsing Tests
|
|
|
|
@Test("ListCommand has correct subcommands", .tags(.fast))
|
|
func listCommandSubcommands() throws {
|
|
// Test that ListCommand has the expected subcommands
|
|
#expect(ListCommand.configuration.subcommands.count == 3)
|
|
#expect(ListCommand.configuration.subcommands.contains { $0 == AppsSubcommand.self })
|
|
#expect(ListCommand.configuration.subcommands.contains { $0 == WindowsSubcommand.self })
|
|
#expect(ListCommand.configuration.subcommands.contains { $0 == ServerStatusSubcommand.self })
|
|
}
|
|
|
|
@Test("AppsSubcommand parsing with defaults", .tags(.fast))
|
|
func appsSubcommandParsing() throws {
|
|
// Test parsing apps subcommand
|
|
let command = try AppsSubcommand.parse([])
|
|
#expect(command.jsonOutput == false)
|
|
}
|
|
|
|
@Test("AppsSubcommand with JSON output flag", .tags(.fast))
|
|
func appsSubcommandWithJSONOutput() throws {
|
|
// Test apps subcommand with JSON flag
|
|
let command = try AppsSubcommand.parse(["--json-output"])
|
|
#expect(command.jsonOutput == true)
|
|
}
|
|
|
|
@Test("WindowsSubcommand parsing with required app", .tags(.fast))
|
|
func windowsSubcommandParsing() throws {
|
|
// Test parsing windows subcommand with required app
|
|
let command = try WindowsSubcommand.parse(["--app", "Finder"])
|
|
|
|
#expect(command.app == "Finder")
|
|
#expect(command.jsonOutput == false)
|
|
#expect(command.includeDetails == nil)
|
|
}
|
|
|
|
@Test("WindowsSubcommand with detail options", .tags(.fast))
|
|
func windowsSubcommandWithDetails() throws {
|
|
// Test windows subcommand with detail options
|
|
let command = try WindowsSubcommand.parse([
|
|
"--app", "Finder",
|
|
"--include-details", "bounds,ids"
|
|
])
|
|
|
|
#expect(command.app == "Finder")
|
|
#expect(command.includeDetails == "bounds,ids")
|
|
}
|
|
|
|
@Test("WindowsSubcommand requires app parameter", .tags(.fast))
|
|
func windowsSubcommandMissingApp() {
|
|
// Test that windows subcommand requires app
|
|
#expect(throws: (any Error).self) {
|
|
try WindowsSubcommand.parse([])
|
|
}
|
|
}
|
|
|
|
// MARK: - Parameterized Command Tests
|
|
|
|
@Test(
|
|
"WindowsSubcommand detail parsing",
|
|
arguments: [
|
|
"off_screen",
|
|
"bounds",
|
|
"ids",
|
|
"off_screen,bounds",
|
|
"bounds,ids",
|
|
"off_screen,bounds,ids"
|
|
]
|
|
)
|
|
func windowsDetailParsing(details: String) throws {
|
|
let command = try WindowsSubcommand.parse([
|
|
"--app", "Safari",
|
|
"--include-details", details
|
|
])
|
|
|
|
#expect(command.includeDetails == details)
|
|
}
|
|
|
|
// MARK: - Data Structure Tests
|
|
|
|
@Test("ApplicationInfo JSON encoding", .tags(.fast))
|
|
func applicationInfoEncoding() throws {
|
|
// Test ApplicationInfo JSON encoding
|
|
let appInfo = ApplicationInfo(
|
|
app_name: "Finder",
|
|
bundle_id: "com.apple.finder",
|
|
pid: 123,
|
|
is_active: true,
|
|
window_count: 5
|
|
)
|
|
|
|
let encoder = JSONEncoder()
|
|
// Properties are already in snake_case, no conversion needed
|
|
|
|
let data = try encoder.encode(appInfo)
|
|
let json = try JSONSerialization.jsonObject(with: data) as? [String: Any]
|
|
|
|
#expect(json != nil)
|
|
#expect(json?["app_name"] as? String == "Finder")
|
|
#expect(json?["bundle_id"] as? String == "com.apple.finder")
|
|
#expect(json?["pid"] as? Int32 == 123)
|
|
#expect(json?["is_active"] as? Bool == true)
|
|
#expect(json?["window_count"] as? Int == 5)
|
|
}
|
|
|
|
@Test("ApplicationListData JSON encoding", .tags(.fast))
|
|
func applicationListDataEncoding() throws {
|
|
// Test ApplicationListData JSON encoding
|
|
let appData = ApplicationListData(
|
|
applications: [
|
|
ApplicationInfo(
|
|
app_name: "Finder",
|
|
bundle_id: "com.apple.finder",
|
|
pid: 123,
|
|
is_active: true,
|
|
window_count: 3
|
|
),
|
|
ApplicationInfo(
|
|
app_name: "Safari",
|
|
bundle_id: "com.apple.Safari",
|
|
pid: 456,
|
|
is_active: false,
|
|
window_count: 2
|
|
)
|
|
]
|
|
)
|
|
|
|
let encoder = JSONEncoder()
|
|
// Properties are already in snake_case, no conversion needed
|
|
|
|
let data = try encoder.encode(appData)
|
|
let json = try JSONSerialization.jsonObject(with: data) as? [String: Any]
|
|
|
|
#expect(json != nil)
|
|
let apps = json?["applications"] as? [[String: Any]]
|
|
#expect(apps?.count == 2)
|
|
}
|
|
|
|
@Test("WindowInfo JSON encoding", .tags(.fast))
|
|
func windowInfoEncoding() throws {
|
|
// Test WindowInfo JSON encoding
|
|
let windowInfo = WindowInfo(
|
|
window_title: "Documents",
|
|
window_id: 1001,
|
|
window_index: 0,
|
|
bounds: WindowBounds(xCoordinate: 100, yCoordinate: 200, width: 800, height: 600),
|
|
is_on_screen: true
|
|
)
|
|
|
|
let encoder = JSONEncoder()
|
|
// Properties are already in snake_case, no conversion needed
|
|
|
|
let data = try encoder.encode(windowInfo)
|
|
let json = try JSONSerialization.jsonObject(with: data) as? [String: Any]
|
|
|
|
#expect(json != nil)
|
|
#expect(json?["window_title"] as? String == "Documents")
|
|
#expect(json?["window_id"] as? UInt32 == 1001)
|
|
#expect(json?["is_on_screen"] as? Bool == true)
|
|
|
|
let bounds = json?["bounds"] as? [String: Any]
|
|
#expect(bounds?["x_coordinate"] as? Int == 100)
|
|
#expect(bounds?["y_coordinate"] as? Int == 200)
|
|
#expect(bounds?["width"] as? Int == 800)
|
|
#expect(bounds?["height"] as? Int == 600)
|
|
}
|
|
|
|
@Test("WindowListData JSON encoding", .tags(.fast))
|
|
func windowListDataEncoding() throws {
|
|
// Test WindowListData JSON encoding
|
|
let windowData = WindowListData(
|
|
windows: [
|
|
WindowInfo(
|
|
window_title: "Documents",
|
|
window_id: 1001,
|
|
window_index: 0,
|
|
bounds: WindowBounds(xCoordinate: 100, yCoordinate: 200, width: 800, height: 600),
|
|
is_on_screen: true
|
|
)
|
|
],
|
|
target_application_info: TargetApplicationInfo(
|
|
app_name: "Finder",
|
|
bundle_id: "com.apple.finder",
|
|
pid: 123
|
|
)
|
|
)
|
|
|
|
let encoder = JSONEncoder()
|
|
// Properties are already in snake_case, no conversion needed
|
|
|
|
let data = try encoder.encode(windowData)
|
|
let json = try JSONSerialization.jsonObject(with: data) as? [String: Any]
|
|
|
|
#expect(json != nil)
|
|
|
|
let windows = json?["windows"] as? [[String: Any]]
|
|
#expect(windows?.count == 1)
|
|
|
|
let targetApp = json?["target_application_info"] as? [String: Any]
|
|
#expect(targetApp?["app_name"] as? String == "Finder")
|
|
#expect(targetApp?["bundle_id"] as? String == "com.apple.finder")
|
|
}
|
|
|
|
// MARK: - Window Detail Option Tests
|
|
|
|
@Test("WindowDetailOption raw values", .tags(.fast))
|
|
func windowDetailOptionRawValues() {
|
|
// Test window detail option values
|
|
#expect(WindowDetailOption.off_screen.rawValue == "off_screen")
|
|
#expect(WindowDetailOption.bounds.rawValue == "bounds")
|
|
#expect(WindowDetailOption.ids.rawValue == "ids")
|
|
}
|
|
|
|
// MARK: - Window Specifier Tests
|
|
|
|
@Test("WindowSpecifier with title", .tags(.fast))
|
|
func windowSpecifierTitle() {
|
|
// Test window specifier with title
|
|
let specifier = WindowSpecifier.title("Documents")
|
|
|
|
switch specifier {
|
|
case let .title(title):
|
|
#expect(title == "Documents")
|
|
default:
|
|
Issue.record("Expected title specifier")
|
|
}
|
|
}
|
|
|
|
@Test("WindowSpecifier with index", .tags(.fast))
|
|
func windowSpecifierIndex() {
|
|
// Test window specifier with index
|
|
let specifier = WindowSpecifier.index(0)
|
|
|
|
switch specifier {
|
|
case let .index(index):
|
|
#expect(index == 0)
|
|
default:
|
|
Issue.record("Expected index specifier")
|
|
}
|
|
}
|
|
|
|
// MARK: - Performance Tests
|
|
|
|
@Test(
|
|
"ApplicationListData encoding performance",
|
|
arguments: [10, 50, 100, 200]
|
|
)
|
|
func applicationListEncodingPerformance(appCount: Int) throws {
|
|
// Test performance of encoding many applications
|
|
let apps = (0..<appCount).map { index in
|
|
ApplicationInfo(
|
|
app_name: "App\(index)",
|
|
bundle_id: "com.example.app\(index)",
|
|
pid: Int32(1000 + index),
|
|
is_active: index == 0,
|
|
window_count: index % 5
|
|
)
|
|
}
|
|
|
|
let appData = ApplicationListData(applications: apps)
|
|
let encoder = JSONEncoder()
|
|
// Properties are already in snake_case, no conversion needed
|
|
|
|
// Ensure encoding works correctly
|
|
let data = try encoder.encode(appData)
|
|
#expect(!data.isEmpty)
|
|
}
|
|
}
|
|
|
|
// MARK: - Extended List Command Tests
|
|
|
|
@Suite("ListCommand Advanced Tests", .tags(.integration))
|
|
struct ListCommandAdvancedTests {
|
|
@Test("ServerStatusSubcommand parsing", .tags(.fast))
|
|
func serverStatusSubcommandParsing() throws {
|
|
let command = try ServerStatusSubcommand.parse([])
|
|
#expect(command.jsonOutput == false)
|
|
|
|
let commandWithJSON = try ServerStatusSubcommand.parse(["--json-output"])
|
|
#expect(commandWithJSON.jsonOutput == true)
|
|
}
|
|
|
|
@Test("Command help messages", .tags(.fast))
|
|
func commandHelpMessages() {
|
|
let listHelp = ListCommand.helpMessage()
|
|
#expect(listHelp.contains("List"))
|
|
|
|
let appsHelp = AppsSubcommand.helpMessage()
|
|
#expect(appsHelp.contains("running applications"))
|
|
|
|
let windowsHelp = WindowsSubcommand.helpMessage()
|
|
#expect(windowsHelp.contains("windows"))
|
|
|
|
let statusHelp = ServerStatusSubcommand.helpMessage()
|
|
#expect(statusHelp.contains("status"))
|
|
}
|
|
|
|
@Test(
|
|
"Complex window info structures",
|
|
arguments: [
|
|
(title: "Main Window", id: 1001, onScreen: true),
|
|
(title: "Hidden Window", id: 2001, onScreen: false),
|
|
(title: "Minimized", id: 3001, onScreen: false)
|
|
]
|
|
)
|
|
func complexWindowInfo(title: String, id: UInt32, onScreen: Bool) throws {
|
|
let windowInfo = WindowInfo(
|
|
window_title: title,
|
|
window_id: id,
|
|
window_index: 0,
|
|
bounds: nil,
|
|
is_on_screen: onScreen
|
|
)
|
|
|
|
let encoder = JSONEncoder()
|
|
// No need for convertToSnakeCase since properties are already in snake_case
|
|
let data = try encoder.encode(windowInfo)
|
|
|
|
let decoder = JSONDecoder()
|
|
// No need for convertFromSnakeCase since properties are already in snake_case
|
|
let decoded = try decoder.decode(WindowInfo.self, from: data)
|
|
|
|
#expect(decoded.window_title == title)
|
|
#expect(decoded.window_id == id)
|
|
#expect(decoded.is_on_screen == onScreen)
|
|
}
|
|
|
|
@Test(
|
|
"Application state combinations",
|
|
arguments: [
|
|
(active: true, windowCount: 5),
|
|
(active: false, windowCount: 0),
|
|
(active: true, windowCount: 0),
|
|
(active: false, windowCount: 10)
|
|
]
|
|
)
|
|
func applicationStates(active: Bool, windowCount: Int) {
|
|
let appInfo = ApplicationInfo(
|
|
app_name: "TestApp",
|
|
bundle_id: "com.test.app",
|
|
pid: 1234,
|
|
is_active: active,
|
|
window_count: windowCount
|
|
)
|
|
|
|
#expect(appInfo.is_active == active)
|
|
#expect(appInfo.window_count == windowCount)
|
|
|
|
// Logical consistency checks
|
|
if windowCount > 0 {
|
|
// Apps with windows can be active or inactive
|
|
#expect(appInfo.window_count > 0)
|
|
}
|
|
}
|
|
|
|
@Test("Server permissions data encoding", .tags(.fast))
|
|
func serverPermissionsEncoding() throws {
|
|
let permissions = ServerPermissions(
|
|
screen_recording: true,
|
|
accessibility: false
|
|
)
|
|
|
|
let statusData = ServerStatusData(permissions: permissions)
|
|
|
|
let encoder = JSONEncoder()
|
|
// Properties are already in snake_case, no conversion needed
|
|
let data = try encoder.encode(statusData)
|
|
|
|
let json = try JSONSerialization.jsonObject(with: data) as? [String: Any]
|
|
let permsJson = json?["permissions"] as? [String: Any]
|
|
|
|
#expect(permsJson?["screen_recording"] as? Bool == true)
|
|
#expect(permsJson?["accessibility"] as? Bool == false)
|
|
}
|
|
}
|