chore: remove Worker API

This commit is contained in:
Pavel 2019-11-19 14:17:37 -08:00
parent 60f332d01b
commit 40abb7df53
13 changed files with 3 additions and 357 deletions

View file

@ -78,8 +78,6 @@
* [event: 'requestfailed'](#event-requestfailed)
* [event: 'requestfinished'](#event-requestfinished)
* [event: 'response'](#event-response)
* [event: 'workercreated'](#event-workercreated)
* [event: 'workerdestroyed'](#event-workerdestroyed)
* [page.$(selector)](#pageselector)
* [page.$$(selector)](#pageselector-1)
* [page.$$eval(selector, pageFunction[, ...args])](#pageevalselector-pagefunction-args)
@ -154,12 +152,6 @@
* [page.waitForResponse(urlOrPredicate[, options])](#pagewaitforresponseurlorpredicate-options)
* [page.waitForSelector(selector[, options])](#pagewaitforselectorselector-options)
* [page.waitForXPath(xpath[, options])](#pagewaitforxpathxpath-options)
* [page.workers()](#pageworkers)
- [class: Worker](#class-worker)
* [worker.evaluate(pageFunction[, ...args])](#workerevaluatepagefunction-args)
* [worker.evaluateHandle(pageFunction[, ...args])](#workerevaluatehandlepagefunction-args)
* [worker.executionContext()](#workerexecutioncontext)
* [worker.url()](#workerurl)
- [class: Accessibility](#class-accessibility)
* [accessibility.snapshot([options])](#accessibilitysnapshotoptions)
- [class: Keyboard](#class-keyboard)
@ -317,7 +309,6 @@
* [target.page()](#targetpage)
* [target.type()](#targettype)
* [target.url()](#targeturl)
* [target.worker()](#targetworker)
- [class: CDPSession](#class-cdpsession)
* [cdpSession.detach()](#cdpsessiondetach)
* [cdpSession.send(method[, params])](#cdpsessionsendmethod-params)
@ -344,7 +335,6 @@ The Playwright API is hierarchical and mirrors the browser structure.
- [`BrowserContext`](#class-browsercontext) instance defines a browsing session and can own multiple pages.
- [`Page`](#class-page) has at least one frame: main frame. There might be other frames created by [iframe](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe) or [frame](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/frame) tags.
- [`Frame`](#class-frame) has at least one execution context - the default execution context - where the frame's JavaScript is executed. A Frame might have additional execution contexts that are associated with [extensions](https://developer.chrome.com/extensions).
- [`Worker`](#class-worker) has a single execution context and facilitates interacting with [WebWorkers](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API).
(Diagram source: [link](https://docs.google.com/drawings/d/1Q_AM6KYs9kbyLZF-Lpp5mtpAWth73Cq8IKCsWYgi8MM/edit?usp=sharing))
@ -391,36 +381,6 @@ If Playwright doesn't find them in the environment during the installation step,
> **NOTE** PLAYWRIGHT_* env variables are not accounted for in the [`playwright-core`](https://www.npmjs.com/package/playwright-core) package.
### Working with Chrome Extensions
Playwright can be used for testing Chrome Extensions.
> **NOTE** Extensions in Chrome / Chromium currently only work in non-headless mode.
The following is code for getting a handle to the [background page](https://developer.chrome.com/extensions/background_pages) of an extension whose source is located in `./my-extension`:
```js
const playwright = require('playwright');
(async () => {
const pathToExtension = require('path').join(__dirname, 'my-extension');
const browser = await playwright.launch({
headless: false,
args: [
`--disable-extensions-except=${pathToExtension}`,
`--load-extension=${pathToExtension}`
]
});
const targets = await browser.targets();
const backgroundPageTarget = targets.find(target => target.type() === 'background_page');
const backgroundPage = await backgroundPageTarget.page();
// Test the background page as you would any other page.
await browser.close();
})();
```
> **NOTE** It is not yet possible to test extension popups or content scripts.
### class: Playwright
Playwright module provides a method to launch a Chromium instance.
@ -918,7 +878,7 @@ const newWindowTarget = await browserContext.waitForTarget(target => target.url(
* extends: [EventEmitter](https://nodejs.org/api/events.html#events_class_eventemitter)
Page provides methods to interact with a single tab or [extension background page](https://developer.chrome.com/extensions/background_pages) in Chromium. One [Browser] instance might have multiple [Page] instances.
Page provides methods to interact with a single tab. One [Browser] instance might have multiple [Page] instances.
This example creates a page, navigates it to a URL, and then saves a screenshot:
```js
@ -1062,16 +1022,6 @@ Emitted when a request finishes successfully.
Emitted when a [response] is received.
#### event: 'workercreated'
- <[Worker]>
Emitted when a dedicated [WebWorker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) is spawned by the page.
#### event: 'workerdestroyed'
- <[Worker]>
Emitted when a dedicated [WebWorker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) is terminated.
#### page.$(selector)
- `selector` <[string]> A [selector] to query page for
- returns: <[Promise]<?[ElementHandle]>>
@ -2171,54 +2121,6 @@ const playwright = require('playwright');
```
Shortcut for [page.mainFrame().waitForXPath(xpath[, options])](#framewaitforxpathxpath-options).
#### page.workers()
- returns: <[Array]<[Worker]>>
This method returns all of the dedicated [WebWorkers](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) associated with the page.
> **NOTE** This does not contain ServiceWorkers
### class: Worker
The Worker class represents a [WebWorker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API).
The events `workercreated` and `workerdestroyed` are emitted on the page object to signal the worker lifecycle.
```js
page.on('workercreated', worker => console.log('Worker created: ' + worker.url()));
page.on('workerdestroyed', worker => console.log('Worker destroyed: ' + worker.url()));
console.log('Current workers:');
for (const worker of page.workers())
console.log(' ' + worker.url());
```
#### worker.evaluate(pageFunction[, ...args])
- `pageFunction` <[function]|[string]> Function to be evaluated in the worker context
- `...args` <...[Serializable]|[JSHandle]> Arguments to pass to `pageFunction`
- returns: <[Promise]<[Serializable]>> Promise which resolves to the return value of `pageFunction`
If the function passed to the `worker.evaluate` returns a [Promise], then `worker.evaluate` would wait for the promise to resolve and return its value.
If the function passed to the `worker.evaluate` returns a non-[Serializable] value, then `worker.evaluate` resolves to `undefined`. DevTools Protocol also supports transferring some additional values that are not serializable by `JSON`: `-0`, `NaN`, `Infinity`, `-Infinity`, and bigint literals.
Shortcut for [(await worker.executionContext()).evaluate(pageFunction, ...args)](#executioncontextevaluatepagefunction-args).
#### worker.evaluateHandle(pageFunction[, ...args])
- `pageFunction` <[function]|[string]> Function to be evaluated in the page context
- `...args` <...[Serializable]|[JSHandle]> Arguments to pass to `pageFunction`
- returns: <[Promise]<[JSHandle]>> Promise which resolves to the return value of `pageFunction` as in-page object (JSHandle)
The only difference between `worker.evaluate` and `worker.evaluateHandle` is that `worker.evaluateHandle` returns in-page object (JSHandle).
If the function passed to the `worker.evaluateHandle` returns a [Promise], then `worker.evaluateHandle` would wait for the promise to resolve and return its value.
Shortcut for [(await worker.executionContext()).evaluateHandle(pageFunction, ...args)](#executioncontextevaluatehandlepagefunction-args).
#### worker.executionContext()
- returns: <[Promise]<[ExecutionContext]>>
#### worker.url()
- returns: <[string]>
### class: Accessibility
The Accessibility class provides methods for inspecting Chromium's accessibility tree. The accessibility tree is used by assistive technology such as [screen readers](https://en.wikipedia.org/wiki/Screen_reader) or [switches](https://en.wikipedia.org/wiki/Switch_access).
@ -3096,8 +2998,6 @@ The class represents a context for JavaScript execution. A [Page] might have man
always created after frame is attached to DOM. This context is returned by the [`frame.executionContext()`](#frameexecutioncontext) method.
- [Extensions](https://developer.chrome.com/extensions)'s content scripts create additional execution contexts.
Besides pages, execution contexts can be found in [workers](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API).
#### executionContext.evaluate(pageFunction[, ...args])
- `pageFunction` <[function]|[string]> Function to be evaluated in `executionContext`
- `...args` <...[Serializable]|[JSHandle]> Arguments to pass to `pageFunction`
@ -3162,8 +3062,6 @@ await resultHandle.dispose();
#### executionContext.frame()
- returns: <?[Frame]> Frame associated with this execution context.
> **NOTE** Not every execution context is associated with a frame. For example, workers and extensions have execution contexts that are not associated with frames.
#### executionContext.queryObjects(prototypeHandle)
- `prototypeHandle` <[JSHandle]> A handle to the object prototype.
@ -3871,11 +3769,6 @@ Identifies what kind of target this is. Can be `"page"`, [`"background_page"`](h
#### target.url()
- returns: <[string]>
#### target.worker()
- returns: <[Promise]<?[Worker]>>
If the target is not of type `"service_worker"` or `"shared_worker"`, returns `null`.
### class: CDPSession
* extends: [EventEmitter](https://nodejs.org/api/events.html#events_class_eventemitter)
@ -4024,7 +3917,6 @@ TimeoutError is emitted whenever certain operations are terminated due to timeou
[UIEvent.detail]: https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail "UIEvent.detail"
[USKeyboardLayout]: ../lib/USKeyboardLayout.js "USKeyboardLayout"
[UnixTime]: https://en.wikipedia.org/wiki/Unix_time "Unix Time"
[Worker]: #class-worker "Worker"
[boolean]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Boolean_type "Boolean"
[function]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function "Function"
[iterator]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols "Iterator"

View file

@ -35,8 +35,6 @@ export const Events = {
Load: 'load',
Metrics: 'metrics',
Popup: 'popup',
WorkerCreated: 'workercreated',
WorkerDestroyed: 'workerdestroyed',
},
Browser: {

View file

@ -40,7 +40,6 @@ export = {
TimeoutError: require('./Errors').TimeoutError,
Touchscreen: require('./chromium/Input').Touchscreen,
Tracing: require('./chromium/Tracing').Tracing,
Worker: require('./chromium/Worker').Worker,
},
Firefox: {
Accessibility: require('./firefox/Accessibility').Accessibility,

View file

@ -35,7 +35,6 @@ import { Response, NetworkManagerEvents } from './NetworkManager';
import { TaskQueue } from './TaskQueue';
import { TimeoutSettings } from '../TimeoutSettings';
import { Tracing } from './Tracing';
import { Worker } from './Worker';
import { Target } from './Target';
import { Browser } from './Browser';
import { BrowserContext } from './BrowserContext';
@ -69,7 +68,6 @@ export class Page extends EventEmitter {
_javascriptEnabled = true;
private _viewport: Viewport | null = null;
private _screenshotTaskQueue: TaskQueue;
private _workers = new Map<string, Worker>();
private _fileChooserInterceptionIsDisabled = false;
private _fileChooserInterceptors = new Set<(chooser: FileChooser) => void>();
private _disconnectPromise: Promise<Error> | undefined;
@ -98,27 +96,6 @@ export class Page extends EventEmitter {
this._screenshotTaskQueue = screenshotTaskQueue;
client.on('Target.attachedToTarget', event => {
if (event.targetInfo.type !== 'worker') {
// If we don't detach from service workers, they will never die.
client.send('Target.detachFromTarget', {
sessionId: event.sessionId
}).catch(debugError);
return;
}
const session = Connection.fromSession(client).session(event.sessionId);
const worker = new Worker(session, event.targetInfo.url, this._addConsoleMessage.bind(this), this._handleException.bind(this));
this._workers.set(event.sessionId, worker);
this.emit(Events.Page.WorkerCreated, worker);
});
client.on('Target.detachedFromTarget', event => {
const worker = this._workers.get(event.sessionId);
if (!worker)
return;
this.emit(Events.Page.WorkerDestroyed, worker);
this._workers.delete(event.sessionId);
});
this._frameManager.on(FrameManagerEvents.FrameAttached, event => this.emit(Events.Page.FrameAttached, event));
this._frameManager.on(FrameManagerEvents.FrameDetached, event => this.emit(Events.Page.FrameDetached, event));
this._frameManager.on(FrameManagerEvents.FrameNavigated, event => this.emit(Events.Page.FrameNavigated, event));
@ -215,8 +192,7 @@ export class Page extends EventEmitter {
const {level, text, args, source, url, lineNumber} = event.entry;
if (args)
args.map(arg => releaseObject(this._client, arg));
if (source !== 'worker')
this.emit(Events.Page.Console, new ConsoleMessage(level, text, [], {url, lineNumber}));
this.emit(Events.Page.Console, new ConsoleMessage(level, text, [], {url, lineNumber}));
}
mainFrame(): Frame {
@ -247,10 +223,6 @@ export class Page extends EventEmitter {
return this._frameManager.frames();
}
workers(): Worker[] {
return Array.from(this._workers.values());
}
async setRequestInterception(value: boolean) {
return this._frameManager.networkManager().setRequestInterception(value);
}

View file

@ -21,7 +21,6 @@ import { BrowserContext } from './BrowserContext';
import { CDPSession } from './Connection';
import { Page, Viewport } from './Page';
import { TaskQueue } from './TaskQueue';
import { Worker } from './Worker';
import { Protocol } from './protocol';
export class Target {
@ -33,7 +32,7 @@ export class Target {
private _defaultViewport: Viewport;
private _screenshotTaskQueue: TaskQueue;
private _pagePromise: Promise<Page> | null = null;
private _workerPromise: Promise<Worker> | null = null;
_initializedPromise: Promise<boolean>;
_initializedCallback: (value?: unknown) => void;
_isClosedPromise: Promise<void>;
@ -85,17 +84,6 @@ export class Target {
return this._pagePromise;
}
async worker(): Promise<Worker | null> {
if (this._targetInfo.type !== 'service_worker' && this._targetInfo.type !== 'shared_worker')
return null;
if (!this._workerPromise) {
// TODO(einbinder): Make workers send their console logs.
this._workerPromise = this._sessionFactory()
.then(client => new Worker(client, this._targetInfo.url, () => {} /* consoleAPICalled */, () => {} /* exceptionThrown */));
}
return this._workerPromise;
}
url(): string {
return this._targetInfo.url;
}

View file

@ -1,63 +0,0 @@
/**
* Copyright 2018 Google Inc. All rights reserved.
* Modifications 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 { EventEmitter } from 'events';
import { CDPSession } from './Connection';
import { ExecutionContext } from './ExecutionContext';
import { debugError } from '../helper';
import { JSHandle } from './JSHandle';
import { Protocol } from './protocol';
export class Worker extends EventEmitter {
private _client: CDPSession;
private _url: string;
private _executionContextPromise: Promise<ExecutionContext>;
private _executionContextCallback: (value?: ExecutionContext) => void;
constructor(client: CDPSession, url: string, consoleAPICalled: (arg0: string, arg1: JSHandle[], arg2: Protocol.Runtime.StackTrace | undefined) => void, exceptionThrown: (arg0: Protocol.Runtime.ExceptionDetails) => void) {
super();
this._client = client;
this._url = url;
this._executionContextPromise = new Promise(x => this._executionContextCallback = x);
let jsHandleFactory: (o: Protocol.Runtime.RemoteObject) => JSHandle;
this._client.once('Runtime.executionContextCreated', async event => {
jsHandleFactory = remoteObject => new JSHandle(executionContext, client, remoteObject);
const executionContext = new ExecutionContext(client, event.context, null);
this._executionContextCallback(executionContext);
});
// This might fail if the target is closed before we recieve all execution contexts.
this._client.send('Runtime.enable', {}).catch(debugError);
this._client.on('Runtime.consoleAPICalled', event => consoleAPICalled(event.type, event.args.map(jsHandleFactory), event.stackTrace));
this._client.on('Runtime.exceptionThrown', exception => exceptionThrown(exception.exceptionDetails));
}
url(): string {
return this._url;
}
async executionContext(): Promise<ExecutionContext> {
return this._executionContextPromise;
}
async evaluate(pageFunction: Function | string, ...args: any[]): Promise<any> {
return (await this._executionContextPromise).evaluate(pageFunction, ...args);
}
async evaluateHandle(pageFunction: Function | string, ...args: any[]): Promise<JSHandle> {
return (await this._executionContextPromise).evaluateHandle(pageFunction, ...args);
}
}

View file

@ -50,7 +50,6 @@ export class Page extends EventEmitter {
_javascriptEnabled = true;
private _viewport: Viewport | null = null;
private _screenshotTaskQueue: TaskQueue;
private _workers = new Map<string, Worker>();
private _disconnectPromise: Promise<Error> | undefined;
private _sessionListeners: RegisteredListener[] = [];
@ -179,11 +178,6 @@ export class Page extends EventEmitter {
return this._frameManager.frames();
}
workers(): Worker[] {
return Array.from(this._workers.values());
}
setDefaultNavigationTimeout(timeout: number) {
this._timeoutSettings.setDefaultNavigationTimeout(timeout);
}

View file

@ -1,14 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Worker test</title>
</head>
<body>
<script>
var worker = new Worker('worker.js');
worker.onmessage = function(message) {
console.log(message.data);
};
</script>
</body>
</html>

View file

@ -1,16 +0,0 @@
console.log('hello from the worker');
function workerFunction() {
return 'worker function result';
}
self.addEventListener('message', event => {
console.log('got this data: ' + event.data);
});
(async function() {
while (true) {
self.postMessage(workerFunction.toString());
await new Promise(x => setTimeout(x, 100));
}
})();

View file

@ -55,14 +55,6 @@ module.exports.addTests = function({testRunner, expect, playwright, defaultBrows
await browserWithExtension.close();
expect(backgroundPageTarget).toBeTruthy();
});
it('target.page() should return a background_page', async({}) => {
const browserWithExtension = await playwright.launch(extensionOptions);
const backgroundPageTarget = await browserWithExtension.waitForTarget(target => target.type() === 'background_page');
const page = await backgroundPageTarget.page();
expect(await page.evaluate(() => 2 * 3)).toBe(6);
expect(await page.evaluate(() => window.MAGIC)).toBe(42);
await browserWithExtension.close();
});
it('should have default url when launching browser', async function() {
const browser = await playwright.launch(extensionOptions);
const pages = (await browser.pages()).map(page => page.url());

View file

@ -152,7 +152,6 @@ module.exports.addTests = ({testRunner, product, playwrightPath}) => {
require('./target.spec.js').addTests(testOptions);
require('./touchscreen.spec.js').addTests(testOptions);
require('./waittask.spec.js').addTests(testOptions);
require('./worker.spec.js').addTests(testOptions);
if (CHROME) {
require('./CDPSession.spec.js').addTests(testOptions);
require('./coverage.spec.js').addTests(testOptions);

View file

@ -72,35 +72,6 @@ module.exports.addTests = function({testRunner, expect, playwright, FFOX, CHROME
expect(allPages).toContain(page);
expect(allPages).not.toContain(otherPage);
});
it.skip(FFOX || WEBKIT)('should report when a service worker is created and destroyed', async({page, server, context}) => {
await page.goto(server.EMPTY_PAGE);
const createdTarget = new Promise(fulfill => context.once('targetcreated', target => fulfill(target)));
await page.goto(server.PREFIX + '/serviceworkers/empty/sw.html');
expect((await createdTarget).type()).toBe('service_worker');
expect((await createdTarget).url()).toBe(server.PREFIX + '/serviceworkers/empty/sw.js');
const destroyedTarget = new Promise(fulfill => context.once('targetdestroyed', target => fulfill(target)));
await page.evaluate(() => window.registrationPromise.then(registration => registration.unregister()));
expect(await destroyedTarget).toBe(await createdTarget);
});
it.skip(FFOX || WEBKIT)('should create a worker from a service worker', async({page, server, context}) => {
await page.goto(server.PREFIX + '/serviceworkers/empty/sw.html');
const target = await context.waitForTarget(target => target.type() === 'service_worker');
const worker = await target.worker();
expect(await worker.evaluate(() => self.toString())).toBe('[object ServiceWorkerGlobalScope]');
});
it.skip(FFOX || WEBKIT)('should create a worker from a shared worker', async({page, server, context}) => {
await page.goto(server.EMPTY_PAGE);
await page.evaluate(() => {
new SharedWorker('data:text/javascript,console.log("hi")');
});
const target = await context.waitForTarget(target => target.type() === 'shared_worker');
const worker = await target.worker();
expect(await worker.evaluate(() => self.toString())).toBe('[object SharedWorkerGlobalScope]');
});
it.skip(WEBKIT)('should report when a target url changes', async({page, server, context}) => {
await page.goto(server.EMPTY_PAGE);
let changedTarget = new Promise(fulfill => context.once('targetchanged', target => fulfill(target)));

View file

@ -1,66 +0,0 @@
const utils = require('./utils');
const {waitEvent} = utils;
module.exports.addTests = function({testRunner, expect, FFOX, CHROME, WEBKIT}) {
const {describe, xdescribe, fdescribe} = testRunner;
const {it, fit, xit} = testRunner;
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
describe.skip(FFOX || WEBKIT)('Workers', function() {
it('Page.workers', async function({page, server}) {
await Promise.all([
new Promise(x => page.once('workercreated', x)),
page.goto(server.PREFIX + '/worker/worker.html')]);
const worker = page.workers()[0];
expect(worker.url()).toContain('worker.js');
expect(await worker.evaluate(() => self.workerFunction())).toBe('worker function result');
await page.goto(server.EMPTY_PAGE);
expect(page.workers().length).toBe(0);
});
it('should emit created and destroyed events', async function({page}) {
const workerCreatedPromise = new Promise(x => page.once('workercreated', x));
const workerObj = await page.evaluateHandle(() => new Worker('data:text/javascript,1'));
const worker = await workerCreatedPromise;
const workerThisObj = await worker.evaluateHandle(() => this);
const workerDestroyedPromise = new Promise(x => page.once('workerdestroyed', x));
await page.evaluate(workerObj => workerObj.terminate(), workerObj);
expect(await workerDestroyedPromise).toBe(worker);
const error = await workerThisObj.getProperty('self').catch(error => error);
expect(error.message).toContain('Most likely the worker has been closed.');
});
it('should report console logs', async function({page}) {
const [message] = await Promise.all([
waitEvent(page, 'console'),
page.evaluate(() => new Worker(`data:text/javascript,console.log(1)`)),
]);
expect(message.text()).toBe('1');
expect(message.location()).toEqual({
url: 'data:text/javascript,console.log(1)',
lineNumber: 0,
columnNumber: 8,
});
});
it('should have JSHandles for console logs', async function({page}) {
const logPromise = new Promise(x => page.on('console', x));
await page.evaluate(() => new Worker(`data:text/javascript,console.log(1,2,3,this)`));
const log = await logPromise;
expect(log.text()).toBe('1 2 3 JSHandle@object');
expect(log.args().length).toBe(4);
expect(await (await log.args()[3].getProperty('origin')).jsonValue()).toBe('null');
});
it('should have an execution context', async function({page}) {
const workerCreatedPromise = new Promise(x => page.once('workercreated', x));
await page.evaluate(() => new Worker(`data:text/javascript,console.log(1)`));
const worker = await workerCreatedPromise;
expect(await (await worker.executionContext()).evaluate('1+1')).toBe(2);
});
it('should report errors', async function({page}) {
const errorPromise = new Promise(x => page.on('pageerror', x));
await page.evaluate(() => new Worker(`data:text/javascript, throw new Error('this is my error');`));
const errorLog = await errorPromise;
expect(errorLog.message).toContain('this is my error');
});
});
};