UNPKG

25.4 kBJavaScriptView Raw
1"use strict";
2import 'source-map-support/register';
3const sdk = require("../..");
4const RoomState = sdk.RoomState;
5const RoomMember = sdk.RoomMember;
6const utils = require("../test-utils");
7
8import expect from 'expect';
9
10describe("RoomState", function() {
11 const roomId = "!foo:bar";
12 const userA = "@alice:bar";
13 const userB = "@bob:bar";
14 const userC = "@cleo:bar";
15 const userLazy = "@lazy:bar";
16
17 let state;
18
19 beforeEach(function() {
20 utils.beforeEach(this); // eslint-disable-line babel/no-invalid-this
21 state = new RoomState(roomId);
22 state.setStateEvents([
23 utils.mkMembership({ // userA joined
24 event: true, mship: "join", user: userA, room: roomId,
25 }),
26 utils.mkMembership({ // userB joined
27 event: true, mship: "join", user: userB, room: roomId,
28 }),
29 utils.mkEvent({ // Room name is "Room name goes here"
30 type: "m.room.name", user: userA, room: roomId, event: true, content: {
31 name: "Room name goes here",
32 },
33 }),
34 utils.mkEvent({ // Room creation
35 type: "m.room.create", user: userA, room: roomId, event: true, content: {
36 creator: userA,
37 },
38 }),
39 ]);
40 });
41
42 describe("getMembers", function() {
43 it("should return an empty list if there are no members", function() {
44 state = new RoomState(roomId);
45 expect(state.getMembers().length).toEqual(0);
46 });
47
48 it("should return a member for each m.room.member event", function() {
49 const members = state.getMembers();
50 expect(members.length).toEqual(2);
51 // ordering unimportant
52 expect([userA, userB].indexOf(members[0].userId)).toNotEqual(-1);
53 expect([userA, userB].indexOf(members[1].userId)).toNotEqual(-1);
54 });
55 });
56
57 describe("getMember", function() {
58 it("should return null if there is no member", function() {
59 expect(state.getMember("@no-one:here")).toEqual(null);
60 });
61
62 it("should return a member if they exist", function() {
63 expect(state.getMember(userB)).toBeTruthy();
64 });
65
66 it("should return a member which changes as state changes", function() {
67 const member = state.getMember(userB);
68 expect(member.membership).toEqual("join");
69 expect(member.name).toEqual(userB);
70
71 state.setStateEvents([
72 utils.mkMembership({
73 room: roomId, user: userB, mship: "leave", event: true,
74 name: "BobGone",
75 }),
76 ]);
77
78 expect(member.membership).toEqual("leave");
79 expect(member.name).toEqual("BobGone");
80 });
81 });
82
83 describe("getSentinelMember", function() {
84 it("should return a member with the user id as name", function() {
85 expect(state.getSentinelMember("@no-one:here").name).toEqual("@no-one:here");
86 });
87
88 it("should return a member which doesn't change when the state is updated",
89 function() {
90 const preLeaveUser = state.getSentinelMember(userA);
91 state.setStateEvents([
92 utils.mkMembership({
93 room: roomId, user: userA, mship: "leave", event: true,
94 name: "AliceIsGone",
95 }),
96 ]);
97 const postLeaveUser = state.getSentinelMember(userA);
98
99 expect(preLeaveUser.membership).toEqual("join");
100 expect(preLeaveUser.name).toEqual(userA);
101
102 expect(postLeaveUser.membership).toEqual("leave");
103 expect(postLeaveUser.name).toEqual("AliceIsGone");
104 });
105 });
106
107 describe("getStateEvents", function() {
108 it("should return null if a state_key was specified and there was no match",
109 function() {
110 expect(state.getStateEvents("foo.bar.baz", "keyname")).toEqual(null);
111 });
112
113 it("should return an empty list if a state_key was not specified and there" +
114 " was no match", function() {
115 expect(state.getStateEvents("foo.bar.baz")).toEqual([]);
116 });
117
118 it("should return a list of matching events if no state_key was specified",
119 function() {
120 const events = state.getStateEvents("m.room.member");
121 expect(events.length).toEqual(2);
122 // ordering unimportant
123 expect([userA, userB].indexOf(events[0].getStateKey())).toNotEqual(-1);
124 expect([userA, userB].indexOf(events[1].getStateKey())).toNotEqual(-1);
125 });
126
127 it("should return a single MatrixEvent if a state_key was specified",
128 function() {
129 const event = state.getStateEvents("m.room.member", userA);
130 expect(event.getContent()).toEqual({
131 membership: "join",
132 });
133 });
134 });
135
136 describe("setStateEvents", function() {
137 it("should emit 'RoomState.members' for each m.room.member event", function() {
138 const memberEvents = [
139 utils.mkMembership({
140 user: "@cleo:bar", mship: "invite", room: roomId, event: true,
141 }),
142 utils.mkMembership({
143 user: "@daisy:bar", mship: "join", room: roomId, event: true,
144 }),
145 ];
146 let emitCount = 0;
147 state.on("RoomState.members", function(ev, st, mem) {
148 expect(ev).toEqual(memberEvents[emitCount]);
149 expect(st).toEqual(state);
150 expect(mem).toEqual(state.getMember(ev.getSender()));
151 emitCount += 1;
152 });
153 state.setStateEvents(memberEvents);
154 expect(emitCount).toEqual(2);
155 });
156
157 it("should emit 'RoomState.newMember' for each new member added", function() {
158 const memberEvents = [
159 utils.mkMembership({
160 user: "@cleo:bar", mship: "invite", room: roomId, event: true,
161 }),
162 utils.mkMembership({
163 user: "@daisy:bar", mship: "join", room: roomId, event: true,
164 }),
165 ];
166 let emitCount = 0;
167 state.on("RoomState.newMember", function(ev, st, mem) {
168 expect(state.getMember(mem.userId)).toEqual(mem);
169 expect(mem.userId).toEqual(memberEvents[emitCount].getSender());
170 expect(mem.membership).toBeFalsy(); // not defined yet
171 emitCount += 1;
172 });
173 state.setStateEvents(memberEvents);
174 expect(emitCount).toEqual(2);
175 });
176
177 it("should emit 'RoomState.events' for each state event", function() {
178 const events = [
179 utils.mkMembership({
180 user: "@cleo:bar", mship: "invite", room: roomId, event: true,
181 }),
182 utils.mkEvent({
183 user: userB, room: roomId, type: "m.room.topic", event: true,
184 content: {
185 topic: "boo!",
186 },
187 }),
188 utils.mkMessage({ // Not a state event
189 user: userA, room: roomId, event: true,
190 }),
191 ];
192 let emitCount = 0;
193 state.on("RoomState.events", function(ev, st) {
194 expect(ev).toEqual(events[emitCount]);
195 expect(st).toEqual(state);
196 emitCount += 1;
197 });
198 state.setStateEvents(events);
199 expect(emitCount).toEqual(2);
200 });
201
202 it("should call setPowerLevelEvent on each RoomMember for m.room.power_levels",
203 function() {
204 // mock up the room members
205 state.members[userA] = utils.mock(RoomMember);
206 state.members[userB] = utils.mock(RoomMember);
207
208 const powerLevelEvent = utils.mkEvent({
209 type: "m.room.power_levels", room: roomId, user: userA, event: true,
210 content: {
211 users_default: 10,
212 state_default: 50,
213 events_default: 25,
214 },
215 });
216
217 state.setStateEvents([powerLevelEvent]);
218
219 expect(state.members[userA].setPowerLevelEvent).toHaveBeenCalledWith(
220 powerLevelEvent,
221 );
222 expect(state.members[userB].setPowerLevelEvent).toHaveBeenCalledWith(
223 powerLevelEvent,
224 );
225 });
226
227 it("should call setPowerLevelEvent on a new RoomMember if power levels exist",
228 function() {
229 const memberEvent = utils.mkMembership({
230 mship: "join", user: userC, room: roomId, event: true,
231 });
232 const powerLevelEvent = utils.mkEvent({
233 type: "m.room.power_levels", room: roomId, user: userA, event: true,
234 content: {
235 users_default: 10,
236 state_default: 50,
237 events_default: 25,
238 users: {},
239 },
240 });
241
242 state.setStateEvents([powerLevelEvent]);
243 state.setStateEvents([memberEvent]);
244
245 // TODO: We do this because we don't DI the RoomMember constructor
246 // so we can't inject a mock :/ so we have to infer.
247 expect(state.members[userC]).toBeTruthy();
248 expect(state.members[userC].powerLevel).toEqual(10);
249 });
250
251 it("should call setMembershipEvent on the right RoomMember", function() {
252 // mock up the room members
253 state.members[userA] = utils.mock(RoomMember);
254 state.members[userB] = utils.mock(RoomMember);
255
256 const memberEvent = utils.mkMembership({
257 user: userB, mship: "leave", room: roomId, event: true,
258 });
259 state.setStateEvents([memberEvent]);
260
261 expect(state.members[userA].setMembershipEvent).toNotHaveBeenCalled();
262 expect(state.members[userB].setMembershipEvent).toHaveBeenCalledWith(
263 memberEvent, state,
264 );
265 });
266 });
267
268 describe("setOutOfBandMembers", function() {
269 it("should add a new member", function() {
270 const oobMemberEvent = utils.mkMembership({
271 user: userLazy, mship: "join", room: roomId, event: true,
272 });
273 state.markOutOfBandMembersStarted();
274 state.setOutOfBandMembers([oobMemberEvent]);
275 const member = state.getMember(userLazy);
276 expect(member.userId).toEqual(userLazy);
277 expect(member.isOutOfBand()).toEqual(true);
278 });
279
280 it("should have no effect when not in correct status", function() {
281 state.setOutOfBandMembers([utils.mkMembership({
282 user: userLazy, mship: "join", room: roomId, event: true,
283 })]);
284 expect(state.getMember(userLazy)).toBeFalsy();
285 });
286
287 it("should emit newMember when adding a member", function() {
288 const userLazy = "@oob:hs";
289 const oobMemberEvent = utils.mkMembership({
290 user: userLazy, mship: "join", room: roomId, event: true,
291 });
292 let eventReceived = false;
293 state.once('RoomState.newMember', (_, __, member) => {
294 expect(member.userId).toEqual(userLazy);
295 eventReceived = true;
296 });
297 state.markOutOfBandMembersStarted();
298 state.setOutOfBandMembers([oobMemberEvent]);
299 expect(eventReceived).toEqual(true);
300 });
301
302 it("should never overwrite existing members", function() {
303 const oobMemberEvent = utils.mkMembership({
304 user: userA, mship: "join", room: roomId, event: true,
305 });
306 state.markOutOfBandMembersStarted();
307 state.setOutOfBandMembers([oobMemberEvent]);
308 const memberA = state.getMember(userA);
309 expect(memberA.events.member.getId()).toNotEqual(oobMemberEvent.getId());
310 expect(memberA.isOutOfBand()).toEqual(false);
311 });
312
313 it("should emit members when updating a member", function() {
314 const doesntExistYetUserId = "@doesntexistyet:hs";
315 const oobMemberEvent = utils.mkMembership({
316 user: doesntExistYetUserId, mship: "join", room: roomId, event: true,
317 });
318 let eventReceived = false;
319 state.once('RoomState.members', (_, __, member) => {
320 expect(member.userId).toEqual(doesntExistYetUserId);
321 eventReceived = true;
322 });
323
324 state.markOutOfBandMembersStarted();
325 state.setOutOfBandMembers([oobMemberEvent]);
326 expect(eventReceived).toEqual(true);
327 });
328 });
329
330 describe("clone", function() {
331 it("should contain same information as original", function() {
332 // include OOB members in copy
333 state.markOutOfBandMembersStarted();
334 state.setOutOfBandMembers([utils.mkMembership({
335 user: userLazy, mship: "join", room: roomId, event: true,
336 })]);
337 const copy = state.clone();
338 // check individual members
339 [userA, userB, userLazy].forEach((userId) => {
340 const member = state.getMember(userId);
341 const memberCopy = copy.getMember(userId);
342 expect(member.name).toEqual(memberCopy.name);
343 expect(member.isOutOfBand()).toEqual(memberCopy.isOutOfBand());
344 });
345 // check member keys
346 expect(Object.keys(state.members)).toEqual(Object.keys(copy.members));
347 // check join count
348 expect(state.getJoinedMemberCount()).toEqual(copy.getJoinedMemberCount());
349 });
350
351 it("should mark old copy as not waiting for out of band anymore", function() {
352 state.markOutOfBandMembersStarted();
353 const copy = state.clone();
354 copy.setOutOfBandMembers([utils.mkMembership({
355 user: userA, mship: "join", room: roomId, event: true,
356 })]);
357 // should have no effect as it should be marked in status finished just like copy
358 state.setOutOfBandMembers([utils.mkMembership({
359 user: userLazy, mship: "join", room: roomId, event: true,
360 })]);
361 expect(state.getMember(userLazy)).toBeFalsy();
362 });
363
364 it("should return copy independent of original", function() {
365 const copy = state.clone();
366 copy.setStateEvents([utils.mkMembership({
367 user: userLazy, mship: "join", room: roomId, event: true,
368 })]);
369
370 expect(state.getMember(userLazy)).toBeFalsy();
371 expect(state.getJoinedMemberCount()).toEqual(2);
372 expect(copy.getJoinedMemberCount()).toEqual(3);
373 });
374 });
375
376 describe("setTypingEvent", function() {
377 it("should call setTypingEvent on each RoomMember", function() {
378 const typingEvent = utils.mkEvent({
379 type: "m.typing", room: roomId, event: true, content: {
380 user_ids: [userA],
381 },
382 });
383 // mock up the room members
384 state.members[userA] = utils.mock(RoomMember);
385 state.members[userB] = utils.mock(RoomMember);
386 state.setTypingEvent(typingEvent);
387
388 expect(state.members[userA].setTypingEvent).toHaveBeenCalledWith(
389 typingEvent,
390 );
391 expect(state.members[userB].setTypingEvent).toHaveBeenCalledWith(
392 typingEvent,
393 );
394 });
395 });
396
397 describe("maySendStateEvent", function() {
398 it("should say any member may send state with no power level event",
399 function() {
400 expect(state.maySendStateEvent('m.room.name', userA)).toEqual(true);
401 });
402
403 it("should say members with power >=50 may send state with power level event " +
404 "but no state default",
405 function() {
406 const powerLevelEvent = {
407 type: "m.room.power_levels", room: roomId, user: userA, event: true,
408 content: {
409 users_default: 10,
410 // state_default: 50, "intentionally left blank"
411 events_default: 25,
412 users: {
413 },
414 },
415 };
416 powerLevelEvent.content.users[userA] = 50;
417
418 state.setStateEvents([utils.mkEvent(powerLevelEvent)]);
419
420 expect(state.maySendStateEvent('m.room.name', userA)).toEqual(true);
421 expect(state.maySendStateEvent('m.room.name', userB)).toEqual(false);
422 });
423
424 it("should obey state_default",
425 function() {
426 const powerLevelEvent = {
427 type: "m.room.power_levels", room: roomId, user: userA, event: true,
428 content: {
429 users_default: 10,
430 state_default: 30,
431 events_default: 25,
432 users: {
433 },
434 },
435 };
436 powerLevelEvent.content.users[userA] = 30;
437 powerLevelEvent.content.users[userB] = 29;
438
439 state.setStateEvents([utils.mkEvent(powerLevelEvent)]);
440
441 expect(state.maySendStateEvent('m.room.name', userA)).toEqual(true);
442 expect(state.maySendStateEvent('m.room.name', userB)).toEqual(false);
443 });
444
445 it("should honour explicit event power levels in the power_levels event",
446 function() {
447 const powerLevelEvent = {
448 type: "m.room.power_levels", room: roomId, user: userA, event: true,
449 content: {
450 events: {
451 "m.room.other_thing": 76,
452 },
453 users_default: 10,
454 state_default: 50,
455 events_default: 25,
456 users: {
457 },
458 },
459 };
460 powerLevelEvent.content.users[userA] = 80;
461 powerLevelEvent.content.users[userB] = 50;
462
463 state.setStateEvents([utils.mkEvent(powerLevelEvent)]);
464
465 expect(state.maySendStateEvent('m.room.name', userA)).toEqual(true);
466 expect(state.maySendStateEvent('m.room.name', userB)).toEqual(true);
467
468 expect(state.maySendStateEvent('m.room.other_thing', userA)).toEqual(true);
469 expect(state.maySendStateEvent('m.room.other_thing', userB)).toEqual(false);
470 });
471 });
472
473 describe("getJoinedMemberCount", function() {
474 beforeEach(() => {
475 state = new RoomState(roomId);
476 });
477
478 it("should update after adding joined member", function() {
479 state.setStateEvents([
480 utils.mkMembership({event: true, mship: "join",
481 user: userA, room: roomId}),
482 ]);
483 expect(state.getJoinedMemberCount()).toEqual(1);
484 state.setStateEvents([
485 utils.mkMembership({event: true, mship: "join",
486 user: userC, room: roomId}),
487 ]);
488 expect(state.getJoinedMemberCount()).toEqual(2);
489 });
490 });
491
492 describe("getInvitedMemberCount", function() {
493 beforeEach(() => {
494 state = new RoomState(roomId);
495 });
496
497 it("should update after adding invited member", function() {
498 state.setStateEvents([
499 utils.mkMembership({event: true, mship: "invite",
500 user: userA, room: roomId}),
501 ]);
502 expect(state.getInvitedMemberCount()).toEqual(1);
503 state.setStateEvents([
504 utils.mkMembership({event: true, mship: "invite",
505 user: userC, room: roomId}),
506 ]);
507 expect(state.getInvitedMemberCount()).toEqual(2);
508 });
509 });
510
511 describe("setJoinedMemberCount", function() {
512 beforeEach(() => {
513 state = new RoomState(roomId);
514 });
515
516 it("should, once used, override counting members from state", function() {
517 state.setStateEvents([
518 utils.mkMembership({event: true, mship: "join",
519 user: userA, room: roomId}),
520 ]);
521 expect(state.getJoinedMemberCount()).toEqual(1);
522 state.setJoinedMemberCount(100);
523 expect(state.getJoinedMemberCount()).toEqual(100);
524 state.setStateEvents([
525 utils.mkMembership({event: true, mship: "join",
526 user: userC, room: roomId}),
527 ]);
528 expect(state.getJoinedMemberCount()).toEqual(100);
529 });
530
531 it("should, once used, override counting members from state, " +
532 "also after clone", function() {
533 state.setStateEvents([
534 utils.mkMembership({event: true, mship: "join",
535 user: userA, room: roomId}),
536 ]);
537 state.setJoinedMemberCount(100);
538 const copy = state.clone();
539 copy.setStateEvents([
540 utils.mkMembership({event: true, mship: "join",
541 user: userC, room: roomId}),
542 ]);
543 expect(state.getJoinedMemberCount()).toEqual(100);
544 });
545 });
546
547 describe("setInvitedMemberCount", function() {
548 beforeEach(() => {
549 state = new RoomState(roomId);
550 });
551
552 it("should, once used, override counting members from state", function() {
553 state.setStateEvents([
554 utils.mkMembership({event: true, mship: "invite",
555 user: userB, room: roomId}),
556 ]);
557 expect(state.getInvitedMemberCount()).toEqual(1);
558 state.setInvitedMemberCount(100);
559 expect(state.getInvitedMemberCount()).toEqual(100);
560 state.setStateEvents([
561 utils.mkMembership({event: true, mship: "invite",
562 user: userC, room: roomId}),
563 ]);
564 expect(state.getInvitedMemberCount()).toEqual(100);
565 });
566
567 it("should, once used, override counting members from state, " +
568 "also after clone", function() {
569 state.setStateEvents([
570 utils.mkMembership({event: true, mship: "invite",
571 user: userB, room: roomId}),
572 ]);
573 state.setInvitedMemberCount(100);
574 const copy = state.clone();
575 copy.setStateEvents([
576 utils.mkMembership({event: true, mship: "invite",
577 user: userC, room: roomId}),
578 ]);
579 expect(state.getInvitedMemberCount()).toEqual(100);
580 });
581 });
582
583 describe("maySendEvent", function() {
584 it("should say any member may send events with no power level event",
585 function() {
586 expect(state.maySendEvent('m.room.message', userA)).toEqual(true);
587 expect(state.maySendMessage(userA)).toEqual(true);
588 });
589
590 it("should obey events_default",
591 function() {
592 const powerLevelEvent = {
593 type: "m.room.power_levels", room: roomId, user: userA, event: true,
594 content: {
595 users_default: 10,
596 state_default: 30,
597 events_default: 25,
598 users: {
599 },
600 },
601 };
602 powerLevelEvent.content.users[userA] = 26;
603 powerLevelEvent.content.users[userB] = 24;
604
605 state.setStateEvents([utils.mkEvent(powerLevelEvent)]);
606
607 expect(state.maySendEvent('m.room.message', userA)).toEqual(true);
608 expect(state.maySendEvent('m.room.message', userB)).toEqual(false);
609
610 expect(state.maySendMessage(userA)).toEqual(true);
611 expect(state.maySendMessage(userB)).toEqual(false);
612 });
613
614 it("should honour explicit event power levels in the power_levels event",
615 function() {
616 const powerLevelEvent = {
617 type: "m.room.power_levels", room: roomId, user: userA, event: true,
618 content: {
619 events: {
620 "m.room.other_thing": 33,
621 },
622 users_default: 10,
623 state_default: 50,
624 events_default: 25,
625 users: {
626 },
627 },
628 };
629 powerLevelEvent.content.users[userA] = 40;
630 powerLevelEvent.content.users[userB] = 30;
631
632 state.setStateEvents([utils.mkEvent(powerLevelEvent)]);
633
634 expect(state.maySendEvent('m.room.message', userA)).toEqual(true);
635 expect(state.maySendEvent('m.room.message', userB)).toEqual(true);
636
637 expect(state.maySendMessage(userA)).toEqual(true);
638 expect(state.maySendMessage(userB)).toEqual(true);
639
640 expect(state.maySendEvent('m.room.other_thing', userA)).toEqual(true);
641 expect(state.maySendEvent('m.room.other_thing', userB)).toEqual(false);
642 });
643 });
644});