Files
honeyDueKMP/iosApp/HoneyDueUITests/PageObjects/RegisterScreen.swift
Trey T 4df8707b92 UI test infrastructure overhaul — 58% to 96% pass rate (231/241)
Major infrastructure changes:
- BaseUITestCase: per-suite app termination via class setUp() prevents
  stale state when parallel clones share simulators
- relaunchBetweenTests override for suites that modify login/onboarding state
- focusAndType: dedicated SecureTextField path handles iOS strong password
  autofill suggestions (Choose My Own Password / Not Now dialogs)
- LoginScreenObject: tapSignUp/tapForgotPassword use scrollIntoView for
  offscreen buttons instead of simple swipeUp
- Removed all coordinate taps from ForgotPasswordScreen, VerifyResetCodeScreen,
  ResetPasswordScreen (Rule 3 compliance)
- Removed all usleep calls from screen objects (Rule 14 compliance)

App fixes exposed by tests:
- ContractorsListView: added onDismiss to sheet for list refresh after save
- AllTasksView: added Task.RefreshButton accessibility identifier
- AccessibilityIdentifiers: added Task.refreshButton
- DocumentsWarrantiesView: onDismiss handler for document list refresh
- Various form views: textContentType, submitLabel, onSubmit for keyboard flow

Test fixes:
- PasswordResetTests: handle auto-login after reset (app skips success screen)
- AuthenticatedUITestCase: refreshTasks() helper for kanban toolbar button
- All pre-login suites use relaunchBetweenTests for test independence
- Deleted dead code: AuthenticatedTestCase, SeededTestData, SeedTests,
  CleanupTests, old Suite0/2/3, Suite1_RegistrationRebuildTests

10 remaining failures: 5 iOS strong password autofill (simulator env),
3 pull-to-refresh gesture on empty lists, 2 feature coverage edge cases.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 15:05:37 -05:00

84 lines
2.7 KiB
Swift

import XCTest
/// Page object for the registration screen.
///
/// Uses accessibility identifiers from `AccessibilityIdentifiers.Authentication`
/// to locate registration form elements and perform sign-up actions.
class RegisterScreen: BaseScreen {
// MARK: - Elements
var usernameField: XCUIElement {
app.textFields[AccessibilityIdentifiers.Authentication.registerUsernameField]
}
var emailField: XCUIElement {
app.textFields[AccessibilityIdentifiers.Authentication.registerEmailField]
}
var passwordField: XCUIElement {
app.secureTextFields[AccessibilityIdentifiers.Authentication.registerPasswordField]
}
var confirmPasswordField: XCUIElement {
app.secureTextFields[AccessibilityIdentifiers.Authentication.registerConfirmPasswordField]
}
var registerButton: XCUIElement {
app.buttons[AccessibilityIdentifiers.Authentication.registerButton]
}
var cancelButton: XCUIElement {
app.buttons[AccessibilityIdentifiers.Authentication.registerCancelButton]
}
/// Fallback element lookup for the register/create account button using predicate
var registerButtonByLabel: XCUIElement {
app.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Register' OR label CONTAINS[c] 'Create Account'")).firstMatch
}
override var isDisplayed: Bool {
// Registration screen is visible if any of the register-specific fields exist
let usernameExists = usernameField.waitForExistence(timeout: timeout)
let emailExists = emailField.exists
return usernameExists || emailExists
}
// MARK: - Actions
/// Fills in the registration form and submits it.
/// Returns a MainTabScreen assuming successful registration leads to the main app.
@discardableResult
func register(username: String, email: String, password: String) -> MainTabScreen {
waitForElement(usernameField)
usernameField.focusAndType(username, app: app)
emailField.focusAndType(email, app: app)
passwordField.focusAndType(password, app: app)
confirmPasswordField.focusAndType(password, app: app)
// Try accessibility identifier first, fall back to label search
if registerButton.exists {
registerButton.tap()
} else {
registerButtonByLabel.tap()
}
return MainTabScreen(app: app)
}
/// Taps cancel to return to the login screen.
@discardableResult
func tapCancel() -> LoginScreen {
if cancelButton.exists {
cancelButton.tap()
} else {
// Fall back to navigation back button
tapBackButton()
}
return LoginScreen(app: app)
}
}