[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
}