LGTM
This commit is contained in:
parent
9248e26af2
commit
f2a2651b8a
95 changed files with 3993 additions and 1471 deletions
|
|
@ -3,48 +3,52 @@ import {
|
|||
type Browser,
|
||||
type BrowserContext,
|
||||
type Page,
|
||||
} from "playwright";
|
||||
import {
|
||||
AUTH_STATE_PATH,
|
||||
CAPTURE_SCALE,
|
||||
OUTPUT_DIR,
|
||||
VIDEO_SIZE,
|
||||
VIEWPORT,
|
||||
} from "./config.js";
|
||||
} from 'playwright';
|
||||
import { AUTH_STATE_PATH } from './config.js';
|
||||
import { viewportFor, type Storyboard } from './script.js';
|
||||
|
||||
export interface RecordingBrowser {
|
||||
browser: Browser;
|
||||
context: BrowserContext;
|
||||
}
|
||||
|
||||
export async function launchRecordingBrowser(): Promise<RecordingBrowser> {
|
||||
export interface LaunchOptions {
|
||||
/** Directory the playwright recorder writes the raw .webm into. */
|
||||
recordDir: string;
|
||||
}
|
||||
|
||||
export async function launchRecordingBrowser(
|
||||
storyboard: Storyboard,
|
||||
opts: LaunchOptions
|
||||
): Promise<RecordingBrowser> {
|
||||
const browser = await chromium.launch({
|
||||
headless: true,
|
||||
args: [
|
||||
"--disable-blink-features=AutomationControlled",
|
||||
"--enable-gpu",
|
||||
"--use-gl=angle",
|
||||
"--use-angle=gl-egl",
|
||||
"--ignore-gpu-blocklist",
|
||||
"--enable-webgl",
|
||||
"--enable-webgl2",
|
||||
"--enable-gpu-rasterization",
|
||||
"--enable-zero-copy",
|
||||
"--disable-software-rasterizer",
|
||||
"--disable-frame-rate-limit",
|
||||
"--disable-gpu-vsync",
|
||||
"--disable-features=CalculateNativeWinOcclusion,IntensiveWakeUpThrottling",
|
||||
"--disable-renderer-backgrounding",
|
||||
"--disable-background-timer-throttling",
|
||||
"--disable-backgrounding-occluded-windows",
|
||||
'--disable-blink-features=AutomationControlled',
|
||||
'--enable-gpu',
|
||||
'--use-gl=angle',
|
||||
'--use-angle=gl-egl',
|
||||
'--ignore-gpu-blocklist',
|
||||
'--enable-webgl',
|
||||
'--enable-webgl2',
|
||||
'--enable-gpu-rasterization',
|
||||
'--enable-zero-copy',
|
||||
'--disable-software-rasterizer',
|
||||
'--disable-frame-rate-limit',
|
||||
'--disable-gpu-vsync',
|
||||
'--disable-features=CalculateNativeWinOcclusion,IntensiveWakeUpThrottling',
|
||||
'--disable-renderer-backgrounding',
|
||||
'--disable-background-timer-throttling',
|
||||
'--disable-backgrounding-occluded-windows',
|
||||
],
|
||||
});
|
||||
|
||||
const viewport = viewportFor(storyboard.video);
|
||||
const context = await browser.newContext({
|
||||
storageState: AUTH_STATE_PATH,
|
||||
viewport: VIEWPORT,
|
||||
deviceScaleFactor: CAPTURE_SCALE,
|
||||
recordVideo: { dir: OUTPUT_DIR, size: VIDEO_SIZE },
|
||||
viewport,
|
||||
deviceScaleFactor: storyboard.video.captureScale,
|
||||
recordVideo: { dir: opts.recordDir, size: viewport },
|
||||
});
|
||||
await suppressDevServerNoise(context);
|
||||
return { browser, context };
|
||||
|
|
@ -52,11 +56,11 @@ export async function launchRecordingBrowser(): Promise<RecordingBrowser> {
|
|||
|
||||
export async function assertHardwareWebGL(page: Page): Promise<void> {
|
||||
const info = await page.evaluate(() => {
|
||||
const canvas = document.createElement("canvas");
|
||||
const gl = canvas.getContext("webgl2");
|
||||
if (!gl) return { webgl: false, vendor: "", renderer: "" };
|
||||
const canvas = document.createElement('canvas');
|
||||
const gl = canvas.getContext('webgl2');
|
||||
if (!gl) return { webgl: false, vendor: '', renderer: '' };
|
||||
|
||||
const ext = gl.getExtension("WEBGL_debug_renderer_info");
|
||||
const ext = gl.getExtension('WEBGL_debug_renderer_info');
|
||||
const vendor = String(
|
||||
ext
|
||||
? gl.getParameter(ext.UNMASKED_VENDOR_WEBGL)
|
||||
|
|
@ -71,15 +75,15 @@ export async function assertHardwareWebGL(page: Page): Promise<void> {
|
|||
});
|
||||
|
||||
console.log(
|
||||
`[gpu] WebGL renderer: ${info.webgl ? `${info.vendor} / ${info.renderer}` : "none"}`,
|
||||
`[gpu] WebGL renderer: ${info.webgl ? `${info.vendor} / ${info.renderer}` : 'none'}`,
|
||||
);
|
||||
if (
|
||||
process.env.ALLOW_SOFTWARE_GL !== "1" &&
|
||||
process.env.ALLOW_SOFTWARE_GL !== '1' &&
|
||||
(!info.webgl ||
|
||||
/SwiftShader|llvmpipe|software/i.test(`${info.vendor} ${info.renderer}`))
|
||||
) {
|
||||
throw new Error(
|
||||
"Recording browser did not get hardware WebGL. Set ALLOW_SOFTWARE_GL=1 to bypass this guard.",
|
||||
'Recording browser did not get hardware WebGL. Set ALLOW_SOFTWARE_GL=1 to bypass this guard.',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -89,45 +93,45 @@ async function suppressDevServerNoise(context: BrowserContext) {
|
|||
const RealWS = window.WebSocket;
|
||||
window.WebSocket = new Proxy(RealWS, {
|
||||
construct(target, args) {
|
||||
const url = String(args[0] ?? "");
|
||||
const proto = (args[1] as string | string[] | undefined) ?? "";
|
||||
const protoStr = Array.isArray(proto) ? proto.join(",") : proto;
|
||||
const url = String(args[0] ?? '');
|
||||
const proto = (args[1] as string | string[] | undefined) ?? '';
|
||||
const protoStr = Array.isArray(proto) ? proto.join(',') : proto;
|
||||
if (
|
||||
protoStr.includes("vite-hmr") ||
|
||||
protoStr.includes("webpack") ||
|
||||
url.includes("/ws") ||
|
||||
url.includes("sockjs-node")
|
||||
protoStr.includes('vite-hmr') ||
|
||||
protoStr.includes('webpack') ||
|
||||
url.includes('/ws') ||
|
||||
url.includes('sockjs-node')
|
||||
) {
|
||||
const fake = new EventTarget() as WebSocket;
|
||||
Object.defineProperties(fake, {
|
||||
readyState: { value: RealWS.CLOSED },
|
||||
url: { value: url },
|
||||
protocol: { value: "" },
|
||||
extensions: { value: "" },
|
||||
protocol: { value: '' },
|
||||
extensions: { value: '' },
|
||||
bufferedAmount: { value: 0 },
|
||||
binaryType: { value: "blob", writable: true },
|
||||
binaryType: { value: 'blob', writable: true },
|
||||
});
|
||||
fake.send = () => {};
|
||||
fake.close = () => fake.dispatchEvent(new Event("close"));
|
||||
queueMicrotask(() => fake.dispatchEvent(new Event("close")));
|
||||
fake.close = () => fake.dispatchEvent(new Event('close'));
|
||||
queueMicrotask(() => fake.dispatchEvent(new Event('close')));
|
||||
return fake;
|
||||
}
|
||||
return Reflect.construct(target, args);
|
||||
},
|
||||
});
|
||||
|
||||
Object.defineProperty(window.location, "reload", {
|
||||
Object.defineProperty(window.location, 'reload', {
|
||||
value: () => {},
|
||||
configurable: true,
|
||||
});
|
||||
window.addEventListener("error", (e) => e.stopImmediatePropagation(), true);
|
||||
window.addEventListener('error', (e) => e.stopImmediatePropagation(), true);
|
||||
window.addEventListener(
|
||||
"unhandledrejection",
|
||||
'unhandledrejection',
|
||||
(e) => e.stopImmediatePropagation(),
|
||||
true,
|
||||
);
|
||||
|
||||
const styleEl = document.createElement("style");
|
||||
const styleEl = document.createElement('style');
|
||||
styleEl.textContent = `
|
||||
vite-error-overlay,
|
||||
wds-overlay,
|
||||
|
|
@ -148,12 +152,12 @@ async function suppressDevServerNoise(context: BrowserContext) {
|
|||
|
||||
const killOverlay = (node: Element) => {
|
||||
const tag = node.tagName?.toLowerCase();
|
||||
const id = (node as HTMLElement).id?.toLowerCase() ?? "";
|
||||
const id = (node as HTMLElement).id?.toLowerCase() ?? '';
|
||||
if (
|
||||
tag === "vite-error-overlay" ||
|
||||
tag === "wds-overlay" ||
|
||||
id.includes("webpack-dev-server-client") ||
|
||||
id.includes("webpack-error")
|
||||
tag === 'vite-error-overlay' ||
|
||||
tag === 'wds-overlay' ||
|
||||
id.includes('webpack-dev-server-client') ||
|
||||
id.includes('webpack-error')
|
||||
) {
|
||||
(node as HTMLElement).remove();
|
||||
}
|
||||
|
|
@ -168,7 +172,7 @@ async function suppressDevServerNoise(context: BrowserContext) {
|
|||
if (document.body)
|
||||
obs.observe(document.body, { childList: true, subtree: true });
|
||||
else {
|
||||
document.addEventListener("DOMContentLoaded", () =>
|
||||
document.addEventListener('DOMContentLoaded', () =>
|
||||
obs.observe(document.body, { childList: true, subtree: true }),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue