text: /**
   * Copyright (c) 2013-present, Facebook, Inc.
   *
   * 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
   */
  
  'use strict';
  
  var React;
  var ReactDOM;
  
  var TestComponent;
  
  describe('ReactCompositeComponent-state', () => {
    beforeEach(() => {
      React = require('react');
  
      ReactDOM = require('react-dom');
  
      TestComponent = class extends React.Component {
        constructor(props) {
          super(props);
          this.peekAtState('getInitialState', undefined, props);
          this.state = {color: 'red'};
        }
  
        peekAtState = (from, state = this.state, props = this.props) => {
          props.stateListener(from, state && state.color);
        };
  
        peekAtCallback = from => {
          return () => this.peekAtState(from);
        };
  
        setFavoriteColor(nextColor) {
          this.setState(
            {color: nextColor},
            this.peekAtCallback('setFavoriteColor'),
          );
        }
  
        render() {
          this.peekAtState('render');
          return <div>{this.state.color}</div>;
        }
  
        componentWillMount() {
          this.peekAtState('componentWillMount-start');
          this.setState(function(state) {
            this.peekAtState('before-setState-sunrise', state);
          });
          this.setState(
            {color: 'sunrise'},
            this.peekAtCallback('setState-sunrise'),
          );
          this.setState(function(state) {
            this.peekAtState('after-setState-sunrise', state);
          });
          this.peekAtState('componentWillMount-after-sunrise');
          this.setState(
            {color: 'orange'},
            this.peekAtCallback('setState-orange'),
          );
          this.setState(function(state) {
            this.peekAtState('after-setState-orange', state);
          });
          this.peekAtState('componentWillMount-end');
        }
  
        componentDidMount() {
          this.peekAtState('componentDidMount-start');
          this.setState(
            {color: 'yellow'},
            this.peekAtCallback('setState-yellow'),
          );
          this.peekAtState('componentDidMount-end');
        }
  
        componentWillReceiveProps(newProps) {
          this.peekAtState('componentWillReceiveProps-start');
          if (newProps.nextColor) {
            this.setState(function(state) {
              this.peekAtState('before-setState-receiveProps', state);
              return {color: newProps.nextColor};
            });
            // No longer a public API, but we can test that it works internally by
            // reaching into the updater.
            this.updater.enqueueReplaceState(this, {color: undefined});
            this.setState(function(state) {
              this.peekAtState('before-setState-again-receiveProps', state);
              return {color: newProps.nextColor};
            }, this.peekAtCallback('setState-receiveProps'));
            this.setState(function(state) {
              this.peekAtState('after-setState-receiveProps', state);
            });
          }
          this.peekAtState('componentWillReceiveProps-end');
        }
  
        shouldComponentUpdate(nextProps, nextState) {
          this.peekAtState('shouldComponentUpdate-currentState');
          this.peekAtState('shouldComponentUpdate-nextState', nextState);
          return true;
        }
  
        componentWillUpdate(nextProps, nextState) {
          this.peekAtState('componentWillUpdate-currentState');
          this.peekAtState('componentWillUpdate-nextState', nextState);
        }
  
        componentDidUpdate(prevProps, prevState) {
          this.peekAtState('componentDidUpdate-currentState');
          this.peekAtState('componentDidUpdate-prevState', prevState);
        }
  
        componentWillUnmount() {
          this.peekAtState('componentWillUnmount');
        }
      };
    });
  
    it('should support setting state', () => {
      var container = document.createElement('div');
      document.body.appendChild(container);
  
      var stateListener = jest.fn();
      var instance = ReactDOM.render(
        <TestComponent stateListener={stateListener} />,
        container,
        function peekAtInitialCallback() {
          this.peekAtState('initial-callback');
        },
      );
      ReactDOM.render(
        <TestComponent stateListener={stateListener} nextColor="green" />,
        container,
        instance.peekAtCallback('setProps'),
      );
      instance.setFavoriteColor('blue');
      instance.forceUpdate(instance.peekAtCallback('forceUpdate'));
  
      ReactDOM.unmountComponentAtNode(container);
  
      let expected = [
        // there is no state when getInitialState() is called
        ['getInitialState', null],
        ['componentWillMount-start', 'red'],
        // setState()'s only enqueue pending states.
        ['componentWillMount-after-sunrise', 'red'],
        ['componentWillMount-end', 'red'],
        // pending state queue is processed
        ['before-setState-sunrise', 'red'],
        ['after-setState-sunrise', 'sunrise'],
        ['after-setState-orange', 'orange'],
        // pending state has been applied
        ['render', 'orange'],
        ['componentDidMount-start', 'orange'],
        // setState-sunrise and setState-orange should be called here,
        // after the bug in #1740
        // componentDidMount() called setState({color:'yellow'}), which is async.
        // The update doesn't happen until the next flush.
        ['componentDidMount-end', 'orange'],
        ['setState-sunrise', 'orange'],
        ['setState-orange', 'orange'],
        ['initial-callback', 'orange'],
        ['shouldComponentUpdate-currentState', 'orange'],
        ['shouldComponentUpdate-nextState', 'yellow'],
        ['componentWillUpdate-currentState', 'orange'],
        ['componentWillUpdate-nextState', 'yellow'],
        ['render', 'yellow'],
        ['componentDidUpdate-currentState', 'yellow'],
        ['componentDidUpdate-prevState', 'orange'],
        ['setState-yellow', 'yellow'],
        ['componentWillReceiveProps-start', 'yellow'],
        // setState({color:'green'}) only enqueues a pending state.
        ['componentWillReceiveProps-end', 'yellow'],
        // pending state queue is processed
        // We keep updates in the queue to support
        // replaceState(prevState => newState).
        ['before-setState-receiveProps', 'yellow'],
        ['before-setState-again-receiveProps', undefined],
        ['after-setState-receiveProps', 'green'],
        ['shouldComponentUpdate-currentState', 'yellow'],
        ['shouldComponentUpdate-nextState', 'green'],
        ['componentWillUpdate-currentState', 'yellow'],
        ['componentWillUpdate-nextState', 'green'],
        ['render', 'green'],
        ['componentDidUpdate-currentState', 'green'],
        ['componentDidUpdate-prevState', 'yellow'],
        ['setState-receiveProps', 'green'],
        ['setProps', 'green'],
        // setFavoriteColor('blue')
        ['shouldComponentUpdate-currentState', 'green'],
        ['shouldComponentUpdate-nextState', 'blue'],
        ['componentWillUpdate-currentState', 'green'],
        ['componentWillUpdate-nextState', 'blue'],
        ['render', 'blue'],
        ['componentDidUpdate-currentState', 'blue'],
        ['componentDidUpdate-prevState', 'green'],
        ['setFavoriteColor', 'blue'],
        // forceUpdate()
        ['componentWillUpdate-currentState', 'blue'],
        ['componentWillUpdate-nextState', 'blue'],
        ['render', 'blue'],
        ['componentDidUpdate-currentState', 'blue'],
        ['componentDidUpdate-prevState', 'blue'],
        ['forceUpdate', 'blue'],
        // unmountComponent()
        // state is available within `componentWillUnmount()`
        ['componentWillUnmount', 'blue'],
      ];
  
      expect(stateListener.mock.calls.join('\n')).toEqual(expected.join('\n'));
    });
  
    it('should call componentDidUpdate of children first', () => {
      var container = document.createElement('div');
  
      var ops = [];
  
      var child = null;
      var parent = null;
  
      class Child extends React.Component {
        state = {bar: false};
        componentDidMount() {
          child = this;
        }
        componentDidUpdate() {
          ops.push('child did update');
        }
        render() {
          return <div />;
        }
      }
  
      var shouldUpdate = true;
  
      class Intermediate extends React.Component {
        shouldComponentUpdate() {
          return shouldUpdate;
        }
        render() {
          return <Child />;
        }
      }
  
      class Parent extends React.Component {
        state = {foo: false};
        componentDidMount() {
          parent = this;
        }
        componentDidUpdate() {
          ops.push('parent did update');
        }
        render() {
          return <Intermediate />;
        }
      }
  
      ReactDOM.render(<Parent />, container);
  
      ReactDOM.unstable_batchedUpdates(() => {
        parent.setState({foo: true});
        child.setState({bar: true});
      });
      // When we render changes top-down in a batch, children's componentDidUpdate
      // happens before the parent.
      expect(ops).toEqual(['child did update', 'parent did update']);
  
      shouldUpdate = false;
  
      ops = [];
  
      ReactDOM.unstable_batchedUpdates(() => {
        parent.setState({foo: false});
        child.setState({bar: false});
      });
      // We expect the same thing to happen if we bail out in the middle.
      expect(ops).toEqual(['child did update', 'parent did update']);
    });
  
    it('should batch unmounts', () => {
      var outer;
  
      class Inner extends React.Component {
        render() {
          return <div />;
        }
  
        componentWillUnmount() {
          // This should get silently ignored (maybe with a warning), but it
          // shouldn't break React.
          outer.setState({showInner: false});
        }
      }
  
      class Outer extends React.Component {
        state = {showInner: true};
  
        render() {
          return <div>{this.state.showInner && <Inner />}</div>;
        }
      }
  
      var container = document.createElement('div');
      outer = ReactDOM.render(<Outer />, container);
      expect(() => {
        ReactDOM.unmountComponentAtNode(container);
      }).not.toThrow();
    });
  
    it('should update state when called from child cWRP', function() {
      const log = [];
      class Parent extends React.Component {
        state = {value: 'one'};
        render() {
          log.push('parent render ' + this.state.value);
          return <Child parent={this} value={this.state.value} />;
        }
      }
      let updated = false;
      class Child extends React.Component {
        componentWillReceiveProps() {
          if (updated) {
            return;
          }
          log.push('child componentWillReceiveProps ' + this.props.value);
          this.props.parent.setState({value: 'two'});
          log.push('child componentWillReceiveProps done ' + this.props.value);
          updated = true;
        }
        render() {
          log.push('child render ' + this.props.value);
          return <div>{this.props.value}</div>;
        }
      }
      var container = document.createElement('div');
      ReactDOM.render(<Parent />, container);
      ReactDOM.render(<Parent />, container);
      expect(log).toEqual([
        'parent render one',
        'child render one',
        'parent render one',
        'child componentWillReceiveProps one',
        'child componentWillReceiveProps done one',
        'child render one',
        'parent render two',
        'child render two',
      ]);
    });
  
    it('should merge state when sCU returns false', function() {
      const log = [];
      class Test extends React.Component {
        state = {a: 0};
        render() {
          return null;
        }
        shouldComponentUpdate(nextProps, nextState) {
          log.push(
            'scu from ' +
              Object.keys(this.state) +
              ' to ' +
              Object.keys(nextState),
          );
          return false;
        }
      }
  
      const container = document.createElement('div');
      const test = ReactDOM.render(<Test />, container);
      test.setState({b: 0});
      expect(log.length).toBe(1);
      test.setState({c: 0});
      expect(log.length).toBe(2);
      expect(log).toEqual(['scu from a to a,b', 'scu from a,b to a,b,c']);
    });
  
    it('should treat assigning to this.state inside cWRP as a replaceState, with a warning', () => {
      spyOn(console, 'error');
  
      let ops = [];
      class Test extends React.Component {
        state = {step: 1, extra: true};
        componentWillReceiveProps() {
          this.setState({step: 2}, () => {
            // Tests that earlier setState callbacks are not dropped
            ops.push(
              `callback -- step: ${this.state.step}, extra: ${!!this.state
                .extra}`,
            );
          });
          // Treat like replaceState
          this.state = {step: 3};
        }
        render() {
          ops.push(
            `render -- step: ${this.state.step}, extra: ${!!this.state.extra}`,
          );
          return null;
        }
      }
  
      // Mount
      const container = document.createElement('div');
      ReactDOM.render(<Test />, container);
      // Update
      ReactDOM.render(<Test />, container);
  
      expect(ops).toEqual([
        'render -- step: 1, extra: true',
        'render -- step: 3, extra: false',
        'callback -- step: 3, extra: false',
      ]);
      expect(console.error.calls.count()).toEqual(1);
      expect(console.error.calls.argsFor(0)[0]).toEqual(
        'Warning: Test.componentWillReceiveProps(): Assigning directly to ' +
          "this.state is deprecated (except inside a component's constructor). " +
          'Use setState instead.',
      );
  
      // Check deduplication
      ReactDOM.render(<Test />, container);
      expect(console.error.calls.count()).toEqual(1);
    });
  
    it('should treat assigning to this.state inside cWM as a replaceState, with a warning', () => {
      spyOn(console, 'error');
  
      let ops = [];
      class Test extends React.Component {
        state = {step: 1, extra: true};
        componentWillMount() {
          this.setState({step: 2}, () => {
            // Tests that earlier setState callbacks are not dropped
            ops.push(
              `callback -- step: ${this.state.step}, extra: ${!!this.state
                .extra}`,
            );
          });
          // Treat like replaceState
          this.state = {step: 3};
        }
        render() {
          ops.push(
            `render -- step: ${this.state.step}, extra: ${!!this.state.extra}`,
          );
          return null;
        }
      }
  
      // Mount
      const container = document.createElement('div');
      ReactDOM.render(<Test />, container);
  
      expect(ops).toEqual([
        'render -- step: 3, extra: false',
        'callback -- step: 3, extra: false',
      ]);
      expect(console.error.calls.count()).toEqual(1);
      expect(console.error.calls.argsFor(0)[0]).toEqual(
        'Warning: Test.componentWillMount(): Assigning directly to ' +
          "this.state is deprecated (except inside a component's constructor). " +
          'Use setState instead.',
      );
    });
  });
  