Move to XCTest for stability
This commit is contained in:
parent
b46ae09ee9
commit
d4d899da85
@ -1,13 +1,12 @@
|
||||
import AppKit
|
||||
@testable import AXorcist
|
||||
import Foundation
|
||||
import Testing
|
||||
import XCTest
|
||||
|
||||
// MARK: - Action Command Tests
|
||||
|
||||
@Test("Perform Action: Set Value of TextEdit Text Area")
|
||||
@MainActor
|
||||
func performActionSetTextEditTextAreaValue() async throws {
|
||||
class ActionIntegrationTests: XCTestCase {
|
||||
func testPerformActionSetTextEditTextAreaValue() async throws {
|
||||
let actionCommandId = "performaction-setvalue-\(UUID().uuidString)"
|
||||
let queryCommandId = "query-verify-setvalue-\(UUID().uuidString)"
|
||||
let textEditBundleId = "com.apple.TextEdit"
|
||||
@ -37,9 +36,7 @@ func performActionSetTextEditTextAreaValue() async throws {
|
||||
)
|
||||
}
|
||||
|
||||
@Test("Extract Text from TextEdit Text Area")
|
||||
@MainActor
|
||||
func extractTextFromTextEditTextArea() async throws {
|
||||
func testExtractTextFromTextEditTextArea() async throws {
|
||||
let setValueCommandId = "setvalue-for-extract-\(UUID().uuidString)"
|
||||
let extractTextCommandId = "extracttext-textedit-textarea-\(UUID().uuidString)"
|
||||
let textEditBundleId = "com.apple.TextEdit"
|
||||
@ -89,9 +86,9 @@ private func performSetValueAction(
|
||||
|
||||
let response = try await executeCommand(performActionEnvelope)
|
||||
|
||||
#expect(response.commandId == actionCommandId)
|
||||
#expect(
|
||||
response.success == true,
|
||||
XCTAssertEqual(response.commandId , actionCommandId)
|
||||
XCTAssertEqual(
|
||||
response.success , true,
|
||||
"performAction command was not successful. Error: \(response.error?.message ?? "N/A")"
|
||||
)
|
||||
|
||||
@ -115,9 +112,9 @@ private func verifyTextValue(
|
||||
|
||||
let response = try await executeCommand(queryEnvelope)
|
||||
|
||||
#expect(response.commandId == queryCommandId)
|
||||
#expect(
|
||||
response.success == true,
|
||||
XCTAssertEqual(response.commandId , queryCommandId)
|
||||
XCTAssertEqual(
|
||||
response.success , true,
|
||||
"Query (verify) command failed. Error: \(response.error?.message ?? "N/A")"
|
||||
)
|
||||
|
||||
@ -126,12 +123,12 @@ private func verifyTextValue(
|
||||
}
|
||||
|
||||
let retrievedValue = attributes["AXValue"]?.value as? String
|
||||
#expect(
|
||||
retrievedValue == expectedText,
|
||||
XCTAssertEqual(
|
||||
retrievedValue , expectedText,
|
||||
"AXValue did not match. Expected: '\(expectedText)'. Got: '\(retrievedValue ?? "nil")'"
|
||||
)
|
||||
|
||||
#expect(response.debugLogs != nil)
|
||||
XCTAssertNotEqual(response.debugLogs , nil)
|
||||
}
|
||||
|
||||
private func extractAndVerifyText(
|
||||
@ -150,25 +147,25 @@ private func extractAndVerifyText(
|
||||
|
||||
let response = try await executeCommand(extractTextEnvelope)
|
||||
|
||||
#expect(response.commandId == extractTextCommandId)
|
||||
#expect(
|
||||
response.success == true,
|
||||
XCTAssertEqual(response.commandId , extractTextCommandId)
|
||||
XCTAssertEqual(
|
||||
response.success , true,
|
||||
"extractText command failed. Error: \(response.error?.message ?? "N/A")"
|
||||
)
|
||||
#expect(response.command == CommandType.extractText.rawValue)
|
||||
XCTAssertEqual(response.command , CommandType.extractText.rawValue)
|
||||
|
||||
guard let attributes = response.data?.attributes else {
|
||||
throw TestError.generic("Attributes nil in extractText response.")
|
||||
}
|
||||
|
||||
let extractedValue = attributes["AXValue"]?.value as? String
|
||||
#expect(
|
||||
extractedValue == expectedText,
|
||||
XCTAssertEqual(
|
||||
extractedValue , expectedText,
|
||||
"Extracted text did not match. Expected: '\(expectedText)'. Got: '\(extractedValue ?? "nil")'"
|
||||
)
|
||||
|
||||
#expect(response.debugLogs != nil)
|
||||
#expect(
|
||||
XCTAssertNotEqual(response.debugLogs , nil)
|
||||
XCTAssertTrue(
|
||||
response.debugLogs?
|
||||
.contains { log in
|
||||
log.contains("Handling extractText command") ||
|
||||
@ -190,9 +187,9 @@ private func executeCommand(_ command: CommandEnvelope) async throws -> QueryRes
|
||||
let result = try runAXORCCommand(arguments: [jsonString])
|
||||
let (output, errorOutput, exitCode) = (result.output, result.errorOutput, result.exitCode)
|
||||
|
||||
#expect(exitCode == 0, Comment(rawValue: "Command failed. Error: \(errorOutput ?? "N/A")"))
|
||||
#expect(
|
||||
errorOutput == nil || errorOutput!.isEmpty,
|
||||
XCTAssertEqual(exitCode , 0, Comment(rawValue: "Command failed. Error: \(errorOutput ?? "N/A")"))
|
||||
XCTAssertEqual(
|
||||
errorOutput , nil || errorOutput!.isEmpty,
|
||||
Comment(rawValue: "STDERR should be empty. Got: \(errorOutput ?? "")")
|
||||
)
|
||||
|
||||
@ -212,3 +209,5 @@ private func executeCommand(_ command: CommandEnvelope) async throws -> QueryRes
|
||||
throw TestError.generic("Failed to decode response: \(error.localizedDescription). JSON: \(outputString)")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,11 +1,11 @@
|
||||
import AppKit
|
||||
@testable import AXorcist
|
||||
import Testing
|
||||
import XCTest
|
||||
|
||||
// MARK: - Application Query Tests
|
||||
|
||||
@Test("Get All Running Applications")
|
||||
func getAllApplications() async throws {
|
||||
class ApplicationQueryTests: XCTestCase {
|
||||
func testGetAllApplications() async throws {
|
||||
let command = CommandEnvelope(
|
||||
commandId: "test-get-all-apps",
|
||||
command: .collectAll,
|
||||
@ -23,8 +23,8 @@ func getAllApplications() async throws {
|
||||
|
||||
let result = try runAXORCCommand(arguments: [jsonString])
|
||||
|
||||
#expect(result.exitCode == 0, "Command should succeed")
|
||||
#expect(result.output != nil, "Should have output")
|
||||
XCTAssertEqual(result.exitCode , 0, "Command should succeed")
|
||||
XCTAssertNotEqual(result.output , nil, "Should have output")
|
||||
|
||||
guard let output = result.output,
|
||||
let responseData = output.data(using: String.Encoding.utf8)
|
||||
@ -34,28 +34,26 @@ func getAllApplications() async throws {
|
||||
|
||||
let response = try JSONDecoder().decode(SimpleSuccessResponse.self, from: responseData)
|
||||
|
||||
#expect(response.success == true)
|
||||
XCTAssertEqual(response.success , true)
|
||||
// TODO: Fix response type - SimpleSuccessResponse doesn't have data property
|
||||
// The following code expects response.data which doesn't exist
|
||||
/*
|
||||
#expect(response.data?["elements"] != nil, "Should have elements")
|
||||
XCTAssertNotEqual(response.data?["elements"] , nil, "Should have elements")
|
||||
|
||||
if let elements = response.data?["elements"] as? [[String: Any]] {
|
||||
#expect(!elements.isEmpty, "Should have at least one application")
|
||||
XCTAssertTrue(!elements.isEmpty, "Should have at least one application")
|
||||
|
||||
// Check for Finder
|
||||
let appTitles = elements.compactMap { element -> String? in
|
||||
guard let attrs = element["attributes"] as? [String: Any] else { return nil }
|
||||
return attrs["AXTitle"] as? String
|
||||
}
|
||||
#expect(appTitles.contains("Finder"), "Finder should be running")
|
||||
XCTAssertTrue(appTitles.contains("Finder"), "Finder should be running")
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
@Test("Get Windows of TextEdit")
|
||||
@MainActor
|
||||
func getWindowsOfApplication() async throws {
|
||||
func testGetWindowsOfApplication() async throws {
|
||||
await closeTextEdit()
|
||||
try await Task.sleep(for: .milliseconds(500))
|
||||
|
||||
@ -86,7 +84,7 @@ func getWindowsOfApplication() async throws {
|
||||
|
||||
let result = try runAXORCCommand(arguments: [jsonString])
|
||||
|
||||
#expect(result.exitCode == 0)
|
||||
XCTAssertEqual(result.exitCode , 0)
|
||||
|
||||
guard let output = result.output,
|
||||
let responseData = output.data(using: String.Encoding.utf8)
|
||||
@ -96,24 +94,23 @@ func getWindowsOfApplication() async throws {
|
||||
|
||||
let response = try JSONDecoder().decode(SimpleSuccessResponse.self, from: responseData)
|
||||
|
||||
#expect(response.success == true)
|
||||
XCTAssertEqual(response.success , true)
|
||||
// TODO: Fix response type - SimpleSuccessResponse doesn't have data property
|
||||
/*
|
||||
if let elements = response.data?["elements"] as? [[String: Any]] {
|
||||
#expect(!elements.isEmpty, "Should have at least one window")
|
||||
XCTAssertTrue(!elements.isEmpty, "Should have at least one window")
|
||||
|
||||
for window in elements {
|
||||
if let attrs = window["attributes"] as? [String: Any] {
|
||||
#expect(attrs["AXRole"] as? String == "AXWindow")
|
||||
#expect(attrs["AXTitle"] != nil, "Window should have title")
|
||||
XCTAssertEqual(attrs["AXRole"] as? String , "AXWindow")
|
||||
XCTAssertNotEqual(attrs["AXTitle"] , nil, "Window should have title")
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
@Test("Query Non-Existent Application")
|
||||
func queryNonExistentApp() async throws {
|
||||
func testQueryNonExistentApp() async throws {
|
||||
let command = CommandEnvelope(
|
||||
commandId: "test-nonexistent",
|
||||
command: .query,
|
||||
@ -131,7 +128,7 @@ func queryNonExistentApp() async throws {
|
||||
let result = try runAXORCCommand(arguments: [jsonString])
|
||||
|
||||
// Command should succeed but return no elements
|
||||
#expect(result.exitCode == 0)
|
||||
XCTAssertEqual(result.exitCode , 0)
|
||||
|
||||
guard let output = result.output,
|
||||
let responseData = output.data(using: String.Encoding.utf8)
|
||||
@ -145,7 +142,9 @@ func queryNonExistentApp() async throws {
|
||||
// For non-existent app, we expect success but should check message or details
|
||||
// to verify no elements were found. Since SimpleSuccessResponse doesn't
|
||||
// have element data, we verify through the success status and message.
|
||||
#expect(response.message.contains("No") || response.message.contains("not found") || response.message.isEmpty,
|
||||
XCTAssertTrue(response.message.contains("No") || response.message.contains("not found") || response.message.isEmpty,
|
||||
"Message should indicate no elements found or be empty")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,12 +1,11 @@
|
||||
import AppKit
|
||||
@testable import AXorcist
|
||||
import Testing
|
||||
import XCTest
|
||||
|
||||
// MARK: - Batch Command Tests
|
||||
|
||||
@Test("Batch Command: GetFocusedElement and Query TextEdit")
|
||||
@MainActor
|
||||
func batchCommandGetFocusedElementAndQuery() async throws {
|
||||
class BatchIntegrationTests: XCTestCase {
|
||||
func testBatchCommandGetFocusedElementAndQuery() async throws {
|
||||
let batchCommandId = "batch-textedit-\(UUID().uuidString)"
|
||||
let focusedElementSubCmdId = "batch-sub-getfocused-\(UUID().uuidString)"
|
||||
let querySubCmdId = "batch-sub-querytextarea-\(UUID().uuidString)"
|
||||
@ -85,12 +84,12 @@ private func executeBatchCommand(_ command: CommandEnvelope) async throws -> Bat
|
||||
let result = try runAXORCCommand(arguments: [jsonString])
|
||||
let (output, errorOutput, exitCode) = (result.output, result.errorOutput, result.exitCode)
|
||||
|
||||
#expect(
|
||||
exitCode == 0,
|
||||
XCTAssertEqual(
|
||||
exitCode , 0,
|
||||
Comment(rawValue: "axorc process for batch command should exit with 0. Error: \(errorOutput ?? "N/A")")
|
||||
)
|
||||
#expect(
|
||||
errorOutput == nil || errorOutput!.isEmpty,
|
||||
XCTAssertEqual(
|
||||
errorOutput , nil || errorOutput!.isEmpty,
|
||||
Comment(rawValue: "STDERR should be empty. Got: \(errorOutput ?? "")")
|
||||
)
|
||||
|
||||
@ -113,25 +112,27 @@ private func verifyBatchResponse(
|
||||
querySubCmdId: String,
|
||||
textAreaRole: String
|
||||
) {
|
||||
#expect(batchResponse.commandId == batchCommandId)
|
||||
#expect(batchResponse.success == true, "Batch command should succeed")
|
||||
#expect(batchResponse.results.count == 2, "Expected 2 results")
|
||||
XCTAssertEqual(batchResponse.commandId , batchCommandId)
|
||||
XCTAssertEqual(batchResponse.success , true, "Batch command should succeed")
|
||||
XCTAssertEqual(batchResponse.results.count , 2, "Expected 2 results")
|
||||
|
||||
// Verify first sub-command
|
||||
let result1 = batchResponse.results[0]
|
||||
#expect(result1.commandId == focusedElementSubCmdId)
|
||||
#expect(result1.success == true, "GetFocusedElement should succeed")
|
||||
#expect(result1.command == CommandType.getFocusedElement.rawValue)
|
||||
#expect(result1.data != nil)
|
||||
#expect(result1.data?.attributes?["AXRole"]?.value as? String == textAreaRole)
|
||||
XCTAssertEqual(result1.commandId , focusedElementSubCmdId)
|
||||
XCTAssertEqual(result1.success , true, "GetFocusedElement should succeed")
|
||||
XCTAssertEqual(result1.command , CommandType.getFocusedElement.rawValue)
|
||||
XCTAssertNotEqual(result1.data , nil)
|
||||
XCTAssertEqual(result1.data?.attributes?["AXRole"]?.value as? String , textAreaRole)
|
||||
|
||||
// Verify second sub-command
|
||||
let result2 = batchResponse.results[1]
|
||||
#expect(result2.commandId == querySubCmdId)
|
||||
#expect(result2.success == true, "Query should succeed")
|
||||
#expect(result2.command == CommandType.query.rawValue)
|
||||
#expect(result2.data != nil)
|
||||
#expect(result2.data?.attributes?["AXRole"]?.value as? String == textAreaRole)
|
||||
XCTAssertEqual(result2.commandId , querySubCmdId)
|
||||
XCTAssertEqual(result2.success , true, "Query should succeed")
|
||||
XCTAssertEqual(result2.command , CommandType.query.rawValue)
|
||||
XCTAssertNotEqual(result2.data , nil)
|
||||
XCTAssertEqual(result2.data?.attributes?["AXRole"]?.value as? String , textAreaRole)
|
||||
|
||||
#expect(batchResponse.debugLogs != nil)
|
||||
XCTAssertNotEqual(batchResponse.debugLogs , nil)
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,6 +1,5 @@
|
||||
import AppKit
|
||||
@testable import AXorcist
|
||||
import Testing
|
||||
import XCTest
|
||||
|
||||
// Result struct for AXORC commands
|
||||
|
||||
@ -1,12 +1,11 @@
|
||||
import AppKit
|
||||
@testable import AXorcist
|
||||
import Testing
|
||||
import XCTest
|
||||
|
||||
// MARK: - Element Search and Navigation Tests
|
||||
|
||||
@Test("Search Elements by Role")
|
||||
@MainActor
|
||||
func searchElementsByRole() async throws {
|
||||
class ElementSearchTests: XCTestCase {
|
||||
func testSearchElementsByRole() async throws {
|
||||
await closeTextEdit()
|
||||
try await Task.sleep(for: .milliseconds(500))
|
||||
|
||||
@ -37,7 +36,7 @@ func searchElementsByRole() async throws {
|
||||
|
||||
let result = try runAXORCCommand(arguments: [jsonString])
|
||||
|
||||
#expect(result.exitCode == 0)
|
||||
XCTAssertEqual(result.exitCode , 0)
|
||||
|
||||
guard let output = result.output,
|
||||
let responseData = output.data(using: String.Encoding.utf8)
|
||||
@ -47,19 +46,17 @@ func searchElementsByRole() async throws {
|
||||
|
||||
let response = try JSONDecoder().decode(QueryResponse.self, from: responseData)
|
||||
|
||||
#expect(response.success == true)
|
||||
XCTAssertEqual(response.success , true)
|
||||
|
||||
if let data = response.data, let attributes = data.attributes {
|
||||
// For a query response, we should find button elements
|
||||
if let role = attributes["AXRole"]?.value as? String {
|
||||
#expect(role == "AXButton", "Should find button elements")
|
||||
XCTAssertEqual(role , "AXButton", "Should find button elements")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test("Describe Element with Hierarchy")
|
||||
@MainActor
|
||||
func describeElementHierarchy() async throws {
|
||||
func testDescribeElementHierarchy() async throws {
|
||||
await closeTextEdit()
|
||||
try await Task.sleep(for: .milliseconds(500))
|
||||
|
||||
@ -91,7 +88,7 @@ func describeElementHierarchy() async throws {
|
||||
|
||||
let result = try runAXORCCommand(arguments: [jsonString])
|
||||
|
||||
#expect(result.exitCode == 0)
|
||||
XCTAssertEqual(result.exitCode , 0)
|
||||
|
||||
guard let output = result.output,
|
||||
let responseData = output.data(using: String.Encoding.utf8)
|
||||
@ -101,20 +98,18 @@ func describeElementHierarchy() async throws {
|
||||
|
||||
let response = try JSONDecoder().decode(QueryResponse.self, from: responseData)
|
||||
|
||||
#expect(response.success == true)
|
||||
#expect(response.data != nil)
|
||||
XCTAssertEqual(response.success , true)
|
||||
XCTAssertNotEqual(response.data , nil)
|
||||
|
||||
// Check hierarchy
|
||||
if let data = response.data, let attributes = data.attributes {
|
||||
if let role = attributes["AXRole"]?.value as? String {
|
||||
#expect(role == "AXApplication", "Should find application element")
|
||||
XCTAssertEqual(role , "AXApplication", "Should find application element")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test("Set and Verify Text Content")
|
||||
@MainActor
|
||||
func setAndVerifyText() async throws {
|
||||
func testSetAndVerifyText() async throws {
|
||||
await closeTextEdit()
|
||||
try await Task.sleep(for: .milliseconds(500))
|
||||
|
||||
@ -145,7 +140,7 @@ func setAndVerifyText() async throws {
|
||||
}
|
||||
|
||||
var result = try runAXORCCommand(arguments: [setJsonString])
|
||||
#expect(result.exitCode == 0)
|
||||
XCTAssertEqual(result.exitCode , 0)
|
||||
|
||||
// Query to verify
|
||||
let queryText = CommandEnvelope(
|
||||
@ -163,7 +158,7 @@ func setAndVerifyText() async throws {
|
||||
}
|
||||
|
||||
result = try runAXORCCommand(arguments: [queryJsonString])
|
||||
#expect(result.exitCode == 0)
|
||||
XCTAssertEqual(result.exitCode , 0)
|
||||
|
||||
guard let output = result.output,
|
||||
let responseData = output.data(using: String.Encoding.utf8)
|
||||
@ -173,17 +168,15 @@ func setAndVerifyText() async throws {
|
||||
|
||||
let response = try JSONDecoder().decode(QueryResponse.self, from: responseData)
|
||||
|
||||
#expect(response.success == true)
|
||||
XCTAssertEqual(response.success , true)
|
||||
|
||||
if let data = response.data, let attributes = data.attributes {
|
||||
if let value = attributes["AXValue"]?.value as? String {
|
||||
#expect(value.contains("Hello from AXorcist tests!"), "Should find the text we set")
|
||||
XCTAssertTrue(value.contains("Hello from AXorcist tests!"), "Should find the text we set")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test("Extract Text from Window")
|
||||
@MainActor
|
||||
func testExtractText() async throws {
|
||||
await closeTextEdit()
|
||||
try await Task.sleep(for: .milliseconds(500))
|
||||
@ -232,7 +225,7 @@ func testExtractText() async throws {
|
||||
}
|
||||
|
||||
let result = try runAXORCCommand(arguments: [extractJsonString])
|
||||
#expect(result.exitCode == 0)
|
||||
XCTAssertEqual(result.exitCode , 0)
|
||||
|
||||
guard let output = result.output,
|
||||
let responseData = output.data(using: String.Encoding.utf8)
|
||||
@ -242,16 +235,18 @@ func testExtractText() async throws {
|
||||
|
||||
let response = try JSONDecoder().decode(QueryResponse.self, from: responseData)
|
||||
|
||||
#expect(response.success == true)
|
||||
XCTAssertEqual(response.success , true)
|
||||
|
||||
if let data = response.data, let attributes = data.attributes {
|
||||
// For extract text commands, check for extracted text in attributes
|
||||
if let extractedText = attributes["extractedText"]?.value as? String {
|
||||
#expect(extractedText.contains("This is test content"), "Should extract the test content")
|
||||
#expect(extractedText.contains("multiple lines"), "Should extract multiple lines")
|
||||
XCTAssertTrue(extractedText.contains("This is test content"), "Should extract the test content")
|
||||
XCTAssertTrue(extractedText.contains("multiple lines"), "Should extract multiple lines")
|
||||
} else if let value = attributes["AXValue"]?.value as? String {
|
||||
#expect(value.contains("This is test content"), "Should extract the test content")
|
||||
#expect(value.contains("multiple lines"), "Should extract multiple lines")
|
||||
XCTAssertTrue(value.contains("This is test content"), "Should extract the test content")
|
||||
XCTAssertTrue(value.contains("multiple lines"), "Should extract multiple lines")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,11 +1,11 @@
|
||||
@testable import AXorcist
|
||||
import Foundation
|
||||
import Testing
|
||||
import XCTest
|
||||
|
||||
// MARK: - Ping Command Tests
|
||||
|
||||
@Test("Test Ping via STDIN")
|
||||
func pingViaStdin() async throws {
|
||||
class PingIntegrationTests: XCTestCase {
|
||||
func testPingViaStdin() async throws {
|
||||
let inputJSON = """
|
||||
{
|
||||
"command_id": "test_ping_stdin",
|
||||
@ -20,38 +20,37 @@ func pingViaStdin() async throws {
|
||||
arguments: ["--stdin"]
|
||||
)
|
||||
|
||||
#expect(
|
||||
result.exitCode == 0,
|
||||
XCTAssertEqual(
|
||||
result.exitCode , 0,
|
||||
Comment(rawValue: "axorc command failed with status \(result.exitCode). Error: \(result.errorOutput ?? "N/A")")
|
||||
)
|
||||
#expect(
|
||||
result.errorOutput == nil || result.errorOutput!.isEmpty,
|
||||
XCTAssertEqual(
|
||||
result.errorOutput , nil || result.errorOutput!.isEmpty,
|
||||
Comment(rawValue: "Expected no error output, but got: \(result.errorOutput!)")
|
||||
)
|
||||
|
||||
guard let outputString = result.output else {
|
||||
#expect(Bool(false), Comment(rawValue: "Output was nil for ping via STDIN"))
|
||||
XCTAssertTrue(Bool(false), Comment(rawValue: "Output was nil for ping via STDIN"))
|
||||
return
|
||||
}
|
||||
|
||||
guard let responseData = outputString.data(using: String.Encoding.utf8) else {
|
||||
#expect(
|
||||
XCTAssertTrue(
|
||||
Bool(false),
|
||||
Comment(rawValue: "Failed to convert output to Data for ping via STDIN. Output: \(outputString)")
|
||||
)
|
||||
return
|
||||
}
|
||||
let decodedResponse = try JSONDecoder().decode(SimpleSuccessResponse.self, from: responseData)
|
||||
#expect(decodedResponse.success == true)
|
||||
#expect(
|
||||
decodedResponse.message == "Ping handled by AXORCCommand. Input source: STDIN",
|
||||
XCTAssertEqual(decodedResponse.success , true)
|
||||
XCTAssertEqual(
|
||||
decodedResponse.message , "Ping handled by AXORCCommand. Input source: STDIN",
|
||||
Comment(rawValue: "Unexpected success message: \(decodedResponse.message)")
|
||||
)
|
||||
#expect(decodedResponse.details == "Hello from testPingViaStdin")
|
||||
XCTAssertEqual(decodedResponse.details , "Hello from testPingViaStdin")
|
||||
}
|
||||
|
||||
@Test("Test Ping via --file")
|
||||
func pingViaFile() async throws {
|
||||
func testPingViaFile() async throws {
|
||||
let payloadMessage = "Hello from testPingViaFile"
|
||||
let inputJSON = """
|
||||
{
|
||||
@ -65,74 +64,72 @@ func pingViaFile() async throws {
|
||||
|
||||
let result = try runAXORCCommand(arguments: ["--file", tempFilePath])
|
||||
|
||||
#expect(
|
||||
result.exitCode == 0,
|
||||
XCTAssertEqual(
|
||||
result.exitCode , 0,
|
||||
Comment(rawValue: "axorc command failed with status \(result.exitCode). Error: \(result.errorOutput ?? "N/A")")
|
||||
)
|
||||
#expect(
|
||||
result.errorOutput == nil || result.errorOutput!.isEmpty,
|
||||
XCTAssertEqual(
|
||||
result.errorOutput , nil || result.errorOutput!.isEmpty,
|
||||
Comment(rawValue: "Expected no error output, but got: \(result.errorOutput ?? "N/A")")
|
||||
)
|
||||
|
||||
guard let outputString = result.output else {
|
||||
#expect(Bool(false), Comment(rawValue: "Output was nil for ping via file"))
|
||||
XCTAssertTrue(Bool(false), Comment(rawValue: "Output was nil for ping via file"))
|
||||
return
|
||||
}
|
||||
guard let responseData = outputString.data(using: String.Encoding.utf8) else {
|
||||
#expect(
|
||||
XCTAssertTrue(
|
||||
Bool(false),
|
||||
Comment(rawValue: "Failed to convert output to Data for ping via file. Output: \(outputString)")
|
||||
)
|
||||
return
|
||||
}
|
||||
let decodedResponse = try JSONDecoder().decode(SimpleSuccessResponse.self, from: responseData)
|
||||
#expect(decodedResponse.success == true)
|
||||
#expect(
|
||||
XCTAssertEqual(decodedResponse.success , true)
|
||||
XCTAssertTrue(
|
||||
decodedResponse.message.lowercased().contains("file: \(tempFilePath.lowercased())"),
|
||||
Comment(rawValue: "Message should contain file path. Got: \(decodedResponse.message)")
|
||||
)
|
||||
#expect(decodedResponse.details == payloadMessage)
|
||||
XCTAssertEqual(decodedResponse.details , payloadMessage)
|
||||
}
|
||||
|
||||
@Test("Test Ping via direct positional argument")
|
||||
func pingViaDirectPayload() async throws {
|
||||
func testPingViaDirectPayload() async throws {
|
||||
let payloadMessage = "Hello from testPingViaDirectPayload"
|
||||
let inputJSON =
|
||||
"{\"command_id\":\"test_ping_direct\",\"command\":\"ping\",\"payload\":{\"message\":\"\(payloadMessage)\"}}"
|
||||
|
||||
let result = try runAXORCCommand(arguments: [inputJSON])
|
||||
|
||||
#expect(
|
||||
result.exitCode == 0,
|
||||
XCTAssertEqual(
|
||||
result.exitCode , 0,
|
||||
Comment(rawValue: "axorc command failed with status \(result.exitCode). Error: \(result.errorOutput ?? "N/A")")
|
||||
)
|
||||
#expect(
|
||||
result.errorOutput == nil || result.errorOutput!.isEmpty,
|
||||
XCTAssertEqual(
|
||||
result.errorOutput , nil || result.errorOutput!.isEmpty,
|
||||
Comment(rawValue: "Expected no error output, but got: \(result.errorOutput ?? "N/A")")
|
||||
)
|
||||
|
||||
guard let outputString = result.output else {
|
||||
#expect(Bool(false), Comment(rawValue: "Output was nil for ping via direct payload"))
|
||||
XCTAssertTrue(Bool(false), Comment(rawValue: "Output was nil for ping via direct payload"))
|
||||
return
|
||||
}
|
||||
guard let responseData = outputString.data(using: String.Encoding.utf8) else {
|
||||
#expect(
|
||||
XCTAssertTrue(
|
||||
Bool(false),
|
||||
Comment(rawValue: "Failed to convert output to Data for ping via direct payload. Output: \(outputString)")
|
||||
)
|
||||
return
|
||||
}
|
||||
let decodedResponse = try JSONDecoder().decode(SimpleSuccessResponse.self, from: responseData)
|
||||
#expect(decodedResponse.success == true)
|
||||
#expect(
|
||||
XCTAssertEqual(decodedResponse.success , true)
|
||||
XCTAssertTrue(
|
||||
decodedResponse.message.contains("Direct Argument Payload"),
|
||||
Comment(rawValue: "Unexpected success message: \(decodedResponse.message)")
|
||||
)
|
||||
#expect(decodedResponse.details == payloadMessage)
|
||||
XCTAssertEqual(decodedResponse.details , payloadMessage)
|
||||
}
|
||||
|
||||
@Test("Test Error: Multiple Input Methods (stdin and file)")
|
||||
func errorMultipleInputMethods() async throws {
|
||||
func testErrorMultipleInputMethods() async throws {
|
||||
let inputJSON = """
|
||||
{
|
||||
"command_id": "test_error_multiple_inputs",
|
||||
@ -148,18 +145,18 @@ func errorMultipleInputMethods() async throws {
|
||||
arguments: ["--file", tempFilePath]
|
||||
)
|
||||
|
||||
#expect(
|
||||
result.exitCode == 0,
|
||||
XCTAssertEqual(
|
||||
result.exitCode , 0,
|
||||
Comment(rawValue: "axorc command should return 0 with error on stdout. Status: \(result.exitCode). " +
|
||||
"Error STDOUT: \(result.output ?? "nil"). Error STDERR: \(result.errorOutput ?? "nil")")
|
||||
)
|
||||
|
||||
guard let outputString = result.output, !outputString.isEmpty else {
|
||||
#expect(Bool(false), Comment(rawValue: "Output was nil or empty for multiple input methods error test"))
|
||||
XCTAssertTrue(Bool(false), Comment(rawValue: "Output was nil or empty for multiple input methods error test"))
|
||||
return
|
||||
}
|
||||
guard let responseData = outputString.data(using: String.Encoding.utf8) else {
|
||||
#expect(
|
||||
XCTAssertTrue(
|
||||
Bool(false),
|
||||
Comment(
|
||||
rawValue: "Failed to convert output to Data for multiple input methods error. Output: \(outputString)"
|
||||
@ -168,42 +165,43 @@ func errorMultipleInputMethods() async throws {
|
||||
return
|
||||
}
|
||||
let errorResponse = try JSONDecoder().decode(ErrorResponse.self, from: responseData)
|
||||
#expect(errorResponse.success == false)
|
||||
#expect(
|
||||
XCTAssertEqual(errorResponse.success , false)
|
||||
XCTAssertTrue(
|
||||
errorResponse.error.message.contains("Multiple input flags specified"),
|
||||
Comment(rawValue: "Unexpected error message: \(errorResponse.error.message)")
|
||||
)
|
||||
}
|
||||
|
||||
@Test("Test Error: No Input Provided for Ping")
|
||||
func errorNoInputProvidedForPing() async throws {
|
||||
func testErrorNoInputProvidedForPing() async throws {
|
||||
let result = try runAXORCCommand(arguments: [])
|
||||
|
||||
#expect(
|
||||
result.exitCode == 0,
|
||||
XCTAssertEqual(
|
||||
result.exitCode , 0,
|
||||
Comment(rawValue: "axorc should return 0 with error on stdout. Status: \(result.exitCode). " +
|
||||
"Error STDOUT: \(result.output ?? "nil"). Error STDERR: \(result.errorOutput ?? "nil")")
|
||||
)
|
||||
|
||||
guard let outputString = result.output, !outputString.isEmpty else {
|
||||
#expect(Bool(false), Comment(rawValue: "Output was nil or empty for no input test."))
|
||||
XCTAssertTrue(Bool(false), Comment(rawValue: "Output was nil or empty for no input test."))
|
||||
return
|
||||
}
|
||||
guard let responseData = outputString.data(using: String.Encoding.utf8) else {
|
||||
#expect(
|
||||
XCTAssertTrue(
|
||||
Bool(false),
|
||||
Comment(rawValue: "Failed to convert output to Data for no input error. Output: \(outputString)")
|
||||
)
|
||||
return
|
||||
}
|
||||
let errorResponse = try JSONDecoder().decode(ErrorResponse.self, from: responseData)
|
||||
#expect(errorResponse.success == false)
|
||||
#expect(
|
||||
errorResponse.commandId == "input_error",
|
||||
XCTAssertEqual(errorResponse.success , false)
|
||||
XCTAssertEqual(
|
||||
errorResponse.commandId , "input_error",
|
||||
Comment(rawValue: "Expected commandId to be input_error, got \(errorResponse.commandId)")
|
||||
)
|
||||
#expect(
|
||||
XCTAssertTrue(
|
||||
errorResponse.error.message.contains("No JSON input method specified"),
|
||||
Comment(rawValue: "Unexpected error message for no input: \(errorResponse.error.message)")
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,16 +1,16 @@
|
||||
import AppKit
|
||||
@testable import AXorcist
|
||||
import Testing
|
||||
import XCTest
|
||||
|
||||
// MARK: - Query Command Tests
|
||||
|
||||
@Test("Launch TextEdit, Get Focused Element via STDIN")
|
||||
func launchAndQueryTextEdit() async throws {
|
||||
class QueryIntegrationTests: XCTestCase {
|
||||
func testLaunchAndQueryTextEdit() async throws {
|
||||
await closeTextEdit()
|
||||
try await Task.sleep(for: .milliseconds(500))
|
||||
|
||||
let (pid, _) = try await setupTextEditAndGetInfo()
|
||||
#expect(pid != 0, "PID should not be zero after TextEdit setup")
|
||||
XCTAssertNotEqual(pid , 0, "PID should not be zero after TextEdit setup")
|
||||
|
||||
let commandId = "focused_textedit_test_\(UUID().uuidString)"
|
||||
let attributesToFetch: [String] = [
|
||||
@ -61,13 +61,13 @@ func launchAndQueryTextEdit() async throws {
|
||||
let expectedRole = ApplicationServices.kAXTextAreaRole as String
|
||||
let actualRole = elementData.attributes?[ApplicationServices.kAXRoleAttribute as String]?.value as? String
|
||||
let attributeKeys = elementData.attributes?.keys.map { Array($0) } ?? []
|
||||
#expect(
|
||||
actualRole == expectedRole,
|
||||
XCTAssertEqual(
|
||||
actualRole , expectedRole,
|
||||
Comment(rawValue: "Focused element role should be '\(expectedRole)'. Got: '\(actualRole ?? "nil")'. " +
|
||||
"Attributes: \(attributeKeys)")
|
||||
)
|
||||
|
||||
#expect(
|
||||
XCTAssertTrue(
|
||||
elementData.attributes?.keys.contains(ApplicationServices.kAXValueAttribute as String) == true,
|
||||
"Focused element attributes should contain kAXValueAttribute as it was requested."
|
||||
)
|
||||
@ -80,9 +80,7 @@ func launchAndQueryTextEdit() async throws {
|
||||
await closeTextEdit()
|
||||
}
|
||||
|
||||
@Test("Get Attributes for TextEdit Application")
|
||||
@MainActor
|
||||
func getAttributesForTextEditApplication() async throws {
|
||||
func testGetAttributesForTextEditApplication() async throws {
|
||||
let commandId = "getattributes-textedit-app-\(UUID().uuidString)"
|
||||
let textEditBundleId = "com.apple.TextEdit"
|
||||
let requestedAttributes = ["AXRole", "AXTitle", "AXWindows", "AXFocusedWindow", "AXMainWindow", "AXIdentifier"]
|
||||
@ -122,38 +120,38 @@ func getAttributesForTextEditApplication() async throws {
|
||||
|
||||
let queryResponse = try decodeQueryResponse(from: outputString, commandName: "getAttributes")
|
||||
validateQueryResponseBasics(queryResponse, expectedCommandId: commandId, expectedCommand: .getAttributes)
|
||||
#expect(queryResponse.data?.attributes != nil, "AXElement attributes should not be nil.")
|
||||
XCTAssertNotEqual(queryResponse.data?.attributes , nil, "AXElement attributes should not be nil.")
|
||||
|
||||
let attributes = queryResponse.data?.attributes
|
||||
#expect(
|
||||
attributes?["AXRole"]?.value as? String == "AXApplication",
|
||||
XCTAssertEqual(
|
||||
attributes?["AXRole"]?.value as? String , "AXApplication",
|
||||
Comment(
|
||||
rawValue: "Application role should be AXApplication. Got: \(String(describing: attributes?["AXRole"]?.value))"
|
||||
)
|
||||
)
|
||||
#expect(
|
||||
attributes?["AXTitle"]?.value as? String == "TextEdit",
|
||||
XCTAssertEqual(
|
||||
attributes?["AXTitle"]?.value as? String , "TextEdit",
|
||||
Comment(
|
||||
rawValue: "Application title should be TextEdit. Got: \(String(describing: attributes?["AXTitle"]?.value))"
|
||||
)
|
||||
)
|
||||
|
||||
if let windowsAttr = attributes?["AXWindows"] {
|
||||
#expect(
|
||||
XCTAssertTrue(
|
||||
windowsAttr.value is [Any],
|
||||
Comment(rawValue: "AXWindows should be an array. Type: \(type(of: windowsAttr.value))")
|
||||
)
|
||||
if let windowsArray = windowsAttr.value as? [AnyCodable] {
|
||||
#expect(!windowsArray.isEmpty, "AXWindows array should not be empty if TextEdit has windows.")
|
||||
XCTAssertTrue(!windowsArray.isEmpty, "AXWindows array should not be empty if TextEdit has windows.")
|
||||
} else if let windowsArray = windowsAttr.value as? [Any] {
|
||||
#expect(!windowsArray.isEmpty, "AXWindows array should not be empty (general type check).")
|
||||
XCTAssertTrue(!windowsArray.isEmpty, "AXWindows array should not be empty (general type check).")
|
||||
}
|
||||
} else {
|
||||
#expect(attributes?["AXWindows"] != nil, "AXWindows attribute should be present.")
|
||||
XCTAssertNotEqual(attributes?["AXWindows"] , nil, "AXWindows attribute should be present.")
|
||||
}
|
||||
|
||||
#expect(queryResponse.debugLogs != nil, "Debug logs should be present.")
|
||||
#expect(
|
||||
XCTAssertNotEqual(queryResponse.debugLogs , nil, "Debug logs should be present.")
|
||||
XCTAssertTrue(
|
||||
queryResponse.debugLogs?
|
||||
.contains {
|
||||
$0.contains("Handling getAttributes command") || $0.contains("handleGetAttributes completed")
|
||||
@ -163,9 +161,7 @@ func getAttributesForTextEditApplication() async throws {
|
||||
)
|
||||
}
|
||||
|
||||
@Test("Query for TextEdit Text Area")
|
||||
@MainActor
|
||||
func queryForTextEditTextArea() async throws {
|
||||
func testQueryForTextEditTextArea() async throws {
|
||||
let commandId = "query-textedit-textarea-\(UUID().uuidString)"
|
||||
let textEditBundleId = "com.apple.TextEdit"
|
||||
let textAreaRole = ApplicationServices.kAXTextAreaRole as String
|
||||
@ -208,30 +204,28 @@ func queryForTextEditTextArea() async throws {
|
||||
|
||||
let queryResponse = try decodeQueryResponse(from: outputString, commandName: "query")
|
||||
validateQueryResponseBasics(queryResponse, expectedCommandId: commandId, expectedCommand: .query)
|
||||
#expect(queryResponse.data?.attributes != nil, "AXElement attributes should not be nil.")
|
||||
XCTAssertNotEqual(queryResponse.data?.attributes , nil, "AXElement attributes should not be nil.")
|
||||
|
||||
let attributes = queryResponse.data?.attributes
|
||||
#expect(
|
||||
attributes?["AXRole"]?.value as? String == textAreaRole,
|
||||
XCTAssertEqual(
|
||||
attributes?["AXRole"]?.value as? String , textAreaRole,
|
||||
Comment(
|
||||
rawValue: "Element role should be \(textAreaRole). Got: \(String(describing: attributes?["AXRole"]?.value))"
|
||||
)
|
||||
)
|
||||
|
||||
#expect(attributes?["AXValue"]?.value is String, "AXValue should exist and be a string.")
|
||||
#expect(attributes?["AXNumberOfCharacters"]?.value is Int, "AXNumberOfCharacters should exist and be an Int.")
|
||||
XCTAssertTrue(attributes?["AXValue"]?.value is String, "AXValue should exist and be a string.")
|
||||
XCTAssertTrue(attributes?["AXNumberOfCharacters"]?.value is Int, "AXNumberOfCharacters should exist and be an Int.")
|
||||
|
||||
#expect(queryResponse.debugLogs != nil, "Debug logs should be present.")
|
||||
#expect(
|
||||
XCTAssertNotEqual(queryResponse.debugLogs , nil, "Debug logs should be present.")
|
||||
XCTAssertTrue(
|
||||
queryResponse.debugLogs?
|
||||
.contains { $0.contains("Handling query command") || $0.contains("handleQuery completed") } == true,
|
||||
"Debug logs should indicate query execution."
|
||||
)
|
||||
}
|
||||
|
||||
@Test("Describe TextEdit Text Area")
|
||||
@MainActor
|
||||
func describeTextEditTextArea() async throws {
|
||||
func testDescribeTextEditTextArea() async throws {
|
||||
let commandId = "describe-textedit-textarea-\(UUID().uuidString)"
|
||||
let textEditBundleId = "com.apple.TextEdit"
|
||||
let textAreaRole = ApplicationServices.kAXTextAreaRole as String
|
||||
@ -277,24 +271,24 @@ func describeTextEditTextArea() async throws {
|
||||
throw TestError.generic("Attributes dictionary is nil in describeElement response.")
|
||||
}
|
||||
|
||||
#expect(
|
||||
attributes["AXRole"]?.value as? String == textAreaRole,
|
||||
XCTAssertEqual(
|
||||
attributes["AXRole"]?.value as? String , textAreaRole,
|
||||
Comment(
|
||||
rawValue: "Element role should be \(textAreaRole). Got: \(String(describing: attributes["AXRole"]?.value))"
|
||||
)
|
||||
)
|
||||
|
||||
#expect(attributes["AXRoleDescription"]?.value is String, "AXRoleDescription should exist.")
|
||||
#expect(attributes["AXEnabled"]?.value is Bool, "AXEnabled should exist.")
|
||||
#expect(attributes["AXPosition"]?.value != nil, "AXPosition should exist.")
|
||||
#expect(attributes["AXSize"]?.value != nil, "AXSize should exist.")
|
||||
#expect(
|
||||
XCTAssertTrue(attributes["AXRoleDescription"]?.value is String, "AXRoleDescription should exist.")
|
||||
XCTAssertTrue(attributes["AXEnabled"]?.value is Bool, "AXEnabled should exist.")
|
||||
XCTAssertNotEqual(attributes["AXPosition"]?.value , nil, "AXPosition should exist.")
|
||||
XCTAssertNotEqual(attributes["AXSize"]?.value , nil, "AXSize should exist.")
|
||||
XCTAssertGreaterThan(
|
||||
attributes.count > 10,
|
||||
Comment(rawValue: "Expected describeElement to return many attributes (e.g., > 10). Got \(attributes.count)")
|
||||
Comment(rawValue: "Expected describeElement to return many attributes (e.g., , 10). Got \(attributes.count)")
|
||||
)
|
||||
|
||||
#expect(queryResponse.debugLogs != nil, "Debug logs should be present.")
|
||||
#expect(
|
||||
XCTAssertNotEqual(queryResponse.debugLogs , nil, "Debug logs should be present.")
|
||||
XCTAssertTrue(
|
||||
queryResponse.debugLogs?
|
||||
.contains {
|
||||
$0.contains("Handling describeElement command") || $0.contains("handleDescribeElement completed")
|
||||
@ -357,12 +351,12 @@ private func validateCommandExecution(
|
||||
exitCode: Int32,
|
||||
commandName: String
|
||||
) throws -> String {
|
||||
#expect(
|
||||
exitCode == 0,
|
||||
XCTAssertEqual(
|
||||
exitCode , 0,
|
||||
Comment(rawValue: "axorc process should exit with 0 for \(commandName). Error: \(errorOutput ?? "N/A")")
|
||||
)
|
||||
#expect(
|
||||
errorOutput == nil || errorOutput!.isEmpty,
|
||||
XCTAssertEqual(
|
||||
errorOutput , nil || errorOutput!.isEmpty,
|
||||
Comment(rawValue: "STDERR should be empty on success. Got: \(errorOutput ?? "")")
|
||||
)
|
||||
|
||||
@ -379,15 +373,17 @@ private func validateQueryResponseBasics(
|
||||
expectedCommandId: String,
|
||||
expectedCommand: CommandType
|
||||
) {
|
||||
#expect(queryResponse.commandId == expectedCommandId)
|
||||
#expect(
|
||||
queryResponse.success == true,
|
||||
XCTAssertEqual(queryResponse.commandId , expectedCommandId)
|
||||
XCTAssertEqual(
|
||||
queryResponse.success , true,
|
||||
Comment(rawValue: "Command should succeed. Error: \(queryResponse.error?.message ?? "None")")
|
||||
)
|
||||
#expect(queryResponse.command == expectedCommand.rawValue)
|
||||
#expect(
|
||||
queryResponse.error == nil,
|
||||
XCTAssertEqual(queryResponse.command , expectedCommand.rawValue)
|
||||
XCTAssertEqual(
|
||||
queryResponse.error , nil,
|
||||
Comment(rawValue: "Error field should be nil. Got: \(queryResponse.error?.message ?? "N/A")")
|
||||
)
|
||||
#expect(queryResponse.data != nil, "Data field should not be nil.")
|
||||
XCTAssertNotEqual(queryResponse.data , nil, "Data field should not be nil.")
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user