mirror of
https://github.com/vector-im/element-web.git
synced 2025-12-07 18:31:33 +01:00
578 lines
25 KiB
TypeScript
578 lines
25 KiB
TypeScript
/*
|
|
Copyright 2025 New Vector Ltd.
|
|
Copyright (C) 2025 Element Creations Ltd
|
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
|
Please see LICENSE files in the repository root for full details.
|
|
*/
|
|
|
|
import { readFile } from "node:fs/promises";
|
|
import { type Page } from "playwright-core";
|
|
|
|
import type { EventType, Preset } from "matrix-js-sdk/src/matrix";
|
|
import { SettingLevel } from "../../../src/settings/SettingLevel";
|
|
import { test, expect } from "../../element-web-test";
|
|
import type { Credentials } from "../../plugins/homeserver";
|
|
import { Bot } from "../../pages/bot";
|
|
|
|
// Load a copy of our fake Element Call app, and the latest widget API.
|
|
// The fake call app does *just* enough to convince Element Web that a call is ongoing
|
|
// and functions like PiP work. It does not actually do anything though, to limit the
|
|
// surface we test.
|
|
const widgetApi = readFile("node_modules/matrix-widget-api/dist/api.min.js", "utf-8");
|
|
const fakeCallClient = readFile("playwright/sample-files/fake-element-call.html", "utf-8");
|
|
|
|
function assertCommonCallParameters(
|
|
url: URLSearchParams,
|
|
hash: URLSearchParams,
|
|
user: Credentials,
|
|
room: { roomId: string },
|
|
): void {
|
|
expect(url.has("widgetId")).toEqual(true);
|
|
expect(url.has("parentUrl")).toEqual(true);
|
|
|
|
expect(hash.get("perParticipantE2EE")).toEqual("false");
|
|
expect(hash.get("userId")).toEqual(user.userId);
|
|
expect(hash.get("deviceId")).toEqual(user.deviceId);
|
|
expect(hash.get("roomId")).toEqual(room.roomId);
|
|
expect(hash.get("preload")).toEqual("false");
|
|
}
|
|
|
|
async function sendRTCState(bot: Bot, roomId: string, notification?: "ring" | "notification", intent?: string) {
|
|
const resp = await bot.sendStateEvent(
|
|
roomId,
|
|
"org.matrix.msc3401.call.member",
|
|
{
|
|
"application": "m.call",
|
|
"call_id": "",
|
|
"m.call.intent": intent,
|
|
"device_id": "OiDFxsZrjz",
|
|
"expires": 180000000,
|
|
"foci_preferred": [
|
|
{
|
|
livekit_alias: roomId,
|
|
livekit_service_url: "https://example.org",
|
|
type: "livekit",
|
|
},
|
|
],
|
|
"focus_active": {
|
|
focus_selection: "oldest_membership",
|
|
type: "livekit",
|
|
},
|
|
"scope": "m.room",
|
|
},
|
|
`_@${bot.credentials.userId}_OiDFxsZrjz_m.call`,
|
|
);
|
|
if (!notification) {
|
|
return;
|
|
}
|
|
await bot.sendEvent(roomId, null, "org.matrix.msc4075.rtc.notification", {
|
|
"lifetime": 30000,
|
|
"m.mentions": {
|
|
room: true,
|
|
user_ids: [],
|
|
},
|
|
"m.relates_to": {
|
|
event_id: resp.event_id,
|
|
rel_type: "org.matrix.msc4075.rtc.notification.parent",
|
|
},
|
|
"m.call.intent": intent,
|
|
"notification_type": notification,
|
|
"sender_ts": 1758611895996,
|
|
});
|
|
}
|
|
|
|
test.describe("Element Call", () => {
|
|
test.use({
|
|
config: {
|
|
element_call: {
|
|
use_exclusively: false,
|
|
},
|
|
features: {
|
|
feature_group_calls: true,
|
|
},
|
|
},
|
|
displayName: "Alice",
|
|
botCreateOpts: {
|
|
autoAcceptInvites: true,
|
|
displayName: "Bob",
|
|
},
|
|
});
|
|
|
|
test.beforeEach(async ({ page, user, app }) => {
|
|
// Mock a widget page. We use a fake version of Element Call here.
|
|
// We should match on things after .html as these widgets get a ton of extra params.
|
|
await page.route(/\/widget.html.+/, async (route) => {
|
|
await route.fulfill({
|
|
status: 200,
|
|
// Do enough to
|
|
body: (await fakeCallClient).replace("widgetCodeHere", await widgetApi),
|
|
});
|
|
});
|
|
await app.settings.setValue(
|
|
"Developer.elementCallUrl",
|
|
null,
|
|
SettingLevel.DEVICE,
|
|
new URL("/widget.html#", page.url()).toString(),
|
|
);
|
|
});
|
|
|
|
test.describe("Group Chat", () => {
|
|
let charlie: Bot;
|
|
test.use({
|
|
room: async ({ page, app, user, homeserver, bot }, use) => {
|
|
charlie = new Bot(page, homeserver, { displayName: "Charlie" });
|
|
await charlie.prepareClient();
|
|
const roomId = await app.client.createRoom({
|
|
name: "TestRoom",
|
|
invite: [bot.credentials.userId, charlie.credentials.userId],
|
|
});
|
|
await use({ roomId });
|
|
},
|
|
});
|
|
test("should be able to start a video call", async ({ page, user, room, app }) => {
|
|
await app.viewRoomById(room.roomId);
|
|
await expect(page.getByText("Bob and one other were invited and joined")).toBeVisible();
|
|
|
|
await page.getByRole("button", { name: "Video call" }).click();
|
|
await page.getByRole("menuitem", { name: "Element Call" }).click();
|
|
|
|
const frameUrlStr = await page.locator("iframe").getAttribute("src");
|
|
await expect(frameUrlStr).toBeDefined();
|
|
// Ensure we set the correct parameters for ECall.
|
|
const url = new URL(frameUrlStr);
|
|
const hash = new URLSearchParams(url.hash.slice(1));
|
|
assertCommonCallParameters(url.searchParams, hash, user, room);
|
|
expect(hash.get("intent")).toEqual("start_call");
|
|
expect(hash.get("skipLobby")).toEqual(null);
|
|
});
|
|
|
|
test("should NOT be able to start a voice call", async ({ page, user, room, app }) => {
|
|
// Voice calls do not exist in group rooms
|
|
await app.viewRoomById(room.roomId);
|
|
await expect(page.getByText("Bob and one other were invited and joined")).toBeVisible();
|
|
await expect(page.getByRole("button", { name: "Voice call" })).not.toBeVisible();
|
|
});
|
|
|
|
test("should be able to skip lobby by holding down shift", async ({ page, user, bot, room, app }) => {
|
|
await app.viewRoomById(room.roomId);
|
|
await expect(page.getByText("Bob and one other were invited and joined")).toBeVisible();
|
|
|
|
await page.getByRole("button", { name: "Video call" }).click();
|
|
await page.keyboard.down("Shift");
|
|
await page.getByRole("menuitem", { name: "Element Call" }).click();
|
|
await page.keyboard.up("Shift");
|
|
|
|
const frameUrlStr = await page.locator("iframe").getAttribute("src");
|
|
await expect(frameUrlStr).toBeDefined();
|
|
const url = new URL(frameUrlStr);
|
|
const hash = new URLSearchParams(url.hash.slice(1));
|
|
assertCommonCallParameters(url.searchParams, hash, user, room);
|
|
expect(hash.get("intent")).toEqual("start_call");
|
|
expect(hash.get("skipLobby")).toEqual("true");
|
|
});
|
|
|
|
test("should be able to join a call in progress", async ({ page, user, bot, room, app }) => {
|
|
await app.viewRoomById(room.roomId);
|
|
// Allow bob to create a call
|
|
await expect(page.getByText("Bob and one other were invited and joined")).toBeVisible();
|
|
await app.client.setPowerLevel(room.roomId, bot.credentials.userId, 50);
|
|
// Fake a start of a call
|
|
await sendRTCState(bot, room.roomId);
|
|
const button = page.getByTestId("join-call-button");
|
|
await expect(button).toBeInViewport({ timeout: 5000 });
|
|
// And test joining
|
|
await button.click();
|
|
const frameUrlStr = await page.locator("iframe").getAttribute("src");
|
|
await expect(frameUrlStr).toBeDefined();
|
|
const url = new URL(frameUrlStr);
|
|
const hash = new URLSearchParams(url.hash.slice(1));
|
|
assertCommonCallParameters(url.searchParams, hash, user, room);
|
|
|
|
expect(hash.get("intent")).toEqual("join_existing");
|
|
expect(hash.get("skipLobby")).toEqual(null);
|
|
});
|
|
|
|
[true, false].forEach((skipLobbyToggle) => {
|
|
test(
|
|
`should be able to join a call via incoming video call toast (skipLobby=${skipLobbyToggle})`,
|
|
{ tag: ["@screenshot"] },
|
|
async ({ page, user, bot, room, app }) => {
|
|
await app.viewRoomById(room.roomId);
|
|
// Allow bob to create a call
|
|
await expect(page.getByText("Bob and one other were invited and joined")).toBeVisible();
|
|
await app.client.setPowerLevel(room.roomId, bot.credentials.userId, 50);
|
|
// Fake a start of a call
|
|
await sendRTCState(bot, room.roomId, "notification", "video");
|
|
const toast = page.locator(".mx_Toast_toast");
|
|
const button = toast.getByRole("button", { name: "Join" });
|
|
|
|
if (skipLobbyToggle) {
|
|
await toast.getByRole("switch").check();
|
|
await expect(toast).toMatchScreenshot(`incoming-call-group-video-toast-checked.png`);
|
|
} else {
|
|
await toast.getByRole("switch").uncheck();
|
|
await expect(toast).toMatchScreenshot(`incoming-call-group-video-toast-unchecked.png`);
|
|
}
|
|
|
|
// And test joining
|
|
await button.click();
|
|
const frameUrlStr = await page.locator("iframe").getAttribute("src");
|
|
await expect(frameUrlStr).toBeDefined();
|
|
const url = new URL(frameUrlStr);
|
|
const hash = new URLSearchParams(url.hash.slice(1));
|
|
assertCommonCallParameters(url.searchParams, hash, user, room);
|
|
|
|
expect(hash.get("intent")).toEqual("join_existing");
|
|
expect(hash.get("skipLobby")).toEqual(skipLobbyToggle.toString());
|
|
},
|
|
);
|
|
});
|
|
|
|
test(
|
|
`should be able to join a call via incoming voice call toast`,
|
|
{ tag: ["@screenshot"] },
|
|
async ({ page, user, bot, room, app }) => {
|
|
await app.viewRoomById(room.roomId);
|
|
// Allow bob to create a call
|
|
await expect(page.getByText("Bob and one other were invited and joined")).toBeVisible();
|
|
await app.client.setPowerLevel(room.roomId, bot.credentials.userId, 50);
|
|
// Fake a start of a call
|
|
await sendRTCState(bot, room.roomId, "notification", "audio");
|
|
const toast = page.locator(".mx_Toast_toast");
|
|
const button = toast.getByRole("button", { name: "Join" });
|
|
|
|
await expect(toast).toMatchScreenshot(`incoming-call-group-voice-toast.png`);
|
|
|
|
// And test joining
|
|
await button.click();
|
|
const frameUrlStr = await page.locator("iframe").getAttribute("src");
|
|
await expect(frameUrlStr).toBeDefined();
|
|
const url = new URL(frameUrlStr);
|
|
const hash = new URLSearchParams(url.hash.slice(1));
|
|
assertCommonCallParameters(url.searchParams, hash, user, room);
|
|
|
|
expect(hash.get("intent")).toEqual("join_existing");
|
|
expect(hash.get("skipLobby")).toEqual("true");
|
|
},
|
|
);
|
|
});
|
|
|
|
test.describe("DMs", () => {
|
|
test.use({
|
|
room: async ({ page, app, user, bot }, use) => {
|
|
const roomId = await app.client.createRoom({
|
|
preset: "trusted_private_chat" as Preset.TrustedPrivateChat,
|
|
invite: [bot.credentials.userId],
|
|
});
|
|
await app.client.setAccountData("m.direct" as EventType.Direct, {
|
|
[bot.credentials.userId]: [roomId],
|
|
});
|
|
await use({ roomId });
|
|
},
|
|
});
|
|
|
|
test("should be able to start a video call", async ({ page, user, room, app }) => {
|
|
await app.viewRoomById(room.roomId);
|
|
await expect(page.getByText("Bob joined the room")).toBeVisible();
|
|
|
|
await page.getByRole("button", { name: "Video call" }).click();
|
|
await page.getByRole("menuitem", { name: "Element Call" }).click();
|
|
const frameUrlStr = await page.locator("iframe").getAttribute("src");
|
|
|
|
await expect(frameUrlStr).toBeDefined();
|
|
const url = new URL(frameUrlStr);
|
|
const hash = new URLSearchParams(url.hash.slice(1));
|
|
assertCommonCallParameters(url.searchParams, hash, user, room);
|
|
expect(hash.get("intent")).toEqual("start_call_dm");
|
|
expect(hash.get("skipLobby")).toEqual(null);
|
|
});
|
|
|
|
test("should be able to skip lobby by holding down shift", async ({ page, user, room, app }) => {
|
|
await app.viewRoomById(room.roomId);
|
|
await expect(page.getByText("Bob joined the room")).toBeVisible();
|
|
|
|
await page.getByRole("button", { name: "Video call" }).click();
|
|
await page.keyboard.down("Shift");
|
|
await page.getByRole("menuitem", { name: "Element Call" }).click();
|
|
await page.keyboard.up("Shift");
|
|
const frameUrlStr = await page.locator("iframe").getAttribute("src");
|
|
|
|
await expect(frameUrlStr).toBeDefined();
|
|
const url = new URL(frameUrlStr);
|
|
const hash = new URLSearchParams(url.hash.slice(1));
|
|
assertCommonCallParameters(url.searchParams, hash, user, room);
|
|
expect(hash.get("intent")).toEqual("start_call_dm");
|
|
expect(hash.get("skipLobby")).toEqual("true");
|
|
});
|
|
|
|
test("should be able to join a call in progress", async ({ page, user, bot, room, app }) => {
|
|
await app.viewRoomById(room.roomId);
|
|
await expect(page.getByText("Bob joined the room")).toBeVisible();
|
|
// Fake a start of a call
|
|
await sendRTCState(bot, room.roomId);
|
|
const button = page.getByTestId("join-call-button");
|
|
await expect(button).toBeInViewport({ timeout: 5000 });
|
|
// And test joining
|
|
await button.click();
|
|
const frameUrlStr = await page.locator("iframe").getAttribute("src");
|
|
await expect(frameUrlStr).toBeDefined();
|
|
const url = new URL(frameUrlStr);
|
|
const hash = new URLSearchParams(url.hash.slice(1));
|
|
assertCommonCallParameters(url.searchParams, hash, user, room);
|
|
|
|
expect(hash.get("intent")).toEqual("join_existing_dm");
|
|
expect(hash.get("skipLobby")).toEqual(null);
|
|
});
|
|
|
|
[true, false].forEach((skipLobbyToggle) => {
|
|
test(
|
|
`should be able to join a call via incoming call toast (skipLobby=${skipLobbyToggle})`,
|
|
{ tag: ["@screenshot"] },
|
|
async ({ page, user, bot, room, app }) => {
|
|
await app.viewRoomById(room.roomId);
|
|
await expect(page.getByText("Bob joined the room")).toBeVisible();
|
|
// Fake a start of a call
|
|
await sendRTCState(bot, room.roomId, "ring", "video");
|
|
const toast = page.locator(".mx_Toast_toast");
|
|
const button = toast.getByRole("button", { name: "Accept" });
|
|
if (skipLobbyToggle) {
|
|
await toast.getByRole("switch").check();
|
|
} else {
|
|
await toast.getByRole("switch").uncheck();
|
|
}
|
|
await expect(toast).toMatchScreenshot(
|
|
`incoming-call-dm-video-toast-${skipLobbyToggle ? "checked" : "unchecked"}.png`,
|
|
{
|
|
// Hide UserId
|
|
css: `
|
|
.mx_IncomingCallToast_AvatarWithDetails span:nth-child(2) {
|
|
opacity: 0;
|
|
}
|
|
`,
|
|
},
|
|
);
|
|
|
|
// And test joining
|
|
await button.click();
|
|
const frameUrlStr = await page.locator("iframe").getAttribute("src");
|
|
await expect(frameUrlStr).toBeDefined();
|
|
const url = new URL(frameUrlStr);
|
|
const hash = new URLSearchParams(url.hash.slice(1));
|
|
assertCommonCallParameters(url.searchParams, hash, user, room);
|
|
|
|
expect(hash.get("intent")).toEqual("join_existing_dm");
|
|
expect(hash.get("skipLobby")).toEqual(skipLobbyToggle.toString());
|
|
},
|
|
);
|
|
});
|
|
|
|
test(
|
|
`should be able to join a call via incoming voice call toast`,
|
|
{ tag: ["@screenshot"] },
|
|
async ({ page, user, bot, room, app }) => {
|
|
await app.viewRoomById(room.roomId);
|
|
await expect(page.getByText("Bob joined the room")).toBeVisible();
|
|
// Fake a start of a call
|
|
await sendRTCState(bot, room.roomId, "ring", "audio");
|
|
const toast = page.locator(".mx_Toast_toast");
|
|
const button = toast.getByRole("button", { name: "Accept" });
|
|
|
|
await expect(toast).toMatchScreenshot(`incoming-call-dm-voice-toast.png`, {
|
|
// Hide UserId
|
|
css: `
|
|
.mx_IncomingCallToast_AvatarWithDetails span:nth-child(2) {
|
|
opacity: 0;
|
|
}
|
|
`,
|
|
});
|
|
|
|
// And test joining
|
|
await button.click();
|
|
const frameUrlStr = await page.locator("iframe").getAttribute("src");
|
|
await expect(frameUrlStr).toBeDefined();
|
|
const url = new URL(frameUrlStr);
|
|
const hash = new URLSearchParams(url.hash.slice(1));
|
|
assertCommonCallParameters(url.searchParams, hash, user, room);
|
|
|
|
expect(hash.get("intent")).toEqual("join_existing_dm_voice");
|
|
expect(hash.get("skipLobby")).toEqual("true");
|
|
},
|
|
);
|
|
});
|
|
|
|
test.describe("Video Rooms", () => {
|
|
test.use({
|
|
config: {
|
|
features: {
|
|
feature_video_rooms: true,
|
|
feature_element_call_video_rooms: true,
|
|
},
|
|
},
|
|
});
|
|
test("should be able to create and join a video room", async ({ page, user }) => {
|
|
await page
|
|
.getByRole("navigation", { name: "Room list" })
|
|
.getByRole("button", { name: "New conversation" })
|
|
.click();
|
|
await page.getByRole("menuitem", { name: "New video room" }).click();
|
|
await page.getByRole("textbox", { name: "Name" }).fill("Test room");
|
|
await page.getByRole("button", { name: "Create video room" }).click();
|
|
await expect(page).toHaveURL(new RegExp(`/#/room/`));
|
|
const roomId = new URL(page.url()).hash.slice("#/room/".length);
|
|
|
|
const frameUrlStr = await page.locator("iframe").getAttribute("src");
|
|
await expect(frameUrlStr).toBeDefined();
|
|
// Ensure we set the correct parameters for ECall.
|
|
const url = new URL(frameUrlStr);
|
|
const hash = new URLSearchParams(url.hash.slice(1));
|
|
assertCommonCallParameters(url.searchParams, hash, user, { roomId });
|
|
expect(hash.get("intent")).toEqual("join_existing");
|
|
expect(hash.get("skipLobby")).toEqual("false");
|
|
expect(hash.get("returnToLobby")).toEqual("true");
|
|
});
|
|
});
|
|
|
|
test.describe("Switching rooms", () => {
|
|
let charlie: Bot;
|
|
test.use({
|
|
room: async ({ page, app, user, homeserver, bot }, use) => {
|
|
charlie = new Bot(page, homeserver, { displayName: "Charlie" });
|
|
await charlie.prepareClient();
|
|
const roomId = await app.client.createRoom({
|
|
name: "TestRoom",
|
|
invite: [bot.credentials.userId, charlie.credentials.userId],
|
|
});
|
|
await app.client.createRoom({
|
|
name: "OtherRoom",
|
|
});
|
|
await use({ roomId });
|
|
},
|
|
});
|
|
|
|
async function openAndJoinCall(page: Page, existing = false) {
|
|
if (existing) {
|
|
await page.getByTestId("join-call-button").click();
|
|
} else {
|
|
await page.getByRole("button", { name: "Video call" }).click();
|
|
await page.getByRole("menuitem", { name: "Element Call" }).click();
|
|
}
|
|
const iframe = page.locator("iframe");
|
|
await expect(iframe).toBeVisible();
|
|
const frameUrlStr = await page.locator("iframe").getAttribute("src");
|
|
const callFrame = page.frame({ url: frameUrlStr });
|
|
await callFrame.getByRole("button", { name: "Join Call" }).click();
|
|
await expect(callFrame.getByText("In call", { exact: true })).toBeVisible();
|
|
|
|
// Wait for Element Web to pickup the RTC session and update the room list entry.
|
|
await expect(await page.getByTestId("notification-decoration")).toBeVisible();
|
|
}
|
|
|
|
test("should be able to switch rooms and have the call persist", async ({ page, user, room, app }) => {
|
|
await app.viewRoomById(room.roomId);
|
|
await expect(page.getByText("Bob and one other were invited and joined")).toBeVisible();
|
|
|
|
await openAndJoinCall(page);
|
|
await app.viewRoomByName("OtherRoom");
|
|
|
|
// We should have a PiP container here.
|
|
await expect(page.locator(".mx_AppTile_persistedWrapper")).toBeVisible();
|
|
});
|
|
|
|
test("should be able to start a call, close it via PiP, and start again in the same room", async ({
|
|
page,
|
|
user,
|
|
room,
|
|
app,
|
|
}) => {
|
|
await app.viewRoomById(room.roomId);
|
|
await expect(page.getByText("Bob and one other were invited and joined")).toBeVisible();
|
|
|
|
await openAndJoinCall(page);
|
|
await app.viewRoomByName("OtherRoom");
|
|
const pipContainer = page.locator(".mx_WidgetPip");
|
|
|
|
// We should have a PiP container here.
|
|
await expect(pipContainer).toBeVisible();
|
|
|
|
// Leave the call.
|
|
const overlay = page.locator(".mx_WidgetPip_overlay");
|
|
await overlay.hover({ timeout: 2000 }); // Show the call footer.
|
|
await overlay.getByRole("button", { name: "Leave", exact: true }).click();
|
|
|
|
// PiP container goes.
|
|
await expect(pipContainer).not.toBeVisible();
|
|
|
|
// Wait for call to stop.
|
|
await expect(await page.getByTestId("notification-decoration")).not.toBeVisible();
|
|
await app.viewRoomById(room.roomId);
|
|
await expect(await page.getByTestId("join-call-button")).not.toBeVisible();
|
|
|
|
// Join the call again.
|
|
await openAndJoinCall(page);
|
|
});
|
|
|
|
test("should be able to start a call, close it via PiP, and start again in a different room", async ({
|
|
page,
|
|
user,
|
|
room,
|
|
app,
|
|
}) => {
|
|
await app.viewRoomById(room.roomId);
|
|
await expect(page.getByText("Bob and one other were invited and joined")).toBeVisible();
|
|
|
|
await openAndJoinCall(page);
|
|
await app.viewRoomByName("OtherRoom");
|
|
const pipContainer = page.locator(".mx_WidgetPip");
|
|
|
|
// We should have a PiP container here.
|
|
await expect(pipContainer).toBeVisible();
|
|
|
|
// Leave the call.
|
|
const overlay = page.locator(".mx_WidgetPip_overlay");
|
|
await overlay.hover({ timeout: 2000 }); // Show the call footer.
|
|
await overlay.getByRole("button", { name: "Leave", exact: true }).click();
|
|
|
|
// PiP container goes.
|
|
await expect(pipContainer).not.toBeVisible();
|
|
|
|
// Wait for call to stop.
|
|
await expect(await page.getByTestId("notification-decoration")).not.toBeVisible();
|
|
await expect(await page.getByTestId("join-call-button")).not.toBeVisible();
|
|
|
|
// Join the call again, but from the other room.
|
|
await openAndJoinCall(page);
|
|
});
|
|
|
|
// For https://github.com/element-hq/element-web/issues/30838
|
|
test.fail(
|
|
"should be able to join a call, leave via PiP, and rejoin the call",
|
|
async ({ page, user, room, app, bot }) => {
|
|
await app.viewRoomById(room.roomId);
|
|
await expect(page.getByText("Bob and one other were invited and joined")).toBeVisible();
|
|
await app.client.setPowerLevel(room.roomId, bot.credentials.userId, 50);
|
|
|
|
await sendRTCState(bot, room.roomId);
|
|
await openAndJoinCall(page, true);
|
|
|
|
await app.viewRoomByName("OtherRoom");
|
|
const pipContainer = page.locator(".mx_WidgetPip");
|
|
|
|
// We should have a PiP container here.
|
|
await expect(pipContainer).toBeVisible();
|
|
|
|
// Leave the call.
|
|
const overlay = page.locator(".mx_WidgetPip_overlay");
|
|
await overlay.hover({ timeout: 2000 }); // Show the call footer.
|
|
await overlay.getByRole("button", { name: "Leave", exact: true }).click();
|
|
|
|
// PiP container goes.
|
|
await expect(pipContainer).not.toBeVisible();
|
|
|
|
// Rejoin the call
|
|
await app.viewRoomById(room.roomId);
|
|
await openAndJoinCall(page, true);
|
|
},
|
|
);
|
|
});
|
|
});
|