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

@@ -14,6 +14,8 @@ final class MultiUserSharingTests: XCTestCase {
private var userA: TestSession!
private var userB: TestSession!
private var cleanerA: TestDataCleaner!
private var cleanerB: TestDataCleaner!
override func setUpWithError() throws {
continueAfterFailure = false
@@ -29,18 +31,27 @@ final class MultiUserSharingTests: XCTestCase {
email: "sharer_a_\(runId)@test.com",
password: "TestPass123!"
) else {
throw XCTSkip("Could not create User A")
XCTFail("Could not create User A"); return
}
userA = a
cleanerA = TestDataCleaner(token: a.token)
guard let b = TestAccountAPIClient.createVerifiedAccount(
username: "sharer_b_\(runId)",
email: "sharer_b_\(runId)@test.com",
password: "TestPass123!"
) else {
throw XCTSkip("Could not create User B")
XCTFail("Could not create User B"); return
}
userB = b
cleanerB = TestDataCleaner(token: b.token)
}
override func tearDownWithError() throws {
// Clean up any resources tracked during tests (handles mid-test failures)
cleanerA?.cleanAll()
cleanerB?.cleanAll()
try super.tearDownWithError()
}
// MARK: - Full Sharing Flow
@@ -403,7 +414,7 @@ final class MultiUserSharingTests: XCTestCase {
email: "sharer_c_\(runId)@test.com",
password: "TestPass123!"
) else {
throw XCTSkip("Could not create User C")
XCTFail("Could not create User C"); return
}
let (residenceId, shareCode) = try createSharedResidence() // A + B
@@ -539,23 +550,25 @@ final class MultiUserSharingTests: XCTestCase {
/// Creates a shared residence: User A owns it, User B joins via share code.
/// Returns (residenceId, shareCode).
private enum SetupError: Error { case failed(String) }
@discardableResult
private func createSharedResidence() throws -> (Int, String) {
let name = "Shared \(UUID().uuidString.prefix(6))"
guard let residence = TestAccountAPIClient.createResidence(
token: userA.token, name: name
) else {
XCTFail("Should create residence"); throw XCTSkip("No residence")
XCTFail("Should create residence"); throw SetupError.failed("No residence")
}
guard let shareCode = TestAccountAPIClient.generateShareCode(
token: userA.token, residenceId: residence.id
) else {
XCTFail("Should generate share code"); throw XCTSkip("No share code")
XCTFail("Should generate share code"); throw SetupError.failed("No share code")
}
guard TestAccountAPIClient.joinWithCode(token: userB.token, code: shareCode.code) != nil else {
XCTFail("User B should join"); throw XCTSkip("Join failed")
XCTFail("User B should join"); throw SetupError.failed("Join failed")
}
return (residence.id, shareCode.code)