- Add Apple Sign-In backend (JWKS verification, account linking, session management) - Implement push notification deep linking with NotificationDeepLinkRouter - Add jailbreak detection, runtime integrity monitoring, secure enclave service - Implement OAuth social login, token refresh, and secure logout flows - Add image caching (memory/disk), optimizer, upload queue, async semaphore - Implement notification analytics, type preferences, and category setup - Expand UI test suite with UITestBase, accessibility, auth flow, performance tests - Add CI pipeline for iOS UI tests (3 device sizes) and performance benchmarks - Restructure Xcode project to manual groups with KordantWidgets target - Add SwiftLint, Swift Collections/Algorithms/GoogleSignIn dependencies - Update project.yml for XcodeGen with new targets and configurations
188 lines
6.7 KiB
Swift
188 lines
6.7 KiB
Swift
import XCTest
|
|
|
|
/// UI tests for service screens: DarkWatch, VoicePrint, SpamShield, HomeTitle, RemoveBrokers.
|
|
final class ServiceUITests: UITestBase {
|
|
|
|
// MARK: - Helpers
|
|
|
|
/// Relaunch the app with a specific scenario
|
|
private func relaunch(scenario: UITestScenario) {
|
|
app.terminate()
|
|
app = XCUIApplication()
|
|
app.launchArguments = ["-UITesting"]
|
|
app.launchEnvironment["UITestScenario"] = scenario.rawValue
|
|
app.launch()
|
|
}
|
|
|
|
/// Navigate to the Services tab
|
|
private func navigateToServicesList() {
|
|
navigateToTab(.services)
|
|
XCTAssertTrue(app.navigationBars["Services"].waitForExistence(timeout: 5),
|
|
"Services list should load")
|
|
}
|
|
|
|
/// Tap a service row by its name in the services list
|
|
private func tapService(_ name: String) {
|
|
app.buttons[name].tap()
|
|
}
|
|
|
|
// MARK: - DarkWatch
|
|
|
|
/// Verify DarkWatch screen loads with watchlist items
|
|
func testDarkWatchWatchlistLoads() {
|
|
relaunch(scenario: .darkWatchPopulated)
|
|
navigateToServicesList()
|
|
tapService("DarkWatch")
|
|
|
|
XCTAssertTrue(app.navigationBars["DarkWatch"].waitForExistence(timeout: 5),
|
|
"DarkWatch screen should load")
|
|
|
|
// Verify watchlist items are shown
|
|
let watchlistItem = app.staticTexts["test@kordant.com"]
|
|
XCTAssertTrue(watchlistItem.waitForExistence(timeout: 3),
|
|
"Watchlist item should be visible")
|
|
captureScreen(name: "DarkWatchWatchlist")
|
|
}
|
|
|
|
/// Verify adding a watchlist item opens the add sheet
|
|
func testDarkWatchAddWatchlistItem() {
|
|
relaunch(scenario: .darkWatchPopulated)
|
|
navigateToServicesList()
|
|
tapService("DarkWatch")
|
|
XCTAssertTrue(app.navigationBars["DarkWatch"].waitForExistence(timeout: 5))
|
|
|
|
// Tap the add button in the toolbar
|
|
let addButton = app.navigationBars["DarkWatch"].buttons.firstMatch
|
|
guard addButton.waitForExistence(timeout: 3) else {
|
|
XCTFail("Add button in DarkWatch navigation bar not found")
|
|
return
|
|
}
|
|
addButton.tap()
|
|
|
|
// Wait for add sheet to appear and fill in the form
|
|
let termField = app.textFields.firstMatch
|
|
guard termField.waitForExistence(timeout: 3) else {
|
|
XCTFail("Term field in add sheet not found")
|
|
return
|
|
}
|
|
termField.tap()
|
|
termField.typeText("new-item@test.com")
|
|
|
|
// Tap the Add confirmation button
|
|
let confirmAdd = app.buttons["Add"]
|
|
if confirmAdd.waitForExistence(timeout: 2) {
|
|
confirmAdd.tap()
|
|
}
|
|
|
|
// Verify sheet dismisses
|
|
let sheetGone = termField.waitForExistence(timeout: 2) == false
|
|
XCTAssertTrue(sheetGone, "Sheet should dismiss after adding item")
|
|
captureScreen(name: "DarkWatchAddItem")
|
|
}
|
|
|
|
// MARK: - VoicePrint
|
|
|
|
/// Verify VoicePrint screen loads with enrollment information
|
|
func testVoicePrintEnrollmentScreen() {
|
|
relaunch(scenario: .voicePrintPopulated)
|
|
navigateToServicesList()
|
|
tapService("VoicePrint")
|
|
|
|
XCTAssertTrue(app.navigationBars["VoicePrint"].waitForExistence(timeout: 5),
|
|
"VoicePrint screen should load")
|
|
|
|
// Verify enrollment section is shown
|
|
let enrollmentSection = app.staticTexts["Voice Enrollments"]
|
|
XCTAssertTrue(enrollmentSection.waitForExistence(timeout: 3),
|
|
"Voice Enrollments section should be visible")
|
|
captureScreen(name: "VoicePrintEnrollments")
|
|
}
|
|
|
|
// MARK: - SpamShield
|
|
|
|
/// Verify SpamShield screen loads with rules
|
|
func testSpamShieldRulesList() {
|
|
relaunch(scenario: .spamShieldPopulated)
|
|
navigateToServicesList()
|
|
tapService("SpamShield")
|
|
|
|
XCTAssertTrue(app.navigationBars["SpamShield"].waitForExistence(timeout: 5),
|
|
"SpamShield screen should load")
|
|
|
|
// Verify rules are visible
|
|
let rulePattern = app.staticTexts["+1 (555) 999-9999"]
|
|
XCTAssertTrue(rulePattern.waitForExistence(timeout: 3),
|
|
"Spam rule pattern should be visible")
|
|
captureScreen(name: "SpamShieldRules")
|
|
}
|
|
|
|
// MARK: - HomeTitle
|
|
|
|
/// Verify HomeTitle screen loads with property list
|
|
func testHomeTitlePropertyList() {
|
|
relaunch(scenario: .homeTitlePopulated)
|
|
navigateToServicesList()
|
|
tapService("HomeTitle")
|
|
|
|
XCTAssertTrue(app.navigationBars["HomeTitle"].waitForExistence(timeout: 5),
|
|
"HomeTitle screen should load")
|
|
|
|
// Verify properties are visible
|
|
let propertyAddress = app.staticTexts["123 Main St"]
|
|
XCTAssertTrue(propertyAddress.waitForExistence(timeout: 3),
|
|
"Property address should be visible")
|
|
captureScreen(name: "HomeTitleProperties")
|
|
}
|
|
|
|
// MARK: - RemoveBrokers
|
|
|
|
/// Verify RemoveBrokers screen loads with broker listings and removal requests
|
|
func testRemoveBrokersListingsShown() {
|
|
relaunch(scenario: .removeBrokersPopulated)
|
|
navigateToServicesList()
|
|
tapService("Remove Brokers")
|
|
|
|
XCTAssertTrue(app.navigationBars["Remove Brokers"].waitForExistence(timeout: 5),
|
|
"Remove Brokers screen should load")
|
|
|
|
// Verify broker registry section
|
|
let brokerSection = app.staticTexts["Broker Registry"]
|
|
XCTAssertTrue(brokerSection.waitForExistence(timeout: 3),
|
|
"Broker Registry section should be visible")
|
|
|
|
// Verify broker names are shown
|
|
let brokerName = app.staticTexts["DataAggregator Inc"]
|
|
XCTAssertTrue(brokerName.waitForExistence(timeout: 3),
|
|
"Broker listing should be visible")
|
|
captureScreen(name: "RemoveBrokersListings")
|
|
}
|
|
|
|
// MARK: - Back Navigation
|
|
|
|
/// Verify back navigation works from a service to the services list
|
|
func testBackNavigationFromService() {
|
|
navigateToServicesList()
|
|
tapService("DarkWatch")
|
|
XCTAssertTrue(app.navigationBars["DarkWatch"].waitForExistence(timeout: 5))
|
|
|
|
// Tap back button
|
|
app.navigationBars["DarkWatch"].buttons["Services"].tap()
|
|
XCTAssertTrue(app.navigationBars["Services"].waitForExistence(timeout: 3),
|
|
"Should navigate back to Services list")
|
|
}
|
|
|
|
// MARK: - Service Row Accessibility
|
|
|
|
/// Verify service rows exist and are tappable
|
|
func testAllServiceRowsAreVisible() {
|
|
navigateToServicesList()
|
|
|
|
let serviceNames = ["DarkWatch", "VoicePrint", "SpamShield", "HomeTitle", "Remove Brokers"]
|
|
for name in serviceNames {
|
|
let row = app.buttons[name]
|
|
XCTAssertTrue(row.waitForExistence(timeout: 3),
|
|
"Service row '\(name)' should be visible")
|
|
}
|
|
}
|
|
}
|