[coverage-85] Add HomeFiltersFlowTests, fix stuck activeModal on interactive sheet dismiss

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.
This commit is contained in:
Daniel Arantes Loverde
2026-08-12 22:41:26 -03:00
parent b68cd92307
commit 2a934483fd
3 changed files with 73 additions and 1 deletions

View File

@@ -0,0 +1,68 @@
import XCTest
/// Covers Home's filter sheet (FiltersModalView.swift, ~700 lines, was
/// 0% covered), reachable via the search bar's filter icon
/// (identifier "slider.horizontal.3"). Uses the authenticated QA account
/// rather than guest mode - the guest address picker
/// (PublicLocationPickerView) currently can't load its states/cities list
/// at all, because the backend rejects the simulator's App Attest bypass
/// with 403 APP_ATTEST_VERIFICATION_FAILED (confirmed directly via curl
/// against /api/public/session, a pre-existing backend-side issue, see
/// decisions/2026-08-06-ui-test-account-and-app-attest-bypass.md). That
/// leaves the guest address-picker sheet re-presenting itself indefinitely
/// (HomeView+Data.loadGuestStores() keeps re-triggering it while no
/// location is set), permanently covering Home. Logging in with the
/// standing QA account swaps AddressPickerModalView to the
/// App-Attest-free AddressesView instead, routing around the guest wall
/// entirely.
final class HomeFiltersFlowTests: XCTestCase {
override func setUpWithError() throws {
continueAfterFailure = false
}
override func tearDownWithError() throws {
XCUIApplication().logoutIfAuthenticated()
}
func testFiltersSheetSelectionsAndApply() throws {
let app = XCUIApplication()
app.launch()
XCTAssertTrue(app.ensureLoggedIn(), "Login never completed")
// ensureLoggedIn can land on whichever tab it detected the
// authenticated session from - switch explicitly.
XCTAssertTrue(app.buttons["Home"].waitForExistence(timeout: 10))
app.buttons["Home"].tap()
// The filter icon lives inside Home's collapsing header
// (SearchBar, positioned with .offset(y: collapseProgress * -120))
// - a plain .tap() was separately confirmed (while still chasing
// the guest-mode blocker above) to sometimes resolve to an invalid
// "Computed hit point {-1, -1}" for this transform-positioned
// element. Tapping a normalized-offset coordinate on the element
// bypasses XCUITest's automatic hit-point computation.
let filterIcon = app.buttons.matching(identifier: "slider.horizontal.3").firstMatch
XCTAssertTrue(filterIcon.waitForExistence(timeout: 10), "Filter icon never appeared on Home")
filterIcon.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5)).tap()
XCTAssertTrue(app.staticTexts["Filtros"].waitForExistence(timeout: 5), "Never reached the filters sheet")
// Sort option - just needs to be selectable, no assertion on the
// resulting Home state (that's real backend store data, out of
// scope here).
let ratingSort = app.buttons["Avaliação"]
if ratingSort.exists {
ratingSort.tap()
}
// Price tier - label is the raw "$"/"$$"/etc symbol.
let mediumPrice = app.buttons["$$"]
if mediumPrice.exists {
mediumPrice.tap()
}
app.buttons["Aplicar Filtros"].tap()
// Applying dismisses back to Home.
XCTAssertTrue(app.textFields["Search menu, restaurant or craving"].waitForExistence(timeout: 10), "Never returned to Home after applying filters")
}
}