feat: add unit tests for models and ListStore and UI test for adding a todo

This commit is contained in:
2026-03-04 19:14:23 +01:00
parent 680320784f
commit 76bba19959
3 changed files with 196 additions and 1 deletions

View File

@@ -7,7 +7,11 @@ struct MindDumpApp: App {
@State private var store: ListStore
init() {
let container = try! ModelContainer(for: TodoList.self, TodoItem.self)
let isUITest = ProcessInfo.processInfo.arguments.contains("UI_TESTING")
let config = isUITest
? ModelConfiguration(isStoredInMemoryOnly: true)
: ModelConfiguration()
let container = try! ModelContainer(for: TodoList.self, TodoItem.self, configurations: config)
self.container = container
self._store = State(initialValue: ListStore(modelContext: container.mainContext))
}

View File

@@ -0,0 +1,139 @@
import XCTest
import SwiftData
@testable import MindDump
final class TodoItemTests: XCTestCase {
// MARK: - isOverdue
/// Past deadline should be overdue
func testIsOverdue_deadlineYesterday_returnsTrue() {
let yesterday = Calendar.current.date(byAdding: .day, value: -1, to: Date())!
let item = TodoItem(title: "Test", deadline: yesterday)
XCTAssertTrue(item.isOverdue)
}
/// Completed items are never overdue, even with past deadline
func testIsOverdue_completedWithPastDeadline_returnsFalse() {
let yesterday = Calendar.current.date(byAdding: .day, value: -1, to: Date())!
let item = TodoItem(title: "Test", isCompleted: true, deadline: yesterday)
XCTAssertFalse(item.isOverdue)
}
}
final class TodoListTests: XCTestCase {
// MARK: - openItems / completedItems
/// Items are correctly split into open and completed
func testOpenAndCompletedItems_correctSplit() {
let open1 = TodoItem(title: "Open 1")
let open2 = TodoItem(title: "Open 2")
let done = TodoItem(title: "Done", isCompleted: true)
let list = TodoList(name: "Test", items: [open1, done, open2])
XCTAssertEqual(list.openItems.count, 2)
XCTAssertEqual(list.completedItems.count, 1)
XCTAssertEqual(list.completedItems.first?.title, "Done")
}
/// Open items are sorted oldest first
func testOpenItems_sortedByCreatedAtAscending() {
let older = TodoItem(title: "Older", createdAt: Date().addingTimeInterval(-100))
let newer = TodoItem(title: "Newer", createdAt: Date())
let list = TodoList(name: "Test", items: [newer, older])
XCTAssertEqual(list.openItems.map(\.title), ["Older", "Newer"])
}
/// Completed items are sorted newest first (by modifiedAt)
func testCompletedItems_sortedByModifiedAtDescending() {
let earlier = TodoItem(title: "Earlier", isCompleted: true, modifiedAt: Date().addingTimeInterval(-100))
let later = TodoItem(title: "Later", isCompleted: true, modifiedAt: Date())
let list = TodoList(name: "Test", items: [earlier, later])
XCTAssertEqual(list.completedItems.map(\.title), ["Later", "Earlier"])
}
}
// MARK: - ListStore Tests
@MainActor
final class ListStoreTests: XCTestCase {
private var container: ModelContainer!
private var store: ListStore!
override func setUp() {
let config = ModelConfiguration(isStoredInMemoryOnly: true)
container = try! ModelContainer(for: TodoList.self, TodoItem.self, configurations: config)
store = ListStore(modelContext: container.mainContext)
}
// MARK: - addItem / deleteItem
/// Adding an item increases the list count and stores the title
func testAddItem_increasesCount() {
let inboxID = store.inboxID
store.addItem(to: inboxID, title: "Buy milk")
XCTAssertEqual(store.lists.first!.items.count, 1)
XCTAssertEqual(store.lists.first!.items.first?.title, "Buy milk")
}
/// Deleting an item removes it from the list
func testDeleteItem_removesItem() {
let inboxID = store.inboxID
store.addItem(to: inboxID, title: "Temp")
let itemID = store.lists.first!.items.first!.id
store.deleteItem(itemID, from: inboxID)
XCTAssertTrue(store.lists.first!.items.isEmpty)
}
// MARK: - toggleItemCompleted
/// Toggling flips isCompleted back and forth
func testToggleItemCompleted_flipsStatus() {
let inboxID = store.inboxID
store.addItem(to: inboxID, title: "Task")
let itemID = store.lists.first!.items.first!.id
XCTAssertFalse(store.lists.first!.items.first!.isCompleted)
store.toggleItemCompleted(itemID, in: inboxID)
XCTAssertTrue(store.lists.first!.items.first!.isCompleted)
store.toggleItemCompleted(itemID, in: inboxID)
XCTAssertFalse(store.lists.first!.items.first!.isCompleted)
}
// MARK: - moveItem
/// Moving an item removes it from the source list, adds it to the target list, and updates modifiedAt
func testMoveItem_movesAcrossLists() {
let inboxID = store.inboxID
store.addList(name: "Arbeit")
let arbeitID = store.lists.first { $0.name == "Arbeit" }!.id
store.addItem(to: inboxID, title: "Wichtige Aufgabe")
let itemID = store.lists.first { $0.isInbox }!.items.first!.id
store.moveItem(itemID, from: inboxID, to: arbeitID)
let inbox = store.lists.first { $0.isInbox }!
let arbeit = store.lists.first { $0.name == "Arbeit" }!
XCTAssertTrue(inbox.items.isEmpty, "Item should be removed from source lists")
XCTAssertEqual(arbeit.items.count, 1, "Item should be in target list")
XCTAssertEqual(arbeit.items.first?.title, "Wichtige Aufgabe")
XCTAssertNotNil(arbeit.items.first?.modifiedAt, "modifiedAt should be set after move")
}
}

View File

@@ -0,0 +1,52 @@
import XCTest
final class MindDumpUITests: XCTestCase {
override func setUpWithError() throws {
continueAfterFailure = false
}
/// Tapping the MindDumpButton, entering a title and saving should show the entry in the list
@MainActor
func testQuickDump_createsEntry() throws {
let app = XCUIApplication()
app.launchArguments = ["UI_TESTING"]
app.launch()
// Tap the MindDumpButton button
app.buttons["🧠"].tap()
// Type a title in the editor sheet
let titleField = app.textFields["Titel"]
XCTAssertTrue(titleField.waitForExistence(timeout: 2))
titleField.typeText("UI Test Eintrag")
// Add a note
app.buttons["Notiz"].tap()
let noteField = app.textFields["Notiz hinzufügen..."]
XCTAssertTrue(noteField.waitForExistence(timeout: 2))
noteField.tap()
noteField.typeText("Wichtige Notiz")
// Add high priority tap the add-field button, then the picker row, then the option
app.buttons["Priorität"].tap()
app.buttons["Priorität, 🟡 Mittel"].tap()
app.buttons["🔴 Hoch"].tap()
// Tap "Fertig" to save
app.buttons["Fertig"].tap()
// Navigate into Inbox to see the entry
app.staticTexts["Inbox"].tap()
// Verify the entry appears in the list
let entryExists = app.descendants(matching: .any)["UI Test Eintrag"].waitForExistence(timeout: 3)
XCTAssertTrue(entryExists, "Entry title should be visible")
// Verify the note snippet is shown below the title
XCTAssertTrue(app.descendants(matching: .any)["Wichtige Notiz"].exists, "Note should be visible")
// Verify the red priority dot is shown
XCTAssertTrue(app.images["circle.fill"].exists, "Priority dot should be visible")
}
}