@samitouri / QOS-React / commits / 346b628b43

Add failing tests for Suspense with same type

Dan Abramov committed Apr 18, 2019 at 19:58 UTC 346b628b43f24e08ee7c0f09492a292436f13324
2 files changed +690
src/__tests__/__snapshots__/storeStressSync-test.js.snap
+254
@@ -115,6 +115,260 @@ exports[`StoreStress (Sync Mode) should handle a stress test for Suspense (Sync
115 <Y>
116 `;
117
118 +exports[`StoreStress (Sync Mode) should handle a stress test for Suspense without type change (Sync Mode) 1`] = `
119 +[root]
120 + ▾ <Root>
121 + <X>
122 + ▾ <Suspense>
123 + ▾ <MaybeSuspend>
124 + <A key="a">
125 + <Z>
126 + <Y>
127 +`;
128 +
129 +exports[`StoreStress (Sync Mode) should handle a stress test for Suspense without type change (Sync Mode) 2`] = `
130 +[root]
131 + ▾ <Root>
132 + <X>
133 + ▾ <Suspense>
134 + ▾ <MaybeSuspend>
135 + <A key="a">
136 + <Z>
137 + <Y>
138 +`;
139 +
140 +exports[`StoreStress (Sync Mode) should handle a stress test for Suspense without type change (Sync Mode) 3`] = `
141 +[root]
142 + ▾ <Root>
143 + <X>
144 + ▾ <Suspense>
145 + ▾ <MaybeSuspend>
146 + <A key="a">
147 + <B key="b">
148 + <C key="c">
149 + <Z>
150 + <Y>
151 +`;
152 +
153 +exports[`StoreStress (Sync Mode) should handle a stress test for Suspense without type change (Sync Mode) 4`] = `
154 +[root]
155 + ▾ <Root>
156 + <X>
157 + ▾ <Suspense>
158 + ▾ <MaybeSuspend>
159 + <C key="c">
160 + <B key="b">
161 + <A key="a">
162 + <Z>
163 + <Y>
164 +`;
165 +
166 +exports[`StoreStress (Sync Mode) should handle a stress test for Suspense without type change (Sync Mode) 5`] = `
167 +[root]
168 + ▾ <Root>
169 + <X>
170 + ▾ <Suspense>
171 + ▾ <MaybeSuspend>
172 + <C key="c">
173 + <A key="a">
174 + <Z>
175 + <Y>
176 +`;
177 +
178 +exports[`StoreStress (Sync Mode) should handle a stress test for Suspense without type change (Sync Mode) 6`] = `
179 +[root]
180 + ▾ <Root>
181 + <X>
182 + ▾ <Suspense>
183 + ▾ <MaybeSuspend>
184 + <C key="c">
185 + <A key="a">
186 + <Z>
187 + <Y>
188 +`;
189 +
190 +exports[`StoreStress (Sync Mode) should handle a stress test for Suspense without type change (Sync Mode) 7`] = `
191 +[root]
192 + ▾ <Root>
193 + <X>
194 + ▾ <Suspense>
195 + ▾ <MaybeSuspend>
196 + <C key="c">
197 + <A key="a">
198 + <Z>
199 + <Y>
200 +`;
201 +
202 +exports[`StoreStress (Sync Mode) should handle a stress test for Suspense without type change (Sync Mode) 8`] = `
203 +[root]
204 + ▾ <Root>
205 + <X>
206 + ▾ <Suspense>
207 + ▾ <MaybeSuspend>
208 + <A key="a">
209 + <B key="b">
210 + <Z>
211 + <Y>
212 +`;
213 +
214 +exports[`StoreStress (Sync Mode) should handle a stress test for Suspense without type change (Sync Mode) 9`] = `
215 +[root]
216 + ▾ <Root>
217 + <X>
218 + ▾ <Suspense>
219 + ▾ <MaybeSuspend>
220 + <A key="a">
221 + <Z>
222 + <Y>
223 +`;
224 +
225 +exports[`StoreStress (Sync Mode) should handle a stress test for Suspense without type change (Sync Mode) 10`] = `
226 +[root]
227 + ▾ <Root>
228 + <X>
229 + ▾ <Suspense>
230 + ▾ <MaybeSuspend>
231 + <Z>
232 + <Y>
233 +`;
234 +
235 +exports[`StoreStress (Sync Mode) should handle a stress test for Suspense without type change (Sync Mode) 11`] = `
236 +[root]
237 + ▾ <Root>
238 + <X>
239 + ▾ <Suspense>
240 + ▾ <MaybeSuspend>
241 + <B key="b">
242 + <Z>
243 + <Y>
244 +`;
245 +
246 +exports[`StoreStress (Sync Mode) should handle a stress test for Suspense without type change (Sync Mode) 12`] = `
247 +[root]
248 + ▾ <Root>
249 + <X>
250 + ▾ <Suspense>
251 + ▾ <MaybeSuspend>
252 + <A key="a">
253 + <Z>
254 + <Y>
255 +`;
256 +
257 +exports[`StoreStress (Sync Mode) should handle a stress test for Suspense without type change (Sync Mode) 13`] = `
258 +[root]
259 + ▾ <Root>
260 + <X>
261 + ▾ <Suspense>
262 + <A key="a">
263 + <Y>
264 +`;
265 +
266 +exports[`StoreStress (Sync Mode) should handle a stress test for Suspense without type change (Sync Mode) 14`] = `
267 +[root]
268 + ▾ <Root>
269 + <X>
270 + ▾ <Suspense>
271 + <A key="a">
272 + <Y>
273 +`;
274 +
275 +exports[`StoreStress (Sync Mode) should handle a stress test for Suspense without type change (Sync Mode) 15`] = `
276 +[root]
277 + ▾ <Root>
278 + <X>
279 + ▾ <Suspense>
280 + <A key="a">
281 + <B key="b">
282 + <C key="c">
283 + <Y>
284 +`;
285 +
286 +exports[`StoreStress (Sync Mode) should handle a stress test for Suspense without type change (Sync Mode) 16`] = `
287 +[root]
288 + ▾ <Root>
289 + <X>
290 + ▾ <Suspense>
291 + <C key="c">
292 + <B key="b">
293 + <A key="a">
294 + <Y>
295 +`;
296 +
297 +exports[`StoreStress (Sync Mode) should handle a stress test for Suspense without type change (Sync Mode) 17`] = `
298 +[root]
299 + ▾ <Root>
300 + <X>
301 + ▾ <Suspense>
302 + <C key="c">
303 + <A key="a">
304 + <Y>
305 +`;
306 +
307 +exports[`StoreStress (Sync Mode) should handle a stress test for Suspense without type change (Sync Mode) 18`] = `
308 +[root]
309 + ▾ <Root>
310 + <X>
311 + ▾ <Suspense>
312 + <C key="c">
313 + <A key="a">
314 + <Y>
315 +`;
316 +
317 +exports[`StoreStress (Sync Mode) should handle a stress test for Suspense without type change (Sync Mode) 19`] = `
318 +[root]
319 + ▾ <Root>
320 + <X>
321 + ▾ <Suspense>
322 + <C key="c">
323 + <A key="a">
324 + <Y>
325 +`;
326 +
327 +exports[`StoreStress (Sync Mode) should handle a stress test for Suspense without type change (Sync Mode) 20`] = `
328 +[root]
329 + ▾ <Root>
330 + <X>
331 + ▾ <Suspense>
332 + <A key="a">
333 + <B key="b">
334 + <Y>
335 +`;
336 +
337 +exports[`StoreStress (Sync Mode) should handle a stress test for Suspense without type change (Sync Mode) 21`] = `
338 +[root]
339 + ▾ <Root>
340 + <X>
341 + ▾ <Suspense>
342 + <A key="a">
343 + <Y>
344 +`;
345 +
346 +exports[`StoreStress (Sync Mode) should handle a stress test for Suspense without type change (Sync Mode) 22`] = `
347 +[root]
348 + ▾ <Root>
349 + <X>
350 + <Suspense>
351 + <Y>
352 +`;
353 +
354 +exports[`StoreStress (Sync Mode) should handle a stress test for Suspense without type change (Sync Mode) 23`] = `
355 +[root]
356 + ▾ <Root>
357 + <X>
358 + ▾ <Suspense>
359 + <B key="b">
360 + <Y>
361 +`;
362 +
363 +exports[`StoreStress (Sync Mode) should handle a stress test for Suspense without type change (Sync Mode) 24`] = `
364 +[root]
365 + ▾ <Root>
366 + <X>
367 + ▾ <Suspense>
368 + <A key="a">
369 + <Y>
370 +`;
371 +
372 exports[`StoreStress (Sync Mode) should handle a stress test with different tree operations (Sync Mode): 1: abcde 1`] = `
373 [root]
374 ▾ <Parent>
src/__tests__/storeStressSync-test.js
+436
@@ -663,4 +663,440 @@ describe('StoreStress (Sync Mode)', () => {
663 }
664 }
665 });
666 +
667 + it('should handle a stress test for Suspense without type change (Sync Mode)', () => {
668 + const A = () => 'a';
669 + const B = () => 'b';
670 + const C = () => 'c';
671 + const X = () => 'x';
672 + const Y = () => 'y';
673 + const Z = () => 'z';
674 + const a = <A key="a" />;
675 + const b = <B key="b" />;
676 + const c = <C key="c" />;
677 + const z = <Z key="z" />;
678 +
679 + // prettier-ignore
680 + const steps = [
681 + a,
682 + [a],
683 + [a, b, c],
684 + [c, b, a],
685 + [c, null, a],
686 + <React.Fragment>{c}{a}</React.Fragment>,
687 + <div>{c}{a}</div>,
688 + <div><span>{a}</span>{b}</div>,
689 + [[a]],
690 + null,
691 + b,
692 + a
693 + ];
694 +
695 + const Never = () => {
696 + throw new Promise(() => {});
697 + };
698 +
699 + const MaybeSuspend = ({ children, suspend }) => {
700 + if (suspend) {
701 + return (
702 + <div>
703 + {children}
704 + <Never />
705 + <X />
706 + </div>
707 + );
708 + }
709 + return (
710 + <div>
711 + {children}
712 + <Z />
713 + </div>
714 + );
715 + };
716 +
717 + const Root = ({ children }) => {
718 + return children;
719 + };
720 +
721 + // 1. For each step, check Suspense can render them as initial primary content.
722 + // This is the only step where we use Jest snapshots.
723 + let snapshots = [];
724 + let container = document.createElement('div');
725 + for (let i = 0; i < steps.length; i++) {
726 + act(() =>
727 + ReactDOM.render(
728 + <Root>
729 + <X />
730 + <React.Suspense fallback={z}>
731 + <MaybeSuspend suspend={false}>{steps[i]}</MaybeSuspend>
732 + </React.Suspense>
733 + <Y />
734 + </Root>,
735 + container
736 + )
737 + );
738 + // We snapshot each step once so it doesn't regress.
739 + expect(store).toMatchSnapshot();
740 + snapshots.push(print(store));
741 + act(() => ReactDOM.unmountComponentAtNode(container));
742 + expect(print(store)).toBe('');
743 + }
744 +
745 + // 2. Verify check Suspense can render same steps as initial fallback content.
746 + // We don't actually assert here because the tree includes <MaybeSuspend>
747 + // which is different from the snapshots above. So we take more snapshots.
748 + let fallbackSnapshots = [];
749 + for (let i = 0; i < steps.length; i++) {
750 + act(() =>
751 + ReactDOM.render(
752 + <Root>
753 + <X />
754 + <React.Suspense fallback={steps[i]}>
755 + <Z />
756 + <MaybeSuspend suspend={true}>{steps[i]}</MaybeSuspend>
757 + <Z />
758 + </React.Suspense>
759 + <Y />
760 + </Root>,
761 + container
762 + )
763 + );
764 + // We snapshot each step once so it doesn't regress.
765 + expect(store).toMatchSnapshot();
766 + fallbackSnapshots.push(print(store));
767 + act(() => ReactDOM.unmountComponentAtNode(container));
768 + expect(print(store)).toBe('');
769 + }
770 +
771 + // 3. Verify we can update from each step to each step in primary mode.
772 + for (let i = 0; i < steps.length; i++) {
773 + for (let j = 0; j < steps.length; j++) {
774 + // Always start with a fresh container and steps[i].
775 + container = document.createElement('div');
776 + act(() =>
777 + ReactDOM.render(
778 + <Root>
779 + <X />
780 + <React.Suspense fallback={z}>
781 + <MaybeSuspend suspend={false}>{steps[i]}</MaybeSuspend>
782 + </React.Suspense>
783 + <Y />
784 + </Root>,
785 + container
786 + )
787 + );
788 + expect(print(store)).toEqual(snapshots[i]);
789 + // Re-render with steps[j].
790 + act(() =>
791 + ReactDOM.render(
792 + <Root>
793 + <X />
794 + <React.Suspense fallback={z}>
795 + <MaybeSuspend suspend={false}>{steps[j]}</MaybeSuspend>
796 + </React.Suspense>
797 + <Y />
798 + </Root>,
799 + container
800 + )
801 + );
802 + // Verify the successful transition to steps[j].
803 + expect(print(store)).toEqual(snapshots[j]);
804 + // Check that we can transition back again.
805 + act(() =>
806 + ReactDOM.render(
807 + <Root>
808 + <X />
809 + <React.Suspense fallback={z}>
810 + <MaybeSuspend suspend={false}>{steps[i]}</MaybeSuspend>
811 + </React.Suspense>
812 + <Y />
813 + </Root>,
814 + container
815 + )
816 + );
817 + expect(print(store)).toEqual(snapshots[i]);
818 + // Clean up after every iteration.
819 + act(() => ReactDOM.unmountComponentAtNode(container));
820 + expect(print(store)).toBe('');
821 + }
822 + }
823 +
824 + // 4. Verify we can update from each step to each step in fallback mode.
825 + for (let i = 0; i < steps.length; i++) {
826 + for (let j = 0; j < steps.length; j++) {
827 + // Always start with a fresh container and steps[i].
828 + container = document.createElement('div');
829 + act(() =>
830 + ReactDOM.render(
831 + <Root>
832 + <X />
833 + <React.Suspense fallback={steps[i]}>
834 + <Z />
835 + <MaybeSuspend suspend={true}>
836 + <X />
837 + <Y />
838 + </MaybeSuspend>
839 + <Z />
840 + </React.Suspense>
841 + <Y />
842 + </Root>,
843 + container
844 + )
845 + );
846 + expect(print(store)).toEqual(fallbackSnapshots[i]);
847 + // Re-render with steps[j].
848 + act(() =>
849 + ReactDOM.render(
850 + <Root>
851 + <X />
852 + <React.Suspense fallback={steps[j]}>
853 + <Z />
854 + <MaybeSuspend suspend={true}>
855 + <Y />
856 + <X />
857 + </MaybeSuspend>
858 + <Z />
859 + </React.Suspense>
860 + <Y />
861 + </Root>,
862 + container
863 + )
864 + );
865 + // Verify the successful transition to steps[j].
866 + expect(print(store)).toEqual(fallbackSnapshots[j]);
867 + // Check that we can transition back again.
868 + act(() =>
869 + ReactDOM.render(
870 + <Root>
871 + <X />
872 + <React.Suspense fallback={steps[i]}>
873 + <Z />
874 + <MaybeSuspend suspend={true}>
875 + <X />
876 + <Y />
877 + </MaybeSuspend>
878 + <Z />
879 + </React.Suspense>
880 + <Y />
881 + </Root>,
882 + container
883 + )
884 + );
885 + expect(print(store)).toEqual(fallbackSnapshots[i]);
886 + // Clean up after every iteration.
887 + act(() => ReactDOM.unmountComponentAtNode(container));
888 + expect(print(store)).toBe('');
889 + }
890 + }
891 +
892 + // 5. Verify we can update from each step to each step when moving primary -> fallback.
893 + for (let i = 0; i < steps.length; i++) {
894 + for (let j = 0; j < steps.length; j++) {
895 + // Always start with a fresh container and steps[i].
896 + container = document.createElement('div');
897 + act(() =>
898 + ReactDOM.render(
899 + <Root>
900 + <X />
901 + <React.Suspense fallback={z}>
902 + <MaybeSuspend suspend={false}>{steps[i]}</MaybeSuspend>
903 + </React.Suspense>
904 + <Y />
905 + </Root>,
906 + container
907 + )
908 + );
909 + expect(print(store)).toEqual(snapshots[i]);
910 + // Re-render with steps[j].
911 + act(() =>
912 + ReactDOM.render(
913 + <Root>
914 + <X />
915 + <React.Suspense fallback={steps[j]}>
916 + <MaybeSuspend suspend={true}>{steps[i]}</MaybeSuspend>
917 + </React.Suspense>
918 + <Y />
919 + </Root>,
920 + container
921 + )
922 + );
923 + // Verify the successful transition to steps[j].
924 + expect(print(store)).toEqual(fallbackSnapshots[j]);
925 + // Check that we can transition back again.
926 + act(() =>
927 + ReactDOM.render(
928 + <Root>
929 + <X />
930 + <React.Suspense fallback={z}>
931 + <MaybeSuspend suspend={false}>{steps[i]}</MaybeSuspend>
932 + </React.Suspense>
933 + <Y />
934 + </Root>,
935 + container
936 + )
937 + );
938 + expect(print(store)).toEqual(snapshots[i]);
939 + // Clean up after every iteration.
940 + act(() => ReactDOM.unmountComponentAtNode(container));
941 + expect(print(store)).toBe('');
942 + }
943 + }
944 +
945 + // 6. Verify we can update from each step to each step when moving fallback -> primary.
946 + for (let i = 0; i < steps.length; i++) {
947 + for (let j = 0; j < steps.length; j++) {
948 + // Always start with a fresh container and steps[i].
949 + container = document.createElement('div');
950 + act(() =>
951 + ReactDOM.render(
952 + <Root>
953 + <X />
954 + <React.Suspense fallback={steps[i]}>
955 + <MaybeSuspend suspend={true}>{steps[j]}</MaybeSuspend>
956 + </React.Suspense>
957 + <Y />
958 + </Root>,
959 + container
960 + )
961 + );
962 + expect(print(store)).toEqual(fallbackSnapshots[i]);
963 + // Re-render with steps[j].
964 + act(() =>
965 + ReactDOM.render(
966 + <Root>
967 + <X />
968 + <React.Suspense fallback={steps[i]}>
969 + <MaybeSuspend suspend={false}>{steps[j]}</MaybeSuspend>
970 + </React.Suspense>
971 + <Y />
972 + </Root>,
973 + container
974 + )
975 + );
976 + // Verify the successful transition to steps[j].
977 + expect(print(store)).toEqual(snapshots[j]);
978 + // Check that we can transition back again.
979 + act(() =>
980 + ReactDOM.render(
981 + <Root>
982 + <X />
983 + <React.Suspense fallback={steps[i]}>
984 + <MaybeSuspend suspend={true}>{steps[j]}</MaybeSuspend>
985 + </React.Suspense>
986 + <Y />
987 + </Root>,
988 + container
989 + )
990 + );
991 + expect(print(store)).toEqual(fallbackSnapshots[i]);
992 + // Clean up after every iteration.
993 + act(() => ReactDOM.unmountComponentAtNode(container));
994 + expect(print(store)).toBe('');
995 + }
996 + }
997 +
998 + // 7. Verify we can update from each step to each step when toggling Suspense.
999 + for (let i = 0; i < steps.length; i++) {
1000 + for (let j = 0; j < steps.length; j++) {
1001 + // Always start with a fresh container and steps[i].
1002 + container = document.createElement('div');
1003 + act(() =>
1004 + ReactDOM.render(
1005 + <Root>
1006 + <X />
1007 + <React.Suspense fallback={steps[j]}>
1008 + <MaybeSuspend suspend={false}>{steps[i]}</MaybeSuspend>
1009 + </React.Suspense>
1010 + <Y />
1011 + </Root>,
1012 + container
1013 + )
1014 + );
1015 +
1016 + // We get ID from the index in the tree above:
1017 + // Root, X, Suspense, ...
1018 + // ^ (index is 2)
1019 + const suspenseID = store.getElementIDAtIndex(2);
1020 +
1021 + // Force fallback.
1022 + expect(print(store)).toEqual(snapshots[i]);
1023 + act(() => {
1024 + const suspenseID = store.getElementIDAtIndex(2);
1025 + bridge.send('overrideSuspense', {
1026 + id: suspenseID,
1027 + rendererID: store.getRendererIDForElement(suspenseID),
1028 + forceFallback: true,
1029 + });
1030 + });
1031 + expect(print(store)).toEqual(fallbackSnapshots[j]);
1032 +
1033 + // Stop forcing fallback.
1034 + act(() => {
1035 + bridge.send('overrideSuspense', {
1036 + id: suspenseID,
1037 + rendererID: store.getRendererIDForElement(suspenseID),
1038 + forceFallback: false,
1039 + });
1040 + });
1041 + expect(print(store)).toEqual(snapshots[i]);
1042 +
1043 + // Trigger actual fallback.
1044 + act(() =>
1045 + ReactDOM.render(
1046 + <Root>
1047 + <X />
1048 + <React.Suspense fallback={steps[j]}>
1049 + <MaybeSuspend suspend={true}>{steps[i]}</MaybeSuspend>
1050 + </React.Suspense>
1051 + <Y />
1052 + </Root>,
1053 + container
1054 + )
1055 + );
1056 + expect(print(store)).toEqual(fallbackSnapshots[j]);
1057 +
1058 + // Force fallback while we're in fallback mode.
1059 + act(() => {
1060 + bridge.send('overrideSuspense', {
1061 + id: suspenseID,
1062 + rendererID: store.getRendererIDForElement(suspenseID),
1063 + forceFallback: true,
1064 + });
1065 + });
1066 + // Keep seeing fallback content.
1067 + expect(print(store)).toEqual(fallbackSnapshots[j]);
1068 +
1069 + // Switch to primary mode.
1070 + act(() =>
1071 + ReactDOM.render(
1072 + <Root>
1073 + <X />
1074 + <React.Suspense fallback={steps[j]}>
1075 + <MaybeSuspend suspend={false}>{steps[i]}</MaybeSuspend>
1076 + </React.Suspense>
1077 + <Y />
1078 + </Root>,
1079 + container
1080 + )
1081 + );
1082 + // Fallback is still forced though.
1083 + expect(print(store)).toEqual(fallbackSnapshots[j]);
1084 +
1085 + // Stop forcing fallback. This reverts to primary content.
1086 + act(() => {
1087 + bridge.send('overrideSuspense', {
1088 + id: suspenseID,
1089 + rendererID: store.getRendererIDForElement(suspenseID),
1090 + forceFallback: false,
1091 + });
1092 + });
1093 + // Now we see primary content.
1094 + expect(print(store)).toEqual(snapshots[i]);
1095 +
1096 + // Clean up after every iteration.
1097 + act(() => ReactDOM.unmountComponentAtNode(container));
1098 + expect(print(store)).toBe('');
1099 + }
1100 + }
1101 + });
1102 });