// // AndromidaUITests.swift // AndromidaUITests // // Created by Matt Bruce on 1/25/26. // import XCTest final class AndromidaUITests: XCTestCase { override func setUpWithError() throws { continueAfterFailure = false } @MainActor func testOnboardingFlowCompletes() throws { let onboardingApp = makeApp(resetDefaults: true, hasCompletedSetupWizard: false) onboardingApp.launch() let getStarted = onboardingApp.buttons["onboarding.getStarted"] XCTAssertTrue(getStarted.waitForExistence(timeout: 8)) onboardingApp.terminate() let completedApp = makeApp(resetDefaults: false, hasCompletedSetupWizard: true) completedApp.launch() let ritualsTab = completedApp.tabBars.buttons["Rituals"] XCTAssertTrue(ritualsTab.waitForExistence(timeout: 8)) } @MainActor func testOnboardingHappyPath() throws { let app = makeApp(resetDefaults: true, hasCompletedSetupWizard: false) app.launch() XCTAssertTrue(app.buttons["onboarding.getStarted"].waitForExistence(timeout: 8)) app.buttons["onboarding.getStarted"].tap() tapFirstAvailableElement( app: app, identifiers: ["onboarding.goal.health"], fallbackLabels: ["Health"] ) XCTAssertTrue(app.buttons["onboarding.goalContinue"].waitForExistence(timeout: 8)) app.buttons["onboarding.goalContinue"].tap() tapFirstAvailableElement( app: app, identifiers: ["onboarding.time.morning"], fallbackLabels: ["Morning"] ) XCTAssertTrue(app.buttons["onboarding.timeContinue"].waitForExistence(timeout: 8)) app.buttons["onboarding.timeContinue"].tap() if app.buttons["onboarding.startRitual"].waitForExistence(timeout: 8) { app.buttons["onboarding.startRitual"].tap() } else { XCTAssertTrue(app.buttons["onboarding.skipRitual"].waitForExistence(timeout: 8)) app.buttons["onboarding.skipRitual"].tap() } if app.buttons["onboarding.firstCheckInSkip"].waitForExistence(timeout: 8) { app.buttons["onboarding.firstCheckInSkip"].tap() } else { XCTAssertTrue(app.buttons["onboarding.firstCheckInContinueToRituals"].waitForExistence(timeout: 8)) app.buttons["onboarding.firstCheckInContinueToRituals"].tap() } XCTAssertTrue(app.buttons["onboarding.notificationsMaybeLater"].waitForExistence(timeout: 8)) app.buttons["onboarding.notificationsMaybeLater"].tap() XCTAssertTrue(app.buttons["onboarding.letsGo"].waitForExistence(timeout: 8)) app.buttons["onboarding.letsGo"].tap() XCTAssertTrue(app.tabBars.buttons["Rituals"].waitForExistence(timeout: 8)) } @MainActor func testCreateRitualFromRitualsTab() throws { let app = makeApp(resetDefaults: true, hasCompletedSetupWizard: true) app.launch() let ritualsTab = app.tabBars.buttons["Rituals"] XCTAssertTrue(ritualsTab.waitForExistence(timeout: 8)) ritualsTab.tap() let addMenu = app.buttons["rituals.addMenu"] XCTAssertTrue(addMenu.waitForExistence(timeout: 8)) addMenu.tap() let createNew = app.buttons["rituals.createNew"] XCTAssertTrue(createNew.waitForExistence(timeout: 8)) createNew.tap() let titleField = app.textFields["ritualEditor.titleField"] XCTAssertTrue(titleField.waitForExistence(timeout: 8)) titleField.tap() titleField.typeText("UI Test Ritual") let themeField = app.textFields["ritualEditor.themeField"] XCTAssertTrue(themeField.waitForExistence(timeout: 8)) themeField.tap() themeField.typeText("Daily focus") let newHabitField = app.textFields["ritualEditor.newHabitField"] XCTAssertTrue(newHabitField.waitForExistence(timeout: 8)) newHabitField.tap() newHabitField.typeText("Stretch") let addHabitButton = app.buttons["ritualEditor.addHabitButton"] XCTAssertTrue(addHabitButton.waitForExistence(timeout: 8)) addHabitButton.tap() let saveButton = app.buttons["ritualEditor.saveButton"] XCTAssertTrue(saveButton.waitForExistence(timeout: 8)) saveButton.tap() XCTAssertTrue(app.staticTexts["UI Test Ritual"].waitForExistence(timeout: 8)) } @MainActor func testLaunchPerformance() throws { let app = makeApp(resetDefaults: true, hasCompletedSetupWizard: true) measure(metrics: [XCTApplicationLaunchMetric()]) { app.launch() app.terminate() } } private func makeApp(resetDefaults: Bool, hasCompletedSetupWizard: Bool) -> XCUIApplication { let app = XCUIApplication() app.launchEnvironment["UITEST_MODE"] = "1" if resetDefaults { app.launchEnvironment["UITEST_RESET_USER_DEFAULTS"] = "1" } app.launchEnvironment["UITEST_HAS_COMPLETED_SETUP_WIZARD"] = hasCompletedSetupWizard ? "1" : "0" return app } private func tapFirstAvailableElement( app: XCUIApplication, identifiers: [String], fallbackLabels: [String], timeout: TimeInterval = 8 ) { for identifier in identifiers { let candidates = [ app.buttons[identifier], app.otherElements[identifier], app.staticTexts[identifier], app.descendants(matching: .any)[identifier] ] for element in candidates where element.waitForExistence(timeout: 1) { element.tap() return } } for label in fallbackLabels { let candidates = [ app.buttons[label], app.otherElements[label], app.staticTexts[label] ] for element in candidates where element.waitForExistence(timeout: timeout) { element.tap() return } } XCTFail("Unable to locate tap target. identifiers=\(identifiers), labels=\(fallbackLabels)") } }