// // Copyright 2021 New Vector Ltd // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // import Combine import XCTest @testable import RiotSwiftUI 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") service.processTextMessage("@al") XCTAssertEqual(service.items.value.first?.asUser?.displayName, "Alice") service.processTextMessage("@ice") XCTAssertEqual(service.items.value.first?.asUser?.displayName, "Alice") service.processTextMessage("@Alice") XCTAssertEqual(service.items.value.first?.asUser?.displayName, "Alice") service.processTextMessage("@alice:matrix.org") XCTAssertEqual(service.items.value.first?.asUser?.displayName, "Alice") } func testBob() { service.processTextMessage("@ob") XCTAssertEqual(service.items.value.first?.asUser?.displayName, "Bob") service.processTextMessage("@ob:") XCTAssertEqual(service.items.value.first?.asUser?.displayName, "Bob") service.processTextMessage("@b:matrix") XCTAssertEqual(service.items.value.first?.asUser?.displayName, "Bob") } func testBoth() { service.processTextMessage("@:matrix") XCTAssertEqual(service.items.value.first?.asUser?.displayName, "Alice") XCTAssertEqual(service.items.value.last?.asUser?.displayName, "Bob") service.processTextMessage("@.org") XCTAssertEqual(service.items.value.first?.asUser?.displayName, "Alice") XCTAssertEqual(service.items.value.last?.asUser?.displayName, "Bob") } func testEmptyResult() { service.processTextMessage("Lorem ipsum idolor") XCTAssertTrue(service.items.value.isEmpty) service.processTextMessage("@") XCTAssertTrue(service.items.value.isEmpty) service.processTextMessage("@@") XCTAssertTrue(service.items.value.isEmpty) service.processTextMessage("alice@matrix.org") XCTAssertTrue(service.items.value.isEmpty) } func testStuff() { service.processTextMessage("@@") XCTAssertTrue(service.items.value.isEmpty) } func testWhitespaces() { service.processTextMessage("") XCTAssertTrue(service.items.value.isEmpty) service.processTextMessage(" ") XCTAssertTrue(service.items.value.isEmpty) service.processTextMessage("\n") XCTAssertTrue(service.items.value.isEmpty) service.processTextMessage(" \n ") XCTAssertTrue(service.items.value.isEmpty) service.processTextMessage("@A ") XCTAssertTrue(service.items.value.isEmpty) service.processTextMessage(" @A ") XCTAssertTrue(service.items.value.isEmpty) } func testRoomWithoutPower() { // Given a user without the power to mention a room. canMentionRoom = false // Given a user without the power to mention a room. service.processTextMessage("@ro") // Then the completion for a room mention should not be shown. XCTAssertTrue(service.items.value.isEmpty) } func testRoomWithPower() { // Given a user with the power to mention a room. canMentionRoom = true // Given a user with the power to mention a room. service.processTextMessage("@ro") // 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 { func fetchMembers(_ members: @escaping ([RoomMembersProviderMember]) -> Void) { let users = [("Alice", "@alice:matrix.org"), ("Bob", "@bob:matrix.org")] members(users.map { user in RoomMembersProviderMember(userId: user.1, displayName: user.0, avatarUrl: "") }) } } extension CompletionSuggestionServiceTests: CommandsProviderProtocol { func fetchCommands(_ commands: @escaping ([CommandsProviderCommand]) -> Void) { commands([ CommandsProviderCommand(name: "/ban", parametersFormat: " []", description: "Bans user with given id", requiresAdminPowerLevel: false), CommandsProviderCommand(name: "/invite", parametersFormat: "", description: "Invites user with given id to current room", requiresAdminPowerLevel: false), CommandsProviderCommand(name: "/join", parametersFormat: "", description: "Joins room with given address", requiresAdminPowerLevel: false), CommandsProviderCommand(name: "/op", parametersFormat: " ", description: "Define the power level of a user", requiresAdminPowerLevel: true), CommandsProviderCommand(name: "/deop", parametersFormat: "", description: "Deops user with given id", requiresAdminPowerLevel: true), CommandsProviderCommand(name: "/me", parametersFormat: "", description: "Displays action", requiresAdminPowerLevel: false) ]) } } extension CompletionSuggestionItem { var asUser: CompletionSuggestionUserItemProtocol? { if case let .user(value) = self { return value } else { return nil } } var asCommand: CompletionSuggestionCommandItemProtocol? { if case let .command(value) = self { return value } else { return nil } } }