import XCTest final class AuthenticationTests: BaseUITestCase { func testF201_OnboardingLoginEntryShowsLoginScreen() { let login = TestFlows.navigateToLoginFromOnboarding(app: app) login.waitForLoad(timeout: defaultTimeout) } func testF202_LoginScreenCanTogglePasswordVisibility() { let login = TestFlows.navigateToLoginFromOnboarding(app: app) login.enterUsername("u") login.enterPassword("p") login.tapPasswordVisibilityToggle() login.assertPasswordFieldVisible() } func testF203_RegisterSheetCanOpenAndDismiss() { let register = TestFlows.openRegisterFromLogin(app: app) register.tapCancel() let login = LoginScreenObject(app: app) login.waitForLoad(timeout: defaultTimeout) } func testF204_RegisterFormAcceptsInput() { let register = TestFlows.openRegisterFromLogin(app: app) register.waitForLoad(timeout: defaultTimeout) XCTAssertTrue(app.buttons[UITestID.Auth.registerButton].exists) } func testF205_LoginButtonDisabledWhenCredentialsAreEmpty() { let login = TestFlows.navigateToLoginFromOnboarding(app: app) login.waitForLoad(timeout: defaultTimeout) let loginButton = app.buttons[UITestID.Auth.loginButton] loginButton.waitForExistenceOrFail(timeout: defaultTimeout) XCTAssertFalse(loginButton.isEnabled, "Login button should be disabled when username/password are empty") } // MARK: - Additional Authentication Coverage func testF206_ForgotPasswordButtonIsAccessible() { let login = TestFlows.navigateToLoginFromOnboarding(app: app) login.waitForLoad(timeout: defaultTimeout) let forgotButton = app.buttons[UITestID.Auth.forgotPasswordButton] forgotButton.waitForExistenceOrFail(timeout: defaultTimeout) XCTAssertTrue(forgotButton.isHittable, "Forgot password button should be accessible") } func testF207_LoginScreenShowsAllExpectedElements() { let login = TestFlows.navigateToLoginFromOnboarding(app: app) login.waitForLoad(timeout: defaultTimeout) XCTAssertTrue(app.textFields[UITestID.Auth.usernameField].exists, "Username field should exist") XCTAssertTrue( app.secureTextFields[UITestID.Auth.passwordField].exists || app.textFields[UITestID.Auth.passwordField].exists, "Password field should exist" ) XCTAssertTrue(app.buttons[UITestID.Auth.loginButton].exists, "Login button should exist") XCTAssertTrue(app.buttons[UITestID.Auth.signUpButton].exists, "Sign up button should exist") XCTAssertTrue(app.buttons[UITestID.Auth.forgotPasswordButton].exists, "Forgot password button should exist") XCTAssertTrue(app.buttons[UITestID.Auth.passwordVisibilityToggle].exists, "Password visibility toggle should exist") } func testF208_RegisterFormShowsAllRequiredFields() { let register = TestFlows.openRegisterFromLogin(app: app) register.waitForLoad(timeout: defaultTimeout) XCTAssertTrue(app.textFields[UITestID.Auth.registerUsernameField].exists, "Register username field should exist") XCTAssertTrue(app.textFields[UITestID.Auth.registerEmailField].exists, "Register email field should exist") XCTAssertTrue(app.secureTextFields[UITestID.Auth.registerPasswordField].exists, "Register password field should exist") XCTAssertTrue(app.secureTextFields[UITestID.Auth.registerConfirmPasswordField].exists, "Register confirm password field should exist") XCTAssertTrue(app.buttons[UITestID.Auth.registerButton].exists, "Register button should exist") XCTAssertTrue(app.buttons[UITestID.Auth.registerCancelButton].exists, "Register cancel button should exist") } func testF209_ForgotPasswordNavigatesToResetFlow() { let login = TestFlows.navigateToLoginFromOnboarding(app: app) login.waitForLoad(timeout: defaultTimeout) login.tapForgotPassword() // Verify that tapping forgot password transitions away from login // The forgot password screen should appear (either sheet or navigation) let forgotPasswordAppeared = app.staticTexts.containing( NSPredicate(format: "label CONTAINS[c] 'Forgot' OR label CONTAINS[c] 'Reset' OR label CONTAINS[c] 'Password'") ).firstMatch.waitForExistence(timeout: defaultTimeout) XCTAssertTrue(forgotPasswordAppeared, "Forgot password flow should appear after tapping button") } // MARK: - AUTH-005: Invalid token at startup clears session and returns to login func test08_invalidatedTokenRedirectsToLogin() throws { try XCTSkipIf(!TestAccountAPIClient.isBackendReachable(), "Backend not reachable") // Create a verified account via API guard let session = TestAccountManager.createVerifiedAccount() else { XCTFail("Could not create verified test account") return } // Login via UI let login = TestFlows.navigateToLoginFromOnboarding(app: app) login.waitForLoad(timeout: defaultTimeout) TestFlows.loginWithCredentials(app: app, username: session.username, password: session.password) // Wait until the main tab bar is visible, confirming successful login let mainTabs = app.otherElements[UITestID.Root.mainTabs] XCTAssertTrue( mainTabs.waitForExistence(timeout: longTimeout), "Expected main tabs after login" ) // Invalidate the token via the logout API (simulates a server-side token revocation) TestAccountManager.invalidateToken(session) // Force restart the app — terminate and relaunch without --reset-state so the // app restores its persisted session, which should then be rejected by the server. app.terminate() app.launchArguments = ["--ui-testing", "--disable-animations"] app.launch() app.otherElements[UITestID.Root.ready].waitForExistenceOrFail(timeout: defaultTimeout) // The app should detect the invalid token and redirect to the login screen let usernameField = app.textFields[UITestID.Auth.usernameField] XCTAssertTrue( usernameField.waitForExistence(timeout: longTimeout), "Expected login screen after startup with an invalidated token" ) } }