mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
+152
-77
@@ -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(
|
||||
<div ref={c => (GRANDPARENT = c)} {...GRANDPARENT_PROPS}>
|
||||
<div ref={c => (PARENT = c)} {...PARENT_PROPS}>
|
||||
<ChildWrapper {...CHILD_PROPS} />
|
||||
<button disabled={true} ref={c => (BUTTON = c)} {...BUTTON_PROPS} />
|
||||
</div>
|
||||
</div>,
|
||||
container,
|
||||
);
|
||||
}
|
||||
const root = ReactDOMClient.createRoot(container);
|
||||
|
||||
renderTree();
|
||||
renderTree = async function () {
|
||||
await act(() => {
|
||||
root.render(
|
||||
<div ref={c => (GRANDPARENT = c)} {...GRANDPARENT_PROPS}>
|
||||
<div ref={c => (PARENT = c)} {...PARENT_PROPS}>
|
||||
<ChildWrapper {...CHILD_PROPS} />
|
||||
<button
|
||||
disabled={true}
|
||||
ref={c => (BUTTON = c)}
|
||||
{...BUTTON_PROPS}
|
||||
/>
|
||||
</div>
|
||||
</div>,
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user