- /**
- * Copyright (c) Meta Platforms, Inc. and affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- *
- * @emails react-core
- * @jest-environment ./scripts/jest/ReactDOMServerIntegrationEnvironment
- */
- /* eslint-disable no-func-assign */
- 'use strict'; 
- const ReactDOMServerIntegrationUtils = require('./utils/ReactDOMServerIntegrationTestUtils'); 
- let React; 
- let ReactDOM; 
- let ReactDOMServer; 
- let ReactTestUtils; 
- let useState; 
- let useReducer; 
- let useEffect; 
- let useContext; 
- let useCallback; 
- let useMemo; 
- let useRef; 
- let useImperativeHandle; 
- let useInsertionEffect; 
- let useLayoutEffect; 
- let useDebugValue; 
- let forwardRef; 
- let yieldedValues; 
- let yieldValue; 
- let clearLog; 
- function initModules() { 
- // Reset warning cache. 
- jest.resetModules(); 
- React = require('react'); 
- ReactDOM = require('react-dom'); 
- ReactDOMServer = require('react-dom/server'); 
- ReactTestUtils = require('react-dom/test-utils'); 
- useState = React.useState; 
- useReducer = React.useReducer; 
- useEffect = React.useEffect; 
- useContext = React.useContext; 
- useCallback = React.useCallback; 
- useMemo = React.useMemo; 
- useRef = React.useRef; 
- useDebugValue = React.useDebugValue; 
- useImperativeHandle = React.useImperativeHandle; 
- useInsertionEffect = React.useInsertionEffect; 
- useLayoutEffect = React.useLayoutEffect; 
- forwardRef = React.forwardRef; 
- yieldedValues = []; 
- yieldValue = value => { 
- yieldedValues.push(value); 
- };
- clearLog = () => { 
- const ret = yieldedValues; 
- yieldedValues = []; 
- return ret; 
- };
- // Make them available to the helpers. 
- return { 
- ReactDOM, 
- ReactDOMServer, 
- ReactTestUtils, 
- };
- }
- const {resetModules, itRenders, itThrowsWhenRendering, serverRender} = 
- ReactDOMServerIntegrationUtils(initModules); 
- describe('ReactDOMServerHooks', () => { 
- beforeEach(() => { 
- resetModules(); 
- });
- function Text(props) { 
- yieldValue(props.text); 
- return <span>{props.text}</span>; 
- }
- describe('useState', () => {
- itRenders('basic render', async render => {
- function Counter(props) {
- const [count] = useState(0); 
- return <span>Count: {count}</span>;
- }
- const domNode = await render(<Counter />);
- expect(domNode.textContent).toEqual('Count: 0'); 
- });
- itRenders('lazy state initialization', async render => {
- function Counter(props) {
- const [count] = useState(() => { 
- return 0;
- });
- return <span>Count: {count}</span>;
- }
- const domNode = await render(<Counter />);
- expect(domNode.textContent).toEqual('Count: 0'); 
- });
- it('does not trigger a re-renders when updater is invoked outside current render function', async () => {
- function UpdateCount({setCount, count, children}) {
- if (count < 3) {
- setCount(c => c + 1); 
- }
- return <span>{children}</span>;
- }
- function Counter() {
- const [count, setCount] = useState(0); 
- return (
- <div>
- <UpdateCount setCount={setCount} count={count}>
- Count: {count}
- </UpdateCount>
- </div>
- );
- }
- const domNode = await serverRender(<Counter />);
- expect(domNode.textContent).toEqual('Count: 0'); 
- });
- itThrowsWhenRendering(
- 'if used inside a class component',
- async render => {
- class Counter extends React.Component { 
- render() {
- const [count] = useState(0); 
- return <Text text={count} />;
- }
- }
- return render(<Counter />);
- },
- 'Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for' + 
- ' one of the following reasons:\n' + 
- '1. You might have mismatching versions of React and the renderer (such as React DOM)\n' + 
- '2. You might be breaking the Rules of Hooks\n' + 
- '3. You might have more than one copy of React in the same app\n' + 
- 'See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.', 
- );
- itRenders('multiple times when an updater is called', async render => {
- function Counter() {
- const [count, setCount] = useState(0); 
- if (count < 12) {
- setCount(c => c + 1); 
- setCount(c => c + 1); 
- setCount(c => c + 1); 
- }
- return <Text text={'Count: ' + count} />; 
- }
- const domNode = await render(<Counter />);
- expect(domNode.textContent).toEqual('Count: 12'); 
- });
- itRenders('until there are no more new updates', async render => {
- function Counter() {
- const [count, setCount] = useState(0); 
- if (count < 3) {
- setCount(count + 1); 
- }
- return <span>Count: {count}</span>;
- }
- const domNode = await render(<Counter />);
- expect(domNode.textContent).toEqual('Count: 3'); 
- });
- itThrowsWhenRendering(
- 'after too many iterations',
- async render => {
- function Counter() {
- const [count, setCount] = useState(0); 
- setCount(count + 1); 
- return <span>{count}</span>;
- }
- return render(<Counter />);
- },
- 'Too many re-renders. React limits the number of renders to prevent ' + 
- 'an infinite loop.', 
- );
- });
- describe('useReducer', () => {
- itRenders('with initial state', async render => {
- function reducer(state, action) {
- return action === 'increment' ? state + 1 : state; 
- }
- function Counter() {
- const [count] = useReducer(reducer, 0); 
- yieldValue('Render: ' + count); 
- return <Text text={count} />;
- }
- const domNode = await render(<Counter />);
- expect(clearLog()).toEqual(['Render: 0', 0]); 
- expect(domNode.tagName).toEqual('SPAN'); 
- expect(domNode.textContent).toEqual('0'); 
- });
- itRenders('lazy initialization', async render => {
- function reducer(state, action) {
- return action === 'increment' ? state + 1 : state; 
- }
- function Counter() {
- const [count] = useReducer(reducer, 0, c => c + 1); 
- yieldValue('Render: ' + count); 
- return <Text text={count} />;
- }
- const domNode = await render(<Counter />);
- expect(clearLog()).toEqual(['Render: 1', 1]); 
- expect(domNode.tagName).toEqual('SPAN'); 
- expect(domNode.textContent).toEqual('1'); 
- });
- itRenders(
- 'multiple times when updates happen during the render phase',
- async render => {
- function reducer(state, action) {
- return action === 'increment' ? state + 1 : state; 
- }
- function Counter() {
- const [count, dispatch] = useReducer(reducer, 0); 
- if (count < 3) {
- dispatch('increment');
- }
- yieldValue('Render: ' + count); 
- return <Text text={count} />;
- }
- const domNode = await render(<Counter />);
- expect(clearLog()).toEqual([ 
- 'Render: 0',
- 'Render: 1',
- 'Render: 2',
- 'Render: 3',
- 3,
- ]); 
- expect(domNode.tagName).toEqual('SPAN'); 
- expect(domNode.textContent).toEqual('3'); 
- },
- );
- itRenders(
- 'using reducer passed at time of render, not time of dispatch',
- async render => {
- // This test is a bit contrived but it demonstrates a subtle edge case. 
- // Reducer A increments by 1. Reducer B increments by 10. 
- function reducerA(state, action) {
- switch (action) {
- case 'increment':
- return state + 1; 
- case 'reset':
- return 0;
- }
- }
- function reducerB(state, action) {
- switch (action) {
- case 'increment':
- return state + 10; 
- case 'reset':
- return 0;
- }
- }
- function Counter() {
- const [reducer, setReducer] = useState(() => reducerA); 
- const [count, dispatch] = useReducer(reducer, 0); 
- if (count < 20) {
- dispatch('increment');
- // Swap reducers each time we increment
- if (reducer === reducerA) {
- setReducer(() => reducerB);
- } else {
- setReducer(() => reducerA);
- }
- }
- yieldValue('Render: ' + count); 
- return <Text text={count} />;
- }
- const domNode = await render(<Counter />);
- expect(clearLog()).toEqual([ 
- // The count should increase by alternating amounts of 10 and 1
- // until we reach 21.
- 'Render: 0',
- 'Render: 10',
- 'Render: 11',
- 'Render: 21',
- 21,
- ]); 
- expect(domNode.tagName).toEqual('SPAN'); 
- expect(domNode.textContent).toEqual('21'); 
- },
- );
- });
- describe('useMemo', () => {
- itRenders('basic render', async render => {
- function CapitalizedText(props) {
- const text = props.text; 
- const capitalizedText = useMemo(() => {
- yieldValue(`Capitalize '${text}'`); 
- return text.toUpperCase(); 
- }, [text]); 
- return <Text text={capitalizedText} />;
- }
- const domNode = await render(<CapitalizedText text="hello" />);
- expect(clearLog()).toEqual(["Capitalize 'hello'", 'HELLO']); 
- expect(domNode.tagName).toEqual('SPAN'); 
- expect(domNode.textContent).toEqual('HELLO'); 
- });
- itRenders('if no inputs are provided', async render => {
- function LazyCompute(props) {
- const computed = useMemo(props.compute); 
- return <Text text={computed} />;
- }
- function computeA() {
- yieldValue('compute A');
- return 'A';
- }
- const domNode = await render(<LazyCompute compute={computeA} />);
- expect(clearLog()).toEqual(['compute A', 'A']); 
- expect(domNode.tagName).toEqual('SPAN'); 
- expect(domNode.textContent).toEqual('A'); 
- });
- itRenders(
- 'multiple times when updates happen during the render phase',
- async render => {
- function CapitalizedText(props) {
- const [text, setText] = useState(props.text); 
- const capitalizedText = useMemo(() => {
- yieldValue(`Capitalize '${text}'`); 
- return text.toUpperCase(); 
- }, [text]); 
- if (text === 'hello') {
- setText('hello, world.'); 
- }
- return <Text text={capitalizedText} />;
- }
- const domNode = await render(<CapitalizedText text="hello" />);
- expect(clearLog()).toEqual([ 
- "Capitalize 'hello'",
- "Capitalize 'hello, world.'",
- 'HELLO, WORLD.',
- ]); 
- expect(domNode.tagName).toEqual('SPAN'); 
- expect(domNode.textContent).toEqual('HELLO, WORLD.'); 
- },
- );
- itRenders(
- 'should only invoke the memoized function when the inputs change',
- async render => {
- function CapitalizedText(props) {
- const [text, setText] = useState(props.text); 
- const [count, setCount] = useState(0); 
- const capitalizedText = useMemo(() => {
- yieldValue(`Capitalize '${text}'`); 
- return text.toUpperCase(); 
- }, [text]); 
- yieldValue(count);
- if (count < 3) {
- setCount(count + 1); 
- }
- if (text === 'hello' && count === 2) {
- setText('hello, world.'); 
- }
- return <Text text={capitalizedText} />;
- }
- const domNode = await render(<CapitalizedText text="hello" />);
- expect(clearLog()).toEqual([ 
- "Capitalize 'hello'",
- 0,
- 1,
- 2,
- // `capitalizedText` only recomputes when the text has changed
- "Capitalize 'hello, world.'",
- 3,
- 'HELLO, WORLD.',
- ]); 
- expect(domNode.tagName).toEqual('SPAN'); 
- expect(domNode.textContent).toEqual('HELLO, WORLD.'); 
- },
- );
- itRenders('with a warning for useState inside useMemo', async render => {
- function App() {
- useMemo(() => {
- useState();
- return 0;
- });
- return 'hi';
- }
- const domNode = await render(<App />, 1);
- expect(domNode.textContent).toEqual('hi'); 
- });
- itRenders('with a warning for useRef inside useState', async render => {
- function App() {
- const [value] = useState(() => { 
- useRef(0);
- return 0;
- });
- return value;
- }
- const domNode = await render(<App />, 1);
- expect(domNode.textContent).toEqual('0'); 
- });
- });
- describe('useRef', () => {
- itRenders('basic render', async render => {
- function Counter(props) {
- const ref = useRef();
- return <span ref={ref}>Hi</span>;
- }
- const domNode = await render(<Counter />);
- expect(domNode.textContent).toEqual('Hi'); 
- });
- itRenders(
- 'multiple times when updates happen during the render phase',
- async render => {
- function Counter(props) {
- const [count, setCount] = useState(0); 
- const ref = useRef();
- if (count < 3) {
- const newCount = count + 1; 
- setCount(newCount);
- }
- yieldValue(count);
- return <span ref={ref}>Count: {count}</span>;
- }
- const domNode = await render(<Counter />);
- expect(clearLog()).toEqual([0, 1, 2, 3]); 
- expect(domNode.textContent).toEqual('Count: 3'); 
- },
- );
- itRenders(
- 'always return the same reference through multiple renders',
- async render => {
- let firstRef = null;
- function Counter(props) {
- const [count, setCount] = useState(0); 
- const ref = useRef();
- if (firstRef === null) {
- firstRef = ref;
- } else if (firstRef !== ref) {
- throw new Error('should never change');
- }
- if (count < 3) {
- setCount(count + 1); 
- } else {
- firstRef = null;
- }
- yieldValue(count);
- return <span ref={ref}>Count: {count}</span>;
- }
- const domNode = await render(<Counter />);
- expect(clearLog()).toEqual([0, 1, 2, 3]); 
- expect(domNode.textContent).toEqual('Count: 3'); 
- },
- );
- });
- describe('useEffect', () => {
- const yields = []; 
- itRenders('should ignore effects on the server', async render => {
- function Counter(props) {
- useEffect(() => {
- yieldValue('invoked on client');
- });
- return <Text text={'Count: ' + props.count} />; 
- }
- const domNode = await render(<Counter count={0} />); 
- yields.push(clearLog()); 
- expect(domNode.tagName).toEqual('SPAN'); 
- expect(domNode.textContent).toEqual('Count: 0'); 
- });
- it('verifies yields in order', () => {
- expect(yields).toEqual([ 
- ['Count: 0'], // server render 
- ['Count: 0'], // server stream 
- ['Count: 0', 'invoked on client'], // clean render 
- ['Count: 0', 'invoked on client'], // hydrated render 
- // nothing yielded for bad markup
- ]);
- });
- });
- describe('useCallback', () => {
- itRenders('should not invoke the passed callbacks', async render => {
- function Counter(props) {
- useCallback(() => {
- yieldValue('should not be invoked');
- });
- return <Text text={'Count: ' + props.count} />; 
- }
- const domNode = await render(<Counter count={0} />); 
- expect(clearLog()).toEqual(['Count: 0']); 
- expect(domNode.tagName).toEqual('SPAN'); 
- expect(domNode.textContent).toEqual('Count: 0'); 
- });
- itRenders('should support render time callbacks', async render => {
- function Counter(props) {
- const renderCount = useCallback(increment => {
- return 'Count: ' + (props.count + increment); 
- });
- return <Text text={renderCount(3)} />;
- }
- const domNode = await render(<Counter count={2} />); 
- expect(clearLog()).toEqual(['Count: 5']); 
- expect(domNode.tagName).toEqual('SPAN'); 
- expect(domNode.textContent).toEqual('Count: 5'); 
- });
- itRenders(
- 'should only change the returned reference when the inputs change',
- async render => {
- function CapitalizedText(props) {
- const [text, setText] = useState(props.text); 
- const [count, setCount] = useState(0); 
- const capitalizeText = useCallback(() => text.toUpperCase(), [text]); 
- yieldValue(capitalizeText);
- if (count < 3) {
- setCount(count + 1); 
- }
- if (text === 'hello' && count === 2) {
- setText('hello, world.'); 
- }
- return <Text text={capitalizeText()} />;
- }
- const domNode = await render(<CapitalizedText text="hello" />);
- const [first, second, third, fourth, result] = clearLog(); 
- expect(first).toBe(second); 
- expect(second).toBe(third); 
- expect(third).not.toBe(fourth); 
- expect(result).toEqual('HELLO, WORLD.'); 
- expect(domNode.tagName).toEqual('SPAN'); 
- expect(domNode.textContent).toEqual('HELLO, WORLD.'); 
- },
- );
- });
- describe('useImperativeHandle', () => {
- it('should not be invoked on the server', async () => {
- function Counter(props, ref) {
- useImperativeHandle(ref, () => {
- throw new Error('should not be invoked');
- });
- return <Text text={props.label + ': ' + ref.current} />; 
- }
- Counter = forwardRef(Counter);
- const counter = React.createRef(); 
- counter.current = 0; 
- const domNode = await serverRender(
- <Counter label="Count" ref={counter} />,
- );
- expect(clearLog()).toEqual(['Count: 0']); 
- expect(domNode.tagName).toEqual('SPAN'); 
- expect(domNode.textContent).toEqual('Count: 0'); 
- });
- });
- describe('useInsertionEffect', () => {
- it('should warn when invoked during render', async () => {
- function Counter() {
- useInsertionEffect(() => {
- throw new Error('should not be invoked');
- });
- return <Text text="Count: 0" />;
- }
- const domNode = await serverRender(<Counter />, 1);
- expect(clearLog()).toEqual(['Count: 0']); 
- expect(domNode.tagName).toEqual('SPAN'); 
- expect(domNode.textContent).toEqual('Count: 0'); 
- });
- });
- describe('useLayoutEffect', () => {
- it('should warn when invoked during render', async () => {
- function Counter() {
- useLayoutEffect(() => {
- throw new Error('should not be invoked');
- });
- return <Text text="Count: 0" />;
- }
- const domNode = await serverRender(<Counter />, 1);
- expect(clearLog()).toEqual(['Count: 0']); 
- expect(domNode.tagName).toEqual('SPAN'); 
- expect(domNode.textContent).toEqual('Count: 0'); 
- });
- });
- describe('useContext', () => {
- itThrowsWhenRendering(
- 'if used inside a class component',
- async render => {
- const Context = React.createContext({}, () => {}); 
- class Counter extends React.Component { 
- render() {
- const [count] = useContext(Context); 
- return <Text text={count} />;
- }
- }
- return render(<Counter />);
- },
- 'Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for' + 
- ' one of the following reasons:\n' + 
- '1. You might have mismatching versions of React and the renderer (such as React DOM)\n' + 
- '2. You might be breaking the Rules of Hooks\n' + 
- '3. You might have more than one copy of React in the same app\n' + 
- 'See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.', 
- );
- });
- describe('invalid hooks', () => {
- it('warns when calling useRef inside useReducer', async () => {
- function App() {
- const [value, dispatch] = useReducer((state, action) => { 
- useRef(0);
- return state + 1; 
- }, 0);
- if (value === 0) {
- dispatch();
- }
- return value;
- }
- let error;
- try {
- await serverRender(<App />);
- } catch (x) {
- error = x;
- }
- expect(error).not.toBe(undefined); 
- expect(error.message).toContain( 
- 'Rendered more hooks than during the previous render',
- );
- });
- });
- itRenders(
- 'can use the same context multiple times in the same function',
- async render => {
- const Context = React.createContext({foo: 0, bar: 0, baz: 0}); 
- function Provider(props) {
- return (
- <Context.Provider 
- value={{foo: props.foo, bar: props.bar, baz: props.baz}}> 
- {props.children} 
- </Context.Provider> 
- );
- }
- function FooAndBar() {
- const {foo} = useContext(Context);
- const {bar} = useContext(Context);
- return <Text text={`Foo: ${foo}, Bar: ${bar}`} />; 
- }
- function Baz() {
- const {baz} = useContext(Context);
- return <Text text={'Baz: ' + baz} />; 
- }
- class Indirection extends React.Component { 
- render() {
- return this.props.children; 
- }
- }
- function App(props) {
- return (
- <div>
- <Provider foo={props.foo} bar={props.bar} baz={props.baz}> 
- <Indirection>
- <Indirection>
- <FooAndBar />
- </Indirection>
- <Indirection>
- <Baz />
- </Indirection>
- </Indirection>
- </Provider>
- </div>
- );
- }
- const domNode = await render(<App foo={1} bar={3} baz={5} />); 
- expect(clearLog()).toEqual(['Foo: 1, Bar: 3', 'Baz: 5']); 
- expect(domNode.childNodes.length).toBe(2); 
- expect(domNode.firstChild.tagName).toEqual('SPAN'); 
- expect(domNode.firstChild.textContent).toEqual('Foo: 1, Bar: 3'); 
- expect(domNode.lastChild.tagName).toEqual('SPAN'); 
- expect(domNode.lastChild.textContent).toEqual('Baz: 5'); 
- },
- );
- describe('useDebugValue', () => {
- itRenders('is a noop', async render => {
- function Counter(props) {
- const debugValue = useDebugValue(123);
- return <Text text={typeof debugValue} />;
- }
- const domNode = await render(<Counter />);
- expect(domNode.textContent).toEqual('undefined'); 
- });
- });
- describe('readContext', () => {
- function readContext(Context) {
- const dispatcher =
- React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED 
- .ReactCurrentDispatcher.current; 
- return dispatcher.readContext(Context); 
- }
- itRenders(
- 'can read the same context multiple times in the same function',
- async render => {
- const Context = React.createContext( 
- {foo: 0, bar: 0, baz: 0},
- (a, b) => {
- let result = 0;
- if (a.foo !== b.foo) { 
- result |= 0b001; 
- }
- if (a.bar !== b.bar) { 
- result |= 0b010; 
- }
- if (a.baz !== b.baz) { 
- result |= 0b100; 
- }
- return result;
- },
- );
- function Provider(props) {
- return (
- <Context.Provider 
- value={{foo: props.foo, bar: props.bar, baz: props.baz}}> 
- {props.children} 
- </Context.Provider> 
- );
- }
- function FooAndBar() {
- const {foo} = readContext(Context, 0b001);
- const {bar} = readContext(Context, 0b010);
- return <Text text={`Foo: ${foo}, Bar: ${bar}`} />; 
- }
- function Baz() {
- const {baz} = readContext(Context, 0b100);
- return <Text text={'Baz: ' + baz} />; 
- }
- class Indirection extends React.Component { 
- shouldComponentUpdate() {
- return false;
- }
- render() {
- return this.props.children; 
- }
- }
- function App(props) {
- return (
- <div>
- <Provider foo={props.foo} bar={props.bar} baz={props.baz}> 
- <Indirection>
- <Indirection>
- <FooAndBar />
- </Indirection>
- <Indirection>
- <Baz />
- </Indirection>
- </Indirection>
- </Provider>
- </div>
- );
- }
- const domNode = await render(<App foo={1} bar={3} baz={5} />); 
- expect(clearLog()).toEqual(['Foo: 1, Bar: 3', 'Baz: 5']); 
- expect(domNode.childNodes.length).toBe(2); 
- expect(domNode.firstChild.tagName).toEqual('SPAN'); 
- expect(domNode.firstChild.textContent).toEqual('Foo: 1, Bar: 3'); 
- expect(domNode.lastChild.tagName).toEqual('SPAN'); 
- expect(domNode.lastChild.textContent).toEqual('Baz: 5'); 
- },
- );
- itRenders('with a warning inside useMemo and useReducer', async render => {
- const Context = React.createContext(42); 
- function ReadInMemo(props) {
- const count = React.useMemo(() => readContext(Context), []); 
- return <Text text={count} />;
- }
- function ReadInReducer(props) {
- const [count, dispatch] = React.useReducer(() => readContext(Context)); 
- if (count !== 42) {
- dispatch();
- }
- return <Text text={count} />;
- }
- const domNode1 = await render(<ReadInMemo />, 1);
- expect(domNode1.textContent).toEqual('42'); 
- const domNode2 = await render(<ReadInReducer />, 1);
- expect(domNode2.textContent).toEqual('42'); 
- });
- });
- it('renders successfully after a component using hooks throws an error', () => {
- function ThrowingComponent() {
- const [value, dispatch] = useReducer((state, action) => { 
- return state + 1; 
- }, 0);
- // throw an error if the count gets too high during the re-render phase
- if (value >= 3) {
- throw new Error('Error from ThrowingComponent');
- } else {
- // dispatch to trigger a re-render of the component
- dispatch();
- }
- return <div>{value}</div>;
- }
- function NonThrowingComponent() {
- const [count] = useState(0); 
- return <div>{count}</div>;
- }
- // First, render a component that will throw an error during a re-render triggered
- // by a dispatch call. 
- expect(() => ReactDOMServer.renderToString(<ThrowingComponent />)).toThrow( 
- 'Error from ThrowingComponent',
- );
- // Next, assert that we can render a function component using hooks immediately
- // after an error occurred, which indictates the internal hooks state has been
- // reset. 
- const container = document.createElement('div'); 
- container.innerHTML = ReactDOMServer.renderToString( 
- <NonThrowingComponent />,
- );
- expect(container.children[0].textContent).toEqual('0'); 
- });
- });