mirror of
https://gitlab.opencode.de/bwi/bundesmessenger/clients/bundesmessenger-ios.git
synced 2026-04-25 02:52:45 +02:00
Move room admin condition to be usable in UnitTests and add tests
This commit is contained in:
+18
-4
@@ -62,20 +62,34 @@ extension MockCompletionSuggestionScreenState: RoomMembersProviderProtocol {
|
||||
}
|
||||
|
||||
extension MockCompletionSuggestionScreenState: CommandsProviderProtocol {
|
||||
var isRoomAdmin: Bool { false }
|
||||
|
||||
func fetchCommands(_ commands: @escaping ([CommandsProviderCommand]) -> Void) {
|
||||
commands([
|
||||
CommandsProviderCommand(name: "/ban",
|
||||
parametersFormat: "<user-id> [<reason>]",
|
||||
description: "Bans user with given id"),
|
||||
description: "Bans user with given id",
|
||||
requiresAdminPowerLevel: false),
|
||||
CommandsProviderCommand(name: "/invite",
|
||||
parametersFormat: "<user-id>",
|
||||
description: "Invites user with given id to current room"),
|
||||
description: "Invites user with given id to current room",
|
||||
requiresAdminPowerLevel: false),
|
||||
CommandsProviderCommand(name: "/join",
|
||||
parametersFormat: "<room-address>",
|
||||
description: "Joins room with given address"),
|
||||
description: "Joins room with given address",
|
||||
requiresAdminPowerLevel: false),
|
||||
CommandsProviderCommand(name: "/op",
|
||||
parametersFormat: "<user-id> <power-level>",
|
||||
description: "Define the power level of a user",
|
||||
requiresAdminPowerLevel: true),
|
||||
CommandsProviderCommand(name: "/deop",
|
||||
parametersFormat: "<user-id>",
|
||||
description: "Deops user with given id",
|
||||
requiresAdminPowerLevel: true),
|
||||
CommandsProviderCommand(name: "/me",
|
||||
parametersFormat: "<message>",
|
||||
description: "Displays action")
|
||||
description: "Displays action",
|
||||
requiresAdminPowerLevel: false)
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
+14
-10
@@ -207,6 +207,7 @@ private class CompletionSuggestionCoordinatorCommandProvider: CommandsProviderPr
|
||||
private let userID: String
|
||||
|
||||
var commands = MXKSlashCommand.allCases
|
||||
var isRoomAdmin = false
|
||||
|
||||
init(room: MXRoom, userID: String) {
|
||||
self.room = room
|
||||
@@ -218,21 +219,13 @@ private class CompletionSuggestionCoordinatorCommandProvider: CommandsProviderPr
|
||||
room.state { [weak self] state in
|
||||
guard let self, let powerLevels = state?.powerLevels else { return }
|
||||
|
||||
// Note: for now only filter out `/op` and `/deop` (same as Element-Web),
|
||||
// but we could use power level for ban/invite/etc to filter further.
|
||||
let adminOnlyCommands: [MXKSlashCommand] = [.setUserPowerLevel, .resetUserPowerLevel]
|
||||
let userPowerLevel = powerLevels.powerLevelOfUser(withUserID: self.userID)
|
||||
|
||||
if RoomPowerLevel(rawValue: userPowerLevel) != .admin {
|
||||
self.commands = self.commands.filter {
|
||||
!adminOnlyCommands.contains($0)
|
||||
}
|
||||
}
|
||||
isRoomAdmin = RoomPowerLevel(rawValue: userPowerLevel) == .admin
|
||||
}
|
||||
}
|
||||
|
||||
func fetchCommands(_ commands: @escaping ([CommandsProviderCommand]) -> Void) {
|
||||
commands(self.commands.map { CommandsProviderCommand(name: $0.cmd, parametersFormat: $0.parametersFormat, description: $0.description) })
|
||||
commands(self.commands.map { CommandsProviderCommand(name: $0.cmd, parametersFormat: $0.parametersFormat, description: $0.description, requiresAdminPowerLevel: $0.requiresAdminPowerLevel) })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -266,4 +259,15 @@ private extension MXKSlashCommand {
|
||||
return "Forces the current outbound group session in an encrypted room to be discarded"
|
||||
}
|
||||
}
|
||||
|
||||
// Note: for now only filter out `/op` and `/deop` (same as Element-Web),
|
||||
// but we could use power level for ban/invite/etc to filter further.
|
||||
var requiresAdminPowerLevel: Bool {
|
||||
switch self {
|
||||
case .setUserPowerLevel, .resetUserPowerLevel:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+10
-1
@@ -28,6 +28,7 @@ struct CommandsProviderCommand {
|
||||
let name: String
|
||||
let parametersFormat: String
|
||||
let description: String
|
||||
let requiresAdminPowerLevel: Bool
|
||||
}
|
||||
|
||||
class CompletionSuggestionUserID: NSObject {
|
||||
@@ -41,6 +42,7 @@ protocol RoomMembersProviderProtocol {
|
||||
}
|
||||
|
||||
protocol CommandsProviderProtocol {
|
||||
var isRoomAdmin: Bool { get }
|
||||
func fetchCommands(_ commands: @escaping ([CommandsProviderCommand]) -> Void)
|
||||
}
|
||||
|
||||
@@ -159,7 +161,7 @@ class CompletionSuggestionService: CompletionSuggestionServiceProtocol {
|
||||
commandProvider.fetchCommands { [weak self] commands in
|
||||
guard let self else { return }
|
||||
|
||||
self.suggestionItems = commands.map { command in
|
||||
self.suggestionItems = commands.filtered(isRoomAdmin: self.commandProvider.isRoomAdmin).map { command in
|
||||
CompletionSuggestionItem.command(value: CompletionSuggestionServiceCommandItem(
|
||||
name: command.name,
|
||||
parametersFormat: command.parametersFormat,
|
||||
@@ -190,6 +192,13 @@ extension Array where Element == RoomMembersProviderMember {
|
||||
}
|
||||
}
|
||||
|
||||
extension Array where Element == CommandsProviderCommand {
|
||||
func filtered(isRoomAdmin: Bool) -> Self {
|
||||
guard !isRoomAdmin else { return self }
|
||||
return filter { !$0.requiresAdminPowerLevel }
|
||||
}
|
||||
}
|
||||
|
||||
private enum SuggestionKey: Character {
|
||||
case at = "@"
|
||||
case slash = "/"
|
||||
|
||||
+100
-5
@@ -22,14 +22,18 @@ import XCTest
|
||||
class CompletionSuggestionServiceTests: XCTestCase {
|
||||
var service: CompletionSuggestionService!
|
||||
var canMentionRoom = false
|
||||
var isRoomAdmin = false
|
||||
|
||||
override func setUp() {
|
||||
service = CompletionSuggestionService(roomMemberProvider: self,
|
||||
commandProvider: self,
|
||||
shouldDebounce: false)
|
||||
canMentionRoom = false
|
||||
isRoomAdmin = false
|
||||
}
|
||||
|
||||
|
||||
// MARK: - User suggestions
|
||||
|
||||
func testAlice() {
|
||||
service.processTextMessage("@Al")
|
||||
XCTAssertEqual(service.items.value.first?.asUser?.displayName, "Alice")
|
||||
@@ -128,6 +132,85 @@ class CompletionSuggestionServiceTests: XCTestCase {
|
||||
// Then the completion for a room mention should be shown.
|
||||
XCTAssertEqual(service.items.value.first?.asUser?.userId, CompletionSuggestionUserID.room)
|
||||
}
|
||||
|
||||
// MARK: - Command suggestions
|
||||
|
||||
func testJoin() {
|
||||
service.processTextMessage("/jo")
|
||||
XCTAssertEqual(service.items.value.first?.asCommand?.name, "/join")
|
||||
|
||||
service.processTextMessage("/joi")
|
||||
XCTAssertEqual(service.items.value.first?.asCommand?.name, "/join")
|
||||
|
||||
service.processTextMessage("/join")
|
||||
XCTAssertEqual(service.items.value.first?.asCommand?.name, "/join")
|
||||
|
||||
service.processTextMessage("/oin")
|
||||
XCTAssertEqual(service.items.value.first?.asCommand?.name, "/join")
|
||||
}
|
||||
|
||||
func testInvite() {
|
||||
service.processTextMessage("/inv")
|
||||
XCTAssertEqual(service.items.value.first?.asCommand?.name, "/invite")
|
||||
|
||||
service.processTextMessage("/invite")
|
||||
XCTAssertEqual(service.items.value.first?.asCommand?.name, "/invite")
|
||||
|
||||
service.processTextMessage("/vite")
|
||||
XCTAssertEqual(service.items.value.first?.asCommand?.name, "/invite")
|
||||
}
|
||||
|
||||
func testMultipleResults() {
|
||||
service.processTextMessage("/in")
|
||||
XCTAssertEqual(
|
||||
service.items.value.compactMap { $0.asCommand?.name },
|
||||
["/invite", "/join"]
|
||||
)
|
||||
}
|
||||
|
||||
func testDoubleSlashDontTrigger() {
|
||||
service.processTextMessage("//")
|
||||
XCTAssertTrue(service.items.value.isEmpty)
|
||||
}
|
||||
|
||||
func testNonLeadingSlashCommandDontTrigger() {
|
||||
service.processTextMessage("test /joi")
|
||||
XCTAssertTrue(service.items.value.isEmpty)
|
||||
}
|
||||
|
||||
func testAdminCommandsAreNotAvailable() {
|
||||
isRoomAdmin = false
|
||||
|
||||
service.processTextMessage("/op")
|
||||
XCTAssertTrue(service.items.value.isEmpty)
|
||||
}
|
||||
|
||||
func testAdminCommandsAreAvailable() {
|
||||
isRoomAdmin = true
|
||||
|
||||
service.processTextMessage("/op")
|
||||
XCTAssertEqual(service.items.value.compactMap { $0.asCommand?.name }, ["/op", "/deop"])
|
||||
}
|
||||
|
||||
func testDisplayAllCommandsAsStandardUser() {
|
||||
isRoomAdmin = false
|
||||
|
||||
service.processTextMessage("/")
|
||||
XCTAssertEqual(
|
||||
service.items.value.compactMap { $0.asCommand?.name },
|
||||
["/ban", "/invite", "/join", "/me"]
|
||||
)
|
||||
}
|
||||
|
||||
func testDisplayAllCommandsAsAdmin() {
|
||||
isRoomAdmin = true
|
||||
|
||||
service.processTextMessage("/")
|
||||
XCTAssertEqual(
|
||||
service.items.value.compactMap { $0.asCommand?.name },
|
||||
["/ban", "/invite", "/join", "/op", "/deop", "/me"]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
extension CompletionSuggestionServiceTests: RoomMembersProviderProtocol {
|
||||
@@ -146,16 +229,28 @@ extension CompletionSuggestionServiceTests: CommandsProviderProtocol {
|
||||
commands([
|
||||
CommandsProviderCommand(name: "/ban",
|
||||
parametersFormat: "<user-id> [<reason>]",
|
||||
description: "Bans user with given id"),
|
||||
description: "Bans user with given id",
|
||||
requiresAdminPowerLevel: false),
|
||||
CommandsProviderCommand(name: "/invite",
|
||||
parametersFormat: "<user-id>",
|
||||
description: "Invites user with given id to current room"),
|
||||
description: "Invites user with given id to current room",
|
||||
requiresAdminPowerLevel: false),
|
||||
CommandsProviderCommand(name: "/join",
|
||||
parametersFormat: "<room-address>",
|
||||
description: "Joins room with given address"),
|
||||
description: "Joins room with given address",
|
||||
requiresAdminPowerLevel: false),
|
||||
CommandsProviderCommand(name: "/op",
|
||||
parametersFormat: "<user-id> <power-level>",
|
||||
description: "Define the power level of a user",
|
||||
requiresAdminPowerLevel: true),
|
||||
CommandsProviderCommand(name: "/deop",
|
||||
parametersFormat: "<user-id>",
|
||||
description: "Deops user with given id",
|
||||
requiresAdminPowerLevel: true),
|
||||
CommandsProviderCommand(name: "/me",
|
||||
parametersFormat: "<message>",
|
||||
description: "Displays action")
|
||||
description: "Displays action",
|
||||
requiresAdminPowerLevel: false)
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -34,13 +34,13 @@ struct CompletionSuggestionListWithInput: View {
|
||||
var body: some View {
|
||||
VStack(spacing: 0.0) {
|
||||
CompletionSuggestionList(viewModel: viewModel.listViewModel.context)
|
||||
TextField("Search for user", text: $inputText)
|
||||
TextField("Search for user/command", text: $inputText)
|
||||
.background(Color.white)
|
||||
.onChange(of: inputText, perform: viewModel.callback)
|
||||
.textFieldStyle(RoundedBorderTextFieldStyle())
|
||||
.padding([.leading, .trailing])
|
||||
.onAppear {
|
||||
inputText = "@-" // Make the list show all available mock results
|
||||
inputText = "@-" // Make the list show all available user mock results
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user