mirror of
https://github.com/luxfi/safe-ios.git
synced 2026-10-30 15:19:20 +00:00
327 lines
13 KiB
Swift
327 lines
13 KiB
Swift
//
|
|
// CreatePasscodeFlowTests.swift
|
|
// MultisigIntegrationTests
|
|
//
|
|
// Created by Dmitry Bespalov on 08.07.22.
|
|
// Copyright © 2022 Gnosis Ltd. All rights reserved.
|
|
//
|
|
|
|
import XCTest
|
|
@testable import Multisig
|
|
|
|
class CreatePasscodeFlowTests: UIIntegrationTestCase {
|
|
var keychainService: SecureStore!
|
|
let animationDuration: TimeInterval = 1
|
|
|
|
override func setUpWithError() throws {
|
|
try super.setUpWithError()
|
|
continueAfterFailure = false
|
|
keychainService = KeychainService(identifier: "KeychainIntegrationTest \(name)")
|
|
App.shared.keychainService = keychainService
|
|
}
|
|
|
|
// Given passcode is not set,
|
|
// when create passcode flow starts modally,
|
|
// then it shows 'create passcode' screen
|
|
func test_showsCreatePasscode_modal() {
|
|
// 1. Given passcode is not set
|
|
XCTAssertEqual(App.shared.auth.isPasscodeSetAndAvailable, false, "passcode must be not set")
|
|
|
|
// 2. when create passcode flow starts,
|
|
_ = startModalCreatePasscodeFlow()
|
|
|
|
// 3. then it shows 'create passcode' screen
|
|
let topScreen = topPresentedController()
|
|
XCTAssertTrue(topScreen is CreatePasscodeViewController, "not a create passcode screen")
|
|
}
|
|
|
|
// Given passcode is not set,
|
|
// when create passcode flow starts from existing navigation stack,
|
|
// then it shows 'create passcode' screen
|
|
func test_showsCreatePasscode_push() {
|
|
// 1. Given passcode is not set
|
|
XCTAssertEqual(App.shared.auth.isPasscodeSetAndAvailable, false, "passcode must be not set")
|
|
|
|
// 2. when create passcode flow starts,
|
|
_ = pushCreatePasscodeFlow()
|
|
|
|
// 3. then it shows 'create passcode' screen
|
|
let topScreen = topPresentedController()
|
|
XCTAssertTrue(topScreen is CreatePasscodeViewController, "not a create passcode screen")
|
|
}
|
|
|
|
// Given passcode is set,
|
|
// when create passcode flow starts modally,
|
|
// then it doesn't show anything
|
|
func test_doesntShowCreatePasscode_modal() throws {
|
|
// Given passcode is set,
|
|
try App.shared.auth.createPasscode(plaintextPasscode: "123456")
|
|
XCTAssertEqual(App.shared.auth.isPasscodeSetAndAvailable, true, "passcode must be set")
|
|
|
|
// when create passcode flow starts,
|
|
let _ = startModalCreatePasscodeFlow()
|
|
|
|
// then it doesn't show anything
|
|
XCTAssertNil(presentedController, "expected that nothing is presented")
|
|
}
|
|
|
|
// Given passcode is set,
|
|
// when create passcode flow starts from existing navigation stack,
|
|
// then it doesn't show anything
|
|
func test_doesntShowCreatePasscode_push() throws {
|
|
// Given passcode is set,
|
|
try App.shared.auth.createPasscode(plaintextPasscode: "123456")
|
|
XCTAssertEqual(App.shared.auth.isPasscodeSetAndAvailable, true, "passcode must be set")
|
|
|
|
// when create passcode flow starts,
|
|
_ = pushCreatePasscodeFlow()
|
|
|
|
// then it doesn't show anything
|
|
XCTAssertTrue(pushingNavVC.topViewController === pushingVC, "expected that nothing is presented")
|
|
}
|
|
|
|
// Given passcode is not set
|
|
// and create passcode flow started
|
|
// when user enters a passcode
|
|
// then "repeat passcode" screen shown
|
|
func test_whenEntersPasscode_thenOpensRepeatPasscode() {
|
|
// Given passcode is not set
|
|
XCTAssertEqual(App.shared.auth.isPasscodeSetAndAvailable, false, "passcode must be not set")
|
|
// and create passcode flow starts,
|
|
let flow = startModalCreatePasscodeFlow()
|
|
XCTAssertNotNil(flow) // to silence the 'unused' warning and retain the flow for duration of the test
|
|
|
|
// when user enters a passcode
|
|
guard let passcodeVC = topPresentedController() as? CreatePasscodeViewController else {
|
|
XCTFail()
|
|
return
|
|
}
|
|
passcodeVC.append(text: "123456")
|
|
wait(timeout: animationDuration)
|
|
|
|
// then "repeat passcode" screen shown
|
|
let topScreen = topPresentedController()
|
|
XCTAssertTrue(topScreen is RepeatPasscodeViewController, "not a repeat passcode screen")
|
|
}
|
|
|
|
// Given passcode is not set
|
|
// and create passcode flow started
|
|
// when user skips creating passcode
|
|
// then the flow is closed
|
|
func test_whenSkips_thenFlowClosed() {
|
|
// Given passcode is not set
|
|
XCTAssertEqual(App.shared.auth.isPasscodeSetAndAvailable, false, "passcode must be not set")
|
|
// and create passcode flow starts,
|
|
let flow = startModalCreatePasscodeFlow()
|
|
XCTAssertNotNil(flow) // to silence the 'unused' warning and retain the flow for duration of the test
|
|
|
|
// when user skips creating passcode
|
|
guard let passcodeVC = topPresentedController() as? CreatePasscodeViewController else {
|
|
XCTFail()
|
|
return
|
|
}
|
|
passcodeVC.didTapButton(passcodeVC)
|
|
wait(timeout: animationDuration)
|
|
|
|
// then the flow is closed
|
|
XCTAssertNil(presentedController, "expected that nothing is presented")
|
|
}
|
|
|
|
// Given passcode is not set
|
|
// and create passcode flow started
|
|
// when user swipes down
|
|
// then the flow is closed
|
|
func test_whenSwipesDown_thenFlowClosed() {
|
|
// Given passcode is not set
|
|
XCTAssertEqual(App.shared.auth.isPasscodeSetAndAvailable, false, "passcode must be not set")
|
|
// and create passcode flow starts,
|
|
let flow = startModalCreatePasscodeFlow()
|
|
XCTAssertNotNil(flow) // to silence the 'unused' warning and retain the flow for duration of the test
|
|
|
|
// when user swipes down
|
|
guard let presentationController = presentedController?.presentationController,
|
|
let presentationDelegate = presentationController.delegate else {
|
|
XCTFail()
|
|
return
|
|
}
|
|
presentationDelegate.presentationControllerDidDismiss?(presentationController)
|
|
wait(timeout: animationDuration)
|
|
|
|
// then the flow is closed
|
|
XCTAssertNil(presentedController, "expected that nothing is presented")
|
|
}
|
|
|
|
// Given passcode is not set
|
|
// and create passcode flow started
|
|
// and it is not dismissable on swipe
|
|
// when user swipes down
|
|
// then the create passcode screen still shown
|
|
func test_whenSwipesDownButNotDismissable_thenFlowNotClosed() {
|
|
// Given passcode is not set
|
|
XCTAssertEqual(App.shared.auth.isPasscodeSetAndAvailable, false, "passcode must be not set")
|
|
// and create passcode flow starts,
|
|
let flow = startModalCreatePasscodeFlow(dismissableOnSwipe: false)
|
|
XCTAssertNotNil(flow) // to silence the 'unused' warning and retain the flow for duration of the test
|
|
|
|
// when user swipes down
|
|
guard let presentationController = presentedController?.presentationController,
|
|
let presentationDelegate = presentationController.delegate else {
|
|
XCTFail()
|
|
return
|
|
}
|
|
presentationDelegate.presentationControllerDidDismiss?(presentationController)
|
|
wait(timeout: animationDuration)
|
|
|
|
if let vc = presentedController {
|
|
XCTAssertTrue(vc.isModalInPresentation)
|
|
}
|
|
|
|
// then the screen is still shown
|
|
XCTAssertNotNil(presentedController, "expected that controller is still on screen")
|
|
}
|
|
|
|
// Given repeat passcode is shown
|
|
// when user taps back
|
|
// then goes back to create passcode
|
|
func test_whenBackFromRepeatPasscode_thenShowsCreatePasscode() {
|
|
// Given repeat passcode is shown
|
|
let flow = navigateToRepeatPasscodeScreen()
|
|
XCTAssertNotNil(flow)
|
|
|
|
// when user taps back
|
|
(topPresentedController() as? RepeatPasscodeViewController)?.navigateBack()
|
|
wait(timeout: animationDuration)
|
|
|
|
// then goes back to create passcode
|
|
XCTAssertTrue(topPresentedController() is CreatePasscodeViewController, "expected to find create passcode screen")
|
|
}
|
|
|
|
// Given repeat passcode is shown
|
|
// when user taps skip
|
|
// then flow is closed
|
|
func test_whenSkipsRepeatPasscode_thenClosesFlow() {
|
|
// Given repeat passcode is shown
|
|
let flow = navigateToRepeatPasscodeScreen()
|
|
XCTAssertNotNil(flow)
|
|
|
|
// when user taps skip
|
|
(topPresentedController() as? RepeatPasscodeViewController)?.didTapButton(self)
|
|
wait(timeout: animationDuration)
|
|
|
|
// then flow is closed
|
|
XCTAssertNil(presentedController, "expected that nothing is presented")
|
|
}
|
|
|
|
// Given repeat passcode is shown
|
|
// and device does not supports biometrics
|
|
// when user enters the correct passcode
|
|
// then flow completed with success
|
|
func test_whenRepeatsPasscodeCorrectly_thenSuccessfullyCompletesFlow() {
|
|
// Given repeat passcode is shown
|
|
// and device does not supports biometrics
|
|
App.shared.auth.test_override_isBiometrySupported = false
|
|
|
|
var completionResult: Bool?
|
|
let exp = expectation(description: "Flow completed")
|
|
let flow = navigateToRepeatPasscodeScreen(passcode: "123456") { result in
|
|
completionResult = result
|
|
exp.fulfill()
|
|
}
|
|
XCTAssertNotNil(flow)
|
|
|
|
// when user enters the correct passcode
|
|
(topPresentedController() as? RepeatPasscodeViewController)?.append(text: "123456")
|
|
wait(timeout: animationDuration)
|
|
|
|
_ = XCTWaiter.wait(for: [exp], timeout: animationDuration)
|
|
|
|
// then flow completed with success
|
|
XCTAssertEqual(completionResult, true, "Flow not completed successfully")
|
|
}
|
|
|
|
// Given repeat passcode is shown
|
|
// and device supports biometrics
|
|
// when user enters the correct passcode
|
|
// then biometry prompt is shown
|
|
func test_whenRepeatsPasscodeCorrectlyAndBiometricsSupported_thenShowsBiometryPrompt() {
|
|
// Given repeat passcode is shown
|
|
// and device supports biometrics
|
|
App.shared.auth.test_override_isBiometrySupported = true
|
|
let flow = navigateToRepeatPasscodeScreen(passcode: "123456")
|
|
XCTAssertNotNil(flow)
|
|
|
|
// when user enters the correct passcode
|
|
(topPresentedController() as? RepeatPasscodeViewController)?.append(text: "123456")
|
|
wait(timeout: animationDuration)
|
|
|
|
// then biometry prompt is shown
|
|
XCTAssertTrue(topPresentedController().presentedViewController is UIAlertController)
|
|
}
|
|
|
|
// Given biometry prompt is shown
|
|
// when cancels
|
|
// then flow completed with success
|
|
func test_whenBiometryCancelled_thenClosesWithSuccess() {
|
|
// Given biometry prompt is shown
|
|
App.shared.auth.test_override_isBiometrySupported = true
|
|
var completionResult: Bool?
|
|
let exp = expectation(description: "Flow completed")
|
|
let flow = navigateToRepeatPasscodeScreen(passcode: "123456") { result in
|
|
completionResult = result
|
|
exp.fulfill()
|
|
}
|
|
XCTAssertNotNil(flow)
|
|
(topPresentedController() as? RepeatPasscodeViewController)?.append(text: "123456")
|
|
wait(timeout: animationDuration)
|
|
|
|
// when cancels
|
|
(topPresentedController().presentedViewController as? ActivateBiometryAlertController)?.cancel()
|
|
|
|
_ = XCTWaiter.wait(for: [exp], timeout: animationDuration)
|
|
|
|
// then flow completed with success
|
|
XCTAssertEqual(completionResult, true, "Flow not completed successfully")
|
|
}
|
|
|
|
// TODO: re-write biometry testing using the Darwin notifications https://github.com/KaneCheshire/BiometricAutomationDemo
|
|
|
|
func startModalCreatePasscodeFlow(dismissableOnSwipe: Bool = true, completion: @escaping (Bool) -> Void = { _ in }) -> CreatePasscodeFlow {
|
|
let flow = CreatePasscodeFlow(completion: completion)
|
|
flow.modal(from: presenterVC, dismissableOnSwipe: dismissableOnSwipe)
|
|
// wait for presentation animation to complete
|
|
wait(timeout: animationDuration)
|
|
return flow
|
|
}
|
|
|
|
func navigateToRepeatPasscodeScreen(passcode: String = "123456", line: UInt = #line, completion: @escaping (Bool) -> Void = { _ in }) -> UIFlow {
|
|
let flow = startModalCreatePasscodeFlow(completion: completion)
|
|
|
|
(topPresentedController() as? CreatePasscodeViewController)?.append(text: passcode)
|
|
wait(timeout: animationDuration)
|
|
XCTAssertTrue(topPresentedController() is RepeatPasscodeViewController, "repeat vc not found", line: line)
|
|
|
|
return flow
|
|
}
|
|
|
|
func pushCreatePasscodeFlow() -> CreatePasscodeFlow {
|
|
// present the hosting navigation stack
|
|
presenterVC.present(pushingNavVC, animated: true)
|
|
wait(timeout: animationDuration)
|
|
|
|
// push flow on top of the stack
|
|
let flow = CreatePasscodeFlow(completion: { _ in })
|
|
pushingVC.push(flow: flow)
|
|
wait(timeout: animationDuration)
|
|
|
|
return flow
|
|
}
|
|
}
|
|
|
|
// create passcode screen
|
|
// enter 3 digits, erase one digit
|
|
|
|
// repeat passcode
|
|
// enter 3 digits, erase
|
|
// enter 6 digits, all different
|
|
// enter 6 digits, 5 same, 1 different
|