feat(rpc): support electron (#2933)

This commit is contained in:
Dmitry Gozman 2020-07-13 21:46:59 -07:00 committed by GitHub
parent 9fdb3e23c3
commit cc8fe5a763
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 253 additions and 19 deletions

View File

@ -29,6 +29,7 @@ export type PlaywrightInitializer = {
chromium: BrowserTypeChannel, chromium: BrowserTypeChannel,
firefox: BrowserTypeChannel, firefox: BrowserTypeChannel,
webkit: BrowserTypeChannel, webkit: BrowserTypeChannel,
electron?: ElectronChannel,
deviceDescriptors: types.Devices, deviceDescriptors: types.Devices,
selectors: SelectorsChannel, selectors: SelectorsChannel,
}; };
@ -384,4 +385,33 @@ export type PDFOptions = {
preferCSSPageSize?: boolean, preferCSSPageSize?: boolean,
margin?: {top?: string, bottom?: string, left?: string, right?: string}, margin?: {top?: string, bottom?: string, left?: string, right?: string},
path?: string, path?: string,
};
export type ElectronLaunchOptions = {
args?: string[],
cwd?: string,
env?: {[key: string]: string|number|boolean},
handleSIGINT?: boolean,
handleSIGTERM?: boolean,
handleSIGHUP?: boolean,
timeout?: number,
};
export interface ElectronChannel extends Channel {
launch(params: { executablePath: string } & ElectronLaunchOptions): Promise<ElectronApplicationChannel>;
} }
export type ElectronInitializer = {};
export interface ElectronApplicationChannel extends Channel {
on(event: 'close', callback: () => void): this;
on(event: 'window', callback: (params: PageChannel) => void): this;
newBrowserWindow(params: { arg: any }): Promise<PageChannel>;
evaluateExpression(params: { expression: string, isFunction: boolean, arg: any }): Promise<any>;
evaluateExpressionHandle(params: { expression: string, isFunction: boolean, arg: any }): Promise<JSHandleChannel>;
close(): Promise<void>;
}
export type ElectronApplicationInitializer = {
context: BrowserContextChannel,
};

View File

@ -25,7 +25,6 @@ import { helper } from '../../helper';
import { Browser } from './browser'; import { Browser } from './browser';
import { Events } from '../../events'; import { Events } from '../../events';
import { TimeoutSettings } from '../../timeoutSettings'; import { TimeoutSettings } from '../../timeoutSettings';
import { BrowserType } from './browserType';
import { Waiter } from './waiter'; import { Waiter } from './waiter';
import { TimeoutError } from '../../errors'; import { TimeoutError } from '../../errors';
@ -33,7 +32,7 @@ export class BrowserContext extends ChannelOwner<BrowserContextChannel, BrowserC
_pages = new Set<Page>(); _pages = new Set<Page>();
private _routes: { url: types.URLMatch, handler: network.RouteHandler }[] = []; private _routes: { url: types.URLMatch, handler: network.RouteHandler }[] = [];
readonly _browser: Browser | undefined; readonly _browser: Browser | undefined;
readonly _browserType: BrowserType; readonly _browserName: string;
readonly _bindings = new Map<string, frames.FunctionWithSource>(); readonly _bindings = new Map<string, frames.FunctionWithSource>();
_timeoutSettings = new TimeoutSettings(); _timeoutSettings = new TimeoutSettings();
_ownerPage: Page | undefined; _ownerPage: Page | undefined;
@ -48,15 +47,11 @@ export class BrowserContext extends ChannelOwner<BrowserContextChannel, BrowserC
return context ? BrowserContext.from(context) : null; return context ? BrowserContext.from(context) : null;
} }
constructor(parent: ChannelOwner, type: string, guid: string, initializer: BrowserContextInitializer) { constructor(parent: ChannelOwner, type: string, guid: string, initializer: BrowserContextInitializer, browserName: string) {
super(parent, type, guid, initializer, true); super(parent, type, guid, initializer, true);
if (parent instanceof Browser) { if (parent instanceof Browser)
this._browser = parent; this._browser = parent;
this._browserType = this._browser._browserType; this._browserName = browserName;
} else {
// Launching persistent context does not produce a browser at all.
this._browserType = parent as BrowserType;
}
this._channel.on('bindingCall', bindingCall => this._onBinding(BindingCall.from(bindingCall))); this._channel.on('bindingCall', bindingCall => this._onBinding(BindingCall.from(bindingCall)));
this._channel.on('close', () => this._onClose()); this._channel.on('close', () => this._onClose());

View File

@ -28,7 +28,7 @@ export class ChromiumBrowserContext extends BrowserContext {
_serviceWorkers = new Set<Worker>(); _serviceWorkers = new Set<Worker>();
constructor(parent: ChannelOwner, type: string, guid: string, initializer: BrowserContextInitializer) { constructor(parent: ChannelOwner, type: string, guid: string, initializer: BrowserContextInitializer) {
super(parent, type, guid, initializer); super(parent, type, guid, initializer, 'chromium');
this._channel.on('crBackgroundPage', pageChannel => { this._channel.on('crBackgroundPage', pageChannel => {
const page = Page.from(pageChannel); const page = Page.from(pageChannel);
this._backgroundPages.add(page); this._backgroundPages.add(page);

View File

@ -32,6 +32,7 @@ import { parseError } from '../serializers';
import { BrowserServer } from './browserServer'; import { BrowserServer } from './browserServer';
import { CDPSession } from './cdpSession'; import { CDPSession } from './cdpSession';
import { Playwright } from './playwright'; import { Playwright } from './playwright';
import { Electron, ElectronApplication } from './electron';
import { Channel } from '../channels'; import { Channel } from '../channels';
import { ChromiumBrowser } from './chromiumBrowser'; import { ChromiumBrowser } from './chromiumBrowser';
import { ChromiumBrowserContext } from './chromiumBrowserContext'; import { ChromiumBrowserContext } from './chromiumBrowserContext';
@ -151,12 +152,21 @@ export class Connection {
result = new CDPSession(parent, type, guid, initializer); result = new CDPSession(parent, type, guid, initializer);
break; break;
case 'context': case 'context':
// Launching persistent context produces BrowserType parent directly for BrowserContext. let browserName = '';
const browserType = parent instanceof Browser ? parent._browserType : parent as BrowserType; if (parent instanceof Electron) {
if (browserType.name() === 'chromium') // Launching electron produces Electron parent for BrowserContext.
browserName = 'electron';
} else if (parent instanceof Browser) {
// Launching a browser produces Browser parent for BrowserContext.
browserName = parent._browserType.name();
} else {
// Launching persistent context produces BrowserType parent for BrowserContext.
browserName = (parent as BrowserType).name();
}
if (browserName === 'chromium')
result = new ChromiumBrowserContext(parent, type, guid, initializer); result = new ChromiumBrowserContext(parent, type, guid, initializer);
else else
result = new BrowserContext(parent, type, guid, initializer); result = new BrowserContext(parent, type, guid, initializer, browserName);
break; break;
case 'consoleMessage': case 'consoleMessage':
result = new ConsoleMessage(parent, type, guid, initializer); result = new ConsoleMessage(parent, type, guid, initializer);
@ -167,6 +177,12 @@ export class Connection {
case 'download': case 'download':
result = new Download(parent, type, guid, initializer); result = new Download(parent, type, guid, initializer);
break; break;
case 'electron':
result = new Electron(parent, type, guid, initializer);
break;
case 'electronApplication':
result = new ElectronApplication(parent, type, guid, initializer);
break;
case 'elementHandle': case 'elementHandle':
result = new ElementHandle(parent, type, guid, initializer); result = new ElementHandle(parent, type, guid, initializer);
break; break;

112
src/rpc/client/electron.ts Normal file
View File

@ -0,0 +1,112 @@
/**
* 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 * as types from '../../types';
import { ElectronChannel, ElectronInitializer, ElectronLaunchOptions, ElectronApplicationChannel, ElectronApplicationInitializer } from '../channels';
import { BrowserContext } from './browserContext';
import { ChannelOwner } from './channelOwner';
import { Page } from './page';
import { serializeArgument, FuncOn, parseResult, SmartHandle, JSHandle } from './jsHandle';
import { ElectronEvents } from '../../server/electron';
import { TimeoutSettings } from '../../timeoutSettings';
import { Waiter } from './waiter';
import { TimeoutError } from '../../errors';
export class Electron extends ChannelOwner<ElectronChannel, ElectronInitializer> {
static from(electron: ElectronChannel): Electron {
return (electron as any)._object;
}
constructor(parent: ChannelOwner, type: string, guid: string, initializer: ElectronInitializer) {
super(parent, type, guid, initializer, true);
}
async launch(executablePath: string, options: ElectronLaunchOptions = {}): Promise<ElectronApplication> {
options = { ...options };
delete (options as any).logger;
return ElectronApplication.from(await this._channel.launch({ executablePath, ...options }));
}
}
export class ElectronApplication extends ChannelOwner<ElectronApplicationChannel, ElectronApplicationInitializer> {
private _context: BrowserContext;
private _windows = new Set<Page>();
private _timeoutSettings = new TimeoutSettings();
static from(electronApplication: ElectronApplicationChannel): ElectronApplication {
return (electronApplication as any)._object;
}
constructor(parent: ChannelOwner, type: string, guid: string, initializer: ElectronApplicationInitializer) {
super(parent, type, guid, initializer);
this._context = BrowserContext.from(initializer.context);
this._channel.on('window', pageChannel => {
const page = Page.from(pageChannel);
this._windows.add(page);
this.emit(ElectronEvents.ElectronApplication.Window, page);
});
this._channel.on('close', () => {
this.emit(ElectronEvents.ElectronApplication.Close);
});
}
windows(): Page[] {
return [...this._windows];
}
async firstWindow(): Promise<Page> {
if (this._windows.size)
return this._windows.values().next().value;
return this.waitForEvent('window');
}
async newBrowserWindow(options: any): Promise<Page> {
return Page.from(await this._channel.newBrowserWindow({ arg: serializeArgument(options) }));
}
context(): BrowserContext {
return this._context;
}
async close() {
await this._channel.close();
}
async waitForEvent(event: string, optionsOrPredicate: types.WaitForEventOptions = {}): Promise<any> {
const timeout = this._timeoutSettings.timeout(optionsOrPredicate instanceof Function ? {} : optionsOrPredicate);
const predicate = optionsOrPredicate instanceof Function ? optionsOrPredicate : optionsOrPredicate.predicate;
const waiter = new Waiter();
waiter.rejectOnTimeout(timeout, new TimeoutError(`Timeout while waiting for event "${event}"`));
if (event !== ElectronEvents.ElectronApplication.Close)
waiter.rejectOnEvent(this, ElectronEvents.ElectronApplication.Close, new Error('Electron application closed'));
const result = await waiter.waitForEvent(this, event, predicate as any);
waiter.dispose();
return result;
}
async evaluate<R, Arg>(pageFunction: FuncOn<any, Arg, R>, arg: Arg): Promise<R>;
async evaluate<R>(pageFunction: FuncOn<any, void, R>, arg?: any): Promise<R>;
async evaluate<R, Arg>(pageFunction: FuncOn<any, Arg, R>, arg: Arg): Promise<R> {
return parseResult(await this._channel.evaluateExpression({ expression: String(pageFunction), isFunction: typeof pageFunction === 'function', arg: serializeArgument(arg) }));
}
async evaluateHandle<R, Arg>(pageFunction: FuncOn<any, Arg, R>, arg: Arg): Promise<SmartHandle<R>>;
async evaluateHandle<R>(pageFunction: FuncOn<any, void, R>, arg?: any): Promise<SmartHandle<R>>;
async evaluateHandle<R, Arg>(pageFunction: FuncOn<any, Arg, R>, arg: Arg): Promise<SmartHandle<R>> {
const handleChannel = await this._channel.evaluateExpressionHandle({ expression: String(pageFunction), isFunction: typeof pageFunction === 'function', arg: serializeArgument(arg) });
return JSHandle.from(handleChannel) as SmartHandle<R>;
}
}

View File

@ -104,7 +104,7 @@ export class Page extends ChannelOwner<PageChannel, PageInitializer> {
this._channel.on('route', ({ route, request }) => this._onRoute(Route.from(route), Request.from(request))); this._channel.on('route', ({ route, request }) => this._onRoute(Route.from(route), Request.from(request)));
this._channel.on('worker', worker => this._onWorker(Worker.from(worker))); this._channel.on('worker', worker => this._onWorker(Worker.from(worker)));
if (this._browserContext._browserType.name() === 'chromium') { if (this._browserContext._browserName === 'chromium') {
this.coverage = new Coverage(this._channel); this.coverage = new Coverage(this._channel);
this.pdf = options => this._pdf(options); this.pdf = options => this._pdf(options);
} }

View File

@ -19,6 +19,7 @@ import * as types from '../../types';
import { BrowserType } from './browserType'; import { BrowserType } from './browserType';
import { ChannelOwner } from './channelOwner'; import { ChannelOwner } from './channelOwner';
import { Selectors } from './selectors'; import { Selectors } from './selectors';
import { Electron } from './electron';
export class Playwright extends ChannelOwner<PlaywrightChannel, PlaywrightInitializer> { export class Playwright extends ChannelOwner<PlaywrightChannel, PlaywrightInitializer> {
chromium: BrowserType; chromium: BrowserType;
@ -32,6 +33,8 @@ export class Playwright extends ChannelOwner<PlaywrightChannel, PlaywrightInitia
this.chromium = BrowserType.from(initializer.chromium); this.chromium = BrowserType.from(initializer.chromium);
this.firefox = BrowserType.from(initializer.firefox); this.firefox = BrowserType.from(initializer.firefox);
this.webkit = BrowserType.from(initializer.webkit); this.webkit = BrowserType.from(initializer.webkit);
if (initializer.electron)
(this as any).electron = Electron.from(initializer.electron);
this.devices = initializer.deviceDescriptors; this.devices = initializer.deviceDescriptors;
this.selectors = Selectors.from(initializer.selectors); this.selectors = Selectors.from(initializer.selectors);
} }

View File

@ -18,6 +18,7 @@ import { Transport } from './transport';
import { DispatcherConnection } from './server/dispatcher'; import { DispatcherConnection } from './server/dispatcher';
import { Playwright } from '../server/playwright'; import { Playwright } from '../server/playwright';
import { PlaywrightDispatcher } from './server/playwrightDispatcher'; import { PlaywrightDispatcher } from './server/playwrightDispatcher';
import { Electron } from '../server/electron';
const dispatcherConnection = new DispatcherConnection(); const dispatcherConnection = new DispatcherConnection();
const transport = new Transport(process.stdout, process.stdin); const transport = new Transport(process.stdout, process.stdin);
@ -26,4 +27,5 @@ transport.onmessage = message => dispatcherConnection.dispatch(JSON.parse(messag
dispatcherConnection.onmessage = message => transport.send(JSON.stringify(message)); dispatcherConnection.onmessage = message => transport.send(JSON.stringify(message));
const playwright = new Playwright(__dirname, require('../../browsers.json')['browsers']); const playwright = new Playwright(__dirname, require('../../browsers.json')['browsers']);
(playwright as any).electron = new Electron();
new PlaywrightDispatcher(dispatcherConnection.rootDispatcher(), playwright); new PlaywrightDispatcher(dispatcherConnection.rootDispatcher(), playwright);

View File

@ -0,0 +1,66 @@
/**
* 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 { Dispatcher, DispatcherScope, lookupDispatcher } from './dispatcher';
import { Electron, ElectronApplication, ElectronEvents } from '../../server/electron';
import { ElectronApplicationChannel, ElectronApplicationInitializer, PageChannel, JSHandleChannel, ElectronInitializer, ElectronChannel, ElectronLaunchOptions } from '../channels';
import { BrowserContextDispatcher } from './browserContextDispatcher';
import { BrowserContextBase } from '../../browserContext';
import { Page } from '../../page';
import { PageDispatcher } from './pageDispatcher';
import { parseArgument } from './jsHandleDispatcher';
import { createHandle } from './elementHandlerDispatcher';
export class ElectronDispatcher extends Dispatcher<Electron, ElectronInitializer> implements ElectronChannel {
constructor(scope: DispatcherScope, electron: Electron) {
super(scope, electron, 'electron', {}, true);
}
async launch(params: { executablePath: string } & ElectronLaunchOptions): Promise<ElectronApplicationChannel> {
const electronApplication = await this._object.launch(params.executablePath, params);
return new ElectronApplicationDispatcher(this._scope, electronApplication);
}
}
export class ElectronApplicationDispatcher extends Dispatcher<ElectronApplication, ElectronApplicationInitializer> implements ElectronApplicationChannel {
constructor(scope: DispatcherScope, electronApplication: ElectronApplication) {
super(scope, electronApplication, 'electronApplication', {
context: new BrowserContextDispatcher(scope, electronApplication.context() as BrowserContextBase),
});
electronApplication.on(ElectronEvents.ElectronApplication.Close, () => this._dispatchEvent('close'));
electronApplication.on(ElectronEvents.ElectronApplication.Window, (page: Page) => this._dispatchEvent('window', lookupDispatcher<PageDispatcher>(page)));
}
async newBrowserWindow(params: { arg: any }): Promise<PageChannel> {
return lookupDispatcher<PageChannel>(await this._object.newBrowserWindow(parseArgument(params.arg)));
}
async evaluateExpression(params: { expression: string, isFunction: boolean, arg: any }): Promise<any> {
const handle = this._object._nodeElectronHandle!;
return handle._evaluateExpression(params.expression, params.isFunction, true /* returnByValue */, parseArgument(params.arg));
}
async evaluateExpressionHandle(params: { expression: string, isFunction: boolean, arg: any}): Promise<JSHandleChannel> {
const handle = this._object._nodeElectronHandle!;
const result = await handle._evaluateExpression(params.expression, params.isFunction, false /* returnByValue */, parseArgument(params.arg));
return createHandle(this._scope, result);
}
async close(): Promise<void> {
await this._object.close();
}
}

View File

@ -19,13 +19,17 @@ import { PlaywrightChannel, PlaywrightInitializer } from '../channels';
import { BrowserTypeDispatcher } from './browserTypeDispatcher'; import { BrowserTypeDispatcher } from './browserTypeDispatcher';
import { Dispatcher, DispatcherScope } from './dispatcher'; import { Dispatcher, DispatcherScope } from './dispatcher';
import { SelectorsDispatcher } from './selectorsDispatcher'; import { SelectorsDispatcher } from './selectorsDispatcher';
import { Electron } from '../../server/electron';
import { ElectronDispatcher } from './electronDispatcher';
export class PlaywrightDispatcher extends Dispatcher<Playwright, PlaywrightInitializer> implements PlaywrightChannel { export class PlaywrightDispatcher extends Dispatcher<Playwright, PlaywrightInitializer> implements PlaywrightChannel {
constructor(scope: DispatcherScope, playwright: Playwright) { constructor(scope: DispatcherScope, playwright: Playwright) {
const electron = (playwright as any).electron as (Electron | undefined);
super(scope, playwright, 'playwright', { super(scope, playwright, 'playwright', {
chromium: new BrowserTypeDispatcher(scope, playwright.chromium!), chromium: new BrowserTypeDispatcher(scope, playwright.chromium!),
firefox: new BrowserTypeDispatcher(scope, playwright.firefox!), firefox: new BrowserTypeDispatcher(scope, playwright.firefox!),
webkit: new BrowserTypeDispatcher(scope, playwright.webkit!), webkit: new BrowserTypeDispatcher(scope, playwright.webkit!),
electron: electron ? new ElectronDispatcher(scope, electron) : undefined,
deviceDescriptors: playwright.devices, deviceDescriptors: playwright.devices,
selectors: new SelectorsDispatcher(scope, playwright.selectors), selectors: new SelectorsDispatcher(scope, playwright.selectors),
}, false, 'playwright'); }, false, 'playwright');

View File

@ -63,7 +63,7 @@ export class ElectronApplication extends EventEmitter {
private _nodeConnection: CRConnection; private _nodeConnection: CRConnection;
private _nodeSession: CRSession; private _nodeSession: CRSession;
private _nodeExecutionContext: js.ExecutionContext | undefined; private _nodeExecutionContext: js.ExecutionContext | undefined;
private _nodeElectronHandle: js.JSHandle<any> | undefined; _nodeElectronHandle: js.JSHandle<any> | undefined;
private _windows = new Set<ElectronPage>(); private _windows = new Set<ElectronPage>();
private _lastWindowId = 0; private _lastWindowId = 0;
readonly _timeoutSettings = new TimeoutSettings(); readonly _timeoutSettings = new TimeoutSettings();

View File

@ -33,6 +33,7 @@ describe.skip(!CHANNEL)('Channels', function() {
{ _guid: 'browserType', objects: [] }, { _guid: 'browserType', objects: [] },
{ _guid: 'playwright' }, { _guid: 'playwright' },
{ _guid: 'selectors' }, { _guid: 'selectors' },
{ _guid: 'electron', objects: [] },
] ]
}; };
await expectScopeState(browser, GOLDEN_PRECONDITION); await expectScopeState(browser, GOLDEN_PRECONDITION);
@ -57,6 +58,7 @@ describe.skip(!CHANNEL)('Channels', function() {
] }, ] },
{ _guid: 'playwright' }, { _guid: 'playwright' },
{ _guid: 'selectors' }, { _guid: 'selectors' },
{ _guid: 'electron', objects: [] },
] ]
}); });
@ -75,6 +77,7 @@ describe.skip(!CHANNEL)('Channels', function() {
{ _guid: 'browserType', objects: [] }, { _guid: 'browserType', objects: [] },
{ _guid: 'playwright' }, { _guid: 'playwright' },
{ _guid: 'selectors' }, { _guid: 'selectors' },
{ _guid: 'electron', objects: [] },
] ]
}; };
await expectScopeState(browserType, GOLDEN_PRECONDITION); await expectScopeState(browserType, GOLDEN_PRECONDITION);
@ -92,6 +95,7 @@ describe.skip(!CHANNEL)('Channels', function() {
{ _guid: 'browserType', objects: [] }, { _guid: 'browserType', objects: [] },
{ _guid: 'playwright' }, { _guid: 'playwright' },
{ _guid: 'selectors' }, { _guid: 'selectors' },
{ _guid: 'electron', objects: [] },
] ]
}); });
@ -110,6 +114,7 @@ describe.skip(!CHANNEL)('Channels', function() {
{ _guid: 'browserType', objects: [] }, { _guid: 'browserType', objects: [] },
{ _guid: 'playwright' }, { _guid: 'playwright' },
{ _guid: 'selectors' }, { _guid: 'selectors' },
{ _guid: 'electron', objects: [] },
] ]
}; };
await expectScopeState(browserType, GOLDEN_PRECONDITION); await expectScopeState(browserType, GOLDEN_PRECONDITION);
@ -129,6 +134,7 @@ describe.skip(!CHANNEL)('Channels', function() {
{ _guid: 'browserType', objects: [] }, { _guid: 'browserType', objects: [] },
{ _guid: 'playwright' }, { _guid: 'playwright' },
{ _guid: 'selectors' }, { _guid: 'selectors' },
{ _guid: 'electron', objects: [] },
] ]
}); });

View File

@ -21,7 +21,7 @@ const {CHANNEL} = utils.testOptions(browserType);
const electronName = process.platform === 'win32' ? 'electron.cmd' : 'electron'; const electronName = process.platform === 'win32' ? 'electron.cmd' : 'electron';
describe.skip(CHANNEL)('Electron', function() { describe('Electron', function() {
beforeEach(async (state, testRun) => { beforeEach(async (state, testRun) => {
const electronPath = path.join(__dirname, '..', '..', 'node_modules', '.bin', electronName); const electronPath = path.join(__dirname, '..', '..', 'node_modules', '.bin', electronName);
state.logger = utils.createTestLogger(config.dumpLogOnFailure, testRun); state.logger = utils.createTestLogger(config.dumpLogOnFailure, testRun);
@ -55,7 +55,7 @@ describe.skip(CHANNEL)('Electron', function() {
await page.goto('data:text/html,<title>Hello World 2</title>'); await page.goto('data:text/html,<title>Hello World 2</title>');
expect(await page.title()).toBe('Hello World 2'); expect(await page.title()).toBe('Hello World 2');
}); });
it('should create multiple windows', async ({ application }) => { it.skip(CHANNEL)('should create multiple windows', async ({ application }) => {
const createPage = async ordinal => { const createPage = async ordinal => {
const page = await application.newBrowserWindow({ width: 800, height: 600 }); const page = await application.newBrowserWindow({ width: 800, height: 600 });
await Promise.all([ await Promise.all([
@ -117,7 +117,7 @@ describe.skip(CHANNEL)('Electron', function() {
}); });
}); });
describe.skip(CHANNEL)('Electron per window', function() { describe('Electron per window', function() {
beforeAll(async state => { beforeAll(async state => {
const electronPath = path.join(__dirname, '..', '..', 'node_modules', '.bin', electronName); const electronPath = path.join(__dirname, '..', '..', 'node_modules', '.bin', electronName);
state.application = await state.playwright.electron.launch(electronPath, { state.application = await state.playwright.electron.launch(electronPath, {