/
githubmr
/
facebook-react
Обзор
Документация
Войти
/
githubmr
/
facebook-react
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
main
packages/react-dom/src/__tests__/ReactDOMConsoleErrorReportingLegacy-test.js
586 строк
16 KB
Sebastian Markbåge
Remove Component Stack from React Logged Warnings and Error Reporting (#30308)
12 июл 2024, 20:02
Не верифицирован
12 июл 2024, 20:02
400e822
Код
Авторство
О чём код?
/** * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ 'use strict'; describe('ReactDOMConsoleErrorReporting', () => { let act; let React; let ReactDOM; let ErrorBoundary; let NoError; let container; let windowOnError; let waitForThrow; beforeEach(() => { jest.resetModules(); act = require('internal-test-utils').act; React = require('react'); ReactDOM = require('react-dom'); const InternalTestUtils = require('internal-test-utils'); waitForThrow = InternalTestUtils.waitForThrow; ErrorBoundary = class extends React.Component { state = {error: null}; static getDerivedStateFromError(error) { return {error}; } render() { if (this.state.error) { return <h1>Caught: {this.state.error.message}</h1>; } return this.props.children; } }; NoError = function () { return <h1>OK</h1>; }; container = document.createElement('div'); document.body.appendChild(container); windowOnError = jest.fn(); window.addEventListener('error', windowOnError); spyOnDevAndProd(console, 'error'); spyOnDevAndProd(console, 'warn'); }); afterEach(() => { document.body.removeChild(container); window.removeEventListener('error', windowOnError); jest.restoreAllMocks(); }); describe('ReactDOM.render', () => { // @gate !disableLegacyMode it('logs errors during event handlers', async () => { function Foo() { return ( <button onClick={() => { throw Error('Boom'); }}> click me </button> ); } await act(() => { ReactDOM.render(<Foo />, container); }); await expect(async () => { await act(() => { container.firstChild.dispatchEvent( new MouseEvent('click', { bubbles: true, }), ); }); }).rejects.toThrow( expect.objectContaining({ message: 'Boom', }), ); // Reported because we're in a browser click event: expect(windowOnError.mock.calls).toEqual([ [ expect.objectContaining({ message: 'Boom', }), ], ]); expect(console.warn).not.toBeCalled(); if (__DEV__) { expect(console.error.mock.calls).toEqual([ [ expect.stringContaining( 'ReactDOM.render has not been supported since React 18', ), ], ]); } else { expect(console.error).not.toBeCalled(); } // Check next render doesn't throw. windowOnError.mockReset(); console.warn.mockReset(); console.error.mockReset(); await act(() => { ReactDOM.render(<NoError />, container); }); expect(container.textContent).toBe('OK'); expect(windowOnError).not.toBeCalled(); expect(console.warn).not.toBeCalled(); if (__DEV__) { expect(console.error.mock.calls).toEqual([ [ expect.stringContaining( 'ReactDOM.render has not been supported since React 18', ), ], ]); } else { expect(console.error).not.toBeCalled(); } }); // @gate !disableLegacyMode it('logs render errors without an error boundary', async () => { function Foo() { throw Error('Boom'); } await expect(async () => { await act(() => { ReactDOM.render(<Foo />, container); }); }).rejects.toThrow('Boom'); // Reported because errors without a boundary are reported to window. expect(windowOnError.mock.calls).toEqual([ [ expect.objectContaining({ message: 'Boom', }), ], ]); if (__DEV__) { expect(console.warn.mock.calls).toEqual([ [ // Formatting expect.stringContaining('%s'), // Addendum by React: expect.stringContaining('An error occurred in the <Foo> component'), expect.stringContaining('Consider adding an error boundary'), // The component stack is not added without the polyfill/devtools. // expect.stringContaining('Foo'), ], ]); expect(console.error.mock.calls).toEqual([ [ expect.stringContaining( 'ReactDOM.render has not been supported since React 18', ), ], ]); } else { expect(console.warn).not.toBeCalled(); expect(console.error).not.toBeCalled(); } // Check next render doesn't throw. windowOnError.mockReset(); console.warn.mockReset(); console.error.mockReset(); await act(() => { ReactDOM.render(<NoError />, container); }); expect(container.textContent).toBe('OK'); expect(console.warn).not.toBeCalled(); expect(windowOnError).not.toBeCalled(); if (__DEV__) { expect(console.error.mock.calls).toEqual([ [ expect.stringContaining( 'ReactDOM.render has not been supported since React 18', ), ], ]); } else { expect(console.error).not.toBeCalled(); } }); // @gate !disableLegacyMode it('logs render errors with an error boundary', async () => { function Foo() { throw Error('Boom'); } await act(() => { ReactDOM.render( <ErrorBoundary> <Foo /> </ErrorBoundary>, container, ); }); // The top-level error was caught with try/catch, // so we don't see an error event. expect(windowOnError).not.toBeCalled(); expect(console.warn).not.toBeCalled(); if (__DEV__) { expect(console.error.mock.calls).toEqual([ [ expect.stringContaining( 'ReactDOM.render has not been supported since React 18', ), ], [ // Formatting expect.stringContaining('%o'), expect.objectContaining({ message: 'Boom', }), // Addendum by React: expect.stringContaining( 'The above error occurred in the <Foo> component', ), expect.stringContaining('ErrorBoundary'), // The component stack is not added without the polyfill/devtools. // expect.stringContaining('Foo'), ], ]); } else { expect(console.error.mock.calls).toEqual([ [ // Reported by React with no extra message: expect.objectContaining({ message: 'Boom', }), ], ]); } // Check next render doesn't throw. windowOnError.mockReset(); console.error.mockReset(); console.warn.mockReset(); await act(() => { ReactDOM.render(<NoError />, container); }); expect(container.textContent).toBe('OK'); expect(windowOnError).not.toBeCalled(); expect(console.warn).not.toBeCalled(); if (__DEV__) { expect(console.error.mock.calls).toEqual([ [ expect.stringContaining( 'ReactDOM.render has not been supported since React 18', ), ], ]); } else { expect(console.error).not.toBeCalled(); } }); // @gate !disableLegacyMode it('logs layout effect errors without an error boundary', async () => { function Foo() { React.useLayoutEffect(() => { throw Error('Boom'); }, []); return null; } await expect(async () => { await act(() => { ReactDOM.render(<Foo />, container); }); }).rejects.toThrow('Boom'); // Reported because errors without a boundary are reported to window. expect(windowOnError.mock.calls).toEqual([ [ expect.objectContaining({ message: 'Boom', }), ], ]); if (__DEV__) { expect(console.warn.mock.calls).toEqual([ [ // Formatting expect.stringContaining('%s'), // Addendum by React: expect.stringContaining('An error occurred in the <Foo> component'), expect.stringContaining('Consider adding an error boundary'), // The component stack is not added without the polyfill/devtools. // expect.stringContaining('Foo'), ], ]); expect(console.error.mock.calls).toEqual([ [ expect.stringContaining( 'ReactDOM.render has not been supported since React 18', ), ], ]); } else { expect(console.warn).not.toBeCalled(); expect(console.error).not.toBeCalled(); } // Check next render doesn't throw. windowOnError.mockReset(); console.warn.mockReset(); console.error.mockReset(); await act(() => { ReactDOM.render(<NoError />, container); }); expect(container.textContent).toBe('OK'); expect(console.warn).not.toBeCalled(); expect(windowOnError).not.toBeCalled(); if (__DEV__) { expect(console.error.mock.calls).toEqual([ [ expect.stringContaining( 'ReactDOM.render has not been supported since React 18', ), ], ]); } else { expect(console.error).not.toBeCalled(); } }); // @gate !disableLegacyMode it('logs layout effect errors with an error boundary', async () => { function Foo() { React.useLayoutEffect(() => { throw Error('Boom'); }, []); return null; } await act(() => { ReactDOM.render( <ErrorBoundary> <Foo /> </ErrorBoundary>, container, ); }); // The top-level error was caught with try/catch, // so we don't see an error event. expect(windowOnError).not.toBeCalled(); expect(console.warn).not.toBeCalled(); if (__DEV__) { expect(console.error.mock.calls).toEqual([ [ expect.stringContaining( 'ReactDOM.render has not been supported since React 18', ), ], [ // Formatting expect.stringContaining('%o'), expect.objectContaining({ message: 'Boom', }), // Addendum by React: expect.stringContaining( 'The above error occurred in the <Foo> component', ), expect.stringContaining('ErrorBoundary'), // The component stack is not added without the polyfill/devtools. // expect.stringContaining('Foo'), ], ]); } else { expect(console.error.mock.calls).toEqual([ [ // Reported by React with no extra message: expect.objectContaining({ message: 'Boom', }), ], ]); } // Check next render doesn't throw. windowOnError.mockReset(); console.warn.mockReset(); console.error.mockReset(); await act(() => { ReactDOM.render(<NoError />, container); }); expect(container.textContent).toBe('OK'); expect(windowOnError).not.toBeCalled(); expect(console.warn).not.toBeCalled(); if (__DEV__) { expect(console.error.mock.calls).toEqual([ [ expect.stringContaining( 'ReactDOM.render has not been supported since React 18', ), ], ]); } else { expect(console.error).not.toBeCalled(); } }); // @gate !disableLegacyMode it('logs passive effect errors without an error boundary', async () => { function Foo() { React.useEffect(() => { throw Error('Boom'); }, []); return null; } await act(async () => { ReactDOM.render(<Foo />, container); await waitForThrow('Boom'); }); // The top-level error was caught with try/catch, // so we don't see an error event. expect(windowOnError.mock.calls).toEqual([ [ expect.objectContaining({ message: 'Boom', }), ], ]); if (__DEV__) { expect(console.warn.mock.calls).toEqual([ [ // Formatting expect.stringContaining('%s'), // Addendum by React: expect.stringContaining('An error occurred in the <Foo> component'), expect.stringContaining('Consider adding an error boundary'), // The component stack is not added without the polyfill/devtools. // expect.stringContaining('Foo'), ], ]); expect(console.error.mock.calls).toEqual([ [ expect.stringContaining( 'ReactDOM.render has not been supported since React 18', ), ], ]); } else { expect(console.warn).not.toBeCalled(); expect(console.error).not.toBeCalled(); } // Check next render doesn't throw. windowOnError.mockReset(); console.warn.mockReset(); console.error.mockReset(); await act(() => { ReactDOM.render(<NoError />, container); }); expect(container.textContent).toBe('OK'); expect(windowOnError).not.toBeCalled(); expect(console.warn).not.toBeCalled(); if (__DEV__) { expect(console.error.mock.calls).toEqual([ [ expect.stringContaining( 'ReactDOM.render has not been supported since React 18', ), ], ]); } else { expect(console.error).not.toBeCalled(); } }); // @gate !disableLegacyMode it('logs passive effect errors with an error boundary', async () => { function Foo() { React.useEffect(() => { throw Error('Boom'); }, []); return null; } await act(() => { ReactDOM.render( <ErrorBoundary> <Foo /> </ErrorBoundary>, container, ); }); // The top-level error was caught with try/catch, // so we don't see an error event. expect(windowOnError).not.toBeCalled(); expect(console.warn).not.toBeCalled(); if (__DEV__) { expect(console.error.mock.calls).toEqual([ [ expect.stringContaining( 'ReactDOM.render has not been supported since React 18', ), ], [ // Formatting expect.stringContaining('%o'), expect.objectContaining({ message: 'Boom', }), // Addendum by React: expect.stringContaining( 'The above error occurred in the <Foo> component', ), expect.stringContaining('ErrorBoundary'), // The component stack is not added without the polyfill/devtools. // expect.stringContaining('Foo'), ], ]); } else { expect(console.error.mock.calls).toEqual([ [ // Reported by React with no extra message: expect.objectContaining({ message: 'Boom', }), ], ]); } // Check next render doesn't throw. windowOnError.mockReset(); console.warn.mockReset(); console.error.mockReset(); await act(() => { ReactDOM.render(<NoError />, container); }); expect(container.textContent).toBe('OK'); expect(windowOnError).not.toBeCalled(); expect(console.warn).not.toBeCalled(); if (__DEV__) { expect(console.error.mock.calls).toEqual([ [ expect.stringContaining( 'ReactDOM.render has not been supported since React 18', ), ], ]); } else { expect(console.warn).not.toBeCalled(); } }); }); });