mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-10-31 16:24:11 +00:00
Add support for manually starting SaS verification flows and accepting remotely started ones
This commit is contained in:
committed by
Stefan Ceriu
parent
7888736a4d
commit
956e92a9ac
@@ -23,8 +23,6 @@
|
||||
|
||||
"session_verification_banner_title" = "Help keep your messages secure";
|
||||
"session_verification_banner_message" = "Looks like you’re using a new device. Verify its you.";
|
||||
"session_verification_screen_emojis_title" = "Lets check if these";
|
||||
"session_verification_screen_emojis_message" = "Open Element on one of your other sessions to compare.";
|
||||
|
||||
"server_selection_server_footer" = "You can only connect to a server that has already been set up";
|
||||
|
||||
|
||||
@@ -58,10 +58,6 @@ extension ElementL10n {
|
||||
public static let sessionVerificationBannerMessage = ElementL10n.tr("Untranslated", "session_verification_banner_message")
|
||||
/// Help keep your messages secure
|
||||
public static let sessionVerificationBannerTitle = ElementL10n.tr("Untranslated", "session_verification_banner_title")
|
||||
/// Open Element on one of your other sessions to compare.
|
||||
public static let sessionVerificationScreenEmojisMessage = ElementL10n.tr("Untranslated", "session_verification_screen_emojis_message")
|
||||
/// Lets check if these
|
||||
public static let sessionVerificationScreenEmojisTitle = ElementL10n.tr("Untranslated", "session_verification_screen_emojis_title")
|
||||
/// Appearance
|
||||
public static let settingsAppearance = ElementL10n.tr("Untranslated", "settings_appearance")
|
||||
/// Timeline Style
|
||||
|
||||
@@ -22,22 +22,19 @@ enum SessionVerificationViewModelAction {
|
||||
|
||||
struct SessionVerificationViewState: BindableState {
|
||||
var verificationState: SessionVerificationStateMachine.State = .initial
|
||||
|
||||
var title: String? {
|
||||
switch verificationState {
|
||||
case .showingChallenge:
|
||||
return ElementL10n.sessionVerificationScreenEmojisTitle
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var message: String {
|
||||
switch verificationState {
|
||||
case .initial:
|
||||
return ElementL10n.verificationOpenOtherToVerify
|
||||
case .requestingVerification:
|
||||
return ElementL10n.verificationRequestWaiting
|
||||
case .verificationRequestAccepted:
|
||||
return ElementL10n.verificationEmojiNotice
|
||||
case .startingSasVerification:
|
||||
return ElementL10n.verificationRequestWaiting
|
||||
case .sasVerificationStarted:
|
||||
return ElementL10n.verificationRequestWaiting
|
||||
case .acceptingChallenge:
|
||||
return ElementL10n.verificationRequestWaiting
|
||||
case .decliningChallenge:
|
||||
@@ -45,7 +42,7 @@ struct SessionVerificationViewState: BindableState {
|
||||
case .cancelling:
|
||||
return ElementL10n.verificationRequestWaiting
|
||||
case .showingChallenge:
|
||||
return ElementL10n.sessionVerificationScreenEmojisMessage
|
||||
return ElementL10n.verificationCodeNotice
|
||||
case .verified:
|
||||
return ElementL10n.verificationConclusionOkSelfNotice
|
||||
case .cancelled:
|
||||
@@ -55,7 +52,8 @@ struct SessionVerificationViewState: BindableState {
|
||||
}
|
||||
|
||||
enum SessionVerificationViewAction {
|
||||
case start
|
||||
case requestVerification
|
||||
case startSasVerification
|
||||
case restart
|
||||
case accept
|
||||
case decline
|
||||
|
||||
@@ -24,6 +24,12 @@ class SessionVerificationStateMachine {
|
||||
case initial
|
||||
/// Waiting for verification acceptance
|
||||
case requestingVerification
|
||||
/// Verification request accepted. Waiting for start
|
||||
case verificationRequestAccepted
|
||||
/// Waiting for SaS verification start
|
||||
case startingSasVerification
|
||||
/// A SaS verification flow has been started
|
||||
case sasVerificationStarted
|
||||
/// Verification accepted and emojis received
|
||||
case showingChallenge(emojis: [SessionVerificationEmoji])
|
||||
/// Emojis match locally
|
||||
@@ -42,6 +48,12 @@ class SessionVerificationStateMachine {
|
||||
enum Event: EventType {
|
||||
/// Request verification
|
||||
case requestVerification
|
||||
/// The current verification request has been accepted
|
||||
case didAcceptVerificationRequest
|
||||
/// Start a SaS verification flow
|
||||
case startSasVerification
|
||||
/// Started a SaS verification flow
|
||||
case didStartSasVerification
|
||||
/// Has received emojis
|
||||
case didReceiveChallenge(emojis: [SessionVerificationEmoji])
|
||||
/// Emojis match
|
||||
@@ -69,15 +81,19 @@ class SessionVerificationStateMachine {
|
||||
init() {
|
||||
stateMachine = StateMachine(state: .initial) { machine in
|
||||
machine.addRoutes(event: .requestVerification, transitions: [.initial => .requestingVerification])
|
||||
machine.addRoutes(event: .didAcceptVerificationRequest, transitions: [.requestingVerification => .verificationRequestAccepted])
|
||||
machine.addRoutes(event: .startSasVerification, transitions: [.verificationRequestAccepted => .startingSasVerification])
|
||||
machine.addRoutes(event: .didFail, transitions: [.requestingVerification => .initial])
|
||||
machine.addRoutes(event: .restart, transitions: [.cancelled => .initial])
|
||||
|
||||
// Transitions with associated values need to be handled through `addRouteMapping`
|
||||
machine.addRouteMapping { event, fromState, _ in
|
||||
switch (event, fromState) {
|
||||
case (.didReceiveChallenge(let emojis), .requestingVerification):
|
||||
case (.didStartSasVerification, _):
|
||||
return .sasVerificationStarted
|
||||
|
||||
case (.didReceiveChallenge(let emojis), .sasVerificationStarted):
|
||||
return .showingChallenge(emojis: emojis)
|
||||
|
||||
case (.acceptChallenge, .showingChallenge(let emojis)):
|
||||
return .acceptingChallenge(emojis: emojis)
|
||||
case (.didFail, .acceptingChallenge(let emojis)):
|
||||
@@ -100,6 +116,14 @@ class SessionVerificationStateMachine {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
addTransitionHandler { context in
|
||||
if let event = context.event {
|
||||
MXLog.info("Transitioning from `\(context.fromState)` to `\(context.toState)` with event `\(event)`")
|
||||
} else {
|
||||
MXLog.info("Transitioning from \(context.fromState)` to `\(context.toState)`")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -41,8 +41,12 @@ class SessionVerificationViewModel: SessionVerificationViewModelType, SessionVer
|
||||
guard let self else { return }
|
||||
|
||||
switch callback {
|
||||
case .acceptedVerificationRequest:
|
||||
self.stateMachine.processEvent(.didAcceptVerificationRequest)
|
||||
case .startedSasVerification:
|
||||
self.stateMachine.processEvent(.didStartSasVerification)
|
||||
case .receivedVerificationData(let emojis):
|
||||
guard self.stateMachine.state == .requestingVerification else {
|
||||
guard self.stateMachine.state == .sasVerificationStarted else {
|
||||
MXLog.warning("Callbacks: Ignoring receivedVerificationData due to invalid state.")
|
||||
return
|
||||
}
|
||||
@@ -61,8 +65,10 @@ class SessionVerificationViewModel: SessionVerificationViewModelType, SessionVer
|
||||
|
||||
override func process(viewAction: SessionVerificationViewAction) async {
|
||||
switch viewAction {
|
||||
case .start:
|
||||
case .requestVerification:
|
||||
stateMachine.processEvent(.requestVerification)
|
||||
case .startSasVerification:
|
||||
stateMachine.processEvent(.startSasVerification)
|
||||
case .restart:
|
||||
stateMachine.processEvent(.restart)
|
||||
case .close:
|
||||
@@ -92,6 +98,8 @@ class SessionVerificationViewModel: SessionVerificationViewModelType, SessionVer
|
||||
switch (context.fromState, context.event, context.toState) {
|
||||
case (.initial, .requestVerification, .requestingVerification):
|
||||
self.requestVerification()
|
||||
case (.verificationRequestAccepted, .startSasVerification, .startingSasVerification):
|
||||
self.startSasVerification()
|
||||
case (.showingChallenge, .acceptChallenge, .acceptingChallenge):
|
||||
self.acceptChallenge()
|
||||
case (.showingChallenge, .declineChallenge, .decliningChallenge):
|
||||
@@ -131,6 +139,18 @@ class SessionVerificationViewModel: SessionVerificationViewModelType, SessionVer
|
||||
}
|
||||
}
|
||||
|
||||
private func startSasVerification() {
|
||||
Task {
|
||||
switch await sessionVerificationControllerProxy.startSasVerification() {
|
||||
case .success:
|
||||
// Need to wait for the callback from the remote
|
||||
break
|
||||
case .failure:
|
||||
stateMachine.processEvent(.didFail)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func acceptChallenge() {
|
||||
Task {
|
||||
switch await sessionVerificationControllerProxy.approveVerification() {
|
||||
|
||||
@@ -23,13 +23,6 @@ struct SessionVerificationScreen: View {
|
||||
NavigationView {
|
||||
ScrollView {
|
||||
VStack(spacing: 32.0) {
|
||||
if let title = context.viewState.title {
|
||||
Text(title)
|
||||
.font(.element.headlineBold)
|
||||
.foregroundColor(.element.systemPrimaryLabel)
|
||||
.multilineTextAlignment(.center)
|
||||
}
|
||||
|
||||
Text(context.viewState.message)
|
||||
.font(.body)
|
||||
.multilineTextAlignment(.center)
|
||||
@@ -66,6 +59,15 @@ struct SessionVerificationScreen: View {
|
||||
case .requestingVerification:
|
||||
ProgressView()
|
||||
.accessibilityIdentifier("requestingVerificationProgressView")
|
||||
|
||||
case .verificationRequestAccepted:
|
||||
StateIcon(systemName: "lock.shield")
|
||||
case .startingSasVerification:
|
||||
ProgressView()
|
||||
.accessibilityIdentifier("startingSasVerification")
|
||||
case .sasVerificationStarted:
|
||||
ProgressView()
|
||||
.accessibilityIdentifier("startedSasVerification")
|
||||
case .cancelling:
|
||||
ProgressView()
|
||||
.accessibilityIdentifier("cancellingVerificationProgressView")
|
||||
@@ -98,10 +100,10 @@ struct SessionVerificationScreen: View {
|
||||
switch context.viewState.verificationState {
|
||||
case .initial:
|
||||
Button(ElementL10n.startVerification) {
|
||||
context.send(viewAction: .start)
|
||||
context.send(viewAction: .requestVerification)
|
||||
}
|
||||
.buttonStyle(.elementAction(.xLarge))
|
||||
.accessibilityIdentifier("startButton")
|
||||
.accessibilityIdentifier("requestVerificationButton")
|
||||
|
||||
case .cancelled:
|
||||
Button(ElementL10n.globalRetry) {
|
||||
@@ -109,16 +111,23 @@ struct SessionVerificationScreen: View {
|
||||
}
|
||||
.buttonStyle(.elementAction(.xLarge))
|
||||
.accessibilityIdentifier("restartButton")
|
||||
|
||||
case .verificationRequestAccepted:
|
||||
Button(ElementL10n.startVerification) {
|
||||
context.send(viewAction: .startSasVerification)
|
||||
}
|
||||
.buttonStyle(.elementAction(.xLarge))
|
||||
.accessibilityIdentifier("sasVerificationStartButton")
|
||||
|
||||
case .showingChallenge:
|
||||
VStack(spacing: 30) {
|
||||
Button { context.send(viewAction: .accept) } label: {
|
||||
Label(ElementL10n.actionMatch, systemImage: "checkmark")
|
||||
Label(ElementL10n.verificationSasMatch, systemImage: "checkmark")
|
||||
}
|
||||
.buttonStyle(.elementAction(.xLarge))
|
||||
.accessibilityLabel("challengeAcceptButton")
|
||||
|
||||
Button(ElementL10n.no) {
|
||||
Button(ElementL10n.verificationSasDoNotMatch) {
|
||||
context.send(viewAction: .decline)
|
||||
}
|
||||
.font(.element.bodyBold)
|
||||
|
||||
+15
-1
@@ -27,7 +27,21 @@ struct MockSessionVerificationControllerProxy: SessionVerificationControllerProx
|
||||
func requestVerification() async -> Result<Void, SessionVerificationControllerProxyError> {
|
||||
Task.detached {
|
||||
try await Task.sleep(for: requestDelay)
|
||||
callbacks.send(.receivedVerificationData(Self.emojis))
|
||||
callbacks.send(.acceptedVerificationRequest)
|
||||
}
|
||||
|
||||
return .success(())
|
||||
}
|
||||
|
||||
func startSasVerification() async -> Result<Void, SessionVerificationControllerProxyError> {
|
||||
Task.detached {
|
||||
try await Task.sleep(for: requestDelay)
|
||||
callbacks.send(.startedSasVerification)
|
||||
|
||||
Task.detached {
|
||||
try await Task.sleep(for: requestDelay)
|
||||
callbacks.send(.receivedVerificationData(Self.emojis))
|
||||
}
|
||||
}
|
||||
|
||||
return .success(())
|
||||
|
||||
+27
@@ -31,6 +31,14 @@ private class WeakSessionVerificationControllerProxy: SessionVerificationControl
|
||||
proxy?.didReceiveData(data)
|
||||
}
|
||||
|
||||
func didAcceptVerificationRequest() {
|
||||
proxy?.didAcceptVerificationRequest()
|
||||
}
|
||||
|
||||
func didStartSasVerification() {
|
||||
proxy?.didStartSasVerification()
|
||||
}
|
||||
|
||||
func didFail() {
|
||||
proxy?.didFail()
|
||||
}
|
||||
@@ -73,6 +81,17 @@ class SessionVerificationControllerProxy: SessionVerificationControllerProxyProt
|
||||
}
|
||||
}
|
||||
|
||||
func startSasVerification() async -> Result<Void, SessionVerificationControllerProxyError> {
|
||||
await Task.dispatch(on: .global()) {
|
||||
do {
|
||||
try self.sessionVerificationController.startSasVerification()
|
||||
return .success(())
|
||||
} catch {
|
||||
return .failure(.failedStartingSasVerification)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func approveVerification() async -> Result<Void, SessionVerificationControllerProxyError> {
|
||||
await Task.dispatch(on: .global()) {
|
||||
do {
|
||||
@@ -108,6 +127,14 @@ class SessionVerificationControllerProxy: SessionVerificationControllerProxyProt
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
fileprivate func didAcceptVerificationRequest() {
|
||||
callbacks.send(.acceptedVerificationRequest)
|
||||
}
|
||||
|
||||
fileprivate func didStartSasVerification() {
|
||||
callbacks.send(.startedSasVerification)
|
||||
}
|
||||
|
||||
fileprivate func didReceiveData(_ data: [MatrixRustSDK.SessionVerificationEmoji]) {
|
||||
callbacks.send(.receivedVerificationData(data.map { emoji in
|
||||
SessionVerificationEmoji(symbol: emoji.symbol(), description: emoji.description())
|
||||
|
||||
+5
@@ -19,12 +19,15 @@ import Foundation
|
||||
|
||||
enum SessionVerificationControllerProxyError: Error {
|
||||
case failedRequestingVerification
|
||||
case failedStartingSasVerification
|
||||
case failedApprovingVerification
|
||||
case failedDecliningVerification
|
||||
case failedCancellingVerification
|
||||
}
|
||||
|
||||
enum SessionVerificationControllerProxyCallback {
|
||||
case acceptedVerificationRequest
|
||||
case startedSasVerification
|
||||
case receivedVerificationData([SessionVerificationEmoji])
|
||||
case finished
|
||||
case cancelled
|
||||
@@ -43,6 +46,8 @@ protocol SessionVerificationControllerProxyProtocol {
|
||||
|
||||
func requestVerification() async -> Result<Void, SessionVerificationControllerProxyError>
|
||||
|
||||
func startSasVerification() async -> Result<Void, SessionVerificationControllerProxyError>
|
||||
|
||||
func approveVerification() async -> Result<Void, SessionVerificationControllerProxyError>
|
||||
|
||||
func declineVerification() async -> Result<Void, SessionVerificationControllerProxyError>
|
||||
|
||||
@@ -22,16 +22,22 @@ class SessionVerificationUITests: XCTestCase {
|
||||
let app = Application.launch()
|
||||
app.goToScreenWithIdentifier(.sessionVerification)
|
||||
|
||||
XCTAssert(app.buttons["startButton"].exists)
|
||||
XCTAssert(app.buttons["requestVerificationButton"].exists)
|
||||
XCTAssert(app.buttons["closeButton"].exists)
|
||||
XCTAssert(app.staticTexts["titleLabel"].exists)
|
||||
|
||||
app.assertScreenshot(.sessionVerification)
|
||||
|
||||
app.buttons["startButton"].tap()
|
||||
app.buttons["requestVerificationButton"].tap()
|
||||
|
||||
XCTAssert(app.activityIndicators["requestingVerificationProgressView"].exists)
|
||||
|
||||
XCTAssert(app.buttons["sasVerificationStartButton"].waitForExistence(timeout: 5.0))
|
||||
app.buttons["sasVerificationStartButton"].tap()
|
||||
|
||||
XCTAssert(app.activityIndicators["startingSasVerification"].waitForExistence(timeout: 5.0))
|
||||
XCTAssert(app.activityIndicators["startedSasVerification"].waitForExistence(timeout: 5.0))
|
||||
|
||||
XCTAssert(app.buttons["challengeAcceptButton"].waitForExistence(timeout: 5.0))
|
||||
XCTAssert(app.buttons["challengeDeclineButton"].waitForExistence(timeout: 5.0))
|
||||
|
||||
@@ -50,14 +56,20 @@ class SessionVerificationUITests: XCTestCase {
|
||||
let app = Application.launch()
|
||||
app.goToScreenWithIdentifier(.sessionVerification)
|
||||
|
||||
XCTAssert(app.buttons["startButton"].exists)
|
||||
XCTAssert(app.buttons["requestVerificationButton"].exists)
|
||||
XCTAssert(app.buttons["closeButton"].exists)
|
||||
XCTAssert(app.staticTexts["titleLabel"].exists)
|
||||
|
||||
app.buttons["startButton"].tap()
|
||||
app.buttons["requestVerificationButton"].tap()
|
||||
|
||||
XCTAssert(app.activityIndicators["requestingVerificationProgressView"].exists)
|
||||
|
||||
XCTAssert(app.buttons["sasVerificationStartButton"].waitForExistence(timeout: 5.0))
|
||||
app.buttons["sasVerificationStartButton"].tap()
|
||||
|
||||
XCTAssert(app.activityIndicators["startingSasVerification"].waitForExistence(timeout: 5.0))
|
||||
XCTAssert(app.activityIndicators["startedSasVerification"].waitForExistence(timeout: 5.0))
|
||||
|
||||
XCTAssert(app.buttons["challengeAcceptButton"].waitForExistence(timeout: 5.0))
|
||||
XCTAssert(app.buttons["challengeDeclineButton"].waitForExistence(timeout: 5.0))
|
||||
|
||||
@@ -74,14 +86,20 @@ class SessionVerificationUITests: XCTestCase {
|
||||
let app = Application.launch()
|
||||
app.goToScreenWithIdentifier(.sessionVerification)
|
||||
|
||||
XCTAssert(app.buttons["startButton"].exists)
|
||||
XCTAssert(app.buttons["requestVerificationButton"].exists)
|
||||
XCTAssert(app.buttons["closeButton"].exists)
|
||||
XCTAssert(app.staticTexts["titleLabel"].exists)
|
||||
|
||||
app.buttons["startButton"].tap()
|
||||
app.buttons["requestVerificationButton"].tap()
|
||||
|
||||
XCTAssert(app.activityIndicators["requestingVerificationProgressView"].waitForExistence(timeout: 1))
|
||||
|
||||
XCTAssert(app.buttons["sasVerificationStartButton"].waitForExistence(timeout: 5.0))
|
||||
app.buttons["sasVerificationStartButton"].tap()
|
||||
|
||||
XCTAssert(app.activityIndicators["startingSasVerification"].waitForExistence(timeout: 5.0))
|
||||
XCTAssert(app.activityIndicators["startedSasVerification"].waitForExistence(timeout: 5.0))
|
||||
|
||||
app.buttons["closeButton"].tap()
|
||||
|
||||
XCTAssert(app.images["sessionVerificationFailedIcon"].waitForExistence(timeout: 1))
|
||||
|
||||
@@ -33,6 +33,12 @@ class SessionVerificationStateMachineTests: XCTestCase {
|
||||
stateMachine.processEvent(.requestVerification)
|
||||
XCTAssertEqual(stateMachine.state, .requestingVerification)
|
||||
|
||||
stateMachine.processEvent(.didAcceptVerificationRequest)
|
||||
XCTAssertEqual(stateMachine.state, .verificationRequestAccepted)
|
||||
|
||||
stateMachine.processEvent(.didStartSasVerification)
|
||||
XCTAssertEqual(stateMachine.state, .sasVerificationStarted)
|
||||
|
||||
stateMachine.processEvent(.didReceiveChallenge(emojis: MockSessionVerificationControllerProxy.emojis))
|
||||
XCTAssertEqual(stateMachine.state, .showingChallenge(emojis: MockSessionVerificationControllerProxy.emojis))
|
||||
|
||||
@@ -49,6 +55,12 @@ class SessionVerificationStateMachineTests: XCTestCase {
|
||||
stateMachine.processEvent(.requestVerification)
|
||||
XCTAssertEqual(stateMachine.state, .requestingVerification)
|
||||
|
||||
stateMachine.processEvent(.didAcceptVerificationRequest)
|
||||
XCTAssertEqual(stateMachine.state, .verificationRequestAccepted)
|
||||
|
||||
stateMachine.processEvent(.didStartSasVerification)
|
||||
XCTAssertEqual(stateMachine.state, .sasVerificationStarted)
|
||||
|
||||
stateMachine.processEvent(.didReceiveChallenge(emojis: MockSessionVerificationControllerProxy.emojis))
|
||||
XCTAssertEqual(stateMachine.state, .showingChallenge(emojis: MockSessionVerificationControllerProxy.emojis))
|
||||
|
||||
@@ -84,6 +96,12 @@ class SessionVerificationStateMachineTests: XCTestCase {
|
||||
stateMachine.processEvent(.requestVerification)
|
||||
XCTAssertEqual(stateMachine.state, .requestingVerification)
|
||||
|
||||
stateMachine.processEvent(.didAcceptVerificationRequest)
|
||||
XCTAssertEqual(stateMachine.state, .verificationRequestAccepted)
|
||||
|
||||
stateMachine.processEvent(.didStartSasVerification)
|
||||
XCTAssertEqual(stateMachine.state, .sasVerificationStarted)
|
||||
|
||||
stateMachine.processEvent(.didReceiveChallenge(emojis: MockSessionVerificationControllerProxy.emojis))
|
||||
XCTAssertEqual(stateMachine.state, .showingChallenge(emojis: MockSessionVerificationControllerProxy.emojis))
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ class SessionVerificationViewModelTests: XCTestCase {
|
||||
func testRequestVerification() async {
|
||||
XCTAssertEqual(context.viewState.verificationState, .initial)
|
||||
|
||||
context.send(viewAction: .start)
|
||||
context.send(viewAction: .requestVerification)
|
||||
|
||||
await Task.yield()
|
||||
|
||||
@@ -45,7 +45,7 @@ class SessionVerificationViewModelTests: XCTestCase {
|
||||
func testVerificationCancellation() async throws {
|
||||
XCTAssertEqual(context.viewState.verificationState, .initial)
|
||||
|
||||
context.send(viewAction: .start)
|
||||
context.send(viewAction: .requestVerification)
|
||||
|
||||
context.send(viewAction: .close)
|
||||
|
||||
@@ -125,14 +125,20 @@ class SessionVerificationViewModelTests: XCTestCase {
|
||||
// MARK: - Private
|
||||
|
||||
private func setupChallengeReceived() {
|
||||
let expectation = XCTestExpectation(description: "Wait for challenge")
|
||||
let requestAcceptanceExpectation = XCTestExpectation(description: "Wait for request acceptance")
|
||||
let sasVerificationStartExpectation = XCTestExpectation(description: "Wait for SaS verification start")
|
||||
let verificationDataReceivalExpectation = XCTestExpectation(description: "Wait for Emoji data")
|
||||
|
||||
let cancellable = sessionVerificationController.callbacks
|
||||
.debounce(for: .seconds(2.0), scheduler: DispatchQueue.main)
|
||||
.sink { callback in
|
||||
switch callback {
|
||||
case .acceptedVerificationRequest:
|
||||
requestAcceptanceExpectation.fulfill()
|
||||
case .startedSasVerification:
|
||||
sasVerificationStartExpectation.fulfill()
|
||||
case .receivedVerificationData:
|
||||
expectation.fulfill()
|
||||
verificationDataReceivalExpectation.fulfill()
|
||||
default:
|
||||
break
|
||||
}
|
||||
@@ -142,10 +148,15 @@ class SessionVerificationViewModelTests: XCTestCase {
|
||||
cancellable.cancel()
|
||||
}
|
||||
|
||||
context.send(viewAction: .start)
|
||||
context.send(viewAction: .requestVerification)
|
||||
wait(for: [requestAcceptanceExpectation], timeout: 10.0)
|
||||
XCTAssertEqual(context.viewState.verificationState, .verificationRequestAccepted)
|
||||
|
||||
wait(for: [expectation], timeout: 10.0)
|
||||
context.send(viewAction: .startSasVerification)
|
||||
wait(for: [sasVerificationStartExpectation], timeout: 10.0)
|
||||
XCTAssertEqual(context.viewState.verificationState, .sasVerificationStarted)
|
||||
|
||||
wait(for: [verificationDataReceivalExpectation], timeout: 10.0)
|
||||
XCTAssertEqual(context.viewState.verificationState, .showingChallenge(emojis: MockSessionVerificationControllerProxy.emojis))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
Add support for manually starting SaS verification flows and accepting remotely started ones
|
||||
Reference in New Issue
Block a user