Skip to content

Commit

Permalink
feat(pw-web): generate playwright/web.js which can be used in the bro…
Browse files Browse the repository at this point in the history
…wser (microsoft#455)
  • Loading branch information
dgozman authored Jan 11, 2020
1 parent 15b05e4 commit c77fd5e
Show file tree
Hide file tree
Showing 22 changed files with 273 additions and 163 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,5 @@ yarn.lock
/utils/browser/playwright-web.js
lib/
playwright-*.tgz
/web.js
/web.js.map
6 changes: 5 additions & 1 deletion .npmignore
Original file line number Diff line number Diff line change
Expand Up @@ -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
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down Expand Up @@ -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)
35 changes: 35 additions & 0 deletions docs/web.md
Original file line number Diff line number Diff line change
@@ -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
<script src='../playwright/web.js'></script>
<script>
async function usePlaywright() {
const connect = window.playwrightweb('chromium'); // or 'firefox', 'webkit'
const browser = await connect(options);
// ... drive automation ...
await browser.disconnect();
}
</script>
```

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.
3 changes: 0 additions & 3 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
10 changes: 0 additions & 10 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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}"`);
};
4 changes: 1 addition & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
2 changes: 2 additions & 0 deletions src/platform.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down
31 changes: 31 additions & 0 deletions src/web.ts
Original file line number Diff line number Diff line change
@@ -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;
56 changes: 56 additions & 0 deletions src/web.webpack.config.js
Original file line number Diff line number Diff line change
@@ -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',
}
};
13 changes: 13 additions & 0 deletions test/assets/playwrightweb.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<script src='../../web.js'></script>
<script>
async function setup(product, connectOptions) {
window.connect = window.playwrightweb(product);
window.browser = await window.connect(connectOptions);
window.context = await window.browser.newContext();
window.page = await window.context.newPage();
}
async function teardown() {
await window.context.close();
await window.browser.disconnect();
}
</script>
2 changes: 2 additions & 0 deletions test/playwright.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
};
7 changes: 6 additions & 1 deletion test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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}) => {
Expand Down
84 changes: 84 additions & 0 deletions test/web.spec.js
Original file line number Diff line number Diff line change
@@ -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');
});
});
};
37 changes: 0 additions & 37 deletions utils/browser/README.md

This file was deleted.

1 change: 0 additions & 1 deletion utils/browser/WebSocket.js

This file was deleted.

Loading

0 comments on commit c77fd5e

Please sign in to comment.