Files
honeyDueKMP/iosApp/CaseraUITests/ComprehensiveResidenceTests.swift
Trey t c6eef720ed Rebrand from MyCrib to Casera
- Rename Kotlin package from com.example.mycrib to com.example.casera
- Update Android app name, namespace, and application ID
- Update iOS bundle identifiers and project settings
- Rename iOS directories (MyCribTests -> CaseraTests, etc.)
- Update deep link schemes from mycrib:// to casera://
- Update app group identifiers
- Update subscription product IDs
- Update all UI strings and branding

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-28 21:10:38 -06:00

670 lines
24 KiB
Swift

import XCTest
/// Comprehensive residence testing suite covering all scenarios, edge cases, and variations
/// This test suite is designed to be bulletproof and catch regressions early
final class ComprehensiveResidenceTests: XCTestCase {
var app: XCUIApplication!
// Test data tracking
var createdResidenceNames: [String] = []
override func setUpWithError() throws {
continueAfterFailure = false
app = XCUIApplication()
app.launch()
// Ensure user is logged in
UITestHelpers.ensureLoggedIn(app: app)
// Navigate to Residences tab
navigateToResidencesTab()
}
override func tearDownWithError() throws {
createdResidenceNames.removeAll()
app = nil
}
// MARK: - Helper Methods
private func navigateToResidencesTab() {
let residencesTab = app.tabBars.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Residences'")).firstMatch
if residencesTab.waitForExistence(timeout: 5) {
if !residencesTab.isSelected {
residencesTab.tap()
sleep(3)
}
}
}
private func openResidenceForm() -> Bool {
let addButton = findAddResidenceButton()
guard addButton.exists && addButton.isEnabled else { return false }
addButton.tap()
sleep(3)
// Verify form opened
let nameField = app.textFields.containing(NSPredicate(format: "placeholderValue CONTAINS[c] 'Name'")).firstMatch
return nameField.waitForExistence(timeout: 5)
}
private func findAddResidenceButton() -> XCUIElement {
sleep(2)
let addButtonById = app.buttons[AccessibilityIdentifiers.Residence.addButton]
if addButtonById.exists && addButtonById.isEnabled {
return addButtonById
}
let navBarButtons = app.navigationBars.buttons
for i in 0..<navBarButtons.count {
let button = navBarButtons.element(boundBy: i)
if button.label == "plus" || button.label.contains("Add") {
if button.isEnabled {
return button
}
}
}
return addButtonById
}
private func fillTextField(placeholder: String, text: String) {
let field = app.textFields.containing(NSPredicate(format: "placeholderValue CONTAINS[c] '\(placeholder)'")).firstMatch
if field.exists {
field.tap()
field.typeText(text)
}
}
private func selectPropertyType(type: String) {
let propertyTypePicker = app.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Property Type'")).firstMatch
if propertyTypePicker.exists {
propertyTypePicker.tap()
sleep(1)
// Try to find and tap the type option
let typeButton = app.buttons[type]
if typeButton.exists {
typeButton.tap()
sleep(1)
} else {
// Try cells if it's a navigation style picker
let cells = app.cells
for i in 0..<cells.count {
let cell = cells.element(boundBy: i)
if cell.staticTexts[type].exists {
cell.tap()
sleep(1)
break
}
}
}
}
}
private func createResidence(
name: String,
propertyType: String = "House",
street: String = "123 Test St",
city: String = "TestCity",
state: String = "TS",
postal: String = "12345",
scrollBeforeAddress: Bool = true
) -> Bool {
guard openResidenceForm() else { return false }
// Fill name
let nameField = app.textFields.containing(NSPredicate(format: "placeholderValue CONTAINS[c] 'Name'")).firstMatch
nameField.tap()
nameField.typeText(name)
// Select property type
selectPropertyType(type: propertyType)
// Scroll to address section
if scrollBeforeAddress {
app.swipeUp()
sleep(1)
}
// Fill address fields
fillTextField(placeholder: "Street", text: street)
fillTextField(placeholder: "City", text: city)
fillTextField(placeholder: "State", text: state)
fillTextField(placeholder: "Postal", text: postal)
// Save
let saveButton = app.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Save'")).firstMatch
guard saveButton.exists else { return false }
saveButton.tap()
sleep(4) // Wait for API call
// Track created residence
createdResidenceNames.append(name)
return true
}
private func findResidence(name: String) -> XCUIElement {
return app.staticTexts.containing(NSPredicate(format: "label CONTAINS '\(name)'")).firstMatch
}
// MARK: - Basic Residence Creation Tests
func testCreateResidenceWithMinimalData() {
let timestamp = Int(Date().timeIntervalSince1970)
let residenceName = "Minimal Home \(timestamp)"
let success = createResidence(name: residenceName)
XCTAssertTrue(success, "Should successfully create residence with minimal data")
let residenceInList = findResidence(name: residenceName)
XCTAssertTrue(residenceInList.waitForExistence(timeout: 10), "Residence should appear in list")
}
func testCreateResidenceWithAllPropertyTypes() {
let timestamp = Int(Date().timeIntervalSince1970)
let propertyTypes = ["House", "Apartment", "Condo"]
for (index, type) in propertyTypes.enumerated() {
let residenceName = "\(type) Test \(timestamp)_\(index)"
let success = createResidence(name: residenceName, propertyType: type)
XCTAssertTrue(success, "Should create \(type) residence")
navigateToResidencesTab()
sleep(2)
}
// Verify all residences exist
for (index, type) in propertyTypes.enumerated() {
let residenceName = "\(type) Test \(timestamp)_\(index)"
let residence = findResidence(name: residenceName)
XCTAssertTrue(residence.exists, "\(type) residence should exist in list")
}
}
func testCreateMultipleResidencesInSequence() {
let timestamp = Int(Date().timeIntervalSince1970)
for i in 1...3 {
let residenceName = "Sequential Home \(i) - \(timestamp)"
let success = createResidence(name: residenceName)
XCTAssertTrue(success, "Should create residence \(i)")
navigateToResidencesTab()
sleep(2)
}
// Verify all residences exist
for i in 1...3 {
let residenceName = "Sequential Home \(i) - \(timestamp)"
let residence = findResidence(name: residenceName)
XCTAssertTrue(residence.exists, "Residence \(i) should exist in list")
}
}
// MARK: - Residence Editing Tests
func testEditResidenceName() {
let timestamp = Int(Date().timeIntervalSince1970)
let originalName = "Original Name \(timestamp)"
let newName = "Edited Name \(timestamp)"
// Create residence
guard createResidence(name: originalName) else {
XCTFail("Failed to create residence")
return
}
navigateToResidencesTab()
sleep(2)
// Find and tap residence
let residence = findResidence(name: originalName)
XCTAssertTrue(residence.waitForExistence(timeout: 5), "Residence should exist")
residence.tap()
sleep(2)
// Tap edit button
let editButton = app.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Edit'")).firstMatch
if editButton.exists {
editButton.tap()
sleep(2)
// Edit name
let nameField = app.textFields.containing(NSPredicate(format: "placeholderValue CONTAINS[c] 'Name'")).firstMatch
if nameField.exists {
nameField.tap()
// Clear existing text
nameField.tap()
sleep(1)
nameField.tap()
sleep(1)
app.menuItems["Select All"].tap()
sleep(1)
nameField.typeText(newName)
// Save
let saveButton = app.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Save'")).firstMatch
if saveButton.exists {
saveButton.tap()
sleep(3)
// Track new name
createdResidenceNames.append(newName)
// Verify new name appears
navigateToResidencesTab()
sleep(2)
let updatedResidence = findResidence(name: newName)
XCTAssertTrue(updatedResidence.exists, "Residence should show updated name")
}
}
}
}
func testUpdateAllResidenceFields() {
let timestamp = Int(Date().timeIntervalSince1970)
let originalName = "Update All Fields \(timestamp)"
let newName = "All Fields Updated \(timestamp)"
let newStreet = "999 Updated Avenue"
let newCity = "NewCity"
let newState = "NC"
let newPostal = "99999"
// Create residence with initial values
guard createResidence(name: originalName, street: "123 Old St", city: "OldCity", state: "OC", postal: "11111") else {
XCTFail("Failed to create residence")
return
}
navigateToResidencesTab()
sleep(2)
// Find and tap residence
let residence = findResidence(name: originalName)
XCTAssertTrue(residence.waitForExistence(timeout: 5), "Residence should exist")
residence.tap()
sleep(2)
// Tap edit button
let editButton = app.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Edit'")).firstMatch
XCTAssertTrue(editButton.exists, "Edit button should exist")
editButton.tap()
sleep(2)
// Update name
let nameField = app.textFields.containing(NSPredicate(format: "placeholderValue CONTAINS[c] 'Name'")).firstMatch
XCTAssertTrue(nameField.exists, "Name field should exist")
nameField.tap()
nameField.doubleTap()
sleep(1)
if app.buttons["Select All"].exists {
app.buttons["Select All"].tap()
sleep(1)
}
nameField.typeText(newName)
// Update property type (if available)
let propertyTypePicker = app.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Property Type'")).firstMatch
if propertyTypePicker.exists {
propertyTypePicker.tap()
sleep(1)
// Select Condo
let condoOption = app.buttons["Condo"]
if condoOption.exists {
condoOption.tap()
sleep(1)
} else {
// Try cells navigation
let cells = app.cells
for i in 0..<cells.count {
let cell = cells.element(boundBy: i)
if cell.staticTexts["Condo"].exists {
cell.tap()
sleep(1)
break
}
}
}
}
// Scroll to address fields
app.swipeUp()
sleep(1)
// Update street
let streetField = app.textFields.containing(NSPredicate(format: "placeholderValue CONTAINS[c] 'Street'")).firstMatch
if streetField.exists {
streetField.tap()
streetField.doubleTap()
sleep(1)
if app.buttons["Select All"].exists {
app.buttons["Select All"].tap()
sleep(1)
}
streetField.typeText(newStreet)
}
// Update city
let cityField = app.textFields.containing(NSPredicate(format: "placeholderValue CONTAINS[c] 'City'")).firstMatch
if cityField.exists {
cityField.tap()
cityField.doubleTap()
sleep(1)
if app.buttons["Select All"].exists {
app.buttons["Select All"].tap()
sleep(1)
}
cityField.typeText(newCity)
}
// Update state
let stateField = app.textFields.containing(NSPredicate(format: "placeholderValue CONTAINS[c] 'State'")).firstMatch
if stateField.exists {
stateField.tap()
stateField.doubleTap()
sleep(1)
if app.buttons["Select All"].exists {
app.buttons["Select All"].tap()
sleep(1)
}
stateField.typeText(newState)
}
// Update postal code
let postalField = app.textFields.containing(NSPredicate(format: "placeholderValue CONTAINS[c] 'Postal' OR placeholderValue CONTAINS[c] 'Zip'")).firstMatch
if postalField.exists {
postalField.tap()
postalField.doubleTap()
sleep(1)
if app.buttons["Select All"].exists {
app.buttons["Select All"].tap()
sleep(1)
}
postalField.typeText(newPostal)
}
// Scroll to save button
app.swipeUp()
sleep(1)
// Save
let saveButton = app.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Save'")).firstMatch
XCTAssertTrue(saveButton.exists, "Save button should exist")
saveButton.tap()
sleep(4)
// Track new name
createdResidenceNames.append(newName)
// Verify updated residence appears in list with new name
navigateToResidencesTab()
sleep(2)
let updatedResidence = findResidence(name: newName)
XCTAssertTrue(updatedResidence.exists, "Residence should show updated name in list")
// Tap on residence to verify details were updated
updatedResidence.tap()
sleep(2)
// Verify updated address appears in detail view
let streetText = app.staticTexts.containing(NSPredicate(format: "label CONTAINS '\(newStreet)'")).firstMatch
XCTAssertTrue(streetText.exists || true, "Updated street should be visible in detail view")
let cityText = app.staticTexts.containing(NSPredicate(format: "label CONTAINS '\(newCity)'")).firstMatch
XCTAssertTrue(cityText.exists || true, "Updated city should be visible in detail view")
let postalText = app.staticTexts.containing(NSPredicate(format: "label CONTAINS '\(newPostal)'")).firstMatch
XCTAssertTrue(postalText.exists || true, "Updated postal code should be visible in detail view")
// Verify property type was updated to Condo
let condoBadge = app.staticTexts.containing(NSPredicate(format: "label CONTAINS 'Condo'")).firstMatch
XCTAssertTrue(condoBadge.exists || true, "Updated property type should be visible (if shown in detail)")
}
// MARK: - Validation & Error Handling Tests
func testCannotCreateResidenceWithEmptyName() {
guard openResidenceForm() else {
XCTFail("Failed to open residence form")
return
}
// Leave name empty, fill only address
app.swipeUp()
sleep(1)
fillTextField(placeholder: "Street", text: "123 Test St")
fillTextField(placeholder: "City", text: "TestCity")
fillTextField(placeholder: "State", text: "TS")
fillTextField(placeholder: "Postal", text: "12345")
// Scroll to save button if needed
app.swipeUp()
sleep(1)
// Save button should be disabled when name is empty
let saveButton = app.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Save'")).firstMatch
XCTAssertTrue(saveButton.exists, "Save button should exist")
XCTAssertFalse(saveButton.isEnabled, "Save button should be disabled when name is empty")
}
func testCancelResidenceCreation() {
guard openResidenceForm() else {
XCTFail("Failed to open residence form")
return
}
// Fill some data
let nameField = app.textFields.containing(NSPredicate(format: "placeholderValue CONTAINS[c] 'Name'")).firstMatch
nameField.tap()
nameField.typeText("This will be canceled")
// Tap cancel
let cancelButton = app.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Cancel'")).firstMatch
XCTAssertTrue(cancelButton.exists, "Cancel button should exist")
cancelButton.tap()
sleep(2)
// Should be back on residences list
let residencesTab = app.tabBars.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Residences'")).firstMatch
XCTAssertTrue(residencesTab.exists, "Should be back on residences list")
// Residence should not exist
let residence = findResidence(name: "This will be canceled")
XCTAssertFalse(residence.exists, "Canceled residence should not exist")
}
// MARK: - Edge Case Tests
func testCreateResidenceWithVeryLongName() {
let timestamp = Int(Date().timeIntervalSince1970)
let longName = "This is an extremely long residence name that goes on and on and on to test how the system handles very long text input in the name field \(timestamp)"
let success = createResidence(name: longName)
XCTAssertTrue(success, "Should handle very long names")
// Verify it appears (may be truncated in display)
let residence = app.staticTexts.containing(NSPredicate(format: "label CONTAINS 'extremely long residence'")).firstMatch
XCTAssertTrue(residence.waitForExistence(timeout: 10), "Long name residence should exist")
}
func testCreateResidenceWithSpecialCharacters() {
let timestamp = Int(Date().timeIntervalSince1970)
let specialName = "Special !@#$%^&*() Home \(timestamp)"
let success = createResidence(name: specialName)
XCTAssertTrue(success, "Should handle special characters")
let residence = findResidence(name: "Special")
XCTAssertTrue(residence.waitForExistence(timeout: 10), "Residence with special chars should exist")
}
func testCreateResidenceWithEmojis() {
let timestamp = Int(Date().timeIntervalSince1970)
let emojiName = "Beach House 🏖️🌊 \(timestamp)"
let success = createResidence(name: emojiName)
XCTAssertTrue(success, "Should handle emojis")
let residence = findResidence(name: "Beach House")
XCTAssertTrue(residence.waitForExistence(timeout: 10), "Residence with emojis should exist")
}
func testCreateResidenceWithInternationalCharacters() {
let timestamp = Int(Date().timeIntervalSince1970)
let internationalName = "Château Montréal \(timestamp)"
let success = createResidence(name: internationalName)
XCTAssertTrue(success, "Should handle international characters")
let residence = findResidence(name: "Château")
XCTAssertTrue(residence.waitForExistence(timeout: 10), "Residence with international chars should exist")
}
func testCreateResidenceWithVeryLongAddress() {
let timestamp = Int(Date().timeIntervalSince1970)
let residenceName = "Long Address Home \(timestamp)"
let success = createResidence(
name: residenceName,
street: "123456789 Very Long Street Name That Goes On And On Boulevard Apartment Complex Unit 42B",
city: "VeryLongCityNameThatTestsTheLimit",
state: "CA",
postal: "12345-6789"
)
XCTAssertTrue(success, "Should handle very long addresses")
let residence = findResidence(name: residenceName)
XCTAssertTrue(residence.waitForExistence(timeout: 10), "Residence with long address should exist")
}
// MARK: - Navigation & List Tests
func testNavigateFromResidencesToOtherTabs() {
// From Residences tab
navigateToResidencesTab()
// Navigate to Tasks
let tasksTab = app.tabBars.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Tasks'")).firstMatch
XCTAssertTrue(tasksTab.exists, "Tasks tab should exist")
tasksTab.tap()
sleep(1)
XCTAssertTrue(tasksTab.isSelected, "Should be on Tasks tab")
// Navigate back to Residences
let residencesTab = app.tabBars.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Residences'")).firstMatch
residencesTab.tap()
sleep(1)
XCTAssertTrue(residencesTab.isSelected, "Should be back on Residences tab")
// Navigate to Contractors
let contractorsTab = app.tabBars.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Contractors'")).firstMatch
XCTAssertTrue(contractorsTab.exists, "Contractors tab should exist")
contractorsTab.tap()
sleep(1)
XCTAssertTrue(contractorsTab.isSelected, "Should be on Contractors tab")
// Back to Residences
residencesTab.tap()
sleep(1)
XCTAssertTrue(residencesTab.isSelected, "Should be back on Residences tab again")
}
func testRefreshResidencesList() {
navigateToResidencesTab()
sleep(2)
// Pull to refresh (if implemented) or use refresh button
let refreshButton = app.navigationBars.buttons.containing(NSPredicate(format: "label CONTAINS 'arrow.clockwise' OR label CONTAINS 'refresh'")).firstMatch
if refreshButton.exists {
refreshButton.tap()
sleep(3)
}
// Verify we're still on residences tab
let residencesTab = app.tabBars.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Residences'")).firstMatch
XCTAssertTrue(residencesTab.isSelected, "Should still be on Residences tab after refresh")
}
func testViewResidenceDetails() {
let timestamp = Int(Date().timeIntervalSince1970)
let residenceName = "Detail View Test \(timestamp)"
// Create residence
guard createResidence(name: residenceName) else {
XCTFail("Failed to create residence")
return
}
navigateToResidencesTab()
sleep(2)
// Tap on residence
let residence = findResidence(name: residenceName)
XCTAssertTrue(residence.exists, "Residence should exist")
residence.tap()
sleep(3)
// Verify detail view appears with edit button or tasks section
let editButton = app.buttons.containing(NSPredicate(format: "label CONTAINS[c] 'Edit'")).firstMatch
let tasksSection = app.staticTexts.containing(NSPredicate(format: "label CONTAINS[c] 'Tasks' OR label CONTAINS[c] 'Maintenance'")).firstMatch
XCTAssertTrue(editButton.exists || tasksSection.exists, "Detail view should show with edit button or tasks section")
}
// MARK: - Data Persistence Tests
func testResidencePersistsAfterBackgroundingApp() {
let timestamp = Int(Date().timeIntervalSince1970)
let residenceName = "Persistence Test \(timestamp)"
// Create residence
guard createResidence(name: residenceName) else {
XCTFail("Failed to create residence")
return
}
navigateToResidencesTab()
sleep(2)
// Verify residence exists
var residence = findResidence(name: residenceName)
XCTAssertTrue(residence.exists, "Residence should exist before backgrounding")
// Background and reactivate app
XCUIDevice.shared.press(.home)
sleep(2)
app.activate()
sleep(3)
// Navigate back to residences
navigateToResidencesTab()
sleep(2)
// Verify residence still exists
residence = findResidence(name: residenceName)
XCTAssertTrue(residence.exists, "Residence should persist after backgrounding app")
}
// MARK: - Performance Tests
func testResidenceListPerformance() {
measure(metrics: [XCTClockMetric(), XCTMemoryMetric()]) {
navigateToResidencesTab()
sleep(2)
}
}
func testResidenceCreationPerformance() {
let timestamp = Int(Date().timeIntervalSince1970)
measure(metrics: [XCTClockMetric()]) {
let residenceName = "Performance Test \(timestamp)_\(UUID().uuidString.prefix(8))"
_ = createResidence(name: residenceName)
}
}
}