- Create unit tests: DataLayerTests (27 tests for DATA-001–007), DataManagerExtendedTests (20 tests for TASK-005, TASK-012, TCOMP-003, THEME-001, QA-002), plus ValidationHelpers, TaskMetrics, StringExtensions, DoubleExtensions, DateUtils, DocumentHelpers, ErrorMessageParser - Create UI tests: AuthenticationTests, PasswordResetTests, OnboardingTests, TaskIntegration, ContractorIntegration, ResidenceIntegration, DocumentIntegration, DataLayer, Stability - Add UI test framework: AuthenticatedTestCase, ScreenObjects, TestFlows, TestAccountManager, TestAccountAPIClient, TestDataCleaner, TestDataSeeder - Add accessibility identifiers to password reset views for UI test support - Add greenfield test plan CSVs and update automated column for 27 test IDs - All 297 unit tests pass across 60 suites Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
131 lines
4.6 KiB
Swift
131 lines
4.6 KiB
Swift
import Foundation
|
|
import XCTest
|
|
|
|
/// Tracks and cleans up resources created during integration tests.
|
|
///
|
|
/// Usage:
|
|
/// ```
|
|
/// let cleaner = TestDataCleaner(token: session.token)
|
|
/// let residence = TestDataSeeder.createResidence(token: session.token)
|
|
/// cleaner.trackResidence(residence.id)
|
|
/// // ... test runs ...
|
|
/// cleaner.cleanAll() // called in tearDown
|
|
/// ```
|
|
class TestDataCleaner {
|
|
private let token: String
|
|
private var residenceIds: [Int] = []
|
|
private var taskIds: [Int] = []
|
|
private var contractorIds: [Int] = []
|
|
private var documentIds: [Int] = []
|
|
|
|
init(token: String) {
|
|
self.token = token
|
|
}
|
|
|
|
// MARK: - Track Resources
|
|
|
|
func trackResidence(_ id: Int) {
|
|
residenceIds.append(id)
|
|
}
|
|
|
|
func trackTask(_ id: Int) {
|
|
taskIds.append(id)
|
|
}
|
|
|
|
func trackContractor(_ id: Int) {
|
|
contractorIds.append(id)
|
|
}
|
|
|
|
func trackDocument(_ id: Int) {
|
|
documentIds.append(id)
|
|
}
|
|
|
|
// MARK: - Seed + Track (Convenience)
|
|
|
|
/// Create a residence and automatically track it for cleanup.
|
|
@discardableResult
|
|
func seedResidence(name: String? = nil) -> TestResidence {
|
|
let residence = TestDataSeeder.createResidence(token: token, name: name)
|
|
trackResidence(residence.id)
|
|
return residence
|
|
}
|
|
|
|
/// Create a task and automatically track it for cleanup.
|
|
@discardableResult
|
|
func seedTask(residenceId: Int, title: String? = nil, fields: [String: Any] = [:]) -> TestTask {
|
|
let task = TestDataSeeder.createTask(token: token, residenceId: residenceId, title: title, fields: fields)
|
|
trackTask(task.id)
|
|
return task
|
|
}
|
|
|
|
/// Create a contractor and automatically track it for cleanup.
|
|
@discardableResult
|
|
func seedContractor(name: String? = nil, fields: [String: Any] = [:]) -> TestContractor {
|
|
let contractor = TestDataSeeder.createContractor(token: token, name: name, fields: fields)
|
|
trackContractor(contractor.id)
|
|
return contractor
|
|
}
|
|
|
|
/// Create a document and automatically track it for cleanup.
|
|
@discardableResult
|
|
func seedDocument(residenceId: Int, title: String? = nil, documentType: String = "Other") -> TestDocument {
|
|
let document = TestDataSeeder.createDocument(token: token, residenceId: residenceId, title: title, documentType: documentType)
|
|
trackDocument(document.id)
|
|
return document
|
|
}
|
|
|
|
/// Create a residence with tasks, all tracked for cleanup.
|
|
func seedResidenceWithTasks(residenceName: String? = nil, taskCount: Int = 3) -> (residence: TestResidence, tasks: [TestTask]) {
|
|
let result = TestDataSeeder.createResidenceWithTasks(token: token, residenceName: residenceName, taskCount: taskCount)
|
|
trackResidence(result.residence.id)
|
|
result.tasks.forEach { trackTask($0.id) }
|
|
return result
|
|
}
|
|
|
|
/// Create a full residence with task, contractor, and document, all tracked.
|
|
func seedFullResidence() -> (residence: TestResidence, task: TestTask, contractor: TestContractor, document: TestDocument) {
|
|
let result = TestDataSeeder.createFullResidence(token: token)
|
|
trackResidence(result.residence.id)
|
|
trackTask(result.task.id)
|
|
trackContractor(result.contractor.id)
|
|
trackDocument(result.document.id)
|
|
return result
|
|
}
|
|
|
|
// MARK: - Cleanup
|
|
|
|
/// Delete all tracked resources in reverse dependency order.
|
|
/// Documents and tasks first (they depend on residences), then contractors, then residences.
|
|
/// Failures are logged but don't fail the test — cleanup is best-effort.
|
|
func cleanAll() {
|
|
// Delete documents first (depend on residences)
|
|
for id in documentIds.reversed() {
|
|
_ = TestAccountAPIClient.deleteDocument(token: token, id: id)
|
|
}
|
|
documentIds.removeAll()
|
|
|
|
// Delete tasks (depend on residences)
|
|
for id in taskIds.reversed() {
|
|
_ = TestAccountAPIClient.deleteTask(token: token, id: id)
|
|
}
|
|
taskIds.removeAll()
|
|
|
|
// Delete contractors (independent, but clean before residences)
|
|
for id in contractorIds.reversed() {
|
|
_ = TestAccountAPIClient.deleteContractor(token: token, id: id)
|
|
}
|
|
contractorIds.removeAll()
|
|
|
|
// Delete residences last
|
|
for id in residenceIds.reversed() {
|
|
_ = TestAccountAPIClient.deleteResidence(token: token, id: id)
|
|
}
|
|
residenceIds.removeAll()
|
|
}
|
|
|
|
/// Number of tracked resources (for debugging).
|
|
var trackedCount: Int {
|
|
residenceIds.count + taskIds.count + contractorIds.count + documentIds.count
|
|
}
|
|
}
|