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:
Trey T
2026-03-23 15:05:37 -05:00
parent 0ca4a44bac
commit 4df8707b92
67 changed files with 3085 additions and 4853 deletions

View File

@@ -1,157 +1,91 @@
import XCTest
/// Critical path tests for core navigation.
///
/// Validates tab bar navigation, settings access, and screen transitions.
/// Requires a logged-in user. Zero sleep() calls all waits are condition-based.
final class NavigationCriticalPathTests: AuthenticatedTestCase {
override var useSeededAccount: Bool { true }
/// Validates tab bar presence, navigation, settings access, and add buttons.
final class NavigationCriticalPathTests: AuthenticatedUITestCase {
override var needsAPISession: Bool { true }
override func setUpWithError() throws {
try super.setUpWithError()
// Precondition: residence must exist for task add button to appear
ensureResidenceExists()
}
// MARK: - Tab Navigation
func testAllTabsExist() {
let tabBar = app.tabBars.firstMatch
guard tabBar.waitForExistence(timeout: defaultTimeout) else {
XCTFail("Main screen did not appear")
return
}
XCTAssertTrue(tabBar.exists, "Tab bar should exist after login")
let residencesTab = app.tabBars.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Residences'")).firstMatch
let tasksTab = app.tabBars.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Tasks'")).firstMatch
let contractorsTab = app.tabBars.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Contractors'")).firstMatch
let documentsTab = app.tabBars.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Doc'")).firstMatch
let residences = tabBar.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Residences'")).firstMatch
let tasks = tabBar.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Tasks'")).firstMatch
let contractors = tabBar.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Contractors'")).firstMatch
let documents = tabBar.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Doc'")).firstMatch
XCTAssertTrue(residencesTab.exists, "Residences tab should exist")
XCTAssertTrue(tasksTab.exists, "Tasks tab should exist")
XCTAssertTrue(contractorsTab.exists, "Contractors tab should exist")
XCTAssertTrue(documentsTab.exists, "Documents tab should exist")
XCTAssertTrue(residences.exists, "Residences tab should exist")
XCTAssertTrue(tasks.exists, "Tasks tab should exist")
XCTAssertTrue(contractors.exists, "Contractors tab should exist")
XCTAssertTrue(documents.exists, "Documents tab should exist")
}
func testNavigateToTasksTab() {
let tabBar = app.tabBars.firstMatch
guard tabBar.waitForExistence(timeout: defaultTimeout) else {
XCTFail("Main screen did not appear")
return
}
navigateToTasks()
let tasksTab = app.tabBars.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Tasks'")).firstMatch
XCTAssertTrue(tasksTab.isSelected, "Tasks tab should be selected")
// Verify by checking for Tasks screen content, not isSelected (unreliable with sidebarAdaptable)
let addButton = app.buttons[AccessibilityIdentifiers.Task.addButton].firstMatch
XCTAssertTrue(addButton.waitForExistence(timeout: navigationTimeout), "Tasks screen should show add button")
}
func testNavigateToContractorsTab() {
let tabBar = app.tabBars.firstMatch
guard tabBar.waitForExistence(timeout: defaultTimeout) else {
XCTFail("Main screen did not appear")
return
}
navigateToContractors()
let contractorsTab = app.tabBars.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Contractors'")).firstMatch
XCTAssertTrue(contractorsTab.isSelected, "Contractors tab should be selected")
let addButton = app.buttons[AccessibilityIdentifiers.Contractor.addButton].firstMatch
XCTAssertTrue(addButton.waitForExistence(timeout: navigationTimeout), "Contractors screen should show add button")
}
func testNavigateToDocumentsTab() {
let tabBar = app.tabBars.firstMatch
guard tabBar.waitForExistence(timeout: defaultTimeout) else {
XCTFail("Main screen did not appear")
return
}
navigateToDocuments()
let documentsTab = app.tabBars.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Doc'")).firstMatch
XCTAssertTrue(documentsTab.isSelected, "Documents tab should be selected")
let addButton = app.buttons[AccessibilityIdentifiers.Document.addButton].firstMatch
XCTAssertTrue(addButton.waitForExistence(timeout: navigationTimeout), "Documents screen should show add button")
}
func testNavigateBackToResidencesTab() {
let tabBar = app.tabBars.firstMatch
guard tabBar.waitForExistence(timeout: defaultTimeout) else {
XCTFail("Main screen did not appear")
return
}
navigateToDocuments()
navigateToResidences()
let residencesTab = app.tabBars.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Residences'")).firstMatch
XCTAssertTrue(residencesTab.isSelected, "Residences tab should be selected")
let addButton = app.buttons[AccessibilityIdentifiers.Residence.addButton].firstMatch
XCTAssertTrue(addButton.waitForExistence(timeout: navigationTimeout), "Residences screen should show add button")
}
// MARK: - Settings Access
func testSettingsButtonExists() {
let tabBar = app.tabBars.firstMatch
guard tabBar.waitForExistence(timeout: defaultTimeout) else {
XCTFail("Main screen did not appear")
return
}
navigateToResidences()
let settingsButton = app.buttons[AccessibilityIdentifiers.Navigation.settingsButton]
XCTAssertTrue(
settingsButton.waitForExistence(timeout: 5),
"Settings button should exist on Residences screen"
)
XCTAssertTrue(settingsButton.waitForExistence(timeout: defaultTimeout), "Settings button should exist on Residences screen")
}
// MARK: - Add Buttons
func testResidenceAddButtonExists() {
let tabBar = app.tabBars.firstMatch
guard tabBar.waitForExistence(timeout: defaultTimeout) else {
XCTFail("Main screen did not appear")
return
}
navigateToResidences()
let addButton = app.buttons[AccessibilityIdentifiers.Residence.addButton].firstMatch
XCTAssertTrue(
addButton.waitForExistence(timeout: 5),
"Residence add button should exist"
)
XCTAssertTrue(addButton.waitForExistence(timeout: defaultTimeout), "Residence add button should exist")
}
func testTaskAddButtonExists() {
let tabBar = app.tabBars.firstMatch
guard tabBar.waitForExistence(timeout: defaultTimeout) else {
XCTFail("Main screen did not appear")
return
}
navigateToTasks()
let addButton = app.buttons[AccessibilityIdentifiers.Task.addButton].firstMatch
XCTAssertTrue(
addButton.waitForExistence(timeout: 5),
"Task add button should exist"
)
XCTAssertTrue(addButton.waitForExistence(timeout: defaultTimeout), "Task add button should exist")
}
func testContractorAddButtonExists() {
let tabBar = app.tabBars.firstMatch
guard tabBar.waitForExistence(timeout: defaultTimeout) else {
XCTFail("Main screen did not appear")
return
}
navigateToContractors()
let addButton = app.buttons[AccessibilityIdentifiers.Contractor.addButton].firstMatch
XCTAssertTrue(
addButton.waitForExistence(timeout: 5),
"Contractor add button should exist"
)
XCTAssertTrue(addButton.waitForExistence(timeout: defaultTimeout), "Contractor add button should exist")
}
func testDocumentAddButtonExists() {
let tabBar = app.tabBars.firstMatch
guard tabBar.waitForExistence(timeout: defaultTimeout) else {
XCTFail("Main screen did not appear")
return
}
navigateToDocuments()
let addButton = app.buttons[AccessibilityIdentifiers.Document.addButton].firstMatch
XCTAssertTrue(
addButton.waitForExistence(timeout: 5),
"Document add button should exist"
)
XCTAssertTrue(addButton.waitForExistence(timeout: defaultTimeout), "Document add button should exist")
}
}