From 643a2a1f2c2bd6a4694793359a9e787d0662a6c9 Mon Sep 17 00:00:00 2001 From: Dmitry Gozman Date: Fri, 10 Jan 2020 13:45:06 -0800 Subject: [PATCH] feat(pw-web): generate playwright/web.js which can be used in the browser --- .gitignore | 2 + .npmignore | 6 +- README.md | 3 +- docs/web.md | 35 ++++++++ index.d.ts | 3 - index.js | 10 --- package.json | 4 +- src/platform.ts | 2 + src/web.ts | 31 +++++++ src/web.webpack.config.js | 56 ++++++++++++ test/assets/playwrightweb.html | 13 +++ test/playwright.spec.js | 2 + test/test.js | 7 +- test/web.spec.js | 84 +++++++++++++++++ utils/browser/README.md | 37 -------- utils/browser/WebSocket.js | 1 - utils/browser/test.js | 99 --------------------- utils/doclint/check_public_api/JSBuilder.js | 13 ++- utils/doclint/check_public_api/index.js | 4 +- utils/doclint/cli.js | 3 +- utils/runWebpack.js | 1 + web.d.ts | 20 +++++ 22 files changed, 273 insertions(+), 163 deletions(-) create mode 100644 docs/web.md create mode 100644 src/web.ts create mode 100644 src/web.webpack.config.js create mode 100644 test/assets/playwrightweb.html create mode 100644 test/web.spec.js delete mode 100644 utils/browser/README.md delete mode 100644 utils/browser/WebSocket.js delete mode 100644 utils/browser/test.js create mode 100644 web.d.ts diff --git a/.gitignore b/.gitignore index 6cd316572fbc5..3d0c2d5b9d54a 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,5 @@ yarn.lock /utils/browser/playwright-web.js lib/ playwright-*.tgz +/web.js +/web.js.map diff --git a/.npmignore b/.npmignore index 2c270eca1c566..037bf682a9b07 100644 --- a/.npmignore +++ b/.npmignore @@ -9,17 +9,21 @@ lib/injected/ #types !lib/**/*.d.ts !index.d.ts +!web.d.ts # Install !install.js # root for "playwright" package !index.js +# root for "playwright/web" +!web.js + # specific browsers !chromium.js !firefox.js !webkit.js -# Dgozman says to remove these +# dgozman says to remove these !DeviceDescriptors.js !Errors.js diff --git a/README.md b/README.md index f1fb4a59ad373..3452a683a157d 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Playwright can be used to create a browser instance, open pages, and then manipu ### Examples -#### Page screenshot +#### Page screenshot This code snippet navigates to example.com in WebKit, and saves a screenshot. @@ -94,3 +94,4 @@ Playwright is actively developed as we get to feature parity across Chromium, Fi ## Resources * [API documentation](https://github.com/microsoft/playwright/blob/master/docs/api.md) +* [Running in the browser](https://github.com/microsoft/playwright/blob/master/docs/web.md) diff --git a/docs/web.md b/docs/web.md new file mode 100644 index 0000000000000..6c74279adf4f8 --- /dev/null +++ b/docs/web.md @@ -0,0 +1,35 @@ +# Bundling for Web + +Playwright contains a version bundled for web browsers under `playwright/web.js`, which +installs playwright under `window.playwrightweb`. +You can use it in the web page to drive another browser instance. + +API consists of a single `connect` function, similar to +[chromiumPlaywright.connect(options)](api.md#chromiumplaywrightconnectoptions), +[firefoxPlaywright.connect(options)](api.md#firefoxplaywrightconnectoptions) and +[webkitPlaywright.connect(options)](api.md#webkitplaywrightconnectoptions). + +```html + + +``` + +See our [playwright-web tests](https://github.com/Microsoft/playwright/blob/master/test/web.spec.js) for example. + +### Running inside Chrome Extension + +You might want to enable `unsafe-eval` inside the extension by adding the following +to your `manifest.json` file: + +``` +"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'" +``` + +Please see discussion in https://github.com/GoogleChrome/puppeteer/issues/3455. diff --git a/index.d.ts b/index.d.ts index 877cc45e28bc5..79897e0a2ae03 100644 --- a/index.d.ts +++ b/index.d.ts @@ -18,6 +18,3 @@ export * from './lib/api'; export function playwright(browser: 'chromium'): import('./lib/api').ChromiumPlaywright; export function playwright(browser: 'firefox'): import('./lib/api').FirefoxPlaywright; export function playwright(browser: 'webkit'): import('./lib/api').WebKitPlaywright; -export function connect(browser: 'chromium'): import('./lib/api').ChromiumBrowser.connect; -export function connect(browser: 'firefox'): import('./lib/api').FirefoxBrowser.connect; -export function connect(browser: 'webkit'): import('./lib/api').WebKitBrowser.connect; diff --git a/index.js b/index.js index 0b6e5b3ae9321..df942e92aa4c7 100644 --- a/index.js +++ b/index.js @@ -33,13 +33,3 @@ module.exports.playwright = browser => { return new api.WebKitPlaywright(__dirname, packageJson.playwright.webkit_revision); throw new Error(`Unsupported browser "${browser}"`); }; - -module.exports.connect = browser => { - if (browser === 'chromium') - return api.ChromiumBrowser.connect; - if (browser === 'firefox') - return api.FirefoxBrowser.connect; - if (browser === 'webkit') - return api.WebKitBrowser.connect; - throw new Error(`Unsupported browser "${browser}"`); -}; diff --git a/package.json b/package.json index 91dce8dcc81a5..bd2a97c44e0a9 100644 --- a/package.json +++ b/package.json @@ -26,9 +26,7 @@ "tsc": "tsc -p .", "build": "node utils/runWebpack.js --mode='development' && tsc -p .", "watch": "node utils/runWebpack.js --mode='development' --watch --silent | tsc -w -p .", - "apply-next-version": "node utils/apply_next_version.js", - "bundle": "npx browserify -r ./index.js:playwright -o utils/browser/playwright-web.js", - "unit-bundle": "node utils/browser/test.js" + "apply-next-version": "node utils/apply_next_version.js" }, "author": { "name": "Microsoft Corporation" diff --git a/src/platform.ts b/src/platform.ts index f8f5a954a826d..adaf56f3523c9 100644 --- a/src/platform.ts +++ b/src/platform.ts @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. +// Note: this is the only file outside of src/server which can import external dependencies. +// All dependencies must be listed in web.webpack.config.js to avoid bundling them. import * as nodeEvents from 'events'; import * as nodeFS from 'fs'; import * as nodePath from 'path'; diff --git a/src/web.ts b/src/web.ts new file mode 100644 index 0000000000000..742938824cf5d --- /dev/null +++ b/src/web.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 { CRBrowser as ChromiumBrowser } from './chromium/crBrowser'; +import { FFBrowser as FirefoxBrowser } from './firefox/ffBrowser'; +import { WKBrowser as WebKitBrowser } from './webkit/wkBrowser'; + +function connect(browser: 'chromium' | 'firefox' | 'webkit') { + if (browser === 'chromium') + return ChromiumBrowser.connect; + if (browser === 'firefox') + return FirefoxBrowser.connect; + if (browser === 'webkit') + return WebKitBrowser.connect; + throw new Error(`Unsupported browser "${browser}"`); +} + +export = connect; diff --git a/src/web.webpack.config.js b/src/web.webpack.config.js new file mode 100644 index 0000000000000..7144ab8d2c342 --- /dev/null +++ b/src/web.webpack.config.js @@ -0,0 +1,56 @@ +/** + * 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. + */ + +const path = require('path'); + +module.exports = { + entry: path.join(__dirname, 'web.ts'), + devtool: 'source-map', + module: { + rules: [ + { + test: /\.tsx?$/, + loader: 'ts-loader', + options: { + transpileOnly: true + }, + exclude: /node_modules/ + } + ] + }, + resolve: { + extensions: [ '.tsx', '.ts', '.js' ] + }, + output: { + filename: 'web.js', + library: 'playwrightweb', + libraryTarget: 'window', + path: path.resolve(__dirname, '../') + }, + externals: { + 'events': 'dummy', + 'fs': 'dummy', + 'path': 'dummy', + 'debug': 'dummy', + 'buffer': 'dummy', + 'mime': 'dummy', + 'jpeg-js': 'dummy', + 'pngjs': 'dummy', + 'http': 'dummy', + 'https': 'dummy', + 'ws': 'dummy', + } +}; diff --git a/test/assets/playwrightweb.html b/test/assets/playwrightweb.html new file mode 100644 index 0000000000000..09b9fdff36bec --- /dev/null +++ b/test/assets/playwrightweb.html @@ -0,0 +1,13 @@ + + \ No newline at end of file diff --git a/test/playwright.spec.js b/test/playwright.spec.js index f9227c0da4232..0b7ac2ff7729f 100644 --- a/test/playwright.spec.js +++ b/test/playwright.spec.js @@ -215,4 +215,6 @@ module.exports.describe = ({testRunner, product, playwrightPath}) => { if (WEBKIT) { testRunner.loadTests(require('./webkit/launcher.spec.js'), testOptions); } + + testRunner.loadTests(require('./web.spec.js'), testOptions); }; diff --git a/test/test.js b/test/test.js index a5c592d369811..bb0b5eb7da777 100644 --- a/test/test.js +++ b/test/test.js @@ -44,7 +44,7 @@ beforeAll(async state => { const assetsPath = path.join(__dirname, 'assets'); const cachedPath = path.join(__dirname, 'assets', 'cached'); - const port = 8907 + state.parallelIndex * 2; + const port = 8907 + state.parallelIndex * 3; state.server = await TestServer.create(assetsPath, port); state.server.enableHTTPCache(cachedPath); state.server.PORT = port; @@ -59,6 +59,11 @@ beforeAll(async state => { state.httpsServer.PREFIX = `https://localhost:${httpsPort}`; state.httpsServer.CROSS_PROCESS_PREFIX = `https://127.0.0.1:${httpsPort}`; state.httpsServer.EMPTY_PAGE = `https://localhost:${httpsPort}/empty.html`; + + const sourcePort = port + 2; + state.sourceServer = await TestServer.create(path.join(__dirname, '..'), sourcePort); + state.sourceServer.PORT = sourcePort; + state.sourceServer.PREFIX = `http://localhost:${sourcePort}`; }); afterAll(async({server, httpsServer}) => { diff --git a/test/web.spec.js b/test/web.spec.js new file mode 100644 index 0000000000000..f9cf73e6dc505 --- /dev/null +++ b/test/web.spec.js @@ -0,0 +1,84 @@ +/** + * 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. + */ + +module.exports.describe = function({testRunner, expect, defaultBrowserOptions, playwright, product, WEBKIT}) { + const {describe, xdescribe, fdescribe} = testRunner; + const {it, fit, xit, dit} = testRunner; + const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; + + describe.skip(WEBKIT)('Web', function() { + beforeAll(async state => { + state.controlledBrowserServer = await playwright.launchServer({ ...defaultBrowserOptions, pipe: false }); + state.hostBrowserServer = await playwright.launchServer(defaultBrowserOptions); + state.hostBrowser = await state.hostBrowserServer.connect(); + }); + + afterAll(async state => { + await state.hostBrowserServer.close(); + state.hostBrowser = null; + state.hostBrowserServer = null; + + await state.controlledBrowserServer.close(); + state.controlledBrowserServer = null; + state.webUrl = null; + }); + + beforeEach(async state => { + state.page = await state.hostBrowser.defaultContext().newPage(); + state.page.on('console', message => console.log('TEST: ' + message.text())); + await state.page.goto(state.sourceServer.PREFIX + '/test/assets/playwrightweb.html'); + await state.page.evaluate((product, connectOptions) => setup(product, connectOptions), product.toLowerCase(), state.controlledBrowserServer.connectOptions()); + }); + + afterEach(async state => { + await state.page.evaluate(() => teardown()); + await state.page.close(); + state.page = null; + }); + + it('should navigate', async({page, server}) => { + const url = await page.evaluate(async url => { + await page.goto(url); + return page.evaluate(() => window.location.href); + }, server.EMPTY_PAGE); + expect(url).toBe(server.EMPTY_PAGE); + }); + + it('should receive events', async({page, server}) => { + const logs = await page.evaluate(async url => { + const logs = []; + page.on('console', message => logs.push(message.text())); + await page.evaluate(() => console.log('hello')); + await page.evaluate(() => console.log('world')); + return logs; + }, server.EMPTY_PAGE); + expect(logs).toEqual(['hello', 'world']); + }); + + it('should take screenshot', async({page, server}) => { + const { base64, bufferClassName } = await page.evaluate(async url => { + await page.setViewport({width: 500, height: 500}); + await page.goto(url); + const screenshot = await page.screenshot(); + return { base64: screenshot.toString('base64'), bufferClassName: screenshot.constructor.name }; + }, server.PREFIX + '/grid.html'); + const screenshot = Buffer.from(base64, 'base64'); + expect(screenshot).toBeGolden('screenshot-sanity.png'); + // Verify that we use web versions of node-specific classes. + expect(bufferClassName).toBe('BufferImpl'); + }); + }); +}; diff --git a/utils/browser/README.md b/utils/browser/README.md deleted file mode 100644 index b26f48d8a96d7..0000000000000 --- a/utils/browser/README.md +++ /dev/null @@ -1,37 +0,0 @@ -# Bundling For Web Browsers - -To bundle Playwright using [Browserify](http://browserify.org/): - -1. Clone Playwright repository: `git clone https://github.com/Microsoft/playwright && cd playwright` -2. `npm install` -3. Run `npm run bundle` - -This will create `./utils/browser/playwright-web.js` file that contains Playwright bundle. - -You can use it later on in your web page to drive -another browser instance through its WS Endpoint: - -```html - - -``` - -See our [playwright-web tests](https://github.com/Microsoft/playwright/blob/master/utils/browser/test.js) -for details. - -### Running inside Chrome Extension - -You might want to enable `unsafe-eval` inside the extension by adding the following -to your `manifest.json` file: - -``` -"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'" -``` - -Please see discussion in https://github.com/GoogleChrome/puppeteer/issues/3455. diff --git a/utils/browser/WebSocket.js b/utils/browser/WebSocket.js deleted file mode 100644 index a30d920fbf264..0000000000000 --- a/utils/browser/WebSocket.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = window.WebSocket; diff --git a/utils/browser/test.js b/utils/browser/test.js deleted file mode 100644 index 7bea4f116d058..0000000000000 --- a/utils/browser/test.js +++ /dev/null @@ -1,99 +0,0 @@ -const path = require('path'); -const fs = require('fs'); -const playwright = require('../..'); -const {TestServer} = require('../testserver/'); -const {TestRunner, Reporter, Matchers} = require('../testrunner/'); - -const playwrightWebPath = path.join(__dirname, 'playwright-web.js'); -if (!fs.existsSync(playwrightWebPath)) - throw new Error(`playwright-web is not built; run "npm run bundle"`); -const playwrightWeb = fs.readFileSync(playwrightWebPath, 'utf8'); - -const testRunner = new TestRunner(); -const {describe, fdescribe, xdescribe} = testRunner; -const {it, xit, fit} = testRunner; -const {afterAll, beforeAll, afterEach, beforeEach} = testRunner; -const {expect} = new Matchers(); - -beforeAll(async state => { - const assetsPath = path.join(__dirname, '..', '..', 'test', 'assets'); - const port = 8998; - state.server = await TestServer.create(assetsPath, port); - state.serverConfig = { - PREFIX: `http://localhost:${port}`, - EMPTY_PAGE: `http://localhost:${port}/empty.html`, - }; - state.browser = await playwright.launch(); -}); - -afterAll(async state => { - await Promise.all([ - state.server.stop(), - state.browser.close() - ]); - state.browser = null; - state.server = null; -}); - -beforeEach(async state => { - state.page = await state.browser.defaultContext().newPage(); - await state.page.evaluateOnNewDocument(playwrightWeb); - await state.page.addScriptTag({ - content: playwrightWeb + '\n//# sourceURL=playwright-web.js' - }); -}); - -afterEach(async state => { - await state.page.close(); - state.page = null; -}); - -describe('Playwright-Web', () => { - it('should work over web socket', async({page, serverConfig}) => { - const browserServer = await playwright.launchServer(); - // Use in-page playwright to create a new page and navigate it to the EMPTY_PAGE - await page.evaluate(async(browserWSEndpoint, serverConfig) => { - const playwright = require('playwright'); - const browser = await playwright.connect({browserWSEndpoint}); - const page = await browser.defaultContext().newPage(); - await page.goto(serverConfig.EMPTY_PAGE); - }, browserServer.wsEndpoint(), serverConfig); - const browser = await browserServer.connect(); - const pageURLs = (await browser.defaultContext().pages()).map(page => page.url()).sort(); - expect(pageURLs).toEqual([ - 'about:blank', - serverConfig.EMPTY_PAGE - ]); - await browserServer.close(); - }); - it('should work over exposed DevTools protocol', async({browser, page, serverConfig}) => { - // Expose devtools protocol binding into page. - const session = await browser.browserTarget().createCDPSession(); - const pageInfo = (await session.send('Target.getTargets')).targetInfos.find(info => info.attached); - await session.send('Target.exposeDevToolsProtocol', {targetId: pageInfo.targetId}); - await session.detach(); - - // Use in-page playwright to create a new page and navigate it to the EMPTY_PAGE - await page.evaluate(async serverConfig => { - const playwright = require('playwright'); - window.cdp.close = () => {}; - const browser = await playwright.connect({transport: window.cdp}); - const page = await browser.defaultContext().newPage(); - await page.goto(serverConfig.EMPTY_PAGE); - }, serverConfig); - const pageURLs = (await browser.defaultContext().pages()).map(page => page.url()).sort(); - expect(pageURLs).toEqual([ - 'about:blank', - 'about:blank', - serverConfig.EMPTY_PAGE - ]); - }); -}); - -if (process.env.CI && testRunner.hasFocusedTestsOrSuites()) { - console.error('ERROR: "focused" tests/suites are prohibitted on bots. Remove any "fit"/"fdescribe" declarations.'); - process.exit(1); -} - -new Reporter(testRunner); -testRunner.run(); diff --git a/utils/doclint/check_public_api/JSBuilder.js b/utils/doclint/check_public_api/JSBuilder.js index f9623cc4f04ef..d57b056638b4b 100644 --- a/utils/doclint/check_public_api/JSBuilder.js +++ b/utils/doclint/check_public_api/JSBuilder.js @@ -23,8 +23,9 @@ module.exports = { checkSources, expandPrefix }; /** * @param {!Array} sources + * @param {!Array} externalDependencies */ -function checkSources(sources) { +function checkSources(sources, externalDependencies) { // special treatment for Events.js const classEvents = new Map(); const eventsSources = sources.filter(source => source.name().startsWith('events.ts')); @@ -106,17 +107,21 @@ function checkSources(sources) { } if (fileName.endsWith('/api.ts') && ts.isExportSpecifier(node)) apiClassNames.add(expandPrefix((node.propertyName || node.name).text)); - if (!fileName.endsWith('platform.ts') && !fileName.includes('src/server/')) { + const isPlatform = fileName.endsWith('platform.ts'); + if (!fileName.includes('src/server/')) { // Only relative imports. if (ts.isImportDeclaration(node) && ts.isStringLiteral(node.moduleSpecifier)) { const module = node.moduleSpecifier.text; - if (!module.startsWith('.') || path.resolve(path.dirname(fileName), module).includes('src/server')) { + const isRelative = module.startsWith('.'); + const isPlatformDependency = isPlatform && externalDependencies.includes(module); + const isServerDependency = path.resolve(path.dirname(fileName), module).includes('src/server'); + if (isServerDependency || (!isRelative && !isPlatformDependency)) { const lac = ts.getLineAndCharacterOfPosition(node.getSourceFile(), node.moduleSpecifier.pos); errors.push(`Disallowed import "${module}" at ${node.getSourceFile().fileName}:${lac.line + 1}`); } } // No references to external types. - if (ts.isTypeReferenceNode(node)) { + if (!isPlatform && ts.isTypeReferenceNode(node)) { const isPlatformReference = ts.isQualifiedName(node.typeName) && ts.isIdentifier(node.typeName.left) && node.typeName.left.escapedText === 'platform'; if (!isPlatformReference) { const type = checker.getTypeAtLocation(node); diff --git a/utils/doclint/check_public_api/index.js b/utils/doclint/check_public_api/index.js index 90d2197d7865b..defbcadead4b2 100644 --- a/utils/doclint/check_public_api/index.js +++ b/utils/doclint/check_public_api/index.js @@ -33,9 +33,9 @@ const EXCLUDE_PROPERTIES = new Set([ * @param {!Array} mdSources * @return {!Promise>} */ -module.exports = async function lint(page, mdSources, jsSources) { +module.exports = async function lint(page, mdSources, jsSources, externalDependencies) { const mdResult = await mdBuilder(page, mdSources); - const jsResult = jsBuilder.checkSources(jsSources); + const jsResult = jsBuilder.checkSources(jsSources, externalDependencies); const jsDocumentation = filterJSDocumentation(jsSources, jsResult.documentation); const mdDocumentation = mdResult.documentation; diff --git a/utils/doclint/cli.js b/utils/doclint/cli.js index 24741b23a9681..ac25a4c80f686 100755 --- a/utils/doclint/cli.js +++ b/utils/doclint/cli.js @@ -49,7 +49,8 @@ async function run() { const page = await browser.defaultContext().newPage(); const checkPublicAPI = require('./check_public_api'); const jsSources = await Source.readdir(path.join(PROJECT_DIR, 'src')); - messages.push(...await checkPublicAPI(page, mdSources, jsSources)); + const externalDependencies = Object.keys(require('../../src/web.webpack.config').externals); + messages.push(...await checkPublicAPI(page, mdSources, jsSources, externalDependencies)); await browser.close(); for (const source of mdSources) { diff --git a/utils/runWebpack.js b/utils/runWebpack.js index 9de1a33a9f7a0..8b61e5a87e8d3 100644 --- a/utils/runWebpack.js +++ b/utils/runWebpack.js @@ -20,6 +20,7 @@ const path = require('path'); const files = [ path.join('src', 'injected', 'zsSelectorEngine.webpack.config.js'), path.join('src', 'injected', 'injected.webpack.config.js'), + path.join('src', 'web.webpack.config.js'), ]; function runOne(runner, file) { diff --git a/web.d.ts b/web.d.ts new file mode 100644 index 0000000000000..417be8c272056 --- /dev/null +++ b/web.d.ts @@ -0,0 +1,20 @@ +/** + * 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. + */ + +function connect(browser: 'chromium'): import('./lib/api').ChromiumBrowser.connect; +function connect(browser: 'firefox'): import('./lib/api').FirefoxBrowser.connect; +function connect(browser: 'webkit'): import('./lib/api').WebKitBrowser.connect; +export = connect;