From bfa269008a8709a756c0c744e941e54936e34589 Mon Sep 17 00:00:00 2001 From: Iacami Gevaerd Date: Fri, 3 Nov 2017 18:43:56 -0200 Subject: [PATCH] Use only public api for ReactDOMEventListener-test.js (#11327) * Use only public api for ReactDOMEventListener-test.js * Use less confusing naming There was no need to extract React elements into separate variables. * Replace the "disappearance" test I could not get it to fail on master so it was probably testing something specific to Stack implementation details. It was also already broken because it didn't look at the right argument and never actually called `unmountComponentAtNode`. Instead I replaced it with original repro case from https://github.com/facebook/react/issues/1105 which is when it was introduced. * Tweak naming and add comments * Missed this one --- .../__tests__/ReactDOMEventListener-test.js | 282 +++++++++--------- 1 file changed, 141 insertions(+), 141 deletions(-) diff --git a/packages/react-dom/src/__tests__/ReactDOMEventListener-test.js b/packages/react-dom/src/__tests__/ReactDOMEventListener-test.js index 0e04aac94e..c1d2fd6758 100644 --- a/packages/react-dom/src/__tests__/ReactDOMEventListener-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMEventListener-test.js @@ -9,184 +9,183 @@ 'use strict'; -var EVENT_TARGET_PARAM = 1; - describe('ReactDOMEventListener', () => { var React; var ReactDOM; - var ReactDOMComponentTree; - var ReactDOMEventListener; - var ReactTestUtils; - var handleTopLevel; beforeEach(() => { jest.resetModules(); React = require('react'); ReactDOM = require('react-dom'); - // TODO: can we express this test with only public API? - ReactDOMComponentTree = require('../client/ReactDOMComponentTree').default; - ReactDOMEventListener = require('../events/ReactDOMEventListener').default; - ReactTestUtils = require('react-dom/test-utils'); - - handleTopLevel = jest.fn(); - ReactDOMEventListener._handleTopLevel = handleTopLevel; }); it('should dispatch events from outside React tree', () => { + var mock = jest.fn(); + + var container = document.createElement('div'); + var node = ReactDOM.render(
, container); var otherNode = document.createElement('h1'); - var component = ReactDOM.render(
, document.createElement('div')); - expect(handleTopLevel.mock.calls.length).toBe(0); - ReactDOMEventListener.dispatchEvent('topMouseOut', { - type: 'mouseout', - fromElement: otherNode, - target: otherNode, - srcElement: otherNode, - toElement: ReactDOM.findDOMNode(component), - relatedTarget: ReactDOM.findDOMNode(component), - view: window, - path: [otherNode, otherNode], - }); - expect(handleTopLevel.mock.calls.length).toBe(1); + document.body.appendChild(container); + document.body.appendChild(otherNode); + + otherNode.dispatchEvent( + new MouseEvent('mouseout', { + bubbles: true, + cancelable: true, + relatedTarget: node, + }), + ); + expect(mock).toBeCalled(); }); describe('Propagation', () => { it('should propagate events one level down', () => { + var mouseOut = jest.fn(); + var onMouseOut = event => mouseOut(event.currentTarget); + var childContainer = document.createElement('div'); - var childControl =
Child
; var parentContainer = document.createElement('div'); - var parentControl =
Parent
; - childControl = ReactDOM.render(childControl, childContainer); - parentControl = ReactDOM.render(parentControl, parentContainer); - ReactDOM.findDOMNode(parentControl).appendChild(childContainer); - - var callback = ReactDOMEventListener.dispatchEvent.bind(null, 'test'); - callback({ - target: ReactDOM.findDOMNode(childControl), - }); - - var calls = handleTopLevel.mock.calls; - expect(calls.length).toBe(2); - expect(calls[0][EVENT_TARGET_PARAM]).toBe( - ReactDOMComponentTree.getInstanceFromNode(childControl), + var childNode = ReactDOM.render( +
Child
, + childContainer, ); - expect(calls[1][EVENT_TARGET_PARAM]).toBe( - ReactDOMComponentTree.getInstanceFromNode(parentControl), + var parentNode = ReactDOM.render( +
div
, + parentContainer, ); + parentNode.appendChild(childContainer); + document.body.appendChild(parentContainer); + + var nativeEvent = document.createEvent('Event'); + nativeEvent.initEvent('mouseout', true, true); + childNode.dispatchEvent(nativeEvent); + + expect(mouseOut).toBeCalled(); + expect(mouseOut.mock.calls.length).toBe(2); + expect(mouseOut.mock.calls[0][0]).toEqual(childNode); + expect(mouseOut.mock.calls[1][0]).toEqual(parentNode); + + document.body.removeChild(parentContainer); }); it('should propagate events two levels down', () => { + var mouseOut = jest.fn(); + var onMouseOut = event => mouseOut(event.currentTarget); + var childContainer = document.createElement('div'); - var childControl =
Child
; var parentContainer = document.createElement('div'); - var parentControl =
Parent
; var grandParentContainer = document.createElement('div'); - var grandParentControl =
Parent
; - childControl = ReactDOM.render(childControl, childContainer); - parentControl = ReactDOM.render(parentControl, parentContainer); - grandParentControl = ReactDOM.render( - grandParentControl, + var childNode = ReactDOM.render( +
Child
, + childContainer, + ); + var parentNode = ReactDOM.render( +
Parent
, + parentContainer, + ); + var grandParentNode = ReactDOM.render( +
Parent
, grandParentContainer, ); - ReactDOM.findDOMNode(parentControl).appendChild(childContainer); - ReactDOM.findDOMNode(grandParentControl).appendChild(parentContainer); + parentNode.appendChild(childContainer); + grandParentNode.appendChild(parentContainer); - var callback = ReactDOMEventListener.dispatchEvent.bind(null, 'test'); - callback({ - target: ReactDOM.findDOMNode(childControl), - }); + document.body.appendChild(grandParentContainer); - var calls = handleTopLevel.mock.calls; - expect(calls.length).toBe(3); - expect(calls[0][EVENT_TARGET_PARAM]).toBe( - ReactDOMComponentTree.getInstanceFromNode(childControl), - ); - expect(calls[1][EVENT_TARGET_PARAM]).toBe( - ReactDOMComponentTree.getInstanceFromNode(parentControl), - ); - expect(calls[2][EVENT_TARGET_PARAM]).toBe( - ReactDOMComponentTree.getInstanceFromNode(grandParentControl), - ); + var nativeEvent = document.createEvent('Event'); + nativeEvent.initEvent('mouseout', true, true); + childNode.dispatchEvent(nativeEvent); + + expect(mouseOut).toBeCalled(); + expect(mouseOut.mock.calls.length).toBe(3); + expect(mouseOut.mock.calls[0][0]).toEqual(childNode); + expect(mouseOut.mock.calls[1][0]).toEqual(parentNode); + expect(mouseOut.mock.calls[2][0]).toEqual(grandParentNode); + + document.body.removeChild(grandParentContainer); }); + // Regression test for https://github.com/facebook/react/issues/1105 it('should not get confused by disappearing elements', () => { - var childContainer = document.createElement('div'); - var childControl =
Child
; - var parentContainer = document.createElement('div'); - var parentControl =
Parent
; - childControl = ReactDOM.render(childControl, childContainer); - parentControl = ReactDOM.render(parentControl, parentContainer); - ReactDOM.findDOMNode(parentControl).appendChild(childContainer); - - // ReactBrowserEventEmitter.handleTopLevel might remove the - // target from the DOM. Here, we have handleTopLevel remove the - // node when the first event handlers are called; we'll still - // expect to receive a second call for the parent control. - var childNode = ReactDOM.findDOMNode(childControl); - handleTopLevel.mockImplementation(function( - topLevelType, - topLevelTarget, - topLevelTargetID, - nativeEvent, - ) { - if (topLevelTarget === childNode) { - ReactDOM.unmountComponentAtNode(childContainer); + var container = document.createElement('div'); + document.body.appendChild(container); + class MyComponent extends React.Component { + state = {clicked: false}; + handleClick = () => { + this.setState({clicked: true}); + }; + componentDidMount() { + expect(ReactDOM.findDOMNode(this)).toBe(container.firstChild); } - }); - - var callback = ReactDOMEventListener.dispatchEvent.bind(null, 'test'); - callback({ - target: childNode, - }); - - var calls = handleTopLevel.mock.calls; - expect(calls.length).toBe(2); - expect(calls[0][EVENT_TARGET_PARAM]).toBe( - ReactDOMComponentTree.getInstanceFromNode(childNode), - ); - expect(calls[1][EVENT_TARGET_PARAM]).toBe( - ReactDOMComponentTree.getInstanceFromNode(parentControl), + componentDidUpdate() { + expect(ReactDOM.findDOMNode(this)).toBe(container.firstChild); + } + render() { + if (this.state.clicked) { + return clicked!; + } else { + return ; + } + } + } + ReactDOM.render(, container); + container.firstChild.dispatchEvent( + new MouseEvent('click', { + bubbles: true, + }), ); + expect(container.firstChild.textContent).toBe('clicked!'); + document.body.removeChild(container); }); it('should batch between handlers from different roots', () => { + var mock = jest.fn(); + var childContainer = document.createElement('div'); + var handleChildMouseOut = () => { + ReactDOM.render(
1
, childContainer); + mock(childNode.textContent); + }; + var parentContainer = document.createElement('div'); - var childControl = ReactDOM.render(
Child
, childContainer); - var parentControl = ReactDOM.render(
Parent
, parentContainer); - ReactDOM.findDOMNode(parentControl).appendChild(childContainer); + var handleParentMouseOut = () => { + ReactDOM.render(
2
, childContainer); + mock(childNode.textContent); + }; - // Suppose an event handler in each root enqueues an update to the - // childControl element -- the two updates should get batched together. - var childNode = ReactDOM.findDOMNode(childControl); - handleTopLevel.mockImplementation(function( - topLevelType, - topLevelTarget, - topLevelTargetID, - nativeEvent, - ) { - ReactDOM.render( -
{topLevelTarget === childNode ? '1' : '2'}
, - childContainer, - ); - // Since we're batching, neither update should yet have gone through. - expect(childNode.textContent).toBe('Child'); - }); - - var callback = ReactDOMEventListener.dispatchEvent.bind( - ReactDOMEventListener, - 'test', + var childNode = ReactDOM.render( +
Child
, + childContainer, ); - callback({ - target: childNode, - }); + var parentNode = ReactDOM.render( +
Parent
, + parentContainer, + ); + parentNode.appendChild(childContainer); + document.body.appendChild(parentContainer); - var calls = handleTopLevel.mock.calls; - expect(calls.length).toBe(2); + var nativeEvent = document.createEvent('Event'); + nativeEvent.initEvent('mouseout', true, true); + childNode.dispatchEvent(nativeEvent); + + // Child and parent should both call from event handlers. + expect(mock.mock.calls.length).toBe(2); + // The first call schedules a render of '1' into the 'Child'. + // However, we're batching so it isn't flushed yet. + expect(mock.mock.calls[0][0]).toBe('Child'); + // The first call schedules a render of '2' into the 'Child'. + // We're still batching so it isn't flushed yet either. + expect(mock.mock.calls[1][0]).toBe('Child'); + // By the time we leave the handler, the second update is flushed. expect(childNode.textContent).toBe('2'); + document.body.removeChild(parentContainer); }); }); it('should not fire duplicate events for a React DOM tree', () => { + var mouseOut = jest.fn(); + var onMouseOut = event => mouseOut(event.target); + class Wrapper extends React.Component { getInner = () => { return this.refs.inner; @@ -194,21 +193,22 @@ describe('ReactDOMEventListener', () => { render() { var inner =
Inner
; - return
{inner}
; + return
{inner}
; } } - var instance = ReactTestUtils.renderIntoDocument(); + var container = document.createElement('div'); + var instance = ReactDOM.render(, container); - var callback = ReactDOMEventListener.dispatchEvent.bind(null, 'test'); - callback({ - target: ReactDOM.findDOMNode(instance.getInner()), - }); + document.body.appendChild(container); - var calls = handleTopLevel.mock.calls; - expect(calls.length).toBe(1); - expect(calls[0][EVENT_TARGET_PARAM]).toBe( - ReactDOMComponentTree.getInstanceFromNode(instance.getInner()), - ); + var nativeEvent = document.createEvent('Event'); + nativeEvent.initEvent('mouseout', true, true); + instance.getInner().dispatchEvent(nativeEvent); + + expect(mouseOut).toBeCalled(); + expect(mouseOut.mock.calls.length).toBe(1); + expect(mouseOut.mock.calls[0][0]).toEqual(instance.getInner()); + document.body.removeChild(container); }); });