Swift Testing for Unit Tests PART 1 (#5119)

* migrated a lot of unit tests to Swift Testing and added a new implementation for deferred fulfillment

more tests migration

Cleaned the code manually to establish some good patterns

more code improvements

some more code improvements

removed empty tests

update project

* more pr suggestions and cleanups

* removed the TestSetup pattern

* fixing claude not reusing tests

* pr suggestion + added indent rule to swiftformat so that we can prevent AIs to change that
This commit is contained in:
Mauro
2026-02-19 16:20:47 +01:00
committed by GitHub
parent c92e847ed7
commit 173b39a07f
118 changed files with 4630 additions and 4129 deletions

View File

@@ -9,89 +9,97 @@
import Combine
@testable import ElementX
import MatrixRustSDKMocks
import XCTest
import Testing
@MainActor
final class QRCodeLoginScreenViewModelTests: XCTestCase {
private var qrLoginProgressSubject: CurrentValueSubject<QRLoginProgress, AuthenticationServiceError>!
private var qrCodeLoginService: QRCodeLoginServiceMock!
@Suite
struct QRCodeLoginScreenViewModelTests {
private enum Mode { case login, linkDesktop, linkMobile }
private var linkMobileProgressSubject: CurrentValueSubject<LinkNewDeviceService.LinkMobileProgress, QRCodeLoginError>!
private var linkDesktopProgressSubject: CurrentValueSubject<LinkNewDeviceService.LinkDesktopProgress, QRCodeLoginError>!
private var linkNewDeviceService: LinkNewDeviceServiceMock!
var qrLoginProgressSubject: CurrentValueSubject<QRLoginProgress, AuthenticationServiceError>!
var qrCodeLoginService: QRCodeLoginServiceMock!
private var appMediator: AppMediatorMock!
var linkMobileProgressSubject: CurrentValueSubject<LinkNewDeviceService.LinkMobileProgress, QRCodeLoginError>!
var linkDesktopProgressSubject: CurrentValueSubject<LinkNewDeviceService.LinkDesktopProgress, QRCodeLoginError>!
var linkNewDeviceService: LinkNewDeviceServiceMock!
private var viewModel: QRCodeLoginScreenViewModelProtocol!
private var context: QRCodeLoginScreenViewModelType.Context {
var appMediator: AppMediatorMock!
var viewModel: QRCodeLoginScreenViewModelProtocol!
var context: QRCodeLoginScreenViewModelType.Context {
viewModel.context
}
func testLoginInitialState() {
setupViewModel(mode: .login)
@Test
mutating func loginInitialState() {
setup(mode: .login)
XCTAssertEqual(context.viewState.state, .loginInstructions)
XCTAssertNil(context.qrResult)
XCTAssertFalse(qrCodeLoginService.loginWithQRCodeDataCalled)
XCTAssertFalse(appMediator.requestAuthorizationIfNeededCalled)
XCTAssertFalse(appMediator.openAppSettingsCalled)
#expect(context.viewState.state == .loginInstructions)
#expect(context.qrResult == nil)
#expect(!qrCodeLoginService.loginWithQRCodeDataCalled)
#expect(!appMediator.requestAuthorizationIfNeededCalled)
#expect(!appMediator.openAppSettingsCalled)
XCTAssertFalse(linkNewDeviceService.linkMobileDeviceCalled)
XCTAssertFalse(linkNewDeviceService.linkDesktopDeviceWithCalled)
#expect(!linkNewDeviceService.linkMobileDeviceCalled)
#expect(!linkNewDeviceService.linkDesktopDeviceWithCalled)
}
func testLinkDesktopInitialState() {
setupViewModel(mode: .linkDesktop)
@Test
mutating func linkDesktopInitialState() {
setup(mode: .linkDesktop)
XCTAssertEqual(context.viewState.state, .linkDesktopInstructions)
XCTAssertNil(context.qrResult)
XCTAssertFalse(linkNewDeviceService.linkDesktopDeviceWithCalled)
XCTAssertFalse(appMediator.requestAuthorizationIfNeededCalled)
XCTAssertFalse(appMediator.openAppSettingsCalled)
#expect(context.viewState.state == .linkDesktopInstructions)
#expect(context.qrResult == nil)
#expect(!linkNewDeviceService.linkDesktopDeviceWithCalled)
#expect(!appMediator.requestAuthorizationIfNeededCalled)
#expect(!appMediator.openAppSettingsCalled)
XCTAssertFalse(linkNewDeviceService.linkMobileDeviceCalled)
XCTAssertFalse(qrCodeLoginService.loginWithQRCodeDataCalled)
#expect(!linkNewDeviceService.linkMobileDeviceCalled)
#expect(!qrCodeLoginService.loginWithQRCodeDataCalled)
}
func testLinkMobileInitialState() {
setupViewModel(mode: .linkMobile)
@Test
mutating func linkMobileInitialState() {
setup(mode: .linkMobile)
XCTAssertTrue(context.viewState.state.isDisplayQR)
XCTAssertTrue(linkNewDeviceService.linkMobileDeviceCalled)
#expect(context.viewState.state.isDisplayQR)
#expect(linkNewDeviceService.linkMobileDeviceCalled)
XCTAssertFalse(linkNewDeviceService.linkDesktopDeviceWithCalled)
XCTAssertFalse(qrCodeLoginService.loginWithQRCodeDataCalled)
XCTAssertNil(context.qrResult)
#expect(!linkNewDeviceService.linkDesktopDeviceWithCalled)
#expect(!qrCodeLoginService.loginWithQRCodeDataCalled)
#expect(context.qrResult == nil)
}
func testRequestCameraPermission() async throws {
setupViewModel(mode: .login)
@Test
mutating func requestCameraPermission() async throws {
setup(mode: .login)
appMediator.requestAuthorizationIfNeededReturnValue = false
XCTAssert(context.viewState.state == .loginInstructions)
#expect(context.viewState.state == .loginInstructions)
let deferred = deferFulfillment(viewModel.context.$viewState) { state in
state.state == .error(.noCameraPermission)
}
context.send(viewAction: .startScan)
try await deferred.fulfill()
XCTAssertTrue(appMediator.requestAuthorizationIfNeededCalled)
#expect(appMediator.requestAuthorizationIfNeededCalled)
context.send(viewAction: .errorAction(.openSettings))
await Task.yield()
XCTAssertTrue(appMediator.openAppSettingsCalled)
XCTAssertNil(context.qrResult)
#expect(appMediator.openAppSettingsCalled)
#expect(context.qrResult == nil)
}
func testLogin() async throws {
setupViewModel(mode: .login)
XCTAssert(context.viewState.state == .loginInstructions)
@Test
mutating func login() async throws {
setup(mode: .login)
#expect(context.viewState.state == .loginInstructions)
var deferred = deferFulfillment(context.$viewState) { state in
state.state == .scan(.scanning)
}
context.send(viewAction: .startScan)
try await deferred.fulfill()
XCTAssertTrue(appMediator.requestAuthorizationIfNeededCalled)
#expect(appMediator.requestAuthorizationIfNeededCalled)
deferred = deferFulfillment(context.$viewState) { state in
state.state == .scan(.connecting)
@@ -121,14 +129,15 @@ final class QRCodeLoginScreenViewModelTests: XCTestCase {
try await deferredAction.fulfill()
}
func testLinkDesktopComputer() async throws {
setupViewModel(mode: .linkDesktop)
XCTAssert(context.viewState.state == .linkDesktopInstructions)
@Test
mutating func linkDesktopComputer() async throws {
setup(mode: .linkDesktop)
#expect(context.viewState.state == .linkDesktopInstructions)
var deferred = deferFulfillment(context.$viewState) { $0.state == .scan(.scanning) }
context.send(viewAction: .startScan)
try await deferred.fulfill()
XCTAssertTrue(appMediator.requestAuthorizationIfNeededCalled)
#expect(appMediator.requestAuthorizationIfNeededCalled)
deferred = deferFulfillment(context.$viewState) { $0.state == .scan(.connecting) }
context.qrResult = .init()
@@ -146,7 +155,7 @@ final class QRCodeLoginScreenViewModelTests: XCTestCase {
try await deferredAction.fulfill()
let currentState = context.viewState.state
let deferredFailure = deferFailure(context.$viewState, timeout: 1) { $0.state != currentState }
let deferredFailure = deferFailure(context.$viewState, timeout: .seconds(1)) { $0.state != currentState }
linkDesktopProgressSubject.send(.syncingSecrets)
try await deferredFailure.fulfill()
@@ -158,9 +167,10 @@ final class QRCodeLoginScreenViewModelTests: XCTestCase {
try await deferredAction.fulfill()
}
func testLinkMobileDevice() async throws {
setupViewModel(mode: .linkMobile)
XCTAssert(context.viewState.state.isDisplayQR)
@Test
mutating func linkMobileDevice() async throws {
setup(mode: .linkMobile)
#expect(context.viewState.state.isDisplayQR)
let checkCodeSender = CheckCodeSenderSDKMock()
let checkCodeSenderProxy = CheckCodeSenderProxy(underlyingSender: checkCodeSender)
@@ -189,16 +199,14 @@ final class QRCodeLoginScreenViewModelTests: XCTestCase {
try await deferredAction.fulfill()
let currentState = context.viewState.state
let deferredFailure = deferFailure(context.$viewState, timeout: 1) { $0.state != currentState }
let deferredFailure = deferFailure(context.$viewState, timeout: .seconds(1)) { $0.state != currentState }
linkMobileProgressSubject.send(.done)
try await deferredFailure.fulfill()
}
// MARK: - Helpers
enum Mode { case login, linkDesktop, linkMobile }
private func setupViewModel(mode: Mode) {
private mutating func setup(mode: Mode) {
qrLoginProgressSubject = .init(.starting)
qrCodeLoginService = QRCodeLoginServiceMock()
qrCodeLoginService.loginWithQRCodeDataReturnValue = qrLoginProgressSubject.asCurrentValuePublisher()