From 84be24f274aac768ad8518f384ff508c2843460b Mon Sep 17 00:00:00 2001 From: "Developer @ Loverde Company" Date: Fri, 11 Sep 2026 10:00:51 -0300 Subject: [PATCH] fix(uitests): move forced logout to class setUp, fix ambiguous Ver Detalhes match Previous commit's per-test logoutIfAuthenticated() in HomeGuestFlowTests and ProfileLoggedOutFlowTests ran the guest address-picker navigation dance (reachProfileTabRegardlessOfAuthState) twice back-to-back per test - once in the new launch helper, once again inside reachGuestHome/ reachLoggedOutProfile - doubling exposure to that dance's known race and regressing 5 previously-stable tests ('Login never completed' was fixed, but new failures appeared in its place). Moved the forced logout into override class func setUp() so it runs once per class instead of once per test; test bodies are back to the plain XCUIApplication()+launch() pattern. Also fixed OrderDetailsFlowTests: 'Ver Detalhes' now matches multiple elements once the QA account has more than one real order (previously masked because login itself was failing first). Switched to a label predicate + .firstMatch. See decisions/2026-09-11-ui-test-shared-login-session.md follow-ups. --- PediFoodsUITests/HomeGuestFlowTests.swift | 39 ++++++++++++------- PediFoodsUITests/OrderDetailsFlowTests.swift | 10 ++++- .../ProfileLoggedOutFlowTests.swift | 37 +++++++++++------- 3 files changed, 57 insertions(+), 29 deletions(-) diff --git a/PediFoodsUITests/HomeGuestFlowTests.swift b/PediFoodsUITests/HomeGuestFlowTests.swift index 34a61be..fd8b24d 100644 --- a/PediFoodsUITests/HomeGuestFlowTests.swift +++ b/PediFoodsUITests/HomeGuestFlowTests.swift @@ -6,23 +6,31 @@ import XCTest /// blocked by a separate login-navigation issue documented there and are /// not covered here. final class HomeGuestFlowTests: XCTestCase { + /// Authenticated-flow tests no longer log out after themselves (see + /// decisions/2026-09-11-ui-test-shared-login-session.md), so this + /// class must force a clean logged-out state before its tests run, + /// rather than assume one. Done once per class, not once per test: + /// the underlying navigate-to-Profile-tab dance + /// (`reachProfileTabRegardlessOfAuthState`) is a known race against + /// the guest address-picker sheet (see + /// decisions/2026-08-06-ui-test-account-and-app-attest-bypass.md) - + /// running it a second time back-to-back inside every test (once + /// here, once again inside `reachGuestHome`'s own callers) doubled + /// exposure to that race and broke tests that were previously stable. + override class func setUp() { + super.setUp() + let app = XCUIApplication() + app.launch() + app.logoutIfAuthenticated() + } + override func setUpWithError() throws { continueAfterFailure = false } - /// 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 { + func testHomeRendersSearchAndGreeting() throws { let app = XCUIApplication() app.launch() - app.logoutIfAuthenticated() - return app - } - - func testHomeRendersSearchAndGreeting() throws { - let app = launchGuestApp() XCTAssertTrue(app.reachGuestHome(), "Never reached Home's search field") XCTAssertTrue(app.staticTexts["ENTREGAR EM:"].exists) @@ -31,7 +39,8 @@ final class HomeGuestFlowTests: XCTestCase { } func testHomeShowsDefaultCategoryChip() throws { - let app = launchGuestApp() + let app = XCUIApplication() + app.launch() XCTAssertTrue(app.reachGuestHome()) XCTAssertTrue(app.staticTexts["Categories"].exists) @@ -39,7 +48,8 @@ final class HomeGuestFlowTests: XCTestCase { } func testHomeShowsGuestLocationPromptWhenNoLocationChosen() throws { - let app = launchGuestApp() + let app = XCUIApplication() + app.launch() XCTAssertTrue(app.reachGuestHome()) // No guest state/city selected -> Home's own inline message, not @@ -48,7 +58,8 @@ final class HomeGuestFlowTests: XCTestCase { } func testTappingAddressChipReopensLocationPicker() throws { - let app = launchGuestApp() + let app = XCUIApplication() + app.launch() XCTAssertTrue(app.reachGuestHome()) // The "ENTREGAR EM:" address button re-opens the same address diff --git a/PediFoodsUITests/OrderDetailsFlowTests.swift b/PediFoodsUITests/OrderDetailsFlowTests.swift index 5a3a987..b1c3b82 100644 --- a/PediFoodsUITests/OrderDetailsFlowTests.swift +++ b/PediFoodsUITests/OrderDetailsFlowTests.swift @@ -21,8 +21,14 @@ final class OrderDetailsFlowTests: XCTestCase { XCTAssertTrue(app.staticTexts["Meus Pedidos"].waitForExistence(timeout: 10)) // "Ver Detalhes" appears once per order card; this account has real - // order history (confirmed in AuthenticatedProfileNavigationTests). - let detailsButton = app.buttons["Ver Detalhes"] + // order history (confirmed in AuthenticatedProfileNavigationTests) + // which has grown to more than one order across many manual and + // automated runs, so an exact-match subscript now resolves to + // multiple elements and throws on .tap() ("Find single matching + // element"). .firstMatch tolerates any count - the test only + // needs *an* order's details screen to be reachable, not a + // specific one. + let detailsButton = app.buttons.matching(NSPredicate(format: "label == %@", "Ver Detalhes")).firstMatch XCTAssertTrue(detailsButton.waitForExistence(timeout: 10), "No orders with a 'Ver Detalhes' action found") detailsButton.tap() diff --git a/PediFoodsUITests/ProfileLoggedOutFlowTests.swift b/PediFoodsUITests/ProfileLoggedOutFlowTests.swift index 068bfa6..d49eb03 100644 --- a/PediFoodsUITests/ProfileLoggedOutFlowTests.swift +++ b/PediFoodsUITests/ProfileLoggedOutFlowTests.swift @@ -3,23 +3,32 @@ import XCTest /// Covers the Profile tab's logged-out state (`ProfileLoggedOutView`) and /// the auth intro screen (`LoginView`) it opens. final class ProfileLoggedOutFlowTests: XCTestCase { + /// Authenticated-flow tests no longer log out after themselves (see + /// decisions/2026-09-11-ui-test-shared-login-session.md), so this + /// class must force a clean logged-out state before its tests run, + /// rather than assume one. Done once per class, not once per test: + /// the underlying navigate-to-Profile-tab dance + /// (`reachProfileTabRegardlessOfAuthState`) is a known race against + /// the guest address-picker sheet (see + /// decisions/2026-08-06-ui-test-account-and-app-attest-bypass.md) - + /// running it a second time back-to-back inside every test (once + /// here, once again inside `reachLoggedOutProfile`'s own callers) + /// doubled exposure to that race and broke tests that were + /// previously stable. + override class func setUp() { + super.setUp() + let app = XCUIApplication() + app.launch() + app.logoutIfAuthenticated() + } + override func setUpWithError() throws { continueAfterFailure = false } - /// 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 { + func testLoggedOutProfileShowsLoginPrompt() throws { let app = XCUIApplication() app.launch() - app.logoutIfAuthenticated() - return app - } - - func testLoggedOutProfileShowsLoginPrompt() throws { - let app = launchLoggedOutApp() XCTAssertTrue(app.reachLoggedOutProfile(), "Never reached the logged-out profile screen") XCTAssertTrue(app.staticTexts["Entre na sua conta"].exists) @@ -32,7 +41,8 @@ final class ProfileLoggedOutFlowTests: XCTestCase { /// a working way back out (its `LCENavigationView` back button, which /// sets `root = .main`). func testAuthIntroScreenShowsChoicesAndCanGoBack() throws { - let app = launchLoggedOutApp() + let app = XCUIApplication() + app.launch() XCTAssertTrue(app.reachLoggedOutProfile()) app.buttons["Entrar ou Cadastrar"].tap() @@ -54,7 +64,8 @@ final class ProfileLoggedOutFlowTests: XCTestCase { /// app's standard `AppBackButtonIcon` via `LCENavigationView`, not the /// oversized iOS 26 system glass back button. func testPushedAuthScreensUseAppBackButton() throws { - let app = launchLoggedOutApp() + let app = XCUIApplication() + app.launch() XCTAssertTrue(app.reachLoggedOutProfile()) app.buttons["Entrar ou Cadastrar"].tap()