event.js 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. import '../_setup.js';
  2. describe('lib/evnet', function () {
  3. let listenerRegistry;
  4. let spyAEL;
  5. let spyREL;
  6. //
  7. let registerListeners;
  8. let unregisterListeners;
  9. let findElementInEventPath;
  10. before(function () {
  11. spyAEL = sinon.spy(EventTarget.prototype, 'addEventListener');
  12. spyREL = sinon.spy(EventTarget.prototype, 'removeEventListener');
  13. const origWeakMap = global.WeakMap;
  14. global.WeakMap = function (...args) {
  15. return listenerRegistry = new origWeakMap(...args);
  16. };
  17. return import('../../../js/lib/event.js')
  18. .then((module) => {
  19. global.WeakMap = origWeakMap;
  20. registerListeners = module.registerListeners;
  21. unregisterListeners = module.unregisterListeners;
  22. findElementInEventPath = module.findElementInEventPath;
  23. });
  24. });
  25. after(function () {
  26. spyAEL.restore();
  27. spyREL.restore();
  28. });
  29. afterEach(function () {
  30. spyAEL.resetHistory();
  31. spyREL.resetHistory();
  32. });
  33. describe('registerListeners()', function () {
  34. let target;
  35. before(function () {
  36. target = document.createElement('input');
  37. testContainer.appendChild(target);
  38. });
  39. after(function () {
  40. testContainer.removeChild(target);
  41. });
  42. it('registers event listeres with key object calling addEventListener()', function () {
  43. const keyObj = {};
  44. const onClick = sinon.spy();
  45. const onKeydown = sinon.spy();
  46. const onMounseEnter = () => {};
  47. const listeners = [
  48. [document, 'click', onClick],
  49. [target, 'keydown', onKeydown, {capture: false}],
  50. [target, 'mouseenter', onMounseEnter, true],
  51. ];
  52. registerListeners(keyObj, listeners);
  53. expect(listenerRegistry.get(keyObj), 'to equal', listeners);
  54. expect(spyAEL.args, 'to equal', listeners.map(listener => listener.slice(1)));
  55. testContainer.click();
  56. expect(onClick.called, 'to be true');
  57. simulant.fire(target, 'keydown', {key: 'enter'});
  58. expect(onKeydown.called, 'to be true');
  59. listeners.forEach((listener) => {
  60. EventTarget.prototype.removeEventListener.call(...listener);
  61. });
  62. listenerRegistry.delete(keyObj);
  63. });
  64. it('appends listeners if registration for key object already exists', function () {
  65. const keyObj = {};
  66. const onClick = () => {};
  67. const onKeydown = () => {};
  68. const onMounseEnter = () => {};
  69. const listeners = [
  70. [document, 'click', onClick],
  71. ];
  72. const listenersToAdd = [
  73. [target, 'keydown', onKeydown, {capture: false}],
  74. [target, 'mouseenter', onMounseEnter, true],
  75. ];
  76. listenerRegistry.set(keyObj, listeners);
  77. registerListeners(keyObj, listenersToAdd);
  78. expect(listeners.length, 'to be', 3);
  79. expect(listenerRegistry.get(keyObj), 'to equal', listeners);
  80. expect(spyAEL.args, 'to equal', listenersToAdd.map(listener => listener.slice(1)));
  81. listenersToAdd.forEach((listener) => {
  82. EventTarget.prototype.removeEventListener.call(...listener);
  83. });
  84. listenerRegistry.delete(keyObj);
  85. });
  86. });
  87. describe('unregisterListeners()', function () {
  88. let target;
  89. before(function () {
  90. target = document.createElement('input');
  91. testContainer.appendChild(target);
  92. });
  93. after(function () {
  94. testContainer.removeChild(target);
  95. });
  96. it('unregisters all event listeres for key object calling removeEventListener()', function () {
  97. const keyObj = {};
  98. const onClick = sinon.spy();
  99. const onKeydown = sinon.spy();
  100. const onMounseEnter = () => {};
  101. const listeners = [
  102. [document, 'click', onClick],
  103. [target, 'keydown', onKeydown, {capture: false}],
  104. [target, 'mouseenter', onMounseEnter, true],
  105. ];
  106. listeners.forEach((listener) => {
  107. EventTarget.prototype.addEventListener.call(...listener);
  108. });
  109. listenerRegistry.set(keyObj, listeners);
  110. unregisterListeners(keyObj);
  111. expect(listenerRegistry.get(keyObj), 'to be undefined');
  112. expect(spyREL.args, 'to equal', listeners.map(listener => listener.slice(1)));
  113. testContainer.click();
  114. expect(onClick.called, 'to be false');
  115. simulant.fire(target, 'keydown', {key: 'enter'});
  116. expect(onKeydown.called, 'to be false');
  117. });
  118. });
  119. describe('findElementInEventPath()', function () {
  120. let field;
  121. let control;
  122. let input;
  123. before(function () {
  124. input = document.createElement('input');
  125. control = document.createElement('div');
  126. control.className = 'control';
  127. control.appendChild(input);
  128. field = document.createElement('div');
  129. field.className = 'field';
  130. field.appendChild(control);
  131. testContainer.appendChild(field);
  132. });
  133. after(function () {
  134. testContainer.removeChild(field);
  135. });
  136. it('returns the first element in event\'s path that matches given selector', function () {
  137. const test = (ev) => {
  138. expect(findElementInEventPath(ev, '.control'), 'to be', control);
  139. expect(findElementInEventPath(ev, '.field'), 'to be', field);
  140. expect(findElementInEventPath(ev, 'div'), 'to be', control);
  141. };
  142. document.addEventListener('click', test);
  143. simulant.fire(input, 'click');
  144. document.removeEventListener('click', test);
  145. });
  146. it('returns undefined if no matched element is in event\'s path', function () {
  147. const test = (ev) => {
  148. expect(findElementInEventPath(ev, '.foo'), 'to be undefined',);
  149. };
  150. document.addEventListener('click', test);
  151. simulant.fire(input, 'click');
  152. document.removeEventListener('click', test);
  153. });
  154. it('stops searching when it reaches event\'s currentTarget', function () {
  155. const test = (ev) => {
  156. expect(findElementInEventPath(ev, '.control'), 'to be', control);
  157. expect(findElementInEventPath(ev, '.field'), 'to be undefined',);
  158. };
  159. control.addEventListener('click', test);
  160. simulant.fire(input, 'click');
  161. control.removeEventListener('click', test);
  162. });
  163. it('function can be used to evaluate the condition instead of css selector', function () {
  164. const test = (ev) => {
  165. expect(findElementInEventPath(ev, target => [field, control].includes(target)), 'to be', control);
  166. expect(findElementInEventPath(ev, target => target === field), 'to be', field);
  167. expect(findElementInEventPath(ev, target => target.tagName === 'DIV'), 'to be', control);
  168. };
  169. document.addEventListener('click', test);
  170. simulant.fire(input, 'click');
  171. document.removeEventListener('click', test);
  172. });
  173. });
  174. });