mirror of
https://github.com/element-hq/element-x-ios.git
synced 2025-03-10 13:37:11 +00:00
Fix various flakey unit tests (#1783)
* Fix flakey emoji provider tests * Fix flakey RoomScreenViewModel tests * Fix flakey HomeScreenViewModel tests * Fix flakey RoomMemberListScreen tests, problem with bindings getting overriden and deferFulfillment cancellable not getting stored * Fix flakey RoomNotificationSettingsScreen tests and crashes * Fix flakey RoomMemberDetailsScreen tests * Deprecate old `deferFulfillment` and `nextViewState` methods * Convert more files to the new `deferFulfillment` * Converted the rest of the tests to the new deferFulfillment * Removed now unused `nextViewState` and `deferFulfillment` * Remove automatic retries from unit tests * Reset analytics flag after running unit tests * Address PR comments * Introduce a new `deferFulfillment(publisher, keyPath, transitionValues)` method and use it where appropiate
This commit is contained in:
parent
1f3898c69d
commit
a05c3e3774
@ -485,7 +485,6 @@
|
|||||||
992F5E750F5030C4BA2D0D03 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 01C4C7DB37597D7D8379511A /* Assets.xcassets */; };
|
992F5E750F5030C4BA2D0D03 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 01C4C7DB37597D7D8379511A /* Assets.xcassets */; };
|
||||||
9965CB800CE6BC74ACA969FC /* EncryptedHistoryRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75697AB5E64A12F1F069F511 /* EncryptedHistoryRoomTimelineView.swift */; };
|
9965CB800CE6BC74ACA969FC /* EncryptedHistoryRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75697AB5E64A12F1F069F511 /* EncryptedHistoryRoomTimelineView.swift */; };
|
||||||
99ED42B8F8D6BFB1DBCF4C45 /* AnalyticsEvents in Frameworks */ = {isa = PBXBuildFile; productRef = D661CAB418C075A94306A792 /* AnalyticsEvents */; };
|
99ED42B8F8D6BFB1DBCF4C45 /* AnalyticsEvents in Frameworks */ = {isa = PBXBuildFile; productRef = D661CAB418C075A94306A792 /* AnalyticsEvents */; };
|
||||||
99F8DA4CCC6772EE5FE68E24 /* ViewModelContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 818CBE6249ED6E8FC30E8366 /* ViewModelContext.swift */; };
|
|
||||||
9A3B0CDF097E3838FB1B9595 /* Bundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6E89E530A8E92EC44301CA1 /* Bundle.swift */; };
|
9A3B0CDF097E3838FB1B9595 /* Bundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6E89E530A8E92EC44301CA1 /* Bundle.swift */; };
|
||||||
9A4E3D5AA44B041DAC3A0D81 /* OIDCAuthenticationPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92390F9FA98255440A6BF5F8 /* OIDCAuthenticationPresenter.swift */; };
|
9A4E3D5AA44B041DAC3A0D81 /* OIDCAuthenticationPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92390F9FA98255440A6BF5F8 /* OIDCAuthenticationPresenter.swift */; };
|
||||||
9AC5F8142413862A9E3A2D98 /* DeviceKit in Frameworks */ = {isa = PBXBuildFile; productRef = A7CA6F33C553805035C3B114 /* DeviceKit */; };
|
9AC5F8142413862A9E3A2D98 /* DeviceKit in Frameworks */ = {isa = PBXBuildFile; productRef = A7CA6F33C553805035C3B114 /* DeviceKit */; };
|
||||||
@ -1259,7 +1258,6 @@
|
|||||||
80C4927D09099497233E9980 /* WaitlistScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaitlistScreen.swift; sourceTree = "<group>"; };
|
80C4927D09099497233E9980 /* WaitlistScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaitlistScreen.swift; sourceTree = "<group>"; };
|
||||||
80E815FF3CC5E5A355E3A25E /* RoomMessageEventStringBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMessageEventStringBuilder.swift; sourceTree = "<group>"; };
|
80E815FF3CC5E5A355E3A25E /* RoomMessageEventStringBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMessageEventStringBuilder.swift; sourceTree = "<group>"; };
|
||||||
818695BED971753243FEF897 /* StickerRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StickerRoomTimelineItem.swift; sourceTree = "<group>"; };
|
818695BED971753243FEF897 /* StickerRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StickerRoomTimelineItem.swift; sourceTree = "<group>"; };
|
||||||
818CBE6249ED6E8FC30E8366 /* ViewModelContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewModelContext.swift; sourceTree = "<group>"; };
|
|
||||||
8196D64EB9CF2AF1F43E4ED1 /* AnalyticsPromptScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsPromptScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
8196D64EB9CF2AF1F43E4ED1 /* AnalyticsPromptScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsPromptScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
||||||
81A9B5225D0881CEFA2CF7C9 /* RoomNotificationSettingsScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomNotificationSettingsScreenViewModel.swift; sourceTree = "<group>"; };
|
81A9B5225D0881CEFA2CF7C9 /* RoomNotificationSettingsScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomNotificationSettingsScreenViewModel.swift; sourceTree = "<group>"; };
|
||||||
81B17B1F29448D1B9049B11C /* ReportContentScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportContentScreenViewModel.swift; sourceTree = "<group>"; };
|
81B17B1F29448D1B9049B11C /* ReportContentScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportContentScreenViewModel.swift; sourceTree = "<group>"; };
|
||||||
@ -2647,7 +2645,6 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
60F18AECC9D38C2B6D85F99C /* Publisher.swift */,
|
60F18AECC9D38C2B6D85F99C /* Publisher.swift */,
|
||||||
818CBE6249ED6E8FC30E8366 /* ViewModelContext.swift */,
|
|
||||||
74611A4182DCF5F4D42696EC /* XCTestCase.swift */,
|
74611A4182DCF5F4D42696EC /* XCTestCase.swift */,
|
||||||
);
|
);
|
||||||
path = Extensions;
|
path = Extensions;
|
||||||
@ -4525,7 +4522,6 @@
|
|||||||
A1DF0E1E526A981ED6D5DF44 /* UserIndicatorControllerTests.swift in Sources */,
|
A1DF0E1E526A981ED6D5DF44 /* UserIndicatorControllerTests.swift in Sources */,
|
||||||
04F17DE71A50206336749BAC /* UserPreferenceTests.swift in Sources */,
|
04F17DE71A50206336749BAC /* UserPreferenceTests.swift in Sources */,
|
||||||
81A7C020CB5F6232242A8414 /* UserSessionTests.swift in Sources */,
|
81A7C020CB5F6232242A8414 /* UserSessionTests.swift in Sources */,
|
||||||
99F8DA4CCC6772EE5FE68E24 /* ViewModelContext.swift in Sources */,
|
|
||||||
FB9A1DD83EF641A75ABBCE69 /* WaitlistScreenViewModelTests.swift in Sources */,
|
FB9A1DD83EF641A75ABBCE69 /* WaitlistScreenViewModelTests.swift in Sources */,
|
||||||
7F02063FB3D1C3E5601471A1 /* WelcomeScreenScreenViewModelTests.swift in Sources */,
|
7F02063FB3D1C3E5601471A1 /* WelcomeScreenScreenViewModelTests.swift in Sources */,
|
||||||
3116693C5EB476E028990416 /* XCTestCase.swift in Sources */,
|
3116693C5EB476E028990416 /* XCTestCase.swift in Sources */,
|
||||||
|
@ -41,7 +41,7 @@ class EmojiPickerScreenViewModel: EmojiPickerScreenViewModelType, EmojiPickerScr
|
|||||||
switch viewAction {
|
switch viewAction {
|
||||||
case let .search(searchString: searchString):
|
case let .search(searchString: searchString):
|
||||||
Task {
|
Task {
|
||||||
let categories = await emojiProvider.getCategories(searchString: searchString)
|
let categories = await emojiProvider.categories(searchString: searchString)
|
||||||
state.categories = convert(emojiCategories: categories)
|
state.categories = convert(emojiCategories: categories)
|
||||||
}
|
}
|
||||||
case let .emojiTapped(emoji: emoji):
|
case let .emojiTapped(emoji: emoji):
|
||||||
@ -56,7 +56,7 @@ class EmojiPickerScreenViewModel: EmojiPickerScreenViewModelType, EmojiPickerScr
|
|||||||
private func loadEmojis() {
|
private func loadEmojis() {
|
||||||
Task(priority: .userInitiated) { [weak self] in
|
Task(priority: .userInitiated) { [weak self] in
|
||||||
guard let self else { return }
|
guard let self else { return }
|
||||||
let categories = await self.emojiProvider.getCategories(searchString: nil)
|
let categories = await self.emojiProvider.categories(searchString: nil)
|
||||||
self.state.categories = convert(emojiCategories: categories)
|
self.state.categories = convert(emojiCategories: categories)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@ struct RoomMembersListScreenViewState: BindableState {
|
|||||||
init(joinedMembersCount: Int,
|
init(joinedMembersCount: Int,
|
||||||
joinedMembers: [RoomMemberDetails] = [],
|
joinedMembers: [RoomMemberDetails] = [],
|
||||||
invitedMembers: [RoomMemberDetails] = [],
|
invitedMembers: [RoomMemberDetails] = [],
|
||||||
bindings: RoomMembersListScreenViewStateBindings = .init()) {
|
bindings: RoomMembersListScreenViewStateBindings) {
|
||||||
self.joinedMembersCount = joinedMembersCount
|
self.joinedMembersCount = joinedMembersCount
|
||||||
self.joinedMembers = joinedMembers
|
self.joinedMembers = joinedMembers
|
||||||
self.invitedMembers = invitedMembers
|
self.invitedMembers = invitedMembers
|
||||||
|
@ -37,7 +37,7 @@ class RoomMembersListScreenViewModel: RoomMembersListScreenViewModelType, RoomMe
|
|||||||
self.roomProxy = roomProxy
|
self.roomProxy = roomProxy
|
||||||
self.userIndicatorController = userIndicatorController
|
self.userIndicatorController = userIndicatorController
|
||||||
|
|
||||||
super.init(initialViewState: .init(joinedMembersCount: roomProxy.joinedMembersCount),
|
super.init(initialViewState: .init(joinedMembersCount: roomProxy.joinedMembersCount, bindings: .init()),
|
||||||
imageProvider: mediaProvider)
|
imageProvider: mediaProvider)
|
||||||
|
|
||||||
setupMembers()
|
setupMembers()
|
||||||
@ -83,7 +83,8 @@ class RoomMembersListScreenViewModel: RoomMembersListScreenViewModelType, RoomMe
|
|||||||
self.members = members
|
self.members = members
|
||||||
self.state = .init(joinedMembersCount: roomProxy.joinedMembersCount,
|
self.state = .init(joinedMembersCount: roomProxy.joinedMembersCount,
|
||||||
joinedMembers: roomMembersDetails.joinedMembers,
|
joinedMembers: roomMembersDetails.joinedMembers,
|
||||||
invitedMembers: roomMembersDetails.invitedMembers)
|
invitedMembers: roomMembersDetails.invitedMembers,
|
||||||
|
bindings: state.bindings)
|
||||||
self.state.canInviteUsers = roomMembersDetails.accountOwner?.canInviteUsers ?? false
|
self.state.canInviteUsers = roomMembersDetails.accountOwner?.canInviteUsers ?? false
|
||||||
hideLoader()
|
hideLoader()
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ import Foundation
|
|||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
protocol EmojiProviderProtocol {
|
protocol EmojiProviderProtocol {
|
||||||
func getCategories(searchString: String?) async -> [EmojiCategory]
|
func categories(searchString: String?) async -> [EmojiCategory]
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum EmojiProviderState {
|
private enum EmojiProviderState {
|
||||||
@ -39,7 +39,7 @@ class EmojiProvider: EmojiProviderProtocol {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getCategories(searchString: String? = nil) async -> [EmojiCategory] {
|
func categories(searchString: String? = nil) async -> [EmojiCategory] {
|
||||||
let emojiCategories = await loadIfNeeded()
|
let emojiCategories = await loadIfNeeded()
|
||||||
if let searchString, searchString.isEmpty == false {
|
if let searchString, searchString.isEmpty == false {
|
||||||
return search(searchString: searchString, emojiCategories: emojiCategories)
|
return search(searchString: searchString, emojiCategories: emojiCategories)
|
||||||
|
@ -24,6 +24,10 @@ class AnalyticsSettingsScreenViewModelTests: XCTestCase {
|
|||||||
private var viewModel: AnalyticsSettingsScreenViewModelProtocol!
|
private var viewModel: AnalyticsSettingsScreenViewModelProtocol!
|
||||||
private var context: AnalyticsSettingsScreenViewModelType.Context!
|
private var context: AnalyticsSettingsScreenViewModelType.Context!
|
||||||
|
|
||||||
|
override func tearDown() {
|
||||||
|
appSettings.analyticsConsentState = .unknown
|
||||||
|
}
|
||||||
|
|
||||||
@MainActor override func setUpWithError() throws {
|
@MainActor override func setUpWithError() throws {
|
||||||
AppSettings.reset()
|
AppSettings.reset()
|
||||||
appSettings = AppSettings()
|
appSettings = AppSettings()
|
||||||
|
@ -71,18 +71,19 @@ class BugReportViewModelTests: XCTestCase {
|
|||||||
deviceID: nil,
|
deviceID: nil,
|
||||||
screenshot: nil, isModallyPresented: false)
|
screenshot: nil, isModallyPresented: false)
|
||||||
let context = viewModel.context
|
let context = viewModel.context
|
||||||
let deferred = deferFulfillment(viewModel.actions.collect(2).first())
|
|
||||||
|
let deferred = deferFulfillment(viewModel.actions) { action in
|
||||||
|
switch action {
|
||||||
|
case .submitFinished:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
context.send(viewAction: .submit)
|
context.send(viewAction: .submit)
|
||||||
let actions = try await deferred.fulfill()
|
try await deferred.fulfill()
|
||||||
|
|
||||||
guard case .submitStarted = actions[0] else {
|
|
||||||
return XCTFail("Action 1 was not .submitFailed")
|
|
||||||
}
|
|
||||||
|
|
||||||
guard case .submitFinished = actions[1] else {
|
|
||||||
return XCTFail("Action 2 was not .submitFinished")
|
|
||||||
}
|
|
||||||
|
|
||||||
XCTAssert(mockService.submitBugReportProgressListenerCallsCount == 1)
|
XCTAssert(mockService.submitBugReportProgressListenerCallsCount == 1)
|
||||||
XCTAssert(mockService.submitBugReportProgressListenerReceivedArguments?.bugReport == BugReport(userID: "@mock.client.com", deviceID: nil, text: "", includeLogs: true, includeCrashLog: true, canContact: false, githubLabels: [], files: []))
|
XCTAssert(mockService.submitBugReportProgressListenerReceivedArguments?.bugReport == BugReport(userID: "@mock.client.com", deviceID: nil, text: "", includeLogs: true, includeCrashLog: true, canContact: false, githubLabels: [], files: []))
|
||||||
}
|
}
|
||||||
@ -97,18 +98,18 @@ class BugReportViewModelTests: XCTestCase {
|
|||||||
deviceID: nil,
|
deviceID: nil,
|
||||||
screenshot: nil, isModallyPresented: false)
|
screenshot: nil, isModallyPresented: false)
|
||||||
|
|
||||||
let deferred = deferFulfillment(viewModel.actions.collect(2).first())
|
let deferred = deferFulfillment(viewModel.actions) { action in
|
||||||
let context = viewModel.context
|
switch action {
|
||||||
context.send(viewAction: .submit)
|
case .submitFailed:
|
||||||
let actions = try await deferred.fulfill()
|
return true
|
||||||
|
default:
|
||||||
guard case .submitStarted = actions[0] else {
|
return false
|
||||||
return XCTFail("Action 1 was not .submitFailed")
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
guard case .submitFailed = actions[1] else {
|
let context = viewModel.context
|
||||||
return XCTFail("Action 2 was not .submitFailed")
|
context.send(viewAction: .submit)
|
||||||
}
|
try await deferred.fulfill()
|
||||||
|
|
||||||
XCTAssert(mockService.submitBugReportProgressListenerCallsCount == 1)
|
XCTAssert(mockService.submitBugReportProgressListenerCallsCount == 1)
|
||||||
XCTAssert(mockService.submitBugReportProgressListenerReceivedArguments?.bugReport == BugReport(userID: "@mock.client.com", deviceID: nil, text: "", includeLogs: true, includeCrashLog: true, canContact: false, githubLabels: [], files: []))
|
XCTAssert(mockService.submitBugReportProgressListenerReceivedArguments?.bugReport == BugReport(userID: "@mock.client.com", deviceID: nil, text: "", includeLogs: true, includeCrashLog: true, canContact: false, githubLabels: [], files: []))
|
||||||
|
@ -44,8 +44,17 @@ class CreatePollScreenViewModelTests: XCTestCase {
|
|||||||
context.options[1].text = "bla2"
|
context.options[1].text = "bla2"
|
||||||
XCTAssertFalse(context.viewState.bindings.isCreateButtonDisabled)
|
XCTAssertFalse(context.viewState.bindings.isCreateButtonDisabled)
|
||||||
|
|
||||||
let deferred = deferFulfillment(viewModel.actions.first())
|
let deferred = deferFulfillment(viewModel.actions) { action in
|
||||||
|
switch action {
|
||||||
|
case .create:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
context.send(viewAction: .create)
|
context.send(viewAction: .create)
|
||||||
|
|
||||||
let action = try await deferred.fulfill()
|
let action = try await deferred.fulfill()
|
||||||
|
|
||||||
guard case .create(let question, let options, let kind) = action else {
|
guard case .create(let question, let options, let kind) = action else {
|
||||||
|
@ -18,46 +18,55 @@ import XCTest
|
|||||||
|
|
||||||
@testable import ElementX
|
@testable import ElementX
|
||||||
|
|
||||||
|
@MainActor
|
||||||
final class EmojiProviderTests: XCTestCase {
|
final class EmojiProviderTests: XCTestCase {
|
||||||
var sut: EmojiProvider!
|
func testWhenEmojisLoadedCategoriesAreLoadedFromLoader() async throws {
|
||||||
private var emojiLoaderMock: EmojiLoaderMock!
|
|
||||||
|
|
||||||
@MainActor override func setUp() {
|
|
||||||
emojiLoaderMock = EmojiLoaderMock()
|
|
||||||
sut = EmojiProvider(loader: emojiLoaderMock)
|
|
||||||
}
|
|
||||||
|
|
||||||
func test_whenEmojisLoaded_categoriesAreLoadedFromLoader() async throws {
|
|
||||||
let item = EmojiItem(label: "test", unicode: "test", keywords: ["1", "2"], shortcodes: ["1", "2"], skins: ["🙂"])
|
let item = EmojiItem(label: "test", unicode: "test", keywords: ["1", "2"], shortcodes: ["1", "2"], skins: ["🙂"])
|
||||||
let category = EmojiCategory(id: "test", emojis: [item])
|
let category = EmojiCategory(id: "test", emojis: [item])
|
||||||
|
|
||||||
|
let emojiLoaderMock = EmojiLoaderMock()
|
||||||
emojiLoaderMock.categories = [category]
|
emojiLoaderMock.categories = [category]
|
||||||
let categories = await sut.getCategories()
|
|
||||||
|
let emojiProvider = EmojiProvider(loader: emojiLoaderMock)
|
||||||
|
|
||||||
|
let categories = await emojiProvider.categories()
|
||||||
XCTAssertEqual(emojiLoaderMock.categories, categories)
|
XCTAssertEqual(emojiLoaderMock.categories, categories)
|
||||||
}
|
}
|
||||||
|
|
||||||
func test_whenEmojisLoadedAndSearchStringEmpty_allCategoriesReturned() async throws {
|
func testWhenEmojisLoadedAndSearchStringEmptyAllCategoriesReturned() async throws {
|
||||||
let item = EmojiItem(label: "test", unicode: "test", keywords: ["1", "2"], shortcodes: ["1", "2"], skins: ["🙂"])
|
let item = EmojiItem(label: "test", unicode: "test", keywords: ["1", "2"], shortcodes: ["1", "2"], skins: ["🙂"])
|
||||||
let category = EmojiCategory(id: "test", emojis: [item])
|
let category = EmojiCategory(id: "test", emojis: [item])
|
||||||
|
|
||||||
|
let emojiLoaderMock = EmojiLoaderMock()
|
||||||
emojiLoaderMock.categories = [category]
|
emojiLoaderMock.categories = [category]
|
||||||
let categories = await sut.getCategories(searchString: "")
|
|
||||||
|
let emojiProvider = EmojiProvider(loader: emojiLoaderMock)
|
||||||
|
|
||||||
|
let categories = await emojiProvider.categories(searchString: "")
|
||||||
XCTAssertEqual(emojiLoaderMock.categories, categories)
|
XCTAssertEqual(emojiLoaderMock.categories, categories)
|
||||||
}
|
}
|
||||||
|
|
||||||
func test_whenEmojisLoadedSecondTime_cachedValuesAreUsed() async throws {
|
func testWhenEmojisLoadedSecondTimeCachedValuesAreUsed() async throws {
|
||||||
let item = EmojiItem(label: "test", unicode: "test", keywords: ["1", "2"], shortcodes: ["1", "2"], skins: ["🙂"])
|
let item = EmojiItem(label: "test", unicode: "test", keywords: ["1", "2"], shortcodes: ["1", "2"], skins: ["🙂"])
|
||||||
let item2 = EmojiItem(label: "test2", unicode: "test2", keywords: ["3", "4"], shortcodes: ["3", "4"], skins: ["🙂"])
|
let item2 = EmojiItem(label: "test2", unicode: "test2", keywords: ["3", "4"], shortcodes: ["3", "4"], skins: ["🙂"])
|
||||||
let categoriesForFirstLoad = [EmojiCategory(id: "test",
|
let categoriesForFirstLoad = [EmojiCategory(id: "test",
|
||||||
emojis: [item])]
|
emojis: [item])]
|
||||||
let categoriesForSecondLoad = [EmojiCategory(id: "test2",
|
let categoriesForSecondLoad = [EmojiCategory(id: "test2",
|
||||||
emojis: [item2])]
|
emojis: [item2])]
|
||||||
|
|
||||||
|
let emojiLoaderMock = EmojiLoaderMock()
|
||||||
emojiLoaderMock.categories = categoriesForFirstLoad
|
emojiLoaderMock.categories = categoriesForFirstLoad
|
||||||
_ = await sut.getCategories()
|
|
||||||
|
let emojiProvider = EmojiProvider(loader: emojiLoaderMock)
|
||||||
|
|
||||||
|
_ = await emojiProvider.categories()
|
||||||
emojiLoaderMock.categories = categoriesForSecondLoad
|
emojiLoaderMock.categories = categoriesForSecondLoad
|
||||||
let categories = await sut.getCategories()
|
|
||||||
|
let categories = await emojiProvider.categories()
|
||||||
XCTAssertEqual(categories, categoriesForFirstLoad)
|
XCTAssertEqual(categories, categoriesForFirstLoad)
|
||||||
}
|
}
|
||||||
|
|
||||||
func test_whenEmojisSearched_correctNumberOfCategoriesReturned() async throws {
|
func testWhenEmojisSearchedCorrectNumberOfCategoriesReturned() async throws {
|
||||||
let searchString = "smile"
|
let searchString = "smile"
|
||||||
var categories = [EmojiCategory]()
|
var categories = [EmojiCategory]()
|
||||||
let item0WithSearchString = EmojiItem(label: "emoji0", unicode: "\(searchString)_123", keywords: ["key1", "key1"], shortcodes: ["key1", "key1"], skins: ["🙂"])
|
let item0WithSearchString = EmojiItem(label: "emoji0", unicode: "\(searchString)_123", keywords: ["key1", "key1"], shortcodes: ["key1", "key1"], skins: ["🙂"])
|
||||||
@ -74,9 +83,14 @@ final class EmojiProviderTests: XCTestCase {
|
|||||||
item4WithoutSearchString]))
|
item4WithoutSearchString]))
|
||||||
categories.append(EmojiCategory(id: "test",
|
categories.append(EmojiCategory(id: "test",
|
||||||
emojis: [item5WithSearchString]))
|
emojis: [item5WithSearchString]))
|
||||||
|
|
||||||
|
let emojiLoaderMock = EmojiLoaderMock()
|
||||||
emojiLoaderMock.categories = categories
|
emojiLoaderMock.categories = categories
|
||||||
_ = await sut.getCategories()
|
|
||||||
let result = await sut.getCategories(searchString: searchString)
|
let emojiProvider = EmojiProvider(loader: emojiLoaderMock)
|
||||||
|
|
||||||
|
_ = await emojiProvider.categories()
|
||||||
|
let result = await emojiProvider.categories(searchString: searchString)
|
||||||
XCTAssertEqual(result.count, 2)
|
XCTAssertEqual(result.count, 2)
|
||||||
XCTAssertEqual(result.first?.emojis.count, 4)
|
XCTAssertEqual(result.first?.emojis.count, 4)
|
||||||
}
|
}
|
||||||
|
@ -1,24 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright 2023 New Vector Ltd
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
//
|
|
||||||
|
|
||||||
@testable import ElementX
|
|
||||||
|
|
||||||
extension StateStoreViewModel.Context {
|
|
||||||
@discardableResult
|
|
||||||
func nextViewState() async -> State? {
|
|
||||||
await $viewState.nextValue
|
|
||||||
}
|
|
||||||
}
|
|
@ -19,34 +19,37 @@ import XCTest
|
|||||||
|
|
||||||
extension XCTestCase {
|
extension XCTestCase {
|
||||||
/// XCTest utility that assists in subscribing to a publisher and deferring the fulfilment and results until some other actions have been performed.
|
/// XCTest utility that assists in subscribing to a publisher and deferring the fulfilment and results until some other actions have been performed.
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// let collectedEvents = somePublisher.collect(3).first()
|
|
||||||
/// let awaitDeferred = deferFulfillment(collectedEvents)
|
|
||||||
/// // Do some other work that publishes to somePublisher
|
|
||||||
/// XCTAssertEqual(try await awaitDeferred.execute(), [expected, values, here])
|
|
||||||
/// ```
|
|
||||||
/// - Parameters:
|
/// - Parameters:
|
||||||
/// - publisher: The publisher to wait on.
|
/// - publisher: The publisher to wait on.
|
||||||
/// - timeout: A timeout after which we give up.
|
/// - timeout: A timeout after which we give up.
|
||||||
|
/// - message: An optional custom expectation message
|
||||||
|
/// - until: callback that evaluates outputs until some condition is reached
|
||||||
/// - Returns: The deferred fulfilment to be executed after some actions and that returns the result of the publisher.
|
/// - Returns: The deferred fulfilment to be executed after some actions and that returns the result of the publisher.
|
||||||
func deferFulfillment<T: Publisher>(_ publisher: T, timeout: TimeInterval = 10, message: String? = nil) -> DeferredFulfillment<T.Output> {
|
func deferFulfillment<P: Publisher>(_ publisher: P,
|
||||||
var result: Result<T.Output, Error>?
|
timeout: TimeInterval = 10,
|
||||||
|
message: String? = nil,
|
||||||
|
until condition: @escaping (P.Output) -> Bool) -> DeferredFulfillment<P.Output> {
|
||||||
|
var result: Result<P.Output, Error>?
|
||||||
let expectation = expectation(description: message ?? "Awaiting publisher")
|
let expectation = expectation(description: message ?? "Awaiting publisher")
|
||||||
|
var hasFullfilled = false
|
||||||
let cancellable = publisher
|
let cancellable = publisher
|
||||||
.sink { completion in
|
.sink { completion in
|
||||||
switch completion {
|
switch completion {
|
||||||
case .failure(let error):
|
case .failure(let error):
|
||||||
result = .failure(error)
|
result = .failure(error)
|
||||||
|
expectation.fulfill()
|
||||||
case .finished:
|
case .finished:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
expectation.fulfill()
|
|
||||||
} receiveValue: { value in
|
} receiveValue: { value in
|
||||||
result = .success(value)
|
if condition(value), !hasFullfilled {
|
||||||
|
result = .success(value)
|
||||||
|
expectation.fulfill()
|
||||||
|
hasFullfilled = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return DeferredFulfillment<T.Output> {
|
return DeferredFulfillment<P.Output> {
|
||||||
await self.fulfillment(of: [expectation], timeout: timeout)
|
await self.fulfillment(of: [expectation], timeout: timeout)
|
||||||
cancellable.cancel()
|
cancellable.cancel()
|
||||||
let unwrappedResult = try XCTUnwrap(result, "Awaited publisher did not produce any output")
|
let unwrappedResult = try XCTUnwrap(result, "Awaited publisher did not produce any output")
|
||||||
@ -54,6 +57,32 @@ extension XCTestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// XCTest utility that assists in subscribing to a publisher and deferring the fulfilment and results until some other actions have been performed.
|
||||||
|
/// - Parameters:
|
||||||
|
/// - publisher: The publisher to wait on.
|
||||||
|
/// - keyPath: the key path for the expected values
|
||||||
|
/// - transitionValues: the values through which the keypath needs to transition through
|
||||||
|
/// - timeout: A timeout after which we give up.
|
||||||
|
/// - message: An optional custom expectation message
|
||||||
|
/// - Returns: The deferred fulfilment to be executed after some actions and that returns the result of the publisher.
|
||||||
|
func deferFulfillment<P: Publisher, K: KeyPath<P.Output, V>, V: Equatable>(_ publisher: P,
|
||||||
|
keyPath: K,
|
||||||
|
transitionValues: [V],
|
||||||
|
timeout: TimeInterval = 10,
|
||||||
|
message: String? = nil) -> DeferredFulfillment<P.Output> {
|
||||||
|
var expectedOrder = transitionValues
|
||||||
|
let deferred = deferFulfillment<P>(publisher, timeout: timeout, message: message) { value in
|
||||||
|
let receivedValue = value[keyPath: keyPath]
|
||||||
|
if let index = expectedOrder.firstIndex(where: { $0 == receivedValue }), index == 0 {
|
||||||
|
expectedOrder.remove(at: index)
|
||||||
|
}
|
||||||
|
|
||||||
|
return expectedOrder.isEmpty
|
||||||
|
}
|
||||||
|
|
||||||
|
return deferred
|
||||||
|
}
|
||||||
|
|
||||||
struct DeferredFulfillment<T> {
|
struct DeferredFulfillment<T> {
|
||||||
let closure: () async throws -> T
|
let closure: () async throws -> T
|
||||||
@discardableResult func fulfill() async throws -> T {
|
@discardableResult func fulfill() async throws -> T {
|
||||||
|
@ -83,8 +83,15 @@ class HomeScreenViewModelTests: XCTestCase {
|
|||||||
func testLeaveRoomAlert() async throws {
|
func testLeaveRoomAlert() async throws {
|
||||||
let mockRoomId = "1"
|
let mockRoomId = "1"
|
||||||
clientProxy.roomForIdentifierMocks[mockRoomId] = .init(with: .init(id: mockRoomId, displayName: "Some room"))
|
clientProxy.roomForIdentifierMocks[mockRoomId] = .init(with: .init(id: mockRoomId, displayName: "Some room"))
|
||||||
|
|
||||||
|
let deferred = deferFulfillment(context.$viewState) { value in
|
||||||
|
value.bindings.leaveRoomAlertItem != nil
|
||||||
|
}
|
||||||
|
|
||||||
context.send(viewAction: .leaveRoom(roomIdentifier: mockRoomId))
|
context.send(viewAction: .leaveRoom(roomIdentifier: mockRoomId))
|
||||||
await context.nextViewState()
|
|
||||||
|
try await deferred.fulfill()
|
||||||
|
|
||||||
XCTAssertEqual(context.leaveRoomAlertItem?.roomId, mockRoomId)
|
XCTAssertEqual(context.leaveRoomAlertItem?.roomId, mockRoomId)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,9 +100,16 @@ class HomeScreenViewModelTests: XCTestCase {
|
|||||||
let room: RoomProxyMock = .init(with: .init(id: mockRoomId, displayName: "Some room"))
|
let room: RoomProxyMock = .init(with: .init(id: mockRoomId, displayName: "Some room"))
|
||||||
room.leaveRoomClosure = { .failure(.failedLeavingRoom) }
|
room.leaveRoomClosure = { .failure(.failedLeavingRoom) }
|
||||||
clientProxy.roomForIdentifierMocks[mockRoomId] = room
|
clientProxy.roomForIdentifierMocks[mockRoomId] = room
|
||||||
|
|
||||||
|
let deferred = deferFulfillment(context.$viewState) { value in
|
||||||
|
value.bindings.alertInfo != nil
|
||||||
|
}
|
||||||
|
|
||||||
context.send(viewAction: .confirmLeaveRoom(roomIdentifier: mockRoomId))
|
context.send(viewAction: .confirmLeaveRoom(roomIdentifier: mockRoomId))
|
||||||
let state = await context.nextViewState()
|
|
||||||
XCTAssertNotNil(state?.bindings.alertInfo)
|
try await deferred.fulfill()
|
||||||
|
|
||||||
|
XCTAssertNotNil(context.alertInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testLeaveRoomSuccess() async throws {
|
func testLeaveRoomSuccess() async throws {
|
||||||
|
@ -67,25 +67,26 @@ class InviteUsersScreenViewModelTests: XCTestCase {
|
|||||||
let mockedMembers: [RoomMemberProxyMock] = [.mockAlice, .mockBob]
|
let mockedMembers: [RoomMemberProxyMock] = [.mockAlice, .mockBob]
|
||||||
setupWithRoomType(roomType: .room(roomProxy: RoomProxyMock(with: .init(displayName: "test", members: mockedMembers))))
|
setupWithRoomType(roomType: .room(roomProxy: RoomProxyMock(with: .init(displayName: "test", members: mockedMembers))))
|
||||||
|
|
||||||
let deferredState = deferFulfillment(viewModel.context.$viewState
|
let deferredState = deferFulfillment(viewModel.context.$viewState) { state in
|
||||||
.map(\.membershipState)
|
state.isUserSelected(.mockAlice)
|
||||||
.map(\.isEmpty)
|
|
||||||
.removeDuplicates()
|
|
||||||
.collect(2).first(), message: "2 states should be published.")
|
|
||||||
context.send(viewAction: .toggleUser(.mockAlice))
|
|
||||||
|
|
||||||
let states = try await deferredState.fulfill()
|
|
||||||
XCTAssertEqual(states, [true, false])
|
|
||||||
|
|
||||||
let deferredAction = deferFulfillment(viewModel.actions.first(), message: "1 action should be published.")
|
|
||||||
|
|
||||||
Task.detached(priority: .low) {
|
|
||||||
await self.context.send(viewAction: .proceed)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let action = try await deferredAction.fulfill()
|
context.send(viewAction: .toggleUser(.mockAlice))
|
||||||
|
|
||||||
guard case let .invite(members) = action else {
|
try await deferredState.fulfill()
|
||||||
|
|
||||||
|
let deferredAction = deferFulfillment(viewModel.actions) { action in
|
||||||
|
switch action {
|
||||||
|
case .invite:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
context.send(viewAction: .proceed)
|
||||||
|
|
||||||
|
guard case let .invite(members) = try await deferredAction.fulfill() else {
|
||||||
XCTFail("Sent action should be 'invite'")
|
XCTFail("Sent action should be 'invite'")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,13 @@ class InvitesScreenViewModelTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
setupViewModel(roomSummaries: invites)
|
setupViewModel(roomSummaries: invites)
|
||||||
|
|
||||||
let deferred = deferFulfillment(viewModel.actions.first(), message: "1 action should be published.")
|
let deferred = deferFulfillment(viewModel.actions) { action in
|
||||||
|
switch action {
|
||||||
|
case .openRoom:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
context.send(viewAction: .accept(.init(roomDetails: details, isUnread: false)))
|
context.send(viewAction: .accept(.init(roomDetails: details, isUnread: false)))
|
||||||
let action = try await deferred.fulfill()
|
let action = try await deferred.fulfill()
|
||||||
|
|
||||||
|
@ -49,9 +49,12 @@ class NotificationSettingsEditScreenViewModelTests: XCTestCase {
|
|||||||
userSession: userSession,
|
userSession: userSession,
|
||||||
notificationSettingsProxy: notificationSettingsProxy)
|
notificationSettingsProxy: notificationSettingsProxy)
|
||||||
|
|
||||||
let deferred = deferFulfillment(viewModel.context.$viewState.map(\.defaultMode)
|
let deferred = deferFulfillment(viewModel.context.$viewState) { state in
|
||||||
.first(where: { !$0.isNil }))
|
state.defaultMode != nil
|
||||||
|
}
|
||||||
|
|
||||||
viewModel.fetchInitialContent()
|
viewModel.fetchInitialContent()
|
||||||
|
|
||||||
try await deferred.fulfill()
|
try await deferred.fulfill()
|
||||||
|
|
||||||
// `getDefaultRoomNotificationModeIsEncryptedIsOneToOne` must have been called twice (for encrypted and unencrypted group chats)
|
// `getDefaultRoomNotificationModeIsEncryptedIsOneToOne` must have been called twice (for encrypted and unencrypted group chats)
|
||||||
@ -74,20 +77,19 @@ class NotificationSettingsEditScreenViewModelTests: XCTestCase {
|
|||||||
viewModel = NotificationSettingsEditScreenViewModel(chatType: .groupChat,
|
viewModel = NotificationSettingsEditScreenViewModel(chatType: .groupChat,
|
||||||
userSession: userSession,
|
userSession: userSession,
|
||||||
notificationSettingsProxy: notificationSettingsProxy)
|
notificationSettingsProxy: notificationSettingsProxy)
|
||||||
let deferred = deferFulfillment(viewModel.context.$viewState.map(\.defaultMode)
|
let deferred = deferFulfillment(viewModel.context.$viewState) { state in
|
||||||
.first(where: { !$0.isNil }))
|
state.defaultMode != nil
|
||||||
|
}
|
||||||
|
|
||||||
viewModel.fetchInitialContent()
|
viewModel.fetchInitialContent()
|
||||||
|
|
||||||
try await deferred.fulfill()
|
try await deferred.fulfill()
|
||||||
|
|
||||||
// Set mode to .allMessages
|
var deferredViewState = deferFulfillment(viewModel.context.$viewState, keyPath: \.pendingMode, transitionValues: [nil, .allMessages, nil])
|
||||||
let deferredViewState = deferFulfillment(context.$viewState
|
|
||||||
.map(\.pendingMode)
|
|
||||||
.removeDuplicates()
|
|
||||||
.collect(3).first())
|
|
||||||
context.send(viewAction: .setMode(.allMessages))
|
context.send(viewAction: .setMode(.allMessages))
|
||||||
let pendingModes = try await deferredViewState.fulfill()
|
|
||||||
|
|
||||||
XCTAssertEqual(pendingModes, [nil, .allMessages, nil])
|
try await deferredViewState.fulfill()
|
||||||
|
|
||||||
// `setDefaultRoomNotificationModeIsEncryptedIsOneToOneMode` must have been called twice (for encrypted and unencrypted group chats)
|
// `setDefaultRoomNotificationModeIsEncryptedIsOneToOneMode` must have been called twice (for encrypted and unencrypted group chats)
|
||||||
let invocations = notificationSettingsProxy.setDefaultRoomNotificationModeIsEncryptedIsOneToOneModeReceivedInvocations
|
let invocations = notificationSettingsProxy.setDefaultRoomNotificationModeIsEncryptedIsOneToOneModeReceivedInvocations
|
||||||
@ -101,34 +103,36 @@ class NotificationSettingsEditScreenViewModelTests: XCTestCase {
|
|||||||
XCTAssertEqual(invocations[1].isOneToOne, false)
|
XCTAssertEqual(invocations[1].isOneToOne, false)
|
||||||
XCTAssertEqual(invocations[1].mode, .allMessages)
|
XCTAssertEqual(invocations[1].mode, .allMessages)
|
||||||
|
|
||||||
// The default mode should be updated
|
deferredViewState = deferFulfillment(viewModel.context.$viewState,
|
||||||
let deferredNewViewState = deferFulfillment(context.$viewState
|
keyPath: \.defaultMode,
|
||||||
.map(\.defaultMode)
|
transitionValues: [.allMessages])
|
||||||
.first(where: { $0 == .allMessages }))
|
|
||||||
try await deferredNewViewState.fulfill()
|
try await deferredViewState.fulfill()
|
||||||
|
|
||||||
XCTAssertEqual(context.viewState.defaultMode, .allMessages)
|
XCTAssertEqual(context.viewState.defaultMode, .allMessages)
|
||||||
XCTAssertNil(context.viewState.bindings.alertInfo)
|
XCTAssertNil(context.viewState.bindings.alertInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testSetModeMentions() async throws {
|
func testSetModeMentions() async throws {
|
||||||
viewModel = NotificationSettingsEditScreenViewModel(chatType: .groupChat,
|
viewModel = NotificationSettingsEditScreenViewModel(chatType: .groupChat,
|
||||||
userSession: userSession,
|
userSession: userSession,
|
||||||
notificationSettingsProxy: notificationSettingsProxy)
|
notificationSettingsProxy: notificationSettingsProxy)
|
||||||
let deferred = deferFulfillment(viewModel.context.$viewState.map(\.defaultMode)
|
|
||||||
.first(where: { !$0.isNil }))
|
|
||||||
viewModel.fetchInitialContent()
|
|
||||||
try await deferred.fulfill()
|
|
||||||
|
|
||||||
// Set mode to .allMessages
|
|
||||||
let deferredViewState = deferFulfillment(context.$viewState
|
|
||||||
.map(\.pendingMode)
|
|
||||||
.removeDuplicates()
|
|
||||||
.collect(3).first())
|
|
||||||
context.send(viewAction: .setMode(.mentionsAndKeywordsOnly))
|
|
||||||
let pendingModes = try await deferredViewState.fulfill()
|
|
||||||
|
|
||||||
XCTAssertEqual(pendingModes, [nil, .mentionsAndKeywordsOnly, nil])
|
let deferred = deferFulfillment(viewModel.context.$viewState) { state in
|
||||||
|
state.defaultMode != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
viewModel.fetchInitialContent()
|
||||||
|
|
||||||
|
try await deferred.fulfill()
|
||||||
|
|
||||||
|
var deferredViewState = deferFulfillment(viewModel.context.$viewState,
|
||||||
|
keyPath: \.pendingMode,
|
||||||
|
transitionValues: [nil, .mentionsAndKeywordsOnly, nil])
|
||||||
|
|
||||||
|
context.send(viewAction: .setMode(.mentionsAndKeywordsOnly))
|
||||||
|
|
||||||
|
try await deferredViewState.fulfill()
|
||||||
|
|
||||||
// `setDefaultRoomNotificationModeIsEncryptedIsOneToOneMode` must have been called twice (for encrypted and unencrypted group chats)
|
// `setDefaultRoomNotificationModeIsEncryptedIsOneToOneMode` must have been called twice (for encrypted and unencrypted group chats)
|
||||||
let invocations = notificationSettingsProxy.setDefaultRoomNotificationModeIsEncryptedIsOneToOneModeReceivedInvocations
|
let invocations = notificationSettingsProxy.setDefaultRoomNotificationModeIsEncryptedIsOneToOneModeReceivedInvocations
|
||||||
@ -142,37 +146,39 @@ class NotificationSettingsEditScreenViewModelTests: XCTestCase {
|
|||||||
XCTAssertEqual(invocations[1].isOneToOne, false)
|
XCTAssertEqual(invocations[1].isOneToOne, false)
|
||||||
XCTAssertEqual(invocations[1].mode, .mentionsAndKeywordsOnly)
|
XCTAssertEqual(invocations[1].mode, .mentionsAndKeywordsOnly)
|
||||||
|
|
||||||
// The default mode should be updated
|
deferredViewState = deferFulfillment(viewModel.context.$viewState,
|
||||||
let deferredNewViewState = deferFulfillment(context.$viewState
|
keyPath: \.defaultMode,
|
||||||
.map(\.defaultMode)
|
transitionValues: [.mentionsAndKeywordsOnly])
|
||||||
.first(where: { $0 == .mentionsAndKeywordsOnly }))
|
|
||||||
try await deferredNewViewState.fulfill()
|
try await deferredViewState.fulfill()
|
||||||
|
|
||||||
XCTAssertEqual(context.viewState.defaultMode, .mentionsAndKeywordsOnly)
|
XCTAssertEqual(context.viewState.defaultMode, .mentionsAndKeywordsOnly)
|
||||||
XCTAssertNil(context.viewState.bindings.alertInfo)
|
XCTAssertNil(context.viewState.bindings.alertInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testSetModeDirectChats() async throws {
|
func testSetModeDirectChats() async throws {
|
||||||
notificationSettingsProxy.getDefaultRoomNotificationModeIsEncryptedIsOneToOneReturnValue = .mentionsAndKeywordsOnly
|
notificationSettingsProxy.getDefaultRoomNotificationModeIsEncryptedIsOneToOneReturnValue = .mentionsAndKeywordsOnly
|
||||||
// Initialize for direct chats
|
// Initialize for direct chats
|
||||||
viewModel = NotificationSettingsEditScreenViewModel(chatType: .oneToOneChat,
|
viewModel = NotificationSettingsEditScreenViewModel(chatType: .oneToOneChat,
|
||||||
userSession: userSession,
|
userSession: userSession,
|
||||||
notificationSettingsProxy: notificationSettingsProxy)
|
notificationSettingsProxy: notificationSettingsProxy)
|
||||||
let deferred = deferFulfillment(viewModel.context.$viewState.map(\.defaultMode)
|
|
||||||
.first(where: { !$0.isNil }))
|
let deferred = deferFulfillment(viewModel.context.$viewState) { state in
|
||||||
|
state.defaultMode != nil
|
||||||
|
}
|
||||||
|
|
||||||
viewModel.fetchInitialContent()
|
viewModel.fetchInitialContent()
|
||||||
|
|
||||||
try await deferred.fulfill()
|
try await deferred.fulfill()
|
||||||
|
|
||||||
// Set mode to .allMessages
|
let deferredViewState = deferFulfillment(viewModel.context.$viewState,
|
||||||
let deferredViewState = deferFulfillment(context.$viewState
|
keyPath: \.pendingMode,
|
||||||
.map(\.pendingMode)
|
transitionValues: [nil, .allMessages, nil])
|
||||||
.removeDuplicates()
|
|
||||||
.collect(3).first())
|
|
||||||
context.send(viewAction: .setMode(.allMessages))
|
context.send(viewAction: .setMode(.allMessages))
|
||||||
let pendingModes = try await deferredViewState.fulfill()
|
|
||||||
|
|
||||||
XCTAssertEqual(pendingModes, [nil, .allMessages, nil])
|
|
||||||
|
|
||||||
|
try await deferredViewState.fulfill()
|
||||||
|
|
||||||
// `setDefaultRoomNotificationModeIsEncryptedIsOneToOneMode` must have been called twice (for encrypted and unencrypted direct chats)
|
// `setDefaultRoomNotificationModeIsEncryptedIsOneToOneMode` must have been called twice (for encrypted and unencrypted direct chats)
|
||||||
let invocations = notificationSettingsProxy.setDefaultRoomNotificationModeIsEncryptedIsOneToOneModeReceivedInvocations
|
let invocations = notificationSettingsProxy.setDefaultRoomNotificationModeIsEncryptedIsOneToOneModeReceivedInvocations
|
||||||
XCTAssertEqual(notificationSettingsProxy.setDefaultRoomNotificationModeIsEncryptedIsOneToOneModeCallsCount, 2)
|
XCTAssertEqual(notificationSettingsProxy.setDefaultRoomNotificationModeIsEncryptedIsOneToOneModeCallsCount, 2)
|
||||||
@ -185,43 +191,53 @@ class NotificationSettingsEditScreenViewModelTests: XCTestCase {
|
|||||||
XCTAssertEqual(invocations[1].isOneToOne, true)
|
XCTAssertEqual(invocations[1].isOneToOne, true)
|
||||||
XCTAssertEqual(invocations[1].mode, .allMessages)
|
XCTAssertEqual(invocations[1].mode, .allMessages)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testSetModeFailure() async throws {
|
func testSetModeFailure() async throws {
|
||||||
notificationSettingsProxy.getDefaultRoomNotificationModeIsEncryptedIsOneToOneReturnValue = .mentionsAndKeywordsOnly
|
notificationSettingsProxy.getDefaultRoomNotificationModeIsEncryptedIsOneToOneReturnValue = .mentionsAndKeywordsOnly
|
||||||
notificationSettingsProxy.setDefaultRoomNotificationModeIsEncryptedIsOneToOneModeThrowableError = NotificationSettingsError.Generic(message: "error")
|
notificationSettingsProxy.setDefaultRoomNotificationModeIsEncryptedIsOneToOneModeThrowableError = NotificationSettingsError.Generic(message: "error")
|
||||||
viewModel = NotificationSettingsEditScreenViewModel(chatType: .oneToOneChat,
|
viewModel = NotificationSettingsEditScreenViewModel(chatType: .oneToOneChat,
|
||||||
userSession: userSession,
|
userSession: userSession,
|
||||||
notificationSettingsProxy: notificationSettingsProxy)
|
notificationSettingsProxy: notificationSettingsProxy)
|
||||||
let deferred = deferFulfillment(viewModel.context.$viewState.map(\.defaultMode)
|
|
||||||
.first(where: { !$0.isNil }))
|
|
||||||
viewModel.fetchInitialContent()
|
|
||||||
try await deferred.fulfill()
|
|
||||||
|
|
||||||
// Set mode to .allMessages
|
|
||||||
let deferredViewState = deferFulfillment(context.$viewState
|
|
||||||
.map(\.pendingMode)
|
|
||||||
.removeDuplicates()
|
|
||||||
.collect(3).first())
|
|
||||||
context.send(viewAction: .setMode(.allMessages))
|
|
||||||
let pendingModes = try await deferredViewState.fulfill()
|
|
||||||
|
|
||||||
XCTAssertEqual(pendingModes, [nil, .allMessages, nil])
|
let deferred = deferFulfillment(viewModel.context.$viewState) { state in
|
||||||
|
state.defaultMode != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
viewModel.fetchInitialContent()
|
||||||
|
|
||||||
|
try await deferred.fulfill()
|
||||||
|
|
||||||
|
let deferredViewState = deferFulfillment(viewModel.context.$viewState,
|
||||||
|
keyPath: \.pendingMode,
|
||||||
|
transitionValues: [nil, .allMessages, nil])
|
||||||
|
|
||||||
|
context.send(viewAction: .setMode(.allMessages))
|
||||||
|
|
||||||
|
try await deferredViewState.fulfill()
|
||||||
|
|
||||||
XCTAssertNotNil(context.viewState.bindings.alertInfo)
|
XCTAssertNotNil(context.viewState.bindings.alertInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testSelectRoom() async throws {
|
func testSelectRoom() async throws {
|
||||||
let roomID = "!roomidentifier:matrix.org"
|
let roomID = "!roomidentifier:matrix.org"
|
||||||
viewModel = NotificationSettingsEditScreenViewModel(chatType: .oneToOneChat,
|
viewModel = NotificationSettingsEditScreenViewModel(chatType: .oneToOneChat,
|
||||||
userSession: userSession,
|
userSession: userSession,
|
||||||
notificationSettingsProxy: notificationSettingsProxy)
|
notificationSettingsProxy: notificationSettingsProxy)
|
||||||
|
|
||||||
let deferredActions = deferFulfillment(viewModel.actions.first())
|
let deferredActions = deferFulfillment(viewModel.actions) { action in
|
||||||
|
switch action {
|
||||||
|
case .requestRoomNotificationSettingsPresentation:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
context.send(viewAction: .selectRoom(roomIdentifier: roomID))
|
context.send(viewAction: .selectRoom(roomIdentifier: roomID))
|
||||||
let sentActions = try await deferredActions.fulfill()
|
|
||||||
|
let sentAction = try await deferredActions.fulfill()
|
||||||
|
|
||||||
let expectedAction = NotificationSettingsEditScreenViewModelAction.requestRoomNotificationSettingsPresentation(roomID: roomID)
|
let expectedAction = NotificationSettingsEditScreenViewModelAction.requestRoomNotificationSettingsPresentation(roomID: roomID)
|
||||||
guard case let .requestRoomNotificationSettingsPresentation(roomID: receivedRoomID) = sentActions, receivedRoomID == roomID else {
|
guard case let .requestRoomNotificationSettingsPresentation(roomID: receivedRoomID) = sentAction, receivedRoomID == roomID else {
|
||||||
XCTFail("Expected action \(expectedAction), but was \(sentActions)")
|
XCTFail("Expected action \(expectedAction), but was \(sentAction)")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,10 +27,10 @@ class NotificationSettingsScreenViewModelTests: XCTestCase {
|
|||||||
private var userSession: UserSessionProtocol!
|
private var userSession: UserSessionProtocol!
|
||||||
private var userNotificationCenter: UserNotificationCenterMock!
|
private var userNotificationCenter: UserNotificationCenterMock!
|
||||||
private var notificationSettingsProxy: NotificationSettingsProxyMock!
|
private var notificationSettingsProxy: NotificationSettingsProxyMock!
|
||||||
|
|
||||||
@MainActor override func setUpWithError() throws {
|
@MainActor override func setUpWithError() throws {
|
||||||
AppSettings.reset()
|
AppSettings.reset()
|
||||||
|
|
||||||
userNotificationCenter = UserNotificationCenterMock()
|
userNotificationCenter = UserNotificationCenterMock()
|
||||||
userNotificationCenter.authorizationStatusReturnValue = .authorized
|
userNotificationCenter.authorizationStatusReturnValue = .authorized
|
||||||
appSettings = AppSettings()
|
appSettings = AppSettings()
|
||||||
@ -38,10 +38,10 @@ class NotificationSettingsScreenViewModelTests: XCTestCase {
|
|||||||
notificationSettingsProxy.getDefaultRoomNotificationModeIsEncryptedIsOneToOneReturnValue = .allMessages
|
notificationSettingsProxy.getDefaultRoomNotificationModeIsEncryptedIsOneToOneReturnValue = .allMessages
|
||||||
notificationSettingsProxy.isRoomMentionEnabledReturnValue = true
|
notificationSettingsProxy.isRoomMentionEnabledReturnValue = true
|
||||||
notificationSettingsProxy.isCallEnabledReturnValue = true
|
notificationSettingsProxy.isCallEnabledReturnValue = true
|
||||||
|
|
||||||
let clientProxy = MockClientProxy(userID: "@a:b.com")
|
let clientProxy = MockClientProxy(userID: "@a:b.com")
|
||||||
userSession = MockUserSession(clientProxy: clientProxy, mediaProvider: MockMediaProvider())
|
userSession = MockUserSession(clientProxy: clientProxy, mediaProvider: MockMediaProvider())
|
||||||
|
|
||||||
viewModel = NotificationSettingsScreenViewModel(userSession: userSession,
|
viewModel = NotificationSettingsScreenViewModel(userSession: userSession,
|
||||||
appSettings: appSettings,
|
appSettings: appSettings,
|
||||||
userNotificationCenter: userNotificationCenter,
|
userNotificationCenter: userNotificationCenter,
|
||||||
@ -49,19 +49,19 @@ class NotificationSettingsScreenViewModelTests: XCTestCase {
|
|||||||
isModallyPresented: false)
|
isModallyPresented: false)
|
||||||
context = viewModel.context
|
context = viewModel.context
|
||||||
}
|
}
|
||||||
|
|
||||||
func testEnableNotifications() {
|
func testEnableNotifications() {
|
||||||
appSettings.enableNotifications = false
|
appSettings.enableNotifications = false
|
||||||
context.send(viewAction: .changedEnableNotifications)
|
context.send(viewAction: .changedEnableNotifications)
|
||||||
XCTAssertTrue(appSettings.enableNotifications)
|
XCTAssertTrue(appSettings.enableNotifications)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testDisableNotifications() {
|
func testDisableNotifications() {
|
||||||
appSettings.enableNotifications = true
|
appSettings.enableNotifications = true
|
||||||
context.send(viewAction: .changedEnableNotifications)
|
context.send(viewAction: .changedEnableNotifications)
|
||||||
XCTAssertFalse(appSettings.enableNotifications)
|
XCTAssertFalse(appSettings.enableNotifications)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testFetchSettings() async throws {
|
func testFetchSettings() async throws {
|
||||||
notificationSettingsProxy.getDefaultRoomNotificationModeIsEncryptedIsOneToOneClosure = { isEncrypted, isOneToOne in
|
notificationSettingsProxy.getDefaultRoomNotificationModeIsEncryptedIsOneToOneClosure = { isEncrypted, isOneToOne in
|
||||||
switch (isEncrypted, isOneToOne) {
|
switch (isEncrypted, isOneToOne) {
|
||||||
@ -71,21 +71,25 @@ class NotificationSettingsScreenViewModelTests: XCTestCase {
|
|||||||
return .mentionsAndKeywordsOnly
|
return .mentionsAndKeywordsOnly
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let deferred = deferFulfillment(viewModel.context.$viewState.map(\.settings)
|
|
||||||
.first(where: { $0 != nil }))
|
|
||||||
notificationSettingsProxy.callbacks.send(.settingsDidChange)
|
|
||||||
try await deferred.fulfill()
|
|
||||||
|
|
||||||
|
let deferred = deferFulfillment(viewModel.context.$viewState) { state in
|
||||||
|
state.settings != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
notificationSettingsProxy.callbacks.send(.settingsDidChange)
|
||||||
|
|
||||||
|
try await deferred.fulfill()
|
||||||
|
|
||||||
XCTAssertEqual(notificationSettingsProxy.getDefaultRoomNotificationModeIsEncryptedIsOneToOneCallsCount, 4)
|
XCTAssertEqual(notificationSettingsProxy.getDefaultRoomNotificationModeIsEncryptedIsOneToOneCallsCount, 4)
|
||||||
XCTAssert(notificationSettingsProxy.isRoomMentionEnabledCalled)
|
XCTAssert(notificationSettingsProxy.isRoomMentionEnabledCalled)
|
||||||
XCTAssert(notificationSettingsProxy.isCallEnabledCalled)
|
XCTAssert(notificationSettingsProxy.isCallEnabledCalled)
|
||||||
|
|
||||||
XCTAssertEqual(context.viewState.settings?.groupChatsMode, .mentionsAndKeywordsOnly)
|
XCTAssertEqual(context.viewState.settings?.groupChatsMode, .mentionsAndKeywordsOnly)
|
||||||
XCTAssertEqual(context.viewState.settings?.directChatsMode, .allMessages)
|
XCTAssertEqual(context.viewState.settings?.directChatsMode, .allMessages)
|
||||||
XCTAssertEqual(context.viewState.settings?.inconsistentSettings, [])
|
XCTAssertEqual(context.viewState.settings?.inconsistentSettings, [])
|
||||||
XCTAssertNil(context.viewState.bindings.alertInfo)
|
XCTAssertNil(context.viewState.bindings.alertInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testInconsistentGroupChatsSettings() async throws {
|
func testInconsistentGroupChatsSettings() async throws {
|
||||||
notificationSettingsProxy.getDefaultRoomNotificationModeIsEncryptedIsOneToOneClosure = { isEncrypted, isOneToOne in
|
notificationSettingsProxy.getDefaultRoomNotificationModeIsEncryptedIsOneToOneClosure = { isEncrypted, isOneToOne in
|
||||||
switch (isEncrypted, isOneToOne) {
|
switch (isEncrypted, isOneToOne) {
|
||||||
@ -97,16 +101,19 @@ class NotificationSettingsScreenViewModelTests: XCTestCase {
|
|||||||
return .allMessages
|
return .allMessages
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let deferred = deferFulfillment(viewModel.context.$viewState.map(\.settings)
|
let deferred = deferFulfillment(viewModel.context.$viewState) { state in
|
||||||
.first(where: { $0 != nil }))
|
state.settings != nil
|
||||||
notificationSettingsProxy.callbacks.send(.settingsDidChange)
|
}
|
||||||
try await deferred.fulfill()
|
|
||||||
|
|
||||||
|
notificationSettingsProxy.callbacks.send(.settingsDidChange)
|
||||||
|
|
||||||
|
try await deferred.fulfill()
|
||||||
|
|
||||||
XCTAssertEqual(context.viewState.settings?.groupChatsMode, .allMessages)
|
XCTAssertEqual(context.viewState.settings?.groupChatsMode, .allMessages)
|
||||||
XCTAssertEqual(context.viewState.settings?.inconsistentSettings, [.init(chatType: .groupChat, isEncrypted: false)])
|
XCTAssertEqual(context.viewState.settings?.inconsistentSettings, [.init(chatType: .groupChat, isEncrypted: false)])
|
||||||
}
|
}
|
||||||
|
|
||||||
func testInconsistentDirectChatsSettings() async throws {
|
func testInconsistentDirectChatsSettings() async throws {
|
||||||
notificationSettingsProxy.getDefaultRoomNotificationModeIsEncryptedIsOneToOneClosure = { isEncrypted, isOneToOne in
|
notificationSettingsProxy.getDefaultRoomNotificationModeIsEncryptedIsOneToOneClosure = { isEncrypted, isOneToOne in
|
||||||
switch (isEncrypted, isOneToOne) {
|
switch (isEncrypted, isOneToOne) {
|
||||||
@ -118,16 +125,19 @@ class NotificationSettingsScreenViewModelTests: XCTestCase {
|
|||||||
return .allMessages
|
return .allMessages
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let deferred = deferFulfillment(viewModel.context.$viewState.map(\.settings)
|
let deferred = deferFulfillment(viewModel.context.$viewState) { state in
|
||||||
.first(where: { $0 != nil }))
|
state.settings != nil
|
||||||
|
}
|
||||||
|
|
||||||
notificationSettingsProxy.callbacks.send(.settingsDidChange)
|
notificationSettingsProxy.callbacks.send(.settingsDidChange)
|
||||||
|
|
||||||
try await deferred.fulfill()
|
try await deferred.fulfill()
|
||||||
|
|
||||||
XCTAssertEqual(context.viewState.settings?.directChatsMode, .allMessages)
|
XCTAssertEqual(context.viewState.settings?.directChatsMode, .allMessages)
|
||||||
XCTAssertEqual(context.viewState.settings?.inconsistentSettings, [.init(chatType: .oneToOneChat, isEncrypted: false)])
|
XCTAssertEqual(context.viewState.settings?.inconsistentSettings, [.init(chatType: .oneToOneChat, isEncrypted: false)])
|
||||||
}
|
}
|
||||||
|
|
||||||
func testFixInconsistentSettings() async throws {
|
func testFixInconsistentSettings() async throws {
|
||||||
// Initialize with a configuration mismatch where encrypted one-to-one chats is `.allMessages` and unencrypted one-to-one chats is `.mentionsAndKeywordsOnly`
|
// Initialize with a configuration mismatch where encrypted one-to-one chats is `.allMessages` and unencrypted one-to-one chats is `.mentionsAndKeywordsOnly`
|
||||||
notificationSettingsProxy.getDefaultRoomNotificationModeIsEncryptedIsOneToOneClosure = { isEncrypted, isOneToOne in
|
notificationSettingsProxy.getDefaultRoomNotificationModeIsEncryptedIsOneToOneClosure = { isEncrypted, isOneToOne in
|
||||||
@ -140,25 +150,24 @@ class NotificationSettingsScreenViewModelTests: XCTestCase {
|
|||||||
return .allMessages
|
return .allMessages
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let deferred = deferFulfillment(viewModel.context.$viewState.map(\.settings)
|
var deferred = deferFulfillment(viewModel.context.$viewState) { state in
|
||||||
.first(where: { $0 != nil }))
|
state.settings != nil
|
||||||
notificationSettingsProxy.callbacks.send(.settingsDidChange)
|
}
|
||||||
try await deferred.fulfill()
|
|
||||||
|
|
||||||
|
notificationSettingsProxy.callbacks.send(.settingsDidChange)
|
||||||
|
|
||||||
|
try await deferred.fulfill()
|
||||||
|
|
||||||
XCTAssertEqual(context.viewState.settings?.directChatsMode, .allMessages)
|
XCTAssertEqual(context.viewState.settings?.directChatsMode, .allMessages)
|
||||||
XCTAssertEqual(context.viewState.settings?.inconsistentSettings, [.init(chatType: .oneToOneChat, isEncrypted: false)])
|
XCTAssertEqual(context.viewState.settings?.inconsistentSettings, [.init(chatType: .oneToOneChat, isEncrypted: false)])
|
||||||
|
|
||||||
let deferredState = deferFulfillment(viewModel.context.$viewState
|
deferred = deferFulfillment(viewModel.context.$viewState, keyPath: \.fixingConfigurationMismatch, transitionValues: [false, true, false])
|
||||||
.map(\.fixingConfigurationMismatch)
|
|
||||||
.removeDuplicates()
|
|
||||||
.collect(3)
|
|
||||||
.first())
|
|
||||||
context.send(viewAction: .fixConfigurationMismatchTapped)
|
context.send(viewAction: .fixConfigurationMismatchTapped)
|
||||||
let fixingStates = try await deferredState.fulfill()
|
|
||||||
|
|
||||||
XCTAssertEqual(fixingStates, [false, true, false])
|
|
||||||
|
|
||||||
|
try await deferred.fulfill()
|
||||||
|
|
||||||
// Ensure we only fix the invalid setting: unencrypted one-to-one chats should be set to `.allMessages` (to match encrypted one-to-one chats)
|
// Ensure we only fix the invalid setting: unencrypted one-to-one chats should be set to `.allMessages` (to match encrypted one-to-one chats)
|
||||||
XCTAssertEqual(notificationSettingsProxy.setDefaultRoomNotificationModeIsEncryptedIsOneToOneModeCallsCount, 1)
|
XCTAssertEqual(notificationSettingsProxy.setDefaultRoomNotificationModeIsEncryptedIsOneToOneModeCallsCount, 1)
|
||||||
let callArguments = notificationSettingsProxy.setDefaultRoomNotificationModeIsEncryptedIsOneToOneModeReceivedArguments
|
let callArguments = notificationSettingsProxy.setDefaultRoomNotificationModeIsEncryptedIsOneToOneModeReceivedArguments
|
||||||
@ -166,7 +175,7 @@ class NotificationSettingsScreenViewModelTests: XCTestCase {
|
|||||||
XCTAssertEqual(callArguments?.isOneToOne, true)
|
XCTAssertEqual(callArguments?.isOneToOne, true)
|
||||||
XCTAssertEqual(callArguments?.mode, .allMessages)
|
XCTAssertEqual(callArguments?.mode, .allMessages)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testFixAllInconsistentSettings() async throws {
|
func testFixAllInconsistentSettings() async throws {
|
||||||
// Initialize with a configuration mismatch where
|
// Initialize with a configuration mismatch where
|
||||||
// - encrypted one-to-one chats is `.allMessages` and unencrypted one-to-one chats is `.mentionsAndKeywordsOnly`
|
// - encrypted one-to-one chats is `.allMessages` and unencrypted one-to-one chats is `.mentionsAndKeywordsOnly`
|
||||||
@ -179,25 +188,32 @@ class NotificationSettingsScreenViewModelTests: XCTestCase {
|
|||||||
return .mentionsAndKeywordsOnly
|
return .mentionsAndKeywordsOnly
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let deferred = deferFulfillment(viewModel.context.$viewState.map(\.settings)
|
var deferred = deferFulfillment(viewModel.context.$viewState) { state in
|
||||||
.first(where: { $0 != nil }))
|
state.settings != nil
|
||||||
|
}
|
||||||
|
|
||||||
notificationSettingsProxy.callbacks.send(.settingsDidChange)
|
notificationSettingsProxy.callbacks.send(.settingsDidChange)
|
||||||
|
|
||||||
try await deferred.fulfill()
|
try await deferred.fulfill()
|
||||||
|
|
||||||
XCTAssertEqual(context.viewState.settings?.directChatsMode, .allMessages)
|
XCTAssertEqual(context.viewState.settings?.directChatsMode, .allMessages)
|
||||||
XCTAssertEqual(context.viewState.settings?.inconsistentSettings, [.init(chatType: .groupChat, isEncrypted: false), .init(chatType: .oneToOneChat, isEncrypted: false)])
|
XCTAssertEqual(context.viewState.settings?.inconsistentSettings, [.init(chatType: .groupChat, isEncrypted: false), .init(chatType: .oneToOneChat, isEncrypted: false)])
|
||||||
|
|
||||||
|
deferred = deferFulfillment(viewModel.context.$viewState) { state in
|
||||||
|
state.fixingConfigurationMismatch == true
|
||||||
|
}
|
||||||
|
|
||||||
let deferredState = deferFulfillment(viewModel.context.$viewState
|
|
||||||
.map(\.fixingConfigurationMismatch)
|
|
||||||
.removeDuplicates()
|
|
||||||
.collect(3)
|
|
||||||
.first())
|
|
||||||
context.send(viewAction: .fixConfigurationMismatchTapped)
|
context.send(viewAction: .fixConfigurationMismatchTapped)
|
||||||
let fixingStates = try await deferredState.fulfill()
|
|
||||||
|
|
||||||
XCTAssertEqual(fixingStates, [false, true, false])
|
try await deferred.fulfill()
|
||||||
|
|
||||||
|
deferred = deferFulfillment(viewModel.context.$viewState) { state in
|
||||||
|
state.fixingConfigurationMismatch == false
|
||||||
|
}
|
||||||
|
|
||||||
|
try await deferred.fulfill()
|
||||||
|
|
||||||
// All problems should be fixed
|
// All problems should be fixed
|
||||||
XCTAssertEqual(notificationSettingsProxy.setDefaultRoomNotificationModeIsEncryptedIsOneToOneModeCallsCount, 2)
|
XCTAssertEqual(notificationSettingsProxy.setDefaultRoomNotificationModeIsEncryptedIsOneToOneModeCallsCount, 2)
|
||||||
let callArguments = notificationSettingsProxy.setDefaultRoomNotificationModeIsEncryptedIsOneToOneModeReceivedInvocations
|
let callArguments = notificationSettingsProxy.setDefaultRoomNotificationModeIsEncryptedIsOneToOneModeReceivedInvocations
|
||||||
@ -210,112 +226,164 @@ class NotificationSettingsScreenViewModelTests: XCTestCase {
|
|||||||
XCTAssertEqual(callArguments[1].isOneToOne, true)
|
XCTAssertEqual(callArguments[1].isOneToOne, true)
|
||||||
XCTAssertEqual(callArguments[1].mode, .allMessages)
|
XCTAssertEqual(callArguments[1].mode, .allMessages)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testToggleRoomMentionOff() async throws {
|
func testToggleRoomMentionOff() async throws {
|
||||||
notificationSettingsProxy.isRoomMentionEnabledReturnValue = true
|
notificationSettingsProxy.isRoomMentionEnabledReturnValue = true
|
||||||
let deferredInitialFetch = deferFulfillment(viewModel.context.$viewState.map(\.settings)
|
|
||||||
.first(where: { $0 != nil }))
|
let deferredState = deferFulfillment(viewModel.context.$viewState) { state in
|
||||||
|
state.settings != nil
|
||||||
|
}
|
||||||
|
|
||||||
notificationSettingsProxy.callbacks.send(.settingsDidChange)
|
notificationSettingsProxy.callbacks.send(.settingsDidChange)
|
||||||
try await deferredInitialFetch.fulfill()
|
|
||||||
|
|
||||||
|
try await deferredState.fulfill()
|
||||||
|
|
||||||
context.roomMentionsEnabled = false
|
context.roomMentionsEnabled = false
|
||||||
let deferred = deferFulfillment(notificationSettingsProxy.callbacks
|
|
||||||
.first(where: { $0 == .settingsDidChange }))
|
|
||||||
context.send(viewAction: .roomMentionChanged)
|
|
||||||
try await deferred.fulfill()
|
|
||||||
|
|
||||||
|
let deferred = deferFulfillment(notificationSettingsProxy.callbacks) { callback in
|
||||||
|
callback == .settingsDidChange
|
||||||
|
}
|
||||||
|
|
||||||
|
context.send(viewAction: .roomMentionChanged)
|
||||||
|
|
||||||
|
try await deferred.fulfill()
|
||||||
|
|
||||||
XCTAssert(notificationSettingsProxy.setRoomMentionEnabledEnabledCalled)
|
XCTAssert(notificationSettingsProxy.setRoomMentionEnabledEnabledCalled)
|
||||||
XCTAssertEqual(notificationSettingsProxy.setRoomMentionEnabledEnabledReceivedEnabled, false)
|
XCTAssertEqual(notificationSettingsProxy.setRoomMentionEnabledEnabledReceivedEnabled, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testToggleRoomMentionOn() async throws {
|
func testToggleRoomMentionOn() async throws {
|
||||||
notificationSettingsProxy.isRoomMentionEnabledReturnValue = false
|
notificationSettingsProxy.isRoomMentionEnabledReturnValue = false
|
||||||
let deferredInitialFetch = deferFulfillment(viewModel.context.$viewState.map(\.settings)
|
|
||||||
.first(where: { $0 != nil }))
|
let deferredInitialFetch = deferFulfillment(viewModel.context.$viewState) { state in
|
||||||
|
state.settings != nil
|
||||||
|
}
|
||||||
|
|
||||||
viewModel.fetchInitialContent()
|
viewModel.fetchInitialContent()
|
||||||
try await deferredInitialFetch.fulfill()
|
try await deferredInitialFetch.fulfill()
|
||||||
|
|
||||||
context.roomMentionsEnabled = true
|
context.roomMentionsEnabled = true
|
||||||
let deferred = deferFulfillment(notificationSettingsProxy.callbacks
|
|
||||||
.first(where: { $0 == .settingsDidChange }))
|
|
||||||
context.send(viewAction: .roomMentionChanged)
|
|
||||||
try await deferred.fulfill()
|
|
||||||
|
|
||||||
|
let deferred = deferFulfillment(notificationSettingsProxy.callbacks) { callback in
|
||||||
|
callback == .settingsDidChange
|
||||||
|
}
|
||||||
|
|
||||||
|
context.send(viewAction: .roomMentionChanged)
|
||||||
|
|
||||||
|
try await deferred.fulfill()
|
||||||
|
|
||||||
XCTAssert(notificationSettingsProxy.setRoomMentionEnabledEnabledCalled)
|
XCTAssert(notificationSettingsProxy.setRoomMentionEnabledEnabledCalled)
|
||||||
XCTAssertEqual(notificationSettingsProxy.setRoomMentionEnabledEnabledReceivedEnabled, true)
|
XCTAssertEqual(notificationSettingsProxy.setRoomMentionEnabledEnabledReceivedEnabled, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testToggleRoomMentionFailure() async throws {
|
func testToggleRoomMentionFailure() async throws {
|
||||||
notificationSettingsProxy.setRoomMentionEnabledEnabledThrowableError = NotificationSettingsError.Generic(message: "error")
|
notificationSettingsProxy.setRoomMentionEnabledEnabledThrowableError = NotificationSettingsError.Generic(message: "error")
|
||||||
notificationSettingsProxy.isRoomMentionEnabledReturnValue = false
|
notificationSettingsProxy.isRoomMentionEnabledReturnValue = false
|
||||||
let deferredInitialFetch = deferFulfillment(viewModel.context.$viewState.map(\.settings)
|
|
||||||
.first(where: { $0 != nil }))
|
let deferredInitialFetch = deferFulfillment(viewModel.context.$viewState) { state in
|
||||||
|
state.settings != nil
|
||||||
|
}
|
||||||
|
|
||||||
viewModel.fetchInitialContent()
|
viewModel.fetchInitialContent()
|
||||||
|
|
||||||
try await deferredInitialFetch.fulfill()
|
try await deferredInitialFetch.fulfill()
|
||||||
|
|
||||||
context.roomMentionsEnabled = true
|
context.roomMentionsEnabled = true
|
||||||
let deferred = deferFulfillment(context.$viewState.map(\.applyingChange)
|
|
||||||
.removeDuplicates()
|
var deferred = deferFulfillment(context.$viewState) { state in
|
||||||
.collect(3)
|
state.applyingChange == true
|
||||||
.first())
|
}
|
||||||
|
|
||||||
context.send(viewAction: .roomMentionChanged)
|
context.send(viewAction: .roomMentionChanged)
|
||||||
let states = try await deferred.fulfill()
|
|
||||||
|
|
||||||
XCTAssertEqual(states, [false, true, false])
|
|
||||||
XCTAssertNotNil(context.alertInfo)
|
|
||||||
}
|
|
||||||
|
|
||||||
func testToggleCallsOff() async throws {
|
|
||||||
notificationSettingsProxy.isCallEnabledReturnValue = true
|
|
||||||
let deferredInitialFetch = deferFulfillment(viewModel.context.$viewState.map(\.settings)
|
|
||||||
.first(where: { $0 != nil }))
|
|
||||||
viewModel.fetchInitialContent()
|
|
||||||
try await deferredInitialFetch.fulfill()
|
|
||||||
|
|
||||||
context.callsEnabled = false
|
|
||||||
let deferred = deferFulfillment(notificationSettingsProxy.callbacks
|
|
||||||
.first(where: { $0 == .settingsDidChange }))
|
|
||||||
context.send(viewAction: .callsChanged)
|
|
||||||
try await deferred.fulfill()
|
try await deferred.fulfill()
|
||||||
|
|
||||||
|
deferred = deferFulfillment(context.$viewState) { state in
|
||||||
|
state.applyingChange == false
|
||||||
|
}
|
||||||
|
|
||||||
|
try await deferred.fulfill()
|
||||||
|
|
||||||
|
XCTAssertNotNil(context.alertInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testToggleCallsOff() async throws {
|
||||||
|
notificationSettingsProxy.isCallEnabledReturnValue = true
|
||||||
|
|
||||||
|
let deferredInitialFetch = deferFulfillment(viewModel.context.$viewState) { state in
|
||||||
|
state.settings != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
viewModel.fetchInitialContent()
|
||||||
|
|
||||||
|
try await deferredInitialFetch.fulfill()
|
||||||
|
|
||||||
|
context.callsEnabled = false
|
||||||
|
let deferred = deferFulfillment(notificationSettingsProxy.callbacks) { callback in
|
||||||
|
callback == .settingsDidChange
|
||||||
|
}
|
||||||
|
|
||||||
|
context.send(viewAction: .callsChanged)
|
||||||
|
|
||||||
|
try await deferred.fulfill()
|
||||||
|
|
||||||
XCTAssert(notificationSettingsProxy.setCallEnabledEnabledCalled)
|
XCTAssert(notificationSettingsProxy.setCallEnabledEnabledCalled)
|
||||||
XCTAssertEqual(notificationSettingsProxy.setCallEnabledEnabledReceivedEnabled, false)
|
XCTAssertEqual(notificationSettingsProxy.setCallEnabledEnabledReceivedEnabled, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testToggleCallsOn() async throws {
|
func testToggleCallsOn() async throws {
|
||||||
notificationSettingsProxy.isCallEnabledReturnValue = false
|
notificationSettingsProxy.isCallEnabledReturnValue = false
|
||||||
let deferredInitialFetch = deferFulfillment(viewModel.context.$viewState.map(\.settings)
|
|
||||||
.first(where: { $0 != nil }))
|
let deferredInitialFetch = deferFulfillment(viewModel.context.$viewState) { state in
|
||||||
|
state.settings != nil
|
||||||
|
}
|
||||||
|
|
||||||
viewModel.fetchInitialContent()
|
viewModel.fetchInitialContent()
|
||||||
|
|
||||||
try await deferredInitialFetch.fulfill()
|
try await deferredInitialFetch.fulfill()
|
||||||
|
|
||||||
context.callsEnabled = true
|
context.callsEnabled = true
|
||||||
let deferred = deferFulfillment(notificationSettingsProxy.callbacks
|
|
||||||
.first(where: { $0 == .settingsDidChange }))
|
let deferred = deferFulfillment(notificationSettingsProxy.callbacks) { callback in
|
||||||
|
callback == .settingsDidChange
|
||||||
|
}
|
||||||
|
|
||||||
context.send(viewAction: .callsChanged)
|
context.send(viewAction: .callsChanged)
|
||||||
|
|
||||||
try await deferred.fulfill()
|
try await deferred.fulfill()
|
||||||
|
|
||||||
XCTAssert(notificationSettingsProxy.setCallEnabledEnabledCalled)
|
XCTAssert(notificationSettingsProxy.setCallEnabledEnabledCalled)
|
||||||
XCTAssertEqual(notificationSettingsProxy.setCallEnabledEnabledReceivedEnabled, true)
|
XCTAssertEqual(notificationSettingsProxy.setCallEnabledEnabledReceivedEnabled, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testToggleCallsFailure() async throws {
|
func testToggleCallsFailure() async throws {
|
||||||
notificationSettingsProxy.setCallEnabledEnabledThrowableError = NotificationSettingsError.Generic(message: "error")
|
notificationSettingsProxy.setCallEnabledEnabledThrowableError = NotificationSettingsError.Generic(message: "error")
|
||||||
notificationSettingsProxy.isCallEnabledReturnValue = false
|
notificationSettingsProxy.isCallEnabledReturnValue = false
|
||||||
let deferredInitialFetch = deferFulfillment(viewModel.context.$viewState.map(\.settings)
|
|
||||||
.first(where: { $0 != nil }))
|
let deferredInitialFetch = deferFulfillment(viewModel.context.$viewState) { state in
|
||||||
|
state.settings != nil
|
||||||
|
}
|
||||||
|
|
||||||
viewModel.fetchInitialContent()
|
viewModel.fetchInitialContent()
|
||||||
|
|
||||||
try await deferredInitialFetch.fulfill()
|
try await deferredInitialFetch.fulfill()
|
||||||
|
|
||||||
context.callsEnabled = true
|
context.callsEnabled = true
|
||||||
let deferred = deferFulfillment(context.$viewState.map(\.applyingChange)
|
|
||||||
.removeDuplicates()
|
|
||||||
.collect(3)
|
|
||||||
.first())
|
|
||||||
context.send(viewAction: .callsChanged)
|
|
||||||
let states = try await deferred.fulfill()
|
|
||||||
|
|
||||||
XCTAssertEqual(states, [false, true, false])
|
var deferred = deferFulfillment(context.$viewState) { state in
|
||||||
|
state.applyingChange == true
|
||||||
|
}
|
||||||
|
|
||||||
|
context.send(viewAction: .callsChanged)
|
||||||
|
|
||||||
|
try await deferred.fulfill()
|
||||||
|
|
||||||
|
deferred = deferFulfillment(context.$viewState) { state in
|
||||||
|
state.applyingChange == false
|
||||||
|
}
|
||||||
|
|
||||||
|
try await deferred.fulfill()
|
||||||
|
|
||||||
XCTAssertNotNil(context.alertInfo)
|
XCTAssertNotNil(context.alertInfo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,15 +31,16 @@ class ReportContentScreenViewModelTests: XCTestCase {
|
|||||||
senderID: senderID,
|
senderID: senderID,
|
||||||
roomProxy: roomProxy)
|
roomProxy: roomProxy)
|
||||||
|
|
||||||
let deferred = deferFulfillment(viewModel.actions.collect(2).first(), message: "2 actions should be published.")
|
|
||||||
|
|
||||||
// When reporting the content without ignoring the user.
|
// When reporting the content without ignoring the user.
|
||||||
viewModel.state.bindings.reasonText = reportReason
|
viewModel.state.bindings.reasonText = reportReason
|
||||||
viewModel.state.bindings.ignoreUser = false
|
viewModel.state.bindings.ignoreUser = false
|
||||||
viewModel.context.send(viewAction: .submit)
|
viewModel.context.send(viewAction: .submit)
|
||||||
|
|
||||||
let actions = try await deferred.fulfill()
|
let deferred = deferFulfillment(viewModel.actions) { action in
|
||||||
XCTAssertEqual(actions, [.submitStarted, .submitFinished])
|
action == .submitFinished
|
||||||
|
}
|
||||||
|
|
||||||
|
try await deferred.fulfill()
|
||||||
|
|
||||||
// Then the content should be reported, but the user should not be included.
|
// Then the content should be reported, but the user should not be included.
|
||||||
XCTAssertEqual(roomProxy.reportContentReasonCallsCount, 1, "The content should always be reported.")
|
XCTAssertEqual(roomProxy.reportContentReasonCallsCount, 1, "The content should always be reported.")
|
||||||
@ -62,10 +63,13 @@ class ReportContentScreenViewModelTests: XCTestCase {
|
|||||||
viewModel.state.bindings.reasonText = reportReason
|
viewModel.state.bindings.reasonText = reportReason
|
||||||
viewModel.state.bindings.ignoreUser = true
|
viewModel.state.bindings.ignoreUser = true
|
||||||
|
|
||||||
let deferred = deferFulfillment(viewModel.actions.collect(2).first())
|
|
||||||
viewModel.context.send(viewAction: .submit)
|
viewModel.context.send(viewAction: .submit)
|
||||||
let result = try await deferred.fulfill()
|
|
||||||
XCTAssertEqual(result, [.submitStarted, .submitFinished])
|
let deferred = deferFulfillment(viewModel.actions) { action in
|
||||||
|
action == .submitFinished
|
||||||
|
}
|
||||||
|
|
||||||
|
try await deferred.fulfill()
|
||||||
|
|
||||||
// Then the content should be reported, and the user should be ignored.
|
// Then the content should be reported, and the user should be ignored.
|
||||||
XCTAssertEqual(roomProxy.reportContentReasonCallsCount, 1, "The content should always be reported.")
|
XCTAssertEqual(roomProxy.reportContentReasonCallsCount, 1, "The content should always be reported.")
|
||||||
|
@ -95,9 +95,13 @@ class RoomDetailsEditScreenViewModelTests: XCTestCase {
|
|||||||
setupViewModel(accountOwner: .mockOwner(allowedStateEvents: [.roomAvatar, .roomName, .roomTopic]),
|
setupViewModel(accountOwner: .mockOwner(allowedStateEvents: [.roomAvatar, .roomName, .roomTopic]),
|
||||||
roomProxyConfiguration: .init(name: "Some room", displayName: "Some room"))
|
roomProxyConfiguration: .init(name: "Some room", displayName: "Some room"))
|
||||||
|
|
||||||
let deferred = deferFulfillment(viewModel.actions.first())
|
let deferred = deferFulfillment(viewModel.actions) { action in
|
||||||
|
action == .saveFinished
|
||||||
|
}
|
||||||
|
|
||||||
context.name = "name"
|
context.name = "name"
|
||||||
context.send(viewAction: .save)
|
context.send(viewAction: .save)
|
||||||
|
|
||||||
let action = try await deferred.fulfill()
|
let action = try await deferred.fulfill()
|
||||||
XCTAssertEqual(action, .saveFinished)
|
XCTAssertEqual(action, .saveFinished)
|
||||||
}
|
}
|
||||||
|
@ -50,13 +50,15 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
|
|||||||
mediaProvider: MockMediaProvider(),
|
mediaProvider: MockMediaProvider(),
|
||||||
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
||||||
notificationSettingsProxy: NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()))
|
notificationSettingsProxy: NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()))
|
||||||
let deferred = deferFulfillment(context.$viewState.collect(2).first())
|
let deferred = deferFulfillment(context.$viewState) { state in
|
||||||
|
state.bindings.leaveRoomAlertItem != nil
|
||||||
|
}
|
||||||
|
|
||||||
context.send(viewAction: .processTapLeave)
|
context.send(viewAction: .processTapLeave)
|
||||||
let states = try await deferred.fulfill()
|
try await deferred.fulfill()
|
||||||
|
|
||||||
XCTAssertNil(states[0].bindings.leaveRoomAlertItem)
|
XCTAssertEqual(context.viewState.bindings.leaveRoomAlertItem?.state, .public)
|
||||||
XCTAssertEqual(states[1].bindings.leaveRoomAlertItem?.state, .public)
|
XCTAssertEqual(context.viewState.bindings.leaveRoomAlertItem?.subtitle, L10n.leaveRoomAlertSubtitle)
|
||||||
XCTAssertEqual(states[1].bindings.leaveRoomAlertItem?.subtitle, L10n.leaveRoomAlertSubtitle)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func testLeaveRoomTappedWhenRoomNotPublic() async throws {
|
func testLeaveRoomTappedWhenRoomNotPublic() async throws {
|
||||||
@ -67,13 +69,16 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
|
|||||||
mediaProvider: MockMediaProvider(),
|
mediaProvider: MockMediaProvider(),
|
||||||
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
||||||
notificationSettingsProxy: NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()))
|
notificationSettingsProxy: NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()))
|
||||||
let deferred = deferFulfillment(context.$viewState.collect(2).first())
|
let deferred = deferFulfillment(context.$viewState) { state in
|
||||||
|
state.bindings.leaveRoomAlertItem != nil
|
||||||
|
}
|
||||||
|
|
||||||
context.send(viewAction: .processTapLeave)
|
context.send(viewAction: .processTapLeave)
|
||||||
let states = try await deferred.fulfill()
|
|
||||||
context.send(viewAction: .processTapLeave)
|
try await deferred.fulfill()
|
||||||
XCTAssertNil(states[0].bindings.leaveRoomAlertItem)
|
|
||||||
XCTAssertEqual(states[1].bindings.leaveRoomAlertItem?.state, .private)
|
XCTAssertEqual(context.viewState.bindings.leaveRoomAlertItem?.state, .private)
|
||||||
XCTAssertEqual(states[1].bindings.leaveRoomAlertItem?.subtitle, L10n.leaveRoomAlertPrivateSubtitle)
|
XCTAssertEqual(context.viewState.bindings.leaveRoomAlertItem?.subtitle, L10n.leaveRoomAlertPrivateSubtitle)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testLeaveRoomTappedWithLessThanTwoMembers() async {
|
func testLeaveRoomTappedWithLessThanTwoMembers() async {
|
||||||
@ -82,24 +87,24 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
|
|||||||
XCTAssertEqual(context.leaveRoomAlertItem?.subtitle, L10n.leaveRoomAlertEmptySubtitle)
|
XCTAssertEqual(context.leaveRoomAlertItem?.subtitle, L10n.leaveRoomAlertEmptySubtitle)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testLeaveRoomSuccess() async {
|
func testLeaveRoomSuccess() async throws {
|
||||||
let expectation = expectation(description: #function)
|
|
||||||
roomProxyMock.leaveRoomClosure = {
|
roomProxyMock.leaveRoomClosure = {
|
||||||
.success(())
|
.success(())
|
||||||
}
|
}
|
||||||
viewModel.actions
|
|
||||||
.sink { action in
|
let deferred = deferFulfillment(viewModel.actions) { action in
|
||||||
switch action {
|
switch action {
|
||||||
case .leftRoom:
|
case .leftRoom:
|
||||||
break
|
return true
|
||||||
default:
|
default:
|
||||||
XCTFail("leftRoom expected")
|
return false
|
||||||
}
|
|
||||||
expectation.fulfill()
|
|
||||||
}
|
}
|
||||||
.store(in: &cancellables)
|
}
|
||||||
|
|
||||||
context.send(viewAction: .confirmLeave)
|
context.send(viewAction: .confirmLeave)
|
||||||
await fulfillment(of: [expectation])
|
|
||||||
|
try await deferred.fulfill()
|
||||||
|
|
||||||
XCTAssertEqual(roomProxyMock.leaveRoomCallsCount, 1)
|
XCTAssertEqual(roomProxyMock.leaveRoomCallsCount, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,7 +122,7 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
|
|||||||
XCTAssertNotNil(context.alertInfo)
|
XCTAssertNotNil(context.alertInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testInitialDMDetailsState() async {
|
func testInitialDMDetailsState() async throws {
|
||||||
let recipient = RoomMemberProxyMock.mockDan
|
let recipient = RoomMemberProxyMock.mockDan
|
||||||
let mockedMembers: [RoomMemberProxyMock] = [.mockMe, recipient]
|
let mockedMembers: [RoomMemberProxyMock] = [.mockMe, recipient]
|
||||||
roomProxyMock = RoomProxyMock(with: .init(displayName: "Test", isDirect: true, isEncrypted: true, members: mockedMembers, activeMembersCount: mockedMembers.count))
|
roomProxyMock = RoomProxyMock(with: .init(displayName: "Test", isDirect: true, isEncrypted: true, members: mockedMembers, activeMembersCount: mockedMembers.count))
|
||||||
@ -126,7 +131,13 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
|
|||||||
mediaProvider: MockMediaProvider(),
|
mediaProvider: MockMediaProvider(),
|
||||||
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
||||||
notificationSettingsProxy: NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()))
|
notificationSettingsProxy: NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()))
|
||||||
await context.nextViewState()
|
|
||||||
|
let deferred = deferFulfillment(viewModel.context.$viewState) { state in
|
||||||
|
state.dmRecipient != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
try await deferred.fulfill()
|
||||||
|
|
||||||
XCTAssertEqual(context.viewState.dmRecipient, RoomMemberDetails(withProxy: recipient))
|
XCTAssertEqual(context.viewState.dmRecipient, RoomMemberDetails(withProxy: recipient))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,6 +147,7 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
|
|||||||
try? await Task.sleep(for: .milliseconds(100))
|
try? await Task.sleep(for: .milliseconds(100))
|
||||||
return .success(())
|
return .success(())
|
||||||
}
|
}
|
||||||
|
|
||||||
let mockedMembers: [RoomMemberProxyMock] = [.mockMe, recipient]
|
let mockedMembers: [RoomMemberProxyMock] = [.mockMe, recipient]
|
||||||
roomProxyMock = RoomProxyMock(with: .init(displayName: "Test", isDirect: true, isEncrypted: true, members: mockedMembers, activeMembersCount: mockedMembers.count))
|
roomProxyMock = RoomProxyMock(with: .init(displayName: "Test", isDirect: true, isEncrypted: true, members: mockedMembers, activeMembersCount: mockedMembers.count))
|
||||||
viewModel = RoomDetailsScreenViewModel(accountUserID: "@owner:somewhere.com",
|
viewModel = RoomDetailsScreenViewModel(accountUserID: "@owner:somewhere.com",
|
||||||
@ -143,16 +155,23 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
|
|||||||
mediaProvider: MockMediaProvider(),
|
mediaProvider: MockMediaProvider(),
|
||||||
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
||||||
notificationSettingsProxy: NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()))
|
notificationSettingsProxy: NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()))
|
||||||
await context.nextViewState()
|
|
||||||
|
var deferred = deferFulfillment(viewModel.context.$viewState) { state in
|
||||||
|
state.dmRecipient != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
try await deferred.fulfill()
|
||||||
|
|
||||||
XCTAssertEqual(context.viewState.dmRecipient, RoomMemberDetails(withProxy: recipient))
|
XCTAssertEqual(context.viewState.dmRecipient, RoomMemberDetails(withProxy: recipient))
|
||||||
|
|
||||||
let deferred = deferFulfillment(context.$viewState.map(\.isProcessingIgnoreRequest)
|
deferred = deferFulfillment(viewModel.context.$viewState,
|
||||||
.removeDuplicates()
|
keyPath: \.isProcessingIgnoreRequest,
|
||||||
.collect(3).first())
|
transitionValues: [false, true, false])
|
||||||
|
|
||||||
context.send(viewAction: .ignoreConfirmed)
|
context.send(viewAction: .ignoreConfirmed)
|
||||||
|
|
||||||
let states = try await deferred.fulfill()
|
try await deferred.fulfill()
|
||||||
XCTAssertEqual(states, [false, true, false])
|
|
||||||
XCTAssert(context.viewState.dmRecipient?.isIgnored == true)
|
XCTAssert(context.viewState.dmRecipient?.isIgnored == true)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,16 +188,23 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
|
|||||||
mediaProvider: MockMediaProvider(),
|
mediaProvider: MockMediaProvider(),
|
||||||
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
||||||
notificationSettingsProxy: NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()))
|
notificationSettingsProxy: NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()))
|
||||||
await context.nextViewState()
|
|
||||||
|
var deferred = deferFulfillment(viewModel.context.$viewState) { state in
|
||||||
|
state.dmRecipient != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
try await deferred.fulfill()
|
||||||
|
|
||||||
XCTAssertEqual(context.viewState.dmRecipient, RoomMemberDetails(withProxy: recipient))
|
XCTAssertEqual(context.viewState.dmRecipient, RoomMemberDetails(withProxy: recipient))
|
||||||
|
|
||||||
let deferred = deferFulfillment(context.$viewState.map(\.isProcessingIgnoreRequest)
|
deferred = deferFulfillment(viewModel.context.$viewState,
|
||||||
.removeDuplicates()
|
keyPath: \.isProcessingIgnoreRequest,
|
||||||
.collect(3).first())
|
transitionValues: [false, true, false])
|
||||||
|
|
||||||
context.send(viewAction: .ignoreConfirmed)
|
context.send(viewAction: .ignoreConfirmed)
|
||||||
|
|
||||||
let states = try await deferred.fulfill()
|
try await deferred.fulfill()
|
||||||
XCTAssertEqual(states, [false, true, false])
|
|
||||||
XCTAssert(context.viewState.dmRecipient?.isIgnored == false)
|
XCTAssert(context.viewState.dmRecipient?.isIgnored == false)
|
||||||
XCTAssertNotNil(context.alertInfo)
|
XCTAssertNotNil(context.alertInfo)
|
||||||
}
|
}
|
||||||
@ -196,16 +222,23 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
|
|||||||
mediaProvider: MockMediaProvider(),
|
mediaProvider: MockMediaProvider(),
|
||||||
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
||||||
notificationSettingsProxy: NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()))
|
notificationSettingsProxy: NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()))
|
||||||
await context.nextViewState()
|
|
||||||
|
var deferred = deferFulfillment(viewModel.context.$viewState) { state in
|
||||||
|
state.dmRecipient != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
try await deferred.fulfill()
|
||||||
|
|
||||||
XCTAssertEqual(context.viewState.dmRecipient, RoomMemberDetails(withProxy: recipient))
|
XCTAssertEqual(context.viewState.dmRecipient, RoomMemberDetails(withProxy: recipient))
|
||||||
|
|
||||||
let deferred = deferFulfillment(context.$viewState.map(\.isProcessingIgnoreRequest)
|
deferred = deferFulfillment(viewModel.context.$viewState,
|
||||||
.removeDuplicates()
|
keyPath: \.isProcessingIgnoreRequest,
|
||||||
.collect(3).first())
|
transitionValues: [false, true, false])
|
||||||
|
|
||||||
context.send(viewAction: .unignoreConfirmed)
|
context.send(viewAction: .unignoreConfirmed)
|
||||||
let states = try await deferred.fulfill()
|
|
||||||
XCTAssertEqual(states, [false, true, false])
|
try await deferred.fulfill()
|
||||||
|
|
||||||
XCTAssert(context.viewState.dmRecipient?.isIgnored == false)
|
XCTAssert(context.viewState.dmRecipient?.isIgnored == false)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -222,16 +255,23 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
|
|||||||
mediaProvider: MockMediaProvider(),
|
mediaProvider: MockMediaProvider(),
|
||||||
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
||||||
notificationSettingsProxy: NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()))
|
notificationSettingsProxy: NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()))
|
||||||
await context.nextViewState()
|
|
||||||
|
var deferred = deferFulfillment(viewModel.context.$viewState) { state in
|
||||||
|
state.dmRecipient != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
try await deferred.fulfill()
|
||||||
|
|
||||||
XCTAssertEqual(context.viewState.dmRecipient, RoomMemberDetails(withProxy: recipient))
|
XCTAssertEqual(context.viewState.dmRecipient, RoomMemberDetails(withProxy: recipient))
|
||||||
|
|
||||||
let deferred = deferFulfillment(context.$viewState.map(\.isProcessingIgnoreRequest)
|
deferred = deferFulfillment(viewModel.context.$viewState,
|
||||||
.removeDuplicates()
|
keyPath: \.isProcessingIgnoreRequest,
|
||||||
.collect(3).first())
|
transitionValues: [false, true, false])
|
||||||
|
|
||||||
context.send(viewAction: .unignoreConfirmed)
|
context.send(viewAction: .unignoreConfirmed)
|
||||||
let states = try await deferred.fulfill()
|
|
||||||
XCTAssertEqual(states, [false, true, false])
|
try await deferred.fulfill()
|
||||||
|
|
||||||
XCTAssert(context.viewState.dmRecipient?.isIgnored == true)
|
XCTAssert(context.viewState.dmRecipient?.isIgnored == true)
|
||||||
XCTAssertNotNil(context.alertInfo)
|
XCTAssertNotNil(context.alertInfo)
|
||||||
}
|
}
|
||||||
@ -283,7 +323,7 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
|
|||||||
await Task.yield()
|
await Task.yield()
|
||||||
XCTAssertTrue(callbackCorrectlyCalled)
|
XCTAssertTrue(callbackCorrectlyCalled)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testCanEditAvatar() async {
|
func testCanEditAvatar() async {
|
||||||
let owner: RoomMemberProxyMock = .mockOwner(allowedStateEvents: [.roomAvatar])
|
let owner: RoomMemberProxyMock = .mockOwner(allowedStateEvents: [.roomAvatar])
|
||||||
let mockedMembers: [RoomMemberProxyMock] = [owner, .mockBob, .mockAlice]
|
let mockedMembers: [RoomMemberProxyMock] = [owner, .mockBob, .mockAlice]
|
||||||
@ -379,10 +419,19 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
|
|||||||
mediaProvider: MockMediaProvider(),
|
mediaProvider: MockMediaProvider(),
|
||||||
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
||||||
notificationSettingsProxy: notificationSettingsProxyMock)
|
notificationSettingsProxy: notificationSettingsProxyMock)
|
||||||
let deferred = deferFulfillment(context.$viewState.map(\.notificationSettingsState)
|
|
||||||
.filter(\.isError)
|
var deferred = deferFulfillment(context.$viewState) { state in
|
||||||
.first())
|
state.notificationSettingsState.isError
|
||||||
|
}
|
||||||
|
|
||||||
|
try await deferred.fulfill()
|
||||||
|
|
||||||
notificationSettingsProxyMock.callbacks.send(.settingsDidChange)
|
notificationSettingsProxyMock.callbacks.send(.settingsDidChange)
|
||||||
|
|
||||||
|
deferred = deferFulfillment(context.$viewState) { state in
|
||||||
|
state.notificationSettingsState.isError
|
||||||
|
}
|
||||||
|
|
||||||
try await deferred.fulfill()
|
try await deferred.fulfill()
|
||||||
|
|
||||||
let expectedAlertInfo = AlertInfo(id: RoomDetailsScreenErrorType.alert,
|
let expectedAlertInfo = AlertInfo(id: RoomDetailsScreenErrorType.alert,
|
||||||
@ -395,28 +444,40 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
|
|||||||
|
|
||||||
func testNotificationDefaultMode() async throws {
|
func testNotificationDefaultMode() async throws {
|
||||||
notificationSettingsProxyMock.getNotificationSettingsRoomIdIsEncryptedIsOneToOneReturnValue = RoomNotificationSettingsProxyMock(with: .init(mode: .allMessages, isDefault: true))
|
notificationSettingsProxyMock.getNotificationSettingsRoomIdIsEncryptedIsOneToOneReturnValue = RoomNotificationSettingsProxyMock(with: .init(mode: .allMessages, isDefault: true))
|
||||||
let deferred = deferFulfillment(context.$viewState.map(\.notificationSettingsState).first(where: \.isLoaded))
|
|
||||||
|
let deferred = deferFulfillment(context.$viewState) { state in
|
||||||
|
state.notificationSettingsState.isLoaded
|
||||||
|
}
|
||||||
|
|
||||||
notificationSettingsProxyMock.callbacks.send(.settingsDidChange)
|
notificationSettingsProxyMock.callbacks.send(.settingsDidChange)
|
||||||
try await deferred.fulfill()
|
try await deferred.fulfill()
|
||||||
|
|
||||||
XCTAssertEqual(context.viewState.notificationSettingsState.label, "Default")
|
XCTAssertEqual(context.viewState.notificationSettingsState.label, "Default")
|
||||||
}
|
}
|
||||||
|
|
||||||
func testNotificationCustomMode() async throws {
|
func testNotificationCustomMode() async throws {
|
||||||
notificationSettingsProxyMock.getNotificationSettingsRoomIdIsEncryptedIsOneToOneReturnValue = RoomNotificationSettingsProxyMock(with: .init(mode: .allMessages, isDefault: false))
|
notificationSettingsProxyMock.getNotificationSettingsRoomIdIsEncryptedIsOneToOneReturnValue = RoomNotificationSettingsProxyMock(with: .init(mode: .allMessages, isDefault: false))
|
||||||
let deferred = deferFulfillment(context.$viewState.map(\.notificationSettingsState).first(where: \.isCustom))
|
|
||||||
|
let deferred = deferFulfillment(context.$viewState) { state in
|
||||||
|
state.notificationSettingsState.isCustom
|
||||||
|
}
|
||||||
|
|
||||||
notificationSettingsProxyMock.callbacks.send(.settingsDidChange)
|
notificationSettingsProxyMock.callbacks.send(.settingsDidChange)
|
||||||
try await deferred.fulfill()
|
try await deferred.fulfill()
|
||||||
|
|
||||||
XCTAssertEqual(context.viewState.notificationSettingsState.label, "Custom")
|
XCTAssertEqual(context.viewState.notificationSettingsState.label, "Custom")
|
||||||
}
|
}
|
||||||
|
|
||||||
func testNotificationRoomMuted() async throws {
|
func testNotificationRoomMuted() async throws {
|
||||||
notificationSettingsProxyMock.getNotificationSettingsRoomIdIsEncryptedIsOneToOneReturnValue = RoomNotificationSettingsProxyMock(with: .init(mode: .mute, isDefault: false))
|
notificationSettingsProxyMock.getNotificationSettingsRoomIdIsEncryptedIsOneToOneReturnValue = RoomNotificationSettingsProxyMock(with: .init(mode: .mute, isDefault: false))
|
||||||
let deferred = deferFulfillment(context.$viewState.map(\.notificationSettingsState).first(where: \.isLoaded))
|
|
||||||
|
let deferred = deferFulfillment(context.$viewState) { state in
|
||||||
|
state.notificationSettingsState.isLoaded
|
||||||
|
}
|
||||||
|
|
||||||
notificationSettingsProxyMock.callbacks.send(.settingsDidChange)
|
notificationSettingsProxyMock.callbacks.send(.settingsDidChange)
|
||||||
try await deferred.fulfill()
|
try await deferred.fulfill()
|
||||||
|
|
||||||
_ = await context.$viewState.debounce(for: .milliseconds(100), scheduler: DispatchQueue.main).values.first()
|
_ = await context.$viewState.debounce(for: .milliseconds(100), scheduler: DispatchQueue.main).values.first()
|
||||||
|
|
||||||
XCTAssertEqual(context.viewState.notificationShortcutButtonTitle, L10n.commonUnmute)
|
XCTAssertEqual(context.viewState.notificationShortcutButtonTitle, L10n.commonUnmute)
|
||||||
@ -425,10 +486,14 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
|
|||||||
|
|
||||||
func testNotificationRoomNotMuted() async throws {
|
func testNotificationRoomNotMuted() async throws {
|
||||||
notificationSettingsProxyMock.getNotificationSettingsRoomIdIsEncryptedIsOneToOneReturnValue = RoomNotificationSettingsProxyMock(with: .init(mode: .mentionsAndKeywordsOnly, isDefault: false))
|
notificationSettingsProxyMock.getNotificationSettingsRoomIdIsEncryptedIsOneToOneReturnValue = RoomNotificationSettingsProxyMock(with: .init(mode: .mentionsAndKeywordsOnly, isDefault: false))
|
||||||
let deferred = deferFulfillment(context.$viewState.map(\.notificationSettingsState).first(where: \.isLoaded))
|
|
||||||
|
let deferred = deferFulfillment(context.$viewState) { state in
|
||||||
|
state.notificationSettingsState.isLoaded
|
||||||
|
}
|
||||||
|
|
||||||
notificationSettingsProxyMock.callbacks.send(.settingsDidChange)
|
notificationSettingsProxyMock.callbacks.send(.settingsDidChange)
|
||||||
try await deferred.fulfill()
|
try await deferred.fulfill()
|
||||||
|
|
||||||
XCTAssertEqual(context.viewState.notificationShortcutButtonTitle, L10n.commonMute)
|
XCTAssertEqual(context.viewState.notificationShortcutButtonTitle, L10n.commonMute)
|
||||||
XCTAssertEqual(context.viewState.notificationShortcutButtonImage, Image(systemName: "bell"))
|
XCTAssertEqual(context.viewState.notificationShortcutButtonImage, Image(systemName: "bell"))
|
||||||
}
|
}
|
||||||
@ -485,7 +550,7 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
|
|||||||
|
|
||||||
func testMuteTapped() async throws {
|
func testMuteTapped() async throws {
|
||||||
try await testNotificationRoomNotMuted()
|
try await testNotificationRoomNotMuted()
|
||||||
|
|
||||||
let expectation = expectation(description: #function)
|
let expectation = expectation(description: #function)
|
||||||
notificationSettingsProxyMock.setNotificationModeRoomIdModeClosure = { [weak notificationSettingsProxyMock] _, mode in
|
notificationSettingsProxyMock.setNotificationModeRoomIdModeClosure = { [weak notificationSettingsProxyMock] _, mode in
|
||||||
notificationSettingsProxyMock?.getNotificationSettingsRoomIdIsEncryptedIsOneToOneReturnValue = RoomNotificationSettingsProxyMock(with: .init(mode: mode, isDefault: false))
|
notificationSettingsProxyMock?.getNotificationSettingsRoomIdIsEncryptedIsOneToOneReturnValue = RoomNotificationSettingsProxyMock(with: .init(mode: mode, isDefault: false))
|
||||||
@ -495,9 +560,12 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
|
|||||||
await fulfillment(of: [expectation])
|
await fulfillment(of: [expectation])
|
||||||
|
|
||||||
XCTAssertFalse(context.viewState.isProcessingMuteToggleAction)
|
XCTAssertFalse(context.viewState.isProcessingMuteToggleAction)
|
||||||
|
|
||||||
do {
|
do {
|
||||||
let deferred = deferFulfillment(context.$viewState.first())
|
let deferred = deferFulfillment(context.$viewState) { state in
|
||||||
|
state.notificationSettingsState.isLoaded
|
||||||
|
}
|
||||||
|
|
||||||
notificationSettingsProxyMock.callbacks.send(.settingsDidChange)
|
notificationSettingsProxyMock.callbacks.send(.settingsDidChange)
|
||||||
try await deferred.fulfill()
|
try await deferred.fulfill()
|
||||||
}
|
}
|
||||||
@ -512,7 +580,7 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
|
|||||||
|
|
||||||
func testUnmuteTapped() async throws {
|
func testUnmuteTapped() async throws {
|
||||||
try await testNotificationRoomMuted()
|
try await testNotificationRoomMuted()
|
||||||
|
|
||||||
let expectation = expectation(description: #function)
|
let expectation = expectation(description: #function)
|
||||||
notificationSettingsProxyMock.unmuteRoomRoomIdIsEncryptedIsOneToOneClosure = { [weak notificationSettingsProxyMock] _, _, _ in
|
notificationSettingsProxyMock.unmuteRoomRoomIdIsEncryptedIsOneToOneClosure = { [weak notificationSettingsProxyMock] _, _, _ in
|
||||||
notificationSettingsProxyMock?.getNotificationSettingsRoomIdIsEncryptedIsOneToOneReturnValue = RoomNotificationSettingsProxyMock(with: .init(mode: .allMessages, isDefault: false))
|
notificationSettingsProxyMock?.getNotificationSettingsRoomIdIsEncryptedIsOneToOneReturnValue = RoomNotificationSettingsProxyMock(with: .init(mode: .allMessages, isDefault: false))
|
||||||
@ -524,7 +592,10 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
|
|||||||
XCTAssertFalse(context.viewState.isProcessingMuteToggleAction)
|
XCTAssertFalse(context.viewState.isProcessingMuteToggleAction)
|
||||||
|
|
||||||
do {
|
do {
|
||||||
let deferred = deferFulfillment(context.$viewState.first())
|
let deferred = deferFulfillment(context.$viewState) { state in
|
||||||
|
state.notificationSettingsState.isLoaded
|
||||||
|
}
|
||||||
|
|
||||||
notificationSettingsProxyMock.callbacks.send(.settingsDidChange)
|
notificationSettingsProxyMock.callbacks.send(.settingsDidChange)
|
||||||
try await deferred.fulfill()
|
try await deferred.fulfill()
|
||||||
}
|
}
|
||||||
|
@ -119,17 +119,22 @@ class RoomFlowCoordinatorTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func process(route: AppRoute, expectedActions: [RoomFlowCoordinatorAction]) async throws {
|
private func process(route: AppRoute, expectedActions: [RoomFlowCoordinatorAction]) async throws {
|
||||||
let deferred = deferFulfillment(roomFlowCoordinator.actions.collect(expectedActions.count).first(),
|
guard !expectedActions.isEmpty else {
|
||||||
message: "The expected number of actions should be published.")
|
return
|
||||||
|
|
||||||
Task {
|
|
||||||
await Task.yield()
|
|
||||||
self.roomFlowCoordinator.handleAppRoute(route, animated: true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !expectedActions.isEmpty {
|
var fulfillments = [DeferredFulfillment<RoomFlowCoordinatorAction>]()
|
||||||
let actions = try await deferred.fulfill()
|
|
||||||
XCTAssertEqual(actions, expectedActions)
|
for expectedAction in expectedActions {
|
||||||
|
fulfillments.append(deferFulfillment(roomFlowCoordinator.actions) { action in
|
||||||
|
action == expectedAction
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
roomFlowCoordinator.handleAppRoute(route, animated: true)
|
||||||
|
|
||||||
|
for fulfillment in fulfillments {
|
||||||
|
try await fulfillment.fulfill()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,16 +55,17 @@ class RoomMemberDetailsViewModelTests: XCTestCase {
|
|||||||
context.send(viewAction: .showIgnoreAlert)
|
context.send(viewAction: .showIgnoreAlert)
|
||||||
XCTAssertEqual(context.ignoreUserAlert, .init(action: .ignore))
|
XCTAssertEqual(context.ignoreUserAlert, .init(action: .ignore))
|
||||||
|
|
||||||
let deferred = deferFulfillment(context.$viewState.map(\.isProcessingIgnoreRequest)
|
|
||||||
.removeDuplicates()
|
|
||||||
.collect(3).first())
|
|
||||||
|
|
||||||
context.send(viewAction: .ignoreConfirmed)
|
context.send(viewAction: .ignoreConfirmed)
|
||||||
let states = try await deferred.fulfill()
|
|
||||||
XCTAssertEqual(states, [false, true, false])
|
let deferred = deferFulfillment(context.$viewState) { state in
|
||||||
|
state.details.isIgnored
|
||||||
|
}
|
||||||
|
|
||||||
|
try await deferred.fulfill()
|
||||||
|
|
||||||
XCTAssertFalse(context.viewState.isProcessingIgnoreRequest)
|
XCTAssertFalse(context.viewState.isProcessingIgnoreRequest)
|
||||||
XCTAssertTrue(context.viewState.details.isIgnored)
|
XCTAssertTrue(context.viewState.details.isIgnored)
|
||||||
try await Task.sleep(for: .microseconds(100))
|
try await Task.sleep(for: .milliseconds(100))
|
||||||
XCTAssertTrue(roomProxyMock.updateMembersCalled)
|
XCTAssertTrue(roomProxyMock.updateMembersCalled)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,17 +81,18 @@ class RoomMemberDetailsViewModelTests: XCTestCase {
|
|||||||
userIndicatorController: ServiceLocator.shared.userIndicatorController)
|
userIndicatorController: ServiceLocator.shared.userIndicatorController)
|
||||||
context.send(viewAction: .showIgnoreAlert)
|
context.send(viewAction: .showIgnoreAlert)
|
||||||
XCTAssertEqual(context.ignoreUserAlert, .init(action: .ignore))
|
XCTAssertEqual(context.ignoreUserAlert, .init(action: .ignore))
|
||||||
|
|
||||||
let deferred = deferFulfillment(context.$viewState.map(\.isProcessingIgnoreRequest)
|
|
||||||
.removeDuplicates()
|
|
||||||
.collect(3).first())
|
|
||||||
|
|
||||||
context.send(viewAction: .ignoreConfirmed)
|
context.send(viewAction: .ignoreConfirmed)
|
||||||
let states = try await deferred.fulfill()
|
|
||||||
XCTAssertEqual(states, [false, true, false])
|
let deferred = deferFulfillment(context.$viewState) { state in
|
||||||
|
state.bindings.alertInfo != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
try await deferred.fulfill()
|
||||||
|
|
||||||
XCTAssertNotNil(context.alertInfo)
|
XCTAssertNotNil(context.alertInfo)
|
||||||
XCTAssertFalse(context.viewState.details.isIgnored)
|
XCTAssertFalse(context.viewState.details.isIgnored)
|
||||||
try await Task.sleep(for: .microseconds(100))
|
try await Task.sleep(for: .milliseconds(100))
|
||||||
XCTAssertFalse(roomProxyMock.updateMembersCalled)
|
XCTAssertFalse(roomProxyMock.updateMembersCalled)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,16 +109,17 @@ class RoomMemberDetailsViewModelTests: XCTestCase {
|
|||||||
|
|
||||||
context.send(viewAction: .showUnignoreAlert)
|
context.send(viewAction: .showUnignoreAlert)
|
||||||
XCTAssertEqual(context.ignoreUserAlert, .init(action: .unignore))
|
XCTAssertEqual(context.ignoreUserAlert, .init(action: .unignore))
|
||||||
|
|
||||||
let deferred = deferFulfillment(context.$viewState.map(\.isProcessingIgnoreRequest)
|
|
||||||
.removeDuplicates()
|
|
||||||
.collect(3).first())
|
|
||||||
|
|
||||||
context.send(viewAction: .unignoreConfirmed)
|
context.send(viewAction: .unignoreConfirmed)
|
||||||
let states = try await deferred.fulfill()
|
|
||||||
XCTAssertEqual(states, [false, true, false])
|
let deferred = deferFulfillment(context.$viewState) { state in
|
||||||
|
state.details.isIgnored == false
|
||||||
|
}
|
||||||
|
|
||||||
|
try await deferred.fulfill()
|
||||||
|
|
||||||
XCTAssertFalse(context.viewState.details.isIgnored)
|
XCTAssertFalse(context.viewState.details.isIgnored)
|
||||||
try await Task.sleep(for: .microseconds(100))
|
try await Task.sleep(for: .milliseconds(100))
|
||||||
XCTAssertTrue(roomProxyMock.updateMembersCalled)
|
XCTAssertTrue(roomProxyMock.updateMembersCalled)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,17 +137,18 @@ class RoomMemberDetailsViewModelTests: XCTestCase {
|
|||||||
|
|
||||||
context.send(viewAction: .showUnignoreAlert)
|
context.send(viewAction: .showUnignoreAlert)
|
||||||
XCTAssertEqual(context.ignoreUserAlert, .init(action: .unignore))
|
XCTAssertEqual(context.ignoreUserAlert, .init(action: .unignore))
|
||||||
|
|
||||||
let deferred = deferFulfillment(context.$viewState.map(\.isProcessingIgnoreRequest)
|
|
||||||
.removeDuplicates()
|
|
||||||
.collect(3).first())
|
|
||||||
|
|
||||||
context.send(viewAction: .unignoreConfirmed)
|
context.send(viewAction: .unignoreConfirmed)
|
||||||
let states = try await deferred.fulfill()
|
|
||||||
XCTAssertEqual(states, [false, true, false])
|
let deferred = deferFulfillment(context.$viewState) { state in
|
||||||
|
state.bindings.alertInfo != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
try await deferred.fulfill()
|
||||||
|
|
||||||
XCTAssertTrue(context.viewState.details.isIgnored)
|
XCTAssertTrue(context.viewState.details.isIgnored)
|
||||||
XCTAssertNotNil(context.alertInfo)
|
XCTAssertNotNil(context.alertInfo)
|
||||||
try await Task.sleep(for: .microseconds(100))
|
try await Task.sleep(for: .milliseconds(100))
|
||||||
XCTAssertFalse(roomProxyMock.updateMembersCalled)
|
XCTAssertFalse(roomProxyMock.updateMembersCalled)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,49 +26,87 @@ class RoomMembersListScreenViewModelTests: XCTestCase {
|
|||||||
viewModel.context
|
viewModel.context
|
||||||
}
|
}
|
||||||
|
|
||||||
func testJoinedMembers() async {
|
func testJoinedMembers() async throws {
|
||||||
setup(with: [.mockAlice, .mockBob])
|
setup(with: [.mockAlice, .mockBob])
|
||||||
await context.nextViewState()
|
|
||||||
|
let deferred = deferFulfillment(context.$viewState) { state in
|
||||||
|
state.visibleJoinedMembers.count == 2
|
||||||
|
}
|
||||||
|
|
||||||
|
try await deferred.fulfill()
|
||||||
|
|
||||||
XCTAssertEqual(viewModel.state.joinedMembersCount, 2)
|
XCTAssertEqual(viewModel.state.joinedMembersCount, 2)
|
||||||
XCTAssertEqual(viewModel.state.visibleJoinedMembers.count, 2)
|
XCTAssertEqual(viewModel.state.visibleJoinedMembers.count, 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testSearch() async {
|
func testSearch() async throws {
|
||||||
setup(with: [.mockAlice, .mockBob])
|
setup(with: [.mockAlice, .mockBob])
|
||||||
await context.nextViewState()
|
|
||||||
|
let deferred = deferFulfillment(context.$viewState) { state in
|
||||||
|
state.visibleJoinedMembers.count == 1
|
||||||
|
}
|
||||||
|
|
||||||
context.searchQuery = "alice"
|
context.searchQuery = "alice"
|
||||||
|
|
||||||
|
try await deferred.fulfill()
|
||||||
|
|
||||||
XCTAssertEqual(viewModel.state.joinedMembersCount, 2)
|
XCTAssertEqual(viewModel.state.joinedMembersCount, 2)
|
||||||
XCTAssertEqual(viewModel.state.visibleJoinedMembers.count, 1)
|
XCTAssertEqual(viewModel.state.visibleJoinedMembers.count, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testEmptySearch() async {
|
func testEmptySearch() async throws {
|
||||||
setup(with: [.mockAlice, .mockBob])
|
setup(with: [.mockAlice, .mockBob])
|
||||||
await context.nextViewState()
|
|
||||||
context.searchQuery = "WWW"
|
context.searchQuery = "WWW"
|
||||||
|
|
||||||
|
let deferred = deferFulfillment(context.$viewState) { state in
|
||||||
|
state.joinedMembersCount == 2
|
||||||
|
}
|
||||||
|
|
||||||
|
try await deferred.fulfill()
|
||||||
|
|
||||||
XCTAssertEqual(viewModel.state.joinedMembersCount, 2)
|
XCTAssertEqual(viewModel.state.joinedMembersCount, 2)
|
||||||
XCTAssertEqual(viewModel.state.visibleJoinedMembers.count, 0)
|
XCTAssertEqual(viewModel.state.visibleJoinedMembers.count, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testJoinedAndInvitedMembers() async {
|
func testJoinedAndInvitedMembers() async throws {
|
||||||
setup(with: [.mockInvitedAlice, .mockBob])
|
setup(with: [.mockInvitedAlice, .mockBob])
|
||||||
await context.nextViewState()
|
|
||||||
|
let deferred = deferFulfillment(context.$viewState) { state in
|
||||||
|
state.visibleInvitedMembers.count == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
try await deferred.fulfill()
|
||||||
|
|
||||||
XCTAssertEqual(viewModel.state.joinedMembersCount, 1)
|
XCTAssertEqual(viewModel.state.joinedMembersCount, 1)
|
||||||
XCTAssertEqual(viewModel.state.visibleInvitedMembers.count, 1)
|
XCTAssertEqual(viewModel.state.visibleInvitedMembers.count, 1)
|
||||||
XCTAssertEqual(viewModel.state.visibleJoinedMembers.count, 1)
|
XCTAssertEqual(viewModel.state.visibleJoinedMembers.count, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testInvitedMembers() async {
|
func testInvitedMembers() async throws {
|
||||||
setup(with: [.mockInvitedAlice])
|
setup(with: [.mockInvitedAlice])
|
||||||
await context.nextViewState()
|
|
||||||
|
let deferred = deferFulfillment(context.$viewState) { state in
|
||||||
|
state.visibleInvitedMembers.count == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
try await deferred.fulfill()
|
||||||
|
|
||||||
XCTAssertEqual(viewModel.state.joinedMembersCount, 0)
|
XCTAssertEqual(viewModel.state.joinedMembersCount, 0)
|
||||||
XCTAssertEqual(viewModel.state.visibleInvitedMembers.count, 1)
|
XCTAssertEqual(viewModel.state.visibleInvitedMembers.count, 1)
|
||||||
XCTAssertEqual(viewModel.state.visibleJoinedMembers.count, 0)
|
XCTAssertEqual(viewModel.state.visibleJoinedMembers.count, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testSearchInvitedMembers() async {
|
func testSearchInvitedMembers() async throws {
|
||||||
setup(with: [.mockInvitedAlice])
|
setup(with: [.mockInvitedAlice])
|
||||||
|
|
||||||
context.searchQuery = "alice"
|
context.searchQuery = "alice"
|
||||||
await context.nextViewState()
|
|
||||||
|
let deferred = deferFulfillment(context.$viewState) { state in
|
||||||
|
state.visibleInvitedMembers.count == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
try await deferred.fulfill()
|
||||||
|
|
||||||
XCTAssertEqual(viewModel.state.joinedMembersCount, 0)
|
XCTAssertEqual(viewModel.state.joinedMembersCount, 0)
|
||||||
XCTAssertEqual(viewModel.state.visibleInvitedMembers.count, 1)
|
XCTAssertEqual(viewModel.state.visibleInvitedMembers.count, 1)
|
||||||
XCTAssertEqual(viewModel.state.visibleJoinedMembers.count, 0)
|
XCTAssertEqual(viewModel.state.visibleJoinedMembers.count, 0)
|
||||||
|
@ -22,99 +22,118 @@ import XCTest
|
|||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
class RoomNotificationSettingsScreenViewModelTests: XCTestCase {
|
class RoomNotificationSettingsScreenViewModelTests: XCTestCase {
|
||||||
var viewModel: RoomNotificationSettingsScreenViewModel!
|
|
||||||
var roomProxyMock: RoomProxyMock!
|
var roomProxyMock: RoomProxyMock!
|
||||||
var notificationSettingsProxyMock: NotificationSettingsProxyMock!
|
var notificationSettingsProxyMock: NotificationSettingsProxyMock!
|
||||||
var context: RoomNotificationSettingsScreenViewModelType.Context { viewModel.context }
|
|
||||||
var cancellables = Set<AnyCancellable>()
|
var cancellables = Set<AnyCancellable>()
|
||||||
|
|
||||||
override func setUpWithError() throws {
|
override func setUpWithError() throws {
|
||||||
cancellables.removeAll()
|
cancellables.removeAll()
|
||||||
roomProxyMock = RoomProxyMock(with: .init(displayName: "Test", joinedMembersCount: 0))
|
roomProxyMock = RoomProxyMock(with: .init(displayName: "Test", joinedMembersCount: 0))
|
||||||
notificationSettingsProxyMock = NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration())
|
notificationSettingsProxyMock = NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration())
|
||||||
viewModel = RoomNotificationSettingsScreenViewModel(notificationSettingsProxy: notificationSettingsProxyMock,
|
|
||||||
roomProxy: roomProxyMock,
|
|
||||||
displayAsUserDefinedRoomSettings: false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func testInitialStateDefaultMode() async throws {
|
func testInitialStateDefaultMode() async throws {
|
||||||
|
let roomProxyMock = RoomProxyMock(with: .init(displayName: "Test", joinedMembersCount: 0))
|
||||||
|
let notificationSettingsProxyMock = NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration())
|
||||||
|
|
||||||
notificationSettingsProxyMock.getNotificationSettingsRoomIdIsEncryptedIsOneToOneReturnValue = RoomNotificationSettingsProxyMock(with: .init(mode: .mentionsAndKeywordsOnly, isDefault: true))
|
notificationSettingsProxyMock.getNotificationSettingsRoomIdIsEncryptedIsOneToOneReturnValue = RoomNotificationSettingsProxyMock(with: .init(mode: .mentionsAndKeywordsOnly, isDefault: true))
|
||||||
viewModel = RoomNotificationSettingsScreenViewModel(notificationSettingsProxy: notificationSettingsProxyMock,
|
|
||||||
roomProxy: roomProxyMock,
|
let viewModel = RoomNotificationSettingsScreenViewModel(notificationSettingsProxy: notificationSettingsProxyMock,
|
||||||
displayAsUserDefinedRoomSettings: false)
|
roomProxy: roomProxyMock,
|
||||||
let deferred = deferFulfillment(context.$viewState.map(\.notificationSettingsState)
|
displayAsUserDefinedRoomSettings: false)
|
||||||
.first(where: \.isLoaded))
|
|
||||||
|
let deferred = deferFulfillment(viewModel.context.$viewState) { state in
|
||||||
|
state.notificationSettingsState.isLoaded
|
||||||
|
}
|
||||||
|
|
||||||
notificationSettingsProxyMock.callbacks.send(.settingsDidChange)
|
notificationSettingsProxyMock.callbacks.send(.settingsDidChange)
|
||||||
try await deferred.fulfill()
|
try await deferred.fulfill()
|
||||||
|
|
||||||
XCTAssertFalse(context.allowCustomSetting)
|
XCTAssertFalse(viewModel.context.allowCustomSetting)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testInitialStateCustomMode() async throws {
|
func testInitialStateCustomMode() async throws {
|
||||||
notificationSettingsProxyMock.getNotificationSettingsRoomIdIsEncryptedIsOneToOneReturnValue = RoomNotificationSettingsProxyMock(with: .init(mode: .mentionsAndKeywordsOnly, isDefault: false))
|
notificationSettingsProxyMock.getNotificationSettingsRoomIdIsEncryptedIsOneToOneReturnValue = RoomNotificationSettingsProxyMock(with: .init(mode: .mentionsAndKeywordsOnly, isDefault: false))
|
||||||
viewModel = RoomNotificationSettingsScreenViewModel(notificationSettingsProxy: notificationSettingsProxyMock,
|
let viewModel = RoomNotificationSettingsScreenViewModel(notificationSettingsProxy: notificationSettingsProxyMock,
|
||||||
roomProxy: roomProxyMock,
|
roomProxy: roomProxyMock,
|
||||||
displayAsUserDefinedRoomSettings: false)
|
displayAsUserDefinedRoomSettings: false)
|
||||||
let deferred = deferFulfillment(context.$viewState.map(\.notificationSettingsState)
|
let deferred = deferFulfillment(viewModel.context.$viewState) { state in
|
||||||
.first(where: \.isLoaded))
|
state.notificationSettingsState.isLoaded
|
||||||
|
}
|
||||||
|
|
||||||
notificationSettingsProxyMock.callbacks.send(.settingsDidChange)
|
notificationSettingsProxyMock.callbacks.send(.settingsDidChange)
|
||||||
try await deferred.fulfill()
|
try await deferred.fulfill()
|
||||||
|
|
||||||
XCTAssertTrue(context.allowCustomSetting)
|
XCTAssertTrue(viewModel.context.allowCustomSetting)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testInitialStateFailure() async throws {
|
func testInitialStateFailure() async throws {
|
||||||
notificationSettingsProxyMock.getNotificationSettingsRoomIdIsEncryptedIsOneToOneThrowableError = NotificationSettingsError.Generic(message: "error")
|
notificationSettingsProxyMock.getNotificationSettingsRoomIdIsEncryptedIsOneToOneThrowableError = NotificationSettingsError.Generic(message: "error")
|
||||||
viewModel = RoomNotificationSettingsScreenViewModel(notificationSettingsProxy: notificationSettingsProxyMock,
|
let viewModel = RoomNotificationSettingsScreenViewModel(notificationSettingsProxy: notificationSettingsProxyMock,
|
||||||
roomProxy: roomProxyMock,
|
roomProxy: roomProxyMock,
|
||||||
displayAsUserDefinedRoomSettings: false)
|
displayAsUserDefinedRoomSettings: false)
|
||||||
let deferred = deferFulfillment(context.$viewState.map(\.notificationSettingsState)
|
let deferred = deferFulfillment(viewModel.context.$viewState) { state in
|
||||||
.first(where: \.isError))
|
state.notificationSettingsState.isError
|
||||||
|
}
|
||||||
|
|
||||||
notificationSettingsProxyMock.callbacks.send(.settingsDidChange)
|
notificationSettingsProxyMock.callbacks.send(.settingsDidChange)
|
||||||
try await deferred.fulfill()
|
try await deferred.fulfill()
|
||||||
|
|
||||||
let expectedAlertInfo = AlertInfo(id: RoomNotificationSettingsScreenErrorType.loadingSettingsFailed,
|
let expectedAlertInfo = AlertInfo(id: RoomNotificationSettingsScreenErrorType.loadingSettingsFailed,
|
||||||
title: L10n.commonError,
|
title: L10n.commonError,
|
||||||
message: L10n.screenRoomNotificationSettingsErrorLoadingSettings)
|
message: L10n.screenRoomNotificationSettingsErrorLoadingSettings)
|
||||||
XCTAssertEqual(context.viewState.bindings.alertInfo?.id, expectedAlertInfo.id)
|
XCTAssertEqual(viewModel.context.viewState.bindings.alertInfo?.id, expectedAlertInfo.id)
|
||||||
XCTAssertEqual(context.viewState.bindings.alertInfo?.title, expectedAlertInfo.title)
|
XCTAssertEqual(viewModel.context.viewState.bindings.alertInfo?.title, expectedAlertInfo.title)
|
||||||
XCTAssertEqual(context.viewState.bindings.alertInfo?.message, expectedAlertInfo.message)
|
XCTAssertEqual(viewModel.context.viewState.bindings.alertInfo?.message, expectedAlertInfo.message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testToggleAllCustomSettingOff() async throws {
|
func testToggleAllCustomSettingOff() async throws {
|
||||||
notificationSettingsProxyMock.getNotificationSettingsRoomIdIsEncryptedIsOneToOneReturnValue = RoomNotificationSettingsProxyMock(with: .init(mode: .mentionsAndKeywordsOnly, isDefault: false))
|
notificationSettingsProxyMock.getNotificationSettingsRoomIdIsEncryptedIsOneToOneReturnValue = RoomNotificationSettingsProxyMock(with: .init(mode: .mentionsAndKeywordsOnly, isDefault: false))
|
||||||
viewModel = RoomNotificationSettingsScreenViewModel(notificationSettingsProxy: notificationSettingsProxyMock,
|
let viewModel = RoomNotificationSettingsScreenViewModel(notificationSettingsProxy: notificationSettingsProxyMock,
|
||||||
roomProxy: roomProxyMock,
|
roomProxy: roomProxyMock,
|
||||||
displayAsUserDefinedRoomSettings: false)
|
displayAsUserDefinedRoomSettings: false)
|
||||||
let deferred = deferFulfillment(context.$viewState.map(\.notificationSettingsState)
|
let deferred = deferFulfillment(viewModel.context.$viewState) { state in
|
||||||
.first(where: \.isLoaded))
|
state.notificationSettingsState.isLoaded
|
||||||
|
}
|
||||||
|
|
||||||
notificationSettingsProxyMock.callbacks.send(.settingsDidChange)
|
notificationSettingsProxyMock.callbacks.send(.settingsDidChange)
|
||||||
try await deferred.fulfill()
|
try await deferred.fulfill()
|
||||||
|
|
||||||
|
let deferredIsRestoringDefaultSettings = deferFulfillment(viewModel.context.$viewState,
|
||||||
|
keyPath: \.isRestoringDefaultSetting,
|
||||||
|
transitionValues: [false, true, false])
|
||||||
|
|
||||||
let deferredIsRestoringDefaultSettings = deferFulfillment(context.$viewState.map(\.isRestoringDefaultSetting)
|
|
||||||
.removeDuplicates()
|
|
||||||
.collect(3).first())
|
|
||||||
viewModel.state.bindings.allowCustomSetting = false
|
viewModel.state.bindings.allowCustomSetting = false
|
||||||
context.send(viewAction: .changedAllowCustomSettings)
|
viewModel.context.send(viewAction: .changedAllowCustomSettings)
|
||||||
let states = try await deferredIsRestoringDefaultSettings.fulfill()
|
|
||||||
XCTAssertEqual(states, [false, true, false])
|
try await deferredIsRestoringDefaultSettings.fulfill()
|
||||||
|
|
||||||
XCTAssertEqual(notificationSettingsProxyMock.restoreDefaultNotificationModeRoomIdReceivedRoomId, roomProxyMock.id)
|
XCTAssertEqual(notificationSettingsProxyMock.restoreDefaultNotificationModeRoomIdReceivedRoomId, roomProxyMock.id)
|
||||||
XCTAssertEqual(notificationSettingsProxyMock.restoreDefaultNotificationModeRoomIdCallsCount, 1)
|
XCTAssertEqual(notificationSettingsProxyMock.restoreDefaultNotificationModeRoomIdCallsCount, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testToggleAllCustomSettingOffOn() async throws {
|
func testToggleAllCustomSettingOffOn() async throws {
|
||||||
|
let notificationSettingsProxyMock = NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration())
|
||||||
notificationSettingsProxyMock.getNotificationSettingsRoomIdIsEncryptedIsOneToOneReturnValue = RoomNotificationSettingsProxyMock(with: .init(mode: .mentionsAndKeywordsOnly, isDefault: true))
|
notificationSettingsProxyMock.getNotificationSettingsRoomIdIsEncryptedIsOneToOneReturnValue = RoomNotificationSettingsProxyMock(with: .init(mode: .mentionsAndKeywordsOnly, isDefault: true))
|
||||||
viewModel = RoomNotificationSettingsScreenViewModel(notificationSettingsProxy: notificationSettingsProxyMock,
|
let viewModel = RoomNotificationSettingsScreenViewModel(notificationSettingsProxy: notificationSettingsProxyMock,
|
||||||
roomProxy: roomProxyMock,
|
roomProxy: roomProxyMock,
|
||||||
displayAsUserDefinedRoomSettings: false)
|
displayAsUserDefinedRoomSettings: false)
|
||||||
var deferred = deferFulfillment(context.$viewState.map(\.notificationSettingsState).first(where: \.isLoaded))
|
|
||||||
|
var deferred = deferFulfillment(viewModel.context.$viewState) { state in
|
||||||
|
state.notificationSettingsState.isLoaded
|
||||||
|
}
|
||||||
|
|
||||||
notificationSettingsProxyMock.callbacks.send(.settingsDidChange)
|
notificationSettingsProxyMock.callbacks.send(.settingsDidChange)
|
||||||
|
|
||||||
try await deferred.fulfill()
|
try await deferred.fulfill()
|
||||||
|
|
||||||
deferred = deferFulfillment(context.$viewState.map(\.notificationSettingsState).first(where: \.isLoaded))
|
deferred = deferFulfillment(viewModel.context.$viewState) { state in
|
||||||
|
state.notificationSettingsState.isLoaded
|
||||||
|
}
|
||||||
|
|
||||||
viewModel.state.bindings.allowCustomSetting = true
|
viewModel.state.bindings.allowCustomSetting = true
|
||||||
context.send(viewAction: .changedAllowCustomSettings)
|
viewModel.context.send(viewAction: .changedAllowCustomSettings)
|
||||||
|
|
||||||
try await deferred.fulfill()
|
try await deferred.fulfill()
|
||||||
|
|
||||||
XCTAssertEqual(notificationSettingsProxyMock.setNotificationModeRoomIdModeReceivedArguments?.0, roomProxyMock.id)
|
XCTAssertEqual(notificationSettingsProxyMock.setNotificationModeRoomIdModeReceivedArguments?.0, roomProxyMock.id)
|
||||||
@ -124,17 +143,25 @@ class RoomNotificationSettingsScreenViewModelTests: XCTestCase {
|
|||||||
|
|
||||||
func testSetCustomMode() async throws {
|
func testSetCustomMode() async throws {
|
||||||
notificationSettingsProxyMock.getNotificationSettingsRoomIdIsEncryptedIsOneToOneReturnValue = RoomNotificationSettingsProxyMock(with: .init(mode: .mentionsAndKeywordsOnly, isDefault: false))
|
notificationSettingsProxyMock.getNotificationSettingsRoomIdIsEncryptedIsOneToOneReturnValue = RoomNotificationSettingsProxyMock(with: .init(mode: .mentionsAndKeywordsOnly, isDefault: false))
|
||||||
viewModel = RoomNotificationSettingsScreenViewModel(notificationSettingsProxy: notificationSettingsProxyMock,
|
let viewModel = RoomNotificationSettingsScreenViewModel(notificationSettingsProxy: notificationSettingsProxyMock,
|
||||||
roomProxy: roomProxyMock,
|
roomProxy: roomProxyMock,
|
||||||
displayAsUserDefinedRoomSettings: false)
|
displayAsUserDefinedRoomSettings: false)
|
||||||
let deferredState = deferFulfillment(context.$viewState.map(\.notificationSettingsState).first(where: \.isLoaded))
|
|
||||||
|
let deferredState = deferFulfillment(viewModel.context.$viewState) { state in
|
||||||
|
state.notificationSettingsState.isLoaded
|
||||||
|
}
|
||||||
|
|
||||||
notificationSettingsProxyMock.callbacks.send(.settingsDidChange)
|
notificationSettingsProxyMock.callbacks.send(.settingsDidChange)
|
||||||
try await deferredState.fulfill()
|
try await deferredState.fulfill()
|
||||||
|
|
||||||
do {
|
do {
|
||||||
let deferredViewState = deferFulfillment(context.$viewState.collect(2).first())
|
viewModel.context.send(viewAction: .setCustomMode(.allMessages))
|
||||||
context.send(viewAction: .setCustomMode(.allMessages))
|
|
||||||
try await deferredViewState.fulfill()
|
let deferredState = deferFulfillment(viewModel.context.$viewState) { state in
|
||||||
|
state.pendingCustomMode == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
try await deferredState.fulfill()
|
||||||
|
|
||||||
XCTAssertEqual(notificationSettingsProxyMock.setNotificationModeRoomIdModeReceivedArguments?.0, roomProxyMock.id)
|
XCTAssertEqual(notificationSettingsProxyMock.setNotificationModeRoomIdModeReceivedArguments?.0, roomProxyMock.id)
|
||||||
XCTAssertEqual(notificationSettingsProxyMock.setNotificationModeRoomIdModeReceivedArguments?.1, .allMessages)
|
XCTAssertEqual(notificationSettingsProxyMock.setNotificationModeRoomIdModeReceivedArguments?.1, .allMessages)
|
||||||
@ -142,9 +169,13 @@ class RoomNotificationSettingsScreenViewModelTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
do {
|
do {
|
||||||
let deferredViewState = deferFulfillment(context.$viewState.collect(2).first())
|
viewModel.context.send(viewAction: .setCustomMode(.mute))
|
||||||
context.send(viewAction: .setCustomMode(.mute))
|
|
||||||
try await deferredViewState.fulfill()
|
let deferredState = deferFulfillment(viewModel.context.$viewState) { state in
|
||||||
|
state.pendingCustomMode == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
try await deferredState.fulfill()
|
||||||
|
|
||||||
XCTAssertEqual(notificationSettingsProxyMock.setNotificationModeRoomIdModeReceivedArguments?.0, roomProxyMock.id)
|
XCTAssertEqual(notificationSettingsProxyMock.setNotificationModeRoomIdModeReceivedArguments?.0, roomProxyMock.id)
|
||||||
XCTAssertEqual(notificationSettingsProxyMock.setNotificationModeRoomIdModeReceivedArguments?.1, .mute)
|
XCTAssertEqual(notificationSettingsProxyMock.setNotificationModeRoomIdModeReceivedArguments?.1, .mute)
|
||||||
@ -152,9 +183,13 @@ class RoomNotificationSettingsScreenViewModelTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
do {
|
do {
|
||||||
let deferredViewState = deferFulfillment(context.$viewState.collect(2).first())
|
viewModel.context.send(viewAction: .setCustomMode(.mentionsAndKeywordsOnly))
|
||||||
context.send(viewAction: .setCustomMode(.mentionsAndKeywordsOnly))
|
|
||||||
try await deferredViewState.fulfill()
|
let deferredState = deferFulfillment(viewModel.context.$viewState) { state in
|
||||||
|
state.pendingCustomMode == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
try await deferredState.fulfill()
|
||||||
|
|
||||||
XCTAssertEqual(notificationSettingsProxyMock.setNotificationModeRoomIdModeReceivedArguments?.0, roomProxyMock.id)
|
XCTAssertEqual(notificationSettingsProxyMock.setNotificationModeRoomIdModeReceivedArguments?.0, roomProxyMock.id)
|
||||||
XCTAssertEqual(notificationSettingsProxyMock.setNotificationModeRoomIdModeReceivedArguments?.1, .mentionsAndKeywordsOnly)
|
XCTAssertEqual(notificationSettingsProxyMock.setNotificationModeRoomIdModeReceivedArguments?.1, .mentionsAndKeywordsOnly)
|
||||||
@ -164,12 +199,15 @@ class RoomNotificationSettingsScreenViewModelTests: XCTestCase {
|
|||||||
|
|
||||||
func testDeleteCustomSettingTapped() async throws {
|
func testDeleteCustomSettingTapped() async throws {
|
||||||
notificationSettingsProxyMock.getNotificationSettingsRoomIdIsEncryptedIsOneToOneReturnValue = RoomNotificationSettingsProxyMock(with: .init(mode: .mentionsAndKeywordsOnly, isDefault: false))
|
notificationSettingsProxyMock.getNotificationSettingsRoomIdIsEncryptedIsOneToOneReturnValue = RoomNotificationSettingsProxyMock(with: .init(mode: .mentionsAndKeywordsOnly, isDefault: false))
|
||||||
viewModel = RoomNotificationSettingsScreenViewModel(notificationSettingsProxy: notificationSettingsProxyMock,
|
let viewModel = RoomNotificationSettingsScreenViewModel(notificationSettingsProxy: notificationSettingsProxyMock,
|
||||||
roomProxy: roomProxyMock,
|
roomProxy: roomProxyMock,
|
||||||
displayAsUserDefinedRoomSettings: true)
|
displayAsUserDefinedRoomSettings: true)
|
||||||
let deferredState = deferFulfillment(context.$viewState.map(\.notificationSettingsState).first(where: \.isLoaded))
|
let deferred = deferFulfillment(viewModel.context.$viewState) { state in
|
||||||
|
state.notificationSettingsState.isLoaded
|
||||||
|
}
|
||||||
|
|
||||||
notificationSettingsProxyMock.callbacks.send(.settingsDidChange)
|
notificationSettingsProxyMock.callbacks.send(.settingsDidChange)
|
||||||
try await deferredState.fulfill()
|
try await deferred.fulfill()
|
||||||
|
|
||||||
var actionSent: RoomNotificationSettingsScreenViewModelAction?
|
var actionSent: RoomNotificationSettingsScreenViewModelAction?
|
||||||
viewModel.actions
|
viewModel.actions
|
||||||
@ -178,33 +216,35 @@ class RoomNotificationSettingsScreenViewModelTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
|
||||||
let deferredViewState = deferFulfillment(context.$viewState
|
let deferredViewState = deferFulfillment(viewModel.context.$viewState,
|
||||||
.map(\.deletingCustomSetting)
|
keyPath: \.deletingCustomSetting,
|
||||||
.removeDuplicates()
|
transitionValues: [false, true, false])
|
||||||
.collect(3).first())
|
|
||||||
context.send(viewAction: .deleteCustomSettingTapped)
|
viewModel.context.send(viewAction: .deleteCustomSettingTapped)
|
||||||
let states = try await deferredViewState.fulfill()
|
|
||||||
|
try await deferredViewState.fulfill()
|
||||||
|
|
||||||
// `deletingCustomSetting` must be set to `true` when deleting, and reset to `false` afterwards.
|
|
||||||
XCTAssertEqual(states, [false, true, false])
|
|
||||||
// the `dismiss` action must have been sent
|
// the `dismiss` action must have been sent
|
||||||
XCTAssertEqual(actionSent, .dismiss)
|
XCTAssertEqual(actionSent, .dismiss)
|
||||||
// `restoreDefaultNotificationMode` should have been called
|
// `restoreDefaultNotificationMode` should have been called
|
||||||
XCTAssert(notificationSettingsProxyMock.restoreDefaultNotificationModeRoomIdCalled)
|
XCTAssert(notificationSettingsProxyMock.restoreDefaultNotificationModeRoomIdCalled)
|
||||||
XCTAssertEqual(notificationSettingsProxyMock.restoreDefaultNotificationModeRoomIdReceivedInvocations, [roomProxyMock.id])
|
XCTAssertEqual(notificationSettingsProxyMock.restoreDefaultNotificationModeRoomIdReceivedInvocations, [roomProxyMock.id])
|
||||||
// and no alert is expected
|
// and no alert is expected
|
||||||
XCTAssertNil(context.alertInfo)
|
XCTAssertNil(viewModel.context.alertInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testDeleteCustomSettingTappedFailure() async throws {
|
func testDeleteCustomSettingTappedFailure() async throws {
|
||||||
notificationSettingsProxyMock.getNotificationSettingsRoomIdIsEncryptedIsOneToOneReturnValue = RoomNotificationSettingsProxyMock(with: .init(mode: .mentionsAndKeywordsOnly, isDefault: false))
|
notificationSettingsProxyMock.getNotificationSettingsRoomIdIsEncryptedIsOneToOneReturnValue = RoomNotificationSettingsProxyMock(with: .init(mode: .mentionsAndKeywordsOnly, isDefault: false))
|
||||||
notificationSettingsProxyMock.restoreDefaultNotificationModeRoomIdThrowableError = NotificationSettingsError.Generic(message: "error")
|
notificationSettingsProxyMock.restoreDefaultNotificationModeRoomIdThrowableError = NotificationSettingsError.Generic(message: "error")
|
||||||
viewModel = RoomNotificationSettingsScreenViewModel(notificationSettingsProxy: notificationSettingsProxyMock,
|
let viewModel = RoomNotificationSettingsScreenViewModel(notificationSettingsProxy: notificationSettingsProxyMock,
|
||||||
roomProxy: roomProxyMock,
|
roomProxy: roomProxyMock,
|
||||||
displayAsUserDefinedRoomSettings: true)
|
displayAsUserDefinedRoomSettings: true)
|
||||||
let deferredState = deferFulfillment(context.$viewState.map(\.notificationSettingsState).first(where: \.isLoaded))
|
let deferred = deferFulfillment(viewModel.context.$viewState) { state in
|
||||||
|
state.notificationSettingsState.isLoaded
|
||||||
|
}
|
||||||
|
|
||||||
notificationSettingsProxyMock.callbacks.send(.settingsDidChange)
|
notificationSettingsProxyMock.callbacks.send(.settingsDidChange)
|
||||||
try await deferredState.fulfill()
|
try await deferred.fulfill()
|
||||||
|
|
||||||
var actionSent: RoomNotificationSettingsScreenViewModelAction?
|
var actionSent: RoomNotificationSettingsScreenViewModelAction?
|
||||||
viewModel.actions
|
viewModel.actions
|
||||||
@ -213,17 +253,16 @@ class RoomNotificationSettingsScreenViewModelTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
|
||||||
let deferredViewState = deferFulfillment(context.$viewState
|
let deferredViewState = deferFulfillment(viewModel.context.$viewState,
|
||||||
.map(\.deletingCustomSetting)
|
keyPath: \.deletingCustomSetting,
|
||||||
.removeDuplicates()
|
transitionValues: [false, true, false])
|
||||||
.collect(3).first())
|
|
||||||
context.send(viewAction: .deleteCustomSettingTapped)
|
|
||||||
let states = try await deferredViewState.fulfill()
|
|
||||||
|
|
||||||
// `deletingCustomSetting` must be set to `true` when deleting, and reset to `false` afterwards.
|
viewModel.context.send(viewAction: .deleteCustomSettingTapped)
|
||||||
XCTAssertEqual(states, [false, true, false])
|
|
||||||
|
try await deferredViewState.fulfill()
|
||||||
|
|
||||||
// an alert is expected
|
// an alert is expected
|
||||||
XCTAssertEqual(context.alertInfo?.id, .restoreDefaultFailed)
|
XCTAssertEqual(viewModel.context.alertInfo?.id, .restoreDefaultFailed)
|
||||||
// the `dismiss` action must not have been sent
|
// the `dismiss` action must not have been sent
|
||||||
XCTAssertNil(actionSent)
|
XCTAssertNil(actionSent)
|
||||||
}
|
}
|
||||||
|
@ -282,11 +282,14 @@ class RoomScreenViewModelTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
|
||||||
// Test
|
let deferred = deferFulfillment(viewModel.context.$viewState) { value in
|
||||||
let deferred = deferFulfillment(viewModel.context.$viewState.collect(3).first(),
|
value.bindings.alertInfo != nil
|
||||||
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"))
|
||||||
|
|
||||||
try await deferred.fulfill()
|
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")
|
||||||
@ -295,7 +298,6 @@ class RoomScreenViewModelTests: XCTestCase {
|
|||||||
// MARK: - Sending
|
// MARK: - Sending
|
||||||
|
|
||||||
func testRetrySend() async throws {
|
func testRetrySend() async throws {
|
||||||
// Setup
|
|
||||||
let timelineController = MockRoomTimelineController()
|
let timelineController = MockRoomTimelineController()
|
||||||
let roomProxyMock = RoomProxyMock(with: .init(displayName: ""))
|
let roomProxyMock = RoomProxyMock(with: .init(displayName: ""))
|
||||||
|
|
||||||
@ -306,16 +308,15 @@ class RoomScreenViewModelTests: XCTestCase {
|
|||||||
analytics: ServiceLocator.shared.analytics,
|
analytics: ServiceLocator.shared.analytics,
|
||||||
userIndicatorController: userIndicatorControllerMock)
|
userIndicatorController: userIndicatorControllerMock)
|
||||||
|
|
||||||
// Test
|
|
||||||
viewModel.context.send(viewAction: .retrySend(itemID: .init(timelineID: UUID().uuidString, transactionID: "test retry send id")))
|
viewModel.context.send(viewAction: .retrySend(itemID: .init(timelineID: UUID().uuidString, transactionID: "test retry send id")))
|
||||||
await Task.yield()
|
|
||||||
try? await Task.sleep(for: .microseconds(500))
|
try? await Task.sleep(for: .milliseconds(100))
|
||||||
|
|
||||||
XCTAssert(roomProxyMock.retrySendTransactionIDCallsCount == 1)
|
XCTAssert(roomProxyMock.retrySendTransactionIDCallsCount == 1)
|
||||||
XCTAssert(roomProxyMock.retrySendTransactionIDReceivedInvocations == ["test retry send id"])
|
XCTAssert(roomProxyMock.retrySendTransactionIDReceivedInvocations == ["test retry send id"])
|
||||||
}
|
}
|
||||||
|
|
||||||
func testRetrySendNoTransactionID() async {
|
func testRetrySendNoTransactionID() async {
|
||||||
// Setup
|
|
||||||
let timelineController = MockRoomTimelineController()
|
let timelineController = MockRoomTimelineController()
|
||||||
let roomProxyMock = RoomProxyMock(with: .init(displayName: ""))
|
let roomProxyMock = RoomProxyMock(with: .init(displayName: ""))
|
||||||
|
|
||||||
@ -326,14 +327,14 @@ class RoomScreenViewModelTests: XCTestCase {
|
|||||||
analytics: ServiceLocator.shared.analytics,
|
analytics: ServiceLocator.shared.analytics,
|
||||||
userIndicatorController: userIndicatorControllerMock)
|
userIndicatorController: userIndicatorControllerMock)
|
||||||
|
|
||||||
// Test
|
|
||||||
viewModel.context.send(viewAction: .retrySend(itemID: .random))
|
viewModel.context.send(viewAction: .retrySend(itemID: .random))
|
||||||
await Task.yield()
|
|
||||||
|
try? await Task.sleep(for: .milliseconds(100))
|
||||||
|
|
||||||
XCTAssert(roomProxyMock.retrySendTransactionIDCallsCount == 0)
|
XCTAssert(roomProxyMock.retrySendTransactionIDCallsCount == 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testCancelSend() async {
|
func testCancelSend() async {
|
||||||
// Setup
|
|
||||||
let timelineController = MockRoomTimelineController()
|
let timelineController = MockRoomTimelineController()
|
||||||
let roomProxyMock = RoomProxyMock(with: .init(displayName: ""))
|
let roomProxyMock = RoomProxyMock(with: .init(displayName: ""))
|
||||||
|
|
||||||
@ -344,15 +345,15 @@ class RoomScreenViewModelTests: XCTestCase {
|
|||||||
analytics: ServiceLocator.shared.analytics,
|
analytics: ServiceLocator.shared.analytics,
|
||||||
userIndicatorController: userIndicatorControllerMock)
|
userIndicatorController: userIndicatorControllerMock)
|
||||||
|
|
||||||
// Test
|
|
||||||
viewModel.context.send(viewAction: .cancelSend(itemID: .init(timelineID: UUID().uuidString, transactionID: "test cancel send id")))
|
viewModel.context.send(viewAction: .cancelSend(itemID: .init(timelineID: UUID().uuidString, transactionID: "test cancel send id")))
|
||||||
try? await Task.sleep(for: .microseconds(500))
|
|
||||||
|
try? await Task.sleep(for: .milliseconds(100))
|
||||||
|
|
||||||
XCTAssert(roomProxyMock.cancelSendTransactionIDCallsCount == 1)
|
XCTAssert(roomProxyMock.cancelSendTransactionIDCallsCount == 1)
|
||||||
XCTAssert(roomProxyMock.cancelSendTransactionIDReceivedInvocations == ["test cancel send id"])
|
XCTAssert(roomProxyMock.cancelSendTransactionIDReceivedInvocations == ["test cancel send id"])
|
||||||
}
|
}
|
||||||
|
|
||||||
func testCancelSendNoTransactionID() async {
|
func testCancelSendNoTransactionID() async {
|
||||||
// Setup
|
|
||||||
let timelineController = MockRoomTimelineController()
|
let timelineController = MockRoomTimelineController()
|
||||||
let roomProxyMock = RoomProxyMock(with: .init(displayName: ""))
|
let roomProxyMock = RoomProxyMock(with: .init(displayName: ""))
|
||||||
|
|
||||||
@ -363,16 +364,16 @@ class RoomScreenViewModelTests: XCTestCase {
|
|||||||
analytics: ServiceLocator.shared.analytics,
|
analytics: ServiceLocator.shared.analytics,
|
||||||
userIndicatorController: userIndicatorControllerMock)
|
userIndicatorController: userIndicatorControllerMock)
|
||||||
|
|
||||||
// Test
|
|
||||||
viewModel.context.send(viewAction: .cancelSend(itemID: .random))
|
viewModel.context.send(viewAction: .cancelSend(itemID: .random))
|
||||||
await Task.yield()
|
|
||||||
|
try? await Task.sleep(for: .milliseconds(100))
|
||||||
|
|
||||||
XCTAssert(roomProxyMock.cancelSendTransactionIDCallsCount == 0)
|
XCTAssert(roomProxyMock.cancelSendTransactionIDCallsCount == 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Read Receipts
|
// MARK: - Read Receipts
|
||||||
|
|
||||||
// swiftlint:disable force_unwrapping
|
// swiftlint:disable force_unwrapping
|
||||||
|
|
||||||
func testSendReadReceipt() async throws {
|
func testSendReadReceipt() async throws {
|
||||||
// Given a room with only text items in the timeline
|
// Given a room with only text items in the timeline
|
||||||
let items = [TextRoomTimelineItem(eventID: "t1"),
|
let items = [TextRoomTimelineItem(eventID: "t1"),
|
||||||
@ -382,7 +383,7 @@ class RoomScreenViewModelTests: XCTestCase {
|
|||||||
|
|
||||||
// When sending a read receipt for the last item.
|
// When sending a read receipt for the last item.
|
||||||
viewModel.context.send(viewAction: .sendReadReceiptIfNeeded(items.last!.id))
|
viewModel.context.send(viewAction: .sendReadReceiptIfNeeded(items.last!.id))
|
||||||
try await Task.sleep(for: .microseconds(100))
|
try await Task.sleep(for: .milliseconds(100))
|
||||||
|
|
||||||
// Then the receipt should be sent.
|
// Then the receipt should be sent.
|
||||||
XCTAssertEqual(roomProxy.sendReadReceiptForCalled, true)
|
XCTAssertEqual(roomProxy.sendReadReceiptForCalled, true)
|
||||||
@ -401,13 +402,13 @@ class RoomScreenViewModelTests: XCTestCase {
|
|||||||
TextRoomTimelineItem(eventID: "t3")]
|
TextRoomTimelineItem(eventID: "t3")]
|
||||||
let (viewModel, roomProxy, timelineController, _) = readReceiptsConfiguration(with: items)
|
let (viewModel, roomProxy, timelineController, _) = readReceiptsConfiguration(with: items)
|
||||||
viewModel.context.send(viewAction: .sendReadReceiptIfNeeded(items.last!.id))
|
viewModel.context.send(viewAction: .sendReadReceiptIfNeeded(items.last!.id))
|
||||||
try await Task.sleep(for: .microseconds(100))
|
try await Task.sleep(for: .milliseconds(100))
|
||||||
XCTAssertEqual(roomProxy.sendReadReceiptForCallsCount, 1)
|
XCTAssertEqual(roomProxy.sendReadReceiptForCallsCount, 1)
|
||||||
XCTAssertEqual(roomProxy.sendReadReceiptForReceivedEventID, "t3")
|
XCTAssertEqual(roomProxy.sendReadReceiptForReceivedEventID, "t3")
|
||||||
|
|
||||||
// When sending a receipt for the first item in the timeline.
|
// When sending a receipt for the first item in the timeline.
|
||||||
viewModel.context.send(viewAction: .sendReadReceiptIfNeeded(items.first!.id))
|
viewModel.context.send(viewAction: .sendReadReceiptIfNeeded(items.first!.id))
|
||||||
try await Task.sleep(for: .microseconds(100))
|
try await Task.sleep(for: .milliseconds(100))
|
||||||
|
|
||||||
// Then the request should be ignored.
|
// Then the request should be ignored.
|
||||||
XCTAssertEqual(roomProxy.sendReadReceiptForCallsCount, 1)
|
XCTAssertEqual(roomProxy.sendReadReceiptForCallsCount, 1)
|
||||||
@ -417,10 +418,10 @@ class RoomScreenViewModelTests: XCTestCase {
|
|||||||
let newMessage = TextRoomTimelineItem(eventID: "t4")
|
let newMessage = TextRoomTimelineItem(eventID: "t4")
|
||||||
timelineController.timelineItems.append(newMessage)
|
timelineController.timelineItems.append(newMessage)
|
||||||
timelineController.callbacks.send(.updatedTimelineItems)
|
timelineController.callbacks.send(.updatedTimelineItems)
|
||||||
try await Task.sleep(for: .microseconds(500))
|
try await Task.sleep(for: .milliseconds(100))
|
||||||
|
|
||||||
viewModel.context.send(viewAction: .sendReadReceiptIfNeeded(newMessage.id))
|
viewModel.context.send(viewAction: .sendReadReceiptIfNeeded(newMessage.id))
|
||||||
try await Task.sleep(for: .microseconds(100))
|
try await Task.sleep(for: .milliseconds(100))
|
||||||
|
|
||||||
// Then the request should be made.
|
// Then the request should be made.
|
||||||
XCTAssertEqual(roomProxy.sendReadReceiptForCallsCount, 2)
|
XCTAssertEqual(roomProxy.sendReadReceiptForCallsCount, 2)
|
||||||
@ -436,7 +437,7 @@ class RoomScreenViewModelTests: XCTestCase {
|
|||||||
|
|
||||||
// When sending a read receipt for the last item.
|
// When sending a read receipt for the last item.
|
||||||
viewModel.context.send(viewAction: .sendReadReceiptIfNeeded(items.last!.id))
|
viewModel.context.send(viewAction: .sendReadReceiptIfNeeded(items.last!.id))
|
||||||
try await Task.sleep(for: .microseconds(100))
|
try await Task.sleep(for: .milliseconds(100))
|
||||||
|
|
||||||
// Then nothing should be sent.
|
// Then nothing should be sent.
|
||||||
XCTAssertEqual(roomProxy.sendReadReceiptForCalled, false)
|
XCTAssertEqual(roomProxy.sendReadReceiptForCalled, false)
|
||||||
@ -451,7 +452,7 @@ class RoomScreenViewModelTests: XCTestCase {
|
|||||||
|
|
||||||
// When sending a read receipt for the last item.
|
// When sending a read receipt for the last item.
|
||||||
viewModel.context.send(viewAction: .sendReadReceiptIfNeeded(items.last!.id))
|
viewModel.context.send(viewAction: .sendReadReceiptIfNeeded(items.last!.id))
|
||||||
try await Task.sleep(for: .microseconds(100))
|
try await Task.sleep(for: .milliseconds(100))
|
||||||
|
|
||||||
// Then a read receipt should be sent for the item before it.
|
// Then a read receipt should be sent for the item before it.
|
||||||
XCTAssertEqual(roomProxy.sendReadReceiptForCalled, true)
|
XCTAssertEqual(roomProxy.sendReadReceiptForCalled, true)
|
||||||
@ -465,13 +466,13 @@ class RoomScreenViewModelTests: XCTestCase {
|
|||||||
SeparatorRoomTimelineItem(timelineID: "v3")]
|
SeparatorRoomTimelineItem(timelineID: "v3")]
|
||||||
let (viewModel, roomProxy, _, _) = readReceiptsConfiguration(with: items)
|
let (viewModel, roomProxy, _, _) = readReceiptsConfiguration(with: items)
|
||||||
viewModel.context.send(viewAction: .sendReadReceiptIfNeeded(items.last!.id))
|
viewModel.context.send(viewAction: .sendReadReceiptIfNeeded(items.last!.id))
|
||||||
try await Task.sleep(for: .microseconds(100))
|
try await Task.sleep(for: .milliseconds(100))
|
||||||
XCTAssertEqual(roomProxy.sendReadReceiptForCallsCount, 1)
|
XCTAssertEqual(roomProxy.sendReadReceiptForCallsCount, 1)
|
||||||
XCTAssertEqual(roomProxy.sendReadReceiptForReceivedEventID, "t2")
|
XCTAssertEqual(roomProxy.sendReadReceiptForReceivedEventID, "t2")
|
||||||
|
|
||||||
// When sending the same receipt again
|
// When sending the same receipt again
|
||||||
viewModel.context.send(viewAction: .sendReadReceiptIfNeeded(items.last!.id))
|
viewModel.context.send(viewAction: .sendReadReceiptIfNeeded(items.last!.id))
|
||||||
try await Task.sleep(for: .microseconds(100))
|
try await Task.sleep(for: .milliseconds(100))
|
||||||
|
|
||||||
// Then the second call should be ignored.
|
// Then the second call should be ignored.
|
||||||
XCTAssertEqual(roomProxy.sendReadReceiptForCallsCount, 1)
|
XCTAssertEqual(roomProxy.sendReadReceiptForCallsCount, 1)
|
||||||
|
@ -50,7 +50,11 @@ class SessionVerificationViewModelTests: XCTestCase {
|
|||||||
|
|
||||||
XCTAssertEqual(context.viewState.verificationState, .cancelling)
|
XCTAssertEqual(context.viewState.verificationState, .cancelling)
|
||||||
|
|
||||||
await context.nextViewState()
|
let deferred = deferFulfillment(context.$viewState) { state in
|
||||||
|
state.verificationState == .cancelled
|
||||||
|
}
|
||||||
|
|
||||||
|
try await deferred.fulfill()
|
||||||
|
|
||||||
XCTAssertEqual(context.viewState.verificationState, .cancelled)
|
XCTAssertEqual(context.viewState.verificationState, .cancelled)
|
||||||
|
|
||||||
|
@ -81,7 +81,16 @@ class StaticLocationScreenViewModelTests: XCTestCase {
|
|||||||
func testSendUserLocation() async throws {
|
func testSendUserLocation() async throws {
|
||||||
context.mapCenterLocation = .init(latitude: 0, longitude: 0)
|
context.mapCenterLocation = .init(latitude: 0, longitude: 0)
|
||||||
context.geolocationUncertainty = 10
|
context.geolocationUncertainty = 10
|
||||||
let deferred = deferFulfillment(viewModel.actions.first())
|
|
||||||
|
let deferred = deferFulfillment(viewModel.actions) { action in
|
||||||
|
switch action {
|
||||||
|
case .sendLocation:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
context.send(viewAction: .selectLocation)
|
context.send(viewAction: .selectLocation)
|
||||||
guard case .sendLocation(let geoUri, let isUserLocation) = try await deferred.fulfill() else {
|
guard case .sendLocation(let geoUri, let isUserLocation) = try await deferred.fulfill() else {
|
||||||
XCTFail("Sent action should be 'sendLocation'")
|
XCTFail("Sent action should be 'sendLocation'")
|
||||||
@ -95,7 +104,16 @@ class StaticLocationScreenViewModelTests: XCTestCase {
|
|||||||
context.mapCenterLocation = .init(latitude: 0, longitude: 0)
|
context.mapCenterLocation = .init(latitude: 0, longitude: 0)
|
||||||
context.isLocationAuthorized = nil
|
context.isLocationAuthorized = nil
|
||||||
context.geolocationUncertainty = 10
|
context.geolocationUncertainty = 10
|
||||||
let deferred = deferFulfillment(viewModel.actions.first())
|
|
||||||
|
let deferred = deferFulfillment(viewModel.actions) { action in
|
||||||
|
switch action {
|
||||||
|
case .sendLocation:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
context.send(viewAction: .selectLocation)
|
context.send(viewAction: .selectLocation)
|
||||||
guard case .sendLocation(let geoUri, let isUserLocation) = try await deferred.fulfill() else {
|
guard case .sendLocation(let geoUri, let isUserLocation) = try await deferred.fulfill() else {
|
||||||
XCTFail("Sent action should be 'sendLocation'")
|
XCTFail("Sent action should be 'sendLocation'")
|
||||||
|
@ -81,7 +81,6 @@ lane :unit_tests do
|
|||||||
device: 'iPhone 14 (16.4)',
|
device: 'iPhone 14 (16.4)',
|
||||||
ensure_devices_found: true,
|
ensure_devices_found: true,
|
||||||
result_bundle: true,
|
result_bundle: true,
|
||||||
number_of_retries: 3,
|
|
||||||
xcargs: '-skipPackagePluginValidation',
|
xcargs: '-skipPackagePluginValidation',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user