Files
honeyDueKMP/iosApp/MyCribTests/TaskUITests.swift
Trey t d5d16c5c48 Add comprehensive unit tests for iOS and Android/KMM
This commit adds extensive unit test coverage for the entire application,
including iOS ViewModels, design system, and shared Kotlin Multiplatform code.

iOS Unit Tests (49 tests):
- LoginViewModelTests: Authentication state and validation tests
- ResidenceViewModelTests: Residence loading and state management
- TaskViewModelTests: Task operations (cancel, archive, mark progress)
- DocumentViewModelTests: Document/warranty CRUD operations
- ContractorViewModelTests: Contractor management and favorites
- DesignSystemTests: Color system, typography, spacing, radius, shadows

Shared KMM Unit Tests (26 tests):
- AuthViewModelTest: Login, register, verify email state initialization
- TaskViewModelTest: Task state management verification
- DocumentViewModelTest: Document state initialization tests
- ResidenceViewModelTest: Residence state management tests
- ContractorViewModelTest: Contractor state initialization tests

Test Infrastructure:
- Reorganized test files from iosAppUITests to MyCribTests
- All shared KMM tests passing successfully (./gradlew test)
- Tests focus on state initialization and core functionality
- Ready for CI/CD integration

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-12 17:50:29 -06:00

432 lines
15 KiB
Swift

import XCTest
/// Comprehensive tests for task management
final class TaskUITests: BaseUITest {
override func setUp() {
super.setUp()
// Login before each test
login(username: "testuser", password: "TestPass123!")
let residencesTab = app.tabBars.buttons["Residences"]
XCTAssertTrue(residencesTab.waitForExistence(timeout: 10))
}
// MARK: - Task List Tests
func testTasksTabDisplays() {
// When: User navigates to tasks tab
navigateToTab("Tasks")
// Then: Tasks screen should be displayed
let navigationBar = app.navigationBars["All Tasks"]
XCTAssertTrue(navigationBar.waitForExistence(timeout: 5), "Should show tasks navigation bar")
}
func testTaskColumnsDisplay() {
// Given: User is on tasks tab
navigateToTab("Tasks")
wait(seconds: 2)
// Then: Task columns should be visible
let upcomingLabel = app.staticTexts["Upcoming"]
let inProgressLabel = app.staticTexts["In Progress"]
let doneLabel = app.staticTexts["Done"]
XCTAssertTrue(upcomingLabel.exists || inProgressLabel.exists || doneLabel.exists ||
app.staticTexts["No tasks yet"].exists,
"Should show task columns or empty state")
}
func testEmptyTasksState() {
// Given: User has no tasks (requires account with no tasks)
navigateToTab("Tasks")
wait(seconds: 2)
// Then: Empty state should be shown
let emptyState = app.staticTexts["No tasks yet"]
let addButton = app.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Add Task'")).firstMatch
// Should show either tasks or empty state
XCTAssertTrue(emptyState.exists || app.scrollViews.firstMatch.exists,
"Should show content or empty state")
}
// MARK: - Create Task Tests
func testCreateTaskFromTasksTab() {
// Given: User is on tasks tab
navigateToTab("Tasks")
// When: User taps add task button
let addButton = app.navigationBars.buttons.matching(identifier: "plus").firstMatch
if addButton.exists {
addButton.tap()
wait(seconds: 1)
// Then: Add task form should be displayed
let titleField = app.textFields.containing(NSPredicate(format: "placeholderValue CONTAINS[c] 'title' OR label CONTAINS[c] 'Title'")).firstMatch
XCTAssertTrue(titleField.exists, "Should show add task form")
// When: User fills in task details
let timestamp = Int(Date().timeIntervalSince1970)
titleField.tap()
titleField.typeText("Test Task \(timestamp)")
let descriptionField = app.textViews.containing(NSPredicate(format: "placeholderValue CONTAINS[c] 'description' OR label CONTAINS[c] 'Description'")).firstMatch
if descriptionField.exists {
descriptionField.tap()
descriptionField.typeText("Test task description")
}
// When: User saves the task
let saveButton = app.buttons["Save"]
if saveButton.exists {
saveButton.tap()
}
// Then: Task should be created and user returned to tasks list
wait(seconds: 2)
XCTAssertTrue(app.navigationBars["All Tasks"].exists || app.staticTexts["Test Task \(timestamp)"].exists,
"Should create task and return to list")
}
}
func testCreateTaskFromResidenceDetails() {
// Given: User is viewing a residence
navigateToTab("Residences")
wait(seconds: 2)
let firstResidence = app.cells.firstMatch
if firstResidence.exists {
firstResidence.tap()
wait(seconds: 2)
// When: User taps add task button
let addButton = app.navigationBars.buttons.matching(identifier: "plus").firstMatch
if addButton.exists {
addButton.tap()
wait(seconds: 1)
// Then: Add task form should be displayed
let titleField = app.textFields.containing(NSPredicate(format: "placeholderValue CONTAINS[c] 'title' OR label CONTAINS[c] 'Title'")).firstMatch
XCTAssertTrue(titleField.exists, "Should show add task form from residence")
// When: User creates the task
let timestamp = Int(Date().timeIntervalSince1970)
titleField.tap()
titleField.typeText("Residence Task \(timestamp)")
let saveButton = app.buttons["Save"]
if saveButton.exists {
saveButton.tap()
}
// Then: Task should be created
wait(seconds: 2)
XCTAssertTrue(app.staticTexts["Residence Task \(timestamp)"].exists || app.navigationBars.element(boundBy: 1).exists,
"Should create task for residence")
}
}
}
func testCreateTaskValidation() {
// Given: User is on add task form
navigateToTab("Tasks")
let addButton = app.navigationBars.buttons.matching(identifier: "plus").firstMatch
if addButton.exists {
addButton.tap()
wait(seconds: 1)
// When: User tries to save without required fields
let saveButton = app.buttons["Save"]
if saveButton.exists {
saveButton.tap()
}
// Then: Validation errors should be shown
let errorMessages = app.staticTexts.containing(NSPredicate(format: "label CONTAINS[c] 'required'"))
XCTAssertTrue(errorMessages.count > 0 || !app.navigationBars["All Tasks"].exists,
"Should show validation errors")
}
}
// MARK: - View Task Details Tests
func testViewTaskDetails() {
// Given: User has tasks
navigateToTab("Tasks")
wait(seconds: 2)
// When: User taps on a task card
let taskCard = app.otherElements.containing(NSPredicate(format: "identifier CONTAINS 'TaskCard'")).firstMatch
if !taskCard.exists {
// Try finding by task title
let taskTitle = app.staticTexts.matching(NSPredicate(format: "label CONTAINS[c] 'task'")).firstMatch
if taskTitle.exists {
taskTitle.tap()
}
} else {
taskCard.tap()
}
// Note: Depending on implementation, tapping a task might show details or edit form
wait(seconds: 1)
// Verify some form of task interaction occurred
}
// MARK: - Edit Task Tests
func testEditTaskFlow() {
// Given: User is viewing/editing a task
navigateToTab("Tasks")
wait(seconds: 2)
// Find and tap edit button on a task
let editButtons = app.buttons.matching(identifier: "pencil")
if editButtons.count > 0 {
editButtons.firstMatch.tap()
wait(seconds: 1)
// When: User modifies task details
let titleField = app.textFields.containing(NSPredicate(format: "value != nil AND value != ''")).firstMatch
if titleField.exists {
app.clearText(in: titleField)
titleField.typeText("Updated Task Title")
// When: User saves changes
let saveButton = app.buttons["Save"]
if saveButton.exists {
saveButton.tap()
}
// Then: Changes should be saved
wait(seconds: 2)
let updatedTitle = app.staticTexts["Updated Task Title"]
XCTAssertTrue(updatedTitle.waitForExistence(timeout: 5) || app.navigationBars["All Tasks"].exists,
"Should save task changes")
}
}
}
// MARK: - Complete Task Tests
func testCompleteTaskFlow() {
// Given: User has an incomplete task
navigateToTab("Tasks")
wait(seconds: 2)
// When: User taps complete button on a task
let completeButton = app.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Complete'")).firstMatch
if completeButton.exists {
completeButton.tap()
wait(seconds: 1)
// Then: Complete task dialog should be shown
let completionDialog = app.sheets.firstMatch
XCTAssertTrue(completionDialog.exists || app.staticTexts.containing(NSPredicate(format: "label CONTAINS[c] 'Complete'")).firstMatch.exists,
"Should show completion dialog")
// When: User confirms completion
let confirmButton = app.buttons["Complete"]
if confirmButton.exists {
confirmButton.tap()
}
// Then: Task should be marked as complete
wait(seconds: 2)
// Task should move to completed column or show completion status
}
}
func testCompleteTaskWithDetails() {
// Given: User is completing a task
navigateToTab("Tasks")
wait(seconds: 2)
let completeButton = app.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Complete'")).firstMatch
if completeButton.exists {
completeButton.tap()
wait(seconds: 1)
// When: User adds completion details
let notesField = app.textViews.containing(NSPredicate(format: "placeholderValue CONTAINS[c] 'notes' OR label CONTAINS[c] 'Notes'")).firstMatch
if notesField.exists {
notesField.tap()
notesField.typeText("Task completed successfully")
}
let costField = app.textFields.containing(NSPredicate(format: "placeholderValue CONTAINS[c] 'cost' OR label CONTAINS[c] 'Cost'")).firstMatch
if costField.exists {
costField.tap()
costField.typeText("100")
}
// When: User saves completion
let saveButton = app.buttons["Save"]
if saveButton.exists {
saveButton.tap()
}
// Then: Task should be completed with details
wait(seconds: 2)
}
}
// MARK: - Task Status Changes Tests
func testMarkTaskInProgress() {
// Given: User has a pending task
navigateToTab("Tasks")
wait(seconds: 2)
// When: User marks task as in progress
let inProgressButton = app.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'In Progress'")).firstMatch
if inProgressButton.exists {
inProgressButton.tap()
wait(seconds: 2)
// Then: Task should move to In Progress column
let inProgressColumn = app.staticTexts["In Progress"]
XCTAssertTrue(inProgressColumn.exists, "Should have In Progress column")
}
}
func testCancelTask() {
// Given: User has an active task
navigateToTab("Tasks")
wait(seconds: 2)
// When: User cancels a task
let cancelButton = app.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Cancel'")).firstMatch
if cancelButton.exists {
cancelButton.tap()
// Confirm cancellation if prompted
let confirmButton = app.alerts.buttons["Cancel Task"]
if confirmButton.exists {
confirmButton.tap()
}
// Then: Task should be cancelled
wait(seconds: 2)
}
}
func testUncancelTask() {
// Given: User has a cancelled task
navigateToTab("Tasks")
wait(seconds: 2)
// When: User uncancels a task
let uncancelButton = app.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Uncancel' OR label CONTAINS[c] 'Restore'")).firstMatch
if uncancelButton.exists {
uncancelButton.tap()
wait(seconds: 2)
// Then: Task should be restored
}
}
// MARK: - Archive Task Tests
func testArchiveTask() {
// Given: User has a completed task
navigateToTab("Tasks")
wait(seconds: 2)
// When: User archives a task
let archiveButton = app.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Archive'")).firstMatch
if archiveButton.exists {
archiveButton.tap()
wait(seconds: 2)
// Then: Task should be archived (moved to archived column or hidden)
let archivedColumn = app.staticTexts["Archived"]
// Task may be in archived column or removed from view
}
}
func testUnarchiveTask() {
// Given: User has archived tasks
navigateToTab("Tasks")
wait(seconds: 2)
// Scroll to archived column if it exists
let scrollView = app.scrollViews.firstMatch
if scrollView.exists {
scrollView.swipeLeft()
scrollView.swipeLeft()
}
// When: User unarchives a task
let unarchiveButton = app.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Unarchive'")).firstMatch
if unarchiveButton.exists {
unarchiveButton.tap()
wait(seconds: 2)
// Then: Task should be restored from archive
}
}
// MARK: - Task Filtering and Viewing Tests
func testSwipeBetweenTaskColumns() {
// Given: User is viewing tasks
navigateToTab("Tasks")
wait(seconds: 2)
let scrollView = app.scrollViews.firstMatch
if scrollView.exists {
// When: User swipes to view different columns
scrollView.swipeLeft()
wait(seconds: 0.5)
// Then: Next column should be visible
scrollView.swipeLeft()
wait(seconds: 0.5)
// User can navigate between columns
scrollView.swipeRight()
wait(seconds: 0.5)
}
}
func testTasksByResidence() {
// Given: User is viewing a residence
navigateToTab("Residences")
wait(seconds: 2)
let firstResidence = app.cells.firstMatch
if firstResidence.exists {
firstResidence.tap()
wait(seconds: 2)
// Then: Tasks for that residence should be shown
let tasksSection = app.staticTexts["Tasks"]
XCTAssertTrue(tasksSection.exists || app.scrollViews.firstMatch.exists,
"Should show tasks section in residence details")
}
}
// MARK: - Task Recurrence Tests
func testCreateRecurringTask() {
// Given: User is creating a new task
navigateToTab("Tasks")
let addButton = app.navigationBars.buttons.matching(identifier: "plus").firstMatch
if addButton.exists {
addButton.tap()
wait(seconds: 1)
// When: User selects recurring frequency
let frequencyPicker = app.pickers.firstMatch
if frequencyPicker.exists {
// Select a frequency (e.g., Monthly)
let monthlyOption = app.pickerWheels.element.adjust(toPickerWheelValue: "Monthly")
// Task creation with recurrence
}
}
}
}