feat(electron): accept BrowserContextOptions in electron.launch (#6621)

This commit is contained in:
Joel Einbinder 2021-05-19 06:56:29 -07:00 committed by GitHub
parent 972f0ec2b5
commit 754ee13c93
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 302 additions and 14 deletions

View File

@ -76,3 +76,20 @@ Specifies environment variables that will be visible to Electron. Defaults to `p
- `timeout` <[float]>
Maximum time in milliseconds to wait for the application to start. Defaults to `30000` (30 seconds). Pass `0` to disable timeout.
### option: Electron.launch.acceptdownloads = %%-context-option-acceptdownloads-%%
### option: Electron.launch.bypassCSP = %%-context-option-bypasscsp-%%
### option: Electron.launch.colorScheme = %%-context-option-colorscheme-%%
### option: Electron.launch.extraHTTPHeaders = %%-context-option-extrahttpheaders-%%
### option: Electron.launch.geolocation = %%-context-option-geolocation-%%
### option: Electron.launch.httpcredentials = %%-context-option-httpcredentials-%%
### option: Electron.launch.ignoreHTTPSErrors = %%-context-option-ignorehttpserrors-%%
### option: Electron.launch.locale = %%-context-option-locale-%%
### option: Electron.launch.offline = %%-context-option-offline-%%
### option: Electron.launch.recordhar = %%-context-option-recordhar-%%
### option: Electron.launch.recordhar.path = %%-context-option-recordhar-path-%%
### option: Electron.launch.recordhar.recordHarOmitContent = %%-context-option-recordhar-omit-content-%%
### option: Electron.launch.recordvideo = %%-context-option-recordvideo-%%
### option: Electron.launch.recordvideo.dir = %%-context-option-recordvideo-dir-%%
### option: Electron.launch.recordvideo.size = %%-context-option-recordvideo-size-%%
### option: Electron.launch.timezoneId = %%-context-option-timezoneid-%%

View File

@ -19,17 +19,19 @@ import * as structs from '../../types/structs';
import * as api from '../../types/types';
import * as channels from '../protocol/channels';
import { TimeoutSettings } from '../utils/timeoutSettings';
import { headersObjectToArray } from '../utils/utils';
import { BrowserContext } from './browserContext';
import { ChannelOwner } from './channelOwner';
import { envObjectToArray } from './clientHelper';
import { Events } from './events';
import { JSHandle, parseResult, serializeArgument } from './jsHandle';
import { Page } from './page';
import { Env, WaitForEventOptions } from './types';
import { Env, WaitForEventOptions, Headers } from './types';
import { Waiter } from './waiter';
type ElectronOptions = Omit<channels.ElectronLaunchOptions, 'env'> & {
type ElectronOptions = Omit<channels.ElectronLaunchOptions, 'env'|'extraHTTPHeaders'> & {
env?: Env,
extraHTTPHeaders?: Headers,
};
type ElectronAppType = typeof import('electron');
@ -48,6 +50,7 @@ export class Electron extends ChannelOwner<channels.ElectronChannel, channels.El
const params: channels.ElectronLaunchParams = {
sdkLanguage: 'javascript',
...options,
extraHTTPHeaders: options.extraHTTPHeaders && headersObjectToArray(options.extraHTTPHeaders),
env: envObjectToArray(options.env ? options.env : process.env),
};
return ElectronApplication.from((await channel.launch(params)).electronApplication);

View File

@ -2555,6 +2555,34 @@ export type ElectronLaunchParams = {
cwd?: string,
env?: NameValue[],
timeout?: number,
acceptDownloads?: boolean,
bypassCSP?: boolean,
colorScheme?: 'dark' | 'light' | 'no-preference',
extraHTTPHeaders?: NameValue[],
geolocation?: {
longitude: number,
latitude: number,
accuracy?: number,
},
httpCredentials?: {
username: string,
password: string,
},
ignoreHTTPSErrors?: boolean,
locale?: string,
offline?: boolean,
recordHar?: {
omitContent?: boolean,
path: string,
},
recordVideo?: {
dir: string,
size?: {
width: number,
height: number,
},
},
timezoneId?: string,
};
export type ElectronLaunchOptions = {
executablePath?: string,
@ -2562,6 +2590,34 @@ export type ElectronLaunchOptions = {
cwd?: string,
env?: NameValue[],
timeout?: number,
acceptDownloads?: boolean,
bypassCSP?: boolean,
colorScheme?: 'dark' | 'light' | 'no-preference',
extraHTTPHeaders?: NameValue[],
geolocation?: {
longitude: number,
latitude: number,
accuracy?: number,
},
httpCredentials?: {
username: string,
password: string,
},
ignoreHTTPSErrors?: boolean,
locale?: string,
offline?: boolean,
recordHar?: {
omitContent?: boolean,
path: string,
},
recordVideo?: {
dir: string,
size?: {
width: number,
height: number,
},
},
timezoneId?: string,
};
export type ElectronLaunchResult = {
electronApplication: ElectronApplicationChannel,

View File

@ -2094,6 +2094,47 @@ Electron:
type: array?
items: NameValue
timeout: number?
acceptDownloads: boolean?
bypassCSP: boolean?
colorScheme:
type: enum?
literals:
- dark
- light
- no-preference
extraHTTPHeaders:
type: array?
items: NameValue
geolocation:
type: object?
properties:
longitude: number
latitude: number
accuracy: number?
httpCredentials:
type: object?
properties:
username: string
password: string
ignoreHTTPSErrors: boolean?
locale: string?
offline: boolean?
recordHar:
type: object?
properties:
omitContent: boolean?
path: string
recordVideo:
type: object?
properties:
dir: string
size:
type: object?
properties:
width: number
height: number
timezoneId: string?
returns:
electronApplication: ElectronApplication

View File

@ -973,6 +973,34 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme {
cwd: tOptional(tString),
env: tOptional(tArray(tType('NameValue'))),
timeout: tOptional(tNumber),
acceptDownloads: tOptional(tBoolean),
bypassCSP: tOptional(tBoolean),
colorScheme: tOptional(tEnum(['dark', 'light', 'no-preference'])),
extraHTTPHeaders: tOptional(tArray(tType('NameValue'))),
geolocation: tOptional(tObject({
longitude: tNumber,
latitude: tNumber,
accuracy: tOptional(tNumber),
})),
httpCredentials: tOptional(tObject({
username: tString,
password: tString,
})),
ignoreHTTPSErrors: tOptional(tBoolean),
locale: tOptional(tString),
offline: tOptional(tBoolean),
recordHar: tOptional(tObject({
omitContent: tOptional(tBoolean),
path: tString,
})),
recordVideo: tOptional(tObject({
dir: tString,
size: tOptional(tObject({
width: tNumber,
height: tNumber,
})),
})),
timezoneId: tOptional(tString),
});
scheme.ElectronApplicationBrowserWindowParams = tObject({
page: tChannel('Page'),

View File

@ -22,7 +22,6 @@ import * as js from '../javascript';
import { Page } from '../page';
import { TimeoutSettings } from '../../utils/timeoutSettings';
import { WebSocketTransport } from '../transport';
import * as types from '../types';
import { launchProcess, envArrayToObject } from '../processLauncher';
import { BrowserContext } from '../browserContext';
import type {BrowserWindow} from 'electron';
@ -33,15 +32,7 @@ import * as childProcess from 'child_process';
import * as readline from 'readline';
import { RecentLogsCollector } from '../../utils/debugLogger';
import { internalCallMetadata, SdkObject } from '../instrumentation';
export type ElectronLaunchOptionsBase = {
sdkLanguage: string,
executablePath?: string,
args?: string[],
cwd?: string,
env?: types.EnvArray,
timeout?: number,
};
import * as channels from '../../protocol/channels';
export class ElectronApplication extends SdkObject {
static Events = {
@ -112,7 +103,7 @@ export class Electron extends SdkObject {
this._playwrightOptions = playwrightOptions;
}
async launch(options: ElectronLaunchOptionsBase): Promise<ElectronApplication> {
async launch(options: channels.ElectronLaunchParams): Promise<ElectronApplication> {
const {
args = [],
} = options;
@ -164,7 +155,22 @@ export class Electron extends SdkObject {
name: 'electron',
isChromium: true,
headful: true,
persistent: { sdkLanguage: options.sdkLanguage, noDefaultViewport: true },
persistent: {
sdkLanguage: options.sdkLanguage,
noDefaultViewport: true,
acceptDownloads: options.acceptDownloads,
bypassCSP: options.bypassCSP,
colorScheme: options.colorScheme,
extraHTTPHeaders: options.extraHTTPHeaders,
geolocation: options.geolocation,
httpCredentials: options.httpCredentials,
ignoreHTTPSErrors: options.ignoreHTTPSErrors,
locale: options.locale,
offline: options.offline,
recordHar: options.recordHar,
recordVideo: options.recordVideo,
timezoneId: options.timezoneId,
},
browserProcess,
protocolLogger: helper.debugProtocolLogger(),
browserLogsCollector,

View File

@ -107,3 +107,22 @@ test('should return browser window', async ({ playwright }) => {
expect(await bwHandle.evaluate((bw: BrowserWindow) => bw.title)).toBe('Electron');
await electronApp.close();
});
test('should bypass csp', async ({playwright, server}) => {
const app = await playwright._electron.launch({
args: [require('path').join(__dirname, 'electron-app.js')],
bypassCSP: true,
});
await app.evaluate(electron => {
const window = new electron.BrowserWindow({
width: 800,
height: 600,
});
window.loadURL('about:blank');
});
const page = await app.firstWindow();
await page.goto(server.PREFIX + '/csp.html');
await page.addScriptTag({content: 'window["__injected"] = 42;'});
expect(await page.evaluate('window["__injected"]')).toBe(42);
await app.close();
});

118
types/types.d.ts vendored
View File

@ -9617,11 +9617,28 @@ export interface Electron {
* @param options
*/
launch(options?: {
/**
* Whether to automatically download all the attachments. Defaults to `false` where all the downloads are canceled.
*/
acceptDownloads?: boolean;
/**
* Additional arguments to pass to the application when launching. You typically pass the main script name here.
*/
args?: Array<string>;
/**
* Toggles bypassing page's Content-Security-Policy.
*/
bypassCSP?: boolean;
/**
* Emulates `'prefers-colors-scheme'` media feature, supported values are `'light'`, `'dark'`, `'no-preference'`. See
* [page.emulateMedia([options])](https://playwright.dev/docs/api/class-page#pageemulatemediaoptions) for more details.
* Defaults to `'light'`.
*/
colorScheme?: "light"|"dark"|"no-preference";
/**
* Current working directory to launch application from.
*/
@ -9637,6 +9654,107 @@ export interface Electron {
* package, located at `node_modules/.bin/electron`.
*/
executablePath?: string;
/**
* An object containing additional HTTP headers to be sent with every request. All header values must be strings.
*/
extraHTTPHeaders?: { [key: string]: string; };
geolocation?: {
/**
* Latitude between -90 and 90.
*/
latitude: number;
/**
* Longitude between -180 and 180.
*/
longitude: number;
/**
* Non-negative accuracy value. Defaults to `0`.
*/
accuracy?: number;
};
/**
* Credentials for [HTTP authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication).
*/
httpCredentials?: {
username: string;
password: string;
};
/**
* Whether to ignore HTTPS errors during navigation. Defaults to `false`.
*/
ignoreHTTPSErrors?: boolean;
/**
* Specify user locale, for example `en-GB`, `de-DE`, etc. Locale will affect `navigator.language` value, `Accept-Language`
* request header value as well as number and date formatting rules.
*/
locale?: string;
/**
* Whether to emulate network being offline. Defaults to `false`.
*/
offline?: boolean;
/**
* Enables [HAR](http://www.softwareishard.com/blog/har-12-spec) recording for all pages into `recordHar.path` file. If not
* specified, the HAR is not recorded. Make sure to await
* [browserContext.close()](https://playwright.dev/docs/api/class-browsercontext#browsercontextclose) for the HAR to be
* saved.
*/
recordHar?: {
/**
* Optional setting to control whether to omit request content from the HAR. Defaults to `false`.
*/
omitContent?: boolean;
/**
* Path on the filesystem to write the HAR file to.
*/
path: string;
};
/**
* Enables video recording for all pages into `recordVideo.dir` directory. If not specified videos are not recorded. Make
* sure to await [browserContext.close()](https://playwright.dev/docs/api/class-browsercontext#browsercontextclose) for
* videos to be saved.
*/
recordVideo?: {
/**
* Path to the directory to put videos into.
*/
dir: string;
/**
* Optional dimensions of the recorded videos. If not specified the size will be equal to `viewport` scaled down to fit
* into 800x800. If `viewport` is not configured explicitly the video size defaults to 800x450. Actual picture of each page
* will be scaled down if necessary to fit the specified size.
*/
size?: {
/**
* Video frame width.
*/
width: number;
/**
* Video frame height.
*/
height: number;
};
};
/**
* Changes the timezone of the context. See
* [ICU's 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;
}): Promise<ElectronApplication>;
}