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>
This commit is contained in:
@@ -82,6 +82,7 @@ struct AccessibilityIdentifiers {
|
||||
struct Task {
|
||||
// List/Kanban
|
||||
static let addButton = "Task.AddButton"
|
||||
static let refreshButton = "Task.RefreshButton"
|
||||
static let tasksList = "Task.List"
|
||||
static let taskCard = "Task.Card"
|
||||
static let emptyStateView = "Task.EmptyState"
|
||||
@@ -164,6 +165,13 @@ struct AccessibilityIdentifiers {
|
||||
static let filePicker = "DocumentForm.FilePicker"
|
||||
static let notesField = "DocumentForm.NotesField"
|
||||
static let expirationDatePicker = "DocumentForm.ExpirationDatePicker"
|
||||
static let itemNameField = "DocumentForm.ItemNameField"
|
||||
static let modelNumberField = "DocumentForm.ModelNumberField"
|
||||
static let serialNumberField = "DocumentForm.SerialNumberField"
|
||||
static let providerField = "DocumentForm.ProviderField"
|
||||
static let providerContactField = "DocumentForm.ProviderContactField"
|
||||
static let tagsField = "DocumentForm.TagsField"
|
||||
static let locationField = "DocumentForm.LocationField"
|
||||
static let saveButton = "DocumentForm.SaveButton"
|
||||
static let formCancelButton = "DocumentForm.CancelButton"
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ enum UITestRuntime {
|
||||
static let disableAnimationsFlag = "--disable-animations"
|
||||
static let resetStateFlag = "--reset-state"
|
||||
static let mockAuthFlag = "--ui-test-mock-auth"
|
||||
static let completeOnboardingFlag = "--complete-onboarding"
|
||||
|
||||
static var launchArguments: [String] {
|
||||
ProcessInfo.processInfo.arguments
|
||||
@@ -29,6 +30,10 @@ enum UITestRuntime {
|
||||
isEnabled && launchArguments.contains(mockAuthFlag)
|
||||
}
|
||||
|
||||
static var shouldCompleteOnboarding: Bool {
|
||||
isEnabled && launchArguments.contains(completeOnboardingFlag)
|
||||
}
|
||||
|
||||
static func configureForLaunch() {
|
||||
guard isEnabled else { return }
|
||||
|
||||
@@ -37,6 +42,12 @@ enum UITestRuntime {
|
||||
}
|
||||
|
||||
UserDefaults.standard.set(true, forKey: "ui_testing_mode")
|
||||
|
||||
// Mark onboarding complete synchronously before SwiftUI renders,
|
||||
// so RootView routes to the standalone LoginView instead of OnboardingCoordinator.
|
||||
if shouldCompleteOnboarding {
|
||||
UserDefaults.standard.set(true, forKey: "hasCompletedOnboarding")
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor static func resetStateIfRequested() {
|
||||
@@ -45,5 +56,18 @@ enum UITestRuntime {
|
||||
DataManager.shared.clear()
|
||||
OnboardingState.shared.reset()
|
||||
ThemeManager.shared.currentTheme = .bright
|
||||
|
||||
// Re-apply onboarding completion after reset so tests that need
|
||||
// both --reset-state and --complete-onboarding work correctly.
|
||||
if shouldCompleteOnboarding {
|
||||
OnboardingState.shared.completeOnboarding()
|
||||
}
|
||||
}
|
||||
|
||||
/// Mark onboarding as complete so the app shows the standalone login
|
||||
/// instead of the onboarding coordinator. Called after resetState (if any).
|
||||
@MainActor static func completeOnboardingIfRequested() {
|
||||
guard shouldCompleteOnboarding else { return }
|
||||
OnboardingState.shared.completeOnboarding()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user