Group tasks feature (#3)

This PR contains the work done to implement the **Group Task** feature, and to integrate it into the main content view of the app. Furthermore, an issue related to triggering a view update after inserting the todo tasks into the model container has been fixed.

Reviewed-on: rock-n-code/todos-app#3
Co-authored-by: Javier Cicchelli <javier@rock-n-code.com>
Co-committed-by: Javier Cicchelli <javier@rock-n-code.com>
This commit was merged in pull request #3.
This commit is contained in:
2026-02-10 02:22:41 +00:00
committed by javier
parent 5506471fe0
commit 0b8e5d8bd0
16 changed files with 806 additions and 9 deletions
@@ -0,0 +1,99 @@
import Foundation
import SwiftUI
import Testing
@testable import Todos
@Suite("TodoGroup enumeration tests")
struct TodoGroupTests {
// MARK: Computed tests
@Test(
"group color match expected values",
arguments: zip(
TodoGroup.allCases,
[Color.blue, .green, .red, .gray]
)
)
func color(
for group: TodoGroup,
expects expectation: Color
) {
// GIVEN
// WHEN
let color = group.color
// THEN
#expect(color == expectation)
}
@Test(
"group icon names match expected values",
arguments: zip(
TodoGroup.allCases,
["calendar.badge.clock", "calendar", "calendar.badge.exclamationmark", "checkmark.circle.fill"]
)
)
func iconName(
for group: TodoGroup,
expects expectation: String
) {
// GIVEN
// WHEN
let iconName = group.iconName
// THEN
#expect(iconName == expectation)
}
@Test(
"group titles match expected values",
arguments: zip(
TodoGroup.allCases,
["today", "upcoming", "overdue", "completed"]
)
)
func title(
for group: TodoGroup,
expects expectation: String
) {
// GIVEN
let key = "enumeration.todo-group.title." + expectation
// WHEN
let title = group.title
// THEN
#expect(title == .init(key))
}
// MARK: Case Iteration Tests
@Test("all cases contains all cases, in correct order")
func allCases() {
// GIVEN
// WHEN
let allCases = TodoGroup.allCases
// THEN
#expect(allCases.count == 4)
#expect(allCases[0] == .today)
#expect(allCases[1] == .upcoming)
#expect(allCases[2] == .overdue)
#expect(allCases[3] == .completed)
}
// MARK: Identifiable Tests
@Test("id returns an identity of a case", arguments: TodoGroup.allCases)
func id(for group: TodoGroup) {
// GIVEN
// WHEN
let id = group.id
// THEN
#expect(id == group)
}
}
@@ -4,7 +4,7 @@ import Testing
@testable import Todos
@Suite("ContentView.ViewModel Tests")
@Suite("ContentView view model tests")
struct ContentViewModelTests {
// MARK: Initialization Tests
@@ -4,7 +4,7 @@ import Testing
@testable import Todos
@Suite("NewTaskView ViewModel Tests")
@Suite("NewTaskView view model tests")
struct NewTaskViewModelTests {
// MARK: Initialization tests
@@ -0,0 +1,82 @@
import Foundation
import SwiftData
import Testing
@testable import Todos
@Suite("TaskGroupListView view model tests")
struct TaskGroupListViewModelTests {
// MARK: Properties
private let calendar: Calendar = .current
// MARK: Computed tests
@Test("groups returns cases in predetermined order")
func groups() {
// GIVEN
let viewModel = TaskGroupListView.ViewModel()
// WHEN
let groups = viewModel.groups
// THEN
#expect(groups.count == 4)
#expect(groups[0] == .today)
#expect(groups[1] == .upcoming)
#expect(groups[2] == .overdue)
#expect(groups[3] == .completed)
}
// MARK: Functions tests
@Test(
"count returns zero when empty data",
arguments: TodoGroup.allCases
)
func countWhenEmptyData(for group: TodoGroup) throws {
// GIVEN
let tasks: [Todo] = []
let viewModel = TaskGroupListView.ViewModel()
// WHEN
let count = viewModel.count(tasks, for: group)
// THEN
#expect(count == 0)
}
@Test(
"count returns correct count when filled data",
arguments: TodoGroup.allCases
)
func countWhenFilledData(for group: TodoGroup) throws {
// GIVEN
let today: Date = .now
let tomorrow = calendar.date(byAdding: .day, value: 1, to: today) ?? today
let yesterday = calendar.date(byAdding: .day, value: -1, to: today) ?? today
let nextWeek = calendar.date(byAdding: .day, value: 7, to: today) ?? today
let lastWeek = calendar.date(byAdding: .day, value: -7, to: today) ?? today
let tasks = [
Todo(title: "Today task #1", due: today),
Todo(title: "Today task #2", due: today),
Todo(title: "Tomorrow task #1", due: tomorrow),
Todo(title: "Next week task #1", due: nextWeek),
Todo(title: "Yesterday task #1", due: yesterday),
Todo(title: "Last week task #1", due: lastWeek),
Todo(title: "Completed task #1", due: tomorrow, completed: today),
Todo(title: "Completed task #2", due: yesterday, completed: today)
]
let viewModel = TaskGroupListView.ViewModel()
// WHEN
let count = viewModel.count(tasks, for: group)
// THEN
#expect(count == 2)
}
}