9dfa06ffee
Docker image / Build (linux/amd64) (push) Has been cancelled
Docker image / Build (linux/arm64) (push) Has been cancelled
Docker image / Merge release multi-arch manifest (push) Has been cancelled
Docker image / Merge debug multi-arch manifest (push) Has been cancelled
Docker image / Build public push gateway (linux/amd64) (push) Has been cancelled
Docker image / Build public push gateway (linux/arm64) (push) Has been cancelled
Docker image / Publish public push gateway image (push) Has been cancelled
Sprig image / Build (linux/amd64) (push) Has been cancelled
Sprig image / Build (linux/arm64) (push) Has been cancelled
Sprig image / Merge multi-arch manifest (push) Has been cancelled
Harbor Buzz Orchestra / Python tests and lint (push) Has been cancelled
CI / Detect Changed Paths (push) Has been cancelled
CI / Rust Lint (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Desktop Core (push) Has been cancelled
CI / Desktop Smoke E2E (1) (push) Has been cancelled
CI / Desktop Smoke E2E (2) (push) Has been cancelled
CI / Desktop Smoke E2E (3) (push) Has been cancelled
CI / Desktop Smoke E2E (4) (push) Has been cancelled
CI / Desktop (push) Has been cancelled
CI / Desktop E2E Relay (push) Has been cancelled
CI / Desktop E2E Integration (1/2) (push) Has been cancelled
CI / Desktop E2E Integration (2/2) (push) Has been cancelled
CI / Desktop E2E Integration (push) Has been cancelled
CI / Backend Integration (relay e2e) (push) Has been cancelled
CI / Relay E2E (push) Has been cancelled
CI / Web (push) Has been cancelled
CI / Mobile (push) Has been cancelled
CI / Security (push) Has been cancelled
CI / Dead Token Reference Guard (push) Has been cancelled
CI / Server Cross-Compile (aarch64-unknown-linux-musl) (push) Has been cancelled
CI / Server Cross-Compile (x86_64-unknown-linux-musl) (push) Has been cancelled
CI / Windows Rust (x86_64-pc-windows-msvc) (push) Has been cancelled
CI / Desktop Build (macOS) (push) Has been cancelled
helm chart / lint + unittest + render matrix (push) Has been cancelled
helm chart / install on kind (gated) (push) Has been cancelled
helm chart / publish chart to GHCR (push) Has been cancelled
Mesh Lifecycle / Relay-Driven Mesh Lifecycle Smoke (push) Has been cancelled
Sprig / Build (aarch64-unknown-linux-musl) (push) Has been cancelled
Sprig / Build (x86_64-unknown-linux-musl) (push) Has been cancelled
Sprig / Publish rolling release (push) Has been cancelled
Sprig / Publish tagged release (push) Has been cancelled
Signed-off-by: cls_宁波本机 <908705107@qq.com>
125 lines
3.9 KiB
TypeScript
125 lines
3.9 KiB
TypeScript
import type { Page } from "@playwright/test";
|
|
|
|
/**
|
|
* Stub getUserMedia with a canvas-generated stream (animated gradient with a
|
|
* moving circle) so the camera phases work headless and deterministically —
|
|
* Playwright's bundled headless shell has no media capture support.
|
|
*/
|
|
export function installFakeCamera(
|
|
page: Page,
|
|
options: {
|
|
cameraDelayMs?: number;
|
|
failRequests?: number;
|
|
holdCamera?: boolean;
|
|
} = {},
|
|
) {
|
|
return page.addInitScript(
|
|
(cameraOptions) => {
|
|
const canvas = document.createElement("canvas");
|
|
canvas.width = 640;
|
|
canvas.height = 480;
|
|
const context = canvas.getContext("2d");
|
|
let hue = 0;
|
|
setInterval(() => {
|
|
if (!context) {
|
|
return;
|
|
}
|
|
hue = (hue + 7) % 360;
|
|
context.fillStyle = `hsl(${hue} 80% 60%)`;
|
|
context.fillRect(0, 0, canvas.width, canvas.height);
|
|
context.fillStyle = "#ffffff";
|
|
context.beginPath();
|
|
context.arc(
|
|
canvas.width / 2 + Math.sin(hue / 30) * 60,
|
|
canvas.height / 2,
|
|
90,
|
|
0,
|
|
Math.PI * 2,
|
|
);
|
|
context.fill();
|
|
}, 90);
|
|
const stream = canvas.captureStream(15);
|
|
const mediaDevices = navigator.mediaDevices ?? ({} as MediaDevices);
|
|
if (!navigator.mediaDevices) {
|
|
Object.defineProperty(navigator, "mediaDevices", {
|
|
configurable: true,
|
|
value: mediaDevices,
|
|
});
|
|
}
|
|
const devices: MediaDeviceInfo[] = [
|
|
{
|
|
deviceId: "builtin-camera",
|
|
groupId: "mac",
|
|
kind: "videoinput",
|
|
label: "FaceTime HD Camera",
|
|
toJSON() {
|
|
return this;
|
|
},
|
|
} as MediaDeviceInfo,
|
|
{
|
|
deviceId: "iphone-continuity",
|
|
groupId: "iphone",
|
|
kind: "videoinput",
|
|
label: "Kenny's iPhone Camera",
|
|
toJSON() {
|
|
return this;
|
|
},
|
|
} as MediaDeviceInfo,
|
|
];
|
|
const testWindow = window as Window & {
|
|
__BUZZ_E2E_CAMERA_CONSTRAINTS__?: MediaStreamConstraints[];
|
|
__BUZZ_E2E_CAMERA_REQUEST_COUNT__?: number;
|
|
__BUZZ_E2E_RELEASE_CAMERA__?: () => void;
|
|
};
|
|
testWindow.__BUZZ_E2E_CAMERA_CONSTRAINTS__ = [];
|
|
testWindow.__BUZZ_E2E_CAMERA_REQUEST_COUNT__ = 0;
|
|
let releaseCamera: (() => void) | null = null;
|
|
testWindow.__BUZZ_E2E_RELEASE_CAMERA__ = () => {
|
|
releaseCamera?.();
|
|
releaseCamera = null;
|
|
};
|
|
Object.defineProperty(mediaDevices, "enumerateDevices", {
|
|
configurable: true,
|
|
value: () => Promise.resolve(devices),
|
|
});
|
|
Object.defineProperty(mediaDevices, "addEventListener", {
|
|
configurable: true,
|
|
value: () => {},
|
|
});
|
|
Object.defineProperty(mediaDevices, "removeEventListener", {
|
|
configurable: true,
|
|
value: () => {},
|
|
});
|
|
Object.defineProperty(mediaDevices, "getUserMedia", {
|
|
configurable: true,
|
|
value: async (constraints: MediaStreamConstraints) => {
|
|
testWindow.__BUZZ_E2E_CAMERA_CONSTRAINTS__?.push(constraints);
|
|
testWindow.__BUZZ_E2E_CAMERA_REQUEST_COUNT__ =
|
|
(testWindow.__BUZZ_E2E_CAMERA_REQUEST_COUNT__ ?? 0) + 1;
|
|
if (
|
|
testWindow.__BUZZ_E2E_CAMERA_REQUEST_COUNT__ <=
|
|
cameraOptions.failRequests
|
|
) {
|
|
throw new DOMException("Camera access denied", "NotAllowedError");
|
|
}
|
|
if (cameraOptions.holdCamera) {
|
|
await new Promise<void>((resolve) => {
|
|
releaseCamera = resolve;
|
|
});
|
|
} else if (cameraOptions.cameraDelayMs > 0) {
|
|
await new Promise((resolve) =>
|
|
setTimeout(resolve, cameraOptions.cameraDelayMs),
|
|
);
|
|
}
|
|
return Promise.resolve(stream);
|
|
},
|
|
});
|
|
},
|
|
{
|
|
cameraDelayMs: options.cameraDelayMs ?? 0,
|
|
failRequests: options.failRequests ?? 0,
|
|
holdCamera: options.holdCamera ?? false,
|
|
},
|
|
);
|
|
}
|