testing.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. /**
  2. * @license
  3. * Copyright Google LLC All Rights Reserved.
  4. *
  5. * Use of this source code is governed by an MIT-style license that can be
  6. * found in the LICENSE file at https://angular.io/license
  7. */
  8. /**
  9. * @fileoverview added by tsickle
  10. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  11. */
  12. /**
  13. * Creates a browser MouseEvent with the specified options.
  14. * \@docs-private
  15. * @param {?} type
  16. * @param {?=} x
  17. * @param {?=} y
  18. * @param {?=} button
  19. * @return {?}
  20. */
  21. function createMouseEvent(type, x = 0, y = 0, button = 0) {
  22. /** @type {?} */
  23. const event = document.createEvent('MouseEvent');
  24. /** @type {?} */
  25. const originalPreventDefault = event.preventDefault;
  26. event.initMouseEvent(type, true, /* canBubble */ true, /* cancelable */ window, /* view */ 0, /* detail */ x, /* screenX */ y, /* screenY */ x, /* clientX */ y, /* clientY */ false, /* ctrlKey */ false, /* altKey */ false, /* shiftKey */ false, /* metaKey */ button, /* button */ null /* relatedTarget */);
  27. // `initMouseEvent` doesn't allow us to pass the `buttons` and
  28. // defaults it to 0 which looks like a fake event.
  29. Object.defineProperty(event, 'buttons', { get: (/**
  30. * @return {?}
  31. */
  32. () => 1) });
  33. // IE won't set `defaultPrevented` on synthetic events so we need to do it manually.
  34. event.preventDefault = (/**
  35. * @return {?}
  36. */
  37. function () {
  38. Object.defineProperty(event, 'defaultPrevented', { get: (/**
  39. * @return {?}
  40. */
  41. () => true) });
  42. return originalPreventDefault.apply(this, arguments);
  43. });
  44. return event;
  45. }
  46. /**
  47. * Creates a browser TouchEvent with the specified pointer coordinates.
  48. * \@docs-private
  49. * @param {?} type
  50. * @param {?=} pageX
  51. * @param {?=} pageY
  52. * @return {?}
  53. */
  54. function createTouchEvent(type, pageX = 0, pageY = 0) {
  55. // In favor of creating events that work for most of the browsers, the event is created
  56. // as a basic UI Event. The necessary details for the event will be set manually.
  57. /** @type {?} */
  58. const event = document.createEvent('UIEvent');
  59. /** @type {?} */
  60. const touchDetails = { pageX, pageY };
  61. // TS3.6 removes the initUIEvent method and suggests porting to "new UIEvent()".
  62. ((/** @type {?} */ (event))).initUIEvent(type, true, true, window, 0);
  63. // Most of the browsers don't have a "initTouchEvent" method that can be used to define
  64. // the touch details.
  65. Object.defineProperties(event, {
  66. touches: { value: [touchDetails] },
  67. targetTouches: { value: [touchDetails] },
  68. changedTouches: { value: [touchDetails] }
  69. });
  70. return event;
  71. }
  72. /**
  73. * Dispatches a keydown event from an element.
  74. * \@docs-private
  75. * @param {?} type
  76. * @param {?=} keyCode
  77. * @param {?=} key
  78. * @param {?=} target
  79. * @param {?=} modifiers
  80. * @return {?}
  81. */
  82. function createKeyboardEvent(type, keyCode = 0, key = '', target, modifiers = {}) {
  83. /** @type {?} */
  84. const event = (/** @type {?} */ (document.createEvent('KeyboardEvent')));
  85. /** @type {?} */
  86. const originalPreventDefault = event.preventDefault;
  87. // Firefox does not support `initKeyboardEvent`, but supports `initKeyEvent`.
  88. if (event.initKeyEvent) {
  89. event.initKeyEvent(type, true, true, window, modifiers.control, modifiers.alt, modifiers.shift, modifiers.meta, keyCode);
  90. }
  91. else {
  92. // `initKeyboardEvent` expects to receive modifiers as a whitespace-delimited string
  93. // See https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/initKeyboardEvent
  94. /** @type {?} */
  95. const modifiersStr = (modifiers.control ? 'Control ' : '' + modifiers.alt ? 'Alt ' : '' +
  96. modifiers.shift ? 'Shift ' : '' + modifiers.meta ? 'Meta' : '').trim();
  97. event.initKeyboardEvent(type, true, /* canBubble */ true, /* cancelable */ window, /* view */ 0, /* char */ key, /* key */ 0, /* location */ modifiersStr, /* modifiersList */ false /* repeat */);
  98. }
  99. // Webkit Browsers don't set the keyCode when calling the init function.
  100. // See related bug https://bugs.webkit.org/show_bug.cgi?id=16735
  101. Object.defineProperties(event, {
  102. keyCode: { get: (/**
  103. * @return {?}
  104. */
  105. () => keyCode) },
  106. key: { get: (/**
  107. * @return {?}
  108. */
  109. () => key) },
  110. target: { get: (/**
  111. * @return {?}
  112. */
  113. () => target) },
  114. ctrlKey: { get: (/**
  115. * @return {?}
  116. */
  117. () => !!modifiers.control) },
  118. altKey: { get: (/**
  119. * @return {?}
  120. */
  121. () => !!modifiers.alt) },
  122. shiftKey: { get: (/**
  123. * @return {?}
  124. */
  125. () => !!modifiers.shift) },
  126. metaKey: { get: (/**
  127. * @return {?}
  128. */
  129. () => !!modifiers.meta) }
  130. });
  131. // IE won't set `defaultPrevented` on synthetic events so we need to do it manually.
  132. event.preventDefault = (/**
  133. * @return {?}
  134. */
  135. function () {
  136. Object.defineProperty(event, 'defaultPrevented', { get: (/**
  137. * @return {?}
  138. */
  139. () => true) });
  140. return originalPreventDefault.apply(this, arguments);
  141. });
  142. return event;
  143. }
  144. /**
  145. * Creates a fake event object with any desired event type.
  146. * \@docs-private
  147. * @param {?} type
  148. * @param {?=} canBubble
  149. * @param {?=} cancelable
  150. * @return {?}
  151. */
  152. function createFakeEvent(type, canBubble = false, cancelable = true) {
  153. /** @type {?} */
  154. const event = document.createEvent('Event');
  155. event.initEvent(type, canBubble, cancelable);
  156. return event;
  157. }
  158. /**
  159. * @fileoverview added by tsickle
  160. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  161. */
  162. /**
  163. * Utility to dispatch any event on a Node.
  164. * \@docs-private
  165. * @param {?} node
  166. * @param {?} event
  167. * @return {?}
  168. */
  169. function dispatchEvent(node, event) {
  170. node.dispatchEvent(event);
  171. return event;
  172. }
  173. /**
  174. * Shorthand to dispatch a fake event on a specified node.
  175. * \@docs-private
  176. * @param {?} node
  177. * @param {?} type
  178. * @param {?=} canBubble
  179. * @return {?}
  180. */
  181. function dispatchFakeEvent(node, type, canBubble) {
  182. return dispatchEvent(node, createFakeEvent(type, canBubble));
  183. }
  184. /**
  185. * Shorthand to dispatch a keyboard event with a specified key code.
  186. * \@docs-private
  187. * @param {?} node
  188. * @param {?} type
  189. * @param {?=} keyCode
  190. * @param {?=} key
  191. * @param {?=} target
  192. * @param {?=} modifiers
  193. * @return {?}
  194. */
  195. function dispatchKeyboardEvent(node, type, keyCode, key, target, modifiers) {
  196. return (/** @type {?} */ (dispatchEvent(node, createKeyboardEvent(type, keyCode, key, target, modifiers))));
  197. }
  198. /**
  199. * Shorthand to dispatch a mouse event on the specified coordinates.
  200. * \@docs-private
  201. * @param {?} node
  202. * @param {?} type
  203. * @param {?=} x
  204. * @param {?=} y
  205. * @param {?=} event
  206. * @return {?}
  207. */
  208. function dispatchMouseEvent(node, type, x = 0, y = 0, event = createMouseEvent(type, x, y)) {
  209. return (/** @type {?} */ (dispatchEvent(node, event)));
  210. }
  211. /**
  212. * Shorthand to dispatch a touch event on the specified coordinates.
  213. * \@docs-private
  214. * @param {?} node
  215. * @param {?} type
  216. * @param {?=} x
  217. * @param {?=} y
  218. * @return {?}
  219. */
  220. function dispatchTouchEvent(node, type, x = 0, y = 0) {
  221. return dispatchEvent(node, createTouchEvent(type, x, y));
  222. }
  223. /**
  224. * @fileoverview added by tsickle
  225. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  226. */
  227. /**
  228. * @param {?} element
  229. * @param {?} event
  230. * @return {?}
  231. */
  232. function triggerFocusChange(element, event) {
  233. /** @type {?} */
  234. let eventFired = false;
  235. /** @type {?} */
  236. const handler = (/**
  237. * @return {?}
  238. */
  239. () => eventFired = true);
  240. element.addEventListener(event, handler);
  241. element[event]();
  242. element.removeEventListener(event, handler);
  243. if (!eventFired) {
  244. dispatchFakeEvent(element, event);
  245. }
  246. }
  247. /**
  248. * Patches an elements focus and blur methods to emit events consistently and predictably.
  249. * This is necessary, because some browsers, like IE11, will call the focus handlers asynchronously,
  250. * while others won't fire them at all if the browser window is not focused.
  251. * \@docs-private
  252. * @param {?} element
  253. * @return {?}
  254. */
  255. function patchElementFocus(element) {
  256. element.focus = (/**
  257. * @return {?}
  258. */
  259. () => dispatchFakeEvent(element, 'focus'));
  260. element.blur = (/**
  261. * @return {?}
  262. */
  263. () => dispatchFakeEvent(element, 'blur'));
  264. }
  265. /**
  266. * \@docs-private
  267. * @param {?} element
  268. * @return {?}
  269. */
  270. function triggerFocus(element) {
  271. triggerFocusChange(element, 'focus');
  272. }
  273. /**
  274. * \@docs-private
  275. * @param {?} element
  276. * @return {?}
  277. */
  278. function triggerBlur(element) {
  279. triggerFocusChange(element, 'blur');
  280. }
  281. /**
  282. * @fileoverview added by tsickle
  283. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  284. */
  285. /**
  286. * Checks whether the given Element is a text input element.
  287. * \@docs-private
  288. * @param {?} element
  289. * @return {?}
  290. */
  291. function isTextInput(element) {
  292. return element.nodeName.toLowerCase() === 'input' ||
  293. element.nodeName.toLowerCase() === 'textarea';
  294. }
  295. /**
  296. * @param {?} element
  297. * @param {...?} modifiersAndKeys
  298. * @return {?}
  299. */
  300. function typeInElement(element, ...modifiersAndKeys) {
  301. /** @type {?} */
  302. const first = modifiersAndKeys[0];
  303. /** @type {?} */
  304. let modifiers;
  305. /** @type {?} */
  306. let rest;
  307. if (typeof first !== 'string' && first.keyCode === undefined && first.key === undefined) {
  308. modifiers = first;
  309. rest = modifiersAndKeys.slice(1);
  310. }
  311. else {
  312. modifiers = {};
  313. rest = modifiersAndKeys;
  314. }
  315. /** @type {?} */
  316. const keys = rest
  317. .map((/**
  318. * @param {?} k
  319. * @return {?}
  320. */
  321. k => typeof k === 'string' ?
  322. k.split('').map((/**
  323. * @param {?} c
  324. * @return {?}
  325. */
  326. c => ({ keyCode: c.toUpperCase().charCodeAt(0), key: c }))) : [k]))
  327. .reduce((/**
  328. * @param {?} arr
  329. * @param {?} k
  330. * @return {?}
  331. */
  332. (arr, k) => arr.concat(k)), []);
  333. triggerFocus(element);
  334. for (const key of keys) {
  335. dispatchKeyboardEvent(element, 'keydown', key.keyCode, key.key, element, modifiers);
  336. dispatchKeyboardEvent(element, 'keypress', key.keyCode, key.key, element, modifiers);
  337. if (isTextInput(element) && key.key && key.key.length === 1) {
  338. element.value += key.key;
  339. dispatchFakeEvent(element, 'input');
  340. }
  341. dispatchKeyboardEvent(element, 'keyup', key.keyCode, key.key, element, modifiers);
  342. }
  343. }
  344. /**
  345. * Clears the text in an input or textarea element.
  346. * \@docs-private
  347. * @param {?} element
  348. * @return {?}
  349. */
  350. function clearElement(element) {
  351. triggerFocus((/** @type {?} */ (element)));
  352. element.value = '';
  353. dispatchFakeEvent(element, 'input');
  354. }
  355. /**
  356. * @fileoverview added by tsickle
  357. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  358. */
  359. /**
  360. * @fileoverview added by tsickle
  361. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  362. */
  363. export { dispatchEvent, dispatchFakeEvent, dispatchKeyboardEvent, dispatchMouseEvent, dispatchTouchEvent, createMouseEvent, createTouchEvent, createKeyboardEvent, createFakeEvent, isTextInput, typeInElement, clearElement, patchElementFocus, triggerFocus, triggerBlur };
  364. //# sourceMappingURL=testing.js.map