From 36b1606ed750719b2ea5c5af68393d8f70015517 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Fri, 11 May 2018 13:39:13 +0200 Subject: [PATCH 01/16] Update to Node 10 API. --- events.js | 354 ++++++++++++++------------------------ package.json | 2 +- tests/add-listeners.js | 2 +- tests/max-listeners.js | 4 +- tests/once.js | 2 +- tests/remove-listeners.js | 2 +- 6 files changed, 136 insertions(+), 230 deletions(-) diff --git a/events.js b/events.js index a17bcba..6a0a335 100644 --- a/events.js +++ b/events.js @@ -19,17 +19,20 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -var objectCreate = Object.create || objectCreatePolyfill -var objectKeys = Object.keys || objectKeysPolyfill -var bind = Function.prototype.bind || functionBindPolyfill +'use strict'; -function EventEmitter() { - if (!this._events || !Object.prototype.hasOwnProperty.call(this, '_events')) { - this._events = objectCreate(null); - this._eventsCount = 0; - } +function ReflectApply(target, receiver, args) { + return Function.prototype.apply.call(target, receiver, args); +} +function ReflectOwnKeys(target) { + return Object.getOwnPropertyNames(target); +} +function ProcessEmitWarning(warning) { + if (console && console.warn) console.warn(warning); +} - this._maxListeners = this._maxListeners || undefined; +function EventEmitter() { + EventEmitter.init.call(this); } module.exports = EventEmitter; @@ -37,41 +40,43 @@ module.exports = EventEmitter; EventEmitter.EventEmitter = EventEmitter; EventEmitter.prototype._events = undefined; +EventEmitter.prototype._eventsCount = 0; EventEmitter.prototype._maxListeners = undefined; // By default EventEmitters will print a warning if more than 10 listeners are // added to it. This is a useful default which helps finding memory leaks. var defaultMaxListeners = 10; -var hasDefineProperty; -try { - var o = {}; - if (Object.defineProperty) Object.defineProperty(o, 'x', { value: 0 }); - hasDefineProperty = o.x === 0; -} catch (err) { hasDefineProperty = false } -if (hasDefineProperty) { - Object.defineProperty(EventEmitter, 'defaultMaxListeners', { - enumerable: true, - get: function() { - return defaultMaxListeners; - }, - set: function(arg) { - // check whether the input is a positive number (whose value is zero or - // greater and not a NaN). - if (typeof arg !== 'number' || arg < 0 || arg !== arg) - throw new TypeError('"defaultMaxListeners" must be a positive number'); - defaultMaxListeners = arg; +Object.defineProperty(EventEmitter, 'defaultMaxListeners', { + enumerable: true, + get: function() { + return defaultMaxListeners; + }, + set: function(arg) { + if (typeof arg !== 'number' || arg < 0 || Number.isNaN(arg)) { + throw new RangeError('The value of "defaultMaxListeners" is out of range. It must be a non-negative number. Received ' + arg + '.'); } - }); -} else { - EventEmitter.defaultMaxListeners = defaultMaxListeners; -} + defaultMaxListeners = arg; + } +}); + +EventEmitter.init = function() { + + if (this._events === undefined || + this._events === Object.getPrototypeOf(this)._events) { + this._events = Object.create(null); + this._eventsCount = 0; + } + + this._maxListeners = this._maxListeners || undefined; +}; // Obviously not all Emitters should be limited to 10. This function allows // that to be increased. Set to zero for unlimited. EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) { - if (typeof n !== 'number' || n < 0 || isNaN(n)) - throw new TypeError('"n" argument must be a positive number'); + 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; }; @@ -86,115 +91,45 @@ EventEmitter.prototype.getMaxListeners = function getMaxListeners() { return $getMaxListeners(this); }; -// These standalone emit* functions are used to optimize calling of event -// handlers for fast cases because emit() itself often has a variable number of -// arguments and can be deoptimized because of that. These functions always have -// the same number of arguments and thus do not get deoptimized, so the code -// inside them can execute faster. -function emitNone(handler, isFn, self) { - if (isFn) - handler.call(self); - else { - var len = handler.length; - var listeners = arrayClone(handler, len); - for (var i = 0; i < len; ++i) - listeners[i].call(self); - } -} -function emitOne(handler, isFn, self, arg1) { - if (isFn) - handler.call(self, arg1); - else { - var len = handler.length; - var listeners = arrayClone(handler, len); - for (var i = 0; i < len; ++i) - listeners[i].call(self, arg1); - } -} -function emitTwo(handler, isFn, self, arg1, arg2) { - if (isFn) - handler.call(self, arg1, arg2); - else { - var len = handler.length; - var listeners = arrayClone(handler, len); - for (var i = 0; i < len; ++i) - listeners[i].call(self, arg1, arg2); - } -} -function emitThree(handler, isFn, self, arg1, arg2, arg3) { - if (isFn) - handler.call(self, arg1, arg2, arg3); - else { - var len = handler.length; - var listeners = arrayClone(handler, len); - for (var i = 0; i < len; ++i) - listeners[i].call(self, arg1, arg2, arg3); - } -} - -function emitMany(handler, isFn, self, args) { - if (isFn) - handler.apply(self, args); - else { - var len = handler.length; - var listeners = arrayClone(handler, len); - for (var i = 0; i < len; ++i) - listeners[i].apply(self, args); - } -} - EventEmitter.prototype.emit = function emit(type) { - var er, handler, len, args, i, events; + var args = []; + for (var i = 1; i < arguments.length; i++) args.push(arguments[i]); var doError = (type === 'error'); - events = this._events; - if (events) - doError = (doError && events.error == null); + var events = this._events; + if (events !== undefined) + doError = (doError && events.error === undefined); else if (!doError) return false; // If there is no 'error' event listener then throw. if (doError) { - if (arguments.length > 1) - er = arguments[1]; + var er; + if (args.length > 0) + er = args[0]; if (er instanceof Error) { + // Note: The comments on the `throw` lines are intentional, they show + // up in Node's output if this results in an unhandled exception. throw er; // Unhandled 'error' event - } else { - // At least give some kind of context to the user - var err = new Error('Unhandled "error" event. (' + er + ')'); - err.context = er; - throw err; } - return false; + // At least give some kind of context to the user + var err = new Error('Unhandled error.' + (er ? ' (' + er.message + ')' : '')); + err.context = er; + throw err; // Unhandled 'error' event } - handler = events[type]; + var handler = events[type]; - if (!handler) + if (handler === undefined) return false; - var isFn = typeof handler === 'function'; - len = arguments.length; - switch (len) { - // fast cases - case 1: - emitNone(handler, isFn, this); - break; - case 2: - emitOne(handler, isFn, this, arguments[1]); - break; - case 3: - emitTwo(handler, isFn, this, arguments[1], arguments[2]); - break; - case 4: - emitThree(handler, isFn, this, arguments[1], arguments[2], arguments[3]); - break; - // slower - default: - args = new Array(len - 1); - for (i = 1; i < len; i++) - args[i - 1] = arguments[i]; - emitMany(handler, isFn, this, args); + if (typeof handler === 'function') { + ReflectApply(handler, this, args); + } else { + var len = handler.length; + var listeners = arrayClone(handler, len); + for (var i = 0; i < len; ++i) + ReflectApply(listeners[i], this, args); } return true; @@ -205,19 +140,20 @@ function _addListener(target, type, listener, prepend) { var events; var existing; - if (typeof listener !== 'function') - throw new TypeError('"listener" argument must be a function'); + if (typeof listener !== 'function') { + throw new TypeError('The "listener" argument must be of type Function. Received type ' + typeof listener); + } events = target._events; - if (!events) { - events = target._events = objectCreate(null); + if (events === undefined) { + events = target._events = Object.create(null); target._eventsCount = 0; } else { // To avoid recursion in the case that type === "newListener"! Before // adding it to the listeners, first emit "newListener". - if (events.newListener) { + if (events.newListener !== undefined) { target.emit('newListener', type, - listener.listener ? listener.listener : listener); + listener.listener ? listener.listener : listener); // Re-assign `events` because a newListener handler could have caused the // this._events to be assigned to a new object @@ -226,7 +162,7 @@ function _addListener(target, type, listener, prepend) { existing = events[type]; } - if (!existing) { + if (existing === undefined) { // Optimize the case of one listener. Don't need the extra array object. existing = events[type] = listener; ++target._eventsCount; @@ -234,33 +170,29 @@ function _addListener(target, type, listener, prepend) { if (typeof existing === 'function') { // Adding the second element, need to change to array. existing = events[type] = - prepend ? [listener, existing] : [existing, listener]; - } else { + prepend ? [listener, existing] : [existing, listener]; // If we've already got an array, just append. - if (prepend) { - existing.unshift(listener); - } else { - existing.push(listener); - } + } else if (prepend) { + existing.unshift(listener); + } else { + existing.push(listener); } // Check for listener leak - if (!existing.warned) { - m = $getMaxListeners(target); - if (m && m > 0 && existing.length > m) { - existing.warned = true; - var w = new Error('Possible EventEmitter memory leak detected. ' + - existing.length + ' "' + String(type) + '" listeners ' + - 'added. Use emitter.setMaxListeners() to ' + - 'increase limit.'); - w.name = 'MaxListenersExceededWarning'; - w.emitter = target; - w.type = type; - w.count = existing.length; - if (typeof console === 'object' && console.warn) { - console.warn('%s: %s', w.name, w.message); - } - } + m = $getMaxListeners(target); + if (m > 0 && existing.length > m && !existing.warned) { + existing.warned = true; + // No error code for this since it is a Warning + // eslint-disable-next-line no-restricted-syntax + var w = new Error('Possible EventEmitter memory leak detected. ' + + existing.length + ' ' + String(type) + ' listeners ' + + 'added. Use emitter.setMaxListeners() to ' + + 'increase limit'); + w.name = 'MaxListenersExceededWarning'; + w.emitter = target; + w.type = type; + w.count = existing.length; + ProcessEmitWarning(w); } } @@ -279,47 +211,36 @@ EventEmitter.prototype.prependListener = }; function onceWrapper() { + var args = []; + for (var i = 0; i < arguments.length; i++) args.push(arguments[i]); if (!this.fired) { this.target.removeListener(this.type, this.wrapFn); this.fired = true; - switch (arguments.length) { - case 0: - return this.listener.call(this.target); - case 1: - return this.listener.call(this.target, arguments[0]); - case 2: - return this.listener.call(this.target, arguments[0], arguments[1]); - case 3: - return this.listener.call(this.target, arguments[0], arguments[1], - arguments[2]); - default: - var args = new Array(arguments.length); - for (var i = 0; i < args.length; ++i) - args[i] = arguments[i]; - this.listener.apply(this.target, args); - } + ReflectApply(this.listener, this.target, args); } } function _onceWrap(target, type, listener) { var state = { fired: false, wrapFn: undefined, target: target, type: type, listener: listener }; - var wrapped = bind.call(onceWrapper, state); + var wrapped = onceWrapper.bind(state); wrapped.listener = listener; state.wrapFn = wrapped; return wrapped; } EventEmitter.prototype.once = function once(type, listener) { - if (typeof listener !== 'function') - throw new TypeError('"listener" argument must be a function'); + if (typeof listener !== 'function') { + throw new TypeError('The "listener" argument must be of type Function. Received type ' + typeof listener); + } this.on(type, _onceWrap(this, type, listener)); return this; }; EventEmitter.prototype.prependOnceListener = function prependOnceListener(type, listener) { - if (typeof listener !== 'function') - throw new TypeError('"listener" argument must be a function'); + if (typeof listener !== 'function') { + throw new TypeError('The "listener" argument must be of type Function. Received type ' + typeof listener); + } this.prependListener(type, _onceWrap(this, type, listener)); return this; }; @@ -329,20 +250,21 @@ EventEmitter.prototype.removeListener = function removeListener(type, listener) { var list, events, position, i, originalListener; - if (typeof listener !== 'function') - throw new TypeError('"listener" argument must be a function'); + if (typeof listener !== 'function') { + throw new TypeError('The "listener" argument must be of type Function. Received type ' + typeof listener); + } events = this._events; - if (!events) + if (events === undefined) return this; list = events[type]; - if (!list) + if (list === undefined) return this; if (list === listener || list.listener === listener) { if (--this._eventsCount === 0) - this._events = objectCreate(null); + this._events = Object.create(null); else { delete events[type]; if (events.removeListener) @@ -364,35 +286,38 @@ EventEmitter.prototype.removeListener = if (position === 0) list.shift(); - else + else { spliceOne(list, position); + } if (list.length === 1) events[type] = list[0]; - if (events.removeListener) + if (events.removeListener !== undefined) this.emit('removeListener', type, originalListener || listener); } return this; }; +EventEmitter.prototype.off = EventEmitter.prototype.removeListener; + EventEmitter.prototype.removeAllListeners = function removeAllListeners(type) { var listeners, events, i; events = this._events; - if (!events) + if (events === undefined) return this; // not listening for removeListener, no need to emit - if (!events.removeListener) { + if (events.removeListener === undefined) { if (arguments.length === 0) { - this._events = objectCreate(null); + this._events = Object.create(null); this._eventsCount = 0; - } else if (events[type]) { + } else if (events[type] !== undefined) { if (--this._eventsCount === 0) - this._events = objectCreate(null); + this._events = Object.create(null); else delete events[type]; } @@ -401,7 +326,7 @@ EventEmitter.prototype.removeAllListeners = // emit removeListener for all listeners on all events if (arguments.length === 0) { - var keys = objectKeys(events); + var keys = Object.keys(events); var key; for (i = 0; i < keys.length; ++i) { key = keys[i]; @@ -409,7 +334,7 @@ EventEmitter.prototype.removeAllListeners = this.removeAllListeners(key); } this.removeAllListeners('removeListener'); - this._events = objectCreate(null); + this._events = Object.create(null); this._eventsCount = 0; return this; } @@ -418,7 +343,7 @@ EventEmitter.prototype.removeAllListeners = if (typeof listeners === 'function') { this.removeListener(type, listeners); - } else if (listeners) { + } else if (listeners !== undefined) { // LIFO order for (i = listeners.length - 1; i >= 0; i--) { this.removeListener(type, listeners[i]); @@ -431,17 +356,18 @@ EventEmitter.prototype.removeAllListeners = function _listeners(target, type, unwrap) { var events = target._events; - if (!events) + if (events === undefined) return []; var evlistener = events[type]; - if (!evlistener) + if (evlistener === undefined) return []; if (typeof evlistener === 'function') return unwrap ? [evlistener.listener || evlistener] : [evlistener]; - return unwrap ? unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length); + return unwrap ? + unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length); } EventEmitter.prototype.listeners = function listeners(type) { @@ -464,12 +390,12 @@ EventEmitter.prototype.listenerCount = listenerCount; function listenerCount(type) { var events = this._events; - if (events) { + if (events !== undefined) { var evlistener = events[type]; if (typeof evlistener === 'function') { return 1; - } else if (evlistener) { + } else if (evlistener !== undefined) { return evlistener.length; } } @@ -478,16 +404,9 @@ function listenerCount(type) { } EventEmitter.prototype.eventNames = function eventNames() { - return this._eventsCount > 0 ? Reflect.ownKeys(this._events) : []; + return this._eventsCount > 0 ? ReflectOwnKeys(this._events) : []; }; -// About 1.5x faster than the two-arg version of Array#splice(). -function spliceOne(list, index) { - for (var i = index, k = i + 1, n = list.length; k < n; i += 1, k += 1) - list[i] = list[k]; - list.pop(); -} - function arrayClone(arr, n) { var copy = new Array(n); for (var i = 0; i < n; ++i) @@ -495,6 +414,12 @@ function arrayClone(arr, n) { return copy; } +function spliceOne(list, index) { + for (; index + 1 < list.length; index++) + list[index] = list[index + 1]; + list.pop(); +} + function unwrapListeners(arr) { var ret = new Array(arr.length); for (var i = 0; i < ret.length; ++i) { @@ -502,22 +427,3 @@ function unwrapListeners(arr) { } return ret; } - -function objectCreatePolyfill(proto) { - var F = function() {}; - F.prototype = proto; - return new F; -} -function objectKeysPolyfill(obj) { - var keys = []; - for (var k in obj) if (Object.prototype.hasOwnProperty.call(obj, k)) { - keys.push(k); - } - return k; -} -function functionBindPolyfill(context) { - var fn = this; - return function () { - return fn.apply(context, arguments); - }; -} diff --git a/package.json b/package.json index 8091a68..2cbc603 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ }, "main": "./events.js", "engines": { - "node": ">=0.4.x" + "node": ">=0.8.x" }, "devDependencies": { "isarray": "^2.0.2", diff --git a/tests/add-listeners.js b/tests/add-listeners.js index 6682862..9b57827 100644 --- a/tests/add-listeners.js +++ b/tests/add-listeners.js @@ -108,4 +108,4 @@ assert.throws(function() { var ee = new EventEmitter(); ee.on('foo', null); -}, /^TypeError: "listener" argument must be a function$/); +}, /^TypeError: The "listener" argument must be of type Function. Received type object$/); diff --git a/tests/max-listeners.js b/tests/max-listeners.js index 162fb03..0b43953 100644 --- a/tests/max-listeners.js +++ b/tests/max-listeners.js @@ -33,8 +33,8 @@ e.on('maxListeners', common.mustCall()); e.setMaxListeners(42); var throwsObjs = [NaN, -1, 'and even this']; -var maxError = /^TypeError: "n" argument must be a positive number$/; -var defError = /^TypeError: "defaultMaxListeners" must be a positive number$/; +var maxError = /^RangeError: The value of "n" is out of range\. It must be a non-negative number\./; +var defError = /^RangeError: The value of "defaultMaxListeners" is out of range\. It must be a non-negative number\./; for (var i = 0; i < throwsObjs.length; i++) { var obj = throwsObjs[i]; diff --git a/tests/once.js b/tests/once.js index 44c0bb0..4b36c05 100644 --- a/tests/once.js +++ b/tests/once.js @@ -53,7 +53,7 @@ assert.throws(function() { var ee = new EventEmitter(); ee.once('foo', null); -}, /^TypeError: "listener" argument must be a function$/); +}, /^TypeError: The "listener" argument must be of type Function. Received type object$/); { // once() has different code paths based on the number of arguments being diff --git a/tests/remove-listeners.js b/tests/remove-listeners.js index 6cccd96..18e4d16 100644 --- a/tests/remove-listeners.js +++ b/tests/remove-listeners.js @@ -176,7 +176,7 @@ assert.throws(function() { var ee = new EventEmitter(); ee.removeListener('foo', null); -}, /^TypeError: "listener" argument must be a function$/); +}, /^TypeError: The "listener" argument must be of type Function\. Received type object$/); { var ee = new EventEmitter(); From b4c67d9a459a8c33d3de265e3fef2e9c0197cd99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Fri, 11 May 2018 13:52:17 +0200 Subject: [PATCH 02/16] Add new tests from Node 10. --- package.json | 1 - tests/errors.js | 13 +++++++++ tests/index.js | 4 +++ tests/listeners-side-effects.js | 3 +-- tests/method-names.js | 35 +++++++++++++++++++++++++ tests/prepend.js | 31 ++++++++++++++++++++++ tests/set-max-listeners-side-effects.js | 5 ++-- tests/subclass.js | 5 ++-- tests/symbols.js | 25 ++++++++++++++++++ 9 files changed, 113 insertions(+), 9 deletions(-) create mode 100644 tests/errors.js create mode 100644 tests/method-names.js create mode 100644 tests/prepend.js create mode 100644 tests/symbols.js diff --git a/package.json b/package.json index 2cbc603..fb7d303 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,6 @@ "devDependencies": { "isarray": "^2.0.2", "mocha": "^3.5.3", - "object-keys": "^1.0.11", "zuul": "^3.11.1" }, "scripts": { diff --git a/tests/errors.js b/tests/errors.js new file mode 100644 index 0000000..a23df43 --- /dev/null +++ b/tests/errors.js @@ -0,0 +1,13 @@ +'use strict'; +var assert = require('assert'); +var EventEmitter = require('../'); + +var EE = new EventEmitter(); + +assert.throws(function () { + EE.emit('error', 'Accepts a string'); +}, 'Error: Unhandled error. (Accepts a string)'); + +assert.throws(function () { + EE.emit('error', { message: 'Error!' }); +}, 'Unhandled error. ([object Object])'); diff --git a/tests/index.js b/tests/index.js index f71bc98..ec6bd2d 100644 --- a/tests/index.js +++ b/tests/index.js @@ -12,14 +12,18 @@ var require = function(file) { require('./add-listeners.js'); require('./check-listener-leaks.js'); +require('./errors.js'); require('./listener-count.js'); require('./listeners-side-effects.js'); require('./listeners.js'); require('./max-listeners.js'); +require('./method-names.js'); require('./modify-in-emit.js'); require('./num-args.js'); require('./once.js'); +require('./prepend.js'); require('./set-max-listeners-side-effects.js'); require('./subclass.js'); +if (typeof Symbol === 'function') require('./symbols.js'); require('./remove-all-listeners.js'); require('./remove-listeners.js'); diff --git a/tests/listeners-side-effects.js b/tests/listeners-side-effects.js index fe223e3..180f833 100644 --- a/tests/listeners-side-effects.js +++ b/tests/listeners-side-effects.js @@ -23,7 +23,6 @@ require('./common'); var assert = require('assert'); var EventEmitter = require('../').EventEmitter; -var objectKeys = require('object-keys'); var e = new EventEmitter(); var fl; // foo listeners @@ -32,7 +31,7 @@ fl = e.listeners('foo'); assert.ok(Array.isArray(fl)); assert.strictEqual(fl.length, 0); if (Object.create) assert.ok(!(e._events instanceof Object)); -assert.strictEqual(objectKeys(e._events).length, 0); +assert.strictEqual(Object.keys(e._events).length, 0); e.on('foo', assert.fail); fl = e.listeners('foo'); diff --git a/tests/method-names.js b/tests/method-names.js new file mode 100644 index 0000000..364a161 --- /dev/null +++ b/tests/method-names.js @@ -0,0 +1,35 @@ +// 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. + +'use strict'; +require('./common'); +var assert = require('assert'); +var events = require('../'); + +var E = events.EventEmitter.prototype; +assert.strictEqual(E.constructor.name, 'EventEmitter'); +assert.strictEqual(E.on, E.addListener); // Same method. +assert.strictEqual(E.off, E.removeListener); // Same method. +Object.getOwnPropertyNames(E).forEach(function(name) { + if (name === 'constructor' || name === 'on' || name === 'off') return; + if (typeof E[name] !== 'function') return; + assert.strictEqual(E[name].name, name); +}); diff --git a/tests/prepend.js b/tests/prepend.js new file mode 100644 index 0000000..79afde0 --- /dev/null +++ b/tests/prepend.js @@ -0,0 +1,31 @@ +'use strict'; + +var common = require('./common'); +var EventEmitter = require('../'); +var assert = require('assert'); + +var myEE = new EventEmitter(); +var m = 0; +// This one comes last. +myEE.on('foo', common.mustCall(function () { + assert.strictEqual(m, 2); +})); + +// This one comes second. +myEE.prependListener('foo', common.mustCall(function () { + assert.strictEqual(m++, 1); +})); + +// This one comes first. +myEE.prependOnceListener('foo', + common.mustCall(function () { + assert.strictEqual(m++, 0); + })); + +myEE.emit('foo'); + +// Verify that the listener must be a function +assert.throws(function () { + var ee = new EventEmitter(); + ee.prependOnceListener('foo', null); +}, 'TypeError: The "listener" argument must be of type Function. Received type object'); diff --git a/tests/set-max-listeners-side-effects.js b/tests/set-max-listeners-side-effects.js index 99471db..13dbb67 100644 --- a/tests/set-max-listeners-side-effects.js +++ b/tests/set-max-listeners-side-effects.js @@ -22,11 +22,10 @@ require('./common'); var assert = require('assert'); var events = require('../'); -var objectKeys = require('object-keys'); var e = new events.EventEmitter(); if (Object.create) assert.ok(!(e._events instanceof Object)); -assert.strictEqual(objectKeys(e._events).length, 0); +assert.strictEqual(Object.keys(e._events).length, 0); e.setMaxListeners(5); -assert.strictEqual(objectKeys(e._events).length, 0); +assert.strictEqual(Object.keys(e._events).length, 0); diff --git a/tests/subclass.js b/tests/subclass.js index 6c2ae64..a727cc8 100644 --- a/tests/subclass.js +++ b/tests/subclass.js @@ -23,7 +23,6 @@ var common = require('./common'); var assert = require('assert'); var EventEmitter = require('../').EventEmitter; var util = require('util'); -var objectKeys = require('object-keys'); var after_checks = []; after(function() { @@ -54,8 +53,8 @@ assert.throws(function() { }, /blerg/); after_checks.push(function() { - if (Object.create) assert.ok(!(myee._events instanceof Object)); - assert.strictEqual(objectKeys(myee._events).length, 0); + assert.ok(!(myee._events instanceof Object)); + assert.strictEqual(Object.keys(myee._events).length, 0); }); diff --git a/tests/symbols.js b/tests/symbols.js new file mode 100644 index 0000000..0721f0e --- /dev/null +++ b/tests/symbols.js @@ -0,0 +1,25 @@ +'use strict'; + +var common = require('./common'); +var EventEmitter = require('../'); +var assert = require('assert'); + +var ee = new EventEmitter(); +var foo = Symbol('foo'); +var listener = common.mustCall(); + +ee.on(foo, listener); +assert.strictEqual(ee.listeners(foo).length, 1); +assert.strictEqual(ee.listeners(foo)[0], listener); + +ee.emit(foo); + +ee.removeAllListeners(); +assert.strictEqual(ee.listeners(foo).length, 0); + +ee.on(foo, listener); +assert.strictEqual(ee.listeners(foo).length, 1); +assert.strictEqual(ee.listeners(foo)[0], listener); + +ee.removeListener(foo, listener); +assert.strictEqual(ee.listeners(foo).length, 0); From e671a1829d990a4417eec0335fb451188265d528 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Fri, 11 May 2018 13:53:40 +0200 Subject: [PATCH 03/16] Drop IE8 and old Safari --- .zuul.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.zuul.yml b/.zuul.yml index 216b335..46f726e 100644 --- a/.zuul.yml +++ b/.zuul.yml @@ -6,8 +6,8 @@ browsers: - name: firefox version: latest - name: safari - version: 7..latest + version: 9..latest - name: iphone version: latest - name: ie - version: 8..latest + version: 9..latest From a795d6054041f053756a475223833723b7fedfb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Fri, 11 May 2018 13:57:56 +0200 Subject: [PATCH 04/16] Run tests in latest node. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 54d7e04..5c0b18c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ sudo: false language: node_js node_js: - - '0.10' + - stable script: - npm test - if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then npm run test:browsers; fi From e3df7ce55d92299e054a05e6a8cd44ae10aa4af7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Sat, 17 Feb 2018 14:49:53 +0100 Subject: [PATCH 05/16] Use tape So we can switch to airtap later! --- .zuul.yml | 2 +- package.json | 4 ++-- tests/common.js | 3 ++- tests/index.js | 6 ++++-- tests/num-args.js | 29 ++++++++++------------------- tests/remove-all-listeners.js | 10 ++-------- tests/subclass.js | 10 ++-------- 7 files changed, 23 insertions(+), 41 deletions(-) diff --git a/.zuul.yml b/.zuul.yml index 46f726e..f8d0b51 100644 --- a/.zuul.yml +++ b/.zuul.yml @@ -1,4 +1,4 @@ -ui: mocha-qunit +ui: tape concurrency: 1 browsers: - name: chrome diff --git a/package.json b/package.json index fb7d303..626808f 100644 --- a/package.json +++ b/package.json @@ -24,11 +24,11 @@ }, "devDependencies": { "isarray": "^2.0.2", - "mocha": "^3.5.3", + "tape": "^4.8.0", "zuul": "^3.11.1" }, "scripts": { - "test": "mocha --ui qunit -- tests/index.js", + "test": "node tests/index.js", "test:browsers": "zuul -- tests/index.js" }, "license": "MIT" diff --git a/tests/common.js b/tests/common.js index cdd5aea..49569b0 100644 --- a/tests/common.js +++ b/tests/common.js @@ -19,6 +19,7 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. +var test = require('tape'); var assert = require('assert'); var noop = function() {}; @@ -77,7 +78,7 @@ function _mustCallInner(fn, criteria, field) { context[field] = criteria; // add the exit listener only once to avoid listener leak warnings - if (mustCallChecks.length === 0) after(function() { runCallChecks(0); }); + if (mustCallChecks.length === 0) test.onFinish(function() { runCallChecks(0); }); mustCallChecks.push(context); diff --git a/tests/index.js b/tests/index.js index ec6bd2d..29588d2 100644 --- a/tests/index.js +++ b/tests/index.js @@ -1,3 +1,4 @@ +var test = require('tape'); require('./legacy-compat'); @@ -5,8 +6,9 @@ require('./legacy-compat'); // and also have browserify be able to statically analyze this file var orig_require = require; var require = function(file) { - test(file, function() { - orig_require(file); + test(file, function(t) { + try { orig_require(file); } catch (err) { t.fail(err); } + t.end(); }); }; diff --git a/tests/num-args.js b/tests/num-args.js index c5a085b..c9b0deb 100644 --- a/tests/num-args.js +++ b/tests/num-args.js @@ -23,13 +23,6 @@ require('./common'); var assert = require('assert'); var events = require('../'); -var after_checks = []; -after(function() { - for (var i = 0 ; i < after_checks.length ; ++i) { - after_checks[i](); - } -}); - var e = new events.EventEmitter(); var num_args_emitted = []; @@ -55,15 +48,13 @@ e.emit('numArgs', null, null, null, null, null); e.emit('foo', null, null, null, null); -after_checks.push(function() { - assert.ok(Array.isArray(num_args_emitted)); - assert.strictEqual(num_args_emitted.length, 8); - assert.strictEqual(num_args_emitted[0], 0); - assert.strictEqual(num_args_emitted[1], 1); - assert.strictEqual(num_args_emitted[2], 2); - assert.strictEqual(num_args_emitted[3], 3); - assert.strictEqual(num_args_emitted[4], 4); - assert.strictEqual(num_args_emitted[5], 5); - assert.strictEqual(num_args_emitted[6], 4); - assert.strictEqual(num_args_emitted[6], 4); -}); +assert.ok(Array.isArray(num_args_emitted)); +assert.strictEqual(num_args_emitted.length, 8); +assert.strictEqual(num_args_emitted[0], 0); +assert.strictEqual(num_args_emitted[1], 1); +assert.strictEqual(num_args_emitted[2], 2); +assert.strictEqual(num_args_emitted[3], 3); +assert.strictEqual(num_args_emitted[4], 4); +assert.strictEqual(num_args_emitted[5], 5); +assert.strictEqual(num_args_emitted[6], 4); +assert.strictEqual(num_args_emitted[6], 4); diff --git a/tests/remove-all-listeners.js b/tests/remove-all-listeners.js index df29998..622941c 100644 --- a/tests/remove-all-listeners.js +++ b/tests/remove-all-listeners.js @@ -22,17 +22,11 @@ var common = require('./common'); var assert = require('assert'); var events = require('../'); - -var after_checks = []; -after(function() { - for (var i = 0 ; i < after_checks.length ; ++i) { - after_checks[i](); - } -}); +var test = require('tape'); function expect(expected) { var actual = []; - after_checks.push(function() { + test.onFinish(function() { var sortedActual = actual.sort(); var sortedExpected = expected.sort(); assert.strictEqual(sortedActual.length, sortedExpected.length); diff --git a/tests/subclass.js b/tests/subclass.js index a727cc8..bd033ff 100644 --- a/tests/subclass.js +++ b/tests/subclass.js @@ -20,17 +20,11 @@ // USE OR OTHER DEALINGS IN THE SOFTWARE. var common = require('./common'); +var test = require('tape'); var assert = require('assert'); var EventEmitter = require('../').EventEmitter; var util = require('util'); -var after_checks = []; -after(function() { - for (var i = 0 ; i < after_checks.length ; ++i) { - after_checks[i](); - } -}); - util.inherits(MyEE, EventEmitter); function MyEE(cb) { @@ -52,7 +46,7 @@ assert.throws(function() { new ErrorEE(); }, /blerg/); -after_checks.push(function() { +test.onFinish(function() { assert.ok(!(myee._events instanceof Object)); assert.strictEqual(Object.keys(myee._events).length, 0); }); From 4121c64b211c25d7ccc0156f1ef58f206cb584df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Fri, 23 Feb 2018 16:13:23 +0100 Subject: [PATCH 06/16] Switch to airtap --- .zuul.yml => .airtap.yml | 1 - .travis.yml | 5 ++++- package.json | 6 +++--- 3 files changed, 7 insertions(+), 5 deletions(-) rename .zuul.yml => .airtap.yml (96%) diff --git a/.zuul.yml b/.airtap.yml similarity index 96% rename from .zuul.yml rename to .airtap.yml index f8d0b51..252e064 100644 --- a/.zuul.yml +++ b/.airtap.yml @@ -1,4 +1,3 @@ -ui: tape concurrency: 1 browsers: - name: chrome diff --git a/.travis.yml b/.travis.yml index 5c0b18c..bf16559 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,9 +2,12 @@ sudo: false language: node_js node_js: - stable + - '0.12' script: - npm test - - if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then npm run test:browsers; fi + - if [ "${TRAVIS_PULL_REQUEST}" = "false" ] && [ "${TRAVIS_NODE_VERSION}" = "stable" ]; then npm run test:browsers; fi +addons: + sauce_connect: true env: global: - secure: XcBiD8yReflut9q7leKsigDZ0mI3qTKH+QrNVY8DaqlomJOZw8aOrVuX9Jz12l86ZJ41nbxmKnRNkFzcVr9mbP9YaeTb3DpeOBWmvaoSfud9Wnc16VfXtc1FCcwDhSVcSiM3UtnrmFU5cH+Dw1LPh5PbfylYOS/nJxUvG0FFLqI= diff --git a/package.json b/package.json index 626808f..6c70b7e 100644 --- a/package.json +++ b/package.json @@ -23,13 +23,13 @@ "node": ">=0.8.x" }, "devDependencies": { + "airtap": "0.0.5", "isarray": "^2.0.2", - "tape": "^4.8.0", - "zuul": "^3.11.1" + "tape": "^4.8.0" }, "scripts": { "test": "node tests/index.js", - "test:browsers": "zuul -- tests/index.js" + "test:browsers": "airtap tests/index.js" }, "license": "MIT" } From c40bd277cc37e94bfca9b2f9823afc4bf3c2dca7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Fri, 11 May 2018 14:31:04 +0200 Subject: [PATCH 07/16] Update readme. --- Readme.md | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/Readme.md b/Readme.md index 4fae922..d08b2f5 100644 --- a/Readme.md +++ b/Readme.md @@ -4,18 +4,24 @@ Node's event emitter for all engines. -## Install ## +## Install ``` npm install events ``` -## Require ## +## Usage ```javascript var EventEmitter = require('events') ``` -## Usage ## +Note that the `events` module uses ES5 features. If you need to support very old browsers like IE8, use a shim like [`es5-shim`](https://www.npmjs.com/package/es5-shim). You need both the shim and the sham versions of `es5-shim`. -See the [node.js event emitter docs](http://nodejs.org/api/events.html) +## API + +See the [Node.js EventEmitter docs](http://nodejs.org/api/events.html). `events` currently matches the Node.js 10.1 API. + +## License + +[MIT](./LICENSE) From 567f4d375580cbd4f094ac9935bb391864e28d61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Fri, 11 May 2018 14:34:32 +0200 Subject: [PATCH 08/16] Add airtap.local loopback and set `sauce_connect: true`. --- .airtap.yml | 23 ++++++++++++----------- .travis.yml | 2 ++ package.json | 2 +- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/.airtap.yml b/.airtap.yml index 252e064..8f1de87 100644 --- a/.airtap.yml +++ b/.airtap.yml @@ -1,12 +1,13 @@ -concurrency: 1 +sauce_connect: true +loopback: airtap.local browsers: - - name: chrome - version: latest - - name: firefox - version: latest - - name: safari - version: 9..latest - - name: iphone - version: latest - - name: ie - version: 9..latest + - name: chrome + version: latest + - name: firefox + version: latest + - name: safari + version: 9..latest + - name: iphone + version: latest + - name: ie + version: 9..latest diff --git a/.travis.yml b/.travis.yml index bf16559..37c7813 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,6 +8,8 @@ script: - if [ "${TRAVIS_PULL_REQUEST}" = "false" ] && [ "${TRAVIS_NODE_VERSION}" = "stable" ]; then npm run test:browsers; fi addons: sauce_connect: true + hosts: + - airtap.local env: global: - secure: XcBiD8yReflut9q7leKsigDZ0mI3qTKH+QrNVY8DaqlomJOZw8aOrVuX9Jz12l86ZJ41nbxmKnRNkFzcVr9mbP9YaeTb3DpeOBWmvaoSfud9Wnc16VfXtc1FCcwDhSVcSiM3UtnrmFU5cH+Dw1LPh5PbfylYOS/nJxUvG0FFLqI= diff --git a/package.json b/package.json index 6c70b7e..a152e78 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ }, "scripts": { "test": "node tests/index.js", - "test:browsers": "airtap tests/index.js" + "test:browsers": "airtap -- tests/index.js" }, "license": "MIT" } From 59d30742975bbb7788e797b21d92a00b296a921b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Fri, 18 May 2018 12:15:04 +0200 Subject: [PATCH 09/16] Skip method-name tests if Function.name is not available And log a message if the Symbol test was skipped, too. --- tests/index.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/tests/index.js b/tests/index.js index 29588d2..a8e6a40 100644 --- a/tests/index.js +++ b/tests/index.js @@ -19,13 +19,23 @@ require('./listener-count.js'); require('./listeners-side-effects.js'); require('./listeners.js'); require('./max-listeners.js'); -require('./method-names.js'); +if ((function A () {}).name === 'A') { + require('./method-names.js'); +} else { + // Function.name is not supported in IE + test('./method-names.js', { skip: true }, function () {}); +} require('./modify-in-emit.js'); require('./num-args.js'); require('./once.js'); require('./prepend.js'); require('./set-max-listeners-side-effects.js'); require('./subclass.js'); -if (typeof Symbol === 'function') require('./symbols.js'); +if (typeof Symbol === 'function') { + require('./symbols.js'); +} else { + // Symbol is not available. + test('./symbols.js', { skip: true }, function () {}); +} require('./remove-all-listeners.js'); require('./remove-listeners.js'); From 38d5a785540104fb113e8659b6f8e62b008e9fbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Fri, 18 May 2018 12:24:44 +0200 Subject: [PATCH 10/16] Polyfill Number.isNaN. --- events.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/events.js b/events.js index 6a0a335..bcf4698 100644 --- a/events.js +++ b/events.js @@ -30,6 +30,9 @@ function ReflectOwnKeys(target) { function ProcessEmitWarning(warning) { if (console && console.warn) console.warn(warning); } +var NumberIsNaN = Number.isNaN || function NumberIsNaN(value) { + return value !== value; +} function EventEmitter() { EventEmitter.init.call(this); @@ -53,7 +56,7 @@ Object.defineProperty(EventEmitter, 'defaultMaxListeners', { return defaultMaxListeners; }, set: function(arg) { - if (typeof arg !== 'number' || arg < 0 || Number.isNaN(arg)) { + if (typeof arg !== 'number' || arg < 0 || NumberIsNaN(arg)) { throw new RangeError('The value of "defaultMaxListeners" is out of range. It must be a non-negative number. Received ' + arg + '.'); } defaultMaxListeners = arg; @@ -74,7 +77,7 @@ EventEmitter.init = function() { // Obviously not all Emitters should be limited to 10. This function allows // that to be increased. Set to zero for unlimited. EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) { - if (typeof n !== 'number' || n < 0 || Number.isNaN(n)) { + if (typeof n !== 'number' || n < 0 || NumberIsNaN(n)) { throw new RangeError('The value of "n" is out of range. It must be a non-negative number. Received ' + n + '.'); } this._maxListeners = n; From 6e1abfa9cc528f08ea77d0102eb1c801c34c2252 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Thu, 24 May 2018 16:27:09 +0200 Subject: [PATCH 11/16] Update readme: clarify Node version, shims, feature contribution policy --- Readme.md | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/Readme.md b/Readme.md index d08b2f5..f2f8c4c 100644 --- a/Readme.md +++ b/Readme.md @@ -1,11 +1,21 @@ -# Status: [Maintainer Needed](https://github.com/Gozala/events/issues/43) - # events [![Build Status](https://travis-ci.org/Gozala/events.png?branch=master)](https://travis-ci.org/Gozala/events) -Node's event emitter for all engines. +> Node's event emitter for all engines. + +This implements the Node.js [`events`](http://nodejs.org/api/events.html) module for environments that do not have it, like browsers. + +> `events` currently matches the **Node.js 10.1** API. + +Note that the `events` module uses ES5 features. If you need to support very old browsers like IE8, use a shim like [`es5-shim`](https://www.npmjs.com/package/es5-shim). You need both the shim and the sham versions of `es5-shim`. + +This module is maintained, but only by very few people. If you'd like to help, let us know in the [Maintainer Needed](https://github.com/Gozala/events/issues/43) issue! ## Install +You usually do not have to install `events` yourself! If your code runs in Node.js, `events` is built in. If your code runs in the browser, bundlers like [browserify](https://github.com/browserify/browserify) or [webpack](https://github.com/webpack/webpack) also include the `events` module. + +But if none of those apply, with npm do: + ``` npm install events ``` @@ -14,14 +24,25 @@ npm install events ```javascript var EventEmitter = require('events') -``` -Note that the `events` module uses ES5 features. If you need to support very old browsers like IE8, use a shim like [`es5-shim`](https://www.npmjs.com/package/es5-shim). You need both the shim and the sham versions of `es5-shim`. +var ee = new EventEmitter() +ee.on('message', function (text) { + console.log(text) +}) +ee.emit('message', 'hello world') +``` ## API See the [Node.js EventEmitter docs](http://nodejs.org/api/events.html). `events` currently matches the Node.js 10.1 API. +## Contributing + +PRs are very welcome! The main way to contribute to `events` is by porting features, bugfixes and tests from Node.js. Ideally, code contributions to this module are copy-pasted from Node.js and transpiled to ES5, rather than reimplemented from scratch. Matching the Node.js code as closely as possible makes maintenance simpler when new changes land in Node.js. +This module intends to provide exactly the same API as Node.js, so features that are not available in the core `events` module will not be accepted. Feature requests should instead be directed at [nodejs/node](https://github.com/nodejs/node) and will be added to this module once they are implemented in Node.js. + +If there is a difference in behaviour between Node.js's `events` module and this module, please open an issue! + ## License [MIT](./LICENSE) From c3e91f92f1aac7bb95b1499de4f662b43701ef06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Thu, 24 May 2018 16:55:14 +0200 Subject: [PATCH 12/16] Add events-list and special-event-names test; more robust Reflect polyfills --- events.js | 26 +++++++++++++++++---- tests/events-list.js | 28 ++++++++++++++++++++++ tests/index.js | 2 ++ tests/special-event-names.js | 45 ++++++++++++++++++++++++++++++++++++ 4 files changed, 96 insertions(+), 5 deletions(-) create mode 100644 tests/events-list.js create mode 100644 tests/special-event-names.js diff --git a/events.js b/events.js index bcf4698..6dbc6eb 100644 --- a/events.js +++ b/events.js @@ -21,15 +21,31 @@ 'use strict'; -function ReflectApply(target, receiver, args) { - return Function.prototype.apply.call(target, receiver, args); -} -function ReflectOwnKeys(target) { - return Object.getOwnPropertyNames(target); +var R = typeof Reflect === 'object' ? Reflect : null +var ReflectApply = R && typeof R.apply === 'function' + ? R.apply + : function ReflectApply(target, receiver, args) { + return Function.prototype.apply.call(target, receiver, args); + } + +var ReflectOwnKeys +if (R && typeof R.ownKeys === 'function') { + ReflectOwnKeys = R.ownKeys +} else if (Object.getOwnPropertySymbols) { + ReflectOwnKeys = function ReflectOwnKeys(target) { + return Object.getOwnPropertyNames(target) + .concat(Object.getOwnPropertySymbols(target)); + }; +} else { + ReflectOwnKeys = function ReflectOwnKeys(target) { + return Object.getOwnPropertyNames(target); + }; } + function ProcessEmitWarning(warning) { if (console && console.warn) console.warn(warning); } + var NumberIsNaN = Number.isNaN || function NumberIsNaN(value) { return value !== value; } diff --git a/tests/events-list.js b/tests/events-list.js new file mode 100644 index 0000000..08aa621 --- /dev/null +++ b/tests/events-list.js @@ -0,0 +1,28 @@ +'use strict'; + +var EventEmitter = require('../'); +var assert = require('assert'); + +var EE = new EventEmitter(); +var m = function() {}; +EE.on('foo', function() {}); +assert.equal(1, EE.eventNames().length); +assert.equal('foo', EE.eventNames()[0]); +EE.on('bar', m); +assert.equal(2, EE.eventNames().length); +assert.equal('foo', EE.eventNames()[0]); +assert.equal('bar', EE.eventNames()[1]); +EE.removeListener('bar', m); +assert.equal(1, EE.eventNames().length); +assert.equal('foo', EE.eventNames()[0]); + +if (typeof Symbol !== 'undefined') { + var s = Symbol('s'); + EE.on(s, m); + assert.equal(2, EE.eventNames().length); + assert.equal('foo', EE.eventNames()[0]); + assert.equal(s, EE.eventNames()[1]); + EE.removeListener(s, m); + assert.equal(1, EE.eventNames().length); + assert.equal('foo', EE.eventNames()[0]); +} diff --git a/tests/index.js b/tests/index.js index a8e6a40..2e5efcb 100644 --- a/tests/index.js +++ b/tests/index.js @@ -15,6 +15,7 @@ var require = function(file) { require('./add-listeners.js'); require('./check-listener-leaks.js'); require('./errors.js'); +require('./events-list.js'); require('./listener-count.js'); require('./listeners-side-effects.js'); require('./listeners.js'); @@ -30,6 +31,7 @@ require('./num-args.js'); require('./once.js'); require('./prepend.js'); require('./set-max-listeners-side-effects.js'); +require('./special-event-names.js'); require('./subclass.js'); if (typeof Symbol === 'function') { require('./symbols.js'); diff --git a/tests/special-event-names.js b/tests/special-event-names.js new file mode 100644 index 0000000..a2f0b74 --- /dev/null +++ b/tests/special-event-names.js @@ -0,0 +1,45 @@ +'use strict'; + +var common = require('./common'); +var EventEmitter = require('../'); +var assert = require('assert'); + +var ee = new EventEmitter(); +var handler = function() {}; + +assert.strictEqual(ee.eventNames().length, 0); + +assert.strictEqual(ee._events.hasOwnProperty, undefined); +assert.strictEqual(ee._events.toString, undefined); + +ee.on('__defineGetter__', handler); +ee.on('toString', handler); +ee.on('__proto__', handler); + +assert.strictEqual(ee.eventNames()[0], '__defineGetter__'); +assert.strictEqual(ee.eventNames()[1], 'toString'); + +assert.strictEqual(ee.listeners('__defineGetter__').length, 1); +assert.strictEqual(ee.listeners('__defineGetter__')[0], handler); +assert.strictEqual(ee.listeners('toString').length, 1); +assert.strictEqual(ee.listeners('toString')[0], handler); + +// Only run __proto__ tests if that property can actually be set +if ({ __proto__: 'ok' }.__proto__ === 'ok') { + assert.strictEqual(ee.eventNames().length, 3); + assert.strictEqual(ee.eventNames()[2], '__proto__'); + assert.strictEqual(ee.listeners('__proto__').length, 1); + assert.strictEqual(ee.listeners('__proto__')[0], handler); + + ee.on('__proto__', common.mustCall(function(val) { + assert.strictEqual(val, 1); + })); + ee.emit('__proto__', 1); + + process.on('__proto__', common.mustCall(function(val) { + assert.strictEqual(val, 1); + })); + process.emit('__proto__', 1); +} else { + console.log('# skipped __proto__') +} From ae8c26753658641691b95c37995e911be3dddbc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Thu, 24 May 2018 17:11:36 +0200 Subject: [PATCH 13/16] Print MaxListenersExceeded warnings as TAP comments. --- tests/check-listener-leaks.js | 8 ++++++++ tests/index.js | 5 +++++ 2 files changed, 13 insertions(+) diff --git a/tests/check-listener-leaks.js b/tests/check-listener-leaks.js index 3cd0aa2..7fce48f 100644 --- a/tests/check-listener-leaks.js +++ b/tests/check-listener-leaks.js @@ -23,6 +23,14 @@ var common = require('./common'); var assert = require('assert'); var events = require('../'); +// Redirect warning output to tape. +var consoleWarn = console.warn; +console.warn = common.test.comment; + +common.test.on('end', function () { + console.warn = consoleWarn; +}); + // default { var e = new events.EventEmitter(); diff --git a/tests/index.js b/tests/index.js index 2e5efcb..1f06256 100644 --- a/tests/index.js +++ b/tests/index.js @@ -1,12 +1,17 @@ var test = require('tape'); require('./legacy-compat'); +var common = require('./common'); // we do this to easily wrap each file in a mocha test // and also have browserify be able to statically analyze this file var orig_require = require; var require = function(file) { test(file, function(t) { + // Store the tape object so tests can access it. + t.on('end', function () { delete common.test; }); + common.test = t; + try { orig_require(file); } catch (err) { t.fail(err); } t.end(); }); From 8b763d2e74a145b0c466a4862d42d9eedd321a14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Thu, 24 May 2018 19:25:40 +0200 Subject: [PATCH 14/16] Update airtap to 0.0.6 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a152e78..d6af97b 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "node": ">=0.8.x" }, "devDependencies": { - "airtap": "0.0.5", + "airtap": "0.0.6", "isarray": "^2.0.2", "tape": "^4.8.0" }, From f1750fd5440b54196d6d56decf6ffdec2bf1e8ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Fri, 25 May 2018 11:04:08 +0200 Subject: [PATCH 15/16] Add MS Edge to test matrix. --- .airtap.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.airtap.yml b/.airtap.yml index 8f1de87..c7a8a87 100644 --- a/.airtap.yml +++ b/.airtap.yml @@ -11,3 +11,5 @@ browsers: version: latest - name: ie version: 9..latest + - name: microsoftedge + version: 13..latest From ebd96864adf2cfbde0ad8450ac2ecc6be9bc3583 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Fri, 25 May 2018 11:30:33 +0200 Subject: [PATCH 16/16] Disable iPhone test for now, SauceLabs is not running it for some reason --- .airtap.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.airtap.yml b/.airtap.yml index c7a8a87..4ef94fa 100644 --- a/.airtap.yml +++ b/.airtap.yml @@ -7,8 +7,8 @@ browsers: version: latest - name: safari version: 9..latest - - name: iphone - version: latest + # - name: iphone + # version: latest - name: ie version: 9..latest - name: microsoftedge