Remove obsolete test files and unused HomeScreenView

Cleaned up old MyCribTests directory containing outdated unit tests that
have been replaced by the comprehensive XCUITest suite in MyCribUITests.
Also removed unused HomeScreenView that was replaced by RootView.

Removed files:
- iosApp/MyCribTests/*.swift: Old unit tests (11 files)
- iosApp/iosApp/HomeScreenView.swift: Replaced by RootView

The new XCUITest suite provides better coverage with end-to-end UI tests
that validate actual user interactions rather than isolated unit tests.

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Trey t
2025-11-20 23:07:29 -06:00
parent 74a474007b
commit 5ba6e6c020
12 changed files with 0 additions and 2497 deletions

View File

@@ -1,269 +0,0 @@
import XCTest
/// Comprehensive tests for authentication flows
final class AuthenticationUITests: BaseUITest {
// MARK: - Login Tests
func testLoginWithValidCredentials() {
// Given: User is on login screen
XCTAssertTrue(app.staticTexts["MyCrib"].exists)
// When: User enters valid credentials and taps login
login(username: "testuser", password: "TestPass123!")
// Then: User should be navigated to main screen
let residencesTab = app.tabBars.buttons["Residences"]
XCTAssertTrue(residencesTab.waitForExistence(timeout: 10), "Should navigate to main tab view")
}
func testLoginWithInvalidCredentials() {
// Given: User is on login screen
XCTAssertTrue(app.staticTexts["MyCrib"].exists)
// When: User enters invalid credentials
login(username: "invaliduser", password: "WrongPassword!")
// Then: Error message should be displayed
let errorMessage = app.staticTexts.containing(NSPredicate(format: "label CONTAINS[c] 'Invalid username or password'"))
XCTAssertTrue(errorMessage.firstMatch.waitForExistence(timeout: 5), "Should show error message")
// And: User should remain on login screen
XCTAssertTrue(app.staticTexts["MyCrib"].exists)
}
func testLoginWithEmptyFields() {
// Given: User is on login screen
let loginButton = app.buttons["Login"]
// When: User taps login without entering credentials
loginButton.tap()
// Then: Validation error should be shown
let usernameError = app.staticTexts.containing(NSPredicate(format: "label CONTAINS[c] 'Username is required'"))
XCTAssertTrue(usernameError.firstMatch.waitForExistence(timeout: 3), "Should show username required error")
}
func testPasswordVisibilityToggle() {
// Given: User has typed password
let passwordField = app.secureTextFields["Password"]
let textField = app.textFields["Password"]
let toggleButton = app.buttons.matching(identifier: "eye").firstMatch
passwordField.tap()
passwordField.typeText("TestPassword")
// When: User taps the visibility toggle
toggleButton.tap()
// Then: Password should be visible as text
XCTAssertTrue(textField.exists, "Password should be visible")
// When: User taps toggle again
toggleButton.tap()
// Then: Password should be hidden again
XCTAssertTrue(passwordField.exists, "Password should be secure")
}
// MARK: - Registration Tests
func testRegistrationWithValidData() {
// Given: User is on login screen
let signUpButton = app.buttons["Sign Up"]
// When: User taps Sign Up
signUpButton.tap()
// Then: Registration screen should be displayed
assertNavigatedTo(title: "Create Account", timeout: 3)
// When: User fills in valid registration data
let timestamp = Int(Date().timeIntervalSince1970)
register(
username: "newuser\(timestamp)",
email: "newuser\(timestamp)@test.com",
password: "TestPass123!",
firstName: "Test",
lastName: "User"
)
// Then: User should be registered and shown verification screen
let verificationTitle = app.staticTexts.containing(NSPredicate(format: "label CONTAINS[c] 'Verify'"))
XCTAssertTrue(verificationTitle.firstMatch.waitForExistence(timeout: 10), "Should show verification screen")
}
func testRegistrationWithExistingUsername() {
// Given: User is on registration screen
let signUpButton = app.buttons["Sign Up"]
signUpButton.tap()
// When: User registers with existing username
register(
username: "existinguser",
email: "newemail@test.com",
password: "TestPass123!"
)
// Then: Error message should be displayed
let errorMessage = app.staticTexts.containing(NSPredicate(format: "label CONTAINS[c] 'username' OR label CONTAINS[c] 'already exists'"))
XCTAssertTrue(errorMessage.firstMatch.waitForExistence(timeout: 5), "Should show username exists error")
}
func testRegistrationWithInvalidEmail() {
// Given: User is on registration screen
let signUpButton = app.buttons["Sign Up"]
signUpButton.tap()
// When: User enters invalid email
let emailField = app.textFields["Email"]
let registerButton = app.buttons["Register"]
emailField.tap()
emailField.typeText("invalidemail")
registerButton.tap()
// Then: Email validation error should be shown
let errorMessage = app.staticTexts.containing(NSPredicate(format: "label CONTAINS[c] 'valid email'"))
XCTAssertTrue(errorMessage.firstMatch.waitForExistence(timeout: 3), "Should show email validation error")
}
func testRegistrationWithWeakPassword() {
// Given: User is on registration screen
let signUpButton = app.buttons["Sign Up"]
signUpButton.tap()
// When: User enters weak password
let timestamp = Int(Date().timeIntervalSince1970)
let usernameField = app.textFields["Username"]
let emailField = app.textFields["Email"]
let passwordField = app.secureTextFields["Password"]
let registerButton = app.buttons["Register"]
usernameField.tap()
usernameField.typeText("testuser\(timestamp)")
emailField.tap()
emailField.typeText("test\(timestamp)@test.com")
passwordField.tap()
passwordField.typeText("weak")
registerButton.tap()
// Then: Password validation error should be shown
let errorMessage = app.staticTexts.containing(NSPredicate(format: "label CONTAINS[c] 'password' AND (label CONTAINS[c] 'strong' OR label CONTAINS[c] 'at least')"))
XCTAssertTrue(errorMessage.firstMatch.waitForExistence(timeout: 3), "Should show password strength error")
}
// MARK: - Logout Tests
func testLogoutFlow() {
// Given: User is logged in
login(username: "testuser", password: "TestPass123!")
let residencesTab = app.tabBars.buttons["Residences"]
XCTAssertTrue(residencesTab.waitForExistence(timeout: 10))
// When: User logs out
logout()
// Then: User should be returned to login screen
XCTAssertTrue(app.staticTexts["MyCrib"].waitForExistence(timeout: 5), "Should return to login screen")
XCTAssertTrue(app.buttons["Login"].exists, "Login button should be visible")
}
func testLogoutClearsUserData() {
// Given: User is logged in and has viewed some data
login(username: "testuser", password: "TestPass123!")
let residencesTab = app.tabBars.buttons["Residences"]
XCTAssertTrue(residencesTab.waitForExistence(timeout: 10))
navigateToTab("Residences")
wait(seconds: 2) // Wait for data to load
// When: User logs out
logout()
// And: User logs back in
login(username: "testuser", password: "TestPass123!")
// Then: Fresh data should be loaded (not cached)
let loadingIndicator = app.activityIndicators.firstMatch
XCTAssertTrue(loadingIndicator.exists || app.staticTexts.containing(NSPredicate(format: "label CONTAINS[c] 'Loading'")).firstMatch.exists,
"Should show loading state for fresh data")
}
// MARK: - Session Management Tests
func testSessionPersistence() {
// Given: User logs in
login(username: "testuser", password: "TestPass123!")
let residencesTab = app.tabBars.buttons["Residences"]
XCTAssertTrue(residencesTab.waitForExistence(timeout: 10))
// When: App is terminated and relaunched
app.terminate()
app.launch()
// Then: User should still be logged in
XCTAssertTrue(residencesTab.waitForExistence(timeout: 5), "User session should persist")
}
func testLoginRedirectsVerifiedUser() {
// Given: Verified user logs in
login(username: "verifieduser", password: "TestPass123!")
// Then: User should go directly to main screen (not verification)
let residencesTab = app.tabBars.buttons["Residences"]
XCTAssertTrue(residencesTab.waitForExistence(timeout: 10), "Verified user should skip verification")
}
func testLoginRedirectsUnverifiedUser() {
// Given: Unverified user logs in
login(username: "unverifieduser", password: "TestPass123!")
// Then: User should be shown verification screen
let verificationTitle = app.staticTexts.containing(NSPredicate(format: "label CONTAINS[c] 'Verify'"))
XCTAssertTrue(verificationTitle.firstMatch.waitForExistence(timeout: 10), "Unverified user should see verification screen")
}
// MARK: - Error Handling Tests
func testLoginWithNetworkError() {
// Note: This test requires network simulation or mocking
// For now, it's a placeholder for future implementation
// Given: Network is unavailable
// When: User attempts to login
// Then: Network error should be displayed
}
func testLoginRetryAfterError() {
// Given: User encountered a login error
login(username: "invaliduser", password: "WrongPassword!")
let errorMessage = app.staticTexts.containing(NSPredicate(format: "label CONTAINS[c] 'Invalid'"))
XCTAssertTrue(errorMessage.firstMatch.waitForExistence(timeout: 5))
// When: User enters correct credentials
let usernameField = app.textFields["Username"]
let passwordField = app.secureTextFields["Password"]
let loginButton = app.buttons["Login"]
app.clearText(in: usernameField)
usernameField.typeText("testuser")
app.clearText(in: passwordField)
passwordField.typeText("TestPass123!")
loginButton.tap()
// Then: Login should succeed
let residencesTab = app.tabBars.buttons["Residences"]
XCTAssertTrue(residencesTab.waitForExistence(timeout: 10), "Should login successfully after retry")
}
}

View File

@@ -1,91 +0,0 @@
import XCTest
@testable import iosApp
import ComposeApp
@MainActor
final class ContractorViewModelTests: XCTestCase {
var sut: ContractorViewModel!
override func setUp() {
super.setUp()
sut = ContractorViewModel()
}
override func tearDown() {
sut = nil
super.tearDown()
}
// MARK: - Initialization Tests
func testInitialState() {
// Then
XCTAssertFalse(sut.isLoading)
XCTAssertNil(sut.errorMessage)
XCTAssertTrue(sut.contractors.isEmpty)
}
// MARK: - Contractor Loading Tests
func testLoadContractorsWithoutFilters() {
// When
sut.loadContractors()
// Then - Should start loading or complete
XCTAssertTrue(sut.isLoading || sut.errorMessage != nil || !sut.contractors.isEmpty || (!sut.isLoading && sut.contractors.isEmpty))
}
func testLoadContractorsWithSpecialtyFilter() {
// When
sut.loadContractors(specialty: "Plumbing")
// Then - Should not crash
XCTAssertNotNil(sut)
}
func testLoadContractorsWithFavoriteFilter() {
// When
sut.loadContractors(isFavorite: true)
// Then - Should not crash
XCTAssertNotNil(sut)
}
func testLoadContractorsWithSearchQuery() {
// When
sut.loadContractors(search: "John")
// Then - Should not crash
XCTAssertNotNil(sut)
}
// MARK: - Toggle Favorite Tests
func testToggleFavoriteWithValidId() {
// Given
let expectation = XCTestExpectation(description: "Toggle favorite callback")
var resultSuccess: Bool?
// When
sut.toggleFavorite(id: 1) { success in
resultSuccess = success
expectation.fulfill()
}
// Then
wait(for: [expectation], timeout: 5.0)
XCTAssertNotNil(resultSuccess)
}
// MARK: - State Management Tests
func testMultipleLoadCallsDontCrash() {
// When
sut.loadContractors()
sut.loadContractors(specialty: "Electrical")
sut.loadContractors(isFavorite: true)
// Then - Should not crash
XCTAssertNotNil(sut)
}
}

View File

@@ -1,172 +0,0 @@
import XCTest
@testable import iosApp
import SwiftUI
final class DesignSystemTests: XCTestCase {
// MARK: - Color Tests
func testPrimaryColorsExist() {
// Then
XCTAssertNotNil(AppColors.primary)
XCTAssertNotNil(AppColors.primaryLight)
XCTAssertNotNil(AppColors.primaryDark)
}
func testAccentColorsExist() {
// Then
XCTAssertNotNil(AppColors.accent)
XCTAssertNotNil(AppColors.accentLight)
}
func testSemanticColorsExist() {
// Then
XCTAssertNotNil(AppColors.success)
XCTAssertNotNil(AppColors.warning)
XCTAssertNotNil(AppColors.error)
XCTAssertNotNil(AppColors.info)
}
func testNeutralColorsExist() {
// Then
XCTAssertNotNil(AppColors.background)
XCTAssertNotNil(AppColors.surface)
XCTAssertNotNil(AppColors.surfaceSecondary)
XCTAssertNotNil(AppColors.textPrimary)
XCTAssertNotNil(AppColors.textSecondary)
XCTAssertNotNil(AppColors.textTertiary)
XCTAssertNotNil(AppColors.border)
XCTAssertNotNil(AppColors.borderLight)
}
func testTaskStatusColorsExist() {
// Then
XCTAssertNotNil(AppColors.taskUpcoming)
XCTAssertNotNil(AppColors.taskInProgress)
XCTAssertNotNil(AppColors.taskCompleted)
XCTAssertNotNil(AppColors.taskCanceled)
XCTAssertNotNil(AppColors.taskArchived)
}
func testGradientsExist() {
// Then
XCTAssertNotNil(AppColors.primaryGradient)
XCTAssertNotNil(AppColors.accentGradient)
}
// MARK: - Typography Tests
func testDisplayTypographyExists() {
// Then
XCTAssertNotNil(AppTypography.displayLarge)
XCTAssertNotNil(AppTypography.displayMedium)
XCTAssertNotNil(AppTypography.displaySmall)
}
func testHeadlineTypographyExists() {
// Then
XCTAssertNotNil(AppTypography.headlineLarge)
XCTAssertNotNil(AppTypography.headlineMedium)
XCTAssertNotNil(AppTypography.headlineSmall)
}
func testTitleTypographyExists() {
// Then
XCTAssertNotNil(AppTypography.titleLarge)
XCTAssertNotNil(AppTypography.titleMedium)
XCTAssertNotNil(AppTypography.titleSmall)
}
func testBodyTypographyExists() {
// Then
XCTAssertNotNil(AppTypography.bodyLarge)
XCTAssertNotNil(AppTypography.bodyMedium)
XCTAssertNotNil(AppTypography.bodySmall)
}
func testLabelTypographyExists() {
// Then
XCTAssertNotNil(AppTypography.labelLarge)
XCTAssertNotNil(AppTypography.labelMedium)
XCTAssertNotNil(AppTypography.labelSmall)
}
// MARK: - Spacing Tests
func testSpacingValuesAreCorrect() {
// Then
XCTAssertEqual(AppSpacing.xxs, 4)
XCTAssertEqual(AppSpacing.xs, 8)
XCTAssertEqual(AppSpacing.sm, 12)
XCTAssertEqual(AppSpacing.md, 16)
XCTAssertEqual(AppSpacing.lg, 24)
XCTAssertEqual(AppSpacing.xl, 32)
XCTAssertEqual(AppSpacing.xxl, 48)
XCTAssertEqual(AppSpacing.xxxl, 64)
}
// MARK: - Radius Tests
func testRadiusValuesAreCorrect() {
// Then
XCTAssertEqual(AppRadius.xs, 4)
XCTAssertEqual(AppRadius.sm, 8)
XCTAssertEqual(AppRadius.md, 12)
XCTAssertEqual(AppRadius.lg, 16)
XCTAssertEqual(AppRadius.xl, 20)
XCTAssertEqual(AppRadius.xxl, 24)
XCTAssertEqual(AppRadius.full, 9999)
}
// MARK: - Shadow Tests
func testShadowsExist() {
// Then
XCTAssertNotNil(AppShadow.sm)
XCTAssertNotNil(AppShadow.md)
XCTAssertNotNil(AppShadow.lg)
XCTAssertNotNil(AppShadow.xl)
}
// MARK: - Color Extension Tests
func testColorFromValidHexString() {
// When
let color = Color(hex: "FF0000")
// Then
XCTAssertNotNil(color)
}
func testColorFromInvalidHexString() {
// When
let color = Color(hex: "INVALID")
// Then
XCTAssertNil(color)
}
func testColorFrom3DigitHex() {
// When
let color = Color(hex: "F00")
// Then
XCTAssertNotNil(color)
}
func testColorFrom6DigitHex() {
// When
let color = Color(hex: "FF0000")
// Then
XCTAssertNotNil(color)
}
func testColorFrom8DigitHex() {
// When
let color = Color(hex: "FF0000FF")
// Then
XCTAssertNotNil(color)
}
}

View File

@@ -1,134 +0,0 @@
import XCTest
@testable import iosApp
import ComposeApp
@MainActor
final class DocumentViewModelTests: XCTestCase {
var sut: DocumentViewModel!
override func setUp() {
super.setUp()
sut = DocumentViewModel()
}
override func tearDown() {
sut = nil
super.tearDown()
}
// MARK: - Initialization Tests
func testInitialState() {
// Then
XCTAssertFalse(sut.isLoading)
XCTAssertNil(sut.errorMessage)
XCTAssertTrue(sut.documents.isEmpty)
}
// MARK: - Document Loading Tests
func testLoadDocumentsWithFilters() {
// When
sut.loadDocuments(
residenceId: 1,
documentType: "warranty",
isActive: true
)
// Then - Should start loading or complete
XCTAssertTrue(sut.isLoading || sut.errorMessage != nil || !sut.documents.isEmpty || (!sut.isLoading && sut.documents.isEmpty))
}
func testLoadDocumentsWithoutFilters() {
// When
sut.loadDocuments()
// Then - Should not crash
XCTAssertNotNil(sut)
}
// MARK: - Document Creation Tests
func testCreateDocumentWithRequiredFields() {
// Given
let expectation = XCTestExpectation(description: "Create document callback")
var resultSuccess: Bool?
var resultError: String?
// When
sut.createDocument(
title: "Test Document",
documentType: "warranty",
residenceId: 1
) { success, error in
resultSuccess = success
resultError = error
expectation.fulfill()
}
// Then
wait(for: [expectation], timeout: 5.0)
XCTAssertNotNil(resultSuccess)
}
func testCreateDocumentWithWarrantyFields() {
// Given
let expectation = XCTestExpectation(description: "Create warranty callback")
// When
sut.createDocument(
title: "Test Warranty",
documentType: "warranty",
residenceId: 1,
itemName: "HVAC System",
provider: "ACME Corp"
) { success, error in
expectation.fulfill()
}
// Then
wait(for: [expectation], timeout: 5.0)
XCTAssertNotNil(sut)
}
// MARK: - Document Update Tests
func testUpdateDocumentWithValidId() {
// Given
let expectation = XCTestExpectation(description: "Update document callback")
// When
sut.updateDocument(
id: 1,
title: "Updated Title"
) { success, error in
expectation.fulfill()
}
// Then
wait(for: [expectation], timeout: 5.0)
XCTAssertNotNil(sut)
}
// MARK: - Document Deletion Tests
func testDeleteDocumentWithValidId() {
// When
sut.deleteDocument(id: 1)
// Then - Should not crash
XCTAssertNotNil(sut)
}
// MARK: - State Management Tests
func testMultipleOperationsDontCrash() {
// When
sut.loadDocuments()
sut.loadDocuments(documentType: "warranty")
sut.deleteDocument(id: 1)
// Then - Should not crash
XCTAssertNotNil(sut)
}
}

View File

@@ -1,130 +0,0 @@
import XCTest
@testable import iosApp
import ComposeApp
@MainActor
final class LoginViewModelTests: XCTestCase {
var sut: LoginViewModel!
override func setUp() {
super.setUp()
sut = LoginViewModel()
}
override func tearDown() {
sut = nil
super.tearDown()
}
// MARK: - Initialization Tests
func testInitialState() {
// Then
XCTAssertEqual(sut.username, "")
XCTAssertEqual(sut.password, "")
XCTAssertFalse(sut.isLoading)
XCTAssertNil(sut.errorMessage)
XCTAssertFalse(sut.isAuthenticated)
XCTAssertFalse(sut.isVerified)
XCTAssertNil(sut.currentUser)
}
// MARK: - Validation Tests
func testLoginWithEmptyUsername() {
// Given
sut.username = ""
sut.password = "password123"
// When
sut.login()
// Then
XCTAssertEqual(sut.errorMessage, "Username is required")
XCTAssertFalse(sut.isLoading)
}
func testLoginWithEmptyPassword() {
// Given
sut.username = "testuser"
sut.password = ""
// When
sut.login()
// Then
XCTAssertEqual(sut.errorMessage, "Password is required")
XCTAssertFalse(sut.isLoading)
}
func testLoginWithValidCredentials() {
// Given
sut.username = "testuser"
sut.password = "password123"
// When
sut.login()
// Then
XCTAssertTrue(sut.isLoading || sut.errorMessage != nil || sut.isAuthenticated)
}
// MARK: - Error Handling Tests
func testCleanErrorMessageRemovesJSONStructures() {
// Given
let dirtyMessage = "Error: {\"detail\": \"Invalid credentials\"}"
// When - We can't directly test private method, but we can test the behavior
sut.errorMessage = dirtyMessage
// Then - Error message should be set (even if not cleaned in this test)
XCTAssertNotNil(sut.errorMessage)
}
func testClearError() {
// Given
sut.errorMessage = "Test error"
// When
sut.clearError()
// Then
XCTAssertNil(sut.errorMessage)
}
// MARK: - Logout Tests
func testLogout() {
// Given
sut.isAuthenticated = true
sut.isVerified = true
sut.username = "testuser"
sut.password = "password"
sut.errorMessage = "Test error"
// When
sut.logout()
// Then
XCTAssertFalse(sut.isAuthenticated)
XCTAssertFalse(sut.isVerified)
XCTAssertNil(sut.currentUser)
XCTAssertEqual(sut.username, "")
XCTAssertEqual(sut.password, "")
XCTAssertNil(sut.errorMessage)
}
// MARK: - State Management Tests
func testLoadingStateChanges() {
// Given
let initialLoadingState = sut.isLoading
// When
sut.login()
// Then - Loading state should change (either true during loading or false after quick failure)
XCTAssertTrue(sut.isLoading != initialLoadingState || sut.errorMessage != nil)
}
}

View File

@@ -1,470 +0,0 @@
import XCTest
/// Comprehensive tests for multi-user residence features
final class MultiUserUITests: BaseUITest {
override func setUp() {
super.setUp()
// Login as primary owner
login(username: "testowner", password: "TestPass123!")
let residencesTab = app.tabBars.buttons["Residences"]
XCTAssertTrue(residencesTab.waitForExistence(timeout: 10))
}
// MARK: - Manage Users Tests
func testManageUsersButtonVisibleForOwner() {
// Given: User owns a residence
navigateToTab("Residences")
wait(seconds: 2)
let firstResidence = app.cells.firstMatch
if firstResidence.exists {
firstResidence.tap()
wait(seconds: 2)
// Then: Manage users button should be visible
let manageUsersButton = app.navigationBars.buttons.matching(identifier: "person.2").firstMatch
XCTAssertTrue(manageUsersButton.exists, "Owner should see manage users button")
}
}
func testManageUsersButtonHiddenForSharedUser() {
// Given: User is a shared user (not owner)
logout()
login(username: "shareduser", password: "TestPass123!")
navigateToTab("Residences")
wait(seconds: 2)
// Find a shared residence
let sharedResidence = app.cells.firstMatch
if sharedResidence.exists {
sharedResidence.tap()
wait(seconds: 2)
// Then: Manage users button should NOT be visible
let manageUsersButton = app.navigationBars.buttons.matching(identifier: "person.2").firstMatch
XCTAssertFalse(manageUsersButton.exists, "Shared user should not see manage users button")
}
}
func testOpenManageUsersScreen() {
// Given: Owner is viewing a residence
navigateToTab("Residences")
wait(seconds: 2)
let firstResidence = app.cells.firstMatch
if firstResidence.exists {
firstResidence.tap()
wait(seconds: 2)
// When: Owner taps manage users button
let manageUsersButton = app.navigationBars.buttons.matching(identifier: "person.2").firstMatch
if manageUsersButton.exists {
manageUsersButton.tap()
wait(seconds: 1)
// Then: Manage users screen should be displayed
let manageUsersTitle = app.navigationBars["Manage Users"]
XCTAssertTrue(manageUsersTitle.waitForExistence(timeout: 3), "Should show manage users screen")
// And: User list should be visible
let usersList = app.scrollViews.firstMatch
XCTAssertTrue(usersList.exists, "Should show users list")
}
}
}
// MARK: - Share Code Tests
func testShareCodeInitiallyBlank() {
// Given: Owner opens manage users screen
navigateToTab("Residences")
wait(seconds: 2)
let firstResidence = app.cells.firstMatch
if firstResidence.exists {
firstResidence.tap()
wait(seconds: 2)
let manageUsersButton = app.navigationBars.buttons.matching(identifier: "person.2").firstMatch
if manageUsersButton.exists {
manageUsersButton.tap()
wait(seconds: 1)
// Then: Share code should be blank initially
let noActiveCode = app.staticTexts["No active code"]
XCTAssertTrue(noActiveCode.exists, "Share code should start blank")
}
}
}
func testGenerateShareCode() {
// Given: Owner is on manage users screen
navigateToTab("Residences")
wait(seconds: 2)
let firstResidence = app.cells.firstMatch
if firstResidence.exists {
firstResidence.tap()
wait(seconds: 2)
let manageUsersButton = app.navigationBars.buttons.matching(identifier: "person.2").firstMatch
if manageUsersButton.exists {
manageUsersButton.tap()
wait(seconds: 1)
// When: Owner taps generate code button
let generateButton = app.buttons["Generate"]
if !generateButton.exists {
// Button might say "New Code" if there's an existing code
let newCodeButton = app.buttons["New Code"]
if newCodeButton.exists {
newCodeButton.tap()
}
} else {
generateButton.tap()
}
// Then: Share code should be generated and displayed
wait(seconds: 2)
let shareCodeTexts = app.staticTexts.matching(NSPredicate(format: "label.length == 6 AND label MATCHES %@", "[A-Z0-9]{6}"))
XCTAssertTrue(shareCodeTexts.count > 0, "Should display 6-character share code")
}
}
}
func testRegenerateShareCode() {
// Given: Owner has generated a share code
navigateToTab("Residences")
wait(seconds: 2)
let firstResidence = app.cells.firstMatch
if firstResidence.exists {
firstResidence.tap()
wait(seconds: 2)
let manageUsersButton = app.navigationBars.buttons.matching(identifier: "person.2").firstMatch
if manageUsersButton.exists {
manageUsersButton.tap()
wait(seconds: 1)
// Generate first code
let generateButton = app.buttons["Generate"]
if generateButton.exists {
generateButton.tap()
wait(seconds: 2)
let firstCode = app.staticTexts.matching(NSPredicate(format: "label.length == 6 AND label MATCHES %@", "[A-Z0-9]{6}")).firstMatch.label
// When: Owner generates new code
let newCodeButton = app.buttons["New Code"]
if newCodeButton.exists {
newCodeButton.tap()
wait(seconds: 2)
// Then: A different code should be generated
let secondCode = app.staticTexts.matching(NSPredicate(format: "label.length == 6 AND label MATCHES %@", "[A-Z0-9]{6}")).firstMatch.label
XCTAssertNotEqual(firstCode, secondCode, "New code should be different")
}
}
}
}
}
// MARK: - Join Residence Tests
func testJoinResidenceWithValidCode() {
// This test requires coordination between two accounts
// Given: Owner generates a share code
var shareCode: String = ""
navigateToTab("Residences")
wait(seconds: 2)
let firstResidence = app.cells.firstMatch
if firstResidence.exists {
firstResidence.tap()
wait(seconds: 2)
let manageUsersButton = app.navigationBars.buttons.matching(identifier: "person.2").firstMatch
if manageUsersButton.exists {
manageUsersButton.tap()
wait(seconds: 1)
let generateButton = app.buttons["Generate"]
if generateButton.exists {
generateButton.tap()
wait(seconds: 2)
shareCode = app.staticTexts.matching(NSPredicate(format: "label.length == 6 AND label MATCHES %@", "[A-Z0-9]{6}")).firstMatch.label
}
// Close manage users screen
let closeButton = app.buttons["Close"]
if closeButton.exists {
closeButton.tap()
}
}
}
// When: Different user joins with code
if !shareCode.isEmpty {
logout()
login(username: "newuser", password: "TestPass123!")
navigateToTab("Residences")
wait(seconds: 1)
// Find join residence button
let joinButton = app.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Join'")).firstMatch
if joinButton.exists {
joinButton.tap()
wait(seconds: 1)
// Enter share code
let codeField = app.textFields.firstMatch
if codeField.exists {
codeField.tap()
codeField.typeText(shareCode)
// Submit
let submitButton = app.buttons["Join"]
if submitButton.exists {
submitButton.tap()
wait(seconds: 2)
// Then: User should be added to residence
let successMessage = app.staticTexts.containing(NSPredicate(format: "label CONTAINS[c] 'Successfully joined'"))
XCTAssertTrue(successMessage.firstMatch.exists || app.cells.count > 0,
"Should join residence successfully")
}
}
}
}
}
func testJoinResidenceWithInvalidCode() {
// Given: User is on join residence screen
navigateToTab("Residences")
let joinButton = app.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Join'")).firstMatch
if joinButton.exists {
joinButton.tap()
wait(seconds: 1)
// When: User enters invalid code
let codeField = app.textFields.firstMatch
if codeField.exists {
codeField.tap()
codeField.typeText("INVALID")
let submitButton = app.buttons["Join"]
if submitButton.exists {
submitButton.tap()
wait(seconds: 2)
// Then: Error message should be shown
let errorMessage = app.staticTexts.containing(NSPredicate(format: "label CONTAINS[c] 'Invalid' OR label CONTAINS[c] 'not found'"))
XCTAssertTrue(errorMessage.firstMatch.exists, "Should show invalid code error")
}
}
}
}
// MARK: - User List Tests
func testUserListShowsAllUsers() {
// Given: Residence has multiple users
navigateToTab("Residences")
wait(seconds: 2)
let firstResidence = app.cells.firstMatch
if firstResidence.exists {
firstResidence.tap()
wait(seconds: 2)
let manageUsersButton = app.navigationBars.buttons.matching(identifier: "person.2").firstMatch
if manageUsersButton.exists {
manageUsersButton.tap()
wait(seconds: 1)
// Then: User count should be displayed
let userCountLabel = app.staticTexts.matching(NSPredicate(format: "label CONTAINS[c] 'Users'"))
XCTAssertTrue(userCountLabel.count > 0, "Should show user count")
// And: Individual users should be listed
let usersList = app.scrollViews.firstMatch
XCTAssertTrue(usersList.exists, "Should show users list")
}
}
}
func testOwnerLabelDisplayed() {
// Given: Owner is viewing manage users screen
navigateToTab("Residences")
wait(seconds: 2)
let firstResidence = app.cells.firstMatch
if firstResidence.exists {
firstResidence.tap()
wait(seconds: 2)
let manageUsersButton = app.navigationBars.buttons.matching(identifier: "person.2").firstMatch
if manageUsersButton.exists {
manageUsersButton.tap()
wait(seconds: 1)
// Then: Owner badge should be visible next to owner's name
let ownerBadge = app.staticTexts["Owner"]
XCTAssertTrue(ownerBadge.exists, "Should show Owner badge")
}
}
}
// MARK: - Remove User Tests
func testRemoveUserAsOwner() {
// Given: Owner has residence with multiple users
navigateToTab("Residences")
wait(seconds: 2)
let firstResidence = app.cells.firstMatch
if firstResidence.exists {
firstResidence.tap()
wait(seconds: 2)
let manageUsersButton = app.navigationBars.buttons.matching(identifier: "person.2").firstMatch
if manageUsersButton.exists {
manageUsersButton.tap()
wait(seconds: 1)
// When: Owner taps remove button on a user
let removeButtons = app.buttons.matching(identifier: "trash")
if removeButtons.count > 0 {
let initialUserCount = removeButtons.count
removeButtons.firstMatch.tap()
// Confirm removal if prompted
let confirmButton = app.alerts.buttons["Remove"]
if confirmButton.exists {
confirmButton.tap()
}
// Then: User should be removed
wait(seconds: 2)
let newUserCount = app.buttons.matching(identifier: "trash").count
XCTAssertTrue(newUserCount < initialUserCount, "Should remove user")
}
}
}
}
func testCannotRemoveOwner() {
// Given: Owner is viewing manage users
navigateToTab("Residences")
wait(seconds: 2)
let firstResidence = app.cells.firstMatch
if firstResidence.exists {
firstResidence.tap()
wait(seconds: 2)
let manageUsersButton = app.navigationBars.buttons.matching(identifier: "person.2").firstMatch
if manageUsersButton.exists {
manageUsersButton.tap()
wait(seconds: 1)
// Then: Owner row should NOT have remove button
let ownerLabel = app.staticTexts["Owner"]
if ownerLabel.exists {
// Check if there's a remove button in the same container
// Owner should not have a remove button next to their name
}
}
}
}
// MARK: - Shared User Access Tests
func testSharedUserCanViewResidence() {
// Given: User is a shared user
logout()
login(username: "shareduser", password: "TestPass123!")
// Then: Shared residences should appear in list
navigateToTab("Residences")
wait(seconds: 2)
let residencesList = app.cells.count
XCTAssertTrue(residencesList > 0, "Shared user should see shared residences")
}
func testSharedUserCanCreateTasks() {
// Given: Shared user is viewing a shared residence
logout()
login(username: "shareduser", password: "TestPass123!")
navigateToTab("Residences")
wait(seconds: 2)
let firstResidence = app.cells.firstMatch
if firstResidence.exists {
firstResidence.tap()
wait(seconds: 2)
// When: Shared user tries to create a task
let addButton = app.navigationBars.buttons.matching(identifier: "plus").firstMatch
XCTAssertTrue(addButton.exists, "Shared user should be able to add tasks")
if addButton.exists {
addButton.tap()
wait(seconds: 1)
// Then: Add task form should be displayed
let titleField = app.textFields.firstMatch
XCTAssertTrue(titleField.exists, "Shared user should be able to create tasks")
}
}
}
func testSharedUserCanEditTasks() {
// Given: Shared user is viewing tasks
logout()
login(username: "shareduser", password: "TestPass123!")
navigateToTab("Tasks")
wait(seconds: 2)
// When: Shared user tries to edit a task
let editButtons = app.buttons.matching(identifier: "pencil")
if editButtons.count > 0 {
// Then: Edit buttons should be available
XCTAssertTrue(editButtons.firstMatch.exists, "Shared user should be able to edit tasks")
}
}
func testUserCountDisplayed() {
// Given: Owner is viewing a residence
navigateToTab("Residences")
wait(seconds: 2)
let firstResidence = app.cells.firstMatch
if firstResidence.exists {
firstResidence.tap()
wait(seconds: 2)
// Then: User count should be visible somewhere on the screen
// This depends on your UI design - adjust as needed
let manageUsersButton = app.navigationBars.buttons.matching(identifier: "person.2").firstMatch
if manageUsersButton.exists {
manageUsersButton.tap()
wait(seconds: 1)
let userCountText = app.staticTexts.matching(NSPredicate(format: "label CONTAINS[c] 'Users'"))
XCTAssertTrue(userCountText.count > 0, "Should display user count")
}
}
}
}

View File

@@ -1,346 +0,0 @@
import XCTest
/// Comprehensive tests for residence management
final class ResidenceUITests: 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: - List View Tests
func testResidenceListDisplays() {
// Given: User is on residences tab
navigateToTab("Residences")
// Then: Residences list should be displayed
let navigationBar = app.navigationBars["Residences"]
XCTAssertTrue(navigationBar.exists, "Should show residences navigation bar")
}
func testResidenceListShowsProperties() {
// Given: User has residences
navigateToTab("Residences")
// Then: Residence cards should be visible
let residenceCards = app.scrollViews.descendants(matching: .other).matching(NSPredicate(format: "identifier CONTAINS 'ResidenceCard'"))
XCTAssertTrue(residenceCards.count > 0 || app.staticTexts["No residences yet"].exists,
"Should show either residence cards or empty state")
}
func testEmptyStateDisplays() {
// Given: User has no residences (requires test account with no data)
navigateToTab("Residences")
// Then: Empty state should be shown
// Note: This test assumes test user has no residences
let emptyStateText = app.staticTexts["No residences yet"]
let addButton = app.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Add'")).firstMatch
// Should show either residences or empty state with add button
XCTAssertTrue(emptyStateText.exists || app.cells.count > 0, "Should show content or empty state")
}
// MARK: - Create Residence Tests
func testCreateResidenceFlow() {
// Given: User is on residences screen
navigateToTab("Residences")
// When: User taps add residence button
let addButton = app.navigationBars.buttons.matching(identifier: "plus").firstMatch
if !addButton.exists {
// Try finding add button in other locations
let fabButton = app.buttons["Add Residence"]
if fabButton.exists {
fabButton.tap()
} else {
XCTFail("Could not find add residence button")
}
} else {
addButton.tap()
}
// Then: Add residence form should be displayed
wait(seconds: 1)
let nameField = app.textFields.containing(NSPredicate(format: "placeholderValue CONTAINS[c] 'name' OR label CONTAINS[c] 'Name'")).firstMatch
XCTAssertTrue(nameField.exists, "Should show residence name field")
// When: User fills in residence details
let timestamp = Int(Date().timeIntervalSince1970)
nameField.tap()
nameField.typeText("Test House \(timestamp)")
let addressField = app.textFields.containing(NSPredicate(format: "placeholderValue CONTAINS[c] 'address' OR label CONTAINS[c] 'Address'")).firstMatch
if addressField.exists {
addressField.tap()
addressField.typeText("123 Test Street")
}
let cityField = app.textFields.containing(NSPredicate(format: "placeholderValue CONTAINS[c] 'city' OR label CONTAINS[c] 'City'")).firstMatch
if cityField.exists {
cityField.tap()
cityField.typeText("Test City")
}
// When: User saves the residence
let saveButton = app.buttons["Save"]
if saveButton.exists {
saveButton.tap()
}
// Then: User should be returned to list view
// And: New residence should appear in the list
wait(seconds: 2)
let residenceTitle = app.staticTexts["Test House \(timestamp)"]
XCTAssertTrue(residenceTitle.waitForExistence(timeout: 5) || app.navigationBars["Residences"].exists,
"Should show new residence or navigate back to list")
}
func testCreateResidenceValidation() {
// Given: User is on add residence screen
navigateToTab("Residences")
let addButton = app.navigationBars.buttons.matching(identifier: "plus").firstMatch
addButton.tap()
wait(seconds: 1)
// When: User attempts 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["Residences"].exists,
"Should show validation errors or prevent saving")
}
// MARK: - View Residence Details Tests
func testViewResidenceDetails() {
// Given: User has residences
navigateToTab("Residences")
wait(seconds: 2)
// When: User taps on a residence
let firstResidence = app.cells.firstMatch
if firstResidence.exists {
firstResidence.tap()
// Then: Residence details should be displayed
wait(seconds: 2)
let detailView = app.scrollViews.firstMatch
XCTAssertTrue(detailView.exists || app.navigationBars.element(boundBy: 1).exists,
"Should show residence details")
}
}
func testResidenceDetailsShowsAllSections() {
// Given: User is viewing residence details
navigateToTab("Residences")
wait(seconds: 2)
let firstResidence = app.cells.firstMatch
if firstResidence.exists {
firstResidence.tap()
wait(seconds: 2)
// Then: All sections should be visible (after scrolling)
let scrollView = app.scrollViews.firstMatch
if scrollView.exists {
// Check for address section
let addressSection = app.staticTexts.containing(NSPredicate(format: "label CONTAINS[c] 'Address'")).firstMatch
// Check for tasks section
scrollView.swipeUp()
let tasksSection = app.staticTexts.containing(NSPredicate(format: "label CONTAINS[c] 'Tasks'")).firstMatch
XCTAssertTrue(addressSection.exists || tasksSection.exists, "Should show residence sections")
}
}
}
// MARK: - Edit Residence Tests
func testEditResidenceFlow() {
// Given: User is viewing residence details
navigateToTab("Residences")
wait(seconds: 2)
let firstResidence = app.cells.firstMatch
if firstResidence.exists {
firstResidence.tap()
wait(seconds: 2)
// When: User taps edit button
let editButton = app.navigationBars.buttons["Edit"]
if editButton.exists {
editButton.tap()
wait(seconds: 1)
// Then: Edit form should be displayed
let nameField = app.textFields.containing(NSPredicate(format: "value != nil AND value != ''")).firstMatch
XCTAssertTrue(nameField.exists, "Should show edit form with current values")
// When: User updates the name
if nameField.exists {
app.clearText(in: nameField)
nameField.typeText("Updated House Name")
// When: User saves changes
let saveButton = app.buttons["Save"]
if saveButton.exists {
saveButton.tap()
}
// Then: Changes should be saved and details view updated
wait(seconds: 2)
let updatedName = app.staticTexts["Updated House Name"]
XCTAssertTrue(updatedName.waitForExistence(timeout: 5) || app.navigationBars.element(boundBy: 1).exists,
"Should show updated residence name or details view")
}
}
}
}
func testCancelEditingResidence() {
// Given: User is editing a residence
navigateToTab("Residences")
wait(seconds: 2)
let firstResidence = app.cells.firstMatch
if firstResidence.exists {
firstResidence.tap()
wait(seconds: 2)
let editButton = app.navigationBars.buttons["Edit"]
if editButton.exists {
editButton.tap()
wait(seconds: 1)
// When: User makes changes
let nameField = app.textFields.firstMatch
let originalValue = nameField.value as? String
if nameField.exists {
app.clearText(in: nameField)
nameField.typeText("Temporary Change")
// When: User cancels
let cancelButton = app.buttons["Cancel"]
if cancelButton.exists {
cancelButton.tap()
}
// Then: Changes should be discarded
wait(seconds: 1)
if let original = originalValue {
let originalText = app.staticTexts[original]
XCTAssertTrue(originalText.exists || app.navigationBars.element(boundBy: 1).exists,
"Should discard changes")
}
}
}
}
}
// MARK: - Delete Residence Tests
func testDeleteResidence() {
// Given: User has a residence to delete
navigateToTab("Residences")
wait(seconds: 2)
let initialResidenceCount = app.cells.count
// When: User swipes to delete (if supported)
let firstResidence = app.cells.firstMatch
if firstResidence.exists {
firstResidence.swipeLeft()
let deleteButton = app.buttons["Delete"]
if deleteButton.exists {
deleteButton.tap()
// Confirm deletion if alert appears
let confirmButton = app.alerts.buttons["Delete"]
if confirmButton.exists {
confirmButton.tap()
}
// Then: Residence should be removed
wait(seconds: 2)
let newCount = app.cells.count
XCTAssertTrue(newCount < initialResidenceCount || app.staticTexts["No residences yet"].exists,
"Should remove residence from list")
}
}
}
// MARK: - Navigation Tests
func testNavigateBackFromDetails() {
// Given: User is viewing residence details
navigateToTab("Residences")
wait(seconds: 2)
let firstResidence = app.cells.firstMatch
if firstResidence.exists {
firstResidence.tap()
wait(seconds: 2)
// When: User taps back button
navigateBack()
// Then: User should return to residences list
let navigationBar = app.navigationBars["Residences"]
XCTAssertTrue(navigationBar.waitForExistence(timeout: 3), "Should navigate back to residences list")
}
}
// MARK: - Search and Filter Tests (if implemented)
func testSearchResidences() {
// Given: User has multiple residences
navigateToTab("Residences")
wait(seconds: 2)
// When: User searches for a residence
let searchField = app.searchFields.firstMatch
if searchField.exists {
searchField.tap()
searchField.typeText("Test")
// Then: Results should be filtered
wait(seconds: 1)
let visibleResidences = app.cells.count
XCTAssertTrue(visibleResidences >= 0, "Should show filtered results")
}
}
// MARK: - Pull to Refresh Tests
func testPullToRefreshResidences() {
// Given: User is on residences list
navigateToTab("Residences")
wait(seconds: 2)
// When: User pulls down to refresh
let scrollView = app.scrollViews.firstMatch
if scrollView.exists {
let startPoint = scrollView.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.2))
let endPoint = scrollView.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.8))
startPoint.press(forDuration: 0.1, thenDragTo: endPoint)
// Then: Loading indicator should appear
let loadingIndicator = app.activityIndicators.firstMatch
XCTAssertTrue(loadingIndicator.exists || scrollView.exists, "Should trigger refresh")
}
}
}

View File

@@ -1,60 +0,0 @@
import XCTest
@testable import iosApp
import ComposeApp
@MainActor
final class ResidenceViewModelTests: XCTestCase {
var sut: ResidenceViewModel!
override func setUp() {
super.setUp()
sut = ResidenceViewModel()
}
override func tearDown() {
sut = nil
super.tearDown()
}
// MARK: - Initialization Tests
func testInitialState() {
// Then
XCTAssertFalse(sut.isLoading)
XCTAssertNil(sut.errorMessage)
XCTAssertNil(sut.myResidences)
}
// MARK: - Loading Tests
func testLoadMyResidencesStartsLoading() {
// Given
let initialLoadingState = sut.isLoading
// When
sut.loadMyResidences()
// Then - Loading should start or complete quickly
XCTAssertTrue(sut.isLoading || sut.errorMessage != nil || sut.myResidences != nil)
}
// MARK: - Error Handling Tests
func testErrorMessageIsSetOnFailure() {
// This test would require mocking the API
// For now, we test that error handling mechanism exists
XCTAssertNil(sut.errorMessage)
}
// MARK: - State Management Tests
func testMultipleLoadCallsDontCrash() {
// When
sut.loadMyResidences()
sut.loadMyResidences()
sut.loadMyResidences()
// Then - Should not crash
XCTAssertNotNil(sut)
}
}

View File

@@ -1,431 +0,0 @@
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
}
}
}
}

View File

@@ -1,118 +0,0 @@
import XCTest
@testable import iosApp
import ComposeApp
@MainActor
final class TaskViewModelTests: XCTestCase {
var sut: TaskViewModel!
override func setUp() {
super.setUp()
sut = TaskViewModel()
}
override func tearDown() {
sut = nil
super.tearDown()
}
// MARK: - Initialization Tests
func testInitialState() {
// Then
XCTAssertFalse(sut.isLoading)
XCTAssertNil(sut.errorMessage)
XCTAssertTrue(sut.tasks.isEmpty)
}
// MARK: - Task Operations Tests
func testCancelTaskWithValidId() {
// Given
let taskId: Int32 = 1
var callbackExecuted = false
// When
sut.cancelTask(id: taskId) { success in
callbackExecuted = true
}
// Then - Callback should eventually be called
let expectation = XCTestExpectation(description: "Callback executed")
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
if callbackExecuted || self.sut.errorMessage != nil {
expectation.fulfill()
}
}
wait(for: [expectation], timeout: 2.0)
}
func testUncancelTaskWithValidId() {
// Given
let taskId: Int32 = 1
var callbackExecuted = false
// When
sut.uncancelTask(id: taskId) { success in
callbackExecuted = true
}
// Then - Should not crash
XCTAssertNotNil(sut)
}
func testArchiveTaskWithValidId() {
// Given
let taskId: Int32 = 1
var callbackExecuted = false
// When
sut.archiveTask(id: taskId) { success in
callbackExecuted = true
}
// Then - Should not crash
XCTAssertNotNil(sut)
}
func testUnarchiveTaskWithValidId() {
// Given
let taskId: Int32 = 1
var callbackExecuted = false
// When
sut.unarchiveTask(id: taskId) { success in
callbackExecuted = true
}
// Then - Should not crash
XCTAssertNotNil(sut)
}
func testMarkInProgressWithValidId() {
// Given
let taskId: Int32 = 1
var callbackExecuted = false
// When
sut.markInProgress(id: taskId) { success in
callbackExecuted = true
}
// Then - Should not crash
XCTAssertNotNil(sut)
}
// MARK: - State Management Tests
func testMultipleOperationsDontCrash() {
// When
sut.cancelTask(id: 1) { _ in }
sut.uncancelTask(id: 2) { _ in }
sut.archiveTask(id: 3) { _ in }
// Then - Should not crash
XCTAssertNotNil(sut)
}
}

View File

@@ -1,180 +0,0 @@
import XCTest
/// Helper extensions and utilities for UI tests
extension XCUIApplication {
/// Launch the app and reset to initial state
func launchAndReset() {
launchArguments = ["--uitesting"]
launch()
}
/// Clear all text from a text field
func clearText(in textField: XCUIElement) {
textField.tap()
textField.press(forDuration: 1.2)
menuItems["Select All"].tap()
textField.typeText(XCUIKeyboardKey.delete.rawValue)
}
}
/// Base test class with common setup and teardown
class BaseUITest: XCTestCase {
var app: XCUIApplication!
override func setUp() {
super.setUp()
continueAfterFailure = false
app = XCUIApplication()
app.launchAndReset()
}
override func tearDown() {
app = nil
super.tearDown()
}
// MARK: - Authentication Helpers
func login(username: String, password: String) {
let usernameField = app.textFields["Username"]
let passwordField = app.secureTextFields["Password"]
let loginButton = app.buttons["Login"]
if usernameField.exists {
usernameField.tap()
usernameField.typeText(username)
}
if passwordField.exists {
passwordField.tap()
passwordField.typeText(password)
}
if loginButton.exists {
loginButton.tap()
}
}
func logout() {
let profileTab = app.tabBars.buttons["Profile"]
profileTab.tap()
let logoutButton = app.buttons["Log Out"]
XCTAssertTrue(logoutButton.waitForExistence(timeout: 5))
logoutButton.tap()
}
func register(username: String, email: String, password: String, firstName: String = "", lastName: String = "") {
let signUpButton = app.buttons["Sign Up"]
XCTAssertTrue(signUpButton.waitForExistence(timeout: 3))
signUpButton.tap()
let usernameField = app.textFields["Username"]
let emailField = app.textFields["Email"]
let passwordField = app.secureTextFields["Password"]
let registerButton = app.buttons["Register"]
usernameField.tap()
usernameField.typeText(username)
emailField.tap()
emailField.typeText(email)
passwordField.tap()
passwordField.typeText(password)
if !firstName.isEmpty {
let firstNameField = app.textFields["First Name"]
firstNameField.tap()
firstNameField.typeText(firstName)
}
if !lastName.isEmpty {
let lastNameField = app.textFields["Last Name"]
lastNameField.tap()
lastNameField.typeText(lastName)
}
registerButton.tap()
}
// MARK: - Navigation Helpers
func navigateToTab(_ tabName: String) {
let tab = app.tabBars.buttons[tabName]
XCTAssertTrue(tab.waitForExistence(timeout: 3))
tab.tap()
}
func navigateBack() {
app.navigationBars.buttons.element(boundBy: 0).tap()
}
// MARK: - Assertion Helpers
func assertElementExists(_ identifier: String, timeout: TimeInterval = 5) {
let element = app.descendants(matching: .any).matching(identifier: identifier).firstMatch
XCTAssertTrue(element.waitForExistence(timeout: timeout), "Element '\(identifier)' does not exist")
}
func assertElementDoesNotExist(_ identifier: String, timeout: TimeInterval = 2) {
let element = app.descendants(matching: .any).matching(identifier: identifier).firstMatch
XCTAssertFalse(element.waitForExistence(timeout: timeout), "Element '\(identifier)' should not exist")
}
func assertNavigatedTo(title: String, timeout: TimeInterval = 5) {
let navigationBar = app.navigationBars[title]
XCTAssertTrue(navigationBar.waitForExistence(timeout: timeout), "Did not navigate to '\(title)'")
}
// MARK: - Wait Helpers
func wait(seconds: TimeInterval) {
Thread.sleep(forTimeInterval: seconds)
}
func waitForElementToAppear(_ element: XCUIElement, timeout: TimeInterval = 5) -> Bool {
return element.waitForExistence(timeout: timeout)
}
func waitForElementToDisappear(_ element: XCUIElement, timeout: TimeInterval = 5) -> Bool {
let predicate = NSPredicate(format: "exists == false")
let expectation = XCTNSPredicateExpectation(predicate: predicate, object: element)
let result = XCTWaiter.wait(for: [expectation], timeout: timeout)
return result == .completed
}
}
/// Identifiers for UI elements (for better testability)
struct Identifiers {
struct Authentication {
static let usernameField = "UsernameField"
static let passwordField = "PasswordField"
static let loginButton = "LoginButton"
static let registerButton = "RegisterButton"
static let logoutButton = "LogoutButton"
}
struct Residence {
static let addButton = "AddResidenceButton"
static let editButton = "EditResidenceButton"
static let deleteButton = "DeleteResidenceButton"
static let nameField = "ResidenceNameField"
static let addressField = "ResidenceAddressField"
}
struct Task {
static let addButton = "AddTaskButton"
static let completeButton = "CompleteTaskButton"
static let editButton = "EditTaskButton"
static let titleField = "TaskTitleField"
static let descriptionField = "TaskDescriptionField"
}
struct MultiUser {
static let manageUsersButton = "ManageUsersButton"
static let generateCodeButton = "GenerateCodeButton"
static let joinButton = "JoinResidenceButton"
static let shareCodeField = "ShareCodeField"
}
}

View File

@@ -1,96 +0,0 @@
import SwiftUI
import ComposeApp
struct HomeScreenView: View {
@StateObject private var viewModel = ResidenceViewModel()
@StateObject private var loginViewModel = LoginViewModel()
var body: some View {
NavigationView {
ZStack {
Color(.systemGroupedBackground)
.ignoresSafeArea()
if viewModel.isLoading {
VStack(spacing: AppSpacing.lg) {
ProgressView()
.scaleEffect(1.2)
Text("Loading...")
.font(.body)
.foregroundColor(Color(.secondaryLabel))
}
} else {
ScrollView(showsIndicators: false) {
VStack(spacing: AppSpacing.xl) {
// Greeting Header
VStack(alignment: .leading, spacing: AppSpacing.xs) {
Text("Hello!")
.font(.title.weight(.bold))
.fontWeight(.bold)
.foregroundColor(Color(.label))
Text("Welcome to MyCrib")
.font(.body)
.foregroundColor(Color(.secondaryLabel))
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.horizontal, AppSpacing.md)
.padding(.top, AppSpacing.md)
// Overview Card
if let summary = viewModel.residenceSummary {
OverviewCard(summary: summary.summary)
.transition(.scale.combined(with: .opacity))
}
// Navigation Cards
VStack(spacing: AppSpacing.md) {
NavigationLink(destination: ResidencesListView()) {
HomeNavigationCard(
icon: "house.fill",
title: "Residences",
subtitle: "Manage your properties"
)
}
.buttonStyle(PlainButtonStyle())
NavigationLink(destination: AllTasksView()) {
HomeNavigationCard(
icon: "checkmark.circle.fill",
title: "Tasks",
subtitle: "View and manage all tasks"
)
}
.buttonStyle(PlainButtonStyle())
}
.padding(.horizontal, AppSpacing.md)
}
.padding(.vertical, AppSpacing.md)
}
}
}
.navigationTitle("MyCrib")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button(action: {
loginViewModel.logout()
}) {
HStack(spacing: AppSpacing.xs) {
Image(systemName: "rectangle.portrait.and.arrow.right")
.font(.system(size: 18, weight: .semibold))
}
.foregroundColor(.red)
}
}
}
.onAppear {
viewModel.loadResidenceSummary()
}
}
}
}
#Preview {
HomeScreenView()
}