import SwiftUI import Testing @testable import Todos @MainActor @Suite("TaskList view model tests") struct TaskListViewModelTests { // MARK: Initializers tests @Test( "properties are set correctly during initialization", arguments: [TodoGroup.today, .upcoming, .overdue, .completed] ) 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.today, .upcoming, .overdue, .completed], [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.today, .upcoming, .overdue, .completed], [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.today, .upcoming, .overdue, .completed] ) 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("delete removes a single todo task from repository") func deleteSingleTodo() { // GIVEN let todos: [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 repository = MockTodoRepository(todos: todos) let viewModel = TaskListView.ViewModel(group: .today) // WHEN viewModel.delete( todos, at: IndexSet(integer: 1), from: repository ) // THEN #expect(repository.todos.count == 2) #expect(repository.todos.contains { $0.title == "Task 1" }) #expect(repository.todos.contains { $0.title == "Task 3" }) #expect(!repository.todos.contains { $0.title == "Task 2" }) } @Test("delete removes multiple todo tasks from repository") func deleteMultipleTodos() { // GIVEN let todos: [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), .init(title: "Task 5", due: .now, order: 4), ] let repository = MockTodoRepository(todos: todos) let viewModel = TaskListView.ViewModel(group: .today) // WHEN viewModel.delete( todos, at: IndexSet([1, 3]), from: repository ) // THEN #expect(repository.todos.count == 3) #expect(repository.todos.contains { $0.title == "Task 1" }) #expect(repository.todos.contains { $0.title == "Task 3" }) #expect(repository.todos.contains { $0.title == "Task 5" }) #expect(!repository.todos.contains { $0.title == "Task 2" }) #expect(!repository.todos.contains { $0.title == "Task 4" }) } @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("toggleCompletedVisibility 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) } }