mirror of https://github.com/vector-im/riot-web
				
				
				
			
		
			
				
	
	
		
			302 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			JavaScript
		
	
	
			
		
		
	
	
			302 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			JavaScript
		
	
	
| import React from 'react';
 | |
| import ReactTestUtils from 'react-addons-test-utils';
 | |
| import ReactDOM from 'react-dom';
 | |
| import expect from 'expect';
 | |
| import lolex from 'lolex';
 | |
| 
 | |
| import * as TestUtils from 'test-utils';
 | |
| 
 | |
| import sdk from '../../../../src/index';
 | |
| import MatrixClientPeg from '../../../../src/MatrixClientPeg';
 | |
| 
 | |
| import {Room, RoomMember, User} from 'matrix-js-sdk';
 | |
| 
 | |
| function generateRoomId() {
 | |
|     return '!' + Math.random().toString().slice(2, 10) + ':domain';
 | |
| }
 | |
| 
 | |
| 
 | |
| describe('MemberList', () => {
 | |
|     function createRoom(opts) {
 | |
|         const room = new Room(generateRoomId(), null, client.getUserId());
 | |
|         if (opts) {
 | |
|             Object.assign(room, opts);
 | |
|         }
 | |
|         return room;
 | |
|     }
 | |
| 
 | |
|     let parentDiv = null;
 | |
|     let sandbox = null;
 | |
|     let client = null;
 | |
|     let root = null;
 | |
|     let clock = null;
 | |
|     let memberListRoom;
 | |
|     let memberList = null;
 | |
| 
 | |
|     let adminUsers = [];
 | |
|     let moderatorUsers = [];
 | |
|     let defaultUsers = [];
 | |
| 
 | |
|     beforeEach(function() {
 | |
|         TestUtils.beforeEach(this);
 | |
|         sandbox = TestUtils.stubClient(sandbox);
 | |
|         client = MatrixClientPeg.get();
 | |
|         client.hasLazyLoadMembersEnabled = () => false;
 | |
| 
 | |
|         clock = lolex.install();
 | |
| 
 | |
|         parentDiv = document.createElement('div');
 | |
|         document.body.appendChild(parentDiv);
 | |
| 
 | |
|         // Make room
 | |
|         memberListRoom = createRoom();
 | |
|         expect(memberListRoom.roomId).toBeTruthy();
 | |
| 
 | |
|         // Make users
 | |
|         adminUsers = [];
 | |
|         moderatorUsers = [];
 | |
|         defaultUsers = [];
 | |
|         const usersPerLevel = 2;
 | |
|         for (let i = 0; i < usersPerLevel; i++) {
 | |
|             const adminUser = new RoomMember(memberListRoom.roomId, `@admin${i}:localhost`);
 | |
|             adminUser.membership = "join";
 | |
|             adminUser.powerLevel = 100;
 | |
|             adminUser.user = new User(adminUser.userId);
 | |
|             adminUser.user.currentlyActive = true;
 | |
|             adminUser.user.presence = 'online';
 | |
|             adminUser.user.lastPresenceTs = 1000;
 | |
|             adminUser.user.lastActiveAgo = 10;
 | |
|             adminUsers.push(adminUser);
 | |
| 
 | |
|             const moderatorUser = new RoomMember(memberListRoom.roomId, `@moderator${i}:localhost`);
 | |
|             moderatorUser.membership = "join";
 | |
|             moderatorUser.powerLevel = 50;
 | |
|             moderatorUser.user = new User(moderatorUser.userId);
 | |
|             moderatorUser.user.currentlyActive = true;
 | |
|             moderatorUser.user.presence = 'online';
 | |
|             moderatorUser.user.lastPresenceTs = 1000;
 | |
|             moderatorUser.user.lastActiveAgo = 10;
 | |
|             moderatorUsers.push(moderatorUser);
 | |
| 
 | |
|             const defaultUser = new RoomMember(memberListRoom.roomId, `@default${i}:localhost`);
 | |
|             defaultUser.membership = "join";
 | |
|             defaultUser.powerLevel = 0;
 | |
|             defaultUser.user = new User(defaultUser.userId);
 | |
|             defaultUser.user.currentlyActive = true;
 | |
|             defaultUser.user.presence = 'online';
 | |
|             defaultUser.user.lastPresenceTs = 1000;
 | |
|             defaultUser.user.lastActiveAgo = 10;
 | |
|             defaultUsers.push(defaultUser);
 | |
|         }
 | |
| 
 | |
|         client.getRoom = (roomId) => {
 | |
|             if (roomId === memberListRoom.roomId) return memberListRoom;
 | |
|             else return null;
 | |
|         };
 | |
|         memberListRoom.currentState = {
 | |
|             members: {},
 | |
|             getStateEvents: () => [], // ignore 3pid invites
 | |
|         };
 | |
|         for (const member of [...adminUsers, ...moderatorUsers, ...defaultUsers]) {
 | |
|             memberListRoom.currentState.members[member.userId] = member;
 | |
|         }
 | |
| 
 | |
|         const MemberList = sdk.getComponent('views.rooms.MemberList');
 | |
|         const WrappedMemberList = TestUtils.wrapInMatrixClientContext(MemberList);
 | |
|         const gatherWrappedRef = (r) => {
 | |
|             memberList = r;
 | |
|         };
 | |
|         root = ReactDOM.render(<WrappedMemberList roomId={memberListRoom.roomId}
 | |
|                                                   wrappedRef={gatherWrappedRef} />, parentDiv);
 | |
|     });
 | |
| 
 | |
|     afterEach((done) => {
 | |
|         if (parentDiv) {
 | |
|             ReactDOM.unmountComponentAtNode(parentDiv);
 | |
|             parentDiv.remove();
 | |
|             parentDiv = null;
 | |
|         }
 | |
|         sandbox.restore();
 | |
| 
 | |
|         clock.uninstall();
 | |
| 
 | |
|         done();
 | |
|     });
 | |
| 
 | |
|     function expectOrderedByPresenceAndPowerLevel(memberTiles, isPresenceEnabled) {
 | |
|         let prevMember = null;
 | |
|         for (const tile of memberTiles) {
 | |
|             const memberA = prevMember;
 | |
|             const memberB = tile.props.member;
 | |
|             prevMember = memberB; // just in case an expect fails, set this early
 | |
|             if (!memberA) {
 | |
|                 continue;
 | |
|             }
 | |
| 
 | |
|             console.log("COMPARING A VS B:");
 | |
|             console.log(memberList.memberString(memberA));
 | |
|             console.log(memberList.memberString(memberB));
 | |
| 
 | |
|             const userA = memberA.user;
 | |
|             const userB = memberB.user;
 | |
| 
 | |
|             let groupChange = false;
 | |
| 
 | |
|             if (isPresenceEnabled) {
 | |
|                 const convertPresence = (p) => p === 'unavailable' ? 'online' : p;
 | |
|                 const presenceIndex = p => {
 | |
|                     const order = ['active', 'online', 'offline'];
 | |
|                     const idx = order.indexOf(convertPresence(p));
 | |
|                     return idx === -1 ? order.length : idx; // unknown states at the end
 | |
|                 };
 | |
| 
 | |
|                 const idxA = presenceIndex(userA.currentlyActive ? 'active' : userA.presence);
 | |
|                 const idxB = presenceIndex(userB.currentlyActive ? 'active' : userB.presence);
 | |
|                 console.log("Comparing presence groups...");
 | |
|                 expect(idxB).toBeGreaterThanOrEqual(idxA);
 | |
|                 groupChange = idxA !== idxB;
 | |
|             } else {
 | |
|                 console.log("Skipped presence groups");
 | |
|             }
 | |
| 
 | |
|             if (!groupChange) {
 | |
|                 console.log("Comparing power levels...");
 | |
|                 expect(memberA.powerLevel).toBeGreaterThanOrEqual(memberB.powerLevel);
 | |
|                 groupChange = memberA.powerLevel !== memberB.powerLevel;
 | |
|             } else {
 | |
|                 console.log("Skipping power level check due to group change");
 | |
|             }
 | |
| 
 | |
|             if (!groupChange) {
 | |
|                 if (isPresenceEnabled) {
 | |
|                     console.log("Comparing last active timestamp...");
 | |
|                     expect(userB.getLastActiveTs()).toBeLessThanOrEqual(userA.getLastActiveTs());
 | |
|                     groupChange = userA.getLastActiveTs() !== userB.getLastActiveTs();
 | |
|                 } else {
 | |
|                     console.log("Skipping last active timestamp");
 | |
|                 }
 | |
|             } else {
 | |
|                 console.log("Skipping last active timestamp check due to group change");
 | |
|             }
 | |
| 
 | |
|             if (!groupChange) {
 | |
|                 const nameA = memberA.name[0] === '@' ? memberA.name.substr(1) : memberA.name;
 | |
|                 const nameB = memberB.name[0] === '@' ? memberB.name.substr(1) : memberB.name;
 | |
|                 const nameCompare = nameB.localeCompare(nameA);
 | |
|                 console.log("Comparing name");
 | |
|                 expect(nameCompare).toBeGreaterThanOrEqual(0);
 | |
|             } else {
 | |
|                 console.log("Skipping name check due to group change");
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     function itDoesOrderMembersCorrectly(enablePresence) {
 | |
|         const MemberTile = sdk.getComponent("rooms.MemberTile");
 | |
|         describe('does order members correctly', () => {
 | |
|             // Note: even if presence is disabled, we still expect that the presence
 | |
|             // tests will pass. All expectOrderedByPresenceAndPowerLevel does is ensure
 | |
|             // the order is perceived correctly, regardless of what we did to the members.
 | |
| 
 | |
|             // Each of the 4 tests here is done to prove that the member list can meet
 | |
|             // all 4 criteria independently. Together, they should work.
 | |
| 
 | |
|             it('by presence state', () => {
 | |
|                 // Intentionally pick users that will confuse the power level sorting
 | |
|                 const activeUsers = [defaultUsers[0]];
 | |
|                 const onlineUsers = [adminUsers[0]];
 | |
|                 const offlineUsers = [...moderatorUsers, ...adminUsers.slice(1), ...defaultUsers.slice(1)];
 | |
|                 activeUsers.forEach((u) => {
 | |
|                     u.user.currentlyActive = true;
 | |
|                     u.user.presence = 'online';
 | |
|                 });
 | |
|                 onlineUsers.forEach((u) => {
 | |
|                     u.user.currentlyActive = false;
 | |
|                     u.user.presence = 'online';
 | |
|                 });
 | |
|                 offlineUsers.forEach((u) => {
 | |
|                     u.user.currentlyActive = false;
 | |
|                     u.user.presence = 'offline';
 | |
|                 });
 | |
| 
 | |
|                 // Bypass all the event listeners and skip to the good part
 | |
|                 memberList._showPresence = enablePresence;
 | |
|                 memberList._updateListNow();
 | |
| 
 | |
|                 const tiles = ReactTestUtils.scryRenderedComponentsWithType(root, MemberTile);
 | |
|                 expectOrderedByPresenceAndPowerLevel(tiles, enablePresence);
 | |
|             });
 | |
| 
 | |
|             it('by power level', () => {
 | |
|                 // We already have admin, moderator, and default users so leave them alone
 | |
| 
 | |
|                 // Bypass all the event listeners and skip to the good part
 | |
|                 memberList._showPresence = enablePresence;
 | |
|                 memberList._updateListNow();
 | |
| 
 | |
|                 const tiles = ReactTestUtils.scryRenderedComponentsWithType(root, MemberTile);
 | |
|                 expectOrderedByPresenceAndPowerLevel(tiles, enablePresence);
 | |
|             });
 | |
| 
 | |
|             it('by last active timestamp', () => {
 | |
|                 // Intentionally pick users that will confuse the power level sorting
 | |
|                 // lastActiveAgoTs == lastPresenceTs - lastActiveAgo
 | |
|                 const activeUsers = [defaultUsers[0]];
 | |
|                 const semiActiveUsers = [adminUsers[0]];
 | |
|                 const inactiveUsers = [...moderatorUsers, ...adminUsers.slice(1), ...defaultUsers.slice(1)];
 | |
|                 activeUsers.forEach((u) => {
 | |
|                     u.powerLevel = 100; // set everyone to the same PL to avoid running that check
 | |
|                     u.user.lastPresenceTs = 1000;
 | |
|                     u.user.lastActiveAgo = 0;
 | |
|                 });
 | |
|                 semiActiveUsers.forEach((u) => {
 | |
|                     u.powerLevel = 100;
 | |
|                     u.user.lastPresenceTs = 1000;
 | |
|                     u.user.lastActiveAgo = 50;
 | |
|                 });
 | |
|                 inactiveUsers.forEach((u) => {
 | |
|                     u.powerLevel = 100;
 | |
|                     u.user.lastPresenceTs = 1000;
 | |
|                     u.user.lastActiveAgo = 100;
 | |
|                 });
 | |
| 
 | |
|                 // Bypass all the event listeners and skip to the good part
 | |
|                 memberList._showPresence = enablePresence;
 | |
|                 memberList._updateListNow();
 | |
| 
 | |
|                 const tiles = ReactTestUtils.scryRenderedComponentsWithType(root, MemberTile);
 | |
|                 expectOrderedByPresenceAndPowerLevel(tiles, enablePresence);
 | |
|             });
 | |
| 
 | |
|             it('by name', () => {
 | |
|                 // Intentionally put everyone on the same level to force a name comparison
 | |
|                 const allUsers = [...adminUsers, ...moderatorUsers, ...defaultUsers];
 | |
|                 allUsers.forEach((u) => {
 | |
|                     u.user.currentlyActive = true;
 | |
|                     u.user.presence = "online";
 | |
|                     u.user.lastPresenceTs = 1000;
 | |
|                     u.user.lastActiveAgo = 0;
 | |
|                     u.powerLevel = 100;
 | |
|                 });
 | |
| 
 | |
|                 // Bypass all the event listeners and skip to the good part
 | |
|                 memberList._showPresence = enablePresence;
 | |
|                 memberList._updateListNow();
 | |
| 
 | |
|                 const tiles = ReactTestUtils.scryRenderedComponentsWithType(root, MemberTile);
 | |
|                 expectOrderedByPresenceAndPowerLevel(tiles, enablePresence);
 | |
|             });
 | |
|         });
 | |
|     }
 | |
| 
 | |
|     describe('when presence is enabled', () => {
 | |
|         itDoesOrderMembersCorrectly(true);
 | |
|     });
 | |
| 
 | |
|     describe('when presence is not enabled', () => {
 | |
|         itDoesOrderMembersCorrectly(false);
 | |
|     });
 | |
| });
 | |
| 
 | |
| 
 |