Best JavaScript code snippet using playwright-internal
ReactErrorBoundaries-test.internal.js
Source:ReactErrorBoundaries-test.internal.js  
1/**2 * Copyright (c) Facebook, Inc. and its affiliates.3 *4 * This source code is licensed under the MIT license found in the5 * LICENSE file in the root directory of this source tree.6 *7 * @emails react-core8 */9'use strict';10let PropTypes;11let React;12let ReactDOM;13let ReactFeatureFlags;14describe('ReactErrorBoundaries', () => {15  let log;16  let BrokenConstructor;17  let BrokenComponentWillMount;18  let BrokenComponentDidMount;19  let BrokenComponentWillReceiveProps;20  let BrokenComponentWillUpdate;21  let BrokenComponentDidUpdate;22  let BrokenComponentWillUnmount;23  let BrokenRenderErrorBoundary;24  let BrokenComponentWillMountErrorBoundary;25  let BrokenComponentDidMountErrorBoundary;26  let BrokenRender;27  let BrokenUseEffect;28  let BrokenUseLayoutEffect;29  let ErrorBoundary;30  let ErrorMessage;31  let NoopErrorBoundary;32  let RetryErrorBoundary;33  let Normal;34  beforeEach(() => {35    jest.useFakeTimers();36    jest.resetModules();37    PropTypes = require('prop-types');38    ReactFeatureFlags = require('shared/ReactFeatureFlags');39    ReactFeatureFlags.replayFailedUnitOfWorkWithInvokeGuardedCallback = false;40    ReactDOM = require('react-dom');41    React = require('react');42    log = [];43    BrokenConstructor = class extends React.Component {44      constructor(props) {45        super(props);46        log.push('BrokenConstructor constructor [!]');47        throw new Error('Hello');48      }49      render() {50        log.push('BrokenConstructor render');51        return <div>{this.props.children}</div>;52      }53      UNSAFE_componentWillMount() {54        log.push('BrokenConstructor componentWillMount');55      }56      componentDidMount() {57        log.push('BrokenConstructor componentDidMount');58      }59      UNSAFE_componentWillReceiveProps() {60        log.push('BrokenConstructor componentWillReceiveProps');61      }62      UNSAFE_componentWillUpdate() {63        log.push('BrokenConstructor componentWillUpdate');64      }65      componentDidUpdate() {66        log.push('BrokenConstructor componentDidUpdate');67      }68      componentWillUnmount() {69        log.push('BrokenConstructor componentWillUnmount');70      }71    };72    BrokenComponentWillMount = class extends React.Component {73      constructor(props) {74        super(props);75        log.push('BrokenComponentWillMount constructor');76      }77      render() {78        log.push('BrokenComponentWillMount render');79        return <div>{this.props.children}</div>;80      }81      UNSAFE_componentWillMount() {82        log.push('BrokenComponentWillMount componentWillMount [!]');83        throw new Error('Hello');84      }85      componentDidMount() {86        log.push('BrokenComponentWillMount componentDidMount');87      }88      UNSAFE_componentWillReceiveProps() {89        log.push('BrokenComponentWillMount componentWillReceiveProps');90      }91      UNSAFE_componentWillUpdate() {92        log.push('BrokenComponentWillMount componentWillUpdate');93      }94      componentDidUpdate() {95        log.push('BrokenComponentWillMount componentDidUpdate');96      }97      componentWillUnmount() {98        log.push('BrokenComponentWillMount componentWillUnmount');99      }100    };101    BrokenComponentDidMount = class extends React.Component {102      constructor(props) {103        super(props);104        log.push('BrokenComponentDidMount constructor');105      }106      render() {107        log.push('BrokenComponentDidMount render');108        return <div>{this.props.children}</div>;109      }110      UNSAFE_componentWillMount() {111        log.push('BrokenComponentDidMount componentWillMount');112      }113      componentDidMount() {114        log.push('BrokenComponentDidMount componentDidMount [!]');115        throw new Error('Hello');116      }117      UNSAFE_componentWillReceiveProps() {118        log.push('BrokenComponentDidMount componentWillReceiveProps');119      }120      UNSAFE_componentWillUpdate() {121        log.push('BrokenComponentDidMount componentWillUpdate');122      }123      componentDidUpdate() {124        log.push('BrokenComponentDidMount componentDidUpdate');125      }126      componentWillUnmount() {127        log.push('BrokenComponentDidMount componentWillUnmount');128      }129    };130    BrokenComponentWillReceiveProps = class extends React.Component {131      constructor(props) {132        super(props);133        log.push('BrokenComponentWillReceiveProps constructor');134      }135      render() {136        log.push('BrokenComponentWillReceiveProps render');137        return <div>{this.props.children}</div>;138      }139      UNSAFE_componentWillMount() {140        log.push('BrokenComponentWillReceiveProps componentWillMount');141      }142      componentDidMount() {143        log.push('BrokenComponentWillReceiveProps componentDidMount');144      }145      UNSAFE_componentWillReceiveProps() {146        log.push(147          'BrokenComponentWillReceiveProps componentWillReceiveProps [!]',148        );149        throw new Error('Hello');150      }151      UNSAFE_componentWillUpdate() {152        log.push('BrokenComponentWillReceiveProps componentWillUpdate');153      }154      componentDidUpdate() {155        log.push('BrokenComponentWillReceiveProps componentDidUpdate');156      }157      componentWillUnmount() {158        log.push('BrokenComponentWillReceiveProps componentWillUnmount');159      }160    };161    BrokenComponentWillUpdate = class extends React.Component {162      constructor(props) {163        super(props);164        log.push('BrokenComponentWillUpdate constructor');165      }166      render() {167        log.push('BrokenComponentWillUpdate render');168        return <div>{this.props.children}</div>;169      }170      UNSAFE_componentWillMount() {171        log.push('BrokenComponentWillUpdate componentWillMount');172      }173      componentDidMount() {174        log.push('BrokenComponentWillUpdate componentDidMount');175      }176      UNSAFE_componentWillReceiveProps() {177        log.push('BrokenComponentWillUpdate componentWillReceiveProps');178      }179      UNSAFE_componentWillUpdate() {180        log.push('BrokenComponentWillUpdate componentWillUpdate [!]');181        throw new Error('Hello');182      }183      componentDidUpdate() {184        log.push('BrokenComponentWillUpdate componentDidUpdate');185      }186      componentWillUnmount() {187        log.push('BrokenComponentWillUpdate componentWillUnmount');188      }189    };190    BrokenComponentDidUpdate = class extends React.Component {191      static defaultProps = {192        errorText: 'Hello',193      };194      constructor(props) {195        super(props);196        log.push('BrokenComponentDidUpdate constructor');197      }198      render() {199        log.push('BrokenComponentDidUpdate render');200        return <div>{this.props.children}</div>;201      }202      UNSAFE_componentWillMount() {203        log.push('BrokenComponentDidUpdate componentWillMount');204      }205      componentDidMount() {206        log.push('BrokenComponentDidUpdate componentDidMount');207      }208      UNSAFE_componentWillReceiveProps() {209        log.push('BrokenComponentDidUpdate componentWillReceiveProps');210      }211      UNSAFE_componentWillUpdate() {212        log.push('BrokenComponentDidUpdate componentWillUpdate');213      }214      componentDidUpdate() {215        log.push('BrokenComponentDidUpdate componentDidUpdate [!]');216        throw new Error(this.props.errorText);217      }218      componentWillUnmount() {219        log.push('BrokenComponentDidUpdate componentWillUnmount');220      }221    };222    BrokenComponentWillUnmount = class extends React.Component {223      static defaultProps = {224        errorText: 'Hello',225      };226      constructor(props) {227        super(props);228        log.push('BrokenComponentWillUnmount constructor');229      }230      render() {231        log.push('BrokenComponentWillUnmount render');232        return <div>{this.props.children}</div>;233      }234      UNSAFE_componentWillMount() {235        log.push('BrokenComponentWillUnmount componentWillMount');236      }237      componentDidMount() {238        log.push('BrokenComponentWillUnmount componentDidMount');239      }240      UNSAFE_componentWillReceiveProps() {241        log.push('BrokenComponentWillUnmount componentWillReceiveProps');242      }243      UNSAFE_componentWillUpdate() {244        log.push('BrokenComponentWillUnmount componentWillUpdate');245      }246      componentDidUpdate() {247        log.push('BrokenComponentWillUnmount componentDidUpdate');248      }249      componentWillUnmount() {250        log.push('BrokenComponentWillUnmount componentWillUnmount [!]');251        throw new Error(this.props.errorText);252      }253    };254    BrokenComponentWillMountErrorBoundary = class extends React.Component {255      constructor(props) {256        super(props);257        this.state = {error: null};258        log.push('BrokenComponentWillMountErrorBoundary constructor');259      }260      render() {261        if (this.state.error) {262          log.push('BrokenComponentWillMountErrorBoundary render error');263          return <div>Caught an error: {this.state.error.message}.</div>;264        }265        log.push('BrokenComponentWillMountErrorBoundary render success');266        return <div>{this.props.children}</div>;267      }268      UNSAFE_componentWillMount() {269        log.push(270          'BrokenComponentWillMountErrorBoundary componentWillMount [!]',271        );272        throw new Error('Hello');273      }274      componentDidMount() {275        log.push('BrokenComponentWillMountErrorBoundary componentDidMount');276      }277      componentWillUnmount() {278        log.push('BrokenComponentWillMountErrorBoundary componentWillUnmount');279      }280      static getDerivedStateFromError(error) {281        log.push(282          'BrokenComponentWillMountErrorBoundary static getDerivedStateFromError',283        );284        return {error};285      }286    };287    BrokenComponentDidMountErrorBoundary = class extends React.Component {288      constructor(props) {289        super(props);290        this.state = {error: null};291        log.push('BrokenComponentDidMountErrorBoundary constructor');292      }293      render() {294        if (this.state.error) {295          log.push('BrokenComponentDidMountErrorBoundary render error');296          return <div>Caught an error: {this.state.error.message}.</div>;297        }298        log.push('BrokenComponentDidMountErrorBoundary render success');299        return <div>{this.props.children}</div>;300      }301      UNSAFE_componentWillMount() {302        log.push('BrokenComponentDidMountErrorBoundary componentWillMount');303      }304      componentDidMount() {305        log.push('BrokenComponentDidMountErrorBoundary componentDidMount [!]');306        throw new Error('Hello');307      }308      componentWillUnmount() {309        log.push('BrokenComponentDidMountErrorBoundary componentWillUnmount');310      }311      static getDerivedStateFromError(error) {312        log.push(313          'BrokenComponentDidMountErrorBoundary static getDerivedStateFromError',314        );315        return {error};316      }317    };318    BrokenRenderErrorBoundary = class extends React.Component {319      constructor(props) {320        super(props);321        this.state = {error: null};322        log.push('BrokenRenderErrorBoundary constructor');323      }324      render() {325        if (this.state.error) {326          log.push('BrokenRenderErrorBoundary render error [!]');327          throw new Error('Hello');328        }329        log.push('BrokenRenderErrorBoundary render success');330        return <div>{this.props.children}</div>;331      }332      UNSAFE_componentWillMount() {333        log.push('BrokenRenderErrorBoundary componentWillMount');334      }335      componentDidMount() {336        log.push('BrokenRenderErrorBoundary componentDidMount');337      }338      componentWillUnmount() {339        log.push('BrokenRenderErrorBoundary componentWillUnmount');340      }341      static getDerivedStateFromError(error) {342        log.push('BrokenRenderErrorBoundary static getDerivedStateFromError');343        return {error};344      }345    };346    BrokenRender = class extends React.Component {347      constructor(props) {348        super(props);349        log.push('BrokenRender constructor');350      }351      render() {352        log.push('BrokenRender render [!]');353        throw new Error('Hello');354      }355      UNSAFE_componentWillMount() {356        log.push('BrokenRender componentWillMount');357      }358      componentDidMount() {359        log.push('BrokenRender componentDidMount');360      }361      UNSAFE_componentWillReceiveProps() {362        log.push('BrokenRender componentWillReceiveProps');363      }364      UNSAFE_componentWillUpdate() {365        log.push('BrokenRender componentWillUpdate');366      }367      componentDidUpdate() {368        log.push('BrokenRender componentDidUpdate');369      }370      componentWillUnmount() {371        log.push('BrokenRender componentWillUnmount');372      }373    };374    BrokenUseEffect = props => {375      log.push('BrokenUseEffect render');376      React.useEffect(() => {377        log.push('BrokenUseEffect useEffect [!]');378        throw new Error('Hello');379      });380      return props.children;381    };382    BrokenUseLayoutEffect = props => {383      log.push('BrokenUseLayoutEffect render');384      React.useLayoutEffect(() => {385        log.push('BrokenUseLayoutEffect useLayoutEffect [!]');386        throw new Error('Hello');387      });388      return props.children;389    };390    NoopErrorBoundary = class extends React.Component {391      constructor(props) {392        super(props);393        log.push('NoopErrorBoundary constructor');394      }395      render() {396        log.push('NoopErrorBoundary render');397        return <BrokenRender />;398      }399      UNSAFE_componentWillMount() {400        log.push('NoopErrorBoundary componentWillMount');401      }402      componentDidMount() {403        log.push('NoopErrorBoundary componentDidMount');404      }405      componentWillUnmount() {406        log.push('NoopErrorBoundary componentWillUnmount');407      }408      static getDerivedStateFromError() {409        log.push('NoopErrorBoundary static getDerivedStateFromError');410      }411    };412    Normal = class extends React.Component {413      static defaultProps = {414        logName: 'Normal',415      };416      constructor(props) {417        super(props);418        log.push(`${this.props.logName} constructor`);419      }420      render() {421        log.push(`${this.props.logName} render`);422        return <div>{this.props.children}</div>;423      }424      UNSAFE_componentWillMount() {425        log.push(`${this.props.logName} componentWillMount`);426      }427      componentDidMount() {428        log.push(`${this.props.logName} componentDidMount`);429      }430      UNSAFE_componentWillReceiveProps() {431        log.push(`${this.props.logName} componentWillReceiveProps`);432      }433      UNSAFE_componentWillUpdate() {434        log.push(`${this.props.logName} componentWillUpdate`);435      }436      componentDidUpdate() {437        log.push(`${this.props.logName} componentDidUpdate`);438      }439      componentWillUnmount() {440        log.push(`${this.props.logName} componentWillUnmount`);441      }442    };443    ErrorBoundary = class extends React.Component {444      constructor(props) {445        super(props);446        this.state = {error: null};447        log.push(`${this.props.logName} constructor`);448      }449      render() {450        if (this.state.error && !this.props.forceRetry) {451          log.push(`${this.props.logName} render error`);452          return this.props.renderError(this.state.error, this.props);453        }454        log.push(`${this.props.logName} render success`);455        return <div>{this.props.children}</div>;456      }457      static getDerivedStateFromError(error) {458        log.push('ErrorBoundary static getDerivedStateFromError');459        return {error};460      }461      UNSAFE_componentWillMount() {462        log.push(`${this.props.logName} componentWillMount`);463      }464      componentDidMount() {465        log.push(`${this.props.logName} componentDidMount`);466      }467      UNSAFE_componentWillReceiveProps() {468        log.push(`${this.props.logName} componentWillReceiveProps`);469      }470      UNSAFE_componentWillUpdate() {471        log.push(`${this.props.logName} componentWillUpdate`);472      }473      componentDidUpdate() {474        log.push(`${this.props.logName} componentDidUpdate`);475      }476      componentWillUnmount() {477        log.push(`${this.props.logName} componentWillUnmount`);478      }479    };480    ErrorBoundary.defaultProps = {481      logName: 'ErrorBoundary',482      renderError(error, props) {483        return (484          <div ref={props.errorMessageRef}>485            Caught an error: {error.message}.486          </div>487        );488      },489    };490    RetryErrorBoundary = class extends React.Component {491      constructor(props) {492        super(props);493        log.push('RetryErrorBoundary constructor');494      }495      render() {496        log.push('RetryErrorBoundary render');497        return <BrokenRender />;498      }499      UNSAFE_componentWillMount() {500        log.push('RetryErrorBoundary componentWillMount');501      }502      componentDidMount() {503        log.push('RetryErrorBoundary componentDidMount');504      }505      componentWillUnmount() {506        log.push('RetryErrorBoundary componentWillUnmount');507      }508      static getDerivedStateFromError(error) {509        log.push('RetryErrorBoundary static getDerivedStateFromError [!]');510        // In Fiber, calling setState() (and failing) is treated as a rethrow.511        return {};512      }513    };514    ErrorMessage = class extends React.Component {515      constructor(props) {516        super(props);517        log.push('ErrorMessage constructor');518      }519      UNSAFE_componentWillMount() {520        log.push('ErrorMessage componentWillMount');521      }522      componentDidMount() {523        log.push('ErrorMessage componentDidMount');524      }525      componentWillUnmount() {526        log.push('ErrorMessage componentWillUnmount');527      }528      render() {529        log.push('ErrorMessage render');530        return <div>Caught an error: {this.props.message}.</div>;531      }532    };533  });534  it('does not swallow exceptions on mounting without boundaries', () => {535    let container = document.createElement('div');536    expect(() => {537      ReactDOM.render(<BrokenRender />, container);538    }).toThrow('Hello');539    container = document.createElement('div');540    expect(() => {541      ReactDOM.render(<BrokenComponentWillMount />, container);542    }).toThrow('Hello');543    container = document.createElement('div');544    expect(() => {545      ReactDOM.render(<BrokenComponentDidMount />, container);546    }).toThrow('Hello');547  });548  it('does not swallow exceptions on updating without boundaries', () => {549    let container = document.createElement('div');550    ReactDOM.render(<BrokenComponentWillUpdate />, container);551    expect(() => {552      ReactDOM.render(<BrokenComponentWillUpdate />, container);553    }).toThrow('Hello');554    container = document.createElement('div');555    ReactDOM.render(<BrokenComponentWillReceiveProps />, container);556    expect(() => {557      ReactDOM.render(<BrokenComponentWillReceiveProps />, container);558    }).toThrow('Hello');559    container = document.createElement('div');560    ReactDOM.render(<BrokenComponentDidUpdate />, container);561    expect(() => {562      ReactDOM.render(<BrokenComponentDidUpdate />, container);563    }).toThrow('Hello');564  });565  it('does not swallow exceptions on unmounting without boundaries', () => {566    const container = document.createElement('div');567    ReactDOM.render(<BrokenComponentWillUnmount />, container);568    expect(() => {569      ReactDOM.unmountComponentAtNode(container);570    }).toThrow('Hello');571  });572  it('prevents errors from leaking into other roots', () => {573    const container1 = document.createElement('div');574    const container2 = document.createElement('div');575    const container3 = document.createElement('div');576    ReactDOM.render(<span>Before 1</span>, container1);577    expect(() => {578      ReactDOM.render(<BrokenRender />, container2);579    }).toThrow('Hello');580    ReactDOM.render(581      <ErrorBoundary>582        <BrokenRender />583      </ErrorBoundary>,584      container3,585    );586    expect(container1.firstChild.textContent).toBe('Before 1');587    expect(container2.firstChild).toBe(null);588    expect(container3.firstChild.textContent).toBe('Caught an error: Hello.');589    ReactDOM.render(<span>After 1</span>, container1);590    ReactDOM.render(<span>After 2</span>, container2);591    ReactDOM.render(592      <ErrorBoundary forceRetry={true}>After 3</ErrorBoundary>,593      container3,594    );595    expect(container1.firstChild.textContent).toBe('After 1');596    expect(container2.firstChild.textContent).toBe('After 2');597    expect(container3.firstChild.textContent).toBe('After 3');598    ReactDOM.unmountComponentAtNode(container1);599    ReactDOM.unmountComponentAtNode(container2);600    ReactDOM.unmountComponentAtNode(container3);601    expect(container1.firstChild).toBe(null);602    expect(container2.firstChild).toBe(null);603    expect(container3.firstChild).toBe(null);604  });605  it('renders an error state if child throws in render', () => {606    const container = document.createElement('div');607    ReactDOM.render(608      <ErrorBoundary>609        <BrokenRender />610      </ErrorBoundary>,611      container,612    );613    expect(container.firstChild.textContent).toBe('Caught an error: Hello.');614    expect(log).toEqual([615      'ErrorBoundary constructor',616      'ErrorBoundary componentWillMount',617      'ErrorBoundary render success',618      'BrokenRender constructor',619      'BrokenRender componentWillMount',620      'BrokenRender render [!]',621      // Catch and render an error message622      'ErrorBoundary static getDerivedStateFromError',623      'ErrorBoundary componentWillMount',624      'ErrorBoundary render error',625      'ErrorBoundary componentDidMount',626    ]);627    log.length = 0;628    ReactDOM.unmountComponentAtNode(container);629    expect(log).toEqual(['ErrorBoundary componentWillUnmount']);630  });631  it('renders an error state if child throws in constructor', () => {632    const container = document.createElement('div');633    ReactDOM.render(634      <ErrorBoundary>635        <BrokenConstructor />636      </ErrorBoundary>,637      container,638    );639    expect(container.firstChild.textContent).toBe('Caught an error: Hello.');640    expect(log).toEqual([641      'ErrorBoundary constructor',642      'ErrorBoundary componentWillMount',643      'ErrorBoundary render success',644      'BrokenConstructor constructor [!]',645      // Catch and render an error message646      'ErrorBoundary static getDerivedStateFromError',647      'ErrorBoundary componentWillMount',648      'ErrorBoundary render error',649      'ErrorBoundary componentDidMount',650    ]);651    log.length = 0;652    ReactDOM.unmountComponentAtNode(container);653    expect(log).toEqual(['ErrorBoundary componentWillUnmount']);654  });655  it('renders an error state if child throws in componentWillMount', () => {656    const container = document.createElement('div');657    ReactDOM.render(658      <ErrorBoundary>659        <BrokenComponentWillMount />660      </ErrorBoundary>,661      container,662    );663    expect(container.firstChild.textContent).toBe('Caught an error: Hello.');664    expect(log).toEqual([665      'ErrorBoundary constructor',666      'ErrorBoundary componentWillMount',667      'ErrorBoundary render success',668      'BrokenComponentWillMount constructor',669      'BrokenComponentWillMount componentWillMount [!]',670      // Catch and render an error message671      'ErrorBoundary static getDerivedStateFromError',672      'ErrorBoundary componentWillMount',673      'ErrorBoundary render error',674      'ErrorBoundary componentDidMount',675    ]);676    log.length = 0;677    ReactDOM.unmountComponentAtNode(container);678    expect(log).toEqual(['ErrorBoundary componentWillUnmount']);679  });680  it('renders an error state if context provider throws in componentWillMount', () => {681    class BrokenComponentWillMountWithContext extends React.Component {682      static childContextTypes = {foo: PropTypes.number};683      getChildContext() {684        return {foo: 42};685      }686      render() {687        return <div>{this.props.children}</div>;688      }689      UNSAFE_componentWillMount() {690        throw new Error('Hello');691      }692    }693    const container = document.createElement('div');694    ReactDOM.render(695      <ErrorBoundary>696        <BrokenComponentWillMountWithContext />697      </ErrorBoundary>,698      container,699    );700    expect(container.firstChild.textContent).toBe('Caught an error: Hello.');701  });702  it('renders an error state if module-style context provider throws in componentWillMount', () => {703    function BrokenComponentWillMountWithContext() {704      return {705        getChildContext() {706          return {foo: 42};707        },708        render() {709          return <div>{this.props.children}</div>;710        },711        UNSAFE_componentWillMount() {712          throw new Error('Hello');713        },714      };715    }716    BrokenComponentWillMountWithContext.childContextTypes = {717      foo: PropTypes.number,718    };719    const container = document.createElement('div');720    ReactDOM.render(721      <ErrorBoundary>722        <BrokenComponentWillMountWithContext />723      </ErrorBoundary>,724      container,725    );726    expect(container.firstChild.textContent).toBe('Caught an error: Hello.');727  });728  it('mounts the error message if mounting fails', () => {729    function renderError(error) {730      return <ErrorMessage message={error.message} />;731    }732    const container = document.createElement('div');733    ReactDOM.render(734      <ErrorBoundary renderError={renderError}>735        <BrokenRender />736      </ErrorBoundary>,737      container,738    );739    expect(log).toEqual([740      'ErrorBoundary constructor',741      'ErrorBoundary componentWillMount',742      'ErrorBoundary render success',743      'BrokenRender constructor',744      'BrokenRender componentWillMount',745      'BrokenRender render [!]',746      'ErrorBoundary static getDerivedStateFromError',747      'ErrorBoundary componentWillMount',748      'ErrorBoundary render error',749      'ErrorMessage constructor',750      'ErrorMessage componentWillMount',751      'ErrorMessage render',752      'ErrorMessage componentDidMount',753      'ErrorBoundary componentDidMount',754    ]);755    log.length = 0;756    ReactDOM.unmountComponentAtNode(container);757    expect(log).toEqual([758      'ErrorBoundary componentWillUnmount',759      'ErrorMessage componentWillUnmount',760    ]);761  });762  it('propagates errors on retry on mounting', () => {763    const container = document.createElement('div');764    ReactDOM.render(765      <ErrorBoundary>766        <RetryErrorBoundary>767          <BrokenRender />768        </RetryErrorBoundary>769      </ErrorBoundary>,770      container,771    );772    expect(container.firstChild.textContent).toBe('Caught an error: Hello.');773    expect(log).toEqual([774      'ErrorBoundary constructor',775      'ErrorBoundary componentWillMount',776      'ErrorBoundary render success',777      'RetryErrorBoundary constructor',778      'RetryErrorBoundary componentWillMount',779      'RetryErrorBoundary render',780      'BrokenRender constructor',781      'BrokenRender componentWillMount',782      'BrokenRender render [!]',783      // Retry784      'RetryErrorBoundary static getDerivedStateFromError [!]',785      'RetryErrorBoundary componentWillMount',786      'RetryErrorBoundary render',787      'BrokenRender constructor',788      'BrokenRender componentWillMount',789      'BrokenRender render [!]',790      // This time, the error propagates to the higher boundary791      'ErrorBoundary static getDerivedStateFromError',792      'ErrorBoundary componentWillMount',793      'ErrorBoundary render error',794      'ErrorBoundary componentDidMount',795    ]);796    log.length = 0;797    ReactDOM.unmountComponentAtNode(container);798    expect(log).toEqual(['ErrorBoundary componentWillUnmount']);799  });800  it('propagates errors inside boundary during componentWillMount', () => {801    const container = document.createElement('div');802    ReactDOM.render(803      <ErrorBoundary>804        <BrokenComponentWillMountErrorBoundary />805      </ErrorBoundary>,806      container,807    );808    expect(container.firstChild.textContent).toBe('Caught an error: Hello.');809    expect(log).toEqual([810      'ErrorBoundary constructor',811      'ErrorBoundary componentWillMount',812      'ErrorBoundary render success',813      'BrokenComponentWillMountErrorBoundary constructor',814      'BrokenComponentWillMountErrorBoundary componentWillMount [!]',815      // The error propagates to the higher boundary816      'ErrorBoundary static getDerivedStateFromError',817      'ErrorBoundary componentWillMount',818      'ErrorBoundary render error',819      'ErrorBoundary componentDidMount',820    ]);821    log.length = 0;822    ReactDOM.unmountComponentAtNode(container);823    expect(log).toEqual(['ErrorBoundary componentWillUnmount']);824  });825  it('propagates errors inside boundary while rendering error state', () => {826    const container = document.createElement('div');827    ReactDOM.render(828      <ErrorBoundary>829        <BrokenRenderErrorBoundary>830          <BrokenRender />831        </BrokenRenderErrorBoundary>832      </ErrorBoundary>,833      container,834    );835    expect(container.firstChild.textContent).toBe('Caught an error: Hello.');836    expect(log).toEqual([837      'ErrorBoundary constructor',838      'ErrorBoundary componentWillMount',839      'ErrorBoundary render success',840      'BrokenRenderErrorBoundary constructor',841      'BrokenRenderErrorBoundary componentWillMount',842      'BrokenRenderErrorBoundary render success',843      'BrokenRender constructor',844      'BrokenRender componentWillMount',845      'BrokenRender render [!]',846      // Attempt to handle the error847      'BrokenRenderErrorBoundary static getDerivedStateFromError',848      'BrokenRenderErrorBoundary componentWillMount',849      'BrokenRenderErrorBoundary render error [!]',850      // Attempt to handle the error again851      'ErrorBoundary static getDerivedStateFromError',852      'ErrorBoundary componentWillMount',853      'ErrorBoundary render error',854      'ErrorBoundary componentDidMount',855    ]);856    log.length = 0;857    ReactDOM.unmountComponentAtNode(container);858    expect(log).toEqual(['ErrorBoundary componentWillUnmount']);859  });860  it('does not call componentWillUnmount when aborting initial mount', () => {861    const container = document.createElement('div');862    ReactDOM.render(863      <ErrorBoundary>864        <Normal />865        <BrokenRender />866        <Normal />867      </ErrorBoundary>,868      container,869    );870    expect(container.firstChild.textContent).toBe('Caught an error: Hello.');871    expect(log).toEqual([872      'ErrorBoundary constructor',873      'ErrorBoundary componentWillMount',874      'ErrorBoundary render success',875      // Render first child876      'Normal constructor',877      'Normal componentWillMount',878      'Normal render',879      // Render second child (it throws)880      'BrokenRender constructor',881      'BrokenRender componentWillMount',882      'BrokenRender render [!]',883      // Render third child, even though an earlier sibling threw.884      'Normal constructor',885      'Normal componentWillMount',886      'Normal render',887      // Handle the error888      'ErrorBoundary static getDerivedStateFromError',889      'ErrorBoundary componentWillMount',890      'ErrorBoundary render error',891      'ErrorBoundary componentDidMount',892    ]);893    log.length = 0;894    ReactDOM.unmountComponentAtNode(container);895    expect(log).toEqual(['ErrorBoundary componentWillUnmount']);896  });897  it('resets callback refs if mounting aborts', () => {898    function childRef(x) {899      log.push('Child ref is set to ' + x);900    }901    function errorMessageRef(x) {902      log.push('Error message ref is set to ' + x);903    }904    const container = document.createElement('div');905    ReactDOM.render(906      <ErrorBoundary errorMessageRef={errorMessageRef}>907        <div ref={childRef} />908        <BrokenRender />909      </ErrorBoundary>,910      container,911    );912    expect(container.textContent).toBe('Caught an error: Hello.');913    expect(log).toEqual([914      'ErrorBoundary constructor',915      'ErrorBoundary componentWillMount',916      'ErrorBoundary render success',917      'BrokenRender constructor',918      'BrokenRender componentWillMount',919      'BrokenRender render [!]',920      // Handle the error921      'ErrorBoundary static getDerivedStateFromError',922      'ErrorBoundary componentWillMount',923      'ErrorBoundary render error',924      'Error message ref is set to [object HTMLDivElement]',925      'ErrorBoundary componentDidMount',926    ]);927    log.length = 0;928    ReactDOM.unmountComponentAtNode(container);929    expect(log).toEqual([930      'ErrorBoundary componentWillUnmount',931      'Error message ref is set to null',932    ]);933  });934  it('resets object refs if mounting aborts', () => {935    let childRef = React.createRef();936    let errorMessageRef = React.createRef();937    const container = document.createElement('div');938    ReactDOM.render(939      <ErrorBoundary errorMessageRef={errorMessageRef}>940        <div ref={childRef} />941        <BrokenRender />942      </ErrorBoundary>,943      container,944    );945    expect(container.textContent).toBe('Caught an error: Hello.');946    expect(log).toEqual([947      'ErrorBoundary constructor',948      'ErrorBoundary componentWillMount',949      'ErrorBoundary render success',950      'BrokenRender constructor',951      'BrokenRender componentWillMount',952      'BrokenRender render [!]',953      // Handle the error954      'ErrorBoundary static getDerivedStateFromError',955      'ErrorBoundary componentWillMount',956      'ErrorBoundary render error',957      'ErrorBoundary componentDidMount',958    ]);959    expect(errorMessageRef.current.toString()).toEqual(960      '[object HTMLDivElement]',961    );962    log.length = 0;963    ReactDOM.unmountComponentAtNode(container);964    expect(log).toEqual(['ErrorBoundary componentWillUnmount']);965    expect(errorMessageRef.current).toEqual(null);966  });967  it('successfully mounts if no error occurs', () => {968    const container = document.createElement('div');969    ReactDOM.render(970      <ErrorBoundary>971        <div>Mounted successfully.</div>972      </ErrorBoundary>,973      container,974    );975    expect(container.firstChild.textContent).toBe('Mounted successfully.');976    expect(log).toEqual([977      'ErrorBoundary constructor',978      'ErrorBoundary componentWillMount',979      'ErrorBoundary render success',980      'ErrorBoundary componentDidMount',981    ]);982    log.length = 0;983    ReactDOM.unmountComponentAtNode(container);984    expect(log).toEqual(['ErrorBoundary componentWillUnmount']);985  });986  it('catches if child throws in constructor during update', () => {987    const container = document.createElement('div');988    ReactDOM.render(989      <ErrorBoundary>990        <Normal />991      </ErrorBoundary>,992      container,993    );994    log.length = 0;995    ReactDOM.render(996      <ErrorBoundary>997        <Normal />998        <Normal logName="Normal2" />999        <BrokenConstructor />1000      </ErrorBoundary>,1001      container,1002    );1003    expect(container.textContent).toBe('Caught an error: Hello.');1004    expect(log).toEqual([1005      'ErrorBoundary componentWillReceiveProps',1006      'ErrorBoundary componentWillUpdate',1007      'ErrorBoundary render success',1008      'Normal componentWillReceiveProps',1009      'Normal componentWillUpdate',1010      'Normal render',1011      // Normal2 will attempt to mount:1012      'Normal2 constructor',1013      'Normal2 componentWillMount',1014      'Normal2 render',1015      // BrokenConstructor will abort rendering:1016      'BrokenConstructor constructor [!]',1017      // Handle the error1018      'ErrorBoundary static getDerivedStateFromError',1019      // Render the error message1020      'ErrorBoundary componentWillUpdate',1021      'ErrorBoundary render error',1022      'Normal componentWillUnmount',1023      'ErrorBoundary componentDidUpdate',1024    ]);1025    log.length = 0;1026    ReactDOM.unmountComponentAtNode(container);1027    expect(log).toEqual(['ErrorBoundary componentWillUnmount']);1028  });1029  it('catches if child throws in componentWillMount during update', () => {1030    const container = document.createElement('div');1031    ReactDOM.render(1032      <ErrorBoundary>1033        <Normal />1034      </ErrorBoundary>,1035      container,1036    );1037    log.length = 0;1038    ReactDOM.render(1039      <ErrorBoundary>1040        <Normal />1041        <Normal logName="Normal2" />1042        <BrokenComponentWillMount />1043      </ErrorBoundary>,1044      container,1045    );1046    expect(container.textContent).toBe('Caught an error: Hello.');1047    expect(log).toEqual([1048      'ErrorBoundary componentWillReceiveProps',1049      'ErrorBoundary componentWillUpdate',1050      'ErrorBoundary render success',1051      'Normal componentWillReceiveProps',1052      'Normal componentWillUpdate',1053      'Normal render',1054      // Normal2 will attempt to mount:1055      'Normal2 constructor',1056      'Normal2 componentWillMount',1057      'Normal2 render',1058      // BrokenComponentWillMount will abort rendering:1059      'BrokenComponentWillMount constructor',1060      'BrokenComponentWillMount componentWillMount [!]',1061      // Handle the error1062      'ErrorBoundary static getDerivedStateFromError',1063      // Render the error message1064      'ErrorBoundary componentWillUpdate',1065      'ErrorBoundary render error',1066      'Normal componentWillUnmount',1067      'ErrorBoundary componentDidUpdate',1068    ]);1069    log.length = 0;1070    ReactDOM.unmountComponentAtNode(container);1071    expect(log).toEqual(['ErrorBoundary componentWillUnmount']);1072  });1073  it('catches if child throws in componentWillReceiveProps during update', () => {1074    const container = document.createElement('div');1075    ReactDOM.render(1076      <ErrorBoundary>1077        <Normal />1078        <BrokenComponentWillReceiveProps />1079      </ErrorBoundary>,1080      container,1081    );1082    log.length = 0;1083    ReactDOM.render(1084      <ErrorBoundary>1085        <Normal />1086        <BrokenComponentWillReceiveProps />1087      </ErrorBoundary>,1088      container,1089    );1090    expect(container.textContent).toBe('Caught an error: Hello.');1091    expect(log).toEqual([1092      'ErrorBoundary componentWillReceiveProps',1093      'ErrorBoundary componentWillUpdate',1094      'ErrorBoundary render success',1095      'Normal componentWillReceiveProps',1096      'Normal componentWillUpdate',1097      'Normal render',1098      // BrokenComponentWillReceiveProps will abort rendering:1099      'BrokenComponentWillReceiveProps componentWillReceiveProps [!]',1100      // Handle the error1101      'ErrorBoundary static getDerivedStateFromError',1102      // Render the error message1103      'ErrorBoundary componentWillUpdate',1104      'ErrorBoundary render error',1105      'Normal componentWillUnmount',1106      'BrokenComponentWillReceiveProps componentWillUnmount',1107      'ErrorBoundary componentDidUpdate',1108    ]);1109    log.length = 0;1110    ReactDOM.unmountComponentAtNode(container);1111    expect(log).toEqual(['ErrorBoundary componentWillUnmount']);1112  });1113  it('catches if child throws in componentWillUpdate during update', () => {1114    const container = document.createElement('div');1115    ReactDOM.render(1116      <ErrorBoundary>1117        <Normal />1118        <BrokenComponentWillUpdate />1119      </ErrorBoundary>,1120      container,1121    );1122    log.length = 0;1123    ReactDOM.render(1124      <ErrorBoundary>1125        <Normal />1126        <BrokenComponentWillUpdate />1127      </ErrorBoundary>,1128      container,1129    );1130    expect(container.textContent).toBe('Caught an error: Hello.');1131    expect(log).toEqual([1132      'ErrorBoundary componentWillReceiveProps',1133      'ErrorBoundary componentWillUpdate',1134      'ErrorBoundary render success',1135      'Normal componentWillReceiveProps',1136      'Normal componentWillUpdate',1137      'Normal render',1138      // BrokenComponentWillUpdate will abort rendering:1139      'BrokenComponentWillUpdate componentWillReceiveProps',1140      'BrokenComponentWillUpdate componentWillUpdate [!]',1141      // Handle the error1142      'ErrorBoundary static getDerivedStateFromError',1143      'ErrorBoundary componentWillUpdate',1144      'ErrorBoundary render error',1145      'Normal componentWillUnmount',1146      'BrokenComponentWillUpdate componentWillUnmount',1147      'ErrorBoundary componentDidUpdate',1148    ]);1149    log.length = 0;1150    ReactDOM.unmountComponentAtNode(container);1151    expect(log).toEqual(['ErrorBoundary componentWillUnmount']);1152  });1153  it('catches if child throws in render during update', () => {1154    const container = document.createElement('div');1155    ReactDOM.render(1156      <ErrorBoundary>1157        <Normal />1158      </ErrorBoundary>,1159      container,1160    );1161    log.length = 0;1162    ReactDOM.render(1163      <ErrorBoundary>1164        <Normal />1165        <Normal logName="Normal2" />1166        <BrokenRender />1167      </ErrorBoundary>,1168      container,1169    );1170    expect(container.textContent).toBe('Caught an error: Hello.');1171    expect(log).toEqual([1172      'ErrorBoundary componentWillReceiveProps',1173      'ErrorBoundary componentWillUpdate',1174      'ErrorBoundary render success',1175      'Normal componentWillReceiveProps',1176      'Normal componentWillUpdate',1177      'Normal render',1178      // Normal2 will attempt to mount:1179      'Normal2 constructor',1180      'Normal2 componentWillMount',1181      'Normal2 render',1182      // BrokenRender will abort rendering:1183      'BrokenRender constructor',1184      'BrokenRender componentWillMount',1185      'BrokenRender render [!]',1186      // Handle the error1187      'ErrorBoundary static getDerivedStateFromError',1188      'ErrorBoundary componentWillUpdate',1189      'ErrorBoundary render error',1190      'Normal componentWillUnmount',1191      'ErrorBoundary componentDidUpdate',1192    ]);1193    log.length = 0;1194    ReactDOM.unmountComponentAtNode(container);1195    expect(log).toEqual(['ErrorBoundary componentWillUnmount']);1196  });1197  it('keeps refs up-to-date during updates', () => {1198    function child1Ref(x) {1199      log.push('Child1 ref is set to ' + x);1200    }1201    function child2Ref(x) {1202      log.push('Child2 ref is set to ' + x);1203    }1204    function errorMessageRef(x) {1205      log.push('Error message ref is set to ' + x);1206    }1207    const container = document.createElement('div');1208    ReactDOM.render(1209      <ErrorBoundary errorMessageRef={errorMessageRef}>1210        <div ref={child1Ref} />1211      </ErrorBoundary>,1212      container,1213    );1214    expect(log).toEqual([1215      'ErrorBoundary constructor',1216      'ErrorBoundary componentWillMount',1217      'ErrorBoundary render success',1218      'Child1 ref is set to [object HTMLDivElement]',1219      'ErrorBoundary componentDidMount',1220    ]);1221    log.length = 0;1222    ReactDOM.render(1223      <ErrorBoundary errorMessageRef={errorMessageRef}>1224        <div ref={child1Ref} />1225        <div ref={child2Ref} />1226        <BrokenRender />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      // BrokenRender will abort rendering:1236      'BrokenRender constructor',1237      'BrokenRender componentWillMount',1238      'BrokenRender render [!]',1239      // Handle the error1240      'ErrorBoundary static getDerivedStateFromError',1241      'ErrorBoundary componentWillUpdate',1242      'ErrorBoundary render error',1243      // Update Child1 ref since Child1 has been unmounted1244      // Child2 ref is never set because its mounting aborted1245      'Child1 ref is set to null',1246      'Error message ref is set to [object HTMLDivElement]',1247      'ErrorBoundary componentDidUpdate',1248    ]);1249    log.length = 0;1250    ReactDOM.unmountComponentAtNode(container);1251    expect(log).toEqual([1252      'ErrorBoundary componentWillUnmount',1253      'Error message ref is set to null',1254    ]);1255  });1256  it('recovers from componentWillUnmount errors on update', () => {1257    const container = document.createElement('div');1258    ReactDOM.render(1259      <ErrorBoundary>1260        <BrokenComponentWillUnmount />1261        <BrokenComponentWillUnmount />1262        <Normal />1263      </ErrorBoundary>,1264      container,1265    );1266    log.length = 0;1267    ReactDOM.render(1268      <ErrorBoundary>1269        <BrokenComponentWillUnmount />1270      </ErrorBoundary>,1271      container,1272    );1273    expect(container.textContent).toBe('Caught an error: Hello.');1274    expect(log).toEqual([1275      'ErrorBoundary componentWillReceiveProps',1276      'ErrorBoundary componentWillUpdate',1277      'ErrorBoundary render success',1278      // Update existing child:1279      'BrokenComponentWillUnmount componentWillReceiveProps',1280      'BrokenComponentWillUnmount componentWillUpdate',1281      'BrokenComponentWillUnmount render',1282      // Unmounting throws:1283      'BrokenComponentWillUnmount componentWillUnmount [!]',1284      // Fiber proceeds with lifecycles despite errors1285      'Normal componentWillUnmount',1286      // The components have updated in this phase1287      'BrokenComponentWillUnmount componentDidUpdate',1288      'ErrorBoundary componentDidUpdate',1289      // The initial render was aborted, so1290      // Fiber retries from the root.1291      'ErrorBoundary static getDerivedStateFromError',1292      'ErrorBoundary componentWillUpdate',1293      'ErrorBoundary render error',1294      'BrokenComponentWillUnmount componentWillUnmount [!]',1295      'ErrorBoundary componentDidUpdate',1296      // The second willUnmount error should be captured and logged, too.1297      'ErrorBoundary static getDerivedStateFromError',1298      'ErrorBoundary componentWillUpdate',1299      // Render an error now (stack will do it later)1300      'ErrorBoundary render error',1301      // Attempt to unmount previous child:1302      // Done1303      'ErrorBoundary componentDidUpdate',1304    ]);1305    log.length = 0;1306    ReactDOM.unmountComponentAtNode(container);1307    expect(log).toEqual(['ErrorBoundary componentWillUnmount']);1308  });1309  it('recovers from nested componentWillUnmount errors on update', () => {1310    const container = document.createElement('div');1311    ReactDOM.render(1312      <ErrorBoundary>1313        <Normal>1314          <BrokenComponentWillUnmount />1315        </Normal>1316        <BrokenComponentWillUnmount />1317      </ErrorBoundary>,1318      container,1319    );1320    log.length = 0;1321    ReactDOM.render(1322      <ErrorBoundary>1323        <Normal>1324          <BrokenComponentWillUnmount />1325        </Normal>1326      </ErrorBoundary>,1327      container,1328    );1329    expect(container.textContent).toBe('Caught an error: Hello.');1330    expect(log).toEqual([1331      'ErrorBoundary componentWillReceiveProps',1332      'ErrorBoundary componentWillUpdate',1333      'ErrorBoundary render success',1334      // Update existing children:1335      'Normal componentWillReceiveProps',1336      'Normal componentWillUpdate',1337      'Normal render',1338      'BrokenComponentWillUnmount componentWillReceiveProps',1339      'BrokenComponentWillUnmount componentWillUpdate',1340      'BrokenComponentWillUnmount render',1341      // Unmounting throws:1342      'BrokenComponentWillUnmount componentWillUnmount [!]',1343      // Fiber proceeds with lifecycles despite errors1344      'BrokenComponentWillUnmount componentDidUpdate',1345      'Normal componentDidUpdate',1346      'ErrorBoundary componentDidUpdate',1347      // Now that commit phase is done, Fiber handles errors1348      'ErrorBoundary static getDerivedStateFromError',1349      'ErrorBoundary componentWillUpdate',1350      'ErrorBoundary render error',1351      'Normal componentWillUnmount',1352      'BrokenComponentWillUnmount componentWillUnmount [!]',1353      'ErrorBoundary componentDidUpdate',1354      // The second willUnmount error should be captured and logged, too.1355      'ErrorBoundary static getDerivedStateFromError',1356      'ErrorBoundary componentWillUpdate',1357      // Render an error now (stack will do it later)1358      'ErrorBoundary render error',1359      // Done1360      'ErrorBoundary componentDidUpdate',1361    ]);1362    log.length = 0;1363    ReactDOM.unmountComponentAtNode(container);1364    expect(log).toEqual(['ErrorBoundary componentWillUnmount']);1365  });1366  it('picks the right boundary when handling unmounting errors', () => {1367    function renderInnerError(error) {1368      return <div>Caught an inner error: {error.message}.</div>;1369    }1370    function renderOuterError(error) {1371      return <div>Caught an outer error: {error.message}.</div>;1372    }1373    const container = document.createElement('div');1374    ReactDOM.render(1375      <ErrorBoundary1376        logName="OuterErrorBoundary"1377        renderError={renderOuterError}>1378        <ErrorBoundary1379          logName="InnerErrorBoundary"1380          renderError={renderInnerError}>1381          <BrokenComponentWillUnmount />1382        </ErrorBoundary>1383      </ErrorBoundary>,1384      container,1385    );1386    log.length = 0;1387    ReactDOM.render(1388      <ErrorBoundary1389        logName="OuterErrorBoundary"1390        renderError={renderOuterError}>1391        <ErrorBoundary1392          logName="InnerErrorBoundary"1393          renderError={renderInnerError}1394        />1395      </ErrorBoundary>,1396      container,1397    );1398    expect(container.textContent).toBe('Caught an inner error: Hello.');1399    expect(log).toEqual([1400      // Update outer boundary1401      'OuterErrorBoundary componentWillReceiveProps',1402      'OuterErrorBoundary componentWillUpdate',1403      'OuterErrorBoundary render success',1404      // Update inner boundary1405      'InnerErrorBoundary componentWillReceiveProps',1406      'InnerErrorBoundary componentWillUpdate',1407      'InnerErrorBoundary render success',1408      // Try unmounting child1409      'BrokenComponentWillUnmount componentWillUnmount [!]',1410      // Now that commit phase is done, Fiber handles errors1411      // Only inner boundary receives the error:1412      'InnerErrorBoundary componentDidUpdate',1413      'OuterErrorBoundary componentDidUpdate',1414      'ErrorBoundary static getDerivedStateFromError',1415      'InnerErrorBoundary componentWillUpdate',1416      // Render an error now1417      'InnerErrorBoundary render error',1418      // In Fiber, this was a local update to the1419      // inner boundary so only its hook fires1420      'InnerErrorBoundary componentDidUpdate',1421    ]);1422    log.length = 0;1423    ReactDOM.unmountComponentAtNode(container);1424    expect(log).toEqual([1425      'OuterErrorBoundary componentWillUnmount',1426      'InnerErrorBoundary componentWillUnmount',1427    ]);1428  });1429  it('can recover from error state', () => {1430    const container = document.createElement('div');1431    ReactDOM.render(1432      <ErrorBoundary>1433        <BrokenRender />1434      </ErrorBoundary>,1435      container,1436    );1437    ReactDOM.render(1438      <ErrorBoundary>1439        <Normal />1440      </ErrorBoundary>,1441      container,1442    );1443    // Error boundary doesn't retry by itself:1444    expect(container.textContent).toBe('Caught an error: Hello.');1445    // Force the success path:1446    log.length = 0;1447    ReactDOM.render(1448      <ErrorBoundary forceRetry={true}>1449        <Normal />1450      </ErrorBoundary>,1451      container,1452    );1453    expect(container.textContent).not.toContain('Caught an error');1454    expect(log).toEqual([1455      'ErrorBoundary componentWillReceiveProps',1456      'ErrorBoundary componentWillUpdate',1457      'ErrorBoundary render success',1458      // Mount children:1459      'Normal constructor',1460      'Normal componentWillMount',1461      'Normal render',1462      // Finalize updates:1463      'Normal componentDidMount',1464      'ErrorBoundary componentDidUpdate',1465    ]);1466    log.length = 0;1467    ReactDOM.unmountComponentAtNode(container);1468    expect(log).toEqual([1469      'ErrorBoundary componentWillUnmount',1470      'Normal componentWillUnmount',1471    ]);1472  });1473  it('can update multiple times in error state', () => {1474    const container = document.createElement('div');1475    ReactDOM.render(1476      <ErrorBoundary>1477        <BrokenRender />1478      </ErrorBoundary>,1479      container,1480    );1481    expect(container.textContent).toBe('Caught an error: Hello.');1482    ReactDOM.render(1483      <ErrorBoundary>1484        <BrokenRender />1485      </ErrorBoundary>,1486      container,1487    );1488    expect(container.textContent).toBe('Caught an error: Hello.');1489    ReactDOM.render(<div>Other screen</div>, container);1490    expect(container.textContent).toBe('Other screen');1491    ReactDOM.unmountComponentAtNode(container);1492  });1493  it("doesn't get into inconsistent state during removals", () => {1494    const container = document.createElement('div');1495    ReactDOM.render(1496      <ErrorBoundary>1497        <Normal />1498        <BrokenComponentWillUnmount />1499        <Normal />1500      </ErrorBoundary>,1501      container,1502    );1503    ReactDOM.render(<ErrorBoundary />, container);1504    expect(container.textContent).toBe('Caught an error: Hello.');1505    log.length = 0;1506    ReactDOM.unmountComponentAtNode(container);1507    expect(log).toEqual(['ErrorBoundary componentWillUnmount']);1508  });1509  it("doesn't get into inconsistent state during additions", () => {1510    const container = document.createElement('div');1511    ReactDOM.render(<ErrorBoundary />, container);1512    ReactDOM.render(1513      <ErrorBoundary>1514        <Normal />1515        <BrokenRender />1516        <Normal />1517      </ErrorBoundary>,1518      container,1519    );1520    expect(container.textContent).toBe('Caught an error: Hello.');1521    log.length = 0;1522    ReactDOM.unmountComponentAtNode(container);1523    expect(log).toEqual(['ErrorBoundary componentWillUnmount']);1524  });1525  it("doesn't get into inconsistent state during reorders", () => {1526    function getAMixOfNormalAndBrokenRenderElements() {1527      const elements = [];1528      for (let i = 0; i < 100; i++) {1529        elements.push(<Normal key={i} />);1530      }1531      elements.push(<MaybeBrokenRender key={100} />);1532      let currentIndex = elements.length;1533      while (0 !== currentIndex) {1534        const randomIndex = Math.floor(Math.random() * currentIndex);1535        currentIndex -= 1;1536        const temporaryValue = elements[currentIndex];1537        elements[currentIndex] = elements[randomIndex];1538        elements[randomIndex] = temporaryValue;1539      }1540      return elements;1541    }1542    class MaybeBrokenRender extends React.Component {1543      render() {1544        if (fail) {1545          throw new Error('Hello');1546        }1547        return <div>{this.props.children}</div>;1548      }1549    }1550    let fail = false;1551    const container = document.createElement('div');1552    ReactDOM.render(1553      <ErrorBoundary>{getAMixOfNormalAndBrokenRenderElements()}</ErrorBoundary>,1554      container,1555    );1556    expect(container.textContent).not.toContain('Caught an error');1557    fail = true;1558    ReactDOM.render(1559      <ErrorBoundary>{getAMixOfNormalAndBrokenRenderElements()}</ErrorBoundary>,1560      container,1561    );1562    expect(container.textContent).toBe('Caught an error: Hello.');1563    log.length = 0;1564    ReactDOM.unmountComponentAtNode(container);1565    expect(log).toEqual(['ErrorBoundary componentWillUnmount']);1566  });1567  it('catches errors originating downstream', () => {1568    let fail = false;1569    class Stateful extends React.Component {1570      state = {shouldThrow: false};1571      render() {1572        if (fail) {1573          log.push('Stateful render [!]');1574          throw new Error('Hello');1575        }1576        return <div>{this.props.children}</div>;1577      }1578    }1579    let statefulInst;1580    const container = document.createElement('div');1581    ReactDOM.render(1582      <ErrorBoundary>1583        <Stateful ref={inst => (statefulInst = inst)} />1584      </ErrorBoundary>,1585      container,1586    );1587    log.length = 0;1588    expect(() => {1589      fail = true;1590      statefulInst.forceUpdate();1591    }).not.toThrow();1592    expect(log).toEqual([1593      'Stateful render [!]',1594      'ErrorBoundary static getDerivedStateFromError',1595      'ErrorBoundary componentWillUpdate',1596      'ErrorBoundary render error',1597      'ErrorBoundary componentDidUpdate',1598    ]);1599    log.length = 0;1600    ReactDOM.unmountComponentAtNode(container);1601    expect(log).toEqual(['ErrorBoundary componentWillUnmount']);1602  });1603  it('catches errors in componentDidMount', () => {1604    const container = document.createElement('div');1605    ReactDOM.render(1606      <ErrorBoundary>1607        <BrokenComponentWillUnmount>1608          <Normal />1609        </BrokenComponentWillUnmount>1610        <BrokenComponentDidMount />1611        <Normal logName="LastChild" />1612      </ErrorBoundary>,1613      container,1614    );1615    expect(log).toEqual([1616      'ErrorBoundary constructor',1617      'ErrorBoundary componentWillMount',1618      'ErrorBoundary render success',1619      'BrokenComponentWillUnmount constructor',1620      'BrokenComponentWillUnmount componentWillMount',1621      'BrokenComponentWillUnmount render',1622      'Normal constructor',1623      'Normal componentWillMount',1624      'Normal render',1625      'BrokenComponentDidMount constructor',1626      'BrokenComponentDidMount componentWillMount',1627      'BrokenComponentDidMount render',1628      'LastChild constructor',1629      'LastChild componentWillMount',1630      'LastChild render',1631      // Start flushing didMount queue1632      'Normal componentDidMount',1633      'BrokenComponentWillUnmount componentDidMount',1634      'BrokenComponentDidMount componentDidMount [!]',1635      // Continue despite the error1636      'LastChild componentDidMount',1637      // Now we are ready to handle the error1638      'ErrorBoundary componentDidMount',1639      'ErrorBoundary static getDerivedStateFromError',1640      'ErrorBoundary componentWillUpdate',1641      'ErrorBoundary render error',1642      // Safely unmount every child1643      'BrokenComponentWillUnmount componentWillUnmount [!]',1644      // Continue unmounting safely despite any errors1645      'Normal componentWillUnmount',1646      'BrokenComponentDidMount componentWillUnmount',1647      'LastChild componentWillUnmount',1648      // The willUnmount error should be captured and logged, too.1649      'ErrorBoundary componentDidUpdate',1650      'ErrorBoundary static getDerivedStateFromError',1651      'ErrorBoundary componentWillUpdate',1652      'ErrorBoundary render error',1653      // The update has finished1654      'ErrorBoundary componentDidUpdate',1655    ]);1656    log.length = 0;1657    ReactDOM.unmountComponentAtNode(container);1658    expect(log).toEqual(['ErrorBoundary componentWillUnmount']);1659  });1660  it('catches errors in componentDidUpdate', () => {1661    const container = document.createElement('div');1662    ReactDOM.render(1663      <ErrorBoundary>1664        <BrokenComponentDidUpdate />1665      </ErrorBoundary>,1666      container,1667    );1668    log.length = 0;1669    ReactDOM.render(1670      <ErrorBoundary>1671        <BrokenComponentDidUpdate />1672      </ErrorBoundary>,1673      container,1674    );1675    expect(log).toEqual([1676      'ErrorBoundary componentWillReceiveProps',1677      'ErrorBoundary componentWillUpdate',1678      'ErrorBoundary render success',1679      'BrokenComponentDidUpdate componentWillReceiveProps',1680      'BrokenComponentDidUpdate componentWillUpdate',1681      'BrokenComponentDidUpdate render',1682      // All lifecycles run1683      'BrokenComponentDidUpdate componentDidUpdate [!]',1684      'ErrorBoundary componentDidUpdate',1685      // Then, error is handled1686      'ErrorBoundary static getDerivedStateFromError',1687      'ErrorBoundary componentWillUpdate',1688      'ErrorBoundary render error',1689      'BrokenComponentDidUpdate componentWillUnmount',1690      'ErrorBoundary componentDidUpdate',1691    ]);1692    log.length = 0;1693    ReactDOM.unmountComponentAtNode(container);1694    expect(log).toEqual(['ErrorBoundary componentWillUnmount']);1695  });1696  it('catches errors in useEffect', () => {1697    const container = document.createElement('div');1698    ReactDOM.render(1699      <ErrorBoundary>1700        <BrokenUseEffect>Initial value</BrokenUseEffect>1701      </ErrorBoundary>,1702      container,1703    );1704    expect(log).toEqual([1705      'ErrorBoundary constructor',1706      'ErrorBoundary componentWillMount',1707      'ErrorBoundary render success',1708      'BrokenUseEffect render',1709      'ErrorBoundary componentDidMount',1710    ]);1711    expect(container.firstChild.textContent).toBe('Initial value');1712    log.length = 0;1713    jest.runAllTimers();1714    // Flush passive effects and handle the error1715    expect(log).toEqual([1716      'BrokenUseEffect useEffect [!]',1717      // Handle the error1718      'ErrorBoundary static getDerivedStateFromError',1719      'ErrorBoundary componentWillUpdate',1720      'ErrorBoundary render error',1721      'ErrorBoundary componentDidUpdate',1722    ]);1723    expect(container.firstChild.textContent).toBe('Caught an error: Hello.');1724  });1725  it('catches errors in useLayoutEffect', () => {1726    const container = document.createElement('div');1727    ReactDOM.render(1728      <ErrorBoundary>1729        <BrokenUseLayoutEffect>Initial value</BrokenUseLayoutEffect>1730      </ErrorBoundary>,1731      container,1732    );1733    expect(log).toEqual([1734      'ErrorBoundary constructor',1735      'ErrorBoundary componentWillMount',1736      'ErrorBoundary render success',1737      'BrokenUseLayoutEffect render',1738      'BrokenUseLayoutEffect useLayoutEffect [!]',1739      // Fiber proceeds with the hooks1740      'ErrorBoundary componentDidMount',1741      // The error propagates to the higher boundary1742      'ErrorBoundary static getDerivedStateFromError',1743      // Fiber retries from the root1744      'ErrorBoundary componentWillUpdate',1745      'ErrorBoundary render error',1746      'ErrorBoundary componentDidUpdate',1747    ]);1748    expect(container.firstChild.textContent).toBe('Caught an error: Hello.');1749  });1750  it('propagates errors inside boundary during componentDidMount', () => {1751    const container = document.createElement('div');1752    ReactDOM.render(1753      <ErrorBoundary>1754        <BrokenComponentDidMountErrorBoundary1755          renderError={error => (1756            <div>We should never catch our own error: {error.message}.</div>1757          )}1758        />1759      </ErrorBoundary>,1760      container,1761    );1762    expect(container.firstChild.textContent).toBe('Caught an error: Hello.');1763    expect(log).toEqual([1764      'ErrorBoundary constructor',1765      'ErrorBoundary componentWillMount',1766      'ErrorBoundary render success',1767      'BrokenComponentDidMountErrorBoundary constructor',1768      'BrokenComponentDidMountErrorBoundary componentWillMount',1769      'BrokenComponentDidMountErrorBoundary render success',1770      'BrokenComponentDidMountErrorBoundary componentDidMount [!]',1771      // Fiber proceeds with the hooks1772      'ErrorBoundary componentDidMount',1773      // The error propagates to the higher boundary1774      'ErrorBoundary static getDerivedStateFromError',1775      // Fiber retries from the root1776      'ErrorBoundary componentWillUpdate',1777      'ErrorBoundary render error',1778      'BrokenComponentDidMountErrorBoundary componentWillUnmount',1779      'ErrorBoundary componentDidUpdate',1780    ]);1781    log.length = 0;1782    ReactDOM.unmountComponentAtNode(container);1783    expect(log).toEqual(['ErrorBoundary componentWillUnmount']);1784  });1785  it('calls static getDerivedStateFromError for each error that is captured', () => {1786    function renderUnmountError(error) {1787      return <div>Caught an unmounting error: {error.message}.</div>;1788    }1789    function renderUpdateError(error) {1790      return <div>Caught an updating error: {error.message}.</div>;1791    }1792    const container = document.createElement('div');1793    ReactDOM.render(1794      <ErrorBoundary logName="OuterErrorBoundary">1795        <ErrorBoundary1796          logName="InnerUnmountBoundary"1797          renderError={renderUnmountError}>1798          <BrokenComponentWillUnmount errorText="E1" />1799          <BrokenComponentWillUnmount errorText="E2" />1800        </ErrorBoundary>1801        <ErrorBoundary1802          logName="InnerUpdateBoundary"1803          renderError={renderUpdateError}>1804          <BrokenComponentDidUpdate errorText="E3" />1805          <BrokenComponentDidUpdate errorText="E4" />1806        </ErrorBoundary>1807      </ErrorBoundary>,1808      container,1809    );1810    log.length = 0;1811    ReactDOM.render(1812      <ErrorBoundary logName="OuterErrorBoundary">1813        <ErrorBoundary1814          logName="InnerUnmountBoundary"1815          renderError={renderUnmountError}1816        />1817        <ErrorBoundary1818          logName="InnerUpdateBoundary"1819          renderError={renderUpdateError}>1820          <BrokenComponentDidUpdate errorText="E3" />1821          <BrokenComponentDidUpdate errorText="E4" />1822        </ErrorBoundary>1823      </ErrorBoundary>,1824      container,1825    );1826    expect(container.firstChild.textContent).toBe(1827      'Caught an unmounting error: E2.' + 'Caught an updating error: E4.',1828    );1829    expect(log).toEqual([1830      // Begin update phase1831      'OuterErrorBoundary componentWillReceiveProps',1832      'OuterErrorBoundary componentWillUpdate',1833      'OuterErrorBoundary render success',1834      'InnerUnmountBoundary componentWillReceiveProps',1835      'InnerUnmountBoundary componentWillUpdate',1836      'InnerUnmountBoundary render success',1837      'InnerUpdateBoundary componentWillReceiveProps',1838      'InnerUpdateBoundary componentWillUpdate',1839      'InnerUpdateBoundary render success',1840      // First come the updates1841      'BrokenComponentDidUpdate componentWillReceiveProps',1842      'BrokenComponentDidUpdate componentWillUpdate',1843      'BrokenComponentDidUpdate render',1844      'BrokenComponentDidUpdate componentWillReceiveProps',1845      'BrokenComponentDidUpdate componentWillUpdate',1846      'BrokenComponentDidUpdate render',1847      // We're in commit phase now, deleting1848      'BrokenComponentWillUnmount componentWillUnmount [!]',1849      'BrokenComponentWillUnmount componentWillUnmount [!]',1850      // Continue despite errors, handle them after commit is done1851      'InnerUnmountBoundary componentDidUpdate',1852      // We're still in commit phase, now calling update lifecycles1853      'BrokenComponentDidUpdate componentDidUpdate [!]',1854      // Again, continue despite errors, we'll handle them later1855      'BrokenComponentDidUpdate componentDidUpdate [!]',1856      'InnerUpdateBoundary componentDidUpdate',1857      'OuterErrorBoundary componentDidUpdate',1858      // After the commit phase, attempt to recover from any errors that1859      // were captured1860      'ErrorBoundary static getDerivedStateFromError',1861      'ErrorBoundary static getDerivedStateFromError',1862      'InnerUnmountBoundary componentWillUpdate',1863      'InnerUnmountBoundary render error',1864      'ErrorBoundary static getDerivedStateFromError',1865      'ErrorBoundary static getDerivedStateFromError',1866      'InnerUpdateBoundary componentWillUpdate',1867      'InnerUpdateBoundary render error',1868      'BrokenComponentDidUpdate componentWillUnmount',1869      'BrokenComponentDidUpdate componentWillUnmount',1870      'InnerUnmountBoundary componentDidUpdate',1871      'InnerUpdateBoundary componentDidUpdate',1872    ]);1873    log.length = 0;1874    ReactDOM.unmountComponentAtNode(container);1875    expect(log).toEqual([1876      'OuterErrorBoundary componentWillUnmount',1877      'InnerUnmountBoundary componentWillUnmount',1878      'InnerUpdateBoundary componentWillUnmount',1879    ]);1880  });1881  it('discards a bad root if the root component fails', () => {1882    const X = null;1883    const Y = undefined;1884    let err1;1885    let err2;1886    try {1887      let container = document.createElement('div');1888      expect(() => ReactDOM.render(<X />, container)).toWarnDev(1889        'React.createElement: type is invalid -- expected a string ' +1890          '(for built-in components) or a class/function ' +1891          '(for composite components) but got: null.',1892      );1893    } catch (err) {1894      err1 = err;1895    }1896    try {1897      let container = document.createElement('div');1898      expect(() => ReactDOM.render(<Y />, container)).toWarnDev(1899        'React.createElement: type is invalid -- expected a string ' +1900          '(for built-in components) or a class/function ' +1901          '(for composite components) but got: undefined.',1902      );1903    } catch (err) {1904      err2 = err;1905    }1906    expect(err1.message).toMatch(/got: null/);1907    expect(err2.message).toMatch(/got: undefined/);1908  });1909  it('renders empty output if error boundary does not handle the error', () => {1910    const container = document.createElement('div');1911    expect(() =>1912      ReactDOM.render(1913        <div>1914          Sibling1915          <NoopErrorBoundary>1916            <BrokenRender />1917          </NoopErrorBoundary>1918        </div>,1919        container,1920      ),1921    ).toThrow('Hello');1922    expect(container.innerHTML).toBe('');1923    expect(log).toEqual([1924      'NoopErrorBoundary constructor',1925      'NoopErrorBoundary componentWillMount',1926      'NoopErrorBoundary render',1927      'BrokenRender constructor',1928      'BrokenRender componentWillMount',1929      'BrokenRender render [!]',1930      // Noop error boundaries retry render (and fail again)1931      'NoopErrorBoundary static getDerivedStateFromError',1932      'NoopErrorBoundary render',1933      'BrokenRender constructor',1934      'BrokenRender componentWillMount',1935      'BrokenRender render [!]',1936    ]);1937  });1938  it('passes first error when two errors happen in commit', () => {1939    const errors = [];1940    let caughtError;1941    class Parent extends React.Component {1942      render() {1943        return <Child />;1944      }1945      componentDidMount() {1946        errors.push('parent sad');1947        throw new Error('parent sad');1948      }1949    }1950    class Child extends React.Component {1951      render() {1952        return <div />;1953      }1954      componentDidMount() {1955        errors.push('child sad');1956        throw new Error('child sad');1957      }1958    }1959    const container = document.createElement('div');1960    try {1961      // Here, we test the behavior where there is no error boundary and we1962      // delegate to the host root.1963      ReactDOM.render(<Parent />, container);1964    } catch (e) {1965      if (e.message !== 'parent sad' && e.message !== 'child sad') {1966        throw e;1967      }1968      caughtError = e;1969    }1970    expect(errors).toEqual(['child sad', 'parent sad']);1971    // Error should be the first thrown1972    expect(caughtError.message).toBe('child sad');1973  });1974  it('propagates uncaught error inside unbatched initial mount', () => {1975    function Foo() {1976      throw new Error('foo error');1977    }1978    const container = document.createElement('div');1979    expect(() => {1980      ReactDOM.unstable_batchedUpdates(() => {1981        ReactDOM.render(<Foo />, container);1982      });1983    }).toThrow('foo error');1984  });1985  it('handles errors that occur in before-mutation commit hook', () => {1986    const errors = [];1987    let caughtError;1988    class Parent extends React.Component {1989      getSnapshotBeforeUpdate() {1990        errors.push('parent sad');1991        throw new Error('parent sad');1992      }1993      componentDidUpdate() {}1994      render() {1995        return <Child {...this.props} />;1996      }1997    }1998    class Child extends React.Component {1999      getSnapshotBeforeUpdate() {2000        errors.push('child sad');2001        throw new Error('child sad');2002      }2003      componentDidUpdate() {}2004      render() {2005        return <div />;2006      }2007    }2008    const container = document.createElement('div');2009    ReactDOM.render(<Parent value={1} />, container);2010    try {2011      ReactDOM.render(<Parent value={2} />, container);2012    } catch (e) {2013      if (e.message !== 'parent sad' && e.message !== 'child sad') {2014        throw e;2015      }2016      caughtError = e;2017    }2018    expect(errors).toEqual(['child sad', 'parent sad']);2019    // Error should be the first thrown2020    expect(caughtError.message).toBe('child sad');2021  });2022  it('should warn if an error boundary with only componentDidCatch does not update state', () => {2023    class InvalidErrorBoundary extends React.Component {2024      componentDidCatch(error, info) {2025        // This component does not define getDerivedStateFromError().2026        // It also doesn't call setState().2027        // So it would swallow errors (which is probably unintentional).2028      }2029      render() {2030        return this.props.children;2031      }2032    }2033    const Throws = () => {2034      throw new Error('expected');2035    };2036    const container = document.createElement('div');2037    expect(() => {2038      ReactDOM.render(2039        <InvalidErrorBoundary>2040          <Throws />2041        </InvalidErrorBoundary>,2042        container,2043      );2044    }).toWarnDev(2045      'InvalidErrorBoundary: Error boundaries should implement getDerivedStateFromError(). ' +2046        'In that method, return a state update to display an error message or fallback UI.',2047      {withoutStack: true},2048    );2049    expect(container.textContent).toBe('');2050  });2051  it('should call both componentDidCatch and getDerivedStateFromError if both exist on a component', () => {2052    let componentDidCatchError, getDerivedStateFromErrorError;2053    class ErrorBoundaryWithBothMethods extends React.Component {2054      state = {error: null};2055      static getDerivedStateFromError(error) {2056        getDerivedStateFromErrorError = error;2057        return {error};2058      }2059      componentDidCatch(error, info) {2060        componentDidCatchError = error;2061      }2062      render() {2063        return this.state.error ? 'ErrorBoundary' : this.props.children;2064      }2065    }2066    const thrownError = new Error('expected');2067    const Throws = () => {2068      throw thrownError;2069    };2070    const container = document.createElement('div');2071    ReactDOM.render(2072      <ErrorBoundaryWithBothMethods>2073        <Throws />2074      </ErrorBoundaryWithBothMethods>,2075      container,2076    );2077    expect(container.textContent).toBe('ErrorBoundary');2078    expect(componentDidCatchError).toBe(thrownError);2079    expect(getDerivedStateFromErrorError).toBe(thrownError);2080  });2081  it('should catch errors from invariants in completion phase', () => {2082    const container = document.createElement('div');2083    ReactDOM.render(2084      <ErrorBoundary>2085        <input>2086          <div />2087        </input>2088      </ErrorBoundary>,2089      container,2090    );2091    expect(container.textContent).toContain(2092      'Caught an error: input is a void element tag',2093    );2094  });...ReactLegacyErrorBoundaries-test.internal.js
Source:ReactLegacyErrorBoundaries-test.internal.js  
1/**2 * Copyright (c) Facebook, Inc. and its affiliates.3 *4 * This source code is licensed under the MIT license found in the5 * LICENSE file in the root directory of this source tree.6 *7 * @emails react-core8 */9'use strict';10let PropTypes;11let React;12let ReactDOM;13let ReactFeatureFlags;14// TODO: Refactor this test once componentDidCatch setState is deprecated.15describe('ReactLegacyErrorBoundaries', () => {16  let log;17  let BrokenConstructor;18  let BrokenComponentWillMount;19  let BrokenComponentDidMount;20  let BrokenComponentWillReceiveProps;21  let BrokenComponentWillUpdate;22  let BrokenComponentDidUpdate;23  let BrokenComponentWillUnmount;24  let BrokenRenderErrorBoundary;25  let BrokenComponentWillMountErrorBoundary;26  let BrokenComponentDidMountErrorBoundary;27  let BrokenRender;28  let ErrorBoundary;29  let ErrorMessage;30  let NoopErrorBoundary;31  let RetryErrorBoundary;32  let Normal;33  beforeEach(() => {34    jest.resetModules();35    PropTypes = require('prop-types');36    ReactFeatureFlags = require('shared/ReactFeatureFlags');37    ReactFeatureFlags.replayFailedUnitOfWorkWithInvokeGuardedCallback = false;38    ReactDOM = require('react-dom');39    React = require('react');40    log = [];41    BrokenConstructor = class extends React.Component {42      constructor(props) {43        super(props);44        log.push('BrokenConstructor constructor [!]');45        throw new Error('Hello');46      }47      render() {48        log.push('BrokenConstructor render');49        return <div>{this.props.children}</div>;50      }51      UNSAFE_componentWillMount() {52        log.push('BrokenConstructor componentWillMount');53      }54      componentDidMount() {55        log.push('BrokenConstructor componentDidMount');56      }57      UNSAFE_componentWillReceiveProps() {58        log.push('BrokenConstructor componentWillReceiveProps');59      }60      UNSAFE_componentWillUpdate() {61        log.push('BrokenConstructor componentWillUpdate');62      }63      componentDidUpdate() {64        log.push('BrokenConstructor componentDidUpdate');65      }66      componentWillUnmount() {67        log.push('BrokenConstructor componentWillUnmount');68      }69    };70    BrokenComponentWillMount = class extends React.Component {71      constructor(props) {72        super(props);73        log.push('BrokenComponentWillMount constructor');74      }75      render() {76        log.push('BrokenComponentWillMount render');77        return <div>{this.props.children}</div>;78      }79      UNSAFE_componentWillMount() {80        log.push('BrokenComponentWillMount componentWillMount [!]');81        throw new Error('Hello');82      }83      componentDidMount() {84        log.push('BrokenComponentWillMount componentDidMount');85      }86      UNSAFE_componentWillReceiveProps() {87        log.push('BrokenComponentWillMount componentWillReceiveProps');88      }89      UNSAFE_componentWillUpdate() {90        log.push('BrokenComponentWillMount componentWillUpdate');91      }92      componentDidUpdate() {93        log.push('BrokenComponentWillMount componentDidUpdate');94      }95      componentWillUnmount() {96        log.push('BrokenComponentWillMount componentWillUnmount');97      }98    };99    BrokenComponentDidMount = class extends React.Component {100      constructor(props) {101        super(props);102        log.push('BrokenComponentDidMount constructor');103      }104      render() {105        log.push('BrokenComponentDidMount render');106        return <div>{this.props.children}</div>;107      }108      UNSAFE_componentWillMount() {109        log.push('BrokenComponentDidMount componentWillMount');110      }111      componentDidMount() {112        log.push('BrokenComponentDidMount componentDidMount [!]');113        throw new Error('Hello');114      }115      UNSAFE_componentWillReceiveProps() {116        log.push('BrokenComponentDidMount componentWillReceiveProps');117      }118      UNSAFE_componentWillUpdate() {119        log.push('BrokenComponentDidMount componentWillUpdate');120      }121      componentDidUpdate() {122        log.push('BrokenComponentDidMount componentDidUpdate');123      }124      componentWillUnmount() {125        log.push('BrokenComponentDidMount componentWillUnmount');126      }127    };128    BrokenComponentWillReceiveProps = class extends React.Component {129      constructor(props) {130        super(props);131        log.push('BrokenComponentWillReceiveProps constructor');132      }133      render() {134        log.push('BrokenComponentWillReceiveProps render');135        return <div>{this.props.children}</div>;136      }137      UNSAFE_componentWillMount() {138        log.push('BrokenComponentWillReceiveProps componentWillMount');139      }140      componentDidMount() {141        log.push('BrokenComponentWillReceiveProps componentDidMount');142      }143      UNSAFE_componentWillReceiveProps() {144        log.push(145          'BrokenComponentWillReceiveProps componentWillReceiveProps [!]',146        );147        throw new Error('Hello');148      }149      UNSAFE_componentWillUpdate() {150        log.push('BrokenComponentWillReceiveProps componentWillUpdate');151      }152      componentDidUpdate() {153        log.push('BrokenComponentWillReceiveProps componentDidUpdate');154      }155      componentWillUnmount() {156        log.push('BrokenComponentWillReceiveProps componentWillUnmount');157      }158    };159    BrokenComponentWillUpdate = class extends React.Component {160      constructor(props) {161        super(props);162        log.push('BrokenComponentWillUpdate constructor');163      }164      render() {165        log.push('BrokenComponentWillUpdate render');166        return <div>{this.props.children}</div>;167      }168      UNSAFE_componentWillMount() {169        log.push('BrokenComponentWillUpdate componentWillMount');170      }171      componentDidMount() {172        log.push('BrokenComponentWillUpdate componentDidMount');173      }174      UNSAFE_componentWillReceiveProps() {175        log.push('BrokenComponentWillUpdate componentWillReceiveProps');176      }177      UNSAFE_componentWillUpdate() {178        log.push('BrokenComponentWillUpdate componentWillUpdate [!]');179        throw new Error('Hello');180      }181      componentDidUpdate() {182        log.push('BrokenComponentWillUpdate componentDidUpdate');183      }184      componentWillUnmount() {185        log.push('BrokenComponentWillUpdate componentWillUnmount');186      }187    };188    BrokenComponentDidUpdate = class extends React.Component {189      static defaultProps = {190        errorText: 'Hello',191      };192      constructor(props) {193        super(props);194        log.push('BrokenComponentDidUpdate constructor');195      }196      render() {197        log.push('BrokenComponentDidUpdate render');198        return <div>{this.props.children}</div>;199      }200      UNSAFE_componentWillMount() {201        log.push('BrokenComponentDidUpdate componentWillMount');202      }203      componentDidMount() {204        log.push('BrokenComponentDidUpdate componentDidMount');205      }206      UNSAFE_componentWillReceiveProps() {207        log.push('BrokenComponentDidUpdate componentWillReceiveProps');208      }209      UNSAFE_componentWillUpdate() {210        log.push('BrokenComponentDidUpdate componentWillUpdate');211      }212      componentDidUpdate() {213        log.push('BrokenComponentDidUpdate componentDidUpdate [!]');214        throw new Error(this.props.errorText);215      }216      componentWillUnmount() {217        log.push('BrokenComponentDidUpdate componentWillUnmount');218      }219    };220    BrokenComponentWillUnmount = class extends React.Component {221      static defaultProps = {222        errorText: 'Hello',223      };224      constructor(props) {225        super(props);226        log.push('BrokenComponentWillUnmount constructor');227      }228      render() {229        log.push('BrokenComponentWillUnmount render');230        return <div>{this.props.children}</div>;231      }232      UNSAFE_componentWillMount() {233        log.push('BrokenComponentWillUnmount componentWillMount');234      }235      componentDidMount() {236        log.push('BrokenComponentWillUnmount componentDidMount');237      }238      UNSAFE_componentWillReceiveProps() {239        log.push('BrokenComponentWillUnmount componentWillReceiveProps');240      }241      UNSAFE_componentWillUpdate() {242        log.push('BrokenComponentWillUnmount componentWillUpdate');243      }244      componentDidUpdate() {245        log.push('BrokenComponentWillUnmount componentDidUpdate');246      }247      componentWillUnmount() {248        log.push('BrokenComponentWillUnmount componentWillUnmount [!]');249        throw new Error(this.props.errorText);250      }251    };252    BrokenComponentWillMountErrorBoundary = class extends React.Component {253      constructor(props) {254        super(props);255        this.state = {error: null};256        log.push('BrokenComponentWillMountErrorBoundary constructor');257      }258      render() {259        if (this.state.error) {260          log.push('BrokenComponentWillMountErrorBoundary render error');261          return <div>Caught an error: {this.state.error.message}.</div>;262        }263        log.push('BrokenComponentWillMountErrorBoundary render success');264        return <div>{this.props.children}</div>;265      }266      UNSAFE_componentWillMount() {267        log.push(268          'BrokenComponentWillMountErrorBoundary componentWillMount [!]',269        );270        throw new Error('Hello');271      }272      componentDidMount() {273        log.push('BrokenComponentWillMountErrorBoundary componentDidMount');274      }275      componentWillUnmount() {276        log.push('BrokenComponentWillMountErrorBoundary componentWillUnmount');277      }278      componentDidCatch(error) {279        log.push('BrokenComponentWillMountErrorBoundary componentDidCatch');280        this.setState({error});281      }282    };283    BrokenComponentDidMountErrorBoundary = class extends React.Component {284      constructor(props) {285        super(props);286        this.state = {error: null};287        log.push('BrokenComponentDidMountErrorBoundary constructor');288      }289      render() {290        if (this.state.error) {291          log.push('BrokenComponentDidMountErrorBoundary render error');292          return <div>Caught an error: {this.state.error.message}.</div>;293        }294        log.push('BrokenComponentDidMountErrorBoundary render success');295        return <div>{this.props.children}</div>;296      }297      UNSAFE_componentWillMount() {298        log.push('BrokenComponentDidMountErrorBoundary componentWillMount');299      }300      componentDidMount() {301        log.push('BrokenComponentDidMountErrorBoundary componentDidMount [!]');302        throw new Error('Hello');303      }304      componentWillUnmount() {305        log.push('BrokenComponentDidMountErrorBoundary componentWillUnmount');306      }307      componentDidCatch(error) {308        log.push('BrokenComponentDidMountErrorBoundary componentDidCatch');309        this.setState({error});310      }311    };312    BrokenRenderErrorBoundary = class extends React.Component {313      constructor(props) {314        super(props);315        this.state = {error: null};316        log.push('BrokenRenderErrorBoundary constructor');317      }318      render() {319        if (this.state.error) {320          log.push('BrokenRenderErrorBoundary render error [!]');321          throw new Error('Hello');322        }323        log.push('BrokenRenderErrorBoundary render success');324        return <div>{this.props.children}</div>;325      }326      UNSAFE_componentWillMount() {327        log.push('BrokenRenderErrorBoundary componentWillMount');328      }329      componentDidMount() {330        log.push('BrokenRenderErrorBoundary componentDidMount');331      }332      componentWillUnmount() {333        log.push('BrokenRenderErrorBoundary componentWillUnmount');334      }335      componentDidCatch(error) {336        log.push('BrokenRenderErrorBoundary componentDidCatch');337        this.setState({error});338      }339    };340    BrokenRender = class extends React.Component {341      constructor(props) {342        super(props);343        log.push('BrokenRender constructor');344      }345      render() {346        log.push('BrokenRender render [!]');347        throw new Error('Hello');348      }349      UNSAFE_componentWillMount() {350        log.push('BrokenRender componentWillMount');351      }352      componentDidMount() {353        log.push('BrokenRender componentDidMount');354      }355      UNSAFE_componentWillReceiveProps() {356        log.push('BrokenRender componentWillReceiveProps');357      }358      UNSAFE_componentWillUpdate() {359        log.push('BrokenRender componentWillUpdate');360      }361      componentDidUpdate() {362        log.push('BrokenRender componentDidUpdate');363      }364      componentWillUnmount() {365        log.push('BrokenRender componentWillUnmount');366      }367    };368    NoopErrorBoundary = class extends React.Component {369      constructor(props) {370        super(props);371        log.push('NoopErrorBoundary constructor');372      }373      render() {374        log.push('NoopErrorBoundary render');375        return <BrokenRender />;376      }377      UNSAFE_componentWillMount() {378        log.push('NoopErrorBoundary componentWillMount');379      }380      componentDidMount() {381        log.push('NoopErrorBoundary componentDidMount');382      }383      componentWillUnmount() {384        log.push('NoopErrorBoundary componentWillUnmount');385      }386      componentDidCatch() {387        log.push('NoopErrorBoundary componentDidCatch');388      }389    };390    Normal = class extends React.Component {391      static defaultProps = {392        logName: 'Normal',393      };394      constructor(props) {395        super(props);396        log.push(`${this.props.logName} constructor`);397      }398      render() {399        log.push(`${this.props.logName} render`);400        return <div>{this.props.children}</div>;401      }402      UNSAFE_componentWillMount() {403        log.push(`${this.props.logName} componentWillMount`);404      }405      componentDidMount() {406        log.push(`${this.props.logName} componentDidMount`);407      }408      UNSAFE_componentWillReceiveProps() {409        log.push(`${this.props.logName} componentWillReceiveProps`);410      }411      UNSAFE_componentWillUpdate() {412        log.push(`${this.props.logName} componentWillUpdate`);413      }414      componentDidUpdate() {415        log.push(`${this.props.logName} componentDidUpdate`);416      }417      componentWillUnmount() {418        log.push(`${this.props.logName} componentWillUnmount`);419      }420    };421    ErrorBoundary = class extends React.Component {422      constructor(props) {423        super(props);424        this.state = {error: null};425        log.push(`${this.props.logName} constructor`);426      }427      render() {428        if (this.state.error && !this.props.forceRetry) {429          log.push(`${this.props.logName} render error`);430          return this.props.renderError(this.state.error, this.props);431        }432        log.push(`${this.props.logName} render success`);433        return <div>{this.props.children}</div>;434      }435      componentDidCatch(error) {436        log.push(`${this.props.logName} componentDidCatch`);437        this.setState({error});438      }439      UNSAFE_componentWillMount() {440        log.push(`${this.props.logName} componentWillMount`);441      }442      componentDidMount() {443        log.push(`${this.props.logName} componentDidMount`);444      }445      UNSAFE_componentWillReceiveProps() {446        log.push(`${this.props.logName} componentWillReceiveProps`);447      }448      UNSAFE_componentWillUpdate() {449        log.push(`${this.props.logName} componentWillUpdate`);450      }451      componentDidUpdate() {452        log.push(`${this.props.logName} componentDidUpdate`);453      }454      componentWillUnmount() {455        log.push(`${this.props.logName} componentWillUnmount`);456      }457    };458    ErrorBoundary.defaultProps = {459      logName: 'ErrorBoundary',460      renderError(error, props) {461        return (462          <div ref={props.errorMessageRef}>463            Caught an error: {error.message}.464          </div>465        );466      },467    };468    RetryErrorBoundary = class extends React.Component {469      constructor(props) {470        super(props);471        log.push('RetryErrorBoundary constructor');472      }473      render() {474        log.push('RetryErrorBoundary render');475        return <BrokenRender />;476      }477      UNSAFE_componentWillMount() {478        log.push('RetryErrorBoundary componentWillMount');479      }480      componentDidMount() {481        log.push('RetryErrorBoundary componentDidMount');482      }483      componentWillUnmount() {484        log.push('RetryErrorBoundary componentWillUnmount');485      }486      componentDidCatch(error) {487        log.push('RetryErrorBoundary componentDidCatch [!]');488        // In Fiber, calling setState() (and failing) is treated as a rethrow.489        this.setState({});490      }491    };492    ErrorMessage = class extends React.Component {493      constructor(props) {494        super(props);495        log.push('ErrorMessage constructor');496      }497      UNSAFE_componentWillMount() {498        log.push('ErrorMessage componentWillMount');499      }500      componentDidMount() {501        log.push('ErrorMessage componentDidMount');502      }503      componentWillUnmount() {504        log.push('ErrorMessage componentWillUnmount');505      }506      render() {507        log.push('ErrorMessage render');508        return <div>Caught an error: {this.props.message}.</div>;509      }510    };511  });512  it('does not swallow exceptions on mounting without boundaries', () => {513    let container = document.createElement('div');514    expect(() => {515      ReactDOM.render(<BrokenRender />, container);516    }).toThrow('Hello');517    container = document.createElement('div');518    expect(() => {519      ReactDOM.render(<BrokenComponentWillMount />, container);520    }).toThrow('Hello');521    container = document.createElement('div');522    expect(() => {523      ReactDOM.render(<BrokenComponentDidMount />, container);524    }).toThrow('Hello');525  });526  it('does not swallow exceptions on updating without boundaries', () => {527    let container = document.createElement('div');528    ReactDOM.render(<BrokenComponentWillUpdate />, container);529    expect(() => {530      ReactDOM.render(<BrokenComponentWillUpdate />, container);531    }).toThrow('Hello');532    container = document.createElement('div');533    ReactDOM.render(<BrokenComponentWillReceiveProps />, container);534    expect(() => {535      ReactDOM.render(<BrokenComponentWillReceiveProps />, container);536    }).toThrow('Hello');537    container = document.createElement('div');538    ReactDOM.render(<BrokenComponentDidUpdate />, container);539    expect(() => {540      ReactDOM.render(<BrokenComponentDidUpdate />, container);541    }).toThrow('Hello');542  });543  it('does not swallow exceptions on unmounting without boundaries', () => {544    const container = document.createElement('div');545    ReactDOM.render(<BrokenComponentWillUnmount />, container);546    expect(() => {547      ReactDOM.unmountComponentAtNode(container);548    }).toThrow('Hello');549  });550  it('prevents errors from leaking into other roots', () => {551    const container1 = document.createElement('div');552    const container2 = document.createElement('div');553    const container3 = document.createElement('div');554    ReactDOM.render(<span>Before 1</span>, container1);555    expect(() => {556      ReactDOM.render(<BrokenRender />, container2);557    }).toThrow('Hello');558    ReactDOM.render(559      <ErrorBoundary>560        <BrokenRender />561      </ErrorBoundary>,562      container3,563    );564    expect(container1.firstChild.textContent).toBe('Before 1');565    expect(container2.firstChild).toBe(null);566    expect(container3.firstChild.textContent).toBe('Caught an error: Hello.');567    ReactDOM.render(<span>After 1</span>, container1);568    ReactDOM.render(<span>After 2</span>, container2);569    ReactDOM.render(570      <ErrorBoundary forceRetry={true}>After 3</ErrorBoundary>,571      container3,572    );573    expect(container1.firstChild.textContent).toBe('After 1');574    expect(container2.firstChild.textContent).toBe('After 2');575    expect(container3.firstChild.textContent).toBe('After 3');576    ReactDOM.unmountComponentAtNode(container1);577    ReactDOM.unmountComponentAtNode(container2);578    ReactDOM.unmountComponentAtNode(container3);579    expect(container1.firstChild).toBe(null);580    expect(container2.firstChild).toBe(null);581    expect(container3.firstChild).toBe(null);582  });583  it('renders an error state if child throws in render', () => {584    const container = document.createElement('div');585    ReactDOM.render(586      <ErrorBoundary>587        <BrokenRender />588      </ErrorBoundary>,589      container,590    );591    expect(container.firstChild.textContent).toBe('Caught an error: Hello.');592    expect(log).toEqual([593      'ErrorBoundary constructor',594      'ErrorBoundary componentWillMount',595      'ErrorBoundary render success',596      'BrokenRender constructor',597      'BrokenRender componentWillMount',598      'BrokenRender render [!]',599      // Fiber mounts with null children before capturing error600      'ErrorBoundary componentDidMount',601      // Catch and render an error message602      'ErrorBoundary componentDidCatch',603      'ErrorBoundary componentWillUpdate',604      'ErrorBoundary render error',605      'ErrorBoundary componentDidUpdate',606    ]);607    log.length = 0;608    ReactDOM.unmountComponentAtNode(container);609    expect(log).toEqual(['ErrorBoundary componentWillUnmount']);610  });611  it('renders an error state if child throws in constructor', () => {612    const container = document.createElement('div');613    ReactDOM.render(614      <ErrorBoundary>615        <BrokenConstructor />616      </ErrorBoundary>,617      container,618    );619    expect(container.firstChild.textContent).toBe('Caught an error: Hello.');620    expect(log).toEqual([621      'ErrorBoundary constructor',622      'ErrorBoundary componentWillMount',623      'ErrorBoundary render success',624      'BrokenConstructor constructor [!]',625      // Fiber mounts with null children before capturing error626      'ErrorBoundary componentDidMount',627      // Catch and render an error message628      'ErrorBoundary componentDidCatch',629      'ErrorBoundary componentWillUpdate',630      'ErrorBoundary render error',631      'ErrorBoundary componentDidUpdate',632    ]);633    log.length = 0;634    ReactDOM.unmountComponentAtNode(container);635    expect(log).toEqual(['ErrorBoundary componentWillUnmount']);636  });637  it('renders an error state if child throws in componentWillMount', () => {638    const container = document.createElement('div');639    ReactDOM.render(640      <ErrorBoundary>641        <BrokenComponentWillMount />642      </ErrorBoundary>,643      container,644    );645    expect(container.firstChild.textContent).toBe('Caught an error: Hello.');646    expect(log).toEqual([647      'ErrorBoundary constructor',648      'ErrorBoundary componentWillMount',649      'ErrorBoundary render success',650      'BrokenComponentWillMount constructor',651      'BrokenComponentWillMount componentWillMount [!]',652      'ErrorBoundary componentDidMount',653      'ErrorBoundary componentDidCatch',654      'ErrorBoundary componentWillUpdate',655      'ErrorBoundary render error',656      'ErrorBoundary componentDidUpdate',657    ]);658    log.length = 0;659    ReactDOM.unmountComponentAtNode(container);660    expect(log).toEqual(['ErrorBoundary componentWillUnmount']);661  });662  it('renders an error state if context provider throws in componentWillMount', () => {663    class BrokenComponentWillMountWithContext extends React.Component {664      static childContextTypes = {foo: PropTypes.number};665      getChildContext() {666        return {foo: 42};667      }668      render() {669        return <div>{this.props.children}</div>;670      }671      UNSAFE_componentWillMount() {672        throw new Error('Hello');673      }674    }675    const container = document.createElement('div');676    ReactDOM.render(677      <ErrorBoundary>678        <BrokenComponentWillMountWithContext />679      </ErrorBoundary>,680      container,681    );682    expect(container.firstChild.textContent).toBe('Caught an error: Hello.');683  });684  it('renders an error state if module-style context provider throws in componentWillMount', () => {685    function BrokenComponentWillMountWithContext() {686      return {687        getChildContext() {688          return {foo: 42};689        },690        render() {691          return <div>{this.props.children}</div>;692        },693        UNSAFE_componentWillMount() {694          throw new Error('Hello');695        },696      };697    }698    BrokenComponentWillMountWithContext.childContextTypes = {699      foo: PropTypes.number,700    };701    const container = document.createElement('div');702    ReactDOM.render(703      <ErrorBoundary>704        <BrokenComponentWillMountWithContext />705      </ErrorBoundary>,706      container,707    );708    expect(container.firstChild.textContent).toBe('Caught an error: Hello.');709  });710  it('mounts the error message if mounting fails', () => {711    function renderError(error) {712      return <ErrorMessage message={error.message} />;713    }714    const container = document.createElement('div');715    ReactDOM.render(716      <ErrorBoundary renderError={renderError}>717        <BrokenRender />718      </ErrorBoundary>,719      container,720    );721    expect(log).toEqual([722      'ErrorBoundary constructor',723      'ErrorBoundary componentWillMount',724      'ErrorBoundary render success',725      'BrokenRender constructor',726      'BrokenRender componentWillMount',727      'BrokenRender render [!]',728      'ErrorBoundary componentDidMount',729      'ErrorBoundary componentDidCatch',730      'ErrorBoundary componentWillUpdate',731      'ErrorBoundary render error',732      'ErrorMessage constructor',733      'ErrorMessage componentWillMount',734      'ErrorMessage render',735      'ErrorMessage componentDidMount',736      'ErrorBoundary componentDidUpdate',737    ]);738    log.length = 0;739    ReactDOM.unmountComponentAtNode(container);740    expect(log).toEqual([741      'ErrorBoundary componentWillUnmount',742      'ErrorMessage componentWillUnmount',743    ]);744  });745  it('propagates errors on retry on mounting', () => {746    const container = document.createElement('div');747    ReactDOM.render(748      <ErrorBoundary>749        <RetryErrorBoundary>750          <BrokenRender />751        </RetryErrorBoundary>752      </ErrorBoundary>,753      container,754    );755    expect(container.firstChild.textContent).toBe('Caught an error: Hello.');756    expect(log).toEqual([757      'ErrorBoundary constructor',758      'ErrorBoundary componentWillMount',759      'ErrorBoundary render success',760      'RetryErrorBoundary constructor',761      'RetryErrorBoundary componentWillMount',762      'RetryErrorBoundary render',763      'BrokenRender constructor',764      'BrokenRender componentWillMount',765      'BrokenRender render [!]',766      // In Fiber, failed error boundaries render null before attempting to recover767      'RetryErrorBoundary componentDidMount',768      'RetryErrorBoundary componentDidCatch [!]',769      'ErrorBoundary componentDidMount',770      // Retry771      'RetryErrorBoundary render',772      'BrokenRender constructor',773      'BrokenRender componentWillMount',774      'BrokenRender render [!]',775      // This time, the error propagates to the higher boundary776      'RetryErrorBoundary componentWillUnmount',777      'ErrorBoundary componentDidCatch',778      // Render the error779      'ErrorBoundary componentWillUpdate',780      'ErrorBoundary render error',781      'ErrorBoundary componentDidUpdate',782    ]);783    log.length = 0;784    ReactDOM.unmountComponentAtNode(container);785    expect(log).toEqual(['ErrorBoundary componentWillUnmount']);786  });787  it('propagates errors inside boundary during componentWillMount', () => {788    const container = document.createElement('div');789    ReactDOM.render(790      <ErrorBoundary>791        <BrokenComponentWillMountErrorBoundary />792      </ErrorBoundary>,793      container,794    );795    expect(container.firstChild.textContent).toBe('Caught an error: Hello.');796    expect(log).toEqual([797      'ErrorBoundary constructor',798      'ErrorBoundary componentWillMount',799      'ErrorBoundary render success',800      'BrokenComponentWillMountErrorBoundary constructor',801      'BrokenComponentWillMountErrorBoundary componentWillMount [!]',802      // The error propagates to the higher boundary803      'ErrorBoundary componentDidMount',804      'ErrorBoundary componentDidCatch',805      'ErrorBoundary componentWillUpdate',806      'ErrorBoundary render error',807      'ErrorBoundary componentDidUpdate',808    ]);809    log.length = 0;810    ReactDOM.unmountComponentAtNode(container);811    expect(log).toEqual(['ErrorBoundary componentWillUnmount']);812  });813  it('propagates errors inside boundary while rendering error state', () => {814    const container = document.createElement('div');815    ReactDOM.render(816      <ErrorBoundary>817        <BrokenRenderErrorBoundary>818          <BrokenRender />819        </BrokenRenderErrorBoundary>820      </ErrorBoundary>,821      container,822    );823    expect(container.firstChild.textContent).toBe('Caught an error: Hello.');824    expect(log).toEqual([825      'ErrorBoundary constructor',826      'ErrorBoundary componentWillMount',827      'ErrorBoundary render success',828      'BrokenRenderErrorBoundary constructor',829      'BrokenRenderErrorBoundary componentWillMount',830      'BrokenRenderErrorBoundary render success',831      'BrokenRender constructor',832      'BrokenRender componentWillMount',833      'BrokenRender render [!]',834      // The first error boundary catches the error835      // It adjusts state but throws displaying the message836      // Finish mounting with null children837      'BrokenRenderErrorBoundary componentDidMount',838      // Attempt to handle the error839      'BrokenRenderErrorBoundary componentDidCatch',840      'ErrorBoundary componentDidMount',841      'BrokenRenderErrorBoundary render error [!]',842      // Boundary fails with new error, propagate to next boundary843      'BrokenRenderErrorBoundary componentWillUnmount',844      // Attempt to handle the error again845      'ErrorBoundary componentDidCatch',846      'ErrorBoundary componentWillUpdate',847      'ErrorBoundary render error',848      'ErrorBoundary componentDidUpdate',849    ]);850    log.length = 0;851    ReactDOM.unmountComponentAtNode(container);852    expect(log).toEqual(['ErrorBoundary componentWillUnmount']);853  });854  it('does not call componentWillUnmount when aborting initial mount', () => {855    const container = document.createElement('div');856    ReactDOM.render(857      <ErrorBoundary>858        <Normal />859        <BrokenRender />860        <Normal />861      </ErrorBoundary>,862      container,863    );864    expect(container.firstChild.textContent).toBe('Caught an error: Hello.');865    expect(log).toEqual([866      'ErrorBoundary constructor',867      'ErrorBoundary componentWillMount',868      'ErrorBoundary render success',869      // Render first child870      'Normal constructor',871      'Normal componentWillMount',872      'Normal render',873      // Render second child (it throws)874      'BrokenRender constructor',875      'BrokenRender componentWillMount',876      'BrokenRender render [!]',877      // Render third child, even though an earlier sibling threw.878      'Normal constructor',879      'Normal componentWillMount',880      'Normal render',881      // Finish mounting with null children882      'ErrorBoundary componentDidMount',883      // Handle the error884      'ErrorBoundary componentDidCatch',885      // Render the error message886      'ErrorBoundary componentWillUpdate',887      'ErrorBoundary render error',888      'ErrorBoundary componentDidUpdate',889    ]);890    log.length = 0;891    ReactDOM.unmountComponentAtNode(container);892    expect(log).toEqual(['ErrorBoundary componentWillUnmount']);893  });894  it('resets callback refs if mounting aborts', () => {895    function childRef(x) {896      log.push('Child ref is set to ' + x);897    }898    function errorMessageRef(x) {899      log.push('Error message ref is set to ' + x);900    }901    const container = document.createElement('div');902    ReactDOM.render(903      <ErrorBoundary errorMessageRef={errorMessageRef}>904        <div ref={childRef} />905        <BrokenRender />906      </ErrorBoundary>,907      container,908    );909    expect(container.textContent).toBe('Caught an error: Hello.');910    expect(log).toEqual([911      'ErrorBoundary constructor',912      'ErrorBoundary componentWillMount',913      'ErrorBoundary render success',914      'BrokenRender constructor',915      'BrokenRender componentWillMount',916      'BrokenRender render [!]',917      // Handle error:918      // Finish mounting with null children919      'ErrorBoundary componentDidMount',920      // Handle the error921      'ErrorBoundary componentDidCatch',922      // Render the error message923      'ErrorBoundary componentWillUpdate',924      'ErrorBoundary render error',925      'Error message ref is set to [object HTMLDivElement]',926      'ErrorBoundary componentDidUpdate',927    ]);928    log.length = 0;929    ReactDOM.unmountComponentAtNode(container);930    expect(log).toEqual([931      'ErrorBoundary componentWillUnmount',932      'Error message ref is set to null',933    ]);934  });935  it('resets object refs if mounting aborts', () => {936    let childRef = React.createRef();937    let errorMessageRef = React.createRef();938    const container = document.createElement('div');939    ReactDOM.render(940      <ErrorBoundary errorMessageRef={errorMessageRef}>941        <div ref={childRef} />942        <BrokenRender />943      </ErrorBoundary>,944      container,945    );946    expect(container.textContent).toBe('Caught an error: Hello.');947    expect(log).toEqual([948      'ErrorBoundary constructor',949      'ErrorBoundary componentWillMount',950      'ErrorBoundary render success',951      'BrokenRender constructor',952      'BrokenRender componentWillMount',953      'BrokenRender render [!]',954      // Handle error:955      // Finish mounting with null children956      'ErrorBoundary componentDidMount',957      // Handle the error958      'ErrorBoundary componentDidCatch',959      // Render the error message960      'ErrorBoundary componentWillUpdate',961      'ErrorBoundary render error',962      'ErrorBoundary componentDidUpdate',963    ]);964    expect(errorMessageRef.current.toString()).toEqual(965      '[object HTMLDivElement]',966    );967    log.length = 0;968    ReactDOM.unmountComponentAtNode(container);969    expect(log).toEqual(['ErrorBoundary componentWillUnmount']);970    expect(errorMessageRef.current).toEqual(null);971  });972  it('successfully mounts if no error occurs', () => {973    const container = document.createElement('div');974    ReactDOM.render(975      <ErrorBoundary>976        <div>Mounted successfully.</div>977      </ErrorBoundary>,978      container,979    );980    expect(container.firstChild.textContent).toBe('Mounted successfully.');981    expect(log).toEqual([982      'ErrorBoundary constructor',983      'ErrorBoundary componentWillMount',984      'ErrorBoundary render success',985      'ErrorBoundary componentDidMount',986    ]);987    log.length = 0;988    ReactDOM.unmountComponentAtNode(container);989    expect(log).toEqual(['ErrorBoundary componentWillUnmount']);990  });991  it('catches if child throws in constructor during update', () => {992    const container = document.createElement('div');993    ReactDOM.render(994      <ErrorBoundary>995        <Normal />996      </ErrorBoundary>,997      container,998    );999    log.length = 0;1000    ReactDOM.render(1001      <ErrorBoundary>1002        <Normal />1003        <Normal logName="Normal2" />1004        <BrokenConstructor />1005      </ErrorBoundary>,1006      container,1007    );1008    expect(container.textContent).toBe('Caught an error: Hello.');1009    expect(log).toEqual([1010      'ErrorBoundary componentWillReceiveProps',1011      'ErrorBoundary componentWillUpdate',1012      'ErrorBoundary render success',1013      'Normal componentWillReceiveProps',1014      'Normal componentWillUpdate',1015      'Normal render',1016      // Normal2 will attempt to mount:1017      'Normal2 constructor',1018      'Normal2 componentWillMount',1019      'Normal2 render',1020      // BrokenConstructor will abort rendering:1021      'BrokenConstructor constructor [!]',1022      // Finish updating with null children1023      'Normal componentWillUnmount',1024      'ErrorBoundary componentDidUpdate',1025      // Handle the error1026      'ErrorBoundary componentDidCatch',1027      // Render the error message1028      'ErrorBoundary componentWillUpdate',1029      'ErrorBoundary render error',1030      'ErrorBoundary componentDidUpdate',1031    ]);1032    log.length = 0;1033    ReactDOM.unmountComponentAtNode(container);1034    expect(log).toEqual(['ErrorBoundary componentWillUnmount']);1035  });1036  it('catches if child throws in componentWillMount during update', () => {1037    const container = document.createElement('div');1038    ReactDOM.render(1039      <ErrorBoundary>1040        <Normal />1041      </ErrorBoundary>,1042      container,1043    );1044    log.length = 0;1045    ReactDOM.render(1046      <ErrorBoundary>1047        <Normal />1048        <Normal logName="Normal2" />1049        <BrokenComponentWillMount />1050      </ErrorBoundary>,1051      container,1052    );1053    expect(container.textContent).toBe('Caught an error: Hello.');1054    expect(log).toEqual([1055      'ErrorBoundary componentWillReceiveProps',1056      'ErrorBoundary componentWillUpdate',1057      'ErrorBoundary render success',1058      'Normal componentWillReceiveProps',1059      'Normal componentWillUpdate',1060      'Normal render',1061      // Normal2 will attempt to mount:1062      'Normal2 constructor',1063      'Normal2 componentWillMount',1064      'Normal2 render',1065      // BrokenComponentWillMount will abort rendering:1066      'BrokenComponentWillMount constructor',1067      'BrokenComponentWillMount componentWillMount [!]',1068      // Finish updating with null children1069      'Normal componentWillUnmount',1070      'ErrorBoundary componentDidUpdate',1071      // Handle the error1072      'ErrorBoundary componentDidCatch',1073      // Render the error message1074      'ErrorBoundary componentWillUpdate',1075      'ErrorBoundary render error',1076      'ErrorBoundary componentDidUpdate',1077    ]);1078    log.length = 0;1079    ReactDOM.unmountComponentAtNode(container);1080    expect(log).toEqual(['ErrorBoundary componentWillUnmount']);1081  });1082  it('catches if child throws in componentWillReceiveProps during update', () => {1083    const container = document.createElement('div');1084    ReactDOM.render(1085      <ErrorBoundary>1086        <Normal />1087        <BrokenComponentWillReceiveProps />1088      </ErrorBoundary>,1089      container,1090    );1091    log.length = 0;1092    ReactDOM.render(1093      <ErrorBoundary>1094        <Normal />1095        <BrokenComponentWillReceiveProps />1096      </ErrorBoundary>,1097      container,1098    );1099    expect(container.textContent).toBe('Caught an error: Hello.');1100    expect(log).toEqual([1101      'ErrorBoundary componentWillReceiveProps',1102      'ErrorBoundary componentWillUpdate',1103      'ErrorBoundary render success',1104      'Normal componentWillReceiveProps',1105      'Normal componentWillUpdate',1106      'Normal render',1107      // BrokenComponentWillReceiveProps will abort rendering:1108      'BrokenComponentWillReceiveProps componentWillReceiveProps [!]',1109      // Finish updating with null children1110      'Normal componentWillUnmount',1111      'BrokenComponentWillReceiveProps componentWillUnmount',1112      'ErrorBoundary componentDidUpdate',1113      // Handle the error1114      'ErrorBoundary componentDidCatch',1115      'ErrorBoundary componentWillUpdate',1116      'ErrorBoundary render error',1117      'ErrorBoundary componentDidUpdate',1118    ]);1119    log.length = 0;1120    ReactDOM.unmountComponentAtNode(container);1121    expect(log).toEqual(['ErrorBoundary componentWillUnmount']);1122  });1123  it('catches if child throws in componentWillUpdate during update', () => {1124    const container = document.createElement('div');1125    ReactDOM.render(1126      <ErrorBoundary>1127        <Normal />1128        <BrokenComponentWillUpdate />1129      </ErrorBoundary>,1130      container,1131    );1132    log.length = 0;1133    ReactDOM.render(1134      <ErrorBoundary>1135        <Normal />1136        <BrokenComponentWillUpdate />1137      </ErrorBoundary>,1138      container,1139    );1140    expect(container.textContent).toBe('Caught an error: Hello.');1141    expect(log).toEqual([1142      'ErrorBoundary componentWillReceiveProps',1143      'ErrorBoundary componentWillUpdate',1144      'ErrorBoundary render success',1145      'Normal componentWillReceiveProps',1146      'Normal componentWillUpdate',1147      'Normal render',1148      // BrokenComponentWillUpdate will abort rendering:1149      'BrokenComponentWillUpdate componentWillReceiveProps',1150      'BrokenComponentWillUpdate componentWillUpdate [!]',1151      // Finish updating with null children1152      'Normal componentWillUnmount',1153      'BrokenComponentWillUpdate componentWillUnmount',1154      'ErrorBoundary componentDidUpdate',1155      // Handle the error1156      'ErrorBoundary componentDidCatch',1157      'ErrorBoundary componentWillUpdate',1158      'ErrorBoundary render error',1159      'ErrorBoundary componentDidUpdate',1160    ]);1161    log.length = 0;1162    ReactDOM.unmountComponentAtNode(container);1163    expect(log).toEqual(['ErrorBoundary componentWillUnmount']);1164  });1165  it('catches if child throws in render during update', () => {1166    const container = document.createElement('div');1167    ReactDOM.render(1168      <ErrorBoundary>1169        <Normal />1170      </ErrorBoundary>,1171      container,1172    );1173    log.length = 0;1174    ReactDOM.render(1175      <ErrorBoundary>1176        <Normal />1177        <Normal logName="Normal2" />1178        <BrokenRender />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      // BrokenRender will abort rendering:1195      'BrokenRender constructor',1196      'BrokenRender componentWillMount',1197      'BrokenRender render [!]',1198      // Finish updating with null children1199      'Normal componentWillUnmount',1200      'ErrorBoundary componentDidUpdate',1201      // Handle the error1202      'ErrorBoundary componentDidCatch',1203      'ErrorBoundary componentWillUpdate',1204      'ErrorBoundary render error',1205      'ErrorBoundary componentDidUpdate',1206    ]);1207    log.length = 0;1208    ReactDOM.unmountComponentAtNode(container);1209    expect(log).toEqual(['ErrorBoundary componentWillUnmount']);1210  });1211  it('keeps refs up-to-date during updates', () => {1212    function child1Ref(x) {1213      log.push('Child1 ref is set to ' + x);1214    }1215    function child2Ref(x) {1216      log.push('Child2 ref is set to ' + x);1217    }1218    function errorMessageRef(x) {1219      log.push('Error message ref is set to ' + x);1220    }1221    const container = document.createElement('div');1222    ReactDOM.render(1223      <ErrorBoundary errorMessageRef={errorMessageRef}>1224        <div ref={child1Ref} />1225      </ErrorBoundary>,1226      container,1227    );1228    expect(log).toEqual([1229      'ErrorBoundary constructor',1230      'ErrorBoundary componentWillMount',1231      'ErrorBoundary render success',1232      'Child1 ref is set to [object HTMLDivElement]',1233      'ErrorBoundary componentDidMount',1234    ]);1235    log.length = 0;1236    ReactDOM.render(1237      <ErrorBoundary errorMessageRef={errorMessageRef}>1238        <div ref={child1Ref} />1239        <div ref={child2Ref} />1240        <BrokenRender />1241      </ErrorBoundary>,1242      container,1243    );1244    expect(container.textContent).toBe('Caught an error: Hello.');1245    expect(log).toEqual([1246      'ErrorBoundary componentWillReceiveProps',1247      'ErrorBoundary componentWillUpdate',1248      'ErrorBoundary render success',1249      // BrokenRender will abort rendering:1250      'BrokenRender constructor',1251      'BrokenRender componentWillMount',1252      'BrokenRender render [!]',1253      // Finish updating with null children1254      'Child1 ref is set to null',1255      'ErrorBoundary componentDidUpdate',1256      // Handle the error1257      'ErrorBoundary componentDidCatch',1258      'ErrorBoundary componentWillUpdate',1259      'ErrorBoundary render error',1260      'Error message ref is set to [object HTMLDivElement]',1261      // Child2 ref is never set because its mounting aborted1262      'ErrorBoundary componentDidUpdate',1263    ]);1264    log.length = 0;1265    ReactDOM.unmountComponentAtNode(container);1266    expect(log).toEqual([1267      'ErrorBoundary componentWillUnmount',1268      'Error message ref is set to null',1269    ]);1270  });1271  it('recovers from componentWillUnmount errors on update', () => {1272    const container = document.createElement('div');1273    ReactDOM.render(1274      <ErrorBoundary>1275        <BrokenComponentWillUnmount />1276        <BrokenComponentWillUnmount />1277        <Normal />1278      </ErrorBoundary>,1279      container,1280    );1281    log.length = 0;1282    ReactDOM.render(1283      <ErrorBoundary>1284        <BrokenComponentWillUnmount />1285      </ErrorBoundary>,1286      container,1287    );1288    expect(container.textContent).toBe('Caught an error: Hello.');1289    expect(log).toEqual([1290      'ErrorBoundary componentWillReceiveProps',1291      'ErrorBoundary componentWillUpdate',1292      'ErrorBoundary render success',1293      // Update existing child:1294      'BrokenComponentWillUnmount componentWillReceiveProps',1295      'BrokenComponentWillUnmount componentWillUpdate',1296      'BrokenComponentWillUnmount render',1297      // Unmounting throws:1298      'BrokenComponentWillUnmount componentWillUnmount [!]',1299      // Fiber proceeds with lifecycles despite errors1300      'Normal componentWillUnmount',1301      // The components have updated in this phase1302      'BrokenComponentWillUnmount componentDidUpdate',1303      'ErrorBoundary componentDidUpdate',1304      // Now that commit phase is done, Fiber unmounts the boundary's children1305      'BrokenComponentWillUnmount componentWillUnmount [!]',1306      'ErrorBoundary componentDidCatch',1307      // The initial render was aborted, so1308      // Fiber retries from the root.1309      'ErrorBoundary componentWillUpdate',1310      'ErrorBoundary componentDidUpdate',1311      // The second willUnmount error should be captured and logged, too.1312      'ErrorBoundary componentDidCatch',1313      'ErrorBoundary componentWillUpdate',1314      // Render an error now (stack will do it later)1315      'ErrorBoundary render error',1316      // Attempt to unmount previous child:1317      // Done1318      'ErrorBoundary componentDidUpdate',1319    ]);1320    log.length = 0;1321    ReactDOM.unmountComponentAtNode(container);1322    expect(log).toEqual(['ErrorBoundary componentWillUnmount']);1323  });1324  it('recovers from nested componentWillUnmount errors on update', () => {1325    const container = document.createElement('div');1326    ReactDOM.render(1327      <ErrorBoundary>1328        <Normal>1329          <BrokenComponentWillUnmount />1330        </Normal>1331        <BrokenComponentWillUnmount />1332      </ErrorBoundary>,1333      container,1334    );1335    log.length = 0;1336    ReactDOM.render(1337      <ErrorBoundary>1338        <Normal>1339          <BrokenComponentWillUnmount />1340        </Normal>1341      </ErrorBoundary>,1342      container,1343    );1344    expect(container.textContent).toBe('Caught an error: Hello.');1345    expect(log).toEqual([1346      'ErrorBoundary componentWillReceiveProps',1347      'ErrorBoundary componentWillUpdate',1348      'ErrorBoundary render success',1349      // Update existing children:1350      'Normal componentWillReceiveProps',1351      'Normal componentWillUpdate',1352      'Normal render',1353      'BrokenComponentWillUnmount componentWillReceiveProps',1354      'BrokenComponentWillUnmount componentWillUpdate',1355      'BrokenComponentWillUnmount render',1356      // Unmounting throws:1357      'BrokenComponentWillUnmount componentWillUnmount [!]',1358      // Fiber proceeds with lifecycles despite errors1359      'BrokenComponentWillUnmount componentDidUpdate',1360      'Normal componentDidUpdate',1361      'ErrorBoundary componentDidUpdate',1362      'Normal componentWillUnmount',1363      'BrokenComponentWillUnmount componentWillUnmount [!]',1364      // Now that commit phase is done, Fiber handles errors1365      'ErrorBoundary componentDidCatch',1366      // The initial render was aborted, so1367      // Fiber retries from the root.1368      'ErrorBoundary componentWillUpdate',1369      'ErrorBoundary componentDidUpdate',1370      // The second willUnmount error should be captured and logged, too.1371      'ErrorBoundary componentDidCatch',1372      'ErrorBoundary componentWillUpdate',1373      // Render an error now (stack will do it later)1374      'ErrorBoundary render error',1375      // Done1376      'ErrorBoundary componentDidUpdate',1377    ]);1378    log.length = 0;1379    ReactDOM.unmountComponentAtNode(container);1380    expect(log).toEqual(['ErrorBoundary componentWillUnmount']);1381  });1382  it('picks the right boundary when handling unmounting errors', () => {1383    function renderInnerError(error) {1384      return <div>Caught an inner error: {error.message}.</div>;1385    }1386    function renderOuterError(error) {1387      return <div>Caught an outer error: {error.message}.</div>;1388    }1389    const container = document.createElement('div');1390    ReactDOM.render(1391      <ErrorBoundary1392        logName="OuterErrorBoundary"1393        renderError={renderOuterError}>1394        <ErrorBoundary1395          logName="InnerErrorBoundary"1396          renderError={renderInnerError}>1397          <BrokenComponentWillUnmount />1398        </ErrorBoundary>1399      </ErrorBoundary>,1400      container,1401    );1402    log.length = 0;1403    ReactDOM.render(1404      <ErrorBoundary1405        logName="OuterErrorBoundary"1406        renderError={renderOuterError}>1407        <ErrorBoundary1408          logName="InnerErrorBoundary"1409          renderError={renderInnerError}1410        />1411      </ErrorBoundary>,1412      container,1413    );1414    expect(container.textContent).toBe('Caught an inner error: Hello.');1415    expect(log).toEqual([1416      // Update outer boundary1417      'OuterErrorBoundary componentWillReceiveProps',1418      'OuterErrorBoundary componentWillUpdate',1419      'OuterErrorBoundary render success',1420      // Update inner boundary1421      'InnerErrorBoundary componentWillReceiveProps',1422      'InnerErrorBoundary componentWillUpdate',1423      'InnerErrorBoundary render success',1424      // Try unmounting child1425      'BrokenComponentWillUnmount componentWillUnmount [!]',1426      // Fiber proceeds with lifecycles despite errors1427      // Inner and outer boundaries have updated in this phase1428      'InnerErrorBoundary componentDidUpdate',1429      'OuterErrorBoundary componentDidUpdate',1430      // Now that commit phase is done, Fiber handles errors1431      // Only inner boundary receives the error:1432      'InnerErrorBoundary componentDidCatch',1433      'InnerErrorBoundary componentWillUpdate',1434      // Render an error now1435      'InnerErrorBoundary render error',1436      // In Fiber, this was a local update to the1437      // inner boundary so only its hook fires1438      'InnerErrorBoundary componentDidUpdate',1439    ]);1440    log.length = 0;1441    ReactDOM.unmountComponentAtNode(container);1442    expect(log).toEqual([1443      'OuterErrorBoundary componentWillUnmount',1444      'InnerErrorBoundary componentWillUnmount',1445    ]);1446  });1447  it('can recover from error state', () => {1448    const container = document.createElement('div');1449    ReactDOM.render(1450      <ErrorBoundary>1451        <BrokenRender />1452      </ErrorBoundary>,1453      container,1454    );1455    ReactDOM.render(1456      <ErrorBoundary>1457        <Normal />1458      </ErrorBoundary>,1459      container,1460    );1461    // Error boundary doesn't retry by itself:1462    expect(container.textContent).toBe('Caught an error: Hello.');1463    // Force the success path:1464    log.length = 0;1465    ReactDOM.render(1466      <ErrorBoundary forceRetry={true}>1467        <Normal />1468      </ErrorBoundary>,1469      container,1470    );1471    expect(container.textContent).not.toContain('Caught an error');1472    expect(log).toEqual([1473      'ErrorBoundary componentWillReceiveProps',1474      'ErrorBoundary componentWillUpdate',1475      'ErrorBoundary render success',1476      // Mount children:1477      'Normal constructor',1478      'Normal componentWillMount',1479      'Normal render',1480      // Finalize updates:1481      'Normal componentDidMount',1482      'ErrorBoundary componentDidUpdate',1483    ]);1484    log.length = 0;1485    ReactDOM.unmountComponentAtNode(container);1486    expect(log).toEqual([1487      'ErrorBoundary componentWillUnmount',1488      'Normal componentWillUnmount',1489    ]);1490  });1491  it('can update multiple times in error state', () => {1492    const container = document.createElement('div');1493    ReactDOM.render(1494      <ErrorBoundary>1495        <BrokenRender />1496      </ErrorBoundary>,1497      container,1498    );1499    expect(container.textContent).toBe('Caught an error: Hello.');1500    ReactDOM.render(1501      <ErrorBoundary>1502        <BrokenRender />1503      </ErrorBoundary>,1504      container,1505    );1506    expect(container.textContent).toBe('Caught an error: Hello.');1507    ReactDOM.render(<div>Other screen</div>, container);1508    expect(container.textContent).toBe('Other screen');1509    ReactDOM.unmountComponentAtNode(container);1510  });1511  it("doesn't get into inconsistent state during removals", () => {1512    const container = document.createElement('div');1513    ReactDOM.render(1514      <ErrorBoundary>1515        <Normal />1516        <BrokenComponentWillUnmount />1517        <Normal />1518      </ErrorBoundary>,1519      container,1520    );1521    ReactDOM.render(<ErrorBoundary />, container);1522    expect(container.textContent).toBe('Caught an error: Hello.');1523    log.length = 0;1524    ReactDOM.unmountComponentAtNode(container);1525    expect(log).toEqual(['ErrorBoundary componentWillUnmount']);1526  });1527  it("doesn't get into inconsistent state during additions", () => {1528    const container = document.createElement('div');1529    ReactDOM.render(<ErrorBoundary />, container);1530    ReactDOM.render(1531      <ErrorBoundary>1532        <Normal />1533        <BrokenRender />1534        <Normal />1535      </ErrorBoundary>,1536      container,1537    );1538    expect(container.textContent).toBe('Caught an error: Hello.');1539    log.length = 0;1540    ReactDOM.unmountComponentAtNode(container);1541    expect(log).toEqual(['ErrorBoundary componentWillUnmount']);1542  });1543  it("doesn't get into inconsistent state during reorders", () => {1544    function getAMixOfNormalAndBrokenRenderElements() {1545      const elements = [];1546      for (let i = 0; i < 100; i++) {1547        elements.push(<Normal key={i} />);1548      }1549      elements.push(<MaybeBrokenRender key={100} />);1550      let currentIndex = elements.length;1551      while (0 !== currentIndex) {1552        const randomIndex = Math.floor(Math.random() * currentIndex);1553        currentIndex -= 1;1554        const temporaryValue = elements[currentIndex];1555        elements[currentIndex] = elements[randomIndex];1556        elements[randomIndex] = temporaryValue;1557      }1558      return elements;1559    }1560    class MaybeBrokenRender extends React.Component {1561      render() {1562        if (fail) {1563          throw new Error('Hello');1564        }1565        return <div>{this.props.children}</div>;1566      }1567    }1568    let fail = false;1569    const container = document.createElement('div');1570    ReactDOM.render(1571      <ErrorBoundary>{getAMixOfNormalAndBrokenRenderElements()}</ErrorBoundary>,1572      container,1573    );1574    expect(container.textContent).not.toContain('Caught an error');1575    fail = true;1576    ReactDOM.render(1577      <ErrorBoundary>{getAMixOfNormalAndBrokenRenderElements()}</ErrorBoundary>,1578      container,1579    );1580    expect(container.textContent).toBe('Caught an error: Hello.');1581    log.length = 0;1582    ReactDOM.unmountComponentAtNode(container);1583    expect(log).toEqual(['ErrorBoundary componentWillUnmount']);1584  });1585  it('catches errors originating downstream', () => {1586    let fail = false;1587    class Stateful extends React.Component {1588      state = {shouldThrow: false};1589      render() {1590        if (fail) {1591          log.push('Stateful render [!]');1592          throw new Error('Hello');1593        }1594        return <div>{this.props.children}</div>;1595      }1596    }1597    let statefulInst;1598    const container = document.createElement('div');1599    ReactDOM.render(1600      <ErrorBoundary>1601        <Stateful ref={inst => (statefulInst = inst)} />1602      </ErrorBoundary>,1603      container,1604    );1605    log.length = 0;1606    expect(() => {1607      fail = true;1608      statefulInst.forceUpdate();1609    }).not.toThrow();1610    expect(log).toEqual([1611      'Stateful render [!]',1612      'ErrorBoundary componentDidCatch',1613      'ErrorBoundary componentWillUpdate',1614      'ErrorBoundary render error',1615      'ErrorBoundary componentDidUpdate',1616    ]);1617    log.length = 0;1618    ReactDOM.unmountComponentAtNode(container);1619    expect(log).toEqual(['ErrorBoundary componentWillUnmount']);1620  });1621  it('catches errors in componentDidMount', () => {1622    const container = document.createElement('div');1623    ReactDOM.render(1624      <ErrorBoundary>1625        <BrokenComponentWillUnmount>1626          <Normal />1627        </BrokenComponentWillUnmount>1628        <BrokenComponentDidMount />1629        <Normal logName="LastChild" />1630      </ErrorBoundary>,1631      container,1632    );1633    expect(log).toEqual([1634      'ErrorBoundary constructor',1635      'ErrorBoundary componentWillMount',1636      'ErrorBoundary render success',1637      'BrokenComponentWillUnmount constructor',1638      'BrokenComponentWillUnmount componentWillMount',1639      'BrokenComponentWillUnmount render',1640      'Normal constructor',1641      'Normal componentWillMount',1642      'Normal render',1643      'BrokenComponentDidMount constructor',1644      'BrokenComponentDidMount componentWillMount',1645      'BrokenComponentDidMount render',1646      'LastChild constructor',1647      'LastChild componentWillMount',1648      'LastChild render',1649      // Start flushing didMount queue1650      'Normal componentDidMount',1651      'BrokenComponentWillUnmount componentDidMount',1652      'BrokenComponentDidMount componentDidMount [!]',1653      // Continue despite the error1654      'LastChild componentDidMount',1655      'ErrorBoundary componentDidMount',1656      // Now we are ready to handle the error1657      // Safely unmount every child1658      'BrokenComponentWillUnmount componentWillUnmount [!]',1659      // Continue unmounting safely despite any errors1660      'Normal componentWillUnmount',1661      'BrokenComponentDidMount componentWillUnmount',1662      'LastChild componentWillUnmount',1663      // Handle the error1664      'ErrorBoundary componentDidCatch',1665      'ErrorBoundary componentWillUpdate',1666      // The willUnmount error should be captured and logged, too.1667      'ErrorBoundary componentDidUpdate',1668      'ErrorBoundary componentDidCatch',1669      'ErrorBoundary componentWillUpdate',1670      'ErrorBoundary render error',1671      // The update has finished1672      'ErrorBoundary componentDidUpdate',1673    ]);1674    log.length = 0;1675    ReactDOM.unmountComponentAtNode(container);1676    expect(log).toEqual(['ErrorBoundary componentWillUnmount']);1677  });1678  it('catches errors in componentDidUpdate', () => {1679    const container = document.createElement('div');1680    ReactDOM.render(1681      <ErrorBoundary>1682        <BrokenComponentDidUpdate />1683      </ErrorBoundary>,1684      container,1685    );1686    log.length = 0;1687    ReactDOM.render(1688      <ErrorBoundary>1689        <BrokenComponentDidUpdate />1690      </ErrorBoundary>,1691      container,1692    );1693    expect(log).toEqual([1694      'ErrorBoundary componentWillReceiveProps',1695      'ErrorBoundary componentWillUpdate',1696      'ErrorBoundary render success',1697      'BrokenComponentDidUpdate componentWillReceiveProps',1698      'BrokenComponentDidUpdate componentWillUpdate',1699      'BrokenComponentDidUpdate render',1700      // All lifecycles run1701      'BrokenComponentDidUpdate componentDidUpdate [!]',1702      'ErrorBoundary componentDidUpdate',1703      'BrokenComponentDidUpdate componentWillUnmount',1704      // Then, error is handled1705      'ErrorBoundary componentDidCatch',1706      'ErrorBoundary componentWillUpdate',1707      'ErrorBoundary render error',1708      'ErrorBoundary componentDidUpdate',1709    ]);1710    log.length = 0;1711    ReactDOM.unmountComponentAtNode(container);1712    expect(log).toEqual(['ErrorBoundary componentWillUnmount']);1713  });1714  it('propagates errors inside boundary during componentDidMount', () => {1715    const container = document.createElement('div');1716    ReactDOM.render(1717      <ErrorBoundary>1718        <BrokenComponentDidMountErrorBoundary1719          renderError={error => (1720            <div>We should never catch our own error: {error.message}.</div>1721          )}1722        />1723      </ErrorBoundary>,1724      container,1725    );1726    expect(container.firstChild.textContent).toBe('Caught an error: Hello.');1727    expect(log).toEqual([1728      'ErrorBoundary constructor',1729      'ErrorBoundary componentWillMount',1730      'ErrorBoundary render success',1731      'BrokenComponentDidMountErrorBoundary constructor',1732      'BrokenComponentDidMountErrorBoundary componentWillMount',1733      'BrokenComponentDidMountErrorBoundary render success',1734      'BrokenComponentDidMountErrorBoundary componentDidMount [!]',1735      // Fiber proceeds with the hooks1736      'ErrorBoundary componentDidMount',1737      'BrokenComponentDidMountErrorBoundary componentWillUnmount',1738      // The error propagates to the higher boundary1739      'ErrorBoundary componentDidCatch',1740      // Fiber retries from the root1741      'ErrorBoundary componentWillUpdate',1742      'ErrorBoundary render error',1743      'ErrorBoundary componentDidUpdate',1744    ]);1745    log.length = 0;1746    ReactDOM.unmountComponentAtNode(container);1747    expect(log).toEqual(['ErrorBoundary componentWillUnmount']);1748  });1749  it('calls componentDidCatch for each error that is captured', () => {1750    function renderUnmountError(error) {1751      return <div>Caught an unmounting error: {error.message}.</div>;1752    }1753    function renderUpdateError(error) {1754      return <div>Caught an updating error: {error.message}.</div>;1755    }1756    const container = document.createElement('div');1757    ReactDOM.render(1758      <ErrorBoundary logName="OuterErrorBoundary">1759        <ErrorBoundary1760          logName="InnerUnmountBoundary"1761          renderError={renderUnmountError}>1762          <BrokenComponentWillUnmount errorText="E1" />1763          <BrokenComponentWillUnmount errorText="E2" />1764        </ErrorBoundary>1765        <ErrorBoundary1766          logName="InnerUpdateBoundary"1767          renderError={renderUpdateError}>1768          <BrokenComponentDidUpdate errorText="E3" />1769          <BrokenComponentDidUpdate errorText="E4" />1770        </ErrorBoundary>1771      </ErrorBoundary>,1772      container,1773    );1774    log.length = 0;1775    ReactDOM.render(1776      <ErrorBoundary logName="OuterErrorBoundary">1777        <ErrorBoundary1778          logName="InnerUnmountBoundary"1779          renderError={renderUnmountError}1780        />1781        <ErrorBoundary1782          logName="InnerUpdateBoundary"1783          renderError={renderUpdateError}>1784          <BrokenComponentDidUpdate errorText="E3" />1785          <BrokenComponentDidUpdate errorText="E4" />1786        </ErrorBoundary>1787      </ErrorBoundary>,1788      container,1789    );1790    expect(container.firstChild.textContent).toBe(1791      'Caught an unmounting error: E2.' + 'Caught an updating error: E4.',1792    );1793    expect(log).toEqual([1794      // Begin update phase1795      'OuterErrorBoundary componentWillReceiveProps',1796      'OuterErrorBoundary componentWillUpdate',1797      'OuterErrorBoundary render success',1798      'InnerUnmountBoundary componentWillReceiveProps',1799      'InnerUnmountBoundary componentWillUpdate',1800      'InnerUnmountBoundary render success',1801      'InnerUpdateBoundary componentWillReceiveProps',1802      'InnerUpdateBoundary componentWillUpdate',1803      'InnerUpdateBoundary render success',1804      // First come the updates1805      'BrokenComponentDidUpdate componentWillReceiveProps',1806      'BrokenComponentDidUpdate componentWillUpdate',1807      'BrokenComponentDidUpdate render',1808      'BrokenComponentDidUpdate componentWillReceiveProps',1809      'BrokenComponentDidUpdate componentWillUpdate',1810      'BrokenComponentDidUpdate render',1811      // We're in commit phase now, deleting1812      'BrokenComponentWillUnmount componentWillUnmount [!]',1813      'BrokenComponentWillUnmount componentWillUnmount [!]',1814      // Continue despite errors, handle them after commit is done1815      'InnerUnmountBoundary componentDidUpdate',1816      // We're still in commit phase, now calling update lifecycles1817      'BrokenComponentDidUpdate componentDidUpdate [!]',1818      // Again, continue despite errors, we'll handle them later1819      'BrokenComponentDidUpdate componentDidUpdate [!]',1820      'InnerUpdateBoundary componentDidUpdate',1821      'OuterErrorBoundary componentDidUpdate',1822      // After the commit phase, attempt to recover from any errors that1823      // were captured1824      'BrokenComponentDidUpdate componentWillUnmount',1825      'BrokenComponentDidUpdate componentWillUnmount',1826      'InnerUnmountBoundary componentDidCatch',1827      'InnerUnmountBoundary componentDidCatch',1828      'InnerUpdateBoundary componentDidCatch',1829      'InnerUpdateBoundary componentDidCatch',1830      'InnerUnmountBoundary componentWillUpdate',1831      'InnerUnmountBoundary render error',1832      'InnerUpdateBoundary componentWillUpdate',1833      'InnerUpdateBoundary render error',1834      'InnerUnmountBoundary componentDidUpdate',1835      'InnerUpdateBoundary componentDidUpdate',1836    ]);1837    log.length = 0;1838    ReactDOM.unmountComponentAtNode(container);1839    expect(log).toEqual([1840      'OuterErrorBoundary componentWillUnmount',1841      'InnerUnmountBoundary componentWillUnmount',1842      'InnerUpdateBoundary componentWillUnmount',1843    ]);1844  });1845  it('discards a bad root if the root component fails', () => {1846    const X = null;1847    const Y = undefined;1848    let err1;1849    let err2;1850    try {1851      let container = document.createElement('div');1852      expect(() => ReactDOM.render(<X />, container)).toWarnDev(1853        'React.createElement: type is invalid -- expected a string ' +1854          '(for built-in components) or a class/function ' +1855          '(for composite components) but got: null.',1856      );1857    } catch (err) {1858      err1 = err;1859    }1860    try {1861      let container = document.createElement('div');1862      expect(() => ReactDOM.render(<Y />, container)).toWarnDev(1863        'React.createElement: type is invalid -- expected a string ' +1864          '(for built-in components) or a class/function ' +1865          '(for composite components) but got: undefined.',1866      );1867    } catch (err) {1868      err2 = err;1869    }1870    expect(err1.message).toMatch(/got: null/);1871    expect(err2.message).toMatch(/got: undefined/);1872  });1873  it('renders empty output if error boundary does not handle the error', () => {1874    const container = document.createElement('div');1875    expect(() => {1876      ReactDOM.render(1877        <div>1878          Sibling1879          <NoopErrorBoundary>1880            <BrokenRender />1881          </NoopErrorBoundary>1882        </div>,1883        container,1884      );1885    }).toWarnDev(1886      'ErrorBoundary: Error boundaries should implement getDerivedStateFromError()',1887      {withoutStack: true},1888    );1889    expect(container.firstChild.textContent).toBe('Sibling');1890    expect(log).toEqual([1891      'NoopErrorBoundary constructor',1892      'NoopErrorBoundary componentWillMount',1893      'NoopErrorBoundary render',1894      'BrokenRender constructor',1895      'BrokenRender componentWillMount',1896      'BrokenRender render [!]',1897      // In Fiber, noop error boundaries render null1898      'NoopErrorBoundary componentDidMount',1899      'NoopErrorBoundary componentDidCatch',1900      // Nothing happens.1901    ]);1902    log.length = 0;1903    ReactDOM.unmountComponentAtNode(container);1904    expect(log).toEqual(['NoopErrorBoundary componentWillUnmount']);1905  });1906  it('passes first error when two errors happen in commit', () => {1907    const errors = [];1908    let caughtError;1909    class Parent extends React.Component {1910      render() {1911        return <Child />;1912      }1913      componentDidMount() {1914        errors.push('parent sad');1915        throw new Error('parent sad');1916      }1917    }1918    class Child extends React.Component {1919      render() {1920        return <div />;1921      }1922      componentDidMount() {1923        errors.push('child sad');1924        throw new Error('child sad');1925      }1926    }1927    const container = document.createElement('div');1928    try {1929      // Here, we test the behavior where there is no error boundary and we1930      // delegate to the host root.1931      ReactDOM.render(<Parent />, container);1932    } catch (e) {1933      if (e.message !== 'parent sad' && e.message !== 'child sad') {1934        throw e;1935      }1936      caughtError = e;1937    }1938    expect(errors).toEqual(['child sad', 'parent sad']);1939    // Error should be the first thrown1940    expect(caughtError.message).toBe('child sad');1941  });1942  it('propagates uncaught error inside unbatched initial mount', () => {1943    function Foo() {1944      throw new Error('foo error');1945    }1946    const container = document.createElement('div');1947    expect(() => {1948      ReactDOM.unstable_batchedUpdates(() => {1949        ReactDOM.render(<Foo />, container);1950      });1951    }).toThrow('foo error');1952  });1953  it('handles errors that occur in before-mutation commit hook', () => {1954    const errors = [];1955    let caughtError;1956    class Parent extends React.Component {1957      getSnapshotBeforeUpdate() {1958        errors.push('parent sad');1959        throw new Error('parent sad');1960      }1961      componentDidUpdate() {}1962      render() {1963        return <Child {...this.props} />;1964      }1965    }1966    class Child extends React.Component {1967      getSnapshotBeforeUpdate() {1968        errors.push('child sad');1969        throw new Error('child sad');1970      }1971      componentDidUpdate() {}1972      render() {1973        return <div />;1974      }1975    }1976    const container = document.createElement('div');1977    ReactDOM.render(<Parent value={1} />, container);1978    try {1979      ReactDOM.render(<Parent value={2} />, container);1980    } catch (e) {1981      if (e.message !== 'parent sad' && e.message !== 'child sad') {1982        throw e;1983      }1984      caughtError = e;1985    }1986    expect(errors).toEqual(['child sad', 'parent sad']);1987    // Error should be the first thrown1988    expect(caughtError.message).toBe('child sad');1989  });...app.js
Source:app.js  
...24  _template_prefix : 'payment-page-',25  settings : {},26  lang : {},27  Init : function ( containerTarget ) {28    containerTarget.find( '[data-payment-page-component]' ).not( '[data-payment-page-component-loaded]' ).each( function() { PaymentPage.initComponent( jQuery(this) ); });29    containerTarget.find( '#payment-page-notification-container .notice-dismiss' ).on( "click", function() {30      PaymentPage.API.post( 'payment-page/v1/administration/dismiss-notification', false );31    });32  },33  initComponent : function( container ) {34    let component = container.attr( 'data-payment-page-component' );35    this.setLoadingContent( container, '', ( component === 'listing-search' ? 'mini' : '' ) );36    if( typeof PaymentPage.Component[ component ] !== "undefined" ) {37      PaymentPage._instanceComponent( component, container );38    } else {39      PaymentPage._loadComponent( component, container );40    }41  },42  getComponentInstance : function( component, component_index ) {43    component_index = parseInt( component_index );44    return PaymentPage._components.instanced[ component ][ component_index ];45  },46  setLoadingContent : function( container, loading_text = '', type = '' ) {47    if( container.find( ' > .payment-page-application-loader-wrapper' ).length === 1 ) {48        container.find( ' > .payment-page-application-loader-status-text' ).remove();49      if( loading_text !== '' )50        container.prepend( '<div class="payment-page-application-loader-status-text">' + loading_text + '</div>' );51      return;52    }53    container.html( payment_page_element_loader( type ) );54    if( loading_text !== '' )55      container.prepend( '<div class="payment-page-application-loader-status-text">' + loading_text + '</div>' );56  },57  setNoResultsContent : function( container ) {58    container.html( '<div data-payment-page-notification="info">' + this.lang.no_results_response + '</div>' );59  },60  setErrorContent : function( container, response ) {61    if( typeof response.error_html !== "undefined" ) {62      container.html( response.error_html );63      PaymentPage.Init( container );64      return;65    }66    let error = ( typeof response.error_notification !== "undefined" ? response.error_notification : response.error );67    container.html( '<div data-payment-page-notification="danger">' + error + '</div>' );68  },69  setCancelledContent : function( container ) {70    if( typeof window.closed !== "undefined" && window.closed ) {71      container.html( '' );72      return;73    }74    container.html( '<div data-payment-page-notification="info">' + this.lang.cancelled_request + '</div>' );75  },76  setFailedAssetFetchContent : function( container ) {77    if( typeof window.closed !== "undefined" && window.closed ) {78      container.html( '' );79      return;80    }81    container.html( '<div data-payment-page-notification="danger">' + this.lang.asset_failed_fetch + '</div>' );82  },83  setHTML : function( container, html ) {84    PaymentPage.Destroy( container );85    container.html( html );86    PaymentPage.Init( container );87  },88  _loadComponent : function( component, componentContainer = false ) {89    if( typeof this._components.pending[ component ] === "undefined" ) {90      if( componentContainer !== false )91        this._components.pending[ component ] = [ componentContainer ];92      this.Resource.loadCSS(93        this.getComponentAssetPath( component, 'style.css' ),94        {95          'payment-page-component-stylesheet' : component96        }97      )98      this.Resource.loadJS( this.getComponentAssetPath( component, 'controller.min.js' ), function() {99        PaymentPage._loadedComponent( component );100      }, function() {101        if( componentContainer !== false )102          PaymentPage.setFailedAssetFetchContent( componentContainer );103      } );104      return;105    }106    if( componentContainer === false )107      return;108    if( typeof this._components.pending[ component ] === "undefined" )109      this._components.pending[ component ] = [];110    this._components.pending[ component ][ this._components.pending[ component ].length ] = componentContainer;111  },112  _loadedComponent : function( component ) {113    if( typeof this._components.pending[ component ] === 'undefined' )114      return;115    if( this._components.pending[ component ].length !== 0 ) {116      jQuery.each( this._components.pending[ component ], function( index, componentContainer ) {117        PaymentPage._instanceComponent( component, componentContainer );118      });119    }120    delete this._components.pending[ component ];121  },122  _instanceComponent : function( component, componentContainer ) {123    if( typeof componentContainer.attr( "data-payment-page-component-loaded" ) !== "undefined" )124      return;125    componentContainer.attr( 'data-payment-page-component-loaded', 0 );126    this.setLoadingContent( componentContainer );127    if( typeof this._components.instanced[ component ] === "undefined" )128      this._components.instanced[ component ] = [];129    this._components.instanced[ component ][ this._components.instanced[ component ].length ] = payment_page_clone_object( this.Component[ component ] );130    componentContainer.attr( "data-payment-page-component-instance-index", this._components.instanced[ component ].length - 1 );131    this._components.instanced[ component ][ this._components.instanced[ component ].length - 1 ].Init( componentContainer );132    componentContainer.attr( 'data-payment-page-component-loaded', 1 );133    this.Hashtag.Init();134  },135  getComponentAssetPath : function( component, path ) {136    if( typeof this.settings.component_injection[ component ] !== "undefined" ) {137      let component_injection = this.settings.component_injection[ component ];138      if( typeof component_injection === 'object' ) {139        let objectKeys   = Object.keys( component_injection ),140          _matched_index = false;141        jQuery.each( objectKeys, function( _objectKeyIndex, _objectKey ) {142          if( path.indexOf( _objectKey ) !== 0 )143            return true;144          _matched_index = _objectKey;145          return false;146        });147        if( false !== _matched_index )148          return component_injection[ _matched_index ] + '/' + path.replace( _matched_index, '' );149        return component_injection[ '__default' ] + '/' + path;150      }151      return this.settings.component_injection[ component ] + '/' + path;152    }153    return this.settings.library_url + 'component/' + component + '/' + path;154  },155  LoadAssets : function( asset, callback = false, _attach_version = true ) {156    if( typeof asset === "string" ) {157      if( payment_page_in_array( asset, PaymentPage._assets.loaded ) ) {158        if( typeof callback === 'function' )159          callback();160        return;161      }162      if( payment_page_in_array( asset, PaymentPage._assets.requested ) ) {163        setTimeout( function() {164          PaymentPage.LoadAssets( asset, callback, _attach_version );165        }, 100 );166        return;167      }168      PaymentPage._assets.requested[ PaymentPage._assets.requested.length ] = asset;169      if( asset.endsWith( '.css' ) ) {170        PaymentPage.Resource.loadCSS( asset );171        if( typeof callback === 'function' )172          callback();173        return;174      }175      PaymentPage.Resource.loadJS( asset, callback, false, _attach_version );176      return;177    }178    let current_asset = _.head( asset );179    if( asset.length === 1 ) {180      this.LoadAssets( current_asset, callback );181      return;182    }183    let objectInstance   = this,184        remaining_assets = _.drop( asset, 1 );185    this.LoadAssets( current_asset, function() {186      objectInstance.LoadAssets( remaining_assets, callback );187    } );188  },189  isAssetLoaded : function( asset ) {190    if( typeof this._assets.loaded === "undefined" )191      return false;192    if( asset instanceof Array ) {193      let response = true;194      jQuery.each( asset, function( k, a ) {195        if( !PaymentPage._assets.loaded.includes( a ) )196          response = false;197      });198      return response;199    }200    return this._assets.loaded.includes( asset );201  },202  Destroy : function( target ) {203    if( typeof target.attr( 'data-payment-page-component' ) !== "undefined" )204      PaymentPage.__destroyComponent( target );205    if( typeof tinyMCE !== "undefined" ) {206      tinyMCE.triggerSave();207      target.find( ".wp-editor-area" ).each( function() {208        if( jQuery(this).attr( "id" ) !== "undefined" )209          tinyMCE.execCommand('mceRemoveEditor', false, jQuery(this).attr('id'));210      });211    }212    target.find( '[data-payment-page-component]' ).each( function() {213      PaymentPage.__destroyComponent( jQuery(this) );214    });215    target.find( '[data-payment-page-library]' ).each( function() {216      let library = jQuery(this).attr( 'data-payment-page-library' );217      library = payment_page_uc_first(library);218      if( typeof PaymentPage.Library[ library ] !== "undefined" ) {219        PaymentPage.Library[library].Destroy( jQuery(this) );220      }221    });222    target.find( "*" ).off();223    target.html( '' );224  },225  __destroyComponent : function( targetContainer ) {226    let component       = targetContainer.attr( "data-payment-page-component" ),227        component_index = targetContainer.attr( "data-payment-page-component-instance-index" );...routes.js
Source:routes.js  
2export default {3  path: '/',4  component: App,5  indexRoute: {6    getComponent(nextState, cb) {7      import('./pages/index').then(component => cb(null, component.default));8    }9  },10  childRoutes: [11    {12      path: 'about-us',13      getComponent(nextState, cb) {14        import('./pages/about-us').then(component =>15          cb(null, component.default)16        );17      }18    },19    {20      path: 'account-notice',21      getComponent(nextState, cb) {22        import('./pages/account-notice').then(component =>23          cb(null, component.default)24        );25      }26    },27    {28      path: 'add-bank',29      getComponent(nextState, cb) {30        import('./pages/add-bank').then(component =>31          cb(null, component.default)32        );33      }34    },35    {36      path: 'advice-feed-back',37      getComponent(nextState, cb) {38        import('./pages/advice-feed-back').then(component =>39          cb(null, component.default)40        );41      }42    },43    {44      path: 'bank-info',45      getComponent(nextState, cb) {46        import('./pages/bank-info').then(component =>47          cb(null, component.default)48        );49      }50    },51    {52      path: 'bank-query',53      getComponent(nextState, cb) {54        import('./pages/bank-query').then(component =>55          cb(null, component.default)56        );57      }58    },59    {60      path: 'buy-notice',61      getComponent(nextState, cb) {62        import('./pages/buy-notice').then(component =>63          cb(null, component.default)64        );65      }66    },67    {68      path: 'change-pwd',69      getComponent(nextState, cb) {70        import('./pages/change-pwd').then(component =>71          cb(null, component.default)72        );73      }74    },75    {76      path: 'comfirm-draw-money-ok',77      getComponent(nextState, cb) {78        import('./pages/comfirm-draw-money-ok').then(component =>79          cb(null, component.default)80        );81      }82    },83    {84      path: 'comfirm-draw-money',85      getComponent(nextState, cb) {86        import('./pages/comfirm-draw-money').then(component =>87          cb(null, component.default)88        );89      }90    },91    {92      path: 'cz-trade-detail/:transCode',93      getComponent(nextState, cb) {94        import('./pages/cz-trade-detail').then(component =>95          cb(null, component.default)96        );97      }98    },99    {100      path: 'devote-recorde',101      getComponent(nextState, cb) {102        import('./pages/devote-recorde').then(component =>103          cb(null, component.default)104        );105      }106    },107    {108      path: 'draw-list',109      getComponent(nextState, cb) {110        import('./pages/draw-list').then(component =>111          cb(null, component.default)112        );113      }114    },115    {116      path: 'draw-money-branch',117      getComponent(nextState, cb) {118        import('./pages/draw-money-branch').then(component =>119          cb(null, component.default)120        );121      }122    },123    {124      path: 'draw-money',125      getComponent(nextState, cb) {126        import('./pages/draw-money').then(component =>127          cb(null, component.default)128        );129      }130    },131    {132      path: 'fourteen-lottery-detail',133      getComponent(nextState, cb) {134        import('./pages/fourteen-lottery-detail').then(component =>135          cb(null, component.default)136        );137      }138    },139    {140      path: 'fourteen-lottery',141      getComponent(nextState, cb) {142        import('./pages/fourteen-lottery').then(component =>143          cb(null, component.default)144        );145      }146    },147    {148      path: 'jjc-lottery-detail',149      getComponent(nextState, cb) {150        import('./pages/jjc-lottery-detail').then(component =>151          cb(null, component.default)152        );153      }154    },155    {156      path: 'lottery-notice',157      getComponent(nextState, cb) {158        import('./pages/lottery-notice').then(component =>159          cb(null, component.default)160        );161      }162    },163    {164      path: 'mail-info/:em/:em_log/:em_sts',165      getComponent(nextState, cb) {166        import('./pages/mail-info').then(component =>167          cb(null, component.default)168        );169      }170    },171    {172      path: 'modify-nick-name',173      getComponent(nextState, cb) {174        import('./pages/modify-nick-name').then(component =>175          cb(null, component.default)176        );177      }178    },179    {180      path: 'modify-user-name',181      getComponent(nextState, cb) {182        import('./pages/modify-user-name').then(component =>183          cb(null, component.default)184        );185      }186    },187    {188      path: 'msg-center',189      getComponent(nextState, cb) {190        import('./pages/msg-center').then(component =>191          cb(null, component.default)192        );193      }194    },195    {196      path: 'my-bank',197      getComponent(nextState, cb) {198        import('./pages/my-bank').then(component =>199          cb(null, component.default)200        );201      }202    },203    {204      path: 'no-bother-notice',205      getComponent(nextState, cb) {206        import('./pages/no-bother-notice').then(component =>207          cb(null, component.default)208        );209      }210    },211    {212      path: 'notice-setting',213      getComponent(nextState, cb) {214        import('./pages/notice-setting').then(component =>215          cb(null, component.default)216        );217      }218    },219    {220      path: 'number-lottery-detail',221      getComponent(nextState, cb) {222        import('./pages/number-lottery-detail').then(component =>223          cb(null, component.default)224        );225      }226    },227    {228      path: 'number-lottery-progress/:orderCode',229      getComponent(nextState, cb) {230        import('./pages/number-lottery-progress').then(component =>231          cb(null, component.default)232        );233      }234    },235    {236      path: 'other-notice',237      getComponent(nextState, cb) {238        import('./pages/other-notice').then(component =>239          cb(null, component.default)240        );241      }242    },243    {244      path: 'phone-info/:mob/:mob_log/:mob_sts',245      getComponent(nextState, cb) {246        import('./pages/phone-info').then(component =>247          cb(null, component.default)248        );249      }250    },251    {252      path: 'question-detail',253      getComponent(nextState, cb) {254        import('./pages/question-detail').then(component =>255          cb(null, component.default)256        );257      }258    },259    {260      path: 'question-detail',261      getComponent(nextState, cb) {262        import('./pages/question-detail').then(component =>263          cb(null, component.default)264        );265      }266    },267    {268      path: 'real-name-again',269      getComponent(nextState, cb) {270        import('./pages/real-name-again').then(component =>271          cb(null, component.default)272        );273      }274    },275    {276      path: 'real-name',277      getComponent(nextState, cb) {278        import('./pages/real-name').then(component =>279          cb(null, component.default)280        );281      }282    },283    {284      path: 'recharge-result',285      getComponent(nextState, cb) {286        import('./pages/recharge-result').then(component =>287          cb(null, component.default)288        );289      }290    },291    {292      path: 'recharge',293      getComponent(nextState, cb) {294        import('./pages/recharge').then(component =>295          cb(null, component.default)296        );297      }298    },299    {300      path: 'red-packet-item',301      getComponent(nextState, cb) {302        import('./pages/red-packet-item').then(component =>303          cb(null, component.default)304        );305      }306    },307    {308      path: 'red-packet-tans/:redCode',309      getComponent(nextState, cb) {310        import('./pages/red-packet-tans').then(component =>311          cb(null, component.default)312        );313      }314    },315    {316      path: 'red-packet',317      getComponent(nextState, cb) {318        import('./pages/red-packet').then(component =>319          cb(null, component.default)320        );321      }322    },323    {324      path: 'register-bank/:bankNo',325      getComponent(nextState, cb) {326        import('./pages/register-bank').then(component =>327          cb(null, component.default)328        );329      }330    },331    {332      path: 'register-mail',333      getComponent(nextState, cb) {334        import('./pages/register-mail').then(component =>335          cb(null, component.default)336        );337      }338    },339    {340      path: 'register-phone',341      getComponent(nextState, cb) {342        import('./pages/register-phone').then(component =>343          cb(null, component.default)344        );345      }346    },347    {348      path: 'register-proxy',349      getComponent(nextState, cb) {350        import('./pages/register-proxy').then(component =>351          cb(null, component.default)352        );353      }354    },355    {356      path: 'safe-account',357      getComponent(nextState, cb) {358        import('./pages/safe-account').then(component =>359          cb(null, component.default)360        );361      }362    },363    {364      path: 'service-hall',365      getComponent(nextState, cb) {366        import('./pages/service-hall').then(component =>367          cb(null, component.default)368        );369      }370    },371    {372      path: 'set-password',373      getComponent(nextState, cb) {374        import('./pages/set-password').then(component =>375          cb(null, component.default)376        );377      }378    },379    {380      path: 'setting',381      getComponent(nextState, cb) {382        import('./pages/setting').then(component =>383          cb(null, component.default)384        );385      }386    },387    {388      path: 'tk-trade-detail/:transCode',389      getComponent(nextState, cb) {390        import('./pages/tk-trade-detail').then(component =>391          cb(null, component.default)392        );393      }394    },395    {396      path: 'trade-detail/:transCode',397      getComponent(nextState, cb) {398        import('./pages/trade-detail').then(component =>399          cb(null, component.default)400        );401      }402    },403    {404      path: 'trade-info',405      getComponent(nextState, cb) {406        import('./pages/trade-info').then(component =>407          cb(null, component.default)408        );409      }410    },411    {412      path: 'trade-notice',413      getComponent(nextState, cb) {414        import('./pages/trade-notice').then(component =>415          cb(null, component.default)416        );417      }418    },419    {420      path: 'user-info',421      getComponent(nextState, cb) {422        import('./pages/user-info').then(component =>423          cb(null, component.default)424        );425      }426    },427    {428      path: 'verify-mail/:em(/:update)',429      getComponent(nextState, cb) {430        import('./pages/verify-mail').then(component =>431          cb(null, component.default)432        );433      }434    },435    {436      path: 'verify-phone/:mob(/:update)',437      getComponent(nextState, cb) {438        import('./pages/verify-phone').then(component =>439          cb(null, component.default)440        );441      }442    },443    {444      path: 'orders/:order',445      getComponent(nextState, cb) {446        import('./orders/components').then(component =>447          cb(null, component.default)448        );449      }450    }451  ]...ComponentWrapper.test.js
Source:ComponentWrapper.test.js  
1"use strict";2Object.defineProperty(exports, "__esModule", { value: true });3const React = require("react");4const react_native_1 = require("react-native");5const renderer = require("react-test-renderer");6const ComponentWrapper_1 = require("./ComponentWrapper");7const Store_1 = require("./Store");8const ts_mockito_1 = require("ts-mockito");9const ComponentEventsObserver_1 = require("../events/ComponentEventsObserver");10describe('ComponentWrapper', () => {11    const componentName = 'example.MyComponent';12    let store;13    let myComponentProps;14    let mockedComponentEventsObserver;15    let componentEventsObserver;16    let uut;17    class MyComponent extends React.Component {18        render() {19            myComponentProps = this.props;20            if (this.props.renderCount) {21                this.props.renderCount();22            }23            return <react_native_1.Text>{this.props.text || 'Hello, World!'}</react_native_1.Text>;24        }25    }26    MyComponent.options = {27        title: 'MyComponentTitle'28    };29    class TestParent extends React.Component {30        constructor(props) {31            super(props);32            this.ChildClass = props.ChildClass;33            this.state = { propsFromState: {} };34        }35        render() {36            return (<this.ChildClass componentId='component1' {...this.state.propsFromState}/>);37        }38    }39    beforeEach(() => {40        store = new Store_1.Store();41        mockedComponentEventsObserver = ts_mockito_1.mock(ComponentEventsObserver_1.ComponentEventsObserver);42        componentEventsObserver = ts_mockito_1.instance(mockedComponentEventsObserver);43        uut = new ComponentWrapper_1.ComponentWrapper();44    });45    it('must have componentId as prop', () => {46        const NavigationComponent = uut.wrap(componentName, () => MyComponent, store, componentEventsObserver);47        const orig = console.error;48        console.error = (a) => a;49        expect(() => {50            renderer.create(<NavigationComponent />);51        }).toThrowError('Component example.MyComponent does not have a componentId!');52        console.error = orig;53    });54    it('wraps the component', () => {55        const NavigationComponent = uut.wrap(componentName, () => MyComponent, store, componentEventsObserver);56        expect(NavigationComponent).not.toBeInstanceOf(MyComponent);57        const tree = renderer.create(<NavigationComponent componentId={'component1'}/>);58        expect(tree.toJSON().children).toEqual(['Hello, World!']);59    });60    it('injects props from wrapper into original component', () => {61        const renderCount = jest.fn();62        const NavigationComponent = uut.wrap(componentName, () => MyComponent, store, componentEventsObserver);63        const tree = renderer.create(<NavigationComponent componentId={'component1'} text={'yo'} renderCount={renderCount}/>);64        expect(tree.toJSON().children).toEqual(['yo']);65        expect(renderCount).toHaveBeenCalledTimes(1);66    });67    it('updates props from wrapper into original component on state change', () => {68        const NavigationComponent = uut.wrap(componentName, () => MyComponent, store, componentEventsObserver);69        const tree = renderer.create(<TestParent ChildClass={NavigationComponent}/>);70        expect(myComponentProps.foo).toEqual(undefined);71        tree.getInstance().setState({ propsFromState: { foo: 'yo' } });72        expect(myComponentProps.foo).toEqual('yo');73    });74    it('pulls props from the store and injects them into the inner component', () => {75        store.setPropsForId('component123', { numberProp: 1, stringProp: 'hello', objectProp: { a: 2 } });76        const NavigationComponent = uut.wrap(componentName, () => MyComponent, store, componentEventsObserver);77        renderer.create(<NavigationComponent componentId={'component123'}/>);78        expect(myComponentProps).toEqual({ componentId: 'component123', numberProp: 1, stringProp: 'hello', objectProp: { a: 2 } });79    });80    it('updates props from store into inner component', () => {81        const NavigationComponent = uut.wrap(componentName, () => MyComponent, store, componentEventsObserver);82        const tree = renderer.create(<TestParent ChildClass={NavigationComponent}/>);83        store.setPropsForId('component1', { myProp: 'hello' });84        expect(myComponentProps.foo).toEqual(undefined);85        expect(myComponentProps.myProp).toEqual(undefined);86        tree.getInstance().setState({ propsFromState: { foo: 'yo' } });87        expect(myComponentProps.foo).toEqual('yo');88        expect(myComponentProps.myProp).toEqual('hello');89    });90    it('protects id from change', () => {91        const NavigationComponent = uut.wrap(componentName, () => MyComponent, store, componentEventsObserver);92        const tree = renderer.create(<TestParent ChildClass={NavigationComponent}/>);93        expect(myComponentProps.componentId).toEqual('component1');94        tree.getInstance().setState({ propsFromState: { id: 'ERROR' } });95        expect(myComponentProps.componentId).toEqual('component1');96    });97    xit('assigns key by componentId', () => {98        const NavigationComponent = uut.wrap(componentName, () => MyComponent, store, componentEventsObserver);99        const tree = renderer.create(<NavigationComponent componentId={'component1'}/>);100        expect(myComponentProps.componentId).toEqual('component1');101        console.log(Object.keys(tree.root.findByType(NavigationComponent).instance._reactInternalFiber));102        console.log(tree.root.findByType(NavigationComponent).instance._reactInternalFiber.child.child.child.return.return.key);103        expect(tree.getInstance()._reactInternalInstance.child.child.Fibernode.key).toEqual('component1');104    });105    it('cleans props from store on unMount', () => {106        store.setPropsForId('component123', { foo: 'bar' });107        const NavigationComponent = uut.wrap(componentName, () => MyComponent, store, componentEventsObserver);108        const tree = renderer.create(<NavigationComponent componentId={'component123'}/>);109        expect(store.getPropsForId('component123')).toEqual({ foo: 'bar' });110        tree.unmount();111        expect(store.getPropsForId('component123')).toEqual({});112    });113    it('merges static members from wrapped component when generated', () => {114        const NavigationComponent = uut.wrap(componentName, () => MyComponent, store, componentEventsObserver);115        expect(NavigationComponent.options).toEqual({ title: 'MyComponentTitle' });116    });117    it('calls unmounted on componentEventsObserver', () => {118        const NavigationComponent = uut.wrap(componentName, () => MyComponent, store, componentEventsObserver);119        const tree = renderer.create(<NavigationComponent componentId={'component123'}/>);120        ts_mockito_1.verify(mockedComponentEventsObserver.unmounted('component123')).never();121        tree.unmount();122        ts_mockito_1.verify(mockedComponentEventsObserver.unmounted('component123')).once();123    });124    it('renders HOC components correctly', () => {125        const generator = () => (props) => (<react_native_1.View>126        <MyComponent {...props}/>127      </react_native_1.View>);128        uut = new ComponentWrapper_1.ComponentWrapper();129        const NavigationComponent = uut.wrap(componentName, generator, store, componentEventsObserver);130        const tree = renderer.create(<NavigationComponent componentId={'component123'}/>);131        expect(tree.root.findByType(react_native_1.View)).toBeDefined();132        expect(tree.root.findByType(MyComponent).props).toEqual({ componentId: 'component123' });133    });134    describe(`register with redux store`, () => {135        class MyReduxComp extends React.Component {136            static options() {137                return { foo: 123 };138            }139            render() {140                return (<react_native_1.Text>{this.props.txt}</react_native_1.Text>);141            }142        }143        function mapStateToProps(state) {144            return {145                txt: state.txt146            };147        }148        const ConnectedComp = require('react-redux').connect(mapStateToProps)(MyReduxComp);149        const ReduxProvider = require('react-redux').Provider;150        const initialState = { txt: 'it just works' };151        const reduxStore = require('redux').createStore((state = initialState) => state);152        it(`wraps the component with a react-redux provider with passed store`, () => {153            const NavigationComponent = uut.wrap(componentName, () => ConnectedComp, store, componentEventsObserver, undefined, ReduxProvider, reduxStore);154            const tree = renderer.create(<NavigationComponent componentId={'theCompId'}/>);155            expect(tree.toJSON().children).toEqual(['it just works']);156            expect(NavigationComponent.options()).toEqual({ foo: 123 });157        });158    });...Component.js
Source:Component.js  
1describe('Component', function() {2    var utHelper = window.utHelper;3    var testCase = utHelper.prepare(['echarts/model/Component']);4    describe('topologicalTravel', function () {5        testCase('topologicalTravel_base', function (ComponentModel) {6            ComponentModel.extend({type: 'm1', dependencies: ['a1', 'a2']});7            ComponentModel.extend({type: 'a1'});8            ComponentModel.extend({type: 'a2'});9            var result = [];10            var allList = ComponentModel.getAllClassMainTypes();11            ComponentModel.topologicalTravel(['m1', 'a1', 'a2'], allList, function (componentType, dependencies) {12                result.push([componentType, dependencies]);13            });14            expect(result).toEqual([['a2', []], ['a1', []], ['m1', ['a1', 'a2']]]);15        });16        testCase('topologicalTravel_a1IsAbsent', function (ComponentModel) {17            ComponentModel.extend({type: 'm1', dependencies: ['a1', 'a2']});18            ComponentModel.extend({type: 'a2'});19            var allList = ComponentModel.getAllClassMainTypes();20            var result = [];21            ComponentModel.topologicalTravel(['m1', 'a2'], allList, function (componentType, dependencies) {22                result.push([componentType, dependencies]);23            });24            expect(result).toEqual([['a2', []], ['m1', ['a1', 'a2']]]);25        });26        testCase('topologicalTravel_empty', function (ComponentModel) {27            ComponentModel.extend({type: 'm1', dependencies: ['a1', 'a2']});28            ComponentModel.extend({type: 'a1'});29            ComponentModel.extend({type: 'a2'});30            var allList = ComponentModel.getAllClassMainTypes();31            var result = [];32            ComponentModel.topologicalTravel([], allList, function (componentType, dependencies) {33                result.push([componentType, dependencies]);34            });35            expect(result).toEqual([]);36        });37        testCase('topologicalTravel_isolate', function (ComponentModel) {38            ComponentModel.extend({type: 'a2'});39            ComponentModel.extend({type: 'a1'});40            ComponentModel.extend({type: 'm1', dependencies: ['a2']});41            var allList = ComponentModel.getAllClassMainTypes();42            var result = [];43            ComponentModel.topologicalTravel(['a1', 'a2', 'm1'], allList, function (componentType, dependencies) {44                result.push([componentType, dependencies]);45            });46            expect(result).toEqual([['a1', []], ['a2', []], ['m1', ['a2']]]);47        });48        testCase('topologicalTravel_diamond', function (ComponentModel) {49            ComponentModel.extend({type: 'a1', dependencies: []});50            ComponentModel.extend({type: 'a2', dependencies: ['a1']});51            ComponentModel.extend({type: 'a3', dependencies: ['a1']});52            ComponentModel.extend({type: 'm1', dependencies: ['a2', 'a3']});53            var allList = ComponentModel.getAllClassMainTypes();54            var result = [];55            ComponentModel.topologicalTravel(['m1', 'a1', 'a2', 'a3'], allList, function (componentType, dependencies) {56                result.push([componentType, dependencies]);57            });58            expect(result).toEqual([['a1', []], ['a3', ['a1']], ['a2', ['a1']], ['m1', ['a2', 'a3']]]);59        });60        testCase('topologicalTravel_loop', function (ComponentModel) {61            ComponentModel.extend({type: 'm1', dependencies: ['a1', 'a2']});62            ComponentModel.extend({type: 'm2', dependencies: ['m1', 'a2']});63            ComponentModel.extend({type: 'a1', dependencies: ['m2', 'a2', 'a3']});64            ComponentModel.extend({type: 'a2'});65            ComponentModel.extend({type: 'a3'});66            var allList = ComponentModel.getAllClassMainTypes();67            expect(function () {68                ComponentModel.topologicalTravel(['m1', 'm2', 'a1'], allList);69            }).toThrowError(/Circl/);70        });71        testCase('topologicalTravel_multipleEchartsInstance', function (ComponentModel) {72            ComponentModel.extend({type: 'm1', dependencies: ['a1', 'a2']});73            ComponentModel.extend({type: 'a1'});74            ComponentModel.extend({type: 'a2'});75            var allList = ComponentModel.getAllClassMainTypes();76            var result = [];77            ComponentModel.topologicalTravel(['m1', 'a1', 'a2'], allList, function (componentType, dependencies) {78                result.push([componentType, dependencies]);79            });80            expect(result).toEqual([['a2', []], ['a1', []], ['m1', ['a1', 'a2']]]);81            result = [];82            ComponentModel.extend({type: 'm2', dependencies: ['a1', 'm1']});83            var allList = ComponentModel.getAllClassMainTypes();84            ComponentModel.topologicalTravel(['m2', 'm1', 'a1', 'a2'], allList, function (componentType, dependencies) {85                result.push([componentType, dependencies]);86            });87            expect(result).toEqual([['a2', []], ['a1', []], ['m1', ['a1', 'a2']], ['m2', ['a1', 'm1']]]);88        });89        testCase('topologicalTravel_missingSomeNodeButHasDependencies', function (ComponentModel) {90            ComponentModel.extend({type: 'm1', dependencies: ['a1', 'a2']});91            ComponentModel.extend({type: 'a2', dependencies: ['a3']});92            ComponentModel.extend({type: 'a3'});93            ComponentModel.extend({type: 'a4'});94            var result = [];95            var allList = ComponentModel.getAllClassMainTypes();96            ComponentModel.topologicalTravel(['a3', 'm1'], allList, function (componentType, dependencies) {97                result.push([componentType, dependencies]);98            });99            expect(result).toEqual([['a3', []], ['a2', ['a3']], ['m1', ['a1', 'a2']]]);100            var result = [];101            var allList = ComponentModel.getAllClassMainTypes();102            ComponentModel.topologicalTravel(['m1', 'a3'], allList, function (componentType, dependencies) {103                result.push([componentType, dependencies]);104            });105            expect(result).toEqual([['a3', []], ['a2', ['a3']], ['m1', ['a1', 'a2']]]);106        });107        testCase('topologicalTravel_subType', function (ComponentModel) {108            ComponentModel.extend({type: 'm1', dependencies: ['a1', 'a2']});109            ComponentModel.extend({type: 'a1.aaa', dependencies: ['a2']});110            ComponentModel.extend({type: 'a1.bbb', dependencies: ['a3', 'a4']});111            ComponentModel.extend({type: 'a2'});112            ComponentModel.extend({type: 'a3'});113            ComponentModel.extend({type: 'a4'});114            var result = [];115            var allList = ComponentModel.getAllClassMainTypes();116            ComponentModel.topologicalTravel(['m1', 'a1', 'a2', 'a4'], allList, function (componentType, dependencies) {117                result.push([componentType, dependencies]);118            });119            expect(result).toEqual([['a4', []], ['a2',[]], ['a1', ['a2','a3','a4']], ['m1', ['a1', 'a2']]]);120        });121    });...ContentComponent.jsx
Source:ContentComponent.jsx  
1import React from 'react';2import { BrowserRouter, Route, Switch } from 'react-router-dom';3// ===Common===4import MainCommonComponent from "./common/MainCommonComponent";5// ===Mbr===6// import UserListComponent from "./mbr/UserListComponent";7// import AddUserComponent from "./mbr/AddUserComponent";8// import EditUserComponent from "./mbr/EditUserComponent";9import LoginMbrComponent from "./mbr/LoginMbrComponent";10import WelcomeComponent from "./mbr/WelcomeComponent";11import LogoutComponent from "./mbr/LogoutComponent";12import AuthenticatedRoute from './mbr/AuthenticatedRoute'13import ErrorComponent from './mbr/ErrorComponent'14import RegiMbrComponent from "./mbr/RegiMbrComponent";15// ===Board===16import CommunityBoardComponent from "./board/CommunityBoardComponent";17import CommunityBoardCreate from "./board/CommunityBoardCreate";18import CommunityBoardUpdate from "./board/CommunityBoardUpdate";19import CommunityBoardDetail from "./board/CommunityBoardDetail";20import ShareBoardComponent from "./board/ShareBoardComponent";21import ShareBoardCreate from "./board/ShareBoardCreate";22import ShareBoardUpdate from "./board/ShareBoardUpdate";23import ShareBoardDetail from "./board/ShareBoardDetail";24// ===News===25import EventNewsComponent from "./news/EventNewsComponent";26import EventNewsCreate from "./news/EventNewsCreate";27import EventNewsUpdate from "./news/EventNewsUpdate";28import EventNewsDetail from "./news/EventNewsDetail"; 29import MagazineNewsComponent from "./news/MagazineNewsComponent";30import MagazineNewsCreate from "./news/MagazineNewsCreate";31import MagazineNewsUpdate from "./news/MagazineNewsUpdate";32import MagazineNewsDetail from "./news/MagazineNewsDetail";33import NoticeNewsComponent from "./news/NoticeNewsComponent";34import NoticeNewsCreate from "./news/NoticeNewsCreate";35import NoticeNewsUpdate from "./news/NoticeNewsUpdate";36import NoticeNewsDetail from "./news/NoticeNewsDetail";37// ===Cell===38import CellComponent from "./cell/CellComponent";39import CellCreate from "./cell/CellCreate";40import CellDetail from "./cell/CellDetail";41const ContentComponent = () => {42  return(43    <div>44      <BrowserRouter>45        <Switch>46          {/* ===Common=== */}47          {/* ë©ì¸íë©´ */}48          <Route exact path="/" component={MainCommonComponent} />49          <Route exact path="/main" component={MainCommonComponent} />50          51          {/* ===Mbr=== */}52          {/* ë¡ê·¸ì¸ */}53          <Route exact path="/login" component={LoginMbrComponent} />54          <AuthenticatedRoute path="/welcome/:name" component={WelcomeComponent}/>55          {/* ë¡ê·¸ìì */}56          <AuthenticatedRoute path="/logout" component={LogoutComponent}/>57          {/* íìê°ì
 */}58          <Route exact path="/regist" component={RegiMbrComponent} />59          {/* íìì¸ì¦ì¤ë¥ */}60          {/* <Route component={ErrorComponent}/> */}61          62          {/* ===Board=== */}63          {/* 커뮤ëí°ê²ìí */}64          <Route exact path="/board/community" component={CommunityBoardComponent} />65          <Route exact path="/board/community/create" component={CommunityBoardCreate} />66          {/* <Route exact path="/board/community/update/{board_id}" component={CommunityBoardUpdate} /> */}67          <Route exact path="/board/community/test" component={CommunityBoardDetail} /> {/* path /board/community/{board_id} ìì í ê²! */}68          {/* ê³µì ê²ìí */}69          <Route exact path="/board/share" component={ShareBoardComponent} />70          <Route exact path="/board/share/create" component={ShareBoardCreate} />71          {/* <Route exact path="/board/share/update/{board_id}" component={ShareBoardUpdate} /> */}72          <Route exact path="/board/share/test" component={ShareBoardDetail} /> {/* path /board/share/{board_id} ìì í ê²! */}73          74          {/* ===News=== */}75          {/* ì´ë²¤í¸ìì */}76          <Route exact path="/news/event" component={EventNewsComponent} />77          <Route exact path="/news/event/create" component={EventNewsCreate} />78          {/* <Route exact path="/news/event/update/{news_id}" component={EventNewsUpdate} /> */}79          <Route exact path="/news/event/test" component={EventNewsDetail} /> {/* path /news/event/{news_id} ìì í ê²! */}80          {/* 매거ì§ìì */}81          <Route exact path="/news/magazine" component={MagazineNewsComponent} />82          <Route exact path="/news/magazine/create" component={MagazineNewsCreate} />83          {/* <Route exact path="/news/magazine/update/{news_id}" component={MagazineNewsUpdate} /> */}84          <Route exact path="/news/magazine/test" component={MagazineNewsDetail} /> {/* path /news/magazine/{news_id} ìì í ê²! */}         85          {/* ê³µì§ì¬íìì */}86          <Route exact path="/news/notice" component={NoticeNewsComponent} />87          <Route exact path="/news/notice/create" component={NoticeNewsCreate} />88          {/* <Route exact path="/news/notice/update/{news_id}" component={NoticeNewsUpdate} /> */}89          <Route exact path="/news/notice/test" component={NoticeNewsDetail} /> {/* path /news/notice/{news_id} ìì í ê²! */}90        91          {/* ===Cell=== */}92          {/* ì
ëª©ë¡ */}93          <Route exact path="/cell" component={CellComponent} />{/* ì´í path를 /user_id/cellë¡ ìì í  ê² */}94          {/* ì
ìì± */}95          <Route exact path="/cell/create" component={CellCreate} />96          {/* ì
ëí
ì¼ ë° ìì  */}97          <Route exact path="/cell/test" component={CellDetail} /> {/* path /cell/{cell_id} ìì í ê²! */}98        </Switch>99      </BrowserRouter>100    </div>101  );102}...index.js
Source:index.js  
1import App from './lib/app';2// misc3import resl from './lib/misc/resl';4import path from './lib/misc/path';5import async from './lib/misc/async';6import utils from './lib/misc/utils';7import registry from './lib/misc/registry';8// components9import ScriptComponent from './lib/framework/script-component';10import CameraComponent from './lib/framework/camera-component';11import LightComponent from './lib/framework/light-component';12import ModelComponent from './lib/framework/model-component';13import SkinningModelComponent from './lib/framework/skinning-model-component';14import AnimationComponent from './lib/framework/animation-component';15import AudioSourceComponent from './lib/framework/audio-source-component';16import SkyboxComponent from './lib/framework/skybox-component';17import ParticleSystemComponent from './lib/framework/particle/particle-system-component';18// ui-widget components19import ScreenComponent from './lib/framework/ui/screen-component';20import ScreenScalerComponent from './lib/framework/ui/screen-scaler-component';21import WidgetComponent from './lib/framework/ui/widget-component';22import ImageComponent from './lib/framework/ui/image-component';23import TextComponent from './lib/framework/ui/text-component';24import MaskComponent from './lib/framework/ui/mask-component';25import UIElementComponent from './lib/framework/ui/ui-element-component';26import ButtonComponent from './lib/framework/ui/button-component';27import ToggleComponent from './lib/framework/ui/toggle-component';28import ToggleGroupComponent from './lib/framework/ui/toggle-group-component';29import SliderComponent from './lib/framework/ui/slider-component';30import EditBoxComponent from './lib/framework/ui/edit-box-component';31import ScrollBarComponent from './lib/framework/ui/scroll-bar-component';32import BoundComponent from './lib/framework/ui/bound-component';33import ScrollViewComponent from './lib/framework/ui/scroll-view-component';34import GridLayoutComponent from './lib/framework/ui/grid-layout-component';35// assets36import Asset from './lib/assets/asset';37import Mesh from './lib/assets/mesh';38import Joints from './lib/assets/joints';39import Material from './lib/assets/material';40import Prefab from './lib/assets/prefab';41import AnimationClip from './lib/assets/animation-clip';42import { AudioClip } from './lib/assets/audio-clip';43import Gltf from './lib/assets/gltf';44import Texture from './lib/assets/texture';45import Texture2D from './lib/assets/texture-2d';46import TextureCube from './lib/assets/texture-cube';47import Sprite from './lib/assets/sprite';48// deps49import { Node } from './lib/scene-graph';50import { Component, System, Level } from './lib/ecs';51import * as math from './lib/vmath';52import * as geometry from './lib/geom-utils';53import * as primitives from './lib/primitives';54import renderer from './lib/renderer';55import gfx from './lib/gfx';56import * as memop from './lib/memop';57export default {58  // registry59  registerLoader: registry.registerLoader,60  registerClass: registry.registerClass,61  registerSystem: registry.registerSystem,62  // ecs.js63  Node,64  // assets65  Asset,66  Mesh,67  Joints,68  Material,69  Prefab,70  AnimationClip,71  AudioClip,72  Gltf,73  Texture,74  Texture2D,75  TextureCube,76  Sprite,77  // framework78  App,79  Level,80  System,81  Component,82  // components83  ScriptComponent,84  CameraComponent,85  LightComponent,86  ModelComponent,87  SkinningModelComponent,88  AnimationComponent,89  AudioSourceComponent,90  SkyboxComponent,91  ParticleSystemComponent,92  // ui-widget components93  ScreenComponent,94  ScreenScalerComponent,95  WidgetComponent,96  ImageComponent,97  TextComponent,98  MaskComponent,99  UIElementComponent,100  ButtonComponent,101  ToggleComponent,102  ToggleGroupComponent,103  SliderComponent,104  EditBoxComponent,105  ScrollBarComponent,106  BoundComponent,107  ScrollViewComponent,108  GridLayoutComponent,109  // modules110  math,111  geometry,112  memop,113  primitives,114  renderer,115  gfx,116  // misc117  utils,118  resl,119  path,120  async,...Using AI Code Generation
1const { chromium } = require('playwright');2(async () => {3  const browser = await chromium.launch();4  const context = await browser.newContext();5  const page = await context.newPage();6  await page.screenshot({ path: `google.png` });7  await browser.close();8})();9const { chromium } = require('playwright');10(async () => {11  const browser = await chromium.launch();12  const context = await browser.newContext();13  const page = await context.newPage();14  await page.screenshot({ path: `google.png` });15  await browser.close();16})();17const { chromium } = require('playwright');18(async () => {19  const browser = await chromium.launch();20  const context = await browser.newContext();21  const page = await context.newPage();22  await page.screenshot({ path: `google.png` });23  await browser.close();24})();25const { chromium } = require('playwright');26(async () => {27  const browser = await chromium.launch();28  const context = await browser.newContext();29  const page = await context.newPage();30  await page.screenshot({ path: `google.png` });31  await browser.close();32})();33const { chromium } = require('playwright');34(async () => {35  const browser = await chromium.launch();36  const context = await browser.newContext();37  const page = await context.newPage();38  await page.screenshot({ path: `google.png` });39  await browser.close();40})();Using AI Code Generation
1const { Component } = require('@playwright/test');2exports.Component = Component;3const { Page } = require('@playwright/test');4exports.Page = Page;5const { Browser } = require('@playwright/test');6exports.Browser = Browser;7const { BrowserContext } = require('@playwright/test');8exports.BrowserContext = BrowserContext;9const { BrowserType } = require('@playwright/test');10exports.BrowserType = BrowserType;11const { DeviceDescriptor } = require('@playwright/test');12exports.DeviceDescriptor = DeviceDescriptor;13const { ElectronApplication } = require('@playwright/test');14exports.ElectronApplication = ElectronApplication;15const { AndroidDevice } = require('@playwright/test');16exports.AndroidDevice = AndroidDevice;17const { AndroidDeviceSocket } = require('@playwright/test');18exports.AndroidDeviceSocket = AndroidDeviceSocket;19const { AndroidDeviceUIAutomator } = require('@playwright/test');20exports.AndroidDeviceUIAutomator = AndroidDeviceUIAutomator;21const { test, expect } = require('@playwright/test');22const { Page } = require('./test');23test('should work', async ({ page }) => {24  const page = new Page();25  expect(await page.innerText('.navbar__inner')).toContain('Playwright');26});Using AI Code Generation
1const { chromium } = require('playwright');2(async () => {3  const browser = await chromium.launch();4  const page = await browser.newPage();5  const component = await page.evaluateHandle(() => {6    return new Component();7  });8  await component.evaluate((element, message) => {9    element.textContent = message;10  }, 'Hello World');11  await browser.close();12})();13class Component {14  constructor() {15    this._element = document.createElement('div');16    document.body.appendChild(this._element);17  }18  async evaluate(pageFunction, ...args) {19    return await pageFunction(this._element, ...args);20  }21}22const { chromium } = require('playwright');23(async () => {24  const browser = await chromium.launch();25  const page = await browser.newPage();26  const input = await page.$('input[type="file"]');27  await input.setInputFiles('/Users/username/Desktop/test.txt');28  await page.click('button[type="submit"]');29  await browser.close();30})();Using AI Code Generation
1const { Component } = require("@playwright/test");2const { chromium } = require("playwright");3class Page extends Component {4  constructor(page) {5    super(page);6  }7  async goto(url) {8    return this.page.goto(url);9  }10}11(async () => {12  const browser = await chromium.launch();13  const context = await browser.newContext();14  const page = await context.newPage();15  const pageObject = new Page(page);16  await browser.close();17})();18const { test, expect } = require("@playwright/test");19test("test", async ({ page }) => {20  const title = await page.title();21  expect(title).toBe("Playwright");22});23const { chromium } = require("playwright");24(async () => {25  const browser = await chromium.launch();26  const context = await browser.newContext();27  const page = await context.newPage();28  await browser.close();29})();Using AI Code Generation
1const { Component } = require('playwright');2const { chromium } = require('playwright');3class MyComponent extends Component {4  constructor(page, selector) {5    super(page, selector);6  }7  async myMethod() {8    await this.page.click(this.selector);9  }10}11const myComponent = new MyComponent(page, 'button');12await myComponent.myMethod();13await myComponent.click();14await myComponent.waitForSelector('text=Hello World');15await myComponent.waitForEvent('close');16await myComponent.clearCookies();17await myComponent.launchServer();18await myComponent.createBrowserServer();19#### constructor(page, selector)20#### async $([selector])21#### async $$(selector)22#### async $eval([selector], pageFunction[, arg])LambdaTest’s Playwright tutorial will give you a broader idea about the Playwright automation framework, its unique features, and use cases with examples to exceed your understanding of Playwright testing. This tutorial will give A to Z guidance, from installing the Playwright framework to some best practices and advanced concepts.
Get 100 minutes of automation test minutes FREE!!
