* Tidy-up Rust to Swift mapping. * Refactor out a dedicated QRCodeErrorView. * Use the new QRCodeErrorView for most error state snapshots. * Simplify QRCodeErrorView structure. Also updates the background to match the designs. * Fix a small compile error in the unit tests.
111 lines
4.0 KiB
Swift
111 lines
4.0 KiB
Swift
//
|
|
// Copyright 2025 Element Creations Ltd.
|
|
// Copyright 2022-2025 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 Combine
|
|
import XCTest
|
|
|
|
import MatrixRustSDK
|
|
|
|
@testable import ElementX
|
|
|
|
@MainActor
|
|
final class QRCodeLoginScreenViewModelTests: XCTestCase {
|
|
private var qrProgressSubject: PassthroughSubject<QrLoginProgress, Never>!
|
|
private var qrServiceMock: QRCodeLoginServiceMock!
|
|
private var appMediatorMock: AppMediatorMock!
|
|
private var viewModel: QRCodeLoginScreenViewModelProtocol!
|
|
|
|
private var context: QRCodeLoginScreenViewModelType.Context {
|
|
viewModel.context
|
|
}
|
|
|
|
override func setUp() {
|
|
qrProgressSubject = PassthroughSubject<QrLoginProgress, Never>()
|
|
qrServiceMock = QRCodeLoginServiceMock()
|
|
qrServiceMock.underlyingQrLoginProgressPublisher = qrProgressSubject.eraseToAnyPublisher()
|
|
appMediatorMock = AppMediatorMock.default
|
|
viewModel = QRCodeLoginScreenViewModel(qrCodeLoginService: qrServiceMock,
|
|
canSignInManually: true,
|
|
appMediator: appMediatorMock)
|
|
}
|
|
|
|
func testInitialState() {
|
|
XCTAssertEqual(context.viewState.state, .initial)
|
|
XCTAssertNil(context.qrResult)
|
|
XCTAssertFalse(qrServiceMock.loginWithQRCodeDataCalled)
|
|
XCTAssertFalse(appMediatorMock.requestAuthorizationIfNeededCalled)
|
|
XCTAssertFalse(appMediatorMock.openAppSettingsCalled)
|
|
}
|
|
|
|
func testRequestCameraPermission() async throws {
|
|
appMediatorMock.requestAuthorizationIfNeededReturnValue = false
|
|
XCTAssert(context.viewState.state == .initial)
|
|
|
|
let deferred = deferFulfillment(viewModel.context.$viewState) { state in
|
|
state.state == .error(.noCameraPermission)
|
|
}
|
|
context.send(viewAction: .startScan)
|
|
try await deferred.fulfill()
|
|
XCTAssertTrue(appMediatorMock.requestAuthorizationIfNeededCalled)
|
|
|
|
context.send(viewAction: .errorAction(.openSettings))
|
|
await Task.yield()
|
|
XCTAssertTrue(appMediatorMock.openAppSettingsCalled)
|
|
XCTAssertNil(context.qrResult)
|
|
}
|
|
|
|
func testLogin() async throws {
|
|
var isCompleted = false
|
|
qrServiceMock.loginWithQRCodeDataClosure = { _ in
|
|
while !isCompleted {
|
|
await Task.yield()
|
|
}
|
|
return .success(UserSessionMock(.init(clientProxy: ClientProxyMock())))
|
|
}
|
|
|
|
XCTAssert(context.viewState.state == .initial)
|
|
|
|
var deferred = deferFulfillment(context.$viewState) { state in
|
|
state.state == .scan(.scanning)
|
|
}
|
|
context.send(viewAction: .startScan)
|
|
try await deferred.fulfill()
|
|
XCTAssertTrue(appMediatorMock.requestAuthorizationIfNeededCalled)
|
|
|
|
deferred = deferFulfillment(context.$viewState) { state in
|
|
state.state == .scan(.connecting)
|
|
}
|
|
context.qrResult = .init()
|
|
try await deferred.fulfill()
|
|
|
|
deferred = deferFulfillment(context.$viewState) { state in
|
|
state.state == .displayCode(.deviceCode("01"))
|
|
}
|
|
qrProgressSubject.send(.establishingSecureChannel(checkCode: 1, checkCodeString: "01"))
|
|
try await deferred.fulfill()
|
|
|
|
deferred = deferFulfillment(context.$viewState) { state in
|
|
state.state == .displayCode(.verificationCode("ABCDEF"))
|
|
}
|
|
qrProgressSubject.send(.waitingForToken(userCode: "ABCDEF"))
|
|
try await deferred.fulfill()
|
|
|
|
let deferredAction = deferFulfillment(viewModel.actionsPublisher) { action in
|
|
switch action {
|
|
case .done:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
qrProgressSubject.send(.done)
|
|
isCompleted = true
|
|
try await deferredAction.fulfill()
|
|
}
|
|
}
|