Fix causa raíz de taps muertos en Charts iPad: AppBackground interceptaba toques (build 50)
El círculo decorativo de AppBackground (70% del ancho del panel, offset -35%) se desborda de su panel por diseño y en layouts side-by-side queda flotando SOBRE el sidebar de Charts. Las shapes de SwiftUI participan en hit-testing por defecto y el panel derecho va después en el HStack → cada tap en la zona del sidebar cubierta por el círculo moría en silencio. El patrón lo delató: fallaban Overview + Analyze (arriba, bajo el círculo) y funcionaban Risk + Forecast (abajo). Dependía de la geometría (orientación/tamaño), por eso no reproducía en el simulador en portrait. - AppBackground: .allowsHitTesting(false) — un fondo decorativo jamás debe interceptar toques (fix global: aplica también a Sources/Journal en iPad) - Panel de detalle de Charts: .clipped() para que la decoración tampoco PINTE sobre el sidebar - Selección premium nunca se bloquea: los charts premium se seleccionan y muestran teaser de desbloqueo en el área del chart (chart_locked_* ×7 idiomas); el paywall se presenta desde el botón (contexto fiable) - UITests: testChartSidebarSelection ahora corre en landscape (geometría que reproducía el bug) + testChartSelectionWithoutPremium nuevo; --no-premium en ScreenshotMode para testear la experiencia free Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WoScpmHdVj1aUf4rAp6hbe
This commit is contained in:
@@ -193,6 +193,9 @@ final class PortfolioJournalUITests: XCTestCase {
|
||||
/// Charts sidebar must actually switch the chart — taps on the old custom tiles
|
||||
/// were unreliable on iPad. Also verifies the Rolling 12M period filter reacts.
|
||||
func testChartSidebarSelection() throws {
|
||||
// Landscape reproduces the decorative-background overlay geometry that
|
||||
// killed sidebar taps (AppBackground circle overflowing its panel).
|
||||
XCUIDevice.shared.orientation = .landscapeLeft
|
||||
let capture = XCUIApplication()
|
||||
capture.launchArguments = ["--screenshots"]
|
||||
capture.launch()
|
||||
@@ -257,6 +260,55 @@ final class PortfolioJournalUITests: XCTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
/// Regression (1.4.2): WITHOUT premium, tapping a premium chart must still
|
||||
/// select it and show the unlock teaser — blocking selection behind a paywall
|
||||
/// sheet made premium tiles look completely dead on non-premium devices.
|
||||
func testChartSelectionWithoutPremium() throws {
|
||||
let capture = XCUIApplication()
|
||||
capture.launchArguments = ["--screenshots", "--no-premium"]
|
||||
capture.launch()
|
||||
Thread.sleep(forTimeInterval: 4.0)
|
||||
|
||||
let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
|
||||
let denyButton = springboard.alerts.buttons["Don't Allow"]
|
||||
if denyButton.waitForExistence(timeout: 3) {
|
||||
denyButton.tap()
|
||||
Thread.sleep(forTimeInterval: 1.0)
|
||||
}
|
||||
|
||||
let tabBar = capture.tabBars.firstMatch
|
||||
if tabBar.exists && tabBar.buttons["Charts"].exists {
|
||||
tabBar.buttons["Charts"].tap()
|
||||
} else {
|
||||
capture.staticTexts["Charts"].firstMatch.tap()
|
||||
}
|
||||
Thread.sleep(forTimeInterval: 2.0)
|
||||
|
||||
// Premium chart: must select and show the unlock teaser (not a dead tap)
|
||||
let allocationRow = capture.staticTexts["Allocation"].firstMatch
|
||||
XCTAssertTrue(allocationRow.waitForExistence(timeout: 5), "Allocation row not found in sidebar")
|
||||
allocationRow.tap()
|
||||
Thread.sleep(forTimeInterval: 2.0)
|
||||
|
||||
let shot = XCTAttachment(screenshot: capture.screenshot())
|
||||
shot.name = "charts_allocation_locked"
|
||||
shot.lifetime = .keepAlways
|
||||
add(shot)
|
||||
|
||||
XCTAssertTrue(
|
||||
capture.buttons["Unlock Premium"].firstMatch.waitForExistence(timeout: 5),
|
||||
"Premium chart tap did not select the chart / show the unlock teaser"
|
||||
)
|
||||
|
||||
// The unlock button must present the paywall
|
||||
capture.buttons["Unlock Premium"].firstMatch.tap()
|
||||
Thread.sleep(forTimeInterval: 2.0)
|
||||
let paywallShot = XCTAttachment(screenshot: capture.screenshot())
|
||||
paywallShot.name = "charts_paywall"
|
||||
paywallShot.lifetime = .keepAlways
|
||||
add(paywallShot)
|
||||
}
|
||||
|
||||
/// Captures full-screen screenshots of the main tabs with demo data for App Store
|
||||
/// marketing. Launches the app in `--screenshots` mode (onboarding/lock skipped,
|
||||
/// SampleDataService seeded). Language can be driven via the SCREENSHOT_LANG env var
|
||||
|
||||
Reference in New Issue
Block a user