How to use unmount method in avocado

Best Python code snippet using avocado_python

ReactLegacyErrorBoundaries-test.internal.js

Source:ReactLegacyErrorBoundaries-test.internal.js Github

copy

Full Screen

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

Full Screen

Full Screen

ReactErrorBoundaries-test.js

Source:ReactErrorBoundaries-test.js Github

copy

Full Screen

1/**2 * Copyright (c) 2013-present, Facebook, Inc.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;13describe('ReactErrorBoundaries', () => {14 let log;15 let BrokenConstructor;16 let BrokenComponentWillMount;17 let BrokenComponentDidMount;18 let BrokenComponentWillReceiveProps;19 let BrokenComponentWillUpdate;20 let BrokenComponentDidUpdate;21 let BrokenComponentWillUnmount;22 let BrokenRenderErrorBoundary;23 let BrokenComponentWillMountErrorBoundary;24 let BrokenComponentDidMountErrorBoundary;25 let BrokenRender;26 let ErrorBoundary;27 let ErrorMessage;28 let NoopErrorBoundary;29 let RetryErrorBoundary;30 let Normal;31 beforeEach(() => {32 PropTypes = require('prop-types');33 ReactDOM = require('react-dom');34 React = require('react');35 log = [];36 BrokenConstructor = class extends React.Component {37 constructor(props) {38 super(props);39 log.push('BrokenConstructor constructor [!]');40 throw new Error('Hello');41 }42 render() {43 log.push('BrokenConstructor render');44 return <div>{this.props.children}</div>;45 }46 UNSAFE_componentWillMount() {47 log.push('BrokenConstructor componentWillMount');48 }49 componentDidMount() {50 log.push('BrokenConstructor componentDidMount');51 }52 UNSAFE_componentWillReceiveProps() {53 log.push('BrokenConstructor componentWillReceiveProps');54 }55 UNSAFE_componentWillUpdate() {56 log.push('BrokenConstructor componentWillUpdate');57 }58 componentDidUpdate() {59 log.push('BrokenConstructor componentDidUpdate');60 }61 componentWillUnmount() {62 log.push('BrokenConstructor componentWillUnmount');63 }64 };65 BrokenComponentWillMount = class extends React.Component {66 constructor(props) {67 super(props);68 log.push('BrokenComponentWillMount constructor');69 }70 render() {71 log.push('BrokenComponentWillMount render');72 return <div>{this.props.children}</div>;73 }74 UNSAFE_componentWillMount() {75 log.push('BrokenComponentWillMount componentWillMount [!]');76 throw new Error('Hello');77 }78 componentDidMount() {79 log.push('BrokenComponentWillMount componentDidMount');80 }81 UNSAFE_componentWillReceiveProps() {82 log.push('BrokenComponentWillMount componentWillReceiveProps');83 }84 UNSAFE_componentWillUpdate() {85 log.push('BrokenComponentWillMount componentWillUpdate');86 }87 componentDidUpdate() {88 log.push('BrokenComponentWillMount componentDidUpdate');89 }90 componentWillUnmount() {91 log.push('BrokenComponentWillMount componentWillUnmount');92 }93 };94 BrokenComponentDidMount = class extends React.Component {95 constructor(props) {96 super(props);97 log.push('BrokenComponentDidMount constructor');98 }99 render() {100 log.push('BrokenComponentDidMount render');101 return <div>{this.props.children}</div>;102 }103 UNSAFE_componentWillMount() {104 log.push('BrokenComponentDidMount componentWillMount');105 }106 componentDidMount() {107 log.push('BrokenComponentDidMount componentDidMount [!]');108 throw new Error('Hello');109 }110 UNSAFE_componentWillReceiveProps() {111 log.push('BrokenComponentDidMount componentWillReceiveProps');112 }113 UNSAFE_componentWillUpdate() {114 log.push('BrokenComponentDidMount componentWillUpdate');115 }116 componentDidUpdate() {117 log.push('BrokenComponentDidMount componentDidUpdate');118 }119 componentWillUnmount() {120 log.push('BrokenComponentDidMount componentWillUnmount');121 }122 };123 BrokenComponentWillReceiveProps = class extends React.Component {124 constructor(props) {125 super(props);126 log.push('BrokenComponentWillReceiveProps constructor');127 }128 render() {129 log.push('BrokenComponentWillReceiveProps render');130 return <div>{this.props.children}</div>;131 }132 UNSAFE_componentWillMount() {133 log.push('BrokenComponentWillReceiveProps componentWillMount');134 }135 componentDidMount() {136 log.push('BrokenComponentWillReceiveProps componentDidMount');137 }138 UNSAFE_componentWillReceiveProps() {139 log.push(140 'BrokenComponentWillReceiveProps componentWillReceiveProps [!]',141 );142 throw new Error('Hello');143 }144 UNSAFE_componentWillUpdate() {145 log.push('BrokenComponentWillReceiveProps componentWillUpdate');146 }147 componentDidUpdate() {148 log.push('BrokenComponentWillReceiveProps componentDidUpdate');149 }150 componentWillUnmount() {151 log.push('BrokenComponentWillReceiveProps componentWillUnmount');152 }153 };154 BrokenComponentWillUpdate = class extends React.Component {155 constructor(props) {156 super(props);157 log.push('BrokenComponentWillUpdate constructor');158 }159 render() {160 log.push('BrokenComponentWillUpdate render');161 return <div>{this.props.children}</div>;162 }163 UNSAFE_componentWillMount() {164 log.push('BrokenComponentWillUpdate componentWillMount');165 }166 componentDidMount() {167 log.push('BrokenComponentWillUpdate componentDidMount');168 }169 UNSAFE_componentWillReceiveProps() {170 log.push('BrokenComponentWillUpdate componentWillReceiveProps');171 }172 UNSAFE_componentWillUpdate() {173 log.push('BrokenComponentWillUpdate componentWillUpdate [!]');174 throw new Error('Hello');175 }176 componentDidUpdate() {177 log.push('BrokenComponentWillUpdate componentDidUpdate');178 }179 componentWillUnmount() {180 log.push('BrokenComponentWillUpdate componentWillUnmount');181 }182 };183 BrokenComponentDidUpdate = class extends React.Component {184 static defaultProps = {185 errorText: 'Hello',186 };187 constructor(props) {188 super(props);189 log.push('BrokenComponentDidUpdate constructor');190 }191 render() {192 log.push('BrokenComponentDidUpdate render');193 return <div>{this.props.children}</div>;194 }195 UNSAFE_componentWillMount() {196 log.push('BrokenComponentDidUpdate componentWillMount');197 }198 componentDidMount() {199 log.push('BrokenComponentDidUpdate componentDidMount');200 }201 UNSAFE_componentWillReceiveProps() {202 log.push('BrokenComponentDidUpdate componentWillReceiveProps');203 }204 UNSAFE_componentWillUpdate() {205 log.push('BrokenComponentDidUpdate componentWillUpdate');206 }207 componentDidUpdate() {208 log.push('BrokenComponentDidUpdate componentDidUpdate [!]');209 throw new Error(this.props.errorText);210 }211 componentWillUnmount() {212 log.push('BrokenComponentDidUpdate componentWillUnmount');213 }214 };215 BrokenComponentWillUnmount = class extends React.Component {216 static defaultProps = {217 errorText: 'Hello',218 };219 constructor(props) {220 super(props);221 log.push('BrokenComponentWillUnmount constructor');222 }223 render() {224 log.push('BrokenComponentWillUnmount render');225 return <div>{this.props.children}</div>;226 }227 UNSAFE_componentWillMount() {228 log.push('BrokenComponentWillUnmount componentWillMount');229 }230 componentDidMount() {231 log.push('BrokenComponentWillUnmount componentDidMount');232 }233 UNSAFE_componentWillReceiveProps() {234 log.push('BrokenComponentWillUnmount componentWillReceiveProps');235 }236 UNSAFE_componentWillUpdate() {237 log.push('BrokenComponentWillUnmount componentWillUpdate');238 }239 componentDidUpdate() {240 log.push('BrokenComponentWillUnmount componentDidUpdate');241 }242 componentWillUnmount() {243 log.push('BrokenComponentWillUnmount componentWillUnmount [!]');244 throw new Error(this.props.errorText);245 }246 };247 BrokenComponentWillMountErrorBoundary = class extends React.Component {248 constructor(props) {249 super(props);250 this.state = {error: null};251 log.push('BrokenComponentWillMountErrorBoundary constructor');252 }253 render() {254 if (this.state.error) {255 log.push('BrokenComponentWillMountErrorBoundary render error');256 return <div>Caught an error: {this.state.error.message}.</div>;257 }258 log.push('BrokenComponentWillMountErrorBoundary render success');259 return <div>{this.props.children}</div>;260 }261 UNSAFE_componentWillMount() {262 log.push(263 'BrokenComponentWillMountErrorBoundary componentWillMount [!]',264 );265 throw new Error('Hello');266 }267 componentDidMount() {268 log.push('BrokenComponentWillMountErrorBoundary componentDidMount');269 }270 componentWillUnmount() {271 log.push('BrokenComponentWillMountErrorBoundary componentWillUnmount');272 }273 componentDidCatch(error) {274 log.push('BrokenComponentWillMountErrorBoundary componentDidCatch');275 this.setState({error});276 }277 };278 BrokenComponentDidMountErrorBoundary = class extends React.Component {279 constructor(props) {280 super(props);281 this.state = {error: null};282 log.push('BrokenComponentDidMountErrorBoundary constructor');283 }284 render() {285 if (this.state.error) {286 log.push('BrokenComponentDidMountErrorBoundary render error');287 return <div>Caught an error: {this.state.error.message}.</div>;288 }289 log.push('BrokenComponentDidMountErrorBoundary render success');290 return <div>{this.props.children}</div>;291 }292 UNSAFE_componentWillMount() {293 log.push('BrokenComponentDidMountErrorBoundary componentWillMount');294 }295 componentDidMount() {296 log.push('BrokenComponentDidMountErrorBoundary componentDidMount [!]');297 throw new Error('Hello');298 }299 componentWillUnmount() {300 log.push('BrokenComponentDidMountErrorBoundary componentWillUnmount');301 }302 componentDidCatch(error) {303 log.push('BrokenComponentDidMountErrorBoundary componentDidCatch');304 this.setState({error});305 }306 };307 BrokenRenderErrorBoundary = class extends React.Component {308 constructor(props) {309 super(props);310 this.state = {error: null};311 log.push('BrokenRenderErrorBoundary constructor');312 }313 render() {314 if (this.state.error) {315 log.push('BrokenRenderErrorBoundary render error [!]');316 throw new Error('Hello');317 }318 log.push('BrokenRenderErrorBoundary render success');319 return <div>{this.props.children}</div>;320 }321 UNSAFE_componentWillMount() {322 log.push('BrokenRenderErrorBoundary componentWillMount');323 }324 componentDidMount() {325 log.push('BrokenRenderErrorBoundary componentDidMount');326 }327 componentWillUnmount() {328 log.push('BrokenRenderErrorBoundary componentWillUnmount');329 }330 componentDidCatch(error) {331 log.push('BrokenRenderErrorBoundary componentDidCatch');332 this.setState({error});333 }334 };335 BrokenRender = class extends React.Component {336 constructor(props) {337 super(props);338 log.push('BrokenRender constructor');339 }340 render() {341 log.push('BrokenRender render [!]');342 throw new Error('Hello');343 }344 UNSAFE_componentWillMount() {345 log.push('BrokenRender componentWillMount');346 }347 componentDidMount() {348 log.push('BrokenRender componentDidMount');349 }350 UNSAFE_componentWillReceiveProps() {351 log.push('BrokenRender componentWillReceiveProps');352 }353 UNSAFE_componentWillUpdate() {354 log.push('BrokenRender componentWillUpdate');355 }356 componentDidUpdate() {357 log.push('BrokenRender componentDidUpdate');358 }359 componentWillUnmount() {360 log.push('BrokenRender componentWillUnmount');361 }362 };363 NoopErrorBoundary = class extends React.Component {364 constructor(props) {365 super(props);366 log.push('NoopErrorBoundary constructor');367 }368 render() {369 log.push('NoopErrorBoundary render');370 return <BrokenRender />;371 }372 UNSAFE_componentWillMount() {373 log.push('NoopErrorBoundary componentWillMount');374 }375 componentDidMount() {376 log.push('NoopErrorBoundary componentDidMount');377 }378 componentWillUnmount() {379 log.push('NoopErrorBoundary componentWillUnmount');380 }381 componentDidCatch() {382 log.push('NoopErrorBoundary componentDidCatch');383 }384 };385 Normal = class extends React.Component {386 static defaultProps = {387 logName: 'Normal',388 };389 constructor(props) {390 super(props);391 log.push(`${this.props.logName} constructor`);392 }393 render() {394 log.push(`${this.props.logName} render`);395 return <div>{this.props.children}</div>;396 }397 UNSAFE_componentWillMount() {398 log.push(`${this.props.logName} componentWillMount`);399 }400 componentDidMount() {401 log.push(`${this.props.logName} componentDidMount`);402 }403 UNSAFE_componentWillReceiveProps() {404 log.push(`${this.props.logName} componentWillReceiveProps`);405 }406 UNSAFE_componentWillUpdate() {407 log.push(`${this.props.logName} componentWillUpdate`);408 }409 componentDidUpdate() {410 log.push(`${this.props.logName} componentDidUpdate`);411 }412 componentWillUnmount() {413 log.push(`${this.props.logName} componentWillUnmount`);414 }415 };416 ErrorBoundary = class extends React.Component {417 constructor(props) {418 super(props);419 this.state = {error: null};420 log.push(`${this.props.logName} constructor`);421 }422 render() {423 if (this.state.error && !this.props.forceRetry) {424 log.push(`${this.props.logName} render error`);425 return this.props.renderError(this.state.error, this.props);426 }427 log.push(`${this.props.logName} render success`);428 return <div>{this.props.children}</div>;429 }430 componentDidCatch(error) {431 log.push(`${this.props.logName} componentDidCatch`);432 this.setState({error});433 }434 UNSAFE_componentWillMount() {435 log.push(`${this.props.logName} componentWillMount`);436 }437 componentDidMount() {438 log.push(`${this.props.logName} componentDidMount`);439 }440 UNSAFE_componentWillReceiveProps() {441 log.push(`${this.props.logName} componentWillReceiveProps`);442 }443 UNSAFE_componentWillUpdate() {444 log.push(`${this.props.logName} componentWillUpdate`);445 }446 componentDidUpdate() {447 log.push(`${this.props.logName} componentDidUpdate`);448 }449 componentWillUnmount() {450 log.push(`${this.props.logName} componentWillUnmount`);451 }452 };453 ErrorBoundary.defaultProps = {454 logName: 'ErrorBoundary',455 renderError(error, props) {456 return (457 <div ref={props.errorMessageRef}>458 Caught an error: {error.message}.459 </div>460 );461 },462 };463 RetryErrorBoundary = class extends React.Component {464 constructor(props) {465 super(props);466 log.push('RetryErrorBoundary constructor');467 }468 render() {469 log.push('RetryErrorBoundary render');470 return <BrokenRender />;471 }472 UNSAFE_componentWillMount() {473 log.push('RetryErrorBoundary componentWillMount');474 }475 componentDidMount() {476 log.push('RetryErrorBoundary componentDidMount');477 }478 componentWillUnmount() {479 log.push('RetryErrorBoundary componentWillUnmount');480 }481 componentDidCatch(error) {482 log.push('RetryErrorBoundary componentDidCatch [!]');483 // In Fiber, calling setState() (and failing) is treated as a rethrow.484 this.setState({});485 }486 };487 ErrorMessage = class extends React.Component {488 constructor(props) {489 super(props);490 log.push('ErrorMessage constructor');491 }492 UNSAFE_componentWillMount() {493 log.push('ErrorMessage componentWillMount');494 }495 componentDidMount() {496 log.push('ErrorMessage componentDidMount');497 }498 componentWillUnmount() {499 log.push('ErrorMessage componentWillUnmount');500 }501 render() {502 log.push('ErrorMessage render');503 return <div>Caught an error: {this.props.message}.</div>;504 }505 };506 });507 it('does not swallow exceptions on mounting without boundaries', () => {508 let container = document.createElement('div');509 expect(() => {510 ReactDOM.render(<BrokenRender />, container);511 }).toThrow('Hello');512 container = document.createElement('div');513 expect(() => {514 ReactDOM.render(<BrokenComponentWillMount />, container);515 }).toThrow('Hello');516 container = document.createElement('div');517 expect(() => {518 ReactDOM.render(<BrokenComponentDidMount />, container);519 }).toThrow('Hello');520 });521 it('does not swallow exceptions on updating without boundaries', () => {522 let container = document.createElement('div');523 ReactDOM.render(<BrokenComponentWillUpdate />, container);524 expect(() => {525 ReactDOM.render(<BrokenComponentWillUpdate />, container);526 }).toThrow('Hello');527 container = document.createElement('div');528 ReactDOM.render(<BrokenComponentWillReceiveProps />, container);529 expect(() => {530 ReactDOM.render(<BrokenComponentWillReceiveProps />, container);531 }).toThrow('Hello');532 container = document.createElement('div');533 ReactDOM.render(<BrokenComponentDidUpdate />, container);534 expect(() => {535 ReactDOM.render(<BrokenComponentDidUpdate />, container);536 }).toThrow('Hello');537 });538 it('does not swallow exceptions on unmounting without boundaries', () => {539 const container = document.createElement('div');540 ReactDOM.render(<BrokenComponentWillUnmount />, container);541 expect(() => {542 ReactDOM.unmountComponentAtNode(container);543 }).toThrow('Hello');544 });545 it('prevents errors from leaking into other roots', () => {546 const container1 = document.createElement('div');547 const container2 = document.createElement('div');548 const container3 = document.createElement('div');549 ReactDOM.render(<span>Before 1</span>, container1);550 expect(() => {551 ReactDOM.render(<BrokenRender />, container2);552 }).toThrow('Hello');553 ReactDOM.render(554 <ErrorBoundary>555 <BrokenRender />556 </ErrorBoundary>,557 container3,558 );559 expect(container1.firstChild.textContent).toBe('Before 1');560 expect(container2.firstChild).toBe(null);561 expect(container3.firstChild.textContent).toBe('Caught an error: Hello.');562 ReactDOM.render(<span>After 1</span>, container1);563 ReactDOM.render(<span>After 2</span>, container2);564 ReactDOM.render(565 <ErrorBoundary forceRetry={true}>After 3</ErrorBoundary>,566 container3,567 );568 expect(container1.firstChild.textContent).toBe('After 1');569 expect(container2.firstChild.textContent).toBe('After 2');570 expect(container3.firstChild.textContent).toBe('After 3');571 ReactDOM.unmountComponentAtNode(container1);572 ReactDOM.unmountComponentAtNode(container2);573 ReactDOM.unmountComponentAtNode(container3);574 expect(container1.firstChild).toBe(null);575 expect(container2.firstChild).toBe(null);576 expect(container3.firstChild).toBe(null);577 });578 it('renders an error state if child throws in render', () => {579 const container = document.createElement('div');580 ReactDOM.render(581 <ErrorBoundary>582 <BrokenRender />583 </ErrorBoundary>,584 container,585 );586 expect(container.firstChild.textContent).toBe('Caught an error: Hello.');587 expect(log).toEqual([588 'ErrorBoundary constructor',589 'ErrorBoundary componentWillMount',590 'ErrorBoundary render success',591 'BrokenRender constructor',592 'BrokenRender componentWillMount',593 'BrokenRender render [!]',594 // Fiber mounts with null children before capturing error595 'ErrorBoundary componentDidMount',596 // Catch and render an error message597 'ErrorBoundary componentDidCatch',598 'ErrorBoundary componentWillUpdate',599 'ErrorBoundary render error',600 'ErrorBoundary componentDidUpdate',601 ]);602 log.length = 0;603 ReactDOM.unmountComponentAtNode(container);604 expect(log).toEqual(['ErrorBoundary componentWillUnmount']);605 });606 it('renders an error state if child throws in constructor', () => {607 const container = document.createElement('div');608 ReactDOM.render(609 <ErrorBoundary>610 <BrokenConstructor />611 </ErrorBoundary>,612 container,613 );614 expect(container.firstChild.textContent).toBe('Caught an error: Hello.');615 expect(log).toEqual([616 'ErrorBoundary constructor',617 'ErrorBoundary componentWillMount',618 'ErrorBoundary render success',619 'BrokenConstructor constructor [!]',620 // Fiber mounts with null children before capturing error621 'ErrorBoundary componentDidMount',622 // Catch and render an error message623 'ErrorBoundary componentDidCatch',624 'ErrorBoundary componentWillUpdate',625 'ErrorBoundary render error',626 'ErrorBoundary componentDidUpdate',627 ]);628 log.length = 0;629 ReactDOM.unmountComponentAtNode(container);630 expect(log).toEqual(['ErrorBoundary componentWillUnmount']);631 });632 it('renders an error state if child throws in componentWillMount', () => {633 const container = document.createElement('div');634 ReactDOM.render(635 <ErrorBoundary>636 <BrokenComponentWillMount />637 </ErrorBoundary>,638 container,639 );640 expect(container.firstChild.textContent).toBe('Caught an error: Hello.');641 expect(log).toEqual([642 'ErrorBoundary constructor',643 'ErrorBoundary componentWillMount',644 'ErrorBoundary render success',645 'BrokenComponentWillMount constructor',646 'BrokenComponentWillMount componentWillMount [!]',647 'ErrorBoundary componentDidMount',648 'ErrorBoundary componentDidCatch',649 'ErrorBoundary componentWillUpdate',650 'ErrorBoundary render error',651 'ErrorBoundary componentDidUpdate',652 ]);653 log.length = 0;654 ReactDOM.unmountComponentAtNode(container);655 expect(log).toEqual(['ErrorBoundary componentWillUnmount']);656 });657 it('renders an error state if context provider throws in componentWillMount', () => {658 class BrokenComponentWillMountWithContext extends React.Component {659 static childContextTypes = {foo: PropTypes.number};660 getChildContext() {661 return {foo: 42};662 }663 render() {664 return <div>{this.props.children}</div>;665 }666 UNSAFE_componentWillMount() {667 throw new Error('Hello');668 }669 }670 const container = document.createElement('div');671 ReactDOM.render(672 <ErrorBoundary>673 <BrokenComponentWillMountWithContext />674 </ErrorBoundary>,675 container,676 );677 expect(container.firstChild.textContent).toBe('Caught an error: Hello.');678 });679 it('renders an error state if module-style context provider throws in componentWillMount', () => {680 function BrokenComponentWillMountWithContext() {681 return {682 getChildContext() {683 return {foo: 42};684 },685 render() {686 return <div>{this.props.children}</div>;687 },688 UNSAFE_componentWillMount() {689 throw new Error('Hello');690 },691 };692 }693 BrokenComponentWillMountWithContext.childContextTypes = {694 foo: PropTypes.number,695 };696 const container = document.createElement('div');697 ReactDOM.render(698 <ErrorBoundary>699 <BrokenComponentWillMountWithContext />700 </ErrorBoundary>,701 container,702 );703 expect(container.firstChild.textContent).toBe('Caught an error: Hello.');704 });705 it('mounts the error message if mounting fails', () => {706 function renderError(error) {707 return <ErrorMessage message={error.message} />;708 }709 const container = document.createElement('div');710 ReactDOM.render(711 <ErrorBoundary renderError={renderError}>712 <BrokenRender />713 </ErrorBoundary>,714 container,715 );716 expect(log).toEqual([717 'ErrorBoundary constructor',718 'ErrorBoundary componentWillMount',719 'ErrorBoundary render success',720 'BrokenRender constructor',721 'BrokenRender componentWillMount',722 'BrokenRender render [!]',723 'ErrorBoundary componentDidMount',724 'ErrorBoundary componentDidCatch',725 'ErrorBoundary componentWillUpdate',726 'ErrorBoundary render error',727 'ErrorMessage constructor',728 'ErrorMessage componentWillMount',729 'ErrorMessage render',730 'ErrorMessage componentDidMount',731 'ErrorBoundary componentDidUpdate',732 ]);733 log.length = 0;734 ReactDOM.unmountComponentAtNode(container);735 expect(log).toEqual([736 'ErrorBoundary componentWillUnmount',737 'ErrorMessage componentWillUnmount',738 ]);739 });740 it('propagates errors on retry on mounting', () => {741 const container = document.createElement('div');742 ReactDOM.render(743 <ErrorBoundary>744 <RetryErrorBoundary>745 <BrokenRender />746 </RetryErrorBoundary>747 </ErrorBoundary>,748 container,749 );750 expect(container.firstChild.textContent).toBe('Caught an error: Hello.');751 expect(log).toEqual([752 'ErrorBoundary constructor',753 'ErrorBoundary componentWillMount',754 'ErrorBoundary render success',755 'RetryErrorBoundary constructor',756 'RetryErrorBoundary componentWillMount',757 'RetryErrorBoundary render',758 'BrokenRender constructor',759 'BrokenRender componentWillMount',760 'BrokenRender render [!]',761 // In Fiber, failed error boundaries render null before attempting to recover762 'RetryErrorBoundary componentDidMount',763 'RetryErrorBoundary componentDidCatch [!]',764 'ErrorBoundary componentDidMount',765 // Retry766 'RetryErrorBoundary render',767 'BrokenRender constructor',768 'BrokenRender componentWillMount',769 'BrokenRender render [!]',770 // This time, the error propagates to the higher boundary771 'RetryErrorBoundary componentWillUnmount',772 'ErrorBoundary componentDidCatch',773 // Render the error774 'ErrorBoundary componentWillUpdate',775 'ErrorBoundary render error',776 'ErrorBoundary componentDidUpdate',777 ]);778 log.length = 0;779 ReactDOM.unmountComponentAtNode(container);780 expect(log).toEqual(['ErrorBoundary componentWillUnmount']);781 });782 it('propagates errors inside boundary during componentWillMount', () => {783 const container = document.createElement('div');784 ReactDOM.render(785 <ErrorBoundary>786 <BrokenComponentWillMountErrorBoundary />787 </ErrorBoundary>,788 container,789 );790 expect(container.firstChild.textContent).toBe('Caught an error: Hello.');791 expect(log).toEqual([792 'ErrorBoundary constructor',793 'ErrorBoundary componentWillMount',794 'ErrorBoundary render success',795 'BrokenComponentWillMountErrorBoundary constructor',796 'BrokenComponentWillMountErrorBoundary componentWillMount [!]',797 // The error propagates to the higher boundary798 'ErrorBoundary componentDidMount',799 'ErrorBoundary componentDidCatch',800 'ErrorBoundary componentWillUpdate',801 'ErrorBoundary render error',802 'ErrorBoundary componentDidUpdate',803 ]);804 log.length = 0;805 ReactDOM.unmountComponentAtNode(container);806 expect(log).toEqual(['ErrorBoundary componentWillUnmount']);807 });808 it('propagates errors inside boundary while rendering error state', () => {809 const container = document.createElement('div');810 ReactDOM.render(811 <ErrorBoundary>812 <BrokenRenderErrorBoundary>813 <BrokenRender />814 </BrokenRenderErrorBoundary>815 </ErrorBoundary>,816 container,817 );818 expect(container.firstChild.textContent).toBe('Caught an error: Hello.');819 expect(log).toEqual([820 'ErrorBoundary constructor',821 'ErrorBoundary componentWillMount',822 'ErrorBoundary render success',823 'BrokenRenderErrorBoundary constructor',824 'BrokenRenderErrorBoundary componentWillMount',825 'BrokenRenderErrorBoundary render success',826 'BrokenRender constructor',827 'BrokenRender componentWillMount',828 'BrokenRender render [!]',829 // The first error boundary catches the error830 // It adjusts state but throws displaying the message831 // Finish mounting with null children832 'BrokenRenderErrorBoundary componentDidMount',833 // Attempt to handle the error834 'BrokenRenderErrorBoundary componentDidCatch',835 'ErrorBoundary componentDidMount',836 'BrokenRenderErrorBoundary render error [!]',837 // Boundary fails with new error, propagate to next boundary838 'BrokenRenderErrorBoundary componentWillUnmount',839 // Attempt to handle the error again840 'ErrorBoundary componentDidCatch',841 'ErrorBoundary componentWillUpdate',842 'ErrorBoundary render error',843 'ErrorBoundary componentDidUpdate',844 ]);845 log.length = 0;846 ReactDOM.unmountComponentAtNode(container);847 expect(log).toEqual(['ErrorBoundary componentWillUnmount']);848 });849 it('does not call componentWillUnmount when aborting initial mount', () => {850 const container = document.createElement('div');851 ReactDOM.render(852 <ErrorBoundary>853 <Normal />854 <BrokenRender />855 <Normal />856 </ErrorBoundary>,857 container,858 );859 expect(container.firstChild.textContent).toBe('Caught an error: Hello.');860 expect(log).toEqual([861 'ErrorBoundary constructor',862 'ErrorBoundary componentWillMount',863 'ErrorBoundary render success',864 // Render first child865 'Normal constructor',866 'Normal componentWillMount',867 'Normal render',868 // Render second child (it throws)869 'BrokenRender constructor',870 'BrokenRender componentWillMount',871 'BrokenRender render [!]',872 // Finish mounting with null children873 'ErrorBoundary componentDidMount',874 // Handle the error875 'ErrorBoundary componentDidCatch',876 // Render the error message877 'ErrorBoundary componentWillUpdate',878 'ErrorBoundary render error',879 'ErrorBoundary componentDidUpdate',880 ]);881 log.length = 0;882 ReactDOM.unmountComponentAtNode(container);883 expect(log).toEqual(['ErrorBoundary componentWillUnmount']);884 });885 it('resets callback refs if mounting aborts', () => {886 function childRef(x) {887 log.push('Child ref is set to ' + x);888 }889 function errorMessageRef(x) {890 log.push('Error message ref is set to ' + x);891 }892 const container = document.createElement('div');893 ReactDOM.render(894 <ErrorBoundary errorMessageRef={errorMessageRef}>895 <div ref={childRef} />896 <BrokenRender />897 </ErrorBoundary>,898 container,899 );900 expect(container.textContent).toBe('Caught an error: Hello.');901 expect(log).toEqual([902 'ErrorBoundary constructor',903 'ErrorBoundary componentWillMount',904 'ErrorBoundary render success',905 'BrokenRender constructor',906 'BrokenRender componentWillMount',907 'BrokenRender render [!]',908 // Handle error:909 // Finish mounting with null children910 'ErrorBoundary componentDidMount',911 // Handle the error912 'ErrorBoundary componentDidCatch',913 // Render the error message914 'ErrorBoundary componentWillUpdate',915 'ErrorBoundary render error',916 'Error message ref is set to [object HTMLDivElement]',917 'ErrorBoundary componentDidUpdate',918 ]);919 log.length = 0;920 ReactDOM.unmountComponentAtNode(container);921 expect(log).toEqual([922 'ErrorBoundary componentWillUnmount',923 'Error message ref is set to null',924 ]);925 });926 it('resets object refs if mounting aborts', () => {927 let childRef = React.createRef();928 let errorMessageRef = React.createRef();929 const container = document.createElement('div');930 ReactDOM.render(931 <ErrorBoundary errorMessageRef={errorMessageRef}>932 <div ref={childRef} />933 <BrokenRender />934 </ErrorBoundary>,935 container,936 );937 expect(container.textContent).toBe('Caught an error: Hello.');938 expect(log).toEqual([939 'ErrorBoundary constructor',940 'ErrorBoundary componentWillMount',941 'ErrorBoundary render success',942 'BrokenRender constructor',943 'BrokenRender componentWillMount',944 'BrokenRender render [!]',945 // Handle error:946 // Finish mounting with null children947 'ErrorBoundary componentDidMount',948 // Handle the error949 'ErrorBoundary componentDidCatch',950 // Render the error message951 'ErrorBoundary componentWillUpdate',952 'ErrorBoundary render error',953 'ErrorBoundary componentDidUpdate',954 ]);955 expect(errorMessageRef.value.toString()).toEqual('[object HTMLDivElement]');956 log.length = 0;957 ReactDOM.unmountComponentAtNode(container);958 expect(log).toEqual(['ErrorBoundary componentWillUnmount']);959 expect(errorMessageRef.value).toEqual(null);960 });961 it('successfully mounts if no error occurs', () => {962 const container = document.createElement('div');963 ReactDOM.render(964 <ErrorBoundary>965 <div>Mounted successfully.</div>966 </ErrorBoundary>,967 container,968 );969 expect(container.firstChild.textContent).toBe('Mounted successfully.');970 expect(log).toEqual([971 'ErrorBoundary constructor',972 'ErrorBoundary componentWillMount',973 'ErrorBoundary render success',974 'ErrorBoundary componentDidMount',975 ]);976 log.length = 0;977 ReactDOM.unmountComponentAtNode(container);978 expect(log).toEqual(['ErrorBoundary componentWillUnmount']);979 });980 it('catches if child throws in constructor during update', () => {981 const container = document.createElement('div');982 ReactDOM.render(983 <ErrorBoundary>984 <Normal />985 </ErrorBoundary>,986 container,987 );988 log.length = 0;989 ReactDOM.render(990 <ErrorBoundary>991 <Normal />992 <Normal logName="Normal2" />993 <BrokenConstructor />994 </ErrorBoundary>,995 container,996 );997 expect(container.textContent).toBe('Caught an error: Hello.');998 expect(log).toEqual([999 'ErrorBoundary componentWillReceiveProps',1000 'ErrorBoundary componentWillUpdate',1001 'ErrorBoundary render success',1002 'Normal componentWillReceiveProps',1003 'Normal componentWillUpdate',1004 'Normal render',1005 // Normal2 will attempt to mount:1006 'Normal2 constructor',1007 'Normal2 componentWillMount',1008 'Normal2 render',1009 // BrokenConstructor will abort rendering:1010 'BrokenConstructor constructor [!]',1011 // Finish updating with null children1012 'Normal componentWillUnmount',1013 'ErrorBoundary componentDidUpdate',1014 // Handle the error1015 'ErrorBoundary componentDidCatch',1016 // Render the error message1017 'ErrorBoundary componentWillUpdate',1018 'ErrorBoundary render error',1019 'ErrorBoundary componentDidUpdate',1020 ]);1021 log.length = 0;1022 ReactDOM.unmountComponentAtNode(container);1023 expect(log).toEqual(['ErrorBoundary componentWillUnmount']);1024 });1025 it('catches if child throws in componentWillMount during update', () => {1026 const container = document.createElement('div');1027 ReactDOM.render(1028 <ErrorBoundary>1029 <Normal />1030 </ErrorBoundary>,1031 container,1032 );1033 log.length = 0;1034 ReactDOM.render(1035 <ErrorBoundary>1036 <Normal />1037 <Normal logName="Normal2" />1038 <BrokenComponentWillMount />1039 </ErrorBoundary>,1040 container,1041 );1042 expect(container.textContent).toBe('Caught an error: Hello.');1043 expect(log).toEqual([1044 'ErrorBoundary componentWillReceiveProps',1045 'ErrorBoundary componentWillUpdate',1046 'ErrorBoundary render success',1047 'Normal componentWillReceiveProps',1048 'Normal componentWillUpdate',1049 'Normal render',1050 // Normal2 will attempt to mount:1051 'Normal2 constructor',1052 'Normal2 componentWillMount',1053 'Normal2 render',1054 // BrokenComponentWillMount will abort rendering:1055 'BrokenComponentWillMount constructor',1056 'BrokenComponentWillMount componentWillMount [!]',1057 // Finish updating with null children1058 'Normal componentWillUnmount',1059 'ErrorBoundary componentDidUpdate',1060 // Handle the error1061 'ErrorBoundary componentDidCatch',1062 // Render the error message1063 'ErrorBoundary componentWillUpdate',1064 'ErrorBoundary render error',1065 'ErrorBoundary componentDidUpdate',1066 ]);1067 log.length = 0;1068 ReactDOM.unmountComponentAtNode(container);1069 expect(log).toEqual(['ErrorBoundary componentWillUnmount']);1070 });1071 it('catches if child throws in componentWillReceiveProps during update', () => {1072 const container = document.createElement('div');1073 ReactDOM.render(1074 <ErrorBoundary>1075 <Normal />1076 <BrokenComponentWillReceiveProps />1077 </ErrorBoundary>,1078 container,1079 );1080 log.length = 0;1081 ReactDOM.render(1082 <ErrorBoundary>1083 <Normal />1084 <BrokenComponentWillReceiveProps />1085 </ErrorBoundary>,1086 container,1087 );1088 expect(container.textContent).toBe('Caught an error: Hello.');1089 expect(log).toEqual([1090 'ErrorBoundary componentWillReceiveProps',1091 'ErrorBoundary componentWillUpdate',1092 'ErrorBoundary render success',1093 'Normal componentWillReceiveProps',1094 'Normal componentWillUpdate',1095 'Normal render',1096 // BrokenComponentWillReceiveProps will abort rendering:1097 'BrokenComponentWillReceiveProps componentWillReceiveProps [!]',1098 // Finish updating with null children1099 'Normal componentWillUnmount',1100 'BrokenComponentWillReceiveProps componentWillUnmount',1101 'ErrorBoundary componentDidUpdate',1102 // Handle the error1103 'ErrorBoundary componentDidCatch',1104 'ErrorBoundary componentWillUpdate',1105 'ErrorBoundary render error',1106 'ErrorBoundary componentDidUpdate',1107 ]);1108 log.length = 0;1109 ReactDOM.unmountComponentAtNode(container);1110 expect(log).toEqual(['ErrorBoundary componentWillUnmount']);1111 });1112 it('catches if child throws in componentWillUpdate during update', () => {1113 const container = document.createElement('div');1114 ReactDOM.render(1115 <ErrorBoundary>1116 <Normal />1117 <BrokenComponentWillUpdate />1118 </ErrorBoundary>,1119 container,1120 );1121 log.length = 0;1122 ReactDOM.render(1123 <ErrorBoundary>1124 <Normal />1125 <BrokenComponentWillUpdate />1126 </ErrorBoundary>,1127 container,1128 );1129 expect(container.textContent).toBe('Caught an error: Hello.');1130 expect(log).toEqual([1131 'ErrorBoundary componentWillReceiveProps',1132 'ErrorBoundary componentWillUpdate',1133 'ErrorBoundary render success',1134 'Normal componentWillReceiveProps',1135 'Normal componentWillUpdate',1136 'Normal render',1137 // BrokenComponentWillUpdate will abort rendering:1138 'BrokenComponentWillUpdate componentWillReceiveProps',1139 'BrokenComponentWillUpdate componentWillUpdate [!]',1140 // Finish updating with null children1141 'Normal componentWillUnmount',1142 'BrokenComponentWillUpdate componentWillUnmount',1143 'ErrorBoundary componentDidUpdate',1144 // Handle the error1145 'ErrorBoundary componentDidCatch',1146 'ErrorBoundary componentWillUpdate',1147 'ErrorBoundary render error',1148 'ErrorBoundary componentDidUpdate',1149 ]);1150 log.length = 0;1151 ReactDOM.unmountComponentAtNode(container);1152 expect(log).toEqual(['ErrorBoundary componentWillUnmount']);1153 });1154 it('catches if child throws in render during update', () => {1155 const container = document.createElement('div');1156 ReactDOM.render(1157 <ErrorBoundary>1158 <Normal />1159 </ErrorBoundary>,1160 container,1161 );1162 log.length = 0;1163 ReactDOM.render(1164 <ErrorBoundary>1165 <Normal />1166 <Normal logName="Normal2" />1167 <BrokenRender />1168 </ErrorBoundary>,1169 container,1170 );1171 expect(container.textContent).toBe('Caught an error: Hello.');1172 expect(log).toEqual([1173 'ErrorBoundary componentWillReceiveProps',1174 'ErrorBoundary componentWillUpdate',1175 'ErrorBoundary render success',1176 'Normal componentWillReceiveProps',1177 'Normal componentWillUpdate',1178 'Normal render',1179 // Normal2 will attempt to mount:1180 'Normal2 constructor',1181 'Normal2 componentWillMount',1182 'Normal2 render',1183 // BrokenRender will abort rendering:1184 'BrokenRender constructor',1185 'BrokenRender componentWillMount',1186 'BrokenRender render [!]',1187 // Finish updating with null children1188 'Normal componentWillUnmount',1189 'ErrorBoundary componentDidUpdate',1190 // Handle the error1191 'ErrorBoundary componentDidCatch',1192 'ErrorBoundary componentWillUpdate',1193 'ErrorBoundary render error',1194 'ErrorBoundary componentDidUpdate',1195 ]);1196 log.length = 0;1197 ReactDOM.unmountComponentAtNode(container);1198 expect(log).toEqual(['ErrorBoundary componentWillUnmount']);1199 });1200 it('keeps refs up-to-date during updates', () => {1201 function child1Ref(x) {1202 log.push('Child1 ref is set to ' + x);1203 }1204 function child2Ref(x) {1205 log.push('Child2 ref is set to ' + x);1206 }1207 function errorMessageRef(x) {1208 log.push('Error message ref is set to ' + x);1209 }1210 const container = document.createElement('div');1211 ReactDOM.render(1212 <ErrorBoundary errorMessageRef={errorMessageRef}>1213 <div ref={child1Ref} />1214 </ErrorBoundary>,1215 container,1216 );1217 expect(log).toEqual([1218 'ErrorBoundary constructor',1219 'ErrorBoundary componentWillMount',1220 'ErrorBoundary render success',1221 'Child1 ref is set to [object HTMLDivElement]',1222 'ErrorBoundary componentDidMount',1223 ]);1224 log.length = 0;1225 ReactDOM.render(1226 <ErrorBoundary errorMessageRef={errorMessageRef}>1227 <div ref={child1Ref} />1228 <div ref={child2Ref} />1229 <BrokenRender />1230 </ErrorBoundary>,1231 container,1232 );1233 expect(container.textContent).toBe('Caught an error: Hello.');1234 expect(log).toEqual([1235 'ErrorBoundary componentWillReceiveProps',1236 'ErrorBoundary componentWillUpdate',1237 'ErrorBoundary render success',1238 // BrokenRender will abort rendering:1239 'BrokenRender constructor',1240 'BrokenRender componentWillMount',1241 'BrokenRender render [!]',1242 // Finish updating with null children1243 'Child1 ref is set to null',1244 'ErrorBoundary componentDidUpdate',1245 // Handle the error1246 'ErrorBoundary componentDidCatch',1247 'ErrorBoundary componentWillUpdate',1248 'ErrorBoundary render error',1249 'Error message ref is set to [object HTMLDivElement]',1250 // Child2 ref is never set because its mounting aborted1251 'ErrorBoundary componentDidUpdate',1252 ]);1253 log.length = 0;1254 ReactDOM.unmountComponentAtNode(container);1255 expect(log).toEqual([1256 'ErrorBoundary componentWillUnmount',1257 'Error message ref is set to null',1258 ]);1259 });1260 it('recovers from componentWillUnmount errors on update', () => {1261 const container = document.createElement('div');1262 ReactDOM.render(1263 <ErrorBoundary>1264 <BrokenComponentWillUnmount />1265 <BrokenComponentWillUnmount />1266 <Normal />1267 </ErrorBoundary>,1268 container,1269 );1270 log.length = 0;1271 ReactDOM.render(1272 <ErrorBoundary>1273 <BrokenComponentWillUnmount />1274 </ErrorBoundary>,1275 container,1276 );1277 expect(container.textContent).toBe('Caught an error: Hello.');1278 expect(log).toEqual([1279 'ErrorBoundary componentWillReceiveProps',1280 'ErrorBoundary componentWillUpdate',1281 'ErrorBoundary render success',1282 // Update existing child:1283 'BrokenComponentWillUnmount componentWillReceiveProps',1284 'BrokenComponentWillUnmount componentWillUpdate',1285 'BrokenComponentWillUnmount render',1286 // Unmounting throws:1287 'BrokenComponentWillUnmount componentWillUnmount [!]',1288 // Fiber proceeds with lifecycles despite errors1289 'Normal componentWillUnmount',1290 // The components have updated in this phase1291 'BrokenComponentWillUnmount componentDidUpdate',1292 'ErrorBoundary componentDidUpdate',1293 // Now that commit phase is done, Fiber unmounts the boundary's children1294 'BrokenComponentWillUnmount componentWillUnmount [!]',1295 'ErrorBoundary componentDidCatch',1296 // The initial render was aborted, so1297 // Fiber retries from the root.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 'Normal componentWillUnmount',1348 'BrokenComponentWillUnmount componentWillUnmount [!]',1349 // Now that commit phase is done, Fiber handles errors1350 'ErrorBoundary componentDidCatch',1351 // The initial render was aborted, so1352 // Fiber retries from the root.1353 'ErrorBoundary componentWillUpdate',1354 // Render an error now (stack will do it later)1355 'ErrorBoundary render error',1356 // Done1357 'ErrorBoundary componentDidUpdate',1358 ]);1359 log.length = 0;1360 ReactDOM.unmountComponentAtNode(container);1361 expect(log).toEqual(['ErrorBoundary componentWillUnmount']);1362 });1363 it('picks the right boundary when handling unmounting errors', () => {1364 function renderInnerError(error) {1365 return <div>Caught an inner error: {error.message}.</div>;1366 }1367 function renderOuterError(error) {1368 return <div>Caught an outer error: {error.message}.</div>;1369 }1370 const container = document.createElement('div');1371 ReactDOM.render(1372 <ErrorBoundary1373 logName="OuterErrorBoundary"1374 renderError={renderOuterError}>1375 <ErrorBoundary1376 logName="InnerErrorBoundary"1377 renderError={renderInnerError}>1378 <BrokenComponentWillUnmount />1379 </ErrorBoundary>1380 </ErrorBoundary>,1381 container,1382 );1383 log.length = 0;1384 ReactDOM.render(1385 <ErrorBoundary1386 logName="OuterErrorBoundary"1387 renderError={renderOuterError}>1388 <ErrorBoundary1389 logName="InnerErrorBoundary"1390 renderError={renderInnerError}1391 />1392 </ErrorBoundary>,1393 container,1394 );1395 expect(container.textContent).toBe('Caught an inner error: Hello.');1396 expect(log).toEqual([1397 // Update outer boundary1398 'OuterErrorBoundary componentWillReceiveProps',1399 'OuterErrorBoundary componentWillUpdate',1400 'OuterErrorBoundary render success',1401 // Update inner boundary1402 'InnerErrorBoundary componentWillReceiveProps',1403 'InnerErrorBoundary componentWillUpdate',1404 'InnerErrorBoundary render success',1405 // Try unmounting child1406 'BrokenComponentWillUnmount componentWillUnmount [!]',1407 // Fiber proceeds with lifecycles despite errors1408 // Inner and outer boundaries have updated in this phase1409 'InnerErrorBoundary componentDidUpdate',1410 'OuterErrorBoundary componentDidUpdate',1411 // Now that commit phase is done, Fiber handles errors1412 // Only inner boundary receives the error:1413 'InnerErrorBoundary componentDidCatch',1414 'InnerErrorBoundary componentWillUpdate',1415 // Render an error now1416 'InnerErrorBoundary render error',1417 // In Fiber, this was a local update to the1418 // inner boundary so only its hook fires1419 'InnerErrorBoundary componentDidUpdate',1420 ]);1421 log.length = 0;1422 ReactDOM.unmountComponentAtNode(container);1423 expect(log).toEqual([1424 'OuterErrorBoundary componentWillUnmount',1425 'InnerErrorBoundary componentWillUnmount',1426 ]);1427 });1428 it('can recover from error state', () => {1429 const container = document.createElement('div');1430 ReactDOM.render(1431 <ErrorBoundary>1432 <BrokenRender />1433 </ErrorBoundary>,1434 container,1435 );1436 ReactDOM.render(1437 <ErrorBoundary>1438 <Normal />1439 </ErrorBoundary>,1440 container,1441 );1442 // Error boundary doesn't retry by itself:1443 expect(container.textContent).toBe('Caught an error: Hello.');1444 // Force the success path:1445 log.length = 0;1446 ReactDOM.render(1447 <ErrorBoundary forceRetry={true}>1448 <Normal />1449 </ErrorBoundary>,1450 container,1451 );1452 expect(container.textContent).not.toContain('Caught an error');1453 expect(log).toEqual([1454 'ErrorBoundary componentWillReceiveProps',1455 'ErrorBoundary componentWillUpdate',1456 'ErrorBoundary render success',1457 // Mount children:1458 'Normal constructor',1459 'Normal componentWillMount',1460 'Normal render',1461 // Finalize updates:1462 'Normal componentDidMount',1463 'ErrorBoundary componentDidUpdate',1464 ]);1465 log.length = 0;1466 ReactDOM.unmountComponentAtNode(container);1467 expect(log).toEqual([1468 'ErrorBoundary componentWillUnmount',1469 'Normal componentWillUnmount',1470 ]);1471 });1472 it('can update multiple times in error state', () => {1473 const container = document.createElement('div');1474 ReactDOM.render(1475 <ErrorBoundary>1476 <BrokenRender />1477 </ErrorBoundary>,1478 container,1479 );1480 expect(container.textContent).toBe('Caught an error: Hello.');1481 ReactDOM.render(1482 <ErrorBoundary>1483 <BrokenRender />1484 </ErrorBoundary>,1485 container,1486 );1487 expect(container.textContent).toBe('Caught an error: Hello.');1488 ReactDOM.render(<div>Other screen</div>, container);1489 expect(container.textContent).toBe('Other screen');1490 ReactDOM.unmountComponentAtNode(container);1491 });1492 it("doesn't get into inconsistent state during removals", () => {1493 const container = document.createElement('div');1494 ReactDOM.render(1495 <ErrorBoundary>1496 <Normal />1497 <BrokenComponentWillUnmount />1498 <Normal />1499 </ErrorBoundary>,1500 container,1501 );1502 ReactDOM.render(<ErrorBoundary />, container);1503 expect(container.textContent).toBe('Caught an error: Hello.');1504 log.length = 0;1505 ReactDOM.unmountComponentAtNode(container);1506 expect(log).toEqual(['ErrorBoundary componentWillUnmount']);1507 });1508 it("doesn't get into inconsistent state during additions", () => {1509 const container = document.createElement('div');1510 ReactDOM.render(<ErrorBoundary />, container);1511 ReactDOM.render(1512 <ErrorBoundary>1513 <Normal />1514 <BrokenRender />1515 <Normal />1516 </ErrorBoundary>,1517 container,1518 );1519 expect(container.textContent).toBe('Caught an error: Hello.');1520 log.length = 0;1521 ReactDOM.unmountComponentAtNode(container);1522 expect(log).toEqual(['ErrorBoundary componentWillUnmount']);1523 });1524 it("doesn't get into inconsistent state during reorders", () => {1525 function getAMixOfNormalAndBrokenRenderElements() {1526 const elements = [];1527 for (let i = 0; i < 100; i++) {1528 elements.push(<Normal key={i} />);1529 }1530 elements.push(<MaybeBrokenRender key={100} />);1531 let currentIndex = elements.length;1532 while (0 !== currentIndex) {1533 const randomIndex = Math.floor(Math.random() * currentIndex);1534 currentIndex -= 1;1535 const temporaryValue = elements[currentIndex];1536 elements[currentIndex] = elements[randomIndex];1537 elements[randomIndex] = temporaryValue;1538 }1539 return elements;1540 }1541 class MaybeBrokenRender extends React.Component {1542 render() {1543 if (fail) {1544 throw new Error('Hello');1545 }1546 return <div>{this.props.children}</div>;1547 }1548 }1549 let fail = false;1550 const container = document.createElement('div');1551 ReactDOM.render(1552 <ErrorBoundary>{getAMixOfNormalAndBrokenRenderElements()}</ErrorBoundary>,1553 container,1554 );1555 expect(container.textContent).not.toContain('Caught an error');1556 fail = true;1557 ReactDOM.render(1558 <ErrorBoundary>{getAMixOfNormalAndBrokenRenderElements()}</ErrorBoundary>,1559 container,1560 );1561 expect(container.textContent).toBe('Caught an error: Hello.');1562 log.length = 0;1563 ReactDOM.unmountComponentAtNode(container);1564 expect(log).toEqual(['ErrorBoundary componentWillUnmount']);1565 });1566 it('catches errors originating downstream', () => {1567 let fail = false;1568 class Stateful extends React.Component {1569 state = {shouldThrow: false};1570 render() {1571 if (fail) {1572 log.push('Stateful render [!]');1573 throw new Error('Hello');1574 }1575 return <div>{this.props.children}</div>;1576 }1577 }1578 let statefulInst;1579 const container = document.createElement('div');1580 ReactDOM.render(1581 <ErrorBoundary>1582 <Stateful ref={inst => (statefulInst = inst)} />1583 </ErrorBoundary>,1584 container,1585 );1586 log.length = 0;1587 expect(() => {1588 fail = true;1589 statefulInst.forceUpdate();1590 }).not.toThrow();1591 expect(log).toEqual([1592 'Stateful render [!]',1593 'ErrorBoundary componentDidCatch',1594 'ErrorBoundary componentWillUpdate',1595 'ErrorBoundary render error',1596 'ErrorBoundary componentDidUpdate',1597 ]);1598 log.length = 0;1599 ReactDOM.unmountComponentAtNode(container);1600 expect(log).toEqual(['ErrorBoundary componentWillUnmount']);1601 });1602 it('catches errors in componentDidMount', () => {1603 const container = document.createElement('div');1604 ReactDOM.render(1605 <ErrorBoundary>1606 <BrokenComponentWillUnmount>1607 <Normal />1608 </BrokenComponentWillUnmount>1609 <BrokenComponentDidMount />1610 <Normal logName="LastChild" />1611 </ErrorBoundary>,1612 container,1613 );1614 expect(log).toEqual([1615 'ErrorBoundary constructor',1616 'ErrorBoundary componentWillMount',1617 'ErrorBoundary render success',1618 'BrokenComponentWillUnmount constructor',1619 'BrokenComponentWillUnmount componentWillMount',1620 'BrokenComponentWillUnmount render',1621 'Normal constructor',1622 'Normal componentWillMount',1623 'Normal render',1624 'BrokenComponentDidMount constructor',1625 'BrokenComponentDidMount componentWillMount',1626 'BrokenComponentDidMount render',1627 'LastChild constructor',1628 'LastChild componentWillMount',1629 'LastChild render',1630 // Start flushing didMount queue1631 'Normal componentDidMount',1632 'BrokenComponentWillUnmount componentDidMount',1633 'BrokenComponentDidMount componentDidMount [!]',1634 // Continue despite the error1635 'LastChild componentDidMount',1636 'ErrorBoundary componentDidMount',1637 // Now we are ready to handle the error1638 // Safely unmount every child1639 'BrokenComponentWillUnmount componentWillUnmount [!]',1640 // Continue unmounting safely despite any errors1641 'Normal componentWillUnmount',1642 'BrokenComponentDidMount componentWillUnmount',1643 'LastChild componentWillUnmount',1644 // Handle the error1645 'ErrorBoundary componentDidCatch',1646 'ErrorBoundary componentWillUpdate',1647 'ErrorBoundary render error',1648 // The update has finished1649 'ErrorBoundary componentDidUpdate',1650 ]);1651 log.length = 0;1652 ReactDOM.unmountComponentAtNode(container);1653 expect(log).toEqual(['ErrorBoundary componentWillUnmount']);1654 });1655 it('catches errors in componentDidUpdate', () => {1656 const container = document.createElement('div');1657 ReactDOM.render(1658 <ErrorBoundary>1659 <BrokenComponentDidUpdate />1660 </ErrorBoundary>,1661 container,1662 );1663 log.length = 0;1664 ReactDOM.render(1665 <ErrorBoundary>1666 <BrokenComponentDidUpdate />1667 </ErrorBoundary>,1668 container,1669 );1670 expect(log).toEqual([1671 'ErrorBoundary componentWillReceiveProps',1672 'ErrorBoundary componentWillUpdate',1673 'ErrorBoundary render success',1674 'BrokenComponentDidUpdate componentWillReceiveProps',1675 'BrokenComponentDidUpdate componentWillUpdate',1676 'BrokenComponentDidUpdate render',1677 // All lifecycles run1678 'BrokenComponentDidUpdate componentDidUpdate [!]',1679 'ErrorBoundary componentDidUpdate',1680 'BrokenComponentDidUpdate componentWillUnmount',1681 // Then, error is handled1682 'ErrorBoundary componentDidCatch',1683 'ErrorBoundary componentWillUpdate',1684 'ErrorBoundary render error',1685 'ErrorBoundary componentDidUpdate',1686 ]);1687 log.length = 0;1688 ReactDOM.unmountComponentAtNode(container);1689 expect(log).toEqual(['ErrorBoundary componentWillUnmount']);1690 });1691 it('propagates errors inside boundary during componentDidMount', () => {1692 const container = document.createElement('div');1693 ReactDOM.render(1694 <ErrorBoundary>1695 <BrokenComponentDidMountErrorBoundary1696 renderError={error => (1697 <div>We should never catch our own error: {error.message}.</div>1698 )}1699 />1700 </ErrorBoundary>,1701 container,1702 );1703 expect(container.firstChild.textContent).toBe('Caught an error: Hello.');1704 expect(log).toEqual([1705 'ErrorBoundary constructor',1706 'ErrorBoundary componentWillMount',1707 'ErrorBoundary render success',1708 'BrokenComponentDidMountErrorBoundary constructor',1709 'BrokenComponentDidMountErrorBoundary componentWillMount',1710 'BrokenComponentDidMountErrorBoundary render success',1711 'BrokenComponentDidMountErrorBoundary componentDidMount [!]',1712 // Fiber proceeds with the hooks1713 'ErrorBoundary componentDidMount',1714 'BrokenComponentDidMountErrorBoundary componentWillUnmount',1715 // The error propagates to the higher boundary1716 'ErrorBoundary componentDidCatch',1717 // Fiber retries from the root1718 'ErrorBoundary componentWillUpdate',1719 'ErrorBoundary render error',1720 'ErrorBoundary componentDidUpdate',1721 ]);1722 log.length = 0;1723 ReactDOM.unmountComponentAtNode(container);1724 expect(log).toEqual(['ErrorBoundary componentWillUnmount']);1725 });1726 it('lets different boundaries catch their own first errors', () => {1727 function renderUnmountError(error) {1728 return <div>Caught an unmounting error: {error.message}.</div>;1729 }1730 function renderUpdateError(error) {1731 return <div>Caught an updating error: {error.message}.</div>;1732 }1733 const container = document.createElement('div');1734 ReactDOM.render(1735 <ErrorBoundary logName="OuterErrorBoundary">1736 <ErrorBoundary1737 logName="InnerUnmountBoundary"1738 renderError={renderUnmountError}>1739 <BrokenComponentWillUnmount errorText="E1" />1740 <BrokenComponentWillUnmount errorText="E2" />1741 </ErrorBoundary>1742 <ErrorBoundary1743 logName="InnerUpdateBoundary"1744 renderError={renderUpdateError}>1745 <BrokenComponentDidUpdate errorText="E3" />1746 <BrokenComponentDidUpdate errorText="E4" />1747 </ErrorBoundary>1748 </ErrorBoundary>,1749 container,1750 );1751 log.length = 0;1752 ReactDOM.render(1753 <ErrorBoundary logName="OuterErrorBoundary">1754 <ErrorBoundary1755 logName="InnerUnmountBoundary"1756 renderError={renderUnmountError}1757 />1758 <ErrorBoundary1759 logName="InnerUpdateBoundary"1760 renderError={renderUpdateError}>1761 <BrokenComponentDidUpdate errorText="E3" />1762 <BrokenComponentDidUpdate errorText="E4" />1763 </ErrorBoundary>1764 </ErrorBoundary>,1765 container,1766 );1767 expect(container.firstChild.textContent).toBe(1768 'Caught an unmounting error: E1.' + 'Caught an updating error: E3.',1769 );1770 expect(log).toEqual([1771 // Begin update phase1772 'OuterErrorBoundary componentWillReceiveProps',1773 'OuterErrorBoundary componentWillUpdate',1774 'OuterErrorBoundary render success',1775 'InnerUnmountBoundary componentWillReceiveProps',1776 'InnerUnmountBoundary componentWillUpdate',1777 'InnerUnmountBoundary render success',1778 'InnerUpdateBoundary componentWillReceiveProps',1779 'InnerUpdateBoundary componentWillUpdate',1780 'InnerUpdateBoundary render success',1781 // First come the updates1782 'BrokenComponentDidUpdate componentWillReceiveProps',1783 'BrokenComponentDidUpdate componentWillUpdate',1784 'BrokenComponentDidUpdate render',1785 'BrokenComponentDidUpdate componentWillReceiveProps',1786 'BrokenComponentDidUpdate componentWillUpdate',1787 'BrokenComponentDidUpdate render',1788 // We're in commit phase now, deleting1789 'BrokenComponentWillUnmount componentWillUnmount [!]',1790 'BrokenComponentWillUnmount componentWillUnmount [!]',1791 // Continue despite errors, handle them after commit is done1792 'InnerUnmountBoundary componentDidUpdate',1793 // We're still in commit phase, now calling update lifecycles1794 'BrokenComponentDidUpdate componentDidUpdate [!]',1795 // Again, continue despite errors, we'll handle them later1796 'BrokenComponentDidUpdate componentDidUpdate [!]',1797 'InnerUpdateBoundary componentDidUpdate',1798 'OuterErrorBoundary componentDidUpdate',1799 // After the commit phase, attempt to recover from any errors that1800 // were captured1801 'BrokenComponentDidUpdate componentWillUnmount',1802 'BrokenComponentDidUpdate componentWillUnmount',1803 'InnerUnmountBoundary componentDidCatch',1804 'InnerUpdateBoundary componentDidCatch',1805 'InnerUnmountBoundary componentWillUpdate',1806 'InnerUnmountBoundary render error',1807 'InnerUpdateBoundary componentWillUpdate',1808 'InnerUpdateBoundary render error',1809 'InnerUnmountBoundary componentDidUpdate',1810 'InnerUpdateBoundary componentDidUpdate',1811 ]);1812 log.length = 0;1813 ReactDOM.unmountComponentAtNode(container);1814 expect(log).toEqual([1815 'OuterErrorBoundary componentWillUnmount',1816 'InnerUnmountBoundary componentWillUnmount',1817 'InnerUpdateBoundary componentWillUnmount',1818 ]);1819 });1820 it('discards a bad root if the root component fails', () => {1821 const X = null;1822 const Y = undefined;1823 let err1;1824 let err2;1825 try {1826 let container = document.createElement('div');1827 expect(() => ReactDOM.render(<X />, container)).toWarnDev(1828 'React.createElement: type is invalid -- expected a string ' +1829 '(for built-in components) or a class/function ' +1830 '(for composite components) but got: null.',1831 );1832 } catch (err) {1833 err1 = err;1834 }1835 try {1836 let container = document.createElement('div');1837 expect(() => ReactDOM.render(<Y />, container)).toWarnDev(1838 'React.createElement: type is invalid -- expected a string ' +1839 '(for built-in components) or a class/function ' +1840 '(for composite components) but got: undefined.',1841 );1842 } catch (err) {1843 err2 = err;1844 }1845 expect(err1.message).toMatch(/got: null/);1846 expect(err2.message).toMatch(/got: undefined/);1847 });1848 it('renders empty output if error boundary does not handle the error', () => {1849 const container = document.createElement('div');1850 ReactDOM.render(1851 <div>1852 Sibling1853 <NoopErrorBoundary>1854 <BrokenRender />1855 </NoopErrorBoundary>1856 </div>,1857 container,1858 );1859 expect(container.firstChild.textContent).toBe('Sibling');1860 expect(log).toEqual([1861 'NoopErrorBoundary constructor',1862 'NoopErrorBoundary componentWillMount',1863 'NoopErrorBoundary render',1864 'BrokenRender constructor',1865 'BrokenRender componentWillMount',1866 'BrokenRender render [!]',1867 // In Fiber, noop error boundaries render null1868 'NoopErrorBoundary componentDidMount',1869 'NoopErrorBoundary componentDidCatch',1870 // Nothing happens.1871 ]);1872 log.length = 0;1873 ReactDOM.unmountComponentAtNode(container);1874 expect(log).toEqual(['NoopErrorBoundary componentWillUnmount']);1875 });1876 it('passes first error when two errors happen in commit', () => {1877 const errors = [];1878 let caughtError;1879 class Parent extends React.Component {1880 render() {1881 return <Child />;1882 }1883 componentDidMount() {1884 errors.push('parent sad');1885 throw new Error('parent sad');1886 }1887 }1888 class Child extends React.Component {1889 render() {1890 return <div />;1891 }1892 componentDidMount() {1893 errors.push('child sad');1894 throw new Error('child sad');1895 }1896 }1897 const container = document.createElement('div');1898 try {1899 // Here, we test the behavior where there is no error boundary and we1900 // delegate to the host root.1901 ReactDOM.render(<Parent />, container);1902 } catch (e) {1903 if (e.message !== 'parent sad' && e.message !== 'child sad') {1904 throw e;1905 }1906 caughtError = e;1907 }1908 expect(errors).toEqual(['child sad', 'parent sad']);1909 // Error should be the first thrown1910 expect(caughtError.message).toBe('child sad');1911 });1912 it('propagates uncaught error inside unbatched initial mount', () => {1913 function Foo() {1914 throw new Error('foo error');1915 }1916 const container = document.createElement('div');1917 expect(() => {1918 ReactDOM.unstable_batchedUpdates(() => {1919 ReactDOM.render(<Foo />, container);1920 });1921 }).toThrow('foo error');1922 });...

Full Screen

Full Screen

StrictEffectsModeDefaults-test.internal.js

Source:StrictEffectsModeDefaults-test.internal.js Github

copy

Full Screen

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 React;11let ReactNoop;12let Scheduler;13describe('StrictEffectsMode defaults', () => {14 beforeEach(() => {15 jest.resetModules();16 React = require('react');17 ReactNoop = require('react-noop-renderer');18 Scheduler = require('scheduler');19 const ReactFeatureFlags = require('shared/ReactFeatureFlags');20 ReactFeatureFlags.enableStrictEffects = __DEV__;21 ReactFeatureFlags.createRootStrictEffectsByDefault = __DEV__;22 });23 it('should not double invoke effects in legacy mode', () => {24 function App({text}) {25 React.useEffect(() => {26 Scheduler.unstable_yieldValue('useEffect mount');27 return () => Scheduler.unstable_yieldValue('useEffect unmount');28 });29 React.useLayoutEffect(() => {30 Scheduler.unstable_yieldValue('useLayoutEffect mount');31 return () => Scheduler.unstable_yieldValue('useLayoutEffect unmount');32 });33 return text;34 }35 ReactNoop.act(() => {36 ReactNoop.renderLegacySyncRoot(<App text={'mount'} />);37 });38 expect(Scheduler).toHaveYielded([39 'useLayoutEffect mount',40 'useEffect mount',41 ]);42 });43 it('should not double invoke class lifecycles in legacy mode', () => {44 class App extends React.PureComponent {45 componentDidMount() {46 Scheduler.unstable_yieldValue('componentDidMount');47 }48 componentDidUpdate() {49 Scheduler.unstable_yieldValue('componentDidUpdate');50 }51 componentWillUnmount() {52 Scheduler.unstable_yieldValue('componentWillUnmount');53 }54 render() {55 return this.props.text;56 }57 }58 ReactNoop.act(() => {59 ReactNoop.renderLegacySyncRoot(<App text={'mount'} />);60 });61 expect(Scheduler).toHaveYielded(['componentDidMount']);62 });63 if (__DEV__) {64 it('should flush double-invoked effects within the same frame as layout effects if there are no passive effects', () => {65 function ComponentWithEffects({label}) {66 React.useLayoutEffect(() => {67 Scheduler.unstable_yieldValue(`useLayoutEffect mount "${label}"`);68 return () =>69 Scheduler.unstable_yieldValue(`useLayoutEffect unmount "${label}"`);70 });71 return label;72 }73 ReactNoop.act(() => {74 ReactNoop.render(75 <>76 <ComponentWithEffects label={'one'} />77 </>,78 );79 expect(Scheduler).toFlushUntilNextPaint([80 'useLayoutEffect mount "one"',81 'useLayoutEffect unmount "one"',82 'useLayoutEffect mount "one"',83 ]);84 });85 ReactNoop.act(() => {86 ReactNoop.render(87 <>88 <ComponentWithEffects label={'one'} />89 <ComponentWithEffects label={'two'} />90 </>,91 );92 expect(Scheduler).toFlushUntilNextPaint([93 // Cleanup and re-run "one" (and "two") since there is no dependencies array.94 'useLayoutEffect unmount "one"',95 'useLayoutEffect mount "one"',96 'useLayoutEffect mount "two"',97 // Since "two" is new, it should be double-invoked.98 'useLayoutEffect unmount "two"',99 'useLayoutEffect mount "two"',100 ]);101 });102 });103 // This test also verifies that double-invoked effects flush synchronously104 // within the same frame as passive effects.105 it('should double invoke effects only for newly mounted components', () => {106 function ComponentWithEffects({label}) {107 React.useEffect(() => {108 Scheduler.unstable_yieldValue(`useEffect mount "${label}"`);109 return () =>110 Scheduler.unstable_yieldValue(`useEffect unmount "${label}"`);111 });112 React.useLayoutEffect(() => {113 Scheduler.unstable_yieldValue(`useLayoutEffect mount "${label}"`);114 return () =>115 Scheduler.unstable_yieldValue(`useLayoutEffect unmount "${label}"`);116 });117 return label;118 }119 ReactNoop.act(() => {120 ReactNoop.render(121 <>122 <ComponentWithEffects label={'one'} />123 </>,124 );125 expect(Scheduler).toFlushAndYieldThrough([126 'useLayoutEffect mount "one"',127 ]);128 expect(Scheduler).toFlushAndYield([129 'useEffect mount "one"',130 'useLayoutEffect unmount "one"',131 'useEffect unmount "one"',132 'useLayoutEffect mount "one"',133 'useEffect mount "one"',134 ]);135 });136 ReactNoop.act(() => {137 ReactNoop.render(138 <>139 <ComponentWithEffects label={'one'} />140 <ComponentWithEffects label={'two'} />141 </>,142 );143 expect(Scheduler).toFlushAndYieldThrough([144 // Cleanup and re-run "one" (and "two") since there is no dependencies array.145 'useLayoutEffect unmount "one"',146 'useLayoutEffect mount "one"',147 'useLayoutEffect mount "two"',148 ]);149 expect(Scheduler).toFlushAndYield([150 'useEffect unmount "one"',151 'useEffect mount "one"',152 'useEffect mount "two"',153 // Since "two" is new, it should be double-invoked.154 'useLayoutEffect unmount "two"',155 'useEffect unmount "two"',156 'useLayoutEffect mount "two"',157 'useEffect mount "two"',158 ]);159 });160 });161 it('double invoking for effects for modern roots', () => {162 function App({text}) {163 React.useEffect(() => {164 Scheduler.unstable_yieldValue('useEffect mount');165 return () => Scheduler.unstable_yieldValue('useEffect unmount');166 });167 React.useLayoutEffect(() => {168 Scheduler.unstable_yieldValue('useLayoutEffect mount');169 return () => Scheduler.unstable_yieldValue('useLayoutEffect unmount');170 });171 return text;172 }173 ReactNoop.act(() => {174 ReactNoop.render(<App text={'mount'} />);175 });176 expect(Scheduler).toHaveYielded([177 'useLayoutEffect mount',178 'useEffect mount',179 'useLayoutEffect unmount',180 'useEffect unmount',181 'useLayoutEffect mount',182 'useEffect mount',183 ]);184 ReactNoop.act(() => {185 ReactNoop.render(<App text={'update'} />);186 });187 expect(Scheduler).toHaveYielded([188 'useLayoutEffect unmount',189 'useLayoutEffect mount',190 'useEffect unmount',191 'useEffect mount',192 ]);193 ReactNoop.act(() => {194 ReactNoop.render(null);195 });196 expect(Scheduler).toHaveYielded([197 'useLayoutEffect unmount',198 'useEffect unmount',199 ]);200 });201 it('multiple effects are double invoked in the right order (all mounted, all unmounted, all remounted)', () => {202 function App({text}) {203 React.useEffect(() => {204 Scheduler.unstable_yieldValue('useEffect One mount');205 return () => Scheduler.unstable_yieldValue('useEffect One unmount');206 });207 React.useEffect(() => {208 Scheduler.unstable_yieldValue('useEffect Two mount');209 return () => Scheduler.unstable_yieldValue('useEffect Two unmount');210 });211 return text;212 }213 ReactNoop.act(() => {214 ReactNoop.render(<App text={'mount'} />);215 });216 expect(Scheduler).toHaveYielded([217 'useEffect One mount',218 'useEffect Two mount',219 'useEffect One unmount',220 'useEffect Two unmount',221 'useEffect One mount',222 'useEffect Two mount',223 ]);224 ReactNoop.act(() => {225 ReactNoop.render(<App text={'update'} />);226 });227 expect(Scheduler).toHaveYielded([228 'useEffect One unmount',229 'useEffect Two unmount',230 'useEffect One mount',231 'useEffect Two mount',232 ]);233 ReactNoop.act(() => {234 ReactNoop.render(null);235 });236 expect(Scheduler).toHaveYielded([237 'useEffect One unmount',238 'useEffect Two unmount',239 ]);240 });241 it('multiple layout effects are double invoked in the right order (all mounted, all unmounted, all remounted)', () => {242 function App({text}) {243 React.useLayoutEffect(() => {244 Scheduler.unstable_yieldValue('useLayoutEffect One mount');245 return () =>246 Scheduler.unstable_yieldValue('useLayoutEffect One unmount');247 });248 React.useLayoutEffect(() => {249 Scheduler.unstable_yieldValue('useLayoutEffect Two mount');250 return () =>251 Scheduler.unstable_yieldValue('useLayoutEffect Two unmount');252 });253 return text;254 }255 ReactNoop.act(() => {256 ReactNoop.render(<App text={'mount'} />);257 });258 expect(Scheduler).toHaveYielded([259 'useLayoutEffect One mount',260 'useLayoutEffect Two mount',261 'useLayoutEffect One unmount',262 'useLayoutEffect Two unmount',263 'useLayoutEffect One mount',264 'useLayoutEffect Two mount',265 ]);266 ReactNoop.act(() => {267 ReactNoop.render(<App text={'update'} />);268 });269 expect(Scheduler).toHaveYielded([270 'useLayoutEffect One unmount',271 'useLayoutEffect Two unmount',272 'useLayoutEffect One mount',273 'useLayoutEffect Two mount',274 ]);275 ReactNoop.act(() => {276 ReactNoop.render(null);277 });278 expect(Scheduler).toHaveYielded([279 'useLayoutEffect One unmount',280 'useLayoutEffect Two unmount',281 ]);282 });283 it('useEffect and useLayoutEffect is called twice when there is no unmount', () => {284 function App({text}) {285 React.useEffect(() => {286 Scheduler.unstable_yieldValue('useEffect mount');287 });288 React.useLayoutEffect(() => {289 Scheduler.unstable_yieldValue('useLayoutEffect mount');290 });291 return text;292 }293 ReactNoop.act(() => {294 ReactNoop.render(<App text={'mount'} />);295 });296 expect(Scheduler).toHaveYielded([297 'useLayoutEffect mount',298 'useEffect mount',299 'useLayoutEffect mount',300 'useEffect mount',301 ]);302 ReactNoop.act(() => {303 ReactNoop.render(<App text={'update'} />);304 });305 expect(Scheduler).toHaveYielded([306 'useLayoutEffect mount',307 'useEffect mount',308 ]);309 ReactNoop.act(() => {310 ReactNoop.render(null);311 });312 expect(Scheduler).toHaveYielded([]);313 });314 it('passes the right context to class component lifecycles', () => {315 class App extends React.PureComponent {316 test() {}317 componentDidMount() {318 this.test();319 Scheduler.unstable_yieldValue('componentDidMount');320 }321 componentDidUpdate() {322 this.test();323 Scheduler.unstable_yieldValue('componentDidUpdate');324 }325 componentWillUnmount() {326 this.test();327 Scheduler.unstable_yieldValue('componentWillUnmount');328 }329 render() {330 return null;331 }332 }333 ReactNoop.act(() => {334 ReactNoop.render(<App />);335 });336 expect(Scheduler).toHaveYielded([337 'componentDidMount',338 'componentWillUnmount',339 'componentDidMount',340 ]);341 });342 it('double invoking works for class components', () => {343 class App extends React.PureComponent {344 componentDidMount() {345 Scheduler.unstable_yieldValue('componentDidMount');346 }347 componentDidUpdate() {348 Scheduler.unstable_yieldValue('componentDidUpdate');349 }350 componentWillUnmount() {351 Scheduler.unstable_yieldValue('componentWillUnmount');352 }353 render() {354 return this.props.text;355 }356 }357 ReactNoop.act(() => {358 ReactNoop.render(<App text={'mount'} />);359 });360 expect(Scheduler).toHaveYielded([361 'componentDidMount',362 'componentWillUnmount',363 'componentDidMount',364 ]);365 ReactNoop.act(() => {366 ReactNoop.render(<App text={'update'} />);367 });368 expect(Scheduler).toHaveYielded(['componentDidUpdate']);369 ReactNoop.act(() => {370 ReactNoop.render(null);371 });372 expect(Scheduler).toHaveYielded(['componentWillUnmount']);373 });374 it('double flushing passive effects only results in one double invoke', () => {375 function App({text}) {376 const [state, setState] = React.useState(0);377 React.useEffect(() => {378 if (state !== 1) {379 setState(1);380 }381 Scheduler.unstable_yieldValue('useEffect mount');382 return () => Scheduler.unstable_yieldValue('useEffect unmount');383 });384 React.useLayoutEffect(() => {385 Scheduler.unstable_yieldValue('useLayoutEffect mount');386 return () => Scheduler.unstable_yieldValue('useLayoutEffect unmount');387 });388 Scheduler.unstable_yieldValue(text);389 return text;390 }391 ReactNoop.act(() => {392 ReactNoop.render(<App text={'mount'} />);393 });394 expect(Scheduler).toHaveYielded([395 'mount',396 'useLayoutEffect mount',397 'useEffect mount',398 'useLayoutEffect unmount',399 'useEffect unmount',400 'useLayoutEffect mount',401 'useEffect mount',402 'mount',403 'useLayoutEffect unmount',404 'useLayoutEffect mount',405 'useEffect unmount',406 'useEffect mount',407 ]);408 });409 it('newly mounted components after initial mount get double invoked', () => {410 let _setShowChild;411 function Child() {412 React.useEffect(() => {413 Scheduler.unstable_yieldValue('Child useEffect mount');414 return () => Scheduler.unstable_yieldValue('Child useEffect unmount');415 });416 React.useLayoutEffect(() => {417 Scheduler.unstable_yieldValue('Child useLayoutEffect mount');418 return () =>419 Scheduler.unstable_yieldValue('Child useLayoutEffect unmount');420 });421 return null;422 }423 function App() {424 const [showChild, setShowChild] = React.useState(false);425 _setShowChild = setShowChild;426 React.useEffect(() => {427 Scheduler.unstable_yieldValue('App useEffect mount');428 return () => Scheduler.unstable_yieldValue('App useEffect unmount');429 });430 React.useLayoutEffect(() => {431 Scheduler.unstable_yieldValue('App useLayoutEffect mount');432 return () =>433 Scheduler.unstable_yieldValue('App useLayoutEffect unmount');434 });435 return showChild && <Child />;436 }437 ReactNoop.act(() => {438 ReactNoop.render(<App />);439 });440 expect(Scheduler).toHaveYielded([441 'App useLayoutEffect mount',442 'App useEffect mount',443 'App useLayoutEffect unmount',444 'App useEffect unmount',445 'App useLayoutEffect mount',446 'App useEffect mount',447 ]);448 ReactNoop.act(() => {449 _setShowChild(true);450 });451 expect(Scheduler).toHaveYielded([452 'App useLayoutEffect unmount',453 'Child useLayoutEffect mount',454 'App useLayoutEffect mount',455 'App useEffect unmount',456 'Child useEffect mount',457 'App useEffect mount',458 'Child useLayoutEffect unmount',459 'Child useEffect unmount',460 'Child useLayoutEffect mount',461 'Child useEffect mount',462 ]);463 });464 it('classes and functions are double invoked together correctly', () => {465 class ClassChild extends React.PureComponent {466 componentDidMount() {467 Scheduler.unstable_yieldValue('componentDidMount');468 }469 componentWillUnmount() {470 Scheduler.unstable_yieldValue('componentWillUnmount');471 }472 render() {473 return this.props.text;474 }475 }476 function FunctionChild({text}) {477 React.useEffect(() => {478 Scheduler.unstable_yieldValue('useEffect mount');479 return () => Scheduler.unstable_yieldValue('useEffect unmount');480 });481 React.useLayoutEffect(() => {482 Scheduler.unstable_yieldValue('useLayoutEffect mount');483 return () => Scheduler.unstable_yieldValue('useLayoutEffect unmount');484 });485 return text;486 }487 function App({text}) {488 return (489 <>490 <ClassChild text={text} />491 <FunctionChild text={text} />492 </>493 );494 }495 ReactNoop.act(() => {496 ReactNoop.render(<App text={'mount'} />);497 });498 expect(Scheduler).toHaveYielded([499 'componentDidMount',500 'useLayoutEffect mount',501 'useEffect mount',502 'componentWillUnmount',503 'useLayoutEffect unmount',504 'useEffect unmount',505 'componentDidMount',506 'useLayoutEffect mount',507 'useEffect mount',508 ]);509 ReactNoop.act(() => {510 ReactNoop.render(<App text={'mount'} />);511 });512 expect(Scheduler).toHaveYielded([513 'useLayoutEffect unmount',514 'useLayoutEffect mount',515 'useEffect unmount',516 'useEffect mount',517 ]);518 ReactNoop.act(() => {519 ReactNoop.render(null);520 });521 expect(Scheduler).toHaveYielded([522 'componentWillUnmount',523 'useLayoutEffect unmount',524 'useEffect unmount',525 ]);526 });527 }...

Full Screen

Full Screen

StrictEffectsMode-test.js

Source:StrictEffectsMode-test.js Github

copy

Full Screen

...90 'useEffect unmount',91 'useEffect mount',92 ]);93 act(() => {94 renderer.unmount();95 });96 expect(Scheduler).toHaveYielded([97 'useLayoutEffect unmount',98 'useEffect unmount',99 ]);100 });101 it('multiple effects are double invoked in the right order (all mounted, all unmounted, all remounted)', () => {102 function App({text}) {103 React.useEffect(() => {104 Scheduler.unstable_yieldValue('useEffect One mount');105 return () => Scheduler.unstable_yieldValue('useEffect One unmount');106 });107 React.useEffect(() => {108 Scheduler.unstable_yieldValue('useEffect Two mount');109 return () => Scheduler.unstable_yieldValue('useEffect Two unmount');110 });111 return text;112 }113 let renderer;114 act(() => {115 renderer = ReactTestRenderer.create(<App text={'mount'} />, {116 unstable_isConcurrent: true,117 });118 });119 if (supportsDoubleInvokeEffects()) {120 expect(Scheduler).toHaveYielded([121 'useEffect One mount',122 'useEffect Two mount',123 'useEffect One unmount',124 'useEffect Two unmount',125 'useEffect One mount',126 'useEffect Two mount',127 ]);128 } else {129 expect(Scheduler).toHaveYielded([130 'useEffect One mount',131 'useEffect Two mount',132 ]);133 }134 act(() => {135 renderer.update(<App text={'update'} />);136 });137 expect(Scheduler).toHaveYielded([138 'useEffect One unmount',139 'useEffect Two unmount',140 'useEffect One mount',141 'useEffect Two mount',142 ]);143 act(() => {144 renderer.unmount(null);145 });146 expect(Scheduler).toHaveYielded([147 'useEffect One unmount',148 'useEffect Two unmount',149 ]);150 });151 it('multiple layout effects are double invoked in the right order (all mounted, all unmounted, all remounted)', () => {152 function App({text}) {153 React.useLayoutEffect(() => {154 Scheduler.unstable_yieldValue('useLayoutEffect One mount');155 return () =>156 Scheduler.unstable_yieldValue('useLayoutEffect One unmount');157 });158 React.useLayoutEffect(() => {159 Scheduler.unstable_yieldValue('useLayoutEffect Two mount');160 return () =>161 Scheduler.unstable_yieldValue('useLayoutEffect Two unmount');162 });163 return text;164 }165 let renderer;166 act(() => {167 renderer = ReactTestRenderer.create(<App text={'mount'} />, {168 unstable_isConcurrent: true,169 });170 });171 if (supportsDoubleInvokeEffects()) {172 expect(Scheduler).toHaveYielded([173 'useLayoutEffect One mount',174 'useLayoutEffect Two mount',175 'useLayoutEffect One unmount',176 'useLayoutEffect Two unmount',177 'useLayoutEffect One mount',178 'useLayoutEffect Two mount',179 ]);180 } else {181 expect(Scheduler).toHaveYielded([182 'useLayoutEffect One mount',183 'useLayoutEffect Two mount',184 ]);185 }186 act(() => {187 renderer.update(<App text={'update'} />);188 });189 expect(Scheduler).toHaveYielded([190 'useLayoutEffect One unmount',191 'useLayoutEffect Two unmount',192 'useLayoutEffect One mount',193 'useLayoutEffect Two mount',194 ]);195 act(() => {196 renderer.unmount();197 });198 expect(Scheduler).toHaveYielded([199 'useLayoutEffect One unmount',200 'useLayoutEffect Two unmount',201 ]);202 });203 it('useEffect and useLayoutEffect is called twice when there is no unmount', () => {204 function App({text}) {205 React.useEffect(() => {206 Scheduler.unstable_yieldValue('useEffect mount');207 });208 React.useLayoutEffect(() => {209 Scheduler.unstable_yieldValue('useLayoutEffect mount');210 });211 return text;212 }213 let renderer;214 act(() => {215 renderer = ReactTestRenderer.create(<App text={'mount'} />, {216 unstable_isConcurrent: true,217 });218 });219 if (supportsDoubleInvokeEffects()) {220 expect(Scheduler).toHaveYielded([221 'useLayoutEffect mount',222 'useEffect mount',223 'useLayoutEffect mount',224 'useEffect mount',225 ]);226 } else {227 expect(Scheduler).toHaveYielded([228 'useLayoutEffect mount',229 'useEffect mount',230 ]);231 }232 act(() => {233 renderer.update(<App text={'update'} />);234 });235 expect(Scheduler).toHaveYielded([236 'useLayoutEffect mount',237 'useEffect mount',238 ]);239 act(() => {240 renderer.unmount();241 });242 expect(Scheduler).toHaveYielded([]);243 });244 it('passes the right context to class component lifecycles', () => {245 class App extends React.PureComponent {246 test() {}247 componentDidMount() {248 this.test();249 Scheduler.unstable_yieldValue('componentDidMount');250 }251 componentDidUpdate() {252 this.test();253 Scheduler.unstable_yieldValue('componentDidUpdate');254 }255 componentWillUnmount() {256 this.test();257 Scheduler.unstable_yieldValue('componentWillUnmount');258 }259 render() {260 return null;261 }262 }263 act(() => {264 ReactTestRenderer.create(<App />, {unstable_isConcurrent: true});265 });266 if (supportsDoubleInvokeEffects()) {267 expect(Scheduler).toHaveYielded([268 'componentDidMount',269 'componentWillUnmount',270 'componentDidMount',271 ]);272 } else {273 expect(Scheduler).toHaveYielded(['componentDidMount']);274 }275 });276 it('double invoking works for class components', () => {277 class App extends React.PureComponent {278 componentDidMount() {279 Scheduler.unstable_yieldValue('componentDidMount');280 }281 componentDidUpdate() {282 Scheduler.unstable_yieldValue('componentDidUpdate');283 }284 componentWillUnmount() {285 Scheduler.unstable_yieldValue('componentWillUnmount');286 }287 render() {288 return this.props.text;289 }290 }291 let renderer;292 act(() => {293 renderer = ReactTestRenderer.create(<App text={'mount'} />, {294 unstable_isConcurrent: true,295 });296 });297 if (supportsDoubleInvokeEffects()) {298 expect(Scheduler).toHaveYielded([299 'componentDidMount',300 'componentWillUnmount',301 'componentDidMount',302 ]);303 } else {304 expect(Scheduler).toHaveYielded(['componentDidMount']);305 }306 act(() => {307 renderer.update(<App text={'update'} />);308 });309 expect(Scheduler).toHaveYielded(['componentDidUpdate']);310 act(() => {311 renderer.unmount();312 });313 expect(Scheduler).toHaveYielded(['componentWillUnmount']);314 });315 it('should not double invoke class lifecycles in legacy mode', () => {316 class App extends React.PureComponent {317 componentDidMount() {318 Scheduler.unstable_yieldValue('componentDidMount');319 }320 componentDidUpdate() {321 Scheduler.unstable_yieldValue('componentDidUpdate');322 }323 componentWillUnmount() {324 Scheduler.unstable_yieldValue('componentWillUnmount');325 }326 render() {327 return this.props.text;328 }329 }330 act(() => {331 ReactTestRenderer.create(<App text={'mount'} />);332 });333 expect(Scheduler).toHaveYielded(['componentDidMount']);334 });335 it('double flushing passive effects only results in one double invoke', () => {336 function App({text}) {337 const [state, setState] = React.useState(0);338 React.useEffect(() => {339 if (state !== 1) {340 setState(1);341 }342 Scheduler.unstable_yieldValue('useEffect mount');343 return () => Scheduler.unstable_yieldValue('useEffect unmount');344 });345 React.useLayoutEffect(() => {346 Scheduler.unstable_yieldValue('useLayoutEffect mount');347 return () => Scheduler.unstable_yieldValue('useLayoutEffect unmount');348 });349 Scheduler.unstable_yieldValue(text);350 return text;351 }352 act(() => {353 ReactTestRenderer.create(<App text={'mount'} />, {354 unstable_isConcurrent: true,355 });356 });357 if (supportsDoubleInvokeEffects()) {358 expect(Scheduler).toHaveYielded([359 'mount',360 'useLayoutEffect mount',361 'useEffect mount',362 'useLayoutEffect unmount',363 'useEffect unmount',364 'useLayoutEffect mount',365 'useEffect mount',366 'mount',367 'useLayoutEffect unmount',368 'useLayoutEffect mount',369 'useEffect unmount',370 'useEffect mount',371 ]);372 } else {373 expect(Scheduler).toHaveYielded([374 'mount',375 'useLayoutEffect mount',376 'useEffect mount',377 'mount',378 'useLayoutEffect unmount',379 'useLayoutEffect mount',380 'useEffect unmount',381 'useEffect mount',382 ]);383 }384 });385 it('newly mounted components after initial mount get double invoked', () => {386 let _setShowChild;387 function Child() {388 React.useEffect(() => {389 Scheduler.unstable_yieldValue('Child useEffect mount');390 return () => Scheduler.unstable_yieldValue('Child useEffect unmount');391 });392 React.useLayoutEffect(() => {393 Scheduler.unstable_yieldValue('Child useLayoutEffect mount');394 return () =>395 Scheduler.unstable_yieldValue('Child useLayoutEffect unmount');396 });397 return null;398 }399 function App() {400 const [showChild, setShowChild] = React.useState(false);401 _setShowChild = setShowChild;402 React.useEffect(() => {403 Scheduler.unstable_yieldValue('App useEffect mount');404 return () => Scheduler.unstable_yieldValue('App useEffect unmount');405 });406 React.useLayoutEffect(() => {407 Scheduler.unstable_yieldValue('App useLayoutEffect mount');408 return () =>409 Scheduler.unstable_yieldValue('App useLayoutEffect unmount');410 });411 return showChild && <Child />;412 }413 act(() => {414 ReactTestRenderer.create(<App />, {unstable_isConcurrent: true});415 });416 if (supportsDoubleInvokeEffects()) {417 expect(Scheduler).toHaveYielded([418 'App useLayoutEffect mount',419 'App useEffect mount',420 'App useLayoutEffect unmount',421 'App useEffect unmount',422 'App useLayoutEffect mount',423 'App useEffect mount',424 ]);425 } else {426 expect(Scheduler).toHaveYielded([427 'App useLayoutEffect mount',428 'App useEffect mount',429 ]);430 }431 act(() => {432 _setShowChild(true);433 });434 if (supportsDoubleInvokeEffects()) {435 expect(Scheduler).toHaveYielded([436 'App useLayoutEffect unmount',437 'Child useLayoutEffect mount',438 'App useLayoutEffect mount',439 'App useEffect unmount',440 'Child useEffect mount',441 'App useEffect mount',442 'Child useLayoutEffect unmount',443 'Child useEffect unmount',444 'Child useLayoutEffect mount',445 'Child useEffect mount',446 ]);447 } else {448 expect(Scheduler).toHaveYielded([449 'App useLayoutEffect unmount',450 'Child useLayoutEffect mount',451 'App useLayoutEffect mount',452 'App useEffect unmount',453 'Child useEffect mount',454 'App useEffect mount',455 ]);456 }457 });458 it('classes and functions are double invoked together correctly', () => {459 class ClassChild extends React.PureComponent {460 componentDidMount() {461 Scheduler.unstable_yieldValue('componentDidMount');462 }463 componentWillUnmount() {464 Scheduler.unstable_yieldValue('componentWillUnmount');465 }466 render() {467 return this.props.text;468 }469 }470 function FunctionChild({text}) {471 React.useEffect(() => {472 Scheduler.unstable_yieldValue('useEffect mount');473 return () => Scheduler.unstable_yieldValue('useEffect unmount');474 });475 React.useLayoutEffect(() => {476 Scheduler.unstable_yieldValue('useLayoutEffect mount');477 return () => Scheduler.unstable_yieldValue('useLayoutEffect unmount');478 });479 return text;480 }481 function App({text}) {482 return (483 <>484 <ClassChild text={text} />485 <FunctionChild text={text} />486 </>487 );488 }489 let renderer;490 act(() => {491 renderer = ReactTestRenderer.create(<App text={'mount'} />, {492 unstable_isConcurrent: true,493 });494 });495 if (supportsDoubleInvokeEffects()) {496 expect(Scheduler).toHaveYielded([497 'componentDidMount',498 'useLayoutEffect mount',499 'useEffect mount',500 'componentWillUnmount',501 'useLayoutEffect unmount',502 'useEffect unmount',503 'componentDidMount',504 'useLayoutEffect mount',505 'useEffect mount',506 ]);507 } else {508 expect(Scheduler).toHaveYielded([509 'componentDidMount',510 'useLayoutEffect mount',511 'useEffect mount',512 ]);513 }514 act(() => {515 renderer.update(<App text={'mount'} />);516 });517 expect(Scheduler).toHaveYielded([518 'useLayoutEffect unmount',519 'useLayoutEffect mount',520 'useEffect unmount',521 'useEffect mount',522 ]);523 act(() => {524 renderer.unmount();525 });526 expect(Scheduler).toHaveYielded([527 'componentWillUnmount',528 'useLayoutEffect unmount',529 'useEffect unmount',530 ]);531 });...

Full Screen

Full Screen

Automation Testing Tutorials

Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.

LambdaTest Learning Hubs:

YouTube

You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.

Run avocado automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

Try LambdaTest Now !!

Get 100 minutes of automation test minutes FREE!!

Next-Gen App & Browser Testing Cloud

Was this article helpful?

Helpful

NotHelpful