Files
todos/UnitTests/Tests/View Models/TaskListViewModelTests.swift
T

218 lines
6.3 KiB
Swift
Raw Normal View History

2026-02-11 13:00:46 +00:00
import SwiftUI
2026-02-10 15:31:52 +00:00
import Testing
@testable import Todos
2026-02-11 18:22:26 +00:00
@MainActor
2026-02-10 15:31:52 +00:00
@Suite("TaskList view model tests")
struct TaskListViewModelTests {
2026-02-11 13:00:46 +00:00
// MARK: Initializers tests
2026-02-10 15:31:52 +00:00
@Test(
2026-02-11 13:00:46 +00:00
"properties are set correctly during initialization",
2026-02-11 18:22:26 +00:00
arguments: [TodoGroup.today, .upcoming, .overdue, .completed]
2026-02-11 13:00:46 +00:00
)
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",
2026-02-11 18:22:26 +00:00
arguments: [TodoGroup.today, .upcoming, .overdue, .completed], [true, false]
2026-02-11 13:00:46 +00:00
)
func buttonCompleted(
for group: TodoGroup,
showCompleted: Bool
) {
// GIVEN
let viewModel = TaskListView.ViewModel(group: group)
2026-02-11 15:28:22 +00:00
2026-02-11 13:00:46 +00:00
viewModel.showCompleted = showCompleted
// WHEN
let (title, icon) = viewModel.buttonCompleted
// THEN
if showCompleted {
#expect(title == "view.task-list.label.hide-completed")
#expect(icon == "eye.slash")
2026-02-11 15:28:22 +00:00
}
else {
2026-02-11 13:00:46 +00:00
#expect(title == "view.task-list.label.show-completed")
#expect(icon == "eye")
}
}
@Test(
"canShowCompleted returns correct value for each group",
arguments: zip(
2026-02-11 18:22:26 +00:00
[TodoGroup.today, .upcoming, .overdue, .completed],
2026-02-11 13:00:46 +00:00
[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",
2026-02-11 18:22:26 +00:00
arguments: [TodoGroup.today, .upcoming, .overdue, .completed]
2026-02-10 15:31:52 +00:00
)
func navigationTitle(for group: TodoGroup) {
// GIVEN
let viewModel = TaskListView.ViewModel(group: group)
// WHEN
let title = viewModel.navigationTitle
// THEN
#expect(title == group.title)
}
2026-02-11 13:00:46 +00:00
// MARK: Method tests
2026-02-11 18:22:26 +00:00
@Test("delete removes a single todo task from repository")
func deleteSingleTodo() {
2026-02-11 16:35:51 +00:00
// GIVEN
2026-02-11 18:22:26 +00:00
let todos: [Todo] = [
2026-02-11 16:35:51 +00:00
.init(title: "Task 1", due: .now, order: 0),
.init(title: "Task 2", due: .now, order: 1),
.init(title: "Task 3", due: .now, order: 2),
]
2026-02-11 18:22:26 +00:00
let repository = MockTodoRepository(todos: todos)
let viewModel = TaskListView.ViewModel(group: .today)
2026-02-11 16:35:51 +00:00
// WHEN
2026-02-11 18:22:26 +00:00
viewModel.delete(
todos,
2026-02-11 16:35:51 +00:00
at: IndexSet(integer: 1),
2026-02-11 18:22:26 +00:00
from: repository
2026-02-11 16:35:51 +00:00
)
// THEN
2026-02-11 18:22:26 +00:00
#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" })
}
2026-02-11 16:35:51 +00:00
2026-02-11 18:22:26 +00:00
@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" })
2026-02-11 16:35:51 +00:00
}
2026-02-11 15:28:22 +00:00
@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)
}
2026-02-11 18:22:26 +00:00
@Test("toggleCompletedVisibility toggles the showCompleted property")
2026-02-11 13:00:46 +00:00
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)
}
2026-02-10 15:31:52 +00:00
}