diff --git a/src/rpc/channels.ts b/src/rpc/channels.ts index 67a7d6b49d..2bf97775ba 100644 --- a/src/rpc/channels.ts +++ b/src/rpc/channels.ts @@ -23,6 +23,16 @@ export interface Channel extends EventEmitter { } +export interface PlaywrightChannel extends Channel { +} +export type PlaywrightInitializer = { + chromium: BrowserTypeChannel, + firefox: BrowserTypeChannel, + webkit: BrowserTypeChannel, + deviceDescriptors: types.Devices, +}; + + export interface BrowserTypeChannel extends Channel { connect(params: types.ConnectOptions): Promise; launch(params: types.LaunchOptions): Promise; diff --git a/src/rpc/client.ts b/src/rpc/client.ts index 77693effa6..6c5ecc7b73 100644 --- a/src/rpc/client.ts +++ b/src/rpc/client.ts @@ -26,8 +26,8 @@ import { Transport } from './transport'; connection.onmessage = message => transport.send(message); transport.onmessage = message => connection.dispatch(message); - const chromium = await connection.waitForObjectWithKnownName('chromium'); - const browser = await chromium.launch({ headless: false }); + const playwright = await connection.waitForObjectWithKnownName('playwright'); + const browser = await playwright.chromium.launch({ headless: false }); const page = await browser.newPage(); await page.goto('https://example.com'); })(); diff --git a/src/rpc/client/browserType.ts b/src/rpc/client/browserType.ts index 4b38bf0a57..3405e86130 100644 --- a/src/rpc/client/browserType.ts +++ b/src/rpc/client/browserType.ts @@ -23,6 +23,11 @@ import { ConnectionScope } from './connection'; import { BrowserServer } from './browserServer'; export class BrowserType extends ChannelOwner { + + static from(browserTyep: BrowserTypeChannel): BrowserType { + return (browserTyep as any)._object; + } + constructor(scope: ConnectionScope, guid: string, initializer: BrowserTypeInitializer) { super(scope, guid, initializer); } diff --git a/src/rpc/client/connection.ts b/src/rpc/client/connection.ts index 77c15651d9..2cd0e08bff 100644 --- a/src/rpc/client/connection.ts +++ b/src/rpc/client/connection.ts @@ -31,6 +31,7 @@ import { Download } from './download'; import { parseError } from '../serializers'; import { BrowserServer } from './browserServer'; import { CDPSession } from './cdpSession'; +import { Playwright } from './playwright'; export class Connection { readonly _objects = new Map>(); @@ -221,6 +222,9 @@ export class ConnectionScope { case 'page': result = new Page(this, guid, initializer); break; + case 'playwright': + result = new Playwright(this, guid, initializer); + break; case 'request': result = new Request(this, guid, initializer); break; diff --git a/src/rpc/client/playwright.ts b/src/rpc/client/playwright.ts new file mode 100644 index 0000000000..1ecf91d152 --- /dev/null +++ b/src/rpc/client/playwright.ts @@ -0,0 +1,36 @@ +/** + * 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 { PlaywrightChannel, PlaywrightInitializer } from '../channels'; +import * as types from '../../types'; +import { BrowserType } from './browserType'; +import { ChannelOwner } from './channelOwner'; +import { ConnectionScope } from './connection'; + +export class Playwright extends ChannelOwner { + chromium: BrowserType; + firefox: BrowserType; + webkit: BrowserType; + devices: types.Devices; + + constructor(scope: ConnectionScope, guid: string, initializer: PlaywrightInitializer) { + super(scope, guid, initializer); + this.chromium = BrowserType.from(initializer.chromium); + this.firefox = BrowserType.from(initializer.firefox); + this.webkit = BrowserType.from(initializer.webkit); + this.devices = initializer.deviceDescriptors; + } +} diff --git a/src/rpc/server.ts b/src/rpc/server.ts index b5deb7f1c1..05c67006a9 100644 --- a/src/rpc/server.ts +++ b/src/rpc/server.ts @@ -17,7 +17,7 @@ import { Transport } from './transport'; import { DispatcherConnection } from './server/dispatcher'; import { Playwright } from '../server/playwright'; -import { BrowserTypeDispatcher } from './server/browserTypeDispatcher'; +import { PlaywrightDispatcher } from './server/playwrightDispatcher'; const dispatcherConnection = new DispatcherConnection(); const transport = new Transport(process.stdout, process.stdin); @@ -25,6 +25,4 @@ transport.onmessage = message => dispatcherConnection.dispatch(message); dispatcherConnection.onmessage = message => transport.send(message); const playwright = new Playwright(__dirname, require('../../browsers.json')['browsers']); -new BrowserTypeDispatcher(dispatcherConnection.rootScope(), playwright.chromium!); -new BrowserTypeDispatcher(dispatcherConnection.rootScope(), playwright.firefox!); -new BrowserTypeDispatcher(dispatcherConnection.rootScope(), playwright.webkit!); +new PlaywrightDispatcher(dispatcherConnection.rootScope(), playwright); diff --git a/src/rpc/server/playwrightDispatcher.ts b/src/rpc/server/playwrightDispatcher.ts new file mode 100644 index 0000000000..94791ca64c --- /dev/null +++ b/src/rpc/server/playwrightDispatcher.ts @@ -0,0 +1,31 @@ +/** + * 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 { Playwright } from '../../server/playwright'; +import { PlaywrightChannel, PlaywrightInitializer } from '../channels'; +import { BrowserTypeDispatcher } from './browserTypeDispatcher'; +import { Dispatcher, DispatcherScope } from './dispatcher'; + +export class PlaywrightDispatcher extends Dispatcher implements PlaywrightChannel { + constructor(scope: DispatcherScope, playwright: Playwright) { + super(scope, playwright, 'playwright', { + chromium: new BrowserTypeDispatcher(scope, playwright.chromium!), + firefox: new BrowserTypeDispatcher(scope, playwright.firefox!), + webkit: new BrowserTypeDispatcher(scope, playwright.webkit!), + deviceDescriptors: playwright.devices + }, false, 'playwright'); + } +} diff --git a/test/channels.spec.js b/test/channels.spec.js index ed96cedddc..25a284cd36 100644 --- a/test/channels.spec.js +++ b/test/channels.spec.js @@ -24,9 +24,9 @@ describe.skip(!CHANNEL)('Channels', function() { it('should scope context handles', async({browser, server}) => { const GOLDEN_PRECONDITION = { - objects: [ 'chromium', 'browser' ], + objects: [ 'chromium', 'firefox', 'webkit', 'playwright', 'browser' ], scopes: [ - { _guid: '', objects: [ 'chromium', 'browser' ] }, + { _guid: '', objects: [ 'chromium', 'firefox', 'webkit', 'playwright', 'browser' ] }, { _guid: 'browser', objects: [] } ] }; @@ -36,9 +36,9 @@ describe.skip(!CHANNEL)('Channels', function() { const page = await context.newPage(); await page.goto(server.EMPTY_PAGE); await expectScopeState(browser, { - objects: [ 'chromium', 'browser', 'context', 'frame', 'page', 'request', 'response' ], + objects: [ 'chromium', 'firefox', 'webkit', 'playwright', 'browser', 'context', 'frame', 'page', 'request', 'response' ], scopes: [ - { _guid: '', objects: [ 'chromium', 'browser' ] }, + { _guid: '', objects: [ 'chromium', 'firefox', 'webkit', 'playwright', 'browser' ] }, { _guid: 'browser', objects: ['context'] }, { _guid: 'context', objects: ['frame', 'page', 'request', 'response'] } ] @@ -50,9 +50,9 @@ describe.skip(!CHANNEL)('Channels', function() { it('should scope CDPSession handles', async({browserType, browser, server}) => { const GOLDEN_PRECONDITION = { - objects: [ 'chromium', 'browser' ], + objects: [ 'chromium', 'firefox', 'webkit', 'playwright', 'browser' ], scopes: [ - { _guid: '', objects: [ 'chromium', 'browser' ] }, + { _guid: '', objects: [ 'chromium', 'firefox', 'webkit', 'playwright', 'browser' ] }, { _guid: 'browser', objects: [] } ] }; @@ -60,9 +60,9 @@ describe.skip(!CHANNEL)('Channels', function() { const session = await browser.newBrowserCDPSession(); await expectScopeState(browserType, { - objects: [ 'chromium', 'browser', 'cdpSession' ], + objects: [ 'chromium', 'firefox', 'webkit', 'playwright', 'browser', 'cdpSession' ], scopes: [ - { _guid: '', objects: [ 'chromium', 'browser' ] }, + { _guid: '', objects: [ 'chromium', 'firefox', 'webkit', 'playwright', 'browser' ] }, { _guid: 'browser', objects: ['cdpSession'] }, { _guid: 'cdpSession', objects: [] }, ] @@ -74,9 +74,9 @@ describe.skip(!CHANNEL)('Channels', function() { it('should scope browser handles', async({browserType, defaultBrowserOptions}) => { const GOLDEN_PRECONDITION = { - objects: [ 'chromium', 'browser' ], + objects: [ 'chromium', 'firefox', 'webkit', 'playwright', 'browser' ], scopes: [ - { _guid: '', objects: [ 'chromium', 'browser' ] }, + { _guid: '', objects: [ 'chromium', 'firefox', 'webkit', 'playwright', 'browser' ] }, { _guid: 'browser', objects: [] } ] }; @@ -85,9 +85,9 @@ describe.skip(!CHANNEL)('Channels', function() { const browser = await browserType.launch(defaultBrowserOptions); await browser.newContext(); await expectScopeState(browserType, { - objects: [ 'chromium', 'browser', 'browser', 'context' ], + objects: [ 'chromium', 'firefox', 'webkit', 'playwright', 'browser', 'browser', 'context' ], scopes: [ - { _guid: '', objects: [ 'chromium', 'browser', 'browser' ] }, + { _guid: '', objects: [ 'chromium', 'firefox', 'webkit', 'playwright', 'browser', 'browser' ] }, { _guid: 'browser', objects: [] }, { _guid: 'browser', objects: ['context'] }, { _guid: 'context', objects: [] }, diff --git a/test/environments.js b/test/environments.js index 2838a967f2..a2cbbd610c 100644 --- a/test/environments.js +++ b/test/environments.js @@ -22,7 +22,7 @@ const rm = require('rimraf').sync; const {TestServer} = require('../utils/testserver/'); const { DispatcherConnection } = require('../lib/rpc/server/dispatcher'); const { Connection } = require('../lib/rpc/client/connection'); -const { BrowserTypeDispatcher } = require('../lib/rpc/server/browserTypeDispatcher'); +const { PlaywrightDispatcher } = require('../lib/rpc/server/playwrightDispatcher'); class ServerEnvironment { async beforeAll(state) { @@ -159,18 +159,10 @@ class PlaywrightEnvironment { } name() { return 'Playwright'; }; - beforeAll(state) { state.playwright = this._playwright; } - afterAll(state) { delete state.playwright; } -} - -class BrowserTypeEnvironment { - constructor(browserType) { - this._browserType = browserType; - } async beforeAll(state) { // Channel substitute - let overridenBrowserType = this._browserType; + this.overriddenPlaywright = this._playwright; if (process.env.PWCHANNEL) { const dispatcherConnection = new DispatcherConnection(); const connection = new Connection(); @@ -182,10 +174,24 @@ class BrowserTypeEnvironment { await new Promise(f => setImmediate(f)); return result; }; - new BrowserTypeDispatcher(dispatcherConnection.rootScope(), this._browserType); - overridenBrowserType = await connection.waitForObjectWithKnownName(this._browserType.name()); + new PlaywrightDispatcher(dispatcherConnection.rootScope(), this._playwright); + this.overriddenPlaywright = await connection.waitForObjectWithKnownName('playwright'); } - state.browserType = overridenBrowserType; + state.playwright = this.overriddenPlaywright; + } + + async afterAll(state) { + delete state.playwright; + } +} + +class BrowserTypeEnvironment { + constructor(browserName) { + this._browserName = browserName; + } + + async beforeAll(state) { + state.browserType = state.playwright[this._browserName]; } async afterAll(state) { diff --git a/test/jest-runner.js b/test/jest-runner.js index 6b9f1d7da8..35a5dbcd7f 100644 --- a/test/jest-runner.js +++ b/test/jest-runner.js @@ -158,7 +158,7 @@ function makeTestRunnerInfo() { const browserInfo = browserNames.map(browserName => { const browserType = playwright[browserName]; - const browserTypeEnvironment = new BrowserTypeEnvironment(browserType); + const browserTypeEnvironment = new BrowserTypeEnvironment(browserName); // TODO: maybe launch options per browser? const launchOptions = { diff --git a/test/test.js b/test/test.js index 51510bfd28..f1c0b48ed2 100644 --- a/test/test.js +++ b/test/test.js @@ -82,7 +82,8 @@ function collect(browserNames) { const { setUnderTest } = require(require('path').join(playwrightPath, 'lib/helper.js')); setUnderTest(); - testRunner.collector().useEnvironment(new PlaywrightEnvironment(playwright)); + const playwrightEnvironment = new PlaywrightEnvironment(playwright); + testRunner.collector().useEnvironment(playwrightEnvironment); for (const e of config.globalEnvironments || []) testRunner.collector().useEnvironment(e); @@ -90,7 +91,7 @@ function collect(browserNames) { for (const browserName of browserNames) { const browserType = playwright[browserName]; - const browserTypeEnvironment = new BrowserTypeEnvironment(browserType); + const browserTypeEnvironment = new BrowserTypeEnvironment(browserName); // TODO: maybe launch options per browser? const launchOptions = {