events-once.js (6115B)
1 'use strict'; 2 3 var common = require('./common'); 4 var EventEmitter = require('../').EventEmitter; 5 var once = require('../').once; 6 var has = require('has'); 7 var assert = require('assert'); 8 9 function Event(type) { 10 this.type = type; 11 } 12 13 function EventTargetMock() { 14 this.events = {}; 15 16 this.addEventListener = common.mustCall(this.addEventListener); 17 this.removeEventListener = common.mustCall(this.removeEventListener); 18 } 19 20 EventTargetMock.prototype.addEventListener = function addEventListener(name, listener, options) { 21 if (!(name in this.events)) { 22 this.events[name] = { listeners: [], options: options || {} } 23 } 24 this.events[name].listeners.push(listener); 25 }; 26 27 EventTargetMock.prototype.removeEventListener = function removeEventListener(name, callback) { 28 if (!(name in this.events)) { 29 return; 30 } 31 var event = this.events[name]; 32 var stack = event.listeners; 33 34 for (var i = 0, l = stack.length; i < l; i++) { 35 if (stack[i] === callback) { 36 stack.splice(i, 1); 37 if (stack.length === 0) { 38 delete this.events[name]; 39 } 40 return; 41 } 42 } 43 }; 44 45 EventTargetMock.prototype.dispatchEvent = function dispatchEvent(arg) { 46 if (!(arg.type in this.events)) { 47 return true; 48 } 49 50 var event = this.events[arg.type]; 51 var stack = event.listeners.slice(); 52 53 for (var i = 0, l = stack.length; i < l; i++) { 54 stack[i].call(null, arg); 55 if (event.options.once) { 56 this.removeEventListener(arg.type, stack[i]); 57 } 58 } 59 return !arg.defaultPrevented; 60 }; 61 62 function onceAnEvent() { 63 var ee = new EventEmitter(); 64 65 process.nextTick(function () { 66 ee.emit('myevent', 42); 67 }); 68 69 return once(ee, 'myevent').then(function (args) { 70 var value = args[0] 71 assert.strictEqual(value, 42); 72 assert.strictEqual(ee.listenerCount('error'), 0); 73 assert.strictEqual(ee.listenerCount('myevent'), 0); 74 }); 75 } 76 77 function onceAnEventWithTwoArgs() { 78 var ee = new EventEmitter(); 79 80 process.nextTick(function () { 81 ee.emit('myevent', 42, 24); 82 }); 83 84 return once(ee, 'myevent').then(function (value) { 85 assert.strictEqual(value.length, 2); 86 assert.strictEqual(value[0], 42); 87 assert.strictEqual(value[1], 24); 88 }); 89 } 90 91 function catchesErrors() { 92 var ee = new EventEmitter(); 93 94 var expected = new Error('kaboom'); 95 var err; 96 process.nextTick(function () { 97 ee.emit('error', expected); 98 }); 99 100 return once(ee, 'myevent').then(function () { 101 throw new Error('should reject') 102 }, function (err) { 103 assert.strictEqual(err, expected); 104 assert.strictEqual(ee.listenerCount('error'), 0); 105 assert.strictEqual(ee.listenerCount('myevent'), 0); 106 }); 107 } 108 109 function stopListeningAfterCatchingError() { 110 var ee = new EventEmitter(); 111 112 var expected = new Error('kaboom'); 113 var err; 114 process.nextTick(function () { 115 ee.emit('error', expected); 116 ee.emit('myevent', 42, 24); 117 }); 118 119 // process.on('multipleResolves', common.mustNotCall()); 120 121 return once(ee, 'myevent').then(common.mustNotCall, function (err) { 122 // process.removeAllListeners('multipleResolves'); 123 assert.strictEqual(err, expected); 124 assert.strictEqual(ee.listenerCount('error'), 0); 125 assert.strictEqual(ee.listenerCount('myevent'), 0); 126 }); 127 } 128 129 function onceError() { 130 var ee = new EventEmitter(); 131 132 var expected = new Error('kaboom'); 133 process.nextTick(function () { 134 ee.emit('error', expected); 135 }); 136 137 var promise = once(ee, 'error'); 138 assert.strictEqual(ee.listenerCount('error'), 1); 139 return promise.then(function (args) { 140 var err = args[0] 141 assert.strictEqual(err, expected); 142 assert.strictEqual(ee.listenerCount('error'), 0); 143 assert.strictEqual(ee.listenerCount('myevent'), 0); 144 }); 145 } 146 147 function onceWithEventTarget() { 148 var et = new EventTargetMock(); 149 var event = new Event('myevent'); 150 process.nextTick(function () { 151 et.dispatchEvent(event); 152 }); 153 return once(et, 'myevent').then(function (args) { 154 var value = args[0]; 155 assert.strictEqual(value, event); 156 assert.strictEqual(has(et.events, 'myevent'), false); 157 }); 158 } 159 160 function onceWithEventTargetError() { 161 var et = new EventTargetMock(); 162 var error = new Event('error'); 163 process.nextTick(function () { 164 et.dispatchEvent(error); 165 }); 166 return once(et, 'error').then(function (args) { 167 var err = args[0]; 168 assert.strictEqual(err, error); 169 assert.strictEqual(has(et.events, 'error'), false); 170 }); 171 } 172 173 function prioritizesEventEmitter() { 174 var ee = new EventEmitter(); 175 ee.addEventListener = assert.fail; 176 ee.removeAllListeners = assert.fail; 177 process.nextTick(function () { 178 ee.emit('foo'); 179 }); 180 return once(ee, 'foo'); 181 } 182 183 var allTests = [ 184 onceAnEvent(), 185 onceAnEventWithTwoArgs(), 186 catchesErrors(), 187 stopListeningAfterCatchingError(), 188 onceError(), 189 onceWithEventTarget(), 190 onceWithEventTargetError(), 191 prioritizesEventEmitter() 192 ]; 193 194 var hasBrowserEventTarget = false; 195 try { 196 hasBrowserEventTarget = typeof (new window.EventTarget().addEventListener) === 'function' && 197 new window.Event('xyz').type === 'xyz'; 198 } catch (err) {} 199 200 if (hasBrowserEventTarget) { 201 var onceWithBrowserEventTarget = function onceWithBrowserEventTarget() { 202 var et = new window.EventTarget(); 203 var event = new window.Event('myevent'); 204 process.nextTick(function () { 205 et.dispatchEvent(event); 206 }); 207 return once(et, 'myevent').then(function (args) { 208 var value = args[0]; 209 assert.strictEqual(value, event); 210 assert.strictEqual(has(et.events, 'myevent'), false); 211 }); 212 } 213 214 var onceWithBrowserEventTargetError = function onceWithBrowserEventTargetError() { 215 var et = new window.EventTarget(); 216 var error = new window.Event('error'); 217 process.nextTick(function () { 218 et.dispatchEvent(error); 219 }); 220 return once(et, 'error').then(function (args) { 221 var err = args[0]; 222 assert.strictEqual(err, error); 223 assert.strictEqual(has(et.events, 'error'), false); 224 }); 225 } 226 227 common.test.comment('Testing with browser built-in EventTarget'); 228 allTests.push([ 229 onceWithBrowserEventTarget(), 230 onceWithBrowserEventTargetError() 231 ]); 232 } 233 234 module.exports = Promise.all(allTests);