'.map(\.label)' failed to build: XCUIElement.label is @MainActor-isolated,
and a bare key path literal must be formable from any isolation context,
which a main-actor-isolated property can't satisfy. This was a hard
compiler error (not just a concurrency warning), and it cascaded into a
flood of secondary diagnostics across the whole file/batch compile unit,
obscuring the actual cause. Replaced with a closure ('{ $0.label }'),
which isn't subject to that restriction.
The file-based diagnostic dump never worked: PediFoodsUITests-Runner
runs as an app inside the simulator, sandboxed to that device's own
container filesystem. NSTemporaryDirectory() called from test code
resolves inside the simulator's data container, invisible to the host
shell's $TMPDIR a later CI step tried to cat - confirmed by the new
step printing 'No UI test diagnostic log was written.' every time.
Replaced with quickDiagnostics(_:), which builds a short live-state
summary (closed-store snackbar, spinning activity indicator, any open
alert, visible 'erro' text, first ~15 visible static texts) and
interpolates it directly into each of the three XCTAssertTrue failure
messages - the one channel already confirmed working end to end on
every run. message: is @autoclosure, so this costs nothing when the
assertion passes. Removed the dead file-write helper and the now-unused
CI step.
See decisions/2026-09-11-ui-test-shared-login-session.md follow-up.
Confirmed (pasted full CI log around a timeout, searched for the
print()'d marker string, found nothing) that plain print() from inside
a UI test never reaches fastlane's xcodebuild log output on this
runner - its formatter only relays lines matching its own known
patterns and drops everything else.
CartCheckoutFlowTests now writes app.debugDescription to
NSTemporaryDirectory()+'pedifoods_uitest_diag.log' on each of its three
failure points (shared dumpDiagnostics helper, appending), clearing any
stale copy in class setUp(). test.yml gained a step after 'Run tests
with coverage' (if: always()) that cats that file when present - TMPDIR
is stable for the whole CI job, unlike the per-run-hashed workspace
path that broke the original screenshot attempt.
See decisions/2026-09-11-ui-test-shared-login-session.md follow-up.
All three failure-path screenshot saves in this file wrote to a
hardcoded path from a different machine/session
(/private/tmp/claude-501/-Users-loverde-co-.../scratchpad), which
doesn't exist under the Mac mini runner's user account - try? silently
swallowed the write failure, so no diagnostic evidence was actually
produced on CI failures.
Replaced with app.debugDescription printed to stdout, which lands
directly in the CI log with no extra file access needed - same
technique already used in
decisions/2026-08-06-ui-test-account-and-app-attest-bypass.md. Next
CartCheckoutFlowTests failure will show the actual accessibility
hierarchy at the point of timeout.
The cart is on-device state (CartState/SessionStateStore), not purely
server-side - it survives app relaunches on the same simulator and has
been accumulating real quantities across every manual and automated run
against the 'MARIBA' test store, with no reset in between. No one-tap
'empty cart' UI action exists; the app's only full-clear path (the
'Trocar de loja?' alert's 'Limpar carrinho e adicionar') only fires when
switching to a genuinely different store, which this suite never does.
Added CartCheckoutFlowTests.setUp() (class) to drain the cart via its
own per-item 'minus' control in a capped loop before this class's tests
run, once per class rather than once per test, so every run starts from
a known-empty cart.
See decisions/2026-09-11-ui-test-shared-login-session.md follow-up.
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.
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.
- RegistrationView / LoginEmailView / OtpView: pushed with no nav bar, so
they showed the oversized iOS 26 system glass back button. Wrapped each
in LCENavigationView with the standard AppBackButtonIcon, matching
LoginView and every other screen. Dropped the now-dead colorScheme dark
branches and forced .preferredColorScheme(.light) (app has no dark
theme).
- ContentView: root .auth <-> .main switched with no transition. Added
.move transitions on both branches and wrapped enterAuthFlow() /
LoginView's back action in withAnimation, so opening auth from Profile's
'Entrar ou Cadastrar' now slides in and back slides out.
- ProfileLoggedOutFlowTests: new test asserting the pushed auth screens
carry the LCENavigationView back button.
App Review rejected the 2026-08 build: tapping checkout as a guest
switched root to .auth, which replaced the whole UI with LoginView as the
NavigationStack root - no nav bar, no back, no dismiss. User was trapped.
- LoginView: rebuilt as a static screen in LCENavigationView with a back
button that sets root = .main, a 'Criar conta' and an 'Entrar' button,
and 'Termos de Uso' / 'Politica de Privacidade' links. Added #Preview.
- Forced .preferredColorScheme(.light) and fixed DS colors (the app has
no dark theme, so system dark mode was inverting the nav bar / title).
- Removed the entry-reveal animation machinery (heroVisible/textVisible/
buttonVisible/token/prepare flags across LoginView, AuthFlowView,
ContentView) - dead since guest browsing shipped and the root cause of
the 2026-08-06 'content stuck hidden' bug.
- enterAuthFlow() is now just root = .auth.
- UITestSupport.ensureLoggedIn taps 'Entrar' (new label).
- ProfileLoggedOutFlowTests: new test covering the choices and the way
back out of .auth.
- Localizable.xcstrings: catalog caught up to the new/removed strings.
FiltersModalView.swift was 0% covered. Reaching it via guest mode hit
the backend's App Attest simulator-bypass rejection (403
APP_ATTEST_VERIFICATION_FAILED on /api/public/session, confirmed via
curl - a pre-existing, out-of-repo backend issue already documented in
decisions/2026-08-06-ui-test-account-and-app-attest-bypass.md), which
kept the guest address picker stuck showing a load-error sub-screen.
While chasing that, found a real bug in ContentView.swift:
.sheet(item: $appState.activeModal) had no onDismiss, so an
interactive swipe-to-dismiss never reset the bound item to nil -
anyone who swipes the address picker away without picking a location
leaves appState.activeModal stuck non-nil, silently breaking the next
modal presentation app-wide. Fixed with
onDismiss: { appState.activeModal = nil }.
Rewrote HomeFiltersFlowTests to use the authenticated QA account
instead of guest mode, routing around the backend-blocked guest wall
entirely. Also fixed a separate, real XCUITest issue: a plain .tap()
on the filter icon (inside Home's .offset()-transformed collapsing
header) reliably produced "Computed hit point {-1, -1}" - fixed via
coordinate(withNormalizedOffset:).tap().
Verified: HomeFiltersFlowTests passed clean across 2 consecutive
isolated runs. Full-suite regression run could not be completed this
round - the local environment stopped completing any instrumented
test launch afterward (reproduced across background/foreground,
clean DerivedData, simulator reboot, decoupled build/test, process
cleanup), unrelated to these changes. Documented in
decisions/2026-08-11-coverage-push-to-85-percent-status.md, with a
next-session TODO to run the full suite once the environment recovers.
testConfirmarEPagarWithCreditCardOpensCardSelectionWithoutSubmitting now
also taps CardSelectionSheet's "Adicionar novo cartão", which opens
PaymentCardView (CheckoutView.swift's own card-entry form - a distinct
struct from AddCardFormView.swift, which is a separate screen reached
from Profile -> Meus Cartões and already covered). Doesn't fill or
submit anything, just reaches the form and dismisses back through both
sheets.
Extracted the shared "x" close-button dismiss logic (used by both
CardSelectionSheet and PaymentCardView) into a private
dismissViaCloseButton helper.
Verified stable across 2 consecutive class-level runs.
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.
Adds testCheckoutPaymentMethodSelectionAndAddressAlterar, exercising
CheckoutView's payment-method row selection and the address picker's
"Alterar" entry point without ever tapping "Confirmar e Pagar" -
deliberately not submitting a real order (explicit user direction:
cover the screen, don't create real order data in the QA account).
Extracted the shared reach-checkout steps from
testAddProductToCartAndReachCheckout into a private helper,
reachCheckoutWithOneItem, reused by both tests.
Fixed the same "not hittable" Back-button bug (already documented in
UITestSupport.swift) inline here too - tapping a Back button that
exists but is mid pop-transition throws a fatal, uncatchable failure;
needs an .isHittable check with a short poll, not just .exists.
Verified stable across 2 consecutive class-level runs.