Files
letro-ios/UnitTests/Sources/QRCodeLoginScreenViewModelTests.swift
Doug 33fcb8e667 Allow the app to be configured to bypass the server selection screen. (#4131)
* Make account provider configuration more flexible.

- Change defaultHomeserverAddress to an array of providers (needs UI).
- Add allowOtherAccountProviders to prevent the user from manually entering a provider.

* Refactor QR code scan failures into a common type.

* Validate scanned QR codes against the allowed account providers.

* Hide the login flow on the QR code screen when restricted.
2025-05-20 11:09:50 +01:00

110 lines
3.9 KiB
Swift

//
// 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 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: .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()
}
}