diff --git a/PediFoodsUITests/CartCheckoutFlowTests.swift b/PediFoodsUITests/CartCheckoutFlowTests.swift index 47b5aaa..f5ca87e 100644 --- a/PediFoodsUITests/CartCheckoutFlowTests.swift +++ b/PediFoodsUITests/CartCheckoutFlowTests.swift @@ -98,7 +98,7 @@ final class CartCheckoutFlowTests: XCTestCase { func testAddProductToCartAndReachCheckout() throws { let app = XCUIApplication() app.launch() - try reachCheckoutWithOneItem(app) + try reachCheckoutWithProducts(app) } /// Exercises CheckoutView's payment-method selection and the address @@ -108,7 +108,7 @@ final class CartCheckoutFlowTests: XCTestCase { func testCheckoutPaymentMethodSelectionAndAddressAlterar() throws { let app = XCUIApplication() app.launch() - try reachCheckoutWithOneItem(app) + try reachCheckoutWithProducts(app) XCTAssertTrue(app.staticTexts["MÉTODO DE PAGAMENTO"].waitForExistence(timeout: 5)) @@ -167,7 +167,7 @@ final class CartCheckoutFlowTests: XCTestCase { func testConfirmarEPagarWithCreditCardOpensCardSelectionWithoutSubmitting() throws { let app = XCUIApplication() app.launch() - try reachCheckoutWithOneItem(app) + try reachCheckoutWithProducts(app) let creditCardRow = app.buttons.matching(NSPredicate(format: "label CONTAINS[c] %@", "Cartão de Crédito")).firstMatch XCTAssertTrue(creditCardRow.waitForExistence(timeout: 5), "This store is expected to offer Cartão de Crédito") @@ -209,7 +209,49 @@ final class CartCheckoutFlowTests: XCTestCase { app.swipeDown() } - private func reachCheckoutWithOneItem(_ app: XCUIApplication) throws { + /// Taps the Nth product row's add button (0-based, top to bottom) and + /// handles both real outcomes on this catalog: a product with add-ons + /// (e.g. "Alcatra", the first product) opens `ProductDetailSheet` + /// ("Detalhes") and needs its own confirm tap; a plain product (no + /// `addonGroups`) adds directly with no sheet at all + /// (`StoreDetailView+Components.swift`). Which products have add-ons + /// is real catalog data, not something this suite controls, so both + /// paths must be handled rather than assumed - asserting "no sheet + /// appeared" for a product that genuinely has add-ons is exactly the + /// wrong-diagnosis mistake this suite already made once for this same + /// screen (see decisions/2026-09-11-ui-test-shared-login-session.md). + /// `ProductDetailSheet` requires no addon selection to confirm - + /// `quantity` defaults to 1 and addons default to none, so its main + /// action button ("Atualizar • ") is always tappable as-is. + private func addProduct(atIndex index: Int, in app: XCUIApplication) { + let addButtons = app.buttons.matching(identifier: "storeDetailProductAddButton") + let button = addButtons.element(boundBy: index) + XCTAssertTrue( + button.waitForExistence(timeout: 25), + "Product add button at index \(index) never appeared. \(quickDiagnostics(app))" + ) + button.tap() + + if app.staticTexts["Detalhes"].waitForExistence(timeout: 3) { + let confirmButton = app.buttons.matching(NSPredicate(format: "label BEGINSWITH %@", "Atualizar")).firstMatch + XCTAssertTrue( + confirmButton.waitForExistence(timeout: 5), + "ProductDetailSheet's confirm button never appeared for product at index \(index). \(quickDiagnostics(app))" + ) + confirmButton.tap() + XCTAssertFalse( + app.staticTexts["Detalhes"].waitForExistence(timeout: 3), + "ProductDetailSheet never dismissed after confirming product at index \(index)" + ) + } + } + + /// Adds the first `count` products from Store Detail's list, top to + /// bottom, then reaches Checkout. Defaults to 3 so both the add-on + /// flow (first product) and the plain-add flow (later products) are + /// both exercised on every run, not just whichever one the first + /// product happens to be. + private func reachCheckoutWithProducts(_ app: XCUIApplication, count: Int = 3) throws { XCTAssertTrue(app.ensureLoggedIn(), "Login never completed") // ensureLoggedIn can land on whichever tab it detected the @@ -237,16 +279,17 @@ final class CartCheckoutFlowTests: XCTestCase { // clearly rendered on screen while `images.matching(identifier: // "plus")` still found nothing, and the same "plus" systemName is // separately reused by CartView's quantity stepper, making it an - // ambiguous identifier to search by in the first place. Matching - // the button itself (not a nested image) also sidesteps the - // quantity-badge accessibility-label-merging issue that affected - // the tab bar's cart icon elsewhere in this suite. - let addButton = app.buttons.matching(identifier: "storeDetailProductAddButton").firstMatch + // ambiguous identifier to search by in the first place. + let addButtons = app.buttons.matching(identifier: "storeDetailProductAddButton") XCTAssertTrue( - addButton.waitForExistence(timeout: 25), + addButtons.firstMatch.waitForExistence(timeout: 25), "Store Detail's product list never loaded. \(quickDiagnostics(app))" ) - addButton.tap() + + let actualCount = min(count, addButtons.count) + for index in 0..