Fix various bugs in the moderation feature. (#2608)

* Fix search field string.

* Show your own user as an Admin when changing roles.

* Also show invited users when changing roles.

* Don't allow admin's to kick/ban other admins or themselves.

* Fix a bug when left members were counted as admins/moderators.

* Show when a member is pending.

* Add sections to the change role screen.
This commit is contained in:
Doug
2024-03-27 10:33:40 +00:00
committed by GitHub
parent bb725db6bb
commit e424a02a2b
51 changed files with 413 additions and 148 deletions
@@ -31,8 +31,10 @@ class RoomChangeRolesScreenViewModelTests: XCTestCase {
setupViewModel(mode: .administrator)
XCTAssertEqual(context.viewState.membersToPromote, [])
XCTAssertEqual(context.viewState.membersToDemote, [])
XCTAssertEqual(context.viewState.members, context.viewState.visibleMembers)
XCTAssertEqual(context.viewState.membersWithRole.count, 1)
XCTAssertEqual(context.viewState.administrators, context.viewState.visibleAdministrators)
XCTAssertEqual(context.viewState.moderators, context.viewState.visibleModerators)
XCTAssertEqual(context.viewState.users, context.viewState.visibleUsers)
XCTAssertEqual(context.viewState.membersWithRole.count, 2)
XCTAssertEqual(context.viewState.membersWithRole.first?.id, RoomMemberProxyMock.mockAdmin.userID)
XCTAssertFalse(context.viewState.hasChanges)
XCTAssertFalse(context.viewState.isSearching)
@@ -42,7 +44,9 @@ class RoomChangeRolesScreenViewModelTests: XCTestCase {
setupViewModel(mode: .moderator)
XCTAssertEqual(context.viewState.membersToPromote, [])
XCTAssertEqual(context.viewState.membersToDemote, [])
XCTAssertEqual(context.viewState.members, context.viewState.visibleMembers)
XCTAssertEqual(context.viewState.administrators, context.viewState.visibleAdministrators)
XCTAssertEqual(context.viewState.moderators, context.viewState.visibleModerators)
XCTAssertEqual(context.viewState.users, context.viewState.visibleUsers)
XCTAssertEqual(context.viewState.membersWithRole.count, 1)
XCTAssertEqual(context.viewState.membersWithRole.first?.id, RoomMemberProxyMock.mockModerator.userID)
XCTAssertFalse(context.viewState.hasChanges)
@@ -51,7 +55,7 @@ class RoomChangeRolesScreenViewModelTests: XCTestCase {
func testToggleUserOn() {
testInitialStateModerators()
guard let firstUser = context.viewState.members.first(where: { !context.viewState.isMemberSelected($0) }) else {
guard let firstUser = context.viewState.users.first(where: { !context.viewState.isMemberSelected($0) }) else {
XCTFail("There should be a regular user available to promote.")
return
}
@@ -150,7 +154,7 @@ class RoomChangeRolesScreenViewModelTests: XCTestCase {
// Given the change roles view model for moderators.
setupViewModel(mode: .moderator)
guard let firstUser = context.viewState.members.first(where: { !context.viewState.isMemberSelected($0) }),
guard let firstUser = context.viewState.users.first(where: { !context.viewState.isMemberSelected($0) }),
let existingModerator = context.viewState.membersWithRole.first else {
XCTFail("There should be a regular user and a moderator to begin with.")
return
@@ -175,7 +179,7 @@ class RoomChangeRolesScreenViewModelTests: XCTestCase {
setupViewModel(mode: .administrator)
XCTAssertNil(context.alertInfo)
guard let firstUser = context.viewState.members.first(where: { !context.viewState.isMemberSelected($0) }) else {
guard let firstUser = context.viewState.users.first(where: { !context.viewState.isMemberSelected($0) }) else {
XCTFail("There should be a regular user to begin with.")
return
}
@@ -126,8 +126,128 @@ class RoomMembersListScreenViewModelTests: XCTestCase {
XCTAssertEqual(viewModel.state.visibleJoinedMembers.count, 0)
}
func testKickMember() async throws {
func testSelectUserAsUser() async throws {
// Given the room list viewed as a regular user.
setup(with: .allMembers)
let deferred = deferFulfillment(context.$viewState) { !$0.visibleInvitedMembers.isEmpty }
try await deferred.fulfill()
// When tapping on another user in the list.
let memberDetailsAction = deferFulfillment(viewModel.actions) { $0.isSelectMember }
guard let user = viewModel.state.visibleJoinedMembers.first(where: { $0.role == .user && $0.id != RoomMemberProxyMock.mockMe.userID }) else {
XCTFail("Expected to find a regular user.")
return
}
context.send(viewAction: .selectMember(user))
// Then the member's details should be shown.
try await memberDetailsAction.fulfill()
XCTAssertNil(context.memberToManage)
}
func testSelectUserAsAdmin() async throws {
// Given the room list viewed as an admin.
setup(with: .allMembersAsAdmin)
var deferred = deferFulfillment(context.$viewState) { !$0.visibleInvitedMembers.isEmpty }
try await deferred.fulfill()
XCTAssertNil(context.memberToManage)
// When tapping on a user in the list.
deferred = deferFulfillment(context.$viewState) { $0.bindings.memberToManage != nil }
guard let user = viewModel.state.visibleJoinedMembers.first(where: { $0.role == .user && $0.id != RoomMemberProxyMock.mockMe.userID }) else {
XCTFail("Expected to find a regular user.")
return
}
context.send(viewAction: .selectMember(user))
try await deferred.fulfill()
// Then member management should be shown for that user.
XCTAssertEqual(context.memberToManage?.member, user)
XCTAssertEqual(context.memberToManage?.actions, [.kick, .ban])
}
func testSelectModeratorAsAdmin() async throws {
// Given the room list viewed as an admin.
setup(with: .allMembersAsAdmin)
var deferred = deferFulfillment(context.$viewState) { !$0.visibleInvitedMembers.isEmpty }
try await deferred.fulfill()
XCTAssertNil(context.memberToManage)
// When tapping on a moderator in the list.
deferred = deferFulfillment(context.$viewState) { $0.bindings.memberToManage != nil }
guard let moderator = viewModel.state.visibleJoinedMembers.first(where: { $0.role == .moderator }) else {
XCTFail("Expected to find a moderator.")
return
}
context.send(viewAction: .selectMember(moderator))
try await deferred.fulfill()
// Then member management should be shown for the moderator.
XCTAssertEqual(context.memberToManage?.member, moderator)
XCTAssertEqual(context.memberToManage?.actions, [.kick, .ban])
}
func testSelectAdminAsAdmin() async throws {
// Given the room list viewed as an admin.
setup(with: .allMembersAsAdmin)
let deferred = deferFulfillment(context.$viewState) { !$0.visibleInvitedMembers.isEmpty }
try await deferred.fulfill()
// When tapping on another administrator in the list.
let memberDetailsAction = deferFulfillment(viewModel.actions) { $0.isSelectMember }
guard let admin = viewModel.state.visibleJoinedMembers.first(where: { $0.role == .administrator && $0.id != RoomMemberProxyMock.mockMe.userID }) else {
XCTFail("Expected to find another admin.")
return
}
context.send(viewAction: .selectMember(admin))
// Then the administrator's details should be shown.
try await memberDetailsAction.fulfill()
XCTAssertNil(context.memberToManage)
}
func testSelectOwnMemberAsAdmin() async throws {
// Given the room list viewed as an admin.
setup(with: .allMembersAsAdmin)
let deferred = deferFulfillment(context.$viewState) { !$0.visibleInvitedMembers.isEmpty }
try await deferred.fulfill()
// When tapping on yourself in the list.
let memberDetailsAction = deferFulfillment(viewModel.actions) { $0.isSelectMember }
guard let ownMember = viewModel.state.visibleJoinedMembers.first(where: { $0.id == RoomMemberProxyMock.mockMe.userID }) else {
XCTFail("Expected to find own user admin.")
return
}
context.send(viewAction: .selectMember(ownMember))
// Then your member's details should be shown.
try await memberDetailsAction.fulfill()
XCTAssertNil(context.memberToManage)
}
func testSelectBannedMember() async throws {
// Given the room list viewed as an admin.
setup(with: .allMembersAsAdmin + RoomMemberProxyMock.mockBanned)
var deferred = deferFulfillment(context.$viewState) { !$0.visibleInvitedMembers.isEmpty }
try await deferred.fulfill()
XCTAssertNil(context.alertInfo)
// When tapping on a banned member in the list.
deferred = deferFulfillment(context.$viewState) { $0.bindings.alertInfo != nil }
guard let bannedMember = viewModel.state.visibleBannedMembers.first else {
XCTFail("Expected to find a banned user.")
return
}
context.send(viewAction: .selectMember(bannedMember))
// Then an alert should be shown to unban the user.
try await deferred.fulfill()
XCTAssertNil(context.memberToManage)
XCTAssertNotNil(context.alertInfo)
}
func testKickMember() async throws {
setup(with: .allMembersAsAdmin)
let deferred = deferFulfillment(context.$viewState) { !$0.visibleJoinedMembers.isEmpty }
try await deferred.fulfill()
@@ -140,7 +260,7 @@ class RoomMembersListScreenViewModelTests: XCTestCase {
}
func testBanMember() async throws {
setup(with: .allMembers)
setup(with: .allMembersAsAdmin)
let deferred = deferFulfillment(context.$viewState) { !$0.visibleJoinedMembers.isEmpty }
try await deferred.fulfill()
@@ -153,7 +273,7 @@ class RoomMembersListScreenViewModelTests: XCTestCase {
}
func testUnbanMember() async throws {
setup(with: .allMembers)
setup(with: .allMembersAsAdmin)
let deferred = deferFulfillment(context.$viewState) { !$0.visibleJoinedMembers.isEmpty }
try await deferred.fulfill()
@@ -166,6 +286,9 @@ class RoomMembersListScreenViewModelTests: XCTestCase {
}
private func setup(with members: [RoomMemberProxyMock]) {
AppSettings.resetAllSettings()
ServiceLocator.shared.settings.roomModerationEnabled = true
roomProxy = RoomProxyMock(with: .init(name: "test", members: members))
viewModel = .init(roomProxy: roomProxy,
mediaProvider: MockMediaProvider(),