chore: use soft event emitter (#31868)

This commit is contained in:
Pavel Feldman 2024-07-29 17:11:31 -07:00 committed by GitHub
parent 7e7319da7d
commit 58b0c76f20
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 1775 additions and 1 deletions

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
import { EventEmitter } from 'events';
import { EventEmitter } from './eventEmitter';
import type * as channels from '@protocol/channels';
import { maybeFindValidator, ValidationError, type ValidatorContext } from '../protocol/validator';
import { debugLogger } from '../utils/debugLogger';

View File

@ -0,0 +1,345 @@
/**
* Copyright Joyent, Inc. and other Node contributors.
* Modifications copyright (c) Microsoft Corporation.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to permit
* persons to whom the Software is furnished to do so, subject to the
* following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
* NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
* USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
type EventType = string | symbol;
type Listener = (...args: any[]) => void;
type EventMap = Record<EventType, Listener | Listener[]>;
import { EventEmitter as OriginalEventEmitter } from 'events';
import type { EventEmitter as EventEmitterType } from 'events';
import { isUnderTest } from '../utils';
export class EventEmitter implements EventEmitterType {
private _events: EventMap | undefined = undefined;
private _eventsCount = 0;
private _maxListeners: number | undefined = undefined;
constructor() {
if (this._events === undefined || this._events === Object.getPrototypeOf(this)._events) {
this._events = Object.create(null);
this._eventsCount = 0;
}
this._maxListeners = this._maxListeners || undefined;
this.on = this.addListener;
this.off = this.removeListener;
}
setMaxListeners(n: number): this {
if (typeof n !== 'number' || n < 0 || Number.isNaN(n))
throw new RangeError('The value of "n" is out of range. It must be a non-negative number. Received ' + n + '.');
this._maxListeners = n;
return this;
}
getMaxListeners(): number {
return this._maxListeners === undefined ? OriginalEventEmitter.defaultMaxListeners : this._maxListeners;
}
emit(type: EventType, ...args: any[]): boolean {
const events = this._events;
if (events === undefined)
return false;
const handler = events?.[type];
if (handler === undefined)
return false;
if (typeof handler === 'function') {
Reflect.apply(handler, this, args);
} else {
const len = handler.length;
const listeners = handler.slice();
for (let i = 0; i < len; ++i)
Reflect.apply(listeners[i], this, args);
}
return true;
}
addListener(type: EventType, listener: Listener): this {
return this._addListener(type, listener, false);
}
on(type: EventType, listener: Listener): this {
return this._addListener(type, listener, false);
}
private _addListener(type: EventType, listener: Listener, prepend: boolean): this {
checkListener(listener);
let events = this._events;
let existing;
if (events === undefined) {
events = this._events = Object.create(null);
this._eventsCount = 0;
} else {
// To avoid recursion in the case that type === "newListener"! Before
// adding it to the listeners, first emit "newListener".
if (events.newListener !== undefined) {
this.emit('newListener', type, unwrapListener(listener));
// Re-assign `events` because a newListener handler could have caused the
// this._events to be assigned to a new object
events = this._events!;
}
existing = events[type];
}
if (existing === undefined) {
// Optimize the case of one listener. Don't need the extra array object.
existing = events![type] = listener;
++this._eventsCount;
} else {
if (typeof existing === 'function') {
// Adding the second element, need to change to array.
existing = events![type] =
prepend ? [listener, existing] : [existing, listener];
// If we've already got an array, just append.
} else if (prepend) {
existing.unshift(listener);
} else {
existing.push(listener);
}
// Check for listener leak
const m = this.getMaxListeners();
if (m > 0 && existing.length > m && !(existing as any).warned) {
(existing as any).warned = true;
// No error code for this since it is a Warning
const w = new Error('Possible EventEmitter memory leak detected. ' +
existing.length + ' ' + String(type) + ' listeners ' +
'added. Use emitter.setMaxListeners() to ' +
'increase limit') as any;
w.name = 'MaxListenersExceededWarning';
w.emitter = this;
w.type = type;
w.count = existing.length;
if (!isUnderTest()) {
// eslint-disable-next-line no-console
console.warn(w);
}
}
}
return this;
}
prependListener(type: EventType, listener: Listener): this {
return this._addListener(type, listener, true);
}
once(type: EventType, listener: Listener): this {
checkListener(listener);
this.on(type, new OnceWrapper(this, type, listener).wrapperFunction);
return this;
}
prependOnceListener(type: EventType, listener: Listener): this {
checkListener(listener);
this.prependListener(type, new OnceWrapper(this, type, listener).wrapperFunction);
return this;
}
removeListener(type: EventType, listener: Listener): this {
checkListener(listener);
const events = this._events;
if (events === undefined)
return this;
const list = events[type];
if (list === undefined)
return this;
if (list === listener || (list as any).listener === listener) {
if (--this._eventsCount === 0) {
this._events = Object.create(null);
} else {
delete events[type];
if (events.removeListener)
this.emit('removeListener', type, (list as any).listener ?? listener);
}
} else if (typeof list !== 'function') {
let position = -1;
let originalListener;
for (let i = list.length - 1; i >= 0; i--) {
if (list[i] === listener || wrappedListener(list[i]) === listener) {
originalListener = wrappedListener(list[i]);
position = i;
break;
}
}
if (position < 0)
return this;
if (position === 0)
list.shift();
else
list.splice(position, 1);
if (list.length === 1)
events[type] = list[0];
if (events.removeListener !== undefined)
this.emit('removeListener', type, originalListener || listener);
}
return this;
}
off(type: EventType, listener: Listener): this {
return this.removeListener(type, listener);
}
removeAllListeners(type?: string): this {
const events = this._events;
if (!events)
return this;
// not listening for removeListener, no need to emit
if (!events.removeListener) {
if (type === undefined) {
this._events = Object.create(null);
this._eventsCount = 0;
} else if (events[type] !== undefined) {
if (--this._eventsCount === 0)
this._events = Object.create(null);
else
delete events[type];
}
return this;
}
// emit removeListener for all listeners on all events
if (type === undefined) {
const keys = Object.keys(events);
let key;
for (let i = 0; i < keys.length; ++i) {
key = keys[i];
if (key === 'removeListener')
continue;
this.removeAllListeners(key);
}
this.removeAllListeners('removeListener');
this._events = Object.create(null);
this._eventsCount = 0;
return this;
}
const listeners = events[type];
if (typeof listeners === 'function') {
this.removeListener(type, listeners);
} else if (listeners !== undefined) {
// LIFO order
for (let i = listeners.length - 1; i >= 0; i--)
this.removeListener(type, listeners[i]);
}
return this;
}
listeners(type: EventType): Listener[] {
return this._listeners(this, type, true);
}
rawListeners(type: EventType): Listener[] {
return this._listeners(this, type, false);
}
listenerCount(type: EventType): number {
const events = this._events;
if (events !== undefined) {
const listener = events[type];
if (typeof listener === 'function')
return 1;
if (listener !== undefined)
return listener.length;
}
return 0;
}
eventNames(): Array<string | symbol> {
return this._eventsCount > 0 && this._events ? Reflect.ownKeys(this._events) : [];
}
private _listeners(target: EventEmitter, type: EventType, unwrap: boolean): Listener[] {
const events = target._events;
if (events === undefined)
return [];
const listener = events[type];
if (listener === undefined)
return [];
if (typeof listener === 'function')
return unwrap ? [unwrapListener(listener)] : [listener];
return unwrap ? unwrapListeners(listener) : listener.slice();
}
}
function checkListener(listener: any) {
if (typeof listener !== 'function')
throw new TypeError('The "listener" argument must be of type Function. Received type ' + typeof listener);
}
class OnceWrapper {
private _fired = false;
readonly wrapperFunction: (...args: any[]) => void;
readonly _listener: Listener;
private _eventEmitter: EventEmitter;
private _eventType: EventType;
constructor(eventEmitter: EventEmitter, eventType: EventType, listener: Listener) {
this._eventEmitter = eventEmitter;
this._eventType = eventType;
this._listener = listener;
this.wrapperFunction = this._handle.bind(this);
(this.wrapperFunction as any).listener = listener;
}
private _handle(...args: any[]) {
if (this._fired)
return;
this._fired = true;
this._eventEmitter.removeListener(this._eventType, this.wrapperFunction);
return this._listener.apply(this._eventEmitter, args);
}
}
function unwrapListener(l: Listener): Listener {
return wrappedListener(l) ?? l;
}
function unwrapListeners(arr: Listener[]): Listener[] {
return arr.map(l => wrappedListener(l) ?? l);
}
function wrappedListener(l: Listener): Listener {
return (l as any).listener;
}

View File

@ -0,0 +1,103 @@
// Copyright Joyent, Inc. and other Node contributors.
// Modifications copyright (c) by Microsoft Corporation
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
import { EventEmitter } from '../../../packages/playwright-core/lib/client/eventEmitter';
import { test, expect } from '@playwright/test';
test.describe('EventEmitter tests', () => {
test('should work', () => {
const events_new_listener_emitted = [];
const listeners_new_listener_emitted = [];
const ee = new EventEmitter();
ee.on('newListener', function(event, listener) {
if (event !== 'newListener') {
events_new_listener_emitted.push(event);
listeners_new_listener_emitted.push(listener);
}
});
const hello = (a, b) => {
expect(a).toEqual('a');
expect(b).toEqual('b');
};
ee.once('newListener', (name, listener) => {
expect(name).toEqual('hello');
expect(listener).toEqual(hello);
const listeners = ee.listeners('hello');
expect(Array.isArray(listeners)).toBeTruthy();
expect(listeners).toHaveLength(0);
});
ee.on('hello', hello);
ee.once('foo', () => { throw new Error('foo error'); });
expect(Array.isArray(events_new_listener_emitted)).toBeTruthy();
expect(events_new_listener_emitted).toHaveLength(2);
expect(events_new_listener_emitted[0]).toEqual('hello');
expect(events_new_listener_emitted[1]).toEqual('foo');
expect(Array.isArray(listeners_new_listener_emitted)).toBeTruthy();
expect(listeners_new_listener_emitted).toHaveLength(2);
expect(listeners_new_listener_emitted[0]).toEqual(hello);
expect(listeners_new_listener_emitted[1]).toThrow();
ee.emit('hello', 'a', 'b');
});
test('set max listeners test', () => {
const f = new EventEmitter();
f.setMaxListeners(0);
});
test('Listener order', () => {
const listen1 = function() {};
const listen2 = function() {};
const ee = new EventEmitter();
ee.once('newListener', function() {
const listeners = ee.listeners('hello');
expect(Array.isArray(listeners)).toBeTruthy();
expect(listeners).toHaveLength(0);
ee.once('newListener', function() {
const listeners = ee.listeners('hello');
expect(Array.isArray(listeners)).toBeTruthy();
expect(listeners).toHaveLength(0);
});
ee.on('hello', listen2);
});
ee.on('hello', listen1);
const listeners = ee.listeners('hello');
expect(Array.isArray(listeners)).toBeTruthy();
expect(listeners).toHaveLength(2);
expect(listeners[0]).toEqual(listen2);
expect(listeners[1]).toEqual(listen1);
});
test('listener type check', () => {
const ee = new EventEmitter();
expect(() => ee.on('foo', null)).toThrow('The "listener" argument must be of type Function. Received type object');
});
});

View File

@ -0,0 +1,92 @@
// Copyright Joyent, Inc. and other Node contributors.
// Modifications copyright (c) by Microsoft Corporation
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
import events from 'events';
import { EventEmitter } from '../../../packages/playwright-core/lib/client/eventEmitter';
import { setUnderTest } from '../../../packages/playwright-core/lib/utils/debug';
import { test, expect } from '@playwright/test';
import * as common from './utils';
setUnderTest();
test('defaultMaxListeners', () => {
const e = new EventEmitter();
for (let i = 0; i < 10; i++)
e.on('default', common.mustNotCall());
expect(e._events['default']).not.toHaveProperty('warned');
e.on('default', common.mustNotCall());
expect(e._events['default'].hasOwnProperty('warned')).toBeTruthy();
e.setMaxListeners(5);
for (let i = 0; i < 5; i++)
e.on('specific', common.mustNotCall());
expect(e._events['specific']).not.toHaveProperty('warned');
e.on('specific', common.mustNotCall());
expect(e._events['specific'].hasOwnProperty('warned')).toBeTruthy();
// only one
e.setMaxListeners(1);
e.on('only one', common.mustNotCall());
expect(e._events['only one']).not.toHaveProperty('warned');
e.on('only one', common.mustNotCall());
expect(e._events['only one'].hasOwnProperty('warned')).toBeTruthy();
// unlimited
e.setMaxListeners(0);
for (let i = 0; i < 1000; i++)
e.on('unlimited', common.mustNotCall());
expect(e._events['unlimited']).not.toHaveProperty('warned');
});
test('process-wide', () => {
events.EventEmitter.defaultMaxListeners = 42;
const e = new EventEmitter();
for (let i = 0; i < 42; ++i)
e.on('fortytwo', common.mustNotCall());
expect(e._events['fortytwo']).not.toHaveProperty('warned');
e.on('fortytwo', common.mustNotCall());
expect(e._events['fortytwo'].hasOwnProperty('warned')).toBeTruthy();
delete e._events['fortytwo'].warned;
events.EventEmitter.defaultMaxListeners = 44;
e.on('fortytwo', common.mustNotCall());
expect(e._events['fortytwo']).not.toHaveProperty('warned');
e.on('fortytwo', common.mustNotCall());
expect(e._events['fortytwo'].hasOwnProperty('warned')).toBeTruthy();
});
test('_maxListeners still has precedence over defaultMaxListeners', () => {
events.EventEmitter.defaultMaxListeners = 42;
const e = new EventEmitter();
e.setMaxListeners(1);
e.on('uno', common.mustNotCall());
expect(e._events['uno']).not.toHaveProperty('warned');
e.on('uno', common.mustNotCall());
expect(e._events['uno'].hasOwnProperty('warned')).toBeTruthy();
});

View File

@ -0,0 +1,55 @@
// Copyright Joyent, Inc. and other Node contributors.
// Modifications copyright (c) by Microsoft Corporation
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
import { EventEmitter } from '../../../packages/playwright-core/lib/client/eventEmitter';
import { test, expect } from '@playwright/test';
test.describe('EventEmitter', () => {
test('should maintain event names correctly', () => {
const e = new EventEmitter();
const m = () => {};
e.on('foo', function() {});
expect(e.eventNames().length).toBe(1);
expect(e.eventNames()[0]).toBe('foo');
e.on('bar', m);
expect(e.eventNames().length).toBe(2);
expect(e.eventNames()[0]).toBe('foo');
expect(e.eventNames()[1]).toBe('bar');
e.removeListener('bar', m);
expect(e.eventNames().length).toBe(1);
expect(e.eventNames()[0]).toBe('foo');
if (typeof Symbol !== 'undefined') {
const s = Symbol('s');
e.on(s, m);
expect(e.eventNames().length).toBe(2);
expect(e.eventNames()[0]).toBe('foo');
expect(e.eventNames()[1]).toBe(s);
e.removeListener(s, m);
expect(e.eventNames().length).toBe(1);
expect(e.eventNames()[0]).toBe('foo');
}
});
});

View File

@ -0,0 +1,40 @@
// Copyright Joyent, Inc. and other Node contributors.
// Modifications copyright (c) by Microsoft Corporation
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
import events from 'events';
import { EventEmitter } from '../../../packages/playwright-core/lib/client/eventEmitter';
import { test, expect } from '@playwright/test';
test('Listener count test', () => {
const emitter = new EventEmitter();
emitter.on('foo', () => {});
emitter.on('foo', () => {});
emitter.on('baz', () => {});
// Allow any type
emitter.on(123, () => {});
expect(events.listenerCount(emitter, 'foo')).toEqual(2);
expect(emitter.listenerCount('foo')).toEqual(2);
expect(emitter.listenerCount('bar')).toEqual(0);
expect(emitter.listenerCount('baz')).toEqual(1);
expect(emitter.listenerCount(123)).toEqual(1);
});

View File

@ -0,0 +1,61 @@
// Copyright Joyent, Inc. and other Node contributors.
// Modifications copyright (c) by Microsoft Corporation
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
import { test, expect } from '@playwright/test';
import { EventEmitter } from '../../../packages/playwright-core/lib/client/eventEmitter';
test('listeners empty check', () => {
const e = new EventEmitter();
let fl; // foo listeners
fl = e.listeners('foo');
expect(Array.isArray(fl)).toBeTruthy();
expect(fl).toHaveLength(0);
expect(e._events instanceof Object).toBeFalsy();
expect(Object.keys(e._events)).toHaveLength(0);
const fail = () => expect(true).toBe(false);
e.on('foo', fail);
fl = e.listeners('foo');
expect(e._events.foo).toBe(fail);
expect(Array.isArray(fl)).toBeTruthy();
expect(fl).toHaveLength(1);
expect(fl[0]).toBe(fail);
e.listeners('bar');
const pass = () => expect(true).toBe(true);
e.on('foo', pass);
fl = e.listeners('foo');
expect(Array.isArray(e._events.foo)).toBeTruthy();
expect(e._events.foo).toHaveLength(2);
expect(e._events.foo[0]).toBe(fail);
expect(e._events.foo[1]).toBe(pass);
expect(Array.isArray(fl)).toBeTruthy();
expect(fl).toHaveLength(2);
expect(fl[0]).toBe(fail);
expect(fl[1]).toBe(pass);
});

View File

@ -0,0 +1,154 @@
// Copyright Joyent, Inc. and other Node contributors.
// Modifications copyright (c) by Microsoft Corporation
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
import { EventEmitter } from '../../../packages/playwright-core/lib/client/eventEmitter';
import { test, expect } from '@playwright/test';
const listener = () => {};
const listener2 = () => {};
const listener3 = () => { return 0; };
const listener4 = () => { return 1; };
class TestStream extends EventEmitter {}
test('EventEmitter listeners with one listener', () => {
const ee = new EventEmitter();
ee.on('foo', listener);
const fooListeners = ee.listeners('foo');
const listeners = ee.listeners('foo');
expect(Array.isArray(listeners)).toBeTruthy();
expect(listeners).toHaveLength(1);
expect(listeners[0]).toEqual(listener);
ee.removeAllListeners('foo');
expect<Array<any>>(ee.listeners('foo')).toHaveLength(0);
expect(Array.isArray(fooListeners)).toBeTruthy();
expect(fooListeners).toHaveLength(1);
expect(fooListeners[0]).toEqual(listener);
});
test('Array copy modification does not modify orig', () => {
const ee = new EventEmitter();
ee.on('foo', listener);
const eeListenersCopy = ee.listeners('foo');
expect(Array.isArray(eeListenersCopy)).toBeTruthy();
expect(eeListenersCopy).toHaveLength(1);
expect(eeListenersCopy[0]).toEqual(listener);
eeListenersCopy.push(listener2);
const listeners = ee.listeners('foo');
expect(Array.isArray(listeners)).toBeTruthy();
expect(listeners).toHaveLength(1);
expect(listeners[0]).toEqual(listener);
expect(eeListenersCopy).toHaveLength(2);
expect(eeListenersCopy[0]).toEqual(listener);
expect(eeListenersCopy[1]).toEqual(listener2);
});
test('Modify array copy after multiple adds', () => {
const ee = new EventEmitter();
ee.on('foo', listener);
const eeListenersCopy = ee.listeners('foo');
ee.on('foo', listener2);
const listeners = ee.listeners('foo');
expect(Array.isArray(listeners)).toBeTruthy();
expect(listeners).toHaveLength(2);
expect(listeners[0]).toEqual(listener);
expect(listeners[1]).toEqual(listener2);
expect(Array.isArray(eeListenersCopy)).toBeTruthy();
expect(eeListenersCopy).toHaveLength(1);
expect(eeListenersCopy[0]).toEqual(listener);
});
test('listeners and once', () => {
const ee = new EventEmitter();
ee.once('foo', listener);
const listeners = ee.listeners('foo');
expect(Array.isArray(listeners)).toBeTruthy();
expect(listeners).toHaveLength(1);
expect(listeners[0]).toEqual(listener);
});
test('listeners with conflicting types', () => {
const ee = new EventEmitter();
ee.on('foo', listener);
ee.once('foo', listener2);
const listeners = ee.listeners('foo');
expect(Array.isArray(listeners)).toBeTruthy();
expect(listeners).toHaveLength(2);
expect(listeners[0]).toEqual(listener);
expect(listeners[1]).toEqual(listener2);
});
test('EventEmitter with no members', () => {
const ee = new EventEmitter();
ee._events = undefined;
const listeners = ee.listeners('foo');
expect(Array.isArray(listeners)).toBeTruthy();
expect(listeners).toHaveLength(0);
});
test('listeners on prototype', () => {
const s = new TestStream();
const listeners = s.listeners('foo');
expect(Array.isArray(listeners)).toBeTruthy();
expect(listeners).toHaveLength(0);
});
test('raw listeners', () => {
const ee = new EventEmitter();
ee.on('foo', listener);
const wrappedListener = ee.rawListeners('foo');
expect(wrappedListener).toHaveLength(1);
expect(wrappedListener[0]).toEqual(listener);
ee.once('foo', listener);
const wrappedListeners = ee.rawListeners('foo');
expect(wrappedListeners).toHaveLength(2);
expect(wrappedListeners[0]).toEqual(listener);
expect(wrappedListeners[1].listener).toEqual(listener);
ee.emit('foo');
expect(wrappedListeners).toHaveLength(2);
expect(wrappedListeners[1].listener).toEqual(listener);
});
test('raw listeners order', () => {
const ee = new EventEmitter();
ee.once('foo', listener3);
ee.on('foo', listener4);
const rawListeners = ee.rawListeners('foo');
expect(rawListeners).toHaveLength(2);
expect(rawListeners[0]()).toEqual(0);
const rawListener = ee.rawListeners('foo');
expect(rawListener).toHaveLength(1);
expect(rawListener[0]()).toEqual(1);
});

View File

@ -0,0 +1,43 @@
// Copyright Joyent, Inc. and other Node contributors.
// Modifications copyright (c) by Microsoft Corporation
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH the SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
import { EventEmitter } from '../../../packages/playwright-core/lib/client/eventEmitter';
import { test, expect } from '@playwright/test';
test('emit maxListeners on e', () => {
const e = new EventEmitter();
e.on('maxListeners', () => {});
// Should not corrupt the 'maxListeners' queue.
e.setMaxListeners(42);
const throwsObjs = [NaN, -1, 'and even this'];
const maxError = /^The value of \"n\" is out of range. It must be a non-negative number./;
for (const obj of throwsObjs) {
expect(() => {
e.setMaxListeners(obj);
}).toThrow(maxError);
}
e.emit('maxListeners');
});

View File

@ -0,0 +1,40 @@
// Copyright Joyent, Inc. and other Node contributors.
// Modifications copyright (c) by Microsoft Corporation
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
import { EventEmitter } from '../../../packages/playwright-core/lib/client/eventEmitter';
import { test, expect } from '@playwright/test';
test('EventEmitter prototype test', () => {
const ee = new EventEmitter();
// eslint-disable-next-line no-proto
expect((ee as any).__proto__.constructor.name).toEqual('EventEmitter');
expect(ee.on).toEqual(ee.addListener); // Same method.
expect(ee.off).toEqual(ee.removeListener); // Same method.
Object.getOwnPropertyNames(ee).forEach(name => {
if (name === 'constructor' || name === 'on' || name === 'off')
return;
if (typeof ee[name] !== 'function')
return;
expect(ee[name].name).toEqual(name);
});
});

View File

@ -0,0 +1,93 @@
// Copyright Joyent, Inc. and other Node contributors.
// Modifications copyright (c) by Microsoft Corporation
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
import { test, expect } from '@playwright/test';
import { EventEmitter } from '../../../packages/playwright-core/lib/client/eventEmitter';
let callbacks_called = [];
const e = new EventEmitter();
const callback1 = () => {
callbacks_called.push('callback1');
e.on('foo', callback2);
e.on('foo', callback3);
e.removeListener('foo', callback1);
};
const callback2 = () => {
callbacks_called.push('callback2');
e.removeListener('foo', callback2);
};
const callback3 = () => {
callbacks_called.push('callback3');
e.removeListener('foo', callback3);
};
test('add and remove listeners', () => {
e.on('foo', callback1);
expect(e.listeners('foo')).toHaveLength(1);
e.emit('foo');
expect(e.listeners('foo')).toHaveLength(2);
expect(Array.isArray(callbacks_called)).toBeTruthy();
expect(callbacks_called).toHaveLength(1);
expect(callbacks_called[0]).toEqual('callback1');
e.emit('foo');
expect(e.listeners('foo')).toHaveLength(0);
expect(Array.isArray(callbacks_called)).toBeTruthy();
expect(callbacks_called).toHaveLength(3);
expect(callbacks_called[0]).toEqual('callback1');
expect(callbacks_called[1]).toEqual('callback2');
expect(callbacks_called[2]).toEqual('callback3');
e.emit('foo');
expect(e.listeners('foo')).toHaveLength(0);
expect(Array.isArray(callbacks_called)).toBeTruthy();
expect(callbacks_called).toHaveLength(3);
expect(callbacks_called[0]).toEqual('callback1');
expect(callbacks_called[1]).toEqual('callback2');
expect(callbacks_called[2]).toEqual('callback3');
e.on('foo', callback1);
e.on('foo', callback2);
expect(e.listeners('foo')).toHaveLength(2);
e.removeAllListeners('foo');
expect(e.listeners('foo')).toHaveLength(0);
});
test('removing callbacks in emit', () => {
// Verify that removing callbacks while in emit allows emits to propagate to
// all listeners
callbacks_called = [];
e.on('foo', callback2);
e.on('foo', callback3);
expect(e.listeners('foo')).toHaveLength(2);
e.emit('foo');
expect(Array.isArray(callbacks_called)).toBeTruthy();
expect(callbacks_called).toHaveLength(2);
expect(callbacks_called[0]).toEqual('callback2');
expect(callbacks_called[1]).toEqual('callback3');
expect(e.listeners('foo')).toHaveLength(0);
});

View File

@ -0,0 +1,62 @@
// Copyright Joyent, Inc. and other Node contributors.
// Modifications copyright (c) by Microsoft Corporation
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
import { EventEmitter } from '../../../packages/playwright-core/lib/client/eventEmitter';
import { test, expect } from '@playwright/test';
test('should work', () => {
const e = new EventEmitter();
const num_args_emitted = [];
e.on('numArgs', (...args) => {
const numArgs = args.length;
num_args_emitted.push(numArgs);
});
e.on('foo', function() {
num_args_emitted.push(arguments.length);
});
e.on('foo', function() {
num_args_emitted.push(arguments.length);
});
e.emit('numArgs');
e.emit('numArgs', null);
e.emit('numArgs', null, null);
e.emit('numArgs', null, null, null);
e.emit('numArgs', null, null, null, null);
e.emit('numArgs', null, null, null, null, null);
e.emit('foo', null, null, null, null);
expect(Array.isArray(num_args_emitted)).toBeTruthy();
expect(num_args_emitted).toHaveLength(8);
expect(num_args_emitted[0]).toEqual(0);
expect(num_args_emitted[1]).toEqual(1);
expect(num_args_emitted[2]).toEqual(2);
expect(num_args_emitted[3]).toEqual(3);
expect(num_args_emitted[4]).toEqual(4);
expect(num_args_emitted[5]).toEqual(5);
expect(num_args_emitted[6]).toEqual(4);
expect(num_args_emitted[6]).toEqual(4);
});

View File

@ -0,0 +1,84 @@
// Copyright Joyent, Inc. and other Node contributors.
// Modifications copyright (c) by Microsoft Corporation
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
import { test, expect } from '@playwright/test';
import { EventEmitter } from '../../../packages/playwright-core/lib/client/eventEmitter';
import * as common from './utils';
test('should work', () => {
const e = new EventEmitter();
e.once('hello', common.mustCall());
e.emit('hello', 'a', 'b');
e.emit('hello', 'a', 'b');
e.emit('hello', 'a', 'b');
e.emit('hello', 'a', 'b');
const remove = () => {
expect(false).toBe(true);
};
e.once('foo', remove);
e.removeListener('foo', remove);
e.emit('foo');
e.once('e', common.mustCall(() => {
e.emit('e');
}));
e.once('e', common.mustCall());
e.emit('e');
// Verify that the listener must be a function
expect(() => {
const ee = new EventEmitter();
ee.once('foo', null);
}).toThrow(TypeError);
});
test('once() has different code paths based on the number of arguments being emitted', () => {
// Verify that all of the cases are covered.
const maxArgs = 4;
for (let i = 0; i <= maxArgs; i++) {
const ee = new EventEmitter();
const args: any[] = ['foo'];
for (let j = 0; j < i; j++)
args.push(j);
ee.once('foo', common.mustCall((...params) => {
const restArgs = args.slice(1);
expect(Array.isArray(params)).toBeTruthy();
expect(params).toHaveLength(restArgs.length);
for (let index = 0; index < params.length; index++) {
const param = params[index];
expect(param).toEqual(restArgs[index]);
}
}));
EventEmitter.prototype.emit.apply(ee, args);
}
});

View File

@ -0,0 +1,52 @@
// Copyright Joyent, Inc. and other Node contributors.
// Modifications copyright (c) by Microsoft Corporation
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
import { EventEmitter } from '../../../packages/playwright-core/lib/client/eventEmitter';
import { test, expect } from '@playwright/test';
test('EventEmitter functionality', () => {
const myEE = new EventEmitter();
let m = 0;
// This one comes last.
myEE.on('foo', () => {
expect(m).toEqual(2);
});
// This one comes second.
myEE.prependListener('foo', () => {
expect(m++).toEqual(1);
});
// This one comes first.
myEE.prependOnceListener('foo', () => {
expect(m++).toEqual(0);
});
myEE.emit('foo');
});
test('Verify that the listener must be a function', () => {
expect(() => {
const ee = new EventEmitter();
ee.prependOnceListener('foo', null);
}).toThrow(TypeError);
});

View File

@ -0,0 +1,136 @@
// Copyright Joyent, Inc. and other Node contributors.
// Modifications copyright (c) by Microsoft Corporation
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
import { EventEmitter } from '../../../packages/playwright-core/lib/client/eventEmitter';
import { test, expect } from '@playwright/test';
import * as common from './utils';
let wrappers: any[];
const expectWrapper = expected => {
const entry: any = { expected };
wrappers.push(entry);
return name => {
entry.actual = entry.actual || [];
entry.actual.push(name);
};
};
test.beforeEach(() => {
wrappers = [];
});
test.afterEach(() => {
for (const wrapper of wrappers) {
const sortedActual = wrapper.actual.sort();
const sortedExpected = wrapper.expected.sort();
expect(sortedActual).toEqual(sortedExpected);
}
});
test('listeners', () => {
const ee = new EventEmitter();
const noop = () => { };
ee.on('foo', noop);
ee.on('bar', noop);
ee.on('baz', noop);
ee.on('baz', noop);
const fooListeners = ee.listeners('foo');
const barListeners = ee.listeners('bar');
const bazListeners = ee.listeners('baz');
ee.on('removeListener', expectWrapper(['bar', 'baz', 'baz']));
ee.removeAllListeners('bar');
ee.removeAllListeners('baz');
let listeners = ee.listeners('foo');
expect(Array.isArray(listeners)).toBeTruthy();
expect(listeners).toHaveLength(1);
expect(listeners[0]).toEqual(noop);
listeners = ee.listeners('bar');
expect(Array.isArray(listeners)).toBeTruthy();
expect(listeners).toHaveLength(0);
listeners = ee.listeners('baz');
expect(Array.isArray(listeners)).toBeTruthy();
expect(listeners).toHaveLength(0);
expect(fooListeners.length).toEqual(1);
expect(fooListeners[0]).toEqual(noop);
expect(barListeners.length).toEqual(1);
expect(barListeners[0]).toEqual(noop);
expect(bazListeners.length).toEqual(2);
expect(bazListeners[0]).toEqual(noop);
expect(bazListeners[1]).toEqual(noop);
expect(ee.listeners('bar')).not.toEqual(barListeners);
expect(ee.listeners('baz')).not.toEqual(bazListeners);
});
test('removeAllListeners removes all listeners', () => {
const ee = new EventEmitter();
ee.on('foo', () => { });
ee.on('bar', () => { });
ee.on('removeListener', expectWrapper(['foo', 'bar', 'removeListener']));
ee.on('removeListener', expectWrapper(['foo', 'bar']));
ee.removeAllListeners();
let listeners = ee.listeners('foo');
expect(Array.isArray(listeners)).toBeTruthy();
expect(listeners).toHaveLength(0);
listeners = ee.listeners('bar');
expect(Array.isArray(listeners)).toBeTruthy();
expect(listeners).toHaveLength(0);
});
test('removeAllListeners with no event type', () => {
const ee = new EventEmitter();
ee.on('removeListener', common.mustNotCall());
// Check for regression where removeAllListeners() throws when
// there exists a 'removeListener' listener, but there exists
// no listeners for the provided event type.
(ee as any).removeAllListeners(ee, 'foo');
});
test('listener count after removeAllListeners', () => {
const ee = new EventEmitter();
let expectLength = 2;
ee.on('removeListener', () => {
expect(expectLength--).toEqual(ee.listeners('baz').length);
});
ee.on('baz', () => { });
ee.on('baz', () => { });
ee.on('baz', () => { });
expect(ee.listeners('baz').length).toEqual(expectLength + 1);
ee.removeAllListeners('baz');
expect(ee.listeners('baz').length).toEqual(0);
});
test('removeAllListeners returns EventEmitter', () => {
const ee = new EventEmitter();
expect(ee).toEqual(ee.removeAllListeners());
});
test('removeAllListeners on undefined _events', () => {
const ee = new EventEmitter();
ee._events = undefined;
expect(ee).toEqual(ee.removeAllListeners());
});

View File

@ -0,0 +1,188 @@
// Copyright Joyent, Inc. and other Node contributors.
// Modifications copyright (c) by Microsoft Corporation
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
import { EventEmitter } from '../../../packages/playwright-core/lib/client/eventEmitter';
import { test, expect } from '@playwright/test';
import * as common from './utils';
const listener1 = () => {};
const listener2 = () => {};
test.beforeEach(() => common.beforeEach());
test.afterEach(() => common.afterEach());
test('First test', () => {
const ee = new EventEmitter();
ee.on('hello', listener1);
ee.on('removeListener', common.mustCall((name, cb) => {
expect(name).toEqual('hello');
expect(cb).toEqual(listener1);
}));
ee.removeListener('hello', listener1);
const listeners = ee.listeners('hello');
expect(Array.isArray(listeners)).toBeTruthy();
expect(listeners).toHaveLength(0);
});
test('Second test', () => {
const ee = new EventEmitter();
ee.on('hello', listener1);
ee.on('removeListener', common.mustNotCall());
ee.removeListener('hello', listener2);
const listeners = ee.listeners('hello');
expect(Array.isArray(listeners)).toBeTruthy();
expect(listeners.length).toEqual(1);
expect(listeners[0]).toEqual(listener1);
});
test('Third test', () => {
const ee = new EventEmitter();
ee.on('hello', listener1);
ee.on('hello', listener2);
let listeners;
ee.once('removeListener', common.mustCall((name, cb) => {
expect(name).toEqual('hello');
expect(cb).toEqual(listener1);
listeners = ee.listeners('hello');
expect(Array.isArray(listeners)).toBeTruthy();
expect(listeners.length).toEqual(1);
expect(listeners[0]).toEqual(listener2);
}));
ee.removeListener('hello', listener1);
listeners = ee.listeners('hello');
expect(Array.isArray(listeners)).toBeTruthy();
expect(listeners.length).toEqual(1);
expect(listeners[0]).toEqual(listener2);
ee.once('removeListener', common.mustCall((name, cb) => {
expect(name).toEqual('hello');
expect(cb).toEqual(listener2);
listeners = ee.listeners('hello');
expect(Array.isArray(listeners)).toBeTruthy();
expect(listeners.length).toEqual(0);
}));
ee.removeListener('hello', listener2);
listeners = ee.listeners('hello');
expect(Array.isArray(listeners)).toBeTruthy();
expect(listeners.length).toEqual(0);
});
test('Fourth test', () => {
const ee = new EventEmitter();
const remove1 = () => {
throw new Error('remove1 should not have been called');
};
const remove2 = () => {
throw new Error('remove2 should not have been called');
};
ee.on('removeListener', common.mustCall((name, cb) => {
if (cb !== remove1)
return;
ee.removeListener('quux', remove2);
ee.emit('quux');
}, 2));
ee.on('quux', remove1);
ee.on('quux', remove2);
ee.removeListener('quux', remove1);
});
test('Fifth test', () => {
const ee = new EventEmitter();
const listener3 = common.mustCall(() => {
ee.removeListener('hello', listener4);
}, 2);
const listener4 = common.mustCall();
ee.on('hello', listener3);
ee.on('hello', listener4);
// listener4 will still be called although it is removed by listener 3.
ee.emit('hello');
// This is so because the interal listener array at time of emit
// was [listener3,listener4]
// Interal listener array [listener3]
ee.emit('hello');
});
test('Sixth test', () => {
const ee = new EventEmitter();
ee.once('hello', listener1);
ee.on('removeListener', common.mustCall((eventName, listener) => {
expect(eventName).toEqual('hello');
expect(listener).toEqual(listener1);
}));
ee.emit('hello');
});
test('Seventh test', () => {
const ee = new EventEmitter();
expect(ee).toEqual(ee.removeListener('foo', () => {}));
});
// Verify that the removed listener must be a function
test('Eighth test', () => {
expect(() => {
const ee = new EventEmitter();
ee.removeListener('foo', null);
}).toThrow(/^The "listener" argument must be of type Function\. Received type object$/);
});
test('Ninth test', () => {
const ee = new EventEmitter();
const listener = () => {};
ee._events = undefined;
const e = ee.removeListener('foo', listener);
expect(e).toEqual(ee);
});
test('Tenth test', () => {
const ee = new EventEmitter();
ee.on('foo', listener1);
ee.on('foo', listener2);
let listeners = ee.listeners('foo');
expect(Array.isArray(listeners)).toBeTruthy();
expect(listeners.length).toEqual(2);
expect(listeners[0]).toEqual(listener1);
expect(listeners[1]).toEqual(listener2);
ee.removeListener('foo', listener1);
expect(ee._events.foo).toEqual(listener2);
ee.on('foo', listener1);
listeners = ee.listeners('foo');
expect(Array.isArray(listeners)).toBeTruthy();
expect(listeners.length).toEqual(2);
expect(listeners[0]).toEqual(listener2);
expect(listeners[1]).toEqual(listener1);
ee.removeListener('foo', listener1);
expect(ee._events.foo).toEqual(listener2);
});

View File

@ -0,0 +1,34 @@
// Copyright Joyent, Inc. and other Node contributors.
// Modifications copyright (c) by Microsoft Corporation
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
import { EventEmitter } from '../../../packages/playwright-core/lib/client/eventEmitter';
import { test, expect } from '@playwright/test';
test('set max listeners test', () => {
const e = new EventEmitter();
expect(!(e._events instanceof Object)).toBeTruthy();
expect(Object.keys(e._events)).toHaveLength(0);
e.setMaxListeners(5);
expect(Object.keys(e._events)).toHaveLength(0);
});

View File

@ -0,0 +1,46 @@
// Copyright Joyent, Inc. and other Node contributors.
// Modifications copyright (c) by Microsoft Corporation
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
import { test, expect } from '@playwright/test';
import { EventEmitter } from '../../../packages/playwright-core/lib/client/eventEmitter';
test('should support special event names', () => {
const ee = new EventEmitter();
const handler = () => {};
expect(ee.eventNames().length).toEqual(0);
expect(ee._events.hasOwnProperty).toEqual(undefined);
expect(ee._events.toString).toEqual(undefined);
ee.on('__defineGetter__', handler);
ee.on('toString', handler);
ee.on('__proto__', handler);
expect(ee.eventNames()[0]).toEqual('__defineGetter__');
expect(ee.eventNames()[1]).toEqual('toString');
expect(ee.listeners('__defineGetter__').length).toEqual(1);
expect(ee.listeners('__defineGetter__')[0]).toEqual(handler);
expect(ee.listeners('toString').length).toEqual(1);
expect(ee.listeners('toString')[0]).toEqual(handler);
});

View File

@ -0,0 +1,54 @@
// Copyright Joyent, Inc. and other Node contributors.
// Modifications copyright (c) by Microsoft Corporation
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
import { EventEmitter } from '../../../packages/playwright-core/lib/client/eventEmitter';
import { test, expect } from '@playwright/test';
class MyEE extends EventEmitter {
constructor(cb) {
super();
this.once(1, cb);
this.emit(1);
this.removeAllListeners();
}
}
test('myee instance', () => {
const myee = new MyEE(() => {});
expect(myee._events).not.toBeInstanceOf(Object);
expect(Object.keys(myee._events).length).toBe(0);
});
class MyEE2 {
ee: EventEmitter;
constructor() {
this.ee = new EventEmitter();
}
}
test('MyEE2 instance', () => {
const ee1 = new MyEE2();
const ee2 = new MyEE2();
ee1.ee.on('x', function() {});
expect(ee2.ee.listenerCount('x')).toBe(0);
});

View File

@ -0,0 +1,46 @@
// Copyright Joyent, Inc. and other Node contributors.
// Modifications copyright (c) by Microsoft Corporation
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
import { EventEmitter } from '../../../packages/playwright-core/lib/client/eventEmitter';
import { test, expect } from '@playwright/test';
test('should support symbols', () => {
const ee = new EventEmitter();
const foo = Symbol('foo');
const listener = () => {};
ee.on(foo, listener);
expect(ee.listeners(foo).length).toEqual(1);
expect(ee.listeners(foo)[0]).toEqual(listener);
ee.emit(foo);
ee.removeAllListeners();
expect(ee.listeners(foo).length).toEqual(0);
ee.on(foo, listener);
expect(ee.listeners(foo).length).toEqual(1);
expect(ee.listeners(foo)[0]).toEqual(listener);
ee.removeListener(foo, listener);
expect(ee.listeners(foo).length).toEqual(0);
});

View File

@ -0,0 +1,46 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
import { expect } from '@playwright/test';
export const mustNotCall = (msg?: string) => {
return function mustNotCall() {
expect(false, msg || 'function should not have been called').toBeTruthy();
};
};
let count;
export const beforeEach = () => {
count = 0;
};
export const afterEach = () => {
expect(count).toBe(0);
};
export const mustCall = (fn?: Function, exact?: number) => {
count += exact || 1;
return (...args: any[]) => {
fn?.(...args);
--count;
};
};