mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-02 21:33:26 +00:00
Room mentioning in the composer (#3868)
* refactored the suggestion item structure to scale with the room pill * Implemented a way for the rooms to appear in the suggestions view for the RTE, however I need to add the pills to the composer and the compatibility with the plain text composer * small code correction * fix * fixed a bug where the suggestion wasn't returning the right suggestion type and the suggestion text properly * implementation done! also updated some tests, but we need more of them * updated toolbar view model tests * updated tests * updated preview tests * renamed the Avatars case for the suggestions
This commit is contained in:
@@ -21,8 +21,10 @@ final class CompletionSuggestionServiceTests: XCTestCase {
|
||||
func testUserSuggestions() async throws {
|
||||
let alice: RoomMemberProxyMock = .mockAlice
|
||||
let members: [RoomMemberProxyMock] = [alice, .mockBob, .mockCharlie, .mockMe]
|
||||
let roomProxyMock = JoinedRoomProxyMock(.init(name: "test", members: members))
|
||||
let service = CompletionSuggestionService(roomProxy: roomProxyMock)
|
||||
let roomProxyMock = JoinedRoomProxyMock(.init(id: "roomID", name: "test", members: members))
|
||||
let roomSummaryProvider = RoomSummaryProviderMock(.init(state: .loaded(.mockRooms)))
|
||||
let service = CompletionSuggestionService(roomProxy: roomProxyMock,
|
||||
roomListPublisher: roomSummaryProvider.roomListPublisher.eraseToAnyPublisher())
|
||||
|
||||
var deferred = deferFulfillment(service.suggestionsPublisher) { suggestions in
|
||||
suggestions == []
|
||||
@@ -31,7 +33,7 @@ final class CompletionSuggestionServiceTests: XCTestCase {
|
||||
try await deferred.fulfill()
|
||||
|
||||
deferred = deferFulfillment(service.suggestionsPublisher) { suggestions in
|
||||
suggestions == [.user(item: .init(id: alice.userID, displayName: alice.displayName, avatarURL: alice.avatarURL, range: .init(), rawSuggestionText: "ali"))]
|
||||
suggestions == [.init(suggestionType: .user(.init(id: alice.userID, displayName: alice.displayName, avatarURL: alice.avatarURL)), range: .init(), rawSuggestionText: "ali")]
|
||||
}
|
||||
service.setSuggestionTrigger(.init(type: .user, text: "ali", range: .init()))
|
||||
try await deferred.fulfill()
|
||||
@@ -58,8 +60,10 @@ final class CompletionSuggestionServiceTests: XCTestCase {
|
||||
func testUserSuggestionsIncludingAllUsers() async throws {
|
||||
let alice: RoomMemberProxyMock = .mockAlice
|
||||
let members: [RoomMemberProxyMock] = [alice, .mockBob, .mockCharlie, .mockMe]
|
||||
let roomProxyMock = JoinedRoomProxyMock(.init(name: "test", members: members, canUserTriggerRoomNotification: true))
|
||||
let service = CompletionSuggestionService(roomProxy: roomProxyMock)
|
||||
let roomProxyMock = JoinedRoomProxyMock(.init(id: "roomID", name: "test", members: members, canUserTriggerRoomNotification: true))
|
||||
let roomSummaryProvider = RoomSummaryProviderMock(.init(state: .loaded(.mockRooms)))
|
||||
let service = CompletionSuggestionService(roomProxy: roomProxyMock,
|
||||
roomListPublisher: roomSummaryProvider.roomListPublisher.eraseToAnyPublisher())
|
||||
|
||||
var deferred = deferFulfillment(service.suggestionsPublisher) { suggestions in
|
||||
suggestions == []
|
||||
@@ -68,13 +72,13 @@ final class CompletionSuggestionServiceTests: XCTestCase {
|
||||
try await deferred.fulfill()
|
||||
|
||||
deferred = deferFulfillment(service.suggestionsPublisher) { suggestions in
|
||||
suggestions == [.allUsers(item: .allUsersMention(roomAvatar: nil, rawSuggestionText: "ro"))]
|
||||
suggestions == [.init(suggestionType: .allUsers(.room(id: "roomID", name: "test", avatarURL: nil)), range: .init(), rawSuggestionText: "ro")]
|
||||
}
|
||||
service.setSuggestionTrigger(.init(type: .user, text: "ro", range: .init()))
|
||||
try await deferred.fulfill()
|
||||
|
||||
deferred = deferFulfillment(service.suggestionsPublisher) { suggestions in
|
||||
suggestions == [.allUsers(item: .allUsersMention(roomAvatar: nil, rawSuggestionText: "every"))]
|
||||
suggestions == [.init(suggestionType: .allUsers(.room(id: "roomID", name: "test", avatarURL: nil)), range: .init(), rawSuggestionText: "every")]
|
||||
}
|
||||
service.setSuggestionTrigger(.init(type: .user, text: "every", range: .init()))
|
||||
try await deferred.fulfill()
|
||||
@@ -84,8 +88,10 @@ final class CompletionSuggestionServiceTests: XCTestCase {
|
||||
let alice: RoomMemberProxyMock = .mockAlice
|
||||
let bob: RoomMemberProxyMock = .mockBob
|
||||
let members: [RoomMemberProxyMock] = [alice, bob, .mockMe]
|
||||
let roomProxyMock = JoinedRoomProxyMock(.init(name: "test", members: members, canUserTriggerRoomNotification: true))
|
||||
let service = CompletionSuggestionService(roomProxy: roomProxyMock)
|
||||
let roomProxyMock = JoinedRoomProxyMock(.init(id: "roomID", name: "test", members: members, canUserTriggerRoomNotification: true))
|
||||
let roomSummaryProvider = RoomSummaryProviderMock(.init(state: .loaded(.mockRooms)))
|
||||
let service = CompletionSuggestionService(roomProxy: roomProxyMock,
|
||||
roomListPublisher: roomSummaryProvider.roomListPublisher.eraseToAnyPublisher())
|
||||
|
||||
var deferred = deferFulfillment(service.suggestionsPublisher) { suggestions in
|
||||
suggestions == []
|
||||
@@ -94,34 +100,45 @@ final class CompletionSuggestionServiceTests: XCTestCase {
|
||||
try await deferred.fulfill()
|
||||
|
||||
deferred = deferFulfillment(service.suggestionsPublisher) { suggestions in
|
||||
suggestions == [.allUsers(item: .allUsersMention(roomAvatar: nil, rawSuggestionText: "")),
|
||||
.user(item: .init(id: alice.userID, displayName: alice.displayName, avatarURL: alice.avatarURL, range: .init(), rawSuggestionText: "")),
|
||||
.user(item: .init(id: bob.userID, displayName: bob.displayName, avatarURL: bob.avatarURL, range: .init(), rawSuggestionText: ""))]
|
||||
suggestions == [.init(suggestionType: .allUsers(.room(id: "roomID", name: "test", avatarURL: nil)), range: .init(), rawSuggestionText: ""),
|
||||
.init(suggestionType: .user(.init(id: alice.userID, displayName: alice.displayName, avatarURL: alice.avatarURL)), range: .init(), rawSuggestionText: ""),
|
||||
.init(suggestionType: .user(.init(id: bob.userID, displayName: bob.displayName, avatarURL: bob.avatarURL)), range: .init(), rawSuggestionText: "")]
|
||||
}
|
||||
service.setSuggestionTrigger(.init(type: .user, text: "", range: .init()))
|
||||
try await deferred.fulfill()
|
||||
|
||||
// Let's test the same with the processTextMessage method
|
||||
deferred = deferFulfillment(service.suggestionsPublisher) { suggestions in
|
||||
suggestions == [.init(suggestionType: .allUsers(.room(id: "roomID", name: "test", avatarURL: nil)), range: .init(location: 0, length: 1), rawSuggestionText: ""),
|
||||
.init(suggestionType: .user(.init(id: alice.userID, displayName: alice.displayName, avatarURL: alice.avatarURL)), range: .init(location: 0, length: 1), rawSuggestionText: ""),
|
||||
.init(suggestionType: .user(.init(id: bob.userID, displayName: bob.displayName, avatarURL: bob.avatarURL)), range: .init(location: 0, length: 1), rawSuggestionText: "")]
|
||||
}
|
||||
service.processTextMessage("@", selectedRange: .init(location: 0, length: 1))
|
||||
try await deferred.fulfill()
|
||||
}
|
||||
|
||||
func testUserSuggestionInDifferentMessagePositions() async throws {
|
||||
let alice: RoomMemberProxyMock = .mockAlice
|
||||
let members: [RoomMemberProxyMock] = [alice, .mockBob, .mockCharlie, .mockMe]
|
||||
let roomProxyMock = JoinedRoomProxyMock(.init(name: "test", members: members))
|
||||
let service = CompletionSuggestionService(roomProxy: roomProxyMock)
|
||||
let roomSummaryProvider = RoomSummaryProviderMock(.init(state: .loaded(.mockRooms)))
|
||||
let service = CompletionSuggestionService(roomProxy: roomProxyMock,
|
||||
roomListPublisher: roomSummaryProvider.roomListPublisher.eraseToAnyPublisher())
|
||||
|
||||
var deferred = deferFulfillment(service.suggestionsPublisher) { suggestion in
|
||||
suggestion == [.user(item: .init(id: alice.userID, displayName: alice.displayName, avatarURL: alice.avatarURL, range: .init(location: 0, length: 3), rawSuggestionText: "al"))]
|
||||
var deferred = deferFulfillment(service.suggestionsPublisher) { suggestions in
|
||||
suggestions == [.init(suggestionType: .user(.init(id: alice.userID, displayName: alice.displayName, avatarURL: alice.avatarURL)), range: .init(location: 0, length: 3), rawSuggestionText: "al")]
|
||||
}
|
||||
service.processTextMessage("@al hello", selectedRange: .init(location: 0, length: 1))
|
||||
try await deferred.fulfill()
|
||||
|
||||
deferred = deferFulfillment(service.suggestionsPublisher) { suggestion in
|
||||
suggestion == [.user(item: .init(id: alice.userID, displayName: alice.displayName, avatarURL: alice.avatarURL, range: .init(location: 5, length: 3), rawSuggestionText: "al"))]
|
||||
deferred = deferFulfillment(service.suggestionsPublisher) { suggestions in
|
||||
suggestions == [.init(suggestionType: .user(.init(id: alice.userID, displayName: alice.displayName, avatarURL: alice.avatarURL)), range: .init(location: 5, length: 3), rawSuggestionText: "al")]
|
||||
}
|
||||
service.processTextMessage("test @al", selectedRange: .init(location: 5, length: 1))
|
||||
try await deferred.fulfill()
|
||||
|
||||
deferred = deferFulfillment(service.suggestionsPublisher) { suggestion in
|
||||
suggestion == [.user(item: .init(id: alice.userID, displayName: alice.displayName, avatarURL: alice.avatarURL, range: .init(location: 5, length: 3), rawSuggestionText: "al"))]
|
||||
deferred = deferFulfillment(service.suggestionsPublisher) { suggestions in
|
||||
suggestions == [.init(suggestionType: .user(.init(id: alice.userID, displayName: alice.displayName, avatarURL: alice.avatarURL)), range: .init(location: 5, length: 3), rawSuggestionText: "al")]
|
||||
}
|
||||
service.processTextMessage("test @al test", selectedRange: .init(location: 5, length: 1))
|
||||
try await deferred.fulfill()
|
||||
@@ -132,16 +149,18 @@ final class CompletionSuggestionServiceTests: XCTestCase {
|
||||
let bob: RoomMemberProxyMock = .mockBob
|
||||
let members: [RoomMemberProxyMock] = [alice, bob, .mockCharlie, .mockMe]
|
||||
let roomProxyMock = JoinedRoomProxyMock(.init(name: "test", members: members))
|
||||
let service = CompletionSuggestionService(roomProxy: roomProxyMock)
|
||||
let roomSummaryProvider = RoomSummaryProviderMock(.init(state: .loaded(.mockRooms)))
|
||||
let service = CompletionSuggestionService(roomProxy: roomProxyMock,
|
||||
roomListPublisher: roomSummaryProvider.roomListPublisher.eraseToAnyPublisher())
|
||||
|
||||
var deffered = deferFulfillment(service.suggestionsPublisher) { suggestion in
|
||||
suggestion == [.user(item: .init(id: alice.userID, displayName: alice.displayName, avatarURL: alice.avatarURL, range: .init(location: 0, length: 3), rawSuggestionText: "al"))]
|
||||
var deffered = deferFulfillment(service.suggestionsPublisher) { suggestions in
|
||||
suggestions == [.init(suggestionType: .user(.init(id: alice.userID, displayName: alice.displayName, avatarURL: alice.avatarURL)), range: .init(location: 0, length: 3), rawSuggestionText: "al")]
|
||||
}
|
||||
service.processTextMessage("@al test @bo", selectedRange: .init(location: 0, length: 1))
|
||||
try await deffered.fulfill()
|
||||
|
||||
deffered = deferFulfillment(service.suggestionsPublisher) { suggestion in
|
||||
suggestion == [.user(item: .init(id: bob.userID, displayName: bob.displayName, avatarURL: bob.avatarURL, range: .init(location: 9, length: 3), rawSuggestionText: "bo"))]
|
||||
deffered = deferFulfillment(service.suggestionsPublisher) { suggestions in
|
||||
suggestions == [.init(suggestionType: .user(.init(id: bob.userID, displayName: bob.displayName, avatarURL: bob.avatarURL)), range: .init(location: 9, length: 3), rawSuggestionText: "bo")]
|
||||
}
|
||||
service.processTextMessage("@al test @bo", selectedRange: .init(location: 9, length: 1))
|
||||
try await deffered.fulfill()
|
||||
@@ -152,10 +171,205 @@ final class CompletionSuggestionServiceTests: XCTestCase {
|
||||
service.processTextMessage("@al test @bo", selectedRange: .init(location: 4, length: 1))
|
||||
try await deffered.fulfill()
|
||||
}
|
||||
}
|
||||
|
||||
private extension MentionSuggestionItem {
|
||||
static func allUsersMention(roomAvatar: URL?, rawSuggestionText: String) -> Self {
|
||||
MentionSuggestionItem(id: PillConstants.atRoom, displayName: PillConstants.everyone, avatarURL: roomAvatar, range: .init(), rawSuggestionText: rawSuggestionText)
|
||||
|
||||
func testRoomSuggestions() async throws {
|
||||
let alice: RoomMemberProxyMock = .mockAlice
|
||||
// We keep the users in the tests since they should not appear in the suggestions when using the room trigger
|
||||
let members: [RoomMemberProxyMock] = [alice, .mockBob, .mockCharlie, .mockMe]
|
||||
let roomProxyMock = JoinedRoomProxyMock(.init(id: "roomID", name: "test", members: members))
|
||||
let roomSummaryProvider = RoomSummaryProviderMock(.init(state: .loaded(.mockRooms)))
|
||||
let service = CompletionSuggestionService(roomProxy: roomProxyMock,
|
||||
roomListPublisher: roomSummaryProvider.roomListPublisher.eraseToAnyPublisher())
|
||||
|
||||
var deferred = deferFulfillment(service.suggestionsPublisher) { suggestions in
|
||||
suggestions == []
|
||||
}
|
||||
|
||||
try await deferred.fulfill()
|
||||
|
||||
// The empty # should trigger suggestions from any room with an alias
|
||||
deferred = deferFulfillment(service.suggestionsPublisher) { suggestions in
|
||||
suggestions == [.init(suggestionType: .room(.init(id: "2",
|
||||
canonicalAlias: "#foundation-and-empire:matrix.org",
|
||||
name: "Foundation and Empire",
|
||||
avatar: .room(id: "2",
|
||||
name: "Foundation and Empire",
|
||||
avatarURL: .mockMXCAvatar))),
|
||||
range: .init(),
|
||||
rawSuggestionText: ""),
|
||||
.init(suggestionType: .room(.init(id: "6",
|
||||
canonicalAlias: "#prelude-foundation:matrix.org",
|
||||
name: "Prelude to Foundation",
|
||||
avatar: .room(id: "6",
|
||||
name: "Prelude to Foundation",
|
||||
avatarURL: nil))),
|
||||
range: .init(),
|
||||
rawSuggestionText: "")]
|
||||
}
|
||||
service.setSuggestionTrigger(.init(type: .room, text: "", range: .init()))
|
||||
try await deferred.fulfill()
|
||||
|
||||
// Same but with the processTextMessage method
|
||||
deferred = deferFulfillment(service.suggestionsPublisher) { suggestions in
|
||||
suggestions == [.init(suggestionType: .room(.init(id: "2",
|
||||
canonicalAlias: "#foundation-and-empire:matrix.org",
|
||||
name: "Foundation and Empire",
|
||||
avatar: .room(id: "2",
|
||||
name: "Foundation and Empire",
|
||||
avatarURL: .mockMXCAvatar))),
|
||||
range: .init(location: 0, length: 1),
|
||||
rawSuggestionText: ""),
|
||||
.init(suggestionType: .room(.init(id: "6",
|
||||
canonicalAlias: "#prelude-foundation:matrix.org",
|
||||
name: "Prelude to Foundation",
|
||||
avatar: .room(id: "6",
|
||||
name: "Prelude to Foundation",
|
||||
avatarURL: nil))),
|
||||
range: .init(location: 0, length: 1),
|
||||
rawSuggestionText: "")]
|
||||
}
|
||||
service.processTextMessage("#", selectedRange: .init(location: 0, length: 1))
|
||||
try await deferred.fulfill()
|
||||
|
||||
deferred = deferFulfillment(service.suggestionsPublisher) { suggestions in
|
||||
suggestions == [.init(suggestionType: .room(.init(id: "6",
|
||||
canonicalAlias: "#prelude-foundation:matrix.org",
|
||||
name: "Prelude to Foundation",
|
||||
avatar: .room(id: "6",
|
||||
name: "Prelude to Foundation",
|
||||
avatarURL: nil))),
|
||||
range: .init(),
|
||||
rawSuggestionText: "prelude")]
|
||||
}
|
||||
service.setSuggestionTrigger(.init(type: .room, text: "prelude", range: .init()))
|
||||
try await deferred.fulfill()
|
||||
}
|
||||
|
||||
func testRoomSuggestionInDifferentMessagePositions() async throws {
|
||||
let alice: RoomMemberProxyMock = .mockAlice
|
||||
// We keep the users in the tests since they should not appear in the suggestions when using the room trigger
|
||||
let members: [RoomMemberProxyMock] = [alice, .mockBob, .mockCharlie, .mockMe]
|
||||
let roomProxyMock = JoinedRoomProxyMock(.init(id: "roomID", name: "test", members: members))
|
||||
let roomSummaryProvider = RoomSummaryProviderMock(.init(state: .loaded(.mockRooms)))
|
||||
let service = CompletionSuggestionService(roomProxy: roomProxyMock,
|
||||
roomListPublisher: roomSummaryProvider.roomListPublisher.eraseToAnyPublisher())
|
||||
|
||||
var deferred = deferFulfillment(service.suggestionsPublisher) { suggestions in
|
||||
suggestions == [.init(suggestionType: .room(.init(id: "6",
|
||||
canonicalAlias: "#prelude-foundation:matrix.org",
|
||||
name: "Prelude to Foundation",
|
||||
avatar: .room(id: "6",
|
||||
name: "Prelude to Foundation",
|
||||
avatarURL: nil))),
|
||||
range: .init(location: 0, length: 3),
|
||||
rawSuggestionText: "pr")]
|
||||
}
|
||||
service.processTextMessage("#pr hello", selectedRange: .init(location: 0, length: 1))
|
||||
try await deferred.fulfill()
|
||||
|
||||
deferred = deferFulfillment(service.suggestionsPublisher) { suggestions in
|
||||
suggestions == [.init(suggestionType: .room(.init(id: "6",
|
||||
canonicalAlias: "#prelude-foundation:matrix.org",
|
||||
name: "Prelude to Foundation",
|
||||
avatar: .room(id: "6",
|
||||
name: "Prelude to Foundation",
|
||||
avatarURL: nil))),
|
||||
range: .init(location: 5, length: 3),
|
||||
rawSuggestionText: "pr")]
|
||||
}
|
||||
service.processTextMessage("test #pr", selectedRange: .init(location: 5, length: 1))
|
||||
try await deferred.fulfill()
|
||||
|
||||
deferred = deferFulfillment(service.suggestionsPublisher) { suggestions in
|
||||
suggestions == [.init(suggestionType: .room(.init(id: "6",
|
||||
canonicalAlias: "#prelude-foundation:matrix.org",
|
||||
name: "Prelude to Foundation",
|
||||
avatar: .room(id: "6",
|
||||
name: "Prelude to Foundation",
|
||||
avatarURL: nil))),
|
||||
range: .init(location: 5, length: 3),
|
||||
rawSuggestionText: "pr")]
|
||||
}
|
||||
service.processTextMessage("test #pr test", selectedRange: .init(location: 5, length: 1))
|
||||
try await deferred.fulfill()
|
||||
}
|
||||
|
||||
func testRoomSuggestionWithMultipleMentionSymbol() async throws {
|
||||
let alice: RoomMemberProxyMock = .mockAlice
|
||||
// We keep the users in the tests since they should not appear in the suggestions when using the room trigger
|
||||
let members: [RoomMemberProxyMock] = [alice, .mockBob, .mockCharlie, .mockMe]
|
||||
let roomProxyMock = JoinedRoomProxyMock(.init(id: "roomID", name: "test", members: members))
|
||||
let roomSummaryProvider = RoomSummaryProviderMock(.init(state: .loaded(.mockRooms)))
|
||||
let service = CompletionSuggestionService(roomProxy: roomProxyMock,
|
||||
roomListPublisher: roomSummaryProvider.roomListPublisher.eraseToAnyPublisher())
|
||||
|
||||
var deffered = deferFulfillment(service.suggestionsPublisher) { suggestions in
|
||||
suggestions == [.init(suggestionType: .room(.init(id: "6",
|
||||
canonicalAlias: "#prelude-foundation:matrix.org",
|
||||
name: "Prelude to Foundation",
|
||||
avatar: .room(id: "6",
|
||||
name: "Prelude to Foundation",
|
||||
avatarURL: nil))),
|
||||
range: .init(location: 0, length: 3),
|
||||
rawSuggestionText: "pr")]
|
||||
}
|
||||
service.processTextMessage("#pr test #fo", selectedRange: .init(location: 0, length: 1))
|
||||
try await deffered.fulfill()
|
||||
|
||||
deffered = deferFulfillment(service.suggestionsPublisher) { suggestions in
|
||||
suggestions == [.init(suggestionType: .room(.init(id: "2",
|
||||
canonicalAlias: "#foundation-and-empire:matrix.org",
|
||||
name: "Foundation and Empire",
|
||||
avatar: .room(id: "2",
|
||||
name: "Foundation and Empire",
|
||||
avatarURL: .mockMXCAvatar))),
|
||||
range: .init(location: 9, length: 3),
|
||||
rawSuggestionText: "fo"),
|
||||
.init(suggestionType: .room(.init(id: "6",
|
||||
canonicalAlias: "#prelude-foundation:matrix.org",
|
||||
name: "Prelude to Foundation",
|
||||
avatar: .room(id: "6",
|
||||
name: "Prelude to Foundation",
|
||||
avatarURL: nil))),
|
||||
range: .init(location: 9, length: 3),
|
||||
rawSuggestionText: "fo")]
|
||||
}
|
||||
service.processTextMessage("#pr test #fo", selectedRange: .init(location: 9, length: 1))
|
||||
try await deffered.fulfill()
|
||||
|
||||
deffered = deferFulfillment(service.suggestionsPublisher) { suggestion in
|
||||
suggestion == []
|
||||
}
|
||||
service.processTextMessage("#pr test #fo", selectedRange: .init(location: 4, length: 1))
|
||||
try await deffered.fulfill()
|
||||
}
|
||||
|
||||
func testSuggestionsWithMultipleDifferentTriggers() async throws {
|
||||
let alice: RoomMemberProxyMock = .mockAlice
|
||||
// We keep the users in the tests since they should not appear in the suggestions when using the room trigger
|
||||
let members: [RoomMemberProxyMock] = [alice, .mockBob, .mockCharlie, .mockMe]
|
||||
let roomProxyMock = JoinedRoomProxyMock(.init(id: "roomID", name: "test", members: members))
|
||||
let roomSummaryProvider = RoomSummaryProviderMock(.init(state: .loaded(.mockRooms)))
|
||||
let service = CompletionSuggestionService(roomProxy: roomProxyMock,
|
||||
roomListPublisher: roomSummaryProvider.roomListPublisher.eraseToAnyPublisher())
|
||||
|
||||
var deffered = deferFulfillment(service.suggestionsPublisher) { suggestions in
|
||||
suggestions == [.init(suggestionType: .room(.init(id: "6",
|
||||
canonicalAlias: "#prelude-foundation:matrix.org",
|
||||
name: "Prelude to Foundation",
|
||||
avatar: .room(id: "6",
|
||||
name: "Prelude to Foundation",
|
||||
avatarURL: nil))),
|
||||
range: .init(location: 0, length: 3),
|
||||
rawSuggestionText: "pr")]
|
||||
}
|
||||
service.processTextMessage("#pr test @al", selectedRange: .init(location: 0, length: 1))
|
||||
try await deffered.fulfill()
|
||||
|
||||
deffered = deferFulfillment(service.suggestionsPublisher) { suggestions in
|
||||
suggestions == [.init(suggestionType: .user(.init(id: alice.userID, displayName: alice.displayName, avatarURL: alice.avatarURL)), range: .init(location: 9, length: 3), rawSuggestionText: "al")]
|
||||
}
|
||||
service.processTextMessage("#pr test @al", selectedRange: .init(location: 9, length: 1))
|
||||
try await deffered.fulfill()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,8 +91,8 @@ class ComposerToolbarViewModelTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testSuggestions() {
|
||||
let suggestions: [SuggestionItem] = [.user(item: MentionSuggestionItem(id: "@user_mention_1:matrix.org", displayName: "User 1", avatarURL: nil, range: .init(), rawSuggestionText: "")),
|
||||
.user(item: MentionSuggestionItem(id: "@user_mention_2:matrix.org", displayName: "User 2", avatarURL: .mockMXCAvatar, range: .init(), rawSuggestionText: ""))]
|
||||
let suggestions: [SuggestionItem] = [.init(suggestionType: .user(.init(id: "@user_mention_1:matrix.org", displayName: "User 1", avatarURL: nil)), range: .init(), rawSuggestionText: ""),
|
||||
.init(suggestionType: .user(.init(id: "@user_mention_2:matrix.org", displayName: "User 2", avatarURL: nil)), range: .init(), rawSuggestionText: "")]
|
||||
let mockCompletionSuggestionService = CompletionSuggestionServiceMock(configuration: .init(suggestions: suggestions))
|
||||
|
||||
viewModel = ComposerToolbarViewModel(roomProxy: JoinedRoomProxyMock(.init()),
|
||||
@@ -107,25 +107,43 @@ class ComposerToolbarViewModelTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testSuggestionTrigger() async throws {
|
||||
let deferred = deferFulfillment(wysiwygViewModel.$attributedContent) { $0.plainText == "#not_implemented_yay" }
|
||||
wysiwygViewModel.setMarkdownContent("@test")
|
||||
wysiwygViewModel.setMarkdownContent("#not_implemented_yay")
|
||||
let deferred = deferFulfillment(wysiwygViewModel.$attributedContent) { $0.plainText == "#room-alias-test" }
|
||||
wysiwygViewModel.setMarkdownContent("@user-test")
|
||||
wysiwygViewModel.setMarkdownContent("#room-alias-test")
|
||||
try await deferred.fulfill()
|
||||
|
||||
// The first one is nil because when initialised the view model is empty
|
||||
XCTAssertEqual(completionSuggestionServiceMock.setSuggestionTriggerReceivedInvocations, [nil, .init(type: .user, text: "test", range: .init(location: 0, length: 5)), nil])
|
||||
XCTAssertEqual(completionSuggestionServiceMock.setSuggestionTriggerReceivedInvocations, [nil,
|
||||
.init(type: .user, text: "user-test", range: .init(location: 0, length: 10)),
|
||||
.init(type: .room, text: "room-alias-test",
|
||||
range: .init(location: 0, length: 16))])
|
||||
}
|
||||
|
||||
func testSelectedUserSuggestion() {
|
||||
let suggestion = SuggestionItem.user(item: .init(id: "@test:matrix.org", displayName: "Test", avatarURL: nil, range: .init(), rawSuggestionText: ""))
|
||||
let suggestion = SuggestionItem(suggestionType: .user(.init(id: "@test:matrix.org", displayName: "Test", avatarURL: nil)), range: .init(), rawSuggestionText: "")
|
||||
viewModel.context.send(viewAction: .selectedSuggestion(suggestion))
|
||||
|
||||
// The display name can be used for HTML injection in the rich text editor and it's useless anyway as the clients don't use it when resolving display names
|
||||
XCTAssertEqual(wysiwygViewModel.content.html, "<a href=\"https://matrix.to/#/@test:matrix.org\">@test:matrix.org</a> ")
|
||||
}
|
||||
|
||||
func testSelectedRoomSuggestion() {
|
||||
let suggestion = SuggestionItem(suggestionType: .room(.init(id: "!room:matrix.org",
|
||||
canonicalAlias: "#room-alias:matrix.org",
|
||||
name: "Room",
|
||||
avatar: .room(id: "!room:matrix.org",
|
||||
name: "Room",
|
||||
avatarURL: nil))),
|
||||
range: .init(), rawSuggestionText: "")
|
||||
viewModel.context.send(viewAction: .selectedSuggestion(suggestion))
|
||||
|
||||
// The display name can be used for HTML injection in the rich text editor and it's useless anyway as the clients don't use it when resolving display names
|
||||
|
||||
XCTAssertEqual(wysiwygViewModel.content.html, "<a href=\"https://matrix.to/#/%23room-alias:matrix.org\">#room-alias:matrix.org</a> ")
|
||||
}
|
||||
|
||||
func testAllUsersSuggestion() {
|
||||
let suggestion = SuggestionItem.allUsers(item: .allUsersMention(roomAvatar: nil))
|
||||
let suggestion = SuggestionItem(suggestionType: .allUsers(.room(id: "", name: nil, avatarURL: nil)), range: .init(), rawSuggestionText: "")
|
||||
viewModel.context.send(viewAction: .selectedSuggestion(suggestion))
|
||||
|
||||
var string = "@room"
|
||||
@@ -138,17 +156,28 @@ class ComposerToolbarViewModelTests: XCTestCase {
|
||||
viewModel.context.send(viewAction: .composerAppeared)
|
||||
await Task.yield()
|
||||
let userID = "@test:matrix.org"
|
||||
let suggestion = SuggestionItem.user(item: .init(id: userID, displayName: "Test", avatarURL: nil, range: .init(), rawSuggestionText: ""))
|
||||
let suggestion = SuggestionItem(suggestionType: .user(.init(id: userID, displayName: "Test", avatarURL: nil)), range: .init(), rawSuggestionText: "")
|
||||
viewModel.context.send(viewAction: .selectedSuggestion(suggestion))
|
||||
|
||||
let attachment = wysiwygViewModel.textView.attributedText.attribute(.attachment, at: 0, effectiveRange: nil) as? PillTextAttachment
|
||||
XCTAssertEqual(attachment?.pillData?.type, .user(userID: userID))
|
||||
}
|
||||
|
||||
func testRoomMentionPillInRTE() async {
|
||||
viewModel.context.send(viewAction: .composerAppeared)
|
||||
await Task.yield()
|
||||
let roomAlias = "#test:matrix.org"
|
||||
let suggestion = SuggestionItem(suggestionType: .room(.init(id: "room-id", canonicalAlias: roomAlias, name: "Room", avatar: .room(id: "room-id", name: "Room", avatarURL: nil))), range: .init(), rawSuggestionText: "")
|
||||
viewModel.context.send(viewAction: .selectedSuggestion(suggestion))
|
||||
|
||||
let attachment = wysiwygViewModel.textView.attributedText.attribute(.attachment, at: 0, effectiveRange: nil) as? PillTextAttachment
|
||||
XCTAssertEqual(attachment?.pillData?.type, .roomAlias(roomAlias))
|
||||
}
|
||||
|
||||
func testAllUsersMentionPillInRTE() async {
|
||||
viewModel.context.send(viewAction: .composerAppeared)
|
||||
await Task.yield()
|
||||
let suggestion = SuggestionItem.allUsers(item: .allUsersMention(roomAvatar: nil))
|
||||
let suggestion = SuggestionItem(suggestionType: .allUsers(.room(id: "", name: nil, avatarURL: nil)), range: .init(), rawSuggestionText: "")
|
||||
viewModel.context.send(viewAction: .selectedSuggestion(suggestion))
|
||||
|
||||
let attachment = wysiwygViewModel.textView.attributedText.attribute(.attachment, at: 0, effectiveRange: nil) as? PillTextAttachment
|
||||
@@ -770,9 +799,3 @@ class ComposerToolbarViewModelTests: XCTestCase {
|
||||
viewModel.context.composerFormattingEnabled = true
|
||||
}
|
||||
}
|
||||
|
||||
private extension MentionSuggestionItem {
|
||||
static func allUsersMention(roomAvatar: URL?) -> Self {
|
||||
MentionSuggestionItem(id: PillConstants.atRoom, displayName: PillConstants.everyone, avatarURL: roomAvatar, range: .init(), rawSuggestionText: "")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user