fix: emulate if captureBeyondViewport is false (#11525)

This commit is contained in:
jrandolf 2023-12-13 00:12:24 +01:00 committed by GitHub
parent 026172761b
commit b6d1163f7f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 162 additions and 85 deletions

View File

@ -16,7 +16,7 @@ export interface Viewport
| ----------------- | --------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ | | ----------------- | --------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ |
| deviceScaleFactor | <code>optional</code> | number | Specify device scale factor. See [devicePixelRatio](https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio) for more info. | <code>1</code> | | deviceScaleFactor | <code>optional</code> | number | Specify device scale factor. See [devicePixelRatio](https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio) for more info. | <code>1</code> |
| hasTouch | <code>optional</code> | boolean | Specify if the viewport supports touch events. | <code>false</code> | | hasTouch | <code>optional</code> | boolean | Specify if the viewport supports touch events. | <code>false</code> |
| height | | number | The page height in pixels. | | | height | | number | The page height in CSS pixels. | |
| isLandscape | <code>optional</code> | boolean | Specifies if the viewport is in landscape mode. | <code>false</code> | | isLandscape | <code>optional</code> | boolean | Specifies if the viewport is in landscape mode. | <code>false</code> |
| isMobile | <code>optional</code> | boolean | Whether the <code>meta viewport</code> tag is taken into account. | <code>false</code> | | isMobile | <code>optional</code> | boolean | Whether the <code>meta viewport</code> tag is taken into account. | <code>false</code> |
| width | | number | The page width in pixels. | | | width | | number | The page width in CSS pixels. | |

View File

@ -85,6 +85,7 @@ import type {ScreenRecorder} from '../node/ScreenRecorder.js';
import {assert} from '../util/assert.js'; import {assert} from '../util/assert.js';
import {guarded} from '../util/decorators.js'; import {guarded} from '../util/decorators.js';
import { import {
AsyncDisposableStack,
asyncDisposeSymbol, asyncDisposeSymbol,
DisposableStack, DisposableStack,
disposeSymbol, disposeSymbol,
@ -2450,18 +2451,47 @@ export abstract class Page extends EventEmitter<PageEvents> {
setDefaultScreenshotOptions(options); setDefaultScreenshotOptions(options);
options.clip = await using stack = new AsyncDisposableStack();
options.clip && roundRectangle(normalizeRectangle(options.clip)); if (options.clip) {
if (options.fullPage) {
if (options.fullPage) { throw new Error("'clip' and 'fullPage' are mutually exclusive");
if (options.clip) { }
throw new Error("'clip' and 'fullPage' are exclusive");
options.clip = roundRectangle(normalizeRectangle(options.clip));
} else {
if (options.fullPage) {
// If `captureBeyondViewport` is `false`, then we set the viewport to
// capture the full page. Note this may be affected by on-page CSS and
// JavaScript.
if (!options.captureBeyondViewport) {
const scrollDimensions = await this.mainFrame()
.isolatedRealm()
.evaluate(() => {
const element = document.documentElement;
return {
width: element.scrollWidth,
height: element.scrollHeight,
};
});
const viewport = this.viewport();
await this.setViewport({
...viewport,
...scrollDimensions,
});
stack.defer(async () => {
if (viewport) {
await this.setViewport(viewport).catch(debugError);
} else {
await this.setViewport({
width: 0,
height: 0,
}).catch(debugError);
}
});
}
} else {
options.captureBeyondViewport = false;
} }
} else if (
!options.clip &&
userOptions.captureBeyondViewport === undefined
) {
options.captureBeyondViewport = false;
} }
const data = await this._screenshot(options); const data = await this._screenshot(options);

View File

@ -666,12 +666,20 @@ export class BidiPage extends Page {
if (options.fromSurface !== undefined && !options.fromSurface) { if (options.fromSurface !== undefined && !options.fromSurface) {
throw new UnsupportedOperation(`BiDi does not support 'fromSurface'.`); throw new UnsupportedOperation(`BiDi does not support 'fromSurface'.`);
} }
if (clip !== undefined && clip.scale !== undefined && clip.scale !== 1) {
throw new UnsupportedOperation(
`BiDi does not support 'scale' in 'clip'.`
);
}
let box: BoundingBox | undefined; let box: BoundingBox | undefined;
if (clip) { if (clip) {
if (captureBeyondViewport) { if (captureBeyondViewport) {
box = clip; box = clip;
} else { } else {
// The clip is always with respect to the document coordinates, so we
// need to convert this to viewport coordinates when we aren't capturing
// beyond the viewport.
const [pageLeft, pageTop] = await this.evaluate(() => { const [pageLeft, pageTop] = await this.evaluate(() => {
if (!window.visualViewport) { if (!window.visualViewport) {
throw new Error('window.visualViewport is not supported.'); throw new Error('window.visualViewport is not supported.');
@ -689,12 +697,6 @@ export class BidiPage extends Page {
} }
} }
if (clip !== undefined && clip.scale !== undefined && clip.scale !== 1) {
throw new UnsupportedOperation(
`BiDi does not support 'scale' in 'clip'.`
);
}
const { const {
result: {data}, result: {data},
} = await this.#connection.send('browsingContext.captureScreenshot', { } = await this.#connection.send('browsingContext.captureScreenshot', {

View File

@ -1122,10 +1122,7 @@ export class CdpPage extends Page {
format: type, format: type,
...(optimizeForSpeed ? {optimizeForSpeed} : {}), ...(optimizeForSpeed ? {optimizeForSpeed} : {}),
...(quality !== undefined ? {quality: Math.round(quality)} : {}), ...(quality !== undefined ? {quality: Math.round(quality)} : {}),
clip: clip && { ...(clip ? {clip: {...clip, scale: clip.scale ?? 1}} : {}),
...clip,
scale: clip.scale ?? 1,
},
...(!fromSurface ? {fromSurface} : {}), ...(!fromSurface ? {fromSurface} : {}),
captureBeyondViewport, captureBeyondViewport,
} }

View File

@ -19,11 +19,17 @@
*/ */
export interface Viewport { export interface Viewport {
/** /**
* The page width in pixels. * The page width in CSS pixels.
*
* @remarks
* Setting this value to `0` will reset this value to the system default.
*/ */
width: number; width: number;
/** /**
* The page height in pixels. * The page height in CSS pixels.
*
* @remarks
* Setting this value to `0` will reset this value to the system default.
*/ */
height: number; height: number;
/** /**
@ -31,7 +37,7 @@ export interface Viewport {
* See {@link https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio | devicePixelRatio} for more info. * See {@link https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio | devicePixelRatio} for more info.
* *
* @remarks * @remarks
* Setting this value to `0` will set the deviceScaleFactor to the system default. * Setting this value to `0` will reset this value to the system default.
* *
* @defaultValue `1` * @defaultValue `1`
*/ */

View File

@ -1157,24 +1157,12 @@
"parameters": ["firefox"], "parameters": ["firefox"],
"expectations": ["PASS"] "expectations": ["PASS"]
}, },
{
"testIdPattern": "[screenshot.spec] Screenshots Page.screenshot should clip clip bigger than the viewport without \"captureBeyondViewport\"",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["firefox"],
"expectations": ["FAIL", "PASS"]
},
{ {
"testIdPattern": "[screenshot.spec] Screenshots Page.screenshot should get screenshot bigger than the viewport", "testIdPattern": "[screenshot.spec] Screenshots Page.screenshot should get screenshot bigger than the viewport",
"platforms": ["darwin", "linux", "win32"], "platforms": ["darwin", "linux", "win32"],
"parameters": ["cdp"], "parameters": ["cdp"],
"expectations": ["FAIL"] "expectations": ["FAIL"]
}, },
{
"testIdPattern": "[screenshot.spec] Screenshots Page.screenshot should use scale for clip",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["webDriverBiDi"],
"expectations": ["FAIL"]
},
{ {
"testIdPattern": "[stacktrace.spec] Stack trace *", "testIdPattern": "[stacktrace.spec] Stack trace *",
"platforms": ["darwin", "linux", "win32"], "platforms": ["darwin", "linux", "win32"],
@ -3371,24 +3359,12 @@
"parameters": ["cdp", "chrome"], "parameters": ["cdp", "chrome"],
"expectations": ["FAIL", "PASS"] "expectations": ["FAIL", "PASS"]
}, },
{
"testIdPattern": "[screenshot.spec] Screenshots ElementHandle.screenshot should work for an element with an offset",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["cdp", "firefox"],
"expectations": ["FAIL"]
},
{ {
"testIdPattern": "[screenshot.spec] Screenshots ElementHandle.screenshot should work for an element with an offset", "testIdPattern": "[screenshot.spec] Screenshots ElementHandle.screenshot should work for an element with an offset",
"platforms": ["darwin", "linux", "win32"], "platforms": ["darwin", "linux", "win32"],
"parameters": ["firefox", "webDriverBiDi"], "parameters": ["firefox", "webDriverBiDi"],
"expectations": ["FAIL"] "expectations": ["FAIL"]
}, },
{
"testIdPattern": "[screenshot.spec] Screenshots ElementHandle.screenshot should work with a rotated element",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["cdp", "firefox"],
"expectations": ["FAIL"]
},
{ {
"testIdPattern": "[screenshot.spec] Screenshots ElementHandle.screenshot should work with a rotated element", "testIdPattern": "[screenshot.spec] Screenshots ElementHandle.screenshot should work with a rotated element",
"platforms": ["darwin", "linux", "win32"], "platforms": ["darwin", "linux", "win32"],
@ -3409,6 +3385,7 @@
}, },
{ {
"testIdPattern": "[screenshot.spec] Screenshots Page.screenshot should return base64", "testIdPattern": "[screenshot.spec] Screenshots Page.screenshot should return base64",
"comment": "Bisected to https://chromium.googlesource.com/chromium/src/+log/b8b95f5715b4bd2348e537b740048e4b6aa281b1..8026f46b0e73174f33834a26748107d1089a83bf",
"platforms": ["darwin", "linux", "win32"], "platforms": ["darwin", "linux", "win32"],
"parameters": ["cdp", "chrome"], "parameters": ["cdp", "chrome"],
"expectations": ["FAIL", "PASS"] "expectations": ["FAIL", "PASS"]
@ -3421,12 +3398,20 @@
}, },
{ {
"testIdPattern": "[screenshot.spec] Screenshots Page.screenshot should take fullPage screenshots", "testIdPattern": "[screenshot.spec] Screenshots Page.screenshot should take fullPage screenshots",
"comment": "Bisected to https://chromium.googlesource.com/chromium/src/+log/b8b95f5715b4bd2348e537b740048e4b6aa281b1..8026f46b0e73174f33834a26748107d1089a83bf",
"platforms": ["darwin", "linux", "win32"], "platforms": ["darwin", "linux", "win32"],
"parameters": ["cdp", "firefox"], "parameters": ["cdp", "chrome"],
"expectations": ["FAIL", "PASS"]
},
{
"testIdPattern": "[screenshot.spec] Screenshots Page.screenshot should take fullPage screenshots without captureBeyondViewport",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["firefox", "webDriverBiDi"],
"expectations": ["FAIL"] "expectations": ["FAIL"]
}, },
{ {
"testIdPattern": "[screenshot.spec] Screenshots Page.screenshot should take fullPage screenshots", "testIdPattern": "[screenshot.spec] Screenshots Page.screenshot should take fullPage screenshots without captureBeyondViewport",
"comment": "Bisected to https://chromium.googlesource.com/chromium/src/+log/b8b95f5715b4bd2348e537b740048e4b6aa281b1..8026f46b0e73174f33834a26748107d1089a83bf",
"platforms": ["darwin", "linux", "win32"], "platforms": ["darwin", "linux", "win32"],
"parameters": ["cdp", "chrome"], "parameters": ["cdp", "chrome"],
"expectations": ["FAIL", "PASS"] "expectations": ["FAIL", "PASS"]
@ -3439,6 +3424,7 @@
}, },
{ {
"testIdPattern": "[screenshot.spec] Screenshots Page.screenshot should work", "testIdPattern": "[screenshot.spec] Screenshots Page.screenshot should work",
"comment": "Bisected to https://chromium.googlesource.com/chromium/src/+log/b8b95f5715b4bd2348e537b740048e4b6aa281b1..8026f46b0e73174f33834a26748107d1089a83bf",
"platforms": ["darwin", "linux", "win32"], "platforms": ["darwin", "linux", "win32"],
"parameters": ["cdp", "chrome"], "parameters": ["cdp", "chrome"],
"expectations": ["FAIL", "PASS"] "expectations": ["FAIL", "PASS"]
@ -3762,15 +3748,39 @@
"expectations": ["FAIL", "PASS"] "expectations": ["FAIL", "PASS"]
}, },
{ {
"testIdPattern": "[screenshot.spec] Screenshots Cdp should work in \"fromSurface: false\" mode", "testIdPattern": "[screenshot.spec] Screenshots Cdp should use scale for clip",
"platforms": ["darwin", "win32"], "platforms": ["darwin", "linux", "win32"],
"parameters": ["cdp", "chrome", "headless"], "parameters": ["cdp", "firefox", "headless"],
"expectations": ["FAIL"] "expectations": ["FAIL"]
}, },
{ {
"testIdPattern": "[screenshot.spec] Screenshots Cdp should work in \"fromSurface: false\" mode", "testIdPattern": "[screenshot.spec] Screenshots ElementHandle.screenshot should work for an element with an offset",
"platforms": ["darwin"], "platforms": ["darwin", "linux", "win32"],
"parameters": ["cdp", "chrome", "new-headless"], "parameters": ["cdp", "firefox", "headful"],
"expectations": ["FAIL"]
},
{
"testIdPattern": "[screenshot.spec] Screenshots ElementHandle.screenshot should work for an element with an offset",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["cdp", "firefox", "headless"],
"expectations": ["FAIL"]
},
{
"testIdPattern": "[screenshot.spec] Screenshots ElementHandle.screenshot should work with a rotated element",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["cdp", "firefox", "headful"],
"expectations": ["FAIL"]
},
{
"testIdPattern": "[screenshot.spec] Screenshots ElementHandle.screenshot should work with a rotated element",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["cdp", "firefox", "headless"],
"expectations": ["FAIL"]
},
{
"testIdPattern": "[screenshot.spec] Screenshots Page.screenshot should clip clip bigger than the viewport without \"captureBeyondViewport\"",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["cdp", "firefox", "headless"],
"expectations": ["FAIL"] "expectations": ["FAIL"]
}, },
{ {
@ -3786,7 +3796,19 @@
"expectations": ["FAIL"] "expectations": ["FAIL"]
}, },
{ {
"testIdPattern": "[screenshot.spec] Screenshots Page.screenshot should use scale for clip", "testIdPattern": "[screenshot.spec] Screenshots Page.screenshot should take fullPage screenshots",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["cdp", "firefox", "headful"],
"expectations": ["FAIL"]
},
{
"testIdPattern": "[screenshot.spec] Screenshots Page.screenshot should take fullPage screenshots",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["cdp", "firefox", "headless"],
"expectations": ["FAIL"]
},
{
"testIdPattern": "[screenshot.spec] Screenshots Page.screenshot should take fullPage screenshots without captureBeyondViewport",
"platforms": ["darwin", "linux", "win32"], "platforms": ["darwin", "linux", "win32"],
"parameters": ["cdp", "firefox", "headless"], "parameters": ["cdp", "firefox", "headless"],
"expectations": ["FAIL"] "expectations": ["FAIL"]

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

View File

@ -18,7 +18,12 @@ import assert from 'assert';
import expect from 'expect'; import expect from 'expect';
import {getTestState, launch, setupTestBrowserHooks} from './mocha-utils.js'; import {
getTestState,
isHeadless,
launch,
setupTestBrowserHooks,
} from './mocha-utils.js';
describe('Screenshots', function () { describe('Screenshots', function () {
setupTestBrowserHooks(); setupTestBrowserHooks();
@ -47,22 +52,6 @@ describe('Screenshots', function () {
}); });
expect(screenshot).toBeGolden('screenshot-clip-rect.png'); expect(screenshot).toBeGolden('screenshot-clip-rect.png');
}); });
it('should use scale for clip', async () => {
const {page, server} = await getTestState();
await page.setViewport({width: 500, height: 500});
await page.goto(server.PREFIX + '/grid.html');
const screenshot = await page.screenshot({
clip: {
x: 50,
y: 100,
width: 150,
height: 100,
scale: 2,
},
});
expect(screenshot).toBeGolden('screenshot-clip-rect-scale2.png');
});
it('should get screenshot bigger than the viewport', async () => { it('should get screenshot bigger than the viewport', async () => {
const {page, server} = await getTestState(); const {page, server} = await getTestState();
await page.setViewport({width: 50, height: 50}); await page.setViewport({width: 50, height: 50});
@ -123,6 +112,18 @@ describe('Screenshots', function () {
}); });
expect(screenshot).toBeGolden('screenshot-grid-fullpage.png'); expect(screenshot).toBeGolden('screenshot-grid-fullpage.png');
}); });
it('should take fullPage screenshots without captureBeyondViewport', async () => {
const {page, server} = await getTestState();
await page.setViewport({width: 500, height: 500});
await page.goto(server.PREFIX + '/grid.html');
const screenshot = await page.screenshot({
fullPage: true,
captureBeyondViewport: false,
});
expect(screenshot).toBeGolden('screenshot-grid-fullpage-2.png');
expect(page.viewport()).toMatchObject({width: 500, height: 500});
});
it('should run in parallel in multiple pages', async () => { it('should run in parallel in multiple pages', async () => {
const {server, context} = await getTestState(); const {server, context} = await getTestState();
@ -371,6 +372,22 @@ describe('Screenshots', function () {
}); });
describe('Cdp', () => { describe('Cdp', () => {
it('should use scale for clip', async () => {
const {page, server} = await getTestState();
await page.setViewport({width: 500, height: 500});
await page.goto(server.PREFIX + '/grid.html');
const screenshot = await page.screenshot({
clip: {
x: 50,
y: 100,
width: 150,
height: 100,
scale: 2,
},
});
expect(screenshot).toBeGolden('screenshot-clip-rect-scale2.png');
});
it('should allow transparency', async () => { it('should allow transparency', async () => {
const {page, server} = await getTestState(); const {page, server} = await getTestState();
@ -390,15 +407,18 @@ describe('Screenshots', function () {
}); });
expect(screenshot).toBeGolden('white.jpg'); expect(screenshot).toBeGolden('white.jpg');
}); });
it('should work in "fromSurface: false" mode', async () => { (!isHeadless ? it : it.skip)(
const {page, server} = await getTestState(); 'should work in "fromSurface: false" mode',
async () => {
const {page, server} = await getTestState();
await page.setViewport({width: 500, height: 500}); await page.setViewport({width: 500, height: 500});
await page.goto(server.PREFIX + '/grid.html'); await page.goto(server.PREFIX + '/grid.html');
const screenshot = await page.screenshot({ const screenshot = await page.screenshot({
fromSurface: false, fromSurface: false,
}); });
expect(screenshot).toBeDefined(); // toBeGolden('screenshot-fromsurface-false.png'); expect(screenshot).toBeDefined(); // toBeGolden('screenshot-fromsurface-false.png');
}); }
);
}); });
}); });