mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-22 06:32:00 +00:00
diffs: apply PDF preflight size and page limits
This commit is contained in:
@@ -144,6 +144,45 @@ describe("PlaywrightDiffScreenshotter", () => {
|
||||
await expect(fs.readFile(pdfPath, "utf8")).resolves.toContain("%PDF-1.7");
|
||||
});
|
||||
|
||||
it("fails fast when PDF render exceeds size limits", async () => {
|
||||
const pages: Array<{
|
||||
close: ReturnType<typeof vi.fn>;
|
||||
screenshot: ReturnType<typeof vi.fn>;
|
||||
pdf: ReturnType<typeof vi.fn>;
|
||||
}> = [];
|
||||
const browser = createMockBrowser(pages, {
|
||||
boundingBox: { x: 40, y: 40, width: 960, height: 60_000 },
|
||||
});
|
||||
launchMock.mockResolvedValue(browser);
|
||||
const { PlaywrightDiffScreenshotter } = await import("./browser.js");
|
||||
|
||||
const screenshotter = new PlaywrightDiffScreenshotter({
|
||||
config: createConfig(),
|
||||
browserIdleMs: 1_000,
|
||||
});
|
||||
const pdfPath = path.join(rootDir, "oversized.pdf");
|
||||
|
||||
await expect(
|
||||
screenshotter.screenshotHtml({
|
||||
html: '<html><head></head><body><main class="oc-frame"></main></body></html>',
|
||||
outputPath: pdfPath,
|
||||
theme: "light",
|
||||
image: {
|
||||
format: "pdf",
|
||||
qualityPreset: "standard",
|
||||
scale: 2,
|
||||
maxWidth: 960,
|
||||
maxPixels: 8_000_000,
|
||||
},
|
||||
}),
|
||||
).rejects.toThrow("Diff frame did not render within image size limits.");
|
||||
|
||||
expect(launchMock).toHaveBeenCalledTimes(1);
|
||||
expect(pages).toHaveLength(1);
|
||||
expect(pages[0]?.pdf).toHaveBeenCalledTimes(0);
|
||||
expect(pages[0]?.screenshot).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
it("fails fast when maxPixels is still exceeded at scale 1", async () => {
|
||||
const pages: Array<{
|
||||
close: ReturnType<typeof vi.fn>;
|
||||
@@ -192,10 +231,11 @@ function createMockBrowser(
|
||||
screenshot: ReturnType<typeof vi.fn>;
|
||||
pdf: ReturnType<typeof vi.fn>;
|
||||
}>,
|
||||
options?: { boundingBox?: { x: number; y: number; width: number; height: number } },
|
||||
) {
|
||||
const browser = {
|
||||
newPage: vi.fn(async () => {
|
||||
const page = createMockPage();
|
||||
const page = createMockPage(options);
|
||||
pages.push(page);
|
||||
return page;
|
||||
}),
|
||||
@@ -205,7 +245,10 @@ function createMockBrowser(
|
||||
return browser;
|
||||
}
|
||||
|
||||
function createMockPage() {
|
||||
function createMockPage(options?: {
|
||||
boundingBox?: { x: number; y: number; width: number; height: number };
|
||||
}) {
|
||||
const box = options?.boundingBox ?? { x: 40, y: 40, width: 640, height: 240 };
|
||||
const screenshot = vi.fn(async ({ path: screenshotPath }: { path: string }) => {
|
||||
await fs.writeFile(screenshotPath, Buffer.from("png"));
|
||||
});
|
||||
@@ -221,7 +264,7 @@ function createMockPage() {
|
||||
emulateMedia: vi.fn(async () => {}),
|
||||
locator: vi.fn(() => ({
|
||||
waitFor: vi.fn(async () => {}),
|
||||
boundingBox: vi.fn(async () => ({ x: 40, y: 40, width: 640, height: 240 })),
|
||||
boundingBox: vi.fn(async () => box),
|
||||
})),
|
||||
setViewportSize: vi.fn(async () => {}),
|
||||
screenshot,
|
||||
|
||||
@@ -9,6 +9,8 @@ import { VIEWER_ASSET_PREFIX, getServedViewerAsset } from "./viewer-assets.js";
|
||||
const DEFAULT_BROWSER_IDLE_MS = 30_000;
|
||||
const SHARED_BROWSER_KEY = "__default__";
|
||||
const IMAGE_SIZE_LIMIT_ERROR = "Diff frame did not render within image size limits.";
|
||||
const PDF_REFERENCE_PAGE_HEIGHT_PX = 1_056;
|
||||
const MAX_PDF_PAGES = 50;
|
||||
|
||||
export type DiffScreenshotter = {
|
||||
screenshotHtml(params: {
|
||||
@@ -177,11 +179,18 @@ export class PlaywrightDiffScreenshotter implements DiffScreenshotter {
|
||||
if (!pdfBox) {
|
||||
throw new Error("Diff frame was lost before PDF render.");
|
||||
}
|
||||
const pdfWidth = Math.max(Math.ceil(pdfBox.width), 1);
|
||||
const pdfHeight = Math.max(Math.ceil(pdfBox.height), 1);
|
||||
const estimatedPixels = pdfWidth * pdfHeight;
|
||||
const estimatedPages = Math.ceil(pdfHeight / PDF_REFERENCE_PAGE_HEIGHT_PX);
|
||||
if (estimatedPixels > params.image.maxPixels || estimatedPages > MAX_PDF_PAGES) {
|
||||
throw new Error(IMAGE_SIZE_LIMIT_ERROR);
|
||||
}
|
||||
|
||||
await page.pdf({
|
||||
path: params.outputPath,
|
||||
width: `${Math.max(Math.ceil(pdfBox.width), 1)}px`,
|
||||
height: `${Math.max(Math.ceil(pdfBox.height), 1)}px`,
|
||||
width: `${pdfWidth}px`,
|
||||
height: `${pdfHeight}px`,
|
||||
printBackground: true,
|
||||
margin: {
|
||||
top: "0",
|
||||
|
||||
Reference in New Issue
Block a user