From bc01134f97655ca38589aba607b9baa932c0318f Mon Sep 17 00:00:00 2001 From: Doug <6060466+pixlwave@users.noreply.github.com> Date: Thu, 27 Jul 2023 12:38:48 +0100 Subject: [PATCH] Fix more unit tests. (#1406) - Randomise test order. - Use deferred fulfilment in more places. - Make sure to change something before awaiting the fulfilment. - Build a fresh AppSettings after reset(). - Retry tests on failure. --- .../RoomDetailsScreenModels.swift | 12 ++++ ...nalyticsSettingsScreenViewModelTests.swift | 10 ++-- UnitTests/Sources/AnalyticsTests.swift | 9 +-- .../Sources/RoomDetailsViewModelTests.swift | 43 +++++--------- .../Sources/RoomFlowCoordinatorTests.swift | 57 ++++++++++--------- ...ficationSettingsScreenViewModelTests.swift | 26 ++++++--- .../Sources/RoomScreenViewModelTests.swift | 8 ++- .../SupportingFiles/UnitTests.xctestplan | 1 + fastlane/Fastfile | 1 + 9 files changed, 92 insertions(+), 75 deletions(-) diff --git a/ElementX/Sources/Screens/RoomDetailsScreen/RoomDetailsScreenModels.swift b/ElementX/Sources/Screens/RoomDetailsScreen/RoomDetailsScreenModels.swift index 99a3918d7..0bc1a5a91 100644 --- a/ElementX/Sources/Screens/RoomDetailsScreen/RoomDetailsScreenModels.swift +++ b/ElementX/Sources/Screens/RoomDetailsScreen/RoomDetailsScreenModels.swift @@ -219,6 +219,18 @@ extension RoomDetailsNotificationSettingsState { return false } + /// Returns `true` when the settings are loaded and `isDefault` is true. + var isDefault: Bool { + guard case let .loaded(settings) = self else { return false } + return settings.isDefault + } + + /// Returns `true` when the settings are loaded and `isDefault` is false. + var isCustom: Bool { + guard case let .loaded(settings) = self else { return false } + return !settings.isDefault + } + var isError: Bool { if case .error = self { return true diff --git a/UnitTests/Sources/AnalyticsSettingsScreenViewModelTests.swift b/UnitTests/Sources/AnalyticsSettingsScreenViewModelTests.swift index 25d252a92..cfcdaeb3d 100644 --- a/UnitTests/Sources/AnalyticsSettingsScreenViewModelTests.swift +++ b/UnitTests/Sources/AnalyticsSettingsScreenViewModelTests.swift @@ -20,18 +20,20 @@ import XCTest @MainActor class AnalyticsSettingsScreenViewModelTests: XCTestCase { + private var appSettings: AppSettings! private var viewModel: AnalyticsSettingsScreenViewModelProtocol! private var context: AnalyticsSettingsScreenViewModelType.Context! @MainActor override func setUpWithError() throws { AppSettings.reset() + appSettings = AppSettings() let analyticsClient = AnalyticsClientMock() analyticsClient.isRunning = false ServiceLocator.shared.register(analytics: AnalyticsService(client: analyticsClient, - appSettings: ServiceLocator.shared.settings, + appSettings: appSettings, bugReportService: ServiceLocator.shared.bugReportService)) - viewModel = AnalyticsSettingsScreenViewModel(appSettings: ServiceLocator.shared.settings, + viewModel = AnalyticsSettingsScreenViewModel(appSettings: appSettings, analytics: ServiceLocator.shared.analytics) context = viewModel.context } @@ -41,13 +43,13 @@ class AnalyticsSettingsScreenViewModelTests: XCTestCase { } func testOptIn() { - ServiceLocator.shared.settings.analyticsConsentState = .optedOut + appSettings.analyticsConsentState = .optedOut context.send(viewAction: .toggleAnalytics) XCTAssertTrue(context.enableAnalytics) } func testOptOut() { - ServiceLocator.shared.settings.analyticsConsentState = .optedIn + appSettings.analyticsConsentState = .optedIn context.send(viewAction: .toggleAnalytics) XCTAssertFalse(context.enableAnalytics) } diff --git a/UnitTests/Sources/AnalyticsTests.swift b/UnitTests/Sources/AnalyticsTests.swift index 4b5c35c6e..f9cfbdb0d 100644 --- a/UnitTests/Sources/AnalyticsTests.swift +++ b/UnitTests/Sources/AnalyticsTests.swift @@ -19,12 +19,13 @@ import AnalyticsEvents import XCTest class AnalyticsTests: XCTestCase { - private var appSettings: AppSettings { ServiceLocator.shared.settings } + private var appSettings: AppSettings! private var analyticsClient: AnalyticsClientMock! private var bugReportService: BugReportServiceMock! override func setUp() { AppSettings.reset() + appSettings = AppSettings() bugReportService = BugReportServiceMock() bugReportService.isRunning = false @@ -32,7 +33,7 @@ class AnalyticsTests: XCTestCase { analyticsClient = AnalyticsClientMock() analyticsClient.isRunning = false ServiceLocator.shared.register(analytics: AnalyticsService(client: analyticsClient, - appSettings: ServiceLocator.shared.settings, + appSettings: appSettings, bugReportService: ServiceLocator.shared.bugReportService)) } @@ -69,7 +70,7 @@ class AnalyticsTests: XCTestCase { func testAnalyticsPromptNotDisplayed() { // Given a fresh install of the app both Analytics and BugReportService should be disabled - XCTAssertEqual(ServiceLocator.shared.settings.analyticsConsentState, .unknown) + XCTAssertEqual(appSettings.analyticsConsentState, .unknown) XCTAssertFalse(ServiceLocator.shared.analytics.isEnabled) XCTAssertFalse(ServiceLocator.shared.analytics.isRunning) XCTAssertFalse(analyticsClient.startAnalyticsConfigurationCalled) @@ -174,7 +175,7 @@ class AnalyticsTests: XCTestCase { numFavouriteRooms: nil, numSpaces: nil, allChatsActiveFilter: nil)) - client.start(analyticsConfiguration: ServiceLocator.shared.settings.analyticsConfiguration) + client.start(analyticsConfiguration: appSettings.analyticsConfiguration) XCTAssertNotNil(client.pendingUserProperties, "The user properties should be cached.") XCTAssertEqual(client.pendingUserProperties?.ftueUseCaseSelection, .PersonalMessaging, "The use case selection should match.") diff --git a/UnitTests/Sources/RoomDetailsViewModelTests.swift b/UnitTests/Sources/RoomDetailsViewModelTests.swift index 76c5d7581..fb75c3dfd 100644 --- a/UnitTests/Sources/RoomDetailsViewModelTests.swift +++ b/UnitTests/Sources/RoomDetailsViewModelTests.swift @@ -378,6 +378,8 @@ class RoomDetailsScreenViewModelTests: XCTestCase { XCTAssertFalse(context.viewState.canEdit) } + // MARK: - Notifications + func testNotificationLoadingSettingsFailure() async throws { notificationSettingsProxyMock.getNotificationSettingsRoomIdIsEncryptedActiveMembersCountThrowableError = NotificationSettingsError.Generic(message: "error") viewModel = RoomDetailsScreenViewModel(accountUserID: "@owner:somewhere.com", @@ -389,6 +391,7 @@ class RoomDetailsScreenViewModelTests: XCTestCase { let deferred = deferFulfillment(context.$viewState.map(\.notificationSettingsState) .filter(\.isError) .first()) + notificationSettingsProxyMock.callbacks.send(.settingsDidChange) try await deferred.fulfill() let expectedAlertInfo = AlertInfo(id: RoomDetailsScreenErrorType.alert, @@ -401,8 +404,8 @@ class RoomDetailsScreenViewModelTests: XCTestCase { func testNotificationDefaultMode() async throws { notificationSettingsProxyMock.getNotificationSettingsRoomIdIsEncryptedActiveMembersCountReturnValue = RoomNotificationSettingsProxyMock(with: .init(mode: .allMessages, isDefault: true)) - let deferred = deferFulfillment(context.$viewState.map(\.notificationSettingsState) - .first(where: \.isLoaded)) + let deferred = deferFulfillment(context.$viewState.map(\.notificationSettingsState).first(where: \.isLoaded)) + notificationSettingsProxyMock.callbacks.send(.settingsDidChange) try await deferred.fulfill() XCTAssertEqual(context.viewState.notificationSettingsState.label, "Default") @@ -410,8 +413,8 @@ class RoomDetailsScreenViewModelTests: XCTestCase { func testNotificationCustomMode() async throws { notificationSettingsProxyMock.getNotificationSettingsRoomIdIsEncryptedActiveMembersCountReturnValue = RoomNotificationSettingsProxyMock(with: .init(mode: .allMessages, isDefault: false)) - let deferred = deferFulfillment(context.$viewState.map(\.notificationSettingsState) - .first(where: \.isLoaded)) + let deferred = deferFulfillment(context.$viewState.map(\.notificationSettingsState).first(where: \.isCustom)) + notificationSettingsProxyMock.callbacks.send(.settingsDidChange) try await deferred.fulfill() XCTAssertEqual(context.viewState.notificationSettingsState.label, "Custom") @@ -419,8 +422,8 @@ class RoomDetailsScreenViewModelTests: XCTestCase { func testNotificationRoomMuted() async throws { notificationSettingsProxyMock.getNotificationSettingsRoomIdIsEncryptedActiveMembersCountReturnValue = RoomNotificationSettingsProxyMock(with: .init(mode: .mute, isDefault: false)) - let deferred = deferFulfillment(context.$viewState.map(\.notificationSettingsState) - .first(where: \.isLoaded)) + let deferred = deferFulfillment(context.$viewState.map(\.notificationSettingsState).first(where: \.isLoaded)) + notificationSettingsProxyMock.callbacks.send(.settingsDidChange) try await deferred.fulfill() XCTAssertEqual(context.viewState.notificationShortcutButtonTitle, L10n.commonUnmute) @@ -429,8 +432,8 @@ class RoomDetailsScreenViewModelTests: XCTestCase { func testNotificationRoomNotMuted() async throws { notificationSettingsProxyMock.getNotificationSettingsRoomIdIsEncryptedActiveMembersCountReturnValue = RoomNotificationSettingsProxyMock(with: .init(mode: .mentionsAndKeywordsOnly, isDefault: false)) - let deferred = deferFulfillment(context.$viewState.map(\.notificationSettingsState) - .first(where: \.isLoaded)) + let deferred = deferFulfillment(context.$viewState.map(\.notificationSettingsState).first(where: \.isLoaded)) + notificationSettingsProxyMock.callbacks.send(.settingsDidChange) try await deferred.fulfill() XCTAssertEqual(context.viewState.notificationShortcutButtonTitle, L10n.commonMute) @@ -438,12 +441,7 @@ class RoomDetailsScreenViewModelTests: XCTestCase { } func testUnmuteTappedFailure() async throws { - notificationSettingsProxyMock.getNotificationSettingsRoomIdIsEncryptedActiveMembersCountReturnValue = RoomNotificationSettingsProxyMock(with: .init(mode: .mute, isDefault: false)) - let deferred = deferFulfillment(context.$viewState.map(\.notificationSettingsState) - .first(where: \.isLoaded)) - try await deferred.fulfill() - - XCTAssertEqual(context.viewState.notificationShortcutButtonTitle, L10n.commonUnmute) + try await testNotificationRoomMuted() let expectation = expectation(description: #function) notificationSettingsProxyMock.unmuteRoomRoomIdIsEncryptedActiveMembersCountClosure = { _, _, _ in @@ -468,12 +466,7 @@ class RoomDetailsScreenViewModelTests: XCTestCase { } func testMuteTappedFailure() async throws { - notificationSettingsProxyMock.getNotificationSettingsRoomIdIsEncryptedActiveMembersCountReturnValue = RoomNotificationSettingsProxyMock(with: .init(mode: .allMessages, isDefault: false)) - let deferred = deferFulfillment(context.$viewState.map(\.notificationSettingsState) - .first(where: \.isLoaded)) - try await deferred.fulfill() - - XCTAssertEqual(context.viewState.notificationShortcutButtonTitle, L10n.commonMute) + try await testNotificationRoomNotMuted() let expectation = expectation(description: #function) notificationSettingsProxyMock.setNotificationModeRoomIdModeClosure = { _, _ in @@ -498,10 +491,7 @@ class RoomDetailsScreenViewModelTests: XCTestCase { } func testMuteTapped() async throws { - notificationSettingsProxyMock.getNotificationSettingsRoomIdIsEncryptedActiveMembersCountReturnValue = RoomNotificationSettingsProxyMock(with: .init(mode: .allMessages, isDefault: false)) - let deferred = deferFulfillment(context.$viewState.map(\.notificationSettingsState) - .first(where: \.isLoaded)) - try await deferred.fulfill() + try await testNotificationRoomNotMuted() let expectation = expectation(description: #function) notificationSettingsProxyMock.setNotificationModeRoomIdModeClosure = { [weak notificationSettingsProxyMock] _, mode in @@ -528,10 +518,7 @@ class RoomDetailsScreenViewModelTests: XCTestCase { } func testUnmuteTapped() async throws { - notificationSettingsProxyMock.getNotificationSettingsRoomIdIsEncryptedActiveMembersCountReturnValue = RoomNotificationSettingsProxyMock(with: .init(mode: .mute, isDefault: false)) - let deferred = deferFulfillment(context.$viewState.map(\.notificationSettingsState) - .first(where: \.isLoaded)) - try await deferred.fulfill() + try await testNotificationRoomMuted() let expectation = expectation(description: #function) notificationSettingsProxyMock.unmuteRoomRoomIdIsEncryptedActiveMembersCountClosure = { [weak notificationSettingsProxyMock] _, _, _ in diff --git a/UnitTests/Sources/RoomFlowCoordinatorTests.swift b/UnitTests/Sources/RoomFlowCoordinatorTests.swift index 9d27dac9c..deabbcd14 100644 --- a/UnitTests/Sources/RoomFlowCoordinatorTests.swift +++ b/UnitTests/Sources/RoomFlowCoordinatorTests.swift @@ -44,87 +44,90 @@ class RoomFlowCoordinatorTests: XCTestCase { userIndicatorController: ServiceLocator.shared.userIndicatorController) } - func testRoomPresentation() async { - await process(route: .room(roomID: "1"), expectedAction: .presentedRoom("1")) + func testRoomPresentation() async throws { + try await process(route: .room(roomID: "1"), expectedAction: .presentedRoom("1")) XCTAssert(navigationStackCoordinator.rootCoordinator is RoomScreenCoordinator) - await process(route: .roomList, expectedAction: .dismissedRoom) + try await process(route: .roomList, expectedAction: .dismissedRoom) XCTAssertNil(navigationStackCoordinator.rootCoordinator) - await process(route: .room(roomID: "1"), expectedAction: .presentedRoom("1")) + try await process(route: .room(roomID: "1"), expectedAction: .presentedRoom("1")) XCTAssert(navigationStackCoordinator.rootCoordinator is RoomScreenCoordinator) - await process(route: .room(roomID: "2"), expectedAction: .presentedRoom("2")) + try await process(route: .room(roomID: "2"), expectedAction: .presentedRoom("2")) XCTAssert(navigationStackCoordinator.rootCoordinator is RoomScreenCoordinator) - await process(route: .roomList, expectedAction: .dismissedRoom) + try await process(route: .roomList, expectedAction: .dismissedRoom) XCTAssertNil(navigationStackCoordinator.rootCoordinator) } - func testRoomDetailsPresentation() async { - await process(route: .roomDetails(roomID: "1"), expectedAction: .presentedRoom("1")) + func testRoomDetailsPresentation() async throws { + try await process(route: .roomDetails(roomID: "1"), expectedAction: .presentedRoom("1")) XCTAssert(navigationStackCoordinator.rootCoordinator is RoomDetailsScreenCoordinator) - await process(route: .roomList, expectedAction: .dismissedRoom) + try await process(route: .roomList, expectedAction: .dismissedRoom) XCTAssertNil(navigationStackCoordinator.rootCoordinator) } - func testStackUnwinding() async { - await process(route: .roomDetails(roomID: "1"), expectedAction: .presentedRoom("1")) + func testStackUnwinding() async throws { + try await process(route: .roomDetails(roomID: "1"), expectedAction: .presentedRoom("1")) XCTAssert(navigationStackCoordinator.rootCoordinator is RoomDetailsScreenCoordinator) - await process(route: .room(roomID: "2"), expectedAction: .presentedRoom("2")) + try await process(route: .room(roomID: "2"), expectedAction: .presentedRoom("2")) XCTAssert(navigationStackCoordinator.rootCoordinator is RoomScreenCoordinator) } - func testNoOp() async { - await process(route: .roomDetails(roomID: "1"), expectedAction: .presentedRoom("1")) + func testNoOp() async throws { + try await process(route: .roomDetails(roomID: "1"), expectedAction: .presentedRoom("1")) XCTAssert(navigationStackCoordinator.rootCoordinator is RoomDetailsScreenCoordinator) roomFlowCoordinator.handleAppRoute(.roomDetails(roomID: "1"), animated: true) await Task.yield() XCTAssert(navigationStackCoordinator.rootCoordinator is RoomDetailsScreenCoordinator) } - func testSwitchToDifferentDetails() async { - await process(route: .roomDetails(roomID: "1"), expectedAction: .presentedRoom("1")) + func testSwitchToDifferentDetails() async throws { + try await process(route: .roomDetails(roomID: "1"), expectedAction: .presentedRoom("1")) XCTAssert(navigationStackCoordinator.rootCoordinator is RoomDetailsScreenCoordinator) - await process(route: .roomDetails(roomID: "2"), expectedAction: .presentedRoom("2")) + try await process(route: .roomDetails(roomID: "2"), expectedAction: .presentedRoom("2")) XCTAssert(navigationStackCoordinator.rootCoordinator is RoomDetailsScreenCoordinator) } - func testPushDetails() async { - await process(route: .room(roomID: "1"), expectedAction: .presentedRoom("1")) + func testPushDetails() async throws { + try await process(route: .room(roomID: "1"), expectedAction: .presentedRoom("1")) XCTAssert(navigationStackCoordinator.rootCoordinator is RoomScreenCoordinator) - await process(route: .roomDetails(roomID: "1"), expectedAction: .presentedRoom("1")) + try await process(route: .roomDetails(roomID: "1"), expectedAction: .presentedRoom("1")) XCTAssert(navigationStackCoordinator.rootCoordinator is RoomScreenCoordinator) XCTAssertEqual(navigationStackCoordinator.stackCoordinators.count, 1) XCTAssert(navigationStackCoordinator.stackCoordinators.first is RoomDetailsScreenCoordinator) } - func testReplaceDetailsWithTimeline() async { - await process(route: .roomDetails(roomID: "1"), expectedAction: .presentedRoom("1")) + func testReplaceDetailsWithTimeline() async throws { + try await process(route: .roomDetails(roomID: "1"), expectedAction: .presentedRoom("1")) XCTAssert(navigationStackCoordinator.rootCoordinator is RoomDetailsScreenCoordinator) - await process(route: .room(roomID: "1"), expectedActions: [.dismissedRoom, .presentedRoom("1")]) + try await process(route: .room(roomID: "1"), expectedActions: [.dismissedRoom, .presentedRoom("1")]) XCTAssert(navigationStackCoordinator.rootCoordinator is RoomScreenCoordinator) } // MARK: - Private - private func process(route: AppRoute, expectedAction: RoomFlowCoordinatorAction) async { - await process(route: route, expectedActions: [expectedAction]) + private func process(route: AppRoute, expectedAction: RoomFlowCoordinatorAction) async throws { + try await process(route: route, expectedActions: [expectedAction]) } - private func process(route: AppRoute, expectedActions: [RoomFlowCoordinatorAction]) async { + private func process(route: AppRoute, expectedActions: [RoomFlowCoordinatorAction]) async throws { + let deferred = deferFulfillment(roomFlowCoordinator.actions.collect(expectedActions.count).first(), + message: "The expected number of actions should be published.") + Task { await Task.yield() self.roomFlowCoordinator.handleAppRoute(route, animated: true) } if !expectedActions.isEmpty { - let actions = await roomFlowCoordinator.actions.collect(expectedActions.count).values.first() + let actions = try await deferred.fulfill() XCTAssertEqual(actions, expectedActions) } } diff --git a/UnitTests/Sources/RoomNotificationSettingsScreenViewModelTests.swift b/UnitTests/Sources/RoomNotificationSettingsScreenViewModelTests.swift index 3c6638f81..cc4c2969c 100644 --- a/UnitTests/Sources/RoomNotificationSettingsScreenViewModelTests.swift +++ b/UnitTests/Sources/RoomNotificationSettingsScreenViewModelTests.swift @@ -39,6 +39,7 @@ class RoomNotificationSettingsScreenViewModelTests: XCTestCase { roomProxy: roomProxyMock) let deferred = deferFulfillment(context.$viewState.map(\.notificationSettingsState) .first(where: \.isLoaded)) + notificationSettingsProxyMock.callbacks.send(.settingsDidChange) try await deferred.fulfill() XCTAssertFalse(context.allowCustomSetting) @@ -50,6 +51,7 @@ class RoomNotificationSettingsScreenViewModelTests: XCTestCase { roomProxy: roomProxyMock) let deferred = deferFulfillment(context.$viewState.map(\.notificationSettingsState) .first(where: \.isLoaded)) + notificationSettingsProxyMock.callbacks.send(.settingsDidChange) try await deferred.fulfill() XCTAssertTrue(context.allowCustomSetting) @@ -61,6 +63,7 @@ class RoomNotificationSettingsScreenViewModelTests: XCTestCase { roomProxy: roomProxyMock) let deferred = deferFulfillment(context.$viewState.map(\.notificationSettingsState) .first(where: \.isError)) + notificationSettingsProxyMock.callbacks.send(.settingsDidChange) try await deferred.fulfill() let expectedAlertInfo = AlertInfo(id: RoomNotificationSettingsScreenErrorType.loadingSettingsFailed, @@ -77,6 +80,7 @@ class RoomNotificationSettingsScreenViewModelTests: XCTestCase { roomProxy: roomProxyMock) let deferred = deferFulfillment(context.$viewState.map(\.notificationSettingsState) .first(where: \.isLoaded)) + notificationSettingsProxyMock.callbacks.send(.settingsDidChange) try await deferred.fulfill() let deferredIsRestoringDefaultSettings = deferFulfillment(context.$viewState.map(\.isRestoringDefautSetting) @@ -95,13 +99,14 @@ class RoomNotificationSettingsScreenViewModelTests: XCTestCase { notificationSettingsProxyMock.getNotificationSettingsRoomIdIsEncryptedActiveMembersCountReturnValue = RoomNotificationSettingsProxyMock(with: .init(mode: .mentionsAndKeywordsOnly, isDefault: true)) viewModel = RoomNotificationSettingsScreenViewModel(notificationSettingsProxy: notificationSettingsProxyMock, roomProxy: roomProxyMock) - let deferred = deferFulfillment(context.$viewState.map(\.notificationSettingsState) - .first(where: \.isLoaded)) + var deferred = deferFulfillment(context.$viewState.map(\.notificationSettingsState).first(where: \.isLoaded)) + notificationSettingsProxyMock.callbacks.send(.settingsDidChange) try await deferred.fulfill() + deferred = deferFulfillment(context.$viewState.map(\.notificationSettingsState).first(where: \.isLoaded)) viewModel.state.bindings.allowCustomSetting = true context.send(viewAction: .changedAllowCustomSettings) - await context.nextViewState() + try await deferred.fulfill() XCTAssertEqual(notificationSettingsProxyMock.setNotificationModeRoomIdModeReceivedArguments?.0, roomProxyMock.id) XCTAssertEqual(notificationSettingsProxyMock.setNotificationModeRoomIdModeReceivedArguments?.1, .mentionsAndKeywordsOnly) @@ -112,13 +117,14 @@ class RoomNotificationSettingsScreenViewModelTests: XCTestCase { notificationSettingsProxyMock.getNotificationSettingsRoomIdIsEncryptedActiveMembersCountReturnValue = RoomNotificationSettingsProxyMock(with: .init(mode: .mentionsAndKeywordsOnly, isDefault: false)) viewModel = RoomNotificationSettingsScreenViewModel(notificationSettingsProxy: notificationSettingsProxyMock, roomProxy: roomProxyMock) - let deferred = deferFulfillment(context.$viewState.map(\.notificationSettingsState) - .first(where: \.isLoaded)) - try await deferred.fulfill() + let deferredState = deferFulfillment(context.$viewState.map(\.notificationSettingsState).first(where: \.isLoaded)) + notificationSettingsProxyMock.callbacks.send(.settingsDidChange) + try await deferredState.fulfill() do { + var deferredViewState = deferFulfillment(context.$viewState.collect(2).first()) context.send(viewAction: .setCustomMode(.allMessages)) - await context.nextViewState() + try await deferredViewState.fulfill() XCTAssertEqual(notificationSettingsProxyMock.setNotificationModeRoomIdModeReceivedArguments?.0, roomProxyMock.id) XCTAssertEqual(notificationSettingsProxyMock.setNotificationModeRoomIdModeReceivedArguments?.1, .allMessages) @@ -126,8 +132,9 @@ class RoomNotificationSettingsScreenViewModelTests: XCTestCase { } do { + var deferredViewState = deferFulfillment(context.$viewState.collect(2).first()) context.send(viewAction: .setCustomMode(.mute)) - await context.nextViewState() + try await deferredViewState.fulfill() XCTAssertEqual(notificationSettingsProxyMock.setNotificationModeRoomIdModeReceivedArguments?.0, roomProxyMock.id) XCTAssertEqual(notificationSettingsProxyMock.setNotificationModeRoomIdModeReceivedArguments?.1, .mute) @@ -135,8 +142,9 @@ class RoomNotificationSettingsScreenViewModelTests: XCTestCase { } do { + var deferredViewState = deferFulfillment(context.$viewState.collect(2).first()) context.send(viewAction: .setCustomMode(.mentionsAndKeywordsOnly)) - await context.nextViewState() + try await deferredViewState.fulfill() XCTAssertEqual(notificationSettingsProxyMock.setNotificationModeRoomIdModeReceivedArguments?.0, roomProxyMock.id) XCTAssertEqual(notificationSettingsProxyMock.setNotificationModeRoomIdModeReceivedArguments?.1, .mentionsAndKeywordsOnly) diff --git a/UnitTests/Sources/RoomScreenViewModelTests.swift b/UnitTests/Sources/RoomScreenViewModelTests.swift index e7a455faa..1b45a27e7 100644 --- a/UnitTests/Sources/RoomScreenViewModelTests.swift +++ b/UnitTests/Sources/RoomScreenViewModelTests.swift @@ -248,7 +248,7 @@ class RoomScreenViewModelTests: XCTestCase { XCTAssertEqual(roomProxyMock.getMemberUserIDReceivedUserID, "bob") } - func testGoToUserDetailsFailure() async { + func testGoToUserDetailsFailure() async throws { // Setup let timelineController = MockRoomTimelineController() let roomProxyMock = RoomProxyMock(with: .init(displayName: "")) @@ -269,14 +269,16 @@ class RoomScreenViewModelTests: XCTestCase { } // Test + let deferred = deferFulfillment(viewModel.context.$viewState.collect(2).first(), + message: "The existing view state plus one new one should be published.") viewModel.context.send(viewAction: .tappedOnUser(userID: "bob")) - await viewModel.context.nextViewState() + try await deferred.fulfill() XCTAssertFalse(viewModel.state.bindings.alertInfo.isNil) XCTAssert(roomProxyMock.getMemberUserIDCallsCount == 1) XCTAssertEqual(roomProxyMock.getMemberUserIDReceivedUserID, "bob") } - func testRetrySend() async { + func testRetrySend() async throws { // Setup let timelineController = MockRoomTimelineController() let roomProxyMock = RoomProxyMock(with: .init(displayName: "")) diff --git a/UnitTests/SupportingFiles/UnitTests.xctestplan b/UnitTests/SupportingFiles/UnitTests.xctestplan index 7b5209dd3..d7c911081 100644 --- a/UnitTests/SupportingFiles/UnitTests.xctestplan +++ b/UnitTests/SupportingFiles/UnitTests.xctestplan @@ -16,6 +16,7 @@ "value" : "1" } ], + "testExecutionOrdering" : "random", "testTimeoutsEnabled" : true }, "testTargets" : [ diff --git a/fastlane/Fastfile b/fastlane/Fastfile index cf952d607..4ab49f11e 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -99,6 +99,7 @@ lane :unit_tests do run_tests( scheme: "UnitTests", result_bundle: true, + number_of_retries: 3, ) slather(