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.setExtraHTTPHeaders(headers)](#pagesetextrahttpheadersheaders)
- [page.setOfflineMode(enabled)](#pagesetofflinemodeenabled)
- [page.setViewport(viewport)](#pagesetviewportviewport)
- [page.setViewportSize(viewportSize)](#pagesetviewportsizeviewportsize)
- [page.title()](#pagetitle)
- [page.tripleclick(selector[, options])](#pagetripleclickselector-options)
- [page.type(selector, text[, options])](#pagetypeselector-text-options)
- [page.uncheck(selector, [options])](#pageuncheckselector-options)
- [page.url()](#pageurl)
- [page.viewport()](#pageviewport)
- [page.viewportSize()](#pageviewportsize)
- [page.waitFor(selectorOrFunctionOrTimeout[, options[, ...args]])](#pagewaitforselectororfunctionortimeout-options-args)
- [page.waitForEvent(event[, optionsOrPredicate])](#pagewaitforeventevent-optionsorpredicate)
- [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.
- returns: <[Promise]>
#### page.setViewport(viewport)
- `viewport` <[Object]>
#### page.setViewportSize(viewportSize)
- `viewportSize` <[Object]>
- `width` <[number]> page width 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]>
> **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.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.
`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.
```js
const page = await browser.newPage();
await page.setViewport({
await page.setViewportSize({
width: 640,
height: 480,
deviceScaleFactor: 1,
});
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)
#### page.viewport()
#### page.viewportSize()
- returns: <?[Object]>
- `width` <[number]> page width 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]])
- `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 page = await browser.newPage();
const watchDog = page.waitForFunction('window.innerWidth < 100');
await page.setViewport({width: 50, height: 50});
await page.setViewportSize({width: 50, height: 50});
await watchDog;
await browser.close();
})();
@ -2136,7 +2129,7 @@ const { firefox } = require('playwright'); // Or 'chromium' or 'webkit'.
const browser = await firefox.launch();
const page = await browser.newPage();
const watchDog = page.mainFrame().waitForFunction('window.innerWidth < 100');
page.setViewport({width: 50, height: 50});
page.setViewportSize({width: 50, height: 50});
await watchDog;
await browser.close();
})();

View File

@ -18,7 +18,7 @@
import * as dom from '../dom';
import * as js from '../javascript';
import * as frames from '../frames';
import { debugError, helper, RegisteredListener } from '../helper';
import { debugError, helper, RegisteredListener, assert } from '../helper';
import * as network from '../network';
import { CRSession, CRConnection } from './crConnection';
import { EVALUATION_SCRIPT_URL, CRExecutionContext } from './crExecutionContext';
@ -106,7 +106,7 @@ export class CRPage implements PageDelegate {
if (options.ignoreHTTPSErrors)
promises.push(this._client.send('Security.setIgnoreCertificateErrors', { ignore: true }));
if (options.viewport)
promises.push(this.setViewport(options.viewport));
promises.push(this._updateViewport(true /* updateTouch */));
if (options.javaScriptEnabled === false)
promises.push(this._client.send('Emulation.setScriptExecutionDisabled', { value: true }));
if (options.userAgent)
@ -317,19 +317,29 @@ export class CRPage implements PageDelegate {
await this._client.send('Network.setExtraHTTPHeaders', { headers });
}
async setViewport(viewport: types.Viewport): Promise<void> {
const {
width,
height,
isMobile = false,
deviceScaleFactor = 1,
} = viewport;
const isLandscape = width > height;
const screenOrientation: Protocol.Emulation.ScreenOrientation = isLandscape ? { angle: 90, type: 'landscapePrimary' } : { angle: 0, type: 'portraitPrimary' };
await Promise.all([
this._client.send('Emulation.setDeviceMetricsOverride', { mobile: isMobile, width, height, deviceScaleFactor, screenOrientation }),
this._client.send('Emulation.setTouchEmulationEnabled', { enabled: isMobile })
]);
async setViewportSize(viewportSize: types.Size): Promise<void> {
assert(this._page._state.viewportSize === viewportSize);
await this._updateViewport(false /* updateTouch */);
}
async _updateViewport(updateTouch: boolean): Promise<void> {
let viewport = this._page.browserContext()._options.viewport || { width: 0, height: 0 };
const viewportSize = this._page._state.viewportSize;
if (viewportSize)
viewport = { ...viewport, ...viewportSize };
const isLandscape = viewport.width > viewport.height;
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> {
@ -414,7 +424,7 @@ export class CRPage implements PageDelegate {
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', {});
const clip = options.clip ? { ...options.clip, scale: 1 } : undefined;
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 { helper, RegisteredListener, debugError } from '../helper';
import { helper, RegisteredListener, debugError, assert } from '../helper';
import * as dom from '../dom';
import { FFSession } from './ffConnection';
import { FFExecutionContext } from './ffExecutionContext';
@ -84,7 +84,7 @@ export class FFPage implements PageDelegate {
];
const options = this._page.browserContext()._options;
if (options.viewport)
promises.push(this.setViewport(options.viewport));
promises.push(this._updateViewport());
if (options.bypassCSP)
promises.push(this._session.send('Page.setBypassCSP', { enabled: true }));
if (options.javaScriptEnabled === false)
@ -266,15 +266,25 @@ export class FFPage implements PageDelegate {
await this._session.send('Network.setExtraHTTPHeaders', { headers: array });
}
async setViewport(viewport: types.Viewport): Promise<void> {
const {
width,
height,
isMobile = false,
deviceScaleFactor = 1,
} = viewport;
async setViewportSize(viewportSize: types.Size): Promise<void> {
assert(this._page._state.viewportSize === viewportSize);
await this._updateViewport();
}
async _updateViewport() {
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', {
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');
}
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', {
mimeType: ('image/' + format) as ('image/png' | 'image/jpeg'),
fullPage: options.fullPage,

View File

@ -46,7 +46,7 @@ export interface PageDelegate {
navigateFrame(frame: frames.Frame, url: string, referrer: string | undefined): Promise<frames.GotoResult>;
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>;
setCacheEnabled(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>;
canScreenshotOutsideViewport(): boolean;
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>;
isElementHandle(remoteObject: any): boolean;
@ -76,7 +76,7 @@ export interface PageDelegate {
}
type PageState = {
viewport: types.Viewport | null;
viewportSize: types.Size | null;
mediaType: types.MediaType | null;
colorScheme: types.ColorScheme | null;
extraHTTPHeaders: network.Headers | null;
@ -122,8 +122,15 @@ export class Page extends platform.EventEmitter {
this._disconnectedCallback = () => {};
this._disconnectedPromise = new Promise(f => this._disconnectedCallback = f);
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 = {
viewport: browserContext._options.viewport || null,
viewportSize,
mediaType: null,
colorScheme: null,
extraHTTPHeaders: null,
@ -390,17 +397,13 @@ export class Page extends platform.EventEmitter {
await this._delegate.setEmulateMedia(this._state.mediaType, this._state.colorScheme);
}
async setViewport(viewport: types.Viewport) {
const oldIsMobile = this._state.viewport ? !!this._state.viewport.isMobile : false;
const newIsMobile = !!viewport.isMobile;
this._state.viewport = { ...viewport };
await this._delegate.setViewport(viewport);
if (oldIsMobile !== newIsMobile)
await this.reload();
async setViewportSize(viewportSize: types.Size) {
this._state.viewportSize = { ...viewportSize };
await this._delegate.setViewportSize(this._state.viewportSize);
}
viewport(): types.Viewport | null {
return this._state.viewport;
viewportSize(): types.Size | null {
return this._state.viewportSize;
}
evaluate: types.Evaluate = async (pageFunction, ...args) => {

View File

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

View File

@ -73,8 +73,8 @@ export class WKPage implements PageDelegate {
const contextOptions = this._page.browserContext()._options;
if (contextOptions.javaScriptEnabled === false)
promises.push(this._pageProxySession.send('Emulation.setJavaScriptEnabled', { enabled: false }));
if (this._page._state.viewport)
promises.push(this.setViewport(this._page._state.viewport));
if (this._page._state.viewportSize || contextOptions.viewport)
promises.push(this._updateViewport(true /* updateTouch */));
await Promise.all(promises);
}
@ -384,16 +384,27 @@ export class WKPage implements PageDelegate {
await this._forAllSessions(session => WKPage._setEmulateMedia(session, mediaType, colorScheme));
}
async setViewport(viewport: types.Viewport): Promise<void> {
const width = viewport.width;
const height = viewport.height;
const fixedLayout = !!viewport.isMobile;
const deviceScaleFactor = viewport.deviceScaleFactor || 1;
this._page._state.hasTouch = !!viewport.isMobile;
await Promise.all([
this._pageProxySession.send('Emulation.setDeviceMetricsOverride', {width, height, fixedLayout, deviceScaleFactor }),
this._updateState('Page.setTouchEmulationEnabled', { enabled: !!viewport.isMobile }),
]);
async setViewportSize(viewportSize: types.Size): Promise<void> {
assert(this._page._state.viewportSize === viewportSize);
await this._updateViewport(false /* updateTouch */);
}
async _updateViewport(updateTouch: boolean): Promise<void> {
let viewport = this._page.browserContext()._options.viewport || { width: 0, height: 0 };
const viewportSize = this._page._state.viewportSize;
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> {
@ -478,8 +489,8 @@ export class WKPage implements PageDelegate {
await this._session.send('Page.setDefaultBackgroundColorOverride', { color });
}
async takeScreenshot(format: string, options: types.ScreenshotOptions, viewport: types.Viewport): Promise<platform.BufferType> {
const rect = options.clip || { x: 0, y: 0, width: viewport.width, height: viewport.height };
async takeScreenshot(format: string, options: types.ScreenshotOptions, viewportSize: types.Size): Promise<platform.BufferType> {
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 prefix = 'data:image/png;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 }) => {
const page = await newPage({ viewport: { width: 456, height: 789 } });
expect(page.viewport().width).toBe(456);
expect(page.viewport().height).toBe(789);
expect(page.viewportSize().width).toBe(456);
expect(page.viewportSize().height).toBe(789);
expect(await page.evaluate('window.innerWidth')).toBe(456);
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 }) => {
const page = await newPage({ viewport: { width: 456, height: 789 } });
expect(page.viewport().width).toBe(456);
expect(page.viewport().height).toBe(789);
expect(page.viewportSize().width).toBe(456);
expect(page.viewportSize().height).toBe(789);
expect(await page.evaluate('window.innerWidth')).toBe(456);
expect(await page.evaluate('window.innerHeight')).toBe(789);
const screenshot = await page.screenshot({ fullPage: true });
expect(screenshot).toBeInstanceOf(Buffer);
expect(page.viewport().width).toBe(456);
expect(page.viewport().height).toBe(789);
expect(page.viewportSize().width).toBe(456);
expect(page.viewportSize().height).toBe(789);
expect(await page.evaluate('window.innerWidth')).toBe(456);
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 });
viewport.width = 567;
const page = await context.newPage();
expect(page.viewport().width).toBe(456);
expect(page.viewport().height).toBe(789);
expect(page.viewportSize().width).toBe(456);
expect(page.viewportSize().height).toBe(789);
expect(await page.evaluate('window.innerWidth')).toBe(456);
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');
});
// @see https://github.com/GoogleChrome/puppeteer/issues/161
it('should not hang with touch-enabled viewports', async({page, server}) => {
await page.setViewport(playwright.devices['iPhone 6'].viewport);
it('should not hang with touch-enabled viewports', async({server, newContext}) => {
const context = await newContext({ viewport: playwright.devices['iPhone 6'].viewport });
const page = await context.newPage();
await page.mouse.down();
await page.mouse.move(100, 10);
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
xit('should click the button with fixed position inside an iframe', async({page, server}) => {
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 utils.attachFrame(page, 'button-test', server.CROSS_PROCESS_PREFIX + '/input/button.html');
const frame = page.frames()[1];
@ -292,8 +293,9 @@ module.exports.describe = function({testRunner, expect, playwright, FFOX, CHROMI
await frame.click('button');
expect(await frame.evaluate(() => window.result)).toBe('Clicked');
});
it('should click the button with deviceScaleFactor set', async({page, server}) => {
await page.setViewport({width: 400, height: 400, deviceScaleFactor: 5});
it('should click the button with deviceScaleFactor set', async({newContext, server}) => {
const context = await newContext({ viewport: {width: 400, height: 400, deviceScaleFactor: 5} });
const page = await context.newPage();
expect(await page.evaluate(() => window.devicePixelRatio)).toBe(5);
await page.setContent('<div style="width:100px;height:100px">spacer</div>');
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() {
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');
const elementHandle = await page.$('.box:nth-of-type(13)');
const box = await elementHandle.boundingBox();
expect(box).toEqual({ x: 100, y: 50, width: 50, height: 50 });
});
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');
const nestedFrame = page.frames().find(frame => frame.name() === 'dos');
const elementHandle = await nestedFrame.$('div');
@ -44,7 +44,7 @@ module.exports.describe = function({testRunner, expect, FFOX, CHROMIUM, WEBKIT})
expect(await element.boundingBox()).toBe(null);
});
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>');
const elementHandle = await page.$('div');
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() {
it('should get the proper viewport size', async({page, server}) => {
expect(page.viewport()).toEqual({width: 800, height: 600});
await page.setViewport({width: 123, height: 456});
expect(page.viewport()).toEqual({width: 123, height: 456});
expect(page.viewportSize()).toEqual({width: 800, height: 600});
await page.setViewportSize({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');
expect(await page.evaluate(() => window.innerWidth)).toBe(800);
await page.setViewport(iPhone.viewport);
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);
});
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');
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(dispatchTouch)).toBe('Received touch');
await page.setViewport({width: 100, height: 100});
expect(await page.evaluate(() => 'ontouchstart' in window)).toBe(false);
function dispatchTouch() {
let fulfill;
@ -58,48 +62,54 @@ module.exports.describe = function({testRunner, expect, playwright, FFOX, CHROMI
return promise;
}
});
it('should be detectable by Modernizr', async({page, server}) => {
await page.goto(server.PREFIX + '/detect-touch.html');
expect(await page.evaluate(() => document.body.textContent.trim())).toBe('NO');
await page.setViewport(iPhone.viewport);
it('should be detectable by Modernizr', async({newContext, server}) => {
const context = await newContext({ viewport: iPhone.viewport });
const page = await context.newPage();
await page.goto(server.PREFIX + '/detect-touch.html');
expect(await page.evaluate(() => document.body.textContent.trim())).toBe('YES');
});
it('should detect touch when applying viewport with touches', async({page, server}) => {
await page.setViewport({ width: 800, height: 600, isMobile: true });
it('should detect touch when applying viewport with touches', async({newContext, server}) => {
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'});
expect(await page.evaluate(() => Modernizr.touchevents)).toBe(true);
});
it.skip(FFOX)('should support landscape emulation', async({page, server}) => {
await page.goto(server.PREFIX + '/mobile.html');
await page.setViewport(iPhone.viewport);
expect(await page.evaluate(() => matchMedia('(orientation: landscape)').matches)).toBe(false);
await page.setViewport(iPhoneLandscape.viewport);
expect(await page.evaluate(() => matchMedia('(orientation: landscape)').matches)).toBe(true)
it.skip(FFOX)('should support landscape emulation', async({newContext, server}) => {
const context1 = await newContext({ viewport: iPhone.viewport });
const page1 = await context1.newPage();
await page1.goto(server.PREFIX + '/mobile.html');
expect(await page1.evaluate(() => matchMedia('(orientation: landscape)').matches)).toBe(false);
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.setViewport(iPhone.viewport);
await page.evaluate(() => {
window.counter = 0;
window.addEventListener('orientationchange', () => console.log(++window.counter));
});
const event1 = page.waitForEvent('console');
await page.setViewport(iPhoneLandscape.viewport);
await page.setViewportSize({width: 400, height: 300});
expect((await event1).text()).toBe('1');
const event2 = page.waitForEvent('console');
await page.setViewport(iPhone.viewport);
await page.setViewportSize({width: 300, height: 400});
expect((await event2).text()).toBe('2');
});
it.skip(FFOX)('default mobile viewports to 980 width', async({page, server}) => {
await page.setViewport({width: 320, height: 480, isMobile: true});
it.skip(FFOX)('default mobile viewports to 980 width', async({newContext, server}) => {
const context = await newContext({ viewport: {width: 320, height: 480, isMobile: true} });
const page = await context.newPage();
await page.goto(server.PREFIX + '/empty.html');
expect(await page.evaluate(() => window.innerWidth)).toBe(980);
});
it.skip(FFOX)('respect meta viewport tag', async({page, server}) => {
await page.setViewport({width: 320, height: 480, isMobile: true});
it.skip(FFOX)('respect meta viewport tag', async({newContext, server}) => {
const context = await newContext({ viewport: {width: 320, height: 480, isMobile: true} });
const page = await context.newPage();
await page.goto(server.PREFIX + '/mobile.html');
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
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.setViewport({width: 360, height: 640, isMobile: true});
await page.goto(server.CROSS_PROCESS_PREFIX + '/mobile.html');
await page.evaluate(() => {
document.addEventListener('click', event => {

View File

@ -22,13 +22,13 @@ module.exports.describe = function({testRunner, expect, product, FFOX, CHROMIUM,
describe('Page.screenshot', function() {
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');
const screenshot = await page.screenshot();
expect(screenshot).toBeGolden('screenshot-sanity.png');
});
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');
const screenshot = await page.screenshot({
clip: {
@ -41,7 +41,7 @@ module.exports.describe = function({testRunner, expect, product, FFOX, CHROMIUM,
expect(screenshot).toBeGolden('screenshot-clip-rect.png');
});
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');
const screenshot = await page.screenshot({
clip: {
@ -54,7 +54,7 @@ module.exports.describe = function({testRunner, expect, product, FFOX, CHROMIUM,
expect(screenshot).toBeGolden('screenshot-offscreen-clip.png');
});
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');
const screenshotError = await page.screenshot({
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');
});
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');
const promises = [];
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');
});
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');
const screenshot = await page.screenshot({
fullPage: true
@ -92,12 +92,12 @@ module.exports.describe = function({testRunner, expect, product, FFOX, CHROMIUM,
expect(screenshot).toBeGolden('screenshot-grid-fullpage.png');
});
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');
const screenshot = await page.screenshot({ fullPage: true });
expect(screenshot).toBeInstanceOf(Buffer);
expect(page.viewport().width).toBe(500);
expect(page.viewport().height).toBe(500);
expect(page.viewportSize().width).toBe(500);
expect(page.viewportSize().height).toBe(500);
});
it('should run in parallel in multiple pages', async({page, server, context}) => {
const N = 2;
@ -115,7 +115,7 @@ module.exports.describe = function({testRunner, expect, product, FFOX, CHROMIUM,
await Promise.all(pages.map(page => page.close()));
});
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(`
<style>
body { margin: 0 }
@ -129,7 +129,7 @@ module.exports.describe = function({testRunner, expect, product, FFOX, CHROMIUM,
expect(screenshot).toBeGolden('transparent.png');
});
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);
const screenshot = await page.screenshot({omitBackground: true, type: 'jpeg'});
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');
});
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');
const screenshot = await page.screenshot({
encoding: 'base64'
});
expect(Buffer.from(screenshot, 'base64')).toBeGolden('screenshot-sanity.png');
});
it.skip(FFOX)('should work with a mobile viewport', async({page, server}) => {
await page.setViewport({
width: 320,
height: 480,
isMobile: true
});
it.skip(FFOX)('should work with a mobile viewport', async({newContext, server}) => {
const context = await newContext({viewport: { width: 320, height: 480, isMobile: true }});
const page = await context.newPage();
await page.goto(server.PREFIX + '/overflow.html');
const screenshot = await page.screenshot();
expect(screenshot).toBeGolden('screenshot-mobile.png');
});
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');
const screenshot = await page.screenshot();
expect(screenshot).toBeGolden('screenshot-canvas.png');
});
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');
const screenshot = await page.screenshot();
expect(screenshot).toBeGolden('screenshot-translateZ.png');
});
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');
const screenshot = await page.screenshot();
expect(screenshot).toBeGolden('screenshot-webgl.png');
});
// firefox is flaky
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');
for (let i = 0; i < 10; i++) {
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() {
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.evaluate(() => window.scrollBy(50, 100));
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');
});
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(`
<div style="height: 14px">oooo</div>
<style>div {
@ -223,7 +220,7 @@ module.exports.describe = function({testRunner, expect, product, FFOX, CHROMIUM,
expect(screenshot).toBeGolden('screenshot-element-padding-border.png');
});
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(`
<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 });
});
// Fails on GTK due to async setViewport.
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(`
<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 });
});
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(`
<div style="height: 14px">oooo</div>
<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');
});
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;
top: 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}) => {
await Promise.all([
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}) => {

View File

@ -68,7 +68,7 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
it('should take screenshot', async({page, server}) => {
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);
const screenshot = await page.screenshot();
return { base64: screenshot.toString('base64'), bufferClassName: screenshot.constructor.name };