From ffeed9a51126894659eaffd4b589dd91ae76ae7b Mon Sep 17 00:00:00 2001 From: "Kent C. Dodds" Date: Thu, 11 Jun 2020 09:22:43 -0600 Subject: [PATCH] fix(hover): correctness audit on hover --- src/event-map.js | 708 ++++++++++++++-------------- src/user-event/__tests__/hover.js | 3 + src/user-event/__tests__/unhover.js | 3 + src/user-event/hover.js | 6 + 4 files changed, 371 insertions(+), 349 deletions(-) diff --git a/src/event-map.js b/src/event-map.js index 98d78e663..0eba4a281 100644 --- a/src/event-map.js +++ b/src/event-map.js @@ -1,350 +1,360 @@ 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', - } + // 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, button: -1}, + }, + pointerEnter: { + EventType: 'PointerEvent', + defaultInit: {bubbles: false, cancelable: false, button: -1}, + }, + pointerDown: { + EventType: 'PointerEvent', + defaultInit: {bubbles: true, cancelable: true, composed: true, button: -1}, + }, + pointerMove: { + EventType: 'PointerEvent', + defaultInit: {bubbles: true, cancelable: true, composed: true, button: -1}, + }, + pointerUp: { + EventType: 'PointerEvent', + defaultInit: {bubbles: true, cancelable: true, composed: true, button: -1}, + }, + pointerCancel: { + EventType: 'PointerEvent', + defaultInit: {bubbles: true, cancelable: false, composed: true, button: -1}, + }, + pointerOut: { + EventType: 'PointerEvent', + defaultInit: {bubbles: true, cancelable: true, composed: true, button: -1}, + }, + pointerLeave: { + EventType: 'PointerEvent', + defaultInit: {bubbles: false, cancelable: false, button: -1}, + }, + gotPointerCapture: { + EventType: 'PointerEvent', + defaultInit: { + bubbles: false, + cancelable: false, + composed: true, + button: -1, + }, + }, + lostPointerCapture: { + EventType: 'PointerEvent', + defaultInit: { + bubbles: false, + cancelable: false, + composed: true, + button: -1, + }, + }, + // history events + popState: { + EventType: 'PopStateEvent', + defaultInit: {bubbles: true, cancelable: false}, + }, +} + +export const eventAliasMap = { + doubleClick: 'dblClick', +} diff --git a/src/user-event/__tests__/hover.js b/src/user-event/__tests__/hover.js index 91222d080..e3a410206 100644 --- a/src/user-event/__tests__/hover.js +++ b/src/user-event/__tests__/hover.js @@ -8,8 +8,11 @@ test('hover', async () => { expect(getEventCalls()).toMatchInlineSnapshot(` Events fired on: button + pointerover + pointerenter mouseover: Left (0) mouseenter: Left (0) + pointermove mousemove: Left (0) `) }) diff --git a/src/user-event/__tests__/unhover.js b/src/user-event/__tests__/unhover.js index 6306bcf7e..d5769fb99 100644 --- a/src/user-event/__tests__/unhover.js +++ b/src/user-event/__tests__/unhover.js @@ -8,7 +8,10 @@ test('unhover', async () => { expect(getEventCalls()).toMatchInlineSnapshot(` Events fired on: button + pointermove mousemove: Left (0) + pointerout + pointerleave mouseout: Left (0) mouseleave: Left (0) `) diff --git a/src/user-event/hover.js b/src/user-event/hover.js index a57da9f67..f521e5e05 100644 --- a/src/user-event/hover.js +++ b/src/user-event/hover.js @@ -2,14 +2,20 @@ import {wrapAsync} from '../wrap-async' import {fireEvent, getMouseEventOptions} from './utils' async function hover(element, init) { + await fireEvent.pointerOver(element, init) + await fireEvent.pointerEnter(element, init) await fireEvent.mouseOver(element, getMouseEventOptions('mouseover', init)) await fireEvent.mouseEnter(element, getMouseEventOptions('mouseenter', init)) + await fireEvent.pointerMove(element, init) await fireEvent.mouseMove(element, getMouseEventOptions('mousemove', init)) } hover = wrapAsync(hover) async function unhover(element, init) { + await fireEvent.pointerMove(element, init) await fireEvent.mouseMove(element, getMouseEventOptions('mousemove', init)) + await fireEvent.pointerOut(element, init) + await fireEvent.pointerLeave(element, init) await fireEvent.mouseOut(element, getMouseEventOptions('mouseout', init)) await fireEvent.mouseLeave(element, getMouseEventOptions('mouseleave', init)) }