Add alias to public room creation (#3450)

* added the address section

* updated code and strings

* syncing name and address

* improved code

* added a way to reset the state

* better documentation

* update strings

* handling the alias

* alias error state

* update strings

* error handling

* improved the error handling

* new preview tests, even if they do not work well

* improved tests

* unit tests

* pr comments and using the correct value

* fix

* pr comments

* to improve safety and control of the FF

* fixed a test

* updated tests

* update SDK
This commit is contained in:
Mauro
2024-11-08 15:49:20 +01:00
committed by GitHub
parent c567901ec5
commit e7d4ea0d35
39 changed files with 592 additions and 115 deletions
@@ -25,7 +25,7 @@ class CreateRoomScreenViewModelTests: XCTestCase {
override func setUpWithError() throws {
cancellables.removeAll()
clientProxy = ClientProxyMock(.init(userID: "@a:b.com"))
clientProxy = ClientProxyMock(.init(userIDServerName: "matrix.org", userID: "@a:b.com"))
userSession = UserSessionMock(.init(clientProxy: clientProxy))
let parameters = CreateRoomFlowParameters()
usersSubject.send([.mockAlice, .mockBob, .mockCharlie])
@@ -68,21 +68,25 @@ class CreateRoomScreenViewModelTests: XCTestCase {
func testCreateRoomRequirements() {
XCTAssertFalse(context.viewState.canCreateRoom)
context.roomName = "A"
context.send(viewAction: .updateRoomName("A"))
XCTAssertTrue(context.viewState.canCreateRoom)
}
func testCreateKnockingRoom() async {
context.roomName = "A"
context.send(viewAction: .updateRoomName("A"))
context.roomTopic = "B"
context.isRoomPrivate = false
// When setting the room as private we always reset the knocking state to the default value of false
// so we need to wait a main actor cycle to ensure the view state is updated
await Task.yield()
context.isKnockingOnly = true
XCTAssertTrue(context.viewState.canCreateRoom)
let expectation = expectation(description: "Wait for the room to be created")
clientProxy.createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLClosure = { _, _, isPrivate, isKnockingOnly, _, _ in
clientProxy.createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartClosure = { _, _, isPrivate, isKnockingOnly, _, _, localAliasPart in
XCTAssertTrue(isKnockingOnly)
XCTAssertFalse(isPrivate)
XCTAssertEqual(localAliasPart, "a")
defer { expectation.fulfill() }
return .success("")
}
@@ -90,14 +94,67 @@ class CreateRoomScreenViewModelTests: XCTestCase {
await fulfillment(of: [expectation])
}
func testCreatePublicRoomFailsForInvalidAlias() async throws {
context.send(viewAction: .updateRoomName("A"))
context.roomTopic = "B"
context.isRoomPrivate = false
// When setting the room as private we always reset the alias
// so we need to wait a main actor cycle to ensure the view state is updated
await Task.yield()
// we wait for the debounce to show the error
let deferred = deferFulfillment(context.$viewState) { viewState in
viewState.aliasErrors.contains(.invalidSymbols) && !viewState.canCreateRoom
}
context.send(viewAction: .updateAliasLocalPart("#:"))
try await deferred.fulfill()
// We also want to force the room creation in case the user may tap the button before the debounce
// blocked it
context.send(viewAction: .createRoom)
await Task.yield()
XCTAssertFalse(clientProxy.createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartCalled)
}
func testCreatePublicRoomFailsForExistingAlias() async throws {
clientProxy.isAliasAvailableReturnValue = .success(false)
context.send(viewAction: .updateRoomName("A"))
context.roomTopic = "B"
context.isRoomPrivate = false
// When setting the room as private we always reset the alias
// so we need to wait a main actor cycle to ensure the view state is updated
await Task.yield()
// we wait for the debounce to show the error
let deferred = deferFulfillment(context.$viewState) { viewState in
viewState.aliasErrors.contains(.alreadyExists) && !viewState.canCreateRoom
}
context.send(viewAction: .updateAliasLocalPart("abc"))
try await deferred.fulfill()
// We also want to force the room creation in case the user may tap the button before the debounce
// blocked it
let expectation = expectation(description: "Wait for the alias to be checked again")
clientProxy.isAliasAvailableClosure = { _ in
defer {
expectation.fulfill()
}
return .success(false)
}
context.send(viewAction: .createRoom)
await fulfillment(of: [expectation])
XCTAssertEqual(clientProxy.isAliasAvailableCallsCount, 2)
XCTAssertFalse(clientProxy.createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartCalled)
}
func testCreatePrivateRoomCantHaveKnockRule() async {
context.roomName = "A"
context.send(viewAction: .updateRoomName("A"))
context.roomTopic = "B"
context.isRoomPrivate = true
context.isKnockingOnly = true
context.send(viewAction: .createRoom)
let expectation = expectation(description: "Wait for the room to be created")
clientProxy.createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLClosure = { _, _, isPrivate, isKnockingOnly, _, _ in
clientProxy.createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartClosure = { _, _, isPrivate, isKnockingOnly, _, _, _ in
XCTAssertFalse(isKnockingOnly)
XCTAssertTrue(isPrivate)
expectation.fulfill()
@@ -105,4 +162,35 @@ class CreateRoomScreenViewModelTests: XCTestCase {
}
await fulfillment(of: [expectation])
}
func testNameAndAddressSync() async {
context.isRoomPrivate = true
await Task.yield()
context.send(viewAction: .updateRoomName("abc"))
XCTAssertEqual(context.viewState.aliasLocalPart, "abc")
XCTAssertEqual(context.viewState.roomName, "abc")
context.send(viewAction: .updateRoomName("DEF"))
XCTAssertEqual(context.viewState.roomName, "DEF")
XCTAssertEqual(context.viewState.aliasLocalPart, "def")
context.send(viewAction: .updateRoomName("a b c"))
XCTAssertEqual(context.viewState.aliasLocalPart, "a-b-c")
XCTAssertEqual(context.viewState.roomName, "a b c")
context.send(viewAction: .updateAliasLocalPart("hello-world"))
// This removes the sync
XCTAssertEqual(context.viewState.aliasLocalPart, "hello-world")
XCTAssertEqual(context.viewState.roomName, "a b c")
context.send(viewAction: .updateRoomName("Hello Matrix!"))
XCTAssertEqual(context.viewState.aliasLocalPart, "hello-world")
XCTAssertEqual(context.viewState.roomName, "Hello Matrix!")
// Deleting the whole name will restore the sync
context.send(viewAction: .updateRoomName(""))
XCTAssertEqual(context.viewState.aliasLocalPart, "")
XCTAssertEqual(context.viewState.roomName, "")
context.send(viewAction: .updateRoomName("Hello# Matrix!"))
XCTAssertEqual(context.viewState.aliasLocalPart, "hello-matrix!")
XCTAssertEqual(context.viewState.roomName, "Hello# Matrix!")
}
}