Kotlin/KMM: - Update Contractor model with optional residenceId and specialties array - Rename averageRating to rating, update address field names - Add ContractorMinimal model for task references - Add residence picker and multi-select specialty chips to AddContractorDialog - Fix ContractorsScreen and ContractorDetailScreen field references iOS: - Rewrite ContractorFormSheet with residence and specialty pickers - Update ContractorDetailView with FlowLayout for specialties - Add FlowLayout component for wrapping badge layouts - Fix ContractorCard and CompleteTaskView field references - Update ContractorFormState with residence/specialty selection 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
676 lines
24 KiB
Swift
676 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
|
|
///
|
|
/// Test Order (least to most complex):
|
|
/// 1. Error/incomplete data tests
|
|
/// 2. Creation tests
|
|
/// 3. Edit/update tests
|
|
/// 4. Delete/remove tests (none currently)
|
|
/// 5. Navigation/view tests
|
|
/// 6. Performance tests
|
|
final class Suite4_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: - 1. Error/Validation Tests
|
|
|
|
func test01_cannotCreateResidenceWithEmptyName() {
|
|
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 test02_cancelResidenceCreation() {
|
|
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: - 2. Creation Tests
|
|
|
|
func test03_createResidenceWithMinimalData() {
|
|
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 test04_createResidenceWithAllPropertyTypes() {
|
|
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 test05_createMultipleResidencesInSequence() {
|
|
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")
|
|
}
|
|
}
|
|
|
|
func test06_createResidenceWithVeryLongName() {
|
|
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 test07_createResidenceWithSpecialCharacters() {
|
|
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 test08_createResidenceWithEmojis() {
|
|
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 test09_createResidenceWithInternationalCharacters() {
|
|
let timestamp = Int(Date().timeIntervalSince1970)
|
|
let internationalName = "Chateau Montreal \(timestamp)"
|
|
|
|
let success = createResidence(name: internationalName)
|
|
XCTAssertTrue(success, "Should handle international characters")
|
|
|
|
let residence = findResidence(name: "Chateau")
|
|
XCTAssertTrue(residence.waitForExistence(timeout: 10), "Residence with international chars should exist")
|
|
}
|
|
|
|
func test10_createResidenceWithVeryLongAddress() {
|
|
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: - 3. Edit/Update Tests
|
|
|
|
func test11_editResidenceName() {
|
|
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 test12_updateAllResidenceFields() {
|
|
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: - 4. View/Navigation Tests
|
|
|
|
func test13_viewResidenceDetails() {
|
|
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")
|
|
}
|
|
|
|
func test14_navigateFromResidencesToOtherTabs() {
|
|
// 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 test15_refreshResidencesList() {
|
|
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")
|
|
}
|
|
|
|
// MARK: - 5. Persistence Tests
|
|
|
|
func test16_residencePersistsAfterBackgroundingApp() {
|
|
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: - 6. Performance Tests
|
|
|
|
func test17_residenceListPerformance() {
|
|
measure(metrics: [XCTClockMetric(), XCTMemoryMetric()]) {
|
|
navigateToResidencesTab()
|
|
sleep(2)
|
|
}
|
|
}
|
|
|
|
func test18_residenceCreationPerformance() {
|
|
let timestamp = Int(Date().timeIntervalSince1970)
|
|
|
|
measure(metrics: [XCTClockMetric()]) {
|
|
let residenceName = "Performance Test \(timestamp)_\(UUID().uuidString.prefix(8))"
|
|
_ = createResidence(name: residenceName)
|
|
}
|
|
}
|
|
}
|