import XCTest @MainActor final class SettingsUITests: XCTestCase { override func setUpWithError() throws { continueAfterFailure = false } func testSettingsView() throws { let app = XCUIApplication() app.launchPaired() // 1. Open Settings let settingsButton = app.buttons["Settings"] XCTAssertTrue(settingsButton.waitForExistence(timeout: 10)) settingsButton.tap() // 2. Assert Navigation Title let navTitle = app.staticTexts["Settings"] XCTAssertTrue(navTitle.waitForExistence(timeout: 5), "Settings sheet should have title 'Settings'") // 3. Assert section content (SwiftUI renders Section headers as // uppercase "SECURITY" etc. but visibility is fragile across SDKs. // Asserting on the section's contents is more robust.) XCTAssertTrue(app.switches["Require Face ID"].waitForExistence(timeout: 5), "Security → Face ID toggle should exist") XCTAssertTrue(app.buttons["Unpair"].exists, "Danger → Unpair button should exist") // 4. Toggle Face ID. SwiftUI's Toggle inside a Form is famously // sticky for XCUI's .tap() — a coordinate hit on the switch itself // is more reliable. let faceIDToggle = app.switches["Require Face ID"] XCTAssertTrue(faceIDToggle.exists, "Require Face ID toggle should exist") let initialState = faceIDToggle.value as? String faceIDToggle.coordinate(withNormalizedOffset: CGVector(dx: 0.95, dy: 0.5)).tap() // Give SwiftUI a moment to update + persist into UserDefaults. let flipped = NSPredicate(format: "value != %@", initialState ?? "") expectation(for: flipped, evaluatedWith: faceIDToggle, handler: nil) waitForExpectations(timeout: 3) // 5. Verify Sidecar Info // Host:Port is in a LabeledContent. In XCUITest, LabeledContent often appears as two staticTexts. // We look for the value that matches the pattern. let hostText = app.staticTexts.matching(NSPredicate(format: "label CONTAINS '10.13.37.2:17373'")).firstMatch XCTAssertTrue(hostText.exists, "Sidecar host:port should be displayed correctly") // 6. Dismiss let doneButton = app.buttons["Done"] XCTAssertTrue(doneButton.exists) doneButton.tap() XCTAssertFalse(navTitle.waitForExistence(timeout: 5), "Settings sheet should dismiss") } }