main
js 804 lines 24.5 KB
Raw
1 /**
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 *
7 * @flow
8 */
9
10 import {
11 getVersionedRenderImplementation,
12 normalizeCodeLocInfo,
13 } from 'react-devtools-shared/src/__tests__/utils';
14
15 let React;
16 let ReactDOMClient;
17 let act;
18 let rendererID;
19 let supportsOwnerStacks = false;
20
21 describe('console', () => {
22 beforeEach(() => {
23 const inject = global.__REACT_DEVTOOLS_GLOBAL_HOOK__.inject;
24 global.__REACT_DEVTOOLS_GLOBAL_HOOK__.inject = internals => {
25 rendererID = inject(internals);
26
27 return rendererID;
28 };
29
30 React = require('react');
31 if (
32 React.version.startsWith('19') &&
33 React.version.includes('experimental')
34 ) {
35 supportsOwnerStacks = true;
36 }
37 ReactDOMClient = require('react-dom/client');
38
39 const utils = require('./utils');
40 act = utils.act;
41 });
42
43 const {render} = getVersionedRenderImplementation();
44
45 // @reactVersion >= 18.0
46 it('should pass through logs when there is no current fiber', () => {
47 expect(global.consoleLogMock).toHaveBeenCalledTimes(0);
48 expect(global.consoleWarnMock).toHaveBeenCalledTimes(0);
49 expect(global.consoleErrorMock).toHaveBeenCalledTimes(0);
50
51 console.log('log');
52 console.warn('warn');
53 console.error('error');
54
55 expect(global.consoleLogMock.mock.calls).toEqual([['log']]);
56 expect(global.consoleWarnMock.mock.calls).toEqual([['warn']]);
57 expect(global.consoleErrorMock.mock.calls).toEqual([['error']]);
58 });
59
60 // @reactVersion >= 18.0
61 it('should not append multiple stacks', () => {
62 global.__REACT_DEVTOOLS_GLOBAL_HOOK__.settings.appendComponentStack = true;
63
64 const Child = ({children}) => {
65 console.warn('warn', '\n in Child (at fake.js:123)');
66 console.error('error', '\n in Child (at fake.js:123)');
67 return null;
68 };
69
70 act(() => render(<Child />));
71
72 expect(
73 global.consoleWarnMock.mock.calls[0].map(normalizeCodeLocInfo),
74 ).toEqual(['warn', '\n in Child (at **)']);
75 expect(
76 global.consoleErrorMock.mock.calls[0].map(normalizeCodeLocInfo),
77 ).toEqual(['error', '\n in Child (at **)']);
78 });
79
80 // @reactVersion >= 18.0
81 it('should append component stacks to errors and warnings logged during render', () => {
82 global.__REACT_DEVTOOLS_GLOBAL_HOOK__.settings.appendComponentStack = true;
83
84 const Intermediate = ({children}) => children;
85 const Parent = ({children}) => (
86 <Intermediate>
87 <Child />
88 </Intermediate>
89 );
90 const Child = ({children}) => {
91 console.error('error');
92 console.log('log');
93 console.warn('warn');
94 return null;
95 };
96
97 act(() => render(<Parent />));
98
99 expect(global.consoleLogMock.mock.calls).toEqual([['log']]);
100 expect(
101 global.consoleWarnMock.mock.calls[0].map(normalizeCodeLocInfo),
102 ).toEqual([
103 'warn',
104 supportsOwnerStacks
105 ? '\n in Child (at **)\n in Parent (at **)'
106 : '\n in Child (at **)\n in Intermediate (at **)\n in Parent (at **)',
107 ]);
108 expect(
109 global.consoleErrorMock.mock.calls[0].map(normalizeCodeLocInfo),
110 ).toEqual([
111 'error',
112 supportsOwnerStacks
113 ? '\n in Child (at **)\n in Parent (at **)'
114 : '\n in Child (at **)\n in Intermediate (at **)\n in Parent (at **)',
115 ]);
116 });
117
118 // @reactVersion >= 18.0
119 it('should append component stacks to errors and warnings logged from effects', () => {
120 const Intermediate = ({children}) => children;
121 const Parent = ({children}) => (
122 <Intermediate>
123 <Child />
124 </Intermediate>
125 );
126 const Child = ({children}) => {
127 React.useLayoutEffect(function Child_useLayoutEffect() {
128 console.error('active error');
129 console.log('active log');
130 console.warn('active warn');
131 });
132 React.useEffect(function Child_useEffect() {
133 console.error('passive error');
134 console.log('passive log');
135 console.warn('passive warn');
136 });
137 return null;
138 };
139
140 act(() => render(<Parent />));
141
142 expect(global.consoleLogMock.mock.calls).toEqual([
143 ['active log'],
144 ['passive log'],
145 ]);
146
147 expect(
148 global.consoleWarnMock.mock.calls[0].map(normalizeCodeLocInfo),
149 ).toEqual([
150 'active warn',
151 supportsOwnerStacks
152 ? '\n in Child_useLayoutEffect (at **)\n in Parent (at **)'
153 : '\n in Child (at **)\n in Intermediate (at **)\n in Parent (at **)',
154 ]);
155 expect(
156 global.consoleWarnMock.mock.calls[1].map(normalizeCodeLocInfo),
157 ).toEqual([
158 'passive warn',
159 supportsOwnerStacks
160 ? '\n in Child_useEffect (at **)\n in Parent (at **)'
161 : '\n in Child (at **)\n in Intermediate (at **)\n in Parent (at **)',
162 ]);
163
164 expect(
165 global.consoleErrorMock.mock.calls[0].map(normalizeCodeLocInfo),
166 ).toEqual([
167 'active error',
168 supportsOwnerStacks
169 ? '\n in Child_useLayoutEffect (at **)\n in Parent (at **)'
170 : '\n in Child (at **)\n in Intermediate (at **)\n in Parent (at **)',
171 ]);
172 expect(
173 global.consoleErrorMock.mock.calls[1].map(normalizeCodeLocInfo),
174 ).toEqual([
175 'passive error',
176 supportsOwnerStacks
177 ? '\n in Child_useEffect (at **)\n in Parent (at **)'
178 : '\n in Child (at **)\n in Intermediate (at **)\n in Parent (at **)',
179 ]);
180 });
181
182 // @reactVersion >= 18.0
183 it('should append component stacks to errors and warnings logged from commit hooks', () => {
184 global.__REACT_DEVTOOLS_GLOBAL_HOOK__.settings.appendComponentStack = true;
185
186 const Intermediate = ({children}) => children;
187 const Parent = ({children}) => (
188 <Intermediate>
189 <Child />
190 </Intermediate>
191 );
192 class Child extends React.Component<any> {
193 componentDidMount() {
194 console.error('didMount error');
195 console.log('didMount log');
196 console.warn('didMount warn');
197 }
198 componentDidUpdate() {
199 console.error('didUpdate error');
200 console.log('didUpdate log');
201 console.warn('didUpdate warn');
202 }
203 render() {
204 return null;
205 }
206 }
207
208 act(() => render(<Parent />));
209 act(() => render(<Parent />));
210
211 expect(global.consoleLogMock.mock.calls).toEqual([
212 ['didMount log'],
213 ['didUpdate log'],
214 ]);
215
216 expect(
217 global.consoleWarnMock.mock.calls[0].map(normalizeCodeLocInfo),
218 ).toEqual([
219 'didMount warn',
220 supportsOwnerStacks
221 ? '\n in Child.componentDidMount (at **)\n in Parent (at **)'
222 : '\n in Child (at **)\n in Intermediate (at **)\n in Parent (at **)',
223 ]);
224 expect(
225 global.consoleWarnMock.mock.calls[1].map(normalizeCodeLocInfo),
226 ).toEqual([
227 'didUpdate warn',
228 supportsOwnerStacks
229 ? '\n in Child.componentDidUpdate (at **)\n in Parent (at **)'
230 : '\n in Child (at **)\n in Intermediate (at **)\n in Parent (at **)',
231 ]);
232
233 expect(
234 global.consoleErrorMock.mock.calls[0].map(normalizeCodeLocInfo),
235 ).toEqual([
236 'didMount error',
237 supportsOwnerStacks
238 ? '\n in Child.componentDidMount (at **)\n in Parent (at **)'
239 : '\n in Child (at **)\n in Intermediate (at **)\n in Parent (at **)',
240 ]);
241 expect(
242 global.consoleErrorMock.mock.calls[1].map(normalizeCodeLocInfo),
243 ).toEqual([
244 'didUpdate error',
245 supportsOwnerStacks
246 ? '\n in Child.componentDidUpdate (at **)\n in Parent (at **)'
247 : '\n in Child (at **)\n in Intermediate (at **)\n in Parent (at **)',
248 ]);
249 });
250
251 // @reactVersion >= 18.0
252 it('should append component stacks to errors and warnings logged from gDSFP', () => {
253 const Intermediate = ({children}) => children;
254 const Parent = ({children}) => (
255 <Intermediate>
256 <Child />
257 </Intermediate>
258 );
259 class Child extends React.Component<any, any> {
260 state = {};
261 static getDerivedStateFromProps() {
262 console.error('error');
263 console.log('log');
264 console.warn('warn');
265 return null;
266 }
267 render() {
268 return null;
269 }
270 }
271
272 act(() => render(<Parent />));
273
274 expect(global.consoleLogMock.mock.calls).toEqual([['log']]);
275 expect(
276 global.consoleWarnMock.mock.calls[0].map(normalizeCodeLocInfo),
277 ).toEqual([
278 'warn',
279 supportsOwnerStacks
280 ? '\n in Parent (at **)'
281 : '\n in Child (at **)\n in Intermediate (at **)\n in Parent (at **)',
282 ]);
283 expect(
284 global.consoleErrorMock.mock.calls[0].map(normalizeCodeLocInfo),
285 ).toEqual([
286 'error',
287 supportsOwnerStacks
288 ? '\n in Parent (at **)'
289 : '\n in Child (at **)\n in Intermediate (at **)\n in Parent (at **)',
290 ]);
291 });
292
293 // @reactVersion >= 18.0
294 it('should be resilient to prepareStackTrace', () => {
295 Error.prepareStackTrace = function (error, callsites) {
296 const stack = ['An error occurred:', error.message];
297 for (let i = 0; i < callsites.length; i++) {
298 const callsite = callsites[i];
299 stack.push(
300 '\t' + callsite.getFunctionName(),
301 '\t\tat ' + callsite.getFileName(),
302 '\t\ton line ' + callsite.getLineNumber(),
303 );
304 }
305
306 return stack.join('\n');
307 };
308
309 const Intermediate = ({children}) => children;
310 const Parent = ({children}) => (
311 <Intermediate>
312 <Child />
313 </Intermediate>
314 );
315 const Child = ({children}) => {
316 console.error('error');
317 console.log('log');
318 console.warn('warn');
319 return null;
320 };
321
322 act(() => render(<Parent />));
323
324 expect(global.consoleLogMock.mock.calls).toEqual([['log']]);
325 expect(
326 global.consoleWarnMock.mock.calls[0].map(normalizeCodeLocInfo),
327 ).toEqual([
328 'warn',
329 supportsOwnerStacks
330 ? '\n in Child (at **)\n in Parent (at **)'
331 : '\n in Child (at **)\n in Intermediate (at **)\n in Parent (at **)',
332 ]);
333 expect(
334 global.consoleErrorMock.mock.calls[0].map(normalizeCodeLocInfo),
335 ).toEqual([
336 'error',
337 supportsOwnerStacks
338 ? '\n in Child (at **)\n in Parent (at **)'
339 : '\n in Child (at **)\n in Intermediate (at **)\n in Parent (at **)',
340 ]);
341 });
342
343 // @reactVersion >= 18.0
344 it('should correctly log Symbols', () => {
345 global.__REACT_DEVTOOLS_GLOBAL_HOOK__.settings.appendComponentStack = false;
346
347 const Component = ({children}) => {
348 console.warn('Symbol:', Symbol(''));
349 return null;
350 };
351
352 act(() => render(<Component />));
353
354 expect(global.consoleWarnMock.mock.calls).toMatchInlineSnapshot(`
355 [
356 [
357 "Symbol:",
358 Symbol(),
359 ],
360 ]
361 `);
362 });
363
364 it('should double log if hideConsoleLogsInStrictMode is disabled in Strict mode', () => {
365 global.__REACT_DEVTOOLS_GLOBAL_HOOK__.settings.appendComponentStack = false;
366 global.__REACT_DEVTOOLS_GLOBAL_HOOK__.settings.hideConsoleLogsInStrictMode = false;
367
368 const container = document.createElement('div');
369 const root = ReactDOMClient.createRoot(container);
370
371 function App() {
372 console.log('log');
373 console.warn('warn');
374 console.error('error');
375 return <div />;
376 }
377
378 act(() =>
379 root.render(
380 <React.StrictMode>
381 <App />
382 </React.StrictMode>,
383 ),
384 );
385
386 expect(global.consoleLogMock).toHaveBeenCalledTimes(2);
387 expect(global.consoleLogMock.mock.calls[1]).toEqual([
388 '\x1b[2;38;2;124;124;124m%s\x1b[0m',
389 'log',
390 ]);
391
392 expect(global.consoleWarnMock).toHaveBeenCalledTimes(2);
393 expect(global.consoleWarnMock.mock.calls[1]).toEqual([
394 '\x1b[2;38;2;124;124;124m%s\x1b[0m',
395 'warn',
396 ]);
397
398 expect(global.consoleErrorMock).toHaveBeenCalledTimes(2);
399 expect(global.consoleErrorMock.mock.calls[1]).toEqual([
400 '\x1b[2;38;2;124;124;124m%s\x1b[0m',
401 'error',
402 ]);
403 });
404
405 it('should not double log if hideConsoleLogsInStrictMode is enabled in Strict mode', () => {
406 global.__REACT_DEVTOOLS_GLOBAL_HOOK__.settings.appendComponentStack = false;
407 global.__REACT_DEVTOOLS_GLOBAL_HOOK__.settings.hideConsoleLogsInStrictMode = true;
408
409 const container = document.createElement('div');
410 const root = ReactDOMClient.createRoot(container);
411
412 function App() {
413 console.log('log');
414 console.warn('warn');
415 console.error('error');
416 return <div />;
417 }
418
419 act(() =>
420 root.render(
421 <React.StrictMode>
422 <App />
423 </React.StrictMode>,
424 ),
425 );
426
427 expect(global.consoleLogMock).toHaveBeenCalledTimes(1);
428 expect(global.consoleWarnMock).toHaveBeenCalledTimes(1);
429 expect(global.consoleErrorMock).toHaveBeenCalledTimes(1);
430 });
431
432 it('should double log from Effects if hideConsoleLogsInStrictMode is disabled in Strict mode', () => {
433 global.__REACT_DEVTOOLS_GLOBAL_HOOK__.settings.appendComponentStack = false;
434 global.__REACT_DEVTOOLS_GLOBAL_HOOK__.settings.hideConsoleLogsInStrictMode = false;
435
436 const container = document.createElement('div');
437 const root = ReactDOMClient.createRoot(container);
438
439 function App() {
440 React.useEffect(() => {
441 console.log('log effect create');
442 console.warn('warn effect create');
443 console.error('error effect create');
444
445 return () => {
446 console.log('log effect cleanup');
447 console.warn('warn effect cleanup');
448 console.error('error effect cleanup');
449 };
450 });
451
452 return <div />;
453 }
454
455 act(() =>
456 root.render(
457 <React.StrictMode>
458 <App />
459 </React.StrictMode>,
460 ),
461 );
462 expect(global.consoleLogMock.mock.calls).toEqual([
463 ['log effect create'],
464 ['\x1b[2;38;2;124;124;124m%s\x1b[0m', 'log effect cleanup'],
465 ['\x1b[2;38;2;124;124;124m%s\x1b[0m', 'log effect create'],
466 ]);
467 expect(global.consoleWarnMock.mock.calls).toEqual([
468 ['warn effect create'],
469 ['\x1b[2;38;2;124;124;124m%s\x1b[0m', 'warn effect cleanup'],
470 ['\x1b[2;38;2;124;124;124m%s\x1b[0m', 'warn effect create'],
471 ]);
472 expect(global.consoleErrorMock.mock.calls).toEqual([
473 ['error effect create'],
474 ['\x1b[2;38;2;124;124;124m%s\x1b[0m', 'error effect cleanup'],
475 ['\x1b[2;38;2;124;124;124m%s\x1b[0m', 'error effect create'],
476 ]);
477 });
478
479 it('should not double log from Effects if hideConsoleLogsInStrictMode is enabled in Strict mode', () => {
480 global.__REACT_DEVTOOLS_GLOBAL_HOOK__.settings.appendComponentStack = false;
481 global.__REACT_DEVTOOLS_GLOBAL_HOOK__.settings.hideConsoleLogsInStrictMode = true;
482
483 const container = document.createElement('div');
484 const root = ReactDOMClient.createRoot(container);
485
486 function App() {
487 React.useEffect(() => {
488 console.log('log effect create');
489 console.warn('warn effect create');
490 console.error('error effect create');
491
492 return () => {
493 console.log('log effect cleanup');
494 console.warn('warn effect cleanup');
495 console.error('error effect cleanup');
496 };
497 });
498
499 return <div />;
500 }
501
502 act(() =>
503 root.render(
504 <React.StrictMode>
505 <App />
506 </React.StrictMode>,
507 ),
508 );
509
510 expect(global.consoleLogMock).toHaveBeenCalledTimes(1);
511 expect(global.consoleWarnMock).toHaveBeenCalledTimes(1);
512 expect(global.consoleErrorMock).toHaveBeenCalledTimes(1);
513 });
514
515 it('should double log from useMemo if hideConsoleLogsInStrictMode is disabled in Strict mode', () => {
516 global.__REACT_DEVTOOLS_GLOBAL_HOOK__.settings.appendComponentStack = false;
517 global.__REACT_DEVTOOLS_GLOBAL_HOOK__.settings.hideConsoleLogsInStrictMode = false;
518
519 const container = document.createElement('div');
520 const root = ReactDOMClient.createRoot(container);
521
522 function App() {
523 React.useMemo(() => {
524 console.log('log');
525 console.warn('warn');
526 console.error('error');
527 }, []);
528 return <div />;
529 }
530
531 act(() =>
532 root.render(
533 <React.StrictMode>
534 <App />
535 </React.StrictMode>,
536 ),
537 );
538
539 expect(global.consoleLogMock).toHaveBeenCalledTimes(2);
540 expect(global.consoleLogMock.mock.calls[1]).toEqual([
541 '\x1b[2;38;2;124;124;124m%s\x1b[0m',
542 'log',
543 ]);
544
545 expect(global.consoleWarnMock).toHaveBeenCalledTimes(2);
546 expect(global.consoleWarnMock.mock.calls[1]).toEqual([
547 '\x1b[2;38;2;124;124;124m%s\x1b[0m',
548 'warn',
549 ]);
550
551 expect(global.consoleErrorMock).toHaveBeenCalledTimes(2);
552 expect(global.consoleErrorMock.mock.calls[1]).toEqual([
553 '\x1b[2;38;2;124;124;124m%s\x1b[0m',
554 'error',
555 ]);
556 });
557
558 it('should not double log from useMemo fns if hideConsoleLogsInStrictMode is enabled in Strict mode', () => {
559 global.__REACT_DEVTOOLS_GLOBAL_HOOK__.settings.appendComponentStack = false;
560 global.__REACT_DEVTOOLS_GLOBAL_HOOK__.settings.hideConsoleLogsInStrictMode = true;
561
562 const container = document.createElement('div');
563 const root = ReactDOMClient.createRoot(container);
564
565 function App() {
566 React.useMemo(() => {
567 console.log('log');
568 console.warn('warn');
569 console.error('error');
570 }, []);
571 return <div />;
572 }
573
574 act(() =>
575 root.render(
576 <React.StrictMode>
577 <App />
578 </React.StrictMode>,
579 ),
580 );
581
582 expect(global.consoleLogMock).toHaveBeenCalledTimes(1);
583 expect(global.consoleWarnMock).toHaveBeenCalledTimes(1);
584 expect(global.consoleErrorMock).toHaveBeenCalledTimes(1);
585 });
586
587 it('should double log in Strict mode initial render for extension', () => {
588 global.__REACT_DEVTOOLS_GLOBAL_HOOK__.settings.appendComponentStack = false;
589 global.__REACT_DEVTOOLS_GLOBAL_HOOK__.settings.hideConsoleLogsInStrictMode = false;
590
591 // This simulates a render that happens before React DevTools have finished
592 // their handshake to attach the React DOM renderer functions to DevTools
593 // In this case, we should still be able to mock the console in Strict mode
594 global.__REACT_DEVTOOLS_GLOBAL_HOOK__.rendererInterfaces.delete(rendererID);
595 const container = document.createElement('div');
596 const root = ReactDOMClient.createRoot(container);
597
598 function App() {
599 console.log('log');
600 console.warn('warn');
601 console.error('error');
602 return <div />;
603 }
604
605 act(() =>
606 root.render(
607 <React.StrictMode>
608 <App />
609 </React.StrictMode>,
610 ),
611 );
612
613 expect(global.consoleLogMock).toHaveBeenCalledTimes(2);
614 expect(global.consoleLogMock.mock.calls[1]).toEqual([
615 '\x1b[2;38;2;124;124;124m%s\x1b[0m',
616 'log',
617 ]);
618
619 expect(global.consoleWarnMock).toHaveBeenCalledTimes(2);
620 expect(global.consoleWarnMock.mock.calls[1]).toEqual([
621 '\x1b[2;38;2;124;124;124m%s\x1b[0m',
622 'warn',
623 ]);
624
625 expect(global.consoleErrorMock).toHaveBeenCalledTimes(2);
626 expect(global.consoleErrorMock.mock.calls[1]).toEqual([
627 '\x1b[2;38;2;124;124;124m%s\x1b[0m',
628 'error',
629 ]);
630 });
631
632 it('should not double log in Strict mode initial render for extension', () => {
633 global.__REACT_DEVTOOLS_GLOBAL_HOOK__.settings.appendComponentStack = false;
634 global.__REACT_DEVTOOLS_GLOBAL_HOOK__.settings.hideConsoleLogsInStrictMode = true;
635
636 // This simulates a render that happens before React DevTools have finished
637 // their handshake to attach the React DOM renderer functions to DevTools
638 // In this case, we should still be able to mock the console in Strict mode
639 global.__REACT_DEVTOOLS_GLOBAL_HOOK__.rendererInterfaces.delete(rendererID);
640 const container = document.createElement('div');
641 const root = ReactDOMClient.createRoot(container);
642
643 function App() {
644 console.log('log');
645 console.warn('warn');
646 console.error('error');
647 return <div />;
648 }
649
650 act(() =>
651 root.render(
652 <React.StrictMode>
653 <App />
654 </React.StrictMode>,
655 ),
656 );
657
658 expect(global.consoleLogMock).toHaveBeenCalledTimes(1);
659 expect(global.consoleWarnMock).toHaveBeenCalledTimes(1);
660 expect(global.consoleErrorMock).toHaveBeenCalledTimes(1);
661 });
662
663 it('should properly dim component stacks during strict mode double log', () => {
664 global.__REACT_DEVTOOLS_GLOBAL_HOOK__.settings.appendComponentStack = true;
665 global.__REACT_DEVTOOLS_GLOBAL_HOOK__.settings.hideConsoleLogsInStrictMode = false;
666
667 const container = document.createElement('div');
668 const root = ReactDOMClient.createRoot(container);
669
670 const Intermediate = ({children}) => children;
671 const Parent = ({children}) => (
672 <Intermediate>
673 <Child />
674 </Intermediate>
675 );
676 const Child = ({children}) => {
677 console.error('error');
678 console.warn('warn');
679 return null;
680 };
681
682 act(() =>
683 root.render(
684 <React.StrictMode>
685 <Parent />
686 </React.StrictMode>,
687 ),
688 );
689
690 expect(
691 global.consoleWarnMock.mock.calls[0].map(normalizeCodeLocInfo),
692 ).toEqual([
693 'warn',
694 supportsOwnerStacks
695 ? '\n in Child (at **)\n in Parent (at **)'
696 : '\n in Child (at **)\n in Intermediate (at **)\n in Parent (at **)',
697 ]);
698
699 expect(
700 global.consoleWarnMock.mock.calls[1].map(normalizeCodeLocInfo),
701 ).toEqual([
702 '\x1b[2;38;2;124;124;124m%s %o\x1b[0m',
703 'warn',
704 supportsOwnerStacks
705 ? '\n in Child (at **)\n in Parent (at **)'
706 : 'in Child (at **)\n in Intermediate (at **)\n in Parent (at **)',
707 ]);
708
709 expect(
710 global.consoleErrorMock.mock.calls[0].map(normalizeCodeLocInfo),
711 ).toEqual([
712 'error',
713 supportsOwnerStacks
714 ? '\n in Child (at **)\n in Parent (at **)'
715 : '\n in Child (at **)\n in Intermediate (at **)\n in Parent (at **)',
716 ]);
717 expect(
718 global.consoleErrorMock.mock.calls[1].map(normalizeCodeLocInfo),
719 ).toEqual([
720 '\x1b[2;38;2;124;124;124m%s %o\x1b[0m',
721 'error',
722 supportsOwnerStacks
723 ? '\n in Child (at **)\n in Parent (at **)'
724 : 'in Child (at **)\n in Intermediate (at **)\n in Parent (at **)',
725 ]);
726 });
727
728 it('should not dim console logs if disableSecondConsoleLogDimmingInStrictMode is enabled', () => {
729 global.__REACT_DEVTOOLS_GLOBAL_HOOK__.settings.appendComponentStack = false;
730 global.__REACT_DEVTOOLS_GLOBAL_HOOK__.settings.hideConsoleLogsInStrictMode = false;
731 global.__REACT_DEVTOOLS_GLOBAL_HOOK__.settings.disableSecondConsoleLogDimmingInStrictMode = true;
732
733 const container = document.createElement('div');
734 const root = ReactDOMClient.createRoot(container);
735
736 function App() {
737 console.log('log');
738 console.warn('warn');
739 console.error('error');
740 return <div />;
741 }
742
743 act(() =>
744 root.render(
745 <React.StrictMode>
746 <App />
747 </React.StrictMode>,
748 ),
749 );
750
751 // Both logs should be called (double logging)
752 expect(global.consoleLogMock).toHaveBeenCalledTimes(2);
753 expect(global.consoleWarnMock).toHaveBeenCalledTimes(2);
754 expect(global.consoleErrorMock).toHaveBeenCalledTimes(2);
755
756 // The second log should NOT have dimming (no ANSI codes)
757 expect(global.consoleLogMock.mock.calls[1]).toEqual(['log']);
758 expect(global.consoleWarnMock.mock.calls[1]).toEqual(['warn']);
759 expect(global.consoleErrorMock.mock.calls[1]).toEqual(['error']);
760 });
761
762 it('should dim console logs if disableSecondConsoleLogDimmingInStrictMode is disabled', () => {
763 global.__REACT_DEVTOOLS_GLOBAL_HOOK__.settings.appendComponentStack = false;
764 global.__REACT_DEVTOOLS_GLOBAL_HOOK__.settings.hideConsoleLogsInStrictMode = false;
765 global.__REACT_DEVTOOLS_GLOBAL_HOOK__.settings.disableSecondConsoleLogDimmingInStrictMode = false;
766
767 const container = document.createElement('div');
768 const root = ReactDOMClient.createRoot(container);
769
770 function App() {
771 console.log('log');
772 console.warn('warn');
773 console.error('error');
774 return <div />;
775 }
776
777 act(() =>
778 root.render(
779 <React.StrictMode>
780 <App />
781 </React.StrictMode>,
782 ),
783 );
784
785 // Both logs should be called (double logging)
786 expect(global.consoleLogMock).toHaveBeenCalledTimes(2);
787 expect(global.consoleWarnMock).toHaveBeenCalledTimes(2);
788 expect(global.consoleErrorMock).toHaveBeenCalledTimes(2);
789
790 // The second log should have dimming (ANSI codes present)
791 expect(global.consoleLogMock.mock.calls[1]).toEqual([
792 '\x1b[2;38;2;124;124;124m%s\x1b[0m',
793 'log',
794 ]);
795 expect(global.consoleWarnMock.mock.calls[1]).toEqual([
796 '\x1b[2;38;2;124;124;124m%s\x1b[0m',
797 'warn',
798 ]);
799 expect(global.consoleErrorMock.mock.calls[1]).toEqual([
800 '\x1b[2;38;2;124;124;124m%s\x1b[0m',
801 'error',
802 ]);
803 });
804 });