mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-02 20:39:24 +00:00
Run SwiftFormat.
This commit is contained in:
@@ -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()
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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!
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user