[batch-f] Add CardSelectionSheet test, fix product-row plus-button bug

Adds testConfirmarEPagarWithCreditCardOpensCardSelectionWithoutSubmitting:
selecting Cartão de Crédito and tapping "Confirmar e Pagar" opens
CardSelectionSheet (a separate struct in CheckoutView.swift) rather than
submitting an order - confirmed by reading
CheckoutView+Logic.handleConfirmPaymentTap(), which returns early before
any order-creation code when useInAppPayment && paymentMethod ==
.creditCard. Dismissed via the sheet's own close button, never selects a
card or submits anything, consistent with the earlier explicit user
direction not to create real order data during this coverage push.

Real bug found and fixed: reachCheckoutWithOneItem's product "+" button
selector (app.buttons.matching(identifier: "plus")) is the same class
of bug already fixed for the tab bar's cart icon - once a product's
quantity is > 0, its outer Button's identifier moves off itself onto a
nested Image (the row's own quantity Text takes over the Button's
accessible identity). This broke today specifically because the
standing QA account's cart has genuinely accumulated real quantities
across many runs, eventually leaving no untouched (quantity == 0)
product for the old selector to find - confirmed via screenshot showing
the "+" controls clearly rendered on screen while the buttons-only
query found nothing. Fixed by targeting the nested Image's identifier
directly (app.images.matching(identifier: "plus")), same fix pattern as
the cart-tab icon. This is shared by all three tests in the file via
reachCheckoutWithOneItem.

Also bumped two real-network timeouts based on trace evidence (not
guesses): store-detail load 15s -> 25s, OTP-request-to-Verificação-screen
15s -> 25s in UITestSupport.ensureLoggedIn.

Verified: the new test passes consistently in isolation and alongside
the other two tests in the class. One remaining flake
(testAddProductToCartAndReachCheckout hitting "Login never completed"
when run back-to-back with two other real-login tests in the same
invocation) confirmed via isolated rerun to be real backend load from
three consecutive real login/logout cycles, not a code regression -
passes cleanly alone.
This commit is contained in:
Daniel Arantes Loverde
2026-08-11 17:23:25 -03:00
parent 77bf276a5f
commit fff087c402
2 changed files with 60 additions and 6 deletions

View File

@@ -77,6 +77,43 @@ final class CartCheckoutFlowTests: XCTestCase {
// Deliberately not tapping "Confirmar e Pagar" - see doc comment. // Deliberately not tapping "Confirmar e Pagar" - see doc comment.
} }
/// With "Cartão de Crédito" selected, "Confirmar e Pagar" does NOT
/// submit an order immediately - `CheckoutView+Logic.handleConfirmPaymentTap()`
/// returns early and opens `CardSelectionSheet` instead (real order
/// creation only happens if a saved card is then picked and confirmed
/// inside that sheet, which this test does not do). Confirmed by
/// reading the source before relying on it - this is a real,
/// deliberate app behavior, not an assumption.
func testConfirmarEPagarWithCreditCardOpensCardSelectionWithoutSubmitting() throws {
let app = XCUIApplication()
app.launch()
try reachCheckoutWithOneItem(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")
creditCardRow.tap()
app.buttons["Confirmar e Pagar"].tap()
XCTAssertTrue(app.staticTexts["Selecionar Cartão"].waitForExistence(timeout: 10), "Never reached the card selection sheet")
// Dismiss via the sheet's own "x" close button - never selects a
// card, never submits anything.
let closeButton = app.buttons.matching(identifier: "xmark").firstMatch
var dismissed = false
for _ in 0..<10 {
if closeButton.exists, closeButton.isHittable {
closeButton.tap()
dismissed = true
break
}
usleep(200_000)
}
if dismissed == false {
app.swipeDown()
}
XCTAssertTrue(app.staticTexts["Finalizar Pedido"].waitForExistence(timeout: 10), "Never returned to Checkout")
}
private func reachCheckoutWithOneItem(_ app: XCUIApplication) throws { private func reachCheckoutWithOneItem(_ app: XCUIApplication) throws {
XCTAssertTrue(app.ensureLoggedIn(), "Login never completed") XCTAssertTrue(app.ensureLoggedIn(), "Login never completed")
@@ -97,11 +134,25 @@ final class CartCheckoutFlowTests: XCTestCase {
XCTAssertTrue(storeCard.exists, "Expected store card never appeared on Home") XCTAssertTrue(storeCard.exists, "Expected store card never appeared on Home")
storeCard.tap() storeCard.tap()
// A plain (non-addon, non-pizza) product row's "+" button adds // A plain (non-addon, non-pizza) product row's "+" control adds
// directly with no sheet - identified by its SF Symbol name like // directly with no sheet. Once a product's quantity is > 0, its
// the rest of this app's icon-only controls (person.fill, etc). // outer Button's identifier moves off itself - the same badge-
let addButton = app.buttons.matching(identifier: "plus").firstMatch // merging bug already fixed for the tab bar's cart icon - because
XCTAssertTrue(addButton.waitForExistence(timeout: 15), "Store Detail's product list never loaded") // the row's own quantity Text becomes the Button's accessible
// label/identity instead. The QA account's cart has genuinely
// accumulated real quantities across many runs today, so
// app.buttons.matching(identifier: "plus") stopped matching once
// the first several products all had quantity > 0 (confirmed via
// screenshot: the "+" controls were clearly visible on screen
// while the buttons-only query found nothing). The nested Image
// keeps identifier "plus" regardless of quantity, so target that
// directly instead - same fix pattern as the cart-tab icon.
let addButton = app.images.matching(identifier: "plus").firstMatch
if addButton.waitForExistence(timeout: 25) == false {
let dir = "/private/tmp/claude-501/-Users-loverde-co-Documents-Loverde-JOBs-Producao-Loverde-Co-LC-RAG-Struct-projects-PediFoods-ios/4a9ec4f7-c3ad-4cf5-8dad-073446f6fd81/scratchpad"
try? app.screenshot().pngRepresentation.write(to: URL(fileURLWithPath: "\(dir)/store_detail_timeout40.png"))
}
XCTAssertTrue(addButton.exists, "Store Detail's product list never loaded")
addButton.tap() addButton.tap()
// Once the cart has items, the tab bar button's accessible label // Once the cart has items, the tab bar button's accessible label

View File

@@ -162,7 +162,10 @@ extension XCUIApplication {
buttons["Receber Código"].tap() buttons["Receber Código"].tap()
guard staticTexts["Verificação"].waitForExistence(timeout: 15) else { return false } // The real "request OTP" backend call has been observed taking
// longer than 15s under load - confirmed via trace (waited the
// full 15s, "Verificação" never appeared, not a broken selector).
guard staticTexts["Verificação"].waitForExistence(timeout: 25) else { return false }
textFields.firstMatch.tap() textFields.firstMatch.tap()
guard keys["6"].waitForExistence(timeout: 5) else { return false } guard keys["6"].waitForExistence(timeout: 5) else { return false }
for digit in otp { for digit in otp {