Run SwiftFormat.

This commit is contained in:
Doug
2026-05-27 14:38:50 +01:00
committed by Doug
parent 5138e05e47
commit fa67713726
573 changed files with 2426 additions and 2426 deletions
@@ -13,7 +13,7 @@ import Testing
final class AnalyticsSettingsScreenViewModelTests {
private let appSettings: AppSettings
private let analytics: AnalyticsService
private var viewModel: AnalyticsSettingsScreenViewModelProtocol!
private var context: AnalyticsSettingsScreenViewModelType.Context!
@@ -22,7 +22,7 @@ final class AnalyticsSettingsScreenViewModelTests {
let client = AnalyticsClientMock()
client.isRunning = false
analytics = AnalyticsService(client: client, appSettings: appSettings)
viewModel = AnalyticsSettingsScreenViewModel(appSettings: appSettings,
analytics: analytics)
context = viewModel.context
@@ -32,7 +32,7 @@ final class AnalyticsSettingsScreenViewModelTests {
func initialState() {
#expect(!context.enableAnalytics)
}
@Test
func optIn() {
appSettings.analyticsConsentState = .optedOut
@@ -24,7 +24,7 @@ struct AppLockSetupSettingsScreenViewModelTests {
appLockService = AppLockService(keychainController: keychainController, appSettings: .volatile())
viewModel = AppLockSetupSettingsScreenViewModel(appLockService: AppLockServiceMock.mock())
}
@Test
func disablingShowsAlert() {
// Given a fresh screen with the PIN code enabled.
@@ -36,7 +36,7 @@ final class AppLockSetupBiometricsScreenViewModelTests {
// Then the service should now have biometric unlock enabled.
#expect(appLockService.enableBiometricUnlockCallsCount == 1)
}
@Test
func skip() async throws {
// When skipping biometrics.
@@ -277,22 +277,22 @@ struct AudioPlayerStateTests {
@Test
func setPlaybackSpeed() {
audioPlayerState.attachAudioPlayer(audioPlayerMock)
#expect(audioPlayerState.playbackSpeed == .default)
audioPlayerState.setPlaybackSpeed(.fast)
#expect(audioPlayerState.playbackSpeed == .fast)
#expect(audioPlayerMock.setPlaybackSpeedReceivedSpeed == 1.5)
audioPlayerState.setPlaybackSpeed(.fastest)
#expect(audioPlayerState.playbackSpeed == .fastest)
#expect(audioPlayerMock.setPlaybackSpeedReceivedSpeed == 2.0)
audioPlayerState.setPlaybackSpeed(.slow)
#expect(audioPlayerState.playbackSpeed == .slow)
#expect(audioPlayerMock.setPlaybackSpeedReceivedSpeed == 0.5)
}
@Test
func audioPlayerActionsDidFailed() async throws {
audioPlayerState.attachAudioPlayer(audioPlayerMock)
@@ -19,7 +19,7 @@ final class ComposerToolbarViewModelTests {
private var viewModel: ComposerToolbarViewModel!
private var completionSuggestionServiceMock: CompletionSuggestionServiceMock!
private var draftServiceMock: ComposerDraftServiceMock!
init() {
setUpViewModel()
}
@@ -16,19 +16,19 @@ final class CreateRoomScreenViewModelTests {
var clientProxy: ClientProxyMock!
var spaceService: SpaceServiceProxyMock!
var userSession: UserSessionMock!
private let appSettings: AppSettings
private let usersSubject = CurrentValueSubject<[UserProfileProxy], Never>([])
var context: CreateRoomScreenViewModel.Context {
viewModel.context
}
init() {
appSettings = AppSettings.volatile()
}
deinit {
viewModel = nil
clientProxy = nil
@@ -121,20 +121,20 @@ struct EditRoomAddressScreenViewModelTests {
confirm()
return .success(true)
}
roomProxy.updateCanonicalAliasAltAliasesClosure = { roomAlias, altAliases in
#expect(altAliases == [])
#expect(roomAlias == "#room-name:matrix.org")
confirm()
return .success(())
}
roomProxy.removeRoomAliasFromRoomDirectoryClosure = { roomAlias in
#expect(roomAlias == "#old-room-name:matrix.org")
confirm()
return .success(true)
}
context.send(viewAction: .save)
}
@@ -17,7 +17,7 @@ struct EmojiPickerScreenViewModelTests {
var context: EmojiPickerScreenViewModel.Context {
viewModel.context
}
@Test
mutating func toggleReaction() async throws {
setupViewModel()
+2 -2
View File
@@ -24,7 +24,7 @@ struct EmojiProviderTests {
let categories = await emojiProvider.categories()
#expect(emojiLoaderMock.categories == categories)
}
@Test @MainActor
func emojisLoadedAndSearchStringEmptyAllCategoriesReturned() async {
let item = EmojiItem(label: "test", unicode: "test", keywords: ["1", "2"], shortcodes: ["1", "2"])
@@ -38,7 +38,7 @@ struct EmojiProviderTests {
let categories = await emojiProvider.categories(searchString: "")
#expect(emojiLoaderMock.categories == categories)
}
@Test @MainActor
func emojisLoadedSecondTimeCachedValuesAreUsed() async {
let item = EmojiItem(label: "test", unicode: "test", keywords: ["1", "2"], shortcodes: ["1", "2"])
@@ -108,7 +108,7 @@ final class InviteUsersScreenViewModelTests {
context.send(viewAction: .toggleUser(.mockAlice))
try await deferredState.fulfill()
context.send(viewAction: .proceed)
#expect(context.presentConfirmationDialog)
@@ -179,24 +179,24 @@ final class LiveLocationManagerTests {
}
// MARK: - Beacon info updates
@Test
func beaconInfoUpdateFromAnotherDeviceRemovesActiveSession() async throws {
setUp()
let roomProxy = makeRoomProxy(roomID: "!room:matrix.org")
clientProxy.roomForIdentifierClosure = { _ in .joined(roomProxy) }
try await manager.startLiveLocation(roomID: "!room:matrix.org", duration: .seconds(300)).get()
try await simulateBeaconEcho(roomID: "!room:matrix.org", eventID: "$event:matrix.org")
#expect(appSettings.liveLocationSharingSessionsByRoomID["!room:matrix.org"] != nil)
let deferred = deferFulfillment(appSettings.$liveLocationSharingSessionsByRoomID) { $0["!room:matrix.org"] == nil }
beaconInfoSubject.send(LiveLocationOwnInfoUpdate(roomID: "!room:matrix.org", eventID: "$external_event:matrix.org", isLive: true))
try await deferred.fulfill()
#expect(appSettings.liveLocationSharingSessionsByRoomID["!room:matrix.org"] == nil)
}
// MARK: - Reduced accuracy
@Test
@@ -404,7 +404,7 @@ struct LocationSharingScreenViewModelTests {
analytics: AnalyticsServiceMock(.init()),
userIndicatorController: UserIndicatorControllerMock(),
mediaProvider: MediaProviderMock(.init()))
// Initially no annotations and no map center since sender and share are both nil.
#expect(context.viewState.annotations.isEmpty)
#expect(context.mapCenterLocation == nil)
@@ -441,7 +441,7 @@ struct LocationSharingScreenViewModelTests {
mediaProvider: MediaProviderMock(.init()))
viewModel.state.bindings.isLocationAuthorized = true
}
private mutating func setupViewModel(liveLocationManagerMock: LiveLocationManagerMock,
members: [RoomMemberProxyMock] = .allMembersAsAdmin) {
let appSettings = AppSettings.volatile()
@@ -153,7 +153,7 @@ struct LoginScreenViewModelTests {
#expect(context.viewState.canSubmit,
"The form should be ready to submit.")
}
@Test
mutating func oAuthServer() async throws {
// Given the screen configured for matrix.org
@@ -166,7 +166,7 @@ struct LoginScreenViewModelTests {
context.username = "@bob:company.com"
context.send(viewAction: .parseUsername)
try await deferred.fulfill()
// Then the view state should be updated with the homeserver and show the OAuth button.
#expect(context.viewState.loginMode.supportsOAuthFlow,
"The OAuth button should be shown.")
@@ -186,7 +186,7 @@ struct LoginScreenViewModelTests {
context.username = "@bob:server.net"
context.send(viewAction: .parseUsername)
try await deferred.fulfill()
// Then the view state should be updated to show an alert.
#expect(context.alertInfo?.id == .unknown,
"An alert should be shown to the user.")
@@ -206,7 +206,7 @@ struct LoginScreenViewModelTests {
context.username = "@bob:secure.gov"
context.send(viewAction: .parseUsername)
try await deferred.fulfill()
// Then the view state should be updated to show an alert.
#expect(context.alertInfo?.id == .elementProAlert,
"An alert should be shown to the user.")
@@ -139,7 +139,7 @@ struct MediaProviderTests {
let url = URL.mockMXCImage
let key = "\(url.absoluteString){\(avatarSize.scaledValue),\(avatarSize.scaledValue)}"
let expectedImage = try loadTestImage()
mediaLoader.loadMediaThumbnailForSourceWidthHeightReturnValue = expectedImage.pngData()
_ = try await mediaProvider.loadImageFromSource(MediaSourceProxy(url: url, mimeType: "image/jpeg"),
@@ -151,7 +151,7 @@ struct MediaProviderTests {
@Test
func whenLoadImageFromSourceAndImageNotCachedAndRetrieveImageFailsAndNoAvatarSize_imageContentIsLoaded() async throws {
let expectedImage = try loadTestImage()
mediaLoader.loadMediaContentForSourceReturnValue = expectedImage.pngData()
let result = try await mediaProvider.loadImageFromSource(MediaSourceProxy(url: .mockMXCImage, mimeType: "image/jpeg"),
@@ -13,7 +13,7 @@ class MockImageCache: ImageCache, @unchecked Sendable {
var retrievedImagesInMemory = [String: UIImage]()
var retrievedImages = [String: UIImage]()
var storedImages = [String: UIImage]()
override func retrieveImageInMemoryCache(forKey key: String, options: KingfisherParsedOptionsInfo) -> KFCrossPlatformImage? {
retrievedImagesInMemory[key]
}
@@ -16,7 +16,7 @@ final class MediaUploadPreviewScreenViewModelTests {
var clientProxy: ClientProxyMock!
var userIndicatorController: UserIndicatorControllerMock
private let appSettings: AppSettings
var viewModel: MediaUploadPreviewScreenViewModel!
var context: MediaUploadPreviewScreenViewModel.Context {
viewModel.context
@@ -30,7 +30,7 @@ final class MediaUploadPreviewScreenViewModelTests {
init() {
appSettings = AppSettings.volatile()
appSettings.optimizeMediaUploads = false
userIndicatorController = UserIndicatorControllerMock()
}
@@ -286,7 +286,7 @@ final class MediaUploadPreviewScreenViewModelTests {
if let maxUploadSizeResult {
clientProxy.underlyingMaxMediaUploadSize = maxUploadSizeResult
}
viewModel = MediaUploadPreviewScreenViewModel(mediaURLs: urls,
caption: nil,
title: "Some File",
+10 -10
View File
@@ -11,7 +11,7 @@ import Testing
struct NSItemProviderTests {
// MARK: - hasPathExtension
@Test
func hasPathExtensionRecognisesRegisteredExtensions() {
#expect(("IMG_1234.jpg" as NSString).hasPathExtension)
@@ -19,7 +19,7 @@ struct NSItemProviderTests {
#expect(("clip.heic" as NSString).hasPathExtension)
#expect(("doc.pdf" as NSString).hasPathExtension)
}
@Test
func hasPathExtensionRejectsDateLikeTrailingSegments() {
// iOS screenshot drags expose a `suggestedName` like
@@ -30,38 +30,38 @@ struct NSItemProviderTests {
#expect(!("report 2025.10" as NSString).hasPathExtension)
#expect(!("v1.2.3" as NSString).hasPathExtension)
}
@Test
func hasPathExtensionRejectsAbsentExtension() {
#expect(!("plainname" as NSString).hasPathExtension)
#expect(!("" as NSString).hasPathExtension)
#expect(!("trailing dot." as NSString).hasPathExtension)
}
// MARK: - storeData()
@Test
func storeDataAppendsExtensionWhenSuggestedNameLooksLikeAScreenshot() async throws {
let imageURL = try #require(Bundle(for: BundleAnchor.self).url(forResource: "test_image.png", withExtension: nil),
"Failed retrieving fixture")
let provider = try #require(NSItemProvider(contentsOf: imageURL))
provider.suggestedName = "Screenshot 2026-05-22 at 14.49.40"
let storedURL = try #require(await provider.storeData())
#expect(storedURL.pathExtension == "png")
#expect(storedURL.deletingPathExtension().lastPathComponent == "Screenshot 2026-05-22 at 14.49.40")
}
@Test
func storeDataPreservesSuggestedNameWithRealExtension() async throws {
let imageURL = try #require(Bundle(for: BundleAnchor.self).url(forResource: "test_image.png", withExtension: nil),
"Failed retrieving fixture")
let provider = try #require(NSItemProvider(contentsOf: imageURL))
provider.suggestedName = "IMG_1234.png"
let storedURL = try #require(await provider.storeData())
#expect(storedURL.lastPathComponent == "IMG_1234.png")
}
}
@@ -24,7 +24,7 @@ struct NavigationRootCoordinatorTests {
let firstRootCoordinator = SomeTestCoordinator()
navigationRootCoordinator.setRootCoordinator(firstRootCoordinator)
assertCoordinatorsEqual(firstRootCoordinator, navigationRootCoordinator.rootCoordinator)
let secondRootCoordinator = SomeTestCoordinator()
@@ -191,7 +191,7 @@ struct NavigationSplitCoordinatorTests {
func splitTracksEmbeddedStackRootChanges() async {
let sidebarNavigationStackCoordinator = NavigationStackCoordinator(navigationSplitCoordinator: navigationSplitCoordinator)
sidebarNavigationStackCoordinator.setRootCoordinator(SomeTestCoordinator())
navigationSplitCoordinator.setSidebarCoordinator(sidebarNavigationStackCoordinator)
assertCoordinatorsEqual(sidebarNavigationStackCoordinator.rootCoordinator, navigationSplitCoordinator.compactLayoutRootCoordinator)
@@ -210,7 +210,7 @@ struct NavigationSplitCoordinatorTests {
func splitTracksEmbeddedStackChanges() async {
let sidebarNavigationStackCoordinator = NavigationStackCoordinator(navigationSplitCoordinator: navigationSplitCoordinator)
sidebarNavigationStackCoordinator.setRootCoordinator(SomeTestCoordinator())
navigationSplitCoordinator.setSidebarCoordinator(sidebarNavigationStackCoordinator)
assertCoordinatorsEqual(sidebarNavigationStackCoordinator.rootCoordinator, navigationSplitCoordinator.compactLayoutRootCoordinator)
@@ -233,7 +233,7 @@ struct NavigationSplitCoordinatorTests {
let sidebarNavigationStackCoordinator = NavigationStackCoordinator(navigationSplitCoordinator: navigationSplitCoordinator)
sidebarNavigationStackCoordinator.setRootCoordinator(SomeTestCoordinator())
sidebarNavigationStackCoordinator.push(SomeTestCoordinator())
navigationSplitCoordinator.setSidebarCoordinator(sidebarNavigationStackCoordinator)
assertCoordinatorsEqual(sidebarNavigationStackCoordinator.rootCoordinator, navigationSplitCoordinator.compactLayoutRootCoordinator)
@@ -299,20 +299,20 @@ struct NavigationSplitCoordinatorTests {
}
}
}
@Test
mutating func setRootDetailToNilAfterPoppingToRoot() async {
navigationSplitCoordinator = NavigationSplitCoordinator(placeholderCoordinator: SomeTestCoordinator())
let sidebarCoordinator = NavigationStackCoordinator()
sidebarCoordinator.setRootCoordinator(SomeTestCoordinator())
let detailCoordinator = NavigationStackCoordinator()
detailCoordinator.setRootCoordinator(SomeTestCoordinator())
detailCoordinator.push(SomeTestCoordinator())
navigationSplitCoordinator.setSidebarCoordinator(sidebarCoordinator)
navigationSplitCoordinator.setDetailCoordinator(detailCoordinator)
await waitForConfirmation("Details coordinator should be nil, and the compact layout revert to the sidebar root",
timeout: .seconds(1)) { [navigationSplitCoordinator] confirm in
DispatchQueue.main.async {
@@ -24,7 +24,7 @@ struct NavigationStackCoordinatorTests {
let rootCoordinator = SomeTestCoordinator()
navigationStackCoordinator.setRootCoordinator(rootCoordinator)
assertCoordinatorsEqual(rootCoordinator, navigationStackCoordinator.rootCoordinator)
}
@@ -139,7 +139,7 @@ struct NotificationContentBuilderTests {
#expect(notificationContent.threadIdentifier == "bob:matrix.org!test:matrix.orgthread")
#expect(notificationContent.attachments == [])
}
@Test
mutating func roomMessageNotification() async {
let notificationItem = NotificationItemProxyMock(.init(roomID: "!testroom:matrix.org",
@@ -193,7 +193,7 @@ struct NotificationContentBuilderTests {
#expect(notificationContent.threadIdentifier == "bob:matrix.org!testroom:matrix.org")
#expect(notificationContent.attachments == [])
}
@Test
mutating func roomMessageNotificationWithThread() async {
let notificationItem = NotificationItemProxyMock(.init(roomID: "!testroom:matrix.org",
@@ -220,7 +220,7 @@ struct NotificationContentBuilderTests {
#expect(notificationContent.threadIdentifier == "bob:matrix.org!testroom:matrix.orgthread123")
#expect(notificationContent.attachments == [])
}
@Test
mutating func roomMessageNotificationWithThreadAndMention() async {
let notificationItem = NotificationItemProxyMock(.init(roomID: "!testroom:matrix.org",
@@ -16,7 +16,7 @@ final class MockCoder: NSKeyedArchiver {
override func decodeObject(forKey _: String) -> Any {
""
}
override func decodeInt64(forKey key: String) -> Int64 {
0
}
@@ -23,7 +23,7 @@ final class NotificationManagerTests {
private var notificationTappedDelegateCalled = false
private var registerForRemoteNotificationsDelegateCalled: (() -> Void)?
private let appSettings: AppSettings
init() {
appSettings = AppSettings.volatile()
notificationCenter = UserNotificationCenterMock()
@@ -53,7 +53,7 @@ final class NotificationManagerTests {
let success = await notificationManager.register(with: Data())
#expect(success)
}
@Test
func whenRegisteredAndPusherThrowsError_completionFalseIsCalled() async {
enum TestError: Error {
@@ -64,7 +64,7 @@ final class NotificationManagerTests {
let success = await notificationManager.register(with: Data())
#expect(!success)
}
@Test
func whenRegistered_pusherIsCalledWithCorrectValues() async throws {
let pushkeyData = Data("1234".utf8)
@@ -93,21 +93,21 @@ final class NotificationManagerTests {
pusherNotificationClientIdentifier: nil)
#expect(try data.defaultPayload == (defaultPayload.toJsonString()))
}
@Test
func whenRegisteredAndPusherTagNotSetInSettings_tagGeneratedAndSavedInSettings() async {
appSettings.pusherProfileTag = nil
_ = await notificationManager.register(with: Data())
#expect(appSettings.pusherProfileTag != nil)
}
@Test
func whenRegisteredAndPusherTagIsSetInSettings_tagNotGenerated() async {
appSettings.pusherProfileTag = "12345"
_ = await notificationManager.register(with: Data())
#expect(appSettings.pusherProfileTag == "12345")
}
@Test
func whenShowLocalNotification_notificationRequestGetsAdded() async throws {
await notificationManager.showLocalNotification(with: "Title", subtitle: "Subtitle")
@@ -132,13 +132,13 @@ final class NotificationManagerTests {
options: [])
#expect(notificationCenter.setNotificationCategoriesReceivedCategories == [messageCategory, inviteCategory])
}
@Test
func whenStart_delegateIsSet() throws {
let delegate = try #require(notificationCenter.delegate)
#expect(delegate.isEqual(notificationManager))
}
@Test
func whenStart_requestAuthorizationCalledWithCorrectParams() async {
await waitForConfirmation("requestAuthorization should be called", timeout: .seconds(10)) { confirm in
@@ -150,7 +150,7 @@ final class NotificationManagerTests {
}
#expect(notificationCenter.requestAuthorizationOptionsReceivedOptions == [.alert, .sound, .badge])
}
@Test
func whenStartAndAuthorizationGranted_delegateCalled() async {
authorizationStatusWasGranted = false
@@ -191,7 +191,7 @@ final class NotificationManagerTests {
#expect(authorizationStatusWasGranted)
}
@Test
func whenWillPresentNotificationsDelegateNotSet_CorrectPresentationOptionsReturned() async throws {
let archiver = MockCoder(requiringSecureCoding: false)
@@ -199,27 +199,27 @@ final class NotificationManagerTests {
let options = await notificationManager.userNotificationCenter(UNUserNotificationCenter.current(), willPresent: notification)
#expect(options == [.badge, .sound, .list, .banner])
}
@Test
func whenWillPresentNotificationsDelegateSetAndNotificationsShoudNotBeDisplayed_CorrectPresentationOptionsReturned() async throws {
shouldDisplayInAppNotificationReturnValue = false
notificationManager.delegate = self
let notification = try UNNotification.with(userInfo: [AnyHashable: Any]())
let options = await notificationManager.userNotificationCenter(UNUserNotificationCenter.current(), willPresent: notification)
#expect(options == [])
}
@Test
func whenWillPresentNotificationsDelegateSetAndNotificationsShoudBeDisplayed_CorrectPresentationOptionsReturned() async throws {
shouldDisplayInAppNotificationReturnValue = true
notificationManager.delegate = self
let notification = try UNNotification.with(userInfo: [AnyHashable: Any]())
let options = await notificationManager.userNotificationCenter(UNUserNotificationCenter.current(), willPresent: notification)
#expect(options == [.badge, .sound, .list, .banner])
}
@Test
func whenNotificationCenterReceivedResponseInLineReply_delegateIsCalled() async throws {
handleInlineReplyDelegateCalled = false
@@ -228,7 +228,7 @@ final class NotificationManagerTests {
await notificationManager.userNotificationCenter(UNUserNotificationCenter.current(), didReceive: response)
#expect(handleInlineReplyDelegateCalled)
}
@Test
func whenNotificationCenterReceivedResponseWithActionIdentifier_delegateIsCalled() async throws {
notificationTappedDelegateCalled = false
@@ -40,7 +40,7 @@ struct NotificationSettingsEditScreenViewModelTests {
}
}
viewModel = NotificationSettingsEditScreenViewModel(chatType: .groupChat, userSession: userSession)
let deferred = deferFulfillment(viewModel.context.observe(\.viewState.defaultMode)) { $0 != nil }
viewModel.fetchInitialContent()
@@ -78,7 +78,7 @@ struct NotificationSettingsEditScreenViewModelTests {
true
}
viewModel = NotificationSettingsEditScreenViewModel(chatType: .groupChat, userSession: userSession)
let deferred = deferFulfillment(viewModel.context.observe(\.viewState.defaultMode)) { $0 != nil }
viewModel.fetchInitialContent()
@@ -114,7 +114,7 @@ struct NotificationSettingsEditScreenViewModelTests {
var deferredViewState = deferFulfillment(viewModel.context.observe(\.viewState.pendingMode),
transitionValues: [nil, .allMessages, nil])
context.send(viewAction: .setMode(.allMessages))
try await deferredViewState.fulfill()
@@ -135,11 +135,11 @@ struct NotificationSettingsEditScreenViewModelTests {
transitionValues: [.allMessages])
try await deferredViewState.fulfill()
#expect(context.viewState.defaultMode == .allMessages)
#expect(context.viewState.bindings.alertInfo == nil)
}
@Test
mutating func setModeMentions() async throws {
viewModel = NotificationSettingsEditScreenViewModel(chatType: .groupChat, userSession: userSession)
@@ -152,7 +152,7 @@ struct NotificationSettingsEditScreenViewModelTests {
var deferredViewState = deferFulfillment(viewModel.context.observe(\.viewState.pendingMode),
transitionValues: [nil, .mentionsAndKeywordsOnly, nil])
context.send(viewAction: .setMode(.mentionsAndKeywordsOnly))
try await deferredViewState.fulfill()
@@ -173,11 +173,11 @@ struct NotificationSettingsEditScreenViewModelTests {
transitionValues: [.mentionsAndKeywordsOnly])
try await deferredViewState.fulfill()
#expect(context.viewState.defaultMode == .mentionsAndKeywordsOnly)
#expect(context.viewState.bindings.alertInfo == nil)
}
@Test
mutating func setModeDirectChats() async throws {
notificationSettingsProxy.getDefaultRoomNotificationModeIsEncryptedIsOneToOneReturnValue = .mentionsAndKeywordsOnly
@@ -196,7 +196,7 @@ struct NotificationSettingsEditScreenViewModelTests {
context.send(viewAction: .setMode(.allMessages))
try await deferredViewState.fulfill()
// `setDefaultRoomNotificationModeIsEncryptedIsOneToOneMode` must have been called twice (for encrypted and unencrypted direct chats)
let invocations = notificationSettingsProxy.setDefaultRoomNotificationModeIsEncryptedIsOneToOneModeReceivedInvocations
#expect(notificationSettingsProxy.setDefaultRoomNotificationModeIsEncryptedIsOneToOneModeCallsCount == 2)
@@ -209,7 +209,7 @@ struct NotificationSettingsEditScreenViewModelTests {
#expect(invocations[1].isOneToOne == true)
#expect(invocations[1].mode == .allMessages)
}
@Test
mutating func setModeFailure() async throws {
notificationSettingsProxy.getDefaultRoomNotificationModeIsEncryptedIsOneToOneReturnValue = .mentionsAndKeywordsOnly
@@ -224,14 +224,14 @@ struct NotificationSettingsEditScreenViewModelTests {
let deferredViewState = deferFulfillment(viewModel.context.observe(\.viewState.pendingMode),
transitionValues: [nil, .allMessages, nil])
context.send(viewAction: .setMode(.allMessages))
try await deferredViewState.fulfill()
#expect(context.viewState.bindings.alertInfo != nil)
}
@Test
mutating func selectRoom() async throws {
let roomID = "!roomidentifier:matrix.org"
@@ -19,7 +19,7 @@ struct NotificationSettingsScreenViewModelTests {
private var userNotificationCenter: UserNotificationCenterMock
private var notificationSettingsProxy: NotificationSettingsProxyMock
private var toneManager: NotificationToneManagerMock
init() throws {
userNotificationCenter = UserNotificationCenterMock()
userNotificationCenter.authorizationStatusReturnValue = .authorized
@@ -39,21 +39,21 @@ struct NotificationSettingsScreenViewModelTests {
isModallyPresented: false)
context = viewModel.context
}
@Test
func enableNotifications() {
appSettings.enableNotifications = false
context.send(viewAction: .changedEnableNotifications)
#expect(appSettings.enableNotifications)
}
@Test
func disableNotifications() {
appSettings.enableNotifications = true
context.send(viewAction: .changedEnableNotifications)
#expect(!appSettings.enableNotifications)
}
@Test
func fetchSettings() async throws {
notificationSettingsProxy.getDefaultRoomNotificationModeIsEncryptedIsOneToOneClosure = { isEncrypted, isOneToOne in
@@ -70,17 +70,17 @@ struct NotificationSettingsScreenViewModelTests {
notificationSettingsProxy.callbacks.send(.settingsDidChange)
try await deferred.fulfill()
#expect(notificationSettingsProxy.getDefaultRoomNotificationModeIsEncryptedIsOneToOneCallsCount == 4)
#expect(notificationSettingsProxy.isRoomMentionEnabledCalled)
#expect(notificationSettingsProxy.isCallEnabledCalled)
#expect(context.viewState.settings?.groupChatsMode == .mentionsAndKeywordsOnly)
#expect(context.viewState.settings?.directChatsMode == .allMessages)
#expect(context.viewState.settings?.inconsistentSettings == [])
#expect(context.viewState.bindings.alertInfo == nil)
}
@Test
func inconsistentGroupChatsSettings() async throws {
notificationSettingsProxy.getDefaultRoomNotificationModeIsEncryptedIsOneToOneClosure = { isEncrypted, isOneToOne in
@@ -93,17 +93,17 @@ struct NotificationSettingsScreenViewModelTests {
return .allMessages
}
}
let deferred = deferFulfillment(viewModel.context.observe(\.viewState.settings)) { $0 != nil }
notificationSettingsProxy.callbacks.send(.settingsDidChange)
try await deferred.fulfill()
#expect(context.viewState.settings?.groupChatsMode == .allMessages)
#expect(context.viewState.settings?.inconsistentSettings == [.init(chatType: .groupChat, isEncrypted: false)])
}
@Test
func inconsistentDirectChatsSettings() async throws {
notificationSettingsProxy.getDefaultRoomNotificationModeIsEncryptedIsOneToOneClosure = { isEncrypted, isOneToOne in
@@ -116,17 +116,17 @@ struct NotificationSettingsScreenViewModelTests {
return .allMessages
}
}
let deferred = deferFulfillment(viewModel.context.observe(\.viewState.settings)) { $0 != nil }
notificationSettingsProxy.callbacks.send(.settingsDidChange)
try await deferred.fulfill()
#expect(context.viewState.settings?.directChatsMode == .allMessages)
#expect(context.viewState.settings?.inconsistentSettings == [.init(chatType: .oneToOneChat, isEncrypted: false)])
}
@Test
func fixInconsistentSettings() async throws {
// Initialize with a configuration mismatch where encrypted one-to-one chats is `.allMessages` and unencrypted one-to-one chats is `.mentionsAndKeywordsOnly`
@@ -140,13 +140,13 @@ struct NotificationSettingsScreenViewModelTests {
return .allMessages
}
}
let deferredSettings = deferFulfillment(viewModel.context.observe(\.viewState.settings)) { $0 != nil }
notificationSettingsProxy.callbacks.send(.settingsDidChange)
try await deferredSettings.fulfill()
#expect(context.viewState.settings?.directChatsMode == .allMessages)
#expect(context.viewState.settings?.inconsistentSettings == [.init(chatType: .oneToOneChat, isEncrypted: false)])
@@ -156,7 +156,7 @@ struct NotificationSettingsScreenViewModelTests {
context.send(viewAction: .fixConfigurationMismatchTapped)
try await deferredMismatch.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)
#expect(notificationSettingsProxy.setDefaultRoomNotificationModeIsEncryptedIsOneToOneModeCallsCount == 1)
let callArguments = notificationSettingsProxy.setDefaultRoomNotificationModeIsEncryptedIsOneToOneModeReceivedArguments
@@ -164,7 +164,7 @@ struct NotificationSettingsScreenViewModelTests {
#expect(callArguments?.isOneToOne == true)
#expect(callArguments?.mode == .allMessages)
}
@Test
func fixAllInconsistentSettings() async throws {
// Initialize with a configuration mismatch where
@@ -178,16 +178,16 @@ struct NotificationSettingsScreenViewModelTests {
return .mentionsAndKeywordsOnly
}
}
let deferredSettings = deferFulfillment(viewModel.context.observe(\.viewState.settings)) { $0 != nil }
notificationSettingsProxy.callbacks.send(.settingsDidChange)
try await deferredSettings.fulfill()
#expect(context.viewState.settings?.directChatsMode == .allMessages)
#expect(context.viewState.settings?.inconsistentSettings == [.init(chatType: .groupChat, isEncrypted: false), .init(chatType: .oneToOneChat, isEncrypted: false)])
var deferredMismatch = deferFulfillment(viewModel.context.observe(\.viewState.fixingConfigurationMismatch)) { $0 }
context.send(viewAction: .fixConfigurationMismatchTapped)
@@ -197,7 +197,7 @@ struct NotificationSettingsScreenViewModelTests {
deferredMismatch = deferFulfillment(viewModel.context.observe(\.viewState.fixingConfigurationMismatch)) { !$0 }
try await deferredMismatch.fulfill()
// All problems should be fixed
#expect(notificationSettingsProxy.setDefaultRoomNotificationModeIsEncryptedIsOneToOneModeCallsCount == 2)
let callArguments = notificationSettingsProxy.setDefaultRoomNotificationModeIsEncryptedIsOneToOneModeReceivedInvocations
@@ -210,7 +210,7 @@ struct NotificationSettingsScreenViewModelTests {
#expect(callArguments[1].isOneToOne == true)
#expect(callArguments[1].mode == .allMessages)
}
@Test
func toggleRoomMentionOff() async throws {
notificationSettingsProxy.isRoomMentionEnabledReturnValue = true
@@ -220,7 +220,7 @@ struct NotificationSettingsScreenViewModelTests {
notificationSettingsProxy.callbacks.send(.settingsDidChange)
try await deferredState.fulfill()
context.roomMentionsEnabled = false
let deferred = deferFulfillment(notificationSettingsProxy.callbacks) { callback in
@@ -230,11 +230,11 @@ struct NotificationSettingsScreenViewModelTests {
context.send(viewAction: .roomMentionChanged)
try await deferred.fulfill()
#expect(notificationSettingsProxy.setRoomMentionEnabledEnabledCalled)
#expect(notificationSettingsProxy.setRoomMentionEnabledEnabledReceivedEnabled == false)
}
@Test
func toggleRoomMentionOn() async throws {
notificationSettingsProxy.isRoomMentionEnabledReturnValue = false
@@ -243,7 +243,7 @@ struct NotificationSettingsScreenViewModelTests {
viewModel.fetchInitialContent()
try await deferredInitialFetch.fulfill()
context.roomMentionsEnabled = true
let deferred = deferFulfillment(notificationSettingsProxy.callbacks) { callback in
@@ -253,11 +253,11 @@ struct NotificationSettingsScreenViewModelTests {
context.send(viewAction: .roomMentionChanged)
try await deferred.fulfill()
#expect(notificationSettingsProxy.setRoomMentionEnabledEnabledCalled)
#expect(notificationSettingsProxy.setRoomMentionEnabledEnabledReceivedEnabled == true)
}
@Test
func toggleRoomMentionFailure() async throws {
notificationSettingsProxy.setRoomMentionEnabledEnabledThrowableError = NotificationSettingsError.Generic(msg: "error")
@@ -268,7 +268,7 @@ struct NotificationSettingsScreenViewModelTests {
viewModel.fetchInitialContent()
try await deferredInitialFetch.fulfill()
context.roomMentionsEnabled = true
var deferred = deferFulfillment(context.observe(\.viewState.applyingChange)) { $0 }
@@ -280,10 +280,10 @@ struct NotificationSettingsScreenViewModelTests {
deferred = deferFulfillment(context.observe(\.viewState.applyingChange)) { !$0 }
try await deferred.fulfill()
#expect(context.alertInfo != nil)
}
@Test
func toggleCallsOff() async throws {
notificationSettingsProxy.isCallEnabledReturnValue = true
@@ -293,7 +293,7 @@ struct NotificationSettingsScreenViewModelTests {
viewModel.fetchInitialContent()
try await deferredInitialFetch.fulfill()
context.callsEnabled = false
let deferred = deferFulfillment(notificationSettingsProxy.callbacks) { callback in
callback == .settingsDidChange
@@ -302,21 +302,21 @@ struct NotificationSettingsScreenViewModelTests {
context.send(viewAction: .callsChanged)
try await deferred.fulfill()
#expect(notificationSettingsProxy.setCallEnabledEnabledCalled)
#expect(notificationSettingsProxy.setCallEnabledEnabledReceivedEnabled == false)
}
@Test
func toggleCallsOn() async throws {
notificationSettingsProxy.isCallEnabledReturnValue = false
let deferredInitialFetch = deferFulfillment(viewModel.context.observe(\.viewState.settings)) { $0 != nil }
viewModel.fetchInitialContent()
try await deferredInitialFetch.fulfill()
context.callsEnabled = true
let deferred = deferFulfillment(notificationSettingsProxy.callbacks) { callback in
@@ -326,11 +326,11 @@ struct NotificationSettingsScreenViewModelTests {
context.send(viewAction: .callsChanged)
try await deferred.fulfill()
#expect(notificationSettingsProxy.setCallEnabledEnabledCalled)
#expect(notificationSettingsProxy.setCallEnabledEnabledReceivedEnabled == true)
}
@Test
func toggleCallsFailure() async throws {
notificationSettingsProxy.setCallEnabledEnabledThrowableError = NotificationSettingsError.Generic(msg: "error")
@@ -341,7 +341,7 @@ struct NotificationSettingsScreenViewModelTests {
viewModel.fetchInitialContent()
try await deferredInitialFetch.fulfill()
context.callsEnabled = true
var deferred = deferFulfillment(context.observe(\.viewState.applyingChange)) { $0 }
@@ -366,7 +366,7 @@ struct NotificationSettingsScreenViewModelTests {
viewModel.fetchInitialContent()
try await deferredInitialFetch.fulfill()
context.invitationsEnabled = false
let deferred = deferFulfillment(notificationSettingsProxy.callbacks) { callback in
callback == .settingsDidChange
@@ -375,11 +375,11 @@ struct NotificationSettingsScreenViewModelTests {
context.send(viewAction: .invitationsChanged)
try await deferred.fulfill()
#expect(notificationSettingsProxy.setInviteForMeEnabledEnabledCalled)
#expect(notificationSettingsProxy.setInviteForMeEnabledEnabledReceivedEnabled == false)
}
@Test
func toggleInvitationsOn() async throws {
notificationSettingsProxy.isInviteForMeEnabledReturnValue = false
@@ -389,7 +389,7 @@ struct NotificationSettingsScreenViewModelTests {
viewModel.fetchInitialContent()
try await deferredInitialFetch.fulfill()
context.invitationsEnabled = true
let deferred = deferFulfillment(notificationSettingsProxy.callbacks) { callback in
@@ -399,13 +399,13 @@ struct NotificationSettingsScreenViewModelTests {
context.send(viewAction: .invitationsChanged)
try await deferred.fulfill()
#expect(notificationSettingsProxy.setInviteForMeEnabledEnabledCalled)
#expect(notificationSettingsProxy.setInviteForMeEnabledEnabledReceivedEnabled == true)
}
// MARK: - Alert Tones
@Test
func selectingAlertTonePersistsSelection() {
// Given a tone and a manager that persists the selection to app settings
@@ -414,28 +414,28 @@ struct NotificationSettingsScreenViewModelTests {
appSettings.selectedNotificationTone = selectedTone
return NotificationToneManager.libraryLocation
}
// When the user selects that tone
context.send(viewAction: .selectAlertTone(tone))
// Then it is reflected in app settings
#expect(appSettings.selectedNotificationTone == tone)
}
@Test
func deletingActiveToneResetsSelection() {
// Given the active tone is a custom tone
let customTone = NotificationTone.createCustomUserSound(filename: "custom.caf")
appSettings.selectedNotificationTone = customTone
toneManager.customTonesReturnValue = [customTone]
// When that tone is deleted
context.send(viewAction: .deleteCustomAlertTones([customTone]))
// Then the selection is cleared, falling back to the default
#expect(appSettings.selectedNotificationTone == nil)
}
@Test
func deletingNonActiveTonePreservesSelection() {
// Given a custom tone is active and a different custom tone also exists
@@ -443,14 +443,14 @@ struct NotificationSettingsScreenViewModelTests {
let otherTone = NotificationTone.createCustomUserSound(filename: "other.caf")
appSettings.selectedNotificationTone = activeTone
toneManager.customTonesReturnValue = [activeTone, otherTone]
// When the non-active tone is deleted
context.send(viewAction: .deleteCustomAlertTones([otherTone]))
// Then the active selection is unchanged
#expect(appSettings.selectedNotificationTone == activeTone)
}
@Test
func deletingToneUpdatesCustomList() {
// Given a manager that initially returns one custom tone
@@ -463,15 +463,15 @@ struct NotificationSettingsScreenViewModelTests {
notificationSettingsProxy: notificationSettingsProxy,
userIndicatorController: UserIndicatorControllerMock(),
isModallyPresented: false)
// When the tone is deleted and the manager reports no remaining tones
remainingTones = []
localVM.context.send(viewAction: .deleteCustomAlertTones([customTone]))
// Then the available list is empty
#expect(localVM.context.viewState.availableCustomTones.isEmpty)
}
@Test
func deleteCustomToneFailurePreservesSelection() {
// Given the active tone is a custom tone and deletion will fail
@@ -479,14 +479,14 @@ struct NotificationSettingsScreenViewModelTests {
appSettings.selectedNotificationTone = customTone
toneManager.deleteCustomToneThrowableError = NSError(domain: "test", code: 1)
toneManager.customTonesReturnValue = [customTone]
// When deletion is attempted
context.send(viewAction: .deleteCustomAlertTones([customTone]))
// Then the selection is unchanged
#expect(appSettings.selectedNotificationTone == customTone)
}
@Test
func importingCustomToneRefreshesAvailableList() async throws {
// Given a tone file ready to import
@@ -494,16 +494,16 @@ struct NotificationSettingsScreenViewModelTests {
let location = NotificationToneManager.libraryLocation.appending(component: importedTone.filename)
toneManager.addNewToneToLibraryFromReturnValue = location
toneManager.customTonesReturnValue = [importedTone]
// When the import result is received
let deferred = deferFulfillment(context.observe(\.viewState.availableCustomTones)) { !$0.isEmpty }
context.send(viewAction: .addedCustomAlertTone(.success(location)))
// Then the available list is updated to include the new tone
try await deferred.fulfill()
#expect(context.viewState.availableCustomTones == [importedTone])
}
@Test
func toggleInvitesFailure() async throws {
notificationSettingsProxy.setInviteForMeEnabledEnabledThrowableError = NotificationSettingsError.Generic(msg: "error")
@@ -514,7 +514,7 @@ struct NotificationSettingsScreenViewModelTests {
viewModel.fetchInitialContent()
try await deferredInitialFetch.fulfill()
context.invitationsEnabled = true
var deferred = deferFulfillment(context.observe(\.viewState.applyingChange)) { $0 }
@@ -16,18 +16,18 @@ import Testing
/// relying on that behavior.
struct NotificationToneManagerTests {
private let manager: NotificationToneManager
init() {
manager = NotificationToneManager(appSettings: .volatile())
}
// MARK: - Deletion
@Test
func deletingSystemToneThrowsNotACustomTone() {
// Given a system tone (lives outside the library directory)
let tone = NotificationTone.createSystemSound(label: nil, filename: "alarm.caf")
// When deletion is attempted
// Then it throws because only library tones may be deleted
let error = #expect(throws: NotificationToneManager.ManagerError.self) {
@@ -36,12 +36,12 @@ struct NotificationToneManagerTests {
#expect(error == .notACustomTone)
}
@Test
func deletingBundledToneThrowsNotACustomTone() {
// Given a bundled app tone (lives outside the library directory)
let tone = NotificationTone.createBundledSound(label: nil, filename: "message.caf")
// When deletion is attempted
// Then it throws because only library tones may be deleted
let error = #expect(throws: NotificationToneManager.ManagerError.self) {
@@ -50,9 +50,9 @@ struct NotificationToneManagerTests {
#expect(error == .notACustomTone)
}
// MARK: - Custom Tone Listing
@Test
func customTonesFiltersToCAFOnly() throws {
// Given both CAF and non-CAF files written to the library directory
@@ -64,17 +64,17 @@ struct NotificationToneManagerTests {
}
try Data().write(to: cafURL)
try Data().write(to: mp3URL)
// When fetching the list of custom tones
let tones = manager.customTones()
// Then only the CAF file is included
#expect(tones.contains { $0.filename == cafURL.lastPathComponent })
#expect(!tones.contains { $0.filename == mp3URL.lastPathComponent })
}
// MARK: - Import
@Test
func addingDuplicateToneThrowsFileAlreadyExists() async throws {
// Given a CAF file that has already been imported into the library
@@ -88,7 +88,7 @@ struct NotificationToneManagerTests {
}
try Data().write(to: sourceURL)
try await manager.addNewToneToLibrary(from: sourceURL)
// When importing the same file a second time
// Then it throws ConversionError (specifically fileAlreadyExists)
await #expect(throws: NotificationToneManager.ManagerError.self) {
+11 -11
View File
@@ -17,7 +17,7 @@ struct PillContextTests {
func user() async {
let appSettings = AppSettings.volatile()
let userIndicatorController = UserIndicatorControllerMock()
let id = "@test:matrix.org"
let proxyMock = JoinedRoomProxyMock(.init(name: "Test"))
let subject = CurrentValueSubject<[RoomMemberProxyProtocol], Never>([])
@@ -51,7 +51,7 @@ struct PillContextTests {
func ownUser() {
let appSettings = AppSettings.volatile()
let userIndicatorController = UserIndicatorControllerMock()
let id = "@test:matrix.org"
let proxyMock = JoinedRoomProxyMock(.init(name: "Test", ownUserID: id))
let subject = CurrentValueSubject<[RoomMemberProxyProtocol], Never>([])
@@ -76,7 +76,7 @@ struct PillContextTests {
func allUsers() {
let appSettings = AppSettings.volatile()
let userIndicatorController = UserIndicatorControllerMock()
let avatarURL = URL(string: "https://matrix.jpg")
let id = "test_room"
let displayName = "Test"
@@ -104,7 +104,7 @@ struct PillContextTests {
func roomIDMention() {
let appSettings = AppSettings.volatile()
let userIndicatorController = UserIndicatorControllerMock()
let proxyMock = JoinedRoomProxyMock(.init())
let mockController = MockTimelineController()
let clientMock = ClientProxyMock(.init())
@@ -132,7 +132,7 @@ struct PillContextTests {
func roomIDMentionMissingRoom() {
let appSettings = AppSettings.volatile()
let userIndicatorController = UserIndicatorControllerMock()
let proxyMock = JoinedRoomProxyMock(.init())
let mockController = MockTimelineController()
mockController.roomProxy = proxyMock
@@ -158,7 +158,7 @@ struct PillContextTests {
func roomAliasMention() {
let appSettings = AppSettings.volatile()
let userIndicatorController = UserIndicatorControllerMock()
let proxyMock = JoinedRoomProxyMock(.init())
let mockController = MockTimelineController()
mockController.roomProxy = proxyMock
@@ -188,7 +188,7 @@ struct PillContextTests {
func roomAliasMentionMissingRoom() {
let appSettings = AppSettings.volatile()
let userIndicatorController = UserIndicatorControllerMock()
let proxyMock = JoinedRoomProxyMock(.init())
let mockController = MockTimelineController()
mockController.roomProxy = proxyMock
@@ -214,7 +214,7 @@ struct PillContextTests {
func eventOnRoomIDMention() {
let appSettings = AppSettings.volatile()
let userIndicatorController = UserIndicatorControllerMock()
let proxyMock = JoinedRoomProxyMock(.init())
let mockController = MockTimelineController()
mockController.roomProxy = proxyMock
@@ -242,7 +242,7 @@ struct PillContextTests {
func eventOnRoomIDMentionMissingRoom() {
let appSettings = AppSettings.volatile()
let userIndicatorController = UserIndicatorControllerMock()
let proxyMock = JoinedRoomProxyMock(.init())
let mockController = MockTimelineController()
mockController.roomProxy = proxyMock
@@ -268,7 +268,7 @@ struct PillContextTests {
func eventOnRoomAliasMention() {
let appSettings = AppSettings.volatile()
let userIndicatorController = UserIndicatorControllerMock()
let proxyMock = JoinedRoomProxyMock(.init())
let mockController = MockTimelineController()
mockController.roomProxy = proxyMock
@@ -298,7 +298,7 @@ struct PillContextTests {
func eventOnRoomAliasMentionMissingRoom() {
let appSettings = AppSettings.volatile()
let userIndicatorController = UserIndicatorControllerMock()
let proxyMock = JoinedRoomProxyMock(.init())
let mockController = MockTimelineController()
mockController.roomProxy = proxyMock
@@ -17,7 +17,7 @@ struct PollFormScreenViewModelTests {
private var context: PollFormScreenViewModelType.Context {
viewModel.context
}
@Test
mutating func newPollInitialState() async throws {
setupViewModel()
@@ -89,7 +89,7 @@ struct PollFormScreenViewModelTests {
context.send(viewAction: .cancel)
#expect(context.alertInfo != nil)
}
@Test
mutating func newPollSubmit() async throws {
setupViewModel()
@@ -97,7 +97,7 @@ struct PollFormScreenViewModelTests {
context.options[0].text = "bla1"
context.options[1].text = "bla2"
#expect(!context.viewState.isSubmitButtonDisabled)
let deferred = deferFulfillment(viewModel.actions) { $0 == .close }
try await confirmation { confirmation in
@@ -115,7 +115,7 @@ struct PollFormScreenViewModelTests {
try await deferred.fulfill()
}
}
@Test
mutating func editPollSubmit() async throws {
setupViewModel(mode: .edit(eventID: "foo", poll: .emptyDisclosed))
@@ -123,7 +123,7 @@ struct PollFormScreenViewModelTests {
context.question = "What is your favorite country?"
context.options.append(.init(text: "France 🇫🇷"))
#expect(!context.viewState.isSubmitButtonDisabled)
let deferred = deferFulfillment(viewModel.actions) { $0 == .close }
try await confirmation { confirmation in
@@ -152,7 +152,7 @@ struct PollFormScreenViewModelTests {
context.question = "What is your favorite country?"
context.options.append(.init(text: "France 🇫🇷"))
#expect(!context.viewState.isSubmitButtonDisabled)
let deferredFailure = deferFailure(viewModel.actions, timeout: .seconds(1)) { $0 == .close }
context.send(viewAction: .delete)
@@ -107,7 +107,7 @@ struct QRCodeLoginScreenViewModelTests {
}
context.qrResult = .init()
try await deferred.fulfill()
deferred = deferFulfillment(context.$viewState) { state in
state.state == .displayCode(.deviceCode("01"))
}
@@ -195,7 +195,7 @@ struct QRCodeLoginScreenViewModelTests {
let deferredFailure = deferFailure(context.$viewState, timeout: .seconds(1)) { $0.state != currentState }
linkMobileProgressSubject.send(.syncingSecrets)
try await deferredFailure.fulfill()
deferredAction = deferFulfillment(viewModel.actionsPublisher) { action in
guard case .linkedDevice = action else { return false }
return true
@@ -36,7 +36,7 @@ struct ReportContentScreenViewModelTests {
}
try await deferred.fulfill()
// Then the content should be reported, but the user should not be included.
#expect(roomProxy.reportContentReasonCallsCount == 1, "The content should always be reported.")
#expect(roomProxy.reportContentReasonReceivedArguments?.eventID == eventID, "The event ID should match the content being reported.")
@@ -59,7 +59,7 @@ struct ReportContentScreenViewModelTests {
// When reporting the content and also ignoring the user.
viewModel.state.bindings.reasonText = reportReason
viewModel.state.bindings.ignoreUser = true
viewModel.context.send(viewAction: .submit)
let deferred = deferFulfillment(viewModel.actions) { action in
@@ -17,7 +17,7 @@ struct RoomChangePermissionsScreenViewModelTests {
var context: RoomChangePermissionsScreenViewModelType.Context {
viewModel.context
}
@Test
mutating func changeSetting() throws {
setup(isSpace: false)
@@ -21,7 +21,7 @@ struct RoomDetailsScreenViewModelTests {
var context: RoomDetailsScreenViewModelType.Context {
viewModel.context
}
var cancellables = Set<AnyCancellable>()
init() {
@@ -174,7 +174,7 @@ struct RoomDetailsScreenViewModelTests {
try await deferredRecipient.fulfill()
#expect(context.viewState.dmRecipientInfo?.member == RoomMemberDetails(withProxy: recipient))
let deferredProcessing = deferFulfillment(viewModel.context.observe(\.viewState.isProcessingIgnoreRequest),
transitionValues: [false, true, false])
@@ -240,7 +240,7 @@ struct RoomDetailsScreenViewModelTests {
transitionValues: [false, true, false])
context.send(viewAction: .unignoreConfirmed)
try await deferredProcessing.fulfill()
#expect(context.viewState.dmRecipientInfo?.member.isIgnored == false)
@@ -271,7 +271,7 @@ struct RoomDetailsScreenViewModelTests {
transitionValues: [false, true, false])
context.send(viewAction: .unignoreConfirmed)
try await deferredProcessing.fulfill()
#expect(context.viewState.dmRecipientInfo?.member.isIgnored == true)
@@ -18,13 +18,13 @@ final class RoomFlowCoordinatorTests {
var roomFlowCoordinator: RoomFlowCoordinator!
var navigationStackCoordinator: NavigationStackCoordinator!
var cancellables = Set<AnyCancellable>()
private let appSettings: AppSettings
init() {
appSettings = AppSettings.volatile()
}
@Test
func roomPresentation() async throws {
setupRoomFlowCoordinator()
@@ -66,11 +66,11 @@ final class RoomListFiltersStateTests {
state.activateFilter(.people)
#expect(state.activeFilters == [.people])
#expect(state.availableFilters == [.unreads, .favourites])
state.activateFilter(.unreads)
#expect(state.activeFilters == [.people, .unreads])
#expect(state.availableFilters == [.favourites])
state.activateFilter(.favourites)
#expect(state.activeFilters == [.people, .unreads, .favourites])
#expect(state.availableFilters == [])
@@ -86,7 +86,7 @@ final class RoomListFiltersStateTests {
state.activateFilter(.favourites)
#expect(state.activeFilters == [.favourites])
#expect(state.availableFilters == [.unreads, .people, .rooms])
state.deactivateFilter(.favourites)
#expect(state.activeFilters == [])
#expect(state.availableFilters == allCasesWithoutLowPriority)
@@ -94,7 +94,7 @@ final class RoomListFiltersStateTests {
state.activateFilter(.rooms)
#expect(state.activeFilters == [.rooms])
#expect(state.availableFilters == [.unreads, .favourites])
state.activateFilter(.unreads)
#expect(state.activeFilters == [.rooms, .unreads])
#expect(state.availableFilters == [.favourites])
@@ -18,7 +18,7 @@ struct RoomMemberDetailsViewModelTests {
var context: RoomMemberDetailsScreenViewModelType.Context {
viewModel.context
}
@Test
mutating func initialState() async throws {
setup(roomMemberProxyMock: .mockAlice)
@@ -30,7 +30,7 @@ struct RoomMemberDetailsViewModelTests {
#expect(context.ignoreUserAlert == nil)
#expect(context.alertInfo == nil)
}
@Test
mutating func ignoreSuccess() async throws {
setup(roomMemberProxyMock: .mockAlice)
@@ -53,7 +53,7 @@ struct RoomMemberDetailsViewModelTests {
try await Task.sleep(for: .milliseconds(100))
#expect(roomProxyMock.updateMembersCalled)
}
@Test
mutating func ignoreFailure() async throws {
let clientProxy = ClientProxyMock(.init())
@@ -79,7 +79,7 @@ struct RoomMemberDetailsViewModelTests {
try await Task.sleep(for: .milliseconds(100))
#expect(!roomProxyMock.updateMembersCalled)
}
@Test
mutating func unignoreSuccess() async throws {
setup(roomMemberProxyMock: .mockIgnored)
@@ -102,7 +102,7 @@ struct RoomMemberDetailsViewModelTests {
try await Task.sleep(for: .milliseconds(100))
#expect(roomProxyMock.updateMembersCalled)
}
@Test
mutating func unignoreFailure() async throws {
let clientProxy = ClientProxyMock(.init())
@@ -128,7 +128,7 @@ struct RoomMemberDetailsViewModelTests {
try await Task.sleep(for: .milliseconds(100))
#expect(!roomProxyMock.updateMembersCalled)
}
@Test
mutating func initialStateAccountOwner() async throws {
setup(roomMemberProxyMock: .mockMe)
@@ -140,7 +140,7 @@ struct RoomMemberDetailsViewModelTests {
#expect(context.ignoreUserAlert == nil)
#expect(context.alertInfo == nil)
}
@Test
mutating func initialStateIgnoredUser() async throws {
setup(roomMemberProxyMock: .mockIgnored)
@@ -154,7 +154,7 @@ struct RoomMemberDetailsViewModelTests {
}
// MARK: - History Sharing
@Test
mutating func inviteConfirmationFetchesIdentity() async throws {
let clientProxy = ClientProxyMock(.init())
@@ -196,9 +196,9 @@ struct RoomMemberDetailsViewModelTests {
#expect(clientProxy.userIdentityForFallBackToServerCalled)
}
// MARK: - Helpers
private mutating func setup(roomMemberProxyMock: RoomMemberProxyMock, clientProxy: ClientProxyMock? = nil) {
self.roomMemberProxyMock = roomMemberProxyMock
roomProxyMock = JoinedRoomProxyMock(.init(name: ""))
@@ -17,29 +17,29 @@ struct RoomMembersListScreenViewModelTests {
var context: RoomMembersListScreenViewModel.Context {
viewModel.context
}
@Test
mutating func joinedMembers() async throws {
setup(members: [.mockAlice, .mockBob])
let deferred = deferFulfillment(context.$viewState) { state in
state.visibleJoinedMembers.count == 2
}
try await deferred.fulfill()
#expect(viewModel.state.joinedMembersCount == 2)
#expect(viewModel.state.visibleJoinedMembers.count == 2)
}
@Test
mutating func sortingMembers() async throws {
setup(members: [.mockModerator, .mockDan, .mockAlice, .mockAdmin])
let deferred = deferFulfillment(context.$viewState) { state in
state.visibleJoinedMembers.count == 4
}
try await deferred.fulfill()
let sortedMembers: [RoomMemberListScreenEntry] = [
.init(member: .init(withProxy: RoomMemberProxyMock.mockAdmin),
verificationState: .notVerified),
@@ -50,121 +50,121 @@ struct RoomMembersListScreenViewModelTests {
.init(member: .init(withProxy: RoomMemberProxyMock.mockDan),
verificationState: .notVerified)
]
#expect(viewModel.state.visibleJoinedMembers == sortedMembers)
}
@Test
mutating func search() async throws {
setup(members: [.mockAlice, .mockBob])
let deferred = deferFulfillment(context.$viewState) { state in
state.visibleJoinedMembers.count == 1
}
context.searchQuery = "alice"
try await deferred.fulfill()
#expect(viewModel.state.joinedMembersCount == 2)
#expect(viewModel.state.visibleJoinedMembers.count == 1)
}
@Test
mutating func emptySearch() async throws {
setup(members: [.mockAlice, .mockBob])
context.searchQuery = "WWW"
let deferred = deferFulfillment(context.$viewState) { state in
state.joinedMembersCount == 2
}
try await deferred.fulfill()
#expect(viewModel.state.joinedMembersCount == 2)
#expect(viewModel.state.visibleJoinedMembers.count == 0)
}
@Test
mutating func joinedAndInvitedMembers() async throws {
setup(members: [.mockInvited, .mockBob])
let deferred = deferFulfillment(context.$viewState) { state in
state.visibleInvitedMembers.count == 1
}
try await deferred.fulfill()
#expect(viewModel.state.joinedMembersCount == 1)
#expect(viewModel.state.visibleInvitedMembers.count == 1)
#expect(viewModel.state.visibleJoinedMembers.count == 1)
}
@Test
mutating func invitedMembers() async throws {
setup(members: [.mockInvited])
let deferred = deferFulfillment(context.$viewState) { state in
state.visibleInvitedMembers.count == 1
}
try await deferred.fulfill()
#expect(viewModel.state.joinedMembersCount == 0)
#expect(viewModel.state.visibleInvitedMembers.count == 1)
#expect(viewModel.state.visibleJoinedMembers.count == 0)
}
@Test
mutating func searchInvitedMembers() async throws {
setup(members: [.mockInvited])
context.searchQuery = "invited"
let deferred = deferFulfillment(context.$viewState) { state in
state.visibleInvitedMembers.count == 1
}
try await deferred.fulfill()
#expect(viewModel.state.joinedMembersCount == 0)
#expect(viewModel.state.visibleInvitedMembers.count == 1)
#expect(viewModel.state.visibleJoinedMembers.count == 0)
}
@Test
mutating func selectUserAsUser() async throws {
setup(members: .allMembers)
var deferred = deferFulfillment(context.$viewState) { !$0.visibleInvitedMembers.isEmpty }
try await deferred.fulfill()
deferred = deferFulfillment(context.$viewState) { $0.bindings.manageMemeberViewModel != nil }
guard let user = viewModel.state.visibleJoinedMembers.first(where: { $0.member.role == .user && $0.member.id != RoomMemberProxyMock.mockMe.userID })?.member else {
Issue.record("Expected to find a regular user.")
return
}
context.send(viewAction: .selectMember(user))
try await deferred.fulfill()
#expect(context.manageMemeberViewModel != nil)
#expect(context.manageMemeberViewModel?.state.memberDetails.id == user.id)
#expect(context.manageMemeberViewModel?.state.permissions.canKick == false)
#expect(context.manageMemeberViewModel?.state.permissions.canBan == false)
}
@Test
mutating func selectUserAsAdmin() async throws {
setup(members: .allMembersAsAdmin)
var deferred = deferFulfillment(context.$viewState) { !$0.visibleInvitedMembers.isEmpty && $0.canKickUsers && $0.canBanUsers }
try await deferred.fulfill()
#expect(context.manageMemeberViewModel == nil)
deferred = deferFulfillment(context.$viewState) { $0.bindings.manageMemeberViewModel != nil }
guard let user = viewModel.state.visibleJoinedMembers.first(where: { $0.member.role == .user && $0.member.id != RoomMemberProxyMock.mockMe.userID })?.member else {
Issue.record("Expected to find a regular user.")
return
}
context.send(viewAction: .selectMember(user))
try await deferred.fulfill()
#expect(context.manageMemeberViewModel?.state.memberDetails.id == user.id)
#expect(context.manageMemeberViewModel?.state.permissions.canKick == true)
#expect(context.manageMemeberViewModel?.state.permissions.canBan == true)
@@ -172,25 +172,25 @@ struct RoomMembersListScreenViewModelTests {
#expect(context.manageMemeberViewModel?.state.isBanUnbanDisabled == false)
#expect(context.manageMemeberViewModel?.state.isMemberBanned == false)
}
@Test
mutating func selectModeratorAsAdmin() async throws {
setup(members: .allMembersAsAdmin)
var deferred = deferFulfillment(context.$viewState) { !$0.visibleInvitedMembers.isEmpty && $0.canKickUsers && $0.canBanUsers }
try await deferred.fulfill()
#expect(context.manageMemeberViewModel == nil)
deferred = deferFulfillment(context.$viewState) { $0.bindings.manageMemeberViewModel != nil }
guard let moderator = viewModel.state.visibleJoinedMembers.first(where: { $0.member.role == .moderator })?.member else {
Issue.record("Expected to find a moderator.")
return
}
context.send(viewAction: .selectMember(moderator))
try await deferred.fulfill()
#expect(context.manageMemeberViewModel?.state.memberDetails.id == moderator.id)
#expect(context.manageMemeberViewModel?.state.permissions.canKick == true)
#expect(context.manageMemeberViewModel?.state.permissions.canBan == true)
@@ -198,23 +198,23 @@ struct RoomMembersListScreenViewModelTests {
#expect(context.manageMemeberViewModel?.state.isKickDisabled == false)
#expect(context.manageMemeberViewModel?.state.isBanUnbanDisabled == false)
}
@Test
mutating func selectAdminAsAdmin() async throws {
setup(members: .allMembersAsAdmin)
var deferred = deferFulfillment(context.$viewState) { !$0.visibleInvitedMembers.isEmpty && $0.canKickUsers && $0.canBanUsers }
try await deferred.fulfill()
deferred = deferFulfillment(context.$viewState) { $0.bindings.manageMemeberViewModel != nil }
guard let admin = viewModel.state.visibleJoinedMembers.first(where: { $0.member.role.isAdminOrHigher && $0.member.id != RoomMemberProxyMock.mockMe.userID })?.member else {
Issue.record("Expected to find another admin.")
return
}
context.send(viewAction: .selectMember(admin))
try await deferred.fulfill()
#expect(context.manageMemeberViewModel?.state.memberDetails.id == admin.id)
#expect(context.manageMemeberViewModel?.state.permissions.canKick == true)
#expect(context.manageMemeberViewModel?.state.permissions.canBan == true)
@@ -222,44 +222,44 @@ struct RoomMembersListScreenViewModelTests {
#expect(context.manageMemeberViewModel?.state.isBanUnbanDisabled == true)
#expect(context.manageMemeberViewModel?.state.isMemberBanned == false)
}
@Test
mutating func selectOwnMemberAsAdmin() async throws {
setup(members: .allMembersAsAdmin)
let deferred = deferFulfillment(context.$viewState) { !$0.visibleInvitedMembers.isEmpty }
try await deferred.fulfill()
let memberDetailsAction = deferFulfillment(viewModel.actions) { $0.isSelectMember }
guard let ownMember = viewModel.state.visibleJoinedMembers.first(where: { $0.member.id == RoomMemberProxyMock.mockMe.userID })?.member else {
Issue.record("Expected to find own user admin.")
return
}
context.send(viewAction: .selectMember(ownMember))
try await memberDetailsAction.fulfill()
#expect(context.manageMemeberViewModel == nil)
}
@Test
mutating func selectBannedMember() async throws {
setup(members: .allMembersAsAdmin + RoomMemberProxyMock.mockBanned)
var deferred = deferFulfillment(context.$viewState) { !$0.visibleInvitedMembers.isEmpty && $0.canKickUsers && $0.canBanUsers }
try await deferred.fulfill()
#expect(context.alertInfo == nil)
deferred = deferFulfillment(context.$viewState) { $0.bindings.manageMemeberViewModel != nil }
guard let bannedMember = viewModel.state.visibleBannedMembers.first?.member else {
Issue.record("Expected to find a banned user.")
return
}
context.send(viewAction: .selectMember(bannedMember))
try await deferred.fulfill()
#expect(context.manageMemeberViewModel?.state.memberDetails.id == bannedMember.id)
#expect(context.manageMemeberViewModel?.state.permissions.canKick == true)
#expect(context.manageMemeberViewModel?.state.permissions.canBan == true)
@@ -267,32 +267,32 @@ struct RoomMembersListScreenViewModelTests {
#expect(context.manageMemeberViewModel?.state.isBanUnbanDisabled == false)
#expect(context.manageMemeberViewModel?.state.isMemberBanned == true)
}
@Test
mutating func switchesToMembersModeWhenThereAreNoBannedMembers() async throws {
roomProxy = JoinedRoomProxyMock(.init(name: "test"))
let subject = CurrentValueSubject<[RoomMemberProxyProtocol], Never>([RoomMemberProxyMock].allMembersAsAdmin + RoomMemberProxyMock.mockBanned)
roomProxy.membersPublisher = subject.asCurrentValuePublisher()
viewModel = RoomMembersListScreenViewModel(userSession: UserSessionMock(.init()),
roomProxy: roomProxy,
userIndicatorController: UserIndicatorControllerMock(),
analytics: AnalyticsServiceMock(.init()))
let context = viewModel.context
var deferred = deferFulfillment(context.$viewState) { $0.visibleBannedMembers.count == 4 && $0.bindings.mode == .banned }
context.mode = .banned
try await deferred.fulfill()
deferred = deferFulfillment(context.$viewState) { $0.visibleBannedMembers.count == 0 && $0.bindings.mode == .members }
subject.value = [RoomMemberProxyMock].allMembersAsAdmin
try await deferred.fulfill()
}
// MARK: - Helpers
private mutating func setup(members: [RoomMemberProxyMock]) {
roomProxy = JoinedRoomProxyMock(.init(name: "test", members: members))
viewModel = RoomMembersListScreenViewModel(userSession: UserSessionMock(.init()),
@@ -16,7 +16,7 @@ struct RoomNotificationSettingsScreenViewModelTests {
var roomProxyMock: JoinedRoomProxyMock!
var notificationSettingsProxyMock: NotificationSettingsProxyMock!
var cancellables = Set<AnyCancellable>()
init() {
roomProxyMock = JoinedRoomProxyMock(.init(name: "Test"))
notificationSettingsProxyMock = NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration())
@@ -32,14 +32,14 @@ struct RoomNotificationSettingsScreenViewModelTests {
let viewModel = RoomNotificationSettingsScreenViewModel(notificationSettingsProxy: notificationSettingsProxyMock,
roomProxy: roomProxyMock,
displayAsUserDefinedRoomSettings: false)
let deferred = deferFulfillment(viewModel.context.observe(\.viewState)) { state in
state.notificationSettingsState.isLoaded
}
notificationSettingsProxyMock.callbacks.send(.settingsDidChange)
try await deferred.fulfill()
#expect(!viewModel.context.allowCustomSetting)
#expect(viewModel.context.viewState.shouldDisplayMentionsOnlyDisclaimer)
#expect(viewModel.context.viewState.description(mode: .mentionsAndKeywordsOnly) != nil)
@@ -55,14 +55,14 @@ struct RoomNotificationSettingsScreenViewModelTests {
let viewModel = RoomNotificationSettingsScreenViewModel(notificationSettingsProxy: notificationSettingsProxyMock,
roomProxy: roomProxyMock,
displayAsUserDefinedRoomSettings: false)
let deferred = deferFulfillment(viewModel.context.observe(\.viewState)) { state in
state.notificationSettingsState.isLoaded
}
notificationSettingsProxyMock.callbacks.send(.settingsDidChange)
try await deferred.fulfill()
#expect(!viewModel.context.allowCustomSetting)
#expect(!viewModel.context.viewState.shouldDisplayMentionsOnlyDisclaimer)
#expect(viewModel.context.viewState.description(mode: .mentionsAndKeywordsOnly) == nil)
@@ -78,14 +78,14 @@ struct RoomNotificationSettingsScreenViewModelTests {
let viewModel = RoomNotificationSettingsScreenViewModel(notificationSettingsProxy: notificationSettingsProxyMock,
roomProxy: roomProxyMock,
displayAsUserDefinedRoomSettings: false)
let deferred = deferFulfillment(viewModel.context.observe(\.viewState)) { state in
state.notificationSettingsState.isLoaded
}
notificationSettingsProxyMock.callbacks.send(.settingsDidChange)
try await deferred.fulfill()
#expect(!viewModel.context.allowCustomSetting)
#expect(!viewModel.context.viewState.shouldDisplayMentionsOnlyDisclaimer)
#expect(viewModel.context.viewState.description(mode: .mentionsAndKeywordsOnly) == nil)
@@ -103,7 +103,7 @@ struct RoomNotificationSettingsScreenViewModelTests {
notificationSettingsProxyMock.callbacks.send(.settingsDidChange)
try await deferred.fulfill()
#expect(viewModel.context.allowCustomSetting)
}
@@ -119,7 +119,7 @@ struct RoomNotificationSettingsScreenViewModelTests {
notificationSettingsProxyMock.callbacks.send(.settingsDidChange)
try await deferred.fulfill()
let expectedAlertInfo = AlertInfo(id: RoomNotificationSettingsScreenErrorType.loadingSettingsFailed,
title: L10n.commonError,
message: L10n.screenRoomNotificationSettingsErrorLoadingSettings)
@@ -140,7 +140,7 @@ struct RoomNotificationSettingsScreenViewModelTests {
notificationSettingsProxyMock.callbacks.send(.settingsDidChange)
try await deferred.fulfill()
let deferredIsRestoringDefaultSettings = deferFulfillment(viewModel.context.observe(\.viewState.isRestoringDefaultSetting),
transitionValues: [false, true, false])
@@ -296,7 +296,7 @@ struct RoomNotificationSettingsScreenViewModelTests {
viewModel.context.send(viewAction: .deleteCustomSettingTapped)
try await deferredViewState.fulfill()
// an alert is expected
#expect(viewModel.context.alertInfo?.id == .restoreDefaultFailed)
// the `dismiss` action must not have been sent
@@ -23,7 +23,7 @@ struct RoomPollsHistoryScreenViewModelTests {
timelineController: timelineController,
userIndicatorController: UserIndicatorControllerMock())
}
@Test
func backPaginate() async throws {
timelineController.backPaginationResponses = [
@@ -32,7 +32,7 @@ struct RoomPollsHistoryScreenViewModelTests {
PollRoomTimelineItem.mock(poll: .disclosed(createdByAccountOwner: false)),
PollRoomTimelineItem.mock(poll: .endedDisclosed)]
]
let deferredViewState = deferFulfillment(viewModel.context.$viewState, keyPath: \.isBackPaginating, transitionValues: [false, true, false])
viewModel.context.send(viewAction: .loadMore)
@@ -52,7 +52,7 @@ struct RoomPollsHistoryScreenViewModelTests {
PollRoomTimelineItem.mock(poll: .endedDisclosed)],
[]
]
let deferredViewState = deferFulfillment(viewModel.context.$viewState, keyPath: \.isBackPaginating, transitionValues: [false, true, false])
viewModel.context.send(viewAction: .loadMore)
@@ -91,7 +91,7 @@ struct RoomPollsHistoryScreenViewModelTests {
PollRoomTimelineItem.mock(poll: .endedDisclosed)],
[]
]
let deferredViewState = deferFulfillment(viewModel.context.$viewState) { value in
!value.pollTimelineItems.isEmpty
}
@@ -110,25 +110,25 @@ struct RoomPollsHistoryScreenViewModelTests {
@Test
func endPoll() async throws {
let deferred = deferFulfillment(interactionHandler.publisher.delay(for: 0.1, scheduler: DispatchQueue.main)) { _ in true }
interactionHandler.endPollPollStartIDReturnValue = .success(())
viewModel.context.send(viewAction: .end(pollStartID: "somePollID"))
try await deferred.fulfill()
#expect(interactionHandler.endPollPollStartIDCalled)
#expect(interactionHandler.endPollPollStartIDReceivedPollStartID == "somePollID")
}
@Test
func endPollFailure() async throws {
let deferred = deferFulfillment(viewModel.context.$viewState) { value in
value.bindings.alertInfo != nil
}
interactionHandler.endPollPollStartIDReturnValue = .failure(SDKError.generic)
viewModel.context.send(viewAction: .end(pollStartID: "somePollID"))
try await deferred.fulfill()
#expect(interactionHandler.endPollPollStartIDCalled)
@@ -138,26 +138,26 @@ struct RoomPollsHistoryScreenViewModelTests {
@Test
func sendPollResponse() async throws {
let deferred = deferFulfillment(interactionHandler.publisher.delay(for: 0.1, scheduler: DispatchQueue.main)) { _ in true }
interactionHandler.sendPollResponsePollStartIDOptionIDReturnValue = .success(())
viewModel.context.send(viewAction: .sendPollResponse(pollStartID: "somePollID", optionID: "someOptionID"))
try await deferred.fulfill()
#expect(interactionHandler.sendPollResponsePollStartIDOptionIDCalled)
#expect(interactionHandler.sendPollResponsePollStartIDOptionIDReceivedInvocations[0].pollStartID == "somePollID")
#expect(interactionHandler.sendPollResponsePollStartIDOptionIDReceivedInvocations[0].optionID == "someOptionID")
}
@Test
func sendPollResponseFailure() async throws {
let deferred = deferFulfillment(viewModel.context.$viewState) { value in
value.bindings.alertInfo != nil
}
interactionHandler.sendPollResponsePollStartIDOptionIDReturnValue = .failure(SDKError.generic)
viewModel.context.send(viewAction: .sendPollResponse(pollStartID: "somePollID", optionID: "someOptionID"))
try await deferred.fulfill()
#expect(interactionHandler.sendPollResponsePollStartIDOptionIDCalled)
@@ -13,23 +13,23 @@ import Testing
struct RoomRolesAndPermissionsScreenViewModelTests {
var viewModel: RoomRolesAndPermissionsScreenViewModelProtocol!
var roomProxy: JoinedRoomProxyMock!
var context: RoomRolesAndPermissionsScreenViewModelType.Context {
viewModel.context
}
@Test
mutating func emptyCounters() {
setup(members: .allMembers)
#expect(context.viewState.administratorCount == 0)
#expect(context.viewState.moderatorCount == 0)
}
@Test
mutating func filledCounters() {
setup(members: .allMembersAsAdmin)
#expect(context.viewState.administratorCount == 2)
#expect(context.viewState.moderatorCount == 1)
}
@@ -37,10 +37,10 @@ struct RoomRolesAndPermissionsScreenViewModelTests {
@Test
mutating func resetPermissions() async throws {
setup(members: .allMembersAsAdmin)
context.send(viewAction: .reset)
#expect(context.alertInfo != nil)
context.alertInfo?.primaryButton.action?()
try await Task.sleep(for: .milliseconds(100))
@@ -51,14 +51,14 @@ struct RoomRolesAndPermissionsScreenViewModelTests {
@Test
mutating func demoteToModerator() async throws {
setup(members: .allMembersAsAdmin)
context.send(viewAction: .editOwnUserRole)
#expect(context.alertInfo != nil)
context.alertInfo?.verticalButtons?.first { $0.title.localizedStandardContains("moderator") }?.action?()
try await Task.sleep(for: .milliseconds(100))
#expect(roomProxy.updatePowerLevelsForUsersCalled)
#expect(roomProxy.updatePowerLevelsForUsersReceivedUpdates?.first?.powerLevel == RoomRole.moderator.powerLevelValue)
}
@@ -66,20 +66,20 @@ struct RoomRolesAndPermissionsScreenViewModelTests {
@Test
mutating func demoteToMember() async throws {
setup(members: .allMembersAsAdmin)
context.send(viewAction: .editOwnUserRole)
#expect(context.alertInfo != nil)
context.alertInfo?.verticalButtons?.first { $0.title.localizedStandardContains("member") }?.action?()
try await Task.sleep(for: .milliseconds(100))
#expect(roomProxy.updatePowerLevelsForUsersCalled)
#expect(roomProxy.updatePowerLevelsForUsersReceivedUpdates?.first?.powerLevel == RoomRole.user.powerLevelValue)
}
// MARK: - Helpers
private mutating func setup(members: [RoomMemberProxyMock]) {
roomProxy = JoinedRoomProxyMock(.init(members: members))
viewModel = RoomRolesAndPermissionsScreenViewModel(roomProxy: roomProxy,
@@ -56,7 +56,7 @@ final class RoomScreenViewModelTests {
try await deferred.fulfill()
#expect(viewModel.context.viewState.pinnedEventsBannerState.isLoading)
#expect(!viewModel.context.viewState.shouldShowPinnedEventsBanner)
// check if if after the pinned event ids are set the banner is still in a loading state, but is both loading and showing with a counter
deferred = deferFulfillment(viewModel.context.$viewState) { viewState in
viewState.pinnedEventsBannerState.count == 2
@@ -98,7 +98,7 @@ final class RoomScreenViewModelTests {
#expect(!viewModel.context.viewState.pinnedEventsBannerState.isLoading)
#expect(viewModel.context.viewState.shouldShowPinnedEventsBanner)
#expect(viewModel.context.viewState.pinnedEventsBannerState.selectedPinnedIndex == 1)
// check how the scrolling changes the banner visibility
viewModel.timelineHasScrolled(direction: .top)
#expect(!viewModel.context.viewState.shouldShowPinnedEventsBanner)
@@ -254,7 +254,7 @@ final class RoomScreenViewModelTests {
#expect(viewModel.state.roomAvatar == .room(id: "TestID", name: "StartingName", avatarURL: nil))
#expect(!viewModel.state.canJoinCall)
#expect(!viewModel.state.hasOngoingCall)
let deferred = deferFulfillment(viewModel.context.$viewState) { viewState in
viewState.roomTitle == "NewName" &&
viewState.roomAvatar == .room(id: "TestID", name: "NewName", avatarURL: .mockMXCAvatar) &&
@@ -358,7 +358,7 @@ final class RoomScreenViewModelTests {
state.unseenKnockRequests == [.init(displayName: "Alice", avatarURL: nil, userID: "@alice:matrix.org", reason: "Hello World!", eventID: "1")]
}
try await deferred.fulfill()
let deferredAction = deferFulfillment(viewModel.actions) { $0 == .displayKnockRequests }
viewModel.context.send(viewAction: .viewKnockRequests)
try await deferredAction.fulfill()
@@ -450,7 +450,7 @@ final class RoomScreenViewModelTests {
var configuration = JoinedRoomProxyMockConfiguration(isEncrypted: false, historyVisibility: .joined)
let infoSubject = CurrentValueSubject<RoomInfoProxyProtocol, Never>(RoomInfoProxyMock(configuration))
let roomProxyMock = JoinedRoomProxyMock(configuration)
// setup the room proxy actions publisher
roomProxyMock.underlyingInfoPublisher = infoSubject.asCurrentValuePublisher()
let viewModel = RoomScreenViewModel(userSession: UserSessionMock(.init()),
@@ -16,72 +16,72 @@ final class RoomSummaryProviderTests {
private let baseFilters: [RoomListEntriesDynamicFilterKind] = [.any(filters: [.all(filters: [.nonSpace, .nonLeft]),
.all(filters: [.space, .invite])]),
.deduplicateVersions]
var appSettings: AppSettings!
var roomList: RoomListSDKMock!
var dynamicEntriesController: RoomListDynamicEntriesControllerSDKMock!
var roomSummaryProvider: RoomSummaryProvider!
@Test
func defaultRustFilters() async {
// Given a new room provider.
setup()
await Task.yield()
// Then it should have the default Rust filters enabled.
#expect(dynamicEntriesController.setFilterKindCallsCount == 1)
#expect(dynamicEntriesController.setFilterKindReceivedInvocations.last == .all(filters: baseFilters))
// When setting one our user filters.
roomSummaryProvider.setFilter(.all(filters: [.favourites]))
await Task.yield()
// Then that filter should be added to the default Rust filters.
#expect(dynamicEntriesController.setFilterKindCallsCount == 2)
#expect(dynamicEntriesController.setFilterKindReceivedInvocations.last == .all(filters: [.all(filters: [.favourite, .joined])] + baseFilters))
}
@Test
func lowPriorityRustFilters() async {
// Given a new room provider with the low priority filter enabled.
setup(isLowPriorityFilterEnabled: true)
await Task.yield()
// Then the default Rust filters should include the non-low priority filter,
// so that low priority rooms are hidden from the top of the room list.
#expect(dynamicEntriesController.setFilterKindCallsCount == 1)
#expect(dynamicEntriesController.setFilterKindReceivedInvocations.last == .all(filters: baseFilters + [.nonLowPriority]))
// When setting the low priority filter.
roomSummaryProvider.setFilter(.all(filters: [.lowPriority]))
await Task.yield()
// Then the non-low priority filter should be replaced with the low priority filter.
#expect(dynamicEntriesController.setFilterKindCallsCount == 2)
#expect(dynamicEntriesController.setFilterKindReceivedInvocations.last == .all(filters: [.all(filters: [.lowPriority, .joined])] + baseFilters))
// When setting another one of our filters.
roomSummaryProvider.setFilter(.all(filters: [.rooms]))
await Task.yield()
// Then the filter should be combined with the non-low priority filter.
#expect(dynamicEntriesController.setFilterKindCallsCount == 3)
#expect(dynamicEntriesController.setFilterKindReceivedInvocations.last == .all(filters: [.all(filters: [.category(expect: .group), .joined])] + baseFilters + [.nonLowPriority]))
}
@Test
func roomIdentifierFilters() async {
setup()
await Task.yield()
// Then it should have the default Rust filters enabled.
#expect(dynamicEntriesController.setFilterKindCallsCount == 1)
#expect(dynamicEntriesController.setFilterKindReceivedInvocations.last == .all(filters: baseFilters))
// When setting one our user filters.
roomSummaryProvider.setFilter(.rooms(roomsIDs: ["SomeRoom"], filters: [.favourites]))
await Task.yield()
// Then that filter should be added to the default Rust filters.
#expect(dynamicEntriesController.setFilterKindCallsCount == 2)
#expect(dynamicEntriesController.setFilterKindReceivedInvocations.last == .all(filters: [.all(filters: [.favourite, .joined])] + baseFilters + [.identifiers(identifiers: ["SomeRoom"])]))
@@ -92,20 +92,20 @@ final class RoomSummaryProviderTests {
private func setup(isLowPriorityFilterEnabled: Bool = false) {
appSettings = AppSettings.volatile()
appSettings.lowPriorityFilterEnabled = isLowPriorityFilterEnabled
let stateEventStringBuilder = RoomStateEventStringBuilder(userID: "@me:matrix.org")
let attributedStringBuilder = AttributedStringBuilder(mentionBuilder: MentionBuilder())
let eventStringBuilder = RoomEventStringBuilder(stateEventStringBuilder: stateEventStringBuilder,
messageEventStringBuilder: RoomMessageEventStringBuilder(attributedStringBuilder: attributedStringBuilder,
style: .senderPrefixed),
shouldPrefixSenderName: true)
roomSummaryProvider = RoomSummaryProvider(roomListService: RoomListServiceSDKMock(),
eventStringBuilder: eventStringBuilder,
name: "Test",
notificationSettings: NotificationSettingsProxyMock(with: .init()),
appSettings: appSettings)
dynamicEntriesController = RoomListDynamicEntriesControllerSDKMock()
dynamicEntriesController.setFilterKindReturnValue = true
let dynamicAdaptersResult = RoomListEntriesWithDynamicAdaptersResultSDKMock()
+1 -1
View File
@@ -20,7 +20,7 @@ struct RoomTests {
var callIntent = await room.joinCallIntent()
#expect(callIntent == .startCall)
room.isDirectReturnValue = true
callIntent = await room.joinCallIntent()
#expect(callIntent == .startCallDm)
@@ -20,7 +20,7 @@ final class SecurityAndPrivacyScreenViewModelTests {
var context: SecurityAndPrivacyScreenViewModelType.Context {
viewModel.context
}
deinit {
viewModel = nil
roomProxy = nil
@@ -157,7 +157,7 @@ final class SecurityAndPrivacyScreenViewModelTests {
#expect(context.desiredSettings.accessType == .spaceMembers(spaceIDs: [spaces[0].id]))
#expect(context.viewState.shouldShowAccessSectionFooter)
#expect(!context.viewState.isSaveDisabled)
await waitForConfirmation("Join rule has updated") { confirm in
roomProxy.updateJoinRuleClosure = { value in
#expect(value == .restricted(rules: [.roomMembership(roomID: spaces[0].id)]))
@@ -202,7 +202,7 @@ final class SecurityAndPrivacyScreenViewModelTests {
#expect(context.desiredSettings.accessType == .askToJoinWithSpaceMembers(spaceIDs: [spaces[0].id]))
#expect(context.viewState.shouldShowAccessSectionFooter)
#expect(!context.viewState.isSaveDisabled)
await waitForConfirmation("Join rule has updated") { confirm in
roomProxy.updateJoinRuleClosure = { value in
#expect(value == .knockRestricted(rules: [.roomMembership(roomID: spaces[0].id)]))
@@ -249,7 +249,7 @@ final class SecurityAndPrivacyScreenViewModelTests {
#expect(context.desiredSettings.accessType == .spaceMembers(spaceIDs: [spaces[0].id, "unknownSpaceID"]))
#expect(context.viewState.shouldShowAccessSectionFooter)
#expect(!context.viewState.isSaveDisabled)
await waitForConfirmation("Join rule has updated") { confirm in
roomProxy.updateJoinRuleClosure = { value in
#expect(value == .restricted(rules: [.roomMembership(roomID: spaces[0].id), .roomMembership(roomID: "unknownSpaceID")]))
@@ -16,14 +16,14 @@ final class ServerConfirmationScreenViewModelTests {
var clientFactory: AuthenticationClientFactoryMock!
var client: ClientSDKMock!
var service: AuthenticationServiceProtocol!
var viewModel: ServerConfirmationScreenViewModel!
var context: ServerConfirmationScreenViewModel.Context {
viewModel.context
}
private let appSettings: AppSettings
init() {
appSettings = AppSettings.volatile()
}
@@ -100,11 +100,11 @@ private extension SessionDirectories {
var mockStateStorePath: String {
dataDirectory.appending(component: "matrix-sdk-state.sqlite3").path(percentEncoded: false)
}
var mockCryptoStorePath: String {
dataDirectory.appending(component: "matrix-sdk-crypto.sqlite3").path(percentEncoded: false)
}
var mockEventCachePath: String {
cacheDirectory.appending(component: "matrix-sdk-event-cache.sqlite3").path(percentEncoded: false)
}
@@ -25,7 +25,7 @@ struct SessionVerificationViewModelTests {
mediaProvider: MediaProviderMock(.init()))
context = viewModel.context
}
@Test
func requestVerification() async throws {
#expect(context.viewState.verificationState == .initial)
@@ -58,7 +58,7 @@ struct SessionVerificationViewModelTests {
context.send(viewAction: .restart)
#expect(context.viewState.verificationState == .initial)
#expect(sessionVerificationController.requestDeviceVerificationCallsCount == 1)
#expect(sessionVerificationController.cancelVerificationCallsCount == 1)
}
@@ -24,14 +24,14 @@ struct SettingsScreenViewModelTests {
isInSecondaryWindow: false)
context = viewModel.context
}
@Test
func logout() async throws {
let deferred = deferFulfillment(viewModel.actions) { $0 == .logout }
context.send(viewAction: .logout)
try await deferred.fulfill()
}
@Test
func reportBug() async throws {
let deferred = deferFulfillment(viewModel.actions) { $0 == .reportBug }
@@ -37,13 +37,13 @@ struct SoftLogoutScreenViewModelTests {
keyBackupNeeded: true,
password: "12345678")
let context = viewModel.context
// Given a view model where the user hasn't yet sent the verification email.
#expect(context.viewState.canSubmit, "The view model should start with a valid password.")
#expect(context.viewState.loginMode == .password, "The view model should show login form for the given homeserver.")
#expect(context.viewState.showRecoverEncryptionKeysMessage, "The view model should show recover encryption keys message.")
}
@Test
func initialStateForOAuth() {
let viewModel = SoftLogoutScreenViewModel(credentials: credentials,
@@ -64,7 +64,7 @@ struct SoftLogoutScreenViewModelTests {
homeserver: .mockUnsupported,
keyBackupNeeded: false)
let context = viewModel.context
// Given a view model where the user hasn't yet sent the verification email.
#expect(context.password.isEmpty, "The view model should start with an empty password.")
#expect(!context.viewState.canSubmit, "The view model should start with an invalid password.")
@@ -26,7 +26,7 @@ struct SpaceScreenViewModelTests {
var context: SpaceScreenViewModelType.Context {
viewModel.context
}
@Test
mutating func initialState() {
setupViewModel()
+2 -2
View File
@@ -19,7 +19,7 @@ struct StringTests {
func spaceIsAscii() {
#expect("".isASCII)
}
@Test
func johnnyIsAscii() {
#expect("johnny".isASCII)
@@ -54,7 +54,7 @@ struct StringTests {
#expect("Heartbreak Hotel 🏩".asciified() == "Heartbreak Hotel", "The emoji should be stripped.")
#expect("1️⃣2️⃣3️⃣".asciified() == "123", "The emoji should be converted to ASCII.")
}
@Test
func ellipsizeWorks() {
#expect("ellipsize".ellipsize(length: 5) == "ellip…")
@@ -16,7 +16,7 @@ struct TimelineItemFactoryTests {
func callInvite() throws {
let ownUserID = "@alice:matrix.org"
let senderUserID = "@bob:matrix.org"
let factory = RoomTimelineItemFactory(userID: ownUserID,
attributedStringBuilder: AttributedStringBuilder(mentionBuilder: MentionBuilder()),
stateEventStringBuilder: RoomStateEventStringBuilder(userID: ownUserID))
@@ -24,7 +24,7 @@ struct TimelineItemFactoryTests {
let eventTimelineItem = EventTimelineItem.mockCallInvite(sender: senderUserID)
let eventTimelineItemProxy = EventTimelineItemProxy(item: eventTimelineItem, uniqueID: .init("0"))
let item = try #require(factory.buildTimelineItem(for: eventTimelineItemProxy, isDM: false) as? CallInviteRoomTimelineItem,
"Incorrect item type")
@@ -19,7 +19,7 @@ struct TimelineMediaPreviewViewModelTests {
var context: TimelineMediaPreviewViewModel.Context {
viewModel.context
}
var mediaProvider: MediaProviderMock!
var photoLibraryManager: PhotoLibraryManagerMock!
var timelineController: MockTimelineController!
+7 -7
View File
@@ -12,7 +12,7 @@ import Testing
struct TimelineStateTests {
// MARK: - Read marker
@Test("Read marker unique ID is captured when present in the timeline")
func readMarkerUniqueIDPresent() {
var state = TimelineState()
@@ -20,22 +20,22 @@ struct TimelineStateTests {
state.itemsDictionary[.init("read-marker")] = makeReadMarkerViewState(uniqueID: "read-marker")
state.itemsDictionary[.init("msg2")] = makeTextViewState(uniqueID: "msg2")
state.recomputeReadMarkerUniqueID()
#expect(state.readMarkerUniqueID == .init("read-marker"))
}
@Test("Read marker unique ID is nil when no marker exists")
func readMarkerUniqueIDAbsent() {
var state = TimelineState()
state.itemsDictionary[.init("msg1")] = makeTextViewState(uniqueID: "msg1")
state.itemsDictionary[.init("msg2")] = makeTextViewState(uniqueID: "msg2")
state.recomputeReadMarkerUniqueID()
#expect(state.readMarkerUniqueID == nil)
}
// MARK: - Helpers
private func makeTextViewState(uniqueID: String) -> RoomTimelineItemViewState {
let item = TextRoomTimelineItem(id: .virtual(uniqueID: .init(uniqueID)),
timestamp: .mock,
@@ -46,7 +46,7 @@ struct TimelineStateTests {
content: .init(body: "Test message"))
return RoomTimelineItemViewState(type: .text(item), groupStyle: .single)
}
private func makeReadMarkerViewState(uniqueID: String) -> RoomTimelineItemViewState {
let item = ReadMarkerRoomTimelineItem(id: .virtual(uniqueID: .init(uniqueID)))
return RoomTimelineItemViewState(type: .readMarker(item), groupStyle: .single)
+12 -12
View File
@@ -15,13 +15,13 @@ import Testing
@MainActor
final class TimelineViewModelTests {
var cancellables = Set<AnyCancellable>()
init() async throws {
cancellables.removeAll()
}
// MARK: - Message Grouping
@Test
func messageGrouping() {
// Given 3 messages from Bob.
@@ -305,9 +305,9 @@ final class TimelineViewModelTests {
timelineController.timelineItems = items
timelineController.roomProxy = roomProxy
let appSettings = AppSettings.volatile()
let viewModel = TimelineViewModel(roomProxy: roomProxy,
timelineController: timelineController,
userSession: UserSessionMock(.init()),
@@ -333,7 +333,7 @@ final class TimelineViewModelTests {
let id = message.id
let appSettings = AppSettings.volatile()
// When showing them in a timeline.
let timelineController = MockTimelineController()
timelineController.timelineItems = [message]
@@ -360,7 +360,7 @@ final class TimelineViewModelTests {
@Test
func showManageUserAsAdmin() async throws {
let appSettings = AppSettings.volatile()
let viewModel = TimelineViewModel(roomProxy: JoinedRoomProxyMock(.init(name: "",
members: [RoomMemberProxyMock.mockAdmin,
RoomMemberProxyMock.mockAlice],
@@ -399,7 +399,7 @@ final class TimelineViewModelTests {
@Test
func showDetailsForAnAdmin() async throws {
let appSettings = AppSettings.volatile()
let viewModel = TimelineViewModel(roomProxy: JoinedRoomProxyMock(.init(name: "",
members: [RoomMemberProxyMock.mockAdmin,
RoomMemberProxyMock.mockAlice],
@@ -438,7 +438,7 @@ final class TimelineViewModelTests {
@Test
func showDetailsForABannedUser() async throws {
let appSettings = AppSettings.volatile()
let viewModel = TimelineViewModel(roomProxy: JoinedRoomProxyMock(.init(name: "",
members: [RoomMemberProxyMock.mockAdmin,
RoomMemberProxyMock.mockBanned[0]],
@@ -480,7 +480,7 @@ final class TimelineViewModelTests {
@Test
func pinnedEvents() async throws {
let appSettings = AppSettings.volatile()
var configuration = JoinedRoomProxyMockConfiguration(name: "",
pinnedEventIDs: .init(["test1"]))
let roomProxyMock = JoinedRoomProxyMock(configuration)
@@ -511,7 +511,7 @@ final class TimelineViewModelTests {
@Test
func canUserPinEvents() async throws {
let appSettings = AppSettings.volatile()
let configuration = JoinedRoomProxyMockConfiguration(name: "",
powerLevelsConfiguration: .init(canUserPin: true))
let roomProxyMock = JoinedRoomProxyMock(configuration)
@@ -588,7 +588,7 @@ final class TimelineViewModelTests {
focussedEventID: String? = nil,
timelineController: TimelineControllerProtocol) -> TimelineViewModel {
let appSettings = AppSettings.volatile()
return TimelineViewModel(roomProxy: roomProxy ?? JoinedRoomProxyMock(.init(name: "")),
focussedEventID: focussedEventID,
timelineController: timelineController,
@@ -13,7 +13,7 @@ import Testing
@MainActor
struct UserDetailsEditScreenViewModelTests {
private var viewModel: UserDetailsEditScreenViewModel!
private var context: UserDetailsEditScreenViewModelType.Context {
viewModel.context
}
@@ -48,7 +48,7 @@ struct UserDiscoveryServiceTest {
assertSearchResults(results, toBe: 3)
#expect(!clientProxy.profileForCalled)
}
@Test
func getProfileIsNotCalledForAccountOwnerID() async {
clientProxy.searchUsersSearchTermLimitReturnValue = .success(.init(results: searchResults, limited: true))
+1 -1
View File
@@ -127,7 +127,7 @@ struct UserPreferenceTests {
let value = TestPreferences(testDefaults)
value.codable = nil
#expect(value.codable == nil)
#expect(testDefaults.data(forKey: TestsKey.key3.rawValue) == nil)
}
@@ -14,7 +14,7 @@ struct UserProfileScreenViewModelTests {
@Test
func initialState() async throws {
let userIndicatorController = UserIndicatorControllerMock()
let profile = UserProfileProxy(userID: "@alice:matrix.org", displayName: "Alice", avatarURL: .mockMXCAvatar)
let clientProxy = ClientProxyMock(.init())
clientProxy.profileForReturnValue = .success(profile)
@@ -38,7 +38,7 @@ struct UserProfileScreenViewModelTests {
@Test
func initialStateAccountOwner() async throws {
let userIndicatorController = UserIndicatorControllerMock()
let profile = UserProfileProxy(userID: RoomMemberProxyMock.mockMe.userID, displayName: "Me", avatarURL: .mockMXCAvatar)
let clientProxy = ClientProxyMock(.init())
clientProxy.profileForReturnValue = .success(profile)
@@ -62,7 +62,7 @@ struct UserProfileScreenViewModelTests {
@Test
func startingDmWithUnknownUserFetchesIdentity() async throws {
let userIndicatorController = UserIndicatorControllerMock()
let profile = UserProfileProxy.mockAlice
let clientProxy = ClientProxyMock(.init())
@@ -82,7 +82,7 @@ struct UserProfileScreenViewModelTests {
try await waitForMemberToLoad.fulfill()
let deferred = deferFulfillment(context.observe(\.viewState.bindings).compactMap(\.inviteConfirmationUser), timeout: .seconds(5)) { $0.isUnknown }
context.send(viewAction: .openDirectChat)
try await deferred.fulfill()
}
@@ -51,7 +51,7 @@ struct UserSessionFlowCoordinatorTests {
userIndicatorController = UserIndicatorControllerMock()
let appSettings = AppSettings.volatile()
let flowParameters = CommonFlowParameters(userSession: UserSessionMock(.init(clientProxy: clientProxy)),
bugReportService: BugReportServiceMock(.init()),
elementCallService: ElementCallServiceMock(.init()),
@@ -107,7 +107,7 @@ struct VoiceMessageMediaManagerTests {
let loadedFile = URL("/some/url/loaded_file")
// URL representing the final cached file
let cachedConvertedFileURL = URL("/some/url/cached_converted_file")
// Check if the file is not already present in cache
voiceMessageCache.fileURLForReturnValue = nil
let mediaSource = try MediaSourceProxy(url: someURL, mimeType: audioOGGMimeType)
@@ -129,7 +129,7 @@ struct VoiceMessageMediaManagerTests {
// The returned URL must point to the cached converted file
#expect(url == cachedConvertedFileURL)
}
@Test
mutating func loadVoiceMessageFromSourceMultipleCalls() async throws {
// URL representing the file loaded by the media provider
@@ -149,7 +149,7 @@ struct VoiceMessageMediaManagerTests {
let audioConverter = AudioConverterMock()
mediaProvider.loadFileFromSourceFilenameReturnValue = .success(MediaFileHandleProxy.unmanaged(url: loadedFile))
voiceMessageMediaManager = VoiceMessageMediaManager(mediaProvider: mediaProvider,
voiceMessageCache: voiceMessageCache,
audioConverter: audioConverter)
@@ -159,10 +159,10 @@ struct VoiceMessageMediaManagerTests {
let url = try await voiceMessageMediaManager.loadVoiceMessageFromSource(mediaSource, body: nil)
#expect(url == cachedConvertedFileURL)
}
// The file must have been converted only once
#expect(audioConverter.convertToMPEG4AACSourceURLDestinationURLCallsCount == 1)
// The converted file must have been cached only once
#expect(voiceMessageCache.cacheMediaSourceUsingMoveCallsCount == 1)
}
@@ -20,17 +20,17 @@ struct VoiceMessageRecorderTests {
private var audioRecorderActions: AnyPublisher<AudioRecorderAction, Never> {
audioRecorderActionsSubject.eraseToAnyPublisher()
}
private var mediaPlayerProvider: MediaPlayerProviderMock!
private var audioConverter: AudioConverterMock!
private var voiceMessageCache: VoiceMessageCacheMock!
private var audioPlayer: AudioPlayerMock!
private var audioPlayerActionsSubject: PassthroughSubject<AudioPlayerAction, Never> = .init()
private var audioPlayerActions: AnyPublisher<AudioPlayerAction, Never> {
audioPlayerActionsSubject.eraseToAnyPublisher()
}
private let recordingURL = URL("/some/url")
init() async throws {
@@ -58,7 +58,7 @@ struct VoiceMessageRecorderTests {
private func setRecordingComplete() async throws {
audioRecorder.audioFileURL = recordingURL
audioRecorder.currentTime = 5
let deferred = deferFulfillment(voiceMessageRecorder.actions) { action in
switch action {
case .didStopRecording(_, let url) where url == recordingURL:
@@ -104,14 +104,14 @@ struct VoiceMessageRecorderTests {
// The recording audio file must have been deleted
#expect(audioRecorder.deleteRecordingCalled)
}
@Test
func deleteRecording() async {
await voiceMessageRecorder.deleteRecording()
// The recording audio file must have been deleted
#expect(audioRecorder.deleteRecordingCalled)
}
@Test
func startPlaybackNoPreview() async {
guard case .failure(.previewNotAvailable) = await voiceMessageRecorder.startPlayback() else {
@@ -139,10 +139,10 @@ struct VoiceMessageRecorderTests {
@Test
func pausePlayback() async throws {
try await setRecordingComplete()
_ = await voiceMessageRecorder.startPlayback()
#expect(voiceMessageRecorder.previewAudioPlayerState?.isAttached == true)
voiceMessageRecorder.pausePlayback()
#expect(audioPlayer.pauseCalled)
}
@@ -151,7 +151,7 @@ struct VoiceMessageRecorderTests {
func resumePlayback() async throws {
try await setRecordingComplete()
audioPlayer.playbackURL = recordingURL
guard case .success = await voiceMessageRecorder.startPlayback() else {
Issue.record("Playback should start")
return
@@ -161,11 +161,11 @@ struct VoiceMessageRecorderTests {
#expect(!audioPlayer.loadSourceURLPlaybackURLAutoplayCalled)
#expect(audioPlayer.playCalled)
}
@Test
func stopPlayback() async throws {
try await setRecordingComplete()
_ = await voiceMessageRecorder.startPlayback()
#expect(voiceMessageRecorder.previewAudioPlayerState?.isAttached == true)
@@ -177,10 +177,10 @@ struct VoiceMessageRecorderTests {
@Test
func seekPlayback() async throws {
try await setRecordingComplete()
_ = await voiceMessageRecorder.startPlayback()
#expect(voiceMessageRecorder.previewAudioPlayerState?.isAttached == true)
await voiceMessageRecorder.seekPlayback(to: 0.4)
#expect(audioPlayer.seekToReceivedProgress == 0.4)
}
@@ -358,7 +358,7 @@ struct VoiceMessageRecorderTests {
func audioRecorderActionHandling_didStopRecording() async throws {
audioRecorder.audioFileURL = recordingURL
audioRecorder.currentTime = 5
let deferred = deferFulfillment(voiceMessageRecorder.actions) { action in
switch action {
case .didStopRecording(_, let url) where url == recordingURL: