Files
FamilyMealPlanner/MealMoodUITests/OnboardingFlowUITests.swift
T
alexandrev-tibco 718eef16bf reglas: el panel explica que se incumple y como arreglarlo; stats por etiqueta
- RuleViolation.reasons: el motor devuelve ahora QUE regla rompe cada
  asignacion (repetido en la semana, maximo por semana superado, no
  consecutivo, no mismo dia, solo comida/cena, solo entre semana o fin
  de semana) con su etiqueta y limite
- Panel de avisos: bloque con cada regla rota en lenguaje claro + una
  sugerencia de que hacer, y boton nuevo "Cambiar plato" que abre el
  selector de ese hueco (antes solo se podia quitar o ignorar)
- Estadisticas: matriz "Etiquetas por tipo de comida" (p.ej. cuantas
  cenas son de pescado) y "Platos por etiqueta" del catalogo
- Tests: 4 casos de las razones de incumplimiento; harness que renderiza
  las secciones nuevas de stats a /tmp/stats_render.png
- Strings en 6 idiomas

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013H6bXqGX1ygwib1Dm3n3UG
2026-09-10 21:06:42 +02:00

114 lines
5.0 KiB
Swift

import XCTest
/// Walks the whole onboarding on a clean install.
///
/// This exists because of a bug that no unit test could see: the flow used to
/// write `onboardingCompleted` at the dishes step, which swapped the root view
/// for the home and silently skipped the last two steps the Week Ready "aha
/// moment" and the paywall. Shipped in 2.0 and live for a week before GA4
/// (no step 5/6 events despite completions) gave it away.
final class OnboardingFlowUITests: XCTestCase {
private var app: XCUIApplication!
override func setUpWithError() throws {
continueAfterFailure = false
app = XCUIApplication()
// Pin the language so the run doesn't depend on the simulator's locale.
app.launchArguments += ["-AppleLanguages", "(en)", "-AppleLocale", "en_US"]
// Keep synthetic runs out of the production GA4 property. Literal on
// purpose: a UI test bundle doesn't link the app target, so it can't
// reference AnalyticsService.launchArgumentDisablingAnalytics keep the
// two in sync by hand.
app.launchArguments.append("UITEST_DISABLE_ANALYTICS")
}
func testOnboardingReachesWeekReadyAndPaywallBeforeTheHome() {
app.launch()
dismissSystemAlertIfPresent()
tap("onboarding_cta_welcome", "welcome")
tap("onboarding_cta_meal_windows", "meal windows")
tap("onboarding_cta_weekends", "weekends")
tap("onboarding_cta_calendar", "calendar")
tap("onboarding_cta_first_dishes", "first dishes")
// The two steps the bug used to skip.
let weekReady = app.staticTexts["onboarding_week_ready_title"]
XCTAssertTrue(weekReady.waitForExistence(timeout: 10),
"Week Ready (the aha moment) never appeared — the flow skipped step 5")
tap("onboarding_cta_week_ready_skip", "week ready")
let paywallSkip = app.buttons["onboarding_cta_paywall_skip"]
XCTAssertTrue(paywallSkip.waitForExistence(timeout: 10),
"The paywall never appeared — the flow skipped step 6")
paywallSkip.tap()
// Only now should onboarding hand over to the home.
XCTAssertTrue(app.otherElements["home_root"].waitForExistence(timeout: 15),
"Onboarding finished but the home never came up")
}
// MARK: Helpers
private func tap(_ identifier: String, _ step: String, file: StaticString = #filePath, line: UInt = #line) {
let button = app.buttons[identifier]
XCTAssertTrue(button.waitForExistence(timeout: 15),
"Never reached the \(step) step", file: file, line: line)
button.tap()
dismissSystemAlertIfPresent()
}
/// The simulator can raise an Apple Account / iCloud prompt on launch, which
/// would swallow the taps that follow.
private func dismissSystemAlertIfPresent() {
let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
for label in ["Not Now", "Cancel", "Don't Allow", "Allow"] {
let button = springboard.buttons[label]
if button.exists && button.isHittable {
button.tap()
return
}
}
}
}
/// Regression: tapping "next week" must land on an editable week with slots
/// the plan for a not-yet-visited week has to be created on navigation.
final class NextWeekNavigationUITests: XCTestCase {
func testNextWeekShowsSlotsToPlan() throws {
let app = XCUIApplication()
app.launchArguments += ["-AppleLanguages", "(en)", "-AppleLocale", "en_US", "UITEST_DISABLE_ANALYTICS"]
app.launch()
// Reach the home, walking onboarding if this is a clean install.
if !app.otherElements["home_root"].waitForExistence(timeout: 5) {
for id in ["onboarding_cta_welcome", "onboarding_cta_meal_windows", "onboarding_cta_weekends",
"onboarding_cta_calendar", "onboarding_cta_first_dishes",
"onboarding_cta_week_ready_skip", "onboarding_cta_paywall_skip"] {
let button = app.buttons[id]
if button.waitForExistence(timeout: 10) { button.tap() }
}
XCTAssertTrue(app.otherElements["home_root"].waitForExistence(timeout: 15), "Home never appeared")
}
let next = app.buttons["Next"]
XCTAssertTrue(next.waitForExistence(timeout: 10), "Next-week chevron not found")
next.tap()
sleep(2)
let attachment = XCTAttachment(screenshot: app.screenshot())
attachment.name = "next-week"
attachment.lifetime = .keepAlways
add(attachment)
// The paywall must NOT appear (next week is free for everyone) and the
// home must still be there with content, not a blank screen.
XCTAssertFalse(app.staticTexts["premium_title"].exists, "Paywall appeared navigating to next week")
XCTAssertTrue(app.otherElements["home_root"].exists, "Home disappeared after navigating to next week")
}
}