diff --git a/packages/react-dom/src/__tests__/ReactTestUtils-test.js b/packages/react-dom/src/__tests__/ReactTestUtils-test.js index 8b1bccac4416a..4433626f25acb 100644 --- a/packages/react-dom/src/__tests__/ReactTestUtils-test.js +++ b/packages/react-dom/src/__tests__/ReactTestUtils-test.js @@ -9,7 +9,6 @@ 'use strict'; -import ReactShallowRenderer from 'react-test-renderer/shallow'; import * as React from 'react'; import * as ReactDOM from 'react-dom'; import * as ReactDOMServer from 'react-dom/server'; @@ -383,48 +382,6 @@ describe('ReactTestUtils', () => { ); }); - it('should throw when attempting to use a React element', () => { - class SomeComponent extends React.Component { - render() { - return
hello, world.
; - } - } - - const handler = jest.fn().mockName('spy'); - const shallowRenderer = ReactShallowRenderer.createRenderer(); - const result = shallowRenderer.render( - , - ); - - expect(() => ReactTestUtils.Simulate.click(result)).toThrowError( - 'TestUtils.Simulate expected a DOM node as the first argument but received ' + - 'a React element. Pass the DOM node you wish to simulate the event on instead. ' + - 'Note that TestUtils.Simulate will not work if you are using shallow rendering.', - ); - expect(handler).not.toHaveBeenCalled(); - }); - - it('should throw when attempting to use a component instance', () => { - class SomeComponent extends React.Component { - render() { - return
hello, world.
; - } - } - - const handler = jest.fn().mockName('spy'); - const container = document.createElement('div'); - const instance = ReactDOM.render( - , - container, - ); - - expect(() => ReactTestUtils.Simulate.click(instance)).toThrowError( - 'TestUtils.Simulate expected a DOM node as the first argument but received ' + - 'a component instance. Pass the DOM node you wish to simulate the event on instead.', - ); - expect(handler).not.toHaveBeenCalled(); - }); - it('should not warn when used with extra properties', () => { const CLIENT_X = 100; diff --git a/packages/react-test-renderer/src/__tests__/ReactShallowRenderer-test.js b/packages/react-test-renderer/src/__tests__/ReactShallowRenderer-test.js deleted file mode 100644 index c7e7a31acaaf6..0000000000000 --- a/packages/react-test-renderer/src/__tests__/ReactShallowRenderer-test.js +++ /dev/null @@ -1,1604 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its 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 node - */ - -'use strict'; - -import * as PropTypes from 'prop-types'; -import * as React from 'react'; -import ReactShallowRenderer from 'react-test-renderer/shallow'; - -const createRenderer = ReactShallowRenderer.createRenderer; - -describe('ReactShallowRenderer', () => { - it('should call all of the legacy lifecycle hooks', () => { - const logs = []; - const logger = message => () => logs.push(message) || true; - - class SomeComponent extends React.Component { - UNSAFE_componentWillMount = logger('componentWillMount'); - componentDidMount = logger('componentDidMount'); - UNSAFE_componentWillReceiveProps = logger('componentWillReceiveProps'); - shouldComponentUpdate = logger('shouldComponentUpdate'); - UNSAFE_componentWillUpdate = logger('componentWillUpdate'); - componentDidUpdate = logger('componentDidUpdate'); - componentWillUnmount = logger('componentWillUnmount'); - render() { - return
; - } - } - - const shallowRenderer = createRenderer(); - shallowRenderer.render(); - - // Calling cDU might lead to problems with host component references. - // Since our components aren't really mounted, refs won't be available. - expect(logs).toEqual(['componentWillMount']); - - logs.splice(0); - - const instance = shallowRenderer.getMountedInstance(); - instance.setState({}); - - expect(logs).toEqual(['shouldComponentUpdate', 'componentWillUpdate']); - - logs.splice(0); - - shallowRenderer.render(); - - // The previous shallow renderer did not trigger cDU for props changes. - expect(logs).toEqual([ - 'componentWillReceiveProps', - 'shouldComponentUpdate', - 'componentWillUpdate', - ]); - }); - - it('should call all of the new lifecycle hooks', () => { - const logs = []; - const logger = message => () => logs.push(message) || true; - - class SomeComponent extends React.Component { - state = {}; - static getDerivedStateFromProps = logger('getDerivedStateFromProps'); - componentDidMount = logger('componentDidMount'); - shouldComponentUpdate = logger('shouldComponentUpdate'); - componentDidUpdate = logger('componentDidUpdate'); - componentWillUnmount = logger('componentWillUnmount'); - render() { - return
; - } - } - - const shallowRenderer = createRenderer(); - shallowRenderer.render(); - - // Calling cDU might lead to problems with host component references. - // Since our components aren't really mounted, refs won't be available. - expect(logs).toEqual(['getDerivedStateFromProps']); - - logs.splice(0); - - const instance = shallowRenderer.getMountedInstance(); - instance.setState({}); - - expect(logs).toEqual(['getDerivedStateFromProps', 'shouldComponentUpdate']); - - logs.splice(0); - - shallowRenderer.render(); - - // The previous shallow renderer did not trigger cDU for props changes. - expect(logs).toEqual(['getDerivedStateFromProps', 'shouldComponentUpdate']); - }); - - it('should not invoke deprecated lifecycles (cWM/cWRP/cWU) if new static gDSFP is present', () => { - class Component extends React.Component { - state = {}; - static getDerivedStateFromProps() { - return null; - } - componentWillMount() { - throw Error('unexpected'); - } - componentWillReceiveProps() { - throw Error('unexpected'); - } - componentWillUpdate() { - throw Error('unexpected'); - } - render() { - return null; - } - } - - const shallowRenderer = createRenderer(); - shallowRenderer.render(); - }); - - it('should not invoke deprecated lifecycles (cWM/cWRP/cWU) if new getSnapshotBeforeUpdate is present', () => { - class Component extends React.Component { - getSnapshotBeforeUpdate() { - return null; - } - componentWillMount() { - throw Error('unexpected'); - } - componentWillReceiveProps() { - throw Error('unexpected'); - } - componentWillUpdate() { - throw Error('unexpected'); - } - render() { - return null; - } - } - - const shallowRenderer = createRenderer(); - shallowRenderer.render(); - shallowRenderer.render(); - }); - - it('should not call getSnapshotBeforeUpdate or componentDidUpdate when updating since refs wont exist', () => { - class Component extends React.Component { - getSnapshotBeforeUpdate() { - throw Error('unexpected'); - } - componentDidUpdate() { - throw Error('unexpected'); - } - render() { - return null; - } - } - - const shallowRenderer = createRenderer(); - shallowRenderer.render(); - shallowRenderer.render(); - }); - - it('should only render 1 level deep', () => { - function Parent() { - return ( -
- -
- ); - } - function Child() { - throw Error('This component should not render'); - } - - const shallowRenderer = createRenderer(); - shallowRenderer.render(React.createElement(Parent)); - }); - - it('should have shallow rendering', () => { - class SomeComponent extends React.Component { - render() { - return ( -
- - -
- ); - } - } - - const shallowRenderer = createRenderer(); - const result = shallowRenderer.render(); - - expect(result.type).toBe('div'); - expect(result.props.children).toEqual([ - , - , - ]); - }); - - it('should handle ForwardRef', () => { - const testRef = React.createRef(); - const SomeComponent = React.forwardRef((props, ref) => { - expect(ref).toEqual(testRef); - return ( -
- - -
- ); - }); - - const shallowRenderer = createRenderer(); - const result = shallowRenderer.render(); - - expect(result.type).toBe('div'); - expect(result.props.children).toEqual([ - , - , - ]); - }); - - it('should handle Profiler', () => { - class SomeComponent extends React.Component { - render() { - return ( - -
- - -
-
- ); - } - } - - const shallowRenderer = createRenderer(); - const result = shallowRenderer.render(); - - expect(result.type).toBe(React.Profiler); - expect(result.props.children).toEqual( -
- - -
, - ); - }); - - it('should enable shouldComponentUpdate to prevent a re-render', () => { - let renderCounter = 0; - class SimpleComponent extends React.Component { - state = {update: false}; - shouldComponentUpdate(nextProps, nextState) { - return this.state.update !== nextState.update; - } - render() { - renderCounter++; - return
{`${renderCounter}`}
; - } - } - - const shallowRenderer = createRenderer(); - shallowRenderer.render(); - expect(shallowRenderer.getRenderOutput()).toEqual(
1
); - - const instance = shallowRenderer.getMountedInstance(); - instance.setState({update: false}); - expect(shallowRenderer.getRenderOutput()).toEqual(
1
); - - instance.setState({update: true}); - expect(shallowRenderer.getRenderOutput()).toEqual(
2
); - }); - - it('should enable PureComponent to prevent a re-render', () => { - let renderCounter = 0; - class SimpleComponent extends React.PureComponent { - state = {update: false}; - render() { - renderCounter++; - return
{`${renderCounter}`}
; - } - } - - const shallowRenderer = createRenderer(); - shallowRenderer.render(); - expect(shallowRenderer.getRenderOutput()).toEqual(
1
); - - const instance = shallowRenderer.getMountedInstance(); - instance.setState({update: false}); - expect(shallowRenderer.getRenderOutput()).toEqual(
1
); - - instance.setState({update: true}); - expect(shallowRenderer.getRenderOutput()).toEqual(
2
); - }); - - it('should not run shouldComponentUpdate during forced update', () => { - let scuCounter = 0; - class SimpleComponent extends React.Component { - state = {count: 1}; - shouldComponentUpdate() { - scuCounter++; - return false; - } - render() { - return
{`${this.state.count}`}
; - } - } - - const shallowRenderer = createRenderer(); - shallowRenderer.render(); - expect(scuCounter).toEqual(0); - expect(shallowRenderer.getRenderOutput()).toEqual(
1
); - - // Force update the initial state. sCU should not fire. - const instance = shallowRenderer.getMountedInstance(); - instance.forceUpdate(); - expect(scuCounter).toEqual(0); - expect(shallowRenderer.getRenderOutput()).toEqual(
1
); - - // Setting state updates the instance, but doesn't re-render - // because sCU returned false. - instance.setState(state => ({count: state.count + 1})); - expect(scuCounter).toEqual(1); - expect(instance.state.count).toEqual(2); - expect(shallowRenderer.getRenderOutput()).toEqual(
1
); - - // A force update updates the render output, but doesn't call sCU. - instance.forceUpdate(); - expect(scuCounter).toEqual(1); - expect(instance.state.count).toEqual(2); - expect(shallowRenderer.getRenderOutput()).toEqual(
2
); - }); - - it('should rerender when calling forceUpdate', () => { - let renderCounter = 0; - class SimpleComponent extends React.Component { - render() { - renderCounter += 1; - return
; - } - } - - const shallowRenderer = createRenderer(); - shallowRenderer.render(); - expect(renderCounter).toEqual(1); - - const instance = shallowRenderer.getMountedInstance(); - instance.forceUpdate(); - expect(renderCounter).toEqual(2); - }); - - it('should shallow render a function component', () => { - function SomeComponent(props, context) { - return ( -
-
{props.foo}
-
{context.bar}
- - -
- ); - } - SomeComponent.contextTypes = { - bar: PropTypes.string, - }; - - const shallowRenderer = createRenderer(); - const result = shallowRenderer.render(, { - bar: 'BAR', - }); - - expect(result.type).toBe('div'); - expect(result.props.children).toEqual([ -
FOO
, -
BAR
, - , - , - ]); - }); - - it('should shallow render a component returning strings directly from render', () => { - const Text = ({value}) => value; - - const shallowRenderer = createRenderer(); - const result = shallowRenderer.render(); - expect(result).toEqual('foo'); - }); - - it('should shallow render a component returning numbers directly from render', () => { - const Text = ({value}) => value; - - const shallowRenderer = createRenderer(); - const result = shallowRenderer.render(); - expect(result).toEqual(10); - }); - - it('should shallow render a fragment', () => { - class SomeComponent extends React.Component { - render() { - return
; - } - } - class Fragment extends React.Component { - render() { - return [
, , ]; - } - } - const shallowRenderer = createRenderer(); - const result = shallowRenderer.render(); - expect(result).toEqual([ -
, - , - , - ]); - }); - - it('should shallow render a React.fragment', () => { - class SomeComponent extends React.Component { - render() { - return
; - } - } - class Fragment extends React.Component { - render() { - return ( - <> -
- - - - ); - } - } - const shallowRenderer = createRenderer(); - const result = shallowRenderer.render(); - expect(result).toEqual( - <> -
- - - , - ); - }); - - it('should throw for invalid elements', () => { - class SomeComponent extends React.Component { - render() { - return
; - } - } - - const shallowRenderer = createRenderer(); - expect(() => shallowRenderer.render(SomeComponent)).toThrowError( - 'ReactShallowRenderer render(): Invalid component element. Instead of ' + - 'passing a component class, make sure to instantiate it by passing it ' + - 'to React.createElement.', - ); - expect(() => shallowRenderer.render(
)).toThrowError( - 'ReactShallowRenderer render(): Shallow rendering works only with ' + - 'custom components, not primitives (div). Instead of calling ' + - '`.render(el)` and inspecting the rendered output, look at `el.props` ' + - 'directly instead.', - ); - }); - - it('should have shallow unmounting', () => { - const componentWillUnmount = jest.fn(); - - class SomeComponent extends React.Component { - componentWillUnmount = componentWillUnmount; - render() { - return
; - } - } - - const shallowRenderer = createRenderer(); - shallowRenderer.render(); - shallowRenderer.unmount(); - - expect(componentWillUnmount).toBeCalled(); - }); - - it('can shallow render to null', () => { - class SomeComponent extends React.Component { - render() { - return null; - } - } - - const shallowRenderer = createRenderer(); - const result = shallowRenderer.render(); - - expect(result).toBe(null); - }); - - it('can shallow render with a ref', () => { - class SomeComponent extends React.Component { - render() { - return
; - } - } - - const shallowRenderer = createRenderer(); - // Shouldn't crash. - shallowRenderer.render(); - }); - - it('lets you update shallowly rendered components', () => { - class SomeComponent extends React.Component { - state = {clicked: false}; - - onClick = () => { - this.setState({clicked: true}); - }; - - render() { - const className = this.state.clicked ? 'was-clicked' : ''; - - if (this.props.aNew === 'prop') { - return ( - - Test link - - ); - } else { - return ( -
- - -
- ); - } - } - } - - const shallowRenderer = createRenderer(); - const result = shallowRenderer.render(); - expect(result.type).toBe('div'); - expect(result.props.children).toEqual([ - , - , - ]); - - const updatedResult = shallowRenderer.render(); - expect(updatedResult.type).toBe('a'); - - const mockEvent = {}; - updatedResult.props.onClick(mockEvent); - - const updatedResultCausedByClick = shallowRenderer.getRenderOutput(); - expect(updatedResultCausedByClick.type).toBe('a'); - expect(updatedResultCausedByClick.props.className).toBe('was-clicked'); - }); - - it('can access the mounted component instance', () => { - class SimpleComponent extends React.Component { - someMethod = () => { - return this.props.n; - }; - - render() { - return
{this.props.n}
; - } - } - - const shallowRenderer = createRenderer(); - shallowRenderer.render(); - expect(shallowRenderer.getMountedInstance().someMethod()).toEqual(5); - }); - - it('can shallowly render components with contextTypes', () => { - class SimpleComponent extends React.Component { - static contextTypes = { - name: PropTypes.string, - }; - - render() { - return
; - } - } - - const shallowRenderer = createRenderer(); - const result = shallowRenderer.render(); - expect(result).toEqual(
); - }); - - it('passes expected params to legacy component lifecycle methods', () => { - const componentDidUpdateParams = []; - const componentWillReceivePropsParams = []; - const componentWillUpdateParams = []; - const setStateParams = []; - const shouldComponentUpdateParams = []; - - const initialProp = {prop: 'init prop'}; - const initialState = {state: 'init state'}; - const initialContext = {context: 'init context'}; - const updatedState = {state: 'updated state'}; - const updatedProp = {prop: 'updated prop'}; - const updatedContext = {context: 'updated context'}; - - class SimpleComponent extends React.Component { - constructor(props, context) { - super(props, context); - this.state = initialState; - } - static contextTypes = { - context: PropTypes.string, - }; - componentDidUpdate(...args) { - componentDidUpdateParams.push(...args); - } - UNSAFE_componentWillReceiveProps(...args) { - componentWillReceivePropsParams.push(...args); - this.setState((...innerArgs) => { - setStateParams.push(...innerArgs); - return updatedState; - }); - } - UNSAFE_componentWillUpdate(...args) { - componentWillUpdateParams.push(...args); - } - shouldComponentUpdate(...args) { - shouldComponentUpdateParams.push(...args); - return true; - } - render() { - return null; - } - } - - const shallowRenderer = createRenderer(); - shallowRenderer.render( - React.createElement(SimpleComponent, initialProp), - initialContext, - ); - expect(componentDidUpdateParams).toEqual([]); - expect(componentWillReceivePropsParams).toEqual([]); - expect(componentWillUpdateParams).toEqual([]); - expect(setStateParams).toEqual([]); - expect(shouldComponentUpdateParams).toEqual([]); - - // Lifecycle hooks should be invoked with the correct prev/next params on update. - shallowRenderer.render( - React.createElement(SimpleComponent, updatedProp), - updatedContext, - ); - expect(componentWillReceivePropsParams).toEqual([ - updatedProp, - updatedContext, - ]); - expect(setStateParams).toEqual([initialState, initialProp]); - expect(shouldComponentUpdateParams).toEqual([ - updatedProp, - updatedState, - updatedContext, - ]); - expect(componentWillUpdateParams).toEqual([ - updatedProp, - updatedState, - updatedContext, - ]); - expect(componentDidUpdateParams).toEqual([]); - }); - - it('passes expected params to new component lifecycle methods', () => { - const componentDidUpdateParams = []; - const getDerivedStateFromPropsParams = []; - const shouldComponentUpdateParams = []; - - const initialProp = {prop: 'init prop'}; - const initialState = {state: 'init state'}; - const initialContext = {context: 'init context'}; - const updatedProp = {prop: 'updated prop'}; - const updatedContext = {context: 'updated context'}; - - class SimpleComponent extends React.Component { - constructor(props, context) { - super(props, context); - this.state = initialState; - } - static contextTypes = { - context: PropTypes.string, - }; - componentDidUpdate(...args) { - componentDidUpdateParams.push(...args); - } - static getDerivedStateFromProps(...args) { - getDerivedStateFromPropsParams.push(args); - return null; - } - shouldComponentUpdate(...args) { - shouldComponentUpdateParams.push(...args); - return true; - } - render() { - return null; - } - } - - const shallowRenderer = createRenderer(); - - // The only lifecycle hook that should be invoked on initial render - // Is the static getDerivedStateFromProps() methods - shallowRenderer.render( - React.createElement(SimpleComponent, initialProp), - initialContext, - ); - expect(getDerivedStateFromPropsParams).toEqual([ - [initialProp, initialState], - ]); - expect(componentDidUpdateParams).toEqual([]); - expect(shouldComponentUpdateParams).toEqual([]); - - // Lifecycle hooks should be invoked with the correct prev/next params on update. - shallowRenderer.render( - React.createElement(SimpleComponent, updatedProp), - updatedContext, - ); - expect(getDerivedStateFromPropsParams).toEqual([ - [initialProp, initialState], - [updatedProp, initialState], - ]); - expect(shouldComponentUpdateParams).toEqual([ - updatedProp, - initialState, - updatedContext, - ]); - expect(componentDidUpdateParams).toEqual([]); - }); - - it('can shallowly render components with ref as function', () => { - class SimpleComponent extends React.Component { - state = {clicked: false}; - - handleUserClick = () => { - this.setState({clicked: true}); - }; - - render() { - return ( -
{}} - onClick={this.handleUserClick} - className={this.state.clicked ? 'clicked' : ''} - /> - ); - } - } - - const shallowRenderer = createRenderer(); - shallowRenderer.render(); - let result = shallowRenderer.getRenderOutput(); - expect(result.type).toEqual('div'); - expect(result.props.className).toEqual(''); - result.props.onClick(); - - result = shallowRenderer.getRenderOutput(); - expect(result.type).toEqual('div'); - expect(result.props.className).toEqual('clicked'); - }); - - it('can initialize state via static getDerivedStateFromProps', () => { - class SimpleComponent extends React.Component { - state = { - count: 1, - }; - - static getDerivedStateFromProps(props, prevState) { - return { - count: prevState.count + props.incrementBy, - other: 'foobar', - }; - } - - render() { - return ( -
{`count:${this.state.count}, other:${this.state.other}`}
- ); - } - } - - const shallowRenderer = createRenderer(); - const result = shallowRenderer.render(); - expect(result).toEqual(
count:3, other:foobar
); - }); - - it('can setState in componentWillMount when shallow rendering', () => { - class SimpleComponent extends React.Component { - UNSAFE_componentWillMount() { - this.setState({groovy: 'doovy'}); - } - - render() { - return
{this.state.groovy}
; - } - } - - const shallowRenderer = createRenderer(); - const result = shallowRenderer.render(); - expect(result).toEqual(
doovy
); - }); - - it('can setState in componentWillMount repeatedly when shallow rendering', () => { - class SimpleComponent extends React.Component { - state = { - separator: '-', - }; - - UNSAFE_componentWillMount() { - this.setState({groovy: 'doovy'}); - this.setState({doovy: 'groovy'}); - } - - render() { - const {groovy, doovy, separator} = this.state; - - return
{`${groovy}${separator}${doovy}`}
; - } - } - - const shallowRenderer = createRenderer(); - const result = shallowRenderer.render(); - expect(result).toEqual(
doovy-groovy
); - }); - - it('can setState in componentWillMount with an updater function repeatedly when shallow rendering', () => { - class SimpleComponent extends React.Component { - state = { - separator: '-', - }; - - UNSAFE_componentWillMount() { - this.setState(state => ({groovy: 'doovy'})); - this.setState(state => ({doovy: state.groovy})); - } - - render() { - const {groovy, doovy, separator} = this.state; - - return
{`${groovy}${separator}${doovy}`}
; - } - } - - const shallowRenderer = createRenderer(); - const result = shallowRenderer.render(); - expect(result).toEqual(
doovy-doovy
); - }); - - it('can setState in componentWillReceiveProps when shallow rendering', () => { - class SimpleComponent extends React.Component { - state = {count: 0}; - - UNSAFE_componentWillReceiveProps(nextProps) { - if (nextProps.updateState) { - this.setState({count: 1}); - } - } - - render() { - return
{this.state.count}
; - } - } - - const shallowRenderer = createRenderer(); - let result = shallowRenderer.render( - , - ); - expect(result.props.children).toEqual(0); - - result = shallowRenderer.render(); - expect(result.props.children).toEqual(1); - }); - - it('can update state with static getDerivedStateFromProps when shallow rendering', () => { - class SimpleComponent extends React.Component { - state = {count: 1}; - - static getDerivedStateFromProps(nextProps, prevState) { - if (nextProps.updateState) { - return {count: nextProps.incrementBy + prevState.count}; - } - - return null; - } - - render() { - return
{this.state.count}
; - } - } - - const shallowRenderer = createRenderer(); - let result = shallowRenderer.render( - , - ); - expect(result.props.children).toEqual(1); - - result = shallowRenderer.render( - , - ); - expect(result.props.children).toEqual(3); - - result = shallowRenderer.render( - , - ); - expect(result.props.children).toEqual(3); - }); - - it('should not override state with stale values if prevState is spread within getDerivedStateFromProps', () => { - class SimpleComponent extends React.Component { - state = {value: 0}; - - static getDerivedStateFromProps(nextProps, prevState) { - return {...prevState}; - } - - updateState = () => { - this.setState(state => ({value: state.value + 1})); - }; - - render() { - return
{`value:${this.state.value}`}
; - } - } - - const shallowRenderer = createRenderer(); - let result = shallowRenderer.render(); - expect(result).toEqual(
value:0
); - - const instance = shallowRenderer.getMountedInstance(); - instance.updateState(); - result = shallowRenderer.getRenderOutput(); - expect(result).toEqual(
value:1
); - }); - - it('should pass previous state to shouldComponentUpdate even with getDerivedStateFromProps', () => { - class SimpleComponent extends React.Component { - constructor(props) { - super(props); - this.state = { - value: props.value, - }; - } - - static getDerivedStateFromProps(nextProps, prevState) { - if (nextProps.value === prevState.value) { - return null; - } - return {value: nextProps.value}; - } - - shouldComponentUpdate(nextProps, nextState) { - return nextState.value !== this.state.value; - } - - render() { - return
{`value:${this.state.value}`}
; - } - } - - const shallowRenderer = createRenderer(); - const initialResult = shallowRenderer.render( - , - ); - expect(initialResult).toEqual(
value:initial
); - const updatedResult = shallowRenderer.render( - , - ); - expect(updatedResult).toEqual(
value:updated
); - }); - - it('can setState with an updater function', () => { - let instance; - - class SimpleComponent extends React.Component { - state = { - counter: 0, - }; - - render() { - instance = this; - return ( - - ); - } - } - - const shallowRenderer = createRenderer(); - let result = shallowRenderer.render(); - expect(result.props.children).toEqual(0); - - instance.setState((state, props) => { - return {counter: props.defaultCount + 1}; - }); - - result = shallowRenderer.getRenderOutput(); - expect(result.props.children).toEqual(2); - }); - - it('can access component instance from setState updater function', done => { - let instance; - - class SimpleComponent extends React.Component { - state = {}; - - render() { - instance = this; - return null; - } - } - - const shallowRenderer = createRenderer(); - shallowRenderer.render(); - - instance.setState(function updater(state, props) { - expect(this).toBe(instance); - done(); - }); - }); - - it('can setState with a callback', () => { - let instance; - - class SimpleComponent extends React.Component { - state = { - counter: 0, - }; - render() { - instance = this; - return

{this.state.counter}

; - } - } - - const shallowRenderer = createRenderer(); - const result = shallowRenderer.render(); - expect(result.props.children).toBe(0); - - const callback = jest.fn(function() { - expect(this).toBe(instance); - }); - - instance.setState({counter: 1}, callback); - - const updated = shallowRenderer.getRenderOutput(); - expect(updated.props.children).toBe(1); - expect(callback).toHaveBeenCalled(); - }); - - it('can replaceState with a callback', () => { - let instance; - - class SimpleComponent extends React.Component { - state = { - counter: 0, - }; - render() { - instance = this; - return

{this.state.counter}

; - } - } - - const shallowRenderer = createRenderer(); - const result = shallowRenderer.render(); - expect(result.props.children).toBe(0); - - const callback = jest.fn(function() { - expect(this).toBe(instance); - }); - - // No longer a public API, but we can test that it works internally by - // reaching into the updater. - shallowRenderer._updater.enqueueReplaceState( - instance, - {counter: 1}, - callback, - ); - - const updated = shallowRenderer.getRenderOutput(); - expect(updated.props.children).toBe(1); - expect(callback).toHaveBeenCalled(); - }); - - it('can forceUpdate with a callback', () => { - let instance; - - class SimpleComponent extends React.Component { - state = { - counter: 0, - }; - render() { - instance = this; - return

{this.state.counter}

; - } - } - - const shallowRenderer = createRenderer(); - const result = shallowRenderer.render(); - expect(result.props.children).toBe(0); - - const callback = jest.fn(function() { - expect(this).toBe(instance); - }); - - instance.forceUpdate(callback); - - const updated = shallowRenderer.getRenderOutput(); - expect(updated.props.children).toBe(0); - expect(callback).toHaveBeenCalled(); - }); - - it('can pass context when shallowly rendering', () => { - class SimpleComponent extends React.Component { - static contextTypes = { - name: PropTypes.string, - }; - - render() { - return
{this.context.name}
; - } - } - - const shallowRenderer = createRenderer(); - const result = shallowRenderer.render(, { - name: 'foo', - }); - expect(result).toEqual(
foo
); - }); - - it('should track context across updates', () => { - class SimpleComponent extends React.Component { - static contextTypes = { - foo: PropTypes.string, - }; - - state = { - bar: 'bar', - }; - - render() { - return
{`${this.context.foo}:${this.state.bar}`}
; - } - } - - const shallowRenderer = createRenderer(); - let result = shallowRenderer.render(, { - foo: 'foo', - }); - expect(result).toEqual(
foo:bar
); - - const instance = shallowRenderer.getMountedInstance(); - instance.setState({bar: 'baz'}); - - result = shallowRenderer.getRenderOutput(); - expect(result).toEqual(
foo:baz
); - }); - - it('should filter context by contextTypes', () => { - class SimpleComponent extends React.Component { - static contextTypes = { - foo: PropTypes.string, - }; - render() { - return
{`${this.context.foo}:${this.context.bar}`}
; - } - } - - const shallowRenderer = createRenderer(); - const result = shallowRenderer.render(, { - foo: 'foo', - bar: 'bar', - }); - expect(result).toEqual(
foo:undefined
); - }); - - it('can fail context when shallowly rendering', () => { - class SimpleComponent extends React.Component { - static contextTypes = { - name: PropTypes.string.isRequired, - }; - - render() { - return
{this.context.name}
; - } - } - - const shallowRenderer = createRenderer(); - expect(() => shallowRenderer.render()).toErrorDev( - 'Warning: Failed context type: The context `name` is marked as ' + - 'required in `SimpleComponent`, but its value is `undefined`.\n' + - ' in SimpleComponent (at **)', - ); - }); - - it('should warn about propTypes (but only once)', () => { - class SimpleComponent extends React.Component { - render() { - return React.createElement('div', null, this.props.name); - } - } - - SimpleComponent.propTypes = { - name: PropTypes.string.isRequired, - }; - - const shallowRenderer = createRenderer(); - expect(() => - shallowRenderer.render(React.createElement(SimpleComponent, {name: 123})), - ).toErrorDev( - 'Warning: Failed prop type: Invalid prop `name` of type `number` ' + - 'supplied to `SimpleComponent`, expected `string`.\n' + - ' in SimpleComponent', - ); - }); - - it('should enable rendering of cloned element', () => { - class SimpleComponent extends React.Component { - constructor(props) { - super(props); - - this.state = { - bar: 'bar', - }; - } - - render() { - return
{`${this.props.foo}:${this.state.bar}`}
; - } - } - - const shallowRenderer = createRenderer(); - const el = ; - let result = shallowRenderer.render(el); - expect(result).toEqual(
foo:bar
); - - const cloned = React.cloneElement(el, {foo: 'baz'}); - result = shallowRenderer.render(cloned); - expect(result).toEqual(
baz:bar
); - }); - - it('this.state should be updated on setState callback inside componentWillMount', () => { - let stateSuccessfullyUpdated = false; - - class Component extends React.Component { - constructor(props, context) { - super(props, context); - this.state = { - hasUpdatedState: false, - }; - } - - UNSAFE_componentWillMount() { - this.setState( - {hasUpdatedState: true}, - () => (stateSuccessfullyUpdated = this.state.hasUpdatedState), - ); - } - - render() { - return
{this.props.children}
; - } - } - - const shallowRenderer = createRenderer(); - shallowRenderer.render(); - expect(stateSuccessfullyUpdated).toBe(true); - }); - - it('should handle multiple callbacks', () => { - const mockFn = jest.fn(); - const shallowRenderer = createRenderer(); - - class Component extends React.Component { - constructor(props, context) { - super(props, context); - this.state = { - foo: 'foo', - }; - } - - UNSAFE_componentWillMount() { - this.setState({foo: 'bar'}, () => mockFn()); - this.setState({foo: 'foobar'}, () => mockFn()); - } - - render() { - return
{this.state.foo}
; - } - } - - shallowRenderer.render(); - - expect(mockFn).toHaveBeenCalledTimes(2); - - // Ensure the callback queue is cleared after the callbacks are invoked - const mountedInstance = shallowRenderer.getMountedInstance(); - mountedInstance.setState({foo: 'bar'}, () => mockFn()); - expect(mockFn).toHaveBeenCalledTimes(3); - }); - - it('should call the setState callback even if shouldComponentUpdate = false', done => { - const mockFn = jest.fn().mockReturnValue(false); - - class Component extends React.Component { - constructor(props, context) { - super(props, context); - this.state = { - hasUpdatedState: false, - }; - } - - shouldComponentUpdate() { - return mockFn(); - } - - render() { - return
{this.state.hasUpdatedState}
; - } - } - - const shallowRenderer = createRenderer(); - shallowRenderer.render(); - - const mountedInstance = shallowRenderer.getMountedInstance(); - mountedInstance.setState({hasUpdatedState: true}, () => { - expect(mockFn).toBeCalled(); - expect(mountedInstance.state.hasUpdatedState).toBe(true); - done(); - }); - }); - - it('throws usefully when rendering badly-typed elements', () => { - const shallowRenderer = createRenderer(); - - const renderAndVerifyWarningAndError = (Component, typeString) => { - expect(() => { - expect(() => shallowRenderer.render()).toErrorDev( - 'React.createElement: type is invalid -- expected a string ' + - '(for built-in components) or a class/function (for composite components) ' + - `but got: ${typeString}.`, - ); - }).toThrowError( - 'ReactShallowRenderer render(): Shallow rendering works only with custom ' + - `components, but the provided element type was \`${typeString}\`.`, - ); - }; - - renderAndVerifyWarningAndError(undefined, 'undefined'); - renderAndVerifyWarningAndError(null, 'null'); - renderAndVerifyWarningAndError([], 'array'); - renderAndVerifyWarningAndError({}, 'object'); - }); - - it('should have initial state of null if not defined', () => { - class SomeComponent extends React.Component { - render() { - return ; - } - } - - const shallowRenderer = createRenderer(); - shallowRenderer.render(); - - expect(shallowRenderer.getMountedInstance().state).toBeNull(); - }); - - it('should invoke both deprecated and new lifecycles if both are present', () => { - const log = []; - - class Component extends React.Component { - componentWillMount() { - log.push('componentWillMount'); - } - componentWillReceiveProps() { - log.push('componentWillReceiveProps'); - } - componentWillUpdate() { - log.push('componentWillUpdate'); - } - UNSAFE_componentWillMount() { - log.push('UNSAFE_componentWillMount'); - } - UNSAFE_componentWillReceiveProps() { - log.push('UNSAFE_componentWillReceiveProps'); - } - UNSAFE_componentWillUpdate() { - log.push('UNSAFE_componentWillUpdate'); - } - render() { - return null; - } - } - - const shallowRenderer = createRenderer(); - shallowRenderer.render(); - expect(log).toEqual(['componentWillMount', 'UNSAFE_componentWillMount']); - - log.length = 0; - - shallowRenderer.render(); - expect(log).toEqual([ - 'componentWillReceiveProps', - 'UNSAFE_componentWillReceiveProps', - 'componentWillUpdate', - 'UNSAFE_componentWillUpdate', - ]); - }); - - it('should stop the update when setState returns null or undefined', () => { - const log = []; - let instance; - class Component extends React.Component { - constructor(props) { - super(props); - this.state = { - count: 0, - }; - } - render() { - log.push('render'); - instance = this; - return null; - } - } - const shallowRenderer = createRenderer(); - shallowRenderer.render(); - log.length = 0; - instance.setState(() => null); - instance.setState(() => undefined); - instance.setState(null); - instance.setState(undefined); - expect(log).toEqual([]); - instance.setState(state => ({count: state.count + 1})); - expect(log).toEqual(['render']); - }); - - it('should not get this in a function component', () => { - const logs = []; - function Foo() { - logs.push(this); - return
foo
; - } - const shallowRenderer = createRenderer(); - shallowRenderer.render(); - expect(logs).toEqual([undefined]); - }); - - it('should handle memo', () => { - function Foo() { - return
foo
; - } - const MemoFoo = React.memo(Foo); - const shallowRenderer = createRenderer(); - shallowRenderer.render(); - }); - - it('should enable React.memo to prevent a re-render', () => { - const logs = []; - const Foo = React.memo(({count}) => { - logs.push(`Foo: ${count}`); - return
{count}
; - }); - const Bar = React.memo(({count}) => { - logs.push(`Bar: ${count}`); - return
{count}
; - }); - const shallowRenderer = createRenderer(); - shallowRenderer.render(); - expect(logs).toEqual(['Foo: 1']); - logs.length = 0; - // Rendering the same element with the same props should be prevented - shallowRenderer.render(); - expect(logs).toEqual([]); - // A different element with the same props should cause a re-render - shallowRenderer.render(); - expect(logs).toEqual(['Bar: 1']); - }); - - it('should respect a custom comparison function with React.memo', () => { - let renderCount = 0; - function areEqual(props, nextProps) { - return props.foo === nextProps.foo; - } - const Foo = React.memo(({foo, bar}) => { - renderCount++; - return ( -
- {foo} {bar} -
- ); - }, areEqual); - - const shallowRenderer = createRenderer(); - shallowRenderer.render(); - expect(renderCount).toBe(1); - // Change a prop that the comparison function ignores - shallowRenderer.render(); - expect(renderCount).toBe(1); - shallowRenderer.render(); - expect(renderCount).toBe(2); - }); - - it('should not call the comparison function with React.memo on the initial render', () => { - const areEqual = jest.fn(() => false); - const SomeComponent = React.memo(({foo}) => { - return
{foo}
; - }, areEqual); - const shallowRenderer = createRenderer(); - shallowRenderer.render(); - expect(areEqual).not.toHaveBeenCalled(); - expect(shallowRenderer.getRenderOutput()).toEqual(
{1}
); - }); - - it('should handle memo(forwardRef())', () => { - const testRef = React.createRef(); - const SomeComponent = React.forwardRef((props, ref) => { - expect(ref).toEqual(testRef); - return ( -
- - -
- ); - }); - - const SomeMemoComponent = React.memo(SomeComponent); - - const shallowRenderer = createRenderer(); - const result = shallowRenderer.render(); - - expect(result.type).toBe('div'); - expect(result.props.children).toEqual([ - , - , - ]); - }); - - it('should warn for forwardRef(memo())', () => { - const testRef = React.createRef(); - const SomeMemoComponent = React.memo(({foo}) => { - return
{foo}
; - }); - const shallowRenderer = createRenderer(); - expect(() => { - expect(() => { - const SomeComponent = React.forwardRef(SomeMemoComponent); - shallowRenderer.render(); - }).toErrorDev( - 'Warning: forwardRef requires a render function but received ' + - 'a `memo` component. Instead of forwardRef(memo(...)), use ' + - 'memo(forwardRef(...))', - {withoutStack: true}, - ); - }).toThrowError( - 'forwardRef requires a render function but was given object.', - ); - }); - - it('should let you change type', () => { - function Foo({prop}) { - return
Foo {prop}
; - } - function Bar({prop}) { - return
Bar {prop}
; - } - - const shallowRenderer = createRenderer(); - shallowRenderer.render(); - expect(shallowRenderer.getRenderOutput()).toEqual(
Foo {'foo1'}
); - shallowRenderer.render(); - expect(shallowRenderer.getRenderOutput()).toEqual(
Foo {'foo2'}
); - shallowRenderer.render(); - expect(shallowRenderer.getRenderOutput()).toEqual(
Bar {'bar1'}
); - shallowRenderer.render(); - expect(shallowRenderer.getRenderOutput()).toEqual(
Bar {'bar2'}
); - }); - - it('should let you change class type', () => { - class Foo extends React.Component { - render() { - return
Foo {this.props.prop}
; - } - } - class Bar extends React.Component { - render() { - return
Bar {this.props.prop}
; - } - } - - const shallowRenderer = createRenderer(); - shallowRenderer.render(); - expect(shallowRenderer.getRenderOutput()).toEqual(
Foo {'foo1'}
); - shallowRenderer.render(); - expect(shallowRenderer.getRenderOutput()).toEqual(
Foo {'foo2'}
); - shallowRenderer.render(); - expect(shallowRenderer.getRenderOutput()).toEqual(
Bar {'bar1'}
); - shallowRenderer.render(); - expect(shallowRenderer.getRenderOutput()).toEqual(
Bar {'bar2'}
); - }); -}); diff --git a/packages/react-test-renderer/src/__tests__/ReactShallowRendererHooks-test.js b/packages/react-test-renderer/src/__tests__/ReactShallowRendererHooks-test.js deleted file mode 100644 index 64854d3d6d6de..0000000000000 --- a/packages/react-test-renderer/src/__tests__/ReactShallowRendererHooks-test.js +++ /dev/null @@ -1,487 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its 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 node - */ - -'use strict'; - -import * as React from 'react'; -import ReactShallowRenderer from 'react-test-renderer/shallow'; - -const createRenderer = ReactShallowRenderer.createRenderer; - -describe('ReactShallowRenderer with hooks', () => { - it('should work with useState', () => { - function SomeComponent({defaultName}) { - const [name] = React.useState(defaultName); - - return ( -
-

- Your name is: {name} -

-
- ); - } - - const shallowRenderer = createRenderer(); - let result = shallowRenderer.render( - , - ); - - expect(result).toEqual( -
-

- Your name is: Dominic -

-
, - ); - - result = shallowRenderer.render( - , - ); - - expect(result).toEqual( -
-

- Your name is: Dominic -

-
, - ); - }); - - it('should work with updating a value from useState', () => { - function SomeComponent({defaultName}) { - const [name, updateName] = React.useState(defaultName); - - if (name !== 'Dan') { - updateName('Dan'); - } - - return ( -
-

- Your name is: {name} -

-
- ); - } - - const shallowRenderer = createRenderer(); - const result = shallowRenderer.render( - , - ); - - expect(result).toEqual( -
-

- Your name is: Dan -

-
, - ); - }); - - it('should work with updating a derived value from useState', () => { - let _updateName; - - function SomeComponent({defaultName}) { - const [name, updateName] = React.useState(defaultName); - const [prevName, updatePrevName] = React.useState(defaultName); - const [letter, updateLetter] = React.useState(name[0]); - - _updateName = updateName; - - if (name !== prevName) { - updatePrevName(name); - updateLetter(name[0]); - } - - return ( -
-

- Your name is: {name + ' (' + letter + ')'} -

-
- ); - } - - const shallowRenderer = createRenderer(); - let result = shallowRenderer.render( - , - ); - expect(result).toEqual( -
-

- Your name is: Sophie (S) -

-
, - ); - - result = shallowRenderer.render(); - expect(result).toEqual( -
-

- Your name is: Sophie (S) -

-
, - ); - - _updateName('Dan'); - expect(shallowRenderer.getRenderOutput()).toEqual( -
-

- Your name is: Dan (D) -

-
, - ); - }); - - it('should work with useReducer', () => { - function reducer(state, action) { - switch (action.type) { - case 'increment': - return {count: state.count + 1}; - case 'decrement': - return {count: state.count - 1}; - } - } - - function SomeComponent(props) { - const [state] = React.useReducer(reducer, props, p => ({ - count: p.initialCount, - })); - - return ( -
-

- The counter is at: {state.count.toString()} -

-
- ); - } - - const shallowRenderer = createRenderer(); - let result = shallowRenderer.render(); - - expect(result).toEqual( -
-

- The counter is at: 0 -

-
, - ); - - result = shallowRenderer.render(); - - expect(result).toEqual( -
-

- The counter is at: 0 -

-
, - ); - }); - - it('should work with a dispatched state change for a useReducer', () => { - function reducer(state, action) { - switch (action.type) { - case 'increment': - return {count: state.count + 1}; - case 'decrement': - return {count: state.count - 1}; - } - } - - function SomeComponent(props) { - const [state, dispatch] = React.useReducer(reducer, props, p => ({ - count: p.initialCount, - })); - - if (state.count === 0) { - dispatch({type: 'increment'}); - } - - return ( -
-

- The counter is at: {state.count.toString()} -

-
- ); - } - - const shallowRenderer = createRenderer(); - const result = shallowRenderer.render(); - - expect(result).toEqual( -
-

- The counter is at: 1 -

-
, - ); - }); - - it('should not trigger effects', () => { - const effectsCalled = []; - - function SomeComponent({defaultName}) { - React.useEffect(() => { - effectsCalled.push('useEffect'); - }); - - React.useLayoutEffect(() => { - effectsCalled.push('useEffect'); - }); - - return
Hello world
; - } - - const shallowRenderer = createRenderer(); - shallowRenderer.render(); - - expect(effectsCalled).toEqual([]); - }); - - it('should work with useRef', () => { - function SomeComponent() { - const randomNumberRef = React.useRef({number: Math.random()}); - - return ( -
-

The random number is: {randomNumberRef.current.number}

-
- ); - } - - const shallowRenderer = createRenderer(); - const firstResult = shallowRenderer.render(); - const secondResult = shallowRenderer.render(); - - expect(firstResult).toEqual(secondResult); - }); - - it('should work with useMemo', () => { - function SomeComponent() { - const randomNumber = React.useMemo(() => { - return {number: Math.random()}; - }, []); - - return ( -
-

The random number is: {randomNumber.number}

-
- ); - } - - const shallowRenderer = createRenderer(); - const firstResult = shallowRenderer.render(); - const secondResult = shallowRenderer.render(); - - expect(firstResult).toEqual(secondResult); - }); - - it('should work with useContext', () => { - const SomeContext = React.createContext('default'); - - function SomeComponent() { - const value = React.useContext(SomeContext); - - return ( -
-

{value}

-
- ); - } - - const shallowRenderer = createRenderer(); - const result = shallowRenderer.render(); - - expect(result).toEqual( -
-

default

-
, - ); - }); - - it('should not leak state when component type changes', () => { - function SomeComponent({defaultName}) { - const [name] = React.useState(defaultName); - - return ( -
-

- Your name is: {name} -

-
- ); - } - - function SomeOtherComponent({defaultName}) { - const [name] = React.useState(defaultName); - - return ( -
-

- Your name is: {name} -

-
- ); - } - - const shallowRenderer = createRenderer(); - let result = shallowRenderer.render( - , - ); - expect(result).toEqual( -
-

- Your name is: Dominic -

-
, - ); - - result = shallowRenderer.render(); - expect(result).toEqual( -
-

- Your name is: Dan -

-
, - ); - }); - - it('should work with forwardRef + any hook', () => { - const SomeComponent = React.forwardRef((props, ref) => { - const randomNumberRef = React.useRef({number: Math.random()}); - - return ( -
-

The random number is: {randomNumberRef.current.number}

-
- ); - }); - - const shallowRenderer = createRenderer(); - const firstResult = shallowRenderer.render(); - const secondResult = shallowRenderer.render(); - - expect(firstResult).toEqual(secondResult); - }); - - it('should update a value from useState outside the render', () => { - let _dispatch; - - function SomeComponent({defaultName}) { - const [count, dispatch] = React.useReducer( - (s, a) => (a === 'inc' ? s + 1 : s), - 0, - ); - const [name, updateName] = React.useState(defaultName); - _dispatch = () => dispatch('inc'); - - return ( -
updateName('Dan')}> -

- Your name is: {name} ({count}) -

-
- ); - } - - const shallowRenderer = createRenderer(); - const element = ; - const result = shallowRenderer.render(element); - expect(result.props.children).toEqual( -

- Your name is: Dominic ({0}) -

, - ); - - result.props.onClick(); - let updated = shallowRenderer.render(element); - expect(updated.props.children).toEqual( -

- Your name is: Dan ({0}) -

, - ); - - _dispatch('foo'); - updated = shallowRenderer.render(element); - expect(updated.props.children).toEqual( -

- Your name is: Dan ({1}) -

, - ); - - _dispatch('inc'); - updated = shallowRenderer.render(element); - expect(updated.props.children).toEqual( -

- Your name is: Dan ({2}) -

, - ); - }); - - it('should ignore a foreign update outside the render', () => { - let _updateCountForFirstRender; - - function SomeComponent() { - const [count, updateCount] = React.useState(0); - if (!_updateCountForFirstRender) { - _updateCountForFirstRender = updateCount; - } - return count; - } - - const shallowRenderer = createRenderer(); - const element = ; - let result = shallowRenderer.render(element); - expect(result).toEqual(0); - _updateCountForFirstRender(1); - result = shallowRenderer.render(element); - expect(result).toEqual(1); - - shallowRenderer.unmount(); - result = shallowRenderer.render(element); - expect(result).toEqual(0); - _updateCountForFirstRender(1); // Should be ignored. - result = shallowRenderer.render(element); - expect(result).toEqual(0); - }); - - it('should not forget render phase updates', () => { - let _updateCount; - - function SomeComponent() { - const [count, updateCount] = React.useState(0); - _updateCount = updateCount; - if (count < 5) { - updateCount(x => x + 1); - } - return count; - } - - const shallowRenderer = createRenderer(); - const element = ; - let result = shallowRenderer.render(element); - expect(result).toEqual(5); - - _updateCount(10); - result = shallowRenderer.render(element); - expect(result).toEqual(10); - - _updateCount(x => x + 1); - result = shallowRenderer.render(element); - expect(result).toEqual(11); - - _updateCount(x => x - 10); - result = shallowRenderer.render(element); - expect(result).toEqual(5); - }); -}); diff --git a/packages/react-test-renderer/src/__tests__/ReactShallowRendererMemo-test.js b/packages/react-test-renderer/src/__tests__/ReactShallowRendererMemo-test.js deleted file mode 100644 index a0c0f7fc46139..0000000000000 --- a/packages/react-test-renderer/src/__tests__/ReactShallowRendererMemo-test.js +++ /dev/null @@ -1,1514 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its 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 node - */ - -'use strict'; - -import * as PropTypes from 'prop-types'; -import * as React from 'react'; -import ReactShallowRenderer from 'react-test-renderer/shallow'; - -const createRenderer = ReactShallowRenderer.createRenderer; - -describe('ReactShallowRendererMemo', () => { - it('should call all of the legacy lifecycle hooks', () => { - const logs = []; - const logger = message => () => logs.push(message) || true; - - const SomeComponent = React.memo( - class SomeComponent extends React.Component { - UNSAFE_componentWillMount = logger('componentWillMount'); - componentDidMount = logger('componentDidMount'); - UNSAFE_componentWillReceiveProps = logger('componentWillReceiveProps'); - shouldComponentUpdate = logger('shouldComponentUpdate'); - UNSAFE_componentWillUpdate = logger('componentWillUpdate'); - componentDidUpdate = logger('componentDidUpdate'); - componentWillUnmount = logger('componentWillUnmount'); - render() { - return
; - } - }, - ); - - const shallowRenderer = createRenderer(); - shallowRenderer.render(); - - // Calling cDU might lead to problems with host component references. - // Since our components aren't really mounted, refs won't be available. - expect(logs).toEqual(['componentWillMount']); - - logs.splice(0); - - const instance = shallowRenderer.getMountedInstance(); - instance.setState({}); - - expect(logs).toEqual(['shouldComponentUpdate', 'componentWillUpdate']); - - logs.splice(0); - - shallowRenderer.render(); - - // The previous shallow renderer did not trigger cDU for props changes. - expect(logs).toEqual([ - 'componentWillReceiveProps', - 'shouldComponentUpdate', - 'componentWillUpdate', - ]); - }); - - it('should call all of the new lifecycle hooks', () => { - const logs = []; - const logger = message => () => logs.push(message) || true; - - const SomeComponent = React.memo( - class SomeComponent extends React.Component { - state = {}; - static getDerivedStateFromProps = logger('getDerivedStateFromProps'); - componentDidMount = logger('componentDidMount'); - shouldComponentUpdate = logger('shouldComponentUpdate'); - componentDidUpdate = logger('componentDidUpdate'); - componentWillUnmount = logger('componentWillUnmount'); - render() { - return
; - } - }, - ); - - const shallowRenderer = createRenderer(); - shallowRenderer.render(); - - // Calling cDU might lead to problems with host component references. - // Since our components aren't really mounted, refs won't be available. - expect(logs).toEqual(['getDerivedStateFromProps']); - - logs.splice(0); - - const instance = shallowRenderer.getMountedInstance(); - instance.setState({}); - - expect(logs).toEqual(['getDerivedStateFromProps', 'shouldComponentUpdate']); - - logs.splice(0); - - shallowRenderer.render(); - - // The previous shallow renderer did not trigger cDU for props changes. - expect(logs).toEqual(['getDerivedStateFromProps', 'shouldComponentUpdate']); - }); - - it('should not invoke deprecated lifecycles (cWM/cWRP/cWU) if new static gDSFP is present', () => { - const Component = React.memo( - class Component extends React.Component { - state = {}; - static getDerivedStateFromProps() { - return null; - } - componentWillMount() { - throw Error('unexpected'); - } - componentWillReceiveProps() { - throw Error('unexpected'); - } - componentWillUpdate() { - throw Error('unexpected'); - } - render() { - return null; - } - }, - ); - - const shallowRenderer = createRenderer(); - shallowRenderer.render(); - }); - - it('should not invoke deprecated lifecycles (cWM/cWRP/cWU) if new getSnapshotBeforeUpdate is present', () => { - const Component = React.memo( - class Component extends React.Component { - getSnapshotBeforeUpdate() { - return null; - } - componentWillMount() { - throw Error('unexpected'); - } - componentWillReceiveProps() { - throw Error('unexpected'); - } - componentWillUpdate() { - throw Error('unexpected'); - } - render() { - return null; - } - }, - ); - - const shallowRenderer = createRenderer(); - shallowRenderer.render(); - shallowRenderer.render(); - }); - - it('should not call getSnapshotBeforeUpdate or componentDidUpdate when updating since refs wont exist', () => { - const Component = React.memo( - class Component extends React.Component { - getSnapshotBeforeUpdate() { - throw Error('unexpected'); - } - componentDidUpdate() { - throw Error('unexpected'); - } - render() { - return null; - } - }, - ); - - const shallowRenderer = createRenderer(); - shallowRenderer.render(); - shallowRenderer.render(); - }); - - it('should only render 1 level deep', () => { - const Parent = React.memo(function Parent() { - return ( -
- -
- ); - }); - - function Child() { - throw Error('This component should not render'); - } - - const shallowRenderer = createRenderer(); - shallowRenderer.render(React.createElement(Parent)); - }); - - it('should have shallow rendering', () => { - const SomeComponent = React.memo( - class SomeComponent extends React.Component { - render() { - return ( -
- - -
- ); - } - }, - ); - - const shallowRenderer = createRenderer(); - const result = shallowRenderer.render(); - - expect(result.type).toBe('div'); - expect(result.props.children).toEqual([ - , - , - ]); - }); - - it('should handle Profiler', () => { - const SomeComponent = React.memo( - class SomeComponent extends React.Component { - render() { - return ( - -
- - -
-
- ); - } - }, - ); - - const shallowRenderer = createRenderer(); - const result = shallowRenderer.render(); - - expect(result.type).toBe(React.Profiler); - expect(result.props.children).toEqual( -
- - -
, - ); - }); - - it('should enable shouldComponentUpdate to prevent a re-render', () => { - let renderCounter = 0; - const SimpleComponent = React.memo( - class SimpleComponent extends React.Component { - state = {update: false}; - shouldComponentUpdate(nextProps, nextState) { - return this.state.update !== nextState.update; - } - render() { - renderCounter++; - return
{`${renderCounter}`}
; - } - }, - ); - - const shallowRenderer = createRenderer(); - shallowRenderer.render(); - expect(shallowRenderer.getRenderOutput()).toEqual(
1
); - - const instance = shallowRenderer.getMountedInstance(); - instance.setState({update: false}); - expect(shallowRenderer.getRenderOutput()).toEqual(
1
); - - instance.setState({update: true}); - expect(shallowRenderer.getRenderOutput()).toEqual(
2
); - }); - - it('should enable PureComponent to prevent a re-render', () => { - let renderCounter = 0; - const SimpleComponent = React.memo( - class SimpleComponent extends React.PureComponent { - state = {update: false}; - render() { - renderCounter++; - return
{`${renderCounter}`}
; - } - }, - ); - - const shallowRenderer = createRenderer(); - shallowRenderer.render(); - expect(shallowRenderer.getRenderOutput()).toEqual(
1
); - - const instance = shallowRenderer.getMountedInstance(); - instance.setState({update: false}); - expect(shallowRenderer.getRenderOutput()).toEqual(
1
); - - instance.setState({update: true}); - expect(shallowRenderer.getRenderOutput()).toEqual(
2
); - }); - - it('should not run shouldComponentUpdate during forced update', () => { - let scuCounter = 0; - const SimpleComponent = React.memo( - class SimpleComponent extends React.Component { - state = {count: 1}; - shouldComponentUpdate() { - scuCounter++; - return false; - } - render() { - return
{`${this.state.count}`}
; - } - }, - ); - - const shallowRenderer = createRenderer(); - shallowRenderer.render(); - expect(scuCounter).toEqual(0); - expect(shallowRenderer.getRenderOutput()).toEqual(
1
); - - // Force update the initial state. sCU should not fire. - const instance = shallowRenderer.getMountedInstance(); - instance.forceUpdate(); - expect(scuCounter).toEqual(0); - expect(shallowRenderer.getRenderOutput()).toEqual(
1
); - - // Setting state updates the instance, but doesn't re-render - // because sCU returned false. - instance.setState(state => ({count: state.count + 1})); - expect(scuCounter).toEqual(1); - expect(instance.state.count).toEqual(2); - expect(shallowRenderer.getRenderOutput()).toEqual(
1
); - - // A force update updates the render output, but doesn't call sCU. - instance.forceUpdate(); - expect(scuCounter).toEqual(1); - expect(instance.state.count).toEqual(2); - expect(shallowRenderer.getRenderOutput()).toEqual(
2
); - }); - - it('should rerender when calling forceUpdate', () => { - let renderCounter = 0; - const SimpleComponent = React.memo( - class SimpleComponent extends React.Component { - render() { - renderCounter += 1; - return
; - } - }, - ); - - const shallowRenderer = createRenderer(); - shallowRenderer.render(); - expect(renderCounter).toEqual(1); - - const instance = shallowRenderer.getMountedInstance(); - instance.forceUpdate(); - expect(renderCounter).toEqual(2); - }); - - it('should shallow render a function component', () => { - function SomeComponent(props, context) { - return ( -
-
{props.foo}
-
{context.bar}
- - -
- ); - } - const SomeMemoComponent = React.memo(SomeComponent); - - SomeComponent.contextTypes = { - bar: PropTypes.string, - }; - - const shallowRenderer = createRenderer(); - const result = shallowRenderer.render(, { - bar: 'BAR', - }); - - expect(result.type).toBe('div'); - expect(result.props.children).toEqual([ -
FOO
, -
BAR
, - , - , - ]); - }); - - it('should shallow render a component returning strings directly from render', () => { - const Text = React.memo(({value}) => value); - - const shallowRenderer = createRenderer(); - const result = shallowRenderer.render(); - expect(result).toEqual('foo'); - }); - - it('should shallow render a component returning numbers directly from render', () => { - const Text = React.memo(({value}) => value); - - const shallowRenderer = createRenderer(); - const result = shallowRenderer.render(); - expect(result).toEqual(10); - }); - - it('should shallow render a fragment', () => { - class SomeComponent extends React.Component { - render() { - return
; - } - } - class Fragment extends React.Component { - render() { - return [
, , ]; - } - } - const shallowRenderer = createRenderer(); - const result = shallowRenderer.render(); - expect(result).toEqual([ -
, - , - , - ]); - }); - - it('should shallow render a React.fragment', () => { - class SomeComponent extends React.Component { - render() { - return
; - } - } - class Fragment extends React.Component { - render() { - return ( - <> -
- - - - ); - } - } - const shallowRenderer = createRenderer(); - const result = shallowRenderer.render(); - expect(result).toEqual( - <> -
- - - , - ); - }); - - it('should throw for invalid elements', () => { - class SomeComponent extends React.Component { - render() { - return
; - } - } - - const shallowRenderer = createRenderer(); - expect(() => shallowRenderer.render(SomeComponent)).toThrowError( - 'ReactShallowRenderer render(): Invalid component element. Instead of ' + - 'passing a component class, make sure to instantiate it by passing it ' + - 'to React.createElement.', - ); - expect(() => shallowRenderer.render(
)).toThrowError( - 'ReactShallowRenderer render(): Shallow rendering works only with ' + - 'custom components, not primitives (div). Instead of calling ' + - '`.render(el)` and inspecting the rendered output, look at `el.props` ' + - 'directly instead.', - ); - }); - - it('should have shallow unmounting', () => { - const componentWillUnmount = jest.fn(); - - class SomeComponent extends React.Component { - componentWillUnmount = componentWillUnmount; - render() { - return
; - } - } - - const shallowRenderer = createRenderer(); - shallowRenderer.render(); - shallowRenderer.unmount(); - - expect(componentWillUnmount).toBeCalled(); - }); - - it('can shallow render to null', () => { - class SomeComponent extends React.Component { - render() { - return null; - } - } - - const shallowRenderer = createRenderer(); - const result = shallowRenderer.render(); - - expect(result).toBe(null); - }); - - it('can shallow render with a ref', () => { - class SomeComponent extends React.Component { - render() { - return
; - } - } - - const shallowRenderer = createRenderer(); - // Shouldn't crash. - shallowRenderer.render(); - }); - - it('lets you update shallowly rendered components', () => { - class SomeComponent extends React.Component { - state = {clicked: false}; - - onClick = () => { - this.setState({clicked: true}); - }; - - render() { - const className = this.state.clicked ? 'was-clicked' : ''; - - if (this.props.aNew === 'prop') { - return ( - - Test link - - ); - } else { - return ( -
- - -
- ); - } - } - } - - const shallowRenderer = createRenderer(); - const result = shallowRenderer.render(); - expect(result.type).toBe('div'); - expect(result.props.children).toEqual([ - , - , - ]); - - const updatedResult = shallowRenderer.render(); - expect(updatedResult.type).toBe('a'); - - const mockEvent = {}; - updatedResult.props.onClick(mockEvent); - - const updatedResultCausedByClick = shallowRenderer.getRenderOutput(); - expect(updatedResultCausedByClick.type).toBe('a'); - expect(updatedResultCausedByClick.props.className).toBe('was-clicked'); - }); - - it('can access the mounted component instance', () => { - const SimpleComponent = React.memo( - class SimpleComponent extends React.Component { - someMethod = () => { - return this.props.n; - }; - - render() { - return
{this.props.n}
; - } - }, - ); - - const shallowRenderer = createRenderer(); - shallowRenderer.render(); - expect(shallowRenderer.getMountedInstance().someMethod()).toEqual(5); - }); - - it('can shallowly render components with contextTypes', () => { - const SimpleComponent = React.memo( - class SimpleComponent extends React.Component { - static contextTypes = { - name: PropTypes.string, - }; - - render() { - return
; - } - }, - ); - - const shallowRenderer = createRenderer(); - const result = shallowRenderer.render(); - expect(result).toEqual(
); - }); - - it('passes expected params to legacy component lifecycle methods', () => { - const componentDidUpdateParams = []; - const componentWillReceivePropsParams = []; - const componentWillUpdateParams = []; - const setStateParams = []; - const shouldComponentUpdateParams = []; - - const initialProp = {prop: 'init prop'}; - const initialState = {state: 'init state'}; - const initialContext = {context: 'init context'}; - const updatedState = {state: 'updated state'}; - const updatedProp = {prop: 'updated prop'}; - const updatedContext = {context: 'updated context'}; - - const SimpleComponent = React.memo( - class SimpleComponent extends React.Component { - constructor(props, context) { - super(props, context); - this.state = initialState; - } - static contextTypes = { - context: PropTypes.string, - }; - componentDidUpdate(...args) { - componentDidUpdateParams.push(...args); - } - UNSAFE_componentWillReceiveProps(...args) { - componentWillReceivePropsParams.push(...args); - this.setState((...innerArgs) => { - setStateParams.push(...innerArgs); - return updatedState; - }); - } - UNSAFE_componentWillUpdate(...args) { - componentWillUpdateParams.push(...args); - } - shouldComponentUpdate(...args) { - shouldComponentUpdateParams.push(...args); - return true; - } - render() { - return null; - } - }, - ); - - const shallowRenderer = createRenderer(); - shallowRenderer.render( - React.createElement(SimpleComponent, initialProp), - initialContext, - ); - expect(componentDidUpdateParams).toEqual([]); - expect(componentWillReceivePropsParams).toEqual([]); - expect(componentWillUpdateParams).toEqual([]); - expect(setStateParams).toEqual([]); - expect(shouldComponentUpdateParams).toEqual([]); - - // Lifecycle hooks should be invoked with the correct prev/next params on update. - shallowRenderer.render( - React.createElement(SimpleComponent, updatedProp), - updatedContext, - ); - expect(componentWillReceivePropsParams).toEqual([ - updatedProp, - updatedContext, - ]); - expect(setStateParams).toEqual([initialState, initialProp]); - expect(shouldComponentUpdateParams).toEqual([ - updatedProp, - updatedState, - updatedContext, - ]); - expect(componentWillUpdateParams).toEqual([ - updatedProp, - updatedState, - updatedContext, - ]); - expect(componentDidUpdateParams).toEqual([]); - }); - - it('passes expected params to new component lifecycle methods', () => { - const componentDidUpdateParams = []; - const getDerivedStateFromPropsParams = []; - const shouldComponentUpdateParams = []; - - const initialProp = {prop: 'init prop'}; - const initialState = {state: 'init state'}; - const initialContext = {context: 'init context'}; - const updatedProp = {prop: 'updated prop'}; - const updatedContext = {context: 'updated context'}; - - const SimpleComponent = React.memo( - class SimpleComponent extends React.Component { - constructor(props, context) { - super(props, context); - this.state = initialState; - } - static contextTypes = { - context: PropTypes.string, - }; - componentDidUpdate(...args) { - componentDidUpdateParams.push(...args); - } - static getDerivedStateFromProps(...args) { - getDerivedStateFromPropsParams.push(args); - return null; - } - shouldComponentUpdate(...args) { - shouldComponentUpdateParams.push(...args); - return true; - } - render() { - return null; - } - }, - ); - - const shallowRenderer = createRenderer(); - - // The only lifecycle hook that should be invoked on initial render - // Is the static getDerivedStateFromProps() methods - shallowRenderer.render( - React.createElement(SimpleComponent, initialProp), - initialContext, - ); - expect(getDerivedStateFromPropsParams).toEqual([ - [initialProp, initialState], - ]); - expect(componentDidUpdateParams).toEqual([]); - expect(shouldComponentUpdateParams).toEqual([]); - - // Lifecycle hooks should be invoked with the correct prev/next params on update. - shallowRenderer.render( - React.createElement(SimpleComponent, updatedProp), - updatedContext, - ); - expect(getDerivedStateFromPropsParams).toEqual([ - [initialProp, initialState], - [updatedProp, initialState], - ]); - expect(shouldComponentUpdateParams).toEqual([ - updatedProp, - initialState, - updatedContext, - ]); - expect(componentDidUpdateParams).toEqual([]); - }); - - it('can shallowly render components with ref as function', () => { - const SimpleComponent = React.memo( - class SimpleComponent extends React.Component { - state = {clicked: false}; - - handleUserClick = () => { - this.setState({clicked: true}); - }; - - render() { - return ( -
{}} - onClick={this.handleUserClick} - className={this.state.clicked ? 'clicked' : ''} - /> - ); - } - }, - ); - - const shallowRenderer = createRenderer(); - shallowRenderer.render(); - let result = shallowRenderer.getRenderOutput(); - expect(result.type).toEqual('div'); - expect(result.props.className).toEqual(''); - result.props.onClick(); - - result = shallowRenderer.getRenderOutput(); - expect(result.type).toEqual('div'); - expect(result.props.className).toEqual('clicked'); - }); - - it('can initialize state via static getDerivedStateFromProps', () => { - const SimpleComponent = React.memo( - class SimpleComponent extends React.Component { - state = { - count: 1, - }; - - static getDerivedStateFromProps(props, prevState) { - return { - count: prevState.count + props.incrementBy, - other: 'foobar', - }; - } - - render() { - return ( -
{`count:${this.state.count}, other:${this.state.other}`}
- ); - } - }, - ); - - const shallowRenderer = createRenderer(); - const result = shallowRenderer.render(); - expect(result).toEqual(
count:3, other:foobar
); - }); - - it('can setState in componentWillMount when shallow rendering', () => { - const SimpleComponent = React.memo( - class SimpleComponent extends React.Component { - UNSAFE_componentWillMount() { - this.setState({groovy: 'doovy'}); - } - - render() { - return
{this.state.groovy}
; - } - }, - ); - - const shallowRenderer = createRenderer(); - const result = shallowRenderer.render(); - expect(result).toEqual(
doovy
); - }); - - it('can setState in componentWillMount repeatedly when shallow rendering', () => { - const SimpleComponent = React.memo( - class SimpleComponent extends React.Component { - state = { - separator: '-', - }; - - UNSAFE_componentWillMount() { - this.setState({groovy: 'doovy'}); - this.setState({doovy: 'groovy'}); - } - - render() { - const {groovy, doovy, separator} = this.state; - - return
{`${groovy}${separator}${doovy}`}
; - } - }, - ); - - const shallowRenderer = createRenderer(); - const result = shallowRenderer.render(); - expect(result).toEqual(
doovy-groovy
); - }); - - it('can setState in componentWillMount with an updater function repeatedly when shallow rendering', () => { - const SimpleComponent = React.memo( - class SimpleComponent extends React.Component { - state = { - separator: '-', - }; - - UNSAFE_componentWillMount() { - this.setState(state => ({groovy: 'doovy'})); - this.setState(state => ({doovy: state.groovy})); - } - - render() { - const {groovy, doovy, separator} = this.state; - - return
{`${groovy}${separator}${doovy}`}
; - } - }, - ); - - const shallowRenderer = createRenderer(); - const result = shallowRenderer.render(); - expect(result).toEqual(
doovy-doovy
); - }); - - it('can setState in componentWillReceiveProps when shallow rendering', () => { - const SimpleComponent = React.memo( - class SimpleComponent extends React.Component { - state = {count: 0}; - - UNSAFE_componentWillReceiveProps(nextProps) { - if (nextProps.updateState) { - this.setState({count: 1}); - } - } - - render() { - return
{this.state.count}
; - } - }, - ); - - const shallowRenderer = createRenderer(); - let result = shallowRenderer.render( - , - ); - expect(result.props.children).toEqual(0); - - result = shallowRenderer.render(); - expect(result.props.children).toEqual(1); - }); - - it('can update state with static getDerivedStateFromProps when shallow rendering', () => { - const SimpleComponent = React.memo( - class SimpleComponent extends React.Component { - state = {count: 1}; - - static getDerivedStateFromProps(nextProps, prevState) { - if (nextProps.updateState) { - return {count: nextProps.incrementBy + prevState.count}; - } - - return null; - } - - render() { - return
{this.state.count}
; - } - }, - ); - - const shallowRenderer = createRenderer(); - let result = shallowRenderer.render( - , - ); - expect(result.props.children).toEqual(1); - - result = shallowRenderer.render( - , - ); - expect(result.props.children).toEqual(3); - - result = shallowRenderer.render( - , - ); - expect(result.props.children).toEqual(3); - }); - - it('should not override state with stale values if prevState is spread within getDerivedStateFromProps', () => { - const SimpleComponent = React.memo( - class SimpleComponent extends React.Component { - state = {value: 0}; - - static getDerivedStateFromProps(nextProps, prevState) { - return {...prevState}; - } - - updateState = () => { - this.setState(state => ({value: state.value + 1})); - }; - - render() { - return
{`value:${this.state.value}`}
; - } - }, - ); - - const shallowRenderer = createRenderer(); - let result = shallowRenderer.render(); - expect(result).toEqual(
value:0
); - - const instance = shallowRenderer.getMountedInstance(); - instance.updateState(); - result = shallowRenderer.getRenderOutput(); - expect(result).toEqual(
value:1
); - }); - - it('should pass previous state to shouldComponentUpdate even with getDerivedStateFromProps', () => { - const SimpleComponent = React.memo( - class SimpleComponent extends React.Component { - constructor(props) { - super(props); - this.state = { - value: props.value, - }; - } - - static getDerivedStateFromProps(nextProps, prevState) { - if (nextProps.value === prevState.value) { - return null; - } - return {value: nextProps.value}; - } - - shouldComponentUpdate(nextProps, nextState) { - return nextState.value !== this.state.value; - } - - render() { - return
{`value:${this.state.value}`}
; - } - }, - ); - - const shallowRenderer = createRenderer(); - const initialResult = shallowRenderer.render( - , - ); - expect(initialResult).toEqual(
value:initial
); - const updatedResult = shallowRenderer.render( - , - ); - expect(updatedResult).toEqual(
value:updated
); - }); - - it('can setState with an updater function', () => { - let instance; - - const SimpleComponent = React.memo( - class SimpleComponent extends React.Component { - state = { - counter: 0, - }; - - render() { - instance = this; - return ( - - ); - } - }, - ); - - const shallowRenderer = createRenderer(); - let result = shallowRenderer.render(); - expect(result.props.children).toEqual(0); - - instance.setState((state, props) => { - return {counter: props.defaultCount + 1}; - }); - - result = shallowRenderer.getRenderOutput(); - expect(result.props.children).toEqual(2); - }); - - it('can access component instance from setState updater function', done => { - let instance; - - const SimpleComponent = React.memo( - class SimpleComponent extends React.Component { - state = {}; - - render() { - instance = this; - return null; - } - }, - ); - - const shallowRenderer = createRenderer(); - shallowRenderer.render(); - - instance.setState(function updater(state, props) { - expect(this).toBe(instance); - done(); - }); - }); - - it('can setState with a callback', () => { - let instance; - - const SimpleComponent = React.memo( - class SimpleComponent extends React.Component { - state = { - counter: 0, - }; - render() { - instance = this; - return

{this.state.counter}

; - } - }, - ); - - const shallowRenderer = createRenderer(); - const result = shallowRenderer.render(); - expect(result.props.children).toBe(0); - - const callback = jest.fn(function() { - expect(this).toBe(instance); - }); - - instance.setState({counter: 1}, callback); - - const updated = shallowRenderer.getRenderOutput(); - expect(updated.props.children).toBe(1); - expect(callback).toHaveBeenCalled(); - }); - - it('can replaceState with a callback', () => { - let instance; - - const SimpleComponent = React.memo( - class SimpleComponent extends React.Component { - state = { - counter: 0, - }; - render() { - instance = this; - return

{this.state.counter}

; - } - }, - ); - - const shallowRenderer = createRenderer(); - const result = shallowRenderer.render(); - expect(result.props.children).toBe(0); - - const callback = jest.fn(function() { - expect(this).toBe(instance); - }); - - // No longer a public API, but we can test that it works internally by - // reaching into the updater. - shallowRenderer._updater.enqueueReplaceState( - instance, - {counter: 1}, - callback, - ); - - const updated = shallowRenderer.getRenderOutput(); - expect(updated.props.children).toBe(1); - expect(callback).toHaveBeenCalled(); - }); - - it('can forceUpdate with a callback', () => { - let instance; - - const SimpleComponent = React.memo( - class SimpleComponent extends React.Component { - state = { - counter: 0, - }; - render() { - instance = this; - return

{this.state.counter}

; - } - }, - ); - - const shallowRenderer = createRenderer(); - const result = shallowRenderer.render(); - expect(result.props.children).toBe(0); - - const callback = jest.fn(function() { - expect(this).toBe(instance); - }); - - instance.forceUpdate(callback); - - const updated = shallowRenderer.getRenderOutput(); - expect(updated.props.children).toBe(0); - expect(callback).toHaveBeenCalled(); - }); - - it('can pass context when shallowly rendering', () => { - const SimpleComponent = React.memo( - class SimpleComponent extends React.Component { - static contextTypes = { - name: PropTypes.string, - }; - - render() { - return
{this.context.name}
; - } - }, - ); - - const shallowRenderer = createRenderer(); - const result = shallowRenderer.render(, { - name: 'foo', - }); - expect(result).toEqual(
foo
); - }); - - it('should track context across updates', () => { - const SimpleComponent = React.memo( - class SimpleComponent extends React.Component { - static contextTypes = { - foo: PropTypes.string, - }; - - state = { - bar: 'bar', - }; - - render() { - return
{`${this.context.foo}:${this.state.bar}`}
; - } - }, - ); - - const shallowRenderer = createRenderer(); - let result = shallowRenderer.render(, { - foo: 'foo', - }); - expect(result).toEqual(
foo:bar
); - - const instance = shallowRenderer.getMountedInstance(); - instance.setState({bar: 'baz'}); - - result = shallowRenderer.getRenderOutput(); - expect(result).toEqual(
foo:baz
); - }); - - it('should filter context by contextTypes', () => { - const SimpleComponent = React.memo( - class SimpleComponent extends React.Component { - static contextTypes = { - foo: PropTypes.string, - }; - render() { - return
{`${this.context.foo}:${this.context.bar}`}
; - } - }, - ); - - const shallowRenderer = createRenderer(); - const result = shallowRenderer.render(, { - foo: 'foo', - bar: 'bar', - }); - expect(result).toEqual(
foo:undefined
); - }); - - it('can fail context when shallowly rendering', () => { - const SimpleComponent = React.memo( - class SimpleComponent extends React.Component { - static contextTypes = { - name: PropTypes.string.isRequired, - }; - - render() { - return
{this.context.name}
; - } - }, - ); - - const shallowRenderer = createRenderer(); - expect(() => shallowRenderer.render()).toErrorDev( - 'Warning: Failed context type: The context `name` is marked as ' + - 'required in `SimpleComponent`, but its value is `undefined`.\n' + - ' in SimpleComponent (at **)', - ); - }); - - it('should warn about propTypes (but only once)', () => { - const SimpleComponent = React.memo( - class SimpleComponent extends React.Component { - static propTypes = { - name: PropTypes.string.isRequired, - }; - - render() { - return React.createElement('div', null, this.props.name); - } - }, - ); - - const shallowRenderer = createRenderer(); - expect(() => - shallowRenderer.render(React.createElement(SimpleComponent, {name: 123})), - ).toErrorDev( - 'Warning: Failed prop type: Invalid prop `name` of type `number` ' + - 'supplied to `SimpleComponent`, expected `string`.\n' + - ' in SimpleComponent', - ); - }); - - it('should enable rendering of cloned element', () => { - const SimpleComponent = React.memo( - class SimpleComponent extends React.Component { - constructor(props) { - super(props); - - this.state = { - bar: 'bar', - }; - } - - render() { - return
{`${this.props.foo}:${this.state.bar}`}
; - } - }, - ); - - const shallowRenderer = createRenderer(); - const el = ; - let result = shallowRenderer.render(el); - expect(result).toEqual(
foo:bar
); - - const cloned = React.cloneElement(el, {foo: 'baz'}); - result = shallowRenderer.render(cloned); - expect(result).toEqual(
baz:bar
); - }); - - it('this.state should be updated on setState callback inside componentWillMount', () => { - let stateSuccessfullyUpdated = false; - - const Component = React.memo( - class Component extends React.Component { - constructor(props, context) { - super(props, context); - this.state = { - hasUpdatedState: false, - }; - } - - UNSAFE_componentWillMount() { - this.setState( - {hasUpdatedState: true}, - () => (stateSuccessfullyUpdated = this.state.hasUpdatedState), - ); - } - - render() { - return
{this.props.children}
; - } - }, - ); - - const shallowRenderer = createRenderer(); - shallowRenderer.render(); - expect(stateSuccessfullyUpdated).toBe(true); - }); - - it('should handle multiple callbacks', () => { - const mockFn = jest.fn(); - const shallowRenderer = createRenderer(); - - const Component = React.memo( - class Component extends React.Component { - constructor(props, context) { - super(props, context); - this.state = { - foo: 'foo', - }; - } - - UNSAFE_componentWillMount() { - this.setState({foo: 'bar'}, () => mockFn()); - this.setState({foo: 'foobar'}, () => mockFn()); - } - - render() { - return
{this.state.foo}
; - } - }, - ); - - shallowRenderer.render(); - - expect(mockFn).toHaveBeenCalledTimes(2); - - // Ensure the callback queue is cleared after the callbacks are invoked - const mountedInstance = shallowRenderer.getMountedInstance(); - mountedInstance.setState({foo: 'bar'}, () => mockFn()); - expect(mockFn).toHaveBeenCalledTimes(3); - }); - - it('should call the setState callback even if shouldComponentUpdate = false', done => { - const mockFn = jest.fn().mockReturnValue(false); - - const Component = React.memo( - class Component extends React.Component { - constructor(props, context) { - super(props, context); - this.state = { - hasUpdatedState: false, - }; - } - - shouldComponentUpdate() { - return mockFn(); - } - - render() { - return
{this.state.hasUpdatedState}
; - } - }, - ); - - const shallowRenderer = createRenderer(); - shallowRenderer.render(); - - const mountedInstance = shallowRenderer.getMountedInstance(); - mountedInstance.setState({hasUpdatedState: true}, () => { - expect(mockFn).toBeCalled(); - expect(mountedInstance.state.hasUpdatedState).toBe(true); - done(); - }); - }); - - it('throws usefully when rendering badly-typed elements', () => { - const shallowRenderer = createRenderer(); - - const renderAndVerifyWarningAndError = (Component, typeString) => { - expect(() => { - expect(() => shallowRenderer.render()).toErrorDev( - 'React.createElement: type is invalid -- expected a string ' + - '(for built-in components) or a class/function (for composite components) ' + - `but got: ${typeString}.`, - ); - }).toThrowError( - 'ReactShallowRenderer render(): Shallow rendering works only with custom ' + - `components, but the provided element type was \`${typeString}\`.`, - ); - }; - - renderAndVerifyWarningAndError(undefined, 'undefined'); - renderAndVerifyWarningAndError(null, 'null'); - renderAndVerifyWarningAndError([], 'array'); - renderAndVerifyWarningAndError({}, 'object'); - }); - - it('should have initial state of null if not defined', () => { - const SomeComponent = React.memo( - class SomeComponent extends React.Component { - render() { - return ; - } - }, - ); - - const shallowRenderer = createRenderer(); - shallowRenderer.render(); - - expect(shallowRenderer.getMountedInstance().state).toBeNull(); - }); - - it('should invoke both deprecated and new lifecycles if both are present', () => { - const log = []; - - const Component = React.memo( - class Component extends React.Component { - componentWillMount() { - log.push('componentWillMount'); - } - componentWillReceiveProps() { - log.push('componentWillReceiveProps'); - } - componentWillUpdate() { - log.push('componentWillUpdate'); - } - UNSAFE_componentWillMount() { - log.push('UNSAFE_componentWillMount'); - } - UNSAFE_componentWillReceiveProps() { - log.push('UNSAFE_componentWillReceiveProps'); - } - UNSAFE_componentWillUpdate() { - log.push('UNSAFE_componentWillUpdate'); - } - render() { - return null; - } - }, - ); - - const shallowRenderer = createRenderer(); - shallowRenderer.render(); - expect(log).toEqual(['componentWillMount', 'UNSAFE_componentWillMount']); - - log.length = 0; - - shallowRenderer.render(); - expect(log).toEqual([ - 'componentWillReceiveProps', - 'UNSAFE_componentWillReceiveProps', - 'componentWillUpdate', - 'UNSAFE_componentWillUpdate', - ]); - }); - - it('should stop the update when setState returns null or undefined', () => { - const log = []; - let instance; - const Component = React.memo( - class Component extends React.Component { - constructor(props) { - super(props); - this.state = { - count: 0, - }; - } - render() { - log.push('render'); - instance = this; - return null; - } - }, - ); - const shallowRenderer = createRenderer(); - shallowRenderer.render(); - log.length = 0; - instance.setState(() => null); - instance.setState(() => undefined); - instance.setState(null); - instance.setState(undefined); - expect(log).toEqual([]); - instance.setState(state => ({count: state.count + 1})); - expect(log).toEqual(['render']); - }); - - it('should not get this in a function component', () => { - const logs = []; - const Foo = React.memo(function Foo() { - logs.push(this); - return
foo
; - }); - const shallowRenderer = createRenderer(); - shallowRenderer.render(); - expect(logs).toEqual([undefined]); - }); -}); diff --git a/scripts/rollup/forks.js b/scripts/rollup/forks.js index 484a1288f8489..d1f9fbd71ce70 100644 --- a/scripts/rollup/forks.js +++ b/scripts/rollup/forks.js @@ -36,13 +36,6 @@ const __EXPERIMENTAL__ = // algorithm because 1) require.resolve doesn't work with ESM modules, and 2) // the behavior is easier to predict. const forks = Object.freeze({ - // NOTE: This is hard-coded to the main entry point of the (third-party) - // react-shallow-renderer package. - './node_modules/react-shallow-renderer/index.js': () => { - // Use ESM build of `react-shallow-renderer`. - return './node_modules/react-shallow-renderer/esm/index.js'; - }, - // Without this fork, importing `shared/ReactSharedInternals` inside // the `react` package itself would not work due to a cyclical dependency. './packages/shared/ReactSharedInternals.js': (