@samitouri / QOS-React-2 / commits / 597ecd6a0c

[DevTools] Throw error in console without interfering with logs (#22175)

Luna Ruan committed Aug 30, 2021 at 14:37 UTC 597ecd6a0ceebef63edc4c6bc501fe3695790fc6
2 files changed +103 -12
packages/react-devtools-shared/src/__tests__/console-test.js
+100 -12
@@ -6,20 +6,19 @@
6 *
7 * @flow
8 */
9 +let React;
10 +let ReactDOM;
11 +let act;
12 +let fakeConsole;
13 +let legacyRender;
14 +let mockError;
15 +let mockInfo;
16 +let mockLog;
17 +let mockWarn;
18 +let patchConsole;
19 +let unpatchConsole;
20
21 describe('console', () => {
11 - let React;
12 - let ReactDOM;
13 - let act;
14 - let fakeConsole;
15 - let legacyRender;
16 - let mockError;
17 - let mockInfo;
18 - let mockLog;
19 - let mockWarn;
20 - let patchConsole;
21 - let unpatchConsole;
22 -
22 beforeEach(() => {
23 jest.resetModules();
24
@@ -553,3 +552,92 @@ describe('console', () => {
552 expect(mockError.mock.calls[0][0]).toBe('error');
553 });
554 });
555 +
556 +describe('console error', () => {
557 + beforeEach(() => {
558 + jest.resetModules();
559 +
560 + const Console = require('react-devtools-shared/src/backend/console');
561 + patchConsole = Console.patch;
562 + unpatchConsole = Console.unpatch;
563 +
564 + // Patch a fake console so we can verify with tests below.
565 + // Patching the real console is too complicated,
566 + // because Jest itself has hooks into it as does our test env setup.
567 + mockError = jest.fn();
568 + mockInfo = jest.fn();
569 + mockLog = jest.fn();
570 + mockWarn = jest.fn();
571 + fakeConsole = {
572 + error: mockError,
573 + info: mockInfo,
574 + log: mockLog,
575 + warn: mockWarn,
576 + };
577 +
578 + Console.dangerous_setTargetConsoleForTesting(fakeConsole);
579 +
580 + // Note the Console module only patches once,
581 + // so it's important to patch the test console before injection.
582 + patchConsole({
583 + appendComponentStack: true,
584 + breakOnWarn: false,
585 + showInlineWarningsAndErrors: false,
586 + hideDoubleLogsInStrictLegacy: false,
587 + });
588 +
589 + const inject = global.__REACT_DEVTOOLS_GLOBAL_HOOK__.inject;
590 + global.__REACT_DEVTOOLS_GLOBAL_HOOK__.inject = internals => {
591 + internals.getIsStrictMode = () => {
592 + throw Error('foo');
593 + };
594 + inject(internals);
595 +
596 + Console.registerRenderer(internals);
597 + };
598 +
599 + React = require('react');
600 + ReactDOM = require('react-dom');
601 +
602 + const utils = require('./utils');
603 + act = utils.act;
604 + legacyRender = utils.legacyRender;
605 + });
606 +
607 + it('error in console log throws without interfering with logging', () => {
608 + const container = document.createElement('div');
609 + const root = ReactDOM.createRoot(container);
610 +
611 + function App() {
612 + fakeConsole.log('log');
613 + fakeConsole.warn('warn');
614 + fakeConsole.error('error');
615 + return <div />;
616 + }
617 +
618 + patchConsole({
619 + appendComponentStack: true,
620 + breakOnWarn: false,
621 + showInlineWarningsAndErrors: false,
622 + hideConsoleLogsInStrictMode: false,
623 + });
624 +
625 + expect(() => {
626 + act(() => {
627 + root.render(<App />);
628 + });
629 + }).toThrowError('foo');
630 +
631 + expect(mockLog).toHaveBeenCalledTimes(1);
632 + expect(mockLog.mock.calls[0]).toHaveLength(1);
633 + expect(mockLog.mock.calls[0][0]).toBe('log');
634 +
635 + expect(mockWarn).toHaveBeenCalledTimes(1);
636 + expect(mockWarn.mock.calls[0]).toHaveLength(1);
637 + expect(mockWarn.mock.calls[0][0]).toBe('warn');
638 +
639 + expect(mockError).toHaveBeenCalledTimes(1);
640 + expect(mockError.mock.calls[0]).toHaveLength(1);
641 + expect(mockError.mock.calls[0][0]).toBe('error');
642 + });
643 +});
packages/react-devtools-shared/src/backend/console.js
+3
@@ -222,6 +222,9 @@ export function patch({
222 }
223 } catch (error) {
224 // Don't let a DevTools or React internal error interfere with logging.
225 + setTimeout(() => {
226 + throw error;
227 + }, 0);
228 } finally {
229 break;
230 }