import * as utils from "../test-utils"; import {RoomMember} from "../../src/models/room-member"; describe("RoomMember", function() { const roomId = "!foo:bar"; const userA = "@alice:bar"; const userB = "@bertha:bar"; const userC = "@clarissa:bar"; let member; beforeEach(function() { member = new RoomMember(roomId, userA); }); describe("getAvatarUrl", function() { const hsUrl = "https://my.home.server"; it("should return the URL from m.room.member preferentially", function() { member.events.member = utils.mkEvent({ event: true, type: "m.room.member", skey: userA, room: roomId, user: userA, content: { membership: "join", avatar_url: "mxc://flibble/wibble", }, }); const url = member.getAvatarUrl(hsUrl); // we don't care about how the mxc->http conversion is done, other // than it contains the mxc body. expect(url.indexOf("flibble/wibble")).not.toEqual(-1); }); it("should return an identicon HTTP URL if allowDefault was set and there " + "was no m.room.member event", function() { const url = member.getAvatarUrl(hsUrl, 64, 64, "crop", true); expect(url.indexOf("http")).toEqual(0); // don't care about form }); it("should return nothing if there is no m.room.member and allowDefault=false", function() { const url = member.getAvatarUrl(hsUrl, 64, 64, "crop", false); expect(url).toEqual(null); }); }); describe("setPowerLevelEvent", function() { it("should set 'powerLevel' and 'powerLevelNorm'.", function() { const event = utils.mkEvent({ type: "m.room.power_levels", room: roomId, user: userA, content: { users_default: 20, users: { "@bertha:bar": 200, "@invalid:user": 10, // shouldn't barf on this. }, }, event: true, }); member.setPowerLevelEvent(event); expect(member.powerLevel).toEqual(20); expect(member.powerLevelNorm).toEqual(10); const memberB = new RoomMember(roomId, userB); memberB.setPowerLevelEvent(event); expect(memberB.powerLevel).toEqual(200); expect(memberB.powerLevelNorm).toEqual(100); }); it("should emit 'RoomMember.powerLevel' if the power level changes.", function() { const event = utils.mkEvent({ type: "m.room.power_levels", room: roomId, user: userA, content: { users_default: 20, users: { "@bertha:bar": 200, "@invalid:user": 10, // shouldn't barf on this. }, }, event: true, }); let emitCount = 0; member.on("RoomMember.powerLevel", function(emitEvent, emitMember) { emitCount += 1; expect(emitMember).toEqual(member); expect(emitEvent).toEqual(event); }); member.setPowerLevelEvent(event); expect(emitCount).toEqual(1); member.setPowerLevelEvent(event); // no-op expect(emitCount).toEqual(1); }); it("should honour power levels of zero.", function() { const event = utils.mkEvent({ type: "m.room.power_levels", room: roomId, user: userA, content: { users_default: 20, users: { "@alice:bar": 0, }, }, event: true, }); let emitCount = 0; // set the power level to something other than zero or we // won't get an event member.powerLevel = 1; member.on("RoomMember.powerLevel", function(emitEvent, emitMember) { emitCount += 1; expect(emitMember.userId).toEqual('@alice:bar'); expect(emitMember.powerLevel).toEqual(0); expect(emitEvent).toEqual(event); }); member.setPowerLevelEvent(event); expect(member.powerLevel).toEqual(0); expect(emitCount).toEqual(1); }); }); describe("setTypingEvent", function() { it("should set 'typing'", function() { member.typing = false; const memberB = new RoomMember(roomId, userB); memberB.typing = true; const memberC = new RoomMember(roomId, userC); memberC.typing = true; const event = utils.mkEvent({ type: "m.typing", user: userA, room: roomId, content: { user_ids: [ userA, userC, ], }, event: true, }); member.setTypingEvent(event); memberB.setTypingEvent(event); memberC.setTypingEvent(event); expect(member.typing).toEqual(true); expect(memberB.typing).toEqual(false); expect(memberC.typing).toEqual(true); }); it("should emit 'RoomMember.typing' if the typing state changes", function() { const event = utils.mkEvent({ type: "m.typing", room: roomId, content: { user_ids: [ userA, userC, ], }, event: true, }); let emitCount = 0; member.on("RoomMember.typing", function(ev, mem) { expect(mem).toEqual(member); expect(ev).toEqual(event); emitCount += 1; }); member.typing = false; member.setTypingEvent(event); expect(emitCount).toEqual(1); member.setTypingEvent(event); // no-op expect(emitCount).toEqual(1); }); }); describe("isOutOfBand", function() { it("should be set by markOutOfBand", function() { const member = new RoomMember(); expect(member.isOutOfBand()).toEqual(false); member.markOutOfBand(); expect(member.isOutOfBand()).toEqual(true); }); }); describe("setMembershipEvent", function() { const joinEvent = utils.mkMembership({ event: true, mship: "join", user: userA, room: roomId, name: "Alice", }); const inviteEvent = utils.mkMembership({ event: true, mship: "invite", user: userB, skey: userA, room: roomId, }); it("should set 'membership' and assign the event to 'events.member'.", function() { member.setMembershipEvent(inviteEvent); expect(member.membership).toEqual("invite"); expect(member.events.member).toEqual(inviteEvent); member.setMembershipEvent(joinEvent); expect(member.membership).toEqual("join"); expect(member.events.member).toEqual(joinEvent); }); it("should set 'name' based on user_id, displayname and room state", function() { const roomState = { getStateEvents: function(type) { if (type !== "m.room.member") { return []; } return [ utils.mkMembership({ event: true, mship: "join", room: roomId, user: userB, }), utils.mkMembership({ event: true, mship: "join", room: roomId, user: userC, name: "Alice", }), joinEvent, ]; }, getUserIdsWithDisplayName: function(displayName) { return [userA, userC]; }, }; expect(member.name).toEqual(userA); // default = user_id member.setMembershipEvent(joinEvent); expect(member.name).toEqual("Alice"); // prefer displayname member.setMembershipEvent(joinEvent, roomState); expect(member.name).not.toEqual("Alice"); // it should disambig. // user_id should be there somewhere expect(member.name.indexOf(userA)).not.toEqual(-1); }); it("should emit 'RoomMember.membership' if the membership changes", function() { let emitCount = 0; member.on("RoomMember.membership", function(ev, mem) { emitCount += 1; expect(mem).toEqual(member); expect(ev).toEqual(inviteEvent); }); member.setMembershipEvent(inviteEvent); expect(emitCount).toEqual(1); member.setMembershipEvent(inviteEvent); // no-op expect(emitCount).toEqual(1); }); it("should emit 'RoomMember.name' if the name changes", function() { let emitCount = 0; member.on("RoomMember.name", function(ev, mem) { emitCount += 1; expect(mem).toEqual(member); expect(ev).toEqual(joinEvent); }); member.setMembershipEvent(joinEvent); expect(emitCount).toEqual(1); member.setMembershipEvent(joinEvent); // no-op expect(emitCount).toEqual(1); }); it("should set 'name' to user_id if it is just whitespace", function() { const joinEvent = utils.mkMembership({ event: true, mship: "join", user: userA, room: roomId, name: " \u200b ", }); expect(member.name).toEqual(userA); // default = user_id member.setMembershipEvent(joinEvent); expect(member.name).toEqual(userA); // it should fallback because all whitespace }); it("should disambiguate users on a fuzzy displayname match", function() { const joinEvent = utils.mkMembership({ event: true, mship: "join", user: userA, room: roomId, name: "Alíce\u200b", // note diacritic and zero width char }); const roomState = { getStateEvents: function(type) { if (type !== "m.room.member") { return []; } return [ utils.mkMembership({ event: true, mship: "join", room: roomId, user: userC, name: "Alice", }), joinEvent, ]; }, getUserIdsWithDisplayName: function(displayName) { return [userA, userC]; }, }; expect(member.name).toEqual(userA); // default = user_id member.setMembershipEvent(joinEvent, roomState); expect(member.name).not.toEqual("Alíce"); // it should disambig. // user_id should be there somewhere expect(member.name.indexOf(userA)).not.toEqual(-1); }); }); });