fix(uitests): stop logging out after every authenticated test
All 7 authenticated-flow XCTestCase classes logged out for real in tearDown, forcing a real OTP round trip against the production backend on the next authenticated test. Under CI's back-to-back suite run this produced ~9 real logins to the same QA phone number in ~9 minutes - the first few succeeded (~30-40s each) but every one after that failed to complete within timeout, cascading into 'Login never completed' across CartCheckoutFlowTests, HomeFiltersFlowTests, OrderDetailsFlowTests, SavedCardsFlowTests, UserProfileFlowTests. Removed the per-test logout so the QA account's Keychain-backed session persists across the whole suite (ensureLoggedIn already no-ops when already authenticated). Moved the 'must start logged out' guarantee to the two classes that actually need it - HomeGuestFlowTests and ProfileLoggedOutFlowTests now force logout themselves right after app.launch() via a small launch helper, instead of relying on whichever authenticated class happened to run last. See decisions/2026-09-11-ui-test-shared-login-session.md.
This commit is contained in:
@@ -13,9 +13,13 @@ final class AuthenticatedProfileNavigationTests: XCTestCase {
|
|||||||
continueAfterFailure = false
|
continueAfterFailure = false
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tearDownWithError() throws {
|
// No per-test logout: the standing QA account's Keychain session is
|
||||||
XCUIApplication().logoutIfAuthenticated()
|
// meant to persist across authenticated-flow tests, so only the first
|
||||||
}
|
// one in a suite run pays for a real OTP round trip. Tests that need a
|
||||||
|
// guaranteed logged-out state (HomeGuestFlowTests,
|
||||||
|
// ProfileLoggedOutFlowTests) force it themselves in their own setUp
|
||||||
|
// instead of relying on every authenticated class to log out after
|
||||||
|
// itself - see decisions/2026-09-11-ui-test-shared-login-session.md.
|
||||||
|
|
||||||
func testOrdersScreenReachableAndLoadsRealData() throws {
|
func testOrdersScreenReachableAndLoadsRealData() throws {
|
||||||
let app = XCUIApplication()
|
let app = XCUIApplication()
|
||||||
|
|||||||
@@ -2,17 +2,14 @@ import XCTest
|
|||||||
|
|
||||||
/// Covers a real, authenticated session with the standing QA account (see
|
/// Covers a real, authenticated session with the standing QA account (see
|
||||||
/// TestFixtures.swift / decisions/2026-08-06-ui-test-account-and-app-attest-bypass.md).
|
/// TestFixtures.swift / decisions/2026-08-06-ui-test-account-and-app-attest-bypass.md).
|
||||||
/// Logs out in tearDown so guest-only tests elsewhere in the same run don't
|
/// No per-test logout - the Keychain session is meant to persist across
|
||||||
/// inherit an authenticated Keychain session.
|
/// authenticated-flow tests in the same run; see
|
||||||
|
/// decisions/2026-09-11-ui-test-shared-login-session.md.
|
||||||
final class AuthenticatedSessionFlowTests: XCTestCase {
|
final class AuthenticatedSessionFlowTests: XCTestCase {
|
||||||
override func setUpWithError() throws {
|
override func setUpWithError() throws {
|
||||||
continueAfterFailure = false
|
continueAfterFailure = false
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tearDownWithError() throws {
|
|
||||||
XCUIApplication().logoutIfAuthenticated()
|
|
||||||
}
|
|
||||||
|
|
||||||
func testLoginReachesAuthenticatedProfile() throws {
|
func testLoginReachesAuthenticatedProfile() throws {
|
||||||
let app = XCUIApplication()
|
let app = XCUIApplication()
|
||||||
app.launch()
|
app.launch()
|
||||||
|
|||||||
@@ -10,9 +10,7 @@ final class CartCheckoutFlowTests: XCTestCase {
|
|||||||
continueAfterFailure = false
|
continueAfterFailure = false
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tearDownWithError() throws {
|
// No per-test logout - see decisions/2026-09-11-ui-test-shared-login-session.md.
|
||||||
XCUIApplication().logoutIfAuthenticated()
|
|
||||||
}
|
|
||||||
|
|
||||||
func testAddProductToCartAndReachCheckout() throws {
|
func testAddProductToCartAndReachCheckout() throws {
|
||||||
let app = XCUIApplication()
|
let app = XCUIApplication()
|
||||||
|
|||||||
@@ -20,9 +20,7 @@ final class HomeFiltersFlowTests: XCTestCase {
|
|||||||
continueAfterFailure = false
|
continueAfterFailure = false
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tearDownWithError() throws {
|
// No per-test logout - see decisions/2026-09-11-ui-test-shared-login-session.md.
|
||||||
XCUIApplication().logoutIfAuthenticated()
|
|
||||||
}
|
|
||||||
|
|
||||||
func testFiltersSheetSelectionsAndApply() throws {
|
func testFiltersSheetSelectionsAndApply() throws {
|
||||||
let app = XCUIApplication()
|
let app = XCUIApplication()
|
||||||
|
|||||||
@@ -10,9 +10,19 @@ final class HomeGuestFlowTests: XCTestCase {
|
|||||||
continueAfterFailure = false
|
continueAfterFailure = false
|
||||||
}
|
}
|
||||||
|
|
||||||
func testHomeRendersSearchAndGreeting() throws {
|
/// Authenticated-flow tests no longer log out after themselves (see
|
||||||
|
/// decisions/2026-09-11-ui-test-shared-login-session.md), so guest
|
||||||
|
/// tests must force a clean logged-out state themselves right after
|
||||||
|
/// launch, rather than assume one.
|
||||||
|
private func launchGuestApp() -> XCUIApplication {
|
||||||
let app = XCUIApplication()
|
let app = XCUIApplication()
|
||||||
app.launch()
|
app.launch()
|
||||||
|
app.logoutIfAuthenticated()
|
||||||
|
return app
|
||||||
|
}
|
||||||
|
|
||||||
|
func testHomeRendersSearchAndGreeting() throws {
|
||||||
|
let app = launchGuestApp()
|
||||||
XCTAssertTrue(app.reachGuestHome(), "Never reached Home's search field")
|
XCTAssertTrue(app.reachGuestHome(), "Never reached Home's search field")
|
||||||
|
|
||||||
XCTAssertTrue(app.staticTexts["ENTREGAR EM:"].exists)
|
XCTAssertTrue(app.staticTexts["ENTREGAR EM:"].exists)
|
||||||
@@ -21,8 +31,7 @@ final class HomeGuestFlowTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func testHomeShowsDefaultCategoryChip() throws {
|
func testHomeShowsDefaultCategoryChip() throws {
|
||||||
let app = XCUIApplication()
|
let app = launchGuestApp()
|
||||||
app.launch()
|
|
||||||
XCTAssertTrue(app.reachGuestHome())
|
XCTAssertTrue(app.reachGuestHome())
|
||||||
|
|
||||||
XCTAssertTrue(app.staticTexts["Categories"].exists)
|
XCTAssertTrue(app.staticTexts["Categories"].exists)
|
||||||
@@ -30,8 +39,7 @@ final class HomeGuestFlowTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func testHomeShowsGuestLocationPromptWhenNoLocationChosen() throws {
|
func testHomeShowsGuestLocationPromptWhenNoLocationChosen() throws {
|
||||||
let app = XCUIApplication()
|
let app = launchGuestApp()
|
||||||
app.launch()
|
|
||||||
XCTAssertTrue(app.reachGuestHome())
|
XCTAssertTrue(app.reachGuestHome())
|
||||||
|
|
||||||
// No guest state/city selected -> Home's own inline message, not
|
// No guest state/city selected -> Home's own inline message, not
|
||||||
@@ -40,8 +48,7 @@ final class HomeGuestFlowTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func testTappingAddressChipReopensLocationPicker() throws {
|
func testTappingAddressChipReopensLocationPicker() throws {
|
||||||
let app = XCUIApplication()
|
let app = launchGuestApp()
|
||||||
app.launch()
|
|
||||||
XCTAssertTrue(app.reachGuestHome())
|
XCTAssertTrue(app.reachGuestHome())
|
||||||
|
|
||||||
// The "ENTREGAR EM:" address button re-opens the same address
|
// The "ENTREGAR EM:" address button re-opens the same address
|
||||||
|
|||||||
@@ -9,9 +9,7 @@ final class OrderDetailsFlowTests: XCTestCase {
|
|||||||
continueAfterFailure = false
|
continueAfterFailure = false
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tearDownWithError() throws {
|
// No per-test logout - see decisions/2026-09-11-ui-test-shared-login-session.md.
|
||||||
XCUIApplication().logoutIfAuthenticated()
|
|
||||||
}
|
|
||||||
|
|
||||||
func testOrderDetailsReachableFromOrdersList() throws {
|
func testOrderDetailsReachableFromOrdersList() throws {
|
||||||
let app = XCUIApplication()
|
let app = XCUIApplication()
|
||||||
|
|||||||
@@ -7,9 +7,19 @@ final class ProfileLoggedOutFlowTests: XCTestCase {
|
|||||||
continueAfterFailure = false
|
continueAfterFailure = false
|
||||||
}
|
}
|
||||||
|
|
||||||
func testLoggedOutProfileShowsLoginPrompt() throws {
|
/// Authenticated-flow tests no longer log out after themselves (see
|
||||||
|
/// decisions/2026-09-11-ui-test-shared-login-session.md), so
|
||||||
|
/// logged-out tests must force a clean state themselves right after
|
||||||
|
/// launch, rather than assume one.
|
||||||
|
private func launchLoggedOutApp() -> XCUIApplication {
|
||||||
let app = XCUIApplication()
|
let app = XCUIApplication()
|
||||||
app.launch()
|
app.launch()
|
||||||
|
app.logoutIfAuthenticated()
|
||||||
|
return app
|
||||||
|
}
|
||||||
|
|
||||||
|
func testLoggedOutProfileShowsLoginPrompt() throws {
|
||||||
|
let app = launchLoggedOutApp()
|
||||||
XCTAssertTrue(app.reachLoggedOutProfile(), "Never reached the logged-out profile screen")
|
XCTAssertTrue(app.reachLoggedOutProfile(), "Never reached the logged-out profile screen")
|
||||||
|
|
||||||
XCTAssertTrue(app.staticTexts["Entre na sua conta"].exists)
|
XCTAssertTrue(app.staticTexts["Entre na sua conta"].exists)
|
||||||
@@ -22,8 +32,7 @@ final class ProfileLoggedOutFlowTests: XCTestCase {
|
|||||||
/// a working way back out (its `LCENavigationView` back button, which
|
/// a working way back out (its `LCENavigationView` back button, which
|
||||||
/// sets `root = .main`).
|
/// sets `root = .main`).
|
||||||
func testAuthIntroScreenShowsChoicesAndCanGoBack() throws {
|
func testAuthIntroScreenShowsChoicesAndCanGoBack() throws {
|
||||||
let app = XCUIApplication()
|
let app = launchLoggedOutApp()
|
||||||
app.launch()
|
|
||||||
XCTAssertTrue(app.reachLoggedOutProfile())
|
XCTAssertTrue(app.reachLoggedOutProfile())
|
||||||
|
|
||||||
app.buttons["Entrar ou Cadastrar"].tap()
|
app.buttons["Entrar ou Cadastrar"].tap()
|
||||||
@@ -45,8 +54,7 @@ final class ProfileLoggedOutFlowTests: XCTestCase {
|
|||||||
/// app's standard `AppBackButtonIcon` via `LCENavigationView`, not the
|
/// app's standard `AppBackButtonIcon` via `LCENavigationView`, not the
|
||||||
/// oversized iOS 26 system glass back button.
|
/// oversized iOS 26 system glass back button.
|
||||||
func testPushedAuthScreensUseAppBackButton() throws {
|
func testPushedAuthScreensUseAppBackButton() throws {
|
||||||
let app = XCUIApplication()
|
let app = launchLoggedOutApp()
|
||||||
app.launch()
|
|
||||||
XCTAssertTrue(app.reachLoggedOutProfile())
|
XCTAssertTrue(app.reachLoggedOutProfile())
|
||||||
app.buttons["Entrar ou Cadastrar"].tap()
|
app.buttons["Entrar ou Cadastrar"].tap()
|
||||||
|
|
||||||
|
|||||||
@@ -9,9 +9,7 @@ final class SavedCardsFlowTests: XCTestCase {
|
|||||||
continueAfterFailure = false
|
continueAfterFailure = false
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tearDownWithError() throws {
|
// No per-test logout - see decisions/2026-09-11-ui-test-shared-login-session.md.
|
||||||
XCUIApplication().logoutIfAuthenticated()
|
|
||||||
}
|
|
||||||
|
|
||||||
func testAddCardFormReachableFromSavedCards() throws {
|
func testAddCardFormReachableFromSavedCards() throws {
|
||||||
let app = XCUIApplication()
|
let app = XCUIApplication()
|
||||||
|
|||||||
@@ -7,9 +7,7 @@ final class UserProfileFlowTests: XCTestCase {
|
|||||||
continueAfterFailure = false
|
continueAfterFailure = false
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tearDownWithError() throws {
|
// No per-test logout - see decisions/2026-09-11-ui-test-shared-login-session.md.
|
||||||
XCUIApplication().logoutIfAuthenticated()
|
|
||||||
}
|
|
||||||
|
|
||||||
func testUserProfileReachableFromProfileHeader() throws {
|
func testUserProfileReachableFromProfileHeader() throws {
|
||||||
let app = XCUIApplication()
|
let app = XCUIApplication()
|
||||||
|
|||||||
Reference in New Issue
Block a user