Handle info, group, and groupCollapsed in Strict Mode logging (#25172)
* Handle info, group, and groupCollapsed in Strict Mode logging While working on the new Next.js router which heavily relies on useReducer I noticed that `group` and `groupCollapsed` which both take labels were showing as-is in the console for the second render/dispatch in Strict Mode logs. While looking at the code I found that `info` was also not instrumented. I've added additional handling for: - `info` - `group` - `groupCollapsed` * Remove console.log * Fix tests
Tim Neutkens committed
Sep 6, 2022 at 19:03 UTC
a9dc73cfd4db480b4cadf8624351004fe96f5a12
3 files changed
+83
-2
packages/react-devtools-shared/src/__tests__/console-test.js
+65
@@ -16,6 +16,8 @@ let fakeConsole;
16
let legacyRender;
17
let mockError;
18
let mockInfo;
19
+let mockGroup;
20
+let mockGroupCollapsed;
21
let mockLog;
22
let mockWarn;
23
let patchConsole;
@@ -25,6 +27,7 @@ let rendererID;
27
describe('console', () => {
28
beforeEach(() => {
29
const Console = require('react-devtools-shared/src/backend/console');
30
+
31
patchConsole = Console.patch;
32
unpatchConsole = Console.unpatch;
33
@@ -33,6 +36,8 @@ describe('console', () => {
36
// because Jest itself has hooks into it as does our test env setup.
37
mockError = jest.fn();
38
mockInfo = jest.fn();
39
+ mockGroup = jest.fn();
40
+ mockGroupCollapsed = jest.fn();
41
mockLog = jest.fn();
42
mockWarn = jest.fn();
43
fakeConsole = {
@@ -40,6 +45,8 @@ describe('console', () => {
45
info: mockInfo,
46
log: mockLog,
47
warn: mockWarn,
48
+ group: mockGroup,
49
+ groupCollapsed: mockGroupCollapsed,
50
};
51
52
Console.dangerous_setTargetConsoleForTesting(fakeConsole);
@@ -69,6 +76,8 @@ describe('console', () => {
76
expect(fakeConsole.info).toBe(mockInfo);
77
expect(fakeConsole.log).toBe(mockLog);
78
expect(fakeConsole.warn).not.toBe(mockWarn);
79
+ expect(fakeConsole.group).toBe(mockGroup);
80
+ expect(fakeConsole.groupCollapsed).toBe(mockGroupCollapsed);
81
});
82
83
// @reactVersion >=18.0
@@ -491,6 +500,9 @@ describe('console', () => {
500
fakeConsole.log('log');
501
fakeConsole.warn('warn');
502
fakeConsole.error('error');
503
+ fakeConsole.info('info');
504
+ fakeConsole.group('group');
505
+ fakeConsole.groupCollapsed('groupCollapsed');
506
return <div />;
507
}
508
@@ -528,6 +540,36 @@ describe('console', () => {
540
`color: ${process.env.DARK_MODE_DIMMED_ERROR_COLOR}`,
541
'error',
542
]);
543
+
544
+ expect(mockInfo).toHaveBeenCalledTimes(2);
545
+ expect(mockInfo.mock.calls[0]).toHaveLength(1);
546
+ expect(mockInfo.mock.calls[0][0]).toBe('info');
547
+ expect(mockInfo.mock.calls[1]).toHaveLength(3);
548
+ expect(mockInfo.mock.calls[1]).toEqual([
549
+ '%c%s',
550
+ `color: ${process.env.DARK_MODE_DIMMED_LOG_COLOR}`,
551
+ 'info',
552
+ ]);
553
+
554
+ expect(mockGroup).toHaveBeenCalledTimes(2);
555
+ expect(mockGroup.mock.calls[0]).toHaveLength(1);
556
+ expect(mockGroup.mock.calls[0][0]).toBe('group');
557
+ expect(mockGroup.mock.calls[1]).toHaveLength(3);
558
+ expect(mockGroup.mock.calls[1]).toEqual([
559
+ '%c%s',
560
+ `color: ${process.env.DARK_MODE_DIMMED_LOG_COLOR}`,
561
+ 'group',
562
+ ]);
563
+
564
+ expect(mockGroupCollapsed).toHaveBeenCalledTimes(2);
565
+ expect(mockGroupCollapsed.mock.calls[0]).toHaveLength(1);
566
+ expect(mockGroupCollapsed.mock.calls[0][0]).toBe('groupCollapsed');
567
+ expect(mockGroupCollapsed.mock.calls[1]).toHaveLength(3);
568
+ expect(mockGroupCollapsed.mock.calls[1]).toEqual([
569
+ '%c%s',
570
+ `color: ${process.env.DARK_MODE_DIMMED_LOG_COLOR}`,
571
+ 'groupCollapsed',
572
+ ]);
573
});
574
575
it('should not double log if hideConsoleLogsInStrictMode is enabled in Strict mode', () => {
@@ -538,9 +580,16 @@ describe('console', () => {
580
const root = ReactDOMClient.createRoot(container);
581
582
function App() {
583
+ console.log(
584
+ 'CALL',
585
+ global.__REACT_DEVTOOLS_HIDE_CONSOLE_LOGS_IN_STRICT_MODE__,
586
+ );
587
fakeConsole.log('log');
588
fakeConsole.warn('warn');
589
fakeConsole.error('error');
590
+ fakeConsole.info('info');
591
+ fakeConsole.group('group');
592
+ fakeConsole.groupCollapsed('groupCollapsed');
593
return <div />;
594
}
595
@@ -563,6 +612,18 @@ describe('console', () => {
612
expect(mockError).toHaveBeenCalledTimes(1);
613
expect(mockError.mock.calls[0]).toHaveLength(1);
614
expect(mockError.mock.calls[0][0]).toBe('error');
615
+
616
+ expect(mockInfo).toHaveBeenCalledTimes(1);
617
+ expect(mockInfo.mock.calls[0]).toHaveLength(1);
618
+ expect(mockInfo.mock.calls[0][0]).toBe('info');
619
+
620
+ expect(mockGroup).toHaveBeenCalledTimes(1);
621
+ expect(mockGroup.mock.calls[0]).toHaveLength(1);
622
+ expect(mockGroup.mock.calls[0][0]).toBe('group');
623
+
624
+ expect(mockGroupCollapsed).toHaveBeenCalledTimes(1);
625
+ expect(mockGroupCollapsed.mock.calls[0]).toHaveLength(1);
626
+ expect(mockGroupCollapsed.mock.calls[0][0]).toBe('groupCollapsed');
627
});
628
629
it('should double log in Strict mode initial render for extension', () => {
@@ -734,6 +795,8 @@ describe('console error', () => {
795
// because Jest itself has hooks into it as does our test env setup.
796
mockError = jest.fn();
797
mockInfo = jest.fn();
798
+ mockGroup = jest.fn();
799
+ mockGroupCollapsed = jest.fn();
800
mockLog = jest.fn();
801
mockWarn = jest.fn();
802
fakeConsole = {
@@ -741,6 +804,8 @@ describe('console error', () => {
804
info: mockInfo,
805
log: mockLog,
806
warn: mockWarn,
807
+ group: mockGroup,
808
+ groupCollapsed: mockGroupCollapsed,
809
};
810
811
Console.dangerous_setTargetConsoleForTesting(fakeConsole);
packages/react-devtools-shared/src/backend/console.js
+9
-1
@@ -301,7 +301,15 @@ let unpatchForStrictModeFn: null | (() => void) = null;
301
// NOTE: KEEP IN SYNC with src/hook.js:patchConsoleForInitialRenderInStrictMode
302
export function patchForStrictMode() {
303
if (consoleManagedByDevToolsDuringStrictMode) {
304
- const overrideConsoleMethods = ['error', 'trace', 'warn', 'log'];
304
+ const overrideConsoleMethods = [
305
+ 'error',
306
+ 'group',
307
+ 'groupCollapsed',
308
+ 'info',
309
+ 'log',
310
+ 'trace',
311
+ 'warn',
312
+ ];
313
314
if (unpatchForStrictModeFn !== null) {
315
// Don't patch twice.
packages/react-devtools-shared/src/hook.js
+9
-1
@@ -228,7 +228,15 @@ export function installHook(target: any): DevToolsHook | null {
228
hideConsoleLogsInStrictMode: boolean,
229
browserTheme: BrowserTheme,
230
}) {
231
- const overrideConsoleMethods = ['error', 'trace', 'warn', 'log'];
231
+ const overrideConsoleMethods = [
232
+ 'error',
233
+ 'group',
234
+ 'groupCollapsed',
235
+ 'info',
236
+ 'log',
237
+ 'trace',
238
+ 'warn',
239
+ ];
240
241
if (unpatchFn !== null) {
242
// Don't patch twice.