diff --git a/src/__tests__/events.js b/src/__tests__/events.js index fdf78083..f91cf408 100644 --- a/src/__tests__/events.js +++ b/src/__tests__/events.js @@ -1,9 +1,10 @@ +import {eventMap, eventAliasMap} from '../event-map' import {fireEvent} from '..' const eventTypes = [ { type: 'Clipboard', - events: ['copy', 'paste'], + events: ['copy', 'cut', 'paste'], elementType: 'input', }, { @@ -137,92 +138,19 @@ const eventTypes = [ }, ] -const bubblingEvents = [ - 'copy', - 'cut', - 'paste', - 'compositionEnd', - 'compositionStart', - 'compositionUpdate', - 'keyDown', - 'keyPress', - 'keyUp', - 'focusIn', - 'focusOut', - 'change', - 'input', - 'submit', - 'reset', - 'click', - 'contextMenu', - 'dblClick', - 'drag', - 'dragEnd', - 'dragEnter', - 'dragExit', - 'dragLeave', - 'dragOver', - 'dragStart', - 'drop', - 'mouseDown', - 'mouseMove', - 'mouseOut', - 'mouseOver', - 'mouseUp', - 'select', - 'touchCancel', - 'touchEnd', - 'touchMove', - 'touchStart', - 'wheel', - 'animationStart', - 'animationEnd', - 'animationIteration', - 'transitionEnd', - 'pointerOver', - 'pointerDown', - 'pointerMove', - 'pointerUp', - 'pointerCancel', - 'pointerOut', -] +const allEvents = Object.keys(eventMap) -const nonBubblingEvents = [ - 'focus', - 'blur', - 'invalid', - 'mouseEnter', - 'mouseLeave', - 'scroll', - 'abort', - 'canPlay', - 'canPlayThrough', - 'durationChange', - 'emptied', - 'encrypted', - 'ended', - 'loadedData', - 'loadedMetadata', - 'loadStart', - 'pause', - 'play', - 'playing', - 'progress', - 'rateChange', - 'seeked', - 'seeking', - 'stalled', - 'suspend', - 'timeUpdate', - 'volumeChange', - 'waiting', - 'load', - 'error', - 'pointerEnter', - 'pointerLeave', - 'gotPointerCapture', - 'lostPointerCapture', -] +const bubblingEvents = allEvents + .filter(eventName => eventMap[eventName].defaultInit.bubbles) + +const composedEvents = allEvents + .filter(eventName => eventMap[eventName].defaultInit.composed) + +const nonBubblingEvents = allEvents + .filter(eventName => !bubblingEvents.includes(eventName)) + +const nonComposedEvents = allEvents + .filter(eventName => !composedEvents.includes(eventName)) eventTypes.forEach(({type, events, elementType}) => { describe(`${type} Events`, () => { @@ -268,13 +196,48 @@ describe(`Bubbling Events`, () => { ) }) +describe(`Composed Events`, () => { + composedEvents.forEach(event => + it(`${event} crosses shadow DOM boundary`, () => { + const node = document.createElement('div') + const spy = jest.fn() + node.addEventListener(event.toLowerCase(), spy) + + const shadowRoot = node.attachShadow({ mode: 'closed' }) + const innerNode = document.createElement('div') + shadowRoot.appendChild(innerNode) + + fireEvent[event](innerNode) + expect(spy).toHaveBeenCalledTimes(1) + }), + ) + + nonComposedEvents.forEach(event => + it(`${event} does not cross shadow DOM boundary`, () => { + const node = document.createElement('div') + const spy = jest.fn() + node.addEventListener(event.toLowerCase(), spy) + + const shadowRoot = node.attachShadow({ mode: 'closed' }) + const innerNode = document.createElement('div') + shadowRoot.appendChild(innerNode) + + fireEvent[event](innerNode) + expect(spy).not.toHaveBeenCalled() + }), + ) +}) + describe(`Aliased Events`, () => { - it(`fires doubleClick`, () => { - const node = document.createElement('div') - const spy = jest.fn() - node.addEventListener('dblclick', spy) - fireEvent.doubleClick(node) - expect(spy).toHaveBeenCalledTimes(1) + Object.keys(eventAliasMap).forEach(eventAlias => { + it(`fires ${eventAlias}`, () => { + const node = document.createElement('div') + const spy = jest.fn() + node.addEventListener(eventAliasMap[eventAlias].toLowerCase(), spy) + + fireEvent[eventAlias](node) + expect(spy).toHaveBeenCalledTimes(1) + }) }) }) diff --git a/src/event-map.js b/src/event-map.js new file mode 100644 index 00000000..98d78e66 --- /dev/null +++ b/src/event-map.js @@ -0,0 +1,350 @@ +export const eventMap = { + // Clipboard Events + copy: { + EventType: 'ClipboardEvent', + defaultInit: {bubbles: true, cancelable: true, composed: true}, + }, + cut: { + EventType: 'ClipboardEvent', + defaultInit: {bubbles: true, cancelable: true, composed: true}, + }, + paste: { + EventType: 'ClipboardEvent', + defaultInit: {bubbles: true, cancelable: true, composed: true}, + }, + // Composition Events + compositionEnd: { + EventType: 'CompositionEvent', + defaultInit: {bubbles: true, cancelable: true, composed: true}, + }, + compositionStart: { + EventType: 'CompositionEvent', + defaultInit: {bubbles: true, cancelable: true, composed: true}, + }, + compositionUpdate: { + EventType: 'CompositionEvent', + defaultInit: {bubbles: true, cancelable: true, composed: true}, + }, + // Keyboard Events + keyDown: { + EventType: 'KeyboardEvent', + defaultInit: {bubbles: true, cancelable: true, charCode: 0, composed: true}, + }, + keyPress: { + EventType: 'KeyboardEvent', + defaultInit: {bubbles: true, cancelable: true, charCode: 0, composed: true}, + }, + keyUp: { + EventType: 'KeyboardEvent', + defaultInit: {bubbles: true, cancelable: true, charCode: 0, composed: true}, + }, + // Focus Events + focus: { + EventType: 'FocusEvent', + defaultInit: {bubbles: false, cancelable: false, composed: true}, + }, + blur: { + EventType: 'FocusEvent', + defaultInit: {bubbles: false, cancelable: false, composed: true}, + }, + focusIn: { + EventType: 'FocusEvent', + defaultInit: {bubbles: true, cancelable: false, composed: true}, + }, + focusOut: { + EventType: 'FocusEvent', + defaultInit: {bubbles: true, cancelable: false, composed: true}, + }, + // Form Events + change: { + EventType: 'Event', + defaultInit: {bubbles: true, cancelable: false}, + }, + input: { + EventType: 'InputEvent', + defaultInit: {bubbles: true, cancelable: false, composed: true}, + }, + invalid: { + EventType: 'Event', + defaultInit: {bubbles: false, cancelable: true}, + }, + submit: { + EventType: 'Event', + defaultInit: {bubbles: true, cancelable: true}, + }, + reset: { + EventType: 'Event', + defaultInit: {bubbles: true, cancelable: true}, + }, + // Mouse Events + click: { + EventType: 'MouseEvent', + defaultInit: {bubbles: true, cancelable: true, button: 0, composed: true}, + }, + contextMenu: { + EventType: 'MouseEvent', + defaultInit: {bubbles: true, cancelable: true, composed: true}, + }, + dblClick: { + EventType: 'MouseEvent', + defaultInit: {bubbles: true, cancelable: true, composed: true}, + }, + drag: { + EventType: 'DragEvent', + defaultInit: {bubbles: true, cancelable: true, composed: true}, + }, + dragEnd: { + EventType: 'DragEvent', + defaultInit: {bubbles: true, cancelable: false, composed: true}, + }, + dragEnter: { + EventType: 'DragEvent', + defaultInit: {bubbles: true, cancelable: true, composed: true}, + }, + dragExit: { + EventType: 'DragEvent', + defaultInit: {bubbles: true, cancelable: false, composed: true}, + }, + dragLeave: { + EventType: 'DragEvent', + defaultInit: {bubbles: true, cancelable: false, composed: true}, + }, + dragOver: { + EventType: 'DragEvent', + defaultInit: {bubbles: true, cancelable: true, composed: true}, + }, + dragStart: { + EventType: 'DragEvent', + defaultInit: {bubbles: true, cancelable: true, composed: true}, + }, + drop: { + EventType: 'DragEvent', + defaultInit: {bubbles: true, cancelable: true, composed: true}, + }, + mouseDown: { + EventType: 'MouseEvent', + defaultInit: {bubbles: true, cancelable: true, composed: true}, + }, + mouseEnter: { + EventType: 'MouseEvent', + defaultInit: {bubbles: false, cancelable: false, composed: true}, + }, + mouseLeave: { + EventType: 'MouseEvent', + defaultInit: {bubbles: false, cancelable: false, composed: true}, + }, + mouseMove: { + EventType: 'MouseEvent', + defaultInit: {bubbles: true, cancelable: true, composed: true}, + }, + mouseOut: { + EventType: 'MouseEvent', + defaultInit: {bubbles: true, cancelable: true, composed: true}, + }, + mouseOver: { + EventType: 'MouseEvent', + defaultInit: {bubbles: true, cancelable: true, composed: true}, + }, + mouseUp: { + EventType: 'MouseEvent', + defaultInit: {bubbles: true, cancelable: true, composed: true}, + }, + // Selection Events + select: { + EventType: 'Event', + defaultInit: {bubbles: true, cancelable: false}, + }, + // Touch Events + touchCancel: { + EventType: 'TouchEvent', + defaultInit: {bubbles: true, cancelable: false, composed: true}, + }, + touchEnd: { + EventType: 'TouchEvent', + defaultInit: {bubbles: true, cancelable: true, composed: true}, + }, + touchMove: { + EventType: 'TouchEvent', + defaultInit: {bubbles: true, cancelable: true, composed: true}, + }, + touchStart: { + EventType: 'TouchEvent', + defaultInit: {bubbles: true, cancelable: true, composed: true}, + }, + // UI Events + scroll: { + EventType: 'UIEvent', + defaultInit: {bubbles: false, cancelable: false}, + }, + // Wheel Events + wheel: { + EventType: 'WheelEvent', + defaultInit: {bubbles: true, cancelable: true, composed: true}, + }, + // Media Events + abort: { + EventType: 'Event', + defaultInit: {bubbles: false, cancelable: false}, + }, + canPlay: { + EventType: 'Event', + defaultInit: {bubbles: false, cancelable: false}, + }, + canPlayThrough: { + EventType: 'Event', + defaultInit: {bubbles: false, cancelable: false}, + }, + durationChange: { + EventType: 'Event', + defaultInit: {bubbles: false, cancelable: false}, + }, + emptied: { + EventType: 'Event', + defaultInit: {bubbles: false, cancelable: false}, + }, + encrypted: { + EventType: 'Event', + defaultInit: {bubbles: false, cancelable: false}, + }, + ended: { + EventType: 'Event', + defaultInit: {bubbles: false, cancelable: false}, + }, + loadedData: { + EventType: 'Event', + defaultInit: {bubbles: false, cancelable: false}, + }, + loadedMetadata: { + EventType: 'Event', + defaultInit: {bubbles: false, cancelable: false}, + }, + loadStart: { + EventType: 'ProgressEvent', + defaultInit: {bubbles: false, cancelable: false}, + }, + pause: { + EventType: 'Event', + defaultInit: {bubbles: false, cancelable: false}, + }, + play: { + EventType: 'Event', + defaultInit: {bubbles: false, cancelable: false}, + }, + playing: { + EventType: 'Event', + defaultInit: {bubbles: false, cancelable: false}, + }, + progress: { + EventType: 'ProgressEvent', + defaultInit: {bubbles: false, cancelable: false}, + }, + rateChange: { + EventType: 'Event', + defaultInit: {bubbles: false, cancelable: false}, + }, + seeked: { + EventType: 'Event', + defaultInit: {bubbles: false, cancelable: false}, + }, + seeking: { + EventType: 'Event', + defaultInit: {bubbles: false, cancelable: false}, + }, + stalled: { + EventType: 'Event', + defaultInit: {bubbles: false, cancelable: false}, + }, + suspend: { + EventType: 'Event', + defaultInit: {bubbles: false, cancelable: false}, + }, + timeUpdate: { + EventType: 'Event', + defaultInit: {bubbles: false, cancelable: false}, + }, + volumeChange: { + EventType: 'Event', + defaultInit: {bubbles: false, cancelable: false}, + }, + waiting: { + EventType: 'Event', + defaultInit: {bubbles: false, cancelable: false}, + }, + // Image Events + load: { + EventType: 'UIEvent', + defaultInit: {bubbles: false, cancelable: false}, + }, + error: { + EventType: 'Event', + defaultInit: {bubbles: false, cancelable: false}, + }, + // Animation Events + animationStart: { + EventType: 'AnimationEvent', + defaultInit: {bubbles: true, cancelable: false}, + }, + animationEnd: { + EventType: 'AnimationEvent', + defaultInit: {bubbles: true, cancelable: false}, + }, + animationIteration: { + EventType: 'AnimationEvent', + defaultInit: {bubbles: true, cancelable: false}, + }, + // Transition Events + transitionEnd: { + EventType: 'TransitionEvent', + defaultInit: {bubbles: true, cancelable: true}, + }, + // pointer events + pointerOver: { + EventType: 'PointerEvent', + defaultInit: {bubbles: true, cancelable: true, composed: true}, + }, + pointerEnter: { + EventType: 'PointerEvent', + defaultInit: {bubbles: false, cancelable: false}, + }, + pointerDown: { + EventType: 'PointerEvent', + defaultInit: {bubbles: true, cancelable: true, composed: true}, + }, + pointerMove: { + EventType: 'PointerEvent', + defaultInit: {bubbles: true, cancelable: true, composed: true}, + }, + pointerUp: { + EventType: 'PointerEvent', + defaultInit: {bubbles: true, cancelable: true, composed: true}, + }, + pointerCancel: { + EventType: 'PointerEvent', + defaultInit: {bubbles: true, cancelable: false, composed: true}, + }, + pointerOut: { + EventType: 'PointerEvent', + defaultInit: {bubbles: true, cancelable: true, composed: true}, + }, + pointerLeave: { + EventType: 'PointerEvent', + defaultInit: {bubbles: false, cancelable: false}, + }, + gotPointerCapture: { + EventType: 'PointerEvent', + defaultInit: {bubbles: false, cancelable: false, composed: true}, + }, + lostPointerCapture: { + EventType: 'PointerEvent', + defaultInit: {bubbles: false, cancelable: false, composed: true}, + }, + // history events + popState: { + EventType: 'PopStateEvent', + defaultInit: {bubbles: true, cancelable: false}, + }, + } + + export const eventAliasMap = { + doubleClick: 'dblClick', + } diff --git a/src/events.js b/src/events.js index f6896c3c..389fdab3 100644 --- a/src/events.js +++ b/src/events.js @@ -1,354 +1,5 @@ import {getWindowFromNode} from './helpers' -const eventMap = { - // Clipboard Events - copy: { - EventType: 'ClipboardEvent', - defaultInit: {bubbles: true, cancelable: true}, - }, - cut: { - EventType: 'ClipboardEvent', - defaultInit: {bubbles: true, cancelable: true}, - }, - paste: { - EventType: 'ClipboardEvent', - defaultInit: {bubbles: true, cancelable: true}, - }, - // Composition Events - compositionEnd: { - EventType: 'CompositionEvent', - defaultInit: {bubbles: true, cancelable: true}, - }, - compositionStart: { - EventType: 'CompositionEvent', - defaultInit: {bubbles: true, cancelable: true}, - }, - compositionUpdate: { - EventType: 'CompositionEvent', - defaultInit: {bubbles: true, cancelable: true}, - }, - // Keyboard Events - keyDown: { - EventType: 'KeyboardEvent', - defaultInit: {bubbles: true, cancelable: true, charCode: 0}, - }, - keyPress: { - EventType: 'KeyboardEvent', - defaultInit: {bubbles: true, cancelable: true, charCode: 0}, - }, - keyUp: { - EventType: 'KeyboardEvent', - defaultInit: {bubbles: true, cancelable: true, charCode: 0}, - }, - // Focus Events - focus: { - EventType: 'FocusEvent', - defaultInit: {bubbles: false, cancelable: false}, - }, - blur: { - EventType: 'FocusEvent', - defaultInit: {bubbles: false, cancelable: false}, - }, - focusIn: { - EventType: 'FocusEvent', - defaultInit: {bubbles: true, cancelable: false}, - }, - focusOut: { - EventType: 'FocusEvent', - defaultInit: {bubbles: true, cancelable: false}, - }, - // Form Events - change: { - EventType: 'Event', - defaultInit: {bubbles: true, cancelable: false}, - }, - input: { - EventType: 'InputEvent', - defaultInit: {bubbles: true, cancelable: false}, - }, - invalid: { - EventType: 'Event', - defaultInit: {bubbles: false, cancelable: true}, - }, - submit: { - EventType: 'Event', - defaultInit: {bubbles: true, cancelable: true}, - }, - reset: { - EventType: 'Event', - defaultInit: {bubbles: true, cancelable: true}, - }, - // Mouse Events - click: { - EventType: 'MouseEvent', - defaultInit: {bubbles: true, cancelable: true, button: 0}, - }, - contextMenu: { - EventType: 'MouseEvent', - defaultInit: {bubbles: true, cancelable: true}, - }, - dblClick: { - EventType: 'MouseEvent', - defaultInit: {bubbles: true, cancelable: true}, - }, - drag: { - EventType: 'DragEvent', - defaultInit: {bubbles: true, cancelable: true}, - }, - dragEnd: { - EventType: 'DragEvent', - defaultInit: {bubbles: true, cancelable: false}, - }, - dragEnter: { - EventType: 'DragEvent', - defaultInit: {bubbles: true, cancelable: true}, - }, - dragExit: { - EventType: 'DragEvent', - defaultInit: {bubbles: true, cancelable: false}, - }, - dragLeave: { - EventType: 'DragEvent', - defaultInit: {bubbles: true, cancelable: false}, - }, - dragOver: { - EventType: 'DragEvent', - defaultInit: {bubbles: true, cancelable: true}, - }, - dragStart: { - EventType: 'DragEvent', - defaultInit: {bubbles: true, cancelable: true}, - }, - drop: { - EventType: 'DragEvent', - defaultInit: {bubbles: true, cancelable: true}, - }, - mouseDown: { - EventType: 'MouseEvent', - defaultInit: {bubbles: true, cancelable: true}, - }, - mouseEnter: { - EventType: 'MouseEvent', - defaultInit: {bubbles: false, cancelable: false}, - }, - mouseLeave: { - EventType: 'MouseEvent', - defaultInit: {bubbles: false, cancelable: false}, - }, - mouseMove: { - EventType: 'MouseEvent', - defaultInit: {bubbles: true, cancelable: true}, - }, - mouseOut: { - EventType: 'MouseEvent', - defaultInit: {bubbles: true, cancelable: true}, - }, - mouseOver: { - EventType: 'MouseEvent', - defaultInit: {bubbles: true, cancelable: true}, - }, - mouseUp: { - EventType: 'MouseEvent', - defaultInit: {bubbles: true, cancelable: true}, - }, - // Selection Events - select: { - EventType: 'Event', - defaultInit: {bubbles: true, cancelable: false}, - }, - // Touch Events - touchCancel: { - EventType: 'TouchEvent', - defaultInit: {bubbles: true, cancelable: false}, - }, - touchEnd: { - EventType: 'TouchEvent', - defaultInit: {bubbles: true, cancelable: true}, - }, - touchMove: { - EventType: 'TouchEvent', - defaultInit: {bubbles: true, cancelable: true}, - }, - touchStart: { - EventType: 'TouchEvent', - defaultInit: {bubbles: true, cancelable: true}, - }, - // UI Events - scroll: { - EventType: 'UIEvent', - defaultInit: {bubbles: false, cancelable: false}, - }, - // Wheel Events - wheel: { - EventType: 'WheelEvent', - defaultInit: {bubbles: true, cancelable: true}, - }, - // Media Events - abort: { - EventType: 'Event', - defaultInit: {bubbles: false, cancelable: false}, - }, - canPlay: { - EventType: 'Event', - defaultInit: {bubbles: false, cancelable: false}, - }, - canPlayThrough: { - EventType: 'Event', - defaultInit: {bubbles: false, cancelable: false}, - }, - durationChange: { - EventType: 'Event', - defaultInit: {bubbles: false, cancelable: false}, - }, - emptied: { - EventType: 'Event', - defaultInit: {bubbles: false, cancelable: false}, - }, - encrypted: { - EventType: 'Event', - defaultInit: {bubbles: false, cancelable: false}, - }, - ended: { - EventType: 'Event', - defaultInit: {bubbles: false, cancelable: false}, - }, - loadedData: { - EventType: 'Event', - defaultInit: {bubbles: false, cancelable: false}, - }, - loadedMetadata: { - EventType: 'Event', - defaultInit: {bubbles: false, cancelable: false}, - }, - loadStart: { - EventType: 'ProgressEvent', - defaultInit: {bubbles: false, cancelable: false}, - }, - pause: { - EventType: 'Event', - defaultInit: {bubbles: false, cancelable: false}, - }, - play: { - EventType: 'Event', - defaultInit: {bubbles: false, cancelable: false}, - }, - playing: { - EventType: 'Event', - defaultInit: {bubbles: false, cancelable: false}, - }, - progress: { - EventType: 'ProgressEvent', - defaultInit: {bubbles: false, cancelable: false}, - }, - rateChange: { - EventType: 'Event', - defaultInit: {bubbles: false, cancelable: false}, - }, - seeked: { - EventType: 'Event', - defaultInit: {bubbles: false, cancelable: false}, - }, - seeking: { - EventType: 'Event', - defaultInit: {bubbles: false, cancelable: false}, - }, - stalled: { - EventType: 'Event', - defaultInit: {bubbles: false, cancelable: false}, - }, - suspend: { - EventType: 'Event', - defaultInit: {bubbles: false, cancelable: false}, - }, - timeUpdate: { - EventType: 'Event', - defaultInit: {bubbles: false, cancelable: false}, - }, - volumeChange: { - EventType: 'Event', - defaultInit: {bubbles: false, cancelable: false}, - }, - waiting: { - EventType: 'Event', - defaultInit: {bubbles: false, cancelable: false}, - }, - // Image Events - load: { - EventType: 'UIEvent', - defaultInit: {bubbles: false, cancelable: false}, - }, - error: { - EventType: 'Event', - defaultInit: {bubbles: false, cancelable: false}, - }, - // Animation Events - animationStart: { - EventType: 'AnimationEvent', - defaultInit: {bubbles: true, cancelable: false}, - }, - animationEnd: { - EventType: 'AnimationEvent', - defaultInit: {bubbles: true, cancelable: false}, - }, - animationIteration: { - EventType: 'AnimationEvent', - defaultInit: {bubbles: true, cancelable: false}, - }, - // Transition Events - transitionEnd: { - EventType: 'TransitionEvent', - defaultInit: {bubbles: true, cancelable: true}, - }, - // pointer events - pointerOver: { - EventType: 'PointerEvent', - defaultInit: {bubbles: true, cancelable: true}, - }, - pointerEnter: { - EventType: 'PointerEvent', - defaultInit: {bubbles: false, cancelable: false}, - }, - pointerDown: { - EventType: 'PointerEvent', - defaultInit: {bubbles: true, cancelable: true}, - }, - pointerMove: { - EventType: 'PointerEvent', - defaultInit: {bubbles: true, cancelable: true}, - }, - pointerUp: { - EventType: 'PointerEvent', - defaultInit: {bubbles: true, cancelable: true}, - }, - pointerCancel: { - EventType: 'PointerEvent', - defaultInit: {bubbles: true, cancelable: false}, - }, - pointerOut: { - EventType: 'PointerEvent', - defaultInit: {bubbles: true, cancelable: true}, - }, - pointerLeave: { - EventType: 'PointerEvent', - defaultInit: {bubbles: false, cancelable: false}, - }, - gotPointerCapture: { - EventType: 'PointerEvent', - defaultInit: {bubbles: false, cancelable: false}, - }, - lostPointerCapture: { - EventType: 'PointerEvent', - defaultInit: {bubbles: false, cancelable: false}, - }, - // history events - popState: { - EventType: 'PopStateEvent', - defaultInit: {bubbles: true, cancelable: false}, - }, -} - -const eventAliasMap = { - doubleClick: 'dblClick', -} +import {eventMap, eventAliasMap} from './event-map' function fireEvent(element, event) { if (!event) {