diff options
Diffstat (limited to 'comm/chat/protocols/matrix/test')
-rw-r--r-- | comm/chat/protocols/matrix/test/head.js | 291 | ||||
-rw-r--r-- | comm/chat/protocols/matrix/test/test_matrixAccount.js | 399 | ||||
-rw-r--r-- | comm/chat/protocols/matrix/test/test_matrixCommands.js | 177 | ||||
-rw-r--r-- | comm/chat/protocols/matrix/test/test_matrixMessage.js | 441 | ||||
-rw-r--r-- | comm/chat/protocols/matrix/test/test_matrixMessageContent.js | 652 | ||||
-rw-r--r-- | comm/chat/protocols/matrix/test/test_matrixPowerLevels.js | 204 | ||||
-rw-r--r-- | comm/chat/protocols/matrix/test/test_matrixRoom.js | 928 | ||||
-rw-r--r-- | comm/chat/protocols/matrix/test/test_matrixTextForEvent.js | 834 | ||||
-rw-r--r-- | comm/chat/protocols/matrix/test/test_roomTypeChange.js | 54 | ||||
-rw-r--r-- | comm/chat/protocols/matrix/test/xpcshell.ini | 12 |
10 files changed, 3992 insertions, 0 deletions
diff --git a/comm/chat/protocols/matrix/test/head.js b/comm/chat/protocols/matrix/test/head.js new file mode 100644 index 0000000000..ecb485ec6d --- /dev/null +++ b/comm/chat/protocols/matrix/test/head.js @@ -0,0 +1,291 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +var { IMServices } = ChromeUtils.importESModule( + "resource:///modules/IMServices.sys.mjs" +); +const { MatrixProtocol } = ChromeUtils.importESModule( + "resource:///modules/matrix.sys.mjs" +); +const { MatrixRoom, MatrixAccount, MatrixMessage } = ChromeUtils.importESModule( + "resource:///modules/matrixAccount.sys.mjs" +); +var { MatrixSDK } = ChromeUtils.importESModule( + "resource:///modules/matrix-sdk.sys.mjs" +); +function loadMatrix() { + IMServices.conversations.initConversations(); +} + +/** + * Get a MatrixRoom instance with a mocked client. + * + * @param {boolean} isMUC + * @param {string} [name="#test:example.com"] + * @param {(any, string) => any?|object} [clientHandler] + * @returns {MatrixRoom} + */ +function getRoom( + isMUC, + name = "#test:example.com", + clientHandler = () => undefined, + account +) { + if (!account) { + account = getAccount(clientHandler); + } + const room = getClientRoom(name, clientHandler, account._client); + const conversation = new MatrixRoom(account, isMUC, name); + conversation.initRoom(room); + return conversation; +} + +/** + * + * @param {string} roomId + * @param {(any, string) => any?|object} clientHandler + * @param {MatrixClient} client + * @returns {Room} + */ +function getClientRoom(roomId, clientHandler, client) { + const room = new Proxy( + { + roomId, + name: roomId, + tags: {}, + getJoinedMembers() { + return []; + }, + getAvatarUrl() { + return ""; + }, + getLiveTimeline() { + return { + getState() { + return { + getStateEvents() { + return []; + }, + }; + }, + }; + }, + isSpaceRoom() { + return false; + }, + getLastActiveTimestamp() { + return Date.now(); + }, + getMyMembership() { + return "join"; + }, + getAccountData(key) { + return null; + }, + getUnfilteredTimelineSet() { + return { + getLiveTimeline() { + return { + getEvents() { + return []; + }, + getBaseIndex() { + return 0; + }, + getNeighbouringTimeline() { + return null; + }, + getPaginationToken() { + return ""; + }, + }; + }, + }; + }, + guessDMUserId() { + return "@other:example.com"; + }, + loadMembersIfNeeded() { + return Promise.resolve(); + }, + getEncryptionTargetMembers() { + return Promise.resolve([]); + }, + }, + makeProxyHandler(clientHandler) + ); + client._rooms.set(roomId, room); + return room; +} + +/** + * + * @param {(any, string) => any?|object} clientHandler + * @returns {MatrixAccount} + */ +function getAccount(clientHandler) { + const account = new MatrixAccount(Object.create(MatrixProtocol.prototype), { + logDebugMessage(message) { + account._errors.push(message.message); + }, + }); + account._errors = []; + account._client = new Proxy( + { + _rooms: new Map(), + credentials: { + userId: "@user:example.com", + }, + getHomeserverUrl() { + return "https://example.com"; + }, + getRoom(roomId) { + return this._rooms.get(roomId); + }, + async joinRoom(roomId) { + if (!this._rooms.has(roomId)) { + getClientRoom(roomId, clientHandler, this); + } + return this._rooms.get(roomId); + }, + setAccountData(field, data) {}, + async createRoom(spec) { + const roomId = + "!" + spec.name + ":example.com" || "!newroom:example.com"; + if (!this._rooms.has(roomId)) { + getClientRoom(roomId, clientHandler, this); + } + return { + room_id: roomId, + }; + }, + getRooms() { + return Array.from(this._rooms.values()); + }, + getVisibleRooms() { + return Array.from(this._rooms.values()); + }, + isCryptoEnabled() { + return false; + }, + getPushActionsForEvent() { + return {}; + }, + leave(roomId) { + this._rooms.delete(roomId); + }, + downloadKeys() { + return Promise.resolve({}); + }, + getUser(userId) { + return { + displayName: userId, + userId, + }; + }, + getStoredDevicesForUser() { + return []; + }, + isRoomEncrypted(roomId) { + return false; + }, + }, + makeProxyHandler(clientHandler) + ); + return account; +} + +/** + * @param {(any, string) => any?|object} [clientHandler] + * @returns {object} + */ +function makeProxyHandler(clientHandler) { + return { + get(target, key, receiver) { + if (typeof clientHandler === "function") { + const value = clientHandler(target, key); + if (value) { + return value; + } + } else if (clientHandler.hasOwnProperty(key)) { + return clientHandler[key]; + } + return target[key]; + }, + }; +} + +/** + * Build a MatrixEvent like object from a plain object. + * + * @param {{ type: MatrixSDK.EventType, content: object, sender: string, id: number, redacted: boolean, time: Date }} eventSpec - Data the event holds. + * @returns {MatrixEvent} + */ +function makeEvent(eventSpec = {}) { + const time = eventSpec.time || new Date(); + return { + isRedacted() { + return eventSpec.redacted || false; + }, + getType() { + return eventSpec.type; + }, + getContent() { + return eventSpec.content || {}; + }, + getPrevContent() { + return eventSpec.prevContent || {}; + }, + getWireContent() { + return eventSpec.content; + }, + getSender() { + return eventSpec.sender; + }, + getDate() { + return time; + }, + sender: { + name: "foo bar", + getAvatarUrl() { + return "https://example.com/avatar"; + }, + }, + getId() { + return eventSpec.id || 0; + }, + isEncrypted() { + return ( + eventSpec.type == MatrixSDK.EventType.RoomMessageEncrypted || + eventSpec.isEncrypted + ); + }, + shouldAttemptDecryption() { + return Boolean(eventSpec.shouldDecrypt); + }, + isBeingDecrypted() { + return Boolean(eventSpec.decrypting); + }, + isDecryptionFailure() { + return eventSpec.content?.msgtype == "m.bad.encrypted"; + }, + isRedaction() { + return eventSpec.type == MatrixSDK.EventType.RoomRedaction; + }, + getRedactionEvent() { + return eventSpec.redaction; + }, + target: eventSpec.target, + replyEventId: + eventSpec.content?.["m.relates_to"]?.["m.in_reply_to"]?.event_id, + threadRootId: eventSpec.threadRootId || null, + getRoomId() { + return eventSpec.roomId || "!test:example.com"; + }, + status: eventSpec.status || null, + _listeners: {}, + once(event, listener) { + this._listeners[event] = listener; + }, + }; +} diff --git a/comm/chat/protocols/matrix/test/test_matrixAccount.js b/comm/chat/protocols/matrix/test/test_matrixAccount.js new file mode 100644 index 0000000000..e49d150a21 --- /dev/null +++ b/comm/chat/protocols/matrix/test/test_matrixAccount.js @@ -0,0 +1,399 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +var { TestUtils } = ChromeUtils.importESModule( + "resource://testing-common/TestUtils.sys.mjs" +); +const { setTimeout } = ChromeUtils.importESModule( + "resource://gre/modules/Timer.sys.mjs" +); + +loadMatrix(); + +add_task(function test_getConversationById() { + const mockAccount = { + roomList: new Map(), + _pendingRoomAliases: new Map(), + }; + mockAccount.roomList.set("foo", "bar"); + mockAccount._pendingRoomAliases.set("lorem", "ipsum"); + + equal(MatrixAccount.prototype.getConversationById.call(mockAccount), null); + equal( + MatrixAccount.prototype.getConversationById.call(mockAccount, "foo"), + "bar" + ); + equal( + MatrixAccount.prototype.getConversationById.call(mockAccount, "lorem"), + "ipsum" + ); +}); + +add_task(function test_getConversationByIdOrAlias() { + const mockAccount = { + getConversationById(id) { + if (id === "foo") { + return "bar"; + } + if (id === "_lorem") { + return "ipsum"; + } + return null; + }, + _client: { + getRoom(id) { + if (id === "lorem") { + return { + roomId: "_" + id, + }; + } + return null; + }, + }, + }; + + equal( + MatrixAccount.prototype.getConversationByIdOrAlias.call(mockAccount), + null + ); + equal( + MatrixAccount.prototype.getConversationByIdOrAlias.call(mockAccount, "foo"), + "bar" + ); + equal( + MatrixAccount.prototype.getConversationByIdOrAlias.call( + mockAccount, + "lorem" + ), + "ipsum" + ); + equal( + MatrixAccount.prototype.getConversationByIdOrAlias.call(mockAccount, "baz"), + null + ); +}); + +add_task(async function test_getGroupConversation() { + registerCleanupFunction(() => { + const conversations = IMServices.conversations.getConversations(); + for (const conversation of conversations) { + try { + conversation.forget(); + } catch {} + } + }); + + let allowedGetRoomIds = new Set(["baz"]); + const mockAccount = getAccount({ + getRoom(roomId) { + if (this._rooms.has(roomId)) { + return this._rooms.get(roomId); + } + if (allowedGetRoomIds.has(roomId)) { + return getClientRoom("baz", {}, mockAccount._client); + } + return null; + }, + async joinRoom(roomId) { + if (roomId === "lorem") { + allowedGetRoomIds.add(roomId); + return getClientRoom(roomId, {}, mockAccount._client); + } else if (roomId.endsWith(":example.com")) { + const error = new Error("not found"); + error.errcode = "M_NOT_FOUND"; + throw error; + } + throw new Error("Could not join"); + }, + getDomain() { + return "example.com"; + }, + getHomeserverUrl() { + return "https://example.com"; + }, + leave(roomId) { + this._rooms.delete(roomId); + mockAccount.left = true; + }, + }); + const fooRoom = getRoom(true, "bar", {}, mockAccount); + mockAccount.roomList.set("foo", fooRoom); + + equal(mockAccount.getGroupConversation(""), null, "No room with empty ID"); + equal( + mockAccount.getGroupConversation("foo").name, + "bar", + "Room with expected name" + ); + fooRoom.close(); + + const existingRoom = mockAccount.getGroupConversation("baz"); + await existingRoom.waitForRoom(); + strictEqual(existingRoom, mockAccount.roomList.get("baz")); + ok(!existingRoom.joining, "Not joining existing room"); + existingRoom.close(); + + const joinedRoom = mockAccount.getGroupConversation("lorem"); + ok(joinedRoom.joining, "joining room"); + allowedGetRoomIds.add("lorem"); + await joinedRoom.waitForRoom(); + strictEqual(joinedRoom, mockAccount.roomList.get("lorem")); + ok(!joinedRoom.joining, "Joined room"); + joinedRoom.close(); + + const createdRoom = mockAccount.getGroupConversation("#ipsum:example.com"); + ok(createdRoom.joining, "Joining new room"); + await createdRoom.waitForRoom(); + ok(!createdRoom.joining, "Joined new room"); + strictEqual(createdRoom, mockAccount.roomList.get("!ipsum:example.com")); + // Wait for catchup to complete. + await TestUtils.waitForTick(); + createdRoom.close(); + + const roomAlreadyBeingCreated = + mockAccount.getGroupConversation("#lorem:example.com"); + ok( + roomAlreadyBeingCreated.joining, + "Joining room that is about to get replaced" + ); + const pendingRoom = getRoom(true, "hi", {}, mockAccount); + mockAccount._pendingRoomAliases.set("#lorem:example.com", pendingRoom); + await roomAlreadyBeingCreated.waitForRoom(); + ok(!roomAlreadyBeingCreated.joining, "Not joining replaced room"); + ok(roomAlreadyBeingCreated._replacedBy, "Room got replaced"); + pendingRoom.forget(); + + const missingLocalRoom = + mockAccount.getGroupConversation("!ipsum:example.com"); + await TestUtils.waitForTick(); + ok(!missingLocalRoom.joining, "Not joining missing room"); + ok(mockAccount.left, "Left missing room"); + + mockAccount.left = false; + const unjoinableRemoteRoom = + mockAccount.getGroupConversation("#test:matrix.org"); + await TestUtils.waitForTick(); + ok(!unjoinableRemoteRoom.joining, "Not joining unjoinable room"); + ok(mockAccount.left, "Left unjoinable room"); +}); + +add_task(async function test_joinChat() { + const roomId = "!foo:example.com"; + const conversation = { + waitForRoom() { + return Promise.resolve(); + }, + checkForUpdate() { + this.checked = true; + }, + }; + const mockAccount = { + getGroupConversation(id) { + this.groupConv = id; + return conversation; + }, + }; + const components = { + getValue(key) { + if (key === "roomIdOrAlias") { + return roomId; + } + ok(false, "Unknown chat room field"); + return null; + }, + }; + + const conv = MatrixAccount.prototype.joinChat.call(mockAccount, components); + equal(mockAccount.groupConv, roomId); + strictEqual(conv, conversation); + await Promise.resolve(); + ok(conversation.checked); +}); + +add_task(async function test_getDMRoomIdsForUserId() { + const account = getAccount({ + getRoom(roomId) { + if (roomId === "!invalid:example.com") { + return null; + } + return getClientRoom( + roomId, + { + isSpaceRoom() { + return roomId === "!space:example.com"; + }, + getMyMembership() { + return roomId === "!left:example.com" ? "leave" : "join"; + }, + getMember(userId) { + return { + membership: "invite", + }; + }, + }, + account._client + ); + }, + }); + account._userToRoom = { + "@test:example.com": [ + "!asdf:example.com", + "!space:example.com", + "!left:example.com", + "!invalid:example.com", + ], + }; + const invalid = account.getDMRoomIdsForUserId("@nouser:example.com"); + ok(Array.isArray(invalid)); + equal(invalid.length, 0); + + const rooms = account.getDMRoomIdsForUserId("@test:example.com"); + ok(Array.isArray(rooms)); + equal(rooms.length, 1); + equal(rooms[0], "!asdf:example.com"); +}); + +add_task(async function test_invitedToDMIn_deny() { + const dmRoomId = "!test:example.com"; + let leftRoom = false; + const account = getAccount({ + leave(roomId) { + equal(roomId, dmRoomId); + leftRoom = true; + }, + }); + const room = getClientRoom( + dmRoomId, + { + getDMInviter() { + return "@other:example.com"; + }, + }, + account._client + ); + const requestObserver = TestUtils.topicObserved( + "buddy-authorization-request" + ); + account.invitedToDM(room); + const [request] = await requestObserver; + request.QueryInterface(Ci.prplIBuddyRequest); + equal(request.userName, "@other:example.com"); + request.deny(); + ok(leftRoom); +}); + +add_task(async function test_nameIsMXID() { + const account = getAccount(); + account.imAccount.name = "@test:example.com"; + ok(account.nameIsMXID); + account.imAccount.name = "@test:example.com:8443"; + ok(account.nameIsMXID); + account.imAccount.name = "test:example.com"; + ok(!account.nameIsMXID); + account.imAccount.name = "test"; + ok(!account.nameIsMXID); +}); + +add_task(async function test_invitedToChat_deny() { + const chatRoomId = "!test:xample.com"; + let leftRoom = false; + const account = getAccount({ + leave(roomId) { + equal(roomId, chatRoomId); + leftRoom = true; + return Promise.resolve(); + }, + }); + const room = getClientRoom( + chatRoomId, + { + getCanonicalAlias() { + return "#foo:example.com"; + }, + }, + account._client + ); + const requestObserver = TestUtils.topicObserved("conv-authorization-request"); + account.invitedToChat(room); + const [request] = await requestObserver; + request.QueryInterface(Ci.prplIChatRequest); + equal(request.conversationName, "#foo:example.com"); + ok(request.canDeny); + request.deny(); + ok(leftRoom); +}); + +add_task(async function test_invitedToChat_cannotDenyServerNotice() { + const chatRoomId = "!test:xample.com"; + const account = getAccount({}); + const room = getClientRoom( + chatRoomId, + { + getCanonicalAlias() { + return "#foo:example.com"; + }, + tags: { + "m.server_notice": true, + }, + }, + account._client + ); + console.log(room.tags); + const requestObserver = TestUtils.topicObserved("conv-authorization-request"); + account.invitedToChat(room); + const [request] = await requestObserver; + request.QueryInterface(Ci.prplIChatRequest); + equal(request.conversationName, "#foo:example.com"); + ok(!request.canDeny); +}); + +add_task(async function test_deleteAccount() { + let clientLoggedIn = true; + let storesCleared; + let storesPromise = new Promise(resolve => { + storesCleared = resolve; + }); + let stopped = false; + let removedListeners; + const account = getAccount({ + isLoggedIn() { + return true; + }, + logout() { + clientLoggedIn = false; + return Promise.resolve(); + }, + clearStores() { + storesCleared(); + }, + stopClient() { + stopped = true; + }, + removeAllListeners(type) { + removedListeners = type; + }, + }); + const conv = account.getGroupConversation("example"); + await conv.waitForRoom(); + const timeout = setTimeout(() => ok(false), 1000); // eslint-disable-line mozilla/no-arbitrary-setTimeout + account._verificationRequestTimeouts.add(timeout); + let verificationRequestCancelled = false; + account._pendingOutgoingVerificationRequests.set("foo", { + cancel() { + verificationRequestCancelled = true; + return Promise.reject(new Error("test")); + }, + }); + account.remove(); + account.unInit(); + await storesPromise; + ok(!clientLoggedIn, "logged out"); + ok( + !IMServices.conversations.getConversations().includes(conv), + "room closed" + ); + ok(verificationRequestCancelled, "verification request cancelled"); + ok(stopped); + equal(removedListeners, MatrixSDK.ClientEvent.Sync); + equal(account._verificationRequestTimeouts.size, 0); +}); diff --git a/comm/chat/protocols/matrix/test/test_matrixCommands.js b/comm/chat/protocols/matrix/test/test_matrixCommands.js new file mode 100644 index 0000000000..e65ff195e7 --- /dev/null +++ b/comm/chat/protocols/matrix/test/test_matrixCommands.js @@ -0,0 +1,177 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +var { commands } = ChromeUtils.importESModule( + "resource:///modules/matrixCommands.sys.mjs" +); + +add_task(function testUnhandledEmptyCommands() { + const noopCommands = [ + "ban", + "unban", + "invite", + "kick", + "nick", + "op", + "deop", + "topic", + "roomname", + "addalias", + "removealias", + "upgraderoom", + "me", + "msg", + "join", + ]; + for (const command of commands) { + if (noopCommands.includes(command.name)) { + ok( + !command.run(""), + "Command " + command.name + " reports it handled no arguments" + ); + ok( + !command.run(" "), + "Command " + + command.name + + " reports it handled purely whitespace arguments" + ); + } + } +}); + +add_task(function testHelpString() { + for (const command of commands) { + const helpString = command.helpString; + equal( + typeof helpString, + "string", + "Usage help for " + command.name + " is not a string" + ); + ok( + helpString.includes(command.name), + command.name + " is not mentioned in its help string" + ); + } +}); + +add_task(function testTopic() { + const conversation = { + wrappedJSObject: { + set topic(value) { + conversation._topic = value; + }, + }, + }; + const topic = "foo bar"; + const command = _getRunCommand("topic"); + const result = command(topic, conversation); + ok(result, "Setting topic was not handled"); + equal(conversation._topic, topic, "Topic not correctly set"); +}); + +add_task(async function testMsgSuccess() { + const targetUser = "@test:example.com"; + const directMessage = "lorem ipsum"; + let onMessage; + const sendMsgPromise = new Promise(resolve => { + onMessage = resolve; + }); + const dm = { + waitForRoom() { + return Promise.resolve(this); + }, + sendMsg(message) { + onMessage(message); + }, + }; + const conversation = { + wrappedJSObject: { + _account: { + getDirectConversation(userId) { + if (userId === targetUser) { + return dm; + } + return null; + }, + }, + }, + }; + const command = _getRunCommand("msg"); + const result = command(targetUser + " " + directMessage, conversation); + ok(result, "Sending direct message was not handled"); + const message = await sendMsgPromise; + equal(message, directMessage, "Message was not sent in DM room"); +}); + +add_task(function testMsgMissingMessage() { + const targetUser = "@test:example.com"; + const conversation = {}; + const command = _getRunCommand("msg"); + const result = command(targetUser, conversation); + ok(!result, "Sending direct message was handled"); +}); + +add_task(function testMsgNoRoom() { + const targetUser = "@test:example.com"; + const directMessage = "lorem ipsum"; + const conversation = { + wrappedJSObject: { + _account: { + getDirectConversation(userId) { + conversation.userId = userId; + return null; + }, + ERROR(errorMsg) { + conversation.errorMsg = errorMsg; + }, + }, + }, + }; + const command = _getRunCommand("msg"); + const result = command(targetUser + " " + directMessage, conversation); + ok(result, "Sending direct message was not handled"); + equal( + conversation.userId, + targetUser, + "Did not try to get the conversation for the target user" + ); + ok(conversation.errorMsg, "Did not report an error"); +}); + +add_task(function testJoinSuccess() { + const roomName = "#test:example.com"; + const conversation = { + wrappedJSObject: { + _account: { + getGroupConversation(roomId) { + conversation.roomId = roomId; + }, + }, + }, + }; + const command = _getRunCommand("join"); + const result = command(roomName, conversation); + ok(result, "Did not handle join command"); + equal(conversation.roomId, roomName, "Did not try to join expected room"); +}); + +add_task(function testJoinNotRoomId() { + const roomName = "!asdf:example.com"; + const conversation = {}; + const command = _getRunCommand("join"); + const result = command(roomName, conversation); + ok(!result, "Handled join command for unsupported room Id"); +}); + +// Fetch the run() of a named command. +function _getRunCommand(aCommandName) { + for (let command of commands) { + if (command.name == aCommandName) { + return command.run; + } + } + + // Fail if no command was found. + ok(false, "Could not find the '" + aCommandName + "' command."); + return null; +} diff --git a/comm/chat/protocols/matrix/test/test_matrixMessage.js b/comm/chat/protocols/matrix/test/test_matrixMessage.js new file mode 100644 index 0000000000..4d0d2120ba --- /dev/null +++ b/comm/chat/protocols/matrix/test/test_matrixMessage.js @@ -0,0 +1,441 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +const { ReceiptType } = ChromeUtils.importESModule( + "resource:///modules/matrix-sdk.sys.mjs" +); + +const kSendReadPref = "purple.conversations.im.send_read"; + +loadMatrix(); + +add_task(function test_whenDisplayed() { + const mockConv = { + _account: { + _client: { + sendReadReceipt(event, receiptType) { + mockConv.readEvent = event; + mockConv.receiptType = receiptType; + return Promise.resolve(); + }, + }, + }, + }; + const message = new MatrixMessage( + "foo", + "bar", + { + event: "baz", + }, + mockConv + ); + + message.whenDisplayed(); + + equal(mockConv.readEvent, "baz"); + equal( + mockConv.receiptType, + message.hideReadReceipts ? ReceiptType.ReadPrivate : ReceiptType.Read + ); + + mockConv.readEvent = false; + + message.whenDisplayed(); + ok(!mockConv.readEvent); +}); + +add_task(async function test_whenDisplayedError() { + let resolveError; + const errorPromise = new Promise(resolve => { + resolveError = resolve; + }); + const readReceiptRejection = "foo bar"; + const mockConv = { + ERROR(error) { + resolveError(error); + }, + _account: { + _client: { + sendReadReceipt() { + return Promise.reject(readReceiptRejection); + }, + }, + }, + }; + const message = new MatrixMessage( + "foo", + "bar", + { + event: "baz", + }, + mockConv + ); + + message.whenDisplayed(); + const error = await errorPromise; + equal(error, readReceiptRejection); +}); + +add_task(function test_whenRead() { + const mockConv = { + _roomId: "lorem", + _account: { + _client: { + setRoomReadMarkers(roomId, eventId) { + mockConv.readRoomId = roomId; + mockConv.readEventId = eventId; + return Promise.resolve(); + }, + }, + }, + }; + const message = new MatrixMessage( + "foo", + "bar", + { + event: { + getId() { + return "baz"; + }, + }, + }, + mockConv + ); + + message.whenRead(); + + equal(mockConv.readEventId, "baz"); + equal(mockConv.readRoomId, "lorem"); + + mockConv.readEventId = false; + + message.whenRead(); + ok(!mockConv.readEventId); +}); + +add_task(async function test_whenReadError() { + let resolveError; + const errorPromise = new Promise(resolve => { + resolveError = resolve; + }); + const readReceiptRejection = "foo bar"; + const mockConv = { + ERROR(error) { + resolveError(error); + }, + _account: { + _client: { + setRoomReadMarkers() { + return Promise.reject(readReceiptRejection); + }, + }, + }, + }; + const message = new MatrixMessage( + "foo", + "bar", + { + event: { + getId() { + return "baz"; + }, + }, + }, + mockConv + ); + + message.whenRead(); + const error = await errorPromise; + equal(error, readReceiptRejection); +}); + +add_task(async function test_whenDisplayedNoEvent() { + const message = new MatrixMessage("foo", "bar", { + system: true, + }); + + message.whenDisplayed(); + + ok(!message._displayed); +}); + +add_task(async function test_whenReadNoEvent() { + const message = new MatrixMessage("foo", "bar", { + system: true, + }); + + message.whenRead(); + + ok(!message._read); +}); + +add_task(async function test_hideReadReceipts() { + const message = new MatrixMessage("foo", "bar", {}); + const initialSendRead = Services.prefs.getBoolPref(kSendReadPref); + strictEqual(message.hideReadReceipts, !initialSendRead); + Services.prefs.setBoolPref(kSendReadPref, !initialSendRead); + const message2 = new MatrixMessage("lorem", "ipsum", {}); + strictEqual(message2.hideReadReceipts, initialSendRead); + strictEqual(message.hideReadReceipts, !initialSendRead); + Services.prefs.setBoolPref(kSendReadPref, initialSendRead); +}); + +add_task(async function test_getActions() { + const event = makeEvent({ + type: MatrixSDK.EventType.RoomMessage, + }); + const message = new MatrixMessage( + "foo", + "bar", + { event }, + { + roomState: { + maySendRedactionForEvent() { + return false; + }, + }, + } + ); + const actions = message.getActions(); + ok(Array.isArray(actions)); + equal(actions.length, 0); +}); + +add_task(async function test_getActions_decryptionFailure() { + const event = makeEvent({ + type: MatrixSDK.EventType.RoomMessage, + content: { + msgtype: "m.bad.encrypted", + }, + }); + let eventKeysWereRequestedFor; + const message = new MatrixMessage( + "foo", + "bar", + { event }, + { + _account: { + _client: { + cancelAndResendEventRoomKeyRequest(matrixEvent) { + eventKeysWereRequestedFor = matrixEvent; + return Promise.resolve(); + }, + }, + }, + roomState: { + maySendRedactionForEvent() { + return false; + }, + }, + } + ); + const actions = message.getActions(); + ok(Array.isArray(actions)); + equal(actions.length, 1); + const [action] = actions; + ok(action.label); + action.run(); + strictEqual(eventKeysWereRequestedFor, event); +}); + +add_task(async function test_getActions_redact() { + const event = makeEvent({ + type: MatrixSDK.EventType.RoomMessage, + content: { + msgtype: MatrixSDK.MsgType.Text, + body: "foo bar", + }, + roomId: "!actions:example.com", + threadRootId: "$thread:example.com", + id: "$ev:example.com", + }); + let eventRedacted = false; + const message = new MatrixMessage( + "foo", + "bar", + { event }, + { + _account: { + userId: 0, + _client: { + redactEvent(roomId, threadRootId, eventId) { + equal(roomId, "!actions:example.com"); + equal(threadRootId, "$thread:example.com"); + equal(eventId, "$ev:example.com"); + eventRedacted = true; + return Promise.resolve(); + }, + }, + }, + roomState: { + maySendRedactionForEvent(ev, userId) { + equal(ev, event); + equal(userId, 0); + return true; + }, + }, + } + ); + const actions = message.getActions(); + ok(Array.isArray(actions)); + equal(actions.length, 1); + const [action] = actions; + ok(action.label); + action.run(); + ok(eventRedacted); +}); + +add_task(async function test_getActions_noEvent() { + const message = new MatrixMessage("system", "test", { + system: true, + }); + const actions = message.getActions(); + ok(Array.isArray(actions)); + deepEqual(actions, []); +}); + +add_task(async function test_getActions_report() { + const event = makeEvent({ + type: MatrixSDK.EventType.RoomMessage, + content: { + msgtype: MatrixSDK.MsgType.Text, + body: "lorem ipsum", + }, + roomId: "!actions:example.com", + id: "$ev:example.com", + }); + let eventReported = false; + const message = new MatrixMessage( + "user", + "lorem ipsum", + { event, incoming: true }, + { + _account: { + _client: { + reportEvent(roomId, eventId, score, reason) { + equal(roomId, "!actions:example.com"); + equal(eventId, "$ev:example.com"); + equal(score, -100); + equal(reason, ""); + eventReported = true; + return Promise.resolve(); + }, + }, + }, + roomState: { + maySendRedactionForEvent(ev, userId) { + return false; + }, + }, + } + ); + const actions = message.getActions(); + ok(Array.isArray(actions)); + const [action] = actions; + ok(action.label); + action.run(); + ok(eventReported); +}); + +add_task(async function test_getActions_notSent() { + let resendCalled = false; + let cancelCalled = false; + const event = makeEvent({ + status: MatrixSDK.EventStatus.NOT_SENT, + type: MatrixSDK.EventType.RoomMessage, + content: { + msgtype: MatrixSDK.MsgType.Text, + body: "foo bar", + }, + }); + const message = new MatrixMessage( + "!test:example.com", + "Error sending message", + { + event, + error: true, + }, + { + _account: { + _client: { + resendEvent(ev, room) { + equal(ev, event); + ok(room); + resendCalled = true; + }, + cancelPendingEvent(ev) { + equal(ev, event); + cancelCalled = true; + }, + }, + }, + roomState: { + maySendRedactionForEvent(ev, userId) { + return false; + }, + }, + room: {}, + } + ); + const actions = message.getActions(); + ok(Array.isArray(actions)); + equal(actions.length, 2); + const [retryAction, cancelAction] = actions; + ok(retryAction.label); + ok(cancelAction.label); + retryAction.run(); + ok(resendCalled); + ok(!cancelCalled); + cancelAction.run(); + ok(cancelCalled); +}); + +add_task(function test_whenDisplayedUnsent() { + const mockConv = { + _account: { + _client: { + sendReadReceipt(event, options) { + ok(false, "Should not send read receipt for unsent event"); + }, + }, + }, + }; + const message = new MatrixMessage( + "foo", + "bar", + { + event: makeEvent({ + status: MatrixSDK.EventStatus.NOT_SENT, + }), + }, + mockConv + ); + + message.whenDisplayed(); + ok(!message._displayed); +}); + +add_task(function test_whenReadUnsent() { + const mockConv = { + _account: { + _client: { + setRoomReadMarkers(event, options) { + ok(false, "Should not send read marker for unsent event"); + }, + }, + }, + }; + const message = new MatrixMessage( + "foo", + "bar", + { + event: makeEvent({ + status: MatrixSDK.EventStatus.NOT_SENT, + }), + }, + mockConv + ); + + message.whenRead(); + ok(!message._read); +}); diff --git a/comm/chat/protocols/matrix/test/test_matrixMessageContent.js b/comm/chat/protocols/matrix/test/test_matrixMessageContent.js new file mode 100644 index 0000000000..b09d3807ca --- /dev/null +++ b/comm/chat/protocols/matrix/test/test_matrixMessageContent.js @@ -0,0 +1,652 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +var { MatrixMessageContent } = ChromeUtils.importESModule( + "resource:///modules/matrixMessageContent.sys.mjs" +); +const { XPCShellContentUtils } = ChromeUtils.importESModule( + "resource://testing-common/XPCShellContentUtils.sys.mjs" +); +var { getMatrixTextForEvent } = ChromeUtils.importESModule( + "resource:///modules/matrixTextForEvent.sys.mjs" +); +var { l10nHelper } = ChromeUtils.importESModule( + "resource:///modules/imXPCOMUtils.sys.mjs" +); +var _ = l10nHelper("chrome://chat/locale/matrix.properties"); + +// Required to make it so the DOMParser can handle images and such. +XPCShellContentUtils.init(this); + +const PLAIN_FIXTURES = [ + { + description: "Normal text message plain quote", + event: { + type: MatrixSDK.EventType.RoomMessage, + content: { + msgtype: MatrixSDK.MsgType.Text, + body: `> lorem ipsum +> dolor sit amet + +dolor sit amet`, + ["m.relates_to"]: { + "m.in_reply_to": { + event_id: "!event:example.com", + }, + }, + }, + sender: "@bar:example.com", + }, + getEventResult: { + id: "!event:example.com", + type: MatrixSDK.EventType.RoomMessage, + content: { + msgtype: MatrixSDK.MsgType.Text, + body: "lorem ipsum!", + }, + sender: "@foo:example.com", + }, + result: `@foo:example.com: +> lorem ipsum! + +dolor sit amet`, + }, + { + description: "Normal text message plain quote with missing quote message", + event: { + type: MatrixSDK.EventType.RoomMessage, + content: { + msgtype: MatrixSDK.MsgType.Text, + body: `> lorem ipsum + +dolor sit amet`, + ["m.relates_to"]: { + "m.in_reply_to": { + event_id: "!event:example.com", + }, + }, + }, + sender: "@bar:example.com", + }, + result: `> lorem ipsum + +dolor sit amet`, + }, + { + description: "Emote message plain quote", + event: { + type: MatrixSDK.EventType.RoomMessage, + content: { + msgtype: MatrixSDK.MsgType.Text, + body: `> lorem ipsum + +dolor sit amet`, + ["m.relates_to"]: { + "m.in_reply_to": { + event_id: "!event:example.com", + }, + }, + }, + sender: "@bar:example.com", + }, + getEventResult: { + id: "!event:example.com", + type: MatrixSDK.EventType.RoomMessage, + content: { + msgtype: MatrixSDK.MsgType.Emote, + body: "lorem ipsum", + }, + sender: "@foo:example.com", + }, + result: `> * @foo:example.com lorem ipsum * + +dolor sit amet`, + }, + { + description: "Reply is emote", + event: { + type: MatrixSDK.EventType.RoomMessage, + content: { + msgtype: MatrixSDK.MsgType.Emote, + body: `> lorem ipsum + +dolor sit amet`, + ["m.relates_to"]: { + "m.in_reply_to": { + event_id: "!event:example.com", + }, + }, + }, + sender: "@bar:example.com", + }, + getEventResult: { + id: "!event:example.com", + type: MatrixSDK.EventType.RoomMessage, + content: { + msgtype: MatrixSDK.MsgType.Text, + body: "lorem ipsum", + }, + sender: "@foo:example.com", + }, + result: "\ndolor sit amet", + }, + { + description: "Attachment", + event: { + type: MatrixSDK.EventType.RoomMessage, + content: { + msgtype: MatrixSDK.MsgType.File, + body: "example.png", + url: "mxc://example.com/asdf", + }, + sender: "@bar:example.com", + }, + result: "https://example.com/_matrix/media/r0/download/example.com/asdf", + }, + { + description: "Sticker", + event: { + type: MatrixSDK.EventType.Sticker, + content: { + body: "example.png", + url: "mxc://example.com/asdf", + }, + sender: "@bar:example.com", + }, + result: "https://example.com/_matrix/media/r0/download/example.com/asdf", + }, + { + description: "Normal body with HTML-y contents", + event: { + type: MatrixSDK.EventType.Text, + content: { + body: "<foo>", + }, + sender: "@bar:example.com", + }, + result: "<foo>", + }, + { + description: "Non-mxc attachment", + event: { + type: MatrixSDK.EventType.RoomMessage, + content: { + body: "hello.jpg", + msgtype: MatrixSDK.MsgType.Image, + url: "https://example.com/hello.jpg", + }, + sender: "@bar:example.com", + }, + result: "hello.jpg", + }, + { + description: "Key verification request", + event: { + type: MatrixSDK.EventType.RoomMessage, + content: { + msgtype: MatrixSDK.MsgType.KeyVerificationRequest, + }, + sender: "@bar:example.com", + }, + isGetTextForEvent: true, + }, + { + description: "Decryption failure", + event: { + type: MatrixSDK.EventType.RoomMessageEncrypted, + content: { + msgtype: "m.bad.encrypted", + }, + }, + isGetTextForEvent: true, + }, + { + description: "Being decrypted", + event: { + type: MatrixSDK.EventType.RoomMessageEncrypted, + decrypting: true, + }, + result: _("message.decrypting"), + }, + { + description: "Unsent event", + event: { + type: MatrixSDK.EventType.RoomMessage, + content: { + body: "foo", + msgtype: MatrixSDK.MsgType.Text, + }, + sender: "@bar:example.com", + status: MatrixSDK.EventStatus.NOT_SENT, + }, + result: "", + }, + { + description: "Redacted event", + event: { + type: MatrixSDK.EventType.RoomMessage, + content: {}, + sender: "@bar:example.com", + redacted: true, + }, + result: _("message.redacted"), + }, + { + description: "Tombstone", + event: { + type: MatrixSDK.EventType.RoomTombstone, + content: { + body: "tombstone", + }, + sender: "@bar:example.com", + }, + result: "tombstone", + }, + { + description: "Encryption start", + event: { + type: MatrixSDK.EventType.RoomEncryption, + content: {}, + sender: "@bar:example.com", + }, + isGetTextForEvent: true, + }, + { + description: "Reaction", + event: { + type: MatrixSDK.EventType.Reaction, + content: { + ["m.relates_to"]: { + rel_type: MatrixSDK.RelationType.Annotation, + event_id: "!event:example.com", + key: "🐦", + }, + }, + sender: "@bar:example.com", + }, + getEventResult: { + id: "!event:example.com", + type: MatrixSDK.EventType.RoomMessage, + content: { + msgtype: MatrixSDK.MsgType.Text, + body: "lorem ipsum!", + }, + sender: "@foo:example.com", + }, + result: _("message.reaction", "@bar:example.com", "@foo:example.com", "🐦"), + }, +]; + +const HTML_FIXTURES = [ + { + description: "Normal text message plain quote", + event: { + type: MatrixSDK.EventType.RoomMessage, + content: { + msgtype: MatrixSDK.MsgType.Text, + body: `> lorem ipsum +> dolor sit amet + +dolor sit amet`, + format: "org.matrix.custom.html", + formatted_body: `<mx-reply> + <a href="https://matrix.to/#/@foo:example.com">Foo</a> wrote:<br> + <blockquote>lorem ipsum</blockquote> +</mx-reply> +<p>dolor sit amet</p>`, + ["m.relates_to"]: { + "m.in_reply_to": { + event_id: "!event:example.com", + }, + }, + }, + sender: "@bar:example.com", + }, + getEventResult: { + id: "!event:example.com", + type: MatrixSDK.EventType.RoomMessage, + content: { + msgtype: MatrixSDK.MsgType.Text, + body: "lorem ipsum!", + }, + sender: "@foo:example.com", + }, + result: `<span class="ib-person">@foo:example.com</span>:<blockquote>lorem ipsum!</blockquote>\n<p>dolor sit amet</p>`, + }, + { + description: "Normal text message with missing quote message", + event: { + type: MatrixSDK.EventType.RoomMessage, + content: { + msgtype: MatrixSDK.MsgType.Text, + body: `> lorem ipsum +> dolor sit amet + +dolor sit amet`, + format: "org.matrix.custom.html", + formatted_body: `<mx-reply> + <a href="https://matrix.to/#/@foo:example.com">Foo</a> wrote:<br> + <blockquote>lorem ipsum</blockquote> +</mx-reply> +<p>dolor sit amet</p>`, + ["m.relates_to"]: { + "m.in_reply_to": { + event_id: "!event:example.com", + }, + }, + }, + sender: "@bar:example.com", + }, + result: ` + <span class="ib-person">@foo:example.com</span> wrote:<br> + <blockquote>lorem ipsum</blockquote> + +<p>dolor sit amet</p>`, + }, + { + description: "Quoted emote message", + event: { + type: MatrixSDK.EventType.RoomMessage, + content: { + msgtype: MatrixSDK.MsgType.Text, + body: `> lorem ipsum + +dolor sit amet`, + format: "org.matrix.custom.html", + formatted_body: `<mx-reply> + <a href="https://matrix.to/#/@foo:example.com">Foo</a> wrote:<br> + <blockquote>lorem ipsum</blockquote> +</mx-reply> +<p>dolor sit amet</p>`, + ["m.relates_to"]: { + "m.in_reply_to": { + event_id: "!event:example.com", + }, + }, + }, + sender: "@bar:example.com", + }, + getEventResult: { + id: "!event:example.com", + type: MatrixSDK.EventType.RoomMessage, + content: { + msgtype: MatrixSDK.MsgType.Emote, + body: "lorem ipsum", + format: "org.matrix.custom.html", + formatted_body: "<p>lorem ipsum</p>", + }, + sender: "@foo:example.com", + }, + result: `<blockquote>* @foo:example.com <p>lorem ipsum</p> *</blockquote> +<p>dolor sit amet</p>`, + }, + { + description: "Reply is emote", + event: { + type: MatrixSDK.EventType.RoomMessage, + content: { + msgtype: MatrixSDK.MsgType.Emote, + body: `> lorem ipsum + +dolor sit amet`, + format: "org.matrix.custom.html", + formatted_body: `<mx-reply> + <a href="https://matrix.to/#/@foo:example.com">Foo</a> wrote:<br> + <blockquote>lorem ipsum</blockquote> +</mx-reply> +<p>dolor sit amet</p>`, + ["m.relates_to"]: { + "m.in_reply_to": { + event_id: "!event:example.com", + }, + }, + }, + sender: "@bar:example.com", + }, + getEventResult: { + id: "!event:example.com", + type: MatrixSDK.EventType.RoomMessage, + content: { + msgtype: MatrixSDK.MsgType.Text, + body: "lorem ipsum", + }, + sender: "@foo:example.com", + }, + result: "\n<p>dolor sit amet</p>", + }, + { + description: "Attachment", + event: { + type: MatrixSDK.EventType.RoomMessage, + content: { + msgtype: MatrixSDK.MsgType.File, + body: "example.png", + url: "mxc://example.com/asdf", + }, + sender: "@bar:example.com", + }, + result: + '<a href="https://example.com/_matrix/media/r0/download/example.com/asdf">example.png</a>', + }, + { + description: "Sticker", + event: { + type: MatrixSDK.EventType.Sticker, + content: { + body: "example.png", + url: "mxc://example.com/asdf", + }, + sender: "@bar:example.com", + }, + result: + '<a href="https://example.com/_matrix/media/r0/download/example.com/asdf">example.png</a>', + }, + { + description: "Normal formatted body", + event: { + type: MatrixSDK.EventType.RoomMessage, + content: { + body: "foo bar", + msgtype: MatrixSDK.MsgType.Text, + format: "org.matrix.custom.html", + formatted_body: "<p>foo bar</p>", + }, + sender: "@bar:example.com", + }, + result: "<p>foo bar</p>", + }, + { + description: "Inline image", + event: { + type: MatrixSDK.EventType.RoomMessage, + content: { + body: ":emote:", + msgtype: MatrixSDK.MsgType.Text, + format: "org.matrix.custom.html", + formatted_body: '<img alt=":emote:" src="mxc://example.com/emote.png">', + }, + sender: "@bar:example.com", + }, + result: + '<a href="https://example.com/_matrix/media/r0/download/example.com/emote.png">:emote:</a>', + }, + { + description: "Non-mxc attachment", + event: { + type: MatrixSDK.EventType.RoomMessage, + content: { + body: "foo.png", + msgtype: MatrixSDK.MsgType.Image, + url: "https://example.com/image.png", + }, + sender: "@bar:example.com", + }, + result: "foo.png", + }, + { + description: "Fallback to normal body", + event: { + type: MatrixSDK.EventType.RoomMessage, + content: { + body: "hello world <!>", + msgtype: MatrixSDK.MsgType.Notice, + }, + sender: "@bar:example.com", + }, + result: "hello world <!>", + }, + { + description: "Colored text", + event: { + type: MatrixSDK.EventType.RoomMessage, + content: { + body: "rainbow", + msgtype: MatrixSDK.MsgType.Text, + format: "org.matrix.custom.html", + formatted_body: + '<font data-mx-color="ff0000">ra</font><span data-mx-color="00ff00">inb</span><i data-mx-color="0000ff">ow</i>', + }, + sender: "@bar:example.com", + }, + result: + '<font style="color: rgb(255, 0, 0);">ra</font><span style="color: rgb(0, 255, 0);">inb</span><i data-mx-color="0000ff">ow</i>', + }, + { + description: "Unsent event", + event: { + type: MatrixSDK.EventType.RoomMessage, + content: { + body: "foo", + msgtype: MatrixSDK.MsgType.Text, + }, + sender: "@bar:example.com", + status: MatrixSDK.EventStatus.NOT_SENT, + }, + result: "", + }, + { + description: "Redacted event", + event: { + type: MatrixSDK.EventType.RoomMessage, + content: {}, + sender: "@bar:example.com", + redacted: true, + }, + result: _("message.redacted"), + }, + { + description: "Tombstone", + event: { + type: MatrixSDK.EventType.RoomTombstone, + content: { + body: "tombstone", + }, + sender: "@bar:example.com", + }, + result: "tombstone", + }, + { + description: "Encryption start", + event: { + type: MatrixSDK.EventType.RoomEncryption, + content: {}, + sender: "@bar:example.com", + }, + isGetTextForEvent: true, + }, + { + description: "Reaction", + event: { + type: MatrixSDK.EventType.Reaction, + content: { + ["m.relates_to"]: { + rel_type: MatrixSDK.RelationType.Annotation, + event_id: "!event:example.com", + key: "🐦", + }, + }, + sender: "@bar:example.com", + }, + getEventResult: { + id: "!event:example.com", + type: MatrixSDK.EventType.RoomMessage, + content: { + msgtype: MatrixSDK.MsgType.Text, + body: "lorem ipsum!", + }, + sender: "@foo:example.com", + }, + result: _( + "message.reaction", + '<span class="ib-person">@bar:example.com</span>', + '<span class="ib-person">@foo:example.com</span>', + "🐦" + ), + }, + { + description: "URL encoded mention", + event: { + type: MatrixSDK.EventType.RoomMessage, + content: { + msgtype: MatrixSDK.MsgType.Text, + body: `@foo:example.com dolor sit amet`, + format: "org.matrix.custom.html", + formatted_body: `<a href="https://matrix.to/#/%40foo%3Aexample.com">Foo</a> dolor sit amet`, + }, + sender: "@bar:example.com", + }, + result: '<span class="ib-person">@foo:example.com</span> dolor sit amet', + }, +]; + +add_task(function test_plainBody() { + for (const fixture of PLAIN_FIXTURES) { + const event = makeEvent(fixture.event); + const result = MatrixMessageContent.getIncomingPlain( + event, + "https://example.com", + eventId => { + if (fixture.getEventResult) { + equal( + eventId, + fixture.getEventResult.id, + `${fixture.description}: getEvent event ID` + ); + return makeEvent(fixture.getEventResult); + } + return undefined; + } + ); + if (fixture.isGetTextForEvent) { + equal(result, getMatrixTextForEvent(event)); + } else { + equal(result, fixture.result, fixture.description); + } + } +}); + +add_task(function test_htmlBody() { + for (const fixture of HTML_FIXTURES) { + const event = makeEvent(fixture.event); + const result = MatrixMessageContent.getIncomingHTML( + event, + "https://example.com", + eventId => { + if (fixture.getEventResult) { + equal( + eventId, + fixture.getEventResult.id, + `${fixture.description}: getEvent event ID` + ); + return makeEvent(fixture.getEventResult); + } + return undefined; + } + ); + if (fixture.isGetTextForEvent) { + equal(result, getMatrixTextForEvent(event)); + } else { + equal(result, fixture.result, fixture.description); + } + } +}); diff --git a/comm/chat/protocols/matrix/test/test_matrixPowerLevels.js b/comm/chat/protocols/matrix/test/test_matrixPowerLevels.js new file mode 100644 index 0000000000..40237664a3 --- /dev/null +++ b/comm/chat/protocols/matrix/test/test_matrixPowerLevels.js @@ -0,0 +1,204 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +var { MatrixPowerLevels } = ChromeUtils.importESModule( + "resource:///modules/matrixPowerLevels.sys.mjs" +); +var { l10nHelper } = ChromeUtils.importESModule( + "resource:///modules/imXPCOMUtils.sys.mjs" +); +var _ = l10nHelper("chrome://chat/locale/matrix.properties"); + +const TO_TEXT_FIXTURES = [ + { + level: MatrixPowerLevels.user, + defaultLevel: MatrixPowerLevels.user, + result: _( + "powerLevel.detailed", + _("powerLevel.default"), + MatrixPowerLevels.user + ), + name: "Default power level for default 0", + }, + { + level: MatrixPowerLevels.user, + defaultLevel: 10, + result: _( + "powerLevel.detailed", + _("powerLevel.restricted"), + MatrixPowerLevels.user + ), + name: "Restricted power level", + }, + { + level: 10, + defaultLevel: 10, + result: _("powerLevel.detailed", _("powerLevel.default"), 10), + name: "Default power level for default 10", + }, + { + level: MatrixPowerLevels.moderator, + defaultLevel: MatrixPowerLevels.user, + result: _( + "powerLevel.detailed", + _("powerLevel.moderator"), + MatrixPowerLevels.moderator + ), + name: "Moderator", + }, + { + level: MatrixPowerLevels.admin, + defaultLevel: MatrixPowerLevels.user, + result: _( + "powerLevel.detailed", + _("powerLevel.admin"), + MatrixPowerLevels.admin + ), + name: "Admin", + }, + { + level: 25, + defaultLevel: MatrixPowerLevels.user, + result: _("powerLevel.detailed", _("powerLevel.custom"), 25), + name: "Custom power level 25", + }, +]; +const GET_EVENT_LEVEL_FIXTURES = [ + { + powerLevels: undefined, + expected: 0, + }, + { + powerLevels: {}, + expected: 0, + }, + { + powerLevels: { + events_default: 10, + }, + expected: 10, + }, + { + powerLevels: { + events_default: Infinity, + }, + expected: 0, + }, + { + powerLevels: { + events_default: "foo", + }, + expected: 0, + }, + { + powerLevels: { + events_default: 0, + events: {}, + }, + expected: 0, + }, + { + powerLevels: { + events_default: 0, + events: { + [MatrixSDK.EventType.RoomMessage]: 0, + }, + }, + expected: 0, + }, + { + powerLevels: { + events_default: 0, + events: { + [MatrixSDK.EventType.RoomMessage]: Infinity, + }, + }, + expected: 0, + }, + { + powerLevels: { + events_default: 0, + events: { + [MatrixSDK.EventType.RoomMessage]: "foo", + }, + }, + expected: 0, + }, + { + powerLevels: { + events_default: 0, + events: { + [MatrixSDK.EventType.RoomMessage]: 10, + }, + }, + expected: 10, + }, +]; + +add_task(async function testToText() { + for (const fixture of TO_TEXT_FIXTURES) { + const result = MatrixPowerLevels.toText( + fixture.level, + fixture.defaultLevel + ); + equal(result, fixture.result); + } +}); + +add_task(async function testGetUserDefaultLevel() { + equal(MatrixPowerLevels.getUserDefaultLevel(), 0); + equal(MatrixPowerLevels.getUserDefaultLevel({}), 0); + equal( + MatrixPowerLevels.getUserDefaultLevel({ + users_default: 10, + }), + 10 + ); + equal( + MatrixPowerLevels.getUserDefaultLevel({ + users_default: Infinity, + }), + 0 + ); + equal( + MatrixPowerLevels.getUserDefaultLevel({ + users_default: "foo", + }), + 0 + ); +}); + +add_task(async function testGetEventDefaultLevel() { + equal(MatrixPowerLevels.getEventDefaultLevel(), 0); + equal(MatrixPowerLevels.getEventDefaultLevel({}), 0); + equal( + MatrixPowerLevels.getEventDefaultLevel({ + events_default: 10, + }), + 10 + ); + equal( + MatrixPowerLevels.getEventDefaultLevel({ + events_default: Infinity, + }), + 0 + ); + equal( + MatrixPowerLevels.getEventDefaultLevel({ + events_default: "foo", + }), + 0 + ); +}); + +add_task(async function testGetEventLevel() { + for (const eventLevelTest of GET_EVENT_LEVEL_FIXTURES) { + equal( + MatrixPowerLevels.getEventLevel( + eventLevelTest.powerLevels, + MatrixSDK.EventType.RoomMessage + ), + eventLevelTest.expected + ); + } +}); diff --git a/comm/chat/protocols/matrix/test/test_matrixRoom.js b/comm/chat/protocols/matrix/test/test_matrixRoom.js new file mode 100644 index 0000000000..3e4c72a19a --- /dev/null +++ b/comm/chat/protocols/matrix/test/test_matrixRoom.js @@ -0,0 +1,928 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +const { setTimeout, clearTimeout } = ChromeUtils.importESModule( + "resource://gre/modules/Timer.sys.mjs" +); + +loadMatrix(); + +add_task(async function test_initRoom() { + const roomStub = getRoom(true); + equal(typeof roomStub._resolveInitializer, "function"); + ok(roomStub._initialized); + await roomStub._initialized; + roomStub.forget(); +}); + +add_task(async function test_initRoom_withSpace() { + const roomStub = getRoom(true, "#test:example.com", (target, key) => { + if (key === "isSpaceRoom") { + return () => true; + } + return null; + }); + ok(roomStub._initialized); + ok(roomStub.left); + await roomStub._initialized; + roomStub.forget(); +}); + +add_task(function test_replaceRoom() { + const roomStub = { + __proto__: MatrixRoom.prototype, + _resolveInitializer() { + this.initialized = true; + }, + _mostRecentEventId: "foo", + _joiningLocks: new Set(), + }; + const newRoom = {}; + MatrixRoom.prototype.replaceRoom.call(roomStub, newRoom); + strictEqual(roomStub._replacedBy, newRoom); + ok(roomStub.initialized); + equal(newRoom._mostRecentEventId, roomStub._mostRecentEventId); +}); + +add_task(async function test_waitForRoom() { + const roomStub = { + _initialized: Promise.resolve(), + }; + const awaitedRoom = await MatrixRoom.prototype.waitForRoom.call(roomStub); + strictEqual(awaitedRoom, roomStub); +}); + +add_task(async function test_waitForRoomReplaced() { + const roomStub = getRoom(true); + const newRoom = { + waitForRoom() { + return Promise.resolve("success"); + }, + }; + MatrixRoom.prototype.replaceRoom.call(roomStub, newRoom); + const awaitedRoom = await MatrixRoom.prototype.waitForRoom.call(roomStub); + equal(awaitedRoom, "success"); + roomStub.forget(); +}); + +add_task(function test_addEventRedacted() { + const event = makeEvent({ + sender: "@user:example.com", + redacted: true, + redaction: { + event_id: 2, + type: MatrixSDK.EventType.RoomRedaction, + }, + type: MatrixSDK.EventType.RoomMessage, + }); + let updatedMessage; + const roomStub = { + _account: { + userId: "@test:example.com", + _client: { + getHomeserverUrl() { + return "https://example.com/"; + }, + }, + }, + updateMessage(sender, message, opts) { + updatedMessage = { + sender, + message, + opts, + }; + }, + }; + MatrixRoom.prototype.addEvent.call(roomStub, event); + equal(roomStub._mostRecentEventId, 2); + equal(typeof updatedMessage, "object"); + ok(!updatedMessage.opts.system); + ok(updatedMessage.opts.deleted); + equal(typeof updatedMessage.message, "string"); + equal(updatedMessage.sender, "@user:example.com"); +}); + +add_task(function test_addEventMessageIncoming() { + const event = makeEvent({ + sender: "@user:example.com", + content: { + body: "foo", + msgtype: MatrixSDK.MsgType.Text, + }, + type: MatrixSDK.EventType.RoomMessage, + }); + const roomStub = { + _account: { + userId: "@test:example.com", + _client: { + getHomeserverUrl() { + return "https://example.com/"; + }, + }, + }, + _eventsWaitingForDecryption: new Set(), + writeMessage(who, message, options) { + this.who = who; + this.message = message; + this.options = options; + }, + }; + MatrixRoom.prototype.addEvent.call(roomStub, event); + equal(roomStub.who, "@user:example.com"); + equal(roomStub.message, "foo"); + ok(!roomStub.options.system); + ok(!roomStub.options.delayed); + equal(roomStub._mostRecentEventId, 0); +}); + +add_task(function test_addEventMessageOutgoing() { + const event = makeEvent({ + sender: "@test:example.com", + content: { + body: "foo", + msgtype: MatrixSDK.MsgType.Text, + }, + type: MatrixSDK.EventType.RoomMessage, + }); + const roomStub = { + _account: { + userId: "@test:example.com", + _client: { + getHomeserverUrl() { + return "https://example.com"; + }, + }, + }, + _eventsWaitingForDecryption: new Set(), + writeMessage(who, message, options) { + this.who = who; + this.message = message; + this.options = options; + }, + }; + MatrixRoom.prototype.addEvent.call(roomStub, event); + equal(roomStub.who, "@test:example.com"); + equal(roomStub.message, "foo"); + ok(!roomStub.options.system); + ok(!roomStub.options.delayed); + equal(roomStub._mostRecentEventId, 0); +}); + +add_task(function test_addEventMessageEmote() { + const event = makeEvent({ + sender: "@user:example.com", + content: { + body: "foo", + msgtype: MatrixSDK.MsgType.Emote, + }, + type: MatrixSDK.EventType.RoomMessage, + }); + const roomStub = { + _account: { + userId: "@test:example.com", + _client: { + getHomeserverUrl() { + return "https://example.com"; + }, + }, + }, + _eventsWaitingForDecryption: new Set(), + writeMessage(who, message, options) { + this.who = who; + this.message = message; + this.options = options; + }, + }; + MatrixRoom.prototype.addEvent.call(roomStub, event); + equal(roomStub.who, "@user:example.com"); + equal(roomStub.message, "foo"); + ok(roomStub.options.action); + ok(!roomStub.options.system); + ok(!roomStub.options.delayed); + equal(roomStub._mostRecentEventId, 0); +}); + +add_task(function test_addEventMessageDelayed() { + const event = makeEvent({ + sender: "@user:example.com", + content: { + body: "foo", + msgtype: MatrixSDK.MsgType.Text, + }, + type: MatrixSDK.EventType.RoomMessage, + }); + const roomStub = { + _account: { + userId: "@test:example.com", + _client: { + getHomeserverUrl() { + return "https://example.com"; + }, + }, + }, + _eventsWaitingForDecryption: new Set(), + writeMessage(who, message, options) { + this.who = who; + this.message = message; + this.options = options; + }, + }; + MatrixRoom.prototype.addEvent.call(roomStub, event, true); + equal(roomStub.who, "@user:example.com"); + equal(roomStub.message, "foo"); + ok(!roomStub.options.system); + ok(roomStub.options.delayed); + equal(roomStub._mostRecentEventId, 0); +}); + +add_task(function test_addEventTopic() { + const event = makeEvent({ + type: MatrixSDK.EventType.RoomTopic, + id: 1, + content: { + topic: "foo bar", + }, + sender: "@user:example.com", + }); + const roomStub = { + _account: { + userId: "@test:example.com", + _client: { + getHomeserverUrl() { + return "https://example.com/"; + }, + }, + }, + _eventsWaitingForDecryption: new Set(), + setTopic(topic, who) { + this.who = who; + this.topic = topic; + }, + }; + MatrixRoom.prototype.addEvent.call(roomStub, event); + equal(roomStub.who, "@user:example.com"); + equal(roomStub.topic, "foo bar"); + equal(roomStub._mostRecentEventId, 1); +}); + +add_task(async function test_addEventTombstone() { + const event = makeEvent({ + type: MatrixSDK.EventType.RoomTombstone, + id: 1, + content: { + body: "updated room", + replacement_room: "!new_room:example.com", + }, + sender: "@test:example.com", + }); + const conversation = getRoom(true); + const newText = waitForNotification(conversation, "new-text"); + conversation.addEvent(event); + const { subject: message } = await newText; + const newConversation = await conversation.waitForRoom(); + equal(newConversation.normalizedName, event.getContent().replacement_room); + equal(message.who, event.getSender()); + equal(message.message, event.getContent().body); + ok(message.system); + ok(message.incoming); + ok(!conversation._account); + newConversation.forget(); +}); + +add_task(function test_forgetWith_close() { + const roomList = new Map(); + const roomStub = { + closeDm() { + this.closeCalled = true; + }, + _roomId: "foo", + _account: { + roomList, + }, + // stubs for jsProtoHelper implementations + addObserver() {}, + unInit() {}, + _releaseJoiningLock(lock) { + this.releasedLock = lock; + }, + }; + roomList.set(roomStub._roomId, roomStub); + IMServices.conversations.addConversation(roomStub); + + MatrixRoom.prototype.forget.call(roomStub); + ok(!roomList.has(roomStub._roomId)); + ok(roomStub.closeCalled); + equal(roomStub.releasedLock, "roomInit", "Released roomInit lock"); +}); + +add_task(function test_forgetWithout_close() { + const roomList = new Map(); + const roomStub = { + isChat: true, + _roomId: "foo", + _account: { + roomList, + }, + // stubs for jsProtoHelper implementations + addObserver() {}, + unInit() {}, + _releaseJoiningLock(lock) { + this.releasedLock = lock; + }, + }; + roomList.set(roomStub._roomId, roomStub); + IMServices.conversations.addConversation(roomStub); + + MatrixRoom.prototype.forget.call(roomStub); + ok(!roomList.has(roomStub._roomId)); + equal(roomStub.releasedLock, "roomInit", "Released roomInit lock"); +}); + +add_task(function test_close() { + const roomStub = { + forget() { + this.forgetCalled = true; + }, + cleanUpOutgoingVerificationRequests() { + this.cleanUpCalled = true; + }, + _roomId: "foo", + _account: { + _client: { + leave(roomId) { + roomStub.leftRoom = roomId; + }, + }, + }, + }; + + MatrixRoom.prototype.close.call(roomStub); + equal(roomStub.leftRoom, roomStub._roomId); + ok(roomStub.forgetCalled); + ok(roomStub.cleanUpCalled); +}); + +add_task(function test_setTypingState() { + const roomStub = getRoom(true, "foo", { + sendTyping(roomId, isTyping) { + roomStub.typingRoomId = roomId; + roomStub.typing = isTyping; + return Promise.resolve(); + }, + }); + + roomStub._setTypingState(true); + equal(roomStub.typingRoomId, roomStub._roomId); + ok(roomStub.typing); + + roomStub._setTypingState(false); + equal(roomStub.typingRoomId, roomStub._roomId); + ok(!roomStub.typing); + + roomStub._setTypingState(true); + equal(roomStub.typingRoomId, roomStub._roomId); + ok(roomStub.typing); + + roomStub._cleanUpTimers(); + roomStub.forget(); +}); + +add_task(function test_setTypingStateDebounce() { + const roomStub = getRoom(true, "foo", { + sendTyping(roomId, isTyping) { + roomStub.typingRoomId = roomId; + roomStub.typing = isTyping; + return Promise.resolve(); + }, + }); + + roomStub._setTypingState(true); + equal(roomStub.typingRoomId, roomStub._roomId); + ok(roomStub.typing); + ok(roomStub._typingDebounce); + + roomStub.typing = false; + + roomStub._setTypingState(true); + equal(roomStub.typingRoomId, roomStub._roomId); + ok(!roomStub.typing); + ok(roomStub._typingDebounce); + + clearTimeout(roomStub._typingDebounce); + roomStub._typingDebounce = null; + + roomStub._setTypingState(true); + equal(roomStub.typingRoomId, roomStub._roomId); + ok(roomStub.typing); + + roomStub._cleanUpTimers(); + roomStub.forget(); +}); + +add_task(function test_cancelTypingTimer() { + const roomStub = { + _typingTimer: setTimeout(() => {}, 10000), // eslint-disable-line mozilla/no-arbitrary-setTimeout + }; + MatrixRoom.prototype._cancelTypingTimer.call(roomStub); + ok(!roomStub._typingTimer); +}); + +add_task(function test_cleanUpTimers() { + const roomStub = getRoom(true); + roomStub._typingTimer = setTimeout(() => {}, 10000); // eslint-disable-line mozilla/no-arbitrary-setTimeout + roomStub._typingDebounce = setTimeout(() => {}, 1000); // eslint-disable-line mozilla/no-arbitrary-setTimeout + roomStub._cleanUpTimers(); + ok(!roomStub._typingTimer); + ok(!roomStub._typingDebounce); + roomStub.forget(); +}); + +add_task(function test_finishedComposing() { + let typingState = true; + const roomStub = { + __proto__: MatrixRoom.prototype, + shouldSendTypingNotifications: false, + _roomId: "foo", + _account: { + _client: { + sendTyping(roomId, state) { + typingState = state; + return Promise.resolve(); + }, + }, + }, + }; + + MatrixRoom.prototype.finishedComposing.call(roomStub); + ok(typingState); + + roomStub.shouldSendTypingNotifications = true; + MatrixRoom.prototype.finishedComposing.call(roomStub); + ok(!typingState); +}); + +add_task(function test_sendTyping() { + let typingState = false; + const roomStub = getRoom(true, "foo", { + sendTyping(roomId, state) { + typingState = state; + return Promise.resolve(); + }, + }); + Services.prefs.setBoolPref("purple.conversations.im.send_typing", false); + + let result = roomStub.sendTyping("lorem ipsum"); + ok(!roomStub._typingTimer); + equal(result, Ci.prplIConversation.NO_TYPING_LIMIT); + ok(!typingState); + + Services.prefs.setBoolPref("purple.conversations.im.send_typing", true); + result = roomStub.sendTyping("lorem ipsum"); + ok(roomStub._typingTimer); + equal(result, Ci.prplIConversation.NO_TYPING_LIMIT); + ok(typingState); + + result = roomStub.sendTyping(""); + ok(!roomStub._typingTimer); + equal(result, Ci.prplIConversation.NO_TYPING_LIMIT); + ok(!typingState); + + roomStub._cleanUpTimers(); + roomStub.forget(); +}); + +add_task(function test_setInitialized() { + const roomStub = { + _resolveInitializer() { + this.calledResolve = true; + }, + _releaseJoiningLock(lock) { + this.releasedLock = lock; + }, + }; + MatrixRoom.prototype._setInitialized.call(roomStub); + ok(roomStub.calledResolve); + equal(roomStub.releasedLock, "roomInit", "Released roomInit lock"); +}); + +add_task(function test_addEventSticker() { + const date = new Date(); + const event = makeEvent({ + time: date, + sender: "@user:example.com", + type: MatrixSDK.EventType.Sticker, + content: { + body: "foo", + url: "mxc://example.com/sticker.png", + }, + }); + const roomStub = { + _account: { + userId: "@test:example.com", + _client: { + getHomeserverUrl() { + return "https://example.com"; + }, + }, + }, + _eventsWaitingForDecryption: new Set(), + writeMessage(who, message, options) { + this.who = who; + this.message = message; + this.options = options; + }, + }; + MatrixRoom.prototype.addEvent.call(roomStub, event); + equal(roomStub.who, "@user:example.com"); + equal( + roomStub.message, + "https://example.com/_matrix/media/r0/download/example.com/sticker.png" + ); + ok(!roomStub.options.system); + ok(!roomStub.options.delayed); + equal(roomStub._mostRecentEventId, 0); +}); + +add_task(function test_sendMsg() { + let isTyping = true; + let message; + const roomStub = getRoom(true, "#test:example.com", { + sendTyping(roomId, typing) { + equal(roomId, roomStub._roomId); + isTyping = typing; + return Promise.resolve(); + }, + sendTextMessage(roomId, threadId, msg) { + equal(roomId, roomStub._roomId); + equal(threadId, null); + message = msg; + return Promise.resolve(); + }, + }); + roomStub.dispatchMessage("foo bar"); + ok(!isTyping); + equal(message, "foo bar"); + roomStub._cleanUpTimers(); + roomStub.forget(); +}); + +add_task(function test_sendMsg_emote() { + let isTyping = true; + let message; + const roomStub = getRoom(true, "#test:example.com", { + sendTyping(roomId, typing) { + equal(roomId, roomStub._roomId); + isTyping = typing; + return Promise.resolve(); + }, + sendEmoteMessage(roomId, threadId, msg) { + equal(roomId, roomStub._roomId); + equal(threadId, null); + message = msg; + return Promise.resolve(); + }, + }); + roomStub.dispatchMessage("foo bar", true); + ok(!isTyping); + equal(message, "foo bar"); + roomStub._cleanUpTimers(); + roomStub.forget(); +}); + +add_task(function test_createMessage() { + const time = Date.now(); + const event = makeEvent({ + type: MatrixSDK.EventType.RoomMessage, + time, + sender: "@foo:example.com", + }); + const roomStub = getRoom(true, "#test:example.com", { + getPushActionsForEvent(eventToProcess) { + equal(eventToProcess, event); + return { + tweaks: { + highlight: true, + }, + }; + }, + }); + const message = roomStub.createMessage("@foo:example.com", "bar", { + event, + }); + equal(message.message, "bar"); + equal(message.who, "@foo:example.com"); + equal(message.conversation, roomStub); + ok(!message.outgoing); + ok(message.incoming); + equal(message.alias, "foo bar"); + ok(!message.isEncrypted); + ok(message.containsNick); + equal(message.time, Math.floor(time / 1000)); + equal(message.iconURL, "https://example.com/avatar"); + equal(message.remoteId, 0); + roomStub.forget(); +}); + +add_task(async function test_addEventWaitingForDecryption() { + const event = makeEvent({ + sender: "@user:example.com", + type: MatrixSDK.EventType.RoomMessageEncrypted, + shouldDecrypt: true, + }); + const roomStub = getRoom(true, "#test:example.com"); + const writePromise = waitForNotification(roomStub, "new-text"); + roomStub.addEvent(event); + const { subject: result } = await writePromise; + ok(!result.error, "Waiting for decryption message is not an error"); + ok(!result.system, "Waiting for decryption message is not system"); + roomStub.forget(); +}); + +add_task(async function test_addEventReplaceDecryptedEvent() { + //TODO need to emit event on event? + let spec = { + sender: "@user:example.com", + type: MatrixSDK.EventType.RoomMessage, + isEncrypted: true, + shouldDecrypt: true, + content: { + msgtype: MatrixSDK.MsgType.Text, + body: "foo", + }, + }; + const event = makeEvent(spec); + const roomStub = getRoom(true, "#test:example.com"); + const writePromise = waitForNotification(roomStub, "new-text"); + roomStub.addEvent(event); + const { subject: initialEvent } = await writePromise; + ok(!initialEvent.error, "Pending event is not an error"); + ok(!initialEvent.system, "Pending event is not a system message"); + equal( + initialEvent.who, + "@user:example.com", + "Pending message has correct sender" + ); + const updatePromise = waitForNotification(roomStub, "update-text"); + spec.shouldDecrypt = false; + event._listeners[MatrixSDK.MatrixEventEvent.Decrypted](event); + const { subject: result } = await updatePromise; + equal(result.who, "@user:example.com", "Correct message sender"); + equal(result.message, "foo", "Message contents displayed"); + roomStub.forget(); +}); + +add_task(async function test_addEventDecryptionError() { + const event = makeEvent({ + sender: "@user:example.com", + type: MatrixSDK.EventType.RoomMessageEncrypted, + content: { + msgtype: "m.bad.encrypted", + }, + }); + const roomStub = getRoom(true, "#test:example.com"); + const writePromise = waitForNotification(roomStub, "new-text"); + roomStub.addEvent(event); + const { subject: result } = await writePromise; + ok(result.error, "Message is an error"); + ok(!result.system, "Not displayed as system event"); + roomStub.forget(); +}); + +add_task(async function test_addEventPendingDecryption() { + const event = makeEvent({ + sender: "@user:example.com", + type: MatrixSDK.EventType.RoomMessageEncrypted, + decrypting: true, + }); + const roomStub = getRoom(true, "#test:example.com"); + const writePromise = waitForNotification(roomStub, "new-text"); + roomStub.addEvent(event); + const { subject: result } = await writePromise; + ok(!result.error, "Not marked as error"); + ok(!result.system, "Not displayed as system event"); + roomStub.forget(); +}); + +add_task(async function test_addEventRedaction() { + const event = makeEvent({ + sender: "@user:example.com", + id: 1443, + type: MatrixSDK.EventType.RoomRedaction, + }); + const roomStub = { + writeMessage() { + ok(false, "called writeMessage"); + }, + updateMessage() { + ok(false, "called updateMessage"); + }, + }; + MatrixRoom.prototype.addEvent.call(roomStub, event); + equal(roomStub._mostRecentEventId, undefined); +}); + +add_task(function test_encryptionStateUnavailable() { + const room = getRoom(true, "#test:example.com"); + equal( + room.encryptionState, + Ci.prplIConversation.ENCRYPTION_NOT_SUPPORTED, + "Encryption state is encryption not supported with crypto disabled" + ); + room.forget(); +}); + +add_task(function test_encryptionStateCanEncrypt() { + const room = getRoom(true, "#test:example.com", { + isCryptoEnabled() { + return true; + }, + }); + let maySendStateEvent = false; + room.room.currentState = { + mayClientSendStateEvent(eventType, client) { + equal( + eventType, + MatrixSDK.EventType.RoomEncryption, + "mayClientSendStateEvent called for room encryption" + ); + equal( + client, + room._account._client, + "mayClientSendStateEvent got the expected client" + ); + return maySendStateEvent; + }, + }; + equal( + room.encryptionState, + Ci.prplIConversation.ENCRYPTION_NOT_SUPPORTED, + "Encryption state is encryption not supported when state event can't be sent" + ); + maySendStateEvent = true; + equal( + room.encryptionState, + Ci.prplIConversation.ENCRYPTION_AVAILABLE, + "Encryption state is available" + ); + room.forget(); +}); + +add_task(async function test_encryptionStateOn() { + const room = getRoom(true, "#test:example.com", { + isCryptoEnabled() { + return true; + }, + isRoomEncrypted(roomId) { + return true; + }, + }); + room.room.currentState = { + mayClientSendStateEvent(eventType, client) { + equal( + eventType, + MatrixSDK.EventType.RoomEncryption, + "mayClientSendStateEvent called for room encryption" + ); + equal( + client, + room._account._client, + "mayClientSendStateEvent got the expected client" + ); + return false; + }, + }; + equal( + room.encryptionState, + Ci.prplIConversation.ENCRYPTION_ENABLED, + "Encryption state is enabled" + ); + room._hasUnverifiedDevices = false; + equal( + room.encryptionState, + Ci.prplIConversation.ENCRYPTION_TRUSTED, + "Encryption state is trusted" + ); + await Promise.resolve(); + room.forget(); +}); + +add_task(async function test_addEventReaction() { + const event = makeEvent({ + sender: "@user:example.com", + type: MatrixSDK.EventType.Reaction, + content: { + ["m.relates_to"]: { + rel_type: MatrixSDK.RelationType.Annotation, + event_id: "!event:example.com", + key: "🐦", + }, + }, + }); + let wroteMessage = false; + const roomStub = { + _account: { + userId: "@user:example.com", + _client: { + getHomeserverUrl() { + return "https://example.com/"; + }, + }, + }, + room: { + findEventById(id) { + equal(id, "!event:example.com", "Reading expected annotated event"); + return { + getSender() { + return "@foo:example.com"; + }, + }; + }, + }, + writeMessage(who, message, options) { + equal(who, "@user:example.com", "Correct sender for reaction"); + ok(message.includes("🐦"), "Message contains reaction content"); + ok(options.system, "reaction is a system message"); + wroteMessage = true; + }, + }; + MatrixRoom.prototype.addEvent.call(roomStub, event); + ok(wroteMessage, "Wrote reaction to conversation"); +}); + +add_task(async function test_removeParticipant() { + let roomMembers = [ + { + userId: "@foo:example.com", + }, + { + userId: "@bar:example.com", + }, + ]; + const room = getRoom(true, "#test:example.com", { + getJoinedMembers() { + return roomMembers; + }, + }); + for (const member of roomMembers) { + room.addParticipant(member); + } + equal(room._participants.size, 2, "Room has two participants"); + + const participantRemoved = waitForNotification(room, "chat-buddy-remove"); + room.removeParticipant(roomMembers.splice(1, 1)[0].userId); + const { subject } = await participantRemoved; + const participantsArray = Array.from( + subject.QueryInterface(Ci.nsISimpleEnumerator) + ); + equal(participantsArray.length, 1, "One participant is being removed"); + equal( + participantsArray[0].QueryInterface(Ci.nsISupportsString).data, + "@bar:example.com", + "The participant is being removed by its user ID" + ); + equal(room._participants.size, 1, "One participant is left"); + room.forget(); +}); + +add_task(function test_highlightForNotifications() { + const time = Date.now(); + const event = makeEvent({ + type: MatrixSDK.EventType.RoomMessage, + time, + sender: "@foo:example.com", + }); + const roomStub = getRoom(true, "#test:example.com", { + getPushActionsForEvent(eventToProcess) { + equal(eventToProcess, event); + return { + notify: true, + }; + }, + }); + const message = roomStub.createMessage("@foo:example.com", "bar", { + event, + }); + equal(message.message, "bar"); + equal(message.who, "@foo:example.com"); + equal(message.conversation, roomStub); + ok(!message.outgoing); + ok(message.incoming); + equal(message.alias, "foo bar"); + ok(message.containsNick); + roomStub.forget(); +}); + +function waitForNotification(target, expectedTopic) { + let promise = new Promise(resolve => { + let observer = { + observe(subject, topic, data) { + if (topic === expectedTopic) { + resolve({ subject, data }); + target.removeObserver(observer); + } + }, + }; + target.addObserver(observer); + }); + return promise; +} diff --git a/comm/chat/protocols/matrix/test/test_matrixTextForEvent.js b/comm/chat/protocols/matrix/test/test_matrixTextForEvent.js new file mode 100644 index 0000000000..feeab4edee --- /dev/null +++ b/comm/chat/protocols/matrix/test/test_matrixTextForEvent.js @@ -0,0 +1,834 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +var { getMatrixTextForEvent } = ChromeUtils.importESModule( + "resource:///modules/matrixTextForEvent.sys.mjs" +); +var { l10nHelper } = ChromeUtils.importESModule( + "resource:///modules/imXPCOMUtils.sys.mjs" +); +var _ = l10nHelper("chrome://chat/locale/matrix.properties"); + +function run_test() { + add_test(testGetTextForMatrixEvent); + run_next_test(); +} + +const SENDER = "@test:example.com"; +const FIXTURES = [ + { + event: makeEvent({ + type: MatrixSDK.EventType.RoomMember, + content: { + membership: "ban", + }, + target: { + userId: "@foo:example.com", + }, + sender: SENDER, + }), + result: _("message.banned", SENDER, "@foo:example.com"), + name: "Banned without reason", + }, + { + event: makeEvent({ + type: MatrixSDK.EventType.RoomMember, + content: { + membership: "ban", + reason: "test", + }, + target: { + userId: "@foo:example.com", + }, + sender: SENDER, + }), + result: _("message.bannedWithReason", SENDER, "@foo:example.com", "test"), + name: "Banned with reason", + }, + { + event: makeEvent({ + type: MatrixSDK.EventType.RoomMember, + content: { + membership: "invite", + third_party_invite: { + display_name: "bar", + }, + }, + target: { + userId: "@foo:example.com", + }, + sender: SENDER, + }), + result: _("message.acceptedInviteFor", "@foo:example.com", "bar"), + name: "Invite accepted by other user with display name", + }, + { + event: makeEvent({ + type: MatrixSDK.EventType.RoomMember, + content: { + membership: "invite", + third_party_invite: {}, + }, + target: { + userId: "@foo:example.com", + }, + sender: SENDER, + }), + result: _("message.acceptedInvite", "@foo:example.com"), + name: "Invite accepted by other user", + }, + { + event: makeEvent({ + type: MatrixSDK.EventType.RoomMember, + content: { + membership: "invite", + }, + target: { + userId: "@foo:example.com", + }, + sender: SENDER, + }), + result: _("message.invited", SENDER, "@foo:example.com"), + name: "User invited", + }, + { + event: makeEvent({ + type: MatrixSDK.EventType.RoomMember, + content: { + membership: "join", + displayname: "ipsum", + }, + prevContent: { + membership: "join", + displayname: "lorem", + }, + sender: SENDER, + }), + result: _("message.displayName.changed", SENDER, "lorem", "ipsum"), + name: "User changed their display name", + }, + { + event: makeEvent({ + type: MatrixSDK.EventType.RoomMember, + content: { + membership: "join", + displayname: "ipsum", + }, + prevContent: { + membership: "join", + }, + sender: SENDER, + }), + result: _("message.displayName.set", SENDER, "ipsum"), + name: "User set their display name", + }, + { + event: makeEvent({ + type: MatrixSDK.EventType.RoomMember, + content: { + membership: "join", + }, + prevContent: { + membership: "join", + displayname: "lorem", + }, + sender: SENDER, + }), + result: _("message.displayName.remove", SENDER, "lorem"), + name: "User removed their display name", + }, + { + event: makeEvent({ + type: MatrixSDK.EventType.RoomMember, + content: { + membership: "join", + }, + prevContent: { + membership: "join", + }, + sender: SENDER, + }), + result: null, + name: "Users join event was edited without relevant changes", + }, + { + event: makeEvent({ + type: MatrixSDK.EventType.RoomMember, + content: { + membership: "join", + }, + target: { + userId: "@foo:example.com", + }, + sender: SENDER, + }), + result: _("message.joined", "@foo:example.com"), + name: "Users joined", + }, + { + event: makeEvent({ + type: MatrixSDK.EventType.RoomMember, + content: { + membership: "leave", + }, + prevContent: { + membership: "invite", + }, + target: { + userId: "@test:example.com", + }, + sender: SENDER, + }), + result: _("message.rejectedInvite", "@test:example.com"), + name: "Invite rejected", + }, + { + event: makeEvent({ + type: MatrixSDK.EventType.RoomMember, + content: { + membership: "leave", + }, + prevContent: { + membership: "join", + }, + target: { + userId: "@test:example.com", + }, + sender: SENDER, + }), + result: _("message.left", "@test:example.com"), + name: "Left room", + }, + { + event: makeEvent({ + type: MatrixSDK.EventType.RoomMember, + content: { + membership: "leave", + }, + prevContent: { + membership: "ban", + }, + target: { + userId: "@target:example.com", + }, + sender: SENDER, + }), + result: _("message.unbanned", SENDER, "@target:example.com"), + name: "Unbanned", + }, + { + event: makeEvent({ + type: MatrixSDK.EventType.RoomMember, + content: { + membership: "leave", + }, + prevContent: { + membership: "join", + }, + target: { + userId: "@target:example.com", + }, + sender: SENDER, + }), + result: _("message.kicked", SENDER, "@target:example.com"), + name: "Kicked without reason", + }, + { + event: makeEvent({ + type: MatrixSDK.EventType.RoomMember, + content: { + membership: "leave", + reason: "lorem ipsum", + }, + prevContent: { + membership: "join", + }, + target: { + userId: "@target:example.com", + }, + sender: SENDER, + }), + result: _( + "message.kickedWithReason", + SENDER, + "@target:example.com", + "lorem ipsum" + ), + name: "Kicked with reason", + }, + { + event: makeEvent({ + type: MatrixSDK.EventType.RoomMember, + content: { + membership: "leave", + reason: "lorem ipsum", + }, + prevContent: { + membership: "invite", + }, + target: { + userId: "@target:example.com", + }, + sender: SENDER, + }), + result: _( + "message.withdrewInviteWithReason", + SENDER, + "@target:example.com", + "lorem ipsum" + ), + name: "Invite withdrawn with reason", + }, + { + event: makeEvent({ + type: MatrixSDK.EventType.RoomMember, + content: { + membership: "leave", + }, + prevContent: { + membership: "invite", + }, + target: { + userId: "@target:example.com", + }, + sender: SENDER, + }), + result: _("message.withdrewInvite", SENDER, "@target:example.com"), + name: "Invite withdrawn without reason", + }, + { + event: makeEvent({ + type: MatrixSDK.EventType.RoomMember, + content: { + membership: "leave", + }, + prevContent: { + membership: "leave", + }, + target: { + userId: "@target:example.com", + }, + sender: SENDER, + }), + result: null, + name: "No message for leave to leave", + }, + { + event: makeEvent({ + type: MatrixSDK.EventType.RoomPowerLevels, + sender: SENDER, + }), + result: null, + name: "No previous power levels", + }, + { + event: makeEvent({ + type: MatrixSDK.EventType.RoomPowerLevels, + content: { + users: { + "@test:example.com": 100, + }, + }, + prevContent: { + users: { + "@test:example.com": 100, + }, + }, + sender: SENDER, + }), + result: null, + name: "No user power level changes", + }, + { + event: makeEvent({ + type: MatrixSDK.EventType.RoomPowerLevels, + content: { + users: { + "@test:example.com": 100, + "@foo:example.com": 50, + }, + }, + prevContent: { + users: { + "@test:example.com": 100, + }, + }, + sender: SENDER, + }), + result: _( + "message.powerLevel.changed", + SENDER, + _( + "message.powerLevel.fromTo", + "@foo:example.com", + _("powerLevel.default") + " (0)", + _("powerLevel.moderator") + " (50)" + ) + ), + name: "Gave a user power levels", + }, + { + event: makeEvent({ + type: MatrixSDK.EventType.RoomPowerLevels, + content: { + users: { + "@test:example.com": 100, + "@foo:example.com": 50, + }, + users_default: 10, + }, + prevContent: { + users: { + "@test:example.com": 100, + }, + users_default: 10, + }, + sender: SENDER, + }), + result: _( + "message.powerLevel.changed", + SENDER, + _( + "message.powerLevel.fromTo", + "@foo:example.com", + _("powerLevel.default") + " (10)", + _("powerLevel.moderator") + " (50)" + ) + ), + name: "Gave a user power levels with default level", + }, + { + event: makeEvent({ + type: MatrixSDK.EventType.RoomPowerLevels, + content: { + users: { + "@test:example.com": 100, + "@foo:example.com": 10, + }, + users_default: 10, + }, + prevContent: { + users: { + "@test:example.com": 100, + "@foo:example.com": 0, + }, + users_default: 10, + }, + sender: SENDER, + }), + result: _( + "message.powerLevel.changed", + SENDER, + _( + "message.powerLevel.fromTo", + "@foo:example.com", + _("powerLevel.restricted") + " (0)", + _("powerLevel.default") + " (10)" + ) + ), + name: "Promote a restricted user to default", + }, + { + event: makeEvent({ + type: MatrixSDK.EventType.RoomPowerLevels, + content: { + users: { + "@test:example.com": 100, + "@foo:example.com": 100, + }, + users_default: 10, + }, + prevContent: { + users: { + "@test:example.com": 100, + "@foo:example.com": 50, + }, + }, + sender: SENDER, + }), + result: _( + "message.powerLevel.changed", + SENDER, + _( + "message.powerLevel.fromTo", + "@foo:example.com", + _("powerLevel.moderator") + " (50)", + _("powerLevel.admin") + " (100)" + ) + ), + name: "Prompted user from moderator to admin", + }, + { + event: makeEvent({ + type: MatrixSDK.EventType.RoomPowerLevels, + content: { + users: { + "@test:example.com": 100, + "@foo:example.com": 0, + }, + users_default: 0, + }, + prevContent: { + users: { + "@test:example.com": 100, + "@foo:example.com": 100, + }, + }, + sender: SENDER, + }), + result: _( + "message.powerLevel.changed", + SENDER, + _( + "message.powerLevel.fromTo", + "@foo:example.com", + _("powerLevel.admin") + " (100)", + _("powerLevel.default") + " (0)" + ) + ), + name: "Demote user from admin to default", + }, + { + event: makeEvent({ + type: MatrixSDK.EventType.RoomPowerLevels, + content: { + users: { + "@test:example.com": 100, + "@foo:example.com": 50, + "@bar:example.com": 0, + }, + users_default: 0, + }, + prevContent: { + users: { + "@test:example.com": 100, + "@foo:example.com": 0, + "@bar:example.com": 50, + }, + }, + sender: SENDER, + }), + result: _( + "message.powerLevel.changed", + SENDER, + _( + "message.powerLevel.fromTo", + "@foo:example.com", + _("powerLevel.default") + " (0)", + _("powerLevel.moderator") + " (50)" + ) + + ", " + + _( + "message.powerLevel.fromTo", + "@bar:example.com", + _("powerLevel.moderator") + " (50)", + _("powerLevel.default") + " (0)" + ) + ), + name: "Changed multiple users's power level", + }, + { + event: makeEvent({ + type: MatrixSDK.EventType.RoomName, + content: { + name: "test", + }, + sender: SENDER, + }), + result: _("message.roomName.changed", SENDER, "test"), + name: "Set room name", + }, + { + event: makeEvent({ + type: MatrixSDK.EventType.RoomName, + sender: SENDER, + }), + result: _("message.roomName.remove", SENDER), + name: "Remove room name", + }, + { + event: makeEvent({ + type: MatrixSDK.EventType.RoomGuestAccess, + content: { + guest_access: MatrixSDK.GuestAccess.Forbidden, + }, + sender: SENDER, + }), + result: _("message.guest.prevented", SENDER), + name: "Guest access forbidden", + }, + { + event: makeEvent({ + type: MatrixSDK.EventType.RoomGuestAccess, + content: { + guest_access: MatrixSDK.GuestAccess.CanJoin, + }, + sender: SENDER, + }), + result: _("message.guest.allowed", SENDER), + name: "Guest access allowed", + }, + { + event: makeEvent({ + type: MatrixSDK.EventType.RoomHistoryVisibility, + content: { + history_visibility: MatrixSDK.HistoryVisibility.WorldReadable, + }, + sender: SENDER, + }), + result: _("message.history.anyone", SENDER), + name: "History access granted to anyone", + }, + { + event: makeEvent({ + type: MatrixSDK.EventType.RoomHistoryVisibility, + content: { + history_visibility: MatrixSDK.HistoryVisibility.Shared, + }, + sender: SENDER, + }), + result: _("message.history.shared", SENDER), + name: "History access granted to members, including before they joined", + }, + { + event: makeEvent({ + type: MatrixSDK.EventType.RoomHistoryVisibility, + content: { + history_visibility: MatrixSDK.HistoryVisibility.Invited, + }, + sender: SENDER, + }), + result: _("message.history.invited", SENDER), + name: "History access granted to members, including invited", + }, + { + event: makeEvent({ + type: MatrixSDK.EventType.RoomHistoryVisibility, + content: { + history_visibility: MatrixSDK.HistoryVisibility.Joined, + }, + sender: SENDER, + }), + result: _("message.history.joined", SENDER), + name: "History access granted to members from the point they join", + }, + { + event: makeEvent({ + type: MatrixSDK.EventType.RoomCanonicalAlias, + content: { + alias: "#test:example.com", + }, + sender: SENDER, + }), + result: _("message.alias.main", SENDER, undefined, "#test:example.com"), + name: "Room alias added", + }, + { + event: makeEvent({ + type: MatrixSDK.EventType.RoomCanonicalAlias, + content: { + alias: "#test:example.com", + }, + prevContent: { + alias: "#old:example.com", + }, + sender: SENDER, + }), + result: _( + "message.alias.main", + SENDER, + "#old:example.com", + "#test:example.com" + ), + name: "Room alias changed", + }, + { + event: makeEvent({ + type: MatrixSDK.EventType.RoomCanonicalAlias, + content: { + alias: "#test:example.com", + alt_aliases: ["#foo:example.com"], + }, + prevContent: { + alias: "#test:example.com", + }, + sender: SENDER, + }), + result: _("message.alias.added", SENDER, "#foo:example.com"), + name: "Room alt alias added", + }, + { + event: makeEvent({ + type: MatrixSDK.EventType.RoomCanonicalAlias, + content: { + alias: "#test:example.com", + }, + prevContent: { + alias: "#test:example.com", + alt_aliases: ["#foo:example.com"], + }, + sender: SENDER, + }), + result: _("message.alias.removed", SENDER, "#foo:example.com"), + name: "Room alt alias removed", + }, + { + event: makeEvent({ + type: MatrixSDK.EventType.RoomCanonicalAlias, + content: { + alias: "#test:example.com", + alt_aliases: ["#bar:example.com"], + }, + prevContent: { + alias: "#test:example.com", + alt_aliases: ["#foo:example.com", "#bar:example.com"], + }, + sender: SENDER, + }), + result: _("message.alias.removed", SENDER, "#foo:example.com"), + name: "Room alt alias removed with multiple alts", + }, + { + event: makeEvent({ + type: MatrixSDK.EventType.RoomCanonicalAlias, + content: { + alias: "#test:example.com", + alt_aliases: ["#foo:example.com", "#bar:example.com"], + }, + prevContent: { + alias: "#test:example.com", + alt_aliases: ["#bar:example.com"], + }, + sender: SENDER, + }), + result: _("message.alias.added", SENDER, "#foo:example.com"), + name: "Room alt alias added with multiple alts", + }, + { + event: makeEvent({ + type: MatrixSDK.EventType.RoomCanonicalAlias, + content: { + alias: "#test:example.com", + alt_aliases: [ + "#foo:example.com", + "#bar:example.com", + "#baz:example.com", + ], + }, + prevContent: { + alias: "#test:example.com", + alt_aliases: ["#bar:example.com"], + }, + sender: SENDER, + }), + result: _( + "message.alias.added", + SENDER, + "#foo:example.com, #baz:example.com" + ), + name: "Multiple room alt aliases added with multiple alts", + }, + { + event: makeEvent({ + type: MatrixSDK.EventType.RoomCanonicalAlias, + content: { + alias: "#test:example.com", + alt_aliases: ["#foo:example.com", "#bar:example.com"], + }, + prevContent: { + alias: "#test:example.com", + alt_aliases: ["#bar:example.com", "#baz:example.com"], + }, + sender: SENDER, + }), + result: _( + "message.alias.removedAndAdded", + SENDER, + "#baz:example.com", + "#foo:example.com" + ), + name: "Room alias added and removed", + }, + { + event: makeEvent({ + type: MatrixSDK.EventType.RoomCanonicalAlias, + content: { + alias: "#test:example.com", + alt_aliases: [], + }, + prevContent: { + alias: "#test:example.com", + }, + sender: SENDER, + }), + result: null, + name: "No discernible changes to the room aliases", + }, + { + event: makeEvent({ + type: MatrixSDK.EventType.RoomMessage, + content: { + msgtype: MatrixSDK.MsgType.KeyVerificationRequest, + to: "@foo:example.com", + }, + sender: SENDER, + }), + result: _("message.verification.request2", SENDER, "@foo:example.com"), + name: "Inline key verification request", + }, + { + event: makeEvent({ + type: MatrixSDK.EventType.KeyVerificationRequest, + content: { + to: "@foo:example.com", + }, + sender: SENDER, + }), + result: _("message.verification.request2", SENDER, "@foo:example.com"), + name: "Key verification request", + }, + { + event: makeEvent({ + type: MatrixSDK.EventType.KeyVerificationCancel, + content: { + reason: "Lorem ipsum", + }, + sender: SENDER, + }), + result: _("message.verification.cancel2", SENDER, "Lorem ipsum"), + name: "Key verification cancelled", + }, + { + event: makeEvent({ + type: MatrixSDK.EventType.KeyVerificationDone, + sender: SENDER, + }), + result: _("message.verification.done"), + name: "Key verification done", + }, + { + event: makeEvent({ + type: MatrixSDK.EventType.RoomMessageEncrypted, + content: { + msgtype: "m.bad.encrypted", + }, + }), + result: _("message.decryptionError"), + name: "Decryption error", + }, + { + event: makeEvent({ + type: MatrixSDK.EventType.RoomEncryption, + }), + result: _("message.encryptionStart"), + name: "Encryption start", + }, +]; + +function testGetTextForMatrixEvent() { + for (const fixture of FIXTURES) { + const result = getMatrixTextForEvent(fixture.event); + equal(result, fixture.result, fixture.name); + } + run_next_test(); +} diff --git a/comm/chat/protocols/matrix/test/test_roomTypeChange.js b/comm/chat/protocols/matrix/test/test_roomTypeChange.js new file mode 100644 index 0000000000..df2bc39200 --- /dev/null +++ b/comm/chat/protocols/matrix/test/test_roomTypeChange.js @@ -0,0 +1,54 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +loadMatrix(); + +add_task(async function test_toDMConversation() { + const acc = getAccount({}); + const roomId = "#test:example.com"; + acc.isDirectRoom = rId => roomId === rId; + const conversation = new MatrixRoom(acc, true, roomId); + conversation.initRoom( + getClientRoom( + roomId, + { + guessDMUserId() { + return "@user:example.com"; + }, + // Avoid running searchForVerificationRequests + getMyMembership() { + return "leave"; + }, + }, + acc._client + ) + ); + await conversation.checkForUpdate(); + ok(!conversation.isChat); + conversation.forget(); +}); + +add_task(async function test_toGroupConversation() { + const acc = getAccount({}); + const roomId = "#test:example.com"; + acc.isDirectRoom = rId => roomId !== rId; + const conversation = new MatrixRoom(acc, false, roomId); + conversation.initRoom( + getClientRoom( + roomId, + { + guessDMUserId() { + return "@user:example.com"; + }, + // Avoid running searchForVerificationRequests + getMyMembership() { + return "leave"; + }, + }, + acc._client + ) + ); + await conversation.checkForUpdate(); + ok(conversation.isChat); + conversation.forget(); +}); diff --git a/comm/chat/protocols/matrix/test/xpcshell.ini b/comm/chat/protocols/matrix/test/xpcshell.ini new file mode 100644 index 0000000000..92bcc7168e --- /dev/null +++ b/comm/chat/protocols/matrix/test/xpcshell.ini @@ -0,0 +1,12 @@ +[DEFAULT] +head = head.js +tail = + +[test_matrixAccount.js] +[test_matrixCommands.js] +[test_matrixTextForEvent.js] +[test_matrixMessage.js] +[test_matrixMessageContent.js] +[test_matrixPowerLevels.js] +[test_matrixRoom.js] +[test_roomTypeChange.js] |