mirror of
https://github.com/element-hq/element-call.git
synced 2026-10-06 01:37:22 +00:00
- The standalone app at five window sizes, from a large desktop to a phone-shaped 360x640, on Chromium and Firefox: the camera menu stays inside the window, which is the call area there, and the last effect can still be scrolled to and chosen. - As a component in a host page, and as a widget in Element Web: a picture chosen reaches the preview, so the pipeline's model and WebAssembly load from those builds, and in a small container the last effect is reached and chosen. - Compound portals the menu to the page and takes no container for it, so there it has none of the component's scoped styles, as the microphone menu has none. - The colour checks the three share live in playwright/utils. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
56 lines
1.9 KiB
TypeScript
56 lines
1.9 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, type Locator } from "@playwright/test";
|
|
|
|
export type Colour = [number, number, number];
|
|
|
|
/** The camera's colour, once the preview is showing it rather than nothing. */
|
|
export async function cameraColour(preview: Locator): Promise<Colour> {
|
|
await expect
|
|
.poll(async () => Math.max(...(await averageColour(preview))))
|
|
.toBeGreaterThan(40);
|
|
return averageColour(preview);
|
|
}
|
|
|
|
/** The mean colour of a video's current frame or an image, drawn small. */
|
|
export async function averageColour(element: Locator): Promise<Colour> {
|
|
return element.evaluate(
|
|
async (source: HTMLVideoElement | HTMLImageElement) => {
|
|
if (source instanceof HTMLImageElement) await source.decode();
|
|
const canvas = document.createElement("canvas");
|
|
canvas.width = canvas.height = 32;
|
|
const context = canvas.getContext("2d")!;
|
|
context.drawImage(source, 0, 0, 32, 32);
|
|
const { data } = context.getImageData(0, 0, 32, 32);
|
|
const sum = [0, 0, 0];
|
|
for (let i = 0; i < data.length; i += 4)
|
|
for (let c = 0; c < 3; c++) sum[c] += data[i + c];
|
|
const pixels = data.length / 4;
|
|
return sum.map((s) => s / pixels) as Colour;
|
|
},
|
|
);
|
|
}
|
|
|
|
export function distance(a: Colour, b: Colour): number {
|
|
return Math.hypot(a[0] - b[0], a[1] - b[1], a[2] - b[2]);
|
|
}
|
|
|
|
/** Waits until a preview shows a picture rather than the camera. */
|
|
export async function expectWearing(
|
|
preview: Locator,
|
|
picture: Colour,
|
|
camera: Colour,
|
|
): Promise<void> {
|
|
// Long enough for the first effect of a session to be built.
|
|
await expect
|
|
.poll(async () => distance(await averageColour(preview), picture), {
|
|
timeout: 60_000,
|
|
})
|
|
.toBeLessThan(distance(camera, picture) / 2);
|
|
}
|