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:
@@ -50,8 +50,7 @@ struct VerificationScreen {
|
||||
|
||||
func enterCode(_ code: String) {
|
||||
codeField.waitForExistenceOrFail(timeout: 10)
|
||||
codeField.forceTap()
|
||||
codeField.typeText(code)
|
||||
codeField.focusAndType(code, app: app)
|
||||
}
|
||||
|
||||
func submitCode() {
|
||||
@@ -60,7 +59,7 @@ struct VerificationScreen {
|
||||
}
|
||||
|
||||
func tapLogoutIfAvailable() {
|
||||
let logout = app.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Logout' OR label CONTAINS[c] 'Log Out'")).firstMatch
|
||||
let logout = app.buttons[AccessibilityIdentifiers.Profile.logoutButton].firstMatch
|
||||
if logout.waitForExistence(timeout: 3) {
|
||||
logout.forceTap()
|
||||
}
|
||||
@@ -79,6 +78,24 @@ struct MainTabScreenObject {
|
||||
return app.tabBars.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Residences'")).firstMatch
|
||||
}
|
||||
|
||||
var tasksTab: XCUIElement {
|
||||
let byID = app.buttons[AccessibilityIdentifiers.Navigation.tasksTab]
|
||||
if byID.exists { return byID }
|
||||
return app.tabBars.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Tasks'")).firstMatch
|
||||
}
|
||||
|
||||
var contractorsTab: XCUIElement {
|
||||
let byID = app.buttons[AccessibilityIdentifiers.Navigation.contractorsTab]
|
||||
if byID.exists { return byID }
|
||||
return app.tabBars.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Contractors'")).firstMatch
|
||||
}
|
||||
|
||||
var documentsTab: XCUIElement {
|
||||
let byID = app.buttons[AccessibilityIdentifiers.Navigation.documentsTab]
|
||||
if byID.exists { return byID }
|
||||
return app.tabBars.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Doc'")).firstMatch
|
||||
}
|
||||
|
||||
var profileTab: XCUIElement {
|
||||
let byID = app.buttons[AccessibilityIdentifiers.Navigation.profileTab]
|
||||
if byID.exists { return byID }
|
||||
@@ -96,6 +113,21 @@ struct MainTabScreenObject {
|
||||
residencesTab.forceTap()
|
||||
}
|
||||
|
||||
func goToTasks() {
|
||||
tasksTab.waitForExistenceOrFail(timeout: 10)
|
||||
tasksTab.forceTap()
|
||||
}
|
||||
|
||||
func goToContractors() {
|
||||
contractorsTab.waitForExistenceOrFail(timeout: 10)
|
||||
contractorsTab.forceTap()
|
||||
}
|
||||
|
||||
func goToDocuments() {
|
||||
documentsTab.waitForExistenceOrFail(timeout: 10)
|
||||
documentsTab.forceTap()
|
||||
}
|
||||
|
||||
func goToProfile() {
|
||||
profileTab.waitForExistenceOrFail(timeout: 10)
|
||||
profileTab.forceTap()
|
||||
@@ -150,11 +182,15 @@ struct ResidenceFormScreen {
|
||||
|
||||
func enterName(_ value: String) {
|
||||
nameField.waitForExistenceOrFail(timeout: 10)
|
||||
nameField.forceTap()
|
||||
nameField.typeText(value)
|
||||
nameField.focusAndType(value, app: app)
|
||||
}
|
||||
|
||||
func save() {
|
||||
saveButton.waitForExistenceOrFail(timeout: 10)
|
||||
saveButton.forceTap()
|
||||
_ = saveButton.waitForNonExistence(timeout: 15)
|
||||
}
|
||||
|
||||
func save() { saveButton.waitForExistenceOrFail(timeout: 10); saveButton.forceTap() }
|
||||
func cancel() { cancelButton.waitForExistenceOrFail(timeout: 10); cancelButton.forceTap() }
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user