11
'use strict';
12
13
const React = require('react');
14
+const stripAnsi = require('strip-ansi');
15
const {startTransition, useDeferredValue} = React;
16
const chalk = require('chalk');
17
const ReactNoop = require('react-noop-renderer');
29
resetAllUnexpectedConsoleCalls,
30
patchConsoleMethods,
31
} = require('../consoleMock');
32
+const {
33
+ assertConsoleLogDev,
34
+ assertConsoleWarnDev,
35
+ assertConsoleErrorDev,
36
+} = require('../ReactInternalTestUtils');
37
38
describe('ReactInternalTestUtils', () => {
39
test('waitFor', async () => {
307
});
308
});
309
});
310
+
311
+// Helper method to capture assertion failure.
312
+const expectToThrowFailure = expectBlock => {
313
+ let caughtError;
314
+ try {
315
+ expectBlock();
316
+ } catch (error) {
317
+ caughtError = error;
318
+ }
319
+ expect(caughtError).toBeDefined();
320
+ return stripAnsi(caughtError.message);
321
+};
322
+
323
+// Helper method to capture assertion failure with act.
324
+const awaitExpectToThrowFailure = async expectBlock => {
325
+ let caughtError;
326
+ try {
327
+ await expectBlock();
328
+ } catch (error) {
329
+ caughtError = error;
330
+ }
331
+ expect(caughtError).toBeDefined();
332
+ return stripAnsi(caughtError.message);
333
+};
334
+
335
+describe('ReactInternalTestUtils console assertions', () => {
336
+ beforeAll(() => {
337
+ patchConsoleMethods({includeLog: true});
338
+ });
339
+
340
+ describe('assertConsoleLogDev', () => {
341
+ // @gate __DEV__
342
+ it('passes for a single log', () => {
343
+ console.log('Hello');
344
+ assertConsoleLogDev(['Hello']);
345
+ });
346
+
347
+ // @gate __DEV__
348
+ it('passes for multiple logs', () => {
349
+ console.log('Hello');
350
+ console.log('Good day');
351
+ console.log('Bye');
352
+ assertConsoleLogDev(['Hello', 'Good day', 'Bye']);
353
+ });
354
+
355
+ it('fails if act is called without assertConsoleLogDev', async () => {
356
+ const Yield = ({id}) => {
357
+ console.log(id);
358
+ return id;
359
+ };
360
+
361
+ function App() {
362
+ return (
363
+ <div>
364
+ <Yield id="A" />
365
+ <Yield id="B" />
366
+ <Yield id="C" />
367
+ </div>
368
+ );
369
+ }
370
+
371
+ const root = ReactNoop.createRoot();
372
+ await act(() => {
373
+ root.render(<App />);
374
+ });
375
+ const message = await awaitExpectToThrowFailure(async () => {
376
+ await act(() => {
377
+ root.render(<App />);
378
+ });
379
+ });
380
+
381
+ expect(message).toMatchInlineSnapshot(`
382
+ "asserConsoleLogsCleared(expected)
383
+
384
+ console.log was called without assertConsoleLogDev:
385
+ + A
386
+ + B
387
+ + C
388
+
389
+ You must call one of the assertConsoleDev helpers between each act call."
390
+ `);
391
+ });
392
+
393
+ // @gate __DEV__
394
+ it('fails if first expected log is not included', () => {
395
+ const message = expectToThrowFailure(() => {
396
+ console.log('Wow');
397
+ console.log('Bye');
398
+ assertConsoleLogDev(['Hi', 'Wow', 'Bye']);
399
+ });
400
+ expect(message).toMatchInlineSnapshot(`
401
+ "assertConsoleLogDev(expected)
402
+
403
+ Unexpected log(s) recorded.
404
+
405
+ - Expected logs
406
+ + Received logs
407
+
408
+ - Hi
409
+ Wow
410
+ Bye"
411
+ `);
412
+ });
413
+
414
+ // @gate __DEV__
415
+ it('fails if middle expected log is not included', () => {
416
+ const message = expectToThrowFailure(() => {
417
+ console.log('Hi');
418
+ console.log('Bye');
419
+ assertConsoleLogDev(['Hi', 'Wow', 'Bye']);
420
+ });
421
+ expect(message).toMatchInlineSnapshot(`
422
+ "assertConsoleLogDev(expected)
423
+
424
+ Unexpected log(s) recorded.
425
+
426
+ - Expected logs
427
+ + Received logs
428
+
429
+ Hi
430
+ - Wow
431
+ Bye"
432
+ `);
433
+ });
434
+
435
+ // @gate __DEV__
436
+ it('fails if last expected log is not included', () => {
437
+ const message = expectToThrowFailure(() => {
438
+ console.log('Hi');
439
+ console.log('Wow');
440
+ assertConsoleLogDev(['Hi', 'Wow', 'Bye']);
441
+ });
442
+ expect(message).toMatchInlineSnapshot(`
443
+ "assertConsoleLogDev(expected)
444
+
445
+ Expected log was not recorded.
446
+
447
+ - Expected logs
448
+ + Received logs
449
+
450
+ Hi
451
+ Wow
452
+ - Bye"
453
+ `);
454
+ });
455
+
456
+ // @gate __DEV__
457
+ it('fails if first received log is not included', () => {
458
+ const message = expectToThrowFailure(() => {
459
+ console.log('Hi');
460
+ console.log('Wow');
461
+ console.log('Bye');
462
+ assertConsoleLogDev(['Wow', 'Bye']);
463
+ });
464
+ expect(message).toMatchInlineSnapshot(`
465
+ "assertConsoleLogDev(expected)
466
+
467
+ Unexpected log(s) recorded.
468
+
469
+ - Expected logs
470
+ + Received logs
471
+
472
+ + Hi
473
+ Wow
474
+ Bye"
475
+ `);
476
+ });
477
+
478
+ // @gate __DEV__
479
+ it('fails if middle received log is not included', () => {
480
+ const message = expectToThrowFailure(() => {
481
+ console.log('Hi');
482
+ console.log('Wow');
483
+ console.log('Bye');
484
+ assertConsoleLogDev(['Hi', 'Bye']);
485
+ });
486
+ expect(message).toMatchInlineSnapshot(`
487
+ "assertConsoleLogDev(expected)
488
+
489
+ Unexpected log(s) recorded.
490
+
491
+ - Expected logs
492
+ + Received logs
493
+
494
+ Hi
495
+ + Wow
496
+ Bye"
497
+ `);
498
+ });
499
+
500
+ // @gate __DEV__
501
+ it('fails if last received log is not included', () => {
502
+ const message = expectToThrowFailure(() => {
503
+ console.log('Hi');
504
+ console.log('Wow');
505
+ console.log('Bye');
506
+ assertConsoleLogDev(['Hi', 'Wow']);
507
+ });
508
+ expect(message).toMatchInlineSnapshot(`
509
+ "assertConsoleLogDev(expected)
510
+
511
+ Unexpected log(s) recorded.
512
+
513
+ - Expected logs
514
+ + Received logs
515
+
516
+ Hi
517
+ Wow
518
+ + Bye"
519
+ `);
520
+ });
521
+
522
+ // @gate __DEV__
523
+ it('fails if both expected and received mismatch', () => {
524
+ const message = expectToThrowFailure(() => {
525
+ console.log('Hi');
526
+ console.log('Wow');
527
+ console.log('Bye');
528
+ assertConsoleLogDev(['Hi', 'Wow', 'Yikes']);
529
+ });
530
+ expect(message).toMatchInlineSnapshot(`
531
+ "assertConsoleLogDev(expected)
532
+
533
+ Unexpected log(s) recorded.
534
+
535
+ - Expected logs
536
+ + Received logs
537
+
538
+ Hi
539
+ Wow
540
+ - Yikes
541
+ + Bye"
542
+ `);
543
+ });
544
+
545
+ // @gate __DEV__
546
+ it('fails if both expected and received mismatch with multiple lines', () => {
547
+ const message = expectToThrowFailure(() => {
548
+ console.log('Hi\nFoo');
549
+ console.log('Wow\nBar');
550
+ console.log('Bye\nBaz');
551
+ assertConsoleLogDev(['Hi\nFoo', 'Wow\nBar', 'Yikes\nFaz']);
552
+ });
553
+ expect(message).toMatchInlineSnapshot(`
554
+ "assertConsoleLogDev(expected)
555
+
556
+ Unexpected log(s) recorded.
557
+
558
+ - Expected logs
559
+ + Received logs
560
+
561
+ Hi Foo
562
+ Wow Bar
563
+ - Yikes Faz
564
+ + Bye Baz"
565
+ `);
566
+ });
567
+
568
+ // @gate __DEV__
569
+ it('fails if local withoutStack passed to assertConsoleLogDev', () => {
570
+ const message = expectToThrowFailure(() => {
571
+ console.log('Hello');
572
+ assertConsoleLogDev([['Hello', {withoutStack: true}]]);
573
+ });
574
+
575
+ expect(message).toMatchInlineSnapshot(`
576
+ "assertConsoleLogDev(expected)
577
+
578
+ Do not pass withoutStack to assertConsoleLogDev logs, console.log does not have component stacks."
579
+ `);
580
+ });
581
+
582
+ // @gate __DEV__
583
+ it('fails if global withoutStack passed to assertConsoleLogDev', () => {
584
+ const message = expectToThrowFailure(() => {
585
+ console.log('Hello');
586
+ assertConsoleLogDev(['Hello'], {withoutStack: true});
587
+ });
588
+
589
+ expect(message).toMatchInlineSnapshot(`
590
+ "assertConsoleLogDev(expected)
591
+
592
+ Do not pass withoutStack to assertConsoleLogDev, console.log does not have component stacks."
593
+ `);
594
+
595
+ assertConsoleLogDev(['Hello']);
596
+ });
597
+
598
+ // @gate __DEV__
599
+ it('fails if the args is greater than %s argument number', () => {
600
+ const message = expectToThrowFailure(() => {
601
+ console.log('Hi %s', 'Sara', 'extra');
602
+ assertConsoleLogDev(['Hi']);
603
+ });
604
+ expect(message).toMatchInlineSnapshot(`
605
+ "assertConsoleLogDev(expected)
606
+
607
+ Received 2 arguments for a message with 1 placeholders:
608
+ "Hi %s""
609
+ `);
610
+ });
611
+
612
+ // @gate __DEV__
613
+ it('fails if the args is greater than %s argument number for multiple logs', () => {
614
+ const message = expectToThrowFailure(() => {
615
+ console.log('Hi %s', 'Sara', 'extra');
616
+ console.log('Bye %s', 'Sara', 'extra');
617
+ assertConsoleLogDev(['Hi', 'Bye']);
618
+ });
619
+ expect(message).toMatchInlineSnapshot(`
620
+ "assertConsoleLogDev(expected)
621
+
622
+ Received 2 arguments for a message with 1 placeholders:
623
+ "Hi %s"
624
+
625
+ Received 2 arguments for a message with 1 placeholders:
626
+ "Bye %s""
627
+ `);
628
+ });
629
+
630
+ // @gate __DEV__
631
+ it('fails if the %s argument number is greater than args', () => {
632
+ const message = expectToThrowFailure(() => {
633
+ console.log('Hi %s');
634
+ assertConsoleLogDev(['Hi']);
635
+ });
636
+ expect(message).toMatchInlineSnapshot(`
637
+ "assertConsoleLogDev(expected)
638
+
639
+ Received 0 arguments for a message with 1 placeholders:
640
+ "Hi %s""
641
+ `);
642
+ });
643
+
644
+ // @gate __DEV__
645
+ it('fails if the %s argument number is greater than args for multiple logs', () => {
646
+ const message = expectToThrowFailure(() => {
647
+ console.log('Hi %s');
648
+ console.log('Bye %s');
649
+ assertConsoleLogDev(['Hi', 'Bye']);
650
+ });
651
+ expect(message).toMatchInlineSnapshot(`
652
+ "assertConsoleLogDev(expected)
653
+
654
+ Received 0 arguments for a message with 1 placeholders:
655
+ "Hi %s"
656
+
657
+ Received 0 arguments for a message with 1 placeholders:
658
+ "Bye %s""
659
+ `);
660
+ });
661
+
662
+ // @gate __DEV__
663
+ it('fails if first arg is not an array', () => {
664
+ const message = expectToThrowFailure(() => {
665
+ console.log('Hi');
666
+ console.log('Bye');
667
+ assertConsoleLogDev('Hi', 'Bye');
668
+ });
669
+ expect(message).toMatchInlineSnapshot(`
670
+ "assertConsoleLogDev(expected)
671
+
672
+ Expected messages should be an array of strings but was given type "string"."
673
+ `);
674
+
675
+ assertConsoleLogDev(['Hi', 'Bye']);
676
+ });
677
+
678
+ it('should fail if waitFor is called before asserting', async () => {
679
+ const Yield = ({id}) => {
680
+ Scheduler.log(id);
681
+ return id;
682
+ };
683
+
684
+ const root = ReactNoop.createRoot();
685
+ startTransition(() => {
686
+ root.render(
687
+ <div>
688
+ <Yield id="foo" />
689
+ <Yield id="bar" />
690
+ <Yield id="baz" />
691
+ </div>
692
+ );
693
+ });
694
+
695
+ console.log('Not asserted');
696
+
697
+ const message = await awaitExpectToThrowFailure(async () => {
698
+ await waitFor(['foo', 'bar']);
699
+ });
700
+ expect(message).toMatchInlineSnapshot(`
701
+ "asserConsoleLogsCleared(expected)
702
+
703
+ console.log was called without assertConsoleLogDev:
704
+ + Not asserted
705
+
706
+ You must call one of the assertConsoleDev helpers between each act call."
707
+ `);
708
+
709
+ await waitForAll(['foo', 'bar', 'baz']);
710
+ });
711
+
712
+ test('should fail if waitForThrow is called before asserting', async () => {
713
+ const Yield = ({id}) => {
714
+ Scheduler.log(id);
715
+ return id;
716
+ };
717
+
718
+ function BadRender() {
719
+ throw new Error('Oh no!');
720
+ }
721
+
722
+ function App() {
723
+ return (
724
+ <div>
725
+ <Yield id="A" />
726
+ <Yield id="B" />
727
+ <BadRender />
728
+ <Yield id="C" />
729
+ <Yield id="D" />
730
+ </div>
731
+ );
732
+ }
733
+
734
+ const root = ReactNoop.createRoot();
735
+ root.render(<App />);
736
+
737
+ console.log('Not asserted');
738
+
739
+ const message = await awaitExpectToThrowFailure(async () => {
740
+ await waitForThrow('Oh no!');
741
+ });
742
+ expect(message).toMatchInlineSnapshot(`
743
+ "asserConsoleLogsCleared(expected)
744
+
745
+ console.log was called without assertConsoleLogDev:
746
+ + Not asserted
747
+
748
+ You must call one of the assertConsoleDev helpers between each act call."
749
+ `);
750
+
751
+ await waitForAll(['A', 'B', 'A', 'B']);
752
+ });
753
+
754
+ test('should fail if waitForPaint is called before asserting', async () => {
755
+ function App({prop}) {
756
+ const deferred = useDeferredValue(prop);
757
+ const text = `Urgent: ${prop}, Deferred: ${deferred}`;
758
+ Scheduler.log(text);
759
+ return text;
760
+ }
761
+
762
+ const root = ReactNoop.createRoot();
763
+ root.render(<App prop="A" />);
764
+
765
+ await waitForAll(['Urgent: A, Deferred: A']);
766
+ expect(root).toMatchRenderedOutput('Urgent: A, Deferred: A');
767
+
768
+ // This update will result in two separate paints: an urgent one, and a
769
+ // deferred one.
770
+ root.render(<App prop="B" />);
771
+
772
+ console.log('Not asserted');
773
+ const message = await awaitExpectToThrowFailure(async () => {
774
+ await waitForPaint(['Urgent: B, Deferred: A']);
775
+ });
776
+
777
+ expect(message).toMatchInlineSnapshot(`
778
+ "asserConsoleLogsCleared(expected)
779
+
780
+ console.log was called without assertConsoleLogDev:
781
+ + Not asserted
782
+
783
+ You must call one of the assertConsoleDev helpers between each act call."
784
+ `);
785
+
786
+ await waitForAll(['Urgent: B, Deferred: A', 'Urgent: B, Deferred: B']);
787
+ });
788
+
789
+ it('should fail if waitForAll is called before asserting', async () => {
790
+ const Yield = ({id}) => {
791
+ Scheduler.log(id);
792
+ return id;
793
+ };
794
+
795
+ const root = ReactNoop.createRoot();
796
+ startTransition(() => {
797
+ root.render(
798
+ <div>
799
+ <Yield id="foo" />
800
+ <Yield id="bar" />
801
+ <Yield id="baz" />
802
+ </div>
803
+ );
804
+ });
805
+
806
+ console.log('Not asserted');
807
+
808
+ const message = await awaitExpectToThrowFailure(async () => {
809
+ await waitForAll(['foo', 'bar', 'baz']);
810
+ });
811
+ expect(message).toMatchInlineSnapshot(`
812
+ "asserConsoleLogsCleared(expected)
813
+
814
+ console.log was called without assertConsoleLogDev:
815
+ + Not asserted
816
+
817
+ You must call one of the assertConsoleDev helpers between each act call."
818
+ `);
819
+
820
+ await waitForAll(['foo', 'bar', 'baz']);
821
+ });
822
+ it('should fail if toMatchRenderedOutput is called before asserting', async () => {
823
+ const Yield = ({id}) => {
824
+ Scheduler.log(id);
825
+ console.log('Not asserted');
826
+ return id;
827
+ };
828
+
829
+ const root = ReactNoop.createRoot();
830
+ startTransition(() => {
831
+ root.render(
832
+ <div>
833
+ <Yield id="foo" />
834
+ <Yield id="bar" />
835
+ <Yield id="baz" />
836
+ </div>
837
+ );
838
+ });
839
+
840
+ assertLog([]);
841
+
842
+ await waitForAll(['foo', 'bar', 'baz']);
843
+ const message = expectToThrowFailure(() => {
844
+ expect(root).toMatchRenderedOutput(<div>foobarbaz</div>);
845
+ });
846
+ expect(message).toMatchInlineSnapshot(`
847
+ "asserConsoleLogsCleared(expected)
848
+
849
+ console.log was called without assertConsoleLogDev:
850
+ + Not asserted
851
+ + Not asserted
852
+ + Not asserted
853
+
854
+ You must call one of the assertConsoleDev helpers between each act call."
855
+ `);
856
+
857
+ expect(root).toMatchRenderedOutput(<div>foobarbaz</div>);
858
+ });
859
+ });
860
+
861
+ describe('assertConsoleWarnDev', () => {
862
+ // @gate __DEV__
863
+ it('passes if an warning contains a stack', () => {
864
+ console.warn('Hello\n in div');
865
+ assertConsoleWarnDev(['Hello']);
866
+ });
867
+
868
+ // @gate __DEV__
869
+ it('passes if all warnings contain a stack', () => {
870
+ console.warn('Hello\n in div');
871
+ console.warn('Good day\n in div');
872
+ console.warn('Bye\n in div');
873
+ assertConsoleWarnDev(['Hello', 'Good day', 'Bye']);
874
+ });
875
+
876
+ it('fails if act is called without assertConsoleWarnDev', async () => {
877
+ const Yield = ({id}) => {
878
+ console.warn(id);
879
+ return id;
880
+ };
881
+
882
+ function App() {
883
+ return (
884
+ <div>
885
+ <Yield id="A" />
886
+ <Yield id="B" />
887
+ <Yield id="C" />
888
+ </div>
889
+ );
890
+ }
891
+
892
+ const root = ReactNoop.createRoot();
893
+ await act(() => {
894
+ root.render(<App />);
895
+ });
896
+ const message = await awaitExpectToThrowFailure(async () => {
897
+ await act(() => {
898
+ root.render(<App />);
899
+ });
900
+ });
901
+
902
+ expect(message).toMatchInlineSnapshot(`
903
+ "asserConsoleLogsCleared(expected)
904
+
905
+ console.warn was called without assertConsoleWarnDev:
906
+ + A
907
+ + B
908
+ + C
909
+
910
+ You must call one of the assertConsoleDev helpers between each act call."
911
+ `);
912
+ });
913
+
914
+ it('fails if act is called without any assertConsoleDev helpers', async () => {
915
+ const Yield = ({id}) => {
916
+ console.log(id);
917
+ console.warn(id);
918
+ console.error(id);
919
+ return id;
920
+ };
921
+
922
+ function App() {
923
+ return (
924
+ <div>
925
+ <Yield id="A" />
926
+ <Yield id="B" />
927
+ <Yield id="C" />
928
+ </div>
929
+ );
930
+ }
931
+
932
+ const root = ReactNoop.createRoot();
933
+ await act(() => {
934
+ root.render(<App />);
935
+ });
936
+ const message = await awaitExpectToThrowFailure(async () => {
937
+ await act(() => {
938
+ root.render(<App />);
939
+ });
940
+ });
941
+
942
+ expect(message).toMatchInlineSnapshot(`
943
+ "asserConsoleLogsCleared(expected)
944
+
945
+ console.log was called without assertConsoleLogDev:
946
+ + A
947
+ + B
948
+ + C
949
+
950
+ console.warn was called without assertConsoleWarnDev:
951
+ + A
952
+ + B
953
+ + C
954
+
955
+ console.error was called without assertConsoleErrorDev:
956
+ + A
957
+ + B
958
+ + C
959
+
960
+ You must call one of the assertConsoleDev helpers between each act call."
961
+ `);
962
+ });
963
+
964
+ // @gate __DEV__
965
+ it('fails if first expected warning is not included', () => {
966
+ const message = expectToThrowFailure(() => {
967
+ console.warn('Wow \n in div');
968
+ console.warn('Bye \n in div');
969
+ assertConsoleWarnDev(['Hi', 'Wow', 'Bye']);
970
+ });
971
+ expect(message).toMatchInlineSnapshot(`
972
+ "assertConsoleWarnDev(expected)
973
+
974
+ Unexpected warning(s) recorded.
975
+
976
+ - Expected warnings
977
+ + Received warnings
978
+
979
+ - Hi
980
+ - Wow
981
+ - Bye
982
+ + Wow <component stack>
983
+ + Bye <component stack>"
984
+ `);
985
+ });
986
+
987
+ // @gate __DEV__
988
+ it('fails if middle expected warning is not included', () => {
989
+ const message = expectToThrowFailure(() => {
990
+ console.warn('Hi \n in div');
991
+ console.warn('Bye \n in div');
992
+ assertConsoleWarnDev(['Hi', 'Wow', 'Bye']);
993
+ });
994
+ expect(message).toMatchInlineSnapshot(`
995
+ "assertConsoleWarnDev(expected)
996
+
997
+ Unexpected warning(s) recorded.
998
+
999
+ - Expected warnings
1000
+ + Received warnings
1001
+
1002
+ - Hi
1003
+ - Wow
1004
+ - Bye
1005
+ + Hi <component stack>
1006
+ + Bye <component stack>"
1007
+ `);
1008
+ });
1009
+
1010
+ // @gate __DEV__
1011
+ it('fails if last expected warning is not included', () => {
1012
+ const message = expectToThrowFailure(() => {
1013
+ console.warn('Hi \n in div');
1014
+ console.warn('Wow \n in div');
1015
+ assertConsoleWarnDev(['Hi', 'Wow', 'Bye']);
1016
+ });
1017
+ expect(message).toMatchInlineSnapshot(`
1018
+ "assertConsoleWarnDev(expected)
1019
+
1020
+ Expected warning was not recorded.
1021
+
1022
+ - Expected warnings
1023
+ + Received warnings
1024
+
1025
+ - Hi
1026
+ - Wow
1027
+ - Bye
1028
+ + Hi <component stack>
1029
+ + Wow <component stack>"
1030
+ `);
1031
+ });
1032
+
1033
+ // @gate __DEV__
1034
+ it('fails if first received warning is not included', () => {
1035
+ const message = expectToThrowFailure(() => {
1036
+ console.warn('Hi \n in div');
1037
+ console.warn('Wow \n in div');
1038
+ console.warn('Bye \n in div');
1039
+ assertConsoleWarnDev(['Wow', 'Bye']);
1040
+ });
1041
+ expect(message).toMatchInlineSnapshot(`
1042
+ "assertConsoleWarnDev(expected)
1043
+
1044
+ Unexpected warning(s) recorded.
1045
+
1046
+ - Expected warnings
1047
+ + Received warnings
1048
+
1049
+ - Wow
1050
+ - Bye
1051
+ + Hi <component stack>
1052
+ + Wow <component stack>
1053
+ + Bye <component stack>"
1054
+ `);
1055
+ });
1056
+
1057
+ // @gate __DEV__
1058
+ it('fails if middle received warning is not included', () => {
1059
+ const message = expectToThrowFailure(() => {
1060
+ console.warn('Hi \n in div');
1061
+ console.warn('Wow \n in div');
1062
+ console.warn('Bye \n in div');
1063
+ assertConsoleWarnDev(['Hi', 'Bye']);
1064
+ });
1065
+ expect(message).toMatchInlineSnapshot(`
1066
+ "assertConsoleWarnDev(expected)
1067
+
1068
+ Unexpected warning(s) recorded.
1069
+
1070
+ - Expected warnings
1071
+ + Received warnings
1072
+
1073
+ - Hi
1074
+ - Bye
1075
+ + Hi <component stack>
1076
+ + Wow <component stack>
1077
+ + Bye <component stack>"
1078
+ `);
1079
+ });
1080
+
1081
+ // @gate __DEV__
1082
+ it('fails if last received warning is not included', () => {
1083
+ const message = expectToThrowFailure(() => {
1084
+ console.warn('Hi \n in div');
1085
+ console.warn('Wow \n in div');
1086
+ console.warn('Bye \n in div');
1087
+ assertConsoleWarnDev(['Hi', 'Wow']);
1088
+ });
1089
+ expect(message).toMatchInlineSnapshot(`
1090
+ "assertConsoleWarnDev(expected)
1091
+
1092
+ Unexpected warning(s) recorded.
1093
+
1094
+ - Expected warnings
1095
+ + Received warnings
1096
+
1097
+ - Hi
1098
+ - Wow
1099
+ + Hi <component stack>
1100
+ + Wow <component stack>
1101
+ + Bye <component stack>"
1102
+ `);
1103
+ });
1104
+
1105
+ // @gate __DEV__
1106
+ it('fails if only warning does not contain a stack', () => {
1107
+ const message = expectToThrowFailure(() => {
1108
+ console.warn('Hello');
1109
+ assertConsoleWarnDev(['Hello']);
1110
+ });
1111
+ expect(message).toMatchInlineSnapshot(`
1112
+ "assertConsoleWarnDev(expected)
1113
+
1114
+ Missing component stack for:
1115
+ "Hello"
1116
+
1117
+ If this warning should omit a component stack, pass [log, {withoutStack: true}].
1118
+ If all warnings should omit the component stack, add {withoutStack: true} to the assertConsoleWarnDev call."
1119
+ `);
1120
+ });
1121
+
1122
+ // @gate __DEV__
1123
+ it('fails if first warning does not contain a stack', () => {
1124
+ const message = expectToThrowFailure(() => {
1125
+ console.warn('Hello');
1126
+ console.warn('Good day\n in div');
1127
+ console.warn('Bye\n in div');
1128
+ assertConsoleWarnDev(['Hello', 'Good day', 'Bye']);
1129
+ });
1130
+ expect(message).toMatchInlineSnapshot(`
1131
+ "assertConsoleWarnDev(expected)
1132
+
1133
+ Missing component stack for:
1134
+ "Hello"
1135
+
1136
+ If this warning should omit a component stack, pass [log, {withoutStack: true}].
1137
+ If all warnings should omit the component stack, add {withoutStack: true} to the assertConsoleWarnDev call."
1138
+ `);
1139
+ });
1140
+
1141
+ // @gate __DEV__
1142
+ it('fails if middle warning does not contain a stack', () => {
1143
+ const message = expectToThrowFailure(() => {
1144
+ console.warn('Hello\n in div');
1145
+ console.warn('Good day');
1146
+ console.warn('Bye\n in div');
1147
+ assertConsoleWarnDev(['Hello', 'Good day', 'Bye']);
1148
+ });
1149
+ expect(message).toMatchInlineSnapshot(`
1150
+ "assertConsoleWarnDev(expected)
1151
+
1152
+ Missing component stack for:
1153
+ "Good day"
1154
+
1155
+ If this warning should omit a component stack, pass [log, {withoutStack: true}].
1156
+ If all warnings should omit the component stack, add {withoutStack: true} to the assertConsoleWarnDev call."
1157
+ `);
1158
+ });
1159
+
1160
+ // @gate __DEV__
1161
+ it('fails if last warning does not contain a stack', () => {
1162
+ const message = expectToThrowFailure(() => {
1163
+ console.warn('Hello\n in div');
1164
+ console.warn('Good day\n in div');
1165
+ console.warn('Bye');
1166
+ assertConsoleWarnDev(['Hello', 'Good day', 'Bye']);
1167
+ });
1168
+ expect(message).toMatchInlineSnapshot(`
1169
+ "assertConsoleWarnDev(expected)
1170
+
1171
+ Missing component stack for:
1172
+ "Bye"
1173
+
1174
+ If this warning should omit a component stack, pass [log, {withoutStack: true}].
1175
+ If all warnings should omit the component stack, add {withoutStack: true} to the assertConsoleWarnDev call."
1176
+ `);
1177
+ });
1178
+
1179
+ // @gate __DEV__
1180
+ it('fails if all warnings do not contain a stack', () => {
1181
+ const message = expectToThrowFailure(() => {
1182
+ console.warn('Hello');
1183
+ console.warn('Good day');
1184
+ console.warn('Bye');
1185
+ assertConsoleWarnDev(['Hello', 'Good day', 'Bye']);
1186
+ });
1187
+ expect(message).toMatchInlineSnapshot(`
1188
+ "assertConsoleWarnDev(expected)
1189
+
1190
+ Missing component stack for:
1191
+ "Hello"
1192
+
1193
+ Missing component stack for:
1194
+ "Good day"
1195
+
1196
+ Missing component stack for:
1197
+ "Bye"
1198
+
1199
+ If this warning should omit a component stack, pass [log, {withoutStack: true}].
1200
+ If all warnings should omit the component stack, add {withoutStack: true} to the assertConsoleWarnDev call."
1201
+ `);
1202
+ });
1203
+
1204
+ describe('global withoutStack', () => {
1205
+ // @gate __DEV__
1206
+ it('passes if warnings without stack explicitly opt out', () => {
1207
+ console.warn('Hello');
1208
+ assertConsoleWarnDev(['Hello'], {withoutStack: true});
1209
+
1210
+ console.warn('Hello');
1211
+ console.warn('Good day');
1212
+ console.warn('Bye');
1213
+
1214
+ assertConsoleWarnDev(['Hello', 'Good day', 'Bye'], {
1215
+ withoutStack: true,
1216
+ });
1217
+ });
1218
+
1219
+ // @gate __DEV__
1220
+ it('fails if withoutStack is invalid null value', () => {
1221
+ const message = expectToThrowFailure(() => {
1222
+ console.warn('Hi');
1223
+ assertConsoleWarnDev(['Hi'], {withoutStack: null});
1224
+ });
1225
+ expect(message).toMatchInlineSnapshot(`
1226
+ "assertConsoleWarnDev(expected)
1227
+
1228
+ The second argument must be {withoutStack: true}.
1229
+
1230
+ Instead received {"withoutStack":null}."
1231
+ `);
1232
+ assertConsoleWarnDev(['Hi'], {withoutStack: true});
1233
+ });
1234
+
1235
+ // @gate __DEV__
1236
+ it('fails if withoutStack is invalid {} value', () => {
1237
+ const message = expectToThrowFailure(() => {
1238
+ console.warn('Hi');
1239
+ assertConsoleWarnDev(['Hi'], {withoutStack: {}});
1240
+ });
1241
+ expect(message).toMatchInlineSnapshot(`
1242
+ "assertConsoleWarnDev(expected)
1243
+
1244
+ The second argument must be {withoutStack: true}.
1245
+
1246
+ Instead received {"withoutStack":{}}."
1247
+ `);
1248
+ assertConsoleWarnDev(['Hi'], {withoutStack: true});
1249
+ });
1250
+
1251
+ // @gate __DEV__
1252
+ it('fails if withoutStack is invalid string value', () => {
1253
+ const message = expectToThrowFailure(() => {
1254
+ console.warn('Hi');
1255
+ assertConsoleWarnDev(['Hi'], {withoutStack: 'haha'});
1256
+ });
1257
+ expect(message).toMatchInlineSnapshot(`
1258
+ "assertConsoleWarnDev(expected)
1259
+
1260
+ The second argument must be {withoutStack: true}.
1261
+
1262
+ Instead received {"withoutStack":"haha"}."
1263
+ `);
1264
+ assertConsoleWarnDev(['Hi'], {withoutStack: true});
1265
+ });
1266
+
1267
+ // @gate __DEV__
1268
+ it('fails if only warning is not expected to have a stack, but does', () => {
1269
+ const message = expectToThrowFailure(() => {
1270
+ console.warn('Hello\n in div');
1271
+ assertConsoleWarnDev(['Hello'], {withoutStack: true});
1272
+ });
1273
+ expect(message).toMatchInlineSnapshot(`
1274
+ "assertConsoleWarnDev(expected)
1275
+
1276
+ Unexpected component stack for:
1277
+ "Hello <component stack>"
1278
+
1279
+ If this warning should include a component stack, remove {withoutStack: true} from this warning.
1280
+ If all warnings should include the component stack, you may need to remove {withoutStack: true} from the assertConsoleWarnDev call."
1281
+ `);
1282
+ });
1283
+
1284
+ // @gate __DEV__
1285
+ it('fails if warnings are not expected to have a stack, but some do', () => {
1286
+ const message = expectToThrowFailure(() => {
1287
+ console.warn('Hello\n in div');
1288
+ console.warn('Good day');
1289
+ console.warn('Bye\n in div');
1290
+ assertConsoleWarnDev(['Hello', 'Good day', 'Bye'], {
1291
+ withoutStack: true,
1292
+ });
1293
+ });
1294
+ expect(message).toMatchInlineSnapshot(`
1295
+ "assertConsoleWarnDev(expected)
1296
+
1297
+ Unexpected component stack for:
1298
+ "Hello <component stack>"
1299
+
1300
+ Unexpected component stack for:
1301
+ "Bye <component stack>"
1302
+
1303
+ If this warning should include a component stack, remove {withoutStack: true} from this warning.
1304
+ If all warnings should include the component stack, you may need to remove {withoutStack: true} from the assertConsoleWarnDev call."
1305
+ `);
1306
+ });
1307
+ });
1308
+ describe('local withoutStack', () => {
1309
+ // @gate __DEV__
1310
+ it('passes when expected withoutStack logs matches the actual logs', () => {
1311
+ console.warn('Hello\n in div');
1312
+ console.warn('Good day');
1313
+ console.warn('Bye\n in div');
1314
+ assertConsoleWarnDev([
1315
+ 'Hello',
1316
+ ['Good day', {withoutStack: true}],
1317
+ 'Bye',
1318
+ ]);
1319
+ });
1320
+
1321
+ // @gate __DEV__
1322
+ it('fails if withoutStack is invalid null value', () => {
1323
+ const message = expectToThrowFailure(() => {
1324
+ console.warn('Hi');
1325
+ assertConsoleWarnDev([['Hi', {withoutStack: null}]]);
1326
+ });
1327
+ expect(message).toMatchInlineSnapshot(`
1328
+ "assertConsoleWarnDev(expected)
1329
+
1330
+ Log entries that are arrays must be of the form [string, {withoutStack: true}]
1331
+
1332
+ Instead received [string, {"withoutStack":null}]."
1333
+ `);
1334
+ });
1335
+
1336
+ // @gate __DEV__
1337
+ it('fails if withoutStack is invalid {} value', () => {
1338
+ const message = expectToThrowFailure(() => {
1339
+ console.warn('Hi');
1340
+ assertConsoleWarnDev([['Hi', {withoutStack: {}}]]);
1341
+ });
1342
+ expect(message).toMatchInlineSnapshot(`
1343
+ "assertConsoleWarnDev(expected)
1344
+
1345
+ Log entries that are arrays must be of the form [string, {withoutStack: true}]
1346
+
1347
+ Instead received [string, {"withoutStack":{}}]."
1348
+ `);
1349
+ });
1350
+
1351
+ // @gate __DEV__
1352
+ it('fails if withoutStack is invalid string value', () => {
1353
+ const message = expectToThrowFailure(() => {
1354
+ console.warn('Hi');
1355
+ assertConsoleWarnDev([['Hi', {withoutStack: 'haha'}]]);
1356
+ });
1357
+ expect(message).toMatchInlineSnapshot(`
1358
+ "assertConsoleWarnDev(expected)
1359
+
1360
+ Log entries that are arrays must be of the form [string, {withoutStack: true}]
1361
+
1362
+ Instead received [string, {"withoutStack":"haha"}]."
1363
+ `);
1364
+ });
1365
+
1366
+ // @gate __DEV__
1367
+ it('fails if withoutStack is invalid number value', () => {
1368
+ const message = expectToThrowFailure(() => {
1369
+ console.warn('Hi');
1370
+ assertConsoleWarnDev([['Hi', {withoutStack: 4}]]);
1371
+ });
1372
+ expect(message).toMatchInlineSnapshot(`
1373
+ "assertConsoleWarnDev(expected)
1374
+
1375
+ Log entries that are arrays must be of the form [string, {withoutStack: true}]
1376
+
1377
+ Instead received [string, {"withoutStack":4}]."
1378
+ `);
1379
+ });
1380
+
1381
+ // @gate __DEV__
1382
+ it('fails if you forget to wrap local withoutStack in array', () => {
1383
+ const message = expectToThrowFailure(() => {
1384
+ console.warn('Hello\n in div');
1385
+ console.warn('Bye\n in div');
1386
+ assertConsoleWarnDev(['Hello', {withoutStack: true}, 'Bye']);
1387
+ });
1388
+ expect(message).toMatchInlineSnapshot(`
1389
+ "assertConsoleWarnDev(expected)
1390
+
1391
+ Did you forget to wrap a log with withoutStack in an array?
1392
+
1393
+ The expected message for assertConsoleWarnDev() must be a string or an array of length 2.
1394
+
1395
+ Instead received {"withoutStack":true}."
1396
+ `);
1397
+ });
1398
+
1399
+ // @gate __DEV__
1400
+ it('fails if you wrap in an array unnecessarily', () => {
1401
+ const message = expectToThrowFailure(() => {
1402
+ console.warn('Hello');
1403
+ assertConsoleWarnDev([['Hello']]);
1404
+ });
1405
+ expect(message).toMatchInlineSnapshot(`
1406
+ "assertConsoleWarnDev(expected)
1407
+
1408
+ Did you forget to remove the array around the log?
1409
+
1410
+ The expected message for assertConsoleWarnDev() must be a string or an array of length 2, but there's only one item in the array. If this is intentional, remove the extra array."
1411
+ `);
1412
+ });
1413
+
1414
+ // @gate __DEV__
1415
+ it('fails if only warning is not expected to have a stack, but does', () => {
1416
+ const message = expectToThrowFailure(() => {
1417
+ console.warn('Hello\n in div');
1418
+ assertConsoleWarnDev([['Hello', {withoutStack: true}]]);
1419
+ });
1420
+ expect(message).toMatchInlineSnapshot(`
1421
+ "assertConsoleWarnDev(expected)
1422
+
1423
+ Unexpected component stack for:
1424
+ "Hello <component stack>"
1425
+
1426
+ If this warning should include a component stack, remove {withoutStack: true} from this warning.
1427
+ If all warnings should include the component stack, you may need to remove {withoutStack: true} from the assertConsoleWarnDev call."
1428
+ `);
1429
+ });
1430
+
1431
+ // @gate __DEV__
1432
+ it('fails if warnings are not expected to have a stack, but some do', () => {
1433
+ const message = expectToThrowFailure(() => {
1434
+ console.warn('Hello\n in div');
1435
+ console.warn('Good day');
1436
+ console.warn('Bye\n in div');
1437
+ assertConsoleWarnDev([
1438
+ [
1439
+ 'Hello',
1440
+ {
1441
+ withoutStack: true,
1442
+ },
1443
+ ],
1444
+ 'Good day',
1445
+ [
1446
+ 'Bye',
1447
+ {
1448
+ withoutStack: true,
1449
+ },
1450
+ ],
1451
+ ]);
1452
+ });
1453
+ expect(message).toMatchInlineSnapshot(`
1454
+ "assertConsoleWarnDev(expected)
1455
+
1456
+ Unexpected component stack for:
1457
+ "Hello <component stack>"
1458
+
1459
+ Unexpected component stack for:
1460
+ "Bye <component stack>"
1461
+
1462
+ If this warning should include a component stack, remove {withoutStack: true} from this warning.
1463
+ If all warnings should include the component stack, you may need to remove {withoutStack: true} from the assertConsoleWarnDev call."
1464
+ `);
1465
+ });
1466
+ });
1467
+
1468
+ // @gate __DEV__
1469
+ it('fails if the args is greater than %s argument number', () => {
1470
+ const message = expectToThrowFailure(() => {
1471
+ console.warn('Hi %s', 'Sara', 'extra');
1472
+ assertConsoleWarnDev(['Hi'], {withoutStack: true});
1473
+ });
1474
+ expect(message).toMatchInlineSnapshot(`
1475
+ "assertConsoleWarnDev(expected)
1476
+
1477
+ Received 2 arguments for a message with 1 placeholders:
1478
+ "Hi %s""
1479
+ `);
1480
+ });
1481
+
1482
+ // @gate __DEV__
1483
+ it('fails if the args is greater than %s argument number for multiple warnings', () => {
1484
+ const message = expectToThrowFailure(() => {
1485
+ console.warn('Hi %s', 'Sara', 'extra');
1486
+ console.warn('Bye %s', 'Sara', 'extra');
1487
+ assertConsoleWarnDev(['Hi', 'Bye'], {withoutStack: true});
1488
+ });
1489
+ expect(message).toMatchInlineSnapshot(`
1490
+ "assertConsoleWarnDev(expected)
1491
+
1492
+ Received 2 arguments for a message with 1 placeholders:
1493
+ "Hi %s"
1494
+
1495
+ Received 2 arguments for a message with 1 placeholders:
1496
+ "Bye %s""
1497
+ `);
1498
+ });
1499
+
1500
+ // @gate __DEV__
1501
+ it('fails if the %s argument number is greater than args', () => {
1502
+ const message = expectToThrowFailure(() => {
1503
+ console.warn('Hi %s');
1504
+ assertConsoleWarnDev(['Hi'], {withoutStack: true});
1505
+ });
1506
+ expect(message).toMatchInlineSnapshot(`
1507
+ "assertConsoleWarnDev(expected)
1508
+
1509
+ Received 0 arguments for a message with 1 placeholders:
1510
+ "Hi %s""
1511
+ `);
1512
+ });
1513
+
1514
+ // @gate __DEV__
1515
+ it('fails if the %s argument number is greater than args for multiple warnings', () => {
1516
+ const message = expectToThrowFailure(() => {
1517
+ console.warn('Hi %s');
1518
+ console.warn('Bye %s');
1519
+ assertConsoleWarnDev(['Hi', 'Bye'], {withoutStack: true});
1520
+ });
1521
+ expect(message).toMatchInlineSnapshot(`
1522
+ "assertConsoleWarnDev(expected)
1523
+
1524
+ Received 0 arguments for a message with 1 placeholders:
1525
+ "Hi %s"
1526
+
1527
+ Received 0 arguments for a message with 1 placeholders:
1528
+ "Bye %s""
1529
+ `);
1530
+ });
1531
+
1532
+ // @gate __DEV__
1533
+ it('fails if component stack is passed twice', () => {
1534
+ const message = expectToThrowFailure(() => {
1535
+ console.warn('Hi %s%s', '\n in div', '\n in div');
1536
+ assertConsoleWarnDev(['Hi']);
1537
+ });
1538
+ expect(message).toMatchInlineSnapshot(`
1539
+ "assertConsoleWarnDev(expected)
1540
+
1541
+ Received more than one component stack for a warning:
1542
+ "Hi %s%s""
1543
+ `);
1544
+ });
1545
+
1546
+ // @gate __DEV__
1547
+ it('fails if multiple logs pass component stack twice', () => {
1548
+ const message = expectToThrowFailure(() => {
1549
+ console.warn('Hi %s%s', '\n in div', '\n in div');
1550
+ console.warn('Bye %s%s', '\n in div', '\n in div');
1551
+ assertConsoleWarnDev(['Hi', 'Bye']);
1552
+ });
1553
+ expect(message).toMatchInlineSnapshot(`
1554
+ "assertConsoleWarnDev(expected)
1555
+
1556
+ Received more than one component stack for a warning:
1557
+ "Hi %s%s"
1558
+
1559
+ Received more than one component stack for a warning:
1560
+ "Bye %s%s""
1561
+ `);
1562
+ });
1563
+
1564
+ // @gate __DEV__
1565
+ it('fails if multiple strings are passed without an array wrapper for single log', () => {
1566
+ const message = expectToThrowFailure(() => {
1567
+ console.warn('Hi \n in div');
1568
+ console.warn('Bye \n in div');
1569
+ assertConsoleWarnDev('Hi', 'Bye');
1570
+ });
1571
+ expect(message).toMatchInlineSnapshot(`
1572
+ "assertConsoleWarnDev(expected)
1573
+
1574
+ Expected messages should be an array of strings but was given type "string"."
1575
+ `);
1576
+ assertConsoleWarnDev(['Hi', 'Bye']);
1577
+ });
1578
+
1579
+ // @gate __DEV__
1580
+ it('fails if multiple strings are passed without an array wrapper for multiple logs', () => {
1581
+ const message = expectToThrowFailure(() => {
1582
+ console.warn('Hi \n in div');
1583
+ console.warn('Bye \n in div');
1584
+ assertConsoleWarnDev('Hi', 'Bye');
1585
+ });
1586
+ expect(message).toMatchInlineSnapshot(`
1587
+ "assertConsoleWarnDev(expected)
1588
+
1589
+ Expected messages should be an array of strings but was given type "string"."
1590
+ `);
1591
+ assertConsoleWarnDev(['Hi', 'Bye']);
1592
+ });
1593
+
1594
+ // @gate __DEV__
1595
+ it('fails on more than two arguments', () => {
1596
+ const message = expectToThrowFailure(() => {
1597
+ console.warn('Hi \n in div');
1598
+ console.warn('Wow \n in div');
1599
+ console.warn('Bye \n in div');
1600
+ assertConsoleWarnDev('Hi', undefined, 'Bye');
1601
+ });
1602
+ expect(message).toMatchInlineSnapshot(`
1603
+ "assertConsoleWarnDev(expected)
1604
+
1605
+ Expected messages should be an array of strings but was given type "string"."
1606
+ `);
1607
+ assertConsoleWarnDev(['Hi', 'Wow', 'Bye']);
1608
+ });
1609
+
1610
+ it('should fail if waitFor is called before asserting', async () => {
1611
+ const Yield = ({id}) => {
1612
+ Scheduler.log(id);
1613
+ return id;
1614
+ };
1615
+
1616
+ const root = ReactNoop.createRoot();
1617
+ startTransition(() => {
1618
+ root.render(
1619
+ <div>
1620
+ <Yield id="foo" />
1621
+ <Yield id="bar" />
1622
+ <Yield id="baz" />
1623
+ </div>
1624
+ );
1625
+ });
1626
+
1627
+ console.warn('Not asserted');
1628
+
1629
+ const message = await awaitExpectToThrowFailure(async () => {
1630
+ await waitFor(['foo', 'bar']);
1631
+ });
1632
+ expect(message).toMatchInlineSnapshot(`
1633
+ "asserConsoleLogsCleared(expected)
1634
+
1635
+ console.warn was called without assertConsoleWarnDev:
1636
+ + Not asserted
1637
+
1638
+ You must call one of the assertConsoleDev helpers between each act call."
1639
+ `);
1640
+
1641
+ await waitForAll(['foo', 'bar', 'baz']);
1642
+ });
1643
+
1644
+ test('should fail if waitForThrow is called before asserting', async () => {
1645
+ const Yield = ({id}) => {
1646
+ Scheduler.log(id);
1647
+ return id;
1648
+ };
1649
+
1650
+ function BadRender() {
1651
+ throw new Error('Oh no!');
1652
+ }
1653
+
1654
+ function App() {
1655
+ return (
1656
+ <div>
1657
+ <Yield id="A" />
1658
+ <Yield id="B" />
1659
+ <BadRender />
1660
+ <Yield id="C" />
1661
+ <Yield id="D" />
1662
+ </div>
1663
+ );
1664
+ }
1665
+
1666
+ const root = ReactNoop.createRoot();
1667
+ root.render(<App />);
1668
+
1669
+ console.warn('Not asserted');
1670
+
1671
+ const message = await awaitExpectToThrowFailure(async () => {
1672
+ await waitForThrow('Oh no!');
1673
+ });
1674
+ expect(message).toMatchInlineSnapshot(`
1675
+ "asserConsoleLogsCleared(expected)
1676
+
1677
+ console.warn was called without assertConsoleWarnDev:
1678
+ + Not asserted
1679
+
1680
+ You must call one of the assertConsoleDev helpers between each act call."
1681
+ `);
1682
+
1683
+ await waitForAll(['A', 'B', 'A', 'B']);
1684
+ });
1685
+
1686
+ test('should fail if waitForPaint is called before asserting', async () => {
1687
+ function App({prop}) {
1688
+ const deferred = useDeferredValue(prop);
1689
+ const text = `Urgent: ${prop}, Deferred: ${deferred}`;
1690
+ Scheduler.log(text);
1691
+ return text;
1692
+ }
1693
+
1694
+ const root = ReactNoop.createRoot();
1695
+ root.render(<App prop="A" />);
1696
+
1697
+ await waitForAll(['Urgent: A, Deferred: A']);
1698
+ expect(root).toMatchRenderedOutput('Urgent: A, Deferred: A');
1699
+
1700
+ // This update will result in two separate paints: an urgent one, and a
1701
+ // deferred one.
1702
+ root.render(<App prop="B" />);
1703
+
1704
+ console.warn('Not asserted');
1705
+ const message = await awaitExpectToThrowFailure(async () => {
1706
+ await waitForPaint(['Urgent: B, Deferred: A']);
1707
+ });
1708
+
1709
+ expect(message).toMatchInlineSnapshot(`
1710
+ "asserConsoleLogsCleared(expected)
1711
+
1712
+ console.warn was called without assertConsoleWarnDev:
1713
+ + Not asserted
1714
+
1715
+ You must call one of the assertConsoleDev helpers between each act call."
1716
+ `);
1717
+
1718
+ await waitForAll(['Urgent: B, Deferred: A', 'Urgent: B, Deferred: B']);
1719
+ });
1720
+
1721
+ it('should fail if waitForAll is called before asserting', async () => {
1722
+ const Yield = ({id}) => {
1723
+ Scheduler.log(id);
1724
+ return id;
1725
+ };
1726
+
1727
+ const root = ReactNoop.createRoot();
1728
+ startTransition(() => {
1729
+ root.render(
1730
+ <div>
1731
+ <Yield id="foo" />
1732
+ <Yield id="bar" />
1733
+ <Yield id="baz" />
1734
+ </div>
1735
+ );
1736
+ });
1737
+
1738
+ console.warn('Not asserted');
1739
+
1740
+ const message = await awaitExpectToThrowFailure(async () => {
1741
+ await waitForAll(['foo', 'bar', 'baz']);
1742
+ });
1743
+ expect(message).toMatchInlineSnapshot(`
1744
+ "asserConsoleLogsCleared(expected)
1745
+
1746
+ console.warn was called without assertConsoleWarnDev:
1747
+ + Not asserted
1748
+
1749
+ You must call one of the assertConsoleDev helpers between each act call."
1750
+ `);
1751
+
1752
+ await waitForAll(['foo', 'bar', 'baz']);
1753
+ });
1754
+ it('should fail if toMatchRenderedOutput is called before asserting', async () => {
1755
+ const Yield = ({id}) => {
1756
+ Scheduler.log(id);
1757
+ console.warn('Not asserted');
1758
+ return id;
1759
+ };
1760
+
1761
+ const root = ReactNoop.createRoot();
1762
+ startTransition(() => {
1763
+ root.render(
1764
+ <div>
1765
+ <Yield id="foo" />
1766
+ <Yield id="bar" />
1767
+ <Yield id="baz" />
1768
+ </div>
1769
+ );
1770
+ });
1771
+
1772
+ assertLog([]);
1773
+
1774
+ await waitForAll(['foo', 'bar', 'baz']);
1775
+ const message = expectToThrowFailure(() => {
1776
+ expect(root).toMatchRenderedOutput(<div>foobarbaz</div>);
1777
+ });
1778
+ expect(message).toMatchInlineSnapshot(`
1779
+ "asserConsoleLogsCleared(expected)
1780
+
1781
+ console.warn was called without assertConsoleWarnDev:
1782
+ + Not asserted
1783
+ + Not asserted
1784
+ + Not asserted
1785
+
1786
+ You must call one of the assertConsoleDev helpers between each act call."
1787
+ `);
1788
+
1789
+ expect(root).toMatchRenderedOutput(<div>foobarbaz</div>);
1790
+ });
1791
+ });
1792
+
1793
+ describe('assertConsoleErrorDev', () => {
1794
+ // @gate __DEV__
1795
+ it('passes if an error contains a stack', () => {
1796
+ console.error('Hello\n in div');
1797
+ assertConsoleErrorDev(['Hello']);
1798
+ });
1799
+
1800
+ // @gate __DEV__
1801
+ it('passes if all errors contain a stack', () => {
1802
+ console.error('Hello\n in div');
1803
+ console.error('Good day\n in div');
1804
+ console.error('Bye\n in div');
1805
+ assertConsoleErrorDev(['Hello', 'Good day', 'Bye']);
1806
+ });
1807
+
1808
+ it('fails if act is called without assertConsoleErrorDev', async () => {
1809
+ const Yield = ({id}) => {
1810
+ console.error(id);
1811
+ return id;
1812
+ };
1813
+
1814
+ function App() {
1815
+ return (
1816
+ <div>
1817
+ <Yield id="A" />
1818
+ <Yield id="B" />
1819
+ <Yield id="C" />
1820
+ </div>
1821
+ );
1822
+ }
1823
+
1824
+ const root = ReactNoop.createRoot();
1825
+ await act(() => {
1826
+ root.render(<App />);
1827
+ });
1828
+ const message = await awaitExpectToThrowFailure(async () => {
1829
+ await act(() => {
1830
+ root.render(<App />);
1831
+ });
1832
+ });
1833
+
1834
+ expect(message).toMatchInlineSnapshot(`
1835
+ "asserConsoleLogsCleared(expected)
1836
+
1837
+ console.error was called without assertConsoleErrorDev:
1838
+ + A
1839
+ + B
1840
+ + C
1841
+
1842
+ You must call one of the assertConsoleDev helpers between each act call."
1843
+ `);
1844
+ });
1845
+
1846
+ it('fails if act is called without any assertConsoleDev helpers', async () => {
1847
+ const Yield = ({id}) => {
1848
+ console.log(id);
1849
+ console.warn(id);
1850
+ console.error(id);
1851
+ return id;
1852
+ };
1853
+
1854
+ function App() {
1855
+ return (
1856
+ <div>
1857
+ <Yield id="A" />
1858
+ <Yield id="B" />
1859
+ <Yield id="C" />
1860
+ </div>
1861
+ );
1862
+ }
1863
+
1864
+ const root = ReactNoop.createRoot();
1865
+ await act(() => {
1866
+ root.render(<App />);
1867
+ });
1868
+ const message = await awaitExpectToThrowFailure(async () => {
1869
+ await act(() => {
1870
+ root.render(<App />);
1871
+ });
1872
+ });
1873
+
1874
+ expect(message).toMatchInlineSnapshot(`
1875
+ "asserConsoleLogsCleared(expected)
1876
+
1877
+ console.log was called without assertConsoleLogDev:
1878
+ + A
1879
+ + B
1880
+ + C
1881
+
1882
+ console.warn was called without assertConsoleWarnDev:
1883
+ + A
1884
+ + B
1885
+ + C
1886
+
1887
+ console.error was called without assertConsoleErrorDev:
1888
+ + A
1889
+ + B
1890
+ + C
1891
+
1892
+ You must call one of the assertConsoleDev helpers between each act call."
1893
+ `);
1894
+ });
1895
+
1896
+ // @gate __DEV__
1897
+ it('fails if first expected error is not included', () => {
1898
+ const message = expectToThrowFailure(() => {
1899
+ console.error('Wow \n in div');
1900
+ console.error('Bye \n in div');
1901
+ assertConsoleErrorDev(['Hi', 'Wow', 'Bye']);
1902
+ });
1903
+ expect(message).toMatchInlineSnapshot(`
1904
+ "assertConsoleErrorDev(expected)
1905
+
1906
+ Unexpected error(s) recorded.
1907
+
1908
+ - Expected errors
1909
+ + Received errors
1910
+
1911
+ - Hi
1912
+ - Wow
1913
+ - Bye
1914
+ + Wow <component stack>
1915
+ + Bye <component stack>"
1916
+ `);
1917
+ });
1918
+
1919
+ // @gate __DEV__
1920
+ it('fails if middle expected error is not included', () => {
1921
+ const message = expectToThrowFailure(() => {
1922
+ console.error('Hi \n in div');
1923
+ console.error('Bye \n in div');
1924
+ assertConsoleErrorDev(['Hi', 'Wow', 'Bye']);
1925
+ });
1926
+ expect(message).toMatchInlineSnapshot(`
1927
+ "assertConsoleErrorDev(expected)
1928
+
1929
+ Unexpected error(s) recorded.
1930
+
1931
+ - Expected errors
1932
+ + Received errors
1933
+
1934
+ - Hi
1935
+ - Wow
1936
+ - Bye
1937
+ + Hi <component stack>
1938
+ + Bye <component stack>"
1939
+ `);
1940
+ });
1941
+
1942
+ // @gate __DEV__
1943
+ it('fails if last expected error is not included', () => {
1944
+ const message = expectToThrowFailure(() => {
1945
+ console.error('Hi \n in div');
1946
+ console.error('Wow \n in div');
1947
+ assertConsoleErrorDev(['Hi', 'Wow', 'Bye']);
1948
+ });
1949
+ expect(message).toMatchInlineSnapshot(`
1950
+ "assertConsoleErrorDev(expected)
1951
+
1952
+ Expected error was not recorded.
1953
+
1954
+ - Expected errors
1955
+ + Received errors
1956
+
1957
+ - Hi
1958
+ - Wow
1959
+ - Bye
1960
+ + Hi <component stack>
1961
+ + Wow <component stack>"
1962
+ `);
1963
+ });
1964
+
1965
+ // @gate __DEV__
1966
+ it('fails if first received error is not included', () => {
1967
+ const message = expectToThrowFailure(() => {
1968
+ console.error('Hi \n in div');
1969
+ console.error('Wow \n in div');
1970
+ console.error('Bye \n in div');
1971
+ assertConsoleErrorDev(['Wow', 'Bye']);
1972
+ });
1973
+ expect(message).toMatchInlineSnapshot(`
1974
+ "assertConsoleErrorDev(expected)
1975
+
1976
+ Unexpected error(s) recorded.
1977
+
1978
+ - Expected errors
1979
+ + Received errors
1980
+
1981
+ - Wow
1982
+ - Bye
1983
+ + Hi <component stack>
1984
+ + Wow <component stack>
1985
+ + Bye <component stack>"
1986
+ `);
1987
+ });
1988
+
1989
+ // @gate __DEV__
1990
+ it('fails if middle received error is not included', () => {
1991
+ const message = expectToThrowFailure(() => {
1992
+ console.error('Hi \n in div');
1993
+ console.error('Wow \n in div');
1994
+ console.error('Bye \n in div');
1995
+ assertConsoleErrorDev(['Hi', 'Bye']);
1996
+ });
1997
+ expect(message).toMatchInlineSnapshot(`
1998
+ "assertConsoleErrorDev(expected)
1999
+
2000
+ Unexpected error(s) recorded.
2001
+
2002
+ - Expected errors
2003
+ + Received errors
2004
+
2005
+ - Hi
2006
+ - Bye
2007
+ + Hi <component stack>
2008
+ + Wow <component stack>
2009
+ + Bye <component stack>"
2010
+ `);
2011
+ });
2012
+
2013
+ // @gate __DEV__
2014
+ it('fails if last received error is not included', () => {
2015
+ const message = expectToThrowFailure(() => {
2016
+ console.error('Hi \n in div');
2017
+ console.error('Wow \n in div');
2018
+ console.error('Bye \n in div');
2019
+ assertConsoleErrorDev(['Hi', 'Wow']);
2020
+ });
2021
+ expect(message).toMatchInlineSnapshot(`
2022
+ "assertConsoleErrorDev(expected)
2023
+
2024
+ Unexpected error(s) recorded.
2025
+
2026
+ - Expected errors
2027
+ + Received errors
2028
+
2029
+ - Hi
2030
+ - Wow
2031
+ + Hi <component stack>
2032
+ + Wow <component stack>
2033
+ + Bye <component stack>"
2034
+ `);
2035
+ });
2036
+ // @gate __DEV__
2037
+ it('fails if only error does not contain a stack', () => {
2038
+ const message = expectToThrowFailure(() => {
2039
+ console.error('Hello');
2040
+ assertConsoleErrorDev(['Hello']);
2041
+ });
2042
+ expect(message).toMatchInlineSnapshot(`
2043
+ "assertConsoleErrorDev(expected)
2044
+
2045
+ Missing component stack for:
2046
+ "Hello"
2047
+
2048
+ If this error should omit a component stack, pass [log, {withoutStack: true}].
2049
+ If all errors should omit the component stack, add {withoutStack: true} to the assertConsoleErrorDev call."
2050
+ `);
2051
+ });
2052
+
2053
+ // @gate __DEV__
2054
+ it('fails if first error does not contain a stack', () => {
2055
+ const message = expectToThrowFailure(() => {
2056
+ console.error('Hello\n in div');
2057
+ console.error('Good day\n in div');
2058
+ console.error('Bye');
2059
+ assertConsoleErrorDev(['Hello', 'Good day', 'Bye']);
2060
+ });
2061
+ expect(message).toMatchInlineSnapshot(`
2062
+ "assertConsoleErrorDev(expected)
2063
+
2064
+ Missing component stack for:
2065
+ "Bye"
2066
+
2067
+ If this error should omit a component stack, pass [log, {withoutStack: true}].
2068
+ If all errors should omit the component stack, add {withoutStack: true} to the assertConsoleErrorDev call."
2069
+ `);
2070
+ });
2071
+ // @gate __DEV__
2072
+ it('fails if last error does not contain a stack', () => {
2073
+ const message = expectToThrowFailure(() => {
2074
+ console.error('Hello');
2075
+ console.error('Good day\n in div');
2076
+ console.error('Bye\n in div');
2077
+ assertConsoleErrorDev(['Hello', 'Good day', 'Bye']);
2078
+ });
2079
+ expect(message).toMatchInlineSnapshot(`
2080
+ "assertConsoleErrorDev(expected)
2081
+
2082
+ Missing component stack for:
2083
+ "Hello"
2084
+
2085
+ If this error should omit a component stack, pass [log, {withoutStack: true}].
2086
+ If all errors should omit the component stack, add {withoutStack: true} to the assertConsoleErrorDev call."
2087
+ `);
2088
+ });
2089
+ // @gate __DEV__
2090
+ it('fails if middle error does not contain a stack', () => {
2091
+ const message = expectToThrowFailure(() => {
2092
+ console.error('Hello\n in div');
2093
+ console.error('Good day');
2094
+ console.error('Bye\n in div');
2095
+ assertConsoleErrorDev(['Hello', 'Good day', 'Bye']);
2096
+ });
2097
+ expect(message).toMatchInlineSnapshot(`
2098
+ "assertConsoleErrorDev(expected)
2099
+
2100
+ Missing component stack for:
2101
+ "Good day"
2102
+
2103
+ If this error should omit a component stack, pass [log, {withoutStack: true}].
2104
+ If all errors should omit the component stack, add {withoutStack: true} to the assertConsoleErrorDev call."
2105
+ `);
2106
+ });
2107
+ // @gate __DEV__
2108
+ it('fails if all errors do not contain a stack', () => {
2109
+ const message = expectToThrowFailure(() => {
2110
+ console.error('Hello');
2111
+ console.error('Good day');
2112
+ console.error('Bye');
2113
+ assertConsoleErrorDev(['Hello', 'Good day', 'Bye']);
2114
+ });
2115
+ expect(message).toMatchInlineSnapshot(`
2116
+ "assertConsoleErrorDev(expected)
2117
+
2118
+ Missing component stack for:
2119
+ "Hello"
2120
+
2121
+ Missing component stack for:
2122
+ "Good day"
2123
+
2124
+ Missing component stack for:
2125
+ "Bye"
2126
+
2127
+ If this error should omit a component stack, pass [log, {withoutStack: true}].
2128
+ If all errors should omit the component stack, add {withoutStack: true} to the assertConsoleErrorDev call."
2129
+ `);
2130
+ });
2131
+
2132
+ describe('global withoutStack', () => {
2133
+ // @gate __DEV__
2134
+ it('passes if errors without stack explicitly opt out', () => {
2135
+ console.error('Hello');
2136
+ assertConsoleErrorDev(['Hello'], {withoutStack: true});
2137
+
2138
+ console.error('Hello');
2139
+ console.error('Good day');
2140
+ console.error('Bye');
2141
+
2142
+ assertConsoleErrorDev(['Hello', 'Good day', 'Bye'], {
2143
+ withoutStack: true,
2144
+ });
2145
+ });
2146
+
2147
+ // @gate __DEV__
2148
+ it('fails if withoutStack is invalid null value', () => {
2149
+ const message = expectToThrowFailure(() => {
2150
+ console.error('Hi');
2151
+ assertConsoleErrorDev(['Hi'], {withoutStack: null});
2152
+ });
2153
+ expect(message).toMatchInlineSnapshot(`
2154
+ "assertConsoleErrorDev(expected)
2155
+
2156
+ The second argument must be {withoutStack: true}.
2157
+
2158
+ Instead received {"withoutStack":null}."
2159
+ `);
2160
+ assertConsoleErrorDev(['Hi'], {withoutStack: true});
2161
+ });
2162
+
2163
+ // @gate __DEV__
2164
+ it('fails if withoutStack is invalid {} value', () => {
2165
+ const message = expectToThrowFailure(() => {
2166
+ console.error('Hi');
2167
+ assertConsoleErrorDev(['Hi'], {withoutStack: {}});
2168
+ });
2169
+ expect(message).toMatchInlineSnapshot(`
2170
+ "assertConsoleErrorDev(expected)
2171
+
2172
+ The second argument must be {withoutStack: true}.
2173
+
2174
+ Instead received {"withoutStack":{}}."
2175
+ `);
2176
+ assertConsoleErrorDev(['Hi'], {withoutStack: true});
2177
+ });
2178
+
2179
+ // @gate __DEV__
2180
+ it('fails if withoutStack is invalid string value', () => {
2181
+ const message = expectToThrowFailure(() => {
2182
+ console.error('Hi');
2183
+ assertConsoleErrorDev(['Hi'], {withoutStack: 'haha'});
2184
+ });
2185
+ expect(message).toMatchInlineSnapshot(`
2186
+ "assertConsoleErrorDev(expected)
2187
+
2188
+ The second argument must be {withoutStack: true}.
2189
+
2190
+ Instead received {"withoutStack":"haha"}."
2191
+ `);
2192
+ assertConsoleErrorDev(['Hi'], {withoutStack: true});
2193
+ });
2194
+
2195
+ // @gate __DEV__
2196
+ it('fails if only error is not expected to have a stack, but does', () => {
2197
+ const message = expectToThrowFailure(() => {
2198
+ console.error('Hello\n in div');
2199
+ assertConsoleErrorDev(['Hello'], {withoutStack: true});
2200
+ });
2201
+ expect(message).toMatchInlineSnapshot(`
2202
+ "assertConsoleErrorDev(expected)
2203
+
2204
+ Unexpected component stack for:
2205
+ "Hello <component stack>"
2206
+
2207
+ If this error should include a component stack, remove {withoutStack: true} from this error.
2208
+ If all errors should include the component stack, you may need to remove {withoutStack: true} from the assertConsoleErrorDev call."
2209
+ `);
2210
+ });
2211
+
2212
+ // @gate __DEV__
2213
+ it('fails if errors are not expected to have a stack, but some do', () => {
2214
+ const message = expectToThrowFailure(() => {
2215
+ console.error('Hello\n in div');
2216
+ console.error('Good day');
2217
+ console.error('Bye\n in div');
2218
+ assertConsoleErrorDev(['Hello', 'Good day', 'Bye'], {
2219
+ withoutStack: true,
2220
+ });
2221
+ });
2222
+ expect(message).toMatchInlineSnapshot(`
2223
+ "assertConsoleErrorDev(expected)
2224
+
2225
+ Unexpected component stack for:
2226
+ "Hello <component stack>"
2227
+
2228
+ Unexpected component stack for:
2229
+ "Bye <component stack>"
2230
+
2231
+ If this error should include a component stack, remove {withoutStack: true} from this error.
2232
+ If all errors should include the component stack, you may need to remove {withoutStack: true} from the assertConsoleErrorDev call."
2233
+ `);
2234
+ });
2235
+ });
2236
+ describe('local withoutStack', () => {
2237
+ // @gate __DEV__
2238
+ it('passes when expected withoutStack logs matches the actual logs', () => {
2239
+ console.error('Hello\n in div');
2240
+ console.error('Good day');
2241
+ console.error('Bye\n in div');
2242
+ assertConsoleErrorDev([
2243
+ 'Hello',
2244
+ ['Good day', {withoutStack: true}],
2245
+ 'Bye',
2246
+ ]);
2247
+ });
2248
+
2249
+ // @gate __DEV__
2250
+ it('fails if withoutStack is invalid null value', () => {
2251
+ const message = expectToThrowFailure(() => {
2252
+ console.error('Hi');
2253
+ assertConsoleErrorDev([['Hi', {withoutStack: null}]]);
2254
+ });
2255
+ expect(message).toMatchInlineSnapshot(`
2256
+ "assertConsoleErrorDev(expected)
2257
+
2258
+ Log entries that are arrays must be of the form [string, {withoutStack: true}]
2259
+
2260
+ Instead received [string, {"withoutStack":null}]."
2261
+ `);
2262
+ });
2263
+
2264
+ // @gate __DEV__
2265
+ it('fails if withoutStack is invalid {} value', () => {
2266
+ const message = expectToThrowFailure(() => {
2267
+ console.error('Hi');
2268
+ assertConsoleErrorDev([['Hi', {withoutStack: {}}]]);
2269
+ });
2270
+ expect(message).toMatchInlineSnapshot(`
2271
+ "assertConsoleErrorDev(expected)
2272
+
2273
+ Log entries that are arrays must be of the form [string, {withoutStack: true}]
2274
+
2275
+ Instead received [string, {"withoutStack":{}}]."
2276
+ `);
2277
+ });
2278
+
2279
+ // @gate __DEV__
2280
+ it('fails if withoutStack is invalid string value', () => {
2281
+ const message = expectToThrowFailure(() => {
2282
+ console.error('Hi');
2283
+ assertConsoleErrorDev([['Hi', {withoutStack: 'haha'}]]);
2284
+ });
2285
+ expect(message).toMatchInlineSnapshot(`
2286
+ "assertConsoleErrorDev(expected)
2287
+
2288
+ Log entries that are arrays must be of the form [string, {withoutStack: true}]
2289
+
2290
+ Instead received [string, {"withoutStack":"haha"}]."
2291
+ `);
2292
+ });
2293
+
2294
+ // @gate __DEV__
2295
+ it('fails if withoutStack is invalid number value', () => {
2296
+ const message = expectToThrowFailure(() => {
2297
+ console.error('Hi');
2298
+ assertConsoleErrorDev([['Hi', {withoutStack: 4}]]);
2299
+ });
2300
+ expect(message).toMatchInlineSnapshot(`
2301
+ "assertConsoleErrorDev(expected)
2302
+
2303
+ Log entries that are arrays must be of the form [string, {withoutStack: true}]
2304
+
2305
+ Instead received [string, {"withoutStack":4}]."
2306
+ `);
2307
+ });
2308
+
2309
+ // @gate __DEV__
2310
+ it('fails if you forget to wrap local withoutStack in array', () => {
2311
+ const message = expectToThrowFailure(() => {
2312
+ console.error('Hello\n in div');
2313
+ console.error('Bye\n in div');
2314
+ assertConsoleErrorDev(['Hello', {withoutStack: true}, 'Bye']);
2315
+ });
2316
+ expect(message).toMatchInlineSnapshot(`
2317
+ "assertConsoleErrorDev(expected)
2318
+
2319
+ Did you forget to wrap a log with withoutStack in an array?
2320
+
2321
+ The expected message for assertConsoleErrorDev() must be a string or an array of length 2.
2322
+
2323
+ Instead received {"withoutStack":true}."
2324
+ `);
2325
+ });
2326
+
2327
+ // @gate __DEV__
2328
+ it('fails if you wrap in an array unnecessarily', () => {
2329
+ const message = expectToThrowFailure(() => {
2330
+ console.error('Hello');
2331
+ assertConsoleErrorDev([['Hello']]);
2332
+ });
2333
+ expect(message).toMatchInlineSnapshot(`
2334
+ "assertConsoleErrorDev(expected)
2335
+
2336
+ Did you forget to remove the array around the log?
2337
+
2338
+ The expected message for assertConsoleErrorDev() must be a string or an array of length 2, but there's only one item in the array. If this is intentional, remove the extra array."
2339
+ `);
2340
+ });
2341
+
2342
+ // @gate __DEV__
2343
+ it('fails if only error is not expected to have a stack, but does', () => {
2344
+ const message = expectToThrowFailure(() => {
2345
+ console.error('Hello\n in div');
2346
+ assertConsoleErrorDev([['Hello', {withoutStack: true}]]);
2347
+ });
2348
+ expect(message).toMatchInlineSnapshot(`
2349
+ "assertConsoleErrorDev(expected)
2350
+
2351
+ Unexpected component stack for:
2352
+ "Hello <component stack>"
2353
+
2354
+ If this error should include a component stack, remove {withoutStack: true} from this error.
2355
+ If all errors should include the component stack, you may need to remove {withoutStack: true} from the assertConsoleErrorDev call."
2356
+ `);
2357
+ });
2358
+
2359
+ // @gate __DEV__
2360
+ it('fails if errors are not expected to have a stack, but some do', () => {
2361
+ const message = expectToThrowFailure(() => {
2362
+ console.error('Hello\n in div');
2363
+ console.error('Good day');
2364
+ console.error('Bye\n in div');
2365
+ assertConsoleErrorDev([
2366
+ [
2367
+ 'Hello',
2368
+ {
2369
+ withoutStack: true,
2370
+ },
2371
+ ],
2372
+ 'Good day',
2373
+ [
2374
+ 'Bye',
2375
+ {
2376
+ withoutStack: true,
2377
+ },
2378
+ ],
2379
+ ]);
2380
+ });
2381
+ expect(message).toMatchInlineSnapshot(`
2382
+ "assertConsoleErrorDev(expected)
2383
+
2384
+ Unexpected component stack for:
2385
+ "Hello <component stack>"
2386
+
2387
+ Unexpected component stack for:
2388
+ "Bye <component stack>"
2389
+
2390
+ If this error should include a component stack, remove {withoutStack: true} from this error.
2391
+ If all errors should include the component stack, you may need to remove {withoutStack: true} from the assertConsoleErrorDev call."
2392
+ `);
2393
+ });
2394
+ });
2395
+
2396
+ // @gate __DEV__
2397
+ it('fails if the args is greater than %s argument number', () => {
2398
+ const message = expectToThrowFailure(() => {
2399
+ console.error('Hi %s', 'Sara', 'extra');
2400
+ assertConsoleErrorDev(['Hi'], {withoutStack: true});
2401
+ });
2402
+ expect(message).toMatchInlineSnapshot(`
2403
+ "assertConsoleErrorDev(expected)
2404
+
2405
+ Received 2 arguments for a message with 1 placeholders:
2406
+ "Hi %s""
2407
+ `);
2408
+ });
2409
+
2410
+ // @gate __DEV__
2411
+ it('fails if the args is greater than %s argument number for multiple errors', () => {
2412
+ const message = expectToThrowFailure(() => {
2413
+ console.error('Hi %s', 'Sara', 'extra');
2414
+ console.error('Bye %s', 'Sara', 'extra');
2415
+ assertConsoleErrorDev(['Hi', 'Bye'], {withoutStack: true});
2416
+ });
2417
+ expect(message).toMatchInlineSnapshot(`
2418
+ "assertConsoleErrorDev(expected)
2419
+
2420
+ Received 2 arguments for a message with 1 placeholders:
2421
+ "Hi %s"
2422
+
2423
+ Received 2 arguments for a message with 1 placeholders:
2424
+ "Bye %s""
2425
+ `);
2426
+ });
2427
+
2428
+ // @gate __DEV__
2429
+ it('fails if the %s argument number is greater than args', () => {
2430
+ const message = expectToThrowFailure(() => {
2431
+ console.error('Hi %s');
2432
+ assertConsoleErrorDev(['Hi'], {withoutStack: true});
2433
+ });
2434
+ expect(message).toMatchInlineSnapshot(`
2435
+ "assertConsoleErrorDev(expected)
2436
+
2437
+ Received 0 arguments for a message with 1 placeholders:
2438
+ "Hi %s""
2439
+ `);
2440
+ });
2441
+
2442
+ // @gate __DEV__
2443
+ it('fails if the %s argument number is greater than args for multiple errors', () => {
2444
+ const message = expectToThrowFailure(() => {
2445
+ console.error('Hi %s');
2446
+ console.error('Bye %s');
2447
+ assertConsoleErrorDev(['Hi', 'Bye'], {withoutStack: true});
2448
+ });
2449
+ expect(message).toMatchInlineSnapshot(`
2450
+ "assertConsoleErrorDev(expected)
2451
+
2452
+ Received 0 arguments for a message with 1 placeholders:
2453
+ "Hi %s"
2454
+
2455
+ Received 0 arguments for a message with 1 placeholders:
2456
+ "Bye %s""
2457
+ `);
2458
+ });
2459
+
2460
+ // @gate __DEV__
2461
+ it('fails if component stack is passed twice', () => {
2462
+ const message = expectToThrowFailure(() => {
2463
+ console.error('Hi %s%s', '\n in div', '\n in div');
2464
+ assertConsoleErrorDev(['Hi']);
2465
+ });
2466
+ expect(message).toMatchInlineSnapshot(`
2467
+ "assertConsoleErrorDev(expected)
2468
+
2469
+ Received more than one component stack for a warning:
2470
+ "Hi %s%s""
2471
+ `);
2472
+ });
2473
+
2474
+ // @gate __DEV__
2475
+ it('fails if multiple logs pass component stack twice', () => {
2476
+ const message = expectToThrowFailure(() => {
2477
+ console.error('Hi %s%s', '\n in div', '\n in div');
2478
+ console.error('Bye %s%s', '\n in div', '\n in div');
2479
+ assertConsoleErrorDev(['Hi', 'Bye']);
2480
+ });
2481
+ expect(message).toMatchInlineSnapshot(`
2482
+ "assertConsoleErrorDev(expected)
2483
+
2484
+ Received more than one component stack for a warning:
2485
+ "Hi %s%s"
2486
+
2487
+ Received more than one component stack for a warning:
2488
+ "Bye %s%s""
2489
+ `);
2490
+ });
2491
+
2492
+ // @gate __DEV__
2493
+ it('fails if multiple strings are passed without an array wrapper for single log', () => {
2494
+ const message = expectToThrowFailure(() => {
2495
+ console.error('Hi \n in div');
2496
+ console.error('Bye \n in div');
2497
+ assertConsoleErrorDev('Hi', 'Bye');
2498
+ });
2499
+ expect(message).toMatchInlineSnapshot(`
2500
+ "assertConsoleErrorDev(expected)
2501
+
2502
+ Expected messages should be an array of strings but was given type "string"."
2503
+ `);
2504
+ assertConsoleErrorDev(['Hi', 'Bye']);
2505
+ });
2506
+
2507
+ // @gate __DEV__
2508
+ it('fails if multiple strings are passed without an array wrapper for multiple logs', () => {
2509
+ const message = expectToThrowFailure(() => {
2510
+ console.error('Hi \n in div');
2511
+ console.error('Bye \n in div');
2512
+ assertConsoleErrorDev('Hi', 'Bye');
2513
+ });
2514
+ expect(message).toMatchInlineSnapshot(`
2515
+ "assertConsoleErrorDev(expected)
2516
+
2517
+ Expected messages should be an array of strings but was given type "string"."
2518
+ `);
2519
+ assertConsoleErrorDev(['Hi', 'Bye']);
2520
+ });
2521
+
2522
+ // @gate __DEV__
2523
+ it('fails on more than two arguments', () => {
2524
+ const message = expectToThrowFailure(() => {
2525
+ console.error('Hi \n in div');
2526
+ console.error('Wow \n in div');
2527
+ console.error('Bye \n in div');
2528
+ assertConsoleErrorDev('Hi', undefined, 'Bye');
2529
+ });
2530
+ expect(message).toMatchInlineSnapshot(`
2531
+ "assertConsoleErrorDev(expected)
2532
+
2533
+ Expected messages should be an array of strings but was given type "string"."
2534
+ `);
2535
+ assertConsoleErrorDev(['Hi', 'Wow', 'Bye']);
2536
+ });
2537
+
2538
+ it('should fail if waitFor is called before asserting', async () => {
2539
+ const Yield = ({id}) => {
2540
+ Scheduler.log(id);
2541
+ return id;
2542
+ };
2543
+
2544
+ const root = ReactNoop.createRoot();
2545
+ startTransition(() => {
2546
+ root.render(
2547
+ <div>
2548
+ <Yield id="foo" />
2549
+ <Yield id="bar" />
2550
+ <Yield id="baz" />
2551
+ </div>
2552
+ );
2553
+ });
2554
+
2555
+ console.error('Not asserted');
2556
+
2557
+ const message = await awaitExpectToThrowFailure(async () => {
2558
+ await waitFor(['foo', 'bar']);
2559
+ });
2560
+ expect(message).toMatchInlineSnapshot(`
2561
+ "asserConsoleLogsCleared(expected)
2562
+
2563
+ console.error was called without assertConsoleErrorDev:
2564
+ + Not asserted
2565
+
2566
+ You must call one of the assertConsoleDev helpers between each act call."
2567
+ `);
2568
+
2569
+ await waitForAll(['foo', 'bar', 'baz']);
2570
+ });
2571
+
2572
+ test('should fail if waitForThrow is called before asserting', async () => {
2573
+ const Yield = ({id}) => {
2574
+ Scheduler.log(id);
2575
+ return id;
2576
+ };
2577
+
2578
+ function BadRender() {
2579
+ throw new Error('Oh no!');
2580
+ }
2581
+
2582
+ function App() {
2583
+ return (
2584
+ <div>
2585
+ <Yield id="A" />
2586
+ <Yield id="B" />
2587
+ <BadRender />
2588
+ <Yield id="C" />
2589
+ <Yield id="D" />
2590
+ </div>
2591
+ );
2592
+ }
2593
+
2594
+ const root = ReactNoop.createRoot();
2595
+ root.render(<App />);
2596
+
2597
+ console.error('Not asserted');
2598
+
2599
+ const message = await awaitExpectToThrowFailure(async () => {
2600
+ await waitForThrow('Oh no!');
2601
+ });
2602
+ expect(message).toMatchInlineSnapshot(`
2603
+ "asserConsoleLogsCleared(expected)
2604
+
2605
+ console.error was called without assertConsoleErrorDev:
2606
+ + Not asserted
2607
+
2608
+ You must call one of the assertConsoleDev helpers between each act call."
2609
+ `);
2610
+
2611
+ await waitForAll(['A', 'B', 'A', 'B']);
2612
+ });
2613
+
2614
+ test('should fail if waitForPaint is called before asserting', async () => {
2615
+ function App({prop}) {
2616
+ const deferred = useDeferredValue(prop);
2617
+ const text = `Urgent: ${prop}, Deferred: ${deferred}`;
2618
+ Scheduler.log(text);
2619
+ return text;
2620
+ }
2621
+
2622
+ const root = ReactNoop.createRoot();
2623
+ root.render(<App prop="A" />);
2624
+
2625
+ await waitForAll(['Urgent: A, Deferred: A']);
2626
+ expect(root).toMatchRenderedOutput('Urgent: A, Deferred: A');
2627
+
2628
+ // This update will result in two separate paints: an urgent one, and a
2629
+ // deferred one.
2630
+ root.render(<App prop="B" />);
2631
+
2632
+ console.error('Not asserted');
2633
+ const message = await awaitExpectToThrowFailure(async () => {
2634
+ await waitForPaint(['Urgent: B, Deferred: A']);
2635
+ });
2636
+
2637
+ expect(message).toMatchInlineSnapshot(`
2638
+ "asserConsoleLogsCleared(expected)
2639
+
2640
+ console.error was called without assertConsoleErrorDev:
2641
+ + Not asserted
2642
+
2643
+ You must call one of the assertConsoleDev helpers between each act call."
2644
+ `);
2645
+
2646
+ await waitForAll(['Urgent: B, Deferred: A', 'Urgent: B, Deferred: B']);
2647
+ });
2648
+
2649
+ it('should fail if waitForAll is called before asserting', async () => {
2650
+ const Yield = ({id}) => {
2651
+ Scheduler.log(id);
2652
+ return id;
2653
+ };
2654
+
2655
+ const root = ReactNoop.createRoot();
2656
+ startTransition(() => {
2657
+ root.render(
2658
+ <div>
2659
+ <Yield id="foo" />
2660
+ <Yield id="bar" />
2661
+ <Yield id="baz" />
2662
+ </div>
2663
+ );
2664
+ });
2665
+
2666
+ console.error('Not asserted');
2667
+
2668
+ const message = await awaitExpectToThrowFailure(async () => {
2669
+ await waitForAll(['foo', 'bar', 'baz']);
2670
+ });
2671
+ expect(message).toMatchInlineSnapshot(`
2672
+ "asserConsoleLogsCleared(expected)
2673
+
2674
+ console.error was called without assertConsoleErrorDev:
2675
+ + Not asserted
2676
+
2677
+ You must call one of the assertConsoleDev helpers between each act call."
2678
+ `);
2679
+
2680
+ await waitForAll(['foo', 'bar', 'baz']);
2681
+ });
2682
+ it('should fail if toMatchRenderedOutput is called before asserting', async () => {
2683
+ const Yield = ({id}) => {
2684
+ Scheduler.log(id);
2685
+ console.error('Not asserted');
2686
+ return id;
2687
+ };
2688
+
2689
+ const root = ReactNoop.createRoot();
2690
+ startTransition(() => {
2691
+ root.render(
2692
+ <div>
2693
+ <Yield id="foo" />
2694
+ <Yield id="bar" />
2695
+ <Yield id="baz" />
2696
+ </div>
2697
+ );
2698
+ });
2699
+
2700
+ assertLog([]);
2701
+
2702
+ await waitForAll(['foo', 'bar', 'baz']);
2703
+ const message = expectToThrowFailure(() => {
2704
+ expect(root).toMatchRenderedOutput(<div>foobarbaz</div>);
2705
+ });
2706
+ expect(message).toMatchInlineSnapshot(`
2707
+ "asserConsoleLogsCleared(expected)
2708
+
2709
+ console.error was called without assertConsoleErrorDev:
2710
+ + Not asserted
2711
+ + Not asserted
2712
+ + Not asserted
2713
+
2714
+ You must call one of the assertConsoleDev helpers between each act call."
2715
+ `);
2716
+
2717
+ expect(root).toMatchRenderedOutput(<div>foobarbaz</div>);
2718
+ });
2719
+ });
2720
+});