Skip to content

Commit

Permalink
Convert ReactIdentity-test.js to createRoot (#28106)
Browse files Browse the repository at this point in the history
  • Loading branch information
mattcarrollcode authored Jan 26, 2024
1 parent 759811b commit 37bdff6
Showing 1 changed file with 70 additions and 49 deletions.
119 changes: 70 additions & 49 deletions packages/react-dom/src/__tests__/ReactIdentity-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,20 @@
'use strict';

let React;
let ReactDOM;
let ReactDOMClient;
let ReactTestUtils;
let act;

describe('ReactIdentity', () => {
beforeEach(() => {
jest.resetModules();
React = require('react');
ReactDOM = require('react-dom');
ReactDOMClient = require('react-dom/client');
ReactTestUtils = require('react-dom/test-utils');
act = require('internal-test-utils').act;
});

it('should allow key property to express identity', () => {
it('should allow key property to express identity', async () => {
let node;
const Component = props => (
<div ref={c => (node = c)}>
Expand All @@ -31,41 +33,48 @@ describe('ReactIdentity', () => {
);

const container = document.createElement('div');
ReactDOM.render(<Component />, container);
const root = ReactDOMClient.createRoot(container);
await act(async () => {
root.render(<Component />);
});
const origChildren = Array.from(node.childNodes);
ReactDOM.render(<Component swap={true} />, container);
await act(async () => {
root.render(<Component swap={true} />);
});
const newChildren = Array.from(node.childNodes);
expect(origChildren[0]).toBe(newChildren[1]);
expect(origChildren[1]).toBe(newChildren[0]);
});

it('should use composite identity', () => {
it('should use composite identity', async () => {
class Wrapper extends React.Component {
render() {
return <a>{this.props.children}</a>;
}
}

const container = document.createElement('div');
const root = ReactDOMClient.createRoot(container);
let node1;
let node2;
ReactDOM.render(
<Wrapper key="wrap1">
<span ref={c => (node1 = c)} />
</Wrapper>,
container,
);
ReactDOM.render(
<Wrapper key="wrap2">
<span ref={c => (node2 = c)} />
</Wrapper>,
container,
);

await act(async () => {
root.render(
<Wrapper key="wrap1">
<span ref={c => (node1 = c)} />
</Wrapper>,
);
});
await act(async () => {
root.render(
<Wrapper key="wrap2">
<span ref={c => (node2 = c)} />
</Wrapper>,
);
});
expect(node1).not.toBe(node2);
});

function renderAComponentWithKeyIntoContainer(key, container) {
async function renderAComponentWithKeyIntoContainer(key, root) {
class Wrapper extends React.Component {
spanRef = React.createRef();
render() {
Expand All @@ -76,36 +85,41 @@ describe('ReactIdentity', () => {
);
}
}

const instance = ReactDOM.render(<Wrapper />, container);
const span = instance.spanRef.current;
const wrapperRef = React.createRef();
await act(async () => {
root.render(<Wrapper ref={wrapperRef} />);
});
const span = wrapperRef.current.spanRef.current;
expect(span).not.toBe(null);
}

it('should allow any character as a key, in a detached parent', () => {
it('should allow any character as a key, in a detached parent', async () => {
const detachedContainer = document.createElement('div');
renderAComponentWithKeyIntoContainer("<'WEIRD/&\\key'>", detachedContainer);
const root = ReactDOMClient.createRoot(detachedContainer);
await renderAComponentWithKeyIntoContainer("<'WEIRD/&\\key'>", root);
});

it('should allow any character as a key, in an attached parent', () => {
it('should allow any character as a key, in an attached parent', async () => {
// This test exists to protect against implementation details that
// incorrectly query escaped IDs using DOM tools like getElementById.
const attachedContainer = document.createElement('div');
const root = ReactDOMClient.createRoot(attachedContainer);
document.body.appendChild(attachedContainer);

renderAComponentWithKeyIntoContainer("<'WEIRD/&\\key'>", attachedContainer);
await renderAComponentWithKeyIntoContainer("<'WEIRD/&\\key'>", root);

document.body.removeChild(attachedContainer);
});

it('should not allow scripts in keys to execute', () => {
it('should not allow scripts in keys to execute', async () => {
const h4x0rKey =
'"><script>window[\'YOUVEBEENH4X0RED\']=true;</script><div id="';

const attachedContainer = document.createElement('div');
const root = ReactDOMClient.createRoot(attachedContainer);
document.body.appendChild(attachedContainer);

renderAComponentWithKeyIntoContainer(h4x0rKey, attachedContainer);
await renderAComponentWithKeyIntoContainer(h4x0rKey, root);

document.body.removeChild(attachedContainer);

Expand Down Expand Up @@ -209,7 +223,7 @@ describe('ReactIdentity', () => {
}).not.toThrow();
});

it('should retain key during updates in composite components', () => {
it('should retain key during updates in composite components', async () => {
class TestComponent extends React.Component {
render() {
return <div>{this.props.children}</div>;
Expand All @@ -236,16 +250,23 @@ describe('ReactIdentity', () => {
const instance0 = <span key="A" />;
const instance1 = <span key="B" />;

let wrapped = <TestContainer first={instance0} second={instance1} />;

wrapped = ReactDOM.render(wrapped, document.createElement('div'));
const div = ReactDOM.findDOMNode(wrapped);
const container = document.createElement('div');
const root = ReactDOMClient.createRoot(container);
const wrappedRef = React.createRef();
await act(async () => {
root.render(
<TestContainer first={instance0} second={instance1} ref={wrappedRef} />,
);
});
const div = container.firstChild;

const beforeA = div.childNodes[0];
const beforeB = div.childNodes[1];
wrapped.swap();
const afterA = div.childNodes[1];
const afterB = div.childNodes[0];
const beforeA = div.firstChild;
const beforeB = div.lastChild;
await act(async () => {
wrappedRef.current.swap();
});
const afterA = div.lastChild;
const afterB = div.firstChild;

expect(beforeA).toBe(afterA);
expect(beforeB).toBe(afterB);
Expand All @@ -264,7 +285,7 @@ describe('ReactIdentity', () => {
}).not.toThrow();
});

it('should throw if key is a Temporal-like object', () => {
it('should throw if key is a Temporal-like object', async () => {
class TemporalLike {
valueOf() {
// Throwing here is the behavior of ECMAScript "Temporal" date/time API.
Expand All @@ -277,15 +298,15 @@ describe('ReactIdentity', () => {
}

const el = document.createElement('div');
const test = () =>
ReactDOM.render(
<div>
<span key={new TemporalLike()} />
</div>,
el,
);
expect(() =>
expect(test).toThrowError(new TypeError('prod message')),
const root = ReactDOMClient.createRoot(el);
await expect(() =>
expect(() => {
root.render(
<div>
<span key={new TemporalLike()} />
</div>,
);
}).toThrowError(new TypeError('prod message')),
).toErrorDev(
'The provided key is an unsupported type TemporalLike.' +
' This value must be coerced to a string before using it here.',
Expand Down

0 comments on commit 37bdff6

Please sign in to comment.