import XCTest struct UITestID { struct Root { static let ready = "ui.app.ready" static let onboarding = "ui.root.onboarding" static let login = "ui.root.login" } struct Onboarding { static let welcomeTitle = "Onboarding.WelcomeTitle" static let startFreshButton = "Onboarding.StartFreshButton" static let joinExistingButton = "Onboarding.JoinExistingButton" static let loginButton = "Onboarding.LoginButton" static let valuePropsContainer = "Onboarding.ValuePropsTitle" static let valuePropsNextButton = "Onboarding.ValuePropsNextButton" static let nameResidenceTitle = "Onboarding.NameResidenceTitle" static let residenceNameField = "Onboarding.ResidenceNameField" static let nameResidenceContinueButton = "Onboarding.NameResidenceContinueButton" static let createAccountTitle = "Onboarding.CreateAccountTitle" static let emailSignUpExpandButton = "Onboarding.EmailSignUpExpandButton" static let createAccountButton = "Onboarding.CreateAccountButton" static let backButton = "Onboarding.BackButton" static let skipButton = "Onboarding.SkipButton" static let progressIndicator = "Onboarding.ProgressIndicator" } struct Auth { static let usernameField = "Login.UsernameField" static let passwordField = "Login.PasswordField" static let passwordVisibilityToggle = "Login.PasswordVisibilityToggle" static let loginButton = "Login.LoginButton" static let signUpButton = "Login.SignUpButton" static let forgotPasswordButton = "Login.ForgotPasswordButton" static let registerUsernameField = "Register.UsernameField" static let registerEmailField = "Register.EmailField" static let registerPasswordField = "Register.PasswordField" static let registerConfirmPasswordField = "Register.ConfirmPasswordField" static let registerButton = "Register.RegisterButton" static let registerCancelButton = "Register.CancelButton" } } struct RootScreen { let app: XCUIApplication func waitForReady(timeout: TimeInterval = 15) { app.otherElements[UITestID.Root.ready].waitForExistenceOrFail(timeout: timeout) } } struct OnboardingWelcomeScreen { let app: XCUIApplication private var onboardingRoot: XCUIElement { app.otherElements[UITestID.Root.onboarding] } private var startFreshButton: XCUIElement { app.descendants(matching: .any).matching(identifier: UITestID.Onboarding.startFreshButton).firstMatch } private var joinExistingButton: XCUIElement { app.descendants(matching: .any).matching(identifier: UITestID.Onboarding.joinExistingButton).firstMatch } private var loginButton: XCUIElement { app.descendants(matching: .any).matching(identifier: UITestID.Onboarding.loginButton).firstMatch } private var backButton: XCUIElement { app.descendants(matching: .any).matching(identifier: UITestID.Onboarding.backButton).firstMatch } func waitForLoad(timeout: TimeInterval = 15) { onboardingRoot.waitForExistenceOrFail(timeout: timeout) if startFreshButton.waitForExistence(timeout: 2) { return } for _ in 0..<4 { if backButton.exists && backButton.isHittable { backButton.tap() } if startFreshButton.waitForExistence(timeout: 2) { return } } if !startFreshButton.waitForExistence(timeout: timeout) { XCTFail("Expected onboarding welcome entry point. Debug tree:\n\(app.debugDescription)") } } func tapStartFresh() { startFreshButton.waitUntilHittable(timeout: 10).tap() } func tapJoinExisting() { joinExistingButton.waitUntilHittable(timeout: 10).tap() } func tapAlreadyHaveAccount() { loginButton.waitUntilHittable(timeout: 10).tap() } } struct OnboardingValuePropsScreen { let app: XCUIApplication private var container: XCUIElement { app.descendants(matching: .any).matching(identifier: UITestID.Onboarding.valuePropsContainer).firstMatch } private var continueButton: XCUIElement { app.descendants(matching: .any).matching(identifier: UITestID.Onboarding.valuePropsNextButton).firstMatch } private var backButton: XCUIElement { app.descendants(matching: .any).matching(identifier: UITestID.Onboarding.backButton).firstMatch } func waitForLoad(timeout: TimeInterval = 15) { container.waitForExistenceOrFail(timeout: timeout) } func tapContinue() { continueButton.waitUntilHittable(timeout: 10).tap() } func tapBack() { backButton.waitForExistenceOrFail(timeout: 10) backButton.forceTap() } } struct OnboardingNameResidenceScreen { let app: XCUIApplication private var title: XCUIElement { app.descendants(matching: .any).matching(identifier: UITestID.Onboarding.nameResidenceTitle).firstMatch } private var nameField: XCUIElement { app.textFields[UITestID.Onboarding.residenceNameField] } private var continueButton: XCUIElement { app.descendants(matching: .any).matching(identifier: UITestID.Onboarding.nameResidenceContinueButton).firstMatch } private var backButton: XCUIElement { app.descendants(matching: .any).matching(identifier: UITestID.Onboarding.backButton).firstMatch } func waitForLoad(timeout: TimeInterval = 15) { title.waitForExistenceOrFail(timeout: timeout) } func enterResidenceName(_ value: String) { nameField.waitUntilHittable(timeout: 10).tap() nameField.typeText(value) } func tapContinue() { continueButton.waitUntilHittable(timeout: 10).tap() } func tapBack() { backButton.waitForExistenceOrFail(timeout: 10) backButton.forceTap() } } struct OnboardingCreateAccountScreen { let app: XCUIApplication private var title: XCUIElement { app.descendants(matching: .any).matching(identifier: UITestID.Onboarding.createAccountTitle).firstMatch } private var expandEmailButton: XCUIElement { app.descendants(matching: .any).matching(identifier: UITestID.Onboarding.emailSignUpExpandButton).firstMatch } private var createAccountButton: XCUIElement { app.descendants(matching: .any).matching(identifier: UITestID.Onboarding.createAccountButton).firstMatch } func waitForLoad(timeout: TimeInterval = 15) { title.waitForExistenceOrFail(timeout: timeout) } func expandEmailSignup() { expandEmailButton.waitUntilHittable(timeout: 10).tap() } func waitForCreateAccountButton(timeout: TimeInterval = 10) { createAccountButton.waitForExistenceOrFail(timeout: timeout) } } struct LoginScreen { let app: XCUIApplication private var usernameField: XCUIElement { app.textFields[UITestID.Auth.usernameField] } private var passwordSecureField: XCUIElement { app.secureTextFields[UITestID.Auth.passwordField] } private var passwordVisibleField: XCUIElement { app.textFields[UITestID.Auth.passwordField] } private var loginButton: XCUIElement { app.buttons[UITestID.Auth.loginButton] } private var signUpButton: XCUIElement { app.buttons[UITestID.Auth.signUpButton] } private var forgotPasswordButton: XCUIElement { app.buttons[UITestID.Auth.forgotPasswordButton] } private var visibilityToggle: XCUIElement { app.buttons[UITestID.Auth.passwordVisibilityToggle] } func waitForLoad(timeout: TimeInterval = 15) { usernameField.waitForExistenceOrFail(timeout: timeout) loginButton.waitForExistenceOrFail(timeout: timeout) } func enterUsername(_ username: String) { usernameField.waitUntilHittable(timeout: 10).tap() usernameField.typeText(username) } func enterPassword(_ password: String) { if passwordSecureField.exists { passwordSecureField.tap() passwordSecureField.typeText(password) } else { passwordVisibleField.waitUntilHittable(timeout: 10).tap() passwordVisibleField.typeText(password) } } func tapPasswordVisibilityToggle() { visibilityToggle.waitUntilHittable(timeout: 10).tap() } func tapSignUp() { signUpButton.waitUntilHittable(timeout: 10).tap() } func tapForgotPassword() { forgotPasswordButton.waitUntilHittable(timeout: 10).tap() } func assertPasswordFieldVisible() { XCTAssertTrue(passwordVisibleField.waitForExistence(timeout: 5), "Expected visible password text field after toggle") } } struct RegisterScreen { let app: XCUIApplication private var usernameField: XCUIElement { app.textFields[UITestID.Auth.registerUsernameField] } private var emailField: XCUIElement { app.textFields[UITestID.Auth.registerEmailField] } private var passwordField: XCUIElement { app.secureTextFields[UITestID.Auth.registerPasswordField] } private var confirmPasswordField: XCUIElement { app.secureTextFields[UITestID.Auth.registerConfirmPasswordField] } private var registerButton: XCUIElement { app.buttons[UITestID.Auth.registerButton] } private var cancelButton: XCUIElement { app.buttons[UITestID.Auth.registerCancelButton] } func waitForLoad(timeout: TimeInterval = 15) { usernameField.waitForExistenceOrFail(timeout: timeout) registerButton.waitForExistenceOrFail(timeout: timeout) } func fill(username: String, email: String, password: String) { usernameField.waitForExistenceOrFail(timeout: 10) usernameField.forceTap() usernameField.typeText(username) emailField.waitForExistenceOrFail(timeout: 10) emailField.forceTap() emailField.typeText(email) passwordField.waitForExistenceOrFail(timeout: 10) passwordField.forceTap() passwordField.typeText(password) confirmPasswordField.waitForExistenceOrFail(timeout: 10) confirmPasswordField.forceTap() confirmPasswordField.typeText(password) } func tapCancel() { cancelButton.waitUntilHittable(timeout: 10).tap() } }