@samitouri / QOS-React / commits / 72e67b343f

Add Concurrent Mode test

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