import React from 'react'; import ReactTestUtils from 'react-dom/test-utils'; import ReactDOM from 'react-dom'; import { MatrixClient, Room, RoomMember } from 'matrix-js-sdk/src/matrix'; import * as TestUtils from '../../../test-utils'; import { MatrixClientPeg } from '../../../../src/MatrixClientPeg'; import dis from '../../../../src/dispatcher/dispatcher'; import DMRoomMap from '../../../../src/utils/DMRoomMap'; import { DefaultTagID } from "../../../../src/stores/room-list/models"; import RoomListStore, { RoomListStoreClass } from "../../../../src/stores/room-list/RoomListStore"; import RoomListLayoutStore from "../../../../src/stores/room-list/RoomListLayoutStore"; import RoomList from "../../../../src/components/views/rooms/RoomList"; import RoomSublist from "../../../../src/components/views/rooms/RoomSublist"; import RoomTile from "../../../../src/components/views/rooms/RoomTile"; function generateRoomId() { return '!' + Math.random().toString().slice(2, 10) + ':domain'; } describe('RoomList', () => { function createRoom(opts) { const room = new Room(generateRoomId(), MatrixClientPeg.get(), client.getUserId(), { // The room list now uses getPendingEvents(), so we need a detached ordering. pendingEventOrdering: "detached", }); if (opts) { Object.assign(room, opts); } return room; } let parentDiv = null; let client = null; let root = null; const myUserId = '@me:domain'; const movingRoomId = '!someroomid'; let movingRoom; let otherRoom; let myMember; let myOtherMember; beforeEach(async function(done) { RoomListStoreClass.TEST_MODE = true; TestUtils.stubClient(); client = MatrixClientPeg.get(); client.credentials = { userId: myUserId }; //revert this to prototype method as the test-utils monkey-patches this to return a hardcoded value client.getUserId = MatrixClient.prototype.getUserId; DMRoomMap.makeShared(); parentDiv = document.createElement('div'); document.body.appendChild(parentDiv); const WrappedRoomList = TestUtils.wrapInMatrixClientContext(RoomList); root = ReactDOM.render( {}} />, parentDiv, ); ReactTestUtils.findRenderedComponentWithType(root, RoomList); movingRoom = createRoom({ name: 'Moving room' }); expect(movingRoom.roomId).not.toBe(null); // Mock joined member myMember = new RoomMember(movingRoomId, myUserId); myMember.membership = 'join'; movingRoom.updateMyMembership('join'); movingRoom.getMember = (userId) => ({ [client.credentials.userId]: myMember, }[userId]); otherRoom = createRoom({ name: 'Other room' }); myOtherMember = new RoomMember(otherRoom.roomId, myUserId); myOtherMember.membership = 'join'; otherRoom.updateMyMembership('join'); otherRoom.getMember = (userId) => ({ [client.credentials.userId]: myOtherMember, }[userId]); // Mock the matrix client client.getRooms = () => [ movingRoom, otherRoom, createRoom({ tags: { 'm.favourite': { order: 0.1 } }, name: 'Some other room' }), createRoom({ tags: { 'm.favourite': { order: 0.2 } }, name: 'Some other room 2' }), createRoom({ tags: { 'm.lowpriority': {} }, name: 'Some unimportant room' }), createRoom({ tags: { 'custom.tag': {} }, name: 'Some room customly tagged' }), ]; client.getVisibleRooms = client.getRooms; const roomMap = {}; client.getRooms().forEach((r) => { roomMap[r.roomId] = r; }); client.getRoom = (roomId) => roomMap[roomId]; // Now that everything has been set up, prepare and update the store await RoomListStore.instance.makeReady(client); done(); }); afterEach(async (done) => { if (parentDiv) { ReactDOM.unmountComponentAtNode(parentDiv); parentDiv.remove(); parentDiv = null; } await RoomListLayoutStore.instance.resetLayouts(); await RoomListStore.instance.resetStore(); done(); }); function expectRoomInSubList(room, subListTest) { const subLists = ReactTestUtils.scryRenderedComponentsWithType(root, RoomSublist); const containingSubList = subLists.find(subListTest); let expectedRoomTile; try { const roomTiles = ReactTestUtils.scryRenderedComponentsWithType(containingSubList, RoomTile); console.info({ roomTiles: roomTiles.length }); expectedRoomTile = roomTiles.find((tile) => tile.props.room === room); } catch (err) { // truncate the error message because it's spammy err.message = 'Error finding RoomTile for ' + room.roomId + ' in ' + subListTest + ': ' + err.message.split('componentType')[0] + '...'; throw err; } expect(expectedRoomTile).toBeTruthy(); expect(expectedRoomTile.props.room).toBe(room); } function expectCorrectMove(oldTagId, newTagId) { const getTagSubListTest = (tagId) => { return (s) => s.props.tagId === tagId; }; // Default to finding the destination sublist with newTag const destSubListTest = getTagSubListTest(newTagId); const srcSubListTest = getTagSubListTest(oldTagId); // Set up the room that will be moved such that it has the correct state for a room in // the section for oldTagId if (oldTagId === DefaultTagID.Favourite || oldTagId === DefaultTagID.LowPriority) { movingRoom.tags = { [oldTagId]: {} }; } else if (oldTagId === DefaultTagID.DM) { // Mock inverse m.direct DMRoomMap.shared().roomToUser = { [movingRoom.roomId]: '@someotheruser:domain', }; } dis.dispatch({ action: 'MatrixActions.sync', prevState: null, state: 'PREPARED', matrixClient: client }); expectRoomInSubList(movingRoom, srcSubListTest); dis.dispatch({ action: 'RoomListActions.tagRoom.pending', request: { oldTagId, newTagId, room: movingRoom, } }); expectRoomInSubList(movingRoom, destSubListTest); } function itDoesCorrectOptimisticUpdatesForDraggedRoomTiles() { // TODO: Re-enable dragging tests when we support dragging again. describe.skip('does correct optimistic update when dragging from', () => { it('rooms to people', () => { expectCorrectMove(undefined, DefaultTagID.DM); }); it('rooms to favourites', () => { expectCorrectMove(undefined, 'm.favourite'); }); it('rooms to low priority', () => { expectCorrectMove(undefined, 'm.lowpriority'); }); // XXX: Known to fail - the view does not update immediately to reflect the change. // Whe running the app live, it updates when some other event occurs (likely the // m.direct arriving) that these tests do not fire. xit('people to rooms', () => { expectCorrectMove(DefaultTagID.DM, undefined); }); it('people to favourites', () => { expectCorrectMove(DefaultTagID.DM, 'm.favourite'); }); it('people to lowpriority', () => { expectCorrectMove(DefaultTagID.DM, 'm.lowpriority'); }); it('low priority to rooms', () => { expectCorrectMove('m.lowpriority', undefined); }); it('low priority to people', () => { expectCorrectMove('m.lowpriority', DefaultTagID.DM); }); it('low priority to low priority', () => { expectCorrectMove('m.lowpriority', 'm.lowpriority'); }); it('favourites to rooms', () => { expectCorrectMove('m.favourite', undefined); }); it('favourites to people', () => { expectCorrectMove('m.favourite', DefaultTagID.DM); }); it('favourites to low priority', () => { expectCorrectMove('m.favourite', 'm.lowpriority'); }); }); } itDoesCorrectOptimisticUpdatesForDraggedRoomTiles(); });