From 9afb37f9a255ac890f56a41100f1221c7a223f3e Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Fri, 20 Jul 2018 23:03:37 -0700 Subject: [PATCH 1/3] [enzyme-adapter-utils] [new] `mapNativeEventNames`, `propFromEvent`: add support for pointer events --- packages/enzyme-adapter-utils/src/Utils.js | 17 +++++++++-- .../enzyme-test-suite/test/Utils-spec.jsx | 30 +++++++++++++++---- 2 files changed, 40 insertions(+), 7 deletions(-) diff --git a/packages/enzyme-adapter-utils/src/Utils.js b/packages/enzyme-adapter-utils/src/Utils.js index 6f3f77a72..89fb1a2c9 100644 --- a/packages/enzyme-adapter-utils/src/Utils.js +++ b/packages/enzyme-adapter-utils/src/Utils.js @@ -6,6 +6,7 @@ export { createMountWrapper, createRenderWrapper }; export function mapNativeEventNames(event, { animation = false, // should be true for React 15+ + pointerEvents = false, // should be true for React 16.4+ } = {}) { const nativeToReactEventMap = { compositionend: 'compositionEnd', @@ -50,6 +51,18 @@ export function mapNativeEventNames(event, { animationiteration: 'animationIteration', animationend: 'animationEnd', }), + ...(pointerEvents && { + pointerdown: 'pointerDown', + pointermove: 'pointerMove', + pointerup: 'pointerUp', + pointercancel: 'pointerCancel', + gotpointercapture: 'gotPointerCapture', + lostpointercapture: 'lostPointerCapture', + pointerenter: 'pointerEnter', + pointerleave: 'pointerLeave', + pointerover: 'pointerOver', + pointerout: 'pointerOut', + }), }; return nativeToReactEventMap[event] || event; @@ -57,8 +70,8 @@ export function mapNativeEventNames(event, { // 'click' => 'onClick' // 'mouseEnter' => 'onMouseEnter' -export function propFromEvent(event) { - const nativeEvent = mapNativeEventNames(event); +export function propFromEvent(event, eventOptions = {}) { + const nativeEvent = mapNativeEventNames(event, eventOptions); return `on${nativeEvent[0].toUpperCase()}${nativeEvent.slice(1)}`; } diff --git a/packages/enzyme-test-suite/test/Utils-spec.jsx b/packages/enzyme-test-suite/test/Utils-spec.jsx index 94fac9b45..c40e5c731 100644 --- a/packages/enzyme-test-suite/test/Utils-spec.jsx +++ b/packages/enzyme-test-suite/test/Utils-spec.jsx @@ -441,11 +441,26 @@ describe('Utils', () => { }); describe('propFromEvent', () => { - const fn = propFromEvent; - it('should work', () => { - expect(fn('click')).to.equal('onClick'); - expect(fn('mouseEnter')).to.equal('onMouseEnter'); + expect(propFromEvent('click')).to.equal('onClick'); + expect(propFromEvent('mouseEnter')).to.equal('onMouseEnter'); + }); + + describe('conditionally supported events', () => { + it('ignores unsupported events', () => { + const result = propFromEvent('animationIteration'); + expect(result).to.equal('onAnimationIteration'); + }); + + it('transforms animation events when supported', () => { + const result = propFromEvent('animationIteration', { animation: true }); + expect(result).to.equal('onAnimationIteration'); + }); + + it('transforms pointer events when supported', () => { + const result = propFromEvent('pointerOver', { pointerEvents: true }); + expect(result).to.equal('onPointerOver'); + }); }); }); @@ -478,10 +493,15 @@ describe('Utils', () => { expect(result).to.equal('animationiteration'); }); - it('transforms events when supported', () => { + it('transforms animation events when supported', () => { const result = mapNativeEventNames('animationiteration', { animation: true }); expect(result).to.equal('animationIteration'); }); + + it('transforms pointer events when supported', () => { + const result = mapNativeEventNames('pointerover', { pointerEvents: true }); + expect(result).to.equal('pointerOver'); + }); }); }); From 248cbc4842d2859a58f685b78a8969234e43cd54 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Fri, 20 Jul 2018 23:08:00 -0700 Subject: [PATCH 2/3] [enzyme-adapter-react-{15,15.4,16.1,16.2,16.3,16}] improve animation events support --- .../src/ReactFifteenFourAdapter.js | 6 ++-- .../src/ReactFifteenAdapter.js | 6 ++-- .../src/ReactSixteenOneAdapter.js | 6 ++-- .../src/ReactSixteenTwoAdapter.js | 6 ++-- .../src/ReactSixteenThreeAdapter.js | 6 ++-- .../src/ReactSixteenAdapter.js | 13 ++++---- .../test/ReactWrapper-spec.jsx | 30 +++++++++++++++++++ .../test/ShallowWrapper-spec.jsx | 30 +++++++++++++++++++ 8 files changed, 88 insertions(+), 15 deletions(-) diff --git a/packages/enzyme-adapter-react-15.4/src/ReactFifteenFourAdapter.js b/packages/enzyme-adapter-react-15.4/src/ReactFifteenFourAdapter.js index 81897c7ae..1515fce88 100644 --- a/packages/enzyme-adapter-react-15.4/src/ReactFifteenFourAdapter.js +++ b/packages/enzyme-adapter-react-15.4/src/ReactFifteenFourAdapter.js @@ -103,6 +103,8 @@ function instanceToTree(inst) { }; } +const eventOptions = { animation: true }; + class ReactFifteenFourAdapter extends EnzymeAdapter { constructor() { super(); @@ -153,7 +155,7 @@ class ReactFifteenFourAdapter extends EnzymeAdapter { return instance ? instanceToTree(instance._reactInternalInstance).rendered : null; }, simulateEvent(node, event, mock) { - const mappedEvent = mapNativeEventNames(event, { animation: true }); + const mappedEvent = mapNativeEventNames(event, eventOptions); const eventFn = TestUtils.Simulate[mappedEvent]; if (!eventFn) { throw new TypeError(`ReactWrapper::simulate() event '${event}' does not exist`); @@ -201,7 +203,7 @@ class ReactFifteenFourAdapter extends EnzymeAdapter { }; }, simulateEvent(node, event, ...args) { - const handler = node.props[propFromEvent(event)]; + const handler = node.props[propFromEvent(event, eventOptions)]; if (handler) { withSetStateAllowed(() => { // TODO(lmr): create/use synthetic events diff --git a/packages/enzyme-adapter-react-15/src/ReactFifteenAdapter.js b/packages/enzyme-adapter-react-15/src/ReactFifteenAdapter.js index 0c9deaed7..9dfa458a6 100644 --- a/packages/enzyme-adapter-react-15/src/ReactFifteenAdapter.js +++ b/packages/enzyme-adapter-react-15/src/ReactFifteenAdapter.js @@ -103,6 +103,8 @@ function instanceToTree(inst) { }; } +const eventOptions = { animation: true }; + class ReactFifteenAdapter extends EnzymeAdapter { constructor() { super(); @@ -153,7 +155,7 @@ class ReactFifteenAdapter extends EnzymeAdapter { return instance ? instanceToTree(instance._reactInternalInstance).rendered : null; }, simulateEvent(node, event, mock) { - const mappedEvent = mapNativeEventNames(event, { animation: true }); + const mappedEvent = mapNativeEventNames(event, eventOptions); const eventFn = TestUtils.Simulate[mappedEvent]; if (!eventFn) { throw new TypeError(`ReactWrapper::simulate() event '${event}' does not exist`); @@ -201,7 +203,7 @@ class ReactFifteenAdapter extends EnzymeAdapter { }; }, simulateEvent(node, event, ...args) { - const handler = node.props[propFromEvent(event)]; + const handler = node.props[propFromEvent(event, eventOptions)]; if (handler) { withSetStateAllowed(() => { // TODO(lmr): create/use synthetic events diff --git a/packages/enzyme-adapter-react-16.1/src/ReactSixteenOneAdapter.js b/packages/enzyme-adapter-react-16.1/src/ReactSixteenOneAdapter.js index 3608f6733..5058dae4f 100644 --- a/packages/enzyme-adapter-react-16.1/src/ReactSixteenOneAdapter.js +++ b/packages/enzyme-adapter-react-16.1/src/ReactSixteenOneAdapter.js @@ -166,6 +166,8 @@ function nodeToHostNode(_node) { return ReactDOM.findDOMNode(node.instance); } +const eventOptions = { animation: true }; + class ReactSixteenOneAdapter extends EnzymeAdapter { constructor() { super(); @@ -218,7 +220,7 @@ class ReactSixteenOneAdapter extends EnzymeAdapter { return instance ? toTree(instance._reactInternalFiber).rendered : null; }, simulateEvent(node, event, mock) { - const mappedEvent = mapNativeEventNames(event, { animation: true }); + const mappedEvent = mapNativeEventNames(event, eventOptions); const eventFn = TestUtils.Simulate[mappedEvent]; if (!eventFn) { throw new TypeError(`ReactWrapper::simulate() event '${event}' does not exist`); @@ -283,7 +285,7 @@ class ReactSixteenOneAdapter extends EnzymeAdapter { }; }, simulateEvent(node, event, ...args) { - const handler = node.props[propFromEvent(event)]; + const handler = node.props[propFromEvent(event, eventOptions)]; if (handler) { withSetStateAllowed(() => { // TODO(lmr): create/use synthetic events diff --git a/packages/enzyme-adapter-react-16.2/src/ReactSixteenTwoAdapter.js b/packages/enzyme-adapter-react-16.2/src/ReactSixteenTwoAdapter.js index 6e94a9850..7e67a0fe0 100644 --- a/packages/enzyme-adapter-react-16.2/src/ReactSixteenTwoAdapter.js +++ b/packages/enzyme-adapter-react-16.2/src/ReactSixteenTwoAdapter.js @@ -167,6 +167,8 @@ function nodeToHostNode(_node) { return ReactDOM.findDOMNode(node.instance); } +const eventOptions = { animation: true }; + class ReactSixteenTwoAdapter extends EnzymeAdapter { constructor() { super(); @@ -220,7 +222,7 @@ class ReactSixteenTwoAdapter extends EnzymeAdapter { return instance ? toTree(instance._reactInternalFiber).rendered : null; }, simulateEvent(node, event, mock) { - const mappedEvent = mapNativeEventNames(event, { animation: true }); + const mappedEvent = mapNativeEventNames(event, eventOptions); const eventFn = TestUtils.Simulate[mappedEvent]; if (!eventFn) { throw new TypeError(`ReactWrapper::simulate() event '${event}' does not exist`); @@ -285,7 +287,7 @@ class ReactSixteenTwoAdapter extends EnzymeAdapter { }; }, simulateEvent(node, event, ...args) { - const handler = node.props[propFromEvent(event)]; + const handler = node.props[propFromEvent(event, eventOptions)]; if (handler) { withSetStateAllowed(() => { // TODO(lmr): create/use synthetic events diff --git a/packages/enzyme-adapter-react-16.3/src/ReactSixteenThreeAdapter.js b/packages/enzyme-adapter-react-16.3/src/ReactSixteenThreeAdapter.js index 0524d069d..bff713326 100644 --- a/packages/enzyme-adapter-react-16.3/src/ReactSixteenThreeAdapter.js +++ b/packages/enzyme-adapter-react-16.3/src/ReactSixteenThreeAdapter.js @@ -185,6 +185,8 @@ function nodeToHostNode(_node) { return ReactDOM.findDOMNode(node.instance); } +const eventOptions = { animation: true }; + class ReactSixteenThreeAdapter extends EnzymeAdapter { constructor() { super(); @@ -238,7 +240,7 @@ class ReactSixteenThreeAdapter extends EnzymeAdapter { return instance ? toTree(instance._reactInternalFiber).rendered : null; }, simulateEvent(node, event, mock) { - const mappedEvent = mapNativeEventNames(event, { animation: true }); + const mappedEvent = mapNativeEventNames(event, eventOptions); const eventFn = TestUtils.Simulate[mappedEvent]; if (!eventFn) { throw new TypeError(`ReactWrapper::simulate() event '${event}' does not exist`); @@ -303,7 +305,7 @@ class ReactSixteenThreeAdapter extends EnzymeAdapter { }; }, simulateEvent(node, event, ...args) { - const handler = node.props[propFromEvent(event)]; + const handler = node.props[propFromEvent(event, eventOptions)]; if (handler) { withSetStateAllowed(() => { // TODO(lmr): create/use synthetic events diff --git a/packages/enzyme-adapter-react-16/src/ReactSixteenAdapter.js b/packages/enzyme-adapter-react-16/src/ReactSixteenAdapter.js index 1cce2322a..b6c8b43d2 100644 --- a/packages/enzyme-adapter-react-16/src/ReactSixteenAdapter.js +++ b/packages/enzyme-adapter-react-16/src/ReactSixteenAdapter.js @@ -186,6 +186,10 @@ function nodeToHostNode(_node) { return ReactDOM.findDOMNode(node.instance); } +const eventOptions = { + animation: true, +}; + class ReactSixteenAdapter extends EnzymeAdapter { constructor() { super(); @@ -239,12 +243,11 @@ class ReactSixteenAdapter extends EnzymeAdapter { return instance ? toTree(instance._reactInternalFiber).rendered : null; }, simulateEvent(node, event, mock) { - const mappedEvent = mapNativeEventNames(event, { animation: true }); + const mappedEvent = mapNativeEventNames(event, eventOptions); const eventFn = TestUtils.Simulate[mappedEvent]; if (!eventFn) { throw new TypeError(`ReactWrapper::simulate() event '${event}' does not exist`); } - // eslint-disable-next-line react/no-find-dom-node eventFn(nodeToHostNode(node), mock); }, batchedUpdates(fn) { @@ -304,7 +307,7 @@ class ReactSixteenAdapter extends EnzymeAdapter { }; }, simulateEvent(node, event, ...args) { - const handler = node.props[propFromEvent(event)]; + const handler = node.props[propFromEvent(event, eventOptions)]; if (handler) { withSetStateAllowed(() => { // TODO(lmr): create/use synthetic events @@ -340,7 +343,7 @@ class ReactSixteenAdapter extends EnzymeAdapter { // Provided a bag of options, return an `EnzymeRenderer`. Some options can be implementation // specific, like `attach` etc. for React, but not part of this interface explicitly. - // eslint-disable-next-line class-methods-use-this, no-unused-vars + // eslint-disable-next-line class-methods-use-this createRenderer(options) { switch (options.mode) { case EnzymeAdapter.MODES.MOUNT: return this.createMountRenderer(options); @@ -354,7 +357,7 @@ class ReactSixteenAdapter extends EnzymeAdapter { // converts an RSTNode to the corresponding JSX Pragma Element. This will be needed // in order to implement the `Wrapper.mount()` and `Wrapper.shallow()` methods, but should // be pretty straightforward for people to implement. - // eslint-disable-next-line class-methods-use-this, no-unused-vars + // eslint-disable-next-line class-methods-use-this nodeToElement(node) { if (!node || typeof node !== 'object') return null; return React.createElement(node.type, propsWithKeysAndRef(node)); diff --git a/packages/enzyme-test-suite/test/ReactWrapper-spec.jsx b/packages/enzyme-test-suite/test/ReactWrapper-spec.jsx index e9876d79c..7ac327496 100644 --- a/packages/enzyme-test-suite/test/ReactWrapper-spec.jsx +++ b/packages/enzyme-test-suite/test/ReactWrapper-spec.jsx @@ -2061,6 +2061,36 @@ describeWithDOM('mount', () => { expect(spy).to.have.property('callCount', 1); }); }); + + describeIf(is('>= 15'), 'animation events', () => { + it('should convert lowercase events to React camelcase', () => { + const spy = sinon.spy(); + class Foo extends React.Component { + render() { + return ( + foo + ); + } + } + + const wrapper = mount(); + + wrapper.simulate('animationiteration'); + expect(spy).to.have.property('callCount', 1); + }); + + it('should convert lowercase events to React camelcase in stateless components', () => { + const spy = sinon.spy(); + const Foo = () => ( + foo + ); + + const wrapper = mount(); + + wrapper.simulate('animationiteration'); + expect(spy).to.have.property('callCount', 1); + }); + }); }); it('should be batched updates', () => { diff --git a/packages/enzyme-test-suite/test/ShallowWrapper-spec.jsx b/packages/enzyme-test-suite/test/ShallowWrapper-spec.jsx index 2fb84bf8b..ef8d6641e 100644 --- a/packages/enzyme-test-suite/test/ShallowWrapper-spec.jsx +++ b/packages/enzyme-test-suite/test/ShallowWrapper-spec.jsx @@ -1982,6 +1982,36 @@ describe('shallow', () => { expect(spy).to.have.property('callCount', 1); }); }); + + describeIf(is('>= 15'), 'animation events', () => { + it('should convert lowercase events to React camelcase', () => { + const spy = sinon.spy(); + class Foo extends React.Component { + render() { + return ( + foo + ); + } + } + + const wrapper = shallow(); + + wrapper.simulate('animationiteration'); + expect(spy).to.have.property('callCount', 1); + }); + + it('should convert lowercase events to React camelcase in stateless components', () => { + const spy = sinon.spy(); + const Foo = () => ( + foo + ); + + const wrapper = shallow(); + + wrapper.simulate('animationiteration'); + expect(spy).to.have.property('callCount', 1); + }); + }); }); itIf(BATCHING, 'should be batched updates', () => { From 8e7e170de78634b1d353a8d4eef227776bfc2c81 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Tue, 14 Aug 2018 23:20:11 -0700 Subject: [PATCH 3/3] [enzyme-adapter-react-16] [new] add pointer events support Fixes 1719. --- .../src/ReactSixteenAdapter.js | 1 + .../test/ReactWrapper-spec.jsx | 30 +++++++++++++++++++ .../test/ShallowWrapper-spec.jsx | 30 +++++++++++++++++++ 3 files changed, 61 insertions(+) diff --git a/packages/enzyme-adapter-react-16/src/ReactSixteenAdapter.js b/packages/enzyme-adapter-react-16/src/ReactSixteenAdapter.js index b6c8b43d2..0baabffbc 100644 --- a/packages/enzyme-adapter-react-16/src/ReactSixteenAdapter.js +++ b/packages/enzyme-adapter-react-16/src/ReactSixteenAdapter.js @@ -188,6 +188,7 @@ function nodeToHostNode(_node) { const eventOptions = { animation: true, + pointerEvents: true, // 16.4+ }; class ReactSixteenAdapter extends EnzymeAdapter { diff --git a/packages/enzyme-test-suite/test/ReactWrapper-spec.jsx b/packages/enzyme-test-suite/test/ReactWrapper-spec.jsx index 7ac327496..04f358cbe 100644 --- a/packages/enzyme-test-suite/test/ReactWrapper-spec.jsx +++ b/packages/enzyme-test-suite/test/ReactWrapper-spec.jsx @@ -2091,6 +2091,36 @@ describeWithDOM('mount', () => { expect(spy).to.have.property('callCount', 1); }); }); + + describeIf(is('>= 16.4'), 'pointer events', () => { + it('should convert lowercase events to React camelcase', () => { + const spy = sinon.spy(); + class Foo extends React.Component { + render() { + return ( + foo + ); + } + } + + const wrapper = mount(); + + wrapper.simulate('gotpointercapture'); + expect(spy).to.have.property('callCount', 1); + }); + + it('should convert lowercase events to React camelcase in stateless components', () => { + const spy = sinon.spy(); + const Foo = () => ( + foo + ); + + const wrapper = mount(); + + wrapper.simulate('gotpointercapture'); + expect(spy).to.have.property('callCount', 1); + }); + }); }); it('should be batched updates', () => { diff --git a/packages/enzyme-test-suite/test/ShallowWrapper-spec.jsx b/packages/enzyme-test-suite/test/ShallowWrapper-spec.jsx index ef8d6641e..869d045fc 100644 --- a/packages/enzyme-test-suite/test/ShallowWrapper-spec.jsx +++ b/packages/enzyme-test-suite/test/ShallowWrapper-spec.jsx @@ -2012,6 +2012,36 @@ describe('shallow', () => { expect(spy).to.have.property('callCount', 1); }); }); + + describeIf(is('>= 16.4'), 'pointer events', () => { + it('should convert lowercase events to React camelcase', () => { + const spy = sinon.spy(); + class Foo extends React.Component { + render() { + return ( + foo + ); + } + } + + const wrapper = shallow(); + + wrapper.simulate('gotpointercapture'); + expect(spy).to.have.property('callCount', 1); + }); + + it('should convert lowercase events to React camelcase in stateless components', () => { + const spy = sinon.spy(); + const Foo = () => ( + foo + ); + + const wrapper = shallow(); + + wrapper.simulate('gotpointercapture'); + expect(spy).to.have.property('callCount', 1); + }); + }); }); itIf(BATCHING, 'should be batched updates', () => {