main
js 2,406 lines 65.6 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 * @emails react-core
8 * @jest-environment node
9 */
10
11 'use strict';
12
13 const React = require('react');
14 const stripAnsi = require('strip-ansi');
15 const {startTransition, useDeferredValue} = React;
16 const ReactNoop = require('react-noop-renderer');
17 const {
18 waitFor,
19 waitForAll,
20 waitForPaint,
21 waitForThrow,
22 assertLog,
23 } = require('internal-test-utils');
24 const act = require('internal-test-utils').act;
25 const Scheduler = require('scheduler/unstable_mock');
26 const {
27 assertConsoleLogsCleared,
28 resetAllUnexpectedConsoleCalls,
29 patchConsoleMethods,
30 } = require('../consoleMock');
31 const {
32 assertConsoleLogDev,
33 assertConsoleWarnDev,
34 assertConsoleErrorDev,
35 } = require('../ReactInternalTestUtils');
36
37 describe('ReactInternalTestUtils', () => {
38 it('waitFor', async () => {
39 const Yield = ({id}) => {
40 Scheduler.log(id);
41 return id;
42 };
43
44 const root = ReactNoop.createRoot();
45 startTransition(() => {
46 root.render(
47 <div>
48 <Yield id="foo" />
49 <Yield id="bar" />
50 <Yield id="baz" />
51 </div>
52 );
53 });
54
55 await waitFor(['foo', 'bar']);
56 expect(root).toMatchRenderedOutput(null);
57 await waitFor(['baz']);
58 expect(root).toMatchRenderedOutput(null);
59 await waitForAll([]);
60 expect(root).toMatchRenderedOutput(<div>foobarbaz</div>);
61 });
62
63 it('waitForAll', async () => {
64 const Yield = ({id}) => {
65 Scheduler.log(id);
66 return id;
67 };
68
69 const root = ReactNoop.createRoot();
70 startTransition(() => {
71 root.render(
72 <div>
73 <Yield id="foo" />
74 <Yield id="bar" />
75 <Yield id="baz" />
76 </div>
77 );
78 });
79
80 await waitForAll(['foo', 'bar', 'baz']);
81 expect(root).toMatchRenderedOutput(<div>foobarbaz</div>);
82 });
83
84 it('waitForThrow', async () => {
85 const Yield = ({id}) => {
86 Scheduler.log(id);
87 return id;
88 };
89
90 function BadRender() {
91 throw new Error('Oh no!');
92 }
93
94 function App() {
95 return (
96 <div>
97 <Yield id="A" />
98 <Yield id="B" />
99 <BadRender />
100 <Yield id="C" />
101 <Yield id="D" />
102 </div>
103 );
104 }
105
106 const root = ReactNoop.createRoot();
107 root.render(<App />);
108
109 await waitForThrow('Oh no!');
110 assertLog([
111 'A',
112 'B',
113 // React will try one more time before giving up.
114 'A',
115 'B',
116 ]);
117 });
118
119 it('waitForPaint', async () => {
120 function App({prop}) {
121 const deferred = useDeferredValue(prop);
122 const text = `Urgent: ${prop}, Deferred: ${deferred}`;
123 Scheduler.log(text);
124 return text;
125 }
126
127 const root = ReactNoop.createRoot();
128 root.render(<App prop="A" />);
129
130 await waitForAll(['Urgent: A, Deferred: A']);
131 expect(root).toMatchRenderedOutput('Urgent: A, Deferred: A');
132
133 // This update will result in two separate paints: an urgent one, and a
134 // deferred one.
135 root.render(<App prop="B" />);
136 // Urgent paint
137 await waitForPaint(['Urgent: B, Deferred: A']);
138 expect(root).toMatchRenderedOutput('Urgent: B, Deferred: A');
139
140 // Deferred paint
141 await waitForPaint(['Urgent: B, Deferred: B']);
142 expect(root).toMatchRenderedOutput('Urgent: B, Deferred: B');
143 });
144
145 it('assertLog', async () => {
146 const Yield = ({id}) => {
147 Scheduler.log(id);
148 React.useEffect(() => {
149 Scheduler.log(`create effect ${id}`);
150 return () => {
151 Scheduler.log(`cleanup effect ${id}`);
152 };
153 });
154 return id;
155 };
156
157 function App() {
158 return (
159 <div>
160 <Yield id="A" />
161 <Yield id="B" />
162 <Yield id="C" />
163 </div>
164 );
165 }
166
167 const root = ReactNoop.createRoot();
168 await act(() => {
169 root.render(
170 <React.StrictMode>
171 <App />
172 </React.StrictMode>
173 );
174 });
175 assertLog([
176 'A',
177 'B',
178 'C',
179 'create effect A',
180 'create effect B',
181 'create effect C',
182 ]);
183
184 await act(() => {
185 root.render(null);
186 });
187
188 assertLog(['cleanup effect A', 'cleanup effect B', 'cleanup effect C']);
189 });
190 });
191
192 describe('ReactInternalTestUtils console mocks', () => {
193 beforeEach(() => {
194 jest.resetAllMocks();
195 patchConsoleMethods({includeLog: true});
196 });
197
198 afterEach(() => {
199 resetAllUnexpectedConsoleCalls();
200 jest.resetAllMocks();
201 });
202
203 describe('console.log', () => {
204 it('should fail if not asserted', () => {
205 expect(() => {
206 console.log('hit');
207 assertConsoleLogsCleared();
208 }).toThrow(`console.log was called without assertConsoleLogDev`);
209 });
210
211 it('should not fail if mocked with spyOnDev', () => {
212 spyOnDev(console, 'log').mockImplementation(() => {});
213 expect(() => {
214 if (__DEV__) {
215 console.log('hit');
216 }
217 assertConsoleLogsCleared();
218 }).not.toThrow();
219 });
220
221 // @gate !__DEV__
222 it('should not fail if mocked with spyOnProd', () => {
223 spyOnProd(console, 'log').mockImplementation(() => {});
224 expect(() => {
225 console.log('hit');
226 assertConsoleLogsCleared();
227 }).not.toThrow();
228 });
229
230 it('should not fail if mocked with spyOnDevAndProd', () => {
231 spyOnDevAndProd(console, 'log').mockImplementation(() => {});
232 expect(() => {
233 console.log('hit');
234 assertConsoleLogsCleared();
235 }).not.toThrow();
236 });
237 });
238
239 describe('console.warn', () => {
240 it('should fail if not asserted', () => {
241 expect(() => {
242 console.warn('hit');
243 assertConsoleLogsCleared();
244 }).toThrow('console.warn was called without assertConsoleWarnDev');
245 });
246
247 it('should not fail if mocked with spyOnDev', () => {
248 spyOnDev(console, 'warn').mockImplementation(() => {});
249 expect(() => {
250 if (__DEV__) {
251 console.warn('hit');
252 }
253 assertConsoleLogsCleared();
254 }).not.toThrow();
255 });
256
257 // @gate !__DEV__
258 it('should not fail if mocked with spyOnProd', () => {
259 spyOnProd(console, 'warn').mockImplementation(() => {});
260 expect(() => {
261 console.warn('hit');
262 assertConsoleLogsCleared();
263 }).not.toThrow();
264 });
265
266 it('should not fail if mocked with spyOnDevAndProd', () => {
267 spyOnDevAndProd(console, 'warn').mockImplementation(() => {});
268 expect(() => {
269 console.warn('hit');
270 assertConsoleLogsCleared();
271 }).not.toThrow();
272 });
273 });
274
275 describe('console.error', () => {
276 it('should fail if console.error is not asserted', () => {
277 expect(() => {
278 console.error('hit');
279 assertConsoleLogsCleared();
280 }).toThrow('console.error was called without assertConsoleErrorDev');
281 });
282
283 it('should not fail if mocked with spyOnDev', () => {
284 spyOnDev(console, 'error').mockImplementation(() => {});
285 expect(() => {
286 if (__DEV__) {
287 console.error('hit');
288 }
289 assertConsoleLogsCleared();
290 }).not.toThrow();
291 });
292
293 // @gate !__DEV__
294 it('should not fail if mocked with spyOnProd', () => {
295 spyOnProd(console, 'error').mockImplementation(() => {});
296 expect(() => {
297 console.error('hit');
298 assertConsoleLogsCleared();
299 }).not.toThrow();
300 });
301
302 it('should not fail if mocked with spyOnDevAndProd', () => {
303 spyOnDevAndProd(console, 'error').mockImplementation(() => {});
304 expect(() => {
305 console.error('hit');
306 assertConsoleLogsCleared();
307 }).not.toThrow();
308 });
309 });
310 });
311
312 // Helper method to capture assertion failure.
313 const expectToThrowFailure = expectBlock => {
314 let caughtError;
315 try {
316 expectBlock();
317 } catch (error) {
318 caughtError = error;
319 }
320 expect(caughtError).toBeDefined();
321 return stripAnsi(caughtError.message);
322 };
323
324 // Helper method to capture assertion failure with act.
325 const awaitExpectToThrowFailure = async expectBlock => {
326 let caughtError;
327 try {
328 await expectBlock();
329 } catch (error) {
330 caughtError = error;
331 }
332 expect(caughtError).toBeDefined();
333 return stripAnsi(caughtError.message);
334 };
335
336 describe('ReactInternalTestUtils console assertions', () => {
337 beforeAll(() => {
338 patchConsoleMethods({includeLog: true});
339 });
340
341 describe('assertConsoleLogDev', () => {
342 it('passes for a single log', () => {
343 if (__DEV__) {
344 console.log('Hello');
345 }
346 assertConsoleLogDev(['Hello']);
347 });
348
349 it('passes for multiple logs', () => {
350 if (__DEV__) {
351 console.log('Hello');
352 console.log('Good day');
353 console.log('Bye');
354 }
355 assertConsoleLogDev(['Hello', 'Good day', 'Bye']);
356 });
357
358 it('fails if act is called without assertConsoleLogDev', async () => {
359 const Yield = ({id}) => {
360 console.log(id);
361 return id;
362 };
363
364 function App() {
365 return (
366 <div>
367 <Yield id="A" />
368 <Yield id="B" />
369 <Yield id="C" />
370 </div>
371 );
372 }
373
374 const root = ReactNoop.createRoot();
375 await act(() => {
376 root.render(<App />);
377 });
378 const message = await awaitExpectToThrowFailure(async () => {
379 await act(() => {
380 root.render(<App />);
381 });
382 });
383
384 expect(message).toMatchInlineSnapshot(`
385 "asserConsoleLogsCleared(expected)
386
387 console.log was called without assertConsoleLogDev:
388 + A
389 + B
390 + C
391
392 You must call one of the assertConsoleDev helpers between each act call."
393 `);
394 });
395
396 // @gate __DEV__
397 it('fails if first expected log is not included', () => {
398 const message = expectToThrowFailure(() => {
399 console.log('Wow');
400 console.log('Bye');
401 assertConsoleLogDev(['Hi', 'Wow', 'Bye']);
402 });
403 expect(message).toMatchInlineSnapshot(`
404 "assertConsoleLogDev(expected)
405
406 Unexpected log(s) recorded.
407
408 - Expected logs
409 + Received logs
410
411 - Hi
412 Wow
413 Bye"
414 `);
415 });
416
417 // @gate __DEV__
418 it('fails if middle expected log is not included', () => {
419 const message = expectToThrowFailure(() => {
420 console.log('Hi');
421 console.log('Bye');
422 assertConsoleLogDev(['Hi', 'Wow', 'Bye']);
423 });
424 expect(message).toMatchInlineSnapshot(`
425 "assertConsoleLogDev(expected)
426
427 Unexpected log(s) recorded.
428
429 - Expected logs
430 + Received logs
431
432 Hi
433 - Wow
434 Bye"
435 `);
436 });
437
438 // @gate __DEV__
439 it('fails if last expected log is not included', () => {
440 const message = expectToThrowFailure(() => {
441 console.log('Hi');
442 console.log('Wow');
443 assertConsoleLogDev(['Hi', 'Wow', 'Bye']);
444 });
445 expect(message).toMatchInlineSnapshot(`
446 "assertConsoleLogDev(expected)
447
448 Expected log was not recorded.
449
450 - Expected logs
451 + Received logs
452
453 Hi
454 Wow
455 - Bye"
456 `);
457 });
458
459 // @gate __DEV__
460 it('fails if first received log is not included', () => {
461 const message = expectToThrowFailure(() => {
462 console.log('Hi');
463 console.log('Wow');
464 console.log('Bye');
465 assertConsoleLogDev(['Wow', 'Bye']);
466 });
467 expect(message).toMatchInlineSnapshot(`
468 "assertConsoleLogDev(expected)
469
470 Unexpected log(s) recorded.
471
472 - Expected logs
473 + Received logs
474
475 + Hi
476 Wow
477 Bye"
478 `);
479 });
480
481 // @gate __DEV__
482 it('fails if middle received log is not included', () => {
483 const message = expectToThrowFailure(() => {
484 console.log('Hi');
485 console.log('Wow');
486 console.log('Bye');
487 assertConsoleLogDev(['Hi', 'Bye']);
488 });
489 expect(message).toMatchInlineSnapshot(`
490 "assertConsoleLogDev(expected)
491
492 Unexpected log(s) recorded.
493
494 - Expected logs
495 + Received logs
496
497 Hi
498 + Wow
499 Bye"
500 `);
501 });
502
503 // @gate __DEV__
504 it('fails if last received log is not included', () => {
505 const message = expectToThrowFailure(() => {
506 console.log('Hi');
507 console.log('Wow');
508 console.log('Bye');
509 assertConsoleLogDev(['Hi', 'Wow']);
510 });
511 expect(message).toMatchInlineSnapshot(`
512 "assertConsoleLogDev(expected)
513
514 Unexpected log(s) recorded.
515
516 - Expected logs
517 + Received logs
518
519 Hi
520 Wow
521 + Bye"
522 `);
523 });
524
525 // @gate __DEV__
526 it('fails if both expected and received mismatch', () => {
527 const message = expectToThrowFailure(() => {
528 console.log('Hi');
529 console.log('Wow');
530 console.log('Bye');
531 assertConsoleLogDev(['Hi', 'Wow', 'Yikes']);
532 });
533 expect(message).toMatchInlineSnapshot(`
534 "assertConsoleLogDev(expected)
535
536 Unexpected log(s) recorded.
537
538 - Expected logs
539 + Received logs
540
541 Hi
542 Wow
543 - Yikes
544 + Bye"
545 `);
546 });
547
548 // @gate __DEV__
549 it('fails if both expected and received mismatch with multiple lines', () => {
550 const message = expectToThrowFailure(() => {
551 console.log('Hi\nFoo');
552 console.log('Wow\nBar');
553 console.log('Bye\nBaz');
554 assertConsoleLogDev(['Hi\nFoo', 'Wow\nBar', 'Yikes\nFaz']);
555 });
556 expect(message).toMatchInlineSnapshot(`
557 "assertConsoleLogDev(expected)
558
559 Unexpected log(s) recorded.
560
561 - Expected logs
562 + Received logs
563
564 Hi Foo
565 Wow Bar
566 - Yikes Faz
567 + Bye Baz"
568 `);
569 });
570
571 // @gate __DEV__
572 it('fails if the args is greater than %s argument number', () => {
573 const message = expectToThrowFailure(() => {
574 console.log('Hi %s', 'Sara', 'extra');
575 assertConsoleLogDev(['Hi']);
576 });
577 expect(message).toMatchInlineSnapshot(`
578 "assertConsoleLogDev(expected)
579
580 Received 2 arguments for a message with 1 placeholders:
581 "Hi %s""
582 `);
583 });
584
585 // @gate __DEV__
586 it('fails if the args is greater than %s argument number for multiple logs', () => {
587 const message = expectToThrowFailure(() => {
588 console.log('Hi %s', 'Sara', 'extra');
589 console.log('Bye %s', 'Sara', 'extra');
590 assertConsoleLogDev(['Hi', 'Bye']);
591 });
592 expect(message).toMatchInlineSnapshot(`
593 "assertConsoleLogDev(expected)
594
595 Received 2 arguments for a message with 1 placeholders:
596 "Hi %s"
597
598 Received 2 arguments for a message with 1 placeholders:
599 "Bye %s""
600 `);
601 });
602
603 // @gate __DEV__
604 it('fails if the %s argument number is greater than args', () => {
605 const message = expectToThrowFailure(() => {
606 console.log('Hi %s');
607 assertConsoleLogDev(['Hi']);
608 });
609 expect(message).toMatchInlineSnapshot(`
610 "assertConsoleLogDev(expected)
611
612 Received 0 arguments for a message with 1 placeholders:
613 "Hi %s""
614 `);
615 });
616
617 // @gate __DEV__
618 it('fails if the %s argument number is greater than args for multiple logs', () => {
619 const message = expectToThrowFailure(() => {
620 console.log('Hi %s');
621 console.log('Bye %s');
622 assertConsoleLogDev(['Hi', 'Bye']);
623 });
624 expect(message).toMatchInlineSnapshot(`
625 "assertConsoleLogDev(expected)
626
627 Received 0 arguments for a message with 1 placeholders:
628 "Hi %s"
629
630 Received 0 arguments for a message with 1 placeholders:
631 "Bye %s""
632 `);
633 });
634
635 // @gate __DEV__
636 it('fails if first arg is not an array', () => {
637 const message = expectToThrowFailure(() => {
638 console.log('Hi');
639 console.log('Bye');
640 assertConsoleLogDev('Hi', 'Bye');
641 });
642 expect(message).toMatchInlineSnapshot(`
643 "assertConsoleLogDev(expected)
644
645 Expected messages should be an array of strings but was given type "string"."
646 `);
647
648 assertConsoleLogDev(['Hi', 'Bye']);
649 });
650
651 it('should fail if waitFor is called before asserting', async () => {
652 const Yield = ({id}) => {
653 Scheduler.log(id);
654 return id;
655 };
656
657 const root = ReactNoop.createRoot();
658 startTransition(() => {
659 root.render(
660 <div>
661 <Yield id="foo" />
662 <Yield id="bar" />
663 <Yield id="baz" />
664 </div>
665 );
666 });
667
668 console.log('Not asserted');
669
670 const message = await awaitExpectToThrowFailure(async () => {
671 await waitFor(['foo', 'bar']);
672 });
673 expect(message).toMatchInlineSnapshot(`
674 "asserConsoleLogsCleared(expected)
675
676 console.log was called without assertConsoleLogDev:
677 + Not asserted
678
679 You must call one of the assertConsoleDev helpers between each act call."
680 `);
681
682 await waitForAll(['foo', 'bar', 'baz']);
683 });
684
685 it('should fail if waitForThrow is called before asserting', async () => {
686 const Yield = ({id}) => {
687 Scheduler.log(id);
688 return id;
689 };
690
691 function BadRender() {
692 throw new Error('Oh no!');
693 }
694
695 function App() {
696 return (
697 <div>
698 <Yield id="A" />
699 <Yield id="B" />
700 <BadRender />
701 <Yield id="C" />
702 <Yield id="D" />
703 </div>
704 );
705 }
706
707 const root = ReactNoop.createRoot();
708 root.render(<App />);
709
710 console.log('Not asserted');
711
712 const message = await awaitExpectToThrowFailure(async () => {
713 await waitForThrow('Oh no!');
714 });
715 expect(message).toMatchInlineSnapshot(`
716 "asserConsoleLogsCleared(expected)
717
718 console.log was called without assertConsoleLogDev:
719 + Not asserted
720
721 You must call one of the assertConsoleDev helpers between each act call."
722 `);
723
724 await waitForAll(['A', 'B', 'A', 'B']);
725 });
726
727 it('should fail if waitForPaint is called before asserting', async () => {
728 function App({prop}) {
729 const deferred = useDeferredValue(prop);
730 const text = `Urgent: ${prop}, Deferred: ${deferred}`;
731 Scheduler.log(text);
732 return text;
733 }
734
735 const root = ReactNoop.createRoot();
736 root.render(<App prop="A" />);
737
738 await waitForAll(['Urgent: A, Deferred: A']);
739 expect(root).toMatchRenderedOutput('Urgent: A, Deferred: A');
740
741 // This update will result in two separate paints: an urgent one, and a
742 // deferred one.
743 root.render(<App prop="B" />);
744
745 console.log('Not asserted');
746 const message = await awaitExpectToThrowFailure(async () => {
747 await waitForPaint(['Urgent: B, Deferred: A']);
748 });
749
750 expect(message).toMatchInlineSnapshot(`
751 "asserConsoleLogsCleared(expected)
752
753 console.log was called without assertConsoleLogDev:
754 + Not asserted
755
756 You must call one of the assertConsoleDev helpers between each act call."
757 `);
758
759 await waitForAll(['Urgent: B, Deferred: A', 'Urgent: B, Deferred: B']);
760 });
761
762 it('should fail if waitForAll is called before asserting', async () => {
763 const Yield = ({id}) => {
764 Scheduler.log(id);
765 return id;
766 };
767
768 const root = ReactNoop.createRoot();
769 startTransition(() => {
770 root.render(
771 <div>
772 <Yield id="foo" />
773 <Yield id="bar" />
774 <Yield id="baz" />
775 </div>
776 );
777 });
778
779 console.log('Not asserted');
780
781 const message = await awaitExpectToThrowFailure(async () => {
782 await waitForAll(['foo', 'bar', 'baz']);
783 });
784 expect(message).toMatchInlineSnapshot(`
785 "asserConsoleLogsCleared(expected)
786
787 console.log was called without assertConsoleLogDev:
788 + Not asserted
789
790 You must call one of the assertConsoleDev helpers between each act call."
791 `);
792
793 await waitForAll(['foo', 'bar', 'baz']);
794 });
795 it('should fail if toMatchRenderedOutput is called before asserting', async () => {
796 const Yield = ({id}) => {
797 Scheduler.log(id);
798 console.log('Not asserted');
799 return id;
800 };
801
802 const root = ReactNoop.createRoot();
803 startTransition(() => {
804 root.render(
805 <div>
806 <Yield id="foo" />
807 <Yield id="bar" />
808 <Yield id="baz" />
809 </div>
810 );
811 });
812
813 assertLog([]);
814
815 await waitForAll(['foo', 'bar', 'baz']);
816 const message = expectToThrowFailure(() => {
817 expect(root).toMatchRenderedOutput(<div>foobarbaz</div>);
818 });
819 if (!__DEV__) {
820 expect(message).toMatchInlineSnapshot(`
821 "asserConsoleLogsCleared(expected)
822
823 console.log was called without assertConsoleLogDev:
824 + Not asserted
825 + Not asserted
826 + Not asserted
827
828 You must call one of the assertConsoleDev helpers between each act call."
829 `);
830 } else {
831 expect(message).toMatchInlineSnapshot(`
832 "asserConsoleLogsCleared(expected)
833
834 console.log was called without assertConsoleLogDev:
835 + Not asserted
836 + Not asserted
837 + Not asserted
838
839 You must call one of the assertConsoleDev helpers between each act call."
840 `);
841 }
842
843 expect(root).toMatchRenderedOutput(<div>foobarbaz</div>);
844 });
845 });
846
847 describe('assertConsoleWarnDev', () => {
848 it('passes if an warning contains a stack', () => {
849 if (__DEV__) {
850 console.warn('Hello\n in div');
851 }
852 assertConsoleWarnDev(['Hello\n in div']);
853 });
854
855 it('passes if all warnings contain a stack', () => {
856 if (__DEV__) {
857 console.warn('Hello\n in div');
858 console.warn('Good day\n in div');
859 console.warn('Bye\n in div');
860 }
861 assertConsoleWarnDev([
862 'Hello\n in div',
863 'Good day\n in div',
864 'Bye\n in div',
865 ]);
866 });
867
868 it('fails if act is called without assertConsoleWarnDev', async () => {
869 const Yield = ({id}) => {
870 console.warn(id);
871 return id;
872 };
873
874 function App() {
875 return (
876 <div>
877 <Yield id="A" />
878 <Yield id="B" />
879 <Yield id="C" />
880 </div>
881 );
882 }
883
884 const root = ReactNoop.createRoot();
885 await act(() => {
886 root.render(<App />);
887 });
888 const message = await awaitExpectToThrowFailure(async () => {
889 await act(() => {
890 root.render(<App />);
891 });
892 });
893
894 if (!__DEV__) {
895 expect(message).toMatchInlineSnapshot(`
896 "asserConsoleLogsCleared(expected)
897
898 console.warn was called without assertConsoleWarnDev:
899 + A
900 + B
901 + C
902
903 You must call one of the assertConsoleDev helpers between each act call."
904 `);
905 } else {
906 expect(message).toMatchInlineSnapshot(`
907 "asserConsoleLogsCleared(expected)
908
909 console.warn was called without assertConsoleWarnDev:
910 + A%s,
911 + in App (at **)
912 + B%s,
913 + in App (at **)
914 + C%s,
915 + in App (at **)
916
917 You must call one of the assertConsoleDev helpers between each act call."
918 `);
919 }
920 });
921
922 it('fails if act is called without any assertConsoleDev helpers', async () => {
923 const Yield = ({id}) => {
924 console.log(id);
925 console.warn(id);
926 console.error(id);
927 return id;
928 };
929
930 function App() {
931 return (
932 <div>
933 <Yield id="A" />
934 <Yield id="B" />
935 <Yield id="C" />
936 </div>
937 );
938 }
939
940 const root = ReactNoop.createRoot();
941 await act(() => {
942 root.render(<App />);
943 });
944 const message = await awaitExpectToThrowFailure(async () => {
945 await act(() => {
946 root.render(<App />);
947 });
948 });
949
950 if (!__DEV__) {
951 expect(message).toMatchInlineSnapshot(`
952 "asserConsoleLogsCleared(expected)
953
954 console.log was called without assertConsoleLogDev:
955 + A
956 + B
957 + C
958
959 console.warn was called without assertConsoleWarnDev:
960 + A
961 + B
962 + C
963
964 console.error was called without assertConsoleErrorDev:
965 + A
966 + B
967 + C
968
969 You must call one of the assertConsoleDev helpers between each act call."
970 `);
971 } else {
972 expect(message).toMatchInlineSnapshot(`
973 "asserConsoleLogsCleared(expected)
974
975 console.log was called without assertConsoleLogDev:
976 + A
977 + B
978 + C
979
980 console.warn was called without assertConsoleWarnDev:
981 + A%s,
982 + in App (at **)
983 + B%s,
984 + in App (at **)
985 + C%s,
986 + in App (at **)
987
988 console.error was called without assertConsoleErrorDev:
989 + A%s,
990 + in App (at **)
991 + B%s,
992 + in App (at **)
993 + C%s,
994 + in App (at **)
995
996 You must call one of the assertConsoleDev helpers between each act call."
997 `);
998 }
999 });
1000
1001 // @gate __DEV__
1002 it('fails if first expected warning is not included', () => {
1003 const message = expectToThrowFailure(() => {
1004 console.warn('Wow \n in div');
1005 console.warn('Bye \n in div');
1006 assertConsoleWarnDev(['Hi', 'Wow', 'Bye']);
1007 });
1008 expect(message).toMatchInlineSnapshot(`
1009 "assertConsoleWarnDev(expected)
1010
1011 Unexpected warning(s) recorded.
1012
1013 - Expected warnings
1014 + Received warnings
1015
1016 - Hi
1017 - Wow
1018 - Bye
1019 + Wow in div (at **)
1020 + Bye in div (at **)"
1021 `);
1022 });
1023
1024 // @gate __DEV__
1025 it('fails if middle expected warning is not included', () => {
1026 const message = expectToThrowFailure(() => {
1027 console.warn('Hi \n in div');
1028 console.warn('Bye \n in div');
1029 assertConsoleWarnDev(['Hi', 'Wow', 'Bye']);
1030 });
1031 expect(message).toMatchInlineSnapshot(`
1032 "assertConsoleWarnDev(expected)
1033
1034 Unexpected warning(s) recorded.
1035
1036 - Expected warnings
1037 + Received warnings
1038
1039 - Hi
1040 - Wow
1041 - Bye
1042 + Hi in div (at **)
1043 + Bye in div (at **)"
1044 `);
1045 });
1046
1047 // @gate __DEV__
1048 it('fails if last expected warning is not included', () => {
1049 const message = expectToThrowFailure(() => {
1050 console.warn('Hi \n in div');
1051 console.warn('Wow \n in div');
1052 assertConsoleWarnDev([
1053 'Hi \n in div',
1054 'Wow \n in div',
1055 'Bye \n in div',
1056 ]);
1057 });
1058 expect(message).toMatchInlineSnapshot(`
1059 "assertConsoleWarnDev(expected)
1060
1061 Expected warning was not recorded.
1062
1063 - Expected warnings
1064 + Received warnings
1065
1066 - Hi in div
1067 - Wow in div
1068 - Bye in div
1069 + Hi in div (at **)
1070 + Wow in div (at **)"
1071 `);
1072 });
1073
1074 // @gate __DEV__
1075 it('fails if first received warning is not included', () => {
1076 const message = expectToThrowFailure(() => {
1077 console.warn('Hi \n in div');
1078 console.warn('Wow \n in div');
1079 console.warn('Bye \n in div');
1080 assertConsoleWarnDev(['Wow', 'Bye']);
1081 });
1082 expect(message).toMatchInlineSnapshot(`
1083 "assertConsoleWarnDev(expected)
1084
1085 Unexpected warning(s) recorded.
1086
1087 - Expected warnings
1088 + Received warnings
1089
1090 - Wow
1091 - Bye
1092 + Hi in div (at **)
1093 + Wow in div (at **)
1094 + Bye in div (at **)"
1095 `);
1096 });
1097
1098 // @gate __DEV__
1099 it('fails if middle received warning is not included', () => {
1100 const message = expectToThrowFailure(() => {
1101 console.warn('Hi \n in div');
1102 console.warn('Wow \n in div');
1103 console.warn('Bye \n in div');
1104 assertConsoleWarnDev(['Hi', 'Bye']);
1105 });
1106 expect(message).toMatchInlineSnapshot(`
1107 "assertConsoleWarnDev(expected)
1108
1109 Unexpected warning(s) recorded.
1110
1111 - Expected warnings
1112 + Received warnings
1113
1114 - Hi
1115 - Bye
1116 + Hi in div (at **)
1117 + Wow in div (at **)
1118 + Bye in div (at **)"
1119 `);
1120 });
1121
1122 // @gate __DEV__
1123 it('fails if last received warning is not included', () => {
1124 const message = expectToThrowFailure(() => {
1125 console.warn('Hi \n in div');
1126 console.warn('Wow \n in div');
1127 console.warn('Bye \n in div');
1128 assertConsoleWarnDev(['Hi', 'Wow']);
1129 });
1130 expect(message).toMatchInlineSnapshot(`
1131 "assertConsoleWarnDev(expected)
1132
1133 Unexpected warning(s) recorded.
1134
1135 - Expected warnings
1136 + Received warnings
1137
1138 - Hi
1139 - Wow
1140 + Hi in div (at **)
1141 + Wow in div (at **)
1142 + Bye in div (at **)"
1143 `);
1144 });
1145
1146 // @gate __DEV__
1147 it('fails if first warning does not contain a stack', () => {
1148 const message = expectToThrowFailure(() => {
1149 console.warn('Hello');
1150 console.warn('Good day\n in div');
1151 console.warn('Bye\n in div');
1152 assertConsoleWarnDev([
1153 'Hello\n in div',
1154 'Good day\n in div',
1155 'Bye\n in div',
1156 ]);
1157 });
1158 expect(message).toMatchInlineSnapshot(`
1159 "assertConsoleWarnDev(expected)
1160
1161 Unexpected warning(s) recorded.
1162
1163 - Expected warnings
1164 + Received warnings
1165
1166 - Hello in div
1167 - Good day in div
1168 - Bye in div
1169 + Hello
1170 + Good day in div (at **)
1171 + Bye in div (at **)"
1172 `);
1173 });
1174
1175 // @gate __DEV__
1176 it('fails if middle warning does not contain a stack', () => {
1177 const message = expectToThrowFailure(() => {
1178 console.warn('Hello\n in div');
1179 console.warn('Good day');
1180 console.warn('Bye\n in div');
1181 assertConsoleWarnDev([
1182 'Hello\n in div',
1183 'Good day\n in div',
1184 'Bye\n in div',
1185 ]);
1186 });
1187 expect(message).toMatchInlineSnapshot(`
1188 "assertConsoleWarnDev(expected)
1189
1190 Unexpected warning(s) recorded.
1191
1192 - Expected warnings
1193 + Received warnings
1194
1195 - Hello in div
1196 - Good day in div
1197 - Bye in div
1198 + Hello in div (at **)
1199 + Good day
1200 + Bye in div (at **)"
1201 `);
1202 });
1203
1204 // @gate __DEV__
1205 it('fails if last warning does not contain a stack', () => {
1206 const message = expectToThrowFailure(() => {
1207 console.warn('Hello\n in div');
1208 console.warn('Good day\n in div');
1209 console.warn('Bye');
1210 assertConsoleWarnDev([
1211 'Hello\n in div',
1212 'Good day\n in div',
1213 'Bye\n in div',
1214 ]);
1215 });
1216 expect(message).toMatchInlineSnapshot(`
1217 "assertConsoleWarnDev(expected)
1218
1219 Unexpected warning(s) recorded.
1220
1221 - Expected warnings
1222 + Received warnings
1223
1224 - Hello in div
1225 - Good day in div
1226 - Bye in div
1227 + Hello in div (at **)
1228 + Good day in div (at **)
1229 + Bye"
1230 `);
1231 });
1232
1233 // @gate __DEV__
1234 it('fails if the args is greater than %s argument number', () => {
1235 const message = expectToThrowFailure(() => {
1236 console.warn('Hi %s', 'Sara', 'extra');
1237 assertConsoleWarnDev(['Hi']);
1238 });
1239 expect(message).toMatchInlineSnapshot(`
1240 "assertConsoleWarnDev(expected)
1241
1242 Received 2 arguments for a message with 1 placeholders:
1243 "Hi %s""
1244 `);
1245 });
1246
1247 // @gate __DEV__
1248 it('fails if the args is greater than %s argument number for multiple warnings', () => {
1249 const message = expectToThrowFailure(() => {
1250 console.warn('Hi %s', 'Sara', 'extra');
1251 console.warn('Bye %s', 'Sara', 'extra');
1252 assertConsoleWarnDev(['Hi', 'Bye']);
1253 });
1254 expect(message).toMatchInlineSnapshot(`
1255 "assertConsoleWarnDev(expected)
1256
1257 Received 2 arguments for a message with 1 placeholders:
1258 "Hi %s"
1259
1260 Received 2 arguments for a message with 1 placeholders:
1261 "Bye %s""
1262 `);
1263 });
1264
1265 // @gate __DEV__
1266 it('fails if the %s argument number is greater than args', () => {
1267 const message = expectToThrowFailure(() => {
1268 console.warn('Hi %s');
1269 assertConsoleWarnDev(['Hi']);
1270 });
1271 expect(message).toMatchInlineSnapshot(`
1272 "assertConsoleWarnDev(expected)
1273
1274 Received 0 arguments for a message with 1 placeholders:
1275 "Hi %s""
1276 `);
1277 });
1278
1279 // @gate __DEV__
1280 it('fails if the %s argument number is greater than args for multiple warnings', () => {
1281 const message = expectToThrowFailure(() => {
1282 console.warn('Hi %s');
1283 console.warn('Bye %s');
1284 assertConsoleWarnDev(['Hi', 'Bye']);
1285 });
1286 expect(message).toMatchInlineSnapshot(`
1287 "assertConsoleWarnDev(expected)
1288
1289 Received 0 arguments for a message with 1 placeholders:
1290 "Hi %s"
1291
1292 Received 0 arguments for a message with 1 placeholders:
1293 "Bye %s""
1294 `);
1295 });
1296
1297 // @gate __DEV__
1298 it('fails if component stack is passed twice', () => {
1299 const message = expectToThrowFailure(() => {
1300 console.warn('Hi %s%s', '\n in div', '\n in div');
1301 assertConsoleWarnDev(['Hi \n in div (at **)']);
1302 });
1303 expect(message).toMatchInlineSnapshot(`
1304 "assertConsoleWarnDev(expected)
1305
1306 Unexpected warning(s) recorded.
1307
1308 - Expected warnings
1309 + Received warnings
1310
1311 Hi in div (at **)
1312 + in div (at **)"
1313 `);
1314 });
1315
1316 // @gate __DEV__
1317 it('fails if multiple logs pass component stack twice', () => {
1318 const message = expectToThrowFailure(() => {
1319 console.warn('Hi %s%s', '\n in div', '\n in div');
1320 console.warn('Bye %s%s', '\n in div', '\n in div');
1321 assertConsoleWarnDev([
1322 'Hi \n in div (at **)',
1323 'Bye \n in div (at **)',
1324 ]);
1325 });
1326 expect(message).toMatchInlineSnapshot(`
1327 "assertConsoleWarnDev(expected)
1328
1329 Unexpected warning(s) recorded.
1330
1331 - Expected warnings
1332 + Received warnings
1333
1334 Hi in div (at **)
1335 + in div (at **)
1336 Bye in div (at **)
1337 + in div (at **)"
1338 `);
1339 });
1340
1341 // @gate __DEV__
1342 it('fails if multiple strings are passed without an array wrapper for single log', () => {
1343 const message = expectToThrowFailure(() => {
1344 console.warn('Hi \n in div');
1345 console.warn('Bye \n in div');
1346 assertConsoleWarnDev('Hi', 'Bye');
1347 });
1348 expect(message).toMatchInlineSnapshot(`
1349 "assertConsoleWarnDev(expected)
1350
1351 Expected messages should be an array of strings but was given type "string"."
1352 `);
1353 assertConsoleWarnDev(['Hi \n in div', 'Bye \n in div']);
1354 });
1355
1356 // @gate __DEV__
1357 it('fails if multiple strings are passed without an array wrapper for multiple logs', () => {
1358 const message = expectToThrowFailure(() => {
1359 console.warn('Hi \n in div');
1360 console.warn('Bye \n in div');
1361 assertConsoleWarnDev('Hi', 'Bye');
1362 });
1363 expect(message).toMatchInlineSnapshot(`
1364 "assertConsoleWarnDev(expected)
1365
1366 Expected messages should be an array of strings but was given type "string"."
1367 `);
1368 assertConsoleWarnDev(['Hi \n in div', 'Bye \n in div']);
1369 });
1370
1371 // @gate __DEV__
1372 it('fails on more than two arguments', () => {
1373 const message = expectToThrowFailure(() => {
1374 console.warn('Hi \n in div');
1375 console.warn('Wow \n in div');
1376 console.warn('Bye \n in div');
1377 assertConsoleWarnDev('Hi', undefined, 'Bye');
1378 });
1379 expect(message).toMatchInlineSnapshot(`
1380 "assertConsoleWarnDev(expected)
1381
1382 Expected messages should be an array of strings but was given type "string"."
1383 `);
1384 assertConsoleWarnDev([
1385 'Hi \n in div',
1386 'Wow \n in div',
1387 'Bye \n in div',
1388 ]);
1389 });
1390
1391 it('should fail if waitFor is called before asserting', async () => {
1392 const Yield = ({id}) => {
1393 Scheduler.log(id);
1394 return id;
1395 };
1396
1397 const root = ReactNoop.createRoot();
1398 startTransition(() => {
1399 root.render(
1400 <div>
1401 <Yield id="foo" />
1402 <Yield id="bar" />
1403 <Yield id="baz" />
1404 </div>
1405 );
1406 });
1407
1408 console.warn('Not asserted');
1409
1410 const message = await awaitExpectToThrowFailure(async () => {
1411 await waitFor(['foo', 'bar']);
1412 });
1413 expect(message).toMatchInlineSnapshot(`
1414 "asserConsoleLogsCleared(expected)
1415
1416 console.warn was called without assertConsoleWarnDev:
1417 + Not asserted
1418
1419 You must call one of the assertConsoleDev helpers between each act call."
1420 `);
1421
1422 await waitForAll(['foo', 'bar', 'baz']);
1423 });
1424
1425 it('should fail if waitForThrow is called before asserting', async () => {
1426 const Yield = ({id}) => {
1427 Scheduler.log(id);
1428 return id;
1429 };
1430
1431 function BadRender() {
1432 throw new Error('Oh no!');
1433 }
1434
1435 function App() {
1436 return (
1437 <div>
1438 <Yield id="A" />
1439 <Yield id="B" />
1440 <BadRender />
1441 <Yield id="C" />
1442 <Yield id="D" />
1443 </div>
1444 );
1445 }
1446
1447 const root = ReactNoop.createRoot();
1448 root.render(<App />);
1449
1450 console.warn('Not asserted');
1451
1452 const message = await awaitExpectToThrowFailure(async () => {
1453 await waitForThrow('Oh no!');
1454 });
1455 expect(message).toMatchInlineSnapshot(`
1456 "asserConsoleLogsCleared(expected)
1457
1458 console.warn was called without assertConsoleWarnDev:
1459 + Not asserted
1460
1461 You must call one of the assertConsoleDev helpers between each act call."
1462 `);
1463
1464 await waitForAll(['A', 'B', 'A', 'B']);
1465 });
1466
1467 it('should fail if waitForPaint is called before asserting', async () => {
1468 function App({prop}) {
1469 const deferred = useDeferredValue(prop);
1470 const text = `Urgent: ${prop}, Deferred: ${deferred}`;
1471 Scheduler.log(text);
1472 return text;
1473 }
1474
1475 const root = ReactNoop.createRoot();
1476 root.render(<App prop="A" />);
1477
1478 await waitForAll(['Urgent: A, Deferred: A']);
1479 expect(root).toMatchRenderedOutput('Urgent: A, Deferred: A');
1480
1481 // This update will result in two separate paints: an urgent one, and a
1482 // deferred one.
1483 root.render(<App prop="B" />);
1484
1485 console.warn('Not asserted');
1486 const message = await awaitExpectToThrowFailure(async () => {
1487 await waitForPaint(['Urgent: B, Deferred: A']);
1488 });
1489
1490 expect(message).toMatchInlineSnapshot(`
1491 "asserConsoleLogsCleared(expected)
1492
1493 console.warn was called without assertConsoleWarnDev:
1494 + Not asserted
1495
1496 You must call one of the assertConsoleDev helpers between each act call."
1497 `);
1498
1499 await waitForAll(['Urgent: B, Deferred: A', 'Urgent: B, Deferred: B']);
1500 });
1501
1502 it('should fail if waitForAll is called before asserting', async () => {
1503 const Yield = ({id}) => {
1504 Scheduler.log(id);
1505 return id;
1506 };
1507
1508 const root = ReactNoop.createRoot();
1509 startTransition(() => {
1510 root.render(
1511 <div>
1512 <Yield id="foo" />
1513 <Yield id="bar" />
1514 <Yield id="baz" />
1515 </div>
1516 );
1517 });
1518
1519 console.warn('Not asserted');
1520
1521 const message = await awaitExpectToThrowFailure(async () => {
1522 await waitForAll(['foo', 'bar', 'baz']);
1523 });
1524 expect(message).toMatchInlineSnapshot(`
1525 "asserConsoleLogsCleared(expected)
1526
1527 console.warn was called without assertConsoleWarnDev:
1528 + Not asserted
1529
1530 You must call one of the assertConsoleDev helpers between each act call."
1531 `);
1532
1533 await waitForAll(['foo', 'bar', 'baz']);
1534 });
1535 it('should fail if toMatchRenderedOutput is called before asserting', async () => {
1536 const Yield = ({id}) => {
1537 Scheduler.log(id);
1538 console.warn('Not asserted');
1539 return id;
1540 };
1541
1542 const root = ReactNoop.createRoot();
1543 startTransition(() => {
1544 root.render(
1545 <div>
1546 <Yield id="foo" />
1547 <Yield id="bar" />
1548 <Yield id="baz" />
1549 </div>
1550 );
1551 });
1552
1553 assertLog([]);
1554
1555 await waitForAll(['foo', 'bar', 'baz']);
1556 const message = expectToThrowFailure(() => {
1557 expect(root).toMatchRenderedOutput(<div>foobarbaz</div>);
1558 });
1559 if (!__DEV__) {
1560 expect(message).toMatchInlineSnapshot(`
1561 "asserConsoleLogsCleared(expected)
1562
1563 console.warn was called without assertConsoleWarnDev:
1564 + Not asserted
1565 + Not asserted
1566 + Not asserted
1567
1568 You must call one of the assertConsoleDev helpers between each act call."
1569 `);
1570 } else {
1571 expect(message).toMatchInlineSnapshot(`
1572 "asserConsoleLogsCleared(expected)
1573
1574 console.warn was called without assertConsoleWarnDev:
1575 + Not asserted%s,
1576 + in Yield (at **)
1577 + Not asserted%s,
1578 + in Yield (at **)
1579 + Not asserted%s,
1580 + in Yield (at **)
1581
1582 You must call one of the assertConsoleDev helpers between each act call."
1583 `);
1584 }
1585
1586 expect(root).toMatchRenderedOutput(<div>foobarbaz</div>);
1587 });
1588 });
1589
1590 describe('assertConsoleErrorDev', () => {
1591 it('passes if an error contains a stack', () => {
1592 if (__DEV__) {
1593 console.error('Hello\n in div');
1594 }
1595 assertConsoleErrorDev(['Hello\n in div']);
1596 });
1597
1598 it('passes if all errors contain a stack', () => {
1599 if (__DEV__) {
1600 console.error('Hello\n in div');
1601 console.error('Good day\n in div');
1602 console.error('Bye\n in div');
1603 }
1604 assertConsoleErrorDev([
1605 'Hello\n in div',
1606 'Good day\n in div',
1607 'Bye\n in div',
1608 ]);
1609 });
1610
1611 it('fails if act is called without assertConsoleErrorDev', async () => {
1612 const Yield = ({id}) => {
1613 console.error(id);
1614 return id;
1615 };
1616
1617 function App() {
1618 return (
1619 <div>
1620 <Yield id="A" />
1621 <Yield id="B" />
1622 <Yield id="C" />
1623 </div>
1624 );
1625 }
1626
1627 const root = ReactNoop.createRoot();
1628 await act(() => {
1629 root.render(<App />);
1630 });
1631 const message = await awaitExpectToThrowFailure(async () => {
1632 await act(() => {
1633 root.render(<App />);
1634 });
1635 });
1636
1637 if (!__DEV__) {
1638 expect(message).toMatchInlineSnapshot(`
1639 "asserConsoleLogsCleared(expected)
1640
1641 console.error was called without assertConsoleErrorDev:
1642 + A
1643 + B
1644 + C
1645
1646 You must call one of the assertConsoleDev helpers between each act call."
1647 `);
1648 } else {
1649 expect(message).toMatchInlineSnapshot(`
1650 "asserConsoleLogsCleared(expected)
1651
1652 console.error was called without assertConsoleErrorDev:
1653 + A%s,
1654 + in App (at **)
1655 + B%s,
1656 + in App (at **)
1657 + C%s,
1658 + in App (at **)
1659
1660 You must call one of the assertConsoleDev helpers between each act call."
1661 `);
1662 }
1663 });
1664
1665 it('fails if act is called without any assertConsoleDev helpers', async () => {
1666 const Yield = ({id}) => {
1667 console.log(id);
1668 console.warn(id);
1669 console.error(id);
1670 return id;
1671 };
1672
1673 function App() {
1674 return (
1675 <div>
1676 <Yield id="A" />
1677 <Yield id="B" />
1678 <Yield id="C" />
1679 </div>
1680 );
1681 }
1682
1683 const root = ReactNoop.createRoot();
1684 await act(() => {
1685 root.render(<App />);
1686 });
1687 const message = await awaitExpectToThrowFailure(async () => {
1688 await act(() => {
1689 root.render(<App />);
1690 });
1691 });
1692
1693 if (!__DEV__) {
1694 expect(message).toMatchInlineSnapshot(`
1695 "asserConsoleLogsCleared(expected)
1696
1697 console.log was called without assertConsoleLogDev:
1698 + A
1699 + B
1700 + C
1701
1702 console.warn was called without assertConsoleWarnDev:
1703 + A
1704 + B
1705 + C
1706
1707 console.error was called without assertConsoleErrorDev:
1708 + A
1709 + B
1710 + C
1711
1712 You must call one of the assertConsoleDev helpers between each act call."
1713 `);
1714 } else {
1715 expect(message).toMatchInlineSnapshot(`
1716 "asserConsoleLogsCleared(expected)
1717
1718 console.log was called without assertConsoleLogDev:
1719 + A
1720 + B
1721 + C
1722
1723 console.warn was called without assertConsoleWarnDev:
1724 + A%s,
1725 + in App (at **)
1726 + B%s,
1727 + in App (at **)
1728 + C%s,
1729 + in App (at **)
1730
1731 console.error was called without assertConsoleErrorDev:
1732 + A%s,
1733 + in App (at **)
1734 + B%s,
1735 + in App (at **)
1736 + C%s,
1737 + in App (at **)
1738
1739 You must call one of the assertConsoleDev helpers between each act call."
1740 `);
1741 }
1742 });
1743
1744 // @gate __DEV__
1745 it('fails if first expected error is not included', () => {
1746 const message = expectToThrowFailure(() => {
1747 console.error('Wow \n in div');
1748 console.error('Bye \n in div');
1749 assertConsoleErrorDev(['Hi', 'Wow', 'Bye']);
1750 });
1751 expect(message).toMatchInlineSnapshot(`
1752 "assertConsoleErrorDev(expected)
1753
1754 Unexpected error(s) recorded.
1755
1756 - Expected errors
1757 + Received errors
1758
1759 - Hi
1760 - Wow
1761 - Bye
1762 + Wow in div (at **)
1763 + Bye in div (at **)"
1764 `);
1765 });
1766
1767 // @gate __DEV__
1768 it('fails if middle expected error is not included', () => {
1769 const message = expectToThrowFailure(() => {
1770 console.error('Hi \n in div');
1771 console.error('Bye \n in div');
1772 assertConsoleErrorDev(['Hi', 'Wow', 'Bye']);
1773 });
1774 expect(message).toMatchInlineSnapshot(`
1775 "assertConsoleErrorDev(expected)
1776
1777 Unexpected error(s) recorded.
1778
1779 - Expected errors
1780 + Received errors
1781
1782 - Hi
1783 - Wow
1784 - Bye
1785 + Hi in div (at **)
1786 + Bye in div (at **)"
1787 `);
1788 });
1789
1790 // @gate __DEV__
1791 it('fails if last expected error is not included', () => {
1792 const message = expectToThrowFailure(() => {
1793 console.error('Hi \n in div');
1794 console.error('Wow \n in div');
1795 assertConsoleErrorDev([
1796 'Hi \n in div',
1797 'Wow \n in div',
1798 'Bye \n in div',
1799 ]);
1800 });
1801 expect(message).toMatchInlineSnapshot(`
1802 "assertConsoleErrorDev(expected)
1803
1804 Expected error was not recorded.
1805
1806 - Expected errors
1807 + Received errors
1808
1809 - Hi in div
1810 - Wow in div
1811 - Bye in div
1812 + Hi in div (at **)
1813 + Wow in div (at **)"
1814 `);
1815 });
1816
1817 // @gate __DEV__
1818 it('fails if first received error is not included', () => {
1819 const message = expectToThrowFailure(() => {
1820 console.error('Hi \n in div');
1821 console.error('Wow \n in div');
1822 console.error('Bye \n in div');
1823 assertConsoleErrorDev(['Wow', 'Bye']);
1824 });
1825 expect(message).toMatchInlineSnapshot(`
1826 "assertConsoleErrorDev(expected)
1827
1828 Unexpected error(s) recorded.
1829
1830 - Expected errors
1831 + Received errors
1832
1833 - Wow
1834 - Bye
1835 + Hi in div (at **)
1836 + Wow in div (at **)
1837 + Bye in div (at **)"
1838 `);
1839 });
1840
1841 // @gate __DEV__
1842 it('fails if middle received error is not included', () => {
1843 const message = expectToThrowFailure(() => {
1844 console.error('Hi \n in div');
1845 console.error('Wow \n in div');
1846 console.error('Bye \n in div');
1847 assertConsoleErrorDev(['Hi', 'Bye']);
1848 });
1849 expect(message).toMatchInlineSnapshot(`
1850 "assertConsoleErrorDev(expected)
1851
1852 Unexpected error(s) recorded.
1853
1854 - Expected errors
1855 + Received errors
1856
1857 - Hi
1858 - Bye
1859 + Hi in div (at **)
1860 + Wow in div (at **)
1861 + Bye in div (at **)"
1862 `);
1863 });
1864
1865 // @gate __DEV__
1866 it('fails if last received error is not included', () => {
1867 const message = expectToThrowFailure(() => {
1868 console.error('Hi \n in div');
1869 console.error('Wow \n in div');
1870 console.error('Bye \n in div');
1871 assertConsoleErrorDev(['Hi', 'Wow']);
1872 });
1873 expect(message).toMatchInlineSnapshot(`
1874 "assertConsoleErrorDev(expected)
1875
1876 Unexpected error(s) recorded.
1877
1878 - Expected errors
1879 + Received errors
1880
1881 - Hi
1882 - Wow
1883 + Hi in div (at **)
1884 + Wow in div (at **)
1885 + Bye in div (at **)"
1886 `);
1887 });
1888
1889 // @gate __DEV__
1890 it('fails if last received error containing "undefined" is not included', () => {
1891 const message = expectToThrowFailure(() => {
1892 console.error('Hi');
1893 console.error(
1894 "TypeError: Cannot read properties of undefined (reading 'stack')\n" +
1895 ' in Foo (at **)'
1896 );
1897 assertConsoleErrorDev(['Hi']);
1898 });
1899 expect(message).toMatchInlineSnapshot(`
1900 "assertConsoleErrorDev(expected)
1901
1902 Unexpected error(s) recorded.
1903
1904 - Expected errors
1905 + Received errors
1906
1907 Hi
1908 + TypeError: Cannot read properties of undefined (reading 'stack') in Foo (at **)"
1909 `);
1910 });
1911
1912 // @gate __DEV__
1913 it('regression: checks entire string, not just the first letter', async () => {
1914 const message = expectToThrowFailure(() => {
1915 console.error('Message that happens to contain a "T"\n in div');
1916
1917 assertConsoleErrorDev([
1918 'This is a completely different message that happens to start with "T"',
1919 ]);
1920 });
1921 expect(message).toMatchInlineSnapshot(`
1922 "assertConsoleErrorDev(expected)
1923
1924 Unexpected error(s) recorded.
1925
1926 - Expected errors
1927 + Received errors
1928
1929 - This is a completely different message that happens to start with "T"
1930 + Message that happens to contain a "T" in div (at **)"
1931 `);
1932 });
1933
1934 // @gate __DEV__
1935 it('fails if the args is greater than %s argument number', () => {
1936 const message = expectToThrowFailure(() => {
1937 console.error('Hi %s', 'Sara', 'extra');
1938 assertConsoleErrorDev(['Hi']);
1939 });
1940 expect(message).toMatchInlineSnapshot(`
1941 "assertConsoleErrorDev(expected)
1942
1943 Received 2 arguments for a message with 1 placeholders:
1944 "Hi %s""
1945 `);
1946 });
1947
1948 // @gate __DEV__
1949 it('fails if the args is greater than %s argument number for multiple errors', () => {
1950 const message = expectToThrowFailure(() => {
1951 console.error('Hi %s', 'Sara', 'extra');
1952 console.error('Bye %s', 'Sara', 'extra');
1953 assertConsoleErrorDev(['Hi', 'Bye']);
1954 });
1955 expect(message).toMatchInlineSnapshot(`
1956 "assertConsoleErrorDev(expected)
1957
1958 Received 2 arguments for a message with 1 placeholders:
1959 "Hi %s"
1960
1961 Received 2 arguments for a message with 1 placeholders:
1962 "Bye %s""
1963 `);
1964 });
1965
1966 // @gate __DEV__
1967 it('fails if the %s argument number is greater than args', () => {
1968 const message = expectToThrowFailure(() => {
1969 console.error('Hi %s');
1970 assertConsoleErrorDev(['Hi']);
1971 });
1972 expect(message).toMatchInlineSnapshot(`
1973 "assertConsoleErrorDev(expected)
1974
1975 Received 0 arguments for a message with 1 placeholders:
1976 "Hi %s""
1977 `);
1978 });
1979
1980 // @gate __DEV__
1981 it('fails if the %s argument number is greater than args for multiple errors', () => {
1982 const message = expectToThrowFailure(() => {
1983 console.error('Hi %s');
1984 console.error('Bye %s');
1985 assertConsoleErrorDev(['Hi', 'Bye']);
1986 });
1987 expect(message).toMatchInlineSnapshot(`
1988 "assertConsoleErrorDev(expected)
1989
1990 Received 0 arguments for a message with 1 placeholders:
1991 "Hi %s"
1992
1993 Received 0 arguments for a message with 1 placeholders:
1994 "Bye %s""
1995 `);
1996 });
1997
1998 // @gate __DEV__
1999 it('fails if component stack is passed twice', () => {
2000 const message = expectToThrowFailure(() => {
2001 console.error('Hi %s%s', '\n in div', '\n in div');
2002 assertConsoleErrorDev(['Hi \n in div (at **)']);
2003 });
2004 expect(message).toMatchInlineSnapshot(`
2005 "assertConsoleErrorDev(expected)
2006
2007 Unexpected error(s) recorded.
2008
2009 - Expected errors
2010 + Received errors
2011
2012 Hi in div (at **)
2013 + in div (at **)"
2014 `);
2015 });
2016
2017 // @gate __DEV__
2018 it('fails if multiple logs pass component stack twice', () => {
2019 const message = expectToThrowFailure(() => {
2020 console.error('Hi %s%s', '\n in div', '\n in div');
2021 console.error('Bye %s%s', '\n in div', '\n in div');
2022 assertConsoleErrorDev([
2023 'Hi \n in div (at **)',
2024 'Bye \n in div (at **)',
2025 ]);
2026 });
2027 expect(message).toMatchInlineSnapshot(`
2028 "assertConsoleErrorDev(expected)
2029
2030 Unexpected error(s) recorded.
2031
2032 - Expected errors
2033 + Received errors
2034
2035 Hi in div (at **)
2036 + in div (at **)
2037 Bye in div (at **)
2038 + in div (at **)"
2039 `);
2040 });
2041
2042 // @gate __DEV__
2043 it('fails if multiple strings are passed without an array wrapper for single log', () => {
2044 const message = expectToThrowFailure(() => {
2045 console.error('Hi \n in div');
2046 console.error('Bye \n in div');
2047 assertConsoleErrorDev('Hi \n in div', 'Bye \n in div');
2048 });
2049 expect(message).toMatchInlineSnapshot(`
2050 "assertConsoleErrorDev(expected)
2051
2052 Expected messages should be an array of strings but was given type "string"."
2053 `);
2054 assertConsoleErrorDev(['Hi \n in div', 'Bye \n in div']);
2055 });
2056
2057 // @gate __DEV__
2058 it('fails if multiple strings are passed without an array wrapper for multiple logs', () => {
2059 const message = expectToThrowFailure(() => {
2060 console.error('Hi \n in div');
2061 console.error('Bye \n in div');
2062 assertConsoleErrorDev('Hi', 'Bye');
2063 });
2064 expect(message).toMatchInlineSnapshot(`
2065 "assertConsoleErrorDev(expected)
2066
2067 Expected messages should be an array of strings but was given type "string"."
2068 `);
2069 assertConsoleErrorDev(['Hi \n in div', 'Bye \n in div']);
2070 });
2071
2072 // @gate __DEV__
2073 it('fails on more than two arguments', () => {
2074 const message = expectToThrowFailure(() => {
2075 console.error('Hi \n in div');
2076 console.error('Wow \n in div');
2077 console.error('Bye \n in div');
2078 assertConsoleErrorDev('Hi', undefined, 'Bye');
2079 });
2080 expect(message).toMatchInlineSnapshot(`
2081 "assertConsoleErrorDev(expected)
2082
2083 Expected messages should be an array of strings but was given type "string"."
2084 `);
2085 assertConsoleErrorDev([
2086 'Hi \n in div',
2087 'Wow \n in div',
2088 'Bye \n in div',
2089 ]);
2090 });
2091
2092 describe('in <stack> placeholder', () => {
2093 // @gate __DEV__
2094 it('fails if `in <stack>` is used for a component stack instead of an error stack', () => {
2095 const message = expectToThrowFailure(() => {
2096 console.error('Warning message\n in div');
2097 assertConsoleErrorDev(['Warning message\n in <stack>']);
2098 });
2099 expect(message).toMatchInlineSnapshot(`
2100 "assertConsoleErrorDev(expected)
2101
2102 Incorrect use of \\n in <stack> placeholder. The placeholder is for JavaScript Error stack traces (messages starting with "Error:"), not for React component stacks.
2103
2104 Expected: "Warning message
2105 in <stack>"
2106 Received: "Warning message
2107 in div (at **)"
2108
2109 If this error has a component stack, include the full component stack in your expected message (e.g., "Warning message\\n in ComponentName (at **)")."
2110 `);
2111 });
2112
2113 // @gate __DEV__
2114 it('fails if `in <stack>` is used for multiple component stacks', () => {
2115 const message = expectToThrowFailure(() => {
2116 console.error('First warning\n in span');
2117 console.error('Second warning\n in div');
2118 assertConsoleErrorDev([
2119 'First warning\n in <stack>',
2120 'Second warning\n in <stack>',
2121 ]);
2122 });
2123 expect(message).toMatchInlineSnapshot(`
2124 "assertConsoleErrorDev(expected)
2125
2126 Incorrect use of \\n in <stack> placeholder. The placeholder is for JavaScript Error stack traces (messages starting with "Error:"), not for React component stacks.
2127
2128 Expected: "First warning
2129 in <stack>"
2130 Received: "First warning
2131 in span (at **)"
2132
2133 If this error has a component stack, include the full component stack in your expected message (e.g., "Warning message\\n in ComponentName (at **)").
2134
2135 Incorrect use of \\n in <stack> placeholder. The placeholder is for JavaScript Error stack traces (messages starting with "Error:"), not for React component stacks.
2136
2137 Expected: "Second warning
2138 in <stack>"
2139 Received: "Second warning
2140 in div (at **)"
2141
2142 If this error has a component stack, include the full component stack in your expected message (e.g., "Warning message\\n in ComponentName (at **)")."
2143 `);
2144 });
2145
2146 it('allows `in <stack>` for actual error stack traces', () => {
2147 // This should pass - \n in <stack> is correctly used for an error stack
2148 console.error(new Error('Something went wrong'));
2149 assertConsoleErrorDev(['Error: Something went wrong\n in <stack>']);
2150 });
2151
2152 // @gate __DEV__
2153 it('fails if error stack trace is present but \\n in <stack> is not expected', () => {
2154 const message = expectToThrowFailure(() => {
2155 console.error(new Error('Something went wrong'));
2156 assertConsoleErrorDev(['Error: Something went wrong']);
2157 });
2158 expect(message).toMatch(`Unexpected error stack trace for:`);
2159 expect(message).toMatch(`Error: Something went wrong`);
2160 expect(message).toMatch(
2161 'If this error should include an error stack trace, add \\n in <stack> to your expected message'
2162 );
2163 });
2164
2165 // @gate __DEV__
2166 it('fails if `in <stack>` is expected but no stack is present', () => {
2167 const message = expectToThrowFailure(() => {
2168 console.error('Error: Something went wrong');
2169 assertConsoleErrorDev([
2170 'Error: Something went wrong\n in <stack>',
2171 ]);
2172 });
2173 expect(message).toMatchInlineSnapshot(`
2174 "assertConsoleErrorDev(expected)
2175
2176 Missing error stack trace for:
2177 "Error: Something went wrong"
2178
2179 The expected message uses \\n in <stack> but the actual error doesn't include an error stack trace.
2180 If this error should not have an error stack trace, remove \\n in <stack> from your expected message."
2181 `);
2182 });
2183 });
2184
2185 describe('[Environment] placeholder', () => {
2186 // @gate __DEV__
2187 it('expands [Server] to ANSI escape sequence for server badge', () => {
2188 const badge = '\u001b[0m\u001b[7m Server \u001b[0m';
2189 console.error(badge + 'Error: something went wrong');
2190 assertConsoleErrorDev(['[Server] Error: something went wrong']);
2191 });
2192
2193 // @gate __DEV__
2194 it('expands [Prerender] to ANSI escape sequence for server badge', () => {
2195 const badge = '\u001b[0m\u001b[7m Prerender \u001b[0m';
2196 console.error(badge + 'Error: something went wrong');
2197 assertConsoleErrorDev(['[Prerender] Error: something went wrong']);
2198 });
2199
2200 // @gate __DEV__
2201 it('expands [Cache] to ANSI escape sequence for server badge', () => {
2202 const badge = '\u001b[0m\u001b[7m Cache \u001b[0m';
2203 console.error(badge + 'Error: something went wrong');
2204 assertConsoleErrorDev(['[Cache] Error: something went wrong']);
2205 });
2206 });
2207
2208 it('should fail if waitFor is called before asserting', async () => {
2209 const Yield = ({id}) => {
2210 Scheduler.log(id);
2211 return id;
2212 };
2213
2214 const root = ReactNoop.createRoot();
2215 startTransition(() => {
2216 root.render(
2217 <div>
2218 <Yield id="foo" />
2219 <Yield id="bar" />
2220 <Yield id="baz" />
2221 </div>
2222 );
2223 });
2224
2225 console.error('Not asserted');
2226
2227 const message = await awaitExpectToThrowFailure(async () => {
2228 await waitFor(['foo', 'bar']);
2229 });
2230 expect(message).toMatchInlineSnapshot(`
2231 "asserConsoleLogsCleared(expected)
2232
2233 console.error was called without assertConsoleErrorDev:
2234 + Not asserted
2235
2236 You must call one of the assertConsoleDev helpers between each act call."
2237 `);
2238
2239 await waitForAll(['foo', 'bar', 'baz']);
2240 });
2241
2242 it('should fail if waitForThrow is called before asserting', async () => {
2243 const Yield = ({id}) => {
2244 Scheduler.log(id);
2245 return id;
2246 };
2247
2248 function BadRender() {
2249 throw new Error('Oh no!');
2250 }
2251
2252 function App() {
2253 return (
2254 <div>
2255 <Yield id="A" />
2256 <Yield id="B" />
2257 <BadRender />
2258 <Yield id="C" />
2259 <Yield id="D" />
2260 </div>
2261 );
2262 }
2263
2264 const root = ReactNoop.createRoot();
2265 root.render(<App />);
2266
2267 console.error('Not asserted');
2268
2269 const message = await awaitExpectToThrowFailure(async () => {
2270 await waitForThrow('Oh no!');
2271 });
2272 expect(message).toMatchInlineSnapshot(`
2273 "asserConsoleLogsCleared(expected)
2274
2275 console.error was called without assertConsoleErrorDev:
2276 + Not asserted
2277
2278 You must call one of the assertConsoleDev helpers between each act call."
2279 `);
2280
2281 await waitForAll(['A', 'B', 'A', 'B']);
2282 });
2283
2284 it('should fail if waitForPaint is called before asserting', async () => {
2285 function App({prop}) {
2286 const deferred = useDeferredValue(prop);
2287 const text = `Urgent: ${prop}, Deferred: ${deferred}`;
2288 Scheduler.log(text);
2289 return text;
2290 }
2291
2292 const root = ReactNoop.createRoot();
2293 root.render(<App prop="A" />);
2294
2295 await waitForAll(['Urgent: A, Deferred: A']);
2296 expect(root).toMatchRenderedOutput('Urgent: A, Deferred: A');
2297
2298 // This update will result in two separate paints: an urgent one, and a
2299 // deferred one.
2300 root.render(<App prop="B" />);
2301
2302 console.error('Not asserted');
2303 const message = await awaitExpectToThrowFailure(async () => {
2304 await waitForPaint(['Urgent: B, Deferred: A']);
2305 });
2306
2307 expect(message).toMatchInlineSnapshot(`
2308 "asserConsoleLogsCleared(expected)
2309
2310 console.error was called without assertConsoleErrorDev:
2311 + Not asserted
2312
2313 You must call one of the assertConsoleDev helpers between each act call."
2314 `);
2315
2316 await waitForAll(['Urgent: B, Deferred: A', 'Urgent: B, Deferred: B']);
2317 });
2318
2319 it('should fail if waitForAll is called before asserting', async () => {
2320 const Yield = ({id}) => {
2321 Scheduler.log(id);
2322 return id;
2323 };
2324
2325 const root = ReactNoop.createRoot();
2326 startTransition(() => {
2327 root.render(
2328 <div>
2329 <Yield id="foo" />
2330 <Yield id="bar" />
2331 <Yield id="baz" />
2332 </div>
2333 );
2334 });
2335
2336 console.error('Not asserted');
2337
2338 const message = await awaitExpectToThrowFailure(async () => {
2339 await waitForAll(['foo', 'bar', 'baz']);
2340 });
2341 expect(message).toMatchInlineSnapshot(`
2342 "asserConsoleLogsCleared(expected)
2343
2344 console.error was called without assertConsoleErrorDev:
2345 + Not asserted
2346
2347 You must call one of the assertConsoleDev helpers between each act call."
2348 `);
2349
2350 await waitForAll(['foo', 'bar', 'baz']);
2351 });
2352 it('should fail if toMatchRenderedOutput is called before asserting', async () => {
2353 const Yield = ({id}) => {
2354 Scheduler.log(id);
2355 console.error('Not asserted');
2356 return id;
2357 };
2358
2359 const root = ReactNoop.createRoot();
2360 startTransition(() => {
2361 root.render(
2362 <div>
2363 <Yield id="foo" />
2364 <Yield id="bar" />
2365 <Yield id="baz" />
2366 </div>
2367 );
2368 });
2369
2370 assertLog([]);
2371
2372 await waitForAll(['foo', 'bar', 'baz']);
2373 const message = expectToThrowFailure(() => {
2374 expect(root).toMatchRenderedOutput(<div>foobarbaz</div>);
2375 });
2376 if (!__DEV__) {
2377 expect(message).toMatchInlineSnapshot(`
2378 "asserConsoleLogsCleared(expected)
2379
2380 console.error was called without assertConsoleErrorDev:
2381 + Not asserted
2382 + Not asserted
2383 + Not asserted
2384
2385 You must call one of the assertConsoleDev helpers between each act call."
2386 `);
2387 } else {
2388 expect(message).toMatchInlineSnapshot(`
2389 "asserConsoleLogsCleared(expected)
2390
2391 console.error was called without assertConsoleErrorDev:
2392 + Not asserted%s,
2393 + in Yield (at **)
2394 + Not asserted%s,
2395 + in Yield (at **)
2396 + Not asserted%s,
2397 + in Yield (at **)
2398
2399 You must call one of the assertConsoleDev helpers between each act call."
2400 `);
2401 }
2402
2403 expect(root).toMatchRenderedOutput(<div>foobarbaz</div>);
2404 });
2405 });
2406 });