'use strict'; var common = require('./common'); var EventEmitter = require('../').EventEmitter; var once = require('../').once; var has = require('has'); var assert = require('assert'); function Event(type) { this.type = type; } function EventTargetMock() { this.events = {}; this.addEventListener = common.mustCall(this.addEventListener); this.removeEventListener = common.mustCall(this.removeEventListener); } EventTargetMock.prototype.addEventListener = function addEventListener(name, listener, options) { if (!(name in this.events)) { this.events[name] = { listeners: [], options: options || {} } } this.events[name].listeners.push(listener); }; EventTargetMock.prototype.removeEventListener = function removeEventListener(name, callback) { if (!(name in this.events)) { return; } var event = this.events[name]; var stack = event.listeners; for (var i = 0, l = stack.length; i < l; i++) { if (stack[i] === callback) { stack.splice(i, 1); if (stack.length === 0) { delete this.events[name]; } return; } } }; EventTargetMock.prototype.dispatchEvent = function dispatchEvent(arg) { if (!(arg.type in this.events)) { return true; } var event = this.events[arg.type]; var stack = event.listeners.slice(); for (var i = 0, l = stack.length; i < l; i++) { stack[i].call(null, arg); if (event.options.once) { this.removeEventListener(arg.type, stack[i]); } } return !arg.defaultPrevented; }; function onceAnEvent() { var ee = new EventEmitter(); process.nextTick(function () { ee.emit('myevent', 42); }); return once(ee, 'myevent').then(function (args) { var value = args[0] assert.strictEqual(value, 42); assert.strictEqual(ee.listenerCount('error'), 0); assert.strictEqual(ee.listenerCount('myevent'), 0); }); } function onceAnEventWithTwoArgs() { var ee = new EventEmitter(); process.nextTick(function () { ee.emit('myevent', 42, 24); }); return once(ee, 'myevent').then(function (value) { assert.strictEqual(value.length, 2); assert.strictEqual(value[0], 42); assert.strictEqual(value[1], 24); }); } function catchesErrors() { var ee = new EventEmitter(); var expected = new Error('kaboom'); var err; process.nextTick(function () { ee.emit('error', expected); }); return once(ee, 'myevent').then(function () { throw new Error('should reject') }, function (err) { assert.strictEqual(err, expected); assert.strictEqual(ee.listenerCount('error'), 0); assert.strictEqual(ee.listenerCount('myevent'), 0); }); } function stopListeningAfterCatchingError() { var ee = new EventEmitter(); var expected = new Error('kaboom'); var err; process.nextTick(function () { ee.emit('error', expected); ee.emit('myevent', 42, 24); }); // process.on('multipleResolves', common.mustNotCall()); return once(ee, 'myevent').then(common.mustNotCall, function (err) { // process.removeAllListeners('multipleResolves'); assert.strictEqual(err, expected); assert.strictEqual(ee.listenerCount('error'), 0); assert.strictEqual(ee.listenerCount('myevent'), 0); }); } function onceError() { var ee = new EventEmitter(); var expected = new Error('kaboom'); process.nextTick(function () { ee.emit('error', expected); }); var promise = once(ee, 'error'); assert.strictEqual(ee.listenerCount('error'), 1); return promise.then(function (args) { var err = args[0] assert.strictEqual(err, expected); assert.strictEqual(ee.listenerCount('error'), 0); assert.strictEqual(ee.listenerCount('myevent'), 0); }); } function onceWithEventTarget() { var et = new EventTargetMock(); var event = new Event('myevent'); process.nextTick(function () { et.dispatchEvent(event); }); return once(et, 'myevent').then(function (args) { var value = args[0]; assert.strictEqual(value, event); assert.strictEqual(has(et.events, 'myevent'), false); }); } function onceWithEventTargetError() { var et = new EventTargetMock(); var error = new Event('error'); process.nextTick(function () { et.dispatchEvent(error); }); return once(et, 'error').then(function (args) { var err = args[0]; assert.strictEqual(err, error); assert.strictEqual(has(et.events, 'error'), false); }); } function prioritizesEventEmitter() { var ee = new EventEmitter(); ee.addEventListener = assert.fail; ee.removeAllListeners = assert.fail; process.nextTick(function () { ee.emit('foo'); }); return once(ee, 'foo'); } var allTests = [ onceAnEvent(), onceAnEventWithTwoArgs(), catchesErrors(), stopListeningAfterCatchingError(), onceError(), onceWithEventTarget(), onceWithEventTargetError(), prioritizesEventEmitter() ]; var hasBrowserEventTarget = false; try { hasBrowserEventTarget = typeof (new window.EventTarget().addEventListener) === 'function' && new window.Event('xyz').type === 'xyz'; } catch (err) {} if (hasBrowserEventTarget) { var onceWithBrowserEventTarget = function onceWithBrowserEventTarget() { var et = new window.EventTarget(); var event = new window.Event('myevent'); process.nextTick(function () { et.dispatchEvent(event); }); return once(et, 'myevent').then(function (args) { var value = args[0]; assert.strictEqual(value, event); assert.strictEqual(has(et.events, 'myevent'), false); }); } var onceWithBrowserEventTargetError = function onceWithBrowserEventTargetError() { var et = new window.EventTarget(); var error = new window.Event('error'); process.nextTick(function () { et.dispatchEvent(error); }); return once(et, 'error').then(function (args) { var err = args[0]; assert.strictEqual(err, error); assert.strictEqual(has(et.events, 'error'), false); }); } common.test.comment('Testing with browser built-in EventTarget'); allTests.push([ onceWithBrowserEventTarget(), onceWithBrowserEventTargetError() ]); } module.exports = Promise.all(allTests);