test(uitests): cover both add-on and plain-add product flows
The original helper only ever tapped the *first* product's add button
and assumed it went straight to Cart. That's the same single-path
assumption that produced the wrong 'product list never loaded'
diagnosis - the first product on this catalog ('Alcatra') has add-ons
and opens ProductDetailSheet ('Detalhes') instead of adding directly.
Fixing just the button selector would have hidden this and failed
differently on the next run.
Renamed reachCheckoutWithOneItem to reachCheckoutWithProducts(_:count:
Int = 3) and added addProduct(atIndex:in:), which taps the Nth
product's add button and branches on the real outcome: if
ProductDetailSheet appears, confirms via its own main action button
('Atualizar...') and waits for it to dismiss; otherwise the product
already added directly. Loops over the first 3 products (capped by the
actual button count) so both flows are exercised on every run,
regardless of which specific products the real catalog happens to give
add-ons to.
See decisions/2026-09-11-ui-test-shared-login-session.md follow-up.
This commit is contained in:
@@ -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 • <price>") 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..<actualCount {
|
||||
addProduct(atIndex: index, in: app)
|
||||
}
|
||||
|
||||
// Once the cart has items, the tab bar button's accessible label
|
||||
// becomes just the badge count ("4") instead of "Shopping Cart" -
|
||||
@@ -259,7 +302,7 @@ final class CartCheckoutFlowTests: XCTestCase {
|
||||
XCTAssertTrue(cartTab.waitForExistence(timeout: 5))
|
||||
cartTab.tap()
|
||||
XCTAssertTrue(app.staticTexts["Meu Carrinho"].waitForExistence(timeout: 10))
|
||||
XCTAssertFalse(app.staticTexts["Seu carrinho está vazio"].exists, "Cart still shows empty after adding a product")
|
||||
XCTAssertFalse(app.staticTexts["Seu carrinho está vazio"].exists, "Cart still shows empty after adding products")
|
||||
|
||||
app.buttons["Ir para o Pagamento"].tap()
|
||||
XCTAssertTrue(app.staticTexts["Finalizar Pedido"].waitForExistence(timeout: 10), "Never reached Checkout")
|
||||
|
||||
Reference in New Issue
Block a user