element-web/test/components/views/rooms/RoomHeader-test.tsx
Robin 6e86a14cc9
Show a lobby screen in video rooms (#8287)
* Show a lobby screen in video rooms

* Add connecting state

* Test VideoRoomView

* Test VideoLobby

* Get the local video stream with useAsyncMemo

* Clean up code review nits

* Explicitly state what !important is overriding

* Use spacing variables

* Wait for video channel messaging

* Update join button copy

* Show frame on both the lobby and widget

* Force dark theme for video lobby

* Wait for the widget to be ready

* Make VideoChannelStore constructor private

* Allow video lobby to shrink

* Add invite button to video room header

* Show connected members on lobby screen

* Make avatars in video lobby clickable

* Increase video channel store timeout

* Fix Jitsi Meet getting wedged on startup in Chrome and Safari

* Revert "Fix Jitsi Meet getting wedged on startup in Chrome and Safari"

This reverts commit 9f77b8c227c1a5bffa5d91b0c48bf3bbc44d4cec.

* Disable device buttons while connecting

* Factor RoomFacePile into a separate file

* Fix i18n lint

* Fix switching video channels while connected

* Properly limit number of connected members in face pile

* Fix CSS lint
2022-04-20 11:03:33 -04:00

265 lines
9.1 KiB
TypeScript

import React from 'react';
import { mount, ReactWrapper } from 'enzyme';
import { Room, PendingEventOrdering, MatrixEvent, MatrixClient } from 'matrix-js-sdk/src/matrix';
import * as TestUtils from '../../../test-utils';
import { MatrixClientPeg } from '../../../../src/MatrixClientPeg';
import DMRoomMap from '../../../../src/utils/DMRoomMap';
import RoomHeader from '../../../../src/components/views/rooms/RoomHeader';
import { SearchScope } from '../../../../src/components/views/rooms/SearchBar';
import { E2EStatus } from '../../../../src/utils/ShieldUtils';
import { mkEvent } from '../../../test-utils';
import { IRoomState } from "../../../../src/components/structures/RoomView";
import RoomContext from '../../../../src/contexts/RoomContext';
describe('RoomHeader', () => {
it('shows the room avatar in a room with only ourselves', () => {
// When we render a non-DM room with 1 person in it
const room = createRoom({ name: "X Room", isDm: false, userIds: [] });
const rendered = render(room);
// Then the room's avatar is the initial of its name
const initial = findSpan(rendered, ".mx_BaseAvatar_initial");
expect(initial.text()).toEqual("X");
// And there is no image avatar (because it's not set on this room)
const image = findImg(rendered, ".mx_BaseAvatar_image");
expect(image.prop("src")).toEqual("data:image/png;base64,00");
});
it('shows the room avatar in a room with 2 people', () => {
// When we render a non-DM room with 2 people in it
const room = createRoom(
{ name: "Y Room", isDm: false, userIds: ["other"] });
const rendered = render(room);
// Then the room's avatar is the initial of its name
const initial = findSpan(rendered, ".mx_BaseAvatar_initial");
expect(initial.text()).toEqual("Y");
// And there is no image avatar (because it's not set on this room)
const image = findImg(rendered, ".mx_BaseAvatar_image");
expect(image.prop("src")).toEqual("data:image/png;base64,00");
});
it('shows the room avatar in a room with >2 people', () => {
// When we render a non-DM room with 3 people in it
const room = createRoom({ name: "Z Room", isDm: false, userIds: ["other1", "other2"] });
const rendered = render(room);
// Then the room's avatar is the initial of its name
const initial = findSpan(rendered, ".mx_BaseAvatar_initial");
expect(initial.text()).toEqual("Z");
// And there is no image avatar (because it's not set on this room)
const image = findImg(rendered, ".mx_BaseAvatar_image");
expect(image.prop("src")).toEqual("data:image/png;base64,00");
});
it('shows the room avatar in a DM with only ourselves', () => {
// When we render a non-DM room with 1 person in it
const room = createRoom({ name: "Z Room", isDm: true, userIds: [] });
const rendered = render(room);
// Then the room's avatar is the initial of its name
const initial = findSpan(rendered, ".mx_BaseAvatar_initial");
expect(initial.text()).toEqual("Z");
// And there is no image avatar (because it's not set on this room)
const image = findImg(rendered, ".mx_BaseAvatar_image");
expect(image.prop("src")).toEqual("data:image/png;base64,00");
});
it('shows the user avatar in a DM with 2 people', () => {
// Note: this is the interesting case - this is the ONLY
// time we should use the user's avatar.
// When we render a DM room with only 2 people in it
const room = createRoom({ name: "Y Room", isDm: true, userIds: ["other"] });
const rendered = render(room);
// Then we use the other user's avatar as our room's image avatar
const image = findImg(rendered, ".mx_BaseAvatar_image");
expect(image.prop("src")).toEqual("http://this.is.a.url/example.org/other");
// And there is no initial avatar
expect(rendered.find(".mx_BaseAvatar_initial")).toHaveLength(0);
});
it('shows the room avatar in a DM with >2 people', () => {
// When we render a DM room with 3 people in it
const room = createRoom({
name: "Z Room", isDm: true, userIds: ["other1", "other2"] });
const rendered = render(room);
// Then the room's avatar is the initial of its name
const initial = findSpan(rendered, ".mx_BaseAvatar_initial");
expect(initial.text()).toEqual("Z");
// And there is no image avatar (because it's not set on this room)
const image = findImg(rendered, ".mx_BaseAvatar_image");
expect(image.prop("src")).toEqual("data:image/png;base64,00");
});
it("renders call buttons normally", () => {
const room = createRoom({ name: "Room", isDm: false, userIds: [] });
const wrapper = render(room);
expect(wrapper.find('[aria-label="Voice call"]').hostNodes()).toHaveLength(1);
expect(wrapper.find('[aria-label="Video call"]').hostNodes()).toHaveLength(1);
});
it("hides call buttons when the room is tombstoned", () => {
const room = createRoom({ name: "Room", isDm: false, userIds: [] });
const wrapper = render(room, {
tombstone: mkEvent({
event: true,
type: "m.room.tombstone",
room: room.roomId,
user: "@user1:server",
skey: "",
content: {},
ts: Date.now(),
}),
});
expect(wrapper.find('[aria-label="Voice call"]').hostNodes()).toHaveLength(0);
expect(wrapper.find('[aria-label="Video call"]').hostNodes()).toHaveLength(0);
});
});
interface IRoomCreationInfo {
name: string;
isDm: boolean;
userIds: string[];
}
function createRoom(info: IRoomCreationInfo) {
TestUtils.stubClient();
const client: MatrixClient = MatrixClientPeg.get();
const roomId = '!1234567890:domain';
const userId = client.getUserId();
if (info.isDm) {
client.getAccountData = (eventType) => {
expect(eventType).toEqual("m.direct");
return mkDirectEvent(roomId, userId, info.userIds);
};
}
DMRoomMap.makeShared().start();
const room = new Room(roomId, client, userId, {
pendingEventOrdering: PendingEventOrdering.Detached,
});
const otherJoinEvents = [];
for (const otherUserId of info.userIds) {
otherJoinEvents.push(mkJoinEvent(roomId, otherUserId));
}
room.currentState.setStateEvents([
mkCreationEvent(roomId, userId),
mkNameEvent(roomId, userId, info.name),
mkJoinEvent(roomId, userId),
...otherJoinEvents,
]);
room.recalculate();
return room;
}
function render(room: Room, roomContext?: Partial<IRoomState>): ReactWrapper {
return mount((
<RoomContext.Provider value={{ ...roomContext, room } as IRoomState}>
<RoomHeader
room={room}
inRoom={true}
onSearchClick={() => {}}
onInviteClick={null}
onForgetClick={() => {}}
onCallPlaced={(_type) => { }}
onAppsClick={() => {}}
e2eStatus={E2EStatus.Normal}
appsShown={true}
searchInfo={{
searchTerm: "",
searchScope: SearchScope.Room,
searchCount: 0,
}}
/>
</RoomContext.Provider>
));
}
function mkCreationEvent(roomId: string, userId: string): MatrixEvent {
return mkEvent({
event: true,
type: "m.room.create",
room: roomId,
user: userId,
content: {
creator: userId,
room_version: "5",
predecessor: {
room_id: "!prevroom",
event_id: "$someevent",
},
},
});
}
function mkNameEvent(
roomId: string, userId: string, name: string,
): MatrixEvent {
return mkEvent({
event: true,
type: "m.room.name",
room: roomId,
user: userId,
content: { name },
});
}
function mkJoinEvent(roomId: string, userId: string) {
const ret = mkEvent({
event: true,
type: "m.room.member",
room: roomId,
user: userId,
content: {
"membership": "join",
"avatar_url": "mxc://example.org/" + userId,
},
});
ret.event.state_key = userId;
return ret;
}
function mkDirectEvent(
roomId: string, userId: string, otherUsers: string[],
): MatrixEvent {
const content = {};
for (const otherUserId of otherUsers) {
content[otherUserId] = [roomId];
}
return mkEvent({
event: true,
type: "m.direct",
room: roomId,
user: userId,
content,
});
}
function findSpan(wrapper: ReactWrapper, selector: string): ReactWrapper {
const els = wrapper.find(selector).hostNodes();
expect(els).toHaveLength(1);
return els.at(0);
}
function findImg(wrapper: ReactWrapper, selector: string): ReactWrapper {
const els = wrapper.find(selector).hostNodes();
expect(els).toHaveLength(1);
return els.at(0);
}