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
});