diff --git a/packages/react-dom/src/__tests__/ReactBrowserEventEmitter-test.js b/packages/react-dom/src/__tests__/ReactBrowserEventEmitter-test.js index a226c3736bc26..1dd665ce01145 100644 --- a/packages/react-dom/src/__tests__/ReactBrowserEventEmitter-test.js +++ b/packages/react-dom/src/__tests__/ReactBrowserEventEmitter-test.js @@ -10,8 +10,9 @@ 'use strict'; let React; -let ReactDOM; +let ReactDOMClient; let ReactTestUtils; +let act; let idCallOrder; const recordID = function (id) { @@ -34,6 +35,7 @@ let PARENT; let CHILD; let BUTTON; +let renderTree; let putListener; let deleteAllListeners; @@ -47,9 +49,9 @@ describe('ReactBrowserEventEmitter', () => { LISTENER.mockClear(); React = require('react'); - ReactDOM = require('react-dom'); + ReactDOMClient = require('react-dom/client'); ReactTestUtils = require('react-dom/test-utils'); - + act = require('internal-test-utils').act; container = document.createElement('div'); document.body.appendChild(container); @@ -68,21 +70,26 @@ describe('ReactBrowserEventEmitter', () => { } } - function renderTree() { - ReactDOM.render( -
(GRANDPARENT = c)} {...GRANDPARENT_PROPS}> -
(PARENT = c)} {...PARENT_PROPS}> - -
-
, - container, - ); - } - - renderTree(); + const root = ReactDOMClient.createRoot(container); + + renderTree = async function () { + await act(() => { + root.render( +
(GRANDPARENT = c)} {...GRANDPARENT_PROPS}> +
(PARENT = c)} {...PARENT_PROPS}> + +
+
, + ); + }); + }; - putListener = function (node, eventName, listener) { + putListener = async function (node, eventName, listener) { switch (node) { case CHILD: CHILD_PROPS[eventName] = listener; @@ -98,9 +105,10 @@ describe('ReactBrowserEventEmitter', () => { break; } // Rerender with new event listeners - renderTree(); + await renderTree(); }; - deleteAllListeners = function (node) { + + deleteAllListeners = async function (node) { switch (node) { case CHILD: CHILD_PROPS = {}; @@ -115,7 +123,7 @@ describe('ReactBrowserEventEmitter', () => { BUTTON_PROPS = {}; break; } - renderTree(); + await renderTree(); }; idCallOrder = []; @@ -126,120 +134,178 @@ describe('ReactBrowserEventEmitter', () => { container = null; }); - it('should bubble simply', () => { - putListener(CHILD, ON_CLICK_KEY, recordID.bind(null, CHILD)); - putListener(PARENT, ON_CLICK_KEY, recordID.bind(null, PARENT)); - putListener(GRANDPARENT, ON_CLICK_KEY, recordID.bind(null, GRANDPARENT)); - CHILD.click(); + it('should bubble simply', async () => { + await renderTree(); + await putListener(CHILD, ON_CLICK_KEY, recordID.bind(null, CHILD)); + await putListener(PARENT, ON_CLICK_KEY, recordID.bind(null, PARENT)); + await putListener( + GRANDPARENT, + ON_CLICK_KEY, + recordID.bind(null, GRANDPARENT), + ); + await act(() => { + CHILD.click(); + }); expect(idCallOrder.length).toBe(3); expect(idCallOrder[0]).toBe(CHILD); expect(idCallOrder[1]).toBe(PARENT); expect(idCallOrder[2]).toBe(GRANDPARENT); }); - it('should bubble to the right handler after an update', () => { - putListener(GRANDPARENT, ON_CLICK_KEY, recordID.bind(null, 'GRANDPARENT')); - putListener(PARENT, ON_CLICK_KEY, recordID.bind(null, 'PARENT')); - putListener(CHILD, ON_CLICK_KEY, recordID.bind(null, 'CHILD')); - CHILD.click(); + it('should bubble to the right handler after an update', async () => { + await renderTree(); + await putListener( + GRANDPARENT, + ON_CLICK_KEY, + recordID.bind(null, 'GRANDPARENT'), + ); + await putListener(PARENT, ON_CLICK_KEY, recordID.bind(null, 'PARENT')); + await putListener(CHILD, ON_CLICK_KEY, recordID.bind(null, 'CHILD')); + await act(() => { + CHILD.click(); + }); expect(idCallOrder).toEqual(['CHILD', 'PARENT', 'GRANDPARENT']); idCallOrder = []; // Update just the grand parent without updating the child. - putListener( + await putListener( GRANDPARENT, ON_CLICK_KEY, recordID.bind(null, 'UPDATED_GRANDPARENT'), ); - CHILD.click(); + await act(() => { + CHILD.click(); + }); expect(idCallOrder).toEqual(['CHILD', 'PARENT', 'UPDATED_GRANDPARENT']); }); - it('should continue bubbling if an error is thrown', () => { - putListener(CHILD, ON_CLICK_KEY, recordID.bind(null, CHILD)); - putListener(PARENT, ON_CLICK_KEY, function () { + it('should continue bubbling if an error is thrown', async () => { + await renderTree(); + await putListener(CHILD, ON_CLICK_KEY, recordID.bind(null, CHILD)); + await putListener(PARENT, ON_CLICK_KEY, function () { recordID(PARENT); throw new Error('Handler interrupted'); }); - putListener(GRANDPARENT, ON_CLICK_KEY, recordID.bind(null, GRANDPARENT)); - expect(function () { - ReactTestUtils.Simulate.click(CHILD); - }).toThrow(); + await putListener( + GRANDPARENT, + ON_CLICK_KEY, + recordID.bind(null, GRANDPARENT), + ); + await expect( + act(() => { + ReactTestUtils.Simulate.click(CHILD); + }), + ).rejects.toThrow(); expect(idCallOrder.length).toBe(3); expect(idCallOrder[0]).toBe(CHILD); expect(idCallOrder[1]).toBe(PARENT); expect(idCallOrder[2]).toBe(GRANDPARENT); }); - it('should set currentTarget', () => { - putListener(CHILD, ON_CLICK_KEY, function (event) { + it('should set currentTarget', async () => { + await renderTree(); + await putListener(CHILD, ON_CLICK_KEY, function (event) { recordID(CHILD); expect(event.currentTarget).toBe(CHILD); }); - putListener(PARENT, ON_CLICK_KEY, function (event) { + await putListener(PARENT, ON_CLICK_KEY, function (event) { recordID(PARENT); expect(event.currentTarget).toBe(PARENT); }); - putListener(GRANDPARENT, ON_CLICK_KEY, function (event) { + await putListener(GRANDPARENT, ON_CLICK_KEY, function (event) { recordID(GRANDPARENT); expect(event.currentTarget).toBe(GRANDPARENT); }); - CHILD.click(); + await act(() => { + CHILD.click(); + }); expect(idCallOrder.length).toBe(3); expect(idCallOrder[0]).toBe(CHILD); expect(idCallOrder[1]).toBe(PARENT); expect(idCallOrder[2]).toBe(GRANDPARENT); }); - it('should support stopPropagation()', () => { - putListener(CHILD, ON_CLICK_KEY, recordID.bind(null, CHILD)); - putListener( + it('should support stopPropagation()', async () => { + await renderTree(); + await putListener(CHILD, ON_CLICK_KEY, recordID.bind(null, CHILD)); + await putListener( PARENT, ON_CLICK_KEY, recordIDAndStopPropagation.bind(null, PARENT), ); - putListener(GRANDPARENT, ON_CLICK_KEY, recordID.bind(null, GRANDPARENT)); - CHILD.click(); + await putListener( + GRANDPARENT, + ON_CLICK_KEY, + recordID.bind(null, GRANDPARENT), + ); + await act(() => { + CHILD.click(); + }); expect(idCallOrder.length).toBe(2); expect(idCallOrder[0]).toBe(CHILD); expect(idCallOrder[1]).toBe(PARENT); }); - it('should support overriding .isPropagationStopped()', () => { + it('should support overriding .isPropagationStopped()', async () => { + await renderTree(); // Ew. See D4504876. - putListener(CHILD, ON_CLICK_KEY, recordID.bind(null, CHILD)); - putListener(PARENT, ON_CLICK_KEY, function (e) { + await putListener(CHILD, ON_CLICK_KEY, recordID.bind(null, CHILD)); + await putListener(PARENT, ON_CLICK_KEY, function (e) { recordID(PARENT, e); // This stops React bubbling but avoids touching the native event e.isPropagationStopped = () => true; }); - putListener(GRANDPARENT, ON_CLICK_KEY, recordID.bind(null, GRANDPARENT)); - CHILD.click(); + await putListener( + GRANDPARENT, + ON_CLICK_KEY, + recordID.bind(null, GRANDPARENT), + ); + await act(() => { + CHILD.click(); + }); expect(idCallOrder.length).toBe(2); expect(idCallOrder[0]).toBe(CHILD); expect(idCallOrder[1]).toBe(PARENT); }); - it('should stop after first dispatch if stopPropagation', () => { - putListener( + it('should stop after first dispatch if stopPropagation', async () => { + await renderTree(); + await putListener( CHILD, ON_CLICK_KEY, recordIDAndStopPropagation.bind(null, CHILD), ); - putListener(PARENT, ON_CLICK_KEY, recordID.bind(null, PARENT)); - putListener(GRANDPARENT, ON_CLICK_KEY, recordID.bind(null, GRANDPARENT)); - CHILD.click(); + await putListener(PARENT, ON_CLICK_KEY, recordID.bind(null, PARENT)); + await putListener( + GRANDPARENT, + ON_CLICK_KEY, + recordID.bind(null, GRANDPARENT), + ); + await act(() => { + CHILD.click(); + }); expect(idCallOrder.length).toBe(1); expect(idCallOrder[0]).toBe(CHILD); }); - it('should not stopPropagation if false is returned', () => { - putListener(CHILD, ON_CLICK_KEY, recordIDAndReturnFalse.bind(null, CHILD)); - putListener(PARENT, ON_CLICK_KEY, recordID.bind(null, PARENT)); - putListener(GRANDPARENT, ON_CLICK_KEY, recordID.bind(null, GRANDPARENT)); - CHILD.click(); + it('should not stopPropagation if false is returned', async () => { + await renderTree(); + await putListener( + CHILD, + ON_CLICK_KEY, + recordIDAndReturnFalse.bind(null, CHILD), + ); + await putListener(PARENT, ON_CLICK_KEY, recordID.bind(null, PARENT)); + await putListener( + GRANDPARENT, + ON_CLICK_KEY, + recordID.bind(null, GRANDPARENT), + ); + await act(() => { + CHILD.click(); + }); expect(idCallOrder.length).toBe(3); expect(idCallOrder[0]).toBe(CHILD); expect(idCallOrder[1]).toBe(PARENT); @@ -255,30 +321,39 @@ describe('ReactBrowserEventEmitter', () => { * these new listeners. */ - it('should invoke handlers that were removed while bubbling', () => { + it('should invoke handlers that were removed while bubbling', async () => { + await renderTree(); const handleParentClick = jest.fn(); - const handleChildClick = function (event) { - deleteAllListeners(PARENT); + const handleChildClick = async function (event) { + await deleteAllListeners(PARENT); }; - putListener(CHILD, ON_CLICK_KEY, handleChildClick); - putListener(PARENT, ON_CLICK_KEY, handleParentClick); - CHILD.click(); + await putListener(CHILD, ON_CLICK_KEY, handleChildClick); + await putListener(PARENT, ON_CLICK_KEY, handleParentClick); + await act(() => { + CHILD.click(); + }); expect(handleParentClick).toHaveBeenCalledTimes(1); }); - it('should not invoke newly inserted handlers while bubbling', () => { + it('should not invoke newly inserted handlers while bubbling', async () => { + await renderTree(); const handleParentClick = jest.fn(); - const handleChildClick = function (event) { - putListener(PARENT, ON_CLICK_KEY, handleParentClick); + const handleChildClick = async function (event) { + await putListener(PARENT, ON_CLICK_KEY, handleParentClick); }; - putListener(CHILD, ON_CLICK_KEY, handleChildClick); - CHILD.click(); + await putListener(CHILD, ON_CLICK_KEY, handleChildClick); + await act(() => { + CHILD.click(); + }); expect(handleParentClick).toHaveBeenCalledTimes(0); }); - it('should have mouse enter simulated by test utils', () => { - putListener(CHILD, ON_MOUSE_ENTER_KEY, recordID.bind(null, CHILD)); - ReactTestUtils.Simulate.mouseEnter(CHILD); + it('should have mouse enter simulated by test utils', async () => { + await renderTree(); + await putListener(CHILD, ON_MOUSE_ENTER_KEY, recordID.bind(null, CHILD)); + await act(() => { + ReactTestUtils.Simulate.mouseEnter(CHILD); + }); expect(idCallOrder.length).toBe(1); expect(idCallOrder[0]).toBe(CHILD); });