feat(events): allow waiting for removeAllListeners (#31941)
This commit is contained in:
parent
193013c9ee
commit
3c87f217df
|
|
@ -296,6 +296,18 @@ testing frameworks should explicitly create [`method: Browser.newContext`] follo
|
||||||
### option: Browser.newPage.storageStatePath = %%-csharp-java-context-option-storage-state-path-%%
|
### option: Browser.newPage.storageStatePath = %%-csharp-java-context-option-storage-state-path-%%
|
||||||
* since: v1.9
|
* since: v1.9
|
||||||
|
|
||||||
|
## async method: Browser.removeAllListeners
|
||||||
|
* since: v1.47
|
||||||
|
|
||||||
|
Removes all the listeners of the given type if the type is given. Otherwise removes all the listeners.
|
||||||
|
|
||||||
|
### param: Browser.removeAllListeners.type
|
||||||
|
* since: v1.47
|
||||||
|
- `type` ?<[string]>
|
||||||
|
|
||||||
|
### option: Browser.removeAllListeners.behavior = %%-remove-all-listeners-options-behavior-%%
|
||||||
|
* since: v1.47
|
||||||
|
|
||||||
## async method: Browser.startTracing
|
## async method: Browser.startTracing
|
||||||
* since: v1.11
|
* since: v1.11
|
||||||
* langs: java, js, python
|
* langs: java, js, python
|
||||||
|
|
|
||||||
|
|
@ -1016,6 +1016,18 @@ Creates a new page in the browser context.
|
||||||
|
|
||||||
Returns all open pages in the context.
|
Returns all open pages in the context.
|
||||||
|
|
||||||
|
## async method: BrowserContext.removeAllListeners
|
||||||
|
* since: v1.47
|
||||||
|
|
||||||
|
Removes all the listeners of the given type if the type is given. Otherwise removes all the listeners.
|
||||||
|
|
||||||
|
### param: BrowserContext.removeAllListeners.type
|
||||||
|
* since: v1.47
|
||||||
|
- `type` ?<[string]>
|
||||||
|
|
||||||
|
### option: BrowserContext.removeAllListeners.behavior = %%-remove-all-listeners-options-behavior-%%
|
||||||
|
* since: v1.47
|
||||||
|
|
||||||
## property: BrowserContext.request
|
## property: BrowserContext.request
|
||||||
* since: v1.16
|
* since: v1.16
|
||||||
* langs:
|
* langs:
|
||||||
|
|
|
||||||
|
|
@ -3340,6 +3340,17 @@ Specifies the maximum number of times this handler should be called. Unlimited b
|
||||||
|
|
||||||
By default, after calling the handler Playwright will wait until the overlay becomes hidden, and only then Playwright will continue with the action/assertion that triggered the handler. This option allows to opt-out of this behavior, so that overlay can stay visible after the handler has run.
|
By default, after calling the handler Playwright will wait until the overlay becomes hidden, and only then Playwright will continue with the action/assertion that triggered the handler. This option allows to opt-out of this behavior, so that overlay can stay visible after the handler has run.
|
||||||
|
|
||||||
|
## async method: Page.removeAllListeners
|
||||||
|
* since: v1.47
|
||||||
|
|
||||||
|
Removes all the listeners of the given type if the type is given. Otherwise removes all the listeners.
|
||||||
|
|
||||||
|
### param: Page.removeAllListeners.type
|
||||||
|
* since: v1.47
|
||||||
|
- `type` ?<[string]>
|
||||||
|
|
||||||
|
### option: Page.removeAllListeners.behavior = %%-remove-all-listeners-options-behavior-%%
|
||||||
|
* since: v1.47
|
||||||
|
|
||||||
## async method: Page.removeLocatorHandler
|
## async method: Page.removeLocatorHandler
|
||||||
* since: v1.44
|
* since: v1.44
|
||||||
|
|
|
||||||
|
|
@ -781,6 +781,16 @@ Whether to allow sites to register Service workers. Defaults to `'allow'`.
|
||||||
* `'allow'`: [Service Workers](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API) can be registered.
|
* `'allow'`: [Service Workers](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API) can be registered.
|
||||||
* `'block'`: Playwright will block all registration of Service Workers.
|
* `'block'`: Playwright will block all registration of Service Workers.
|
||||||
|
|
||||||
|
## remove-all-listeners-options-behavior
|
||||||
|
* langs: js
|
||||||
|
* since: v1.47
|
||||||
|
- `behavior` <[RemoveAllListenersBehavior]<"wait"|"ignoreErrors"|"default">>
|
||||||
|
|
||||||
|
Specifies whether to wait for already running listeners and what to do if they throw errors:
|
||||||
|
* `'default'` - do not wait for current listener calls (if any) to finish, if the listener throws, it may result in unhandled error
|
||||||
|
* `'wait'` - wait for current listener calls (if any) to finish
|
||||||
|
* `'ignoreErrors'` - do not wait for current listener calls (if any) to finish, all errors thrown by the listeners after removal are silently caught
|
||||||
|
|
||||||
## unroute-all-options-behavior
|
## unroute-all-options-behavior
|
||||||
* langs: js, csharp, python
|
* langs: js, csharp, python
|
||||||
* since: v1.41
|
* since: v1.41
|
||||||
|
|
@ -791,6 +801,7 @@ Specifies whether to wait for already running handlers and what to do if they th
|
||||||
* `'wait'` - wait for current handler calls (if any) to finish
|
* `'wait'` - wait for current handler calls (if any) to finish
|
||||||
* `'ignoreErrors'` - do not wait for current handler calls (if any) to finish, all errors thrown by the handlers after unrouting are silently caught
|
* `'ignoreErrors'` - do not wait for current handler calls (if any) to finish, all errors thrown by the handlers after unrouting are silently caught
|
||||||
|
|
||||||
|
|
||||||
## select-options-values
|
## select-options-values
|
||||||
* langs: java, js, csharp
|
* langs: java, js, csharp
|
||||||
- `values` <[null]|[string]|[ElementHandle]|[Array]<[string]>|[Object]|[Array]<[ElementHandle]>|[Array]<[Object]>>
|
- `values` <[null]|[string]|[ElementHandle]|[Array]<[string]>|[Object]|[Array]<[ElementHandle]>|[Array]<[Object]>>
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
type EventType = string | symbol;
|
type EventType = string | symbol;
|
||||||
type Listener = (...args: any[]) => void;
|
type Listener = (...args: any[]) => any;
|
||||||
type EventMap = Record<EventType, Listener | Listener[]>;
|
type EventMap = Record<EventType, Listener | Listener[]>;
|
||||||
import { EventEmitter as OriginalEventEmitter } from 'events';
|
import { EventEmitter as OriginalEventEmitter } from 'events';
|
||||||
import type { EventEmitter as EventEmitterType } from 'events';
|
import type { EventEmitter as EventEmitterType } from 'events';
|
||||||
|
|
@ -34,6 +34,8 @@ export class EventEmitter implements EventEmitterType {
|
||||||
private _events: EventMap | undefined = undefined;
|
private _events: EventMap | undefined = undefined;
|
||||||
private _eventsCount = 0;
|
private _eventsCount = 0;
|
||||||
private _maxListeners: number | undefined = undefined;
|
private _maxListeners: number | undefined = undefined;
|
||||||
|
readonly _pendingHandlers = new Map<EventType, Set<Promise<void>>>();
|
||||||
|
private _rejectionHandler: ((error: Error) => void) | undefined;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
if (this._events === undefined || this._events === Object.getPrototypeOf(this)._events) {
|
if (this._events === undefined || this._events === Object.getPrototypeOf(this)._events) {
|
||||||
|
|
@ -66,17 +68,34 @@ export class EventEmitter implements EventEmitterType {
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (typeof handler === 'function') {
|
if (typeof handler === 'function') {
|
||||||
Reflect.apply(handler, this, args);
|
this._callHandler(type, handler, args);
|
||||||
} else {
|
} else {
|
||||||
const len = handler.length;
|
const len = handler.length;
|
||||||
const listeners = handler.slice();
|
const listeners = handler.slice();
|
||||||
for (let i = 0; i < len; ++i)
|
for (let i = 0; i < len; ++i)
|
||||||
Reflect.apply(listeners[i], this, args);
|
this._callHandler(type, listeners[i], args);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _callHandler(type: EventType, handler: Listener, args: any[]): void {
|
||||||
|
const promise = Reflect.apply(handler, this, args);
|
||||||
|
if (!(promise instanceof Promise))
|
||||||
|
return;
|
||||||
|
let set = this._pendingHandlers.get(type);
|
||||||
|
if (!set) {
|
||||||
|
set = new Set();
|
||||||
|
this._pendingHandlers.set(type, set);
|
||||||
|
}
|
||||||
|
set.add(promise);
|
||||||
|
promise.catch(e => {
|
||||||
|
if (this._rejectionHandler)
|
||||||
|
this._rejectionHandler(e);
|
||||||
|
else
|
||||||
|
throw e;
|
||||||
|
}).finally(() => set.delete(promise));
|
||||||
|
}
|
||||||
|
|
||||||
addListener(type: EventType, listener: Listener): this {
|
addListener(type: EventType, listener: Listener): this {
|
||||||
return this._addListener(type, listener, false);
|
return this._addListener(type, listener, false);
|
||||||
}
|
}
|
||||||
|
|
@ -214,10 +233,34 @@ export class EventEmitter implements EventEmitterType {
|
||||||
return this.removeListener(type, listener);
|
return this.removeListener(type, listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
removeAllListeners(type?: string): this {
|
removeAllListeners(type?: EventType): this;
|
||||||
|
removeAllListeners(type: EventType | undefined, options: { behavior?: 'wait'|'ignoreErrors'|'default' }): Promise<void>;
|
||||||
|
removeAllListeners(type?: string, options?: { behavior?: 'wait'|'ignoreErrors'|'default' }): this | Promise<void> {
|
||||||
|
this._removeAllListeners(type);
|
||||||
|
if (!options)
|
||||||
|
return this;
|
||||||
|
|
||||||
|
if (options.behavior === 'wait') {
|
||||||
|
const errors: Error[] = [];
|
||||||
|
this._rejectionHandler = error => errors.push(error);
|
||||||
|
// eslint-disable-next-line internal-playwright/await-promise-in-class-returns
|
||||||
|
return this._waitFor(type).then(() => {
|
||||||
|
if (errors.length)
|
||||||
|
throw errors[0];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.behavior === 'ignoreErrors')
|
||||||
|
this._rejectionHandler = () => {};
|
||||||
|
|
||||||
|
// eslint-disable-next-line internal-playwright/await-promise-in-class-returns
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
private _removeAllListeners(type?: string) {
|
||||||
const events = this._events;
|
const events = this._events;
|
||||||
if (!events)
|
if (!events)
|
||||||
return this;
|
return;
|
||||||
|
|
||||||
// not listening for removeListener, no need to emit
|
// not listening for removeListener, no need to emit
|
||||||
if (!events.removeListener) {
|
if (!events.removeListener) {
|
||||||
|
|
@ -230,7 +273,7 @@ export class EventEmitter implements EventEmitterType {
|
||||||
else
|
else
|
||||||
delete events[type];
|
delete events[type];
|
||||||
}
|
}
|
||||||
return this;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// emit removeListener for all listeners on all events
|
// emit removeListener for all listeners on all events
|
||||||
|
|
@ -241,12 +284,12 @@ export class EventEmitter implements EventEmitterType {
|
||||||
key = keys[i];
|
key = keys[i];
|
||||||
if (key === 'removeListener')
|
if (key === 'removeListener')
|
||||||
continue;
|
continue;
|
||||||
this.removeAllListeners(key);
|
this._removeAllListeners(key);
|
||||||
}
|
}
|
||||||
this.removeAllListeners('removeListener');
|
this._removeAllListeners('removeListener');
|
||||||
this._events = Object.create(null);
|
this._events = Object.create(null);
|
||||||
this._eventsCount = 0;
|
this._eventsCount = 0;
|
||||||
return this;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const listeners = events[type];
|
const listeners = events[type];
|
||||||
|
|
@ -258,8 +301,6 @@ export class EventEmitter implements EventEmitterType {
|
||||||
for (let i = listeners.length - 1; i >= 0; i--)
|
for (let i = listeners.length - 1; i >= 0; i--)
|
||||||
this.removeListener(type, listeners[i]);
|
this.removeListener(type, listeners[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
listeners(type: EventType): Listener[] {
|
listeners(type: EventType): Listener[] {
|
||||||
|
|
@ -286,6 +327,18 @@ export class EventEmitter implements EventEmitterType {
|
||||||
return this._eventsCount > 0 && this._events ? Reflect.ownKeys(this._events) : [];
|
return this._eventsCount > 0 && this._events ? Reflect.ownKeys(this._events) : [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async _waitFor(type?: EventType) {
|
||||||
|
let promises: Promise<void>[] = [];
|
||||||
|
if (type) {
|
||||||
|
promises = [...(this._pendingHandlers.get(type) || [])];
|
||||||
|
} else {
|
||||||
|
promises = [];
|
||||||
|
for (const [, pending] of this._pendingHandlers)
|
||||||
|
promises.push(...pending);
|
||||||
|
}
|
||||||
|
await Promise.all(promises);
|
||||||
|
}
|
||||||
|
|
||||||
private _listeners(target: EventEmitter, type: EventType, unwrap: boolean): Listener[] {
|
private _listeners(target: EventEmitter, type: EventType, unwrap: boolean): Listener[] {
|
||||||
const events = target._events;
|
const events = target._events;
|
||||||
|
|
||||||
|
|
@ -310,7 +363,7 @@ function checkListener(listener: any) {
|
||||||
|
|
||||||
class OnceWrapper {
|
class OnceWrapper {
|
||||||
private _fired = false;
|
private _fired = false;
|
||||||
readonly wrapperFunction: (...args: any[]) => void;
|
readonly wrapperFunction: (...args: any[]) => Promise<void> | void;
|
||||||
readonly _listener: Listener;
|
readonly _listener: Listener;
|
||||||
private _eventEmitter: EventEmitter;
|
private _eventEmitter: EventEmitter;
|
||||||
private _eventType: EventType;
|
private _eventType: EventType;
|
||||||
|
|
|
||||||
1964
packages/playwright-core/types/types.d.ts
vendored
1964
packages/playwright-core/types/types.d.ts
vendored
File diff suppressed because it is too large
Load diff
|
|
@ -20,7 +20,6 @@
|
||||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
import events from 'events';
|
|
||||||
import { EventEmitter } from '../../../packages/playwright-core/lib/client/eventEmitter';
|
import { EventEmitter } from '../../../packages/playwright-core/lib/client/eventEmitter';
|
||||||
import { test, expect } from '@playwright/test';
|
import { test, expect } from '@playwright/test';
|
||||||
|
|
||||||
|
|
@ -32,7 +31,6 @@ test('Listener count test', () => {
|
||||||
// Allow any type
|
// Allow any type
|
||||||
emitter.on(123, () => {});
|
emitter.on(123, () => {});
|
||||||
|
|
||||||
expect(events.listenerCount(emitter, 'foo')).toEqual(2);
|
|
||||||
expect(emitter.listenerCount('foo')).toEqual(2);
|
expect(emitter.listenerCount('foo')).toEqual(2);
|
||||||
expect(emitter.listenerCount('bar')).toEqual(0);
|
expect(emitter.listenerCount('bar')).toEqual(0);
|
||||||
expect(emitter.listenerCount('baz')).toEqual(1);
|
expect(emitter.listenerCount('baz')).toEqual(1);
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ test('EventEmitter listeners with one listener', () => {
|
||||||
expect(listeners).toHaveLength(1);
|
expect(listeners).toHaveLength(1);
|
||||||
expect(listeners[0]).toEqual(listener);
|
expect(listeners[0]).toEqual(listener);
|
||||||
|
|
||||||
ee.removeAllListeners('foo');
|
void ee.removeAllListeners('foo');
|
||||||
expect<Array<any>>(ee.listeners('foo')).toHaveLength(0);
|
expect<Array<any>>(ee.listeners('foo')).toHaveLength(0);
|
||||||
|
|
||||||
expect(Array.isArray(fooListeners)).toBeTruthy();
|
expect(Array.isArray(fooListeners)).toBeTruthy();
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,7 @@ test('add and remove listeners', () => {
|
||||||
e.on('foo', callback1);
|
e.on('foo', callback1);
|
||||||
e.on('foo', callback2);
|
e.on('foo', callback2);
|
||||||
expect(e.listeners('foo')).toHaveLength(2);
|
expect(e.listeners('foo')).toHaveLength(2);
|
||||||
e.removeAllListeners('foo');
|
void e.removeAllListeners('foo');
|
||||||
expect(e.listeners('foo')).toHaveLength(0);
|
expect(e.listeners('foo')).toHaveLength(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
80
tests/library/events/remove-all-listeners-wait.spec.ts
Normal file
80
tests/library/events/remove-all-listeners-wait.spec.ts
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
/**
|
||||||
|
* 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 { ManualPromise } from '../../../packages/playwright-core/lib/utils/manualPromise';
|
||||||
|
import { EventEmitter } from '../../../packages/playwright-core/lib/client/eventEmitter';
|
||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
|
||||||
|
test('should not throw with ignoreErrors', async () => {
|
||||||
|
const ee = new EventEmitter();
|
||||||
|
const releaseHandler = new ManualPromise();
|
||||||
|
ee.on('console', async () => {
|
||||||
|
await releaseHandler;
|
||||||
|
throw new Error('Error in console handler');
|
||||||
|
});
|
||||||
|
ee.emit('console');
|
||||||
|
await ee.removeAllListeners('console', { behavior: 'ignoreErrors' });
|
||||||
|
releaseHandler.resolve();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should wait', async () => {
|
||||||
|
const ee = new EventEmitter();
|
||||||
|
const releaseHandler = new ManualPromise();
|
||||||
|
let value = 0;
|
||||||
|
ee.on('console', async () => {
|
||||||
|
await releaseHandler;
|
||||||
|
value = 42;
|
||||||
|
});
|
||||||
|
ee.emit('console');
|
||||||
|
const removePromise = ee.removeAllListeners('console', { behavior: 'wait' });
|
||||||
|
releaseHandler.resolve();
|
||||||
|
await removePromise;
|
||||||
|
expect(value).toBe(42);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should wait all', async () => {
|
||||||
|
const ee = new EventEmitter();
|
||||||
|
const releaseHandler = new ManualPromise();
|
||||||
|
const values = [];
|
||||||
|
ee.on('a', async () => {
|
||||||
|
await releaseHandler;
|
||||||
|
values.push(42);
|
||||||
|
});
|
||||||
|
ee.on('b', async () => {
|
||||||
|
await releaseHandler;
|
||||||
|
values.push(43);
|
||||||
|
});
|
||||||
|
ee.emit('a');
|
||||||
|
ee.emit('b');
|
||||||
|
const removePromise = ee.removeAllListeners(undefined, { behavior: 'wait' });
|
||||||
|
releaseHandler.resolve();
|
||||||
|
await removePromise;
|
||||||
|
expect(values).toEqual([42, 43]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('wait should throw', async () => {
|
||||||
|
const ee = new EventEmitter();
|
||||||
|
const releaseHandler = new ManualPromise();
|
||||||
|
ee.on('console', async () => {
|
||||||
|
await releaseHandler;
|
||||||
|
throw new Error('Error in handler');
|
||||||
|
});
|
||||||
|
ee.emit('console');
|
||||||
|
const removePromise = ee.removeAllListeners('console', { behavior: 'wait' });
|
||||||
|
releaseHandler.resolve();
|
||||||
|
await expect(removePromise).rejects.toThrow('Error in handler');
|
||||||
|
});
|
||||||
|
|
@ -58,8 +58,8 @@ test('listeners', () => {
|
||||||
const barListeners = ee.listeners('bar');
|
const barListeners = ee.listeners('bar');
|
||||||
const bazListeners = ee.listeners('baz');
|
const bazListeners = ee.listeners('baz');
|
||||||
ee.on('removeListener', expectWrapper(['bar', 'baz', 'baz']));
|
ee.on('removeListener', expectWrapper(['bar', 'baz', 'baz']));
|
||||||
ee.removeAllListeners('bar');
|
void ee.removeAllListeners('bar');
|
||||||
ee.removeAllListeners('baz');
|
void ee.removeAllListeners('baz');
|
||||||
|
|
||||||
let listeners = ee.listeners('foo');
|
let listeners = ee.listeners('foo');
|
||||||
expect(Array.isArray(listeners)).toBeTruthy();
|
expect(Array.isArray(listeners)).toBeTruthy();
|
||||||
|
|
@ -91,7 +91,7 @@ test('removeAllListeners removes all listeners', () => {
|
||||||
ee.on('bar', () => { });
|
ee.on('bar', () => { });
|
||||||
ee.on('removeListener', expectWrapper(['foo', 'bar', 'removeListener']));
|
ee.on('removeListener', expectWrapper(['foo', 'bar', 'removeListener']));
|
||||||
ee.on('removeListener', expectWrapper(['foo', 'bar']));
|
ee.on('removeListener', expectWrapper(['foo', 'bar']));
|
||||||
ee.removeAllListeners();
|
void ee.removeAllListeners();
|
||||||
|
|
||||||
let listeners = ee.listeners('foo');
|
let listeners = ee.listeners('foo');
|
||||||
expect(Array.isArray(listeners)).toBeTruthy();
|
expect(Array.isArray(listeners)).toBeTruthy();
|
||||||
|
|
@ -120,7 +120,7 @@ test('listener count after removeAllListeners', () => {
|
||||||
ee.on('baz', () => { });
|
ee.on('baz', () => { });
|
||||||
ee.on('baz', () => { });
|
ee.on('baz', () => { });
|
||||||
expect(ee.listeners('baz').length).toEqual(expectLength + 1);
|
expect(ee.listeners('baz').length).toEqual(expectLength + 1);
|
||||||
ee.removeAllListeners('baz');
|
void ee.removeAllListeners('baz');
|
||||||
expect(ee.listeners('baz').length).toEqual(0);
|
expect(ee.listeners('baz').length).toEqual(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ class MyEE extends EventEmitter {
|
||||||
super();
|
super();
|
||||||
this.once(1, cb);
|
this.once(1, cb);
|
||||||
this.emit(1);
|
this.emit(1);
|
||||||
this.removeAllListeners();
|
void this.removeAllListeners();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ test('should support symbols', () => {
|
||||||
|
|
||||||
ee.emit(foo);
|
ee.emit(foo);
|
||||||
|
|
||||||
ee.removeAllListeners();
|
void ee.removeAllListeners();
|
||||||
expect(ee.listeners(foo).length).toEqual(0);
|
expect(ee.listeners(foo).length).toEqual(0);
|
||||||
|
|
||||||
ee.on(foo, listener);
|
ee.on(foo, listener);
|
||||||
|
|
|
||||||
67
tests/page/page-listeners.spec.ts
Normal file
67
tests/page/page-listeners.spec.ts
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
/**
|
||||||
|
* 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 { ManualPromise } from '../../packages/playwright-core/lib/utils/manualPromise';
|
||||||
|
import { test as it, expect } from './pageTest';
|
||||||
|
|
||||||
|
// This test is mostly for type checking, the actual tests are in the library/events.
|
||||||
|
|
||||||
|
it('should not throw with ignoreErrors', async ({ page }) => {
|
||||||
|
const reachedHandler = new ManualPromise();
|
||||||
|
const releaseHandler = new ManualPromise();
|
||||||
|
page.on('console', async () => {
|
||||||
|
reachedHandler.resolve();
|
||||||
|
await releaseHandler;
|
||||||
|
throw new Error('Error in console handler');
|
||||||
|
});
|
||||||
|
await page.evaluate('console.log(1)');
|
||||||
|
await reachedHandler;
|
||||||
|
await page.removeAllListeners('console', { behavior: 'ignoreErrors' });
|
||||||
|
releaseHandler.resolve();
|
||||||
|
await page.waitForTimeout(1000);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should wait', async ({ page }) => {
|
||||||
|
const reachedHandler = new ManualPromise();
|
||||||
|
const releaseHandler = new ManualPromise();
|
||||||
|
let value = 0;
|
||||||
|
page.on('console', async () => {
|
||||||
|
reachedHandler.resolve();
|
||||||
|
value = 42;
|
||||||
|
});
|
||||||
|
await page.evaluate('console.log(1)');
|
||||||
|
await reachedHandler;
|
||||||
|
const removePromise = page.removeAllListeners('console', { behavior: 'wait' });
|
||||||
|
releaseHandler.resolve();
|
||||||
|
await removePromise;
|
||||||
|
expect(value).toBe(42);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('wait should throw', async ({ page }) => {
|
||||||
|
const reachedHandler = new ManualPromise();
|
||||||
|
const releaseHandler = new ManualPromise();
|
||||||
|
page.on('console', async () => {
|
||||||
|
reachedHandler.resolve();
|
||||||
|
await releaseHandler;
|
||||||
|
throw new Error('Error in handler');
|
||||||
|
});
|
||||||
|
await page.evaluate('console.log(1)');
|
||||||
|
await reachedHandler;
|
||||||
|
const removePromise = page.removeAllListeners('console', { behavior: 'wait' });
|
||||||
|
releaseHandler.resolve();
|
||||||
|
await expect(removePromise).rejects.toThrow('Error in handler');
|
||||||
|
});
|
||||||
|
|
@ -56,7 +56,7 @@ module.exports = function lint(documentation, jsSources, apiFileName) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
for (const member of cls.membersArray) {
|
for (const member of cls.membersArray) {
|
||||||
if (member.kind === 'event')
|
if (member.kind === 'event' || member.alias === 'removeAllListeners')
|
||||||
continue;
|
continue;
|
||||||
const params = methods.get(member.alias);
|
const params = methods.get(member.alias);
|
||||||
if (!params) {
|
if (!params) {
|
||||||
|
|
|
||||||
11
utils/generate_types/overrides.d.ts
vendored
11
utils/generate_types/overrides.d.ts
vendored
|
|
@ -62,6 +62,9 @@ export interface Page {
|
||||||
|
|
||||||
exposeBinding(name: string, playwrightBinding: (source: BindingSource, arg: JSHandle) => any, options: { handle: true }): Promise<void>;
|
exposeBinding(name: string, playwrightBinding: (source: BindingSource, arg: JSHandle) => any, options: { handle: true }): Promise<void>;
|
||||||
exposeBinding(name: string, playwrightBinding: (source: BindingSource, ...args: any[]) => any, options?: { handle?: boolean }): Promise<void>;
|
exposeBinding(name: string, playwrightBinding: (source: BindingSource, ...args: any[]) => any, options?: { handle?: boolean }): Promise<void>;
|
||||||
|
|
||||||
|
removeAllListeners(type?: string): this;
|
||||||
|
removeAllListeners(type: string | undefined, options: { behavior?: 'wait'|'ignoreErrors'|'default' }): Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Frame {
|
export interface Frame {
|
||||||
|
|
@ -101,6 +104,14 @@ export interface BrowserContext {
|
||||||
exposeBinding(name: string, playwrightBinding: (source: BindingSource, ...args: any[]) => any, options?: { handle?: boolean }): Promise<void>;
|
exposeBinding(name: string, playwrightBinding: (source: BindingSource, ...args: any[]) => any, options?: { handle?: boolean }): Promise<void>;
|
||||||
|
|
||||||
addInitScript<Arg>(script: PageFunction<Arg, any> | { path?: string, content?: string }, arg?: Arg): Promise<void>;
|
addInitScript<Arg>(script: PageFunction<Arg, any> | { path?: string, content?: string }, arg?: Arg): Promise<void>;
|
||||||
|
|
||||||
|
removeAllListeners(type?: string): this;
|
||||||
|
removeAllListeners(type: string | undefined, options: { behavior?: 'wait'|'ignoreErrors'|'default' }): Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Browser {
|
||||||
|
removeAllListeners(type?: string): this;
|
||||||
|
removeAllListeners(type: string | undefined, options: { behavior?: 'wait'|'ignoreErrors'|'default' }): Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Worker {
|
export interface Worker {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue