1. /**
    
  2.  * Copyright (c) Meta Platforms, Inc. and affiliates.
    
  3.  *
    
  4.  * This source code is licensed under the MIT license found in the
    
  5.  * LICENSE file in the root directory of this source tree.
    
  6.  *
    
  7.  * @emails react-core
    
  8.  */
    
  9. 
    
  10. 'use strict';
    
  11. 
    
  12. let PropTypes;
    
  13. let React;
    
  14. let ReactDOM;
    
  15. let ReactFeatureFlags;
    
  16. 
    
  17. // TODO: Refactor this test once componentDidCatch setState is deprecated.
    
  18. describe('ReactLegacyErrorBoundaries', () => {
    
  19.   let log;
    
  20. 
    
  21.   let BrokenConstructor;
    
  22.   let BrokenComponentWillMount;
    
  23.   let BrokenComponentDidMount;
    
  24.   let BrokenComponentWillReceiveProps;
    
  25.   let BrokenComponentWillUpdate;
    
  26.   let BrokenComponentDidUpdate;
    
  27.   let BrokenComponentWillUnmount;
    
  28.   let BrokenRenderErrorBoundary;
    
  29.   let BrokenComponentWillMountErrorBoundary;
    
  30.   let BrokenComponentDidMountErrorBoundary;
    
  31.   let BrokenRender;
    
  32.   let ErrorBoundary;
    
  33.   let BothErrorBoundaries;
    
  34.   let ErrorMessage;
    
  35.   let NoopErrorBoundary;
    
  36.   let RetryErrorBoundary;
    
  37.   let Normal;
    
  38. 
    
  39.   beforeEach(() => {
    
  40.     jest.resetModules();
    
  41.     PropTypes = require('prop-types');
    
  42.     ReactFeatureFlags = require('shared/ReactFeatureFlags');
    
  43.     ReactFeatureFlags.replayFailedUnitOfWorkWithInvokeGuardedCallback = false;
    
  44.     ReactDOM = require('react-dom');
    
  45.     React = require('react');
    
  46. 
    
  47.     log = [];
    
  48. 
    
  49.     BrokenConstructor = class extends React.Component {
    
  50.       constructor(props) {
    
  51.         super(props);
    
  52.         log.push('BrokenConstructor constructor [!]');
    
  53.         throw new Error('Hello');
    
  54.       }
    
  55.       render() {
    
  56.         log.push('BrokenConstructor render');
    
  57.         return <div>{this.props.children}</div>;
    
  58.       }
    
  59.       UNSAFE_componentWillMount() {
    
  60.         log.push('BrokenConstructor componentWillMount');
    
  61.       }
    
  62.       componentDidMount() {
    
  63.         log.push('BrokenConstructor componentDidMount');
    
  64.       }
    
  65.       UNSAFE_componentWillReceiveProps() {
    
  66.         log.push('BrokenConstructor componentWillReceiveProps');
    
  67.       }
    
  68.       UNSAFE_componentWillUpdate() {
    
  69.         log.push('BrokenConstructor componentWillUpdate');
    
  70.       }
    
  71.       componentDidUpdate() {
    
  72.         log.push('BrokenConstructor componentDidUpdate');
    
  73.       }
    
  74.       componentWillUnmount() {
    
  75.         log.push('BrokenConstructor componentWillUnmount');
    
  76.       }
    
  77.     };
    
  78. 
    
  79.     BrokenComponentWillMount = class extends React.Component {
    
  80.       constructor(props) {
    
  81.         super(props);
    
  82.         log.push('BrokenComponentWillMount constructor');
    
  83.       }
    
  84.       render() {
    
  85.         log.push('BrokenComponentWillMount render');
    
  86.         return <div>{this.props.children}</div>;
    
  87.       }
    
  88.       UNSAFE_componentWillMount() {
    
  89.         log.push('BrokenComponentWillMount componentWillMount [!]');
    
  90.         throw new Error('Hello');
    
  91.       }
    
  92.       componentDidMount() {
    
  93.         log.push('BrokenComponentWillMount componentDidMount');
    
  94.       }
    
  95.       UNSAFE_componentWillReceiveProps() {
    
  96.         log.push('BrokenComponentWillMount componentWillReceiveProps');
    
  97.       }
    
  98.       UNSAFE_componentWillUpdate() {
    
  99.         log.push('BrokenComponentWillMount componentWillUpdate');
    
  100.       }
    
  101.       componentDidUpdate() {
    
  102.         log.push('BrokenComponentWillMount componentDidUpdate');
    
  103.       }
    
  104.       componentWillUnmount() {
    
  105.         log.push('BrokenComponentWillMount componentWillUnmount');
    
  106.       }
    
  107.     };
    
  108. 
    
  109.     BrokenComponentDidMount = class extends React.Component {
    
  110.       constructor(props) {
    
  111.         super(props);
    
  112.         log.push('BrokenComponentDidMount constructor');
    
  113.       }
    
  114.       render() {
    
  115.         log.push('BrokenComponentDidMount render');
    
  116.         return <div>{this.props.children}</div>;
    
  117.       }
    
  118.       UNSAFE_componentWillMount() {
    
  119.         log.push('BrokenComponentDidMount componentWillMount');
    
  120.       }
    
  121.       componentDidMount() {
    
  122.         log.push('BrokenComponentDidMount componentDidMount [!]');
    
  123.         throw new Error('Hello');
    
  124.       }
    
  125.       UNSAFE_componentWillReceiveProps() {
    
  126.         log.push('BrokenComponentDidMount componentWillReceiveProps');
    
  127.       }
    
  128.       UNSAFE_componentWillUpdate() {
    
  129.         log.push('BrokenComponentDidMount componentWillUpdate');
    
  130.       }
    
  131.       componentDidUpdate() {
    
  132.         log.push('BrokenComponentDidMount componentDidUpdate');
    
  133.       }
    
  134.       componentWillUnmount() {
    
  135.         log.push('BrokenComponentDidMount componentWillUnmount');
    
  136.       }
    
  137.     };
    
  138. 
    
  139.     BrokenComponentWillReceiveProps = class extends React.Component {
    
  140.       constructor(props) {
    
  141.         super(props);
    
  142.         log.push('BrokenComponentWillReceiveProps constructor');
    
  143.       }
    
  144.       render() {
    
  145.         log.push('BrokenComponentWillReceiveProps render');
    
  146.         return <div>{this.props.children}</div>;
    
  147.       }
    
  148.       UNSAFE_componentWillMount() {
    
  149.         log.push('BrokenComponentWillReceiveProps componentWillMount');
    
  150.       }
    
  151.       componentDidMount() {
    
  152.         log.push('BrokenComponentWillReceiveProps componentDidMount');
    
  153.       }
    
  154.       UNSAFE_componentWillReceiveProps() {
    
  155.         log.push(
    
  156.           'BrokenComponentWillReceiveProps componentWillReceiveProps [!]',
    
  157.         );
    
  158.         throw new Error('Hello');
    
  159.       }
    
  160.       UNSAFE_componentWillUpdate() {
    
  161.         log.push('BrokenComponentWillReceiveProps componentWillUpdate');
    
  162.       }
    
  163.       componentDidUpdate() {
    
  164.         log.push('BrokenComponentWillReceiveProps componentDidUpdate');
    
  165.       }
    
  166.       componentWillUnmount() {
    
  167.         log.push('BrokenComponentWillReceiveProps componentWillUnmount');
    
  168.       }
    
  169.     };
    
  170. 
    
  171.     BrokenComponentWillUpdate = class extends React.Component {
    
  172.       constructor(props) {
    
  173.         super(props);
    
  174.         log.push('BrokenComponentWillUpdate constructor');
    
  175.       }
    
  176.       render() {
    
  177.         log.push('BrokenComponentWillUpdate render');
    
  178.         return <div>{this.props.children}</div>;
    
  179.       }
    
  180.       UNSAFE_componentWillMount() {
    
  181.         log.push('BrokenComponentWillUpdate componentWillMount');
    
  182.       }
    
  183.       componentDidMount() {
    
  184.         log.push('BrokenComponentWillUpdate componentDidMount');
    
  185.       }
    
  186.       UNSAFE_componentWillReceiveProps() {
    
  187.         log.push('BrokenComponentWillUpdate componentWillReceiveProps');
    
  188.       }
    
  189.       UNSAFE_componentWillUpdate() {
    
  190.         log.push('BrokenComponentWillUpdate componentWillUpdate [!]');
    
  191.         throw new Error('Hello');
    
  192.       }
    
  193.       componentDidUpdate() {
    
  194.         log.push('BrokenComponentWillUpdate componentDidUpdate');
    
  195.       }
    
  196.       componentWillUnmount() {
    
  197.         log.push('BrokenComponentWillUpdate componentWillUnmount');
    
  198.       }
    
  199.     };
    
  200. 
    
  201.     BrokenComponentDidUpdate = class extends React.Component {
    
  202.       static defaultProps = {
    
  203.         errorText: 'Hello',
    
  204.       };
    
  205.       constructor(props) {
    
  206.         super(props);
    
  207.         log.push('BrokenComponentDidUpdate constructor');
    
  208.       }
    
  209.       render() {
    
  210.         log.push('BrokenComponentDidUpdate render');
    
  211.         return <div>{this.props.children}</div>;
    
  212.       }
    
  213.       UNSAFE_componentWillMount() {
    
  214.         log.push('BrokenComponentDidUpdate componentWillMount');
    
  215.       }
    
  216.       componentDidMount() {
    
  217.         log.push('BrokenComponentDidUpdate componentDidMount');
    
  218.       }
    
  219.       UNSAFE_componentWillReceiveProps() {
    
  220.         log.push('BrokenComponentDidUpdate componentWillReceiveProps');
    
  221.       }
    
  222.       UNSAFE_componentWillUpdate() {
    
  223.         log.push('BrokenComponentDidUpdate componentWillUpdate');
    
  224.       }
    
  225.       componentDidUpdate() {
    
  226.         log.push('BrokenComponentDidUpdate componentDidUpdate [!]');
    
  227.         throw new Error(this.props.errorText);
    
  228.       }
    
  229.       componentWillUnmount() {
    
  230.         log.push('BrokenComponentDidUpdate componentWillUnmount');
    
  231.       }
    
  232.     };
    
  233. 
    
  234.     BrokenComponentWillUnmount = class extends React.Component {
    
  235.       static defaultProps = {
    
  236.         errorText: 'Hello',
    
  237.       };
    
  238.       constructor(props) {
    
  239.         super(props);
    
  240.         log.push('BrokenComponentWillUnmount constructor');
    
  241.       }
    
  242.       render() {
    
  243.         log.push('BrokenComponentWillUnmount render');
    
  244.         return <div>{this.props.children}</div>;
    
  245.       }
    
  246.       UNSAFE_componentWillMount() {
    
  247.         log.push('BrokenComponentWillUnmount componentWillMount');
    
  248.       }
    
  249.       componentDidMount() {
    
  250.         log.push('BrokenComponentWillUnmount componentDidMount');
    
  251.       }
    
  252.       UNSAFE_componentWillReceiveProps() {
    
  253.         log.push('BrokenComponentWillUnmount componentWillReceiveProps');
    
  254.       }
    
  255.       UNSAFE_componentWillUpdate() {
    
  256.         log.push('BrokenComponentWillUnmount componentWillUpdate');
    
  257.       }
    
  258.       componentDidUpdate() {
    
  259.         log.push('BrokenComponentWillUnmount componentDidUpdate');
    
  260.       }
    
  261.       componentWillUnmount() {
    
  262.         log.push('BrokenComponentWillUnmount componentWillUnmount [!]');
    
  263.         throw new Error(this.props.errorText);
    
  264.       }
    
  265.     };
    
  266. 
    
  267.     BrokenComponentWillMountErrorBoundary = class extends React.Component {
    
  268.       constructor(props) {
    
  269.         super(props);
    
  270.         this.state = {error: null};
    
  271.         log.push('BrokenComponentWillMountErrorBoundary constructor');
    
  272.       }
    
  273.       render() {
    
  274.         if (this.state.error) {
    
  275.           log.push('BrokenComponentWillMountErrorBoundary render error');
    
  276.           return <div>Caught an error: {this.state.error.message}.</div>;
    
  277.         }
    
  278.         log.push('BrokenComponentWillMountErrorBoundary render success');
    
  279.         return <div>{this.props.children}</div>;
    
  280.       }
    
  281.       UNSAFE_componentWillMount() {
    
  282.         log.push(
    
  283.           'BrokenComponentWillMountErrorBoundary componentWillMount [!]',
    
  284.         );
    
  285.         throw new Error('Hello');
    
  286.       }
    
  287.       componentDidMount() {
    
  288.         log.push('BrokenComponentWillMountErrorBoundary componentDidMount');
    
  289.       }
    
  290.       componentWillUnmount() {
    
  291.         log.push('BrokenComponentWillMountErrorBoundary componentWillUnmount');
    
  292.       }
    
  293.       componentDidCatch(error) {
    
  294.         log.push('BrokenComponentWillMountErrorBoundary componentDidCatch');
    
  295.         this.setState({error});
    
  296.       }
    
  297.     };
    
  298. 
    
  299.     BrokenComponentDidMountErrorBoundary = class extends React.Component {
    
  300.       constructor(props) {
    
  301.         super(props);
    
  302.         this.state = {error: null};
    
  303.         log.push('BrokenComponentDidMountErrorBoundary constructor');
    
  304.       }
    
  305.       render() {
    
  306.         if (this.state.error) {
    
  307.           log.push('BrokenComponentDidMountErrorBoundary render error');
    
  308.           return <div>Caught an error: {this.state.error.message}.</div>;
    
  309.         }
    
  310.         log.push('BrokenComponentDidMountErrorBoundary render success');
    
  311.         return <div>{this.props.children}</div>;
    
  312.       }
    
  313.       UNSAFE_componentWillMount() {
    
  314.         log.push('BrokenComponentDidMountErrorBoundary componentWillMount');
    
  315.       }
    
  316.       componentDidMount() {
    
  317.         log.push('BrokenComponentDidMountErrorBoundary componentDidMount [!]');
    
  318.         throw new Error('Hello');
    
  319.       }
    
  320.       componentWillUnmount() {
    
  321.         log.push('BrokenComponentDidMountErrorBoundary componentWillUnmount');
    
  322.       }
    
  323.       componentDidCatch(error) {
    
  324.         log.push('BrokenComponentDidMountErrorBoundary componentDidCatch');
    
  325.         this.setState({error});
    
  326.       }
    
  327.     };
    
  328. 
    
  329.     BrokenRenderErrorBoundary = class extends React.Component {
    
  330.       constructor(props) {
    
  331.         super(props);
    
  332.         this.state = {error: null};
    
  333.         log.push('BrokenRenderErrorBoundary constructor');
    
  334.       }
    
  335.       render() {
    
  336.         if (this.state.error) {
    
  337.           log.push('BrokenRenderErrorBoundary render error [!]');
    
  338.           throw new Error('Hello');
    
  339.         }
    
  340.         log.push('BrokenRenderErrorBoundary render success');
    
  341.         return <div>{this.props.children}</div>;
    
  342.       }
    
  343.       UNSAFE_componentWillMount() {
    
  344.         log.push('BrokenRenderErrorBoundary componentWillMount');
    
  345.       }
    
  346.       componentDidMount() {
    
  347.         log.push('BrokenRenderErrorBoundary componentDidMount');
    
  348.       }
    
  349.       componentWillUnmount() {
    
  350.         log.push('BrokenRenderErrorBoundary componentWillUnmount');
    
  351.       }
    
  352.       componentDidCatch(error) {
    
  353.         log.push('BrokenRenderErrorBoundary componentDidCatch');
    
  354.         this.setState({error});
    
  355.       }
    
  356.     };
    
  357. 
    
  358.     BrokenRender = class extends React.Component {
    
  359.       constructor(props) {
    
  360.         super(props);
    
  361.         log.push('BrokenRender constructor');
    
  362.       }
    
  363.       render() {
    
  364.         log.push('BrokenRender render [!]');
    
  365.         throw new Error('Hello');
    
  366.       }
    
  367.       UNSAFE_componentWillMount() {
    
  368.         log.push('BrokenRender componentWillMount');
    
  369.       }
    
  370.       componentDidMount() {
    
  371.         log.push('BrokenRender componentDidMount');
    
  372.       }
    
  373.       UNSAFE_componentWillReceiveProps() {
    
  374.         log.push('BrokenRender componentWillReceiveProps');
    
  375.       }
    
  376.       UNSAFE_componentWillUpdate() {
    
  377.         log.push('BrokenRender componentWillUpdate');
    
  378.       }
    
  379.       componentDidUpdate() {
    
  380.         log.push('BrokenRender componentDidUpdate');
    
  381.       }
    
  382.       componentWillUnmount() {
    
  383.         log.push('BrokenRender componentWillUnmount');
    
  384.       }
    
  385.     };
    
  386. 
    
  387.     NoopErrorBoundary = class extends React.Component {
    
  388.       constructor(props) {
    
  389.         super(props);
    
  390.         log.push('NoopErrorBoundary constructor');
    
  391.       }
    
  392.       render() {
    
  393.         log.push('NoopErrorBoundary render');
    
  394.         return <BrokenRender />;
    
  395.       }
    
  396.       UNSAFE_componentWillMount() {
    
  397.         log.push('NoopErrorBoundary componentWillMount');
    
  398.       }
    
  399.       componentDidMount() {
    
  400.         log.push('NoopErrorBoundary componentDidMount');
    
  401.       }
    
  402.       componentWillUnmount() {
    
  403.         log.push('NoopErrorBoundary componentWillUnmount');
    
  404.       }
    
  405.       componentDidCatch() {
    
  406.         log.push('NoopErrorBoundary componentDidCatch');
    
  407.       }
    
  408.     };
    
  409. 
    
  410.     Normal = class extends React.Component {
    
  411.       static defaultProps = {
    
  412.         logName: 'Normal',
    
  413.       };
    
  414.       constructor(props) {
    
  415.         super(props);
    
  416.         log.push(`${this.props.logName} constructor`);
    
  417.       }
    
  418.       render() {
    
  419.         log.push(`${this.props.logName} render`);
    
  420.         return <div>{this.props.children}</div>;
    
  421.       }
    
  422.       UNSAFE_componentWillMount() {
    
  423.         log.push(`${this.props.logName} componentWillMount`);
    
  424.       }
    
  425.       componentDidMount() {
    
  426.         log.push(`${this.props.logName} componentDidMount`);
    
  427.       }
    
  428.       UNSAFE_componentWillReceiveProps() {
    
  429.         log.push(`${this.props.logName} componentWillReceiveProps`);
    
  430.       }
    
  431.       UNSAFE_componentWillUpdate() {
    
  432.         log.push(`${this.props.logName} componentWillUpdate`);
    
  433.       }
    
  434.       componentDidUpdate() {
    
  435.         log.push(`${this.props.logName} componentDidUpdate`);
    
  436.       }
    
  437.       componentWillUnmount() {
    
  438.         log.push(`${this.props.logName} componentWillUnmount`);
    
  439.       }
    
  440.     };
    
  441. 
    
  442.     ErrorBoundary = class extends React.Component {
    
  443.       constructor(props) {
    
  444.         super(props);
    
  445.         this.state = {error: null};
    
  446.         log.push(`${this.props.logName} constructor`);
    
  447.       }
    
  448.       render() {
    
  449.         if (this.state.error && !this.props.forceRetry) {
    
  450.           log.push(`${this.props.logName} render error`);
    
  451.           return this.props.renderError(this.state.error, this.props);
    
  452.         }
    
  453.         log.push(`${this.props.logName} render success`);
    
  454.         return <div>{this.props.children}</div>;
    
  455.       }
    
  456.       componentDidCatch(error) {
    
  457.         log.push(`${this.props.logName} componentDidCatch`);
    
  458.         this.setState({error});
    
  459.       }
    
  460.       UNSAFE_componentWillMount() {
    
  461.         log.push(`${this.props.logName} componentWillMount`);
    
  462.       }
    
  463.       componentDidMount() {
    
  464.         log.push(`${this.props.logName} componentDidMount`);
    
  465.       }
    
  466.       UNSAFE_componentWillReceiveProps() {
    
  467.         log.push(`${this.props.logName} componentWillReceiveProps`);
    
  468.       }
    
  469.       UNSAFE_componentWillUpdate() {
    
  470.         log.push(`${this.props.logName} componentWillUpdate`);
    
  471.       }
    
  472.       componentDidUpdate() {
    
  473.         log.push(`${this.props.logName} componentDidUpdate`);
    
  474.       }
    
  475.       componentWillUnmount() {
    
  476.         log.push(`${this.props.logName} componentWillUnmount`);
    
  477.       }
    
  478.     };
    
  479.     ErrorBoundary.defaultProps = {
    
  480.       logName: 'ErrorBoundary',
    
  481.       renderError(error, props) {
    
  482.         return (
    
  483.           <div ref={props.errorMessageRef}>
    
  484.             Caught an error: {error.message}.
    
  485.           </div>
    
  486.         );
    
  487.       },
    
  488.     };
    
  489. 
    
  490.     BothErrorBoundaries = class extends React.Component {
    
  491.       constructor(props) {
    
  492.         super(props);
    
  493.         this.state = {error: null};
    
  494.         log.push('BothErrorBoundaries constructor');
    
  495.       }
    
  496. 
    
  497.       static getDerivedStateFromError(error) {
    
  498.         log.push('BothErrorBoundaries static getDerivedStateFromError');
    
  499.         return {error};
    
  500.       }
    
  501. 
    
  502.       render() {
    
  503.         if (this.state.error) {
    
  504.           log.push('BothErrorBoundaries render error');
    
  505.           return <div>Caught an error: {this.state.error.message}.</div>;
    
  506.         }
    
  507.         log.push('BothErrorBoundaries render success');
    
  508.         return <div>{this.props.children}</div>;
    
  509.       }
    
  510. 
    
  511.       componentDidCatch(error) {
    
  512.         log.push('BothErrorBoundaries componentDidCatch');
    
  513.         this.setState({error});
    
  514.       }
    
  515. 
    
  516.       UNSAFE_componentWillMount() {
    
  517.         log.push('BothErrorBoundaries componentWillMount');
    
  518.       }
    
  519. 
    
  520.       componentDidMount() {
    
  521.         log.push('BothErrorBoundaries componentDidMount');
    
  522.       }
    
  523. 
    
  524.       UNSAFE_componentWillReceiveProps() {
    
  525.         log.push('BothErrorBoundaries componentWillReceiveProps');
    
  526.       }
    
  527. 
    
  528.       UNSAFE_componentWillUpdate() {
    
  529.         log.push('BothErrorBoundaries componentWillUpdate');
    
  530.       }
    
  531. 
    
  532.       componentDidUpdate() {
    
  533.         log.push('BothErrorBoundaries componentDidUpdate');
    
  534.       }
    
  535. 
    
  536.       componentWillUnmount() {
    
  537.         log.push('BothErrorBoundaries componentWillUnmount');
    
  538.       }
    
  539.     };
    
  540. 
    
  541.     RetryErrorBoundary = class extends React.Component {
    
  542.       constructor(props) {
    
  543.         super(props);
    
  544.         log.push('RetryErrorBoundary constructor');
    
  545.       }
    
  546.       render() {
    
  547.         log.push('RetryErrorBoundary render');
    
  548.         return <BrokenRender />;
    
  549.       }
    
  550.       UNSAFE_componentWillMount() {
    
  551.         log.push('RetryErrorBoundary componentWillMount');
    
  552.       }
    
  553.       componentDidMount() {
    
  554.         log.push('RetryErrorBoundary componentDidMount');
    
  555.       }
    
  556.       componentWillUnmount() {
    
  557.         log.push('RetryErrorBoundary componentWillUnmount');
    
  558.       }
    
  559.       componentDidCatch(error) {
    
  560.         log.push('RetryErrorBoundary componentDidCatch [!]');
    
  561.         // In Fiber, calling setState() (and failing) is treated as a rethrow.
    
  562.         this.setState({});
    
  563.       }
    
  564.     };
    
  565. 
    
  566.     ErrorMessage = class extends React.Component {
    
  567.       constructor(props) {
    
  568.         super(props);
    
  569.         log.push('ErrorMessage constructor');
    
  570.       }
    
  571.       UNSAFE_componentWillMount() {
    
  572.         log.push('ErrorMessage componentWillMount');
    
  573.       }
    
  574.       componentDidMount() {
    
  575.         log.push('ErrorMessage componentDidMount');
    
  576.       }
    
  577.       componentWillUnmount() {
    
  578.         log.push('ErrorMessage componentWillUnmount');
    
  579.       }
    
  580.       render() {
    
  581.         log.push('ErrorMessage render');
    
  582.         return <div>Caught an error: {this.props.message}.</div>;
    
  583.       }
    
  584.     };
    
  585.   });
    
  586. 
    
  587.   afterEach(() => {
    
  588.     jest.restoreAllMocks();
    
  589.   });
    
  590. 
    
  591.   it('does not swallow exceptions on mounting without boundaries', () => {
    
  592.     let container = document.createElement('div');
    
  593.     expect(() => {
    
  594.       ReactDOM.render(<BrokenRender />, container);
    
  595.     }).toThrow('Hello');
    
  596. 
    
  597.     container = document.createElement('div');
    
  598.     expect(() => {
    
  599.       ReactDOM.render(<BrokenComponentWillMount />, container);
    
  600.     }).toThrow('Hello');
    
  601. 
    
  602.     container = document.createElement('div');
    
  603.     expect(() => {
    
  604.       ReactDOM.render(<BrokenComponentDidMount />, container);
    
  605.     }).toThrow('Hello');
    
  606.   });
    
  607. 
    
  608.   it('does not swallow exceptions on updating without boundaries', () => {
    
  609.     let container = document.createElement('div');
    
  610.     ReactDOM.render(<BrokenComponentWillUpdate />, container);
    
  611.     expect(() => {
    
  612.       ReactDOM.render(<BrokenComponentWillUpdate />, container);
    
  613.     }).toThrow('Hello');
    
  614. 
    
  615.     container = document.createElement('div');
    
  616.     ReactDOM.render(<BrokenComponentWillReceiveProps />, container);
    
  617.     expect(() => {
    
  618.       ReactDOM.render(<BrokenComponentWillReceiveProps />, container);
    
  619.     }).toThrow('Hello');
    
  620. 
    
  621.     container = document.createElement('div');
    
  622.     ReactDOM.render(<BrokenComponentDidUpdate />, container);
    
  623.     expect(() => {
    
  624.       ReactDOM.render(<BrokenComponentDidUpdate />, container);
    
  625.     }).toThrow('Hello');
    
  626.   });
    
  627. 
    
  628.   it('does not swallow exceptions on unmounting without boundaries', () => {
    
  629.     const container = document.createElement('div');
    
  630.     ReactDOM.render(<BrokenComponentWillUnmount />, container);
    
  631.     expect(() => {
    
  632.       ReactDOM.unmountComponentAtNode(container);
    
  633.     }).toThrow('Hello');
    
  634.   });
    
  635. 
    
  636.   it('prevents errors from leaking into other roots', () => {
    
  637.     const container1 = document.createElement('div');
    
  638.     const container2 = document.createElement('div');
    
  639.     const container3 = document.createElement('div');
    
  640. 
    
  641.     ReactDOM.render(<span>Before 1</span>, container1);
    
  642.     expect(() => {
    
  643.       ReactDOM.render(<BrokenRender />, container2);
    
  644.     }).toThrow('Hello');
    
  645.     ReactDOM.render(
    
  646.       <ErrorBoundary>
    
  647.         <BrokenRender />
    
  648.       </ErrorBoundary>,
    
  649.       container3,
    
  650.     );
    
  651.     expect(container1.firstChild.textContent).toBe('Before 1');
    
  652.     expect(container2.firstChild).toBe(null);
    
  653.     expect(container3.firstChild.textContent).toBe('Caught an error: Hello.');
    
  654. 
    
  655.     ReactDOM.render(<span>After 1</span>, container1);
    
  656.     ReactDOM.render(<span>After 2</span>, container2);
    
  657.     ReactDOM.render(
    
  658.       <ErrorBoundary forceRetry={true}>After 3</ErrorBoundary>,
    
  659.       container3,
    
  660.     );
    
  661.     expect(container1.firstChild.textContent).toBe('After 1');
    
  662.     expect(container2.firstChild.textContent).toBe('After 2');
    
  663.     expect(container3.firstChild.textContent).toBe('After 3');
    
  664. 
    
  665.     ReactDOM.unmountComponentAtNode(container1);
    
  666.     ReactDOM.unmountComponentAtNode(container2);
    
  667.     ReactDOM.unmountComponentAtNode(container3);
    
  668.     expect(container1.firstChild).toBe(null);
    
  669.     expect(container2.firstChild).toBe(null);
    
  670.     expect(container3.firstChild).toBe(null);
    
  671.   });
    
  672. 
    
  673.   it('logs a single error using both error boundaries', () => {
    
  674.     const container = document.createElement('div');
    
  675.     spyOnDev(console, 'error');
    
  676.     ReactDOM.render(
    
  677.       <BothErrorBoundaries>
    
  678.         <BrokenRender />
    
  679.       </BothErrorBoundaries>,
    
  680.       container,
    
  681.     );
    
  682.     if (__DEV__) {
    
  683.       expect(console.error).toHaveBeenCalledTimes(2);
    
  684.       expect(console.error.mock.calls[0][0]).toContain(
    
  685.         'ReactDOM.render is no longer supported',
    
  686.       );
    
  687.       expect(console.error.mock.calls[1][0]).toContain(
    
  688.         'The above error occurred in the <BrokenRender> component:',
    
  689.       );
    
  690.     }
    
  691. 
    
  692.     expect(container.firstChild.textContent).toBe('Caught an error: Hello.');
    
  693.     expect(log).toEqual([
    
  694.       'BothErrorBoundaries constructor',
    
  695.       'BothErrorBoundaries componentWillMount',
    
  696.       'BothErrorBoundaries render success',
    
  697.       'BrokenRender constructor',
    
  698.       'BrokenRender componentWillMount',
    
  699.       'BrokenRender render [!]',
    
  700.       // Both getDerivedStateFromError and componentDidCatch should be called
    
  701.       'BothErrorBoundaries static getDerivedStateFromError',
    
  702.       'BothErrorBoundaries componentWillMount',
    
  703.       'BothErrorBoundaries render error',
    
  704.       // Fiber mounts with null children before capturing error
    
  705.       'BothErrorBoundaries componentDidMount',
    
  706.       // Catch and render an error message
    
  707.       'BothErrorBoundaries componentDidCatch',
    
  708.       'BothErrorBoundaries componentWillUpdate',
    
  709.       'BothErrorBoundaries render error',
    
  710.       'BothErrorBoundaries componentDidUpdate',
    
  711.     ]);
    
  712. 
    
  713.     log.length = 0;
    
  714.     ReactDOM.unmountComponentAtNode(container);
    
  715.     expect(log).toEqual(['BothErrorBoundaries componentWillUnmount']);
    
  716.   });
    
  717. 
    
  718.   it('renders an error state if child throws in render', () => {
    
  719.     const container = document.createElement('div');
    
  720.     ReactDOM.render(
    
  721.       <ErrorBoundary>
    
  722.         <BrokenRender />
    
  723.       </ErrorBoundary>,
    
  724.       container,
    
  725.     );
    
  726.     expect(container.firstChild.textContent).toBe('Caught an error: Hello.');
    
  727.     expect(log).toEqual([
    
  728.       'ErrorBoundary constructor',
    
  729.       'ErrorBoundary componentWillMount',
    
  730.       'ErrorBoundary render success',
    
  731.       'BrokenRender constructor',
    
  732.       'BrokenRender componentWillMount',
    
  733.       'BrokenRender render [!]',
    
  734.       // Fiber mounts with null children before capturing error
    
  735.       'ErrorBoundary componentDidMount',
    
  736.       // Catch and render an error message
    
  737.       'ErrorBoundary componentDidCatch',
    
  738.       'ErrorBoundary componentWillUpdate',
    
  739.       'ErrorBoundary render error',
    
  740.       'ErrorBoundary componentDidUpdate',
    
  741.     ]);
    
  742. 
    
  743.     log.length = 0;
    
  744.     ReactDOM.unmountComponentAtNode(container);
    
  745.     expect(log).toEqual(['ErrorBoundary componentWillUnmount']);
    
  746.   });
    
  747. 
    
  748.   it('renders an error state if child throws in constructor', () => {
    
  749.     const container = document.createElement('div');
    
  750.     ReactDOM.render(
    
  751.       <ErrorBoundary>
    
  752.         <BrokenConstructor />
    
  753.       </ErrorBoundary>,
    
  754.       container,
    
  755.     );
    
  756.     expect(container.firstChild.textContent).toBe('Caught an error: Hello.');
    
  757.     expect(log).toEqual([
    
  758.       'ErrorBoundary constructor',
    
  759.       'ErrorBoundary componentWillMount',
    
  760.       'ErrorBoundary render success',
    
  761.       'BrokenConstructor constructor [!]',
    
  762.       // Fiber mounts with null children before capturing error
    
  763.       'ErrorBoundary componentDidMount',
    
  764.       // Catch and render an error message
    
  765.       'ErrorBoundary componentDidCatch',
    
  766.       'ErrorBoundary componentWillUpdate',
    
  767.       'ErrorBoundary render error',
    
  768.       'ErrorBoundary componentDidUpdate',
    
  769.     ]);
    
  770. 
    
  771.     log.length = 0;
    
  772.     ReactDOM.unmountComponentAtNode(container);
    
  773.     expect(log).toEqual(['ErrorBoundary componentWillUnmount']);
    
  774.   });
    
  775. 
    
  776.   it('renders an error state if child throws in componentWillMount', () => {
    
  777.     const container = document.createElement('div');
    
  778.     ReactDOM.render(
    
  779.       <ErrorBoundary>
    
  780.         <BrokenComponentWillMount />
    
  781.       </ErrorBoundary>,
    
  782.       container,
    
  783.     );
    
  784.     expect(container.firstChild.textContent).toBe('Caught an error: Hello.');
    
  785.     expect(log).toEqual([
    
  786.       'ErrorBoundary constructor',
    
  787.       'ErrorBoundary componentWillMount',
    
  788.       'ErrorBoundary render success',
    
  789.       'BrokenComponentWillMount constructor',
    
  790.       'BrokenComponentWillMount componentWillMount [!]',
    
  791.       'ErrorBoundary componentDidMount',
    
  792.       'ErrorBoundary componentDidCatch',
    
  793.       'ErrorBoundary componentWillUpdate',
    
  794.       'ErrorBoundary render error',
    
  795.       'ErrorBoundary componentDidUpdate',
    
  796.     ]);
    
  797. 
    
  798.     log.length = 0;
    
  799.     ReactDOM.unmountComponentAtNode(container);
    
  800.     expect(log).toEqual(['ErrorBoundary componentWillUnmount']);
    
  801.   });
    
  802. 
    
  803.   // @gate !disableLegacyContext || !__DEV__
    
  804.   it('renders an error state if context provider throws in componentWillMount', () => {
    
  805.     class BrokenComponentWillMountWithContext extends React.Component {
    
  806.       static childContextTypes = {foo: PropTypes.number};
    
  807.       getChildContext() {
    
  808.         return {foo: 42};
    
  809.       }
    
  810.       render() {
    
  811.         return <div>{this.props.children}</div>;
    
  812.       }
    
  813.       UNSAFE_componentWillMount() {
    
  814.         throw new Error('Hello');
    
  815.       }
    
  816.     }
    
  817. 
    
  818.     const container = document.createElement('div');
    
  819.     ReactDOM.render(
    
  820.       <ErrorBoundary>
    
  821.         <BrokenComponentWillMountWithContext />
    
  822.       </ErrorBoundary>,
    
  823.       container,
    
  824.     );
    
  825.     expect(container.firstChild.textContent).toBe('Caught an error: Hello.');
    
  826.   });
    
  827. 
    
  828.   if (!require('shared/ReactFeatureFlags').disableModulePatternComponents) {
    
  829.     it('renders an error state if module-style context provider throws in componentWillMount', () => {
    
  830.       function BrokenComponentWillMountWithContext() {
    
  831.         return {
    
  832.           getChildContext() {
    
  833.             return {foo: 42};
    
  834.           },
    
  835.           render() {
    
  836.             return <div>{this.props.children}</div>;
    
  837.           },
    
  838.           UNSAFE_componentWillMount() {
    
  839.             throw new Error('Hello');
    
  840.           },
    
  841.         };
    
  842.       }
    
  843.       BrokenComponentWillMountWithContext.childContextTypes = {
    
  844.         foo: PropTypes.number,
    
  845.       };
    
  846. 
    
  847.       const container = document.createElement('div');
    
  848.       expect(() =>
    
  849.         ReactDOM.render(
    
  850.           <ErrorBoundary>
    
  851.             <BrokenComponentWillMountWithContext />
    
  852.           </ErrorBoundary>,
    
  853.           container,
    
  854.         ),
    
  855.       ).toErrorDev(
    
  856.         'Warning: The <BrokenComponentWillMountWithContext /> component appears to be a function component that ' +
    
  857.           'returns a class instance. ' +
    
  858.           'Change BrokenComponentWillMountWithContext to a class that extends React.Component instead. ' +
    
  859.           "If you can't use a class try assigning the prototype on the function as a workaround. " +
    
  860.           '`BrokenComponentWillMountWithContext.prototype = React.Component.prototype`. ' +
    
  861.           "Don't use an arrow function since it cannot be called with `new` by React.",
    
  862.       );
    
  863.       expect(container.firstChild.textContent).toBe('Caught an error: Hello.');
    
  864.     });
    
  865.   }
    
  866. 
    
  867.   it('mounts the error message if mounting fails', () => {
    
  868.     function renderError(error) {
    
  869.       return <ErrorMessage message={error.message} />;
    
  870.     }
    
  871. 
    
  872.     const container = document.createElement('div');
    
  873.     ReactDOM.render(
    
  874.       <ErrorBoundary renderError={renderError}>
    
  875.         <BrokenRender />
    
  876.       </ErrorBoundary>,
    
  877.       container,
    
  878.     );
    
  879.     expect(log).toEqual([
    
  880.       'ErrorBoundary constructor',
    
  881.       'ErrorBoundary componentWillMount',
    
  882.       'ErrorBoundary render success',
    
  883.       'BrokenRender constructor',
    
  884.       'BrokenRender componentWillMount',
    
  885.       'BrokenRender render [!]',
    
  886.       'ErrorBoundary componentDidMount',
    
  887.       'ErrorBoundary componentDidCatch',
    
  888.       'ErrorBoundary componentWillUpdate',
    
  889.       'ErrorBoundary render error',
    
  890.       'ErrorMessage constructor',
    
  891.       'ErrorMessage componentWillMount',
    
  892.       'ErrorMessage render',
    
  893.       'ErrorMessage componentDidMount',
    
  894.       'ErrorBoundary componentDidUpdate',
    
  895.     ]);
    
  896. 
    
  897.     log.length = 0;
    
  898.     ReactDOM.unmountComponentAtNode(container);
    
  899.     expect(log).toEqual([
    
  900.       'ErrorBoundary componentWillUnmount',
    
  901.       'ErrorMessage componentWillUnmount',
    
  902.     ]);
    
  903.   });
    
  904. 
    
  905.   it('propagates errors on retry on mounting', () => {
    
  906.     const container = document.createElement('div');
    
  907.     ReactDOM.render(
    
  908.       <ErrorBoundary>
    
  909.         <RetryErrorBoundary>
    
  910.           <BrokenRender />
    
  911.         </RetryErrorBoundary>
    
  912.       </ErrorBoundary>,
    
  913.       container,
    
  914.     );
    
  915.     expect(container.firstChild.textContent).toBe('Caught an error: Hello.');
    
  916.     expect(log).toEqual([
    
  917.       'ErrorBoundary constructor',
    
  918.       'ErrorBoundary componentWillMount',
    
  919.       'ErrorBoundary render success',
    
  920.       'RetryErrorBoundary constructor',
    
  921.       'RetryErrorBoundary componentWillMount',
    
  922.       'RetryErrorBoundary render',
    
  923.       'BrokenRender constructor',
    
  924.       'BrokenRender componentWillMount',
    
  925.       'BrokenRender render [!]',
    
  926.       // In Fiber, failed error boundaries render null before attempting to recover
    
  927.       'RetryErrorBoundary componentDidMount',
    
  928.       'RetryErrorBoundary componentDidCatch [!]',
    
  929.       'ErrorBoundary componentDidMount',
    
  930.       // Retry
    
  931.       'RetryErrorBoundary render',
    
  932.       'BrokenRender constructor',
    
  933.       'BrokenRender componentWillMount',
    
  934.       'BrokenRender render [!]',
    
  935.       // This time, the error propagates to the higher boundary
    
  936.       'RetryErrorBoundary componentWillUnmount',
    
  937.       'ErrorBoundary componentDidCatch',
    
  938.       // Render the error
    
  939.       'ErrorBoundary componentWillUpdate',
    
  940.       'ErrorBoundary render error',
    
  941.       'ErrorBoundary componentDidUpdate',
    
  942.     ]);
    
  943. 
    
  944.     log.length = 0;
    
  945.     ReactDOM.unmountComponentAtNode(container);
    
  946.     expect(log).toEqual(['ErrorBoundary componentWillUnmount']);
    
  947.   });
    
  948. 
    
  949.   it('propagates errors inside boundary during componentWillMount', () => {
    
  950.     const container = document.createElement('div');
    
  951.     ReactDOM.render(
    
  952.       <ErrorBoundary>
    
  953.         <BrokenComponentWillMountErrorBoundary />
    
  954.       </ErrorBoundary>,
    
  955.       container,
    
  956.     );
    
  957.     expect(container.firstChild.textContent).toBe('Caught an error: Hello.');
    
  958.     expect(log).toEqual([
    
  959.       'ErrorBoundary constructor',
    
  960.       'ErrorBoundary componentWillMount',
    
  961.       'ErrorBoundary render success',
    
  962.       'BrokenComponentWillMountErrorBoundary constructor',
    
  963.       'BrokenComponentWillMountErrorBoundary componentWillMount [!]',
    
  964.       // The error propagates to the higher boundary
    
  965.       'ErrorBoundary componentDidMount',
    
  966.       'ErrorBoundary componentDidCatch',
    
  967.       'ErrorBoundary componentWillUpdate',
    
  968.       'ErrorBoundary render error',
    
  969.       'ErrorBoundary componentDidUpdate',
    
  970.     ]);
    
  971. 
    
  972.     log.length = 0;
    
  973.     ReactDOM.unmountComponentAtNode(container);
    
  974.     expect(log).toEqual(['ErrorBoundary componentWillUnmount']);
    
  975.   });
    
  976. 
    
  977.   it('propagates errors inside boundary while rendering error state', () => {
    
  978.     const container = document.createElement('div');
    
  979.     ReactDOM.render(
    
  980.       <ErrorBoundary>
    
  981.         <BrokenRenderErrorBoundary>
    
  982.           <BrokenRender />
    
  983.         </BrokenRenderErrorBoundary>
    
  984.       </ErrorBoundary>,
    
  985.       container,
    
  986.     );
    
  987.     expect(container.firstChild.textContent).toBe('Caught an error: Hello.');
    
  988.     expect(log).toEqual([
    
  989.       'ErrorBoundary constructor',
    
  990.       'ErrorBoundary componentWillMount',
    
  991.       'ErrorBoundary render success',
    
  992.       'BrokenRenderErrorBoundary constructor',
    
  993.       'BrokenRenderErrorBoundary componentWillMount',
    
  994.       'BrokenRenderErrorBoundary render success',
    
  995.       'BrokenRender constructor',
    
  996.       'BrokenRender componentWillMount',
    
  997.       'BrokenRender render [!]',
    
  998.       // The first error boundary catches the error
    
  999.       // It adjusts state but throws displaying the message
    
  1000.       // Finish mounting with null children
    
  1001.       'BrokenRenderErrorBoundary componentDidMount',
    
  1002.       // Attempt to handle the error
    
  1003.       'BrokenRenderErrorBoundary componentDidCatch',
    
  1004.       'ErrorBoundary componentDidMount',
    
  1005.       'BrokenRenderErrorBoundary render error [!]',
    
  1006.       // Boundary fails with new error, propagate to next boundary
    
  1007.       'BrokenRenderErrorBoundary componentWillUnmount',
    
  1008.       // Attempt to handle the error again
    
  1009.       'ErrorBoundary componentDidCatch',
    
  1010.       'ErrorBoundary componentWillUpdate',
    
  1011.       'ErrorBoundary render error',
    
  1012.       'ErrorBoundary componentDidUpdate',
    
  1013.     ]);
    
  1014. 
    
  1015.     log.length = 0;
    
  1016.     ReactDOM.unmountComponentAtNode(container);
    
  1017.     expect(log).toEqual(['ErrorBoundary componentWillUnmount']);
    
  1018.   });
    
  1019. 
    
  1020.   it('does not call componentWillUnmount when aborting initial mount', () => {
    
  1021.     const container = document.createElement('div');
    
  1022.     ReactDOM.render(
    
  1023.       <ErrorBoundary>
    
  1024.         <Normal />
    
  1025.         <BrokenRender />
    
  1026.         <Normal />
    
  1027.       </ErrorBoundary>,
    
  1028.       container,
    
  1029.     );
    
  1030.     expect(container.firstChild.textContent).toBe('Caught an error: Hello.');
    
  1031.     expect(log).toEqual([
    
  1032.       'ErrorBoundary constructor',
    
  1033.       'ErrorBoundary componentWillMount',
    
  1034.       'ErrorBoundary render success',
    
  1035.       // Render first child
    
  1036.       'Normal constructor',
    
  1037.       'Normal componentWillMount',
    
  1038.       'Normal render',
    
  1039.       // Render second child (it throws)
    
  1040.       'BrokenRender constructor',
    
  1041.       'BrokenRender componentWillMount',
    
  1042.       'BrokenRender render [!]',
    
  1043.       // Skip the remaining siblings
    
  1044.       // Finish mounting with null children
    
  1045.       'ErrorBoundary componentDidMount',
    
  1046.       // Handle the error
    
  1047.       'ErrorBoundary componentDidCatch',
    
  1048.       // Render the error message
    
  1049.       'ErrorBoundary componentWillUpdate',
    
  1050.       'ErrorBoundary render error',
    
  1051.       'ErrorBoundary componentDidUpdate',
    
  1052.     ]);
    
  1053. 
    
  1054.     log.length = 0;
    
  1055.     ReactDOM.unmountComponentAtNode(container);
    
  1056.     expect(log).toEqual(['ErrorBoundary componentWillUnmount']);
    
  1057.   });
    
  1058. 
    
  1059.   it('resets callback refs if mounting aborts', () => {
    
  1060.     function childRef(x) {
    
  1061.       log.push('Child ref is set to ' + x);
    
  1062.     }
    
  1063.     function errorMessageRef(x) {
    
  1064.       log.push('Error message ref is set to ' + x);
    
  1065.     }
    
  1066. 
    
  1067.     const container = document.createElement('div');
    
  1068.     ReactDOM.render(
    
  1069.       <ErrorBoundary errorMessageRef={errorMessageRef}>
    
  1070.         <div ref={childRef} />
    
  1071.         <BrokenRender />
    
  1072.       </ErrorBoundary>,
    
  1073.       container,
    
  1074.     );
    
  1075.     expect(container.textContent).toBe('Caught an error: Hello.');
    
  1076.     expect(log).toEqual([
    
  1077.       'ErrorBoundary constructor',
    
  1078.       'ErrorBoundary componentWillMount',
    
  1079.       'ErrorBoundary render success',
    
  1080.       'BrokenRender constructor',
    
  1081.       'BrokenRender componentWillMount',
    
  1082.       'BrokenRender render [!]',
    
  1083.       // Handle error:
    
  1084.       // Finish mounting with null children
    
  1085.       'ErrorBoundary componentDidMount',
    
  1086.       // Handle the error
    
  1087.       'ErrorBoundary componentDidCatch',
    
  1088.       // Render the error message
    
  1089.       'ErrorBoundary componentWillUpdate',
    
  1090.       'ErrorBoundary render error',
    
  1091.       'Error message ref is set to [object HTMLDivElement]',
    
  1092.       'ErrorBoundary componentDidUpdate',
    
  1093.     ]);
    
  1094. 
    
  1095.     log.length = 0;
    
  1096.     ReactDOM.unmountComponentAtNode(container);
    
  1097.     expect(log).toEqual([
    
  1098.       'ErrorBoundary componentWillUnmount',
    
  1099.       'Error message ref is set to null',
    
  1100.     ]);
    
  1101.   });
    
  1102. 
    
  1103.   it('resets object refs if mounting aborts', () => {
    
  1104.     const childRef = React.createRef();
    
  1105.     const errorMessageRef = React.createRef();
    
  1106. 
    
  1107.     const container = document.createElement('div');
    
  1108.     ReactDOM.render(
    
  1109.       <ErrorBoundary errorMessageRef={errorMessageRef}>
    
  1110.         <div ref={childRef} />
    
  1111.         <BrokenRender />
    
  1112.       </ErrorBoundary>,
    
  1113.       container,
    
  1114.     );
    
  1115.     expect(container.textContent).toBe('Caught an error: Hello.');
    
  1116.     expect(log).toEqual([
    
  1117.       'ErrorBoundary constructor',
    
  1118.       'ErrorBoundary componentWillMount',
    
  1119.       'ErrorBoundary render success',
    
  1120.       'BrokenRender constructor',
    
  1121.       'BrokenRender componentWillMount',
    
  1122.       'BrokenRender render [!]',
    
  1123.       // Handle error:
    
  1124.       // Finish mounting with null children
    
  1125.       'ErrorBoundary componentDidMount',
    
  1126.       // Handle the error
    
  1127.       'ErrorBoundary componentDidCatch',
    
  1128.       // Render the error message
    
  1129.       'ErrorBoundary componentWillUpdate',
    
  1130.       'ErrorBoundary render error',
    
  1131.       'ErrorBoundary componentDidUpdate',
    
  1132.     ]);
    
  1133.     expect(errorMessageRef.current.toString()).toEqual(
    
  1134.       '[object HTMLDivElement]',
    
  1135.     );
    
  1136. 
    
  1137.     log.length = 0;
    
  1138.     ReactDOM.unmountComponentAtNode(container);
    
  1139.     expect(log).toEqual(['ErrorBoundary componentWillUnmount']);
    
  1140.     expect(errorMessageRef.current).toEqual(null);
    
  1141.   });
    
  1142. 
    
  1143.   it('successfully mounts if no error occurs', () => {
    
  1144.     const container = document.createElement('div');
    
  1145.     ReactDOM.render(
    
  1146.       <ErrorBoundary>
    
  1147.         <div>Mounted successfully.</div>
    
  1148.       </ErrorBoundary>,
    
  1149.       container,
    
  1150.     );
    
  1151.     expect(container.firstChild.textContent).toBe('Mounted successfully.');
    
  1152.     expect(log).toEqual([
    
  1153.       'ErrorBoundary constructor',
    
  1154.       'ErrorBoundary componentWillMount',
    
  1155.       'ErrorBoundary render success',
    
  1156.       'ErrorBoundary componentDidMount',
    
  1157.     ]);
    
  1158. 
    
  1159.     log.length = 0;
    
  1160.     ReactDOM.unmountComponentAtNode(container);
    
  1161.     expect(log).toEqual(['ErrorBoundary componentWillUnmount']);
    
  1162.   });
    
  1163. 
    
  1164.   it('catches if child throws in constructor during update', () => {
    
  1165.     const container = document.createElement('div');
    
  1166.     ReactDOM.render(
    
  1167.       <ErrorBoundary>
    
  1168.         <Normal />
    
  1169.       </ErrorBoundary>,
    
  1170.       container,
    
  1171.     );
    
  1172. 
    
  1173.     log.length = 0;
    
  1174.     ReactDOM.render(
    
  1175.       <ErrorBoundary>
    
  1176.         <Normal />
    
  1177.         <Normal logName="Normal2" />
    
  1178.         <BrokenConstructor />
    
  1179.       </ErrorBoundary>,
    
  1180.       container,
    
  1181.     );
    
  1182.     expect(container.textContent).toBe('Caught an error: Hello.');
    
  1183.     expect(log).toEqual([
    
  1184.       'ErrorBoundary componentWillReceiveProps',
    
  1185.       'ErrorBoundary componentWillUpdate',
    
  1186.       'ErrorBoundary render success',
    
  1187.       'Normal componentWillReceiveProps',
    
  1188.       'Normal componentWillUpdate',
    
  1189.       'Normal render',
    
  1190.       // Normal2 will attempt to mount:
    
  1191.       'Normal2 constructor',
    
  1192.       'Normal2 componentWillMount',
    
  1193.       'Normal2 render',
    
  1194.       // BrokenConstructor will abort rendering:
    
  1195.       'BrokenConstructor constructor [!]',
    
  1196.       // Finish updating with null children
    
  1197.       'Normal componentWillUnmount',
    
  1198.       'ErrorBoundary componentDidUpdate',
    
  1199.       // Handle the error
    
  1200.       'ErrorBoundary componentDidCatch',
    
  1201.       // Render the error message
    
  1202.       'ErrorBoundary componentWillUpdate',
    
  1203.       'ErrorBoundary render error',
    
  1204.       'ErrorBoundary componentDidUpdate',
    
  1205.     ]);
    
  1206. 
    
  1207.     log.length = 0;
    
  1208.     ReactDOM.unmountComponentAtNode(container);
    
  1209.     expect(log).toEqual(['ErrorBoundary componentWillUnmount']);
    
  1210.   });
    
  1211. 
    
  1212.   it('catches if child throws in componentWillMount during update', () => {
    
  1213.     const container = document.createElement('div');
    
  1214.     ReactDOM.render(
    
  1215.       <ErrorBoundary>
    
  1216.         <Normal />
    
  1217.       </ErrorBoundary>,
    
  1218.       container,
    
  1219.     );
    
  1220. 
    
  1221.     log.length = 0;
    
  1222.     ReactDOM.render(
    
  1223.       <ErrorBoundary>
    
  1224.         <Normal />
    
  1225.         <Normal logName="Normal2" />
    
  1226.         <BrokenComponentWillMount />
    
  1227.       </ErrorBoundary>,
    
  1228.       container,
    
  1229.     );
    
  1230.     expect(container.textContent).toBe('Caught an error: Hello.');
    
  1231.     expect(log).toEqual([
    
  1232.       'ErrorBoundary componentWillReceiveProps',
    
  1233.       'ErrorBoundary componentWillUpdate',
    
  1234.       'ErrorBoundary render success',
    
  1235.       'Normal componentWillReceiveProps',
    
  1236.       'Normal componentWillUpdate',
    
  1237.       'Normal render',
    
  1238.       // Normal2 will attempt to mount:
    
  1239.       'Normal2 constructor',
    
  1240.       'Normal2 componentWillMount',
    
  1241.       'Normal2 render',
    
  1242.       // BrokenComponentWillMount will abort rendering:
    
  1243.       'BrokenComponentWillMount constructor',
    
  1244.       'BrokenComponentWillMount componentWillMount [!]',
    
  1245.       // Finish updating with null children
    
  1246.       'Normal componentWillUnmount',
    
  1247.       'ErrorBoundary componentDidUpdate',
    
  1248.       // Handle the error
    
  1249.       'ErrorBoundary componentDidCatch',
    
  1250.       // Render the error message
    
  1251.       'ErrorBoundary componentWillUpdate',
    
  1252.       'ErrorBoundary render error',
    
  1253.       'ErrorBoundary componentDidUpdate',
    
  1254.     ]);
    
  1255. 
    
  1256.     log.length = 0;
    
  1257.     ReactDOM.unmountComponentAtNode(container);
    
  1258.     expect(log).toEqual(['ErrorBoundary componentWillUnmount']);
    
  1259.   });
    
  1260. 
    
  1261.   it('catches if child throws in componentWillReceiveProps during update', () => {
    
  1262.     const container = document.createElement('div');
    
  1263.     ReactDOM.render(
    
  1264.       <ErrorBoundary>
    
  1265.         <Normal />
    
  1266.         <BrokenComponentWillReceiveProps />
    
  1267.       </ErrorBoundary>,
    
  1268.       container,
    
  1269.     );
    
  1270. 
    
  1271.     log.length = 0;
    
  1272.     ReactDOM.render(
    
  1273.       <ErrorBoundary>
    
  1274.         <Normal />
    
  1275.         <BrokenComponentWillReceiveProps />
    
  1276.       </ErrorBoundary>,
    
  1277.       container,
    
  1278.     );
    
  1279.     expect(container.textContent).toBe('Caught an error: Hello.');
    
  1280.     expect(log).toEqual([
    
  1281.       'ErrorBoundary componentWillReceiveProps',
    
  1282.       'ErrorBoundary componentWillUpdate',
    
  1283.       'ErrorBoundary render success',
    
  1284.       'Normal componentWillReceiveProps',
    
  1285.       'Normal componentWillUpdate',
    
  1286.       'Normal render',
    
  1287.       // BrokenComponentWillReceiveProps will abort rendering:
    
  1288.       'BrokenComponentWillReceiveProps componentWillReceiveProps [!]',
    
  1289.       // Finish updating with null children
    
  1290.       'Normal componentWillUnmount',
    
  1291.       'BrokenComponentWillReceiveProps componentWillUnmount',
    
  1292.       'ErrorBoundary componentDidUpdate',
    
  1293.       // Handle the error
    
  1294.       'ErrorBoundary componentDidCatch',
    
  1295.       'ErrorBoundary componentWillUpdate',
    
  1296.       'ErrorBoundary render error',
    
  1297.       'ErrorBoundary componentDidUpdate',
    
  1298.     ]);
    
  1299. 
    
  1300.     log.length = 0;
    
  1301.     ReactDOM.unmountComponentAtNode(container);
    
  1302.     expect(log).toEqual(['ErrorBoundary componentWillUnmount']);
    
  1303.   });
    
  1304. 
    
  1305.   it('catches if child throws in componentWillUpdate during update', () => {
    
  1306.     const container = document.createElement('div');
    
  1307.     ReactDOM.render(
    
  1308.       <ErrorBoundary>
    
  1309.         <Normal />
    
  1310.         <BrokenComponentWillUpdate />
    
  1311.       </ErrorBoundary>,
    
  1312.       container,
    
  1313.     );
    
  1314. 
    
  1315.     log.length = 0;
    
  1316.     ReactDOM.render(
    
  1317.       <ErrorBoundary>
    
  1318.         <Normal />
    
  1319.         <BrokenComponentWillUpdate />
    
  1320.       </ErrorBoundary>,
    
  1321.       container,
    
  1322.     );
    
  1323.     expect(container.textContent).toBe('Caught an error: Hello.');
    
  1324.     expect(log).toEqual([
    
  1325.       'ErrorBoundary componentWillReceiveProps',
    
  1326.       'ErrorBoundary componentWillUpdate',
    
  1327.       'ErrorBoundary render success',
    
  1328.       'Normal componentWillReceiveProps',
    
  1329.       'Normal componentWillUpdate',
    
  1330.       'Normal render',
    
  1331.       // BrokenComponentWillUpdate will abort rendering:
    
  1332.       'BrokenComponentWillUpdate componentWillReceiveProps',
    
  1333.       'BrokenComponentWillUpdate componentWillUpdate [!]',
    
  1334.       // Finish updating with null children
    
  1335.       'Normal componentWillUnmount',
    
  1336.       'BrokenComponentWillUpdate componentWillUnmount',
    
  1337.       'ErrorBoundary componentDidUpdate',
    
  1338.       // Handle the error
    
  1339.       'ErrorBoundary componentDidCatch',
    
  1340.       'ErrorBoundary componentWillUpdate',
    
  1341.       'ErrorBoundary render error',
    
  1342.       'ErrorBoundary componentDidUpdate',
    
  1343.     ]);
    
  1344. 
    
  1345.     log.length = 0;
    
  1346.     ReactDOM.unmountComponentAtNode(container);
    
  1347.     expect(log).toEqual(['ErrorBoundary componentWillUnmount']);
    
  1348.   });
    
  1349. 
    
  1350.   it('catches if child throws in render during update', () => {
    
  1351.     const container = document.createElement('div');
    
  1352.     ReactDOM.render(
    
  1353.       <ErrorBoundary>
    
  1354.         <Normal />
    
  1355.       </ErrorBoundary>,
    
  1356.       container,
    
  1357.     );
    
  1358. 
    
  1359.     log.length = 0;
    
  1360.     ReactDOM.render(
    
  1361.       <ErrorBoundary>
    
  1362.         <Normal />
    
  1363.         <Normal logName="Normal2" />
    
  1364.         <BrokenRender />
    
  1365.       </ErrorBoundary>,
    
  1366.       container,
    
  1367.     );
    
  1368.     expect(container.textContent).toBe('Caught an error: Hello.');
    
  1369.     expect(log).toEqual([
    
  1370.       'ErrorBoundary componentWillReceiveProps',
    
  1371.       'ErrorBoundary componentWillUpdate',
    
  1372.       'ErrorBoundary render success',
    
  1373.       'Normal componentWillReceiveProps',
    
  1374.       'Normal componentWillUpdate',
    
  1375.       'Normal render',
    
  1376.       // Normal2 will attempt to mount:
    
  1377.       'Normal2 constructor',
    
  1378.       'Normal2 componentWillMount',
    
  1379.       'Normal2 render',
    
  1380.       // BrokenRender will abort rendering:
    
  1381.       'BrokenRender constructor',
    
  1382.       'BrokenRender componentWillMount',
    
  1383.       'BrokenRender render [!]',
    
  1384.       // Finish updating with null children
    
  1385.       'Normal componentWillUnmount',
    
  1386.       'ErrorBoundary componentDidUpdate',
    
  1387.       // Handle the error
    
  1388.       'ErrorBoundary componentDidCatch',
    
  1389.       'ErrorBoundary componentWillUpdate',
    
  1390.       'ErrorBoundary render error',
    
  1391.       'ErrorBoundary componentDidUpdate',
    
  1392.     ]);
    
  1393. 
    
  1394.     log.length = 0;
    
  1395.     ReactDOM.unmountComponentAtNode(container);
    
  1396.     expect(log).toEqual(['ErrorBoundary componentWillUnmount']);
    
  1397.   });
    
  1398. 
    
  1399.   it('keeps refs up-to-date during updates', () => {
    
  1400.     function child1Ref(x) {
    
  1401.       log.push('Child1 ref is set to ' + x);
    
  1402.     }
    
  1403.     function child2Ref(x) {
    
  1404.       log.push('Child2 ref is set to ' + x);
    
  1405.     }
    
  1406.     function errorMessageRef(x) {
    
  1407.       log.push('Error message ref is set to ' + x);
    
  1408.     }
    
  1409. 
    
  1410.     const container = document.createElement('div');
    
  1411.     ReactDOM.render(
    
  1412.       <ErrorBoundary errorMessageRef={errorMessageRef}>
    
  1413.         <div ref={child1Ref} />
    
  1414.       </ErrorBoundary>,
    
  1415.       container,
    
  1416.     );
    
  1417.     expect(log).toEqual([
    
  1418.       'ErrorBoundary constructor',
    
  1419.       'ErrorBoundary componentWillMount',
    
  1420.       'ErrorBoundary render success',
    
  1421.       'Child1 ref is set to [object HTMLDivElement]',
    
  1422.       'ErrorBoundary componentDidMount',
    
  1423.     ]);
    
  1424. 
    
  1425.     log.length = 0;
    
  1426.     ReactDOM.render(
    
  1427.       <ErrorBoundary errorMessageRef={errorMessageRef}>
    
  1428.         <div ref={child1Ref} />
    
  1429.         <div ref={child2Ref} />
    
  1430.         <BrokenRender />
    
  1431.       </ErrorBoundary>,
    
  1432.       container,
    
  1433.     );
    
  1434.     expect(container.textContent).toBe('Caught an error: Hello.');
    
  1435.     expect(log).toEqual([
    
  1436.       'ErrorBoundary componentWillReceiveProps',
    
  1437.       'ErrorBoundary componentWillUpdate',
    
  1438.       'ErrorBoundary render success',
    
  1439.       // BrokenRender will abort rendering:
    
  1440.       'BrokenRender constructor',
    
  1441.       'BrokenRender componentWillMount',
    
  1442.       'BrokenRender render [!]',
    
  1443.       // Finish updating with null children
    
  1444.       'Child1 ref is set to null',
    
  1445.       'ErrorBoundary componentDidUpdate',
    
  1446.       // Handle the error
    
  1447.       'ErrorBoundary componentDidCatch',
    
  1448.       'ErrorBoundary componentWillUpdate',
    
  1449.       'ErrorBoundary render error',
    
  1450.       'Error message ref is set to [object HTMLDivElement]',
    
  1451.       // Child2 ref is never set because its mounting aborted
    
  1452.       'ErrorBoundary componentDidUpdate',
    
  1453.     ]);
    
  1454. 
    
  1455.     log.length = 0;
    
  1456.     ReactDOM.unmountComponentAtNode(container);
    
  1457.     expect(log).toEqual([
    
  1458.       'ErrorBoundary componentWillUnmount',
    
  1459.       'Error message ref is set to null',
    
  1460.     ]);
    
  1461.   });
    
  1462. 
    
  1463.   it('recovers from componentWillUnmount errors on update', () => {
    
  1464.     const container = document.createElement('div');
    
  1465.     ReactDOM.render(
    
  1466.       <ErrorBoundary>
    
  1467.         <BrokenComponentWillUnmount />
    
  1468.         <BrokenComponentWillUnmount />
    
  1469.         <Normal />
    
  1470.       </ErrorBoundary>,
    
  1471.       container,
    
  1472.     );
    
  1473. 
    
  1474.     log.length = 0;
    
  1475.     ReactDOM.render(
    
  1476.       <ErrorBoundary>
    
  1477.         <BrokenComponentWillUnmount />
    
  1478.       </ErrorBoundary>,
    
  1479.       container,
    
  1480.     );
    
  1481.     expect(container.textContent).toBe('Caught an error: Hello.');
    
  1482.     expect(log).toEqual([
    
  1483.       'ErrorBoundary componentWillReceiveProps',
    
  1484.       'ErrorBoundary componentWillUpdate',
    
  1485.       'ErrorBoundary render success',
    
  1486.       // Update existing child:
    
  1487.       'BrokenComponentWillUnmount componentWillReceiveProps',
    
  1488.       'BrokenComponentWillUnmount componentWillUpdate',
    
  1489.       'BrokenComponentWillUnmount render',
    
  1490.       // Unmounting throws:
    
  1491.       'BrokenComponentWillUnmount componentWillUnmount [!]',
    
  1492.       // Fiber proceeds with lifecycles despite errors
    
  1493.       'Normal componentWillUnmount',
    
  1494.       // The components have updated in this phase
    
  1495.       'BrokenComponentWillUnmount componentDidUpdate',
    
  1496.       'ErrorBoundary componentDidUpdate',
    
  1497.       // Now that commit phase is done, Fiber unmounts the boundary's children
    
  1498.       'BrokenComponentWillUnmount componentWillUnmount [!]',
    
  1499.       'ErrorBoundary componentDidCatch',
    
  1500.       // The initial render was aborted, so
    
  1501.       // Fiber retries from the root.
    
  1502.       'ErrorBoundary componentWillUpdate',
    
  1503.       'ErrorBoundary componentDidUpdate',
    
  1504.       // The second willUnmount error should be captured and logged, too.
    
  1505.       'ErrorBoundary componentDidCatch',
    
  1506.       'ErrorBoundary componentWillUpdate',
    
  1507.       // Render an error now (stack will do it later)
    
  1508.       'ErrorBoundary render error',
    
  1509.       // Attempt to unmount previous child:
    
  1510.       // Done
    
  1511.       'ErrorBoundary componentDidUpdate',
    
  1512.     ]);
    
  1513. 
    
  1514.     log.length = 0;
    
  1515.     ReactDOM.unmountComponentAtNode(container);
    
  1516.     expect(log).toEqual(['ErrorBoundary componentWillUnmount']);
    
  1517.   });
    
  1518. 
    
  1519.   it('recovers from nested componentWillUnmount errors on update', () => {
    
  1520.     const container = document.createElement('div');
    
  1521.     ReactDOM.render(
    
  1522.       <ErrorBoundary>
    
  1523.         <Normal>
    
  1524.           <BrokenComponentWillUnmount />
    
  1525.         </Normal>
    
  1526.         <BrokenComponentWillUnmount />
    
  1527.       </ErrorBoundary>,
    
  1528.       container,
    
  1529.     );
    
  1530. 
    
  1531.     log.length = 0;
    
  1532.     ReactDOM.render(
    
  1533.       <ErrorBoundary>
    
  1534.         <Normal>
    
  1535.           <BrokenComponentWillUnmount />
    
  1536.         </Normal>
    
  1537.       </ErrorBoundary>,
    
  1538.       container,
    
  1539.     );
    
  1540.     expect(container.textContent).toBe('Caught an error: Hello.');
    
  1541.     expect(log).toEqual([
    
  1542.       'ErrorBoundary componentWillReceiveProps',
    
  1543.       'ErrorBoundary componentWillUpdate',
    
  1544.       'ErrorBoundary render success',
    
  1545.       // Update existing children:
    
  1546.       'Normal componentWillReceiveProps',
    
  1547.       'Normal componentWillUpdate',
    
  1548.       'Normal render',
    
  1549.       'BrokenComponentWillUnmount componentWillReceiveProps',
    
  1550.       'BrokenComponentWillUnmount componentWillUpdate',
    
  1551.       'BrokenComponentWillUnmount render',
    
  1552.       // Unmounting throws:
    
  1553.       'BrokenComponentWillUnmount componentWillUnmount [!]',
    
  1554.       // Fiber proceeds with lifecycles despite errors
    
  1555.       'BrokenComponentWillUnmount componentDidUpdate',
    
  1556.       'Normal componentDidUpdate',
    
  1557.       'ErrorBoundary componentDidUpdate',
    
  1558.       'Normal componentWillUnmount',
    
  1559.       'BrokenComponentWillUnmount componentWillUnmount [!]',
    
  1560.       // Now that commit phase is done, Fiber handles errors
    
  1561.       'ErrorBoundary componentDidCatch',
    
  1562.       // The initial render was aborted, so
    
  1563.       // Fiber retries from the root.
    
  1564.       'ErrorBoundary componentWillUpdate',
    
  1565.       'ErrorBoundary componentDidUpdate',
    
  1566.       // The second willUnmount error should be captured and logged, too.
    
  1567.       'ErrorBoundary componentDidCatch',
    
  1568.       'ErrorBoundary componentWillUpdate',
    
  1569.       // Render an error now (stack will do it later)
    
  1570.       'ErrorBoundary render error',
    
  1571.       // Done
    
  1572.       'ErrorBoundary componentDidUpdate',
    
  1573.     ]);
    
  1574. 
    
  1575.     log.length = 0;
    
  1576.     ReactDOM.unmountComponentAtNode(container);
    
  1577.     expect(log).toEqual(['ErrorBoundary componentWillUnmount']);
    
  1578.   });
    
  1579. 
    
  1580.   it('picks the right boundary when handling unmounting errors', () => {
    
  1581.     function renderInnerError(error) {
    
  1582.       return <div>Caught an inner error: {error.message}.</div>;
    
  1583.     }
    
  1584.     function renderOuterError(error) {
    
  1585.       return <div>Caught an outer error: {error.message}.</div>;
    
  1586.     }
    
  1587. 
    
  1588.     const container = document.createElement('div');
    
  1589.     ReactDOM.render(
    
  1590.       <ErrorBoundary
    
  1591.         logName="OuterErrorBoundary"
    
  1592.         renderError={renderOuterError}>
    
  1593.         <ErrorBoundary
    
  1594.           logName="InnerErrorBoundary"
    
  1595.           renderError={renderInnerError}>
    
  1596.           <BrokenComponentWillUnmount />
    
  1597.         </ErrorBoundary>
    
  1598.       </ErrorBoundary>,
    
  1599.       container,
    
  1600.     );
    
  1601. 
    
  1602.     log.length = 0;
    
  1603.     ReactDOM.render(
    
  1604.       <ErrorBoundary
    
  1605.         logName="OuterErrorBoundary"
    
  1606.         renderError={renderOuterError}>
    
  1607.         <ErrorBoundary
    
  1608.           logName="InnerErrorBoundary"
    
  1609.           renderError={renderInnerError}
    
  1610.         />
    
  1611.       </ErrorBoundary>,
    
  1612.       container,
    
  1613.     );
    
  1614.     expect(container.textContent).toBe('Caught an inner error: Hello.');
    
  1615.     expect(log).toEqual([
    
  1616.       // Update outer boundary
    
  1617.       'OuterErrorBoundary componentWillReceiveProps',
    
  1618.       'OuterErrorBoundary componentWillUpdate',
    
  1619.       'OuterErrorBoundary render success',
    
  1620.       // Update inner boundary
    
  1621.       'InnerErrorBoundary componentWillReceiveProps',
    
  1622.       'InnerErrorBoundary componentWillUpdate',
    
  1623.       'InnerErrorBoundary render success',
    
  1624.       // Try unmounting child
    
  1625.       'BrokenComponentWillUnmount componentWillUnmount [!]',
    
  1626.       // Fiber proceeds with lifecycles despite errors
    
  1627.       // Inner and outer boundaries have updated in this phase
    
  1628.       'InnerErrorBoundary componentDidUpdate',
    
  1629.       'OuterErrorBoundary componentDidUpdate',
    
  1630.       // Now that commit phase is done, Fiber handles errors
    
  1631.       // Only inner boundary receives the error:
    
  1632.       'InnerErrorBoundary componentDidCatch',
    
  1633.       'InnerErrorBoundary componentWillUpdate',
    
  1634.       // Render an error now
    
  1635.       'InnerErrorBoundary render error',
    
  1636.       // In Fiber, this was a local update to the
    
  1637.       // inner boundary so only its hook fires
    
  1638.       'InnerErrorBoundary componentDidUpdate',
    
  1639.     ]);
    
  1640. 
    
  1641.     log.length = 0;
    
  1642.     ReactDOM.unmountComponentAtNode(container);
    
  1643.     expect(log).toEqual([
    
  1644.       'OuterErrorBoundary componentWillUnmount',
    
  1645.       'InnerErrorBoundary componentWillUnmount',
    
  1646.     ]);
    
  1647.   });
    
  1648. 
    
  1649.   it('can recover from error state', () => {
    
  1650.     const container = document.createElement('div');
    
  1651.     ReactDOM.render(
    
  1652.       <ErrorBoundary>
    
  1653.         <BrokenRender />
    
  1654.       </ErrorBoundary>,
    
  1655.       container,
    
  1656.     );
    
  1657. 
    
  1658.     ReactDOM.render(
    
  1659.       <ErrorBoundary>
    
  1660.         <Normal />
    
  1661.       </ErrorBoundary>,
    
  1662.       container,
    
  1663.     );
    
  1664.     // Error boundary doesn't retry by itself:
    
  1665.     expect(container.textContent).toBe('Caught an error: Hello.');
    
  1666. 
    
  1667.     // Force the success path:
    
  1668.     log.length = 0;
    
  1669.     ReactDOM.render(
    
  1670.       <ErrorBoundary forceRetry={true}>
    
  1671.         <Normal />
    
  1672.       </ErrorBoundary>,
    
  1673.       container,
    
  1674.     );
    
  1675.     expect(container.textContent).not.toContain('Caught an error');
    
  1676.     expect(log).toEqual([
    
  1677.       'ErrorBoundary componentWillReceiveProps',
    
  1678.       'ErrorBoundary componentWillUpdate',
    
  1679.       'ErrorBoundary render success',
    
  1680.       // Mount children:
    
  1681.       'Normal constructor',
    
  1682.       'Normal componentWillMount',
    
  1683.       'Normal render',
    
  1684.       // Finalize updates:
    
  1685.       'Normal componentDidMount',
    
  1686.       'ErrorBoundary componentDidUpdate',
    
  1687.     ]);
    
  1688. 
    
  1689.     log.length = 0;
    
  1690.     ReactDOM.unmountComponentAtNode(container);
    
  1691.     expect(log).toEqual([
    
  1692.       'ErrorBoundary componentWillUnmount',
    
  1693.       'Normal componentWillUnmount',
    
  1694.     ]);
    
  1695.   });
    
  1696. 
    
  1697.   it('can update multiple times in error state', () => {
    
  1698.     const container = document.createElement('div');
    
  1699.     ReactDOM.render(
    
  1700.       <ErrorBoundary>
    
  1701.         <BrokenRender />
    
  1702.       </ErrorBoundary>,
    
  1703.       container,
    
  1704.     );
    
  1705.     expect(container.textContent).toBe('Caught an error: Hello.');
    
  1706. 
    
  1707.     ReactDOM.render(
    
  1708.       <ErrorBoundary>
    
  1709.         <BrokenRender />
    
  1710.       </ErrorBoundary>,
    
  1711.       container,
    
  1712.     );
    
  1713.     expect(container.textContent).toBe('Caught an error: Hello.');
    
  1714. 
    
  1715.     ReactDOM.render(<div>Other screen</div>, container);
    
  1716.     expect(container.textContent).toBe('Other screen');
    
  1717. 
    
  1718.     ReactDOM.unmountComponentAtNode(container);
    
  1719.   });
    
  1720. 
    
  1721.   it("doesn't get into inconsistent state during removals", () => {
    
  1722.     const container = document.createElement('div');
    
  1723.     ReactDOM.render(
    
  1724.       <ErrorBoundary>
    
  1725.         <Normal />
    
  1726.         <BrokenComponentWillUnmount />
    
  1727.         <Normal />
    
  1728.       </ErrorBoundary>,
    
  1729.       container,
    
  1730.     );
    
  1731. 
    
  1732.     ReactDOM.render(<ErrorBoundary />, container);
    
  1733.     expect(container.textContent).toBe('Caught an error: Hello.');
    
  1734. 
    
  1735.     log.length = 0;
    
  1736.     ReactDOM.unmountComponentAtNode(container);
    
  1737.     expect(log).toEqual(['ErrorBoundary componentWillUnmount']);
    
  1738.   });
    
  1739. 
    
  1740.   it("doesn't get into inconsistent state during additions", () => {
    
  1741.     const container = document.createElement('div');
    
  1742.     ReactDOM.render(<ErrorBoundary />, container);
    
  1743.     ReactDOM.render(
    
  1744.       <ErrorBoundary>
    
  1745.         <Normal />
    
  1746.         <BrokenRender />
    
  1747.         <Normal />
    
  1748.       </ErrorBoundary>,
    
  1749.       container,
    
  1750.     );
    
  1751.     expect(container.textContent).toBe('Caught an error: Hello.');
    
  1752. 
    
  1753.     log.length = 0;
    
  1754.     ReactDOM.unmountComponentAtNode(container);
    
  1755.     expect(log).toEqual(['ErrorBoundary componentWillUnmount']);
    
  1756.   });
    
  1757. 
    
  1758.   it("doesn't get into inconsistent state during reorders", () => {
    
  1759.     function getAMixOfNormalAndBrokenRenderElements() {
    
  1760.       const elements = [];
    
  1761.       for (let i = 0; i < 100; i++) {
    
  1762.         elements.push(<Normal key={i} />);
    
  1763.       }
    
  1764.       elements.push(<MaybeBrokenRender key={100} />);
    
  1765. 
    
  1766.       let currentIndex = elements.length;
    
  1767.       while (0 !== currentIndex) {
    
  1768.         const randomIndex = Math.floor(Math.random() * currentIndex);
    
  1769.         currentIndex -= 1;
    
  1770.         const temporaryValue = elements[currentIndex];
    
  1771.         elements[currentIndex] = elements[randomIndex];
    
  1772.         elements[randomIndex] = temporaryValue;
    
  1773.       }
    
  1774.       return elements;
    
  1775.     }
    
  1776. 
    
  1777.     class MaybeBrokenRender extends React.Component {
    
  1778.       render() {
    
  1779.         if (fail) {
    
  1780.           throw new Error('Hello');
    
  1781.         }
    
  1782.         return <div>{this.props.children}</div>;
    
  1783.       }
    
  1784.     }
    
  1785. 
    
  1786.     let fail = false;
    
  1787.     const container = document.createElement('div');
    
  1788.     ReactDOM.render(
    
  1789.       <ErrorBoundary>{getAMixOfNormalAndBrokenRenderElements()}</ErrorBoundary>,
    
  1790.       container,
    
  1791.     );
    
  1792.     expect(container.textContent).not.toContain('Caught an error');
    
  1793. 
    
  1794.     fail = true;
    
  1795.     ReactDOM.render(
    
  1796.       <ErrorBoundary>{getAMixOfNormalAndBrokenRenderElements()}</ErrorBoundary>,
    
  1797.       container,
    
  1798.     );
    
  1799.     expect(container.textContent).toBe('Caught an error: Hello.');
    
  1800. 
    
  1801.     log.length = 0;
    
  1802.     ReactDOM.unmountComponentAtNode(container);
    
  1803.     expect(log).toEqual(['ErrorBoundary componentWillUnmount']);
    
  1804.   });
    
  1805. 
    
  1806.   it('catches errors originating downstream', () => {
    
  1807.     let fail = false;
    
  1808.     class Stateful extends React.Component {
    
  1809.       state = {shouldThrow: false};
    
  1810. 
    
  1811.       render() {
    
  1812.         if (fail) {
    
  1813.           log.push('Stateful render [!]');
    
  1814.           throw new Error('Hello');
    
  1815.         }
    
  1816.         return <div>{this.props.children}</div>;
    
  1817.       }
    
  1818.     }
    
  1819. 
    
  1820.     let statefulInst;
    
  1821.     const container = document.createElement('div');
    
  1822.     ReactDOM.render(
    
  1823.       <ErrorBoundary>
    
  1824.         <Stateful ref={inst => (statefulInst = inst)} />
    
  1825.       </ErrorBoundary>,
    
  1826.       container,
    
  1827.     );
    
  1828. 
    
  1829.     log.length = 0;
    
  1830.     expect(() => {
    
  1831.       fail = true;
    
  1832.       statefulInst.forceUpdate();
    
  1833.     }).not.toThrow();
    
  1834. 
    
  1835.     expect(log).toEqual([
    
  1836.       'Stateful render [!]',
    
  1837.       'ErrorBoundary componentDidCatch',
    
  1838.       'ErrorBoundary componentWillUpdate',
    
  1839.       'ErrorBoundary render error',
    
  1840.       'ErrorBoundary componentDidUpdate',
    
  1841.     ]);
    
  1842. 
    
  1843.     log.length = 0;
    
  1844.     ReactDOM.unmountComponentAtNode(container);
    
  1845.     expect(log).toEqual(['ErrorBoundary componentWillUnmount']);
    
  1846.   });
    
  1847. 
    
  1848.   it('catches errors in componentDidMount', () => {
    
  1849.     const container = document.createElement('div');
    
  1850.     ReactDOM.render(
    
  1851.       <ErrorBoundary>
    
  1852.         <BrokenComponentWillUnmount>
    
  1853.           <Normal />
    
  1854.         </BrokenComponentWillUnmount>
    
  1855.         <BrokenComponentDidMount />
    
  1856.         <Normal logName="LastChild" />
    
  1857.       </ErrorBoundary>,
    
  1858.       container,
    
  1859.     );
    
  1860.     expect(log).toEqual([
    
  1861.       'ErrorBoundary constructor',
    
  1862.       'ErrorBoundary componentWillMount',
    
  1863.       'ErrorBoundary render success',
    
  1864.       'BrokenComponentWillUnmount constructor',
    
  1865.       'BrokenComponentWillUnmount componentWillMount',
    
  1866.       'BrokenComponentWillUnmount render',
    
  1867.       'Normal constructor',
    
  1868.       'Normal componentWillMount',
    
  1869.       'Normal render',
    
  1870.       'BrokenComponentDidMount constructor',
    
  1871.       'BrokenComponentDidMount componentWillMount',
    
  1872.       'BrokenComponentDidMount render',
    
  1873.       'LastChild constructor',
    
  1874.       'LastChild componentWillMount',
    
  1875.       'LastChild render',
    
  1876.       // Start flushing didMount queue
    
  1877.       'Normal componentDidMount',
    
  1878.       'BrokenComponentWillUnmount componentDidMount',
    
  1879.       'BrokenComponentDidMount componentDidMount [!]',
    
  1880.       // Continue despite the error
    
  1881.       'LastChild componentDidMount',
    
  1882.       'ErrorBoundary componentDidMount',
    
  1883.       // Now we are ready to handle the error
    
  1884.       // Safely unmount every child
    
  1885.       'BrokenComponentWillUnmount componentWillUnmount [!]',
    
  1886.       // Continue unmounting safely despite any errors
    
  1887.       'Normal componentWillUnmount',
    
  1888.       'BrokenComponentDidMount componentWillUnmount',
    
  1889.       'LastChild componentWillUnmount',
    
  1890.       // Handle the error
    
  1891.       'ErrorBoundary componentDidCatch',
    
  1892.       'ErrorBoundary componentWillUpdate',
    
  1893.       // The willUnmount error should be captured and logged, too.
    
  1894.       'ErrorBoundary componentDidUpdate',
    
  1895.       'ErrorBoundary componentDidCatch',
    
  1896.       'ErrorBoundary componentWillUpdate',
    
  1897.       'ErrorBoundary render error',
    
  1898.       // The update has finished
    
  1899.       'ErrorBoundary componentDidUpdate',
    
  1900.     ]);
    
  1901. 
    
  1902.     log.length = 0;
    
  1903.     ReactDOM.unmountComponentAtNode(container);
    
  1904.     expect(log).toEqual(['ErrorBoundary componentWillUnmount']);
    
  1905.   });
    
  1906. 
    
  1907.   it('catches errors in componentDidUpdate', () => {
    
  1908.     const container = document.createElement('div');
    
  1909.     ReactDOM.render(
    
  1910.       <ErrorBoundary>
    
  1911.         <BrokenComponentDidUpdate />
    
  1912.       </ErrorBoundary>,
    
  1913.       container,
    
  1914.     );
    
  1915. 
    
  1916.     log.length = 0;
    
  1917.     ReactDOM.render(
    
  1918.       <ErrorBoundary>
    
  1919.         <BrokenComponentDidUpdate />
    
  1920.       </ErrorBoundary>,
    
  1921.       container,
    
  1922.     );
    
  1923.     expect(log).toEqual([
    
  1924.       'ErrorBoundary componentWillReceiveProps',
    
  1925.       'ErrorBoundary componentWillUpdate',
    
  1926.       'ErrorBoundary render success',
    
  1927.       'BrokenComponentDidUpdate componentWillReceiveProps',
    
  1928.       'BrokenComponentDidUpdate componentWillUpdate',
    
  1929.       'BrokenComponentDidUpdate render',
    
  1930.       // All lifecycles run
    
  1931.       'BrokenComponentDidUpdate componentDidUpdate [!]',
    
  1932.       'ErrorBoundary componentDidUpdate',
    
  1933.       'BrokenComponentDidUpdate componentWillUnmount',
    
  1934.       // Then, error is handled
    
  1935.       'ErrorBoundary componentDidCatch',
    
  1936.       'ErrorBoundary componentWillUpdate',
    
  1937.       'ErrorBoundary render error',
    
  1938.       'ErrorBoundary componentDidUpdate',
    
  1939.     ]);
    
  1940. 
    
  1941.     log.length = 0;
    
  1942.     ReactDOM.unmountComponentAtNode(container);
    
  1943.     expect(log).toEqual(['ErrorBoundary componentWillUnmount']);
    
  1944.   });
    
  1945. 
    
  1946.   it('propagates errors inside boundary during componentDidMount', () => {
    
  1947.     const container = document.createElement('div');
    
  1948.     ReactDOM.render(
    
  1949.       <ErrorBoundary>
    
  1950.         <BrokenComponentDidMountErrorBoundary
    
  1951.           renderError={error => (
    
  1952.             <div>We should never catch our own error: {error.message}.</div>
    
  1953.           )}
    
  1954.         />
    
  1955.       </ErrorBoundary>,
    
  1956.       container,
    
  1957.     );
    
  1958.     expect(container.firstChild.textContent).toBe('Caught an error: Hello.');
    
  1959.     expect(log).toEqual([
    
  1960.       'ErrorBoundary constructor',
    
  1961.       'ErrorBoundary componentWillMount',
    
  1962.       'ErrorBoundary render success',
    
  1963.       'BrokenComponentDidMountErrorBoundary constructor',
    
  1964.       'BrokenComponentDidMountErrorBoundary componentWillMount',
    
  1965.       'BrokenComponentDidMountErrorBoundary render success',
    
  1966.       'BrokenComponentDidMountErrorBoundary componentDidMount [!]',
    
  1967.       // Fiber proceeds with the hooks
    
  1968.       'ErrorBoundary componentDidMount',
    
  1969.       'BrokenComponentDidMountErrorBoundary componentWillUnmount',
    
  1970.       // The error propagates to the higher boundary
    
  1971.       'ErrorBoundary componentDidCatch',
    
  1972.       // Fiber retries from the root
    
  1973.       'ErrorBoundary componentWillUpdate',
    
  1974.       'ErrorBoundary render error',
    
  1975.       'ErrorBoundary componentDidUpdate',
    
  1976.     ]);
    
  1977. 
    
  1978.     log.length = 0;
    
  1979.     ReactDOM.unmountComponentAtNode(container);
    
  1980.     expect(log).toEqual(['ErrorBoundary componentWillUnmount']);
    
  1981.   });
    
  1982. 
    
  1983.   it('calls componentDidCatch for each error that is captured', () => {
    
  1984.     function renderUnmountError(error) {
    
  1985.       return <div>Caught an unmounting error: {error.message}.</div>;
    
  1986.     }
    
  1987.     function renderUpdateError(error) {
    
  1988.       return <div>Caught an updating error: {error.message}.</div>;
    
  1989.     }
    
  1990. 
    
  1991.     const container = document.createElement('div');
    
  1992.     ReactDOM.render(
    
  1993.       <ErrorBoundary logName="OuterErrorBoundary">
    
  1994.         <ErrorBoundary
    
  1995.           logName="InnerUnmountBoundary"
    
  1996.           renderError={renderUnmountError}>
    
  1997.           <BrokenComponentWillUnmount errorText="E1" />
    
  1998.           <BrokenComponentWillUnmount errorText="E2" />
    
  1999.         </ErrorBoundary>
    
  2000.         <ErrorBoundary
    
  2001.           logName="InnerUpdateBoundary"
    
  2002.           renderError={renderUpdateError}>
    
  2003.           <BrokenComponentDidUpdate errorText="E3" />
    
  2004.           <BrokenComponentDidUpdate errorText="E4" />
    
  2005.         </ErrorBoundary>
    
  2006.       </ErrorBoundary>,
    
  2007.       container,
    
  2008.     );
    
  2009. 
    
  2010.     log.length = 0;
    
  2011.     ReactDOM.render(
    
  2012.       <ErrorBoundary logName="OuterErrorBoundary">
    
  2013.         <ErrorBoundary
    
  2014.           logName="InnerUnmountBoundary"
    
  2015.           renderError={renderUnmountError}
    
  2016.         />
    
  2017.         <ErrorBoundary
    
  2018.           logName="InnerUpdateBoundary"
    
  2019.           renderError={renderUpdateError}>
    
  2020.           <BrokenComponentDidUpdate errorText="E3" />
    
  2021.           <BrokenComponentDidUpdate errorText="E4" />
    
  2022.         </ErrorBoundary>
    
  2023.       </ErrorBoundary>,
    
  2024.       container,
    
  2025.     );
    
  2026. 
    
  2027.     expect(container.firstChild.textContent).toBe(
    
  2028.       'Caught an unmounting error: E2.' + 'Caught an updating error: E4.',
    
  2029.     );
    
  2030.     expect(log).toEqual([
    
  2031.       // Begin update phase
    
  2032.       'OuterErrorBoundary componentWillReceiveProps',
    
  2033.       'OuterErrorBoundary componentWillUpdate',
    
  2034.       'OuterErrorBoundary render success',
    
  2035.       'InnerUnmountBoundary componentWillReceiveProps',
    
  2036.       'InnerUnmountBoundary componentWillUpdate',
    
  2037.       'InnerUnmountBoundary render success',
    
  2038.       'InnerUpdateBoundary componentWillReceiveProps',
    
  2039.       'InnerUpdateBoundary componentWillUpdate',
    
  2040.       'InnerUpdateBoundary render success',
    
  2041.       // First come the updates
    
  2042.       'BrokenComponentDidUpdate componentWillReceiveProps',
    
  2043.       'BrokenComponentDidUpdate componentWillUpdate',
    
  2044.       'BrokenComponentDidUpdate render',
    
  2045.       'BrokenComponentDidUpdate componentWillReceiveProps',
    
  2046.       'BrokenComponentDidUpdate componentWillUpdate',
    
  2047.       'BrokenComponentDidUpdate render',
    
  2048.       // We're in commit phase now, deleting
    
  2049.       'BrokenComponentWillUnmount componentWillUnmount [!]',
    
  2050.       'BrokenComponentWillUnmount componentWillUnmount [!]',
    
  2051.       // Continue despite errors, handle them after commit is done
    
  2052.       'InnerUnmountBoundary componentDidUpdate',
    
  2053.       // We're still in commit phase, now calling update lifecycles
    
  2054.       'BrokenComponentDidUpdate componentDidUpdate [!]',
    
  2055.       // Again, continue despite errors, we'll handle them later
    
  2056.       'BrokenComponentDidUpdate componentDidUpdate [!]',
    
  2057.       'InnerUpdateBoundary componentDidUpdate',
    
  2058.       'OuterErrorBoundary componentDidUpdate',
    
  2059.       // After the commit phase, attempt to recover from any errors that
    
  2060.       // were captured
    
  2061.       'BrokenComponentDidUpdate componentWillUnmount',
    
  2062.       'BrokenComponentDidUpdate componentWillUnmount',
    
  2063.       'InnerUnmountBoundary componentDidCatch',
    
  2064.       'InnerUnmountBoundary componentDidCatch',
    
  2065.       'InnerUpdateBoundary componentDidCatch',
    
  2066.       'InnerUpdateBoundary componentDidCatch',
    
  2067.       'InnerUnmountBoundary componentWillUpdate',
    
  2068.       'InnerUnmountBoundary render error',
    
  2069.       'InnerUpdateBoundary componentWillUpdate',
    
  2070.       'InnerUpdateBoundary render error',
    
  2071.       'InnerUnmountBoundary componentDidUpdate',
    
  2072.       'InnerUpdateBoundary componentDidUpdate',
    
  2073.     ]);
    
  2074. 
    
  2075.     log.length = 0;
    
  2076.     ReactDOM.unmountComponentAtNode(container);
    
  2077.     expect(log).toEqual([
    
  2078.       'OuterErrorBoundary componentWillUnmount',
    
  2079.       'InnerUnmountBoundary componentWillUnmount',
    
  2080.       'InnerUpdateBoundary componentWillUnmount',
    
  2081.     ]);
    
  2082.   });
    
  2083. 
    
  2084.   it('discards a bad root if the root component fails', () => {
    
  2085.     const X = null;
    
  2086.     const Y = undefined;
    
  2087.     let err1;
    
  2088.     let err2;
    
  2089. 
    
  2090.     try {
    
  2091.       const container = document.createElement('div');
    
  2092.       expect(() => ReactDOM.render(<X />, container)).toErrorDev(
    
  2093.         'React.createElement: type is invalid -- expected a string ' +
    
  2094.           '(for built-in components) or a class/function ' +
    
  2095.           '(for composite components) but got: null.',
    
  2096.       );
    
  2097.     } catch (err) {
    
  2098.       err1 = err;
    
  2099.     }
    
  2100.     try {
    
  2101.       const container = document.createElement('div');
    
  2102.       expect(() => ReactDOM.render(<Y />, container)).toErrorDev(
    
  2103.         'React.createElement: type is invalid -- expected a string ' +
    
  2104.           '(for built-in components) or a class/function ' +
    
  2105.           '(for composite components) but got: undefined.',
    
  2106.       );
    
  2107.     } catch (err) {
    
  2108.       err2 = err;
    
  2109.     }
    
  2110. 
    
  2111.     expect(err1.message).toMatch(/got: null/);
    
  2112.     expect(err2.message).toMatch(/got: undefined/);
    
  2113.   });
    
  2114. 
    
  2115.   it('renders empty output if error boundary does not handle the error', () => {
    
  2116.     const container = document.createElement('div');
    
  2117.     expect(() => {
    
  2118.       ReactDOM.render(
    
  2119.         <div>
    
  2120.           Sibling
    
  2121.           <NoopErrorBoundary>
    
  2122.             <BrokenRender />
    
  2123.           </NoopErrorBoundary>
    
  2124.         </div>,
    
  2125.         container,
    
  2126.       );
    
  2127.     }).toErrorDev(
    
  2128.       'ErrorBoundary: Error boundaries should implement getDerivedStateFromError()',
    
  2129.     );
    
  2130.     expect(container.firstChild.textContent).toBe('Sibling');
    
  2131.     expect(log).toEqual([
    
  2132.       'NoopErrorBoundary constructor',
    
  2133.       'NoopErrorBoundary componentWillMount',
    
  2134.       'NoopErrorBoundary render',
    
  2135.       'BrokenRender constructor',
    
  2136.       'BrokenRender componentWillMount',
    
  2137.       'BrokenRender render [!]',
    
  2138.       // In Fiber, noop error boundaries render null
    
  2139.       'NoopErrorBoundary componentDidMount',
    
  2140.       'NoopErrorBoundary componentDidCatch',
    
  2141.       // Nothing happens.
    
  2142.     ]);
    
  2143. 
    
  2144.     log.length = 0;
    
  2145.     ReactDOM.unmountComponentAtNode(container);
    
  2146.     expect(log).toEqual(['NoopErrorBoundary componentWillUnmount']);
    
  2147.   });
    
  2148. 
    
  2149.   it('passes first error when two errors happen in commit', () => {
    
  2150.     const errors = [];
    
  2151.     let caughtError;
    
  2152.     class Parent extends React.Component {
    
  2153.       render() {
    
  2154.         return <Child />;
    
  2155.       }
    
  2156.       componentDidMount() {
    
  2157.         errors.push('parent sad');
    
  2158.         throw new Error('parent sad');
    
  2159.       }
    
  2160.     }
    
  2161.     class Child extends React.Component {
    
  2162.       render() {
    
  2163.         return <div />;
    
  2164.       }
    
  2165.       componentDidMount() {
    
  2166.         errors.push('child sad');
    
  2167.         throw new Error('child sad');
    
  2168.       }
    
  2169.     }
    
  2170. 
    
  2171.     const container = document.createElement('div');
    
  2172.     try {
    
  2173.       // Here, we test the behavior where there is no error boundary and we
    
  2174.       // delegate to the host root.
    
  2175.       ReactDOM.render(<Parent />, container);
    
  2176.     } catch (e) {
    
  2177.       if (e.message !== 'parent sad' && e.message !== 'child sad') {
    
  2178.         throw e;
    
  2179.       }
    
  2180.       caughtError = e;
    
  2181.     }
    
  2182. 
    
  2183.     expect(errors).toEqual(['child sad', 'parent sad']);
    
  2184.     // Error should be the first thrown
    
  2185.     expect(caughtError.message).toBe('child sad');
    
  2186.   });
    
  2187. 
    
  2188.   it('propagates uncaught error inside unbatched initial mount', () => {
    
  2189.     function Foo() {
    
  2190.       throw new Error('foo error');
    
  2191.     }
    
  2192.     const container = document.createElement('div');
    
  2193.     expect(() => {
    
  2194.       ReactDOM.unstable_batchedUpdates(() => {
    
  2195.         ReactDOM.render(<Foo />, container);
    
  2196.       });
    
  2197.     }).toThrow('foo error');
    
  2198.   });
    
  2199. 
    
  2200.   it('handles errors that occur in before-mutation commit hook', () => {
    
  2201.     const errors = [];
    
  2202.     let caughtError;
    
  2203.     class Parent extends React.Component {
    
  2204.       getSnapshotBeforeUpdate() {
    
  2205.         errors.push('parent sad');
    
  2206.         throw new Error('parent sad');
    
  2207.       }
    
  2208.       componentDidUpdate() {}
    
  2209.       render() {
    
  2210.         return <Child {...this.props} />;
    
  2211.       }
    
  2212.     }
    
  2213.     class Child extends React.Component {
    
  2214.       getSnapshotBeforeUpdate() {
    
  2215.         errors.push('child sad');
    
  2216.         throw new Error('child sad');
    
  2217.       }
    
  2218.       componentDidUpdate() {}
    
  2219.       render() {
    
  2220.         return <div />;
    
  2221.       }
    
  2222.     }
    
  2223. 
    
  2224.     const container = document.createElement('div');
    
  2225.     ReactDOM.render(<Parent value={1} />, container);
    
  2226.     try {
    
  2227.       ReactDOM.render(<Parent value={2} />, container);
    
  2228.     } catch (e) {
    
  2229.       if (e.message !== 'parent sad' && e.message !== 'child sad') {
    
  2230.         throw e;
    
  2231.       }
    
  2232.       caughtError = e;
    
  2233.     }
    
  2234. 
    
  2235.     expect(errors).toEqual(['child sad', 'parent sad']);
    
  2236.     // Error should be the first thrown
    
  2237.     expect(caughtError.message).toBe('child sad');
    
  2238.   });
    
  2239. });