fix(api): page.viewport -> page.viewportSize (#878)

We now only allow to resize the page, leaving isMobile and deviceScaleFactor as browser context options.
This commit is contained in:
Dmitry Gozman 2020-02-06 19:02:55 -08:00 committed by GitHub
parent c33a12d8f0
commit fee83b17c9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 217 additions and 180 deletions

View File

@ -484,13 +484,13 @@ page.removeListener('request', logRequest);
- [page.setDefaultTimeout(timeout)](#pagesetdefaulttimeouttimeout) - [page.setDefaultTimeout(timeout)](#pagesetdefaulttimeouttimeout)
- [page.setExtraHTTPHeaders(headers)](#pagesetextrahttpheadersheaders) - [page.setExtraHTTPHeaders(headers)](#pagesetextrahttpheadersheaders)
- [page.setOfflineMode(enabled)](#pagesetofflinemodeenabled) - [page.setOfflineMode(enabled)](#pagesetofflinemodeenabled)
- [page.setViewport(viewport)](#pagesetviewportviewport) - [page.setViewportSize(viewportSize)](#pagesetviewportsizeviewportsize)
- [page.title()](#pagetitle) - [page.title()](#pagetitle)
- [page.tripleclick(selector[, options])](#pagetripleclickselector-options) - [page.tripleclick(selector[, options])](#pagetripleclickselector-options)
- [page.type(selector, text[, options])](#pagetypeselector-text-options) - [page.type(selector, text[, options])](#pagetypeselector-text-options)
- [page.uncheck(selector, [options])](#pageuncheckselector-options) - [page.uncheck(selector, [options])](#pageuncheckselector-options)
- [page.url()](#pageurl) - [page.url()](#pageurl)
- [page.viewport()](#pageviewport) - [page.viewportSize()](#pageviewportsize)
- [page.waitFor(selectorOrFunctionOrTimeout[, options[, ...args]])](#pagewaitforselectororfunctionortimeout-options-args) - [page.waitFor(selectorOrFunctionOrTimeout[, options[, ...args]])](#pagewaitforselectororfunctionortimeout-options-args)
- [page.waitForEvent(event[, optionsOrPredicate])](#pagewaitforeventevent-optionsorpredicate) - [page.waitForEvent(event[, optionsOrPredicate])](#pagewaitforeventevent-optionsorpredicate)
- [page.waitForFunction(pageFunction[, options[, ...args]])](#pagewaitforfunctionpagefunction-options-args) - [page.waitForFunction(pageFunction[, options[, ...args]])](#pagewaitforfunctionpagefunction-options-args)
@ -1338,26 +1338,21 @@ The extra HTTP headers will be sent with every request the page initiates.
- `enabled` <[boolean]> When `true`, enables offline mode for the page. - `enabled` <[boolean]> When `true`, enables offline mode for the page.
- returns: <[Promise]> - returns: <[Promise]>
#### page.setViewport(viewport) #### page.setViewportSize(viewportSize)
- `viewport` <[Object]> - `viewportSize` <[Object]>
- `width` <[number]> page width in pixels. **required** - `width` <[number]> page width in pixels. **required**
- `height` <[number]> page height in pixels. **required** - `height` <[number]> page height in pixels. **required**
- `deviceScaleFactor` <[number]> Specify device scale factor (can be thought of as dpr). Defaults to `1`.
- `isMobile` <[boolean]> Whether the `meta viewport` tag is taken into account. Defaults to `false`.
- returns: <[Promise]> - returns: <[Promise]>
> **NOTE** in certain cases, setting viewport will reload the page in order to set the `isMobile` property. In the case of multiple pages in a single browser, each page can have its own viewport size. However, [browser.newContext(options)](#browsernewcontextoptions) allows to set viewport size (and more) for all pages in the context at once.
In the case of multiple pages in a single browser, each page can have its own viewport size. `page.setViewportSize` will resize the page. A lot of websites don't expect phones to change size, so you should set the viewport size before navigating to the page.
`page.setViewport` will resize the page. A lot of websites don't expect phones to change size, so you should set the viewport before navigating to the page.
```js ```js
const page = await browser.newPage(); const page = await browser.newPage();
await page.setViewport({ await page.setViewportSize({
width: 640, width: 640,
height: 480, height: 480,
deviceScaleFactor: 1,
}); });
await page.goto('https://example.com'); await page.goto('https://example.com');
``` ```
@ -1426,12 +1421,10 @@ Shortcut for [page.mainFrame().uncheck(selector[, options])](#frameuncheckselect
This is a shortcut for [page.mainFrame().url()](#frameurl) This is a shortcut for [page.mainFrame().url()](#frameurl)
#### page.viewport() #### page.viewportSize()
- returns: <?[Object]> - returns: <?[Object]>
- `width` <[number]> page width in pixels. - `width` <[number]> page width in pixels.
- `height` <[number]> page height in pixels. - `height` <[number]> page height in pixels.
- `deviceScaleFactor` <[number]> Specify device scale factor (can be though of as dpr). Defaults to `1`.
- `isMobile` <[boolean]> Whether the `meta viewport` tag is taken into account. Defaults to `false`.
#### page.waitFor(selectorOrFunctionOrTimeout[, options[, ...args]]) #### page.waitFor(selectorOrFunctionOrTimeout[, options[, ...args]])
- `selectorOrFunctionOrTimeout` <[string]|[number]|[function]> A [selector], predicate or timeout to wait for - `selectorOrFunctionOrTimeout` <[string]|[number]|[function]> A [selector], predicate or timeout to wait for
@ -1499,7 +1492,7 @@ const { webkit } = require('playwright'); // Or 'chromium' or 'firefox'.
const browser = await webkit.launch(); const browser = await webkit.launch();
const page = await browser.newPage(); const page = await browser.newPage();
const watchDog = page.waitForFunction('window.innerWidth < 100'); const watchDog = page.waitForFunction('window.innerWidth < 100');
await page.setViewport({width: 50, height: 50}); await page.setViewportSize({width: 50, height: 50});
await watchDog; await watchDog;
await browser.close(); await browser.close();
})(); })();
@ -2136,7 +2129,7 @@ const { firefox } = require('playwright'); // Or 'chromium' or 'webkit'.
const browser = await firefox.launch(); const browser = await firefox.launch();
const page = await browser.newPage(); const page = await browser.newPage();
const watchDog = page.mainFrame().waitForFunction('window.innerWidth < 100'); const watchDog = page.mainFrame().waitForFunction('window.innerWidth < 100');
page.setViewport({width: 50, height: 50}); page.setViewportSize({width: 50, height: 50});
await watchDog; await watchDog;
await browser.close(); await browser.close();
})(); })();

View File

@ -18,7 +18,7 @@
import * as dom from '../dom'; import * as dom from '../dom';
import * as js from '../javascript'; import * as js from '../javascript';
import * as frames from '../frames'; import * as frames from '../frames';
import { debugError, helper, RegisteredListener } from '../helper'; import { debugError, helper, RegisteredListener, assert } from '../helper';
import * as network from '../network'; import * as network from '../network';
import { CRSession, CRConnection } from './crConnection'; import { CRSession, CRConnection } from './crConnection';
import { EVALUATION_SCRIPT_URL, CRExecutionContext } from './crExecutionContext'; import { EVALUATION_SCRIPT_URL, CRExecutionContext } from './crExecutionContext';
@ -106,7 +106,7 @@ export class CRPage implements PageDelegate {
if (options.ignoreHTTPSErrors) if (options.ignoreHTTPSErrors)
promises.push(this._client.send('Security.setIgnoreCertificateErrors', { ignore: true })); promises.push(this._client.send('Security.setIgnoreCertificateErrors', { ignore: true }));
if (options.viewport) if (options.viewport)
promises.push(this.setViewport(options.viewport)); promises.push(this._updateViewport(true /* updateTouch */));
if (options.javaScriptEnabled === false) if (options.javaScriptEnabled === false)
promises.push(this._client.send('Emulation.setScriptExecutionDisabled', { value: true })); promises.push(this._client.send('Emulation.setScriptExecutionDisabled', { value: true }));
if (options.userAgent) if (options.userAgent)
@ -317,19 +317,29 @@ export class CRPage implements PageDelegate {
await this._client.send('Network.setExtraHTTPHeaders', { headers }); await this._client.send('Network.setExtraHTTPHeaders', { headers });
} }
async setViewport(viewport: types.Viewport): Promise<void> { async setViewportSize(viewportSize: types.Size): Promise<void> {
const { assert(this._page._state.viewportSize === viewportSize);
width, await this._updateViewport(false /* updateTouch */);
height, }
isMobile = false,
deviceScaleFactor = 1, async _updateViewport(updateTouch: boolean): Promise<void> {
} = viewport; let viewport = this._page.browserContext()._options.viewport || { width: 0, height: 0 };
const isLandscape = width > height; const viewportSize = this._page._state.viewportSize;
const screenOrientation: Protocol.Emulation.ScreenOrientation = isLandscape ? { angle: 90, type: 'landscapePrimary' } : { angle: 0, type: 'portraitPrimary' }; if (viewportSize)
await Promise.all([ viewport = { ...viewport, ...viewportSize };
this._client.send('Emulation.setDeviceMetricsOverride', { mobile: isMobile, width, height, deviceScaleFactor, screenOrientation }), const isLandscape = viewport.width > viewport.height;
this._client.send('Emulation.setTouchEmulationEnabled', { enabled: isMobile }) const promises = [
]); this._client.send('Emulation.setDeviceMetricsOverride', {
mobile: !!viewport.isMobile,
width: viewport.width,
height: viewport.height,
deviceScaleFactor: viewport.deviceScaleFactor || 1,
screenOrientation: isLandscape ? { angle: 90, type: 'landscapePrimary' } : { angle: 0, type: 'portraitPrimary' },
}),
];
if (updateTouch)
promises.push(this._client.send('Emulation.setTouchEmulationEnabled', { enabled: !!viewport.isMobile }));
await Promise.all(promises);
} }
async setEmulateMedia(mediaType: types.MediaType | null, colorScheme: types.ColorScheme | null): Promise<void> { async setEmulateMedia(mediaType: types.MediaType | null, colorScheme: types.ColorScheme | null): Promise<void> {
@ -414,7 +424,7 @@ export class CRPage implements PageDelegate {
await this._client.send('Emulation.setDefaultBackgroundColorOverride', { color }); await this._client.send('Emulation.setDefaultBackgroundColorOverride', { color });
} }
async takeScreenshot(format: 'png' | 'jpeg', options: types.ScreenshotOptions): Promise<platform.BufferType> { async takeScreenshot(format: 'png' | 'jpeg', options: types.ScreenshotOptions, viewportSize: types.Size): Promise<platform.BufferType> {
await this._client.send('Page.bringToFront', {}); await this._client.send('Page.bringToFront', {});
const clip = options.clip ? { ...options.clip, scale: 1 } : undefined; const clip = options.clip ? { ...options.clip, scale: 1 } : undefined;
const result = await this._client.send('Page.captureScreenshot', { format, quality: options.quality, clip }); const result = await this._client.send('Page.captureScreenshot', { format, quality: options.quality, clip });

View File

@ -16,7 +16,7 @@
*/ */
import * as frames from '../frames'; import * as frames from '../frames';
import { helper, RegisteredListener, debugError } from '../helper'; import { helper, RegisteredListener, debugError, assert } from '../helper';
import * as dom from '../dom'; import * as dom from '../dom';
import { FFSession } from './ffConnection'; import { FFSession } from './ffConnection';
import { FFExecutionContext } from './ffExecutionContext'; import { FFExecutionContext } from './ffExecutionContext';
@ -84,7 +84,7 @@ export class FFPage implements PageDelegate {
]; ];
const options = this._page.browserContext()._options; const options = this._page.browserContext()._options;
if (options.viewport) if (options.viewport)
promises.push(this.setViewport(options.viewport)); promises.push(this._updateViewport());
if (options.bypassCSP) if (options.bypassCSP)
promises.push(this._session.send('Page.setBypassCSP', { enabled: true })); promises.push(this._session.send('Page.setBypassCSP', { enabled: true }));
if (options.javaScriptEnabled === false) if (options.javaScriptEnabled === false)
@ -266,15 +266,25 @@ export class FFPage implements PageDelegate {
await this._session.send('Network.setExtraHTTPHeaders', { headers: array }); await this._session.send('Network.setExtraHTTPHeaders', { headers: array });
} }
async setViewport(viewport: types.Viewport): Promise<void> { async setViewportSize(viewportSize: types.Size): Promise<void> {
const { assert(this._page._state.viewportSize === viewportSize);
width, await this._updateViewport();
height, }
isMobile = false,
deviceScaleFactor = 1, async _updateViewport() {
} = viewport; let viewport = this._page.browserContext()._options.viewport || { width: 0, height: 0 };
const viewportSize = this._page._state.viewportSize;
if (viewportSize)
viewport = { ...viewport, ...viewportSize };
await this._session.send('Page.setViewport', { await this._session.send('Page.setViewport', {
viewport: { width, height, isMobile, deviceScaleFactor, hasTouch: isMobile, isLandscape: width > height }, viewport: {
width: viewport.width,
height: viewport.height,
isMobile: !!viewport.isMobile,
deviceScaleFactor: viewport.deviceScaleFactor || 1,
hasTouch: !!viewport.isMobile,
isLandscape: viewport.width > viewport.height
},
}); });
} }
@ -349,7 +359,7 @@ export class FFPage implements PageDelegate {
throw new Error('Not implemented'); throw new Error('Not implemented');
} }
async takeScreenshot(format: 'png' | 'jpeg', options: types.ScreenshotOptions): Promise<platform.BufferType> { async takeScreenshot(format: 'png' | 'jpeg', options: types.ScreenshotOptions, viewportSize: types.Size): Promise<platform.BufferType> {
const { data } = await this._session.send('Page.screenshot', { const { data } = await this._session.send('Page.screenshot', {
mimeType: ('image/' + format) as ('image/png' | 'image/jpeg'), mimeType: ('image/' + format) as ('image/png' | 'image/jpeg'),
fullPage: options.fullPage, fullPage: options.fullPage,

View File

@ -46,7 +46,7 @@ export interface PageDelegate {
navigateFrame(frame: frames.Frame, url: string, referrer: string | undefined): Promise<frames.GotoResult>; navigateFrame(frame: frames.Frame, url: string, referrer: string | undefined): Promise<frames.GotoResult>;
setExtraHTTPHeaders(extraHTTPHeaders: network.Headers): Promise<void>; setExtraHTTPHeaders(extraHTTPHeaders: network.Headers): Promise<void>;
setViewport(viewport: types.Viewport): Promise<void>; setViewportSize(viewportSize: types.Size): Promise<void>;
setEmulateMedia(mediaType: types.MediaType | null, colorScheme: types.ColorScheme | null): Promise<void>; setEmulateMedia(mediaType: types.MediaType | null, colorScheme: types.ColorScheme | null): Promise<void>;
setCacheEnabled(enabled: boolean): Promise<void>; setCacheEnabled(enabled: boolean): Promise<void>;
setRequestInterception(enabled: boolean): Promise<void>; setRequestInterception(enabled: boolean): Promise<void>;
@ -57,7 +57,7 @@ export interface PageDelegate {
getBoundingBoxForScreenshot(handle: dom.ElementHandle<Node>): Promise<types.Rect | null>; getBoundingBoxForScreenshot(handle: dom.ElementHandle<Node>): Promise<types.Rect | null>;
canScreenshotOutsideViewport(): boolean; canScreenshotOutsideViewport(): boolean;
setBackgroundColor(color?: { r: number; g: number; b: number; a: number; }): Promise<void>; setBackgroundColor(color?: { r: number; g: number; b: number; a: number; }): Promise<void>;
takeScreenshot(format: string, options: types.ScreenshotOptions, viewport: types.Viewport): Promise<platform.BufferType>; takeScreenshot(format: string, options: types.ScreenshotOptions, viewportSize: types.Size): Promise<platform.BufferType>;
resetViewport(oldSize: types.Size): Promise<void>; resetViewport(oldSize: types.Size): Promise<void>;
isElementHandle(remoteObject: any): boolean; isElementHandle(remoteObject: any): boolean;
@ -76,7 +76,7 @@ export interface PageDelegate {
} }
type PageState = { type PageState = {
viewport: types.Viewport | null; viewportSize: types.Size | null;
mediaType: types.MediaType | null; mediaType: types.MediaType | null;
colorScheme: types.ColorScheme | null; colorScheme: types.ColorScheme | null;
extraHTTPHeaders: network.Headers | null; extraHTTPHeaders: network.Headers | null;
@ -122,8 +122,15 @@ export class Page extends platform.EventEmitter {
this._disconnectedCallback = () => {}; this._disconnectedCallback = () => {};
this._disconnectedPromise = new Promise(f => this._disconnectedCallback = f); this._disconnectedPromise = new Promise(f => this._disconnectedCallback = f);
this._browserContext = browserContext; this._browserContext = browserContext;
let viewportSize: types.Size | null = null;
if (browserContext._options.viewport) {
viewportSize = {
width: browserContext._options.viewport.width,
height: browserContext._options.viewport.height,
};
}
this._state = { this._state = {
viewport: browserContext._options.viewport || null, viewportSize,
mediaType: null, mediaType: null,
colorScheme: null, colorScheme: null,
extraHTTPHeaders: null, extraHTTPHeaders: null,
@ -390,17 +397,13 @@ export class Page extends platform.EventEmitter {
await this._delegate.setEmulateMedia(this._state.mediaType, this._state.colorScheme); await this._delegate.setEmulateMedia(this._state.mediaType, this._state.colorScheme);
} }
async setViewport(viewport: types.Viewport) { async setViewportSize(viewportSize: types.Size) {
const oldIsMobile = this._state.viewport ? !!this._state.viewport.isMobile : false; this._state.viewportSize = { ...viewportSize };
const newIsMobile = !!viewport.isMobile; await this._delegate.setViewportSize(this._state.viewportSize);
this._state.viewport = { ...viewport };
await this._delegate.setViewport(viewport);
if (oldIsMobile !== newIsMobile)
await this.reload();
} }
viewport(): types.Viewport | null { viewportSize(): types.Size | null {
return this._state.viewport; return this._state.viewportSize;
} }
evaluate: types.Evaluate = async (pageFunction, ...args) => { evaluate: types.Evaluate = async (pageFunction, ...args) => {

View File

@ -39,10 +39,10 @@ export class Screenshotter {
async screenshotPage(options: types.ScreenshotOptions = {}): Promise<platform.BufferType> { async screenshotPage(options: types.ScreenshotOptions = {}): Promise<platform.BufferType> {
const format = validateScreeshotOptions(options); const format = validateScreeshotOptions(options);
return this._queue.postTask(async () => { return this._queue.postTask(async () => {
let overridenViewport: types.Viewport | undefined; let overridenViewportSize: types.Size | undefined;
const viewport = this._page.viewport(); const originalViewportSize = this._page.viewportSize();
let viewportSize: types.Size | undefined; let viewportSize: types.Size | undefined;
if (!viewport) { if (!originalViewportSize) {
viewportSize = await this._page.evaluate(() => { viewportSize = await this._page.evaluate(() => {
if (!document.body || !document.documentElement) if (!document.body || !document.documentElement)
return; return;
@ -73,17 +73,17 @@ export class Screenshotter {
}); });
if (!fullPageRect) if (!fullPageRect)
throw new Error(kScreenshotDuringNavigationError); throw new Error(kScreenshotDuringNavigationError);
overridenViewport = viewport ? { ...viewport, ...fullPageRect } : fullPageRect; overridenViewportSize = fullPageRect;
await this._page.setViewport(overridenViewport); await this._page.setViewportSize(overridenViewportSize);
} else if (options.clip) { } else if (options.clip) {
options.clip = trimClipToViewport(viewport, options.clip); options.clip = trimClipToViewport(originalViewportSize, options.clip);
} }
const result = await this._screenshot(format, options, (overridenViewport || viewport)!); const result = await this._screenshot(format, options, (overridenViewportSize || originalViewportSize)!);
if (overridenViewport) { if (overridenViewportSize) {
if (viewport) if (originalViewportSize)
await this._page.setViewport(viewport); await this._page.setViewportSize(originalViewportSize);
else else
await this._page._delegate.resetViewport(viewportSize!); await this._page._delegate.resetViewport(viewportSize!);
} }
@ -95,7 +95,7 @@ export class Screenshotter {
const format = validateScreeshotOptions(options); const format = validateScreeshotOptions(options);
const rewrittenOptions: types.ScreenshotOptions = { ...options }; const rewrittenOptions: types.ScreenshotOptions = { ...options };
return this._queue.postTask(async () => { return this._queue.postTask(async () => {
let overridenViewport: types.Viewport | undefined; let overridenViewportSize: types.Size | undefined;
let viewportSize: types.Size; let viewportSize: types.Size;
let maybeBoundingBox = await this._page._delegate.getBoundingBoxForScreenshot(handle); let maybeBoundingBox = await this._page._delegate.getBoundingBoxForScreenshot(handle);
@ -105,9 +105,9 @@ export class Screenshotter {
assert(boundingBox.height !== 0, 'Node has 0 height.'); assert(boundingBox.height !== 0, 'Node has 0 height.');
boundingBox = enclosingIntRect(boundingBox); boundingBox = enclosingIntRect(boundingBox);
const viewport = this._page.viewport(); const originalViewportSize = this._page.viewportSize();
if (!this._page._delegate.canScreenshotOutsideViewport()) { if (!this._page._delegate.canScreenshotOutsideViewport()) {
if (!viewport) { if (!originalViewportSize) {
const maybeViewportSize = await this._page.evaluate(() => { const maybeViewportSize = await this._page.evaluate(() => {
if (!document.body || !document.documentElement) if (!document.body || !document.documentElement)
return; return;
@ -120,13 +120,14 @@ export class Screenshotter {
throw new Error(kScreenshotDuringNavigationError); throw new Error(kScreenshotDuringNavigationError);
viewportSize = maybeViewportSize!; viewportSize = maybeViewportSize!;
} else { } else {
viewportSize = viewport; viewportSize = originalViewportSize;
} }
if (boundingBox.width > viewportSize.width || boundingBox.height > viewportSize.height) { if (boundingBox.width > viewportSize.width || boundingBox.height > viewportSize.height) {
overridenViewport = {... (viewport || viewportSize)}; overridenViewportSize = {
overridenViewport.width = Math.max(viewportSize.width, boundingBox.width); width: Math.max(viewportSize.width, boundingBox.width),
overridenViewport.height = Math.max(viewportSize.height, boundingBox.height); height: Math.max(viewportSize.height, boundingBox.height),
await this._page.setViewport(overridenViewport); };
await this._page.setViewportSize(overridenViewportSize);
} }
await handle.scrollIntoViewIfNeeded(); await handle.scrollIntoViewIfNeeded();
@ -135,14 +136,14 @@ export class Screenshotter {
boundingBox = enclosingIntRect(maybeBoundingBox!); boundingBox = enclosingIntRect(maybeBoundingBox!);
} }
if (!overridenViewport) if (!overridenViewportSize)
rewrittenOptions.clip = boundingBox; rewrittenOptions.clip = boundingBox;
const result = await this._screenshot(format, rewrittenOptions, (overridenViewport || viewport)!); const result = await this._screenshot(format, rewrittenOptions, (overridenViewportSize || originalViewportSize)!);
if (overridenViewport) { if (overridenViewportSize) {
if (viewport) if (originalViewportSize)
await this._page.setViewport(viewport); await this._page.setViewportSize(originalViewportSize);
else else
await this._page._delegate.resetViewport(viewportSize!); await this._page._delegate.resetViewport(viewportSize!);
} }
@ -151,11 +152,11 @@ export class Screenshotter {
}).catch(rewriteError); }).catch(rewriteError);
} }
private async _screenshot(format: 'png' | 'jpeg', options: types.ScreenshotOptions, viewport: types.Viewport): Promise<platform.BufferType> { private async _screenshot(format: 'png' | 'jpeg', options: types.ScreenshotOptions, viewportSize: types.Size): Promise<platform.BufferType> {
const shouldSetDefaultBackground = options.omitBackground && format === 'png'; const shouldSetDefaultBackground = options.omitBackground && format === 'png';
if (shouldSetDefaultBackground) if (shouldSetDefaultBackground)
await this._page._delegate.setBackgroundColor({ r: 0, g: 0, b: 0, a: 0}); await this._page._delegate.setBackgroundColor({ r: 0, g: 0, b: 0, a: 0});
const buffer = await this._page._delegate.takeScreenshot(format, options, viewport); const buffer = await this._page._delegate.takeScreenshot(format, options, viewportSize);
if (shouldSetDefaultBackground) if (shouldSetDefaultBackground)
await this._page._delegate.setBackgroundColor(); await this._page._delegate.setBackgroundColor();
if (options.path) if (options.path)
@ -180,11 +181,11 @@ class TaskQueue {
} }
} }
function trimClipToViewport(viewport: types.Viewport | null, clip: types.Rect | undefined): types.Rect | undefined { function trimClipToViewport(viewportSize: types.Size | null, clip: types.Rect | undefined): types.Rect | undefined {
if (!clip || !viewport) if (!clip || !viewportSize)
return clip; return clip;
const p1 = { x: Math.min(clip.x, viewport.width), y: Math.min(clip.y, viewport.height) }; const p1 = { x: Math.min(clip.x, viewportSize.width), y: Math.min(clip.y, viewportSize.height) };
const p2 = { x: Math.min(clip.x + clip.width, viewport.width), y: Math.min(clip.y + clip.height, viewport.height) }; const p2 = { x: Math.min(clip.x + clip.width, viewportSize.width), y: Math.min(clip.y + clip.height, viewportSize.height) };
const result = { x: p1.x, y: p1.y, width: p2.x - p1.x, height: p2.y - p1.y }; const result = { x: p1.x, y: p1.y, width: p2.x - p1.x, height: p2.y - p1.y };
assert(result.width && result.height, 'Clipped area is either empty or outside the viewport'); assert(result.width && result.height, 'Clipped area is either empty or outside the viewport');
return result; return result;

View File

@ -73,8 +73,8 @@ export class WKPage implements PageDelegate {
const contextOptions = this._page.browserContext()._options; const contextOptions = this._page.browserContext()._options;
if (contextOptions.javaScriptEnabled === false) if (contextOptions.javaScriptEnabled === false)
promises.push(this._pageProxySession.send('Emulation.setJavaScriptEnabled', { enabled: false })); promises.push(this._pageProxySession.send('Emulation.setJavaScriptEnabled', { enabled: false }));
if (this._page._state.viewport) if (this._page._state.viewportSize || contextOptions.viewport)
promises.push(this.setViewport(this._page._state.viewport)); promises.push(this._updateViewport(true /* updateTouch */));
await Promise.all(promises); await Promise.all(promises);
} }
@ -384,16 +384,27 @@ export class WKPage implements PageDelegate {
await this._forAllSessions(session => WKPage._setEmulateMedia(session, mediaType, colorScheme)); await this._forAllSessions(session => WKPage._setEmulateMedia(session, mediaType, colorScheme));
} }
async setViewport(viewport: types.Viewport): Promise<void> { async setViewportSize(viewportSize: types.Size): Promise<void> {
const width = viewport.width; assert(this._page._state.viewportSize === viewportSize);
const height = viewport.height; await this._updateViewport(false /* updateTouch */);
const fixedLayout = !!viewport.isMobile; }
const deviceScaleFactor = viewport.deviceScaleFactor || 1;
this._page._state.hasTouch = !!viewport.isMobile; async _updateViewport(updateTouch: boolean): Promise<void> {
await Promise.all([ let viewport = this._page.browserContext()._options.viewport || { width: 0, height: 0 };
this._pageProxySession.send('Emulation.setDeviceMetricsOverride', {width, height, fixedLayout, deviceScaleFactor }), const viewportSize = this._page._state.viewportSize;
this._updateState('Page.setTouchEmulationEnabled', { enabled: !!viewport.isMobile }), if (viewportSize)
]); viewport = { ...viewport, ...viewportSize };
const promises: Promise<any>[] = [
this._pageProxySession.send('Emulation.setDeviceMetricsOverride', {
width: viewport.width,
height: viewport.height,
fixedLayout: !!viewport.isMobile,
deviceScaleFactor: viewport.deviceScaleFactor || 1
}),
];
if (updateTouch)
promises.push(this._updateState('Page.setTouchEmulationEnabled', { enabled: !!viewport.isMobile }));
await Promise.all(promises);
} }
async setCacheEnabled(enabled: boolean): Promise<void> { async setCacheEnabled(enabled: boolean): Promise<void> {
@ -478,8 +489,8 @@ export class WKPage implements PageDelegate {
await this._session.send('Page.setDefaultBackgroundColorOverride', { color }); await this._session.send('Page.setDefaultBackgroundColorOverride', { color });
} }
async takeScreenshot(format: string, options: types.ScreenshotOptions, viewport: types.Viewport): Promise<platform.BufferType> { async takeScreenshot(format: string, options: types.ScreenshotOptions, viewportSize: types.Size): Promise<platform.BufferType> {
const rect = options.clip || { x: 0, y: 0, width: viewport.width, height: viewport.height }; const rect = options.clip || { x: 0, y: 0, width: viewportSize.width, height: viewportSize.height };
const result = await this._session.send('Page.snapshotRect', { ...rect, coordinateSystem: options.fullPage ? 'Page' : 'Viewport' }); const result = await this._session.send('Page.snapshotRect', { ...rect, coordinateSystem: options.fullPage ? 'Page' : 'Viewport' });
const prefix = 'data:image/png;base64,'; const prefix = 'data:image/png;base64,';
let buffer = platform.Buffer.from(result.dataURL.substr(prefix.length), 'base64'); let buffer = platform.Buffer.from(result.dataURL.substr(prefix.length), 'base64');

View File

@ -100,8 +100,8 @@ module.exports.describe = function({testRunner, expect, playwright, CHROMIUM, WE
}); });
it('should propagate default viewport to the page', async({ newPage }) => { it('should propagate default viewport to the page', async({ newPage }) => {
const page = await newPage({ viewport: { width: 456, height: 789 } }); const page = await newPage({ viewport: { width: 456, height: 789 } });
expect(page.viewport().width).toBe(456); expect(page.viewportSize().width).toBe(456);
expect(page.viewport().height).toBe(789); expect(page.viewportSize().height).toBe(789);
expect(await page.evaluate('window.innerWidth')).toBe(456); expect(await page.evaluate('window.innerWidth')).toBe(456);
expect(await page.evaluate('window.innerHeight')).toBe(789); expect(await page.evaluate('window.innerHeight')).toBe(789);
}); });
@ -120,14 +120,14 @@ module.exports.describe = function({testRunner, expect, playwright, CHROMIUM, WE
}); });
it('should restore default viewport after fullPage screenshot', async({ newPage }) => { it('should restore default viewport after fullPage screenshot', async({ newPage }) => {
const page = await newPage({ viewport: { width: 456, height: 789 } }); const page = await newPage({ viewport: { width: 456, height: 789 } });
expect(page.viewport().width).toBe(456); expect(page.viewportSize().width).toBe(456);
expect(page.viewport().height).toBe(789); expect(page.viewportSize().height).toBe(789);
expect(await page.evaluate('window.innerWidth')).toBe(456); expect(await page.evaluate('window.innerWidth')).toBe(456);
expect(await page.evaluate('window.innerHeight')).toBe(789); expect(await page.evaluate('window.innerHeight')).toBe(789);
const screenshot = await page.screenshot({ fullPage: true }); const screenshot = await page.screenshot({ fullPage: true });
expect(screenshot).toBeInstanceOf(Buffer); expect(screenshot).toBeInstanceOf(Buffer);
expect(page.viewport().width).toBe(456); expect(page.viewportSize().width).toBe(456);
expect(page.viewport().height).toBe(789); expect(page.viewportSize().height).toBe(789);
expect(await page.evaluate('window.innerWidth')).toBe(456); expect(await page.evaluate('window.innerWidth')).toBe(456);
expect(await page.evaluate('window.innerHeight')).toBe(789); expect(await page.evaluate('window.innerHeight')).toBe(789);
}); });
@ -136,8 +136,8 @@ module.exports.describe = function({testRunner, expect, playwright, CHROMIUM, WE
const context = await newContext({ viewport }); const context = await newContext({ viewport });
viewport.width = 567; viewport.width = 567;
const page = await context.newPage(); const page = await context.newPage();
expect(page.viewport().width).toBe(456); expect(page.viewportSize().width).toBe(456);
expect(page.viewport().height).toBe(789); expect(page.viewportSize().height).toBe(789);
expect(await page.evaluate('window.innerWidth')).toBe(456); expect(await page.evaluate('window.innerWidth')).toBe(456);
expect(await page.evaluate('window.innerHeight')).toBe(789); expect(await page.evaluate('window.innerHeight')).toBe(789);
}); });

View File

@ -219,8 +219,9 @@ module.exports.describe = function({testRunner, expect, playwright, FFOX, CHROMI
expect(error.message).toBe('No node found for selector: button.does-not-exist'); expect(error.message).toBe('No node found for selector: button.does-not-exist');
}); });
// @see https://github.com/GoogleChrome/puppeteer/issues/161 // @see https://github.com/GoogleChrome/puppeteer/issues/161
it('should not hang with touch-enabled viewports', async({page, server}) => { it('should not hang with touch-enabled viewports', async({server, newContext}) => {
await page.setViewport(playwright.devices['iPhone 6'].viewport); const context = await newContext({ viewport: playwright.devices['iPhone 6'].viewport });
const page = await context.newPage();
await page.mouse.down(); await page.mouse.down();
await page.mouse.move(100, 10); await page.mouse.move(100, 10);
await page.mouse.up(); await page.mouse.up();
@ -284,7 +285,7 @@ module.exports.describe = function({testRunner, expect, playwright, FFOX, CHROMI
// @see https://github.com/GoogleChrome/puppeteer/issues/4110 // @see https://github.com/GoogleChrome/puppeteer/issues/4110
xit('should click the button with fixed position inside an iframe', async({page, server}) => { xit('should click the button with fixed position inside an iframe', async({page, server}) => {
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
await page.setViewport({width: 500, height: 500}); await page.setViewportSize({width: 500, height: 500});
await page.setContent('<div style="width:100px;height:2000px">spacer</div>'); await page.setContent('<div style="width:100px;height:2000px">spacer</div>');
await utils.attachFrame(page, 'button-test', server.CROSS_PROCESS_PREFIX + '/input/button.html'); await utils.attachFrame(page, 'button-test', server.CROSS_PROCESS_PREFIX + '/input/button.html');
const frame = page.frames()[1]; const frame = page.frames()[1];
@ -292,8 +293,9 @@ module.exports.describe = function({testRunner, expect, playwright, FFOX, CHROMI
await frame.click('button'); await frame.click('button');
expect(await frame.evaluate(() => window.result)).toBe('Clicked'); expect(await frame.evaluate(() => window.result)).toBe('Clicked');
}); });
it('should click the button with deviceScaleFactor set', async({page, server}) => { it('should click the button with deviceScaleFactor set', async({newContext, server}) => {
await page.setViewport({width: 400, height: 400, deviceScaleFactor: 5}); const context = await newContext({ viewport: {width: 400, height: 400, deviceScaleFactor: 5} });
const page = await context.newPage();
expect(await page.evaluate(() => window.devicePixelRatio)).toBe(5); expect(await page.evaluate(() => window.devicePixelRatio)).toBe(5);
await page.setContent('<div style="width:100px;height:100px">spacer</div>'); await page.setContent('<div style="width:100px;height:100px">spacer</div>');
await utils.attachFrame(page, 'button-test', server.PREFIX + '/input/button.html'); await utils.attachFrame(page, 'button-test', server.PREFIX + '/input/button.html');

View File

@ -24,14 +24,14 @@ module.exports.describe = function({testRunner, expect, FFOX, CHROMIUM, WEBKIT})
describe('ElementHandle.boundingBox', function() { describe('ElementHandle.boundingBox', function() {
it('should work', async({page, server}) => { it('should work', async({page, server}) => {
await page.setViewport({width: 500, height: 500}); await page.setViewportSize({width: 500, height: 500});
await page.goto(server.PREFIX + '/grid.html'); await page.goto(server.PREFIX + '/grid.html');
const elementHandle = await page.$('.box:nth-of-type(13)'); const elementHandle = await page.$('.box:nth-of-type(13)');
const box = await elementHandle.boundingBox(); const box = await elementHandle.boundingBox();
expect(box).toEqual({ x: 100, y: 50, width: 50, height: 50 }); expect(box).toEqual({ x: 100, y: 50, width: 50, height: 50 });
}); });
it('should handle nested frames', async({page, server}) => { it('should handle nested frames', async({page, server}) => {
await page.setViewport({width: 500, height: 500}); await page.setViewportSize({width: 500, height: 500});
await page.goto(server.PREFIX + '/frames/nested-frames.html'); await page.goto(server.PREFIX + '/frames/nested-frames.html');
const nestedFrame = page.frames().find(frame => frame.name() === 'dos'); const nestedFrame = page.frames().find(frame => frame.name() === 'dos');
const elementHandle = await nestedFrame.$('div'); const elementHandle = await nestedFrame.$('div');
@ -44,7 +44,7 @@ module.exports.describe = function({testRunner, expect, FFOX, CHROMIUM, WEBKIT})
expect(await element.boundingBox()).toBe(null); expect(await element.boundingBox()).toBe(null);
}); });
it('should force a layout', async({page, server}) => { it('should force a layout', async({page, server}) => {
await page.setViewport({ width: 500, height: 500 }); await page.setViewportSize({ width: 500, height: 500 });
await page.setContent('<div style="width: 100px; height: 100px">hello</div>'); await page.setContent('<div style="width: 100px; height: 100px">hello</div>');
const elementHandle = await page.$('div'); const elementHandle = await page.$('div');
await page.evaluate(element => element.style.height = '200px', elementHandle); await page.evaluate(element => element.style.height = '200px', elementHandle);

View File

@ -24,26 +24,30 @@ module.exports.describe = function({testRunner, expect, playwright, FFOX, CHROMI
describe('Page.viewport', function() { describe('Page.viewport', function() {
it('should get the proper viewport size', async({page, server}) => { it('should get the proper viewport size', async({page, server}) => {
expect(page.viewport()).toEqual({width: 800, height: 600}); expect(page.viewportSize()).toEqual({width: 800, height: 600});
await page.setViewport({width: 123, height: 456}); await page.setViewportSize({width: 123, height: 456});
expect(page.viewport()).toEqual({width: 123, height: 456}); expect(page.viewportSize()).toEqual({width: 123, height: 456});
}); });
it('should support mobile emulation', async({page, server}) => { it('should support mobile emulation', async({newContext, server}) => {
const context = await newContext({ viewport: iPhone.viewport });
const page = await context.newPage();
await page.goto(server.PREFIX + '/mobile.html'); await page.goto(server.PREFIX + '/mobile.html');
expect(await page.evaluate(() => window.innerWidth)).toBe(800);
await page.setViewport(iPhone.viewport);
expect(await page.evaluate(() => window.innerWidth)).toBe(375); expect(await page.evaluate(() => window.innerWidth)).toBe(375);
await page.setViewport({width: 400, height: 300}); await page.setViewportSize({width: 400, height: 300});
expect(await page.evaluate(() => window.innerWidth)).toBe(400); expect(await page.evaluate(() => window.innerWidth)).toBe(400);
}); });
it('should support touch emulation', async({page, server}) => { it('should not have touch by default', async({page, server}) => {
await page.goto(server.PREFIX + '/mobile.html'); await page.goto(server.PREFIX + '/mobile.html');
expect(await page.evaluate(() => 'ontouchstart' in window)).toBe(false); expect(await page.evaluate(() => 'ontouchstart' in window)).toBe(false);
await page.setViewport(iPhone.viewport); await page.goto(server.PREFIX + '/detect-touch.html');
expect(await page.evaluate(() => document.body.textContent.trim())).toBe('NO');
});
it('should support touch emulation', async({newContext, server}) => {
const context = await newContext({ viewport: iPhone.viewport });
const page = await context.newPage();
await page.goto(server.PREFIX + '/mobile.html');
expect(await page.evaluate(() => 'ontouchstart' in window)).toBe(true); expect(await page.evaluate(() => 'ontouchstart' in window)).toBe(true);
expect(await page.evaluate(dispatchTouch)).toBe('Received touch'); expect(await page.evaluate(dispatchTouch)).toBe('Received touch');
await page.setViewport({width: 100, height: 100});
expect(await page.evaluate(() => 'ontouchstart' in window)).toBe(false);
function dispatchTouch() { function dispatchTouch() {
let fulfill; let fulfill;
@ -58,48 +62,54 @@ module.exports.describe = function({testRunner, expect, playwright, FFOX, CHROMI
return promise; return promise;
} }
}); });
it('should be detectable by Modernizr', async({page, server}) => { it('should be detectable by Modernizr', async({newContext, server}) => {
await page.goto(server.PREFIX + '/detect-touch.html'); const context = await newContext({ viewport: iPhone.viewport });
expect(await page.evaluate(() => document.body.textContent.trim())).toBe('NO'); const page = await context.newPage();
await page.setViewport(iPhone.viewport);
await page.goto(server.PREFIX + '/detect-touch.html'); await page.goto(server.PREFIX + '/detect-touch.html');
expect(await page.evaluate(() => document.body.textContent.trim())).toBe('YES'); expect(await page.evaluate(() => document.body.textContent.trim())).toBe('YES');
}); });
it('should detect touch when applying viewport with touches', async({page, server}) => { it('should detect touch when applying viewport with touches', async({newContext, server}) => {
await page.setViewport({ width: 800, height: 600, isMobile: true }); const context = await newContext({ viewport: { width: 800, height: 600, isMobile: true } });
const page = await context.newPage();
await page.goto(server.EMPTY_PAGE);
await page.addScriptTag({url: server.PREFIX + '/modernizr.js'}); await page.addScriptTag({url: server.PREFIX + '/modernizr.js'});
expect(await page.evaluate(() => Modernizr.touchevents)).toBe(true); expect(await page.evaluate(() => Modernizr.touchevents)).toBe(true);
}); });
it.skip(FFOX)('should support landscape emulation', async({page, server}) => { it.skip(FFOX)('should support landscape emulation', async({newContext, server}) => {
await page.goto(server.PREFIX + '/mobile.html'); const context1 = await newContext({ viewport: iPhone.viewport });
await page.setViewport(iPhone.viewport); const page1 = await context1.newPage();
expect(await page.evaluate(() => matchMedia('(orientation: landscape)').matches)).toBe(false); await page1.goto(server.PREFIX + '/mobile.html');
await page.setViewport(iPhoneLandscape.viewport); expect(await page1.evaluate(() => matchMedia('(orientation: landscape)').matches)).toBe(false);
expect(await page.evaluate(() => matchMedia('(orientation: landscape)').matches)).toBe(true) const context2 = await newContext({ viewport: iPhoneLandscape.viewport });
const page2 = await context2.newPage();
expect(await page2.evaluate(() => matchMedia('(orientation: landscape)').matches)).toBe(true);
}); });
it.skip(FFOX || WEBKIT)('should fire orientationchange event', async({page, server}) => { it.skip(FFOX || WEBKIT)('should fire orientationchange event', async({newContext, server}) => {
const context = await newContext({ viewport: { width: 300, height: 400, isMobile: true } });
const page = await context.newPage();
await page.goto(server.PREFIX + '/mobile.html'); await page.goto(server.PREFIX + '/mobile.html');
await page.setViewport(iPhone.viewport);
await page.evaluate(() => { await page.evaluate(() => {
window.counter = 0; window.counter = 0;
window.addEventListener('orientationchange', () => console.log(++window.counter)); window.addEventListener('orientationchange', () => console.log(++window.counter));
}); });
const event1 = page.waitForEvent('console'); const event1 = page.waitForEvent('console');
await page.setViewport(iPhoneLandscape.viewport); await page.setViewportSize({width: 400, height: 300});
expect((await event1).text()).toBe('1'); expect((await event1).text()).toBe('1');
const event2 = page.waitForEvent('console'); const event2 = page.waitForEvent('console');
await page.setViewport(iPhone.viewport); await page.setViewportSize({width: 300, height: 400});
expect((await event2).text()).toBe('2'); expect((await event2).text()).toBe('2');
}); });
it.skip(FFOX)('default mobile viewports to 980 width', async({page, server}) => { it.skip(FFOX)('default mobile viewports to 980 width', async({newContext, server}) => {
await page.setViewport({width: 320, height: 480, isMobile: true}); const context = await newContext({ viewport: {width: 320, height: 480, isMobile: true} });
const page = await context.newPage();
await page.goto(server.PREFIX + '/empty.html'); await page.goto(server.PREFIX + '/empty.html');
expect(await page.evaluate(() => window.innerWidth)).toBe(980); expect(await page.evaluate(() => window.innerWidth)).toBe(980);
}); });
it.skip(FFOX)('respect meta viewport tag', async({page, server}) => { it.skip(FFOX)('respect meta viewport tag', async({newContext, server}) => {
await page.setViewport({width: 320, height: 480, isMobile: true}); const context = await newContext({ viewport: {width: 320, height: 480, isMobile: true} });
const page = await context.newPage();
await page.goto(server.PREFIX + '/mobile.html'); await page.goto(server.PREFIX + '/mobile.html');
expect(await page.evaluate(() => window.innerWidth)).toBe(320); expect(await page.evaluate(() => window.innerWidth)).toBe(320);
}); });

View File

@ -130,9 +130,10 @@ module.exports.describe = function({testRunner, expect, FFOX, CHROMIUM, WEBKIT,
]); ]);
}); });
// @see https://crbug.com/929806 // @see https://crbug.com/929806
it('should work with mobile viewports and cross process navigations', async({page, server}) => { it('should work with mobile viewports and cross process navigations', async({newContext, server}) => {
const context = await newContext({ viewport: {width: 360, height: 640, isMobile: true} });
const page = await context.newPage();
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
await page.setViewport({width: 360, height: 640, isMobile: true});
await page.goto(server.CROSS_PROCESS_PREFIX + '/mobile.html'); await page.goto(server.CROSS_PROCESS_PREFIX + '/mobile.html');
await page.evaluate(() => { await page.evaluate(() => {
document.addEventListener('click', event => { document.addEventListener('click', event => {

View File

@ -22,13 +22,13 @@ module.exports.describe = function({testRunner, expect, product, FFOX, CHROMIUM,
describe('Page.screenshot', function() { describe('Page.screenshot', function() {
it('should work', async({page, server}) => { it('should work', async({page, server}) => {
await page.setViewport({width: 500, height: 500}); await page.setViewportSize({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();
expect(screenshot).toBeGolden('screenshot-sanity.png'); expect(screenshot).toBeGolden('screenshot-sanity.png');
}); });
it('should clip rect', async({page, server}) => { it('should clip rect', async({page, server}) => {
await page.setViewport({width: 500, height: 500}); await page.setViewportSize({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({
clip: { clip: {
@ -41,7 +41,7 @@ module.exports.describe = function({testRunner, expect, product, FFOX, CHROMIUM,
expect(screenshot).toBeGolden('screenshot-clip-rect.png'); expect(screenshot).toBeGolden('screenshot-clip-rect.png');
}); });
it('should clip elements to the viewport', async({page, server}) => { it('should clip elements to the viewport', async({page, server}) => {
await page.setViewport({width: 500, height: 500}); await page.setViewportSize({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({
clip: { clip: {
@ -54,7 +54,7 @@ module.exports.describe = function({testRunner, expect, product, FFOX, CHROMIUM,
expect(screenshot).toBeGolden('screenshot-offscreen-clip.png'); expect(screenshot).toBeGolden('screenshot-offscreen-clip.png');
}); });
it('should throw on clip outside the viewport', async({page, server}) => { it('should throw on clip outside the viewport', async({page, server}) => {
await page.setViewport({width: 500, height: 500}); await page.setViewportSize({width: 500, height: 500});
await page.goto(server.PREFIX + '/grid.html'); await page.goto(server.PREFIX + '/grid.html');
const screenshotError = await page.screenshot({ const screenshotError = await page.screenshot({
clip: { clip: {
@ -67,7 +67,7 @@ module.exports.describe = function({testRunner, expect, product, FFOX, CHROMIUM,
expect(screenshotError.message).toBe('Clipped area is either empty or outside the viewport'); expect(screenshotError.message).toBe('Clipped area is either empty or outside the viewport');
}); });
it('should run in parallel', async({page, server}) => { it('should run in parallel', async({page, server}) => {
await page.setViewport({width: 500, height: 500}); await page.setViewportSize({width: 500, height: 500});
await page.goto(server.PREFIX + '/grid.html'); await page.goto(server.PREFIX + '/grid.html');
const promises = []; const promises = [];
for (let i = 0; i < 3; ++i) { for (let i = 0; i < 3; ++i) {
@ -84,7 +84,7 @@ module.exports.describe = function({testRunner, expect, product, FFOX, CHROMIUM,
expect(screenshots[1]).toBeGolden('grid-cell-1.png'); expect(screenshots[1]).toBeGolden('grid-cell-1.png');
}); });
it('should take fullPage screenshots', async({page, server}) => { it('should take fullPage screenshots', async({page, server}) => {
await page.setViewport({width: 500, height: 500}); await page.setViewportSize({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({
fullPage: true fullPage: true
@ -92,12 +92,12 @@ module.exports.describe = function({testRunner, expect, product, FFOX, CHROMIUM,
expect(screenshot).toBeGolden('screenshot-grid-fullpage.png'); expect(screenshot).toBeGolden('screenshot-grid-fullpage.png');
}); });
it('should restore viewport after fullPage screenshot', async({page, server}) => { it('should restore viewport after fullPage screenshot', async({page, server}) => {
await page.setViewport({width: 500, height: 500}); await page.setViewportSize({width: 500, height: 500});
await page.goto(server.PREFIX + '/grid.html'); await page.goto(server.PREFIX + '/grid.html');
const screenshot = await page.screenshot({ fullPage: true }); const screenshot = await page.screenshot({ fullPage: true });
expect(screenshot).toBeInstanceOf(Buffer); expect(screenshot).toBeInstanceOf(Buffer);
expect(page.viewport().width).toBe(500); expect(page.viewportSize().width).toBe(500);
expect(page.viewport().height).toBe(500); expect(page.viewportSize().height).toBe(500);
}); });
it('should run in parallel in multiple pages', async({page, server, context}) => { it('should run in parallel in multiple pages', async({page, server, context}) => {
const N = 2; const N = 2;
@ -115,7 +115,7 @@ module.exports.describe = function({testRunner, expect, product, FFOX, CHROMIUM,
await Promise.all(pages.map(page => page.close())); await Promise.all(pages.map(page => page.close()));
}); });
it.skip(FFOX)('should allow transparency', async({page, server}) => { it.skip(FFOX)('should allow transparency', async({page, server}) => {
await page.setViewport({ width: 50, height: 150 }); await page.setViewportSize({ width: 50, height: 150 });
await page.setContent(` await page.setContent(`
<style> <style>
body { margin: 0 } body { margin: 0 }
@ -129,7 +129,7 @@ module.exports.describe = function({testRunner, expect, product, FFOX, CHROMIUM,
expect(screenshot).toBeGolden('transparent.png'); expect(screenshot).toBeGolden('transparent.png');
}); });
it('should render white background on jpeg file', async({page, server}) => { it('should render white background on jpeg file', async({page, server}) => {
await page.setViewport({ width: 100, height: 100 }); await page.setViewportSize({ width: 100, height: 100 });
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
const screenshot = await page.screenshot({omitBackground: true, type: 'jpeg'}); const screenshot = await page.screenshot({omitBackground: true, type: 'jpeg'});
expect(screenshot).toBeGolden('white.jpg'); expect(screenshot).toBeGolden('white.jpg');
@ -146,44 +146,41 @@ module.exports.describe = function({testRunner, expect, product, FFOX, CHROMIUM,
expect(screenshot).toBeGolden('screenshot-clip-odd-size.png'); expect(screenshot).toBeGolden('screenshot-clip-odd-size.png');
}); });
it('should return base64', async({page, server}) => { it('should return base64', async({page, server}) => {
await page.setViewport({width: 500, height: 500}); await page.setViewportSize({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({
encoding: 'base64' encoding: 'base64'
}); });
expect(Buffer.from(screenshot, 'base64')).toBeGolden('screenshot-sanity.png'); expect(Buffer.from(screenshot, 'base64')).toBeGolden('screenshot-sanity.png');
}); });
it.skip(FFOX)('should work with a mobile viewport', async({page, server}) => { it.skip(FFOX)('should work with a mobile viewport', async({newContext, server}) => {
await page.setViewport({ const context = await newContext({viewport: { width: 320, height: 480, isMobile: true }});
width: 320, const page = await context.newPage();
height: 480,
isMobile: true
});
await page.goto(server.PREFIX + '/overflow.html'); await page.goto(server.PREFIX + '/overflow.html');
const screenshot = await page.screenshot(); const screenshot = await page.screenshot();
expect(screenshot).toBeGolden('screenshot-mobile.png'); expect(screenshot).toBeGolden('screenshot-mobile.png');
}); });
it('should work for canvas', async({page, server}) => { it('should work for canvas', async({page, server}) => {
await page.setViewport({width: 500, height: 500}); await page.setViewportSize({width: 500, height: 500});
await page.goto(server.PREFIX + '/screenshots/canvas.html'); await page.goto(server.PREFIX + '/screenshots/canvas.html');
const screenshot = await page.screenshot(); const screenshot = await page.screenshot();
expect(screenshot).toBeGolden('screenshot-canvas.png'); expect(screenshot).toBeGolden('screenshot-canvas.png');
}); });
it('should work for translateZ', async({page, server}) => { it('should work for translateZ', async({page, server}) => {
await page.setViewport({width: 500, height: 500}); await page.setViewportSize({width: 500, height: 500});
await page.goto(server.PREFIX + '/screenshots/translateZ.html'); await page.goto(server.PREFIX + '/screenshots/translateZ.html');
const screenshot = await page.screenshot(); const screenshot = await page.screenshot();
expect(screenshot).toBeGolden('screenshot-translateZ.png'); expect(screenshot).toBeGolden('screenshot-translateZ.png');
}); });
it.skip(FFOX || WEBKIT)('should work for webgl', async({page, server}) => { it.skip(FFOX || WEBKIT)('should work for webgl', async({page, server}) => {
await page.setViewport({width: 640, height: 480}); await page.setViewportSize({width: 640, height: 480});
await page.goto(server.PREFIX + '/screenshots/webgl.html'); await page.goto(server.PREFIX + '/screenshots/webgl.html');
const screenshot = await page.screenshot(); const screenshot = await page.screenshot();
expect(screenshot).toBeGolden('screenshot-webgl.png'); expect(screenshot).toBeGolden('screenshot-webgl.png');
}); });
// firefox is flaky // firefox is flaky
it.skip(FFOX)('should work while navigating', async({page, server}) => { it.skip(FFOX)('should work while navigating', async({page, server}) => {
await page.setViewport({width: 500, height: 500}); await page.setViewportSize({width: 500, height: 500});
await page.goto(server.PREFIX + '/redirectloop1.html'); await page.goto(server.PREFIX + '/redirectloop1.html');
for (let i = 0; i < 10; i++) { for (let i = 0; i < 10; i++) {
const screenshot = await page.screenshot({ fullPage: true }).catch(e => { const screenshot = await page.screenshot({ fullPage: true }).catch(e => {
@ -198,7 +195,7 @@ module.exports.describe = function({testRunner, expect, product, FFOX, CHROMIUM,
describe('ElementHandle.screenshot', function() { describe('ElementHandle.screenshot', function() {
it('should work', async({page, server}) => { it('should work', async({page, server}) => {
await page.setViewport({width: 500, height: 500}); await page.setViewportSize({width: 500, height: 500});
await page.goto(server.PREFIX + '/grid.html'); await page.goto(server.PREFIX + '/grid.html');
await page.evaluate(() => window.scrollBy(50, 100)); await page.evaluate(() => window.scrollBy(50, 100));
const elementHandle = await page.$('.box:nth-of-type(3)'); const elementHandle = await page.$('.box:nth-of-type(3)');
@ -206,7 +203,7 @@ module.exports.describe = function({testRunner, expect, product, FFOX, CHROMIUM,
expect(screenshot).toBeGolden('screenshot-element-bounding-box.png'); expect(screenshot).toBeGolden('screenshot-element-bounding-box.png');
}); });
it('should take into account padding and border', async({page, server}) => { it('should take into account padding and border', async({page, server}) => {
await page.setViewport({width: 500, height: 500}); await page.setViewportSize({width: 500, height: 500});
await page.setContent(` await page.setContent(`
<div style="height: 14px">oooo</div> <div style="height: 14px">oooo</div>
<style>div { <style>div {
@ -223,7 +220,7 @@ module.exports.describe = function({testRunner, expect, product, FFOX, CHROMIUM,
expect(screenshot).toBeGolden('screenshot-element-padding-border.png'); expect(screenshot).toBeGolden('screenshot-element-padding-border.png');
}); });
it('should capture full element when larger than viewport in parallel', async({page, server}) => { it('should capture full element when larger than viewport in parallel', async({page, server}) => {
await page.setViewport({width: 500, height: 500}); await page.setViewportSize({width: 500, height: 500});
await page.setContent(` await page.setContent(`
<div style="height: 14px">oooo</div> <div style="height: 14px">oooo</div>
@ -249,9 +246,8 @@ module.exports.describe = function({testRunner, expect, product, FFOX, CHROMIUM,
expect(await page.evaluate(() => ({ w: window.innerWidth, h: window.innerHeight }))).toEqual({ w: 500, h: 500 }); expect(await page.evaluate(() => ({ w: window.innerWidth, h: window.innerHeight }))).toEqual({ w: 500, h: 500 });
}); });
// Fails on GTK due to async setViewport.
it('should capture full element when larger than viewport', async({page, server}) => { it('should capture full element when larger than viewport', async({page, server}) => {
await page.setViewport({width: 500, height: 500}); await page.setViewportSize({width: 500, height: 500});
await page.setContent(` await page.setContent(`
<div style="height: 14px">oooo</div> <div style="height: 14px">oooo</div>
@ -277,7 +273,7 @@ module.exports.describe = function({testRunner, expect, product, FFOX, CHROMIUM,
expect(await page.evaluate(() => ({ w: window.innerWidth, h: window.innerHeight }))).toEqual({ w: 500, h: 500 }); expect(await page.evaluate(() => ({ w: window.innerWidth, h: window.innerHeight }))).toEqual({ w: 500, h: 500 });
}); });
it('should scroll element into view', async({page, server}) => { it('should scroll element into view', async({page, server}) => {
await page.setViewport({width: 500, height: 500}); await page.setViewportSize({width: 500, height: 500});
await page.setContent(` await page.setContent(`
<div style="height: 14px">oooo</div> <div style="height: 14px">oooo</div>
<style>div.above { <style>div.above {
@ -300,7 +296,7 @@ module.exports.describe = function({testRunner, expect, product, FFOX, CHROMIUM,
expect(screenshot).toBeGolden('screenshot-element-scrolled-into-view.png'); expect(screenshot).toBeGolden('screenshot-element-scrolled-into-view.png');
}); });
it('should work with a rotated element', async({page, server}) => { it('should work with a rotated element', async({page, server}) => {
await page.setViewport({width: 500, height: 500}); await page.setViewportSize({width: 500, height: 500});
await page.setContent(`<div style="position:absolute; await page.setContent(`<div style="position:absolute;
top: 100px; top: 100px;
left: 100px; left: 100px;

View File

@ -62,7 +62,7 @@ module.exports.describe = function({testRunner, expect, product, playwright, FFO
it('should wait for predicate', async({page, server}) => { it('should wait for predicate', async({page, server}) => {
await Promise.all([ await Promise.all([
page.waitFor(() => window.innerWidth < 130), // Windows doesn't like windows below 120px wide page.waitFor(() => window.innerWidth < 130), // Windows doesn't like windows below 120px wide
page.setViewport({width: 10, height: 10}), page.setViewportSize({width: 10, height: 10}),
]); ]);
}); });
it('should throw when unknown type', async({page, server}) => { it('should throw when unknown type', async({page, server}) => {

View File

@ -68,7 +68,7 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
it('should take screenshot', async({page, server}) => { it('should take screenshot', async({page, server}) => {
const { base64, bufferClassName } = await page.evaluate(async url => { const { base64, bufferClassName } = await page.evaluate(async url => {
await page.setViewport({width: 500, height: 500}); await page.setViewportSize({width: 500, height: 500});
await page.goto(url); await page.goto(url);
const screenshot = await page.screenshot(); const screenshot = await page.screenshot();
return { base64: screenshot.toString('base64'), bufferClassName: screenshot.constructor.name }; return { base64: screenshot.toString('base64'), bufferClassName: screenshot.constructor.name };