fix: downscale viewport screenshots when no viewport is emulated (#2380)
Fixes #2379. `getSourceBox()` bails when `page.viewport()` is null, which is the normal case – the browser is launched and connected with `defaultViewport: null`, and only `emulate` sets a viewport. So `--screenshot-max-width`/`--screenshot-max-height` never produced a clip for a plain viewport screenshot. This falls back to the window's own `innerWidth`/`innerHeight`. That's the area a viewport screenshot already captures, so the clip only applies `scale` and the framing is unchanged. Full page and element shots keep their existing branches. ## Testing Added `downscales viewport screenshot when no viewport is emulated`, which omits the `setViewport()` call the existing downscale tests make – it fails on `main` (`expected: 100, actual: 1200`, the window width) and passes here. Full `tests/tools/screenshot.test.ts` is green.
This commit is contained in:
+12
-2
@@ -43,10 +43,20 @@ async function getSourceBox(
|
||||
return {x: 0, y: 0, width: dims.width, height: dims.height};
|
||||
}
|
||||
const viewport = page.viewport();
|
||||
if (!viewport) {
|
||||
if (viewport) {
|
||||
return {x: 0, y: 0, width: viewport.width, height: viewport.height};
|
||||
}
|
||||
// The browser is launched and connected with `defaultViewport: null`, so
|
||||
// `page.viewport()` stays null until something emulates one. Fall back to the
|
||||
// window's own dimensions, which is the area a viewport screenshot captures.
|
||||
const dims = await page.evaluate(() => ({
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight,
|
||||
}));
|
||||
if (dims.width <= 0 || dims.height <= 0) {
|
||||
return undefined;
|
||||
}
|
||||
return {x: 0, y: 0, width: viewport.width, height: viewport.height};
|
||||
return {x: 0, y: 0, width: dims.width, height: dims.height};
|
||||
}
|
||||
|
||||
function computeDownscaleClip(
|
||||
|
||||
@@ -345,6 +345,43 @@ describe('screenshot', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('downscales viewport screenshot when no viewport is emulated', async () => {
|
||||
const tool = screenshot({
|
||||
screenshotMaxWidth: 100,
|
||||
} as ParsedArguments);
|
||||
await withMcpContext(async (response, context) => {
|
||||
const page = context.getSelectedMcpPage().pptrPage;
|
||||
// No setViewport call here: the browser is launched and connected with
|
||||
// `defaultViewport: null`, so this is what a page looks like unless the
|
||||
// emulate tool has set a viewport.
|
||||
assert.equal(page.viewport(), null);
|
||||
await page.setContent(
|
||||
html`<div style="width:100vw;height:100vh;background:red"></div>`,
|
||||
);
|
||||
const source = await page.evaluate(() => ({
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight,
|
||||
}));
|
||||
|
||||
await tool.handler(
|
||||
{params: {format: 'png'}, page: context.getSelectedMcpPage()},
|
||||
response,
|
||||
context,
|
||||
);
|
||||
|
||||
assert.equal(response.images.length, 1);
|
||||
const buf = Buffer.from(response.images[0].data, 'base64');
|
||||
assert.equal(pngWidth(buf), 100);
|
||||
// The window size comes from the environment rather than an emulated
|
||||
// viewport, so allow a pixel of rounding slack on the derived height.
|
||||
const expectedHeight = Math.round(source.height * (100 / source.width));
|
||||
assert.ok(
|
||||
Math.abs(pngHeight(buf) - expectedHeight) <= 1,
|
||||
`expected height ~${expectedHeight}, got ${pngHeight(buf)}`,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('downscales using the smaller scale when both max-width and max-height are set', async () => {
|
||||
const tool = screenshot({
|
||||
screenshotMaxWidth: 400,
|
||||
|
||||
Reference in New Issue
Block a user