api(devices): extract isMobile/hasTouch from viewport (#1415)

This commit is contained in:
Pavel Feldman 2020-03-17 18:21:02 -07:00 committed by GitHub
parent 39e5eb7eda
commit 049b336800
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 422 additions and 340 deletions

View file

@ -197,9 +197,10 @@ Indicates that the browser is connected.
- `viewport` <[Object]> Sets a consistent viewport for each page. Defaults to an 1280x720 viewport. `null` disables the default viewport. - `viewport` <[Object]> Sets a consistent viewport for each page. Defaults to an 1280x720 viewport. `null` disables the default viewport.
- `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 thought of as dpr). Defaults to `1`.
- `isMobile` <[boolean]> Whether the `meta viewport` tag is taken into account and touch events are enabled. Defaults to `false`. Not supported in Firefox.
- `userAgent` <[string]> Specific user agent to use in this context. - `userAgent` <[string]> Specific user agent to use in this context.
- `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 and touch events are enabled. Defaults to `false`. Not supported in Firefox.
- `hasTouch` <[boolean]> Specifies if viewport supports touch events. Defaults to false.
- `javaScriptEnabled` <[boolean]> Whether or not to enable or disable JavaScript in the context. Defaults to true. - `javaScriptEnabled` <[boolean]> Whether or not to enable or disable JavaScript in the context. Defaults to true.
- `timezoneId` <?[string]> Changes the timezone of the context. See [ICUs `metaZones.txt`](https://cs.chromium.org/chromium/src/third_party/icu/source/data/misc/metaZones.txt?rcl=faee8bc70570192d82d2978a71e2a615788597d1) for a list of supported timezone IDs. - `timezoneId` <?[string]> Changes the timezone of the context. See [ICUs `metaZones.txt`](https://cs.chromium.org/chromium/src/third_party/icu/source/data/misc/metaZones.txt?rcl=faee8bc70570192d82d2978a71e2a615788597d1) for a list of supported timezone IDs.
- `geolocation` <[Object]> - `geolocation` <[Object]>
@ -232,12 +233,13 @@ Creates a new browser context. It won't share cookies/cache with other browser c
- `options` <[Object]> - `options` <[Object]>
- `ignoreHTTPSErrors` <?[boolean]> Whether to ignore HTTPS errors during navigation. Defaults to `false`. - `ignoreHTTPSErrors` <?[boolean]> Whether to ignore HTTPS errors during navigation. Defaults to `false`.
- `bypassCSP` <?[boolean]> Toggles bypassing page's Content-Security-Policy. - `bypassCSP` <?[boolean]> Toggles bypassing page's Content-Security-Policy.
- `viewport` <?[Object]> Sets a consistent viewport for each page. Defaults to an 1280x720 viewport. `null` disables the default viewport. - `viewport` <[Object]> Sets a consistent viewport for each page. Defaults to an 1280x720 viewport. `null` disables the default viewport.
- `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 thought of as dpr). Defaults to `1`. - `userAgent` <[string]> Specific user agent to use in this context.
- `isMobile` <[boolean]> Whether the `meta viewport` tag is taken into account and touch events are enabled. Defaults to `false`. Not supported in Firefox. - `deviceScaleFactor` <[number]> Specify device scale factor (can be thought of as dpr). Defaults to `1`.
- `userAgent` <?[string]> Specific user agent to use in this context. - `isMobile` <[boolean]> Whether the `meta viewport` tag is taken into account and touch events are enabled. Defaults to `false`. Not supported in Firefox.
- `hasTouch` <[boolean]> Specifies if viewport supports touch events. Defaults to false.
- `javaScriptEnabled` <?[boolean]> Whether or not to enable or disable JavaScript in the context. Defaults to true. - `javaScriptEnabled` <?[boolean]> Whether or not to enable or disable JavaScript in the context. Defaults to true.
- `timezoneId` <?[string]> Changes the timezone of the context. See [ICUs `metaZones.txt`](https://cs.chromium.org/chromium/src/third_party/icu/source/data/misc/metaZones.txt?rcl=faee8bc70570192d82d2978a71e2a615788597d1) for a list of supported timezone IDs. - `timezoneId` <?[string]> Changes the timezone of the context. See [ICUs `metaZones.txt`](https://cs.chromium.org/chromium/src/third_party/icu/source/data/misc/metaZones.txt?rcl=faee8bc70570192d82d2978a71e2a615788597d1) for a list of supported timezone IDs.
- `geolocation` <[Object]> - `geolocation` <[Object]>

View file

@ -24,7 +24,7 @@ import * as types from './types';
import { Events } from './events'; import { Events } from './events';
export type BrowserContextOptions = { export type BrowserContextOptions = {
viewport?: types.Viewport | null, viewport?: types.Size | null,
ignoreHTTPSErrors?: boolean, ignoreHTTPSErrors?: boolean,
javaScriptEnabled?: boolean, javaScriptEnabled?: boolean,
bypassCSP?: boolean, bypassCSP?: boolean,
@ -36,6 +36,9 @@ export type BrowserContextOptions = {
extraHTTPHeaders?: network.Headers, extraHTTPHeaders?: network.Headers,
offline?: boolean, offline?: boolean,
httpCredentials?: types.Credentials, httpCredentials?: types.Credentials,
deviceScaleFactor?: number,
isMobile?: boolean,
hasTouch?: boolean
}; };
export interface BrowserContext { export interface BrowserContext {

View file

@ -350,24 +350,25 @@ export class CRPage implements PageDelegate {
} }
async _updateViewport(updateTouch: boolean): Promise<void> { async _updateViewport(updateTouch: boolean): Promise<void> {
let viewport = this._browserContext._options.viewport || { width: 0, height: 0 }; const options = this._browserContext._options;
let viewport = options.viewport || { width: 0, height: 0 };
const viewportSize = this._page._state.viewportSize; const viewportSize = this._page._state.viewportSize;
if (viewportSize) if (viewportSize)
viewport = { ...viewport, ...viewportSize }; viewport = { ...viewport, ...viewportSize };
const isLandscape = viewport.width > viewport.height; const isLandscape = viewport.width > viewport.height;
const promises = [ const promises = [
this._client.send('Emulation.setDeviceMetricsOverride', { this._client.send('Emulation.setDeviceMetricsOverride', {
mobile: !!viewport.isMobile, mobile: !!options.isMobile,
width: viewport.width, width: viewport.width,
height: viewport.height, height: viewport.height,
screenWidth: viewport.width, screenWidth: viewport.width,
screenHeight: viewport.height, screenHeight: viewport.height,
deviceScaleFactor: viewport.deviceScaleFactor || 1, deviceScaleFactor: options.deviceScaleFactor || 1,
screenOrientation: isLandscape ? { angle: 90, type: 'landscapePrimary' } : { angle: 0, type: 'portraitPrimary' }, screenOrientation: isLandscape ? { angle: 90, type: 'landscapePrimary' } : { angle: 0, type: 'portraitPrimary' },
}), }),
]; ];
if (updateTouch) if (updateTouch)
promises.push(this._client.send('Emulation.setTouchEmulationEnabled', { enabled: !!viewport.isMobile })); promises.push(this._client.send('Emulation.setTouchEmulationEnabled', { enabled: !!options.hasTouch }));
await Promise.all(promises); await Promise.all(promises);
} }

File diff suppressed because it is too large Load diff

View file

@ -73,11 +73,13 @@ export class FFBrowser extends platform.EventEmitter implements Browser {
let viewport; let viewport;
if (options.viewport) { if (options.viewport) {
// TODO: remove isMobile/hasTouch from the protocol? // TODO: remove isMobile/hasTouch from the protocol?
if (options.viewport.isMobile) if (options.isMobile)
throw new Error('viewport.isMobile is not supported in Firefox'); throw new Error('options.isMobile is not supported in Firefox');
if (options.hasTouch)
throw new Error('options.hasTouch is not supported in Firefox');
viewport = { viewport = {
viewportSize: { width: options.viewport.width, height: options.viewport.height }, viewportSize: { width: options.viewport.width, height: options.viewport.height },
deviceScaleFactor: options.viewport.deviceScaleFactor || 1, deviceScaleFactor: options.deviceScaleFactor || 1,
isMobile: false, isMobile: false,
hasTouch: false, hasTouch: false,
}; };

View file

@ -77,13 +77,6 @@ export type ScreenshotOptions = ElementScreenshotOptions & {
clip?: Rect, clip?: Rect,
}; };
export type Viewport = {
width: number;
height: number;
deviceScaleFactor?: number;
isMobile?: boolean;
};
export type URLMatch = string | RegExp | ((url: URL) => boolean); export type URLMatch = string | RegExp | ((url: URL) => boolean);
export type Credentials = { export type Credentials = {
@ -117,7 +110,10 @@ export const colorSchemes: Set<ColorScheme> = new Set(['dark', 'light', 'no-pref
export type DeviceDescriptor = { export type DeviceDescriptor = {
userAgent: string, userAgent: string,
viewport: Viewport, viewport: Size,
deviceScaleFactor: number,
isMobile: boolean,
hasTouch: boolean
}; };
export type Devices = { [name: string]: DeviceDescriptor }; export type Devices = { [name: string]: DeviceDescriptor };

View file

@ -155,7 +155,7 @@ export class WKPage implements PageDelegate {
promises.push(session.send('Network.setExtraHTTPHeaders', { headers: this._calculateExtraHTTPHeaders() })); promises.push(session.send('Network.setExtraHTTPHeaders', { headers: this._calculateExtraHTTPHeaders() }));
if (contextOptions.offline) if (contextOptions.offline)
promises.push(session.send('Network.setEmulateOfflineState', { offline: true })); promises.push(session.send('Network.setEmulateOfflineState', { offline: true }));
promises.push(session.send('Page.setTouchEmulationEnabled', { enabled: contextOptions.viewport ? !!contextOptions.viewport.isMobile : false })); promises.push(session.send('Page.setTouchEmulationEnabled', { enabled: !!contextOptions.hasTouch }));
if (contextOptions.timezoneId) { if (contextOptions.timezoneId) {
promises.push(session.send('Page.setTimeZone', { timeZone: contextOptions.timezoneId }). promises.push(session.send('Page.setTimeZone', { timeZone: contextOptions.timezoneId }).
catch(e => { throw new Error(`Invalid timezone ID: ${contextOptions.timezoneId}`); })); catch(e => { throw new Error(`Invalid timezone ID: ${contextOptions.timezoneId}`); }));
@ -485,7 +485,8 @@ export class WKPage implements PageDelegate {
} }
async _updateViewport(): Promise<void> { async _updateViewport(): Promise<void> {
let viewport = this._browserContext._options.viewport || { width: 0, height: 0 }; const options = this._browserContext._options;
let viewport = options.viewport || { width: 0, height: 0 };
const viewportSize = this._page._state.viewportSize; const viewportSize = this._page._state.viewportSize;
if (viewportSize) if (viewportSize)
viewport = { ...viewport, ...viewportSize }; viewport = { ...viewport, ...viewportSize };
@ -493,8 +494,8 @@ export class WKPage implements PageDelegate {
this._pageProxySession.send('Emulation.setDeviceMetricsOverride', { this._pageProxySession.send('Emulation.setDeviceMetricsOverride', {
width: viewport.width, width: viewport.width,
height: viewport.height, height: viewport.height,
fixedLayout: !!viewport.isMobile, fixedLayout: !!options.isMobile,
deviceScaleFactor: viewport.deviceScaleFactor || 1 deviceScaleFactor: options.deviceScaleFactor || 1
}), }),
this._session.send('Page.setScreenSizeOverride', { this._session.send('Page.setScreenSizeOverride', {
width: viewport.width, width: viewport.width,

View file

@ -307,7 +307,7 @@ module.exports.describe = function({testRunner, expect, playwright, FFOX, CHROMI
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({browser, server}) => { it('should click the button with deviceScaleFactor set', async({browser, server}) => {
const context = await browser.newContext({ viewport: {width: 400, height: 400, deviceScaleFactor: 5} }); const context = await browser.newContext({ viewport: { width: 400, height: 400 }, deviceScaleFactor: 5 });
const page = await context.newPage(); 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>');
@ -367,7 +367,7 @@ module.exports.describe = function({testRunner, expect, playwright, FFOX, CHROMI
expect(await page.evaluate(() => offsetY)).toBe(WEBKIT ? 1910 + 8 : 1910); expect(await page.evaluate(() => offsetY)).toBe(WEBKIT ? 1910 + 8 : 1910);
}); });
it.skip(FFOX)('should click the button with offset with page scale', async({browser, server}) => { it.skip(FFOX)('should click the button with offset with page scale', async({browser, server}) => {
const context = await browser.newContext({ viewport: { width: 400, height: 400, isMobile: true} }); const context = await browser.newContext({ viewport: { width: 400, height: 400 }, isMobile: true });
const page = await context.newPage(); const page = await context.newPage();
await page.goto(server.PREFIX + '/input/button.html'); await page.goto(server.PREFIX + '/input/button.html');
await page.$eval('button', button => { await page.$eval('button', button => {

View file

@ -120,7 +120,7 @@ module.exports.describe = function({testRunner, expect, playwright, headless, FF
await context.close(); await context.close();
}); });
it('should detect touch when applying viewport with touches', async({browser, server}) => { it('should detect touch when applying viewport with touches', async({browser, server}) => {
const context = await browser.newContext({ viewport: { width: 800, height: 600, isMobile: true } }); const context = await browser.newContext({ viewport: { width: 800, height: 600 }, hasTouch: true });
const page = await context.newPage(); const page = await context.newPage();
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
await page.addScriptTag({url: server.PREFIX + '/modernizr.js'}); await page.addScriptTag({url: server.PREFIX + '/modernizr.js'});
@ -139,7 +139,7 @@ module.exports.describe = function({testRunner, expect, playwright, headless, FF
await context2.close(); await context2.close();
}); });
it.fail(WEBKIT)('should fire orientationchange event', async({browser, server}) => { it.fail(WEBKIT)('should fire orientationchange event', async({browser, server}) => {
const context = await browser.newContext({ viewport: { width: 300, height: 400, isMobile: true } }); const context = await browser.newContext({ viewport: { width: 300, height: 400 }, isMobile: true });
const page = await context.newPage(); const page = await context.newPage();
await page.goto(server.PREFIX + '/mobile.html'); await page.goto(server.PREFIX + '/mobile.html');
await page.evaluate(() => { await page.evaluate(() => {
@ -157,14 +157,14 @@ module.exports.describe = function({testRunner, expect, playwright, headless, FF
await context.close(); await context.close();
}); });
it('default mobile viewports to 980 width', async({browser, server}) => { it('default mobile viewports to 980 width', async({browser, server}) => {
const context = await browser.newContext({ viewport: {width: 320, height: 480, isMobile: true} }); const context = await browser.newContext({ viewport: {width: 320, height: 480 }, isMobile: true });
const page = await context.newPage(); 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);
await context.close(); await context.close();
}); });
it('respect meta viewport tag', async({browser, server}) => { it('respect meta viewport tag', async({browser, server}) => {
const context = await browser.newContext({ viewport: {width: 320, height: 480, isMobile: true} }); const context = await browser.newContext({ viewport: {width: 320, height: 480 }, isMobile: true });
const page = await context.newPage(); 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

@ -114,7 +114,8 @@ module.exports.describe = function({testRunner, expect, playwright, CHROMIUM, WE
}); });
it.skip(FFOX)('should inherit touch support from browser context', async function({browser, server}) { it.skip(FFOX)('should inherit touch support from browser context', async function({browser, server}) {
const context = await browser.newContext({ const context = await browser.newContext({
viewport: { width: 400, height: 500, isMobile: true } viewport: { width: 400, height: 500 },
hasTouch: true
}); });
const page = await context.newPage(); const page = await context.newPage();
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);

View file

@ -164,7 +164,7 @@ 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.skip(FFOX)('should work with a mobile viewport', async({browser, server}) => { it.skip(FFOX)('should work with a mobile viewport', async({browser, server}) => {
const context = await browser.newContext({viewport: { width: 320, height: 480, isMobile: true }}); const context = await browser.newContext({ viewport: { width: 320, height: 480 }, isMobile: true });
const page = await context.newPage(); const page = await context.newPage();
await page.goto(server.PREFIX + '/overflow.html'); await page.goto(server.PREFIX + '/overflow.html');
const screenshot = await page.screenshot(); const screenshot = await page.screenshot();
@ -172,7 +172,7 @@ module.exports.describe = function({testRunner, expect, product, FFOX, CHROMIUM,
await context.close(); await context.close();
}); });
it.skip(FFOX)('should work with a mobile viewport and clip', async({browser, server}) => { it.skip(FFOX)('should work with a mobile viewport and clip', async({browser, server}) => {
const context = await browser.newContext({viewport: { width: 320, height: 480, isMobile: true }}); const context = await browser.newContext({viewport: { width: 320, height: 480 }, isMobile: true});
const page = await context.newPage(); const page = await context.newPage();
await page.goto(server.PREFIX + '/overflow.html'); await page.goto(server.PREFIX + '/overflow.html');
const screenshot = await page.screenshot({ clip: { x: 10, y: 10, width: 100, height: 150 } }); const screenshot = await page.screenshot({ clip: { x: 10, y: 10, width: 100, height: 150 } });
@ -180,7 +180,7 @@ module.exports.describe = function({testRunner, expect, product, FFOX, CHROMIUM,
await context.close(); await context.close();
}); });
it.skip(FFOX)('should work with a mobile viewport and fullPage', async({browser, server}) => { it.skip(FFOX)('should work with a mobile viewport and fullPage', async({browser, server}) => {
const context = await browser.newContext({viewport: { width: 320, height: 480, isMobile: true }}); const context = await browser.newContext({viewport: { width: 320, height: 480 }, isMobile: true});
const page = await context.newPage(); const page = await context.newPage();
await page.goto(server.PREFIX + '/overflow-large.html'); await page.goto(server.PREFIX + '/overflow-large.html');
const screenshot = await page.screenshot({ fullPage: true }); const screenshot = await page.screenshot({ fullPage: true });