mirror of
https://github.com/microsoft/playwright.git
synced 2024-12-14 21:53:35 +03:00
feat(geo): enable geolocation overrides on WK (#361)
This commit is contained in:
parent
508a7eb016
commit
2d14d1ec1f
35
docs/api.md
35
docs/api.md
@ -21,6 +21,7 @@
|
||||
* [browserContext.newPage()](#browsercontextnewpage)
|
||||
* [browserContext.pages()](#browsercontextpages)
|
||||
* [browserContext.setCookies(cookies)](#browsercontextsetcookiescookies)
|
||||
* [browserContext.setGeolocation(options)](#browsercontextsetgeolocationoptions)
|
||||
* [browserContext.setPermissions(origin, permissions[])](#browsercontextsetpermissionsorigin-permissions)
|
||||
- [class: BrowserFetcher](#class-browserfetcher)
|
||||
* [browserFetcher.canDownload(revision)](#browserfetchercandownloadrevision)
|
||||
@ -235,8 +236,6 @@
|
||||
* [chromiumCoverage.startJSCoverage([options])](#chromiumcoveragestartjscoverageoptions)
|
||||
* [chromiumCoverage.stopCSSCoverage()](#chromiumcoveragestopcsscoverage)
|
||||
* [chromiumCoverage.stopJSCoverage()](#chromiumcoveragestopjscoverage)
|
||||
- [class: ChromiumOverrides](#class-chromiumoverrides)
|
||||
* [chromiumOverrides.setGeolocation(options)](#chromiumoverridessetgeolocationoptions)
|
||||
- [class: ChromiumPlaywright](#class-chromiumplaywright)
|
||||
* [chromiumPlaywright.connect(options)](#chromiumplaywrightconnectoptions)
|
||||
* [chromiumPlaywright.createBrowserFetcher([options])](#chromiumplaywrightcreatebrowserfetcheroptions)
|
||||
@ -465,6 +464,21 @@ An array of all pages inside the browser context.
|
||||
await browserContext.setCookies([cookieObject1, cookieObject2]);
|
||||
```
|
||||
|
||||
#### browserContext.setGeolocation(options)
|
||||
- `options` <[Object]>
|
||||
- `latitude` <[number]> Latitude between -90 and 90.
|
||||
- `longitude` <[number]> Longitude between -180 and 180.
|
||||
- `accuracy` <[number]> Optional non-negative accuracy value.
|
||||
- returns: <[Promise]>
|
||||
|
||||
Sets the page's geolocation. Passing null or undefined emulates position unavailable.
|
||||
|
||||
```js
|
||||
await browserContext.setGeolocation({latitude: 59.95, longitude: 30.31667});
|
||||
```
|
||||
|
||||
> **NOTE** Consider using [browserContext.setPermissions](#browsercontextsetpermissions-permissions) to grant permissions for the page to read its geolocation.
|
||||
|
||||
#### browserContext.setPermissions(origin, permissions[])
|
||||
- `origin` <[string]> The [origin] to grant permissions to, e.g. "https://example.com".
|
||||
- `permissions` <[Array]<[string]>> An array of permissions to grant. All permissions that are not listed here will be automatically denied. Permissions can be one of the following values:
|
||||
@ -3147,23 +3161,6 @@ _To output coverage in a form consumable by [Istanbul](https://github.com/istanb
|
||||
> **NOTE** JavaScript Coverage doesn't include anonymous scripts by default. However, scripts with sourceURLs are
|
||||
reported.
|
||||
|
||||
### class: ChromiumOverrides
|
||||
|
||||
#### chromiumOverrides.setGeolocation(options)
|
||||
- `options` <[Object]>
|
||||
- `latitude` <[number]> Latitude between -90 and 90.
|
||||
- `longitude` <[number]> Longitude between -180 and 180.
|
||||
- `accuracy` <[number]> Optional non-negative accuracy value.
|
||||
- returns: <[Promise]>
|
||||
|
||||
Sets the page's geolocation.
|
||||
|
||||
```js
|
||||
await browserContext.overrides.setGeolocation({latitude: 59.95, longitude: 30.31667});
|
||||
```
|
||||
|
||||
> **NOTE** Consider using [browserContext.setPermissions](#browsercontextsetpermissions-permissions) to grant permissions for the page to read its geolocation.
|
||||
|
||||
### class: ChromiumPlaywright
|
||||
|
||||
Playwright module provides a method to launch a Chromium instance.
|
||||
|
@ -31,6 +31,8 @@ export interface BrowserContextDelegate {
|
||||
|
||||
setPermissions(origin: string, permissions: string[]): Promise<void>;
|
||||
clearPermissions(): Promise<void>;
|
||||
|
||||
setGeolocation(geolocation: types.Geolocation | null): Promise<void>;
|
||||
}
|
||||
|
||||
export type BrowserContextOptions = {
|
||||
@ -41,7 +43,8 @@ export type BrowserContextOptions = {
|
||||
mediaType?: input.MediaType,
|
||||
colorScheme?: input.ColorScheme,
|
||||
userAgent?: string,
|
||||
timezoneId?: string
|
||||
timezoneId?: string,
|
||||
geolocation?: types.Geolocation
|
||||
};
|
||||
|
||||
export class BrowserContext {
|
||||
@ -84,6 +87,21 @@ export class BrowserContext {
|
||||
await this._delegate.clearPermissions();
|
||||
}
|
||||
|
||||
async setGeolocation(geolocation: types.Geolocation | null): Promise<void> {
|
||||
if (geolocation) {
|
||||
geolocation.accuracy = geolocation.accuracy || 0;
|
||||
const { longitude, latitude, accuracy } = geolocation;
|
||||
if (longitude < -180 || longitude > 180)
|
||||
throw new Error(`Invalid longitude "${longitude}": precondition -180 <= LONGITUDE <= 180 failed.`);
|
||||
if (latitude < -90 || latitude > 90)
|
||||
throw new Error(`Invalid latitude "${latitude}": precondition -90 <= LATITUDE <= 90 failed.`);
|
||||
if (accuracy < 0)
|
||||
throw new Error(`Invalid accuracy "${accuracy}": precondition 0 <= ACCURACY failed.`);
|
||||
}
|
||||
this._options.geolocation = geolocation;
|
||||
await this._delegate.setGeolocation(geolocation);
|
||||
}
|
||||
|
||||
async close() {
|
||||
if (this._closed)
|
||||
return;
|
||||
|
@ -8,5 +8,4 @@ export { CRPlaywright as ChromiumPlaywright } from './crPlaywright';
|
||||
export { CRTarget as ChromiumTarget } from './crTarget';
|
||||
export { CRAccessibility as ChromiumAccessibility } from './features/crAccessibility';
|
||||
export { CRCoverage as ChromiumCoverage } from './features/crCoverage';
|
||||
export { CROverrides as ChromiumOverrides } from './features/crOverrides';
|
||||
export { CRWorker as ChromiumWorker } from './features/crWorkers';
|
||||
|
@ -26,7 +26,7 @@ import { Protocol } from './protocol';
|
||||
import { CRPage } from './crPage';
|
||||
import * as browser from '../browser';
|
||||
import * as network from '../network';
|
||||
import { CROverrides } from './features/crOverrides';
|
||||
import * as types from '../types';
|
||||
import { CRWorker } from './features/crWorkers';
|
||||
import { ConnectionTransport } from '../transport';
|
||||
import { readProtocolStream } from './crProtocolHelper';
|
||||
@ -137,10 +137,13 @@ export class CRBrowser extends browser.Browser {
|
||||
|
||||
clearPermissions: async () => {
|
||||
await this._client.send('Browser.resetPermissions', { browserContextId: contextId || undefined });
|
||||
}
|
||||
},
|
||||
|
||||
setGeolocation: async (geolocation: types.Geolocation | null): Promise<void> => {
|
||||
for (const page of await context.pages())
|
||||
await (page._delegate as CRPage)._client.send('Emulation.setGeolocationOverride', geolocation || {});
|
||||
}
|
||||
}, options);
|
||||
(context as any).overrides = new CROverrides(context);
|
||||
return context;
|
||||
}
|
||||
|
||||
|
@ -38,7 +38,6 @@ import { BrowserContext } from '../browserContext';
|
||||
import * as types from '../types';
|
||||
import * as input from '../input';
|
||||
import { ConsoleMessage } from '../console';
|
||||
import { CROverrides } from './features/crOverrides';
|
||||
|
||||
const UTILITY_WORLD_NAME = '__playwright_utility_world__';
|
||||
|
||||
@ -93,7 +92,6 @@ export class CRPage implements PageDelegate {
|
||||
this._client.send('Page.setLifecycleEventsEnabled', { enabled: true }),
|
||||
this._client.send('Runtime.enable', {}).then(() => this._ensureIsolatedWorld(UTILITY_WORLD_NAME)),
|
||||
this._networkManager.initialize(),
|
||||
((this._page.browserContext() as any).overrides as CROverrides)._applyOverrides(this),
|
||||
];
|
||||
const options = this._page.browserContext()._options;
|
||||
if (options.bypassCSP)
|
||||
@ -112,6 +110,8 @@ export class CRPage implements PageDelegate {
|
||||
}
|
||||
if (options.timezoneId)
|
||||
promises.push(emulateTimezone(this._client, options.timezoneId));
|
||||
if (options.geolocation)
|
||||
promises.push(this._client.send('Emulation.setGeolocationOverride', options.geolocation));
|
||||
await Promise.all(promises);
|
||||
}
|
||||
|
||||
|
@ -1,53 +0,0 @@
|
||||
/**
|
||||
* Copyright 2017 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { BrowserContext } from '../../browserContext';
|
||||
import { CRPage } from '../crPage';
|
||||
|
||||
export class CROverrides {
|
||||
private _context: BrowserContext;
|
||||
private _geolocation: { longitude?: number; latitude?: number; accuracy?: number; } | null = null;
|
||||
|
||||
constructor(context: BrowserContext) {
|
||||
this._context = context;
|
||||
}
|
||||
|
||||
async setGeolocation(options: { longitude?: number; latitude?: number; accuracy?: (number | undefined); } | null) {
|
||||
if (!options) {
|
||||
for (const page of await this._context.pages())
|
||||
await (page._delegate as CRPage)._client.send('Emulation.clearGeolocationOverride', {});
|
||||
this._geolocation = null;
|
||||
return;
|
||||
}
|
||||
|
||||
const { longitude, latitude, accuracy = 0} = options;
|
||||
if (longitude < -180 || longitude > 180)
|
||||
throw new Error(`Invalid longitude "${longitude}": precondition -180 <= LONGITUDE <= 180 failed.`);
|
||||
if (latitude < -90 || latitude > 90)
|
||||
throw new Error(`Invalid latitude "${latitude}": precondition -90 <= LATITUDE <= 90 failed.`);
|
||||
if (accuracy < 0)
|
||||
throw new Error(`Invalid accuracy "${accuracy}": precondition 0 <= ACCURACY failed.`);
|
||||
this._geolocation = { longitude, latitude, accuracy };
|
||||
for (const page of await this._context.pages())
|
||||
await (page._delegate as CRPage)._client.send('Emulation.setGeolocationOverride', this._geolocation);
|
||||
}
|
||||
|
||||
async _applyOverrides(page: CRPage): Promise<void> {
|
||||
if (this._geolocation)
|
||||
await page._client.send('Emulation.setGeolocationOverride', this._geolocation);
|
||||
}
|
||||
}
|
@ -20,6 +20,7 @@ import { BrowserContext, BrowserContextOptions } from '../browserContext';
|
||||
import { Events } from '../events';
|
||||
import { assert, helper, RegisteredListener } from '../helper';
|
||||
import * as network from '../network';
|
||||
import * as types from '../types';
|
||||
import { Page } from '../page';
|
||||
import { ConnectionTransport } from '../transport';
|
||||
import { ConnectionEvents, FFConnection, FFSessionEvents } from './ffConnection';
|
||||
@ -199,8 +200,11 @@ export class FFBrowser extends browser.Browser {
|
||||
|
||||
clearPermissions: async () => {
|
||||
await this._connection.send('Browser.resetPermissions', { browserContextId: browserContextId || undefined });
|
||||
}
|
||||
},
|
||||
|
||||
setGeolocation: async (geolocation: types.Geolocation | null): Promise<void> => {
|
||||
throw new Error('Geolocation emulation is not supported in Firefox');
|
||||
}
|
||||
}, options);
|
||||
return context;
|
||||
}
|
||||
|
@ -56,3 +56,9 @@ export type Credentials = {
|
||||
username: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
export type Geolocation = {
|
||||
longitude?: number;
|
||||
latitude?: number;
|
||||
accuracy?: number | undefined;
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ import { EventEmitter } from 'events';
|
||||
import { helper, RegisteredListener, debugError, assert } from '../helper';
|
||||
import * as browser from '../browser';
|
||||
import * as network from '../network';
|
||||
import * as types from '../types';
|
||||
import { WKConnection, WKConnectionEvents, WKTargetSession } from './wkConnection';
|
||||
import { Page } from '../page';
|
||||
import { WKTarget } from './wkTarget';
|
||||
@ -203,12 +204,27 @@ export class WKBrowser extends browser.Browser {
|
||||
await this._connection.send('Browser.setCookies', { cookies: cc, browserContextId });
|
||||
},
|
||||
|
||||
|
||||
setPermissions: async (origin: string, permissions: string[]): Promise<void> => {
|
||||
throw new Error('Permissions are not supported in WebKit');
|
||||
const webPermissionToProtocol = new Map<string, string>([
|
||||
['geolocation', 'geolocation'],
|
||||
]);
|
||||
const filtered = permissions.map(permission => {
|
||||
const protocolPermission = webPermissionToProtocol.get(permission);
|
||||
if (!protocolPermission)
|
||||
throw new Error('Unknown permission: ' + permission);
|
||||
return protocolPermission;
|
||||
});
|
||||
await this._connection.send('Browser.grantPermissions', { origin, browserContextId, permissions: filtered });
|
||||
},
|
||||
|
||||
clearPermissions: async () => {
|
||||
throw new Error('Permissions are not supported in WebKit');
|
||||
await this._connection.send('Browser.resetPermissions', { browserContextId });
|
||||
},
|
||||
|
||||
setGeolocation: async (geolocation: types.Geolocation | null): Promise<void> => {
|
||||
const payload: any = geolocation ? { ...geolocation, timestamp: Date.now() } : undefined;
|
||||
await this._connection.send('Browser.setGeolocationOverride', { browserContextId, geolocation: payload });
|
||||
}
|
||||
}, options);
|
||||
return context;
|
||||
|
@ -15,18 +15,18 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
module.exports.describe = function ({ testRunner, expect }) {
|
||||
module.exports.describe = function ({ testRunner, expect, FFOX }) {
|
||||
const {describe, xdescribe, fdescribe} = testRunner;
|
||||
const {it, fit, xit, dit} = testRunner;
|
||||
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
|
||||
|
||||
// FIXME: not supported in WebKit (as well as Emulation domain in general).
|
||||
// It was removed from WebKit in https://webkit.org/b/126630
|
||||
describe('Overrides.setGeolocation', function() {
|
||||
describe.skip(FFOX)('Overrides.setGeolocation', function() {
|
||||
it('should work', async({page, server, context}) => {
|
||||
await context.setPermissions(server.PREFIX, ['geolocation']);
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await context.overrides.setGeolocation({longitude: 10, latitude: 10});
|
||||
await context.setGeolocation({longitude: 10, latitude: 10});
|
||||
const geolocation = await page.evaluate(() => new Promise(resolve => navigator.geolocation.getCurrentPosition(position => {
|
||||
resolve({latitude: position.coords.latitude, longitude: position.coords.longitude});
|
||||
})));
|
||||
@ -38,7 +38,7 @@ module.exports.describe = function ({ testRunner, expect }) {
|
||||
it('should throw when invalid longitude', async({context}) => {
|
||||
let error = null;
|
||||
try {
|
||||
await context.overrides.setGeolocation({longitude: 200, latitude: 10});
|
||||
await context.setGeolocation({longitude: 200, latitude: 10});
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
@ -166,11 +166,11 @@ module.exports.describe = ({testRunner, product, playwrightPath}) => {
|
||||
testRunner.loadTests(require('./screenshot.spec.js'), testOptions);
|
||||
testRunner.loadTests(require('./waittask.spec.js'), testOptions);
|
||||
testRunner.loadTests(require('./interception.spec.js'), testOptions);
|
||||
testRunner.loadTests(require('./geolocation.spec.js'), testOptions);
|
||||
|
||||
if (CHROME) {
|
||||
testRunner.loadTests(require('./chromium/chromium.spec.js'), testOptions);
|
||||
testRunner.loadTests(require('./chromium/coverage.spec.js'), testOptions);
|
||||
testRunner.loadTests(require('./chromium/geolocation.spec.js'), testOptions);
|
||||
testRunner.loadTests(require('./chromium/pdf.spec.js'), testOptions);
|
||||
testRunner.loadTests(require('./chromium/session.spec.js'), testOptions);
|
||||
testRunner.loadTests(require('./chromium/workers.spec.js'), testOptions);
|
||||
|
Loading…
Reference in New Issue
Block a user