// // Copyright 2022-2024 New Vector Ltd. // // SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial // Please see LICENSE files in the repository root for full details. // import XCTest @MainActor class AppLockSetupUITests: XCTestCase { var app: XCUIApplication! @MainActor enum Step { static let createPIN = 0 static let confirmPIN = 1 static let setupBiometrics = 2 static let settings = 3 /// iPad shows the settings screen behind the modal, iPhone doesn't. static let changePIN = isPhone ? createPIN : 4 /// iPad shows the settings screen behind the modal, iPhone doesn't. static let confirmChangePIN = isPhone ? confirmPIN : 5 /// Not part of the flow, only to verify the stack is cleared. static let clearedStack = 99 static var isPhone: Bool { UIDevice.current.userInterfaceIdiom == .phone } } func testCreateFlow() async throws { app = Application.launch(.appLockSetupFlow) // Wait for the keyboard to push the sheet up before snapshotting try await Task.sleep(for: .seconds(0.5)) // Create PIN screen. try await app.assertScreenshot(step: Step.createPIN) enterPIN() // Confirm PIN screen. try await app.assertScreenshot(step: Step.confirmPIN) enterPIN() // Setup biometrics screen. try await app.assertScreenshot(step: Step.setupBiometrics) app.buttons[A11yIdentifiers.appLockSetupBiometricsScreen.allow].tap() // Settings screen. try await app.assertScreenshot(step: Step.settings) app.buttons[A11yIdentifiers.appLockSetupSettingsScreen.changePIN].tap() // Change PIN (create). try await app.assertScreenshot(step: Step.changePIN) enterDifferentPIN() // Change PIN (confirm). try await app.assertScreenshot(step: Step.confirmChangePIN) enterDifferentPIN() // Settings screen. try await app.assertScreenshot(step: Step.settings) app.buttons[A11yIdentifiers.appLockSetupSettingsScreen.removePIN].tap() app.alerts.element.buttons[A11yIdentifiers.alertInfo.primaryButton].tap() // Pop the stack returning to whatever was last presented. try await app.assertScreenshot(step: Step.clearedStack) } func testMandatoryCreateFlow() async throws { app = Application.launch(.appLockSetupFlowMandatory) // Create PIN screen (non-modal and no cancellation button). try await app.assertScreenshot(step: Step.createPIN) enterPIN() // Confirm PIN screen (non-modal and no cancellation button). try await app.assertScreenshot(step: Step.confirmPIN) enterPIN() // Setup biometrics screen (non-modal). try await app.assertScreenshot(step: Step.setupBiometrics) let allowButton = app.buttons[A11yIdentifiers.appLockSetupBiometricsScreen.allow] XCTAssertTrue(allowButton.exists, "The biometrics screen should be shown.") allowButton.tap() // The stack should remain on biometrics for the presenting flow to take over navigation. try await app.assertScreenshot(step: Step.setupBiometrics) } func testUnlockFlow() async throws { app = Application.launch(.appLockSetupFlowUnlock) // Create PIN screen. try await app.assertScreenshot() enterPIN() // Settings screen. try await app.assertScreenshot(step: Step.settings) app.buttons[A11yIdentifiers.appLockSetupSettingsScreen.removePIN].tap() app.alerts.element.buttons[A11yIdentifiers.alertInfo.primaryButton].tap() // Pop the stack returning to whatever was last presented. try await app.assertScreenshot(step: Step.clearedStack) } func testCancel() async throws { app = Application.launch(.appLockSetupFlowUnlock) app.showKeyboardIfNeeded() // The secure text field is focussed automatically // Create PIN screen. try await app.assertScreenshot() app.buttons[A11yIdentifiers.appLockSetupPINScreen.cancel].tap() // Return to whatever was last presented. try await app.assertScreenshot(step: Step.clearedStack) } // MARK: - Helpers private func enterPIN() { let textField = app.secureTextFields[A11yIdentifiers.appLockSetupPINScreen.textField] XCTAssert(textField.waitForExistence(timeout: 10)) textField.clearAndTypeText("2023", app: app) } private func enterDifferentPIN() { let textField = app.secureTextFields[A11yIdentifiers.appLockSetupPINScreen.textField] XCTAssert(textField.waitForExistence(timeout: 10)) textField.clearAndTypeText("2233", app: app) } }