events-once.js 6.0 KB

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