Best JavaScript code snippet using wpt
ReactLegacyErrorBoundaries-test.internal.js
Source:ReactLegacyErrorBoundaries-test.internal.js
1/**2 * Copyright (c) Facebook, Inc. and its affiliates.3 *4 * This source code is licensed under the MIT license found in the5 * LICENSE file in the root directory of this source tree.6 *7 * @emails react-core8 */9'use strict';10let PropTypes;11let React;12let ReactDOM;13let ReactFeatureFlags;14// TODO: Refactor this test once componentDidCatch setState is deprecated.15describe('ReactLegacyErrorBoundaries', () => {16 let log;17 let BrokenConstructor;18 let BrokenComponentWillMount;19 let BrokenComponentDidMount;20 let BrokenComponentWillReceiveProps;21 let BrokenComponentWillUpdate;22 let BrokenComponentDidUpdate;23 let BrokenComponentWillUnmount;24 let BrokenRenderErrorBoundary;25 let BrokenComponentWillMountErrorBoundary;26 let BrokenComponentDidMountErrorBoundary;27 let BrokenRender;28 let ErrorBoundary;29 let ErrorMessage;30 let NoopErrorBoundary;31 let RetryErrorBoundary;32 let Normal;33 beforeEach(() => {34 jest.resetModules();35 PropTypes = require('prop-types');36 ReactFeatureFlags = require('shared/ReactFeatureFlags');37 ReactFeatureFlags.replayFailedUnitOfWorkWithInvokeGuardedCallback = false;38 ReactDOM = require('react-dom');39 React = require('react');40 log = [];41 BrokenConstructor = class extends React.Component {42 constructor(props) {43 super(props);44 log.push('BrokenConstructor constructor [!]');45 throw new Error('Hello');46 }47 render() {48 log.push('BrokenConstructor render');49 return <div>{this.props.children}</div>;50 }51 UNSAFE_componentWillMount() {52 log.push('BrokenConstructor componentWillMount');53 }54 componentDidMount() {55 log.push('BrokenConstructor componentDidMount');56 }57 UNSAFE_componentWillReceiveProps() {58 log.push('BrokenConstructor componentWillReceiveProps');59 }60 UNSAFE_componentWillUpdate() {61 log.push('BrokenConstructor componentWillUpdate');62 }63 componentDidUpdate() {64 log.push('BrokenConstructor componentDidUpdate');65 }66 componentWillUnmount() {67 log.push('BrokenConstructor componentWillUnmount');68 }69 };70 BrokenComponentWillMount = class extends React.Component {71 constructor(props) {72 super(props);73 log.push('BrokenComponentWillMount constructor');74 }75 render() {76 log.push('BrokenComponentWillMount render');77 return <div>{this.props.children}</div>;78 }79 UNSAFE_componentWillMount() {80 log.push('BrokenComponentWillMount componentWillMount [!]');81 throw new Error('Hello');82 }83 componentDidMount() {84 log.push('BrokenComponentWillMount componentDidMount');85 }86 UNSAFE_componentWillReceiveProps() {87 log.push('BrokenComponentWillMount componentWillReceiveProps');88 }89 UNSAFE_componentWillUpdate() {90 log.push('BrokenComponentWillMount componentWillUpdate');91 }92 componentDidUpdate() {93 log.push('BrokenComponentWillMount componentDidUpdate');94 }95 componentWillUnmount() {96 log.push('BrokenComponentWillMount componentWillUnmount');97 }98 };99 BrokenComponentDidMount = class extends React.Component {100 constructor(props) {101 super(props);102 log.push('BrokenComponentDidMount constructor');103 }104 render() {105 log.push('BrokenComponentDidMount render');106 return <div>{this.props.children}</div>;107 }108 UNSAFE_componentWillMount() {109 log.push('BrokenComponentDidMount componentWillMount');110 }111 componentDidMount() {112 log.push('BrokenComponentDidMount componentDidMount [!]');113 throw new Error('Hello');114 }115 UNSAFE_componentWillReceiveProps() {116 log.push('BrokenComponentDidMount componentWillReceiveProps');117 }118 UNSAFE_componentWillUpdate() {119 log.push('BrokenComponentDidMount componentWillUpdate');120 }121 componentDidUpdate() {122 log.push('BrokenComponentDidMount componentDidUpdate');123 }124 componentWillUnmount() {125 log.push('BrokenComponentDidMount componentWillUnmount');126 }127 };128 BrokenComponentWillReceiveProps = class extends React.Component {129 constructor(props) {130 super(props);131 log.push('BrokenComponentWillReceiveProps constructor');132 }133 render() {134 log.push('BrokenComponentWillReceiveProps render');135 return <div>{this.props.children}</div>;136 }137 UNSAFE_componentWillMount() {138 log.push('BrokenComponentWillReceiveProps componentWillMount');139 }140 componentDidMount() {141 log.push('BrokenComponentWillReceiveProps componentDidMount');142 }143 UNSAFE_componentWillReceiveProps() {144 log.push(145 'BrokenComponentWillReceiveProps componentWillReceiveProps [!]',146 );147 throw new Error('Hello');148 }149 UNSAFE_componentWillUpdate() {150 log.push('BrokenComponentWillReceiveProps componentWillUpdate');151 }152 componentDidUpdate() {153 log.push('BrokenComponentWillReceiveProps componentDidUpdate');154 }155 componentWillUnmount() {156 log.push('BrokenComponentWillReceiveProps componentWillUnmount');157 }158 };159 BrokenComponentWillUpdate = class extends React.Component {160 constructor(props) {161 super(props);162 log.push('BrokenComponentWillUpdate constructor');163 }164 render() {165 log.push('BrokenComponentWillUpdate render');166 return <div>{this.props.children}</div>;167 }168 UNSAFE_componentWillMount() {169 log.push('BrokenComponentWillUpdate componentWillMount');170 }171 componentDidMount() {172 log.push('BrokenComponentWillUpdate componentDidMount');173 }174 UNSAFE_componentWillReceiveProps() {175 log.push('BrokenComponentWillUpdate componentWillReceiveProps');176 }177 UNSAFE_componentWillUpdate() {178 log.push('BrokenComponentWillUpdate componentWillUpdate [!]');179 throw new Error('Hello');180 }181 componentDidUpdate() {182 log.push('BrokenComponentWillUpdate componentDidUpdate');183 }184 componentWillUnmount() {185 log.push('BrokenComponentWillUpdate componentWillUnmount');186 }187 };188 BrokenComponentDidUpdate = class extends React.Component {189 static defaultProps = {190 errorText: 'Hello',191 };192 constructor(props) {193 super(props);194 log.push('BrokenComponentDidUpdate constructor');195 }196 render() {197 log.push('BrokenComponentDidUpdate render');198 return <div>{this.props.children}</div>;199 }200 UNSAFE_componentWillMount() {201 log.push('BrokenComponentDidUpdate componentWillMount');202 }203 componentDidMount() {204 log.push('BrokenComponentDidUpdate componentDidMount');205 }206 UNSAFE_componentWillReceiveProps() {207 log.push('BrokenComponentDidUpdate componentWillReceiveProps');208 }209 UNSAFE_componentWillUpdate() {210 log.push('BrokenComponentDidUpdate componentWillUpdate');211 }212 componentDidUpdate() {213 log.push('BrokenComponentDidUpdate componentDidUpdate [!]');214 throw new Error(this.props.errorText);215 }216 componentWillUnmount() {217 log.push('BrokenComponentDidUpdate componentWillUnmount');218 }219 };220 BrokenComponentWillUnmount = class extends React.Component {221 static defaultProps = {222 errorText: 'Hello',223 };224 constructor(props) {225 super(props);226 log.push('BrokenComponentWillUnmount constructor');227 }228 render() {229 log.push('BrokenComponentWillUnmount render');230 return <div>{this.props.children}</div>;231 }232 UNSAFE_componentWillMount() {233 log.push('BrokenComponentWillUnmount componentWillMount');234 }235 componentDidMount() {236 log.push('BrokenComponentWillUnmount componentDidMount');237 }238 UNSAFE_componentWillReceiveProps() {239 log.push('BrokenComponentWillUnmount componentWillReceiveProps');240 }241 UNSAFE_componentWillUpdate() {242 log.push('BrokenComponentWillUnmount componentWillUpdate');243 }244 componentDidUpdate() {245 log.push('BrokenComponentWillUnmount componentDidUpdate');246 }247 componentWillUnmount() {248 log.push('BrokenComponentWillUnmount componentWillUnmount [!]');249 throw new Error(this.props.errorText);250 }251 };252 BrokenComponentWillMountErrorBoundary = class extends React.Component {253 constructor(props) {254 super(props);255 this.state = {error: null};256 log.push('BrokenComponentWillMountErrorBoundary constructor');257 }258 render() {259 if (this.state.error) {260 log.push('BrokenComponentWillMountErrorBoundary render error');261 return <div>Caught an error: {this.state.error.message}.</div>;262 }263 log.push('BrokenComponentWillMountErrorBoundary render success');264 return <div>{this.props.children}</div>;265 }266 UNSAFE_componentWillMount() {267 log.push(268 'BrokenComponentWillMountErrorBoundary componentWillMount [!]',269 );270 throw new Error('Hello');271 }272 componentDidMount() {273 log.push('BrokenComponentWillMountErrorBoundary componentDidMount');274 }275 componentWillUnmount() {276 log.push('BrokenComponentWillMountErrorBoundary componentWillUnmount');277 }278 componentDidCatch(error) {279 log.push('BrokenComponentWillMountErrorBoundary componentDidCatch');280 this.setState({error});281 }282 };283 BrokenComponentDidMountErrorBoundary = class extends React.Component {284 constructor(props) {285 super(props);286 this.state = {error: null};287 log.push('BrokenComponentDidMountErrorBoundary constructor');288 }289 render() {290 if (this.state.error) {291 log.push('BrokenComponentDidMountErrorBoundary render error');292 return <div>Caught an error: {this.state.error.message}.</div>;293 }294 log.push('BrokenComponentDidMountErrorBoundary render success');295 return <div>{this.props.children}</div>;296 }297 UNSAFE_componentWillMount() {298 log.push('BrokenComponentDidMountErrorBoundary componentWillMount');299 }300 componentDidMount() {301 log.push('BrokenComponentDidMountErrorBoundary componentDidMount [!]');302 throw new Error('Hello');303 }304 componentWillUnmount() {305 log.push('BrokenComponentDidMountErrorBoundary componentWillUnmount');306 }307 componentDidCatch(error) {308 log.push('BrokenComponentDidMountErrorBoundary componentDidCatch');309 this.setState({error});310 }311 };312 BrokenRenderErrorBoundary = class extends React.Component {313 constructor(props) {314 super(props);315 this.state = {error: null};316 log.push('BrokenRenderErrorBoundary constructor');317 }318 render() {319 if (this.state.error) {320 log.push('BrokenRenderErrorBoundary render error [!]');321 throw new Error('Hello');322 }323 log.push('BrokenRenderErrorBoundary render success');324 return <div>{this.props.children}</div>;325 }326 UNSAFE_componentWillMount() {327 log.push('BrokenRenderErrorBoundary componentWillMount');328 }329 componentDidMount() {330 log.push('BrokenRenderErrorBoundary componentDidMount');331 }332 componentWillUnmount() {333 log.push('BrokenRenderErrorBoundary componentWillUnmount');334 }335 componentDidCatch(error) {336 log.push('BrokenRenderErrorBoundary componentDidCatch');337 this.setState({error});338 }339 };340 BrokenRender = class extends React.Component {341 constructor(props) {342 super(props);343 log.push('BrokenRender constructor');344 }345 render() {346 log.push('BrokenRender render [!]');347 throw new Error('Hello');348 }349 UNSAFE_componentWillMount() {350 log.push('BrokenRender componentWillMount');351 }352 componentDidMount() {353 log.push('BrokenRender componentDidMount');354 }355 UNSAFE_componentWillReceiveProps() {356 log.push('BrokenRender componentWillReceiveProps');357 }358 UNSAFE_componentWillUpdate() {359 log.push('BrokenRender componentWillUpdate');360 }361 componentDidUpdate() {362 log.push('BrokenRender componentDidUpdate');363 }364 componentWillUnmount() {365 log.push('BrokenRender componentWillUnmount');366 }367 };368 NoopErrorBoundary = class extends React.Component {369 constructor(props) {370 super(props);371 log.push('NoopErrorBoundary constructor');372 }373 render() {374 log.push('NoopErrorBoundary render');375 return <BrokenRender />;376 }377 UNSAFE_componentWillMount() {378 log.push('NoopErrorBoundary componentWillMount');379 }380 componentDidMount() {381 log.push('NoopErrorBoundary componentDidMount');382 }383 componentWillUnmount() {384 log.push('NoopErrorBoundary componentWillUnmount');385 }386 componentDidCatch() {387 log.push('NoopErrorBoundary componentDidCatch');388 }389 };390 Normal = class extends React.Component {391 static defaultProps = {392 logName: 'Normal',393 };394 constructor(props) {395 super(props);396 log.push(`${this.props.logName} constructor`);397 }398 render() {399 log.push(`${this.props.logName} render`);400 return <div>{this.props.children}</div>;401 }402 UNSAFE_componentWillMount() {403 log.push(`${this.props.logName} componentWillMount`);404 }405 componentDidMount() {406 log.push(`${this.props.logName} componentDidMount`);407 }408 UNSAFE_componentWillReceiveProps() {409 log.push(`${this.props.logName} componentWillReceiveProps`);410 }411 UNSAFE_componentWillUpdate() {412 log.push(`${this.props.logName} componentWillUpdate`);413 }414 componentDidUpdate() {415 log.push(`${this.props.logName} componentDidUpdate`);416 }417 componentWillUnmount() {418 log.push(`${this.props.logName} componentWillUnmount`);419 }420 };421 ErrorBoundary = class extends React.Component {422 constructor(props) {423 super(props);424 this.state = {error: null};425 log.push(`${this.props.logName} constructor`);426 }427 render() {428 if (this.state.error && !this.props.forceRetry) {429 log.push(`${this.props.logName} render error`);430 return this.props.renderError(this.state.error, this.props);431 }432 log.push(`${this.props.logName} render success`);433 return <div>{this.props.children}</div>;434 }435 componentDidCatch(error) {436 log.push(`${this.props.logName} componentDidCatch`);437 this.setState({error});438 }439 UNSAFE_componentWillMount() {440 log.push(`${this.props.logName} componentWillMount`);441 }442 componentDidMount() {443 log.push(`${this.props.logName} componentDidMount`);444 }445 UNSAFE_componentWillReceiveProps() {446 log.push(`${this.props.logName} componentWillReceiveProps`);447 }448 UNSAFE_componentWillUpdate() {449 log.push(`${this.props.logName} componentWillUpdate`);450 }451 componentDidUpdate() {452 log.push(`${this.props.logName} componentDidUpdate`);453 }454 componentWillUnmount() {455 log.push(`${this.props.logName} componentWillUnmount`);456 }457 };458 ErrorBoundary.defaultProps = {459 logName: 'ErrorBoundary',460 renderError(error, props) {461 return (462 <div ref={props.errorMessageRef}>463 Caught an error: {error.message}.464 </div>465 );466 },467 };468 RetryErrorBoundary = class extends React.Component {469 constructor(props) {470 super(props);471 log.push('RetryErrorBoundary constructor');472 }473 render() {474 log.push('RetryErrorBoundary render');475 return <BrokenRender />;476 }477 UNSAFE_componentWillMount() {478 log.push('RetryErrorBoundary componentWillMount');479 }480 componentDidMount() {481 log.push('RetryErrorBoundary componentDidMount');482 }483 componentWillUnmount() {484 log.push('RetryErrorBoundary componentWillUnmount');485 }486 componentDidCatch(error) {487 log.push('RetryErrorBoundary componentDidCatch [!]');488 // In Fiber, calling setState() (and failing) is treated as a rethrow.489 this.setState({});490 }491 };492 ErrorMessage = class extends React.Component {493 constructor(props) {494 super(props);495 log.push('ErrorMessage constructor');496 }497 UNSAFE_componentWillMount() {498 log.push('ErrorMessage componentWillMount');499 }500 componentDidMount() {501 log.push('ErrorMessage componentDidMount');502 }503 componentWillUnmount() {504 log.push('ErrorMessage componentWillUnmount');505 }506 render() {507 log.push('ErrorMessage render');508 return <div>Caught an error: {this.props.message}.</div>;509 }510 };511 });512 it('does not swallow exceptions on mounting without boundaries', () => {513 let container = document.createElement('div');514 expect(() => {515 ReactDOM.render(<BrokenRender />, container);516 }).toThrow('Hello');517 container = document.createElement('div');518 expect(() => {519 ReactDOM.render(<BrokenComponentWillMount />, container);520 }).toThrow('Hello');521 container = document.createElement('div');522 expect(() => {523 ReactDOM.render(<BrokenComponentDidMount />, container);524 }).toThrow('Hello');525 });526 it('does not swallow exceptions on updating without boundaries', () => {527 let container = document.createElement('div');528 ReactDOM.render(<BrokenComponentWillUpdate />, container);529 expect(() => {530 ReactDOM.render(<BrokenComponentWillUpdate />, container);531 }).toThrow('Hello');532 container = document.createElement('div');533 ReactDOM.render(<BrokenComponentWillReceiveProps />, container);534 expect(() => {535 ReactDOM.render(<BrokenComponentWillReceiveProps />, container);536 }).toThrow('Hello');537 container = document.createElement('div');538 ReactDOM.render(<BrokenComponentDidUpdate />, container);539 expect(() => {540 ReactDOM.render(<BrokenComponentDidUpdate />, container);541 }).toThrow('Hello');542 });543 it('does not swallow exceptions on unmounting without boundaries', () => {544 const container = document.createElement('div');545 ReactDOM.render(<BrokenComponentWillUnmount />, container);546 expect(() => {547 ReactDOM.unmountComponentAtNode(container);548 }).toThrow('Hello');549 });550 it('prevents errors from leaking into other roots', () => {551 const container1 = document.createElement('div');552 const container2 = document.createElement('div');553 const container3 = document.createElement('div');554 ReactDOM.render(<span>Before 1</span>, container1);555 expect(() => {556 ReactDOM.render(<BrokenRender />, container2);557 }).toThrow('Hello');558 ReactDOM.render(559 <ErrorBoundary>560 <BrokenRender />561 </ErrorBoundary>,562 container3,563 );564 expect(container1.firstChild.textContent).toBe('Before 1');565 expect(container2.firstChild).toBe(null);566 expect(container3.firstChild.textContent).toBe('Caught an error: Hello.');567 ReactDOM.render(<span>After 1</span>, container1);568 ReactDOM.render(<span>After 2</span>, container2);569 ReactDOM.render(570 <ErrorBoundary forceRetry={true}>After 3</ErrorBoundary>,571 container3,572 );573 expect(container1.firstChild.textContent).toBe('After 1');574 expect(container2.firstChild.textContent).toBe('After 2');575 expect(container3.firstChild.textContent).toBe('After 3');576 ReactDOM.unmountComponentAtNode(container1);577 ReactDOM.unmountComponentAtNode(container2);578 ReactDOM.unmountComponentAtNode(container3);579 expect(container1.firstChild).toBe(null);580 expect(container2.firstChild).toBe(null);581 expect(container3.firstChild).toBe(null);582 });583 it('renders an error state if child throws in render', () => {584 const container = document.createElement('div');585 ReactDOM.render(586 <ErrorBoundary>587 <BrokenRender />588 </ErrorBoundary>,589 container,590 );591 expect(container.firstChild.textContent).toBe('Caught an error: Hello.');592 expect(log).toEqual([593 'ErrorBoundary constructor',594 'ErrorBoundary componentWillMount',595 'ErrorBoundary render success',596 'BrokenRender constructor',597 'BrokenRender componentWillMount',598 'BrokenRender render [!]',599 // Fiber mounts with null children before capturing error600 'ErrorBoundary componentDidMount',601 // Catch and render an error message602 'ErrorBoundary componentDidCatch',603 'ErrorBoundary componentWillUpdate',604 'ErrorBoundary render error',605 'ErrorBoundary componentDidUpdate',606 ]);607 log.length = 0;608 ReactDOM.unmountComponentAtNode(container);609 expect(log).toEqual(['ErrorBoundary componentWillUnmount']);610 });611 it('renders an error state if child throws in constructor', () => {612 const container = document.createElement('div');613 ReactDOM.render(614 <ErrorBoundary>615 <BrokenConstructor />616 </ErrorBoundary>,617 container,618 );619 expect(container.firstChild.textContent).toBe('Caught an error: Hello.');620 expect(log).toEqual([621 'ErrorBoundary constructor',622 'ErrorBoundary componentWillMount',623 'ErrorBoundary render success',624 'BrokenConstructor constructor [!]',625 // Fiber mounts with null children before capturing error626 'ErrorBoundary componentDidMount',627 // Catch and render an error message628 'ErrorBoundary componentDidCatch',629 'ErrorBoundary componentWillUpdate',630 'ErrorBoundary render error',631 'ErrorBoundary componentDidUpdate',632 ]);633 log.length = 0;634 ReactDOM.unmountComponentAtNode(container);635 expect(log).toEqual(['ErrorBoundary componentWillUnmount']);636 });637 it('renders an error state if child throws in componentWillMount', () => {638 const container = document.createElement('div');639 ReactDOM.render(640 <ErrorBoundary>641 <BrokenComponentWillMount />642 </ErrorBoundary>,643 container,644 );645 expect(container.firstChild.textContent).toBe('Caught an error: Hello.');646 expect(log).toEqual([647 'ErrorBoundary constructor',648 'ErrorBoundary componentWillMount',649 'ErrorBoundary render success',650 'BrokenComponentWillMount constructor',651 'BrokenComponentWillMount componentWillMount [!]',652 'ErrorBoundary componentDidMount',653 'ErrorBoundary componentDidCatch',654 'ErrorBoundary componentWillUpdate',655 'ErrorBoundary render error',656 'ErrorBoundary componentDidUpdate',657 ]);658 log.length = 0;659 ReactDOM.unmountComponentAtNode(container);660 expect(log).toEqual(['ErrorBoundary componentWillUnmount']);661 });662 it('renders an error state if context provider throws in componentWillMount', () => {663 class BrokenComponentWillMountWithContext extends React.Component {664 static childContextTypes = {foo: PropTypes.number};665 getChildContext() {666 return {foo: 42};667 }668 render() {669 return <div>{this.props.children}</div>;670 }671 UNSAFE_componentWillMount() {672 throw new Error('Hello');673 }674 }675 const container = document.createElement('div');676 ReactDOM.render(677 <ErrorBoundary>678 <BrokenComponentWillMountWithContext />679 </ErrorBoundary>,680 container,681 );682 expect(container.firstChild.textContent).toBe('Caught an error: Hello.');683 });684 it('renders an error state if module-style context provider throws in componentWillMount', () => {685 function BrokenComponentWillMountWithContext() {686 return {687 getChildContext() {688 return {foo: 42};689 },690 render() {691 return <div>{this.props.children}</div>;692 },693 UNSAFE_componentWillMount() {694 throw new Error('Hello');695 },696 };697 }698 BrokenComponentWillMountWithContext.childContextTypes = {699 foo: PropTypes.number,700 };701 const container = document.createElement('div');702 ReactDOM.render(703 <ErrorBoundary>704 <BrokenComponentWillMountWithContext />705 </ErrorBoundary>,706 container,707 );708 expect(container.firstChild.textContent).toBe('Caught an error: Hello.');709 });710 it('mounts the error message if mounting fails', () => {711 function renderError(error) {712 return <ErrorMessage message={error.message} />;713 }714 const container = document.createElement('div');715 ReactDOM.render(716 <ErrorBoundary renderError={renderError}>717 <BrokenRender />718 </ErrorBoundary>,719 container,720 );721 expect(log).toEqual([722 'ErrorBoundary constructor',723 'ErrorBoundary componentWillMount',724 'ErrorBoundary render success',725 'BrokenRender constructor',726 'BrokenRender componentWillMount',727 'BrokenRender render [!]',728 'ErrorBoundary componentDidMount',729 'ErrorBoundary componentDidCatch',730 'ErrorBoundary componentWillUpdate',731 'ErrorBoundary render error',732 'ErrorMessage constructor',733 'ErrorMessage componentWillMount',734 'ErrorMessage render',735 'ErrorMessage componentDidMount',736 'ErrorBoundary componentDidUpdate',737 ]);738 log.length = 0;739 ReactDOM.unmountComponentAtNode(container);740 expect(log).toEqual([741 'ErrorBoundary componentWillUnmount',742 'ErrorMessage componentWillUnmount',743 ]);744 });745 it('propagates errors on retry on mounting', () => {746 const container = document.createElement('div');747 ReactDOM.render(748 <ErrorBoundary>749 <RetryErrorBoundary>750 <BrokenRender />751 </RetryErrorBoundary>752 </ErrorBoundary>,753 container,754 );755 expect(container.firstChild.textContent).toBe('Caught an error: Hello.');756 expect(log).toEqual([757 'ErrorBoundary constructor',758 'ErrorBoundary componentWillMount',759 'ErrorBoundary render success',760 'RetryErrorBoundary constructor',761 'RetryErrorBoundary componentWillMount',762 'RetryErrorBoundary render',763 'BrokenRender constructor',764 'BrokenRender componentWillMount',765 'BrokenRender render [!]',766 // In Fiber, failed error boundaries render null before attempting to recover767 'RetryErrorBoundary componentDidMount',768 'RetryErrorBoundary componentDidCatch [!]',769 'ErrorBoundary componentDidMount',770 // Retry771 'RetryErrorBoundary render',772 'BrokenRender constructor',773 'BrokenRender componentWillMount',774 'BrokenRender render [!]',775 // This time, the error propagates to the higher boundary776 'RetryErrorBoundary componentWillUnmount',777 'ErrorBoundary componentDidCatch',778 // Render the error779 'ErrorBoundary componentWillUpdate',780 'ErrorBoundary render error',781 'ErrorBoundary componentDidUpdate',782 ]);783 log.length = 0;784 ReactDOM.unmountComponentAtNode(container);785 expect(log).toEqual(['ErrorBoundary componentWillUnmount']);786 });787 it('propagates errors inside boundary during componentWillMount', () => {788 const container = document.createElement('div');789 ReactDOM.render(790 <ErrorBoundary>791 <BrokenComponentWillMountErrorBoundary />792 </ErrorBoundary>,793 container,794 );795 expect(container.firstChild.textContent).toBe('Caught an error: Hello.');796 expect(log).toEqual([797 'ErrorBoundary constructor',798 'ErrorBoundary componentWillMount',799 'ErrorBoundary render success',800 'BrokenComponentWillMountErrorBoundary constructor',801 'BrokenComponentWillMountErrorBoundary componentWillMount [!]',802 // The error propagates to the higher boundary803 'ErrorBoundary componentDidMount',804 'ErrorBoundary componentDidCatch',805 'ErrorBoundary componentWillUpdate',806 'ErrorBoundary render error',807 'ErrorBoundary componentDidUpdate',808 ]);809 log.length = 0;810 ReactDOM.unmountComponentAtNode(container);811 expect(log).toEqual(['ErrorBoundary componentWillUnmount']);812 });813 it('propagates errors inside boundary while rendering error state', () => {814 const container = document.createElement('div');815 ReactDOM.render(816 <ErrorBoundary>817 <BrokenRenderErrorBoundary>818 <BrokenRender />819 </BrokenRenderErrorBoundary>820 </ErrorBoundary>,821 container,822 );823 expect(container.firstChild.textContent).toBe('Caught an error: Hello.');824 expect(log).toEqual([825 'ErrorBoundary constructor',826 'ErrorBoundary componentWillMount',827 'ErrorBoundary render success',828 'BrokenRenderErrorBoundary constructor',829 'BrokenRenderErrorBoundary componentWillMount',830 'BrokenRenderErrorBoundary render success',831 'BrokenRender constructor',832 'BrokenRender componentWillMount',833 'BrokenRender render [!]',834 // The first error boundary catches the error835 // It adjusts state but throws displaying the message836 // Finish mounting with null children837 'BrokenRenderErrorBoundary componentDidMount',838 // Attempt to handle the error839 'BrokenRenderErrorBoundary componentDidCatch',840 'ErrorBoundary componentDidMount',841 'BrokenRenderErrorBoundary render error [!]',842 // Boundary fails with new error, propagate to next boundary843 'BrokenRenderErrorBoundary componentWillUnmount',844 // Attempt to handle the error again845 'ErrorBoundary componentDidCatch',846 'ErrorBoundary componentWillUpdate',847 'ErrorBoundary render error',848 'ErrorBoundary componentDidUpdate',849 ]);850 log.length = 0;851 ReactDOM.unmountComponentAtNode(container);852 expect(log).toEqual(['ErrorBoundary componentWillUnmount']);853 });854 it('does not call componentWillUnmount when aborting initial mount', () => {855 const container = document.createElement('div');856 ReactDOM.render(857 <ErrorBoundary>858 <Normal />859 <BrokenRender />860 <Normal />861 </ErrorBoundary>,862 container,863 );864 expect(container.firstChild.textContent).toBe('Caught an error: Hello.');865 expect(log).toEqual([866 'ErrorBoundary constructor',867 'ErrorBoundary componentWillMount',868 'ErrorBoundary render success',869 // Render first child870 'Normal constructor',871 'Normal componentWillMount',872 'Normal render',873 // Render second child (it throws)874 'BrokenRender constructor',875 'BrokenRender componentWillMount',876 'BrokenRender render [!]',877 // Render third child, even though an earlier sibling threw.878 'Normal constructor',879 'Normal componentWillMount',880 'Normal render',881 // Finish mounting with null children882 'ErrorBoundary componentDidMount',883 // Handle the error884 'ErrorBoundary componentDidCatch',885 // Render the error message886 'ErrorBoundary componentWillUpdate',887 'ErrorBoundary render error',888 'ErrorBoundary componentDidUpdate',889 ]);890 log.length = 0;891 ReactDOM.unmountComponentAtNode(container);892 expect(log).toEqual(['ErrorBoundary componentWillUnmount']);893 });894 it('resets callback refs if mounting aborts', () => {895 function childRef(x) {896 log.push('Child ref is set to ' + x);897 }898 function errorMessageRef(x) {899 log.push('Error message ref is set to ' + x);900 }901 const container = document.createElement('div');902 ReactDOM.render(903 <ErrorBoundary errorMessageRef={errorMessageRef}>904 <div ref={childRef} />905 <BrokenRender />906 </ErrorBoundary>,907 container,908 );909 expect(container.textContent).toBe('Caught an error: Hello.');910 expect(log).toEqual([911 'ErrorBoundary constructor',912 'ErrorBoundary componentWillMount',913 'ErrorBoundary render success',914 'BrokenRender constructor',915 'BrokenRender componentWillMount',916 'BrokenRender render [!]',917 // Handle error:918 // Finish mounting with null children919 'ErrorBoundary componentDidMount',920 // Handle the error921 'ErrorBoundary componentDidCatch',922 // Render the error message923 'ErrorBoundary componentWillUpdate',924 'ErrorBoundary render error',925 'Error message ref is set to [object HTMLDivElement]',926 'ErrorBoundary componentDidUpdate',927 ]);928 log.length = 0;929 ReactDOM.unmountComponentAtNode(container);930 expect(log).toEqual([931 'ErrorBoundary componentWillUnmount',932 'Error message ref is set to null',933 ]);934 });935 it('resets object refs if mounting aborts', () => {936 let childRef = React.createRef();937 let errorMessageRef = React.createRef();938 const container = document.createElement('div');939 ReactDOM.render(940 <ErrorBoundary errorMessageRef={errorMessageRef}>941 <div ref={childRef} />942 <BrokenRender />943 </ErrorBoundary>,944 container,945 );946 expect(container.textContent).toBe('Caught an error: Hello.');947 expect(log).toEqual([948 'ErrorBoundary constructor',949 'ErrorBoundary componentWillMount',950 'ErrorBoundary render success',951 'BrokenRender constructor',952 'BrokenRender componentWillMount',953 'BrokenRender render [!]',954 // Handle error:955 // Finish mounting with null children956 'ErrorBoundary componentDidMount',957 // Handle the error958 'ErrorBoundary componentDidCatch',959 // Render the error message960 'ErrorBoundary componentWillUpdate',961 'ErrorBoundary render error',962 'ErrorBoundary componentDidUpdate',963 ]);964 expect(errorMessageRef.current.toString()).toEqual(965 '[object HTMLDivElement]',966 );967 log.length = 0;968 ReactDOM.unmountComponentAtNode(container);969 expect(log).toEqual(['ErrorBoundary componentWillUnmount']);970 expect(errorMessageRef.current).toEqual(null);971 });972 it('successfully mounts if no error occurs', () => {973 const container = document.createElement('div');974 ReactDOM.render(975 <ErrorBoundary>976 <div>Mounted successfully.</div>977 </ErrorBoundary>,978 container,979 );980 expect(container.firstChild.textContent).toBe('Mounted successfully.');981 expect(log).toEqual([982 'ErrorBoundary constructor',983 'ErrorBoundary componentWillMount',984 'ErrorBoundary render success',985 'ErrorBoundary componentDidMount',986 ]);987 log.length = 0;988 ReactDOM.unmountComponentAtNode(container);989 expect(log).toEqual(['ErrorBoundary componentWillUnmount']);990 });991 it('catches if child throws in constructor during update', () => {992 const container = document.createElement('div');993 ReactDOM.render(994 <ErrorBoundary>995 <Normal />996 </ErrorBoundary>,997 container,998 );999 log.length = 0;1000 ReactDOM.render(1001 <ErrorBoundary>1002 <Normal />1003 <Normal logName="Normal2" />1004 <BrokenConstructor />1005 </ErrorBoundary>,1006 container,1007 );1008 expect(container.textContent).toBe('Caught an error: Hello.');1009 expect(log).toEqual([1010 'ErrorBoundary componentWillReceiveProps',1011 'ErrorBoundary componentWillUpdate',1012 'ErrorBoundary render success',1013 'Normal componentWillReceiveProps',1014 'Normal componentWillUpdate',1015 'Normal render',1016 // Normal2 will attempt to mount:1017 'Normal2 constructor',1018 'Normal2 componentWillMount',1019 'Normal2 render',1020 // BrokenConstructor will abort rendering:1021 'BrokenConstructor constructor [!]',1022 // Finish updating with null children1023 'Normal componentWillUnmount',1024 'ErrorBoundary componentDidUpdate',1025 // Handle the error1026 'ErrorBoundary componentDidCatch',1027 // Render the error message1028 'ErrorBoundary componentWillUpdate',1029 'ErrorBoundary render error',1030 'ErrorBoundary componentDidUpdate',1031 ]);1032 log.length = 0;1033 ReactDOM.unmountComponentAtNode(container);1034 expect(log).toEqual(['ErrorBoundary componentWillUnmount']);1035 });1036 it('catches if child throws in componentWillMount during update', () => {1037 const container = document.createElement('div');1038 ReactDOM.render(1039 <ErrorBoundary>1040 <Normal />1041 </ErrorBoundary>,1042 container,1043 );1044 log.length = 0;1045 ReactDOM.render(1046 <ErrorBoundary>1047 <Normal />1048 <Normal logName="Normal2" />1049 <BrokenComponentWillMount />1050 </ErrorBoundary>,1051 container,1052 );1053 expect(container.textContent).toBe('Caught an error: Hello.');1054 expect(log).toEqual([1055 'ErrorBoundary componentWillReceiveProps',1056 'ErrorBoundary componentWillUpdate',1057 'ErrorBoundary render success',1058 'Normal componentWillReceiveProps',1059 'Normal componentWillUpdate',1060 'Normal render',1061 // Normal2 will attempt to mount:1062 'Normal2 constructor',1063 'Normal2 componentWillMount',1064 'Normal2 render',1065 // BrokenComponentWillMount will abort rendering:1066 'BrokenComponentWillMount constructor',1067 'BrokenComponentWillMount componentWillMount [!]',1068 // Finish updating with null children1069 'Normal componentWillUnmount',1070 'ErrorBoundary componentDidUpdate',1071 // Handle the error1072 'ErrorBoundary componentDidCatch',1073 // Render the error message1074 'ErrorBoundary componentWillUpdate',1075 'ErrorBoundary render error',1076 'ErrorBoundary componentDidUpdate',1077 ]);1078 log.length = 0;1079 ReactDOM.unmountComponentAtNode(container);1080 expect(log).toEqual(['ErrorBoundary componentWillUnmount']);1081 });1082 it('catches if child throws in componentWillReceiveProps during update', () => {1083 const container = document.createElement('div');1084 ReactDOM.render(1085 <ErrorBoundary>1086 <Normal />1087 <BrokenComponentWillReceiveProps />1088 </ErrorBoundary>,1089 container,1090 );1091 log.length = 0;1092 ReactDOM.render(1093 <ErrorBoundary>1094 <Normal />1095 <BrokenComponentWillReceiveProps />1096 </ErrorBoundary>,1097 container,1098 );1099 expect(container.textContent).toBe('Caught an error: Hello.');1100 expect(log).toEqual([1101 'ErrorBoundary componentWillReceiveProps',1102 'ErrorBoundary componentWillUpdate',1103 'ErrorBoundary render success',1104 'Normal componentWillReceiveProps',1105 'Normal componentWillUpdate',1106 'Normal render',1107 // BrokenComponentWillReceiveProps will abort rendering:1108 'BrokenComponentWillReceiveProps componentWillReceiveProps [!]',1109 // Finish updating with null children1110 'Normal componentWillUnmount',1111 'BrokenComponentWillReceiveProps componentWillUnmount',1112 'ErrorBoundary componentDidUpdate',1113 // Handle the error1114 'ErrorBoundary componentDidCatch',1115 'ErrorBoundary componentWillUpdate',1116 'ErrorBoundary render error',1117 'ErrorBoundary componentDidUpdate',1118 ]);1119 log.length = 0;1120 ReactDOM.unmountComponentAtNode(container);1121 expect(log).toEqual(['ErrorBoundary componentWillUnmount']);1122 });1123 it('catches if child throws in componentWillUpdate during update', () => {1124 const container = document.createElement('div');1125 ReactDOM.render(1126 <ErrorBoundary>1127 <Normal />1128 <BrokenComponentWillUpdate />1129 </ErrorBoundary>,1130 container,1131 );1132 log.length = 0;1133 ReactDOM.render(1134 <ErrorBoundary>1135 <Normal />1136 <BrokenComponentWillUpdate />1137 </ErrorBoundary>,1138 container,1139 );1140 expect(container.textContent).toBe('Caught an error: Hello.');1141 expect(log).toEqual([1142 'ErrorBoundary componentWillReceiveProps',1143 'ErrorBoundary componentWillUpdate',1144 'ErrorBoundary render success',1145 'Normal componentWillReceiveProps',1146 'Normal componentWillUpdate',1147 'Normal render',1148 // BrokenComponentWillUpdate will abort rendering:1149 'BrokenComponentWillUpdate componentWillReceiveProps',1150 'BrokenComponentWillUpdate componentWillUpdate [!]',1151 // Finish updating with null children1152 'Normal componentWillUnmount',1153 'BrokenComponentWillUpdate componentWillUnmount',1154 'ErrorBoundary componentDidUpdate',1155 // Handle the error1156 'ErrorBoundary componentDidCatch',1157 'ErrorBoundary componentWillUpdate',1158 'ErrorBoundary render error',1159 'ErrorBoundary componentDidUpdate',1160 ]);1161 log.length = 0;1162 ReactDOM.unmountComponentAtNode(container);1163 expect(log).toEqual(['ErrorBoundary componentWillUnmount']);1164 });1165 it('catches if child throws in render during update', () => {1166 const container = document.createElement('div');1167 ReactDOM.render(1168 <ErrorBoundary>1169 <Normal />1170 </ErrorBoundary>,1171 container,1172 );1173 log.length = 0;1174 ReactDOM.render(1175 <ErrorBoundary>1176 <Normal />1177 <Normal logName="Normal2" />1178 <BrokenRender />1179 </ErrorBoundary>,1180 container,1181 );1182 expect(container.textContent).toBe('Caught an error: Hello.');1183 expect(log).toEqual([1184 'ErrorBoundary componentWillReceiveProps',1185 'ErrorBoundary componentWillUpdate',1186 'ErrorBoundary render success',1187 'Normal componentWillReceiveProps',1188 'Normal componentWillUpdate',1189 'Normal render',1190 // Normal2 will attempt to mount:1191 'Normal2 constructor',1192 'Normal2 componentWillMount',1193 'Normal2 render',1194 // BrokenRender will abort rendering:1195 'BrokenRender constructor',1196 'BrokenRender componentWillMount',1197 'BrokenRender render [!]',1198 // Finish updating with null children1199 'Normal componentWillUnmount',1200 'ErrorBoundary componentDidUpdate',1201 // Handle the error1202 'ErrorBoundary componentDidCatch',1203 'ErrorBoundary componentWillUpdate',1204 'ErrorBoundary render error',1205 'ErrorBoundary componentDidUpdate',1206 ]);1207 log.length = 0;1208 ReactDOM.unmountComponentAtNode(container);1209 expect(log).toEqual(['ErrorBoundary componentWillUnmount']);1210 });1211 it('keeps refs up-to-date during updates', () => {1212 function child1Ref(x) {1213 log.push('Child1 ref is set to ' + x);1214 }1215 function child2Ref(x) {1216 log.push('Child2 ref is set to ' + x);1217 }1218 function errorMessageRef(x) {1219 log.push('Error message ref is set to ' + x);1220 }1221 const container = document.createElement('div');1222 ReactDOM.render(1223 <ErrorBoundary errorMessageRef={errorMessageRef}>1224 <div ref={child1Ref} />1225 </ErrorBoundary>,1226 container,1227 );1228 expect(log).toEqual([1229 'ErrorBoundary constructor',1230 'ErrorBoundary componentWillMount',1231 'ErrorBoundary render success',1232 'Child1 ref is set to [object HTMLDivElement]',1233 'ErrorBoundary componentDidMount',1234 ]);1235 log.length = 0;1236 ReactDOM.render(1237 <ErrorBoundary errorMessageRef={errorMessageRef}>1238 <div ref={child1Ref} />1239 <div ref={child2Ref} />1240 <BrokenRender />1241 </ErrorBoundary>,1242 container,1243 );1244 expect(container.textContent).toBe('Caught an error: Hello.');1245 expect(log).toEqual([1246 'ErrorBoundary componentWillReceiveProps',1247 'ErrorBoundary componentWillUpdate',1248 'ErrorBoundary render success',1249 // BrokenRender will abort rendering:1250 'BrokenRender constructor',1251 'BrokenRender componentWillMount',1252 'BrokenRender render [!]',1253 // Finish updating with null children1254 'Child1 ref is set to null',1255 'ErrorBoundary componentDidUpdate',1256 // Handle the error1257 'ErrorBoundary componentDidCatch',1258 'ErrorBoundary componentWillUpdate',1259 'ErrorBoundary render error',1260 'Error message ref is set to [object HTMLDivElement]',1261 // Child2 ref is never set because its mounting aborted1262 'ErrorBoundary componentDidUpdate',1263 ]);1264 log.length = 0;1265 ReactDOM.unmountComponentAtNode(container);1266 expect(log).toEqual([1267 'ErrorBoundary componentWillUnmount',1268 'Error message ref is set to null',1269 ]);1270 });1271 it('recovers from componentWillUnmount errors on update', () => {1272 const container = document.createElement('div');1273 ReactDOM.render(1274 <ErrorBoundary>1275 <BrokenComponentWillUnmount />1276 <BrokenComponentWillUnmount />1277 <Normal />1278 </ErrorBoundary>,1279 container,1280 );1281 log.length = 0;1282 ReactDOM.render(1283 <ErrorBoundary>1284 <BrokenComponentWillUnmount />1285 </ErrorBoundary>,1286 container,1287 );1288 expect(container.textContent).toBe('Caught an error: Hello.');1289 expect(log).toEqual([1290 'ErrorBoundary componentWillReceiveProps',1291 'ErrorBoundary componentWillUpdate',1292 'ErrorBoundary render success',1293 // Update existing child:1294 'BrokenComponentWillUnmount componentWillReceiveProps',1295 'BrokenComponentWillUnmount componentWillUpdate',1296 'BrokenComponentWillUnmount render',1297 // Unmounting throws:1298 'BrokenComponentWillUnmount componentWillUnmount [!]',1299 // Fiber proceeds with lifecycles despite errors1300 'Normal componentWillUnmount',1301 // The components have updated in this phase1302 'BrokenComponentWillUnmount componentDidUpdate',1303 'ErrorBoundary componentDidUpdate',1304 // Now that commit phase is done, Fiber unmounts the boundary's children1305 'BrokenComponentWillUnmount componentWillUnmount [!]',1306 'ErrorBoundary componentDidCatch',1307 // The initial render was aborted, so1308 // Fiber retries from the root.1309 'ErrorBoundary componentWillUpdate',1310 'ErrorBoundary componentDidUpdate',1311 // The second willUnmount error should be captured and logged, too.1312 'ErrorBoundary componentDidCatch',1313 'ErrorBoundary componentWillUpdate',1314 // Render an error now (stack will do it later)1315 'ErrorBoundary render error',1316 // Attempt to unmount previous child:1317 // Done1318 'ErrorBoundary componentDidUpdate',1319 ]);1320 log.length = 0;1321 ReactDOM.unmountComponentAtNode(container);1322 expect(log).toEqual(['ErrorBoundary componentWillUnmount']);1323 });1324 it('recovers from nested componentWillUnmount errors on update', () => {1325 const container = document.createElement('div');1326 ReactDOM.render(1327 <ErrorBoundary>1328 <Normal>1329 <BrokenComponentWillUnmount />1330 </Normal>1331 <BrokenComponentWillUnmount />1332 </ErrorBoundary>,1333 container,1334 );1335 log.length = 0;1336 ReactDOM.render(1337 <ErrorBoundary>1338 <Normal>1339 <BrokenComponentWillUnmount />1340 </Normal>1341 </ErrorBoundary>,1342 container,1343 );1344 expect(container.textContent).toBe('Caught an error: Hello.');1345 expect(log).toEqual([1346 'ErrorBoundary componentWillReceiveProps',1347 'ErrorBoundary componentWillUpdate',1348 'ErrorBoundary render success',1349 // Update existing children:1350 'Normal componentWillReceiveProps',1351 'Normal componentWillUpdate',1352 'Normal render',1353 'BrokenComponentWillUnmount componentWillReceiveProps',1354 'BrokenComponentWillUnmount componentWillUpdate',1355 'BrokenComponentWillUnmount render',1356 // Unmounting throws:1357 'BrokenComponentWillUnmount componentWillUnmount [!]',1358 // Fiber proceeds with lifecycles despite errors1359 'BrokenComponentWillUnmount componentDidUpdate',1360 'Normal componentDidUpdate',1361 'ErrorBoundary componentDidUpdate',1362 'Normal componentWillUnmount',1363 'BrokenComponentWillUnmount componentWillUnmount [!]',1364 // Now that commit phase is done, Fiber handles errors1365 'ErrorBoundary componentDidCatch',1366 // The initial render was aborted, so1367 // Fiber retries from the root.1368 'ErrorBoundary componentWillUpdate',1369 'ErrorBoundary componentDidUpdate',1370 // The second willUnmount error should be captured and logged, too.1371 'ErrorBoundary componentDidCatch',1372 'ErrorBoundary componentWillUpdate',1373 // Render an error now (stack will do it later)1374 'ErrorBoundary render error',1375 // Done1376 'ErrorBoundary componentDidUpdate',1377 ]);1378 log.length = 0;1379 ReactDOM.unmountComponentAtNode(container);1380 expect(log).toEqual(['ErrorBoundary componentWillUnmount']);1381 });1382 it('picks the right boundary when handling unmounting errors', () => {1383 function renderInnerError(error) {1384 return <div>Caught an inner error: {error.message}.</div>;1385 }1386 function renderOuterError(error) {1387 return <div>Caught an outer error: {error.message}.</div>;1388 }1389 const container = document.createElement('div');1390 ReactDOM.render(1391 <ErrorBoundary1392 logName="OuterErrorBoundary"1393 renderError={renderOuterError}>1394 <ErrorBoundary1395 logName="InnerErrorBoundary"1396 renderError={renderInnerError}>1397 <BrokenComponentWillUnmount />1398 </ErrorBoundary>1399 </ErrorBoundary>,1400 container,1401 );1402 log.length = 0;1403 ReactDOM.render(1404 <ErrorBoundary1405 logName="OuterErrorBoundary"1406 renderError={renderOuterError}>1407 <ErrorBoundary1408 logName="InnerErrorBoundary"1409 renderError={renderInnerError}1410 />1411 </ErrorBoundary>,1412 container,1413 );1414 expect(container.textContent).toBe('Caught an inner error: Hello.');1415 expect(log).toEqual([1416 // Update outer boundary1417 'OuterErrorBoundary componentWillReceiveProps',1418 'OuterErrorBoundary componentWillUpdate',1419 'OuterErrorBoundary render success',1420 // Update inner boundary1421 'InnerErrorBoundary componentWillReceiveProps',1422 'InnerErrorBoundary componentWillUpdate',1423 'InnerErrorBoundary render success',1424 // Try unmounting child1425 'BrokenComponentWillUnmount componentWillUnmount [!]',1426 // Fiber proceeds with lifecycles despite errors1427 // Inner and outer boundaries have updated in this phase1428 'InnerErrorBoundary componentDidUpdate',1429 'OuterErrorBoundary componentDidUpdate',1430 // Now that commit phase is done, Fiber handles errors1431 // Only inner boundary receives the error:1432 'InnerErrorBoundary componentDidCatch',1433 'InnerErrorBoundary componentWillUpdate',1434 // Render an error now1435 'InnerErrorBoundary render error',1436 // In Fiber, this was a local update to the1437 // inner boundary so only its hook fires1438 'InnerErrorBoundary componentDidUpdate',1439 ]);1440 log.length = 0;1441 ReactDOM.unmountComponentAtNode(container);1442 expect(log).toEqual([1443 'OuterErrorBoundary componentWillUnmount',1444 'InnerErrorBoundary componentWillUnmount',1445 ]);1446 });1447 it('can recover from error state', () => {1448 const container = document.createElement('div');1449 ReactDOM.render(1450 <ErrorBoundary>1451 <BrokenRender />1452 </ErrorBoundary>,1453 container,1454 );1455 ReactDOM.render(1456 <ErrorBoundary>1457 <Normal />1458 </ErrorBoundary>,1459 container,1460 );1461 // Error boundary doesn't retry by itself:1462 expect(container.textContent).toBe('Caught an error: Hello.');1463 // Force the success path:1464 log.length = 0;1465 ReactDOM.render(1466 <ErrorBoundary forceRetry={true}>1467 <Normal />1468 </ErrorBoundary>,1469 container,1470 );1471 expect(container.textContent).not.toContain('Caught an error');1472 expect(log).toEqual([1473 'ErrorBoundary componentWillReceiveProps',1474 'ErrorBoundary componentWillUpdate',1475 'ErrorBoundary render success',1476 // Mount children:1477 'Normal constructor',1478 'Normal componentWillMount',1479 'Normal render',1480 // Finalize updates:1481 'Normal componentDidMount',1482 'ErrorBoundary componentDidUpdate',1483 ]);1484 log.length = 0;1485 ReactDOM.unmountComponentAtNode(container);1486 expect(log).toEqual([1487 'ErrorBoundary componentWillUnmount',1488 'Normal componentWillUnmount',1489 ]);1490 });1491 it('can update multiple times in error state', () => {1492 const container = document.createElement('div');1493 ReactDOM.render(1494 <ErrorBoundary>1495 <BrokenRender />1496 </ErrorBoundary>,1497 container,1498 );1499 expect(container.textContent).toBe('Caught an error: Hello.');1500 ReactDOM.render(1501 <ErrorBoundary>1502 <BrokenRender />1503 </ErrorBoundary>,1504 container,1505 );1506 expect(container.textContent).toBe('Caught an error: Hello.');1507 ReactDOM.render(<div>Other screen</div>, container);1508 expect(container.textContent).toBe('Other screen');1509 ReactDOM.unmountComponentAtNode(container);1510 });1511 it("doesn't get into inconsistent state during removals", () => {1512 const container = document.createElement('div');1513 ReactDOM.render(1514 <ErrorBoundary>1515 <Normal />1516 <BrokenComponentWillUnmount />1517 <Normal />1518 </ErrorBoundary>,1519 container,1520 );1521 ReactDOM.render(<ErrorBoundary />, container);1522 expect(container.textContent).toBe('Caught an error: Hello.');1523 log.length = 0;1524 ReactDOM.unmountComponentAtNode(container);1525 expect(log).toEqual(['ErrorBoundary componentWillUnmount']);1526 });1527 it("doesn't get into inconsistent state during additions", () => {1528 const container = document.createElement('div');1529 ReactDOM.render(<ErrorBoundary />, container);1530 ReactDOM.render(1531 <ErrorBoundary>1532 <Normal />1533 <BrokenRender />1534 <Normal />1535 </ErrorBoundary>,1536 container,1537 );1538 expect(container.textContent).toBe('Caught an error: Hello.');1539 log.length = 0;1540 ReactDOM.unmountComponentAtNode(container);1541 expect(log).toEqual(['ErrorBoundary componentWillUnmount']);1542 });1543 it("doesn't get into inconsistent state during reorders", () => {1544 function getAMixOfNormalAndBrokenRenderElements() {1545 const elements = [];1546 for (let i = 0; i < 100; i++) {1547 elements.push(<Normal key={i} />);1548 }1549 elements.push(<MaybeBrokenRender key={100} />);1550 let currentIndex = elements.length;1551 while (0 !== currentIndex) {1552 const randomIndex = Math.floor(Math.random() * currentIndex);1553 currentIndex -= 1;1554 const temporaryValue = elements[currentIndex];1555 elements[currentIndex] = elements[randomIndex];1556 elements[randomIndex] = temporaryValue;1557 }1558 return elements;1559 }1560 class MaybeBrokenRender extends React.Component {1561 render() {1562 if (fail) {1563 throw new Error('Hello');1564 }1565 return <div>{this.props.children}</div>;1566 }1567 }1568 let fail = false;1569 const container = document.createElement('div');1570 ReactDOM.render(1571 <ErrorBoundary>{getAMixOfNormalAndBrokenRenderElements()}</ErrorBoundary>,1572 container,1573 );1574 expect(container.textContent).not.toContain('Caught an error');1575 fail = true;1576 ReactDOM.render(1577 <ErrorBoundary>{getAMixOfNormalAndBrokenRenderElements()}</ErrorBoundary>,1578 container,1579 );1580 expect(container.textContent).toBe('Caught an error: Hello.');1581 log.length = 0;1582 ReactDOM.unmountComponentAtNode(container);1583 expect(log).toEqual(['ErrorBoundary componentWillUnmount']);1584 });1585 it('catches errors originating downstream', () => {1586 let fail = false;1587 class Stateful extends React.Component {1588 state = {shouldThrow: false};1589 render() {1590 if (fail) {1591 log.push('Stateful render [!]');1592 throw new Error('Hello');1593 }1594 return <div>{this.props.children}</div>;1595 }1596 }1597 let statefulInst;1598 const container = document.createElement('div');1599 ReactDOM.render(1600 <ErrorBoundary>1601 <Stateful ref={inst => (statefulInst = inst)} />1602 </ErrorBoundary>,1603 container,1604 );1605 log.length = 0;1606 expect(() => {1607 fail = true;1608 statefulInst.forceUpdate();1609 }).not.toThrow();1610 expect(log).toEqual([1611 'Stateful render [!]',1612 'ErrorBoundary componentDidCatch',1613 'ErrorBoundary componentWillUpdate',1614 'ErrorBoundary render error',1615 'ErrorBoundary componentDidUpdate',1616 ]);1617 log.length = 0;1618 ReactDOM.unmountComponentAtNode(container);1619 expect(log).toEqual(['ErrorBoundary componentWillUnmount']);1620 });1621 it('catches errors in componentDidMount', () => {1622 const container = document.createElement('div');1623 ReactDOM.render(1624 <ErrorBoundary>1625 <BrokenComponentWillUnmount>1626 <Normal />1627 </BrokenComponentWillUnmount>1628 <BrokenComponentDidMount />1629 <Normal logName="LastChild" />1630 </ErrorBoundary>,1631 container,1632 );1633 expect(log).toEqual([1634 'ErrorBoundary constructor',1635 'ErrorBoundary componentWillMount',1636 'ErrorBoundary render success',1637 'BrokenComponentWillUnmount constructor',1638 'BrokenComponentWillUnmount componentWillMount',1639 'BrokenComponentWillUnmount render',1640 'Normal constructor',1641 'Normal componentWillMount',1642 'Normal render',1643 'BrokenComponentDidMount constructor',1644 'BrokenComponentDidMount componentWillMount',1645 'BrokenComponentDidMount render',1646 'LastChild constructor',1647 'LastChild componentWillMount',1648 'LastChild render',1649 // Start flushing didMount queue1650 'Normal componentDidMount',1651 'BrokenComponentWillUnmount componentDidMount',1652 'BrokenComponentDidMount componentDidMount [!]',1653 // Continue despite the error1654 'LastChild componentDidMount',1655 'ErrorBoundary componentDidMount',1656 // Now we are ready to handle the error1657 // Safely unmount every child1658 'BrokenComponentWillUnmount componentWillUnmount [!]',1659 // Continue unmounting safely despite any errors1660 'Normal componentWillUnmount',1661 'BrokenComponentDidMount componentWillUnmount',1662 'LastChild componentWillUnmount',1663 // Handle the error1664 'ErrorBoundary componentDidCatch',1665 'ErrorBoundary componentWillUpdate',1666 // The willUnmount error should be captured and logged, too.1667 'ErrorBoundary componentDidUpdate',1668 'ErrorBoundary componentDidCatch',1669 'ErrorBoundary componentWillUpdate',1670 'ErrorBoundary render error',1671 // The update has finished1672 'ErrorBoundary componentDidUpdate',1673 ]);1674 log.length = 0;1675 ReactDOM.unmountComponentAtNode(container);1676 expect(log).toEqual(['ErrorBoundary componentWillUnmount']);1677 });1678 it('catches errors in componentDidUpdate', () => {1679 const container = document.createElement('div');1680 ReactDOM.render(1681 <ErrorBoundary>1682 <BrokenComponentDidUpdate />1683 </ErrorBoundary>,1684 container,1685 );1686 log.length = 0;1687 ReactDOM.render(1688 <ErrorBoundary>1689 <BrokenComponentDidUpdate />1690 </ErrorBoundary>,1691 container,1692 );1693 expect(log).toEqual([1694 'ErrorBoundary componentWillReceiveProps',1695 'ErrorBoundary componentWillUpdate',1696 'ErrorBoundary render success',1697 'BrokenComponentDidUpdate componentWillReceiveProps',1698 'BrokenComponentDidUpdate componentWillUpdate',1699 'BrokenComponentDidUpdate render',1700 // All lifecycles run1701 'BrokenComponentDidUpdate componentDidUpdate [!]',1702 'ErrorBoundary componentDidUpdate',1703 'BrokenComponentDidUpdate componentWillUnmount',1704 // Then, error is handled1705 'ErrorBoundary componentDidCatch',1706 'ErrorBoundary componentWillUpdate',1707 'ErrorBoundary render error',1708 'ErrorBoundary componentDidUpdate',1709 ]);1710 log.length = 0;1711 ReactDOM.unmountComponentAtNode(container);1712 expect(log).toEqual(['ErrorBoundary componentWillUnmount']);1713 });1714 it('propagates errors inside boundary during componentDidMount', () => {1715 const container = document.createElement('div');1716 ReactDOM.render(1717 <ErrorBoundary>1718 <BrokenComponentDidMountErrorBoundary1719 renderError={error => (1720 <div>We should never catch our own error: {error.message}.</div>1721 )}1722 />1723 </ErrorBoundary>,1724 container,1725 );1726 expect(container.firstChild.textContent).toBe('Caught an error: Hello.');1727 expect(log).toEqual([1728 'ErrorBoundary constructor',1729 'ErrorBoundary componentWillMount',1730 'ErrorBoundary render success',1731 'BrokenComponentDidMountErrorBoundary constructor',1732 'BrokenComponentDidMountErrorBoundary componentWillMount',1733 'BrokenComponentDidMountErrorBoundary render success',1734 'BrokenComponentDidMountErrorBoundary componentDidMount [!]',1735 // Fiber proceeds with the hooks1736 'ErrorBoundary componentDidMount',1737 'BrokenComponentDidMountErrorBoundary componentWillUnmount',1738 // The error propagates to the higher boundary1739 'ErrorBoundary componentDidCatch',1740 // Fiber retries from the root1741 'ErrorBoundary componentWillUpdate',1742 'ErrorBoundary render error',1743 'ErrorBoundary componentDidUpdate',1744 ]);1745 log.length = 0;1746 ReactDOM.unmountComponentAtNode(container);1747 expect(log).toEqual(['ErrorBoundary componentWillUnmount']);1748 });1749 it('calls componentDidCatch for each error that is captured', () => {1750 function renderUnmountError(error) {1751 return <div>Caught an unmounting error: {error.message}.</div>;1752 }1753 function renderUpdateError(error) {1754 return <div>Caught an updating error: {error.message}.</div>;1755 }1756 const container = document.createElement('div');1757 ReactDOM.render(1758 <ErrorBoundary logName="OuterErrorBoundary">1759 <ErrorBoundary1760 logName="InnerUnmountBoundary"1761 renderError={renderUnmountError}>1762 <BrokenComponentWillUnmount errorText="E1" />1763 <BrokenComponentWillUnmount errorText="E2" />1764 </ErrorBoundary>1765 <ErrorBoundary1766 logName="InnerUpdateBoundary"1767 renderError={renderUpdateError}>1768 <BrokenComponentDidUpdate errorText="E3" />1769 <BrokenComponentDidUpdate errorText="E4" />1770 </ErrorBoundary>1771 </ErrorBoundary>,1772 container,1773 );1774 log.length = 0;1775 ReactDOM.render(1776 <ErrorBoundary logName="OuterErrorBoundary">1777 <ErrorBoundary1778 logName="InnerUnmountBoundary"1779 renderError={renderUnmountError}1780 />1781 <ErrorBoundary1782 logName="InnerUpdateBoundary"1783 renderError={renderUpdateError}>1784 <BrokenComponentDidUpdate errorText="E3" />1785 <BrokenComponentDidUpdate errorText="E4" />1786 </ErrorBoundary>1787 </ErrorBoundary>,1788 container,1789 );1790 expect(container.firstChild.textContent).toBe(1791 'Caught an unmounting error: E2.' + 'Caught an updating error: E4.',1792 );1793 expect(log).toEqual([1794 // Begin update phase1795 'OuterErrorBoundary componentWillReceiveProps',1796 'OuterErrorBoundary componentWillUpdate',1797 'OuterErrorBoundary render success',1798 'InnerUnmountBoundary componentWillReceiveProps',1799 'InnerUnmountBoundary componentWillUpdate',1800 'InnerUnmountBoundary render success',1801 'InnerUpdateBoundary componentWillReceiveProps',1802 'InnerUpdateBoundary componentWillUpdate',1803 'InnerUpdateBoundary render success',1804 // First come the updates1805 'BrokenComponentDidUpdate componentWillReceiveProps',1806 'BrokenComponentDidUpdate componentWillUpdate',1807 'BrokenComponentDidUpdate render',1808 'BrokenComponentDidUpdate componentWillReceiveProps',1809 'BrokenComponentDidUpdate componentWillUpdate',1810 'BrokenComponentDidUpdate render',1811 // We're in commit phase now, deleting1812 'BrokenComponentWillUnmount componentWillUnmount [!]',1813 'BrokenComponentWillUnmount componentWillUnmount [!]',1814 // Continue despite errors, handle them after commit is done1815 'InnerUnmountBoundary componentDidUpdate',1816 // We're still in commit phase, now calling update lifecycles1817 'BrokenComponentDidUpdate componentDidUpdate [!]',1818 // Again, continue despite errors, we'll handle them later1819 'BrokenComponentDidUpdate componentDidUpdate [!]',1820 'InnerUpdateBoundary componentDidUpdate',1821 'OuterErrorBoundary componentDidUpdate',1822 // After the commit phase, attempt to recover from any errors that1823 // were captured1824 'BrokenComponentDidUpdate componentWillUnmount',1825 'BrokenComponentDidUpdate componentWillUnmount',1826 'InnerUnmountBoundary componentDidCatch',1827 'InnerUnmountBoundary componentDidCatch',1828 'InnerUpdateBoundary componentDidCatch',1829 'InnerUpdateBoundary componentDidCatch',1830 'InnerUnmountBoundary componentWillUpdate',1831 'InnerUnmountBoundary render error',1832 'InnerUpdateBoundary componentWillUpdate',1833 'InnerUpdateBoundary render error',1834 'InnerUnmountBoundary componentDidUpdate',1835 'InnerUpdateBoundary componentDidUpdate',1836 ]);1837 log.length = 0;1838 ReactDOM.unmountComponentAtNode(container);1839 expect(log).toEqual([1840 'OuterErrorBoundary componentWillUnmount',1841 'InnerUnmountBoundary componentWillUnmount',1842 'InnerUpdateBoundary componentWillUnmount',1843 ]);1844 });1845 it('discards a bad root if the root component fails', () => {1846 const X = null;1847 const Y = undefined;1848 let err1;1849 let err2;1850 try {1851 let container = document.createElement('div');1852 expect(() => ReactDOM.render(<X />, container)).toWarnDev(1853 'React.createElement: type is invalid -- expected a string ' +1854 '(for built-in components) or a class/function ' +1855 '(for composite components) but got: null.',1856 );1857 } catch (err) {1858 err1 = err;1859 }1860 try {1861 let container = document.createElement('div');1862 expect(() => ReactDOM.render(<Y />, container)).toWarnDev(1863 'React.createElement: type is invalid -- expected a string ' +1864 '(for built-in components) or a class/function ' +1865 '(for composite components) but got: undefined.',1866 );1867 } catch (err) {1868 err2 = err;1869 }1870 expect(err1.message).toMatch(/got: null/);1871 expect(err2.message).toMatch(/got: undefined/);1872 });1873 it('renders empty output if error boundary does not handle the error', () => {1874 const container = document.createElement('div');1875 expect(() => {1876 ReactDOM.render(1877 <div>1878 Sibling1879 <NoopErrorBoundary>1880 <BrokenRender />1881 </NoopErrorBoundary>1882 </div>,1883 container,1884 );1885 }).toWarnDev(1886 'ErrorBoundary: Error boundaries should implement getDerivedStateFromError()',1887 {withoutStack: true},1888 );1889 expect(container.firstChild.textContent).toBe('Sibling');1890 expect(log).toEqual([1891 'NoopErrorBoundary constructor',1892 'NoopErrorBoundary componentWillMount',1893 'NoopErrorBoundary render',1894 'BrokenRender constructor',1895 'BrokenRender componentWillMount',1896 'BrokenRender render [!]',1897 // In Fiber, noop error boundaries render null1898 'NoopErrorBoundary componentDidMount',1899 'NoopErrorBoundary componentDidCatch',1900 // Nothing happens.1901 ]);1902 log.length = 0;1903 ReactDOM.unmountComponentAtNode(container);1904 expect(log).toEqual(['NoopErrorBoundary componentWillUnmount']);1905 });1906 it('passes first error when two errors happen in commit', () => {1907 const errors = [];1908 let caughtError;1909 class Parent extends React.Component {1910 render() {1911 return <Child />;1912 }1913 componentDidMount() {1914 errors.push('parent sad');1915 throw new Error('parent sad');1916 }1917 }1918 class Child extends React.Component {1919 render() {1920 return <div />;1921 }1922 componentDidMount() {1923 errors.push('child sad');1924 throw new Error('child sad');1925 }1926 }1927 const container = document.createElement('div');1928 try {1929 // Here, we test the behavior where there is no error boundary and we1930 // delegate to the host root.1931 ReactDOM.render(<Parent />, container);1932 } catch (e) {1933 if (e.message !== 'parent sad' && e.message !== 'child sad') {1934 throw e;1935 }1936 caughtError = e;1937 }1938 expect(errors).toEqual(['child sad', 'parent sad']);1939 // Error should be the first thrown1940 expect(caughtError.message).toBe('child sad');1941 });1942 it('propagates uncaught error inside unbatched initial mount', () => {1943 function Foo() {1944 throw new Error('foo error');1945 }1946 const container = document.createElement('div');1947 expect(() => {1948 ReactDOM.unstable_batchedUpdates(() => {1949 ReactDOM.render(<Foo />, container);1950 });1951 }).toThrow('foo error');1952 });1953 it('handles errors that occur in before-mutation commit hook', () => {1954 const errors = [];1955 let caughtError;1956 class Parent extends React.Component {1957 getSnapshotBeforeUpdate() {1958 errors.push('parent sad');1959 throw new Error('parent sad');1960 }1961 componentDidUpdate() {}1962 render() {1963 return <Child {...this.props} />;1964 }1965 }1966 class Child extends React.Component {1967 getSnapshotBeforeUpdate() {1968 errors.push('child sad');1969 throw new Error('child sad');1970 }1971 componentDidUpdate() {}1972 render() {1973 return <div />;1974 }1975 }1976 const container = document.createElement('div');1977 ReactDOM.render(<Parent value={1} />, container);1978 try {1979 ReactDOM.render(<Parent value={2} />, container);1980 } catch (e) {1981 if (e.message !== 'parent sad' && e.message !== 'child sad') {1982 throw e;1983 }1984 caughtError = e;1985 }1986 expect(errors).toEqual(['child sad', 'parent sad']);1987 // Error should be the first thrown1988 expect(caughtError.message).toBe('child sad');1989 });...
ReactIncrementalErrorHandling-test.internal.js
Source:ReactIncrementalErrorHandling-test.internal.js
1/**2 * Copyright (c) Facebook, Inc. and its affiliates.3 *4 * This source code is licensed under the MIT license found in the5 * LICENSE file in the root directory of this source tree.6 *7 * @emails react-core8 * @jest-environment node9 */10'use strict';11let PropTypes;12let ReactFeatureFlags;13let React;14let ReactNoop;15describe('ReactIncrementalErrorHandling', () => {16 beforeEach(() => {17 jest.resetModules();18 ReactFeatureFlags = require('shared/ReactFeatureFlags');19 ReactFeatureFlags.debugRenderPhaseSideEffectsForStrictMode = false;20 ReactFeatureFlags.replayFailedUnitOfWorkWithInvokeGuardedCallback = false;21 PropTypes = require('prop-types');22 React = require('react');23 ReactNoop = require('react-noop-renderer');24 });25 function div(...children) {26 children = children.map(c => (typeof c === 'string' ? {text: c} : c));27 return {type: 'div', children, prop: undefined, hidden: false};28 }29 function span(prop) {30 return {type: 'span', children: [], prop, hidden: false};31 }32 function normalizeCodeLocInfo(str) {33 return str && str.replace(/\(at .+?:\d+\)/g, '(at **)');34 }35 it('recovers from errors asynchronously', () => {36 class ErrorBoundary extends React.Component {37 state = {error: null};38 static getDerivedStateFromError(error) {39 ReactNoop.yield('getDerivedStateFromError');40 return {error};41 }42 render() {43 if (this.state.error) {44 ReactNoop.yield('ErrorBoundary (catch)');45 return <ErrorMessage error={this.state.error} />;46 }47 ReactNoop.yield('ErrorBoundary (try)');48 return this.props.children;49 }50 }51 function ErrorMessage(props) {52 ReactNoop.yield('ErrorMessage');53 return <span prop={`Caught an error: ${props.error.message}`} />;54 }55 function Indirection(props) {56 ReactNoop.yield('Indirection');57 return props.children || null;58 }59 function BadRender() {60 ReactNoop.yield('throw');61 throw new Error('oops!');62 }63 ReactNoop.render(64 <ErrorBoundary>65 <Indirection>66 <Indirection>67 <Indirection>68 <BadRender />69 <Indirection />70 <Indirection />71 </Indirection>72 </Indirection>73 </Indirection>74 </ErrorBoundary>,75 );76 // Start rendering asynchronously77 ReactNoop.flushThrough([78 'ErrorBoundary (try)',79 'Indirection',80 'Indirection',81 'Indirection',82 // An error is thrown. React keeps rendering asynchronously.83 'throw',84 ]);85 // Still rendering async...86 ReactNoop.flushThrough(['Indirection']);87 ReactNoop.flushThrough([88 'Indirection',89 // Call getDerivedStateFromError and re-render the error boundary, this90 // time rendering an error message.91 'getDerivedStateFromError',92 'ErrorBoundary (catch)',93 'ErrorMessage',94 ]);95 // Since the error was thrown during an async render, React won't commit96 // the result yet.97 expect(ReactNoop.getChildren()).toEqual([]);98 // Instead, it will try rendering one more time, synchronously, in case that99 // happens to fix the error.100 expect(ReactNoop.flushNextYield()).toEqual([101 'ErrorBoundary (try)',102 'Indirection',103 'Indirection',104 'Indirection',105 // The error was thrown again. This time, React will actually commit106 // the result.107 'throw',108 'Indirection',109 'Indirection',110 'getDerivedStateFromError',111 'ErrorBoundary (catch)',112 'ErrorMessage',113 ]);114 expect(ReactNoop.getChildren()).toEqual([span('Caught an error: oops!')]);115 });116 it('recovers from errors asynchronously (legacy, no getDerivedStateFromError)', () => {117 class ErrorBoundary extends React.Component {118 state = {error: null};119 componentDidCatch(error) {120 ReactNoop.yield('componentDidCatch');121 this.setState({error});122 }123 render() {124 if (this.state.error) {125 ReactNoop.yield('ErrorBoundary (catch)');126 return <ErrorMessage error={this.state.error} />;127 }128 ReactNoop.yield('ErrorBoundary (try)');129 return this.props.children;130 }131 }132 function ErrorMessage(props) {133 ReactNoop.yield('ErrorMessage');134 return <span prop={`Caught an error: ${props.error.message}`} />;135 }136 function Indirection(props) {137 ReactNoop.yield('Indirection');138 return props.children || null;139 }140 function BadRender() {141 ReactNoop.yield('throw');142 throw new Error('oops!');143 }144 ReactNoop.render(145 <ErrorBoundary>146 <Indirection>147 <Indirection>148 <Indirection>149 <BadRender />150 <Indirection />151 <Indirection />152 </Indirection>153 </Indirection>154 </Indirection>155 </ErrorBoundary>,156 );157 // Start rendering asynchronously158 ReactNoop.flushThrough([159 'ErrorBoundary (try)',160 'Indirection',161 'Indirection',162 'Indirection',163 // An error is thrown. React keeps rendering asynchronously.164 'throw',165 ]);166 // Still rendering async...167 ReactNoop.flushThrough(['Indirection']);168 ReactNoop.flushThrough([169 'Indirection',170 // Now that the tree is complete, and there's no remaining work, React171 // reverts to sync mode to retry one more time before handling the error.172 'ErrorBoundary (try)',173 'Indirection',174 'Indirection',175 'Indirection',176 // The error was thrown again. Now we can handle it.177 'throw',178 'Indirection',179 'Indirection',180 'componentDidCatch',181 'ErrorBoundary (catch)',182 'ErrorMessage',183 ]);184 expect(ReactNoop.getChildren()).toEqual([span('Caught an error: oops!')]);185 });186 it("retries at a lower priority if there's additional pending work", () => {187 function App(props) {188 if (props.isBroken) {189 ReactNoop.yield('error');190 throw new Error('Oops!');191 }192 ReactNoop.yield('success');193 return <span prop="Everything is fine." />;194 }195 function onCommit() {196 ReactNoop.yield('commit');197 }198 ReactNoop.render(<App isBroken={true} />, onCommit);199 ReactNoop.expire(2000);200 ReactNoop.render(<App isBroken={false} />, onCommit);201 expect(ReactNoop.flush()).toEqual([202 // The first render fails. But because there's a lower priority pending203 // update, it doesn't throw.204 'error',205 // Now we retry at the lower priority. This time it succeeds.206 'success',207 // Nothing commits until the second update completes.208 'commit',209 'commit',210 ]);211 expect(ReactNoop.getChildren()).toEqual([span('Everything is fine.')]);212 });213 it('on error, retries at a lower priority using the expiration of higher priority', () => {214 class Parent extends React.Component {215 state = {hideChild: false};216 componentDidUpdate() {217 ReactNoop.yield('commit: ' + this.state.hideChild);218 }219 render() {220 if (this.state.hideChild) {221 ReactNoop.yield('(empty)');222 return <span prop="(empty)" />;223 }224 return <Child isBroken={this.props.childIsBroken} />;225 }226 }227 function Child(props) {228 if (props.isBroken) {229 ReactNoop.yield('Error!');230 throw new Error('Error!');231 }232 ReactNoop.yield('Child');233 return <span prop="Child" />;234 }235 // Initial mount236 const parent = React.createRef(null);237 ReactNoop.render(<Parent ref={parent} childIsBroken={false} />);238 expect(ReactNoop.flush()).toEqual(['Child']);239 expect(ReactNoop.getChildren()).toEqual([span('Child')]);240 // Schedule a low priority update to hide the child241 parent.current.setState({hideChild: true});242 // Before the low priority update is flushed, synchronously trigger an243 // error in the child.244 ReactNoop.flushSync(() => {245 ReactNoop.render(<Parent ref={parent} childIsBroken={true} />);246 });247 expect(ReactNoop.clearYields()).toEqual([248 // First the sync update triggers an error249 'Error!',250 // Because there's a pending low priority update, we restart at the251 // lower priority. This hides the children, suppressing the error.252 '(empty)',253 // Now the tree can commit.254 'commit: true',255 ]);256 expect(ReactNoop.getChildren()).toEqual([span('(empty)')]);257 });258 it('retries one more time before handling error', () => {259 let ops = [];260 function BadRender() {261 ops.push('BadRender');262 ReactNoop.yield('BadRender');263 throw new Error('oops');264 }265 function Sibling() {266 ops.push('Sibling');267 ReactNoop.yield('Sibling');268 return <span prop="Sibling" />;269 }270 function Parent() {271 ops.push('Parent');272 ReactNoop.yield('Parent');273 return (274 <React.Fragment>275 <BadRender />276 <Sibling />277 </React.Fragment>278 );279 }280 ReactNoop.render(<Parent />);281 // Render the bad component asynchronously282 ReactNoop.flushThrough(['Parent', 'BadRender']);283 // Finish the rest of the async work284 ReactNoop.flushThrough(['Sibling']);285 // Rendering two more units of work should be enough to trigger the retry286 // and synchronously throw an error.287 ops = [];288 expect(() => ReactNoop.flushUnitsOfWork(2)).toThrow('oops');289 expect(ops).toEqual(['Parent', 'BadRender', 'Sibling']);290 });291 // TODO: This is currently unobservable, but will be once we lift renderRoot292 // and commitRoot into the renderer.293 // it("does not retry synchronously if there's an update between complete and commit");294 it('calls componentDidCatch multiple times for multiple errors', () => {295 let id = 0;296 class BadMount extends React.Component {297 componentDidMount() {298 throw new Error(`Error ${++id}`);299 }300 render() {301 ReactNoop.yield('BadMount');302 return null;303 }304 }305 class ErrorBoundary extends React.Component {306 state = {errorCount: 0};307 componentDidCatch(error) {308 ReactNoop.yield(`componentDidCatch: ${error.message}`);309 this.setState(state => ({errorCount: state.errorCount + 1}));310 }311 render() {312 if (this.state.errorCount > 0) {313 return <span prop={`Number of errors: ${this.state.errorCount}`} />;314 }315 ReactNoop.yield('ErrorBoundary');316 return this.props.children;317 }318 }319 ReactNoop.render(320 <ErrorBoundary>321 <BadMount />322 <BadMount />323 <BadMount />324 </ErrorBoundary>,325 );326 expect(ReactNoop.flush()).toEqual([327 'ErrorBoundary',328 'BadMount',329 'BadMount',330 'BadMount',331 'componentDidCatch: Error 1',332 'componentDidCatch: Error 2',333 'componentDidCatch: Error 3',334 ]);335 expect(ReactNoop.getChildren()).toEqual([span('Number of errors: 3')]);336 });337 it('catches render error in a boundary during full deferred mounting', () => {338 class ErrorBoundary extends React.Component {339 state = {error: null};340 componentDidCatch(error) {341 this.setState({error});342 }343 render() {344 if (this.state.error) {345 return (346 <span prop={`Caught an error: ${this.state.error.message}.`} />347 );348 }349 return this.props.children;350 }351 }352 function BrokenRender(props) {353 throw new Error('Hello');354 }355 ReactNoop.render(356 <ErrorBoundary>357 <BrokenRender />358 </ErrorBoundary>,359 );360 ReactNoop.flushDeferredPri();361 expect(ReactNoop.getChildren()).toEqual([span('Caught an error: Hello.')]);362 });363 it('catches render error in a boundary during partial deferred mounting', () => {364 class ErrorBoundary extends React.Component {365 state = {error: null};366 componentDidCatch(error) {367 ReactNoop.yield('ErrorBoundary componentDidCatch');368 this.setState({error});369 }370 render() {371 if (this.state.error) {372 ReactNoop.yield('ErrorBoundary render error');373 return (374 <span prop={`Caught an error: ${this.state.error.message}.`} />375 );376 }377 ReactNoop.yield('ErrorBoundary render success');378 return this.props.children;379 }380 }381 function BrokenRender(props) {382 ReactNoop.yield('BrokenRender');383 throw new Error('Hello');384 }385 ReactNoop.render(386 <ErrorBoundary>387 <BrokenRender />388 </ErrorBoundary>,389 );390 ReactNoop.flushThrough(['ErrorBoundary render success']);391 expect(ReactNoop.getChildren()).toEqual([]);392 expect(ReactNoop.flush()).toEqual([393 'BrokenRender',394 // React retries one more time395 'ErrorBoundary render success',396 // Errored again on retry. Now handle it.397 'BrokenRender',398 'ErrorBoundary componentDidCatch',399 'ErrorBoundary render error',400 ]);401 expect(ReactNoop.getChildren()).toEqual([span('Caught an error: Hello.')]);402 });403 it('catches render error in a boundary during synchronous mounting', () => {404 const ops = [];405 class ErrorBoundary extends React.Component {406 state = {error: null};407 componentDidCatch(error) {408 ops.push('ErrorBoundary componentDidCatch');409 this.setState({error});410 }411 render() {412 if (this.state.error) {413 ops.push('ErrorBoundary render error');414 return (415 <span prop={`Caught an error: ${this.state.error.message}.`} />416 );417 }418 ops.push('ErrorBoundary render success');419 return this.props.children;420 }421 }422 function BrokenRender(props) {423 ops.push('BrokenRender');424 throw new Error('Hello');425 }426 ReactNoop.flushSync(() => {427 ReactNoop.render(428 <ErrorBoundary>429 <BrokenRender />430 </ErrorBoundary>,431 );432 });433 expect(ops).toEqual([434 'ErrorBoundary render success',435 'BrokenRender',436 // React doesn't retry because we're already rendering synchronously.437 'ErrorBoundary componentDidCatch',438 'ErrorBoundary render error',439 ]);440 expect(ReactNoop.getChildren()).toEqual([span('Caught an error: Hello.')]);441 });442 it('catches render error in a boundary during batched mounting', () => {443 const ops = [];444 class ErrorBoundary extends React.Component {445 state = {error: null};446 componentDidCatch(error) {447 ops.push('ErrorBoundary componentDidCatch');448 this.setState({error});449 }450 render() {451 if (this.state.error) {452 ops.push('ErrorBoundary render error');453 return (454 <span prop={`Caught an error: ${this.state.error.message}.`} />455 );456 }457 ops.push('ErrorBoundary render success');458 return this.props.children;459 }460 }461 function BrokenRender(props) {462 ops.push('BrokenRender');463 throw new Error('Hello');464 }465 ReactNoop.flushSync(() => {466 ReactNoop.render(<ErrorBoundary>Before the storm.</ErrorBoundary>);467 ReactNoop.render(468 <ErrorBoundary>469 <BrokenRender />470 </ErrorBoundary>,471 );472 });473 expect(ops).toEqual([474 'ErrorBoundary render success',475 'BrokenRender',476 // React doesn't retry because we're already rendering synchronously.477 'ErrorBoundary componentDidCatch',478 'ErrorBoundary render error',479 ]);480 expect(ReactNoop.getChildren()).toEqual([span('Caught an error: Hello.')]);481 });482 it('propagates an error from a noop error boundary during full deferred mounting', () => {483 const ops = [];484 class RethrowErrorBoundary extends React.Component {485 componentDidCatch(error) {486 ops.push('RethrowErrorBoundary componentDidCatch');487 throw error;488 }489 render() {490 ops.push('RethrowErrorBoundary render');491 return this.props.children;492 }493 }494 function BrokenRender() {495 ops.push('BrokenRender');496 throw new Error('Hello');497 }498 ReactNoop.render(499 <RethrowErrorBoundary>500 <BrokenRender />501 </RethrowErrorBoundary>,502 );503 expect(() => {504 ReactNoop.flush();505 }).toThrow('Hello');506 expect(ops).toEqual([507 'RethrowErrorBoundary render',508 'BrokenRender',509 // React retries one more time510 'RethrowErrorBoundary render',511 'BrokenRender',512 // Errored again on retry. Now handle it.513 'RethrowErrorBoundary componentDidCatch',514 ]);515 expect(ReactNoop.getChildren()).toEqual([]);516 });517 it('propagates an error from a noop error boundary during partial deferred mounting', () => {518 const ops = [];519 class RethrowErrorBoundary extends React.Component {520 componentDidCatch(error) {521 ops.push('RethrowErrorBoundary componentDidCatch');522 throw error;523 }524 render() {525 ops.push('RethrowErrorBoundary render');526 return this.props.children;527 }528 }529 function BrokenRender() {530 ops.push('BrokenRender');531 throw new Error('Hello');532 }533 ReactNoop.render(534 <RethrowErrorBoundary>535 <BrokenRender />536 </RethrowErrorBoundary>,537 );538 ReactNoop.flushDeferredPri(15);539 expect(ops).toEqual(['RethrowErrorBoundary render']);540 ops.length = 0;541 expect(() => {542 ReactNoop.flush();543 }).toThrow('Hello');544 expect(ops).toEqual([545 'BrokenRender',546 // React retries one more time547 'RethrowErrorBoundary render',548 'BrokenRender',549 // Errored again on retry. Now handle it.550 'RethrowErrorBoundary componentDidCatch',551 ]);552 expect(ReactNoop.getChildren()).toEqual([]);553 });554 it('propagates an error from a noop error boundary during synchronous mounting', () => {555 const ops = [];556 class RethrowErrorBoundary extends React.Component {557 componentDidCatch(error) {558 ops.push('RethrowErrorBoundary componentDidCatch');559 throw error;560 }561 render() {562 ops.push('RethrowErrorBoundary render');563 return this.props.children;564 }565 }566 function BrokenRender() {567 ops.push('BrokenRender');568 throw new Error('Hello');569 }570 expect(() => {571 ReactNoop.flushSync(() => {572 ReactNoop.render(573 <RethrowErrorBoundary>574 <BrokenRender />575 </RethrowErrorBoundary>,576 );577 });578 }).toThrow('Hello');579 expect(ops).toEqual([580 'RethrowErrorBoundary render',581 'BrokenRender',582 // React doesn't retry because we're already rendering synchronously.583 'RethrowErrorBoundary componentDidCatch',584 ]);585 expect(ReactNoop.getChildren()).toEqual([]);586 });587 it('propagates an error from a noop error boundary during batched mounting', () => {588 const ops = [];589 class RethrowErrorBoundary extends React.Component {590 componentDidCatch(error) {591 ops.push('RethrowErrorBoundary componentDidCatch');592 throw error;593 }594 render() {595 ops.push('RethrowErrorBoundary render');596 return this.props.children;597 }598 }599 function BrokenRender() {600 ops.push('BrokenRender');601 throw new Error('Hello');602 }603 expect(() => {604 ReactNoop.flushSync(() => {605 ReactNoop.render(606 <RethrowErrorBoundary>Before the storm.</RethrowErrorBoundary>,607 );608 ReactNoop.render(609 <RethrowErrorBoundary>610 <BrokenRender />611 </RethrowErrorBoundary>,612 );613 });614 }).toThrow('Hello');615 expect(ops).toEqual([616 'RethrowErrorBoundary render',617 'BrokenRender',618 // React doesn't retry because we're already rendering synchronously.619 'RethrowErrorBoundary componentDidCatch',620 ]);621 expect(ReactNoop.getChildren()).toEqual([]);622 });623 it('applies batched updates regardless despite errors in scheduling', () => {624 ReactNoop.render(<span prop="a:1" />);625 expect(() => {626 ReactNoop.batchedUpdates(() => {627 ReactNoop.render(<span prop="a:2" />);628 ReactNoop.render(<span prop="a:3" />);629 throw new Error('Hello');630 });631 }).toThrow('Hello');632 ReactNoop.flush();633 expect(ReactNoop.getChildren()).toEqual([span('a:3')]);634 });635 it('applies nested batched updates despite errors in scheduling', () => {636 ReactNoop.render(<span prop="a:1" />);637 expect(() => {638 ReactNoop.batchedUpdates(() => {639 ReactNoop.render(<span prop="a:2" />);640 ReactNoop.render(<span prop="a:3" />);641 ReactNoop.batchedUpdates(() => {642 ReactNoop.render(<span prop="a:4" />);643 ReactNoop.render(<span prop="a:5" />);644 throw new Error('Hello');645 });646 });647 }).toThrow('Hello');648 ReactNoop.flush();649 expect(ReactNoop.getChildren()).toEqual([span('a:5')]);650 });651 it('applies sync updates regardless despite errors in scheduling', () => {652 ReactNoop.render(<span prop="a:1" />);653 expect(() => {654 ReactNoop.flushSync(() => {655 ReactNoop.batchedUpdates(() => {656 ReactNoop.render(<span prop="a:2" />);657 ReactNoop.render(<span prop="a:3" />);658 throw new Error('Hello');659 });660 });661 }).toThrow('Hello');662 expect(ReactNoop.getChildren()).toEqual([span('a:3')]);663 });664 it('can schedule updates after uncaught error in render on mount', () => {665 let ops = [];666 function BrokenRender() {667 ops.push('BrokenRender');668 throw new Error('Hello');669 }670 function Foo() {671 ops.push('Foo');672 return null;673 }674 ReactNoop.render(<BrokenRender />);675 expect(() => {676 ReactNoop.flush();677 }).toThrow('Hello');678 expect(ops).toEqual([679 'BrokenRender',680 // React retries one more time681 'BrokenRender',682 // Errored again on retry683 ]);684 ops = [];685 ReactNoop.render(<Foo />);686 ReactNoop.flush();687 expect(ops).toEqual(['Foo']);688 });689 it('can schedule updates after uncaught error in render on update', () => {690 let ops = [];691 function BrokenRender(props) {692 ops.push('BrokenRender');693 if (props.throw) {694 throw new Error('Hello');695 }696 return null;697 }698 function Foo() {699 ops.push('Foo');700 return null;701 }702 ReactNoop.render(<BrokenRender throw={false} />);703 ReactNoop.flush();704 ops = [];705 expect(() => {706 ReactNoop.render(<BrokenRender throw={true} />);707 ReactNoop.flush();708 }).toThrow('Hello');709 expect(ops).toEqual([710 'BrokenRender',711 // React retries one more time712 'BrokenRender',713 // Errored again on retry714 ]);715 ops = [];716 ReactNoop.render(<Foo />);717 ReactNoop.flush();718 expect(ops).toEqual(['Foo']);719 });720 it('can schedule updates after uncaught error during umounting', () => {721 let ops = [];722 class BrokenComponentWillUnmount extends React.Component {723 render() {724 return <div />;725 }726 componentWillUnmount() {727 throw new Error('Hello');728 }729 }730 function Foo() {731 ops.push('Foo');732 return null;733 }734 ReactNoop.render(<BrokenComponentWillUnmount />);735 ReactNoop.flush();736 expect(() => {737 ReactNoop.render(<div />);738 ReactNoop.flush();739 }).toThrow('Hello');740 ops = [];741 ReactNoop.render(<Foo />);742 ReactNoop.flush();743 expect(ops).toEqual(['Foo']);744 });745 it('should not attempt to recover an unmounting error boundary', () => {746 class Parent extends React.Component {747 componentWillUnmount() {748 ReactNoop.yield('Parent componentWillUnmount');749 }750 render() {751 return <Boundary />;752 }753 }754 class Boundary extends React.Component {755 componentDidCatch(e) {756 ReactNoop.yield(`Caught error: ${e.message}`);757 }758 render() {759 return <ThrowsOnUnmount />;760 }761 }762 class ThrowsOnUnmount extends React.Component {763 componentWillUnmount() {764 ReactNoop.yield('ThrowsOnUnmount componentWillUnmount');765 throw new Error('unmount error');766 }767 render() {768 return null;769 }770 }771 ReactNoop.render(<Parent />);772 ReactNoop.flush();773 ReactNoop.render(null);774 expect(ReactNoop.flush()).toEqual([775 // Parent unmounts before the error is thrown.776 'Parent componentWillUnmount',777 'ThrowsOnUnmount componentWillUnmount',778 ]);779 ReactNoop.render(<Parent />);780 });781 it('can unmount an error boundary before it is handled', () => {782 let parent;783 class Parent extends React.Component {784 state = {step: 0};785 render() {786 parent = this;787 return this.state.step === 0 ? <Boundary /> : null;788 }789 }790 class Boundary extends React.Component {791 componentDidCatch() {}792 render() {793 return <Child />;794 }795 }796 class Child extends React.Component {797 componentDidUpdate() {798 parent.setState({step: 1});799 throw new Error('update error');800 }801 render() {802 return null;803 }804 }805 ReactNoop.render(<Parent />);806 ReactNoop.flush();807 ReactNoop.flushSync(() => {808 ReactNoop.render(<Parent />);809 });810 });811 it('continues work on other roots despite caught errors', () => {812 class ErrorBoundary extends React.Component {813 state = {error: null};814 componentDidCatch(error) {815 this.setState({error});816 }817 render() {818 if (this.state.error) {819 return (820 <span prop={`Caught an error: ${this.state.error.message}.`} />821 );822 }823 return this.props.children;824 }825 }826 function BrokenRender(props) {827 throw new Error('Hello');828 }829 ReactNoop.renderToRootWithID(830 <ErrorBoundary>831 <BrokenRender />832 </ErrorBoundary>,833 'a',834 );835 ReactNoop.renderToRootWithID(<span prop="b:1" />, 'b');836 ReactNoop.flush();837 expect(ReactNoop.getChildren('a')).toEqual([838 span('Caught an error: Hello.'),839 ]);840 expect(ReactNoop.getChildren('b')).toEqual([span('b:1')]);841 });842 it('continues work on other roots despite uncaught errors', () => {843 function BrokenRender(props) {844 throw new Error('Hello');845 }846 ReactNoop.renderToRootWithID(<BrokenRender />, 'a');847 expect(() => {848 ReactNoop.flush();849 }).toThrow('Hello');850 expect(ReactNoop.getChildren('a')).toEqual([]);851 ReactNoop.renderToRootWithID(<BrokenRender />, 'a');852 ReactNoop.renderToRootWithID(<span prop="b:2" />, 'b');853 expect(() => {854 ReactNoop.flush();855 }).toThrow('Hello');856 expect(ReactNoop.getChildren('a')).toEqual([]);857 expect(ReactNoop.getChildren('b')).toEqual([span('b:2')]);858 ReactNoop.renderToRootWithID(<span prop="a:3" />, 'a');859 ReactNoop.renderToRootWithID(<BrokenRender />, 'b');860 expect(() => {861 ReactNoop.flush();862 }).toThrow('Hello');863 expect(ReactNoop.getChildren('a')).toEqual([span('a:3')]);864 expect(ReactNoop.getChildren('b')).toEqual([]);865 ReactNoop.renderToRootWithID(<span prop="a:4" />, 'a');866 ReactNoop.renderToRootWithID(<BrokenRender />, 'b');867 ReactNoop.renderToRootWithID(<span prop="c:4" />, 'c');868 expect(() => {869 ReactNoop.flush();870 }).toThrow('Hello');871 expect(ReactNoop.getChildren('a')).toEqual([span('a:4')]);872 expect(ReactNoop.getChildren('b')).toEqual([]);873 expect(ReactNoop.getChildren('c')).toEqual([span('c:4')]);874 ReactNoop.renderToRootWithID(<span prop="a:5" />, 'a');875 ReactNoop.renderToRootWithID(<span prop="b:5" />, 'b');876 ReactNoop.renderToRootWithID(<span prop="c:5" />, 'c');877 ReactNoop.renderToRootWithID(<span prop="d:5" />, 'd');878 ReactNoop.renderToRootWithID(<BrokenRender />, 'e');879 expect(() => {880 ReactNoop.flush();881 }).toThrow('Hello');882 expect(ReactNoop.getChildren('a')).toEqual([span('a:5')]);883 expect(ReactNoop.getChildren('b')).toEqual([span('b:5')]);884 expect(ReactNoop.getChildren('c')).toEqual([span('c:5')]);885 expect(ReactNoop.getChildren('d')).toEqual([span('d:5')]);886 expect(ReactNoop.getChildren('e')).toEqual([]);887 ReactNoop.renderToRootWithID(<BrokenRender />, 'a');888 ReactNoop.renderToRootWithID(<span prop="b:6" />, 'b');889 ReactNoop.renderToRootWithID(<BrokenRender />, 'c');890 ReactNoop.renderToRootWithID(<span prop="d:6" />, 'd');891 ReactNoop.renderToRootWithID(<BrokenRender />, 'e');892 ReactNoop.renderToRootWithID(<span prop="f:6" />, 'f');893 expect(() => {894 ReactNoop.flush();895 }).toThrow('Hello');896 expect(ReactNoop.getChildren('a')).toEqual([]);897 expect(ReactNoop.getChildren('b')).toEqual([span('b:6')]);898 expect(ReactNoop.getChildren('c')).toEqual([]);899 expect(ReactNoop.getChildren('d')).toEqual([span('d:6')]);900 expect(ReactNoop.getChildren('e')).toEqual([]);901 expect(ReactNoop.getChildren('f')).toEqual([span('f:6')]);902 ReactNoop.unmountRootWithID('a');903 ReactNoop.unmountRootWithID('b');904 ReactNoop.unmountRootWithID('c');905 ReactNoop.unmountRootWithID('d');906 ReactNoop.unmountRootWithID('e');907 ReactNoop.unmountRootWithID('f');908 ReactNoop.flush();909 expect(ReactNoop.getChildren('a')).toEqual(null);910 expect(ReactNoop.getChildren('b')).toEqual(null);911 expect(ReactNoop.getChildren('c')).toEqual(null);912 expect(ReactNoop.getChildren('d')).toEqual(null);913 expect(ReactNoop.getChildren('e')).toEqual(null);914 expect(ReactNoop.getChildren('f')).toEqual(null);915 });916 it('unwinds the context stack correctly on error', () => {917 class Provider extends React.Component {918 static childContextTypes = {message: PropTypes.string};919 static contextTypes = {message: PropTypes.string};920 getChildContext() {921 return {922 message: (this.context.message || '') + this.props.message,923 };924 }925 render() {926 return this.props.children;927 }928 }929 function Connector(props, context) {930 return <span prop={context.message} />;931 }932 Connector.contextTypes = {933 message: PropTypes.string,934 };935 function BadRender() {936 throw new Error('render error');937 }938 class Boundary extends React.Component {939 state = {error: null};940 componentDidCatch(error) {941 this.setState({error});942 }943 render() {944 return (945 <Provider message="b">946 <Provider message="c">947 <Provider message="d">948 <Provider message="e">949 {!this.state.error && <BadRender />}950 </Provider>951 </Provider>952 </Provider>953 </Provider>954 );955 }956 }957 ReactNoop.render(958 <Provider message="a">959 <Boundary />960 <Connector />961 </Provider>,962 );963 expect(ReactNoop.flush).toWarnDev(964 'Legacy context API has been detected within a strict-mode tree: \n\n' +965 'Please update the following components: Connector, Provider',966 {withoutStack: true},967 );968 // If the context stack does not unwind, span will get 'abcde'969 expect(ReactNoop.getChildren()).toEqual([span('a')]);970 });971 it('catches reconciler errors in a boundary during mounting', () => {972 class ErrorBoundary extends React.Component {973 state = {error: null};974 componentDidCatch(error) {975 this.setState({error});976 }977 render() {978 if (this.state.error) {979 return <span prop={this.state.error.message} />;980 }981 return this.props.children;982 }983 }984 const InvalidType = undefined;985 function BrokenRender(props) {986 return <InvalidType />;987 }988 ReactNoop.render(989 <ErrorBoundary>990 <BrokenRender />991 </ErrorBoundary>,992 );993 expect(ReactNoop.flush).toWarnDev([994 'Warning: React.createElement: type is invalid -- expected a string',995 // React retries once on error996 'Warning: React.createElement: type is invalid -- expected a string',997 ]);998 expect(ReactNoop.getChildren()).toEqual([999 span(1000 'Element type is invalid: expected a string (for built-in components) or ' +1001 'a class/function (for composite components) but got: undefined.' +1002 (__DEV__1003 ? " You likely forgot to export your component from the file it's " +1004 'defined in, or you might have mixed up default and named imports.' +1005 '\n\nCheck the render method of `BrokenRender`.'1006 : ''),1007 ),1008 ]);1009 });1010 it('catches reconciler errors in a boundary during update', () => {1011 class ErrorBoundary extends React.Component {1012 state = {error: null};1013 componentDidCatch(error) {1014 this.setState({error});1015 }1016 render() {1017 if (this.state.error) {1018 return <span prop={this.state.error.message} />;1019 }1020 return this.props.children;1021 }1022 }1023 const InvalidType = undefined;1024 function BrokenRender(props) {1025 return props.fail ? <InvalidType /> : <span />;1026 }1027 ReactNoop.render(1028 <ErrorBoundary>1029 <BrokenRender fail={false} />1030 </ErrorBoundary>,1031 );1032 ReactNoop.flush();1033 ReactNoop.render(1034 <ErrorBoundary>1035 <BrokenRender fail={true} />1036 </ErrorBoundary>,1037 );1038 expect(ReactNoop.flush).toWarnDev([1039 'Warning: React.createElement: type is invalid -- expected a string',1040 // React retries once on error1041 'Warning: React.createElement: type is invalid -- expected a string',1042 ]);1043 expect(ReactNoop.getChildren()).toEqual([1044 span(1045 'Element type is invalid: expected a string (for built-in components) or ' +1046 'a class/function (for composite components) but got: undefined.' +1047 (__DEV__1048 ? " You likely forgot to export your component from the file it's " +1049 'defined in, or you might have mixed up default and named imports.' +1050 '\n\nCheck the render method of `BrokenRender`.'1051 : ''),1052 ),1053 ]);1054 });1055 it('recovers from uncaught reconciler errors', () => {1056 const InvalidType = undefined;1057 expect(() => ReactNoop.render(<InvalidType />)).toWarnDev(1058 'Warning: React.createElement: type is invalid -- expected a string',1059 {withoutStack: true},1060 );1061 expect(ReactNoop.flush).toThrowError(1062 'Element type is invalid: expected a string (for built-in components) or ' +1063 'a class/function (for composite components) but got: undefined.' +1064 (__DEV__1065 ? " You likely forgot to export your component from the file it's " +1066 'defined in, or you might have mixed up default and named imports.'1067 : ''),1068 );1069 ReactNoop.render(<span prop="hi" />);1070 ReactNoop.flush();1071 expect(ReactNoop.getChildren()).toEqual([span('hi')]);1072 });1073 it('unmounts components with uncaught errors', () => {1074 const ops = [];1075 let inst;1076 class BrokenRenderAndUnmount extends React.Component {1077 state = {fail: false};1078 componentWillUnmount() {1079 ops.push('BrokenRenderAndUnmount componentWillUnmount');1080 }1081 render() {1082 inst = this;1083 if (this.state.fail) {1084 throw new Error('Hello.');1085 }1086 return null;1087 }1088 }1089 class Parent extends React.Component {1090 componentWillUnmount() {1091 ops.push('Parent componentWillUnmount [!]');1092 throw new Error('One does not simply unmount me.');1093 }1094 render() {1095 return this.props.children;1096 }1097 }1098 ReactNoop.render(1099 <Parent>1100 <Parent>1101 <BrokenRenderAndUnmount />1102 </Parent>1103 </Parent>,1104 );1105 ReactNoop.flush();1106 inst.setState({fail: true});1107 expect(() => {1108 ReactNoop.flush();1109 }).toThrowError('Hello.');1110 expect(ops).toEqual([1111 // Attempt to clean up.1112 // Errors in parents shouldn't stop children from unmounting.1113 'Parent componentWillUnmount [!]',1114 'Parent componentWillUnmount [!]',1115 'BrokenRenderAndUnmount componentWillUnmount',1116 ]);1117 expect(ReactNoop.getChildren()).toEqual([]);1118 });1119 it('does not interrupt unmounting if detaching a ref throws', () => {1120 let ops = [];1121 class Bar extends React.Component {1122 componentWillUnmount() {1123 ops.push('Bar unmount');1124 }1125 render() {1126 return <span prop="Bar" />;1127 }1128 }1129 function barRef(inst) {1130 if (inst === null) {1131 ops.push('barRef detach');1132 throw new Error('Detach error');1133 }1134 ops.push('barRef attach');1135 }1136 function Foo(props) {1137 return <div>{props.hide ? null : <Bar ref={barRef} />}</div>;1138 }1139 ReactNoop.render(<Foo />);1140 ReactNoop.flush();1141 expect(ops).toEqual(['barRef attach']);1142 expect(ReactNoop.getChildren()).toEqual([div(span('Bar'))]);1143 ops = [];1144 // Unmount1145 ReactNoop.render(<Foo hide={true} />);1146 expect(() => ReactNoop.flush()).toThrow('Detach error');1147 expect(ops).toEqual([1148 'barRef detach',1149 // Bar should unmount even though its ref threw an error while detaching1150 'Bar unmount',1151 ]);1152 // Because there was an error, entire tree should unmount1153 expect(ReactNoop.getChildren()).toEqual([]);1154 });1155 it('handles error thrown by host config while working on failed root', () => {1156 ReactNoop.render(<errorInBeginPhase />);1157 expect(() => ReactNoop.flush()).toThrow('Error in host config.');1158 });1159 it('handles error thrown by top-level callback', () => {1160 ReactNoop.render(<div />, () => {1161 throw new Error('Error!');1162 });1163 expect(() => ReactNoop.flush()).toThrow('Error!');1164 });1165 it('error boundaries capture non-errors', () => {1166 spyOnProd(console, 'error');1167 spyOnDev(console, 'error');1168 let ops = [];1169 class ErrorBoundary extends React.Component {1170 state = {error: null};1171 componentDidCatch(error) {1172 // Should not be called1173 ops.push('componentDidCatch');1174 this.setState({error});1175 }1176 render() {1177 if (this.state.error) {1178 ops.push('ErrorBoundary (catch)');1179 return (1180 <span1181 prop={`Caught an error: ${this.state.error.nonStandardMessage}`}1182 />1183 );1184 }1185 ops.push('ErrorBoundary (try)');1186 return this.props.children;1187 }1188 }1189 function Indirection(props) {1190 ops.push('Indirection');1191 return props.children;1192 }1193 const notAnError = {nonStandardMessage: 'oops'};1194 function BadRender() {1195 ops.push('BadRender');1196 throw notAnError;1197 }1198 ReactNoop.render(1199 <ErrorBoundary>1200 <Indirection>1201 <BadRender />1202 </Indirection>1203 </ErrorBoundary>,1204 );1205 ReactNoop.flush();1206 expect(ops).toEqual([1207 'ErrorBoundary (try)',1208 'Indirection',1209 'BadRender',1210 // React retries one more time1211 'ErrorBoundary (try)',1212 'Indirection',1213 'BadRender',1214 // Errored again on retry. Now handle it.1215 'componentDidCatch',1216 'ErrorBoundary (catch)',1217 ]);1218 expect(ReactNoop.getChildren()).toEqual([span('Caught an error: oops')]);1219 if (__DEV__) {1220 expect(console.error).toHaveBeenCalledTimes(1);1221 expect(console.error.calls.argsFor(0)[0]).toContain(1222 'The above error occurred in the <BadRender> component:',1223 );1224 } else {1225 expect(console.error).toHaveBeenCalledTimes(1);1226 expect(console.error.calls.argsFor(0)[0]).toBe(notAnError);1227 }1228 });1229 // TODO: Error boundary does not catch promises1230 it('continues working on siblings of a component that throws', () => {1231 class ErrorBoundary extends React.Component {1232 state = {error: null};1233 componentDidCatch(error) {1234 ReactNoop.yield('componentDidCatch');1235 this.setState({error});1236 }1237 render() {1238 if (this.state.error) {1239 ReactNoop.yield('ErrorBoundary (catch)');1240 return <ErrorMessage error={this.state.error} />;1241 }1242 ReactNoop.yield('ErrorBoundary (try)');1243 return this.props.children;1244 }1245 }1246 function ErrorMessage(props) {1247 ReactNoop.yield('ErrorMessage');1248 return <span prop={`Caught an error: ${props.error.message}`} />;1249 }1250 function BadRenderSibling(props) {1251 ReactNoop.yield('BadRenderSibling');1252 return null;1253 }1254 function BadRender() {1255 ReactNoop.yield('throw');1256 throw new Error('oops!');1257 }1258 ReactNoop.render(1259 <ErrorBoundary>1260 <BadRender />1261 <BadRenderSibling />1262 <BadRenderSibling />1263 </ErrorBoundary>,1264 );1265 expect(ReactNoop.flush()).toEqual([1266 'ErrorBoundary (try)',1267 'throw',1268 // Continue rendering siblings after BadRender throws1269 'BadRenderSibling',1270 'BadRenderSibling',1271 // React retries one more time1272 'ErrorBoundary (try)',1273 'throw',1274 'BadRenderSibling',1275 'BadRenderSibling',1276 // Errored again on retry. Now handle it.1277 'componentDidCatch',1278 'ErrorBoundary (catch)',1279 'ErrorMessage',1280 ]);1281 expect(ReactNoop.getChildren()).toEqual([span('Caught an error: oops!')]);1282 });1283 it('calls the correct lifecycles on the error boundary after catching an error (mixed)', () => {1284 // This test seems a bit contrived, but it's based on an actual regression1285 // where we checked for the existence of didUpdate instead of didMount, and1286 // didMount was not defined.1287 function BadRender() {1288 ReactNoop.yield('throw');1289 throw new Error('oops!');1290 }1291 class Parent extends React.Component {1292 state = {error: null, other: false};1293 componentDidCatch(error) {1294 ReactNoop.yield('did catch');1295 this.setState({error});1296 }1297 componentDidUpdate() {1298 ReactNoop.yield('did update');1299 }1300 render() {1301 if (this.state.error) {1302 ReactNoop.yield('render error message');1303 return <span prop={`Caught an error: ${this.state.error.message}`} />;1304 }1305 ReactNoop.yield('render');1306 return <BadRender />;1307 }1308 }1309 ReactNoop.render(<Parent step={1} />);1310 ReactNoop.flushThrough([1311 'render',1312 'throw',1313 'render',1314 'throw',1315 'did catch',1316 'render error message',1317 'did update',1318 ]);1319 expect(ReactNoop.getChildren()).toEqual([span('Caught an error: oops!')]);1320 });1321 it('provides component stack to the error boundary with componentDidCatch', () => {1322 class ErrorBoundary extends React.Component {1323 state = {error: null, errorInfo: null};1324 componentDidCatch(error, errorInfo) {1325 this.setState({error, errorInfo});1326 }1327 render() {1328 if (this.state.errorInfo) {1329 ReactNoop.yield('render error message');1330 return (1331 <span1332 prop={`Caught an error:${normalizeCodeLocInfo(1333 this.state.errorInfo.componentStack,1334 )}.`}1335 />1336 );1337 }1338 return this.props.children;1339 }1340 }1341 function BrokenRender(props) {1342 throw new Error('Hello');1343 }1344 ReactNoop.render(1345 <ErrorBoundary>1346 <BrokenRender />1347 </ErrorBoundary>,1348 );1349 ReactNoop.flushDeferredPri();1350 expect(ReactNoop.getChildren()).toEqual([1351 span(1352 'Caught an error:\n' +1353 (__DEV__1354 ? ' in BrokenRender (at **)\n'1355 : ' in BrokenRender\n') +1356 (__DEV__ ? ' in ErrorBoundary (at **).' : ' in ErrorBoundary.'),1357 ),1358 ]);1359 });1360 it('does not provide component stack to the error boundary with getDerivedStateFromError', () => {1361 class ErrorBoundary extends React.Component {1362 state = {error: null};1363 static getDerivedStateFromError(error, errorInfo) {1364 expect(errorInfo).toBeUndefined();1365 return {error};1366 }1367 render() {1368 if (this.state.error) {1369 return <span prop={`Caught an error: ${this.state.error.message}`} />;1370 }1371 return this.props.children;1372 }1373 }1374 function BrokenRender(props) {1375 throw new Error('Hello');1376 }1377 ReactNoop.render(1378 <ErrorBoundary>1379 <BrokenRender />1380 </ErrorBoundary>,1381 );1382 ReactNoop.flushDeferredPri();1383 expect(ReactNoop.getChildren()).toEqual([span('Caught an error: Hello')]);1384 });1385 it('handles error thrown inside getDerivedStateFromProps of a module-style context provider', () => {1386 function Provider() {1387 return {1388 getChildContext() {1389 return {foo: 'bar'};1390 },1391 render() {1392 return 'Hi';1393 },1394 };1395 }1396 Provider.childContextTypes = {1397 x: () => {},1398 };1399 Provider.getDerivedStateFromProps = () => {1400 throw new Error('Oops!');1401 };1402 ReactNoop.render(<Provider />);1403 expect(() => {1404 expect(() => ReactNoop.flush()).toThrow('Oops!');1405 }).toWarnDev(1406 'Legacy context API has been detected within a strict-mode tree: \n\n' +1407 'Please update the following components: Provider',1408 {withoutStack: true},1409 );1410 });...
error_test.js
Source:error_test.js
1// Licensed to the Software Freedom Conservancy (SFC) under one2// or more contributor license agreements. See the NOTICE file3// distributed with this work for additional information4// regarding copyright ownership. The SFC licenses this file5// to you under the Apache License, Version 2.0 (the6// "License"); you may not use this file except in compliance7// with the License. You may obtain a copy of the License at8//9// http://www.apache.org/licenses/LICENSE-2.010//11// Unless required by applicable law or agreed to in writing,12// software distributed under the License is distributed on an13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY14// KIND, either express or implied. See the License for the15// specific language governing permissions and limitations16// under the License.17'use strict';18describe('error', function() {19 let assert = require('assert');20 let error = require('../../lib/error');21 describe('checkResponse', function() {22 it('defaults to WebDriverError if type is unrecognized', function() {23 assert.throws(24 () => error.checkResponse({error: 'foo', message: 'hi there'}),25 (e) => {26 assert.equal(e.constructor, error.WebDriverError);27 return true;28 });29 });30 it('does not throw if error property is not a string', function() {31 let resp = {error: 123, message: 'abc123'};32 let out = error.checkResponse(resp);33 assert.strictEqual(out, resp);34 });35 test('unknown error', error.WebDriverError);36 test('element not interactable', error.ElementNotInteractableError);37 test('element not selectable', error.ElementNotSelectableError);38 test('element not visible', error.ElementNotVisibleError);39 test('invalid argument', error.InvalidArgumentError);40 test('invalid cookie domain', error.InvalidCookieDomainError);41 test('invalid element coordinates', error.InvalidElementCoordinatesError);42 test('invalid element state', error.InvalidElementStateError);43 test('invalid selector', error.InvalidSelectorError);44 test('invalid session id', error.NoSuchSessionError);45 test('javascript error', error.JavascriptError);46 test('move target out of bounds', error.MoveTargetOutOfBoundsError);47 test('no such alert', error.NoSuchAlertError);48 test('no such element', error.NoSuchElementError);49 test('no such frame', error.NoSuchFrameError);50 test('no such window', error.NoSuchWindowError);51 test('script timeout', error.ScriptTimeoutError);52 test('session not created', error.SessionNotCreatedError);53 test('stale element reference', error.StaleElementReferenceError);54 test('timeout', error.TimeoutError);55 test('unable to set cookie', error.UnableToSetCookieError);56 test('unable to capture screen', error.UnableToCaptureScreenError);57 test('unexpected alert open', error.UnexpectedAlertOpenError);58 test('unknown command', error.UnknownCommandError);59 test('unknown method', error.UnknownMethodError);60 test('unsupported operation', error.UnsupportedOperationError);61 function test(status, expectedType) {62 it(`"${status}" => ${expectedType.name}`, function() {63 assert.throws(64 () => error.checkResponse({error: status, message: 'oops'}),65 (e) => {66 assert.equal(expectedType, e.constructor);67 assert.equal(e.message, 'oops');68 return true;69 });70 });71 }72 });73 describe('encodeError', function() {74 describe('defaults to an unknown error', function() {75 it('for a generic error value', function() {76 runTest('hi', 'unknown error', 'hi');77 runTest(1, 'unknown error', '1');78 runTest({}, 'unknown error', '[object Object]');79 });80 it('for a generic Error object', function() {81 runTest(Error('oops'), 'unknown error', 'oops');82 runTest(TypeError('bad value'), 'unknown error', 'bad value');83 });84 });85 test(error.WebDriverError, 'unknown error');86 test(error.ElementNotSelectableError, 'element not selectable');87 test(error.ElementNotVisibleError, 'element not visible');88 test(error.InvalidArgumentError, 'invalid argument');89 test(error.InvalidCookieDomainError, 'invalid cookie domain');90 test(error.InvalidElementStateError, 'invalid element state');91 test(error.InvalidSelectorError, 'invalid selector');92 test(error.NoSuchSessionError, 'invalid session id');93 test(error.JavascriptError, 'javascript error');94 test(error.MoveTargetOutOfBoundsError, 'move target out of bounds');95 test(error.NoSuchAlertError, 'no such alert');96 test(error.NoSuchElementError, 'no such element');97 test(error.NoSuchFrameError, 'no such frame');98 test(error.NoSuchWindowError, 'no such window');99 test(error.ScriptTimeoutError, 'script timeout');100 test(error.SessionNotCreatedError, 'session not created');101 test(error.StaleElementReferenceError, 'stale element reference');102 test(error.TimeoutError, 'timeout');103 test(error.UnableToSetCookieError, 'unable to set cookie');104 test(error.UnableToCaptureScreenError, 'unable to capture screen');105 test(error.UnexpectedAlertOpenError, 'unexpected alert open');106 test(error.UnknownCommandError, 'unknown command');107 test(error.UnknownMethodError, 'unknown method');108 test(error.UnsupportedOperationError, 'unsupported operation');109 function test(ctor, code) {110 it(`${ctor.name} => "${code}"`, () => {111 runTest(new ctor('oops'), code, 'oops');112 });113 }114 function runTest(err, code, message) {115 let obj = error.encodeError(err);116 assert.strictEqual(obj['error'], code);117 assert.strictEqual(obj['message'], message);118 }119 });120 describe('throwDecodedError', function() {121 it('defaults to WebDriverError if type is unrecognized', function() {122 assert.throws(123 () => error.throwDecodedError({error: 'foo', message: 'hi there'}),124 (e) => {125 assert.equal(e.constructor, error.WebDriverError);126 return true;127 });128 });129 it('throws generic error if encoded data is not valid', function() {130 assert.throws(131 () => error.throwDecodedError({error: 123, message: 'abc123'}),132 (e) => {133 assert.strictEqual(e.constructor, error.WebDriverError);134 return true;135 });136 assert.throws(137 () => error.throwDecodedError('null'),138 (e) => {139 assert.strictEqual(e.constructor, error.WebDriverError);140 return true;141 });142 assert.throws(143 () => error.throwDecodedError(''),144 (e) => {145 assert.strictEqual(e.constructor, error.WebDriverError);146 return true;147 });148 });149 test('unknown error', error.WebDriverError);150 test('element not selectable', error.ElementNotSelectableError);151 test('element not visible', error.ElementNotVisibleError);152 test('invalid argument', error.InvalidArgumentError);153 test('invalid cookie domain', error.InvalidCookieDomainError);154 test('invalid element coordinates', error.InvalidElementCoordinatesError);155 test('invalid element state', error.InvalidElementStateError);156 test('invalid selector', error.InvalidSelectorError);157 test('invalid session id', error.NoSuchSessionError);158 test('javascript error', error.JavascriptError);159 test('move target out of bounds', error.MoveTargetOutOfBoundsError);160 test('no such alert', error.NoSuchAlertError);161 test('no such element', error.NoSuchElementError);162 test('no such frame', error.NoSuchFrameError);163 test('no such window', error.NoSuchWindowError);164 test('script timeout', error.ScriptTimeoutError);165 test('session not created', error.SessionNotCreatedError);166 test('stale element reference', error.StaleElementReferenceError);167 test('timeout', error.TimeoutError);168 test('unable to set cookie', error.UnableToSetCookieError);169 test('unable to capture screen', error.UnableToCaptureScreenError);170 test('unexpected alert open', error.UnexpectedAlertOpenError);171 test('unknown command', error.UnknownCommandError);172 test('unknown method', error.UnknownMethodError);173 test('unsupported operation', error.UnsupportedOperationError);174 it('leaves remoteStacktrace empty if not in encoding', function() {175 assert.throws(176 () => error.throwDecodedError({177 error: 'session not created',178 message: 'oops'179 }),180 (e) => {181 assert.strictEqual(e.constructor, error.SessionNotCreatedError);182 assert.strictEqual(e.message, 'oops');183 assert.strictEqual(e.remoteStacktrace, '');184 return true;185 });186 });187 function test(status, expectedType) {188 it(`"${status}" => ${expectedType.name}`, function() {189 assert.throws(190 () => error.throwDecodedError({191 error: status,192 message: 'oops',193 stacktrace: 'some-stacktrace'194 }),195 (e) => {196 assert.strictEqual(e.constructor, expectedType);197 assert.strictEqual(e.message, 'oops');198 assert.strictEqual(e.remoteStacktrace, 'some-stacktrace');199 return true;200 });201 });202 }203 });204 describe('checkLegacyResponse', function() {205 it('does not throw for success', function() {206 let resp = {status: error.ErrorCode.SUCCESS};207 assert.strictEqual(resp, error.checkLegacyResponse(resp));208 });209 test('NO_SUCH_ELEMENT', error.NoSuchElementError);210 test('NO_SUCH_FRAME', error.NoSuchFrameError);211 test('UNKNOWN_COMMAND', error.UnsupportedOperationError);212 test('UNSUPPORTED_OPERATION', error.UnsupportedOperationError);213 test('STALE_ELEMENT_REFERENCE', error.StaleElementReferenceError);214 test('ELEMENT_NOT_VISIBLE', error.ElementNotVisibleError);215 test('INVALID_ELEMENT_STATE', error.InvalidElementStateError);216 test('UNKNOWN_ERROR', error.WebDriverError);217 test('ELEMENT_NOT_SELECTABLE', error.ElementNotSelectableError);218 test('JAVASCRIPT_ERROR', error.JavascriptError);219 test('XPATH_LOOKUP_ERROR', error.InvalidSelectorError);220 test('TIMEOUT', error.TimeoutError);221 test('NO_SUCH_WINDOW', error.NoSuchWindowError);222 test('INVALID_COOKIE_DOMAIN', error.InvalidCookieDomainError);223 test('UNABLE_TO_SET_COOKIE', error.UnableToSetCookieError);224 test('UNEXPECTED_ALERT_OPEN', error.UnexpectedAlertOpenError);225 test('NO_SUCH_ALERT', error.NoSuchAlertError);226 test('SCRIPT_TIMEOUT', error.ScriptTimeoutError);227 test('INVALID_ELEMENT_COORDINATES', error.InvalidElementCoordinatesError);228 test('INVALID_SELECTOR_ERROR', error.InvalidSelectorError);229 test('SESSION_NOT_CREATED', error.SessionNotCreatedError);230 test('MOVE_TARGET_OUT_OF_BOUNDS', error.MoveTargetOutOfBoundsError);231 test('INVALID_XPATH_SELECTOR', error.InvalidSelectorError);232 test('INVALID_XPATH_SELECTOR_RETURN_TYPE', error.InvalidSelectorError);233 test('METHOD_NOT_ALLOWED', error.UnsupportedOperationError);234 describe('UnexpectedAlertOpenError', function() {235 it('includes alert text from the response object', function() {236 let response = {237 status: error.ErrorCode.UNEXPECTED_ALERT_OPEN,238 value: {239 message: 'hi',240 alert: {text: 'alert text here'}241 }242 };243 assert.throws(244 () => error.checkLegacyResponse(response),245 (e) => {246 assert.equal(error.UnexpectedAlertOpenError, e.constructor);247 assert.equal(e.message, 'hi');248 assert.equal(e.getAlertText(), 'alert text here');249 return true;250 });251 });252 it('uses an empty string if alert text omitted', function() {253 let response = {254 status: error.ErrorCode.UNEXPECTED_ALERT_OPEN,255 value: {256 message: 'hi'257 }258 };259 assert.throws(260 () => error.checkLegacyResponse(response),261 (e) => {262 assert.equal(error.UnexpectedAlertOpenError, e.constructor);263 assert.equal(e.message, 'hi');264 assert.equal(e.getAlertText(), '');265 return true;266 });267 });268 });269 function test(codeKey, expectedType) {270 it(`${codeKey} => ${expectedType.name}`, function() {271 let code = error.ErrorCode[codeKey];272 let resp = {status: code, value: {message: 'hi'}};273 assert.throws(274 () => error.checkLegacyResponse(resp),275 (e) => {276 assert.equal(expectedType, e.constructor);277 assert.equal(e.message, 'hi');278 return true;279 });280 });281 }282 });...
error.js
Source:error.js
1// Copyright 2011 The Closure Library Authors. All Rights Reserved.2//3// Licensed under the Apache License, Version 2.0 (the "License");4// you may not use this file except in compliance with the License.5// You may obtain a copy of the License at6//7// http://www.apache.org/licenses/LICENSE-2.08//9// Unless required by applicable law or agreed to in writing, software10// distributed under the License is distributed on an "AS-IS" BASIS,11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.12// See the License for the specific language governing permissions and13// limitations under the License.14/**15 * @fileoverview Error classes for the IndexedDB wrapper.16 *17 */18goog.provide('goog.db.Error');19goog.provide('goog.db.Error.ErrorCode');20goog.provide('goog.db.Error.ErrorName');21goog.provide('goog.db.Error.VersionChangeBlockedError');22goog.require('goog.debug.Error');23/**24 * A database error. Since the stack trace can be unhelpful in an asynchronous25 * context, the error provides a message about where it was produced.26 *27 * @param {number|!DOMError} error The DOMError instance returned by the28 * browser for Chrome22+, or an error code for previous versions.29 * @param {string} context A description of where the error occured.30 * @param {string=} opt_message Additional message.31 * @constructor32 * @extends {goog.debug.Error}33 */34goog.db.Error = function(error, context, opt_message) {35 var errorCode = null;36 var internalError = null;37 if (goog.isNumber(error)) {38 errorCode = error;39 internalError = {name: goog.db.Error.getName(errorCode)};40 } else {41 internalError = error;42 errorCode = goog.db.Error.getCode(error.name);43 }44 /**45 * The code for this error.46 *47 * @type {number}48 */49 this.code = errorCode;50 /**51 * The DOMException as returned by the browser.52 *53 * @type {!DOMError}54 * @private55 */56 this.error_ = /** @type {!DOMError} */ (internalError);57 var msg = 'Error ' + context + ': ' + this.getName();58 if (opt_message) {59 msg += ', ' + opt_message;60 }61 goog.base(this, msg);62};63goog.inherits(goog.db.Error, goog.debug.Error);64/**65 * @return {string} The name of the error.66 */67goog.db.Error.prototype.getName = function() {68 return this.error_.name;69};70/**71 * A specific kind of database error. If a Version Change is unable to proceed72 * due to other open database connections, it will block and this error will be73 * thrown.74 *75 * @constructor76 * @extends {goog.debug.Error}77 */78goog.db.Error.VersionChangeBlockedError = function() {79 goog.base(this, 'Version change blocked');80};81goog.inherits(goog.db.Error.VersionChangeBlockedError, goog.debug.Error);82/**83 * Synthetic error codes for database errors, for use when IndexedDB84 * support is not available. This numbering differs in practice85 * from the browser implementations, but it is not meant to be reliable:86 * this object merely ensures that goog.db.Error is loadable on platforms87 * that do not support IndexedDB.88 *89 * @enum {number}90 * @private91 */92goog.db.Error.DatabaseErrorCode_ = {93 UNKNOWN_ERR: 1,94 NON_TRANSIENT_ERR: 2,95 NOT_FOUND_ERR: 3,96 CONSTRAINT_ERR: 4,97 DATA_ERR: 5,98 NOT_ALLOWED_ERR: 6,99 TRANSACTION_INACTIVE_ERR: 7,100 ABORT_ERR: 8,101 READ_ONLY_ERR: 9,102 TRANSIENT_ERR: 11,103 TIMEOUT_ERR: 10,104 QUOTA_ERR: 11,105 INVALID_ACCESS_ERR: 12,106 INVALID_STATE_ERR: 13107};108/**109 * Error codes for database errors.110 * @see http://www.w3.org/TR/IndexedDB/#idl-def-IDBDatabaseException111 *112 * @enum {number}113 */114goog.db.Error.ErrorCode = {115 UNKNOWN_ERR: (goog.global.IDBDatabaseException ||116 goog.global.webkitIDBDatabaseException ||117 goog.db.Error.DatabaseErrorCode_).UNKNOWN_ERR,118 NON_TRANSIENT_ERR: (goog.global.IDBDatabaseException ||119 goog.global.webkitIDBDatabaseException ||120 goog.db.Error.DatabaseErrorCode_).NON_TRANSIENT_ERR,121 NOT_FOUND_ERR: (goog.global.IDBDatabaseException ||122 goog.global.webkitIDBDatabaseException ||123 goog.db.Error.DatabaseErrorCode_).NOT_FOUND_ERR,124 CONSTRAINT_ERR: (goog.global.IDBDatabaseException ||125 goog.global.webkitIDBDatabaseException ||126 goog.db.Error.DatabaseErrorCode_).CONSTRAINT_ERR,127 DATA_ERR: (goog.global.IDBDatabaseException ||128 goog.global.webkitIDBDatabaseException ||129 goog.db.Error.DatabaseErrorCode_).DATA_ERR,130 NOT_ALLOWED_ERR: (goog.global.IDBDatabaseException ||131 goog.global.webkitIDBDatabaseException ||132 goog.db.Error.DatabaseErrorCode_).NOT_ALLOWED_ERR,133 TRANSACTION_INACTIVE_ERR: (goog.global.IDBDatabaseException ||134 goog.global.webkitIDBDatabaseException ||135 goog.db.Error.DatabaseErrorCode_).TRANSACTION_INACTIVE_ERR,136 ABORT_ERR: (goog.global.IDBDatabaseException ||137 goog.global.webkitIDBDatabaseException ||138 goog.db.Error.DatabaseErrorCode_).ABORT_ERR,139 READ_ONLY_ERR: (goog.global.IDBDatabaseException ||140 goog.global.webkitIDBDatabaseException ||141 goog.db.Error.DatabaseErrorCode_).READ_ONLY_ERR,142 TIMEOUT_ERR: (goog.global.IDBDatabaseException ||143 goog.global.webkitIDBDatabaseException ||144 goog.db.Error.DatabaseErrorCode_).TIMEOUT_ERR,145 QUOTA_ERR: (goog.global.IDBDatabaseException ||146 goog.global.webkitIDBDatabaseException ||147 goog.db.Error.DatabaseErrorCode_).QUOTA_ERR,148 INVALID_ACCESS_ERR: (goog.global.DOMException ||149 goog.db.Error.DatabaseErrorCode_).INVALID_ACCESS_ERR,150 INVALID_STATE_ERR: (goog.global.DOMException ||151 goog.db.Error.DatabaseErrorCode_).INVALID_STATE_ERR152};153/**154 * Translates an error code into a more useful message.155 *156 * @param {number} code Error code.157 * @return {string} A debug message.158 */159goog.db.Error.getMessage = function(code) {160 switch (code) {161 case goog.db.Error.ErrorCode.UNKNOWN_ERR:162 return 'Unknown error';163 case goog.db.Error.ErrorCode.NON_TRANSIENT_ERR:164 return 'Invalid operation';165 case goog.db.Error.ErrorCode.NOT_FOUND_ERR:166 return 'Required database object not found';167 case goog.db.Error.ErrorCode.CONSTRAINT_ERR:168 return 'Constraint unsatisfied';169 case goog.db.Error.ErrorCode.DATA_ERR:170 return 'Invalid data';171 case goog.db.Error.ErrorCode.NOT_ALLOWED_ERR:172 return 'Operation disallowed';173 case goog.db.Error.ErrorCode.TRANSACTION_INACTIVE_ERR:174 return 'Transaction not active';175 case goog.db.Error.ErrorCode.ABORT_ERR:176 return 'Request aborted';177 case goog.db.Error.ErrorCode.READ_ONLY_ERR:178 return 'Modifying operation not allowed in a read-only transaction';179 case goog.db.Error.ErrorCode.TIMEOUT_ERR:180 return 'Transaction timed out';181 case goog.db.Error.ErrorCode.QUOTA_ERR:182 return 'Database storage space quota exceeded';183 case goog.db.Error.ErrorCode.INVALID_ACCESS_ERR:184 return 'Invalid operation';185 case goog.db.Error.ErrorCode.INVALID_STATE_ERR:186 return 'Invalid state';187 default:188 return 'Unrecognized exception with code ' + code;189 }190};191/**192 * Names of all possible errors as returned from the browser.193 * @see http://www.w3.org/TR/IndexedDB/#exceptions194 * @enum {string}195 */196goog.db.Error.ErrorName = {197 ABORT_ERR: 'AbortError',198 CONSTRAINT_ERR: 'ConstraintError',199 DATA_CLONE_ERR: 'DataCloneError',200 DATA_ERR: 'DataError',201 INVALID_ACCESS_ERR: 'InvalidAccessError',202 INVALID_STATE_ERR: 'InvalidStateError',203 NOT_FOUND_ERR: 'NotFoundError',204 QUOTA_EXCEEDED_ERR: 'QuotaExceededError',205 READ_ONLY_ERR: 'ReadOnlyError',206 SYNTAX_ERROR: 'SyntaxError',207 TIMEOUT_ERR: 'TimeoutError',208 TRANSACTION_INACTIVE_ERR: 'TransactionInactiveError',209 UNKNOWN_ERR: 'UnknownError',210 VERSION_ERR: 'VersionError'211};212/**213 * Translates an error name to an error code. This is purely kept for backwards214 * compatibility with Chrome21.215 *216 * @param {string} name The name of the erorr.217 * @return {number} The error code corresponding to the error.218 */219goog.db.Error.getCode = function(name) {220 switch (name) {221 case goog.db.Error.ErrorName.UNKNOWN_ERR:222 return goog.db.Error.ErrorCode.UNKNOWN_ERR;223 case goog.db.Error.ErrorName.NOT_FOUND_ERR:224 return goog.db.Error.ErrorCode.NOT_FOUND_ERR;225 case goog.db.Error.ErrorName.CONSTRAINT_ERR:226 return goog.db.Error.ErrorCode.CONSTRAINT_ERR;227 case goog.db.Error.ErrorName.DATA_ERR:228 return goog.db.Error.ErrorCode.DATA_ERR;229 case goog.db.Error.ErrorName.TRANSACTION_INACTIVE_ERR:230 return goog.db.Error.ErrorCode.TRANSACTION_INACTIVE_ERR;231 case goog.db.Error.ErrorName.ABORT_ERR:232 return goog.db.Error.ErrorCode.ABORT_ERR;233 case goog.db.Error.ErrorName.READ_ONLY_ERR:234 return goog.db.Error.ErrorCode.READ_ONLY_ERR;235 case goog.db.Error.ErrorName.TIMEOUT_ERR:236 return goog.db.Error.ErrorCode.TIMEOUT_ERR;237 case goog.db.Error.ErrorName.QUOTA_EXCEEDED_ERR:238 return goog.db.Error.ErrorCode.QUOTA_ERR;239 case goog.db.Error.ErrorName.INVALID_ACCESS_ERR:240 return goog.db.Error.ErrorCode.INVALID_ACCESS_ERR;241 case goog.db.Error.ErrorName.INVALID_STATE_ERR:242 return goog.db.Error.ErrorCode.INVALID_STATE_ERR;243 default:244 return goog.db.Error.ErrorCode.UNKNOWN_ERR;245 }246};247/**248 * Converts an error code used by the old spec, to an error name used by the249 * latest spec.250 * @see http://www.w3.org/TR/IndexedDB/#exceptions251 *252 * @param {!goog.db.Error.ErrorCode|number} code The error code to convert.253 * @return {!goog.db.Error.ErrorName} The corresponding name of the error.254 */255goog.db.Error.getName = function(code) {256 switch (code) {257 case goog.db.Error.ErrorCode.UNKNOWN_ERR:258 return goog.db.Error.ErrorName.UNKNOWN_ERR;259 case goog.db.Error.ErrorCode.NOT_FOUND_ERR:260 return goog.db.Error.ErrorName.NOT_FOUND_ERR;261 case goog.db.Error.ErrorCode.CONSTRAINT_ERR:262 return goog.db.Error.ErrorName.CONSTRAINT_ERR;263 case goog.db.Error.ErrorCode.DATA_ERR:264 return goog.db.Error.ErrorName.DATA_ERR;265 case goog.db.Error.ErrorCode.TRANSACTION_INACTIVE_ERR:266 return goog.db.Error.ErrorName.TRANSACTION_INACTIVE_ERR;267 case goog.db.Error.ErrorCode.ABORT_ERR:268 return goog.db.Error.ErrorName.ABORT_ERR;269 case goog.db.Error.ErrorCode.READ_ONLY_ERR:270 return goog.db.Error.ErrorName.READ_ONLY_ERR;271 case goog.db.Error.ErrorCode.TIMEOUT_ERR:272 return goog.db.Error.ErrorName.TIMEOUT_ERR;273 case goog.db.Error.ErrorCode.QUOTA_ERR:274 return goog.db.Error.ErrorName.QUOTA_EXCEEDED_ERR;275 case goog.db.Error.ErrorCode.INVALID_ACCESS_ERR:276 return goog.db.Error.ErrorName.INVALID_ACCESS_ERR;277 case goog.db.Error.ErrorCode.INVALID_STATE_ERR:278 return goog.db.Error.ErrorName.INVALID_STATE_ERR;279 default:280 return goog.db.Error.ErrorName.UNKNOWN_ERR;281 }282};283/**284 * Constructs an goog.db.Error instance from an IDBRequest. This abstraction is285 * necessary to provide backwards compatibility with Chrome21.286 *287 * @param {!IDBRequest} request The request that failed.288 * @param {string} message The error message to add to err if it's wrapped.289 * @return {!goog.db.Error} The error that caused the failure.290 */291goog.db.Error.fromRequest = function(request, message) {292 if ('error' in request) {293 // Chrome 21 and before.294 return new goog.db.Error(request.error, message);295 } else if ('name' in request) {296 // Chrome 22+.297 var errorName = goog.db.Error.getName(request.errorCode);298 return new goog.db.Error(299 /**@type {!DOMError} */ ({name: errorName}), message);300 } else {301 return new goog.db.Error(/** @type {!DOMError} */ (302 {name: goog.db.Error.ErrorName.UNKNOWN_ERR}), message);303 }304};305/**306 * Constructs an goog.db.Error instance from an DOMException. This abstraction307 * is necessary to provide backwards compatibility with Chrome21.308 *309 * @param {!IDBDatabaseException} ex The exception that was thrown.310 * @param {string} message The error message to add to err if it's wrapped.311 * @return {!goog.db.Error} The error that caused the failure.312 * @suppress {invalidCasts} The cast from IDBDatabaseException to DOMError313 * is invalid and will not compile.314 */315goog.db.Error.fromException = function(ex, message) {316 if ('name' in ex) {317 // Chrome 21 and before.318 return new goog.db.Error(/** @type {!DOMError} */ (ex), message);319 } else if ('code' in ex) {320 // Chrome 22+.321 var errorName = goog.db.Error.getName(ex.code);322 return new goog.db.Error(323 /** @type {!DOMError} */ ({name: errorName}), message);324 } else {325 return new goog.db.Error(/** @type {!DOMError} */ (326 {name: goog.db.Error.ErrorName.UNKNOWN_ERR}), message);327 }...
extension_error.js
Source:extension_error.js
1// Copyright 2013 The Chromium Authors. All rights reserved.2// Use of this source code is governed by a BSD-style license that can be3// found in the LICENSE file.4cr.define('extensions', function() {5 'use strict';6 /**7 * Clone a template within the extension error template collection.8 * @param {string} templateName The class name of the template to clone.9 * @return {HTMLElement} The clone of the template.10 */11 function cloneTemplate(templateName) {12 return /** @type {HTMLElement} */($('template-collection-extension-error').13 querySelector('.' + templateName).cloneNode(true));14 }15 /**16 * Checks that an Extension ID follows the proper format (i.e., is 3217 * characters long, is lowercase, and contains letters in the range [a, p]).18 * @param {string} id The Extension ID to test.19 * @return {boolean} Whether or not the ID is valid.20 */21 function idIsValid(id) {22 return /^[a-p]{32}$/.test(id);23 }24 /**25 * @param {!Array<(ManifestError|RuntimeError)>} errors26 * @param {number} id27 * @return {number} The index of the error with |id|, or -1 if not found.28 */29 function findErrorById(errors, id) {30 for (var i = 0; i < errors.length; ++i) {31 if (errors[i].id == id)32 return i;33 }34 return -1;35 }36 /**37 * Creates a new ExtensionError HTMLElement; this is used to show a38 * notification to the user when an error is caused by an extension.39 * @param {(RuntimeError|ManifestError)} error The error the element should40 * represent.41 * @param {Element} boundary The boundary for the focus grid.42 * @constructor43 * @extends {cr.ui.FocusRow}44 */45 function ExtensionError(error, boundary) {46 var div = cloneTemplate('extension-error-metadata');47 div.__proto__ = ExtensionError.prototype;48 div.decorateWithError_(error, boundary);49 return div;50 }51 ExtensionError.prototype = {52 __proto__: cr.ui.FocusRow.prototype,53 /** @override */54 getEquivalentElement: function(element) {55 if (element.classList.contains('extension-error-metadata'))56 return this;57 if (element.classList.contains('error-delete-button')) {58 return /** @type {!HTMLElement} */ (this.querySelector(59 '.error-delete-button'));60 }61 assertNotReached();62 return element;63 },64 /**65 * @param {(RuntimeError|ManifestError)} error The error the element should66 * represent.67 * @param {Element} boundary The boundary for the FocusGrid.68 * @private69 */70 decorateWithError_: function(error, boundary) {71 this.decorate(boundary);72 /**73 * The backing error.74 * @type {(ManifestError|RuntimeError)}75 */76 this.error = error;77 // Add an additional class for the severity level.78 if (error.type == chrome.developerPrivate.ErrorType.RUNTIME) {79 switch (error.severity) {80 case chrome.developerPrivate.ErrorLevel.LOG:81 this.classList.add('extension-error-severity-info');82 break;83 case chrome.developerPrivate.ErrorLevel.WARN:84 this.classList.add('extension-error-severity-warning');85 break;86 case chrome.developerPrivate.ErrorLevel.ERROR:87 this.classList.add('extension-error-severity-fatal');88 break;89 default:90 assertNotReached();91 }92 } else {93 // We classify manifest errors as "warnings".94 this.classList.add('extension-error-severity-warning');95 }96 var iconNode = document.createElement('img');97 iconNode.className = 'extension-error-icon';98 // TODO(hcarmona): Populate alt text with a proper description since this99 // icon conveys the severity of the error. (info, warning, fatal).100 iconNode.alt = '';101 this.insertBefore(iconNode, this.firstChild);102 var messageSpan = this.querySelector('.extension-error-message');103 messageSpan.textContent = error.message;104 var deleteButton = this.querySelector('.error-delete-button');105 deleteButton.addEventListener('click', function(e) {106 this.dispatchEvent(107 new CustomEvent('deleteExtensionError',108 {bubbles: true, detail: this.error}));109 }.bind(this));110 this.addEventListener('click', function(e) {111 if (e.target != deleteButton)112 this.requestActive_();113 }.bind(this));114 this.addEventListener('keydown', function(e) {115 if (e.keyIdentifier == 'Enter' && e.target != deleteButton)116 this.requestActive_();117 });118 this.addFocusableElement(this);119 this.addFocusableElement(this.querySelector('.error-delete-button'));120 },121 /**122 * Bubble up an event to request to become active.123 * @private124 */125 requestActive_: function() {126 this.dispatchEvent(127 new CustomEvent('highlightExtensionError',128 {bubbles: true, detail: this.error}));129 },130 };131 /**132 * A variable length list of runtime or manifest errors for a given extension.133 * @param {Array<(RuntimeError|ManifestError)>} errors The list of extension134 * errors with which to populate the list.135 * @param {string} extensionId The id of the extension.136 * @constructor137 * @extends {HTMLDivElement}138 */139 function ExtensionErrorList(errors, extensionId) {140 var div = cloneTemplate('extension-error-list');141 div.__proto__ = ExtensionErrorList.prototype;142 div.extensionId_ = extensionId;143 div.decorate(errors);144 return div;145 }146 ExtensionErrorList.prototype = {147 __proto__: HTMLDivElement.prototype,148 /**149 * Initializes the extension error list.150 * @param {Array<(RuntimeError|ManifestError)>} errors The list of errors.151 */152 decorate: function(errors) {153 /**154 * @private {!Array<(ManifestError|RuntimeError)>}155 */156 this.errors_ = [];157 this.focusGrid_ = new cr.ui.FocusGrid();158 this.gridBoundary_ = this.querySelector('.extension-error-list-contents');159 this.gridBoundary_.addEventListener('focus', this.onFocus_.bind(this));160 this.gridBoundary_.addEventListener('focusin',161 this.onFocusin_.bind(this));162 errors.forEach(this.addError_, this);163 this.addEventListener('highlightExtensionError', function(e) {164 this.setActiveErrorNode_(e.target);165 });166 this.addEventListener('deleteExtensionError', function(e) {167 this.removeError_(e.detail);168 });169 this.querySelector('#extension-error-list-clear').addEventListener(170 'click', function(e) {171 this.clear(true);172 }.bind(this));173 /**174 * The callback for the extension changed event.175 * @private {function(EventData):void}176 */177 this.onItemStateChangedListener_ = function(data) {178 var type = chrome.developerPrivate.EventType;179 if ((data.event_type == type.ERRORS_REMOVED ||180 data.event_type == type.ERROR_ADDED) &&181 data.extensionInfo.id == this.extensionId_) {182 var newErrors = data.extensionInfo.runtimeErrors.concat(183 data.extensionInfo.manifestErrors);184 this.updateErrors_(newErrors);185 }186 }.bind(this);187 chrome.developerPrivate.onItemStateChanged.addListener(188 this.onItemStateChangedListener_);189 /**190 * The active error element in the list.191 * @private {?}192 */193 this.activeError_ = null;194 this.setActiveError(0);195 },196 /**197 * Adds an error to the list.198 * @param {(RuntimeError|ManifestError)} error The error to add.199 * @private200 */201 addError_: function(error) {202 this.querySelector('#no-errors-span').hidden = true;203 this.errors_.push(error);204 var focusRow = new ExtensionError(error, this.gridBoundary_);205 this.gridBoundary_.appendChild(document.createElement('li')).206 appendChild(focusRow);207 this.focusGrid_.addRow(focusRow);208 },209 /**210 * Removes an error from the list.211 * @param {(RuntimeError|ManifestError)} error The error to remove.212 * @private213 */214 removeError_: function(error) {215 var index = 0;216 for (; index < this.errors_.length; ++index) {217 if (this.errors_[index].id == error.id)218 break;219 }220 assert(index != this.errors_.length);221 var errorList = this.querySelector('.extension-error-list-contents');222 var wasActive =223 this.activeError_ && this.activeError_.error.id == error.id;224 this.errors_.splice(index, 1);225 var listElement = errorList.children[index];226 listElement.parentNode.removeChild(listElement);227 if (wasActive) {228 index = Math.min(index, this.errors_.length - 1);229 this.setActiveError(index); // Gracefully handles the -1 case.230 }231 chrome.developerPrivate.deleteExtensionErrors({232 extensionId: error.extensionId,233 errorIds: [error.id]234 });235 if (this.errors_.length == 0)236 this.querySelector('#no-errors-span').hidden = false;237 },238 /**239 * Updates the list of errors.240 * @param {!Array<(ManifestError|RuntimeError)>} newErrors The new list of241 * errors.242 * @private243 */244 updateErrors_: function(newErrors) {245 this.errors_.forEach(function(error) {246 if (findErrorById(newErrors, error.id) == -1)247 this.removeError_(error);248 }, this);249 newErrors.forEach(function(error) {250 var index = findErrorById(this.errors_, error.id);251 if (index == -1)252 this.addError_(error);253 else254 this.errors_[index] = error; // Update the existing reference.255 }, this);256 },257 /**258 * Called when the list is being removed.259 */260 onRemoved: function() {261 chrome.developerPrivate.onItemStateChanged.removeListener(262 this.onItemStateChangedListener_);263 this.clear(false);264 },265 /**266 * Sets the active error in the list.267 * @param {number} index The index to set to be active.268 */269 setActiveError: function(index) {270 var errorList = this.querySelector('.extension-error-list-contents');271 var item = errorList.children[index];272 this.setActiveErrorNode_(273 item ? item.querySelector('.extension-error-metadata') : null);274 var node = null;275 if (index >= 0 && index < errorList.children.length) {276 node = errorList.children[index].querySelector(277 '.extension-error-metadata');278 }279 this.setActiveErrorNode_(node);280 },281 /**282 * Clears the list of all errors.283 * @param {boolean} deleteErrors Whether or not the errors should be deleted284 * on the backend.285 */286 clear: function(deleteErrors) {287 if (this.errors_.length == 0)288 return;289 if (deleteErrors) {290 var ids = this.errors_.map(function(error) { return error.id; });291 chrome.developerPrivate.deleteExtensionErrors({292 extensionId: this.extensionId_,293 errorIds: ids294 });295 }296 this.setActiveErrorNode_(null);297 this.errors_.length = 0;298 var errorList = this.querySelector('.extension-error-list-contents');299 while (errorList.firstChild)300 errorList.removeChild(errorList.firstChild);301 },302 /**303 * Sets the active error in the list.304 * @param {?} node The error to make active.305 * @private306 */307 setActiveErrorNode_: function(node) {308 if (this.activeError_)309 this.activeError_.classList.remove('extension-error-active');310 if (node)311 node.classList.add('extension-error-active');312 this.activeError_ = node;313 this.dispatchEvent(314 new CustomEvent('activeExtensionErrorChanged',315 {bubbles: true, detail: node ? node.error : null}));316 },317 /**318 * The grid should not be focusable once it or an element inside it is319 * focused. This is necessary to allow tabbing out of the grid in reverse.320 * @private321 */322 onFocusin_: function() {323 this.gridBoundary_.tabIndex = -1;324 },325 /**326 * Focus the first focusable row when tabbing into the grid for the327 * first time.328 * @private329 */330 onFocus_: function() {331 var activeRow = this.gridBoundary_.querySelector('.focus-row-active');332 activeRow.getEquivalentElement(null).focus();333 },334 };335 return {336 ExtensionErrorList: ExtensionErrorList337 };...
.eslintrc.js
Source:.eslintrc.js
1module.exports = {2 "env": {3 "browser": true,4 "es6": true5 },6 "extends": "eslint:recommended",7 "parserOptions": {8 "ecmaVersion": 2017,9 "sourceType": "module"10 },11 "rules": {12 "accessor-pairs": "error",13 "array-bracket-newline": "error",14 "array-bracket-spacing": "error",15 "array-callback-return": "error",16 "array-element-newline": "error",17 "arrow-body-style": "error",18 "arrow-parens": [19 "error",20 "always"21 ],22 "arrow-spacing": [23 "error",24 {25 "after": false,26 "before": false27 }28 ],29 "block-scoped-var": "error",30 "block-spacing": "error",31 "brace-style": "error",32 "callback-return": "error",33 "camelcase": "off",34 "capitalized-comments": "error",35 "class-methods-use-this": "error",36 "comma-dangle": "error",37 "comma-spacing": "off",38 "comma-style": [39 "error",40 "last"41 ],42 "complexity": "error",43 "computed-property-spacing": [44 "error",45 "never"46 ],47 "consistent-return": "error",48 "consistent-this": "error",49 "curly": "error",50 "default-case": "error",51 "dot-location": "error",52 "dot-notation": "error",53 "eol-last": "error",54 "eqeqeq": "error",55 "func-call-spacing": "error",56 "func-name-matching": "error",57 "func-names": "off",58 "func-style": [59 "error",60 "declaration"61 ],62 "function-paren-newline": "error",63 "generator-star-spacing": "error",64 "global-require": "error",65 "guard-for-in": "error",66 "handle-callback-err": "error",67 "id-blacklist": "error",68 "id-length": "error",69 "id-match": "error",70 "implicit-arrow-linebreak": [71 "error",72 "beside"73 ],74 "indent": "off",75 "indent-legacy": "off",76 "init-declarations": "error",77 "jsx-quotes": "error",78 "key-spacing": "error",79 "keyword-spacing": "error",80 "line-comment-position": "error",81 "linebreak-style": [82 "error",83 "unix"84 ],85 "lines-around-comment": "error",86 "lines-around-directive": "error",87 "lines-between-class-members": "error",88 "max-classes-per-file": "error",89 "max-depth": "error",90 "max-len": "error",91 "max-lines": "error",92 "max-lines-per-function": "error",93 "max-nested-callbacks": "error",94 "max-params": "error",95 "max-statements": "error",96 "max-statements-per-line": "error",97 "multiline-comment-style": "error",98 "multiline-ternary": "error",99 "new-cap": "error",100 "new-parens": "error",101 "newline-after-var": "off",102 "newline-before-return": "error",103 "newline-per-chained-call": "error",104 "no-alert": "error",105 "no-array-constructor": "error",106 "no-async-promise-executor": "error",107 "no-await-in-loop": "error",108 "no-bitwise": "error",109 "no-buffer-constructor": "error",110 "no-caller": "error",111 "no-catch-shadow": "error",112 "no-confusing-arrow": "error",113 "no-continue": "error",114 "no-div-regex": "error",115 "no-duplicate-imports": "error",116 "no-else-return": "error",117 "no-empty-function": "error",118 "no-eq-null": "error",119 "no-eval": "error",120 "no-extend-native": "error",121 "no-extra-bind": "error",122 "no-extra-label": "error",123 "no-extra-parens": "off",124 "no-floating-decimal": "error",125 "no-implicit-coercion": "error",126 "no-implicit-globals": "error",127 "no-implied-eval": "error",128 "no-inline-comments": "error",129 "no-invalid-this": "error",130 "no-iterator": "error",131 "no-label-var": "error",132 "no-labels": "error",133 "no-lone-blocks": "error",134 "no-lonely-if": "error",135 "no-loop-func": "error",136 "no-magic-numbers": "off",137 "no-misleading-character-class": "error",138 "no-mixed-operators": "error",139 "no-mixed-requires": "error",140 "no-multi-assign": "error",141 "no-multi-spaces": "error",142 "no-multi-str": "error",143 "no-multiple-empty-lines": "error",144 "no-native-reassign": "error",145 "no-negated-condition": "error",146 "no-negated-in-lhs": "error",147 "no-nested-ternary": "error",148 "no-new": "error",149 "no-new-func": "error",150 "no-new-object": "error",151 "no-new-require": "error",152 "no-new-wrappers": "error",153 "no-octal-escape": "error",154 "no-param-reassign": "error",155 "no-path-concat": "error",156 "no-plusplus": "error",157 "no-process-env": "error",158 "no-process-exit": "error",159 "no-proto": "error",160 "no-prototype-builtins": "error",161 "no-restricted-globals": "error",162 "no-restricted-imports": "error",163 "no-restricted-modules": "error",164 "no-restricted-properties": "error",165 "no-restricted-syntax": "error",166 "no-return-assign": "error",167 "no-return-await": "error",168 "no-script-url": "error",169 "no-self-compare": "error",170 "no-sequences": "error",171 "no-shadow": "error",172 "no-shadow-restricted-names": "error",173 "no-spaced-func": "error",174 "no-sync": "error",175 "no-tabs": [176 "error",177 {178 "allowIndentationTabs": true179 }180 ],181 "no-template-curly-in-string": "error",182 "no-ternary": "error",183 "no-throw-literal": "error",184 "no-trailing-spaces": "error",185 "no-undef-init": "error",186 "no-undefined": "error",187 "no-underscore-dangle": "error",188 "no-unmodified-loop-condition": "error",189 "no-unneeded-ternary": "error",190 "no-unused-expressions": "error",191 "no-use-before-define": "error",192 "no-useless-call": "error",193 "no-useless-computed-key": "error",194 "no-useless-concat": "error",195 "no-useless-constructor": "error",196 "no-useless-rename": "error",197 "no-useless-return": "error",198 "no-var": "off",199 "no-void": "error",200 "no-warning-comments": "error",201 "no-whitespace-before-property": "error",202 "no-with": "error",203 "nonblock-statement-body-position": "error",204 "object-curly-newline": "error",205 "object-curly-spacing": "error",206 "object-property-newline": "error",207 "object-shorthand": "error",208 "one-var": "off",209 "one-var-declaration-per-line": "error",210 "operator-assignment": "error",211 "operator-linebreak": "error",212 "padded-blocks": "off",213 "padding-line-between-statements": "error",214 "prefer-arrow-callback": "off",215 "prefer-const": "error",216 "prefer-destructuring": "error",217 "prefer-numeric-literals": "error",218 "prefer-object-spread": "error",219 "prefer-promise-reject-errors": "error",220 "prefer-reflect": "error",221 "prefer-rest-params": "error",222 "prefer-spread": "error",223 "prefer-template": "off",224 "quote-props": "error",225 "quotes": "off",226 "radix": "error",227 "require-atomic-updates": "error",228 "require-await": "error",229 "require-jsdoc": "off",230 "require-unicode-regexp": "error",231 "rest-spread-spacing": "error",232 "semi": "error",233 "semi-spacing": "error",234 "semi-style": [235 "error",236 "last"237 ],238 "sort-imports": "error",239 "sort-keys": [240 "error",241 "asc"242 ],243 "sort-vars": "error",244 "space-before-blocks": "off",245 "space-before-function-paren": "off",246 "space-in-parens": [247 "error",248 "never"249 ],250 "space-infix-ops": "off",251 "space-unary-ops": "error",252 "spaced-comment": "error",253 "strict": "error",254 "switch-colon-spacing": "error",255 "symbol-description": "error",256 "template-curly-spacing": "error",257 "template-tag-spacing": "error",258 "unicode-bom": [259 "error",260 "never"261 ],262 "valid-jsdoc": "error",263 "vars-on-top": "error",264 "wrap-iife": "error",265 "wrap-regex": "error",266 "yield-star-spacing": "error",267 "yoda": "error"268 }...
ReactErrorUtils-test.internal.js
Source:ReactErrorUtils-test.internal.js
1/**2 * Copyright (c) Facebook, Inc. and its affiliates.3 *4 * This source code is licensed under the MIT license found in the5 * LICENSE file in the root directory of this source tree.6 *7 * @emails react-core8 */9'use strict';10let ReactErrorUtils;11describe('ReactErrorUtils', () => {12 beforeEach(() => {13 // TODO: can we express this test with only public API?14 ReactErrorUtils = require('shared/ReactErrorUtils');15 });16 it(`it should rethrow caught errors`, () => {17 const err = new Error('foo');18 const callback = function() {19 throw err;20 };21 ReactErrorUtils.invokeGuardedCallbackAndCatchFirstError(22 'foo',23 callback,24 null,25 );26 expect(ReactErrorUtils.hasCaughtError()).toBe(false);27 expect(() => ReactErrorUtils.rethrowCaughtError()).toThrow(err);28 });29 it(`should call the callback the passed arguments`, () => {30 const callback = jest.fn();31 ReactErrorUtils.invokeGuardedCallback(32 'foo',33 callback,34 null,35 'arg1',36 'arg2',37 );38 expect(callback).toBeCalledWith('arg1', 'arg2');39 });40 it(`should call the callback with the provided context`, () => {41 const context = {didCall: false};42 ReactErrorUtils.invokeGuardedCallback(43 'foo',44 function() {45 this.didCall = true;46 },47 context,48 );49 expect(context.didCall).toBe(true);50 });51 it(`should catch errors`, () => {52 const error = new Error();53 const returnValue = ReactErrorUtils.invokeGuardedCallback(54 'foo',55 function() {56 throw error;57 },58 null,59 'arg1',60 'arg2',61 );62 expect(returnValue).toBe(undefined);63 expect(ReactErrorUtils.hasCaughtError()).toBe(true);64 expect(ReactErrorUtils.clearCaughtError()).toBe(error);65 });66 it(`should return false from clearCaughtError if no error was thrown`, () => {67 const callback = jest.fn();68 ReactErrorUtils.invokeGuardedCallback('foo', callback, null);69 expect(ReactErrorUtils.hasCaughtError()).toBe(false);70 expect(ReactErrorUtils.clearCaughtError).toThrow('no error was captured');71 });72 it(`can nest with same debug name`, () => {73 const err1 = new Error();74 let err2;75 const err3 = new Error();76 let err4;77 ReactErrorUtils.invokeGuardedCallback(78 'foo',79 function() {80 ReactErrorUtils.invokeGuardedCallback(81 'foo',82 function() {83 throw err1;84 },85 null,86 );87 err2 = ReactErrorUtils.clearCaughtError();88 throw err3;89 },90 null,91 );92 err4 = ReactErrorUtils.clearCaughtError();93 expect(err2).toBe(err1);94 expect(err4).toBe(err3);95 });96 it(`handles nested errors`, () => {97 const err1 = new Error();98 let err2;99 ReactErrorUtils.invokeGuardedCallback(100 'foo',101 function() {102 ReactErrorUtils.invokeGuardedCallback(103 'foo',104 function() {105 throw err1;106 },107 null,108 );109 err2 = ReactErrorUtils.clearCaughtError();110 },111 null,112 );113 // Returns null because inner error was already captured114 expect(ReactErrorUtils.hasCaughtError()).toBe(false);115 expect(err2).toBe(err1);116 });117 it('handles nested errors in separate renderers', () => {118 const ReactErrorUtils1 = require('shared/ReactErrorUtils');119 jest.resetModules();120 const ReactErrorUtils2 = require('shared/ReactErrorUtils');121 expect(ReactErrorUtils1).not.toEqual(ReactErrorUtils2);122 let ops = [];123 ReactErrorUtils1.invokeGuardedCallback(124 null,125 () => {126 ReactErrorUtils2.invokeGuardedCallback(127 null,128 () => {129 throw new Error('nested error');130 },131 null,132 );133 // ReactErrorUtils2 should catch the error134 ops.push(ReactErrorUtils2.hasCaughtError());135 ops.push(ReactErrorUtils2.clearCaughtError().message);136 },137 null,138 );139 // ReactErrorUtils1 should not catch the error140 ops.push(ReactErrorUtils1.hasCaughtError());141 expect(ops).toEqual([true, 'nested error', false]);142 });143 if (!__DEV__) {144 // jsdom doesn't handle this properly, but Chrome and Firefox should. Test145 // this with a fixture.146 it('catches null values', () => {147 ReactErrorUtils.invokeGuardedCallback(148 null,149 function() {150 throw null; // eslint-disable-line no-throw-literal151 },152 null,153 );154 expect(ReactErrorUtils.hasCaughtError()).toBe(true);155 expect(ReactErrorUtils.clearCaughtError()).toBe(null);156 });157 }158 it(`can be shimmed`, () => {159 const ops = [];160 jest.resetModules();161 jest.mock(162 'shared/invokeGuardedCallbackImpl',163 () =>164 function invokeGuardedCallback(name, func, context, a) {165 ops.push(a);166 try {167 func.call(context, a);168 } catch (error) {169 this.onError(error);170 }171 },172 );173 ReactErrorUtils = require('shared/ReactErrorUtils');174 try {175 const err = new Error('foo');176 const callback = function() {177 throw err;178 };179 ReactErrorUtils.invokeGuardedCallbackAndCatchFirstError(180 'foo',181 callback,182 null,183 'somearg',184 );185 expect(() => ReactErrorUtils.rethrowCaughtError()).toThrow(err);186 expect(ops).toEqual(['somearg']);187 } finally {188 jest.unmock('shared/invokeGuardedCallbackImpl');189 }190 });...
errors.js
Source:errors.js
1'use strict';2Object.defineProperty(exports, "__esModule", {3 value: true4});5exports.decorateBadRequestError = decorateBadRequestError;6exports.isBadRequestError = isBadRequestError;7exports.decorateNotAuthorizedError = decorateNotAuthorizedError;8exports.isNotAuthorizedError = isNotAuthorizedError;9exports.decorateForbiddenError = decorateForbiddenError;10exports.isForbiddenError = isForbiddenError;11exports.decorateNotFoundError = decorateNotFoundError;12exports.isNotFoundError = isNotFoundError;13exports.decorateConflictError = decorateConflictError;14exports.isConflictError = isConflictError;15exports.decorateEsUnavailableError = decorateEsUnavailableError;16exports.isEsUnavailableError = isEsUnavailableError;17exports.decorateGeneralError = decorateGeneralError;18var _boom = require('boom');19var _boom2 = _interopRequireDefault(_boom);20function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }21const code = Symbol('SavedObjectsClientErrorCode');22function decorate(error, errorCode, statusCode, message) {23 const boom = _boom2.default.boomify(error, {24 statusCode,25 message,26 override: false27 });28 boom[code] = errorCode;29 return boom;30}31// 400 - badRequest32const CODE_BAD_REQUEST = 'SavedObjectsClient/badRequest';33function decorateBadRequestError(error, reason) {34 return decorate(error, CODE_BAD_REQUEST, 400, reason);35}36function isBadRequestError(error) {37 return error && error[code] === CODE_BAD_REQUEST;38}39// 401 - Not Authorized40const CODE_NOT_AUTHORIZED = 'SavedObjectsClient/notAuthorized';41function decorateNotAuthorizedError(error, reason) {42 return decorate(error, CODE_NOT_AUTHORIZED, 401, reason);43}44function isNotAuthorizedError(error) {45 return error && error[code] === CODE_NOT_AUTHORIZED;46}47// 403 - Forbidden48const CODE_FORBIDDEN = 'SavedObjectsClient/forbidden';49function decorateForbiddenError(error, reason) {50 return decorate(error, CODE_FORBIDDEN, 403, reason);51}52function isForbiddenError(error) {53 return error && error[code] === CODE_FORBIDDEN;54}55// 404 - Not Found56const CODE_NOT_FOUND = 'SavedObjectsClient/notFound';57function decorateNotFoundError(error, reason) {58 return decorate(error, CODE_NOT_FOUND, 404, reason);59}60function isNotFoundError(error) {61 return error && error[code] === CODE_NOT_FOUND;62}63// 409 - Conflict64const CODE_CONFLICT = 'SavedObjectsClient/conflict';65function decorateConflictError(error, reason) {66 return decorate(error, CODE_CONFLICT, 409, reason);67}68function isConflictError(error) {69 return error && error[code] === CODE_CONFLICT;70}71// 500 - Es Unavailable72const CODE_ES_UNAVAILABLE = 'SavedObjectsClient/esUnavailable';73function decorateEsUnavailableError(error, reason) {74 return decorate(error, CODE_ES_UNAVAILABLE, 503, reason);75}76function isEsUnavailableError(error) {77 return error && error[code] === CODE_ES_UNAVAILABLE;78}79// 500 - General Error80const CODE_GENERAL_ERROR = 'SavedObjectsClient/generalError';81function decorateGeneralError(error, reason) {82 return decorate(error, CODE_GENERAL_ERROR, 500, reason);...
Using AI Code Generation
1var wpt = require('webpagetest');2var wpt = new WebPageTest('www.webpagetest.org');3 if(err) {4 console.log(err);5 }6});7var wpt = require('webpagetest');8var wpt = new WebPageTest('www.webpagetest.org');9 if(err) {10 console.log(err);11 }12});13var wpt = require('webpagetest');14var wpt = new WebPageTest('www.webpagetest.org');15 if(err) {16 console.log(err);17 }18});19var wpt = require('webpagetest');20var wpt = new WebPageTest('www.webpagetest.org');21 if(err) {22 console.log(err);23 }24});25var wpt = require('webpagetest');26var wpt = new WebPageTest('www.webpagetest.org');27 if(err) {28 console.log(err);29 }30});31var wpt = require('webpagetest');32var wpt = new WebPageTest('www.webpagetest.org');33 if(err) {34 console.log(err);35 }36});
Using AI Code Generation
1wpt.error('Error message');2wpt.log('Log message');3wpt.warn('Warn message');4wpt.info('Info message');5wpt.debug('Debug message');6This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details
Using AI Code Generation
1var wpt = require('webpagetest');2var test = wpt('www.webpagetest.org');3 if (err) {4 console.log('Error: ' + err);5 } else {6 console.log('Test ID: ' + data.data.testId);7 }8});9var wpt = require('webpagetest');10var test = wpt('www.webpagetest.org');11 if (err) {12 console.log('Error: ' + err
Using AI Code Generation
1var wptools = require('wptools');2var page = wptools.page('Albert Einstein');3page.info(function(err, info) {4 if (err) {5 console.log(err);6 } else {7 console.log(info);8 }9});10{ [Error: Request failed with status code 404]11 { adapter: [Function: httpAdapter],12 transformRequest: { '0': [Function: transformRequest] },13 transformResponse: { '0': [Function: transformResponse] },14 headers: { Accept: 'application/json, text/plain, */*' },15 data: undefined },16 { _events: { error: [Function: handleRequestError] },17 { protocol: 'https:',18 pathname: '/w/api.php' },
Using AI Code Generation
1var wptlogger = require('wptlogger');2wptlogger.error('Test error message');3var wptlogger = require('wptlogger');4wptlogger.info('Test info message');5var wptlogger = require('wptlogger');6wptlogger.warning('Test warning message');7var wptlogger = require('wptlogger');8wptlogger.debug('Test debug message');9var wptlogger = require('wptlogger');10wptlogger.log('Test log message');11var wptlogger = require('wptlogger');12wptlogger.fatal('Test fatal message');13var wptlogger = require('wptlogger');14wptlogger.trace('Test trace message');15var wptlogger = require('wptlogger');16wptlogger.setLogLevel('error');
Using AI Code Generation
1var wpt = require('webpagetest');2var wpt = new WebPageTest('www.webpagetest.org');3wpt.runTest(testUrl, function(err, data) {4 if (err) {5 console.log('Error: ' + err);6 } else {7 console.log('Test ID: ' + data.data.testId);8 }9});10wpt.test(testUrl, function(err, data) {11 if (err) {12 console.log('Error: ' + err);13 } else {14 console.log('Test ID: ' + data.data.testId);15 }16});17wpt.runTest(testUrl, function(err, data) {18 if (err) {19 console.log('Error: ' + err);20 } else {21 console.log('Test ID: ' + data.data.testId);22 }23});
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.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!