3694297d4f
completeOnboarding escribia settings.onboardingCompleted al terminar el paso de platos. Ese flag es el que ContentView observa para cambiar de OnboardingView a HomeView, asi que el save destruia el flujo antes de que nextStep() pudiera pintar el paso 5. WeekReadyStepView (el aha moment de la 1.2.0) y PaywallStepView no se han visto nunca. GA4 lo confirma: 3 onboarding_completed en 28 dias y cero eventos con step 5 o 6. Se separa persistir los datos de marcar el onboarding como terminado: completeOnboarding(markFinished:) commitea platos y plan sin tocar el flag, y el nuevo finishOnboarding() lo escribe al salir del paywall. HomeView ya difiere su prompt premium a la segunda sesion, asi que no hay paywall duplicado. De paso, los nombres de paso en AnalyticsService estaban desalineados: el indice 5 decia "5_paywall" cuando el paso 5 es WeekReady, y el 6 no tenia nombre. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012Ks8uUcMA9mjypVK7F2Pkt
103 lines
3.8 KiB
Swift
103 lines
3.8 KiB
Swift
import XCTest
|
|
import SwiftData
|
|
@testable import MealMood
|
|
|
|
@MainActor
|
|
final class OnboardingViewModelTests: XCTestCase {
|
|
|
|
func testAddDishAllowsNameOnlyAndUsesDefaultTag() {
|
|
let vm = OnboardingViewModel()
|
|
vm.newDishName = "Test dish"
|
|
let fallbackId = UUID()
|
|
|
|
vm.addDish(defaultTagId: fallbackId)
|
|
|
|
XCTAssertEqual(vm.addedDishes.count, 1)
|
|
XCTAssertEqual(vm.addedDishes.first?.name, "Test dish")
|
|
XCTAssertEqual(vm.addedDishes.first?.tagIds, [fallbackId])
|
|
}
|
|
|
|
func testAddDishPreventsDuplicateNames() {
|
|
let vm = OnboardingViewModel()
|
|
vm.newDishName = "Lentils"
|
|
vm.addDish(defaultTagId: UUID())
|
|
|
|
vm.newDishName = " lentils "
|
|
vm.addDish(defaultTagId: UUID())
|
|
|
|
XCTAssertEqual(vm.addedDishes.count, 1)
|
|
}
|
|
|
|
func testSuggestedDishFallsBackToFirstTagWhenNoNameMatch() {
|
|
let vm = OnboardingViewModel()
|
|
let fallback = Tag(name: "Carne", nameEN: "Meat", color: "#E74C3C", sortOrder: 0)
|
|
let other = Tag(name: "Pescado", nameEN: "Fish", color: "#3498DB", sortOrder: 1)
|
|
|
|
vm.addSuggestedDish(
|
|
name: "Roast chicken",
|
|
preferredTagNames: ["Unknown tag"],
|
|
availableTags: [other, fallback]
|
|
)
|
|
|
|
XCTAssertEqual(vm.addedDishes.count, 1)
|
|
XCTAssertEqual(vm.addedDishes.first?.tagIds, [fallback.id])
|
|
}
|
|
|
|
// MARK: Onboarding completion flag
|
|
|
|
/// Returns the container, not just its context: releasing the container
|
|
/// leaves `mainContext` dangling and SwiftData traps on the next access.
|
|
private func makeContainer() throws -> ModelContainer {
|
|
let config = ModelConfiguration(isStoredInMemoryOnly: true)
|
|
return try ModelContainer(
|
|
for: AppSettings.self, Tag.self, Dish.self, WeekPlan.self, MealSlot.self,
|
|
ShoppingItem.self,
|
|
configurations: config
|
|
)
|
|
}
|
|
|
|
/// Regression: the flag used to be written at the dishes step, which tore the
|
|
/// flow down before the Week Ready and paywall steps could render — neither
|
|
/// was ever seen (confirmed in GA4: no step 5/6 events despite completions).
|
|
func testCompleteOnboardingKeepsFlowOnScreenWhenNotMarkedFinished() throws {
|
|
let container = try makeContainer()
|
|
let context = container.mainContext
|
|
let vm = OnboardingViewModel()
|
|
vm.newDishName = "Lentils"
|
|
vm.addDish(defaultTagId: UUID())
|
|
|
|
let saved = vm.completeOnboarding(context: context, markFinished: false)
|
|
|
|
XCTAssertTrue(saved)
|
|
let settings = try context.fetch(FetchDescriptor<AppSettings>()).first
|
|
XCTAssertNotNil(settings)
|
|
XCTAssertFalse(settings?.onboardingCompleted ?? true,
|
|
"The flag must stay false so the last two steps still render")
|
|
// The data itself is already committed at this point.
|
|
XCTAssertEqual(try context.fetch(FetchDescriptor<Dish>()).count, 1)
|
|
XCTAssertEqual(try context.fetch(FetchDescriptor<WeekPlan>()).count, 1)
|
|
}
|
|
|
|
func testFinishOnboardingSetsTheFlag() throws {
|
|
let container = try makeContainer()
|
|
let context = container.mainContext
|
|
let vm = OnboardingViewModel()
|
|
vm.completeOnboarding(context: context, markFinished: false)
|
|
|
|
vm.finishOnboarding(context: context)
|
|
|
|
let stored = try context.fetch(FetchDescriptor<AppSettings>()).first
|
|
XCTAssertTrue(stored?.onboardingCompleted ?? false)
|
|
}
|
|
|
|
func testCompleteOnboardingStillMarksFinishedByDefault() throws {
|
|
let container = try makeContainer()
|
|
let context = container.mainContext
|
|
|
|
OnboardingViewModel().completeOnboarding(context: context)
|
|
|
|
let stored = try context.fetch(FetchDescriptor<AppSettings>()).first
|
|
XCTAssertTrue(stored?.onboardingCompleted ?? false)
|
|
}
|
|
}
|