mirror of
https://github.com/vector-im/element-call.git
synced 2026-03-25 06:40:26 +00:00
133 lines
5.0 KiB
TypeScript
133 lines
5.0 KiB
TypeScript
/*
|
|
Copyright 2026 Element Creations Ltd.
|
|
|
|
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
|
Please see LICENSE in the repository root for full details.
|
|
*/
|
|
|
|
import { expect, test } from "@playwright/test";
|
|
|
|
import { SpaHelpers } from "./spa-helpers.ts";
|
|
|
|
test("Bug: Unmuting camera when camera permission was not granted was closing the call with an error", async ({
|
|
page,
|
|
browserName,
|
|
}) => {
|
|
test.skip(browserName === "firefox", "Not passing on CI :/ but ok locally");
|
|
|
|
// ===============
|
|
// We cannot use only the clearPermissions API because it doesn't deny the
|
|
// permission, it just resets it to the default state which is "ask".
|
|
// Instead, we override the getUserMedia method to simulate a denial of camera permission.
|
|
// 1. Override getUserMedia to deny video by throwing NotAllowedError
|
|
// 2. Override enumerateDevices to return videoinput devices with empty labels/deviceIds
|
|
await page.addInitScript(() => {
|
|
const mediaDevices = window.navigator.mediaDevices;
|
|
const originalMediaDevice = mediaDevices.getUserMedia.bind(mediaDevices);
|
|
const originalEnumerateDevices =
|
|
mediaDevices.enumerateDevices.bind(mediaDevices);
|
|
window.navigator.mediaDevices.getUserMedia = async (
|
|
constraints?: MediaStreamConstraints,
|
|
): Promise<MediaStream> => {
|
|
if (constraints?.video) {
|
|
throw new DOMException("Permission denied", "NotAllowedError");
|
|
}
|
|
return originalMediaDevice(constraints);
|
|
};
|
|
|
|
window.navigator.mediaDevices.enumerateDevices = async (): Promise<
|
|
MediaDeviceInfo[]
|
|
> => {
|
|
const devices = await originalEnumerateDevices();
|
|
// Filter out video input devices to simulate no camera permission
|
|
return devices.map((device) => {
|
|
if (device.kind === "videoinput") {
|
|
// When we have no permission, the label/deviceId/groupId are empty strings.
|
|
return {
|
|
kind: "videoinput",
|
|
label: "",
|
|
deviceId: "",
|
|
groupId: "",
|
|
} as InputDeviceInfo;
|
|
}
|
|
return device;
|
|
});
|
|
};
|
|
});
|
|
|
|
// ===============
|
|
// Start a call then try to unmute camera
|
|
await page.goto("/");
|
|
|
|
// Start the call without camera permissio
|
|
await SpaHelpers.createCall(page, "John Doe", "HelloCall", false);
|
|
|
|
await page.pause();
|
|
// Video should be muted initially as we have no permission.
|
|
// When muted the aria-label indicates we can start video
|
|
await expect(page.getByRole("button", { name: "Start video" })).toBeVisible();
|
|
|
|
await page.getByTestId("lobby_joinCall").click();
|
|
|
|
// the test is a bit flaky here, wait for the tile to appear
|
|
await expect(page.getByTestId("videoTile")).toBeVisible();
|
|
await expect(page.getByRole("button", { name: "Start video" })).toBeVisible();
|
|
// await page.pause();
|
|
|
|
// Try to unmute camera without granting permission
|
|
await page.getByRole("button", { name: "Start video" }).click();
|
|
|
|
// There used to have a bug where the call would end with an error here.
|
|
// This was fixed and is not anymore a fatal error.
|
|
await page.waitForTimeout(1000);
|
|
// The call should still be ongoing, but currently the call is ended with an error
|
|
await expect(page.getByText("Something went wrong")).not.toBeVisible();
|
|
|
|
// The video button should still indicate that it is in a muted state.
|
|
// TODO Improve this UI/UX to better inform the user that camera permission is denied.
|
|
await expect(page.getByRole("button", { name: "Start video" })).toBeVisible();
|
|
});
|
|
|
|
test("Should not end call if screen share is cancelled", async ({
|
|
page,
|
|
browserName,
|
|
}) => {
|
|
test.skip(browserName === "firefox", "Not passing on CI :/ but ok locally");
|
|
|
|
// Mock getDisplayMedia to simulate user cancelling the screen share permission dialog
|
|
await page.addInitScript(() => {
|
|
window.navigator.mediaDevices.getDisplayMedia = async (
|
|
options?: DisplayMediaStreamOptions,
|
|
): Promise<MediaStream> => {
|
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
// simulate the user clicking cancel on the native window to share selection dialog
|
|
throw new DOMException("Permission denied", "NotAllowedError");
|
|
};
|
|
});
|
|
|
|
await page.goto("/");
|
|
|
|
await SpaHelpers.createCall(page, "John Doe", "HelloCall", false);
|
|
|
|
await page.getByTestId("lobby_joinCall").click();
|
|
await expect(page.getByTestId("videoTile")).toBeVisible();
|
|
|
|
await expect(
|
|
page.getByRole("button", { name: "Share screen" }),
|
|
).toBeVisible();
|
|
|
|
// Start screen sharing which will be denied
|
|
await page.getByRole("button", { name: "Share screen" }).click();
|
|
|
|
// There used to have a bug where the call would end with an error here.
|
|
// This was fixed and is not anymore a fatal error.
|
|
await page.waitForTimeout(1000);
|
|
// The call should still be ongoing, but currently the call is ended with an error
|
|
await expect(page.getByText("Something went wrong")).not.toBeVisible();
|
|
|
|
// The screen share button should still indicate that screen sharing is not active.
|
|
await expect(
|
|
page.getByRole("button", { name: "Share screen" }),
|
|
).toBeVisible();
|
|
});
|