import Foundation import SwiftData import SwiftUI import Testing @testable import Todos @Suite("TaskList view model tests") struct TaskListViewModelTests { // MARK: Initializers tests @Test( "properties are set correctly during initialization", arguments: TodoGroup.allCases ) func initialization(for group: TodoGroup) { // GIVEN // WHEN let viewModel = TaskListView.ViewModel(group: group) // THEN #expect(viewModel.group == group) #expect(viewModel.showCompleted == false) } // MARK: Computed property tests @Test( "buttonCompleted returns correct values when showCompleted is true", arguments: TodoGroup.allCases, [true, false] ) func buttonCompleted( for group: TodoGroup, showCompleted: Bool ) { // GIVEN let viewModel = TaskListView.ViewModel(group: group) viewModel.showCompleted = showCompleted // WHEN let (title, icon) = viewModel.buttonCompleted // THEN if showCompleted { #expect(title == "view.task-list.label.hide-completed") #expect(icon == "eye.slash") } else { #expect(title == "view.task-list.label.show-completed") #expect(icon == "eye") } } @Test( "canShowCompleted returns correct value for each group", arguments: zip( TodoGroup.allCases, [true, true, true, false] ) ) func canShowCompleted( for group: TodoGroup, expects expectation: Bool ) { // GIVEN let viewModel = TaskListView.ViewModel(group: group) // WHEN let canShowCompleted = viewModel.canShowCompleted // THEN #expect(canShowCompleted == expectation) } @Test( "navigationTitle returns correct view title for each group", arguments: TodoGroup.allCases ) func navigationTitle(for group: TodoGroup) { // GIVEN let viewModel = TaskListView.ViewModel(group: group) // WHEN let title = viewModel.navigationTitle // THEN #expect(title == group.title) } // MARK: Method tests @Test("deleteTasks removes a single task from model context") func deleteTasksSingle() throws { // GIVEN let container = try ModelContainer( for: Todo.self, configurations: .init(isStoredInMemoryOnly: true) ) let context = ModelContext(container) let viewModel = TaskListView.ViewModel(group: .today) let todosToInsert: [Todo] = [ .init(title: "Task 1", due: .now, order: 0), .init(title: "Task 2", due: .now, order: 1), .init(title: "Task 3", due: .now, order: 2), ] let countInitial = todosToInsert.count for todo in todosToInsert { context.insert(todo) } // WHEN viewModel.deleteTasks( todosToInsert, at: IndexSet(integer: 1), from: context ) // THEN let descriptor = FetchDescriptor() let todos = try context.fetch(descriptor) #expect(countInitial == 3) #expect(todos.count == 2) #expect(todos.contains(where: { $0.title == "Task 1" })) #expect(todos.contains(where: { $0.title == "Task 3" })) #expect(!todos.contains(where: { $0.title == "Task 2" })) } @Test("reorderTasks updates order property when moving task forward") func reorderTasksMovingForward() { // GIVEN let viewModel = TaskListView.ViewModel(group: .today) let tasks: [Todo] = [ .init(title: "Task 1", due: .now, order: 0), .init(title: "Task 2", due: .now, order: 1), .init(title: "Task 3", due: .now, order: 2), .init(title: "Task 4", due: .now, order: 3), ] // WHEN viewModel.reorderTasks( tasks, from: IndexSet(integer: 0), to: 3 ) // THEN #expect(tasks[0].order == 2) #expect(tasks[1].order == 0) #expect(tasks[2].order == 1) #expect(tasks[3].order == 3) } @Test("reorderTasks updates order property when moving task backward") func reorderTasksMovingBackward() { // GIVEN let viewModel = TaskListView.ViewModel(group: .today) let tasks: [Todo] = [ .init(title: "Task 1", due: .now, order: 0), .init(title: "Task 2", due: .now, order: 1), .init(title: "Task 3", due: .now, order: 2), .init(title: "Task 4", due: .now, order: 3), ] // WHEN viewModel.reorderTasks( tasks, from: IndexSet(integer: 3), to: 1 ) // THEN #expect(tasks[0].order == 0) #expect(tasks[1].order == 2) #expect(tasks[2].order == 3) #expect(tasks[3].order == 1) } @Test("toggleCompletedVisbility toggles the showCompleted property") func toggleCompletedVisibility() { // GIVEN let viewModel = TaskListView.ViewModel(group: .today) let initialValue = viewModel.showCompleted // WHEN viewModel.toggleCompletedVisibility() let afterFirstToggle = viewModel.showCompleted viewModel.toggleCompletedVisibility() let afterSecondToggle = viewModel.showCompleted // THEN #expect(initialValue == false) #expect(afterFirstToggle == true) #expect(afterSecondToggle == false) } }