@samitouri / QOS-React-1 / commits / 407faf5a69

Remove ReactDOM.render tests in ReactDOMConsoleErrorReporting-test (#28053)

Each it block here was duplicated to cover ReactDOM.render and ReactDOMClient.createRoot. Here we delete the ReactDOM.render coverage. Co-authored-by: Jack Pope <jackpope@meta.com>

Jack Pope committed Jan 26, 2024 at 19:29 UTC 407faf5a69955f23f3549cff77b1a37199daa95f
2 files changed +589 -535
packages/react-dom/src/__tests__/ReactDOMConsoleErrorReporting-test.js
-535
@@ -10,7 +10,6 @@
10 describe('ReactDOMConsoleErrorReporting', () => {
11 let act;
12 let React;
13 - let ReactDOM;
13 let ReactDOMClient;
14
15 let ErrorBoundary;
@@ -23,7 +22,6 @@ describe('ReactDOMConsoleErrorReporting', () => {
22 jest.resetModules();
23 act = require('internal-test-utils').act;
24 React = require('react');
26 - ReactDOM = require('react-dom');
25 ReactDOMClient = require('react-dom/client');
26
27 const InternalTestUtils = require('internal-test-utils');
@@ -607,537 +605,4 @@ describe('ReactDOMConsoleErrorReporting', () => {
605 }
606 });
607 });
610 -
611 - describe('ReactDOM.render', () => {
612 - it('logs errors during event handlers', async () => {
613 - spyOnDevAndProd(console, 'error');
614 -
615 - function Foo() {
616 - return (
617 - <button
618 - onClick={() => {
619 - throw Error('Boom');
620 - }}>
621 - click me
622 - </button>
623 - );
624 - }
625 -
626 - await act(() => {
627 - ReactDOM.render(<Foo />, container);
628 - });
629 -
630 - await act(() => {
631 - container.firstChild.dispatchEvent(
632 - new MouseEvent('click', {
633 - bubbles: true,
634 - }),
635 - );
636 - });
637 -
638 - if (__DEV__) {
639 - expect(windowOnError.mock.calls).toEqual([
640 - [
641 - // Reported because we're in a browser click event:
642 - expect.objectContaining({
643 - message: 'Boom',
644 - }),
645 - ],
646 - [
647 - // This one is jsdom-only. Real browser deduplicates it.
648 - // (In DEV, we have a nested event due to guarded callback.)
649 - expect.objectContaining({
650 - message: 'Boom',
651 - }),
652 - ],
653 - ]);
654 - expect(console.error.mock.calls).toEqual([
655 - [expect.stringContaining('ReactDOM.render is no longer supported')],
656 - [
657 - // Reported because we're in a browser click event:
658 - expect.objectContaining({
659 - detail: expect.objectContaining({
660 - message: 'Boom',
661 - }),
662 - type: 'unhandled exception',
663 - }),
664 - ],
665 - [
666 - // This one is jsdom-only. Real browser deduplicates it.
667 - // (In DEV, we have a nested event due to guarded callback.)
668 - expect.objectContaining({
669 - detail: expect.objectContaining({
670 - message: 'Boom',
671 - }),
672 - type: 'unhandled exception',
673 - }),
674 - ],
675 - ]);
676 - } else {
677 - expect(windowOnError.mock.calls).toEqual([
678 - [
679 - // Reported because we're in a browser click event:
680 - expect.objectContaining({
681 - message: 'Boom',
682 - }),
683 - ],
684 - ]);
685 - expect(console.error.mock.calls).toEqual([
686 - [
687 - // Reported because we're in a browser click event:
688 - expect.objectContaining({
689 - detail: expect.objectContaining({
690 - message: 'Boom',
691 - }),
692 - type: 'unhandled exception',
693 - }),
694 - ],
695 - ]);
696 - }
697 -
698 - // Check next render doesn't throw.
699 - windowOnError.mockReset();
700 - console.error.mockReset();
701 - await act(() => {
702 - ReactDOM.render(<NoError />, container);
703 - });
704 - expect(container.textContent).toBe('OK');
705 - expect(windowOnError.mock.calls).toEqual([]);
706 - if (__DEV__) {
707 - expect(console.error.mock.calls).toEqual([
708 - [expect.stringContaining('ReactDOM.render is no longer supported')],
709 - ]);
710 - }
711 - });
712 -
713 - it('logs render errors without an error boundary', async () => {
714 - spyOnDevAndProd(console, 'error');
715 -
716 - function Foo() {
717 - throw Error('Boom');
718 - }
719 -
720 - expect(() => {
721 - ReactDOM.render(<Foo />, container);
722 - }).toThrow('Boom');
723 -
724 - if (__DEV__) {
725 - expect(windowOnError.mock.calls).toEqual([
726 - [
727 - // Reported due to guarded callback:
728 - expect.objectContaining({
729 - message: 'Boom',
730 - }),
731 - ],
732 - ]);
733 - expect(console.error.mock.calls).toEqual([
734 - [expect.stringContaining('ReactDOM.render is no longer supported')],
735 - [
736 - // Reported due to the guarded callback:
737 - expect.objectContaining({
738 - detail: expect.objectContaining({
739 - message: 'Boom',
740 - }),
741 - type: 'unhandled exception',
742 - }),
743 - ],
744 - [
745 - // Addendum by React:
746 - expect.stringContaining(
747 - 'The above error occurred in the <Foo> component',
748 - ),
749 - ],
750 - ]);
751 - } else {
752 - // The top-level error was caught with try/catch, and there's no guarded callback,
753 - // so in production we don't see an error event.
754 - expect(windowOnError.mock.calls).toEqual([]);
755 - expect(console.error.mock.calls).toEqual([
756 - [
757 - // Reported by React with no extra message:
758 - expect.objectContaining({
759 - message: 'Boom',
760 - }),
761 - ],
762 - ]);
763 - }
764 -
765 - // Check next render doesn't throw.
766 - windowOnError.mockReset();
767 - console.error.mockReset();
768 - await act(() => {
769 - ReactDOM.render(<NoError />, container);
770 - });
771 - expect(container.textContent).toBe('OK');
772 - expect(windowOnError.mock.calls).toEqual([]);
773 - if (__DEV__) {
774 - expect(console.error.mock.calls).toEqual([
775 - [expect.stringContaining('ReactDOM.render is no longer supported')],
776 - ]);
777 - }
778 - });
779 -
780 - it('logs render errors with an error boundary', async () => {
781 - spyOnDevAndProd(console, 'error');
782 -
783 - function Foo() {
784 - throw Error('Boom');
785 - }
786 -
787 - await act(() => {
788 - ReactDOM.render(
789 - <ErrorBoundary>
790 - <Foo />
791 - </ErrorBoundary>,
792 - container,
793 - );
794 - });
795 -
796 - if (__DEV__) {
797 - expect(windowOnError.mock.calls).toEqual([
798 - [
799 - // Reported due to guarded callback:
800 - expect.objectContaining({
801 - message: 'Boom',
802 - }),
803 - ],
804 - ]);
805 - expect(console.error.mock.calls).toEqual([
806 - [expect.stringContaining('ReactDOM.render is no longer supported')],
807 - [
808 - // Reported by jsdom due to the guarded callback:
809 - expect.objectContaining({
810 - detail: expect.objectContaining({
811 - message: 'Boom',
812 - }),
813 - type: 'unhandled exception',
814 - }),
815 - ],
816 - [
817 - // Addendum by React:
818 - expect.stringContaining(
819 - 'The above error occurred in the <Foo> component',
820 - ),
821 - ],
822 - ]);
823 - } else {
824 - // The top-level error was caught with try/catch, and there's no guarded callback,
825 - // so in production we don't see an error event.
826 - expect(windowOnError.mock.calls).toEqual([]);
827 - expect(console.error.mock.calls).toEqual([
828 - [
829 - // Reported by React with no extra message:
830 - expect.objectContaining({
831 - message: 'Boom',
832 - }),
833 - ],
834 - ]);
835 - }
836 -
837 - // Check next render doesn't throw.
838 - windowOnError.mockReset();
839 - console.error.mockReset();
840 - await act(() => {
841 - ReactDOM.render(<NoError />, container);
842 - });
843 - expect(container.textContent).toBe('OK');
844 - expect(windowOnError.mock.calls).toEqual([]);
845 - if (__DEV__) {
846 - expect(console.error.mock.calls).toEqual([
847 - [expect.stringContaining('ReactDOM.render is no longer supported')],
848 - ]);
849 - }
850 - });
851 -
852 - it('logs layout effect errors without an error boundary', async () => {
853 - spyOnDevAndProd(console, 'error');
854 -
855 - function Foo() {
856 - React.useLayoutEffect(() => {
857 - throw Error('Boom');
858 - }, []);
859 - return null;
860 - }
861 -
862 - expect(() => {
863 - ReactDOM.render(<Foo />, container);
864 - }).toThrow('Boom');
865 -
866 - if (__DEV__) {
867 - expect(windowOnError.mock.calls).toEqual([
868 - [
869 - // Reported due to guarded callback:
870 - expect.objectContaining({
871 - message: 'Boom',
872 - }),
873 - ],
874 - ]);
875 - expect(console.error.mock.calls).toEqual([
876 - [expect.stringContaining('ReactDOM.render is no longer supported')],
877 - [
878 - // Reported due to the guarded callback:
879 - expect.objectContaining({
880 - detail: expect.objectContaining({
881 - message: 'Boom',
882 - }),
883 - type: 'unhandled exception',
884 - }),
885 - ],
886 - [
887 - // Addendum by React:
888 - expect.stringContaining(
889 - 'The above error occurred in the <Foo> component',
890 - ),
891 - ],
892 - ]);
893 - } else {
894 - // The top-level error was caught with try/catch, and there's no guarded callback,
895 - // so in production we don't see an error event.
896 - expect(windowOnError.mock.calls).toEqual([]);
897 - expect(console.error.mock.calls).toEqual([
898 - [
899 - // Reported by React with no extra message:
900 - expect.objectContaining({
901 - message: 'Boom',
902 - }),
903 - ],
904 - ]);
905 - }
906 -
907 - // Check next render doesn't throw.
908 - windowOnError.mockReset();
909 - console.error.mockReset();
910 - await act(() => {
911 - ReactDOM.render(<NoError />, container);
912 - });
913 - expect(container.textContent).toBe('OK');
914 - expect(windowOnError.mock.calls).toEqual([]);
915 - if (__DEV__) {
916 - expect(console.error.mock.calls).toEqual([
917 - [expect.stringContaining('ReactDOM.render is no longer supported')],
918 - ]);
919 - }
920 - });
921 -
922 - it('logs layout effect errors with an error boundary', async () => {
923 - spyOnDevAndProd(console, 'error');
924 -
925 - function Foo() {
926 - React.useLayoutEffect(() => {
927 - throw Error('Boom');
928 - }, []);
929 - return null;
930 - }
931 -
932 - await act(() => {
933 - ReactDOM.render(
934 - <ErrorBoundary>
935 - <Foo />
936 - </ErrorBoundary>,
937 - container,
938 - );
939 - });
940 -
941 - if (__DEV__) {
942 - expect(windowOnError.mock.calls).toEqual([
943 - [
944 - // Reported due to guarded callback:
945 - expect.objectContaining({
946 - message: 'Boom',
947 - }),
948 - ],
949 - ]);
950 - expect(console.error.mock.calls).toEqual([
951 - [expect.stringContaining('ReactDOM.render is no longer supported')],
952 - [
953 - // Reported by jsdom due to the guarded callback:
954 - expect.objectContaining({
955 - detail: expect.objectContaining({
956 - message: 'Boom',
957 - }),
958 - type: 'unhandled exception',
959 - }),
960 - ],
961 - [
962 - // Addendum by React:
963 - expect.stringContaining(
964 - 'The above error occurred in the <Foo> component',
965 - ),
966 - ],
967 - ]);
968 - } else {
969 - // The top-level error was caught with try/catch, and there's no guarded callback,
970 - // so in production we don't see an error event.
971 - expect(windowOnError.mock.calls).toEqual([]);
972 - expect(console.error.mock.calls).toEqual([
973 - [
974 - // Reported by React with no extra message:
975 - expect.objectContaining({
976 - message: 'Boom',
977 - }),
978 - ],
979 - ]);
980 - }
981 -
982 - // Check next render doesn't throw.
983 - windowOnError.mockReset();
984 - console.error.mockReset();
985 - await act(() => {
986 - ReactDOM.render(<NoError />, container);
987 - });
988 - expect(container.textContent).toBe('OK');
989 - expect(windowOnError.mock.calls).toEqual([]);
990 - if (__DEV__) {
991 - expect(console.error.mock.calls).toEqual([
992 - [expect.stringContaining('ReactDOM.render is no longer supported')],
993 - ]);
994 - }
995 - });
996 -
997 - it('logs passive effect errors without an error boundary', async () => {
998 - spyOnDevAndProd(console, 'error');
999 -
1000 - function Foo() {
1001 - React.useEffect(() => {
1002 - throw Error('Boom');
1003 - }, []);
1004 - return null;
1005 - }
1006 -
1007 - await act(async () => {
1008 - ReactDOM.render(<Foo />, container);
1009 - await waitForThrow('Boom');
1010 - });
1011 -
1012 - if (__DEV__) {
1013 - expect(windowOnError.mock.calls).toEqual([
1014 - [
1015 - // Reported due to guarded callback:
1016 - expect.objectContaining({
1017 - message: 'Boom',
1018 - }),
1019 - ],
1020 - ]);
1021 - expect(console.error.mock.calls).toEqual([
1022 - [expect.stringContaining('ReactDOM.render is no longer supported')],
1023 - [
1024 - // Reported due to the guarded callback:
1025 - expect.objectContaining({
1026 - detail: expect.objectContaining({
1027 - message: 'Boom',
1028 - }),
1029 - type: 'unhandled exception',
1030 - }),
1031 - ],
1032 - [
1033 - // Addendum by React:
1034 - expect.stringContaining(
1035 - 'The above error occurred in the <Foo> component',
1036 - ),
1037 - ],
1038 - ]);
1039 - } else {
1040 - // The top-level error was caught with try/catch, and there's no guarded callback,
1041 - // so in production we don't see an error event.
1042 - expect(windowOnError.mock.calls).toEqual([]);
1043 - expect(console.error.mock.calls).toEqual([
1044 - [
1045 - // Reported by React with no extra message:
1046 - expect.objectContaining({
1047 - message: 'Boom',
1048 - }),
1049 - ],
1050 - ]);
1051 - }
1052 -
1053 - // Check next render doesn't throw.
1054 - windowOnError.mockReset();
1055 - console.error.mockReset();
1056 - await act(() => {
1057 - ReactDOM.render(<NoError />, container);
1058 - });
1059 - expect(container.textContent).toBe('OK');
1060 - expect(windowOnError.mock.calls).toEqual([]);
1061 - if (__DEV__) {
1062 - expect(console.error.mock.calls).toEqual([
1063 - [expect.stringContaining('ReactDOM.render is no longer supported')],
1064 - ]);
1065 - }
1066 - });
1067 -
1068 - it('logs passive effect errors with an error boundary', async () => {
1069 - spyOnDevAndProd(console, 'error');
1070 -
1071 - function Foo() {
1072 - React.useEffect(() => {
1073 - throw Error('Boom');
1074 - }, []);
1075 - return null;
1076 - }
1077 -
1078 - await act(() => {
1079 - ReactDOM.render(
1080 - <ErrorBoundary>
1081 - <Foo />
1082 - </ErrorBoundary>,
1083 - container,
1084 - );
1085 - });
1086 -
1087 - if (__DEV__) {
1088 - // Reported due to guarded callback:
1089 - expect(windowOnError.mock.calls).toEqual([
1090 - [
1091 - expect.objectContaining({
1092 - message: 'Boom',
1093 - }),
1094 - ],
1095 - ]);
1096 - expect(console.error.mock.calls).toEqual([
1097 - [expect.stringContaining('ReactDOM.render is no longer supported')],
1098 - [
1099 - // Reported by jsdom due to the guarded callback:
1100 - expect.objectContaining({
1101 - detail: expect.objectContaining({
1102 - message: 'Boom',
1103 - }),
1104 - type: 'unhandled exception',
1105 - }),
1106 - ],
1107 - [
1108 - // Addendum by React:
1109 - expect.stringContaining(
1110 - 'The above error occurred in the <Foo> component',
1111 - ),
1112 - ],
1113 - ]);
1114 - } else {
1115 - // The top-level error was caught with try/catch, and there's no guarded callback,
1116 - // so in production we don't see an error event.
1117 - expect(windowOnError.mock.calls).toEqual([]);
1118 - expect(console.error.mock.calls).toEqual([
1119 - [
1120 - // Reported by React with no extra message:
1121 - expect.objectContaining({
1122 - message: 'Boom',
1123 - }),
1124 - ],
1125 - ]);
1126 - }
1127 -
1128 - // Check next render doesn't throw.
1129 - windowOnError.mockReset();
1130 - console.error.mockReset();
1131 - await act(() => {
1132 - ReactDOM.render(<NoError />, container);
1133 - });
1134 - expect(container.textContent).toBe('OK');
1135 - expect(windowOnError.mock.calls).toEqual([]);
1136 - if (__DEV__) {
1137 - expect(console.error.mock.calls).toEqual([
1138 - [expect.stringContaining('ReactDOM.render is no longer supported')],
1139 - ]);
1140 - }
1141 - });
1142 - });
608 });
packages/react-dom/src/__tests__/ReactDOMConsoleErrorReportingLegacy-test.js new
+589
@@ -0,0 +1,589 @@
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 +
8 +'use strict';
9 +
10 +describe('ReactDOMConsoleErrorReporting', () => {
11 + let act;
12 + let React;
13 + let ReactDOM;
14 +
15 + let ErrorBoundary;
16 + let NoError;
17 + let container;
18 + let windowOnError;
19 + let waitForThrow;
20 +
21 + beforeEach(() => {
22 + jest.resetModules();
23 + act = require('internal-test-utils').act;
24 + React = require('react');
25 + ReactDOM = require('react-dom');
26 +
27 + const InternalTestUtils = require('internal-test-utils');
28 + waitForThrow = InternalTestUtils.waitForThrow;
29 +
30 + ErrorBoundary = class extends React.Component {
31 + state = {error: null};
32 + static getDerivedStateFromError(error) {
33 + return {error};
34 + }
35 + render() {
36 + if (this.state.error) {
37 + return <h1>Caught: {this.state.error.message}</h1>;
38 + }
39 + return this.props.children;
40 + }
41 + };
42 + NoError = function () {
43 + return <h1>OK</h1>;
44 + };
45 + container = document.createElement('div');
46 + document.body.appendChild(container);
47 + windowOnError = jest.fn();
48 + window.addEventListener('error', windowOnError);
49 + });
50 +
51 + afterEach(() => {
52 + document.body.removeChild(container);
53 + window.removeEventListener('error', windowOnError);
54 + jest.restoreAllMocks();
55 + });
56 +
57 + describe('ReactDOM.render', () => {
58 + it('logs errors during event handlers', async () => {
59 + spyOnDevAndProd(console, 'error');
60 +
61 + function Foo() {
62 + return (
63 + <button
64 + onClick={() => {
65 + throw Error('Boom');
66 + }}>
67 + click me
68 + </button>
69 + );
70 + }
71 +
72 + await act(() => {
73 + ReactDOM.render(<Foo />, container);
74 + });
75 +
76 + await act(() => {
77 + container.firstChild.dispatchEvent(
78 + new MouseEvent('click', {
79 + bubbles: true,
80 + }),
81 + );
82 + });
83 +
84 + if (__DEV__) {
85 + expect(windowOnError.mock.calls).toEqual([
86 + [
87 + // Reported because we're in a browser click event:
88 + expect.objectContaining({
89 + message: 'Boom',
90 + }),
91 + ],
92 + [
93 + // This one is jsdom-only. Real browser deduplicates it.
94 + // (In DEV, we have a nested event due to guarded callback.)
95 + expect.objectContaining({
96 + message: 'Boom',
97 + }),
98 + ],
99 + ]);
100 + expect(console.error.mock.calls).toEqual([
101 + [expect.stringContaining('ReactDOM.render is no longer supported')],
102 + [
103 + // Reported because we're in a browser click event:
104 + expect.objectContaining({
105 + detail: expect.objectContaining({
106 + message: 'Boom',
107 + }),
108 + type: 'unhandled exception',
109 + }),
110 + ],
111 + [
112 + // This one is jsdom-only. Real browser deduplicates it.
113 + // (In DEV, we have a nested event due to guarded callback.)
114 + expect.objectContaining({
115 + detail: expect.objectContaining({
116 + message: 'Boom',
117 + }),
118 + type: 'unhandled exception',
119 + }),
120 + ],
121 + ]);
122 + } else {
123 + expect(windowOnError.mock.calls).toEqual([
124 + [
125 + // Reported because we're in a browser click event:
126 + expect.objectContaining({
127 + message: 'Boom',
128 + }),
129 + ],
130 + ]);
131 + expect(console.error.mock.calls).toEqual([
132 + [
133 + // Reported because we're in a browser click event:
134 + expect.objectContaining({
135 + detail: expect.objectContaining({
136 + message: 'Boom',
137 + }),
138 + type: 'unhandled exception',
139 + }),
140 + ],
141 + ]);
142 + }
143 +
144 + // Check next render doesn't throw.
145 + windowOnError.mockReset();
146 + console.error.mockReset();
147 + await act(() => {
148 + ReactDOM.render(<NoError />, container);
149 + });
150 + expect(container.textContent).toBe('OK');
151 + expect(windowOnError.mock.calls).toEqual([]);
152 + if (__DEV__) {
153 + expect(console.error.mock.calls).toEqual([
154 + [expect.stringContaining('ReactDOM.render is no longer supported')],
155 + ]);
156 + }
157 + });
158 +
159 + it('logs render errors without an error boundary', async () => {
160 + spyOnDevAndProd(console, 'error');
161 +
162 + function Foo() {
163 + throw Error('Boom');
164 + }
165 +
166 + expect(() => {
167 + ReactDOM.render(<Foo />, container);
168 + }).toThrow('Boom');
169 +
170 + if (__DEV__) {
171 + expect(windowOnError.mock.calls).toEqual([
172 + [
173 + // Reported due to guarded callback:
174 + expect.objectContaining({
175 + message: 'Boom',
176 + }),
177 + ],
178 + ]);
179 + expect(console.error.mock.calls).toEqual([
180 + [expect.stringContaining('ReactDOM.render is no longer supported')],
181 + [
182 + // Reported due to the guarded callback:
183 + expect.objectContaining({
184 + detail: expect.objectContaining({
185 + message: 'Boom',
186 + }),
187 + type: 'unhandled exception',
188 + }),
189 + ],
190 + [
191 + // Addendum by React:
192 + expect.stringContaining(
193 + 'The above error occurred in the <Foo> component',
194 + ),
195 + ],
196 + ]);
197 + } else {
198 + // The top-level error was caught with try/catch, and there's no guarded callback,
199 + // so in production we don't see an error event.
200 + expect(windowOnError.mock.calls).toEqual([]);
201 + expect(console.error.mock.calls).toEqual([
202 + [
203 + // Reported by React with no extra message:
204 + expect.objectContaining({
205 + message: 'Boom',
206 + }),
207 + ],
208 + ]);
209 + }
210 +
211 + // Check next render doesn't throw.
212 + windowOnError.mockReset();
213 + console.error.mockReset();
214 + await act(() => {
215 + ReactDOM.render(<NoError />, container);
216 + });
217 + expect(container.textContent).toBe('OK');
218 + expect(windowOnError.mock.calls).toEqual([]);
219 + if (__DEV__) {
220 + expect(console.error.mock.calls).toEqual([
221 + [expect.stringContaining('ReactDOM.render is no longer supported')],
222 + ]);
223 + }
224 + });
225 +
226 + it('logs render errors with an error boundary', async () => {
227 + spyOnDevAndProd(console, 'error');
228 +
229 + function Foo() {
230 + throw Error('Boom');
231 + }
232 +
233 + await act(() => {
234 + ReactDOM.render(
235 + <ErrorBoundary>
236 + <Foo />
237 + </ErrorBoundary>,
238 + container,
239 + );
240 + });
241 +
242 + if (__DEV__) {
243 + expect(windowOnError.mock.calls).toEqual([
244 + [
245 + // Reported due to guarded callback:
246 + expect.objectContaining({
247 + message: 'Boom',
248 + }),
249 + ],
250 + ]);
251 + expect(console.error.mock.calls).toEqual([
252 + [expect.stringContaining('ReactDOM.render is no longer supported')],
253 + [
254 + // Reported by jsdom due to the guarded callback:
255 + expect.objectContaining({
256 + detail: expect.objectContaining({
257 + message: 'Boom',
258 + }),
259 + type: 'unhandled exception',
260 + }),
261 + ],
262 + [
263 + // Addendum by React:
264 + expect.stringContaining(
265 + 'The above error occurred in the <Foo> component',
266 + ),
267 + ],
268 + ]);
269 + } else {
270 + // The top-level error was caught with try/catch, and there's no guarded callback,
271 + // so in production we don't see an error event.
272 + expect(windowOnError.mock.calls).toEqual([]);
273 + expect(console.error.mock.calls).toEqual([
274 + [
275 + // Reported by React with no extra message:
276 + expect.objectContaining({
277 + message: 'Boom',
278 + }),
279 + ],
280 + ]);
281 + }
282 +
283 + // Check next render doesn't throw.
284 + windowOnError.mockReset();
285 + console.error.mockReset();
286 + await act(() => {
287 + ReactDOM.render(<NoError />, container);
288 + });
289 + expect(container.textContent).toBe('OK');
290 + expect(windowOnError.mock.calls).toEqual([]);
291 + if (__DEV__) {
292 + expect(console.error.mock.calls).toEqual([
293 + [expect.stringContaining('ReactDOM.render is no longer supported')],
294 + ]);
295 + }
296 + });
297 +
298 + it('logs layout effect errors without an error boundary', async () => {
299 + spyOnDevAndProd(console, 'error');
300 +
301 + function Foo() {
302 + React.useLayoutEffect(() => {
303 + throw Error('Boom');
304 + }, []);
305 + return null;
306 + }
307 +
308 + expect(() => {
309 + ReactDOM.render(<Foo />, container);
310 + }).toThrow('Boom');
311 +
312 + if (__DEV__) {
313 + expect(windowOnError.mock.calls).toEqual([
314 + [
315 + // Reported due to guarded callback:
316 + expect.objectContaining({
317 + message: 'Boom',
318 + }),
319 + ],
320 + ]);
321 + expect(console.error.mock.calls).toEqual([
322 + [expect.stringContaining('ReactDOM.render is no longer supported')],
323 + [
324 + // Reported due to the guarded callback:
325 + expect.objectContaining({
326 + detail: expect.objectContaining({
327 + message: 'Boom',
328 + }),
329 + type: 'unhandled exception',
330 + }),
331 + ],
332 + [
333 + // Addendum by React:
334 + expect.stringContaining(
335 + 'The above error occurred in the <Foo> component',
336 + ),
337 + ],
338 + ]);
339 + } else {
340 + // The top-level error was caught with try/catch, and there's no guarded callback,
341 + // so in production we don't see an error event.
342 + expect(windowOnError.mock.calls).toEqual([]);
343 + expect(console.error.mock.calls).toEqual([
344 + [
345 + // Reported by React with no extra message:
346 + expect.objectContaining({
347 + message: 'Boom',
348 + }),
349 + ],
350 + ]);
351 + }
352 +
353 + // Check next render doesn't throw.
354 + windowOnError.mockReset();
355 + console.error.mockReset();
356 + await act(() => {
357 + ReactDOM.render(<NoError />, container);
358 + });
359 + expect(container.textContent).toBe('OK');
360 + expect(windowOnError.mock.calls).toEqual([]);
361 + if (__DEV__) {
362 + expect(console.error.mock.calls).toEqual([
363 + [expect.stringContaining('ReactDOM.render is no longer supported')],
364 + ]);
365 + }
366 + });
367 +
368 + it('logs layout effect errors with an error boundary', async () => {
369 + spyOnDevAndProd(console, 'error');
370 +
371 + function Foo() {
372 + React.useLayoutEffect(() => {
373 + throw Error('Boom');
374 + }, []);
375 + return null;
376 + }
377 +
378 + await act(() => {
379 + ReactDOM.render(
380 + <ErrorBoundary>
381 + <Foo />
382 + </ErrorBoundary>,
383 + container,
384 + );
385 + });
386 +
387 + if (__DEV__) {
388 + expect(windowOnError.mock.calls).toEqual([
389 + [
390 + // Reported due to guarded callback:
391 + expect.objectContaining({
392 + message: 'Boom',
393 + }),
394 + ],
395 + ]);
396 + expect(console.error.mock.calls).toEqual([
397 + [expect.stringContaining('ReactDOM.render is no longer supported')],
398 + [
399 + // Reported by jsdom due to the guarded callback:
400 + expect.objectContaining({
401 + detail: expect.objectContaining({
402 + message: 'Boom',
403 + }),
404 + type: 'unhandled exception',
405 + }),
406 + ],
407 + [
408 + // Addendum by React:
409 + expect.stringContaining(
410 + 'The above error occurred in the <Foo> component',
411 + ),
412 + ],
413 + ]);
414 + } else {
415 + // The top-level error was caught with try/catch, and there's no guarded callback,
416 + // so in production we don't see an error event.
417 + expect(windowOnError.mock.calls).toEqual([]);
418 + expect(console.error.mock.calls).toEqual([
419 + [
420 + // Reported by React with no extra message:
421 + expect.objectContaining({
422 + message: 'Boom',
423 + }),
424 + ],
425 + ]);
426 + }
427 +
428 + // Check next render doesn't throw.
429 + windowOnError.mockReset();
430 + console.error.mockReset();
431 + await act(() => {
432 + ReactDOM.render(<NoError />, container);
433 + });
434 + expect(container.textContent).toBe('OK');
435 + expect(windowOnError.mock.calls).toEqual([]);
436 + if (__DEV__) {
437 + expect(console.error.mock.calls).toEqual([
438 + [expect.stringContaining('ReactDOM.render is no longer supported')],
439 + ]);
440 + }
441 + });
442 +
443 + it('logs passive effect errors without an error boundary', async () => {
444 + spyOnDevAndProd(console, 'error');
445 +
446 + function Foo() {
447 + React.useEffect(() => {
448 + throw Error('Boom');
449 + }, []);
450 + return null;
451 + }
452 +
453 + await act(async () => {
454 + ReactDOM.render(<Foo />, container);
455 + await waitForThrow('Boom');
456 + });
457 +
458 + if (__DEV__) {
459 + expect(windowOnError.mock.calls).toEqual([
460 + [
461 + // Reported due to guarded callback:
462 + expect.objectContaining({
463 + message: 'Boom',
464 + }),
465 + ],
466 + ]);
467 + expect(console.error.mock.calls).toEqual([
468 + [expect.stringContaining('ReactDOM.render is no longer supported')],
469 + [
470 + // Reported due to the guarded callback:
471 + expect.objectContaining({
472 + detail: expect.objectContaining({
473 + message: 'Boom',
474 + }),
475 + type: 'unhandled exception',
476 + }),
477 + ],
478 + [
479 + // Addendum by React:
480 + expect.stringContaining(
481 + 'The above error occurred in the <Foo> component',
482 + ),
483 + ],
484 + ]);
485 + } else {
486 + // The top-level error was caught with try/catch, and there's no guarded callback,
487 + // so in production we don't see an error event.
488 + expect(windowOnError.mock.calls).toEqual([]);
489 + expect(console.error.mock.calls).toEqual([
490 + [
491 + // Reported by React with no extra message:
492 + expect.objectContaining({
493 + message: 'Boom',
494 + }),
495 + ],
496 + ]);
497 + }
498 +
499 + // Check next render doesn't throw.
500 + windowOnError.mockReset();
501 + console.error.mockReset();
502 + await act(() => {
503 + ReactDOM.render(<NoError />, container);
504 + });
505 + expect(container.textContent).toBe('OK');
506 + expect(windowOnError.mock.calls).toEqual([]);
507 + if (__DEV__) {
508 + expect(console.error.mock.calls).toEqual([
509 + [expect.stringContaining('ReactDOM.render is no longer supported')],
510 + ]);
511 + }
512 + });
513 +
514 + it('logs passive effect errors with an error boundary', async () => {
515 + spyOnDevAndProd(console, 'error');
516 +
517 + function Foo() {
518 + React.useEffect(() => {
519 + throw Error('Boom');
520 + }, []);
521 + return null;
522 + }
523 +
524 + await act(() => {
525 + ReactDOM.render(
526 + <ErrorBoundary>
527 + <Foo />
528 + </ErrorBoundary>,
529 + container,
530 + );
531 + });
532 +
533 + if (__DEV__) {
534 + // Reported due to guarded callback:
535 + expect(windowOnError.mock.calls).toEqual([
536 + [
537 + expect.objectContaining({
538 + message: 'Boom',
539 + }),
540 + ],
541 + ]);
542 + expect(console.error.mock.calls).toEqual([
543 + [expect.stringContaining('ReactDOM.render is no longer supported')],
544 + [
545 + // Reported by jsdom due to the guarded callback:
546 + expect.objectContaining({
547 + detail: expect.objectContaining({
548 + message: 'Boom',
549 + }),
550 + type: 'unhandled exception',
551 + }),
552 + ],
553 + [
554 + // Addendum by React:
555 + expect.stringContaining(
556 + 'The above error occurred in the <Foo> component',
557 + ),
558 + ],
559 + ]);
560 + } else {
561 + // The top-level error was caught with try/catch, and there's no guarded callback,
562 + // so in production we don't see an error event.
563 + expect(windowOnError.mock.calls).toEqual([]);
564 + expect(console.error.mock.calls).toEqual([
565 + [
566 + // Reported by React with no extra message:
567 + expect.objectContaining({
568 + message: 'Boom',
569 + }),
570 + ],
571 + ]);
572 + }
573 +
574 + // Check next render doesn't throw.
575 + windowOnError.mockReset();
576 + console.error.mockReset();
577 + await act(() => {
578 + ReactDOM.render(<NoError />, container);
579 + });
580 + expect(container.textContent).toBe('OK');
581 + expect(windowOnError.mock.calls).toEqual([]);
582 + if (__DEV__) {
583 + expect(console.error.mock.calls).toEqual([
584 + [expect.stringContaining('ReactDOM.render is no longer supported')],
585 + ]);
586 + }
587 + });
588 + });
589 +});