mirror of
https://github.com/getpaseo/paseo.git
synced 2026-11-06 08:24:46 +00:00
fix(desktop): share screenshot deadline across paint and capture
This commit is contained in:
@@ -67,6 +67,7 @@ class FakeTab implements TabContents {
|
||||
public networkEntries: unknown[] = [];
|
||||
public consoleMessages: BrowserAutomationConsoleLogEntry[] = [];
|
||||
public dialogsToCapture: BrowserAutomationDialogEvent[] = [];
|
||||
public paintDelayMs = 0;
|
||||
public captureNeverPaints = false;
|
||||
public captureThrows = false;
|
||||
public captureErrorMessage = "capture failed";
|
||||
@@ -118,6 +119,9 @@ class FakeTab implements TabContents {
|
||||
|
||||
public async executeJavaScript(code: string): Promise<unknown> {
|
||||
this.scripts.push(code);
|
||||
if (code.includes("requestAnimationFrame") && this.paintDelayMs > 0) {
|
||||
await new Promise((resolve) => setTimeout(resolve, this.paintDelayMs));
|
||||
}
|
||||
if (code.includes("document.body.innerText")) {
|
||||
return this.bodyText;
|
||||
}
|
||||
@@ -1591,6 +1595,31 @@ describe("executeAutomationCommand", () => {
|
||||
}
|
||||
});
|
||||
|
||||
test("paint and pixel capture share the five-second screenshot budget", async () => {
|
||||
vi.useFakeTimers();
|
||||
try {
|
||||
const browser = new BrowserAutomationHarness();
|
||||
browser.tab.paintDelayMs = 4_000;
|
||||
browser.tab.captureNeverPaints = true;
|
||||
let result: unknown;
|
||||
void browser
|
||||
.execute({
|
||||
command: "screenshot",
|
||||
args: { browserId: BROWSER_A },
|
||||
})
|
||||
.then((value) => {
|
||||
result = value;
|
||||
return value;
|
||||
});
|
||||
await vi.advanceTimersByTimeAsync(4_999);
|
||||
expect(result).toBeUndefined();
|
||||
await vi.advanceTimersByTimeAsync(1);
|
||||
expect(result).toMatchObject({ ok: false, error: { code: "screenshot_no_frame" } });
|
||||
} finally {
|
||||
vi.useRealTimers();
|
||||
}
|
||||
});
|
||||
|
||||
test("screenshot surfaces ordinary viewport capture errors", async () => {
|
||||
const browser = new BrowserAutomationHarness();
|
||||
browser.tab.captureThrows = true;
|
||||
|
||||
@@ -162,8 +162,8 @@ async function runSerializedPixelCapture<T>(capture: () => Promise<T>): Promise<
|
||||
async function capturePixelFrameWithRetry<T>(
|
||||
contents: TabContents,
|
||||
capture: () => Promise<T>,
|
||||
deadline: number,
|
||||
): Promise<T> {
|
||||
const deadline = Date.now() + PIXEL_CAPTURE_TIMEOUT_MS;
|
||||
while (Date.now() < deadline) {
|
||||
try {
|
||||
contents.invalidate();
|
||||
@@ -190,13 +190,14 @@ function isKnownNoFrameCaptureError(error: unknown): boolean {
|
||||
);
|
||||
}
|
||||
|
||||
async function waitForPaint(contents: TabContents): Promise<void> {
|
||||
async function waitForPaint(contents: TabContents, deadline: number): Promise<void> {
|
||||
// A hidden page may have unpainted DOM updates. The first animation callback
|
||||
// precedes paint; the next frame ensures capture cannot reuse the old surface.
|
||||
await withPixelCaptureTimeout(
|
||||
contents.executeJavaScript(
|
||||
"new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(() => resolve())))",
|
||||
),
|
||||
deadline - Date.now(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -206,8 +207,9 @@ async function runPaintedPixelCapture<T>(
|
||||
): Promise<T> {
|
||||
return runSerializedPixelCapture(() =>
|
||||
contents.withFrameProduction(async () => {
|
||||
await waitForPaint(contents);
|
||||
return capturePixelFrameWithRetry(contents, capture);
|
||||
const deadline = Date.now() + PIXEL_CAPTURE_TIMEOUT_MS;
|
||||
await waitForPaint(contents, deadline);
|
||||
return capturePixelFrameWithRetry(contents, capture, deadline);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user