import XCTest final class StabilityTests: BaseUITestCase { func testP001_RapidOnboardingNavigationDoesNotCrash() { for _ in 0..<3 { let welcome = OnboardingWelcomeScreen(app: app) welcome.waitForLoad(timeout: defaultTimeout) welcome.tapStartFresh() let valueProps = OnboardingValuePropsScreen(app: app) valueProps.waitForLoad(timeout: defaultTimeout) valueProps.tapBack() welcome.waitForLoad(timeout: defaultTimeout) } } func testP002_RepeatedForwardNavigationRemainsResponsive() { for index in 0..<3 { let welcome = OnboardingWelcomeScreen(app: app) welcome.waitForLoad(timeout: defaultTimeout) welcome.tapStartFresh() let valueProps = OnboardingValuePropsScreen(app: app) valueProps.waitForLoad(timeout: defaultTimeout) valueProps.tapContinue() let nameResidence = OnboardingNameResidenceScreen(app: app) nameResidence.waitForLoad(timeout: defaultTimeout) nameResidence.enterResidenceName("Stress Home \(index)") nameResidence.tapBack() valueProps.waitForLoad(timeout: defaultTimeout) valueProps.tapBack() welcome.waitForLoad(timeout: defaultTimeout) } } func testP003_RapidDoubleTapOnValuePropsContinueLandsOnNameResidence() { let welcome = OnboardingWelcomeScreen(app: app) welcome.waitForLoad(timeout: defaultTimeout) welcome.tapStartFresh() let valueProps = OnboardingValuePropsScreen(app: app) valueProps.waitForLoad(timeout: defaultTimeout) let continueButton = app.buttons[UITestID.Onboarding.valuePropsNextButton] continueButton.waitUntilHittable(timeout: defaultTimeout).tap() if continueButton.exists && continueButton.isHittable { continueButton.tap() } let nameResidence = OnboardingNameResidenceScreen(app: app) nameResidence.waitForLoad(timeout: defaultTimeout) } // MARK: - Additional Stability Coverage func testP004_StartFreshThenBackToWelcomeThenJoinExistingDoesNotCorruptState() { let welcome = OnboardingWelcomeScreen(app: app) welcome.waitForLoad(timeout: defaultTimeout) // Start fresh path welcome.tapStartFresh() let valueProps = OnboardingValuePropsScreen(app: app) valueProps.waitForLoad(timeout: defaultTimeout) // Go back to welcome valueProps.tapBack() welcome.waitForLoad(timeout: defaultTimeout) // Switch to join existing path welcome.tapJoinExisting() let createAccount = OnboardingCreateAccountScreen(app: app) createAccount.waitForLoad(timeout: defaultTimeout) } func testP005_RepeatedLoginNavigationRemainsStable() { for _ in 0..<3 { let login = TestFlows.navigateToLoginFromOnboarding(app: app) login.waitForLoad(timeout: defaultTimeout) // Dismiss login (swipe down or navigate back) let backButton = app.descendants(matching: .any).matching(identifier: UITestID.Onboarding.backButton).firstMatch if backButton.waitForExistence(timeout: shortTimeout) && backButton.isHittable { backButton.forceTap() } else { // Try swipe down to dismiss sheet app.swipeDown() } // Should return to onboarding let welcome = OnboardingWelcomeScreen(app: app) welcome.waitForLoad(timeout: defaultTimeout) } } // MARK: - OFF-003: Retry Button Existence /// OFF-003: Retry button is accessible from error states. /// /// A true end-to-end retry test (where the network actually fails then succeeds) /// is not feasible in XCUITest without network manipulation infrastructure. This /// test verifies the structural requirement: that the retry accessibility identifier /// `AccessibilityIdentifiers.Common.retryButton` is defined and that any error view /// in the app exposes a tappable retry control. /// /// When an error view IS visible (e.g., backend is unreachable), the test asserts the /// retry button exists and can be tapped without crashing the app. func testP010_retryButtonExistsOnErrorState() { // Navigate to the login screen from onboarding — this is the most common // path that could encounter an error state if the backend is unreachable. let welcome = OnboardingWelcomeScreen(app: app) welcome.waitForLoad(timeout: defaultTimeout) welcome.tapAlreadyHaveAccount() let login = LoginScreenObject(app: app) login.waitForLoad(timeout: defaultTimeout) // Attempt login with intentionally wrong credentials to trigger an error state login.enterUsername("nonexistent_user_off003") login.enterPassword("WrongPass!") let loginButton = app.buttons[UITestID.Auth.loginButton] loginButton.waitUntilHittable(timeout: defaultTimeout).tap() // Wait briefly to allow any error state to appear sleep(3) // Check for error view and retry button let retryButton = app.buttons[AccessibilityIdentifiers.Common.retryButton] let errorView = app.otherElements[AccessibilityIdentifiers.Common.errorView] // If an error view is visible, assert the retry button is also present and tappable if errorView.exists { XCTAssertTrue( retryButton.waitForExistence(timeout: shortTimeout), "Retry button (\(AccessibilityIdentifiers.Common.retryButton)) should exist when an error view is shown" ) XCTAssertTrue( retryButton.isEnabled, "Retry button should be enabled so the user can re-attempt the failed operation" ) // Tapping retry should not crash the app retryButton.forceTap() sleep(1) XCTAssertTrue(app.exists, "App should remain running after tapping retry") } else { // No error view is currently visible — this is acceptable if login // shows an inline error message instead. Confirm the app is still in a // usable state (it did not crash and the login screen is still present). let stillOnLogin = app.textFields[UITestID.Auth.usernameField].exists let showsAlert = app.alerts.firstMatch.exists let showsErrorText = app.staticTexts.containing( NSPredicate(format: "label CONTAINS[c] 'invalid' OR label CONTAINS[c] 'incorrect' OR label CONTAINS[c] 'error'") ).firstMatch.exists XCTAssertTrue( stillOnLogin || showsAlert || showsErrorText, "After a failed login the app should show an error state — login screen, alert, or inline error" ) } } }