Implemented adding the room to the space after selection, and the editableSpace proxied API. Also updated and added tests.

This commit is contained in:
Mauro Romito
2026-01-21 14:58:03 +01:00
committed by Mauro
parent 07243be949
commit 9dead7ed55
50 changed files with 406 additions and 83 deletions
@@ -17100,6 +17100,70 @@ class SpaceServiceProxyMock: SpaceServiceProxyProtocol, @unchecked Sendable {
return joinedParentsChildIDReturnValue
}
}
//MARK: - editableSpaces
var editableSpacesUnderlyingCallsCount = 0
var editableSpacesCallsCount: Int {
get {
if Thread.isMainThread {
return editableSpacesUnderlyingCallsCount
} else {
var returnValue: Int? = nil
DispatchQueue.main.sync {
returnValue = editableSpacesUnderlyingCallsCount
}
return returnValue!
}
}
set {
if Thread.isMainThread {
editableSpacesUnderlyingCallsCount = newValue
} else {
DispatchQueue.main.sync {
editableSpacesUnderlyingCallsCount = newValue
}
}
}
}
var editableSpacesCalled: Bool {
return editableSpacesCallsCount > 0
}
var editableSpacesUnderlyingReturnValue: [SpaceServiceRoomProtocol]!
var editableSpacesReturnValue: [SpaceServiceRoomProtocol]! {
get {
if Thread.isMainThread {
return editableSpacesUnderlyingReturnValue
} else {
var returnValue: [SpaceServiceRoomProtocol]? = nil
DispatchQueue.main.sync {
returnValue = editableSpacesUnderlyingReturnValue
}
return returnValue!
}
}
set {
if Thread.isMainThread {
editableSpacesUnderlyingReturnValue = newValue
} else {
DispatchQueue.main.sync {
editableSpacesUnderlyingReturnValue = newValue
}
}
}
}
var editableSpacesClosure: (() async -> [SpaceServiceRoomProtocol])?
func editableSpaces() async -> [SpaceServiceRoomProtocol] {
editableSpacesCallsCount += 1
if let editableSpacesClosure = editableSpacesClosure {
return await editableSpacesClosure()
} else {
return editableSpacesReturnValue
}
}
//MARK: - addChild
var addChildToUnderlyingCallsCount = 0
@@ -111,6 +111,7 @@ extension [SpaceServiceRoomProtocol] {
childrenCount: 1,
joinedMembersCount: 500,
canonicalAlias: "#the-foundation:matrix.org",
joinRule: .private,
state: .joined)),
SpaceServiceRoomMock(.init(id: "space2",
name: "The Second Foundation",
@@ -16,6 +16,7 @@ extension SpaceServiceProxyMock {
var topLevelSpaces: [SpaceServiceRoomProtocol] = []
var spaceFilters: [SpaceServiceFilter] = []
var joinedParentSpaces: [SpaceServiceRoomProtocol] = []
var editableSpaces: [SpaceServiceRoomProtocol] = []
var spaceRoomLists: [String: SpaceRoomListProxyMock] = [:]
var leaveSpaceRooms: [LeaveSpaceRoom] = []
}
@@ -27,6 +28,7 @@ extension SpaceServiceProxyMock {
spaceFilterPublisher = .init(configuration.spaceFilters)
joinedParentsChildIDReturnValue = .success(configuration.joinedParentSpaces)
editableSpacesReturnValue = configuration.editableSpaces
spaceRoomListSpaceIDClosure = { spaceID in
if let spaceRoomList = configuration.spaceRoomLists[spaceID] {
.success(spaceRoomList)
@@ -31,7 +31,7 @@ struct CreateRoomScreenViewState: BindableState {
let isKnockingFeatureEnabled: Bool
let canSelectSpace: Bool
var aliasLocalPart: String
var topLevelSpaces: [SpaceServiceRoomProtocol]
var editableSpaces: [SpaceServiceRoomProtocol] = []
var bindings: CreateRoomScreenViewStateBindings
var avatarMediaInfo: MediaInfo? {
didSet {
@@ -61,11 +61,16 @@ class CreateRoomScreenViewModel: CreateRoomScreenViewModelType, CreateRoomScreen
isKnockingFeatureEnabled: appSettings.knockingEnabled,
canSelectSpace: canSelectSpace,
aliasLocalPart: roomAliasNameFromRoomDisplayName(roomName: ""),
topLevelSpaces: canSelectSpace ? userSession.clientProxy.spaceService.topLevelSpacesPublisher.value : [],
bindings: bindings),
mediaProvider: userSession.mediaProvider)
setupBindings()
if canSelectSpace {
Task {
state.editableSpaces = await userSession.clientProxy.spaceService.editableSpaces()
}
}
}
// MARK: - Public
@@ -195,15 +200,6 @@ class CreateRoomScreenViewModel: CreateRoomScreenViewModelType, CreateRoomScreen
}
}
.store(in: &cancellables)
if state.canSelectSpace {
userSession
.clientProxy
.spaceService
.topLevelSpacesPublisher
.weakAssign(to: \.state.topLevelSpaces, on: self)
.store(in: &cancellables)
}
}
private func createRoom() async {
@@ -277,15 +273,21 @@ class CreateRoomScreenViewModel: CreateRoomScreenViewModelType, CreateRoomScreen
var spaceRoomListProxy: SpaceRoomListProxyProtocol?
if state.isSpace {
switch await userSession.clientProxy.spaceService.spaceRoomList(spaceID: roomProxy.id) {
switch await userSession.clientProxy.spaceService.spaceRoomList(spaceID: roomID) {
case .success(let value):
spaceRoomListProxy = value
case .failure:
MXLog.error("Failed to get space room list for newly created space with id: \(roomProxy.id)")
MXLog.error("Failed to get space room list for newly created space with id: \(roomID)")
userIndicatorController.submitIndicator(.init(title: L10n.errorUnknown))
}
}
if let selectedSpace = state.bindings.selectedSpace,
case .failure = await userSession.clientProxy.spaceService.addChild(roomID, to: selectedSpace.id) {
MXLog.error("Failed to add the created room with id: \(roomID) to the space with id: \(selectedSpace.id)")
userIndicatorController.submitIndicator(.init(title: L10n.errorUnknown))
}
actionsSubject.send(.createdRoom(roomProxy, spaceRoomListProxy))
case .failure:
state.bindings.alertInfo = AlertInfo(id: .failedCreatingRoom,
@@ -236,8 +236,11 @@ struct CreateRoomScreen: View {
} else {
ListRow(label: .default(title: L10n.screenCreateRoomSpaceSelectionNoSpaceTitle,
description: L10n.screenCreateRoomSpaceSelectionNoSpaceDescription,
icon: \.home,
iconAlignment: .top),
icon: CompoundIcon(\.homeSolid, size: .small, relativeTo: .body)
.foregroundColor(.compound.iconPrimary)
.background(.compound.bgSubtleSecondary)
.scaledFrame(size: 32)
.clipAvatar(isSpace: true, size: 32)),
kind: .navigationLink {
context.showSpaceSelectionSheet = true
})
@@ -446,6 +449,56 @@ struct CreateRoom_Previews: PreviewProvider, TestablePreview {
viewModel.context.send(viewAction: .updateAliasLocalPart("existing"))
return viewModel
}()
static let selectedSpaceViewModel = {
AppSettings.resetAllSettings()
let mockedSpace = SpaceServiceRoomMock(.init(name: "Awesome Space", isSpace: true, joinRule: .private))
let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userID: "@userid:example.com"))))
return CreateRoomScreenViewModel(isSpace: false,
spaceSelectionMode: .selected(mockedSpace),
shouldShowCancelButton: false,
userSession: userSession,
analytics: ServiceLocator.shared.analytics,
userIndicatorController: UserIndicatorControllerMock(),
appSettings: ServiceLocator.shared.settings)
}()
static let selectedSpaceWithListViewModel = {
let clientProxy = ClientProxyMock(.init(userID: "@userid:example.com"))
let spaces = [SpaceServiceRoomProtocol].mockJoinedSpaces2
clientProxy.spaceService = SpaceServiceProxyMock(.init(editableSpaces: spaces))
let userSession = UserSessionMock(.init(clientProxy: clientProxy))
let viewModel = CreateRoomScreenViewModel(isSpace: false,
spaceSelectionMode: .list,
shouldShowCancelButton: false,
userSession: userSession,
analytics: ServiceLocator.shared.analytics,
userIndicatorController: UserIndicatorControllerMock(),
appSettings: ServiceLocator.shared.settings)
viewModel.context.selectedSpace = spaces[0]
return viewModel
}()
static let selectedSpaceWithAskToJoinViewModel = {
AppSettings.resetAllSettings()
let appSettings = AppSettings()
appSettings.knockingEnabled = true
let mockedSpace = SpaceServiceRoomMock(.init(name: "Awesome Space", isSpace: true, joinRule: .private))
let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userID: "@userid:example.com"))))
let viewModel = CreateRoomScreenViewModel(isSpace: false,
spaceSelectionMode: .selected(mockedSpace),
shouldShowCancelButton: false,
userSession: userSession,
analytics: ServiceLocator.shared.analytics,
userIndicatorController: UserIndicatorControllerMock(),
appSettings: appSettings)
viewModel.context.selectedAccessType = .askToJoinWithSpaceMembers
return viewModel
}()
static var previews: some View {
NavigationStack {
@@ -491,5 +544,20 @@ struct CreateRoom_Previews: PreviewProvider, TestablePreview {
}
.snapshotPreferences(expect: publicRoomExistingAliasViewModel.context.$viewState.map { !$0.aliasErrors.isEmpty })
.previewDisplayName("Create Public Room, existing alias")
NavigationStack {
CreateRoomScreen(context: selectedSpaceViewModel.context)
}
.previewDisplayName("Create Room with already selected Space")
NavigationStack {
CreateRoomScreen(context: selectedSpaceWithListViewModel.context)
}
.previewDisplayName("Create Room with a selected Space from the list")
NavigationStack {
CreateRoomScreen(context: selectedSpaceWithAskToJoinViewModel.context)
}
.previewDisplayName("Create Knockable Room with already selected Space")
}
}
@@ -26,12 +26,16 @@ struct CreateRoomSpaceSelectionSheet: View {
Section {
ListRow(label: .default(title: L10n.screenCreateRoomSpaceSelectionNoSpaceTitle,
description: L10n.screenCreateRoomSpaceSelectionNoSpaceDescription,
icon: \.home),
icon: CompoundIcon(\.homeSolid, size: .small, relativeTo: .body)
.foregroundColor(.compound.iconPrimary)
.background(.compound.bgSubtleSecondary)
.scaledFrame(size: 32)
.clipAvatar(isSpace: true, size: 32)),
kind: .selection(isSelected: context.selectedSpace == nil) {
context.selectedSpace = nil
dismiss()
})
ForEach(context.viewState.topLevelSpaces, id: \.id) { space in
ForEach(context.viewState.editableSpaces, id: \.id) { space in
ListRow(label: .avatar(title: space.name,
description: space.canonicalAlias,
icon: RoomAvatarImage(avatar: space.avatar,
@@ -66,7 +70,7 @@ struct CreateRoomSpaceSelectionSheet: View {
struct CreateRoomSpaceSelectionSheet_Previews: PreviewProvider, TestablePreview {
static let viewModel = {
let clientProxy = ClientProxyMock(.init(userID: "@userid:example.com"))
clientProxy.spaceService = SpaceServiceProxyMock(.init(topLevelSpaces: .mockJoinedSpaces2))
clientProxy.spaceService = SpaceServiceProxyMock(.init(editableSpaces: .mockJoinedSpaces2))
let userSession = UserSessionMock(.init(clientProxy: clientProxy))
return CreateRoomScreenViewModel(isSpace: false,
@@ -80,5 +84,6 @@ struct CreateRoomSpaceSelectionSheet_Previews: PreviewProvider, TestablePreview
static var previews: some View {
CreateRoomSpaceSelectionSheet(context: viewModel.context)
.snapshotPreferences(expect: viewModel.context.$viewState.map { $0.editableSpaces.count > 0 })
}
}
@@ -77,6 +77,10 @@ class SpaceServiceProxy: SpaceServiceProxyProtocol {
}
}
func editableSpaces() async -> [SpaceServiceRoomProtocol] {
await spaceService.editableSpaces().map(SpaceServiceRoom.init)
}
func addChild(_ childID: String, to spaceID: String) async -> Result<Void, SpaceServiceProxyError> {
do {
return try await .success(spaceService.addChildToSpace(childId: childID, spaceId: spaceID))
@@ -94,7 +98,7 @@ class SpaceServiceProxy: SpaceServiceProxyProtocol {
return .failure(.sdkError(error))
}
}
// MARK: - Private
private func handleSpaceListUpdates(_ updates: [SpaceListUpdate]) {
@@ -52,6 +52,8 @@ protocol SpaceServiceProxyProtocol {
func leaveSpace(spaceID: String) async -> Result<LeaveSpaceHandleProxy, SpaceServiceProxyError>
/// Returns all the parent spaces of a child that user has joined.
func joinedParents(childID: String) async -> Result<[SpaceServiceRoomProtocol], SpaceServiceProxyError>
/// Returns all the parent spaces that can be edited by the user
func editableSpaces() async -> [SpaceServiceRoomProtocol]
/// Adds a room (or space) as a child of another space.
func addChild(_ childID: String, to spaceID: String) async -> Result<Void, SpaceServiceProxyError>
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:aa327c6149879d4aa96fa4c5574bac7486fc6abfaa003ae74596c8b88adf6b0b
size 156228
oid sha256:cd25013fbbabd76733d42f38aeabefbe579800c005892e1201b65d894dba3a71
size 165592
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:704b115de7bad6d64ea253ea261a8812e536622dc32280a11e91a3bdc9473340
size 186268
oid sha256:2a4261ad92874c8d26ff1030dd9dc0b40e4f49c75f535cc4116b56b72a12764f
size 200632
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:66f4d91f70ce033d8c6553479ecf7471d53ffdb181986cea4c9032701a595c25
size 106657
oid sha256:9c848aa3b95b5a7d2bcd21ff7cb84fadd32cfcb7fad9d02a267d7eb20b5cd975
size 114307
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0c355007d6ac8f669a701f0ef534e6e381895722d3c495be9e4ef49d20a7de79
size 143489
oid sha256:b6f0109d908c6f92b2066be1ec631366d6fe170304eee39716c60da61b9a50e8
size 134090
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:023c513c47df068c3bc47b4b06f028e630048d7289636dc9b9dac1433abcf442
size 139154
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e35b0f0e56364235d60bd8688b7272664ce6c294eb0642d51967d9138289715c
size 165021
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:2482a63567a5a2ccb6f415e3dca4c05f697affecb9fc05e160c69c17c6020b9e
size 89586
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:409ca12c1ad636585be6fef0e1a25cdc17a57a1af00358131ff74995c65e0a26
size 120101
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:31efa5cc91a3b0a623056e85e8d5e0c6544a464d959b29ef771e2c9fa2843d7e
size 156446
oid sha256:783ef584086fb642ea70add67521b336e4bcf48e87d67bbc321bc05cb8801341
size 165850
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e638ad86f90e438978096a9fc928f85b6ca0d883f02221978307fe3068e20e35
size 188066
oid sha256:353482a407588083863c271a0da9fa000e0b2f28db930a1ac355c995377d9cda
size 202463
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:9b51dfc640af59dd5b3564148d48a1267e9423b1c77fb02edc4607d91d10910b
size 108742
oid sha256:d6709be30e596cf5ab8a7a36be93ca3710b801e290c0aad8756dd5002fae6cba
size 116153
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:8b8b277cc422ac3f6fba5b7b555e42c18d2b6ecde65a36eba6c4b63279fb80b5
size 145650
oid sha256:d478eac4ccdc9e70cb83450b771ec600c1aa944ee246cf2de35a76f34fdba02e
size 148446
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b71055f825775861294542ae166fbe7013800fc3672ce4d39ab4419716eedf45
size 141129
oid sha256:857dac4f0f576d660c86a344a754ae948f1c87344ea02c983ab90f81fc67f2bb
size 150715
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d2b85ce0989c5e068e489de22275aeaf723e51fe6b18cb304d16b831e8e8f368
size 164236
oid sha256:a8ba3dcce7b0b2b1f55a795a0f701882a787b8f2ef8b7cf61aef21fa19b2aff7
size 178610
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a5adc15a61b998447a66a4696b59f89413381583c4455be02b1f28594701b986
size 89299
oid sha256:41b047f803117538ea8bd7cdc15464d825d3e86226ceab49fd7925fa8ca53506
size 98304
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:5823312ac25afe1b349f8eda8807299d6c30656e961d387c01e544eda40e6c4e
size 116112
oid sha256:704cc86d9b1ee34f09b9327d935efa3e40654381e4bbbe510167f54ec44afb7f
size 127362
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:400c0c0ed48ced1122851b21a6d3e9e48f0985635711fec7e4abe8660994a6b1
size 159870
oid sha256:2629190bbf8a4101ac0a3cc07ce246d10727c043f6b691c15bdb727ae380f04e
size 169245
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:575f32a8b119f2ffbaf6e234b9ea23366850ea245f3f63403a9ba0a2691a2a3b
size 195889
oid sha256:5f93aab61df326b141ce7ceebde1ff99f0b7fb82610c1c45d7ed7c6a1d636439
size 210144
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:9002a2ad841596a8e206326fa282b131b750cc556e57a8b8fbf355318106e6ac
size 110759
oid sha256:b75b9be2f855101eb35fd48c8219b4dcfc4cd3bcc850683297b5ef6aec2f0a6b
size 118048
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:fab82b9e91cb4b3bad0a3bd13e1b2077a992473889ddb62297d0c902d724e104
size 150819
oid sha256:cdfac096c01f4cb5ea50c834ecd573d3761a77e20fd71e14ce22368beef01180
size 147952
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:9db61c6bde46ef08cfd7d1aceb50f379544053024ac4633d1234c863c98c066a
size 123222
oid sha256:6d19bc6c512bc48a5493b1e3eb8785c33b165daa51a529449c674fed1287ee25
size 132860
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e287da07572fd2bd3424894537844637e57629235b372108a8d99ae1b0994b1b
size 140788
oid sha256:81c651fb4c443c1442fc6b8fccd168e901fb16f994428c44951f57f187a1a38e
size 155183
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:8b820693bf00b97aaa0a797808106267ce741e495bba57936de8082dc4745bbf
size 73369
oid sha256:a0ae349c1658b8ff32004e524ff2f16e31c013dae9e6ea9e4a6e3bd0e3756e64
size 81447
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:ff2841d40e79e09cc472837c7ff350082d6d60894539302056c0d20f645264b1
size 90112
oid sha256:064a83888029fc47666e0b110fc4260e436499aba4a527c02bfa9493b889a6fa
size 100906
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:11421d65287250704b17af8ff783882e61e524f1ad7dd7e332bb308a9fa2b8a0
size 148258
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7e45145df3773cfb4f0852576406e6fb9c1ae168539dedaccb000df45681b5af
size 168502
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d90ac6bdafa18434682cf09e1434fe73358acea095b388902bbc06a43eb4355d
size 95080
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:2b46eb0c44ad26ec12798e0e6a55295e11374141766b856f4eca29b67b519578
size 115710
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6cd61bb621776260dbb0d91621b0bfaa1aa6d5c19511771fa55b3237130b10ad
size 126389
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:1406d5b6cff0b0b73f1b012a58ce8bb306de648d1bfff626fbf68ec14cc86301
size 145450
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:9aff42b3a7cf336f604a40d5f9c5f87aedac8e7656cb9429efb2dbec6ef0738e
size 75993
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7830c4f7a205182d1fc62ddedaa79098b869af45366fbf49de08d42bad646a24
size 95487
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:dcbee0919af7296c78578076842b77cea64add28137e6d6bf27e1837563c9eab
size 156762
oid sha256:d4c40aa821fba074dc2c45d68dad1ea5c4133209b940321a8126ae8da4af8b87
size 166423
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:dd2792d7127bb9e498a814189fb5c5dfa2b270dd333cea7623ea2b9905ee981f
size 174527
oid sha256:7ef469565f325404cbbe1ccd024681bf7779a74055b34402666f31df309163c0
size 188779
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d9d45b7c4d6aebb9c72558ed440ee099c756eb6b0196a5657d7da85557853d39
size 105940
oid sha256:a9cb09216e7c5a198c106c2654a34f449e74d536129256fde1ad9a06bb9063c3
size 114073
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3be54af6b52da9441294266c11a08bcaf85abfd2005e6ecc795e6e2d1b9b6cf6
size 122952
oid sha256:f06e5655fe4409ddc5eed23ffec370d3e9bb6e95d5cf60dfcaa8af1a86ef6135
size 133808
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:30039d5e15ac969e75c966de760a6e3d28659c29a06555c335f6ba8ab4bd0055
size 108672
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:47977fb6d9fb4357cf81cf6724a1a54102ee949a5dd4c4e0fc1ef53160f87328
size 111545
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c17b4ed900e67eab1d9ab41496c4dd228c1ddd5aa6ac80bb16d5d99e1c02c884
size 65065
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:14ab4d7d5890e6838282885e34d232e520a1ce631eae54d88c09b4cc0fc34700
size 67699
+143 -16
View File
@@ -15,6 +15,7 @@ import XCTest
class CreateRoomScreenViewModelTests: XCTestCase {
var viewModel: CreateRoomScreenViewModelProtocol!
var clientProxy: ClientProxyMock!
var spaceService: SpaceServiceProxyMock!
var userSession: UserSessionMock!
private let usersSubject = CurrentValueSubject<[UserProfileProxy], Never>([])
@@ -23,39 +24,31 @@ class CreateRoomScreenViewModelTests: XCTestCase {
viewModel.context
}
override func setUpWithError() throws {
clientProxy = ClientProxyMock(.init(userIDServerName: "matrix.org", userID: "@a:b.com"))
clientProxy.roomForIdentifierClosure = { roomID in .joined(JoinedRoomProxyMock(.init(id: roomID))) }
userSession = UserSessionMock(.init(clientProxy: clientProxy))
ServiceLocator.shared.settings.knockingEnabled = true
let viewModel = CreateRoomScreenViewModel(isSpace: false,
spaceSelectionMode: .list,
shouldShowCancelButton: false,
userSession: userSession,
analytics: ServiceLocator.shared.analytics,
userIndicatorController: UserIndicatorControllerMock(),
appSettings: ServiceLocator.shared.settings)
self.viewModel = viewModel
}
override func tearDown() {
AppSettings.resetAllSettings()
viewModel = nil
clientProxy = nil
spaceService = nil
userSession = nil
}
func testDefaultSecurity() {
func testDefaultState() {
setup()
XCTAssertEqual(context.viewState.bindings.selectedAccessType, .private)
XCTAssertNil(context.selectedSpace)
XCTAssertEqual(context.viewState.availableAccessTypes, [.public, .askToJoin, .private])
XCTAssertTrue(context.viewState.canSelectSpace)
}
func testCreateRoomRequirements() {
setup()
XCTAssertFalse(context.viewState.canCreateRoom)
context.send(viewAction: .updateRoomName("A"))
XCTAssertTrue(context.viewState.canCreateRoom)
}
func testCreateRoom() async throws {
setup()
// Given a form with a blank topic.
context.send(viewAction: .updateRoomName("A"))
context.roomTopic = ""
@@ -76,9 +69,11 @@ class CreateRoomScreenViewModelTests: XCTestCase {
XCTAssertEqual(clientProxy.createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartReceivedArguments?.name, "A")
XCTAssertNil(clientProxy.createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartReceivedArguments?.topic,
"The topic should be sent as nil when it is empty.")
XCTAssertEqual(clientProxy.createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartReceivedArguments?.accessType, .private)
}
func testCreateSpace() async throws {
setup(isSpace: true)
clientProxy = ClientProxyMock(.init(userIDServerName: "matrix.org",
userID: "@a:b.com",
spaceServiceConfiguration: .init(spaceRoomLists: ["1": .init()])))
@@ -116,9 +111,11 @@ class CreateRoomScreenViewModelTests: XCTestCase {
XCTAssertEqual(clientProxy.createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartReceivedArguments?.name, "A")
XCTAssertNil(clientProxy.createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartReceivedArguments?.topic,
"The topic should be sent as nil when it is empty.")
XCTAssertEqual(clientProxy.createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartReceivedArguments?.accessType, .private)
}
func testCreateKnockingRoom() async {
setup()
context.send(viewAction: .updateRoomName("A"))
context.roomTopic = "B"
context.selectedAccessType = .askToJoin
@@ -139,6 +136,7 @@ class CreateRoomScreenViewModelTests: XCTestCase {
}
func testCreatePublicRoomFailsForInvalidAlias() async throws {
setup()
context.send(viewAction: .updateRoomName("A"))
context.roomTopic = "B"
context.selectedAccessType = .public
@@ -161,6 +159,7 @@ class CreateRoomScreenViewModelTests: XCTestCase {
}
func testCreatePublicRoomFailsForExistingAlias() async throws {
setup()
clientProxy.isAliasAvailableReturnValue = .success(false)
context.send(viewAction: .updateRoomName("A"))
context.roomTopic = "B"
@@ -192,6 +191,7 @@ class CreateRoomScreenViewModelTests: XCTestCase {
}
func testNameAndAddressSync() async {
setup()
context.selectedAccessType = .private
await Task.yield()
context.send(viewAction: .updateRoomName("abc"))
@@ -221,4 +221,131 @@ class CreateRoomScreenViewModelTests: XCTestCase {
XCTAssertEqual(context.viewState.aliasLocalPart, "hello-matrix!")
XCTAssertEqual(context.viewState.roomName, "Hello# Matrix!")
}
func testCreateRoomInASelectedSpaceFromTheList() async throws {
let spaces = [SpaceServiceRoomProtocol].mockJoinedSpaces2
setup()
// Given a form with a blank topic.
context.send(viewAction: .updateRoomName("A"))
context.roomTopic = ""
context.selectedAccessType = .public
XCTAssertTrue(context.viewState.canCreateRoom)
XCTAssertNil(context.selectedSpace)
XCTAssertEqual(context.viewState.availableAccessTypes, [.public, .askToJoin, .private])
XCTAssertTrue(context.viewState.canSelectSpace)
var deferred = deferFulfillment(context.$viewState) { viewState in
viewState.editableSpaces.map(\.id) == spaces.map(\.id)
}
try await deferred.fulfill()
context.selectedSpace = spaces[0]
deferred = deferFulfillment(context.$viewState) { viewState in
viewState.bindings.selectedSpace?.id == spaces[0].id &&
viewState.availableAccessTypes == [.spaceMembers, .askToJoinWithSpaceMembers, .private] &&
// The value should reset since the original one is not available anymore
viewState.bindings.selectedAccessType == .private
}
try await deferred.fulfill()
// When creating the room.
clientProxy.createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartReturnValue = .success("1")
let expectation = expectation(description: "Wait for the addChild function to be called")
spaceService.addChildToClosure = { roomID, spaceID in
defer { expectation.fulfill() }
XCTAssertEqual(roomID, "1")
XCTAssertEqual(spaceID, spaces[0].id)
return .success(())
}
let deferredAction = deferFulfillment(viewModel.actions) { action in
guard case .createdRoom(let roomProxy, nil) = action, roomProxy.id == "1" else { return false }
return true
}
context.send(viewAction: .createRoom)
await fulfillment(of: [expectation])
try await deferredAction.fulfill()
// Then the room should be created and a topic should not be set.
XCTAssertTrue(clientProxy.createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartCalled)
XCTAssertEqual(clientProxy.createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartReceivedArguments?.name, "A")
XCTAssertEqual(clientProxy.createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartReceivedArguments?.accessType, .private)
XCTAssertNil(clientProxy.createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartReceivedArguments?.topic,
"The topic should be sent as nil when it is empty.")
}
func testCreateRoomInAnAlreadySelectedSpace() async throws {
let space = SpaceServiceRoomMock(.init(isSpace: true, joinRule: .private))
setup(spacesSelectionMode: .selected(space))
// Given a form with a blank topic.
context.send(viewAction: .updateRoomName("A"))
context.roomTopic = ""
context.selectedAccessType = .spaceMembers
XCTAssertTrue(context.viewState.canCreateRoom)
XCTAssertEqual(context.selectedSpace?.id, space.id)
XCTAssertEqual(context.viewState.availableAccessTypes, [.spaceMembers, .askToJoinWithSpaceMembers, .private])
XCTAssertFalse(context.viewState.canSelectSpace)
// When creating the room.
clientProxy.createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartReturnValue = .success("1")
let expectation = expectation(description: "Wait for the addChild function to be called")
spaceService.addChildToClosure = { roomID, spaceID in
defer { expectation.fulfill() }
XCTAssertEqual(roomID, "1")
XCTAssertEqual(spaceID, space.id)
return .success(())
}
let deferredAction = deferFulfillment(viewModel.actions) { action in
guard case .createdRoom(let roomProxy, nil) = action, roomProxy.id == "1" else { return false }
return true
}
context.send(viewAction: .createRoom)
await fulfillment(of: [expectation])
try await deferredAction.fulfill()
// Then the room should be created and a topic should not be set.
XCTAssertTrue(clientProxy.createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartCalled)
XCTAssertEqual(clientProxy.createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartReceivedArguments?.name, "A")
XCTAssertEqual(clientProxy.createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartReceivedArguments?.accessType, .spaceMembers(spaceID: space.id))
XCTAssertNil(clientProxy.createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartReceivedArguments?.topic,
"The topic should be sent as nil when it is empty.")
}
func testCreateRoomInAnPublicSpaceAvailableTypes() async throws {
let space = SpaceServiceRoomMock(.init(isSpace: true, joinRule: .public))
setup(spacesSelectionMode: .selected(space))
// Given a form with a blank topic.
context.send(viewAction: .updateRoomName("A"))
context.roomTopic = ""
context.selectedAccessType = .spaceMembers
XCTAssertTrue(context.viewState.canCreateRoom)
XCTAssertEqual(context.selectedSpace?.id, space.id)
XCTAssertEqual(context.viewState.availableAccessTypes, [.public, .askToJoin, .private])
XCTAssertFalse(context.viewState.canSelectSpace)
}
private func setup(isSpace: Bool = false, spacesSelectionMode: CreateRoomScreenSpaceSelectionMode = .list) {
spaceService = SpaceServiceProxyMock(.init(editableSpaces: .mockJoinedSpaces2,
spaceRoomLists: ["1": .init()]))
clientProxy = ClientProxyMock(.init(userIDServerName: "matrix.org",
userID: "@a:b.com"))
clientProxy.spaceService = spaceService
clientProxy.roomForIdentifierClosure = { roomID in .joined(JoinedRoomProxyMock(.init(id: roomID))) }
userSession = UserSessionMock(.init(clientProxy: clientProxy))
ServiceLocator.shared.settings.knockingEnabled = true
let viewModel = CreateRoomScreenViewModel(isSpace: isSpace,
spaceSelectionMode: spacesSelectionMode,
shouldShowCancelButton: false,
userSession: userSession,
analytics: ServiceLocator.shared.analytics,
userIndicatorController: UserIndicatorControllerMock(),
appSettings: ServiceLocator.shared.settings)
self.viewModel = viewModel
}
}