UNPKG

ember-native-dom-helpers

Version:

Test helpers for your integration tests that fire native events

211 lines (191 loc) 5.5 kB
import { assign } from '@ember/polyfills'; const DEFAULT_EVENT_OPTIONS = { bubbles: true, cancelable: true }; const KEYBOARD_EVENT_TYPES = ['keydown', 'keypress', 'keyup']; const MOUSE_EVENT_TYPES = ['click', 'mousedown', 'mouseup', 'dblclick', 'mouseenter', 'mouseleave', 'mousemove', 'mouseout', 'mouseover']; const FILE_SELECTION_EVENT_TYPES = ['change']; /* @method fireEvent @param {HTMLElement} element @param {String} type @param {Object} (optional) options @return {Event} The dispatched event @private */ export function fireEvent(element, type, options = {}) { if (!element) { return; } let event; if (KEYBOARD_EVENT_TYPES.indexOf(type) > -1) { event = buildKeyboardEvent(type, options); } else if (MOUSE_EVENT_TYPES.indexOf(type) > -1) { let rect; if (element instanceof Window) { rect = element.document.documentElement.getBoundingClientRect(); } else if (element instanceof Document) { rect = element.documentElement.getBoundingClientRect(); } else if (element instanceof HTMLElement || element instanceof SVGElement) { rect = element.getBoundingClientRect(); } else { return; } let x = rect.left + 1; let y = rect.top + 1; let simulatedCoordinates = { screenX: x + 5, // Those numbers don't really mean anything. screenY: y + 95, // They're just to make the screenX/Y be different of clientX/Y.. clientX: x, clientY: y }; event = buildMouseEvent(type, assign(simulatedCoordinates, options)); } else if (FILE_SELECTION_EVENT_TYPES.indexOf(type) > -1 && element.files) { event = buildFileEvent(type, element, options); } else { event = buildBasicEvent(type, options); } element.dispatchEvent(event); return event; } /* @method buildBasicEvent @param {String} type @param {Object} (optional) options @return {Event} @private */ function buildBasicEvent(type, options = {}) { let event = document.createEvent('Events'); let bubbles = options.bubbles !== undefined ? options.bubbles : true; let cancelable = options.cancelable !== undefined ? options.cancelable : true; delete options.bubbles; delete options.cancelable; // bubbles and cancelable are readonly, so they can be // set when initializing event event.initEvent(type, bubbles, cancelable); assign(event, options); return event; } /* @method buildMouseEvent @param {String} type @param {Object} (optional) options @return {Event} @private */ function buildMouseEvent(type, options = {}) { let event; try { event = document.createEvent('MouseEvents'); let eventOpts = assign(assign({}, DEFAULT_EVENT_OPTIONS), options); event.initMouseEvent( type, eventOpts.bubbles, eventOpts.cancelable, window, eventOpts.detail, eventOpts.screenX, eventOpts.screenY, eventOpts.clientX, eventOpts.clientY, eventOpts.ctrlKey, eventOpts.altKey, eventOpts.shiftKey, eventOpts.metaKey, eventOpts.button, eventOpts.relatedTarget); } catch(e) { event = buildBasicEvent(type, options); } return event; } /* @method buildKeyboardEvent @param {String} type @param {Object} (optional) options @return {Event} @private */ function buildKeyboardEvent(type, options = {}) { let eventOpts = assign(assign({}, DEFAULT_EVENT_OPTIONS), options); let event, eventMethodName; try { event = new KeyboardEvent(type, eventOpts); // Property definitions are required for B/C for keyboard event usage // If this properties are not defined, when listening for key events // keyCode/which will be 0. Also, keyCode and which now are string // and if app compare it with === with integer key definitions, // there will be a fail. // // https://w3c.github.io/uievents/#interface-keyboardevent // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent Object.defineProperty(event, 'keyCode', { get() { return parseInt(this.key); } }); Object.defineProperty(event, 'which', { get() { return parseInt(this.key); } }); return event; } catch(e) { // left intentionally blank } try { event = document.createEvent('KeyboardEvents'); eventMethodName = 'initKeyboardEvent'; } catch(e) { // left intentionally blank } if (!event) { try { event = document.createEvent('KeyEvents'); eventMethodName = 'initKeyEvent'; } catch(e) { // left intentionally blank } } if (event) { event[eventMethodName]( type, eventOpts.bubbles, eventOpts.cancelable, window, eventOpts.ctrlKey, eventOpts.altKey, eventOpts.shiftKey, eventOpts.metaKey, eventOpts.keyCode, eventOpts.charCode ); } else { event = buildBasicEvent(type, options); } return event; } /* @method buildFileEvent @param {String} type @param {Array} array of files @param {HTMLElement} element @return {Event} @private */ function buildFileEvent(type, element, files = []) { let event = buildBasicEvent(type); if (files.length > 0) { Object.defineProperty(files, 'item', { value(index) { return typeof index === 'number' ? this[index] : null; } }); Object.defineProperty(element, 'files', { value: files }); } Object.defineProperty(event, 'target', { value: element }); return event; }