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.
This commit is contained in:
Doug 2023-07-27 12:38:48 +01:00 committed by GitHub
parent 1529b5b8ca
commit 8f2904701b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 92 additions and 75 deletions

View File

@ -219,6 +219,18 @@ extension RoomDetailsNotificationSettingsState {
return false 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 { var isError: Bool {
if case .error = self { if case .error = self {
return true return true

View File

@ -20,18 +20,20 @@ import XCTest
@MainActor @MainActor
class AnalyticsSettingsScreenViewModelTests: XCTestCase { class AnalyticsSettingsScreenViewModelTests: XCTestCase {
private var appSettings: AppSettings!
private var viewModel: AnalyticsSettingsScreenViewModelProtocol! private var viewModel: AnalyticsSettingsScreenViewModelProtocol!
private var context: AnalyticsSettingsScreenViewModelType.Context! private var context: AnalyticsSettingsScreenViewModelType.Context!
@MainActor override func setUpWithError() throws { @MainActor override func setUpWithError() throws {
AppSettings.reset() AppSettings.reset()
appSettings = AppSettings()
let analyticsClient = AnalyticsClientMock() let analyticsClient = AnalyticsClientMock()
analyticsClient.isRunning = false analyticsClient.isRunning = false
ServiceLocator.shared.register(analytics: AnalyticsService(client: analyticsClient, ServiceLocator.shared.register(analytics: AnalyticsService(client: analyticsClient,
appSettings: ServiceLocator.shared.settings, appSettings: appSettings,
bugReportService: ServiceLocator.shared.bugReportService)) bugReportService: ServiceLocator.shared.bugReportService))
viewModel = AnalyticsSettingsScreenViewModel(appSettings: ServiceLocator.shared.settings, viewModel = AnalyticsSettingsScreenViewModel(appSettings: appSettings,
analytics: ServiceLocator.shared.analytics) analytics: ServiceLocator.shared.analytics)
context = viewModel.context context = viewModel.context
} }
@ -41,13 +43,13 @@ class AnalyticsSettingsScreenViewModelTests: XCTestCase {
} }
func testOptIn() { func testOptIn() {
ServiceLocator.shared.settings.analyticsConsentState = .optedOut appSettings.analyticsConsentState = .optedOut
context.send(viewAction: .toggleAnalytics) context.send(viewAction: .toggleAnalytics)
XCTAssertTrue(context.enableAnalytics) XCTAssertTrue(context.enableAnalytics)
} }
func testOptOut() { func testOptOut() {
ServiceLocator.shared.settings.analyticsConsentState = .optedIn appSettings.analyticsConsentState = .optedIn
context.send(viewAction: .toggleAnalytics) context.send(viewAction: .toggleAnalytics)
XCTAssertFalse(context.enableAnalytics) XCTAssertFalse(context.enableAnalytics)
} }

View File

@ -19,12 +19,13 @@ import AnalyticsEvents
import XCTest import XCTest
class AnalyticsTests: XCTestCase { class AnalyticsTests: XCTestCase {
private var appSettings: AppSettings { ServiceLocator.shared.settings } private var appSettings: AppSettings!
private var analyticsClient: AnalyticsClientMock! private var analyticsClient: AnalyticsClientMock!
private var bugReportService: BugReportServiceMock! private var bugReportService: BugReportServiceMock!
override func setUp() { override func setUp() {
AppSettings.reset() AppSettings.reset()
appSettings = AppSettings()
bugReportService = BugReportServiceMock() bugReportService = BugReportServiceMock()
bugReportService.isRunning = false bugReportService.isRunning = false
@ -32,7 +33,7 @@ class AnalyticsTests: XCTestCase {
analyticsClient = AnalyticsClientMock() analyticsClient = AnalyticsClientMock()
analyticsClient.isRunning = false analyticsClient.isRunning = false
ServiceLocator.shared.register(analytics: AnalyticsService(client: analyticsClient, ServiceLocator.shared.register(analytics: AnalyticsService(client: analyticsClient,
appSettings: ServiceLocator.shared.settings, appSettings: appSettings,
bugReportService: ServiceLocator.shared.bugReportService)) bugReportService: ServiceLocator.shared.bugReportService))
} }
@ -69,7 +70,7 @@ class AnalyticsTests: XCTestCase {
func testAnalyticsPromptNotDisplayed() { func testAnalyticsPromptNotDisplayed() {
// Given a fresh install of the app both Analytics and BugReportService should be disabled // 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.isEnabled)
XCTAssertFalse(ServiceLocator.shared.analytics.isRunning) XCTAssertFalse(ServiceLocator.shared.analytics.isRunning)
XCTAssertFalse(analyticsClient.startAnalyticsConfigurationCalled) XCTAssertFalse(analyticsClient.startAnalyticsConfigurationCalled)
@ -174,7 +175,7 @@ class AnalyticsTests: XCTestCase {
numFavouriteRooms: nil, numFavouriteRooms: nil,
numSpaces: nil, numSpaces: nil,
allChatsActiveFilter: nil)) allChatsActiveFilter: nil))
client.start(analyticsConfiguration: ServiceLocator.shared.settings.analyticsConfiguration) client.start(analyticsConfiguration: appSettings.analyticsConfiguration)
XCTAssertNotNil(client.pendingUserProperties, "The user properties should be cached.") XCTAssertNotNil(client.pendingUserProperties, "The user properties should be cached.")
XCTAssertEqual(client.pendingUserProperties?.ftueUseCaseSelection, .PersonalMessaging, "The use case selection should match.") XCTAssertEqual(client.pendingUserProperties?.ftueUseCaseSelection, .PersonalMessaging, "The use case selection should match.")

View File

@ -378,6 +378,8 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
XCTAssertFalse(context.viewState.canEdit) XCTAssertFalse(context.viewState.canEdit)
} }
// MARK: - Notifications
func testNotificationLoadingSettingsFailure() async throws { func testNotificationLoadingSettingsFailure() async throws {
notificationSettingsProxyMock.getNotificationSettingsRoomIdIsEncryptedActiveMembersCountThrowableError = NotificationSettingsError.Generic(message: "error") notificationSettingsProxyMock.getNotificationSettingsRoomIdIsEncryptedActiveMembersCountThrowableError = NotificationSettingsError.Generic(message: "error")
viewModel = RoomDetailsScreenViewModel(accountUserID: "@owner:somewhere.com", viewModel = RoomDetailsScreenViewModel(accountUserID: "@owner:somewhere.com",
@ -389,6 +391,7 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
let deferred = deferFulfillment(context.$viewState.map(\.notificationSettingsState) let deferred = deferFulfillment(context.$viewState.map(\.notificationSettingsState)
.filter(\.isError) .filter(\.isError)
.first()) .first())
notificationSettingsProxyMock.callbacks.send(.settingsDidChange)
try await deferred.fulfill() try await deferred.fulfill()
let expectedAlertInfo = AlertInfo(id: RoomDetailsScreenErrorType.alert, let expectedAlertInfo = AlertInfo(id: RoomDetailsScreenErrorType.alert,
@ -401,8 +404,8 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
func testNotificationDefaultMode() async throws { func testNotificationDefaultMode() async throws {
notificationSettingsProxyMock.getNotificationSettingsRoomIdIsEncryptedActiveMembersCountReturnValue = RoomNotificationSettingsProxyMock(with: .init(mode: .allMessages, isDefault: true)) notificationSettingsProxyMock.getNotificationSettingsRoomIdIsEncryptedActiveMembersCountReturnValue = RoomNotificationSettingsProxyMock(with: .init(mode: .allMessages, isDefault: true))
let deferred = deferFulfillment(context.$viewState.map(\.notificationSettingsState) let deferred = deferFulfillment(context.$viewState.map(\.notificationSettingsState).first(where: \.isLoaded))
.first(where: \.isLoaded)) notificationSettingsProxyMock.callbacks.send(.settingsDidChange)
try await deferred.fulfill() try await deferred.fulfill()
XCTAssertEqual(context.viewState.notificationSettingsState.label, "Default") XCTAssertEqual(context.viewState.notificationSettingsState.label, "Default")
@ -410,8 +413,8 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
func testNotificationCustomMode() async throws { func testNotificationCustomMode() async throws {
notificationSettingsProxyMock.getNotificationSettingsRoomIdIsEncryptedActiveMembersCountReturnValue = RoomNotificationSettingsProxyMock(with: .init(mode: .allMessages, isDefault: false)) notificationSettingsProxyMock.getNotificationSettingsRoomIdIsEncryptedActiveMembersCountReturnValue = RoomNotificationSettingsProxyMock(with: .init(mode: .allMessages, isDefault: false))
let deferred = deferFulfillment(context.$viewState.map(\.notificationSettingsState) let deferred = deferFulfillment(context.$viewState.map(\.notificationSettingsState).first(where: \.isCustom))
.first(where: \.isLoaded)) notificationSettingsProxyMock.callbacks.send(.settingsDidChange)
try await deferred.fulfill() try await deferred.fulfill()
XCTAssertEqual(context.viewState.notificationSettingsState.label, "Custom") XCTAssertEqual(context.viewState.notificationSettingsState.label, "Custom")
@ -419,8 +422,8 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
func testNotificationRoomMuted() async throws { func testNotificationRoomMuted() async throws {
notificationSettingsProxyMock.getNotificationSettingsRoomIdIsEncryptedActiveMembersCountReturnValue = RoomNotificationSettingsProxyMock(with: .init(mode: .mute, isDefault: false)) notificationSettingsProxyMock.getNotificationSettingsRoomIdIsEncryptedActiveMembersCountReturnValue = RoomNotificationSettingsProxyMock(with: .init(mode: .mute, isDefault: false))
let deferred = deferFulfillment(context.$viewState.map(\.notificationSettingsState) let deferred = deferFulfillment(context.$viewState.map(\.notificationSettingsState).first(where: \.isLoaded))
.first(where: \.isLoaded)) notificationSettingsProxyMock.callbacks.send(.settingsDidChange)
try await deferred.fulfill() try await deferred.fulfill()
XCTAssertEqual(context.viewState.notificationShortcutButtonTitle, L10n.commonUnmute) XCTAssertEqual(context.viewState.notificationShortcutButtonTitle, L10n.commonUnmute)
@ -429,8 +432,8 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
func testNotificationRoomNotMuted() async throws { func testNotificationRoomNotMuted() async throws {
notificationSettingsProxyMock.getNotificationSettingsRoomIdIsEncryptedActiveMembersCountReturnValue = RoomNotificationSettingsProxyMock(with: .init(mode: .mentionsAndKeywordsOnly, isDefault: false)) notificationSettingsProxyMock.getNotificationSettingsRoomIdIsEncryptedActiveMembersCountReturnValue = RoomNotificationSettingsProxyMock(with: .init(mode: .mentionsAndKeywordsOnly, isDefault: false))
let deferred = deferFulfillment(context.$viewState.map(\.notificationSettingsState) let deferred = deferFulfillment(context.$viewState.map(\.notificationSettingsState).first(where: \.isLoaded))
.first(where: \.isLoaded)) notificationSettingsProxyMock.callbacks.send(.settingsDidChange)
try await deferred.fulfill() try await deferred.fulfill()
XCTAssertEqual(context.viewState.notificationShortcutButtonTitle, L10n.commonMute) XCTAssertEqual(context.viewState.notificationShortcutButtonTitle, L10n.commonMute)
@ -438,12 +441,7 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
} }
func testUnmuteTappedFailure() async throws { func testUnmuteTappedFailure() async throws {
notificationSettingsProxyMock.getNotificationSettingsRoomIdIsEncryptedActiveMembersCountReturnValue = RoomNotificationSettingsProxyMock(with: .init(mode: .mute, isDefault: false)) try await testNotificationRoomMuted()
let deferred = deferFulfillment(context.$viewState.map(\.notificationSettingsState)
.first(where: \.isLoaded))
try await deferred.fulfill()
XCTAssertEqual(context.viewState.notificationShortcutButtonTitle, L10n.commonUnmute)
let expectation = expectation(description: #function) let expectation = expectation(description: #function)
notificationSettingsProxyMock.unmuteRoomRoomIdIsEncryptedActiveMembersCountClosure = { _, _, _ in notificationSettingsProxyMock.unmuteRoomRoomIdIsEncryptedActiveMembersCountClosure = { _, _, _ in
@ -468,12 +466,7 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
} }
func testMuteTappedFailure() async throws { func testMuteTappedFailure() async throws {
notificationSettingsProxyMock.getNotificationSettingsRoomIdIsEncryptedActiveMembersCountReturnValue = RoomNotificationSettingsProxyMock(with: .init(mode: .allMessages, isDefault: false)) try await testNotificationRoomNotMuted()
let deferred = deferFulfillment(context.$viewState.map(\.notificationSettingsState)
.first(where: \.isLoaded))
try await deferred.fulfill()
XCTAssertEqual(context.viewState.notificationShortcutButtonTitle, L10n.commonMute)
let expectation = expectation(description: #function) let expectation = expectation(description: #function)
notificationSettingsProxyMock.setNotificationModeRoomIdModeClosure = { _, _ in notificationSettingsProxyMock.setNotificationModeRoomIdModeClosure = { _, _ in
@ -498,10 +491,7 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
} }
func testMuteTapped() async throws { func testMuteTapped() async throws {
notificationSettingsProxyMock.getNotificationSettingsRoomIdIsEncryptedActiveMembersCountReturnValue = RoomNotificationSettingsProxyMock(with: .init(mode: .allMessages, isDefault: false)) try await testNotificationRoomNotMuted()
let deferred = deferFulfillment(context.$viewState.map(\.notificationSettingsState)
.first(where: \.isLoaded))
try await deferred.fulfill()
let expectation = expectation(description: #function) let expectation = expectation(description: #function)
notificationSettingsProxyMock.setNotificationModeRoomIdModeClosure = { [weak notificationSettingsProxyMock] _, mode in notificationSettingsProxyMock.setNotificationModeRoomIdModeClosure = { [weak notificationSettingsProxyMock] _, mode in
@ -528,10 +518,7 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
} }
func testUnmuteTapped() async throws { func testUnmuteTapped() async throws {
notificationSettingsProxyMock.getNotificationSettingsRoomIdIsEncryptedActiveMembersCountReturnValue = RoomNotificationSettingsProxyMock(with: .init(mode: .mute, isDefault: false)) try await testNotificationRoomMuted()
let deferred = deferFulfillment(context.$viewState.map(\.notificationSettingsState)
.first(where: \.isLoaded))
try await deferred.fulfill()
let expectation = expectation(description: #function) let expectation = expectation(description: #function)
notificationSettingsProxyMock.unmuteRoomRoomIdIsEncryptedActiveMembersCountClosure = { [weak notificationSettingsProxyMock] _, _, _ in notificationSettingsProxyMock.unmuteRoomRoomIdIsEncryptedActiveMembersCountClosure = { [weak notificationSettingsProxyMock] _, _, _ in

View File

@ -44,87 +44,90 @@ class RoomFlowCoordinatorTests: XCTestCase {
userIndicatorController: ServiceLocator.shared.userIndicatorController) userIndicatorController: ServiceLocator.shared.userIndicatorController)
} }
func testRoomPresentation() async { func testRoomPresentation() async throws {
await process(route: .room(roomID: "1"), expectedAction: .presentedRoom("1")) try await process(route: .room(roomID: "1"), expectedAction: .presentedRoom("1"))
XCTAssert(navigationStackCoordinator.rootCoordinator is RoomScreenCoordinator) XCTAssert(navigationStackCoordinator.rootCoordinator is RoomScreenCoordinator)
await process(route: .roomList, expectedAction: .dismissedRoom) try await process(route: .roomList, expectedAction: .dismissedRoom)
XCTAssertNil(navigationStackCoordinator.rootCoordinator) 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) 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) XCTAssert(navigationStackCoordinator.rootCoordinator is RoomScreenCoordinator)
await process(route: .roomList, expectedAction: .dismissedRoom) try await process(route: .roomList, expectedAction: .dismissedRoom)
XCTAssertNil(navigationStackCoordinator.rootCoordinator) XCTAssertNil(navigationStackCoordinator.rootCoordinator)
} }
func testRoomDetailsPresentation() async { func testRoomDetailsPresentation() async throws {
await process(route: .roomDetails(roomID: "1"), expectedAction: .presentedRoom("1")) try await process(route: .roomDetails(roomID: "1"), expectedAction: .presentedRoom("1"))
XCTAssert(navigationStackCoordinator.rootCoordinator is RoomDetailsScreenCoordinator) XCTAssert(navigationStackCoordinator.rootCoordinator is RoomDetailsScreenCoordinator)
await process(route: .roomList, expectedAction: .dismissedRoom) try await process(route: .roomList, expectedAction: .dismissedRoom)
XCTAssertNil(navigationStackCoordinator.rootCoordinator) XCTAssertNil(navigationStackCoordinator.rootCoordinator)
} }
func testStackUnwinding() async { func testStackUnwinding() async throws {
await process(route: .roomDetails(roomID: "1"), expectedAction: .presentedRoom("1")) try await process(route: .roomDetails(roomID: "1"), expectedAction: .presentedRoom("1"))
XCTAssert(navigationStackCoordinator.rootCoordinator is RoomDetailsScreenCoordinator) 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) XCTAssert(navigationStackCoordinator.rootCoordinator is RoomScreenCoordinator)
} }
func testNoOp() async { func testNoOp() async throws {
await process(route: .roomDetails(roomID: "1"), expectedAction: .presentedRoom("1")) try await process(route: .roomDetails(roomID: "1"), expectedAction: .presentedRoom("1"))
XCTAssert(navigationStackCoordinator.rootCoordinator is RoomDetailsScreenCoordinator) XCTAssert(navigationStackCoordinator.rootCoordinator is RoomDetailsScreenCoordinator)
roomFlowCoordinator.handleAppRoute(.roomDetails(roomID: "1"), animated: true) roomFlowCoordinator.handleAppRoute(.roomDetails(roomID: "1"), animated: true)
await Task.yield() await Task.yield()
XCTAssert(navigationStackCoordinator.rootCoordinator is RoomDetailsScreenCoordinator) XCTAssert(navigationStackCoordinator.rootCoordinator is RoomDetailsScreenCoordinator)
} }
func testSwitchToDifferentDetails() async { func testSwitchToDifferentDetails() async throws {
await process(route: .roomDetails(roomID: "1"), expectedAction: .presentedRoom("1")) try await process(route: .roomDetails(roomID: "1"), expectedAction: .presentedRoom("1"))
XCTAssert(navigationStackCoordinator.rootCoordinator is RoomDetailsScreenCoordinator) 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) XCTAssert(navigationStackCoordinator.rootCoordinator is RoomDetailsScreenCoordinator)
} }
func testPushDetails() async { func testPushDetails() async throws {
await process(route: .room(roomID: "1"), expectedAction: .presentedRoom("1")) try await process(route: .room(roomID: "1"), expectedAction: .presentedRoom("1"))
XCTAssert(navigationStackCoordinator.rootCoordinator is RoomScreenCoordinator) 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) XCTAssert(navigationStackCoordinator.rootCoordinator is RoomScreenCoordinator)
XCTAssertEqual(navigationStackCoordinator.stackCoordinators.count, 1) XCTAssertEqual(navigationStackCoordinator.stackCoordinators.count, 1)
XCTAssert(navigationStackCoordinator.stackCoordinators.first is RoomDetailsScreenCoordinator) XCTAssert(navigationStackCoordinator.stackCoordinators.first is RoomDetailsScreenCoordinator)
} }
func testReplaceDetailsWithTimeline() async { func testReplaceDetailsWithTimeline() async throws {
await process(route: .roomDetails(roomID: "1"), expectedAction: .presentedRoom("1")) try await process(route: .roomDetails(roomID: "1"), expectedAction: .presentedRoom("1"))
XCTAssert(navigationStackCoordinator.rootCoordinator is RoomDetailsScreenCoordinator) 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) XCTAssert(navigationStackCoordinator.rootCoordinator is RoomScreenCoordinator)
} }
// MARK: - Private // MARK: - Private
private func process(route: AppRoute, expectedAction: RoomFlowCoordinatorAction) async { private func process(route: AppRoute, expectedAction: RoomFlowCoordinatorAction) async throws {
await process(route: route, expectedActions: [expectedAction]) 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 { Task {
await Task.yield() await Task.yield()
self.roomFlowCoordinator.handleAppRoute(route, animated: true) self.roomFlowCoordinator.handleAppRoute(route, animated: true)
} }
if !expectedActions.isEmpty { if !expectedActions.isEmpty {
let actions = await roomFlowCoordinator.actions.collect(expectedActions.count).values.first() let actions = try await deferred.fulfill()
XCTAssertEqual(actions, expectedActions) XCTAssertEqual(actions, expectedActions)
} }
} }

View File

@ -39,6 +39,7 @@ class RoomNotificationSettingsScreenViewModelTests: XCTestCase {
roomProxy: roomProxyMock) roomProxy: roomProxyMock)
let deferred = deferFulfillment(context.$viewState.map(\.notificationSettingsState) let deferred = deferFulfillment(context.$viewState.map(\.notificationSettingsState)
.first(where: \.isLoaded)) .first(where: \.isLoaded))
notificationSettingsProxyMock.callbacks.send(.settingsDidChange)
try await deferred.fulfill() try await deferred.fulfill()
XCTAssertFalse(context.allowCustomSetting) XCTAssertFalse(context.allowCustomSetting)
@ -50,6 +51,7 @@ class RoomNotificationSettingsScreenViewModelTests: XCTestCase {
roomProxy: roomProxyMock) roomProxy: roomProxyMock)
let deferred = deferFulfillment(context.$viewState.map(\.notificationSettingsState) let deferred = deferFulfillment(context.$viewState.map(\.notificationSettingsState)
.first(where: \.isLoaded)) .first(where: \.isLoaded))
notificationSettingsProxyMock.callbacks.send(.settingsDidChange)
try await deferred.fulfill() try await deferred.fulfill()
XCTAssertTrue(context.allowCustomSetting) XCTAssertTrue(context.allowCustomSetting)
@ -61,6 +63,7 @@ class RoomNotificationSettingsScreenViewModelTests: XCTestCase {
roomProxy: roomProxyMock) roomProxy: roomProxyMock)
let deferred = deferFulfillment(context.$viewState.map(\.notificationSettingsState) let deferred = deferFulfillment(context.$viewState.map(\.notificationSettingsState)
.first(where: \.isError)) .first(where: \.isError))
notificationSettingsProxyMock.callbacks.send(.settingsDidChange)
try await deferred.fulfill() try await deferred.fulfill()
let expectedAlertInfo = AlertInfo(id: RoomNotificationSettingsScreenErrorType.loadingSettingsFailed, let expectedAlertInfo = AlertInfo(id: RoomNotificationSettingsScreenErrorType.loadingSettingsFailed,
@ -77,6 +80,7 @@ class RoomNotificationSettingsScreenViewModelTests: XCTestCase {
roomProxy: roomProxyMock) roomProxy: roomProxyMock)
let deferred = deferFulfillment(context.$viewState.map(\.notificationSettingsState) let deferred = deferFulfillment(context.$viewState.map(\.notificationSettingsState)
.first(where: \.isLoaded)) .first(where: \.isLoaded))
notificationSettingsProxyMock.callbacks.send(.settingsDidChange)
try await deferred.fulfill() try await deferred.fulfill()
let deferredIsRestoringDefaultSettings = deferFulfillment(context.$viewState.map(\.isRestoringDefautSetting) let deferredIsRestoringDefaultSettings = deferFulfillment(context.$viewState.map(\.isRestoringDefautSetting)
@ -95,13 +99,14 @@ class RoomNotificationSettingsScreenViewModelTests: XCTestCase {
notificationSettingsProxyMock.getNotificationSettingsRoomIdIsEncryptedActiveMembersCountReturnValue = RoomNotificationSettingsProxyMock(with: .init(mode: .mentionsAndKeywordsOnly, isDefault: true)) notificationSettingsProxyMock.getNotificationSettingsRoomIdIsEncryptedActiveMembersCountReturnValue = RoomNotificationSettingsProxyMock(with: .init(mode: .mentionsAndKeywordsOnly, isDefault: true))
viewModel = RoomNotificationSettingsScreenViewModel(notificationSettingsProxy: notificationSettingsProxyMock, viewModel = RoomNotificationSettingsScreenViewModel(notificationSettingsProxy: notificationSettingsProxyMock,
roomProxy: roomProxyMock) roomProxy: roomProxyMock)
let deferred = deferFulfillment(context.$viewState.map(\.notificationSettingsState) var deferred = deferFulfillment(context.$viewState.map(\.notificationSettingsState).first(where: \.isLoaded))
.first(where: \.isLoaded)) notificationSettingsProxyMock.callbacks.send(.settingsDidChange)
try await deferred.fulfill() try await deferred.fulfill()
deferred = deferFulfillment(context.$viewState.map(\.notificationSettingsState).first(where: \.isLoaded))
viewModel.state.bindings.allowCustomSetting = true viewModel.state.bindings.allowCustomSetting = true
context.send(viewAction: .changedAllowCustomSettings) context.send(viewAction: .changedAllowCustomSettings)
await context.nextViewState() try await deferred.fulfill()
XCTAssertEqual(notificationSettingsProxyMock.setNotificationModeRoomIdModeReceivedArguments?.0, roomProxyMock.id) XCTAssertEqual(notificationSettingsProxyMock.setNotificationModeRoomIdModeReceivedArguments?.0, roomProxyMock.id)
XCTAssertEqual(notificationSettingsProxyMock.setNotificationModeRoomIdModeReceivedArguments?.1, .mentionsAndKeywordsOnly) XCTAssertEqual(notificationSettingsProxyMock.setNotificationModeRoomIdModeReceivedArguments?.1, .mentionsAndKeywordsOnly)
@ -112,13 +117,14 @@ class RoomNotificationSettingsScreenViewModelTests: XCTestCase {
notificationSettingsProxyMock.getNotificationSettingsRoomIdIsEncryptedActiveMembersCountReturnValue = RoomNotificationSettingsProxyMock(with: .init(mode: .mentionsAndKeywordsOnly, isDefault: false)) notificationSettingsProxyMock.getNotificationSettingsRoomIdIsEncryptedActiveMembersCountReturnValue = RoomNotificationSettingsProxyMock(with: .init(mode: .mentionsAndKeywordsOnly, isDefault: false))
viewModel = RoomNotificationSettingsScreenViewModel(notificationSettingsProxy: notificationSettingsProxyMock, viewModel = RoomNotificationSettingsScreenViewModel(notificationSettingsProxy: notificationSettingsProxyMock,
roomProxy: roomProxyMock) roomProxy: roomProxyMock)
let deferred = deferFulfillment(context.$viewState.map(\.notificationSettingsState) let deferredState = deferFulfillment(context.$viewState.map(\.notificationSettingsState).first(where: \.isLoaded))
.first(where: \.isLoaded)) notificationSettingsProxyMock.callbacks.send(.settingsDidChange)
try await deferred.fulfill() try await deferredState.fulfill()
do { do {
var deferredViewState = deferFulfillment(context.$viewState.collect(2).first())
context.send(viewAction: .setCustomMode(.allMessages)) context.send(viewAction: .setCustomMode(.allMessages))
await context.nextViewState() try await deferredViewState.fulfill()
XCTAssertEqual(notificationSettingsProxyMock.setNotificationModeRoomIdModeReceivedArguments?.0, roomProxyMock.id) XCTAssertEqual(notificationSettingsProxyMock.setNotificationModeRoomIdModeReceivedArguments?.0, roomProxyMock.id)
XCTAssertEqual(notificationSettingsProxyMock.setNotificationModeRoomIdModeReceivedArguments?.1, .allMessages) XCTAssertEqual(notificationSettingsProxyMock.setNotificationModeRoomIdModeReceivedArguments?.1, .allMessages)
@ -126,8 +132,9 @@ class RoomNotificationSettingsScreenViewModelTests: XCTestCase {
} }
do { do {
var deferredViewState = deferFulfillment(context.$viewState.collect(2).first())
context.send(viewAction: .setCustomMode(.mute)) context.send(viewAction: .setCustomMode(.mute))
await context.nextViewState() try await deferredViewState.fulfill()
XCTAssertEqual(notificationSettingsProxyMock.setNotificationModeRoomIdModeReceivedArguments?.0, roomProxyMock.id) XCTAssertEqual(notificationSettingsProxyMock.setNotificationModeRoomIdModeReceivedArguments?.0, roomProxyMock.id)
XCTAssertEqual(notificationSettingsProxyMock.setNotificationModeRoomIdModeReceivedArguments?.1, .mute) XCTAssertEqual(notificationSettingsProxyMock.setNotificationModeRoomIdModeReceivedArguments?.1, .mute)
@ -135,8 +142,9 @@ class RoomNotificationSettingsScreenViewModelTests: XCTestCase {
} }
do { do {
var deferredViewState = deferFulfillment(context.$viewState.collect(2).first())
context.send(viewAction: .setCustomMode(.mentionsAndKeywordsOnly)) context.send(viewAction: .setCustomMode(.mentionsAndKeywordsOnly))
await context.nextViewState() try await deferredViewState.fulfill()
XCTAssertEqual(notificationSettingsProxyMock.setNotificationModeRoomIdModeReceivedArguments?.0, roomProxyMock.id) XCTAssertEqual(notificationSettingsProxyMock.setNotificationModeRoomIdModeReceivedArguments?.0, roomProxyMock.id)
XCTAssertEqual(notificationSettingsProxyMock.setNotificationModeRoomIdModeReceivedArguments?.1, .mentionsAndKeywordsOnly) XCTAssertEqual(notificationSettingsProxyMock.setNotificationModeRoomIdModeReceivedArguments?.1, .mentionsAndKeywordsOnly)

View File

@ -248,7 +248,7 @@ class RoomScreenViewModelTests: XCTestCase {
XCTAssertEqual(roomProxyMock.getMemberUserIDReceivedUserID, "bob") XCTAssertEqual(roomProxyMock.getMemberUserIDReceivedUserID, "bob")
} }
func testGoToUserDetailsFailure() async { func testGoToUserDetailsFailure() async throws {
// Setup // Setup
let timelineController = MockRoomTimelineController() let timelineController = MockRoomTimelineController()
let roomProxyMock = RoomProxyMock(with: .init(displayName: "")) let roomProxyMock = RoomProxyMock(with: .init(displayName: ""))
@ -269,14 +269,16 @@ class RoomScreenViewModelTests: XCTestCase {
} }
// Test // 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")) viewModel.context.send(viewAction: .tappedOnUser(userID: "bob"))
await viewModel.context.nextViewState() try await deferred.fulfill()
XCTAssertFalse(viewModel.state.bindings.alertInfo.isNil) XCTAssertFalse(viewModel.state.bindings.alertInfo.isNil)
XCTAssert(roomProxyMock.getMemberUserIDCallsCount == 1) XCTAssert(roomProxyMock.getMemberUserIDCallsCount == 1)
XCTAssertEqual(roomProxyMock.getMemberUserIDReceivedUserID, "bob") XCTAssertEqual(roomProxyMock.getMemberUserIDReceivedUserID, "bob")
} }
func testRetrySend() async { func testRetrySend() async throws {
// Setup // Setup
let timelineController = MockRoomTimelineController() let timelineController = MockRoomTimelineController()
let roomProxyMock = RoomProxyMock(with: .init(displayName: "")) let roomProxyMock = RoomProxyMock(with: .init(displayName: ""))

View File

@ -16,6 +16,7 @@
"value" : "1" "value" : "1"
} }
], ],
"testExecutionOrdering" : "random",
"testTimeoutsEnabled" : true "testTimeoutsEnabled" : true
}, },
"testTargets" : [ "testTargets" : [

View File

@ -99,6 +99,7 @@ lane :unit_tests do
run_tests( run_tests(
scheme: "UnitTests", scheme: "UnitTests",
result_bundle: true, result_bundle: true,
number_of_retries: 3,
) )
slather( slather(