mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-04 01:20:12 +00:00
Add UI tests for optional app lock setup. (#2046)
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
// Generated using Sourcery 2.1.1 — https://github.com/krzysztofzablocki/Sourcery
|
||||
// Generated using Sourcery 2.1.2 — https://github.com/krzysztofzablocki/Sourcery
|
||||
// DO NOT EDIT
|
||||
|
||||
// swiftlint:disable all
|
||||
|
||||
@@ -16,8 +16,12 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
struct A11yIdentifiers {
|
||||
enum A11yIdentifiers {
|
||||
static let alertInfo = AlertInfo()
|
||||
static let analyticsPromptScreen = AnalyticsPromptScreen()
|
||||
static let appLockSetupBiometricsScreen = AppLockSetupBiometricsScreen()
|
||||
static let appLockSetupPINScreen = AppLockSetupPINScreen()
|
||||
static let appLockSetupSettingsScreen = AppLockSetupSettingsScreen()
|
||||
static let bugReportScreen = BugReportScreen()
|
||||
static let changeServerScreen = ChangeServer()
|
||||
static let homeScreen = HomeScreen()
|
||||
@@ -41,6 +45,25 @@ struct A11yIdentifiers {
|
||||
static let notificationSettingsScreen = NotificationSettingsScreen()
|
||||
static let notificationSettingsEditScreen = NotificationSettingsEditScreen()
|
||||
static let createPollScreen = CreatePollScreen()
|
||||
|
||||
struct AlertInfo {
|
||||
let primaryButton = "alert_info-primary_button"
|
||||
let secondaryButton = "alert_info-secondary_button"
|
||||
}
|
||||
|
||||
struct AppLockSetupBiometricsScreen {
|
||||
let allow = "app_lock_setup_biometrics-allow"
|
||||
}
|
||||
|
||||
struct AppLockSetupPINScreen {
|
||||
let cancel = "app_lock_setup_pin-cancel"
|
||||
}
|
||||
|
||||
struct AppLockSetupSettingsScreen {
|
||||
let changePIN = "app_lock_setup_settings-change_pin"
|
||||
let removePIN = "app_lock_setup_settings-remove_pin"
|
||||
let removePINAlertYes = "app_lock_setup_settings-remove_pin_alert_yes"
|
||||
}
|
||||
|
||||
struct AnalyticsPromptScreen {
|
||||
let title = "analytics_prompt-title"
|
||||
|
||||
@@ -129,11 +129,13 @@ extension View {
|
||||
Button(item.primaryButton.title, role: item.primaryButton.role) {
|
||||
item.primaryButton.action?()
|
||||
}
|
||||
.accessibilityIdentifier(A11yIdentifiers.alertInfo.primaryButton)
|
||||
|
||||
if let secondaryButton = item.secondaryButton {
|
||||
Button(secondaryButton.title, role: secondaryButton.role) {
|
||||
secondaryButton.action?()
|
||||
}
|
||||
.accessibilityIdentifier(A11yIdentifiers.alertInfo.secondaryButton)
|
||||
}
|
||||
} message: { item in
|
||||
if let message = item.message {
|
||||
|
||||
+2
@@ -49,6 +49,7 @@ struct AppLockSetupBiometricsScreen: View {
|
||||
.font(.system(size: 72))
|
||||
.padding(.top, 58)
|
||||
.padding(.bottom, 26)
|
||||
.accessibilityHidden(true)
|
||||
|
||||
Text(context.viewState.title)
|
||||
.font(.compound.headingMDBold)
|
||||
@@ -66,6 +67,7 @@ struct AppLockSetupBiometricsScreen: View {
|
||||
VStack(spacing: 16) {
|
||||
Button(context.viewState.title) { context.send(viewAction: .allow) }
|
||||
.buttonStyle(.compound(.primary))
|
||||
.accessibilityIdentifier(A11yIdentifiers.appLockSetupBiometricsScreen.allow)
|
||||
|
||||
Button { context.send(viewAction: .skip) } label: {
|
||||
Text(L10n.screenAppLockSetupBiometricUnlockSkip)
|
||||
|
||||
+1
@@ -83,6 +83,7 @@ struct AppLockSetupPINScreen: View {
|
||||
Button(L10n.actionCancel) {
|
||||
context.send(viewAction: .cancel)
|
||||
}
|
||||
.accessibilityIdentifier(A11yIdentifiers.appLockSetupPINScreen.cancel)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -25,10 +25,12 @@ struct AppLockSetupSettingsScreen: View {
|
||||
Section {
|
||||
ListRow(label: .plain(title: L10n.screenAppLockSettingsChangePin),
|
||||
kind: .button { context.send(viewAction: .changePINCode) })
|
||||
.accessibilityIdentifier(A11yIdentifiers.appLockSetupSettingsScreen.changePIN)
|
||||
|
||||
if !context.viewState.isMandatory {
|
||||
ListRow(label: .plain(title: L10n.screenAppLockSettingsRemovePin, role: .destructive),
|
||||
kind: .button { context.send(viewAction: .disable) })
|
||||
.accessibilityIdentifier(A11yIdentifiers.appLockSetupSettingsScreen.removePIN)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
//
|
||||
// Copyright 2023 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import LocalAuthentication
|
||||
|
||||
/// A customised context that allows injecting a few mock values but otherwise behaves as expected.
|
||||
/// It works as the actual context does and won't update the return values of `biometryType` and
|
||||
/// `evaluatedPolicyDomainStateValue` until either `canEvaluatePolicy` or
|
||||
/// `evaluatePolicy` have been called.
|
||||
class LAContextMock: LAContext {
|
||||
var biometryTypeValue: LABiometryType!
|
||||
private var internalBiometryTypeValue: LABiometryType!
|
||||
override var biometryType: LABiometryType { internalBiometryTypeValue }
|
||||
|
||||
var evaluatedPolicyDomainStateValue: Data?
|
||||
private var internalEvaluatedPolicyDomainStateValue: Data?
|
||||
override var evaluatedPolicyDomainState: Data? { internalEvaluatedPolicyDomainStateValue }
|
||||
|
||||
override func canEvaluatePolicy(_ policy: LAPolicy, error: NSErrorPointer) -> Bool {
|
||||
let result = super.canEvaluatePolicy(policy, error: error)
|
||||
updateInternalValues()
|
||||
return result
|
||||
}
|
||||
|
||||
var evaluatePolicyReturnValue: Bool!
|
||||
override func evaluatePolicy(_ policy: LAPolicy, localizedReason: String) async throws -> Bool {
|
||||
updateInternalValues()
|
||||
return evaluatePolicyReturnValue
|
||||
}
|
||||
|
||||
private func updateInternalValues() {
|
||||
internalBiometryTypeValue = biometryTypeValue
|
||||
internalEvaluatedPolicyDomainStateValue = evaluatedPolicyDomainStateValue
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
//
|
||||
// Copyright 2023 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
class BlankFormCoordinator: CoordinatorProtocol {
|
||||
func toPresentable() -> AnyView {
|
||||
AnyView(BlankForm())
|
||||
}
|
||||
}
|
||||
|
||||
/// An empty Form used for UI tests, behind a sheet.
|
||||
private struct BlankForm: View {
|
||||
var body: some View {
|
||||
Form {
|
||||
Text("Nothing to see here.")
|
||||
}
|
||||
.compoundForm()
|
||||
}
|
||||
}
|
||||
|
||||
struct BlankForm_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
BlankForm()
|
||||
}
|
||||
}
|
||||
@@ -42,12 +42,12 @@ class UITestsAppCoordinator: AppCoordinatorProtocol {
|
||||
}
|
||||
|
||||
func start() {
|
||||
// disabling CA animations
|
||||
// Fix the app tint colour.
|
||||
UIApplication.shared.connectedScenes.forEach { scene in
|
||||
guard let delegate = scene.delegate as? UIWindowSceneDelegate else {
|
||||
return
|
||||
}
|
||||
delegate.window??.layer.speed = 0
|
||||
delegate.window??.tintColor = .compound.textActionPrimary
|
||||
}
|
||||
|
||||
guard let screenID = ProcessInfo.testScreenID else { fatalError("Unable to launch with unknown screen.") }
|
||||
@@ -162,15 +162,36 @@ class MockScreen: Identifiable {
|
||||
let appLockService = AppLockService(keychainController: KeychainControllerMock(), appSettings: ServiceLocator.shared.settings)
|
||||
let coordinator = AppLockScreenCoordinator(parameters: .init(appLockService: appLockService))
|
||||
return coordinator
|
||||
case .appLockSetupFlow:
|
||||
case .appLockSetupFlow, .appLockSetupFlowUnlock:
|
||||
let navigationStackCoordinator = NavigationStackCoordinator()
|
||||
let keychainController = KeychainControllerMock()
|
||||
keychainController.containsPINCodeReturnValue = false
|
||||
let appLockService = AppLockService(keychainController: keychainController, appSettings: ServiceLocator.shared.settings)
|
||||
// The flow expects an existing root coordinator, use the placeholder as a placeholder 😅
|
||||
navigationStackCoordinator.setRootCoordinator(BlankFormCoordinator())
|
||||
|
||||
let keychainController = KeychainController(service: .tests, accessGroup: InfoPlistReader.main.keychainAccessGroupIdentifier)
|
||||
keychainController.resetSecrets()
|
||||
if id == .appLockSetupFlowUnlock {
|
||||
do {
|
||||
try keychainController.setPINCode("2023")
|
||||
} catch {
|
||||
fatalError("Failed to pre-set the PIN code")
|
||||
}
|
||||
}
|
||||
|
||||
let context = LAContextMock()
|
||||
context.biometryTypeValue = UIDevice.current.isPhone ? .faceID : .touchID // (iPhone 14 & iPad 9th gen)
|
||||
context.evaluatePolicyReturnValue = true
|
||||
context.evaluatedPolicyDomainStateValue = "😎".data(using: .utf8)
|
||||
|
||||
let appLockService = AppLockService(keychainController: keychainController,
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
context: context)
|
||||
|
||||
let coordinator = AppLockSetupFlowCoordinator(presentingFlow: .settings,
|
||||
appLockService: appLockService,
|
||||
navigationStackCoordinator: navigationStackCoordinator)
|
||||
coordinator.start()
|
||||
retainedState.append(coordinator)
|
||||
|
||||
return navigationStackCoordinator
|
||||
case .home:
|
||||
let navigationStackCoordinator = NavigationStackCoordinator()
|
||||
|
||||
@@ -31,6 +31,7 @@ enum UITestsScreenIdentifier: String {
|
||||
case templateScreen
|
||||
case appLockScreen
|
||||
case appLockSetupFlow
|
||||
case appLockSetupFlowUnlock
|
||||
case home
|
||||
case settings
|
||||
case bugReport
|
||||
|
||||
Reference in New Issue
Block a user