feat: complete Tasks 21-28 — backend integration, security hardening, UI tests & CI

- 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
This commit is contained in:
2026-06-02 15:01:38 -04:00
parent ab0d4857db
commit e33ddf3002
49 changed files with 10472 additions and 421 deletions

View File

@@ -7,6 +7,8 @@
import XCTest
/// Launch tests that verify the app starts correctly on different device configurations.
/// These tests run for each target application configuration (e.g., iPhone and iPad).
final class KordantUITestsLaunchTests: XCTestCase {
override class var runsForEachTargetApplicationUIConfiguration: Bool {
@@ -17,19 +19,70 @@ final class KordantUITestsLaunchTests: XCTestCase {
continueAfterFailure = false
}
@MainActor
/// Verify the app launches and captures the initial screenshot.
/// This is useful for device farm screenshot verification.
func testLaunch() throws {
let app = XCUIApplication()
app.launchArguments = ["-UITesting"]
app.launchEnvironment["UITestScenario"] = UITestScenario.authenticated.rawValue
app.launch()
// Insert steps here to perform after app launch but before taking a screenshot,
// such as logging into a test account or navigating somewhere in the app
// XCUIAutomation Documentation
// https://developer.apple.com/documentation/xcuiautomation
// Allow the app to settle and load initial data
let dashboardTab = app.tabBars.buttons["Dashboard"]
let authScreen = app.staticTexts["Kordant"]
let attachment = XCTAttachment(screenshot: app.screenshot())
// Wait for either auth screen or dashboard to appear
let appeared = XCTWaiter.wait(for: [
XCTNSPredicateExpectation(
predicate: NSPredicate(format: "exists == true"),
object: dashboardTab
),
XCTNSPredicateExpectation(
predicate: NSPredicate(format: "exists == true"),
object: authScreen
)
], timeout: 10)
XCTAssertEqual(app.state, .runningForeground, "App should be running in foreground")
XCTAssertNotEqual(app.state, .unknown, "App state should be known")
// Capture launch screenshot for App Store Connect previews
let screenshot = app.screenshot()
let attachment = XCTAttachment(screenshot: screenshot)
attachment.name = "Launch Screen"
attachment.lifetime = .keepAlways
add(attachment)
}
/// Verify the app launches in unauthenticated mode and shows auth UI
func testLaunchUnauthenticated() throws {
let app = XCUIApplication()
app.launchArguments = ["-UITesting"]
app.launchEnvironment["UITestScenario"] = UITestScenario.unauthenticated.rawValue
app.launch()
// Verify auth UI appears
let brandName = app.staticTexts["Kordant"]
XCTAssertTrue(brandName.waitForExistence(timeout: 5), "Auth screen should show Kordant branding")
// Verify the app is responsive
let googleButton = app.buttons["Continue with Google"]
XCTAssertTrue(googleButton.waitForExistence(timeout: 3), "Google sign-in button should exist")
}
/// Verify the app launches in authenticated mode and shows the main interface
func testLaunchAuthenticated() throws {
let app = XCUIApplication()
app.launchArguments = ["-UITesting"]
app.launchEnvironment["UITestScenario"] = UITestScenario.authenticated.rawValue
app.launch()
// Verify main UI appears
let dashboardNav = app.navigationBars["Dashboard"]
XCTAssertTrue(dashboardNav.waitForExistence(timeout: 5), "Dashboard should appear for authenticated user")
// Tab bar should be visible
XCTAssertTrue(app.tabBars.buttons["Dashboard"].exists, "Dashboard tab should exist")
XCTAssertTrue(app.tabBars.buttons["Settings"].exists, "Settings tab should exist")
}
}