import XCTest import SwiftUI import SwiftData @testable import MealMood /// Not a real assertion suite: renders every export style with sample data to /// /tmp/mealmood-export-previews/ so design changes can be reviewed as images. final class ExportStyleRenderTests: XCTestCase { @MainActor func testRenderAllExportStyles() throws { let config = ModelConfiguration(isStoredInMemoryOnly: true) let container = try ModelContainer( for: AppSettings.self, Dish.self, Tag.self, WeekPlan.self, MealSlot.self, configurations: config ) let context = container.mainContext let settings = AppSettings() settings.activeMealTypes = [.lunch, .dinner] settings.includeWeekends = true settings.language = "es" context.insert(settings) let names = [ "Tortilla de patatas", "Salmón al horno", "Ensalada César", "Lentejas con chorizo", "Pizza casera", "Pollo asado con patatas", "Crema de calabaza", "Espaguetis boloñesa", "Merluza en salsa verde", "Arroz tres delicias", "Fajitas de pollo", "Gazpacho", "Croquetas caseras", "Sopa de fideos" ] let dishes = names.map { Dish(name: $0) } dishes.forEach { context.insert($0) } let monday = Calendar(identifier: .gregorian).date(from: DateComponents(year: 2026, month: 8, day: 31))! let plan = WeekPlan(weekStartDate: monday) context.insert(plan) var dishIndex = 0 for day in 0...6 { for meal in [MealType.lunch, .dinner] { let slot = MealSlot(dayOfWeek: day, mealType: meal.rawValue) if day == 5 && meal == .dinner { slot.isEatingOut = true } else { slot.dishId = dishes[dishIndex % dishes.count].id if day == 2 && meal == .dinner { slot.secondaryDishId = dishes[(dishIndex + 5) % dishes.count].id } dishIndex += 1 } slot.weekPlan = plan plan.slotList.append(slot) context.insert(slot) } } try context.save() let outputDir = URL(fileURLWithPath: "/tmp/mealmood-export-previews") try? FileManager.default.createDirectory(at: outputDir, withIntermediateDirectories: true) for style in WeekExportStyle.allCases { let renderer = ImageRenderer(content: WeekPlanShareView( plan: plan, settings: settings, dishes: dishes, tags: [], exportStyleOverride: style )) let size = WeekPlanShareView.canvasSize(for: style) renderer.proposedSize = ProposedViewSize(width: size.width, height: size.height) renderer.scale = 1 let image = try XCTUnwrap(renderer.uiImage, "render failed for \(style.rawValue)") let data = try XCTUnwrap(image.pngData()) try data.write(to: outputDir.appendingPathComponent("export-\(style.rawValue).png")) } } } final class DateHelpersTests: XCTestCase { func testStartOfWeekReturnsMonday() { var components = DateComponents() components.year = 2026 components.month = 2 components.day = 18 // Wednesday let calendar = Calendar(identifier: .gregorian) let input = calendar.date(from: components)! let start = input.startOfWeek() let weekday = calendar.component(.weekday, from: start) XCTAssertEqual(weekday, 2) // Monday in Gregorian calendar } func testCombineDateAndTime() { let calendar = Calendar(identifier: .gregorian) let date = calendar.date(from: DateComponents(year: 2026, month: 2, day: 20))! let time = calendar.date(from: DateComponents(hour: 21, minute: 30))! let combined = combineDateAndTime(date: date, time: time) let parts = calendar.dateComponents([.year, .month, .day, .hour, .minute], from: combined) XCTAssertEqual(parts.year, 2026) XCTAssertEqual(parts.month, 2) XCTAssertEqual(parts.day, 20) XCTAssertEqual(parts.hour, 21) XCTAssertEqual(parts.minute, 30) } } /// Renders the stats screen with sample data to /tmp so the new sections can /// be reviewed as an image. final class StatsRenderTests: XCTestCase { @MainActor func testRenderStats() throws { let tags = [ Tag(name: "Pasta", nameEN: "Pasta", color: "#E8A87C"), Tag(name: "Pescado", nameEN: "Fish", color: "#7CB7E8"), Tag(name: "Verdura", nameEN: "Veggie", color: "#8CD790"), Tag(name: "Carne", nameEN: "Meat", color: "#E87C7C") ] let dishes: [Dish] = [ Dish(name: "Espaguetis", tagIds: [tags[0].id]), Dish(name: "Lasaña", tagIds: [tags[0].id, tags[3].id]), Dish(name: "Merluza", tagIds: [tags[1].id]), Dish(name: "Salmón", tagIds: [tags[1].id]), Dish(name: "Ensalada", tagIds: [tags[2].id]), Dish(name: "Pollo asado", tagIds: [tags[3].id]), Dish(name: "Crema de calabaza", tagIds: [tags[2].id]) ] var plans: [WeekPlan] = [] for week in 0..<3 { let plan = WeekPlan(weekStartDate: Date().startOfWeek().addingDays(-7 * week)) var i = week for day in 0...4 { for meal in ["lunch", "dinner"] { let slot = MealSlot(dayOfWeek: day, mealType: meal, dishId: dishes[i % dishes.count].id) slot.weekPlan = plan plan.slotList.append(slot) i += 1 } } plans.append(plan) } let view = StatsView(weekPlans: plans, allDishes: dishes, allTags: tags, language: .spanish) // Render just the new sections: ImageRenderer can't draw a NavigationStack. let content = VStack(alignment: .leading, spacing: 24) { view.tagByMealSection view.dishesPerTagSection } .padding(16) .frame(width: 390) .background(Color.mealMoodBackground) let renderer = ImageRenderer(content: content) renderer.scale = 2 let image = try XCTUnwrap(renderer.uiImage) let data = try XCTUnwrap(image.pngData()) try data.write(to: URL(fileURLWithPath: "/tmp/stats_render.png")) } }