Use gate pragma instead of if (__EXPERIMENTAL__) (#19722)
* Use gate pragma instead of if (__EXPERIMENTAL__) * Fix stream error handling in tests Added an error listener so that the tests fail within their Jest scope, instead of crashing the whole process.
Andrew Clark committed
Aug 28, 2020 at 16:21 UTC
4f5fb56100fac50f2c8bb33f984301b550e71407
2 files changed
+936
-922
packages/react-dom/src/__tests__/ReactDOMServerIntegrationHooks-test.js
+932
-920
@@ -905,1072 +905,1084 @@ describe('ReactDOMServerHooks', () => {
905
expect(container.children[0].textContent).toEqual('0');
906
});
907
908
- if (__EXPERIMENTAL__) {
909
- describe('useOpaqueIdentifier', () => {
910
- it('generates unique ids for server string render', async () => {
911
- function App(props) {
912
- const idOne = useOpaqueIdentifier();
913
- const idTwo = useOpaqueIdentifier();
914
- return (
915
- <div>
916
- <div aria-labelledby={idOne} />
917
- <div id={idOne} />
918
- <span aria-labelledby={idTwo} />
919
- <span id={idTwo} />
920
- </div>
921
- );
922
- }
923
-
924
- const domNode = await serverRender(<App />);
925
- expect(domNode.children.length).toEqual(4);
926
- expect(domNode.children[0].getAttribute('aria-labelledby')).toEqual(
927
- domNode.children[1].getAttribute('id'),
928
- );
929
- expect(domNode.children[2].getAttribute('aria-labelledby')).toEqual(
930
- domNode.children[3].getAttribute('id'),
931
- );
932
- expect(domNode.children[0].getAttribute('aria-labelledby')).not.toEqual(
933
- domNode.children[2].getAttribute('aria-labelledby'),
908
+ describe('useOpaqueIdentifier', () => {
909
+ // @gate experimental
910
+ it('generates unique ids for server string render', async () => {
911
+ function App(props) {
912
+ const idOne = useOpaqueIdentifier();
913
+ const idTwo = useOpaqueIdentifier();
914
+ return (
915
+ <div>
916
+ <div aria-labelledby={idOne} />
917
+ <div id={idOne} />
918
+ <span aria-labelledby={idTwo} />
919
+ <span id={idTwo} />
920
+ </div>
921
);
935
- expect(
936
- domNode.children[0].getAttribute('aria-labelledby'),
937
- ).not.toBeNull();
938
- expect(
939
- domNode.children[2].getAttribute('aria-labelledby'),
940
- ).not.toBeNull();
941
- });
922
+ }
923
943
- it('generates unique ids for server stream render', async () => {
944
- function App(props) {
945
- const idOne = useOpaqueIdentifier();
946
- const idTwo = useOpaqueIdentifier();
947
- return (
948
- <div>
949
- <div aria-labelledby={idOne} />
950
- <div id={idOne} />
951
- <span aria-labelledby={idTwo} />
952
- <span id={idTwo} />
953
- </div>
954
- );
955
- }
924
+ const domNode = await serverRender(<App />);
925
+ expect(domNode.children.length).toEqual(4);
926
+ expect(domNode.children[0].getAttribute('aria-labelledby')).toEqual(
927
+ domNode.children[1].getAttribute('id'),
928
+ );
929
+ expect(domNode.children[2].getAttribute('aria-labelledby')).toEqual(
930
+ domNode.children[3].getAttribute('id'),
931
+ );
932
+ expect(domNode.children[0].getAttribute('aria-labelledby')).not.toEqual(
933
+ domNode.children[2].getAttribute('aria-labelledby'),
934
+ );
935
+ expect(
936
+ domNode.children[0].getAttribute('aria-labelledby'),
937
+ ).not.toBeNull();
938
+ expect(
939
+ domNode.children[2].getAttribute('aria-labelledby'),
940
+ ).not.toBeNull();
941
+ });
942
957
- const domNode = await streamRender(<App />);
958
- expect(domNode.children.length).toEqual(4);
959
- expect(domNode.children[0].getAttribute('aria-labelledby')).toEqual(
960
- domNode.children[1].getAttribute('id'),
961
- );
962
- expect(domNode.children[2].getAttribute('aria-labelledby')).toEqual(
963
- domNode.children[3].getAttribute('id'),
964
- );
965
- expect(domNode.children[0].getAttribute('aria-labelledby')).not.toEqual(
966
- domNode.children[2].getAttribute('aria-labelledby'),
943
+ // @gate experimental
944
+ it('generates unique ids for server stream render', async () => {
945
+ function App(props) {
946
+ const idOne = useOpaqueIdentifier();
947
+ const idTwo = useOpaqueIdentifier();
948
+ return (
949
+ <div>
950
+ <div aria-labelledby={idOne} />
951
+ <div id={idOne} />
952
+ <span aria-labelledby={idTwo} />
953
+ <span id={idTwo} />
954
+ </div>
955
);
968
- expect(
969
- domNode.children[0].getAttribute('aria-labelledby'),
970
- ).not.toBeNull();
971
- expect(
972
- domNode.children[2].getAttribute('aria-labelledby'),
973
- ).not.toBeNull();
974
- });
956
+ }
957
976
- it('generates unique ids for client render', async () => {
977
- function App(props) {
978
- const idOne = useOpaqueIdentifier();
979
- const idTwo = useOpaqueIdentifier();
980
- return (
981
- <div>
982
- <div aria-labelledby={idOne} />
983
- <div id={idOne} />
984
- <span aria-labelledby={idTwo} />
985
- <span id={idTwo} />
986
- </div>
987
- );
988
- }
958
+ const domNode = await streamRender(<App />);
959
+ expect(domNode.children.length).toEqual(4);
960
+ expect(domNode.children[0].getAttribute('aria-labelledby')).toEqual(
961
+ domNode.children[1].getAttribute('id'),
962
+ );
963
+ expect(domNode.children[2].getAttribute('aria-labelledby')).toEqual(
964
+ domNode.children[3].getAttribute('id'),
965
+ );
966
+ expect(domNode.children[0].getAttribute('aria-labelledby')).not.toEqual(
967
+ domNode.children[2].getAttribute('aria-labelledby'),
968
+ );
969
+ expect(
970
+ domNode.children[0].getAttribute('aria-labelledby'),
971
+ ).not.toBeNull();
972
+ expect(
973
+ domNode.children[2].getAttribute('aria-labelledby'),
974
+ ).not.toBeNull();
975
+ });
976
990
- const domNode = await clientCleanRender(<App />);
991
- expect(domNode.children.length).toEqual(4);
992
- expect(domNode.children[0].getAttribute('aria-labelledby')).toEqual(
993
- domNode.children[1].getAttribute('id'),
994
- );
995
- expect(domNode.children[2].getAttribute('aria-labelledby')).toEqual(
996
- domNode.children[3].getAttribute('id'),
997
- );
998
- expect(domNode.children[0].getAttribute('aria-labelledby')).not.toEqual(
999
- domNode.children[2].getAttribute('aria-labelledby'),
977
+ // @gate experimental
978
+ it('generates unique ids for client render', async () => {
979
+ function App(props) {
980
+ const idOne = useOpaqueIdentifier();
981
+ const idTwo = useOpaqueIdentifier();
982
+ return (
983
+ <div>
984
+ <div aria-labelledby={idOne} />
985
+ <div id={idOne} />
986
+ <span aria-labelledby={idTwo} />
987
+ <span id={idTwo} />
988
+ </div>
989
);
1001
- expect(
1002
- domNode.children[0].getAttribute('aria-labelledby'),
1003
- ).not.toBeNull();
1004
- expect(
1005
- domNode.children[2].getAttribute('aria-labelledby'),
1006
- ).not.toBeNull();
1007
- });
990
+ }
991
1009
- it('generates unique ids for client render on good server markup', async () => {
1010
- function App(props) {
1011
- const idOne = useOpaqueIdentifier();
1012
- const idTwo = useOpaqueIdentifier();
1013
- return (
1014
- <div>
1015
- <div aria-labelledby={idOne} />
1016
- <div id={idOne} />
1017
- <span aria-labelledby={idTwo} />
1018
- <span id={idTwo} />
1019
- </div>
1020
- );
1021
- }
992
+ const domNode = await clientCleanRender(<App />);
993
+ expect(domNode.children.length).toEqual(4);
994
+ expect(domNode.children[0].getAttribute('aria-labelledby')).toEqual(
995
+ domNode.children[1].getAttribute('id'),
996
+ );
997
+ expect(domNode.children[2].getAttribute('aria-labelledby')).toEqual(
998
+ domNode.children[3].getAttribute('id'),
999
+ );
1000
+ expect(domNode.children[0].getAttribute('aria-labelledby')).not.toEqual(
1001
+ domNode.children[2].getAttribute('aria-labelledby'),
1002
+ );
1003
+ expect(
1004
+ domNode.children[0].getAttribute('aria-labelledby'),
1005
+ ).not.toBeNull();
1006
+ expect(
1007
+ domNode.children[2].getAttribute('aria-labelledby'),
1008
+ ).not.toBeNull();
1009
+ });
1010
1023
- const domNode = await clientRenderOnServerString(<App />);
1024
- expect(domNode.children.length).toEqual(4);
1025
- expect(domNode.children[0].getAttribute('aria-labelledby')).toEqual(
1026
- domNode.children[1].getAttribute('id'),
1027
- );
1028
- expect(domNode.children[2].getAttribute('aria-labelledby')).toEqual(
1029
- domNode.children[3].getAttribute('id'),
1011
+ // @gate experimental
1012
+ it('generates unique ids for client render on good server markup', async () => {
1013
+ function App(props) {
1014
+ const idOne = useOpaqueIdentifier();
1015
+ const idTwo = useOpaqueIdentifier();
1016
+ return (
1017
+ <div>
1018
+ <div aria-labelledby={idOne} />
1019
+ <div id={idOne} />
1020
+ <span aria-labelledby={idTwo} />
1021
+ <span id={idTwo} />
1022
+ </div>
1023
);
1031
- expect(domNode.children[0].getAttribute('aria-labelledby')).not.toEqual(
1032
- domNode.children[2].getAttribute('aria-labelledby'),
1024
+ }
1025
+
1026
+ const domNode = await clientRenderOnServerString(<App />);
1027
+ expect(domNode.children.length).toEqual(4);
1028
+ expect(domNode.children[0].getAttribute('aria-labelledby')).toEqual(
1029
+ domNode.children[1].getAttribute('id'),
1030
+ );
1031
+ expect(domNode.children[2].getAttribute('aria-labelledby')).toEqual(
1032
+ domNode.children[3].getAttribute('id'),
1033
+ );
1034
+ expect(domNode.children[0].getAttribute('aria-labelledby')).not.toEqual(
1035
+ domNode.children[2].getAttribute('aria-labelledby'),
1036
+ );
1037
+ expect(
1038
+ domNode.children[0].getAttribute('aria-labelledby'),
1039
+ ).not.toBeNull();
1040
+ expect(
1041
+ domNode.children[2].getAttribute('aria-labelledby'),
1042
+ ).not.toBeNull();
1043
+ });
1044
+
1045
+ // @gate experimental
1046
+ it('useOpaqueIdentifier does not change id even if the component updates during client render', async () => {
1047
+ let _setShowId;
1048
+ function App() {
1049
+ const id = useOpaqueIdentifier();
1050
+ const [showId, setShowId] = useState(false);
1051
+ _setShowId = setShowId;
1052
+ return (
1053
+ <div>
1054
+ <div aria-labelledby={id} />
1055
+ {showId && <div id={id} />}
1056
+ </div>
1057
);
1034
- expect(
1035
- domNode.children[0].getAttribute('aria-labelledby'),
1036
- ).not.toBeNull();
1037
- expect(
1038
- domNode.children[2].getAttribute('aria-labelledby'),
1039
- ).not.toBeNull();
1040
- });
1058
+ }
1059
1042
- it('useOpaqueIdentifier does not change id even if the component updates during client render', async () => {
1043
- let _setShowId;
1044
- function App() {
1045
- const id = useOpaqueIdentifier();
1046
- const [showId, setShowId] = useState(false);
1047
- _setShowId = setShowId;
1048
- return (
1049
- <div>
1050
- <div aria-labelledby={id} />
1051
- {showId && <div id={id} />}
1052
- </div>
1053
- );
1054
- }
1060
+ const domNode = await clientCleanRender(<App />);
1061
+ const oldClientId = domNode.children[0].getAttribute('aria-labelledby');
1062
1056
- const domNode = await clientCleanRender(<App />);
1057
- const oldClientId = domNode.children[0].getAttribute('aria-labelledby');
1063
+ expect(domNode.children.length).toEqual(1);
1064
+ expect(oldClientId).not.toBeNull();
1065
1059
- expect(domNode.children.length).toEqual(1);
1060
- expect(oldClientId).not.toBeNull();
1066
+ await ReactTestUtils.act(async () => _setShowId(true));
1067
1062
- await ReactTestUtils.act(async () => _setShowId(true));
1068
+ expect(domNode.children.length).toEqual(2);
1069
+ expect(domNode.children[0].getAttribute('aria-labelledby')).toEqual(
1070
+ domNode.children[1].getAttribute('id'),
1071
+ );
1072
+ expect(domNode.children[0].getAttribute('aria-labelledby')).toEqual(
1073
+ oldClientId,
1074
+ );
1075
+ });
1076
1064
- expect(domNode.children.length).toEqual(2);
1065
- expect(domNode.children[0].getAttribute('aria-labelledby')).toEqual(
1066
- domNode.children[1].getAttribute('id'),
1067
- );
1068
- expect(domNode.children[0].getAttribute('aria-labelledby')).toEqual(
1069
- oldClientId,
1077
+ // @gate experimental
1078
+ it('useOpaqueIdentifier identifierPrefix works for server renderer and does not clash', async () => {
1079
+ function ChildTwo({id}) {
1080
+ return <div id={id}>Child Three</div>;
1081
+ }
1082
+ function App() {
1083
+ const id = useOpaqueIdentifier();
1084
+ const idTwo = useOpaqueIdentifier();
1085
+
1086
+ return (
1087
+ <div>
1088
+ <div aria-labelledby={id}>Child One</div>
1089
+ <ChildTwo id={id} />
1090
+ <div aria-labelledby={idTwo}>Child Three</div>
1091
+ <div id={idTwo}>Child Four</div>
1092
+ </div>
1093
);
1094
+ }
1095
+
1096
+ const containerOne = document.createElement('div');
1097
+ document.body.append(containerOne);
1098
+
1099
+ containerOne.innerHTML = ReactDOMServer.renderToString(<App />, {
1100
+ identifierPrefix: 'one',
1101
});
1102
1073
- it('useOpaqueIdentifier identifierPrefix works for server renderer and does not clash', async () => {
1074
- function ChildTwo({id}) {
1075
- return <div id={id}>Child Three</div>;
1076
- }
1077
- function App() {
1078
- const id = useOpaqueIdentifier();
1079
- const idTwo = useOpaqueIdentifier();
1103
+ const containerTwo = document.createElement('div');
1104
+ document.body.append(containerTwo);
1105
1081
- return (
1082
- <div>
1083
- <div aria-labelledby={id}>Child One</div>
1084
- <ChildTwo id={id} />
1085
- <div aria-labelledby={idTwo}>Child Three</div>
1086
- <div id={idTwo}>Child Four</div>
1087
- </div>
1088
- );
1089
- }
1106
+ containerTwo.innerHTML = ReactDOMServer.renderToString(<App />, {
1107
+ identifierPrefix: 'two',
1108
+ });
1109
1091
- const containerOne = document.createElement('div');
1092
- document.body.append(containerOne);
1110
+ expect(document.body.children.length).toEqual(2);
1111
+ const childOne = document.body.children[0];
1112
+ const childTwo = document.body.children[1];
1113
+
1114
+ expect(
1115
+ childOne.children[0].children[0].getAttribute('aria-labelledby'),
1116
+ ).toEqual(childOne.children[0].children[1].getAttribute('id'));
1117
+ expect(
1118
+ childOne.children[0].children[2].getAttribute('aria-labelledby'),
1119
+ ).toEqual(childOne.children[0].children[3].getAttribute('id'));
1120
+
1121
+ expect(
1122
+ childOne.children[0].children[0].getAttribute('aria-labelledby'),
1123
+ ).not.toEqual(
1124
+ childOne.children[0].children[2].getAttribute('aria-labelledby'),
1125
+ );
1126
1094
- containerOne.innerHTML = ReactDOMServer.renderToString(<App />, {
1095
- identifierPrefix: 'one',
1096
- });
1127
+ expect(
1128
+ childOne.children[0].children[0]
1129
+ .getAttribute('aria-labelledby')
1130
+ .startsWith('one'),
1131
+ ).toBe(true);
1132
+ expect(
1133
+ childOne.children[0].children[2]
1134
+ .getAttribute('aria-labelledby')
1135
+ .includes('one'),
1136
+ ).toBe(true);
1137
+
1138
+ expect(
1139
+ childTwo.children[0].children[0].getAttribute('aria-labelledby'),
1140
+ ).toEqual(childTwo.children[0].children[1].getAttribute('id'));
1141
+ expect(
1142
+ childTwo.children[0].children[2].getAttribute('aria-labelledby'),
1143
+ ).toEqual(childTwo.children[0].children[3].getAttribute('id'));
1144
+
1145
+ expect(
1146
+ childTwo.children[0].children[0].getAttribute('aria-labelledby'),
1147
+ ).not.toEqual(
1148
+ childTwo.children[0].children[2].getAttribute('aria-labelledby'),
1149
+ );
1150
1098
- const containerTwo = document.createElement('div');
1099
- document.body.append(containerTwo);
1151
+ expect(
1152
+ childTwo.children[0].children[0]
1153
+ .getAttribute('aria-labelledby')
1154
+ .startsWith('two'),
1155
+ ).toBe(true);
1156
+ expect(
1157
+ childTwo.children[0].children[2]
1158
+ .getAttribute('aria-labelledby')
1159
+ .startsWith('two'),
1160
+ ).toBe(true);
1161
+ });
1162
1101
- containerTwo.innerHTML = ReactDOMServer.renderToString(<App />, {
1102
- identifierPrefix: 'two',
1103
- });
1163
+ // @gate experimental
1164
+ it('useOpaqueIdentifier identifierPrefix works for multiple reads on a streaming server renderer', async () => {
1165
+ function ChildTwo() {
1166
+ const id = useOpaqueIdentifier();
1167
1105
- expect(document.body.children.length).toEqual(2);
1106
- const childOne = document.body.children[0];
1107
- const childTwo = document.body.children[1];
1108
-
1109
- expect(
1110
- childOne.children[0].children[0].getAttribute('aria-labelledby'),
1111
- ).toEqual(childOne.children[0].children[1].getAttribute('id'));
1112
- expect(
1113
- childOne.children[0].children[2].getAttribute('aria-labelledby'),
1114
- ).toEqual(childOne.children[0].children[3].getAttribute('id'));
1115
-
1116
- expect(
1117
- childOne.children[0].children[0].getAttribute('aria-labelledby'),
1118
- ).not.toEqual(
1119
- childOne.children[0].children[2].getAttribute('aria-labelledby'),
1120
- );
1168
+ return <div id={id}>Child Two</div>;
1169
+ }
1170
1122
- expect(
1123
- childOne.children[0].children[0]
1124
- .getAttribute('aria-labelledby')
1125
- .startsWith('one'),
1126
- ).toBe(true);
1127
- expect(
1128
- childOne.children[0].children[2]
1129
- .getAttribute('aria-labelledby')
1130
- .includes('one'),
1131
- ).toBe(true);
1132
-
1133
- expect(
1134
- childTwo.children[0].children[0].getAttribute('aria-labelledby'),
1135
- ).toEqual(childTwo.children[0].children[1].getAttribute('id'));
1136
- expect(
1137
- childTwo.children[0].children[2].getAttribute('aria-labelledby'),
1138
- ).toEqual(childTwo.children[0].children[3].getAttribute('id'));
1139
-
1140
- expect(
1141
- childTwo.children[0].children[0].getAttribute('aria-labelledby'),
1142
- ).not.toEqual(
1143
- childTwo.children[0].children[2].getAttribute('aria-labelledby'),
1171
+ function App() {
1172
+ const id = useOpaqueIdentifier();
1173
+
1174
+ return (
1175
+ <>
1176
+ <div id={id}>Child One</div>
1177
+ <ChildTwo />
1178
+ <div aria-labelledby={id}>Aria One</div>
1179
+ </>
1180
);
1181
+ }
1182
1146
- expect(
1147
- childTwo.children[0].children[0]
1148
- .getAttribute('aria-labelledby')
1149
- .startsWith('two'),
1150
- ).toBe(true);
1151
- expect(
1152
- childTwo.children[0].children[2]
1153
- .getAttribute('aria-labelledby')
1154
- .startsWith('two'),
1155
- ).toBe(true);
1156
- });
1183
+ const container = document.createElement('div');
1184
+ document.body.append(container);
1185
1158
- it('useOpaqueIdentifier identifierPrefix works for multiple reads on a streaming server renderer', async () => {
1159
- function ChildTwo() {
1160
- const id = useOpaqueIdentifier();
1186
+ const streamOne = ReactDOMServer.renderToNodeStream(<App />, {
1187
+ identifierPrefix: 'one',
1188
+ }).setEncoding('utf8');
1189
+ const streamTwo = ReactDOMServer.renderToNodeStream(<App />, {
1190
+ identifierPrefix: 'two',
1191
+ }).setEncoding('utf8');
1192
1162
- return <div id={id}>Child Two</div>;
1163
- }
1193
+ const streamOneIsDone = new Promise((resolve, reject) => {
1194
+ streamOne.on('end', () => resolve());
1195
+ streamOne.on('error', e => reject(e));
1196
+ });
1197
+ const streamTwoIsDone = new Promise((resolve, reject) => {
1198
+ streamTwo.on('end', () => resolve());
1199
+ streamTwo.on('error', e => reject(e));
1200
+ });
1201
1165
- function App() {
1166
- const id = useOpaqueIdentifier();
1202
+ const containerOne = document.createElement('div');
1203
+ const containerTwo = document.createElement('div');
1204
1168
- return (
1169
- <>
1170
- <div id={id}>Child One</div>
1171
- <ChildTwo />
1172
- <div aria-labelledby={id}>Aria One</div>
1173
- </>
1174
- );
1175
- }
1205
+ streamOne._read(10);
1206
+ streamTwo._read(10);
1207
1177
- const container = document.createElement('div');
1178
- document.body.append(container);
1208
+ containerOne.innerHTML = streamOne.read();
1209
+ containerTwo.innerHTML = streamTwo.read();
1210
1180
- const streamOne = ReactDOMServer.renderToNodeStream(<App />, {
1181
- identifierPrefix: 'one',
1182
- }).setEncoding('utf8');
1183
- const streamTwo = ReactDOMServer.renderToNodeStream(<App />, {
1184
- identifierPrefix: 'two',
1185
- }).setEncoding('utf8');
1211
+ expect(containerOne.children[0].getAttribute('id')).not.toEqual(
1212
+ containerOne.children[1].getAttribute('id'),
1213
+ );
1214
+ expect(containerTwo.children[0].getAttribute('id')).not.toEqual(
1215
+ containerTwo.children[1].getAttribute('id'),
1216
+ );
1217
+ expect(containerOne.children[0].getAttribute('id')).not.toEqual(
1218
+ containerTwo.children[0].getAttribute('id'),
1219
+ );
1220
+ expect(containerOne.children[0].getAttribute('id').includes('one')).toBe(
1221
+ true,
1222
+ );
1223
+ expect(containerOne.children[1].getAttribute('id').includes('one')).toBe(
1224
+ true,
1225
+ );
1226
+ expect(containerTwo.children[0].getAttribute('id').includes('two')).toBe(
1227
+ true,
1228
+ );
1229
+ expect(containerTwo.children[1].getAttribute('id').includes('two')).toBe(
1230
+ true,
1231
+ );
1232
+
1233
+ expect(containerOne.children[1].getAttribute('id')).not.toEqual(
1234
+ containerTwo.children[1].getAttribute('id'),
1235
+ );
1236
+ expect(containerOne.children[0].getAttribute('id')).toEqual(
1237
+ containerOne.children[2].getAttribute('aria-labelledby'),
1238
+ );
1239
+ expect(containerTwo.children[0].getAttribute('id')).toEqual(
1240
+ containerTwo.children[2].getAttribute('aria-labelledby'),
1241
+ );
1242
1187
- const containerOne = document.createElement('div');
1188
- const containerTwo = document.createElement('div');
1243
+ // Exhaust the rest of the stream
1244
+ class Sink extends require('stream').Writable {
1245
+ _write(chunk, encoding, done) {
1246
+ done();
1247
+ }
1248
+ }
1249
+ streamOne.pipe(new Sink());
1250
+ streamTwo.pipe(new Sink());
1251
1190
- streamOne._read(10);
1191
- streamTwo._read(10);
1252
+ await Promise.all([streamOneIsDone, streamTwoIsDone]);
1253
+ });
1254
1193
- containerOne.innerHTML = streamOne.read();
1194
- containerTwo.innerHTML = streamTwo.read();
1255
+ // @gate experimental
1256
+ it('useOpaqueIdentifier: IDs match when, after hydration, a new component that uses the ID is rendered', async () => {
1257
+ let _setShowDiv;
1258
+ function App() {
1259
+ const id = useOpaqueIdentifier();
1260
+ const [showDiv, setShowDiv] = useState(false);
1261
+ _setShowDiv = setShowDiv;
1262
1196
- expect(containerOne.children[0].getAttribute('id')).not.toEqual(
1197
- containerOne.children[1].getAttribute('id'),
1198
- );
1199
- expect(containerTwo.children[0].getAttribute('id')).not.toEqual(
1200
- containerTwo.children[1].getAttribute('id'),
1201
- );
1202
- expect(containerOne.children[0].getAttribute('id')).not.toEqual(
1203
- containerTwo.children[0].getAttribute('id'),
1204
- );
1205
- expect(
1206
- containerOne.children[0].getAttribute('id').includes('one'),
1207
- ).toBe(true);
1208
- expect(
1209
- containerOne.children[1].getAttribute('id').includes('one'),
1210
- ).toBe(true);
1211
- expect(
1212
- containerTwo.children[0].getAttribute('id').includes('two'),
1213
- ).toBe(true);
1214
- expect(
1215
- containerTwo.children[1].getAttribute('id').includes('two'),
1216
- ).toBe(true);
1217
-
1218
- expect(containerOne.children[1].getAttribute('id')).not.toEqual(
1219
- containerTwo.children[1].getAttribute('id'),
1220
- );
1221
- expect(containerOne.children[0].getAttribute('id')).toEqual(
1222
- containerOne.children[2].getAttribute('aria-labelledby'),
1223
- );
1224
- expect(containerTwo.children[0].getAttribute('id')).toEqual(
1225
- containerTwo.children[2].getAttribute('aria-labelledby'),
1263
+ return (
1264
+ <div>
1265
+ <div id={id}>Child One</div>
1266
+ {showDiv && <div id={id}>Child Two</div>}
1267
+ </div>
1268
);
1227
- });
1269
+ }
1270
1229
- it('useOpaqueIdentifier: IDs match when, after hydration, a new component that uses the ID is rendered', async () => {
1230
- let _setShowDiv;
1231
- function App() {
1232
- const id = useOpaqueIdentifier();
1233
- const [showDiv, setShowDiv] = useState(false);
1234
- _setShowDiv = setShowDiv;
1271
+ const container = document.createElement('div');
1272
+ document.body.append(container);
1273
1236
- return (
1237
- <div>
1238
- <div id={id}>Child One</div>
1239
- {showDiv && <div id={id}>Child Two</div>}
1240
- </div>
1241
- );
1242
- }
1274
+ container.innerHTML = ReactDOMServer.renderToString(<App />);
1275
+ const root = ReactDOM.unstable_createRoot(container, {hydrate: true});
1276
+ root.render(<App />);
1277
+ Scheduler.unstable_flushAll();
1278
+ jest.runAllTimers();
1279
1244
- const container = document.createElement('div');
1245
- document.body.append(container);
1280
+ expect(container.children[0].children.length).toEqual(1);
1281
+ const oldServerId = container.children[0].children[0].getAttribute('id');
1282
+ expect(oldServerId).not.toBeNull();
1283
1247
- container.innerHTML = ReactDOMServer.renderToString(<App />);
1248
- const root = ReactDOM.unstable_createRoot(container, {hydrate: true});
1249
- root.render(<App />);
1250
- Scheduler.unstable_flushAll();
1251
- jest.runAllTimers();
1284
+ await ReactTestUtils.act(async () => {
1285
+ _setShowDiv(true);
1286
+ });
1287
+ expect(container.children[0].children.length).toEqual(2);
1288
+ expect(container.children[0].children[0].getAttribute('id')).toEqual(
1289
+ container.children[0].children[1].getAttribute('id'),
1290
+ );
1291
+ expect(container.children[0].children[0].getAttribute('id')).not.toEqual(
1292
+ oldServerId,
1293
+ );
1294
+ expect(
1295
+ container.children[0].children[0].getAttribute('id'),
1296
+ ).not.toBeNull();
1297
+ });
1298
1253
- expect(container.children[0].children.length).toEqual(1);
1254
- const oldServerId = container.children[0].children[0].getAttribute(
1255
- 'id',
1256
- );
1257
- expect(oldServerId).not.toBeNull();
1299
+ // @gate experimental
1300
+ it('useOpaqueIdentifier: IDs match when, after hydration, a new component that uses the ID is rendered for legacy', async () => {
1301
+ let _setShowDiv;
1302
+ function App() {
1303
+ const id = useOpaqueIdentifier();
1304
+ const [showDiv, setShowDiv] = useState(false);
1305
+ _setShowDiv = setShowDiv;
1306
1259
- await ReactTestUtils.act(async () => {
1260
- _setShowDiv(true);
1261
- });
1262
- expect(container.children[0].children.length).toEqual(2);
1263
- expect(container.children[0].children[0].getAttribute('id')).toEqual(
1264
- container.children[0].children[1].getAttribute('id'),
1307
+ return (
1308
+ <div>
1309
+ <div id={id}>Child One</div>
1310
+ {showDiv && <div id={id}>Child Two</div>}
1311
+ </div>
1312
);
1266
- expect(
1267
- container.children[0].children[0].getAttribute('id'),
1268
- ).not.toEqual(oldServerId);
1269
- expect(
1270
- container.children[0].children[0].getAttribute('id'),
1271
- ).not.toBeNull();
1272
- });
1313
+ }
1314
1274
- it('useOpaqueIdentifier: IDs match when, after hydration, a new component that uses the ID is rendered for legacy', async () => {
1275
- let _setShowDiv;
1276
- function App() {
1277
- const id = useOpaqueIdentifier();
1278
- const [showDiv, setShowDiv] = useState(false);
1279
- _setShowDiv = setShowDiv;
1315
+ const container = document.createElement('div');
1316
+ document.body.append(container);
1317
1281
- return (
1282
- <div>
1283
- <div id={id}>Child One</div>
1284
- {showDiv && <div id={id}>Child Two</div>}
1285
- </div>
1286
- );
1287
- }
1318
+ container.innerHTML = ReactDOMServer.renderToString(<App />);
1319
+ ReactDOM.hydrate(<App />, container);
1320
1289
- const container = document.createElement('div');
1290
- document.body.append(container);
1321
+ expect(container.children[0].children.length).toEqual(1);
1322
+ const oldServerId = container.children[0].children[0].getAttribute('id');
1323
+ expect(oldServerId).not.toBeNull();
1324
1292
- container.innerHTML = ReactDOMServer.renderToString(<App />);
1293
- ReactDOM.hydrate(<App />, container);
1325
+ await ReactTestUtils.act(async () => {
1326
+ _setShowDiv(true);
1327
+ });
1328
+ expect(container.children[0].children.length).toEqual(2);
1329
+ expect(container.children[0].children[0].getAttribute('id')).toEqual(
1330
+ container.children[0].children[1].getAttribute('id'),
1331
+ );
1332
+ expect(container.children[0].children[0].getAttribute('id')).not.toEqual(
1333
+ oldServerId,
1334
+ );
1335
+ expect(
1336
+ container.children[0].children[0].getAttribute('id'),
1337
+ ).not.toBeNull();
1338
+ });
1339
1295
- expect(container.children[0].children.length).toEqual(1);
1296
- const oldServerId = container.children[0].children[0].getAttribute(
1297
- 'id',
1340
+ // @gate experimental
1341
+ it('useOpaqueIdentifier: ID is not used during hydration but is used in an update', async () => {
1342
+ let _setShow;
1343
+ function App({unused}) {
1344
+ Scheduler.unstable_yieldValue('App');
1345
+ const id = useOpaqueIdentifier();
1346
+ const [show, setShow] = useState(false);
1347
+ _setShow = setShow;
1348
+ return (
1349
+ <div>
1350
+ <span id={show ? id : null}>{'Child One'}</span>
1351
+ </div>
1352
);
1299
- expect(oldServerId).not.toBeNull();
1353
+ }
1354
1301
- await ReactTestUtils.act(async () => {
1302
- _setShowDiv(true);
1303
- });
1304
- expect(container.children[0].children.length).toEqual(2);
1305
- expect(container.children[0].children[0].getAttribute('id')).toEqual(
1306
- container.children[0].children[1].getAttribute('id'),
1307
- );
1308
- expect(
1309
- container.children[0].children[0].getAttribute('id'),
1310
- ).not.toEqual(oldServerId);
1311
- expect(
1312
- container.children[0].children[0].getAttribute('id'),
1313
- ).not.toBeNull();
1355
+ const container = document.createElement('div');
1356
+ document.body.append(container);
1357
+ container.innerHTML = ReactDOMServer.renderToString(<App />);
1358
+ const root = ReactDOM.unstable_createRoot(container, {hydrate: true});
1359
+ ReactTestUtils.act(() => {
1360
+ root.render(<App />);
1361
});
1315
-
1316
- it('useOpaqueIdentifier: ID is not used during hydration but is used in an update', async () => {
1317
- let _setShow;
1318
- function App({unused}) {
1319
- Scheduler.unstable_yieldValue('App');
1320
- const id = useOpaqueIdentifier();
1321
- const [show, setShow] = useState(false);
1322
- _setShow = setShow;
1323
- return (
1324
- <div>
1325
- <span id={show ? id : null}>{'Child One'}</span>
1326
- </div>
1327
- );
1328
- }
1329
-
1330
- const container = document.createElement('div');
1331
- document.body.append(container);
1332
- container.innerHTML = ReactDOMServer.renderToString(<App />);
1333
- const root = ReactDOM.unstable_createRoot(container, {hydrate: true});
1334
- ReactTestUtils.act(() => {
1335
- root.render(<App />);
1336
- });
1337
- expect(Scheduler).toHaveYielded(['App', 'App']);
1338
- // The ID goes from not being used to being added to the page
1339
- ReactTestUtils.act(() => {
1340
- _setShow(true);
1341
- });
1342
- expect(Scheduler).toHaveYielded(['App', 'App']);
1343
- expect(
1344
- container.getElementsByTagName('span')[0].getAttribute('id'),
1345
- ).not.toBeNull();
1362
+ expect(Scheduler).toHaveYielded(['App', 'App']);
1363
+ // The ID goes from not being used to being added to the page
1364
+ ReactTestUtils.act(() => {
1365
+ _setShow(true);
1366
});
1367
+ expect(Scheduler).toHaveYielded(['App', 'App']);
1368
+ expect(
1369
+ container.getElementsByTagName('span')[0].getAttribute('id'),
1370
+ ).not.toBeNull();
1371
+ });
1372
1348
- it('useOpaqueIdentifier: ID is not used during hydration but is used in an update in legacy', async () => {
1349
- let _setShow;
1350
- function App({unused}) {
1351
- Scheduler.unstable_yieldValue('App');
1352
- const id = useOpaqueIdentifier();
1353
- const [show, setShow] = useState(false);
1354
- _setShow = setShow;
1355
- return (
1356
- <div>
1357
- <span id={show ? id : null}>{'Child One'}</span>
1358
- </div>
1359
- );
1360
- }
1373
+ // @gate experimental
1374
+ it('useOpaqueIdentifier: ID is not used during hydration but is used in an update in legacy', async () => {
1375
+ let _setShow;
1376
+ function App({unused}) {
1377
+ Scheduler.unstable_yieldValue('App');
1378
+ const id = useOpaqueIdentifier();
1379
+ const [show, setShow] = useState(false);
1380
+ _setShow = setShow;
1381
+ return (
1382
+ <div>
1383
+ <span id={show ? id : null}>{'Child One'}</span>
1384
+ </div>
1385
+ );
1386
+ }
1387
1362
- const container = document.createElement('div');
1363
- document.body.append(container);
1364
- container.innerHTML = ReactDOMServer.renderToString(<App />);
1365
- ReactDOM.hydrate(<App />, container);
1366
- expect(Scheduler).toHaveYielded(['App', 'App']);
1367
- // The ID goes from not being used to being added to the page
1368
- ReactTestUtils.act(() => {
1369
- _setShow(true);
1370
- });
1371
- expect(Scheduler).toHaveYielded(['App']);
1372
- expect(
1373
- container.getElementsByTagName('span')[0].getAttribute('id'),
1374
- ).not.toBeNull();
1388
+ const container = document.createElement('div');
1389
+ document.body.append(container);
1390
+ container.innerHTML = ReactDOMServer.renderToString(<App />);
1391
+ ReactDOM.hydrate(<App />, container);
1392
+ expect(Scheduler).toHaveYielded(['App', 'App']);
1393
+ // The ID goes from not being used to being added to the page
1394
+ ReactTestUtils.act(() => {
1395
+ _setShow(true);
1396
});
1397
+ expect(Scheduler).toHaveYielded(['App']);
1398
+ expect(
1399
+ container.getElementsByTagName('span')[0].getAttribute('id'),
1400
+ ).not.toBeNull();
1401
+ });
1402
1377
- it('useOpaqueIdentifier: flushSync', async () => {
1378
- let _setShow;
1379
- function App() {
1380
- const id = useOpaqueIdentifier();
1381
- const [show, setShow] = useState(false);
1382
- _setShow = setShow;
1383
- return (
1384
- <div>
1385
- <span id={show ? id : null}>{'Child One'}</span>
1386
- </div>
1387
- );
1388
- }
1403
+ // @gate experimental
1404
+ it('useOpaqueIdentifier: flushSync', async () => {
1405
+ let _setShow;
1406
+ function App() {
1407
+ const id = useOpaqueIdentifier();
1408
+ const [show, setShow] = useState(false);
1409
+ _setShow = setShow;
1410
+ return (
1411
+ <div>
1412
+ <span id={show ? id : null}>{'Child One'}</span>
1413
+ </div>
1414
+ );
1415
+ }
1416
1390
- const container = document.createElement('div');
1391
- document.body.append(container);
1392
- container.innerHTML = ReactDOMServer.renderToString(<App />);
1393
- const root = ReactDOM.unstable_createRoot(container, {hydrate: true});
1394
- ReactTestUtils.act(() => {
1395
- root.render(<App />);
1396
- });
1417
+ const container = document.createElement('div');
1418
+ document.body.append(container);
1419
+ container.innerHTML = ReactDOMServer.renderToString(<App />);
1420
+ const root = ReactDOM.unstable_createRoot(container, {hydrate: true});
1421
+ ReactTestUtils.act(() => {
1422
+ root.render(<App />);
1423
+ });
1424
1398
- // The ID goes from not being used to being added to the page
1399
- ReactTestUtils.act(() => {
1400
- ReactDOM.flushSync(() => {
1401
- _setShow(true);
1402
- });
1425
+ // The ID goes from not being used to being added to the page
1426
+ ReactTestUtils.act(() => {
1427
+ ReactDOM.flushSync(() => {
1428
+ _setShow(true);
1429
});
1404
- expect(
1405
- container.getElementsByTagName('span')[0].getAttribute('id'),
1406
- ).not.toBeNull();
1430
});
1431
+ expect(
1432
+ container.getElementsByTagName('span')[0].getAttribute('id'),
1433
+ ).not.toBeNull();
1434
+ });
1435
1409
- it('useOpaqueIdentifier: children with id hydrates before other children if ID updates', async () => {
1410
- let _setShow;
1411
-
1412
- const child1Ref = React.createRef();
1413
- const childWithIDRef = React.createRef();
1414
- const setShowRef = React.createRef();
1436
+ // @gate experimental
1437
+ it('useOpaqueIdentifier: children with id hydrates before other children if ID updates', async () => {
1438
+ let _setShow;
1439
1416
- // RENAME THESE
1417
- function Child1() {
1418
- Scheduler.unstable_yieldValue('Child One');
1419
- return <span ref={child1Ref}>{'Child One'}</span>;
1420
- }
1440
+ const child1Ref = React.createRef();
1441
+ const childWithIDRef = React.createRef();
1442
+ const setShowRef = React.createRef();
1443
1422
- function Child2() {
1423
- Scheduler.unstable_yieldValue('Child Two');
1424
- return <span>{'Child Two'}</span>;
1425
- }
1444
+ // RENAME THESE
1445
+ function Child1() {
1446
+ Scheduler.unstable_yieldValue('Child One');
1447
+ return <span ref={child1Ref}>{'Child One'}</span>;
1448
+ }
1449
1427
- const Children = React.memo(function Children() {
1428
- return (
1429
- <React.Suspense fallback="Loading 1...">
1430
- <Child1 />
1431
- <Child2 />
1432
- </React.Suspense>
1433
- );
1434
- });
1450
+ function Child2() {
1451
+ Scheduler.unstable_yieldValue('Child Two');
1452
+ return <span>{'Child Two'}</span>;
1453
+ }
1454
1436
- function ChildWithID({parentID}) {
1437
- Scheduler.unstable_yieldValue('Child with ID');
1438
- return (
1439
- <span id={parentID} ref={childWithIDRef}>
1440
- {'Child with ID'}
1441
- </span>
1442
- );
1443
- }
1455
+ const Children = React.memo(function Children() {
1456
+ return (
1457
+ <React.Suspense fallback="Loading 1...">
1458
+ <Child1 />
1459
+ <Child2 />
1460
+ </React.Suspense>
1461
+ );
1462
+ });
1463
1445
- const ChildrenWithID = React.memo(function ChildrenWithID({parentID}) {
1446
- return (
1447
- <React.Suspense fallback="Loading 2...">
1448
- <ChildWithID parentID={parentID} />
1449
- </React.Suspense>
1450
- );
1451
- });
1464
+ function ChildWithID({parentID}) {
1465
+ Scheduler.unstable_yieldValue('Child with ID');
1466
+ return (
1467
+ <span id={parentID} ref={childWithIDRef}>
1468
+ {'Child with ID'}
1469
+ </span>
1470
+ );
1471
+ }
1472
1453
- function App() {
1454
- const id = useOpaqueIdentifier();
1455
- const [show, setShow] = useState(false);
1456
- _setShow = setShow;
1457
- return (
1458
- <div>
1459
- <Children />
1460
- <ChildrenWithID parentID={id} />
1461
- {show && (
1462
- <span aria-labelledby={id} ref={setShowRef}>
1463
- {'Child Three'}
1464
- </span>
1465
- )}
1466
- </div>
1467
- );
1468
- }
1473
+ const ChildrenWithID = React.memo(function ChildrenWithID({parentID}) {
1474
+ return (
1475
+ <React.Suspense fallback="Loading 2...">
1476
+ <ChildWithID parentID={parentID} />
1477
+ </React.Suspense>
1478
+ );
1479
+ });
1480
1470
- const container = document.createElement('div');
1471
- container.innerHTML = ReactDOMServer.renderToString(<App />);
1472
- expect(Scheduler).toHaveYielded([
1473
- 'Child One',
1474
- 'Child Two',
1475
- 'Child with ID',
1476
- ]);
1477
- expect(container.textContent).toEqual(
1478
- 'Child OneChild TwoChild with ID',
1481
+ function App() {
1482
+ const id = useOpaqueIdentifier();
1483
+ const [show, setShow] = useState(false);
1484
+ _setShow = setShow;
1485
+ return (
1486
+ <div>
1487
+ <Children />
1488
+ <ChildrenWithID parentID={id} />
1489
+ {show && (
1490
+ <span aria-labelledby={id} ref={setShowRef}>
1491
+ {'Child Three'}
1492
+ </span>
1493
+ )}
1494
+ </div>
1495
);
1496
+ }
1497
1481
- const serverId = container
1482
- .getElementsByTagName('span')[2]
1483
- .getAttribute('id');
1484
- expect(serverId).not.toBeNull();
1498
+ const container = document.createElement('div');
1499
+ container.innerHTML = ReactDOMServer.renderToString(<App />);
1500
+ expect(Scheduler).toHaveYielded([
1501
+ 'Child One',
1502
+ 'Child Two',
1503
+ 'Child with ID',
1504
+ ]);
1505
+ expect(container.textContent).toEqual('Child OneChild TwoChild with ID');
1506
1486
- const root = ReactDOM.unstable_createRoot(container, {hydrate: true});
1487
- root.render(<App show={false} />);
1488
- expect(Scheduler).toHaveYielded([]);
1507
+ const serverId = container
1508
+ .getElementsByTagName('span')[2]
1509
+ .getAttribute('id');
1510
+ expect(serverId).not.toBeNull();
1511
1490
- //Hydrate just child one before updating state
1491
- expect(Scheduler).toFlushAndYieldThrough(['Child One']);
1492
- expect(child1Ref.current).toBe(null);
1493
- expect(Scheduler).toHaveYielded([]);
1512
+ const root = ReactDOM.unstable_createRoot(container, {hydrate: true});
1513
+ root.render(<App show={false} />);
1514
+ expect(Scheduler).toHaveYielded([]);
1515
1495
- ReactTestUtils.act(() => {
1496
- _setShow(true);
1516
+ //Hydrate just child one before updating state
1517
+ expect(Scheduler).toFlushAndYieldThrough(['Child One']);
1518
+ expect(child1Ref.current).toBe(null);
1519
+ expect(Scheduler).toHaveYielded([]);
1520
1498
- // State update should trigger the ID to update, which changes the props
1499
- // of ChildWithID. This should cause ChildWithID to hydrate before Children
1500
-
1501
- expect(Scheduler).toFlushAndYieldThrough([
1502
- 'Child with ID',
1503
- // Fallbacks are immediately committed in TestUtils version
1504
- // of act
1505
- // 'Child with ID',
1506
- // 'Child with ID',
1507
- 'Child One',
1508
- 'Child Two',
1509
- ]);
1510
-
1511
- expect(child1Ref.current).toBe(null);
1512
- expect(childWithIDRef.current).toEqual(
1513
- container.getElementsByTagName('span')[2],
1514
- );
1521
+ ReactTestUtils.act(() => {
1522
+ _setShow(true);
1523
1516
- expect(setShowRef.current).toEqual(
1517
- container.getElementsByTagName('span')[3],
1518
- );
1524
+ // State update should trigger the ID to update, which changes the props
1525
+ // of ChildWithID. This should cause ChildWithID to hydrate before Children
1526
1520
- expect(childWithIDRef.current.getAttribute('id')).toEqual(
1521
- setShowRef.current.getAttribute('aria-labelledby'),
1522
- );
1523
- expect(childWithIDRef.current.getAttribute('id')).not.toEqual(
1524
- serverId,
1525
- );
1526
- });
1527
+ expect(Scheduler).toFlushAndYieldThrough([
1528
+ 'Child with ID',
1529
+ // Fallbacks are immediately committed in TestUtils version
1530
+ // of act
1531
+ // 'Child with ID',
1532
+ // 'Child with ID',
1533
+ 'Child One',
1534
+ 'Child Two',
1535
+ ]);
1536
1528
- // Children hydrates after ChildWithID
1529
- expect(child1Ref.current).toBe(
1530
- container.getElementsByTagName('span')[0],
1537
+ expect(child1Ref.current).toBe(null);
1538
+ expect(childWithIDRef.current).toEqual(
1539
+ container.getElementsByTagName('span')[2],
1540
);
1541
1533
- Scheduler.unstable_flushAll();
1534
-
1535
- expect(Scheduler).toHaveYielded([]);
1536
- });
1542
+ expect(setShowRef.current).toEqual(
1543
+ container.getElementsByTagName('span')[3],
1544
+ );
1545
1538
- it('useOpaqueIdentifier: IDs match when part of the DOM tree is server rendered and part is client rendered', async () => {
1539
- let suspend = true;
1540
- let resolve;
1541
- const promise = new Promise(
1542
- resolvePromise => (resolve = resolvePromise),
1546
+ expect(childWithIDRef.current.getAttribute('id')).toEqual(
1547
+ setShowRef.current.getAttribute('aria-labelledby'),
1548
);
1549
+ expect(childWithIDRef.current.getAttribute('id')).not.toEqual(serverId);
1550
+ });
1551
1545
- function Child({text}) {
1546
- if (suspend) {
1547
- throw promise;
1548
- } else {
1549
- return text;
1550
- }
1551
- }
1552
+ // Children hydrates after ChildWithID
1553
+ expect(child1Ref.current).toBe(container.getElementsByTagName('span')[0]);
1554
1553
- function RenderedChild() {
1554
- useEffect(() => {
1555
- Scheduler.unstable_yieldValue('Child did commit');
1556
- });
1557
- return null;
1558
- }
1555
+ Scheduler.unstable_flushAll();
1556
1560
- function App() {
1561
- const id = useOpaqueIdentifier();
1562
- useEffect(() => {
1563
- Scheduler.unstable_yieldValue('Did commit');
1564
- });
1565
- return (
1566
- <div>
1567
- <div id={id}>Child One</div>
1568
- <RenderedChild />
1569
- <React.Suspense fallback={'Fallback'}>
1570
- <div id={id}>
1571
- <Child text="Child Two" />
1572
- </div>
1573
- </React.Suspense>
1574
- </div>
1575
- );
1576
- }
1557
+ expect(Scheduler).toHaveYielded([]);
1558
+ });
1559
1578
- const container = document.createElement('div');
1579
- document.body.appendChild(container);
1560
+ // @gate experimental
1561
+ it('useOpaqueIdentifier: IDs match when part of the DOM tree is server rendered and part is client rendered', async () => {
1562
+ let suspend = true;
1563
+ let resolve;
1564
+ const promise = new Promise(resolvePromise => (resolve = resolvePromise));
1565
1581
- container.innerHTML = ReactDOMServer.renderToString(<App />);
1566
+ function Child({text}) {
1567
+ if (suspend) {
1568
+ throw promise;
1569
+ } else {
1570
+ return text;
1571
+ }
1572
+ }
1573
1583
- suspend = true;
1584
- const root = ReactDOM.unstable_createRoot(container, {hydrate: true});
1585
- await ReactTestUtils.act(async () => {
1586
- root.render(<App />);
1587
- });
1588
- jest.runAllTimers();
1589
- expect(Scheduler).toHaveYielded(['Child did commit', 'Did commit']);
1590
- expect(Scheduler).toFlushAndYield([]);
1591
-
1592
- const serverId = container.children[0].children[0].getAttribute('id');
1593
- expect(container.children[0].children.length).toEqual(1);
1594
- expect(
1595
- container.children[0].children[0].getAttribute('id'),
1596
- ).not.toBeNull();
1597
-
1598
- await ReactTestUtils.act(async () => {
1599
- suspend = false;
1600
- resolve();
1601
- await promise;
1574
+ function RenderedChild() {
1575
+ useEffect(() => {
1576
+ Scheduler.unstable_yieldValue('Child did commit');
1577
});
1578
+ return null;
1579
+ }
1580
1604
- expect(Scheduler).toHaveYielded(['Child did commit', 'Did commit']);
1605
- expect(Scheduler).toFlushAndYield([]);
1606
- jest.runAllTimers();
1607
-
1608
- expect(container.children[0].children.length).toEqual(2);
1609
- expect(container.children[0].children[0].getAttribute('id')).toEqual(
1610
- container.children[0].children[1].getAttribute('id'),
1581
+ function App() {
1582
+ const id = useOpaqueIdentifier();
1583
+ useEffect(() => {
1584
+ Scheduler.unstable_yieldValue('Did commit');
1585
+ });
1586
+ return (
1587
+ <div>
1588
+ <div id={id}>Child One</div>
1589
+ <RenderedChild />
1590
+ <React.Suspense fallback={'Fallback'}>
1591
+ <div id={id}>
1592
+ <Child text="Child Two" />
1593
+ </div>
1594
+ </React.Suspense>
1595
+ </div>
1596
);
1612
- expect(
1613
- container.children[0].children[0].getAttribute('id'),
1614
- ).not.toEqual(serverId);
1615
- expect(
1616
- container.children[0].children[0].getAttribute('id'),
1617
- ).not.toBeNull();
1618
- });
1597
+ }
1598
1620
- it('useOpaqueIdentifier warn when there is a hydration error', async () => {
1621
- function Child({appId}) {
1622
- return <div aria-labelledby={appId} />;
1623
- }
1624
- function App() {
1625
- const id = useOpaqueIdentifier();
1626
- return <Child appId={id} />;
1627
- }
1599
+ const container = document.createElement('div');
1600
+ document.body.appendChild(container);
1601
1629
- const container = document.createElement('div');
1630
- document.body.appendChild(container);
1602
+ container.innerHTML = ReactDOMServer.renderToString(<App />);
1603
1632
- // This is the wrong HTML string
1633
- container.innerHTML = '<span></span>';
1634
- ReactDOM.unstable_createRoot(container, {hydrate: true}).render(
1635
- <App />,
1636
- );
1637
- expect(() => Scheduler.unstable_flushAll()).toErrorDev([
1638
- 'Warning: Expected server HTML to contain a matching <div> in <div>.',
1639
- ]);
1604
+ suspend = true;
1605
+ const root = ReactDOM.unstable_createRoot(container, {hydrate: true});
1606
+ await ReactTestUtils.act(async () => {
1607
+ root.render(<App />);
1608
});
1609
+ jest.runAllTimers();
1610
+ expect(Scheduler).toHaveYielded(['Child did commit', 'Did commit']);
1611
+ expect(Scheduler).toFlushAndYield([]);
1612
1642
- it('useOpaqueIdentifier: IDs match when part of the DOM tree is server rendered and part is client rendered', async () => {
1643
- let suspend = true;
1613
+ const serverId = container.children[0].children[0].getAttribute('id');
1614
+ expect(container.children[0].children.length).toEqual(1);
1615
+ expect(
1616
+ container.children[0].children[0].getAttribute('id'),
1617
+ ).not.toBeNull();
1618
1645
- function Child({text}) {
1646
- if (suspend) {
1647
- throw new Promise(() => {});
1648
- } else {
1649
- return text;
1650
- }
1651
- }
1619
+ await ReactTestUtils.act(async () => {
1620
+ suspend = false;
1621
+ resolve();
1622
+ await promise;
1623
+ });
1624
1653
- function RenderedChild() {
1654
- useEffect(() => {
1655
- Scheduler.unstable_yieldValue('Child did commit');
1656
- });
1657
- return null;
1658
- }
1625
+ expect(Scheduler).toHaveYielded(['Child did commit', 'Did commit']);
1626
+ expect(Scheduler).toFlushAndYield([]);
1627
+ jest.runAllTimers();
1628
1660
- function App() {
1661
- const id = useOpaqueIdentifier();
1662
- useEffect(() => {
1663
- Scheduler.unstable_yieldValue('Did commit');
1664
- });
1665
- return (
1666
- <div>
1667
- <div id={id}>Child One</div>
1668
- <RenderedChild />
1669
- <React.Suspense fallback={'Fallback'}>
1670
- <div id={id}>
1671
- <Child text="Child Two" />
1672
- </div>
1673
- </React.Suspense>
1674
- </div>
1675
- );
1676
- }
1629
+ expect(container.children[0].children.length).toEqual(2);
1630
+ expect(container.children[0].children[0].getAttribute('id')).toEqual(
1631
+ container.children[0].children[1].getAttribute('id'),
1632
+ );
1633
+ expect(container.children[0].children[0].getAttribute('id')).not.toEqual(
1634
+ serverId,
1635
+ );
1636
+ expect(
1637
+ container.children[0].children[0].getAttribute('id'),
1638
+ ).not.toBeNull();
1639
+ });
1640
1678
- const container = document.createElement('div');
1679
- document.body.appendChild(container);
1641
+ // @gate experimental
1642
+ it('useOpaqueIdentifier warn when there is a hydration error', async () => {
1643
+ function Child({appId}) {
1644
+ return <div aria-labelledby={appId} />;
1645
+ }
1646
+ function App() {
1647
+ const id = useOpaqueIdentifier();
1648
+ return <Child appId={id} />;
1649
+ }
1650
1681
- container.innerHTML = ReactDOMServer.renderToString(<App />);
1651
+ const container = document.createElement('div');
1652
+ document.body.appendChild(container);
1653
1683
- suspend = false;
1684
- const root = ReactDOM.unstable_createRoot(container, {hydrate: true});
1685
- await ReactTestUtils.act(async () => {
1686
- root.render(<App />);
1687
- });
1688
- jest.runAllTimers();
1689
- expect(Scheduler).toHaveYielded([
1690
- 'Child did commit',
1691
- 'Did commit',
1692
- 'Child did commit',
1693
- 'Did commit',
1694
- ]);
1695
- expect(Scheduler).toFlushAndYield([]);
1654
+ // This is the wrong HTML string
1655
+ container.innerHTML = '<span></span>';
1656
+ ReactDOM.unstable_createRoot(container, {hydrate: true}).render(<App />);
1657
+ expect(() => Scheduler.unstable_flushAll()).toErrorDev([
1658
+ 'Warning: Expected server HTML to contain a matching <div> in <div>.',
1659
+ ]);
1660
+ });
1661
1697
- expect(container.children[0].children.length).toEqual(2);
1698
- expect(container.children[0].children[0].getAttribute('id')).toEqual(
1699
- container.children[0].children[1].getAttribute('id'),
1700
- );
1701
- expect(
1702
- container.children[0].children[0].getAttribute('id'),
1703
- ).not.toBeNull();
1704
- });
1662
+ // @gate experimental
1663
+ it('useOpaqueIdentifier: IDs match when part of the DOM tree is server rendered and part is client rendered', async () => {
1664
+ let suspend = true;
1665
1706
- it('useOpaqueIdentifier warn when there is a hydration error', async () => {
1707
- function Child({appId}) {
1708
- return <div aria-labelledby={appId} />;
1709
- }
1710
- function App() {
1711
- const id = useOpaqueIdentifier();
1712
- return <Child appId={id} />;
1666
+ function Child({text}) {
1667
+ if (suspend) {
1668
+ throw new Promise(() => {});
1669
+ } else {
1670
+ return text;
1671
}
1672
+ }
1673
1715
- const container = document.createElement('div');
1716
- document.body.appendChild(container);
1674
+ function RenderedChild() {
1675
+ useEffect(() => {
1676
+ Scheduler.unstable_yieldValue('Child did commit');
1677
+ });
1678
+ return null;
1679
+ }
1680
1718
- // This is the wrong HTML string
1719
- container.innerHTML = '<span></span>';
1720
- ReactDOM.unstable_createRoot(container, {hydrate: true}).render(
1721
- <App />,
1681
+ function App() {
1682
+ const id = useOpaqueIdentifier();
1683
+ useEffect(() => {
1684
+ Scheduler.unstable_yieldValue('Did commit');
1685
+ });
1686
+ return (
1687
+ <div>
1688
+ <div id={id}>Child One</div>
1689
+ <RenderedChild />
1690
+ <React.Suspense fallback={'Fallback'}>
1691
+ <div id={id}>
1692
+ <Child text="Child Two" />
1693
+ </div>
1694
+ </React.Suspense>
1695
+ </div>
1696
);
1723
- expect(() => Scheduler.unstable_flushAll()).toErrorDev([
1724
- 'Warning: Expected server HTML to contain a matching <div> in <div>.',
1725
- ]);
1726
- });
1697
+ }
1698
1728
- it('useOpaqueIdentifier warns when there is a hydration error and we are using ID as a string', async () => {
1729
- function Child({appId}) {
1730
- return <div aria-labelledby={appId + ''} />;
1731
- }
1732
- function App() {
1733
- const id = useOpaqueIdentifier();
1734
- return <Child appId={id} />;
1735
- }
1699
+ const container = document.createElement('div');
1700
+ document.body.appendChild(container);
1701
1737
- const container = document.createElement('div');
1738
- document.body.appendChild(container);
1702
+ container.innerHTML = ReactDOMServer.renderToString(<App />);
1703
1740
- // This is the wrong HTML string
1741
- container.innerHTML = '<span></span>';
1742
- ReactDOM.unstable_createRoot(container, {hydrate: true}).render(
1743
- <App />,
1744
- );
1745
- expect(() => Scheduler.unstable_flushAll()).toErrorDev(
1746
- [
1747
- 'Warning: The object passed back from useOpaqueIdentifier is meant to be passed through to attributes only. Do not read the value directly.',
1748
- 'Warning: Did not expect server HTML to contain a <span> in <div>.',
1749
- ],
1750
- {withoutStack: 1},
1751
- );
1704
+ suspend = false;
1705
+ const root = ReactDOM.unstable_createRoot(container, {hydrate: true});
1706
+ await ReactTestUtils.act(async () => {
1707
+ root.render(<App />);
1708
});
1709
+ jest.runAllTimers();
1710
+ expect(Scheduler).toHaveYielded([
1711
+ 'Child did commit',
1712
+ 'Did commit',
1713
+ 'Child did commit',
1714
+ 'Did commit',
1715
+ ]);
1716
+ expect(Scheduler).toFlushAndYield([]);
1717
1754
- it('useOpaqueIdentifier warns when there is a hydration error and we are using ID as a string', async () => {
1755
- function Child({appId}) {
1756
- return <div aria-labelledby={appId + ''} />;
1757
- }
1758
- function App() {
1759
- const id = useOpaqueIdentifier();
1760
- return <Child appId={id} />;
1761
- }
1718
+ expect(container.children[0].children.length).toEqual(2);
1719
+ expect(container.children[0].children[0].getAttribute('id')).toEqual(
1720
+ container.children[0].children[1].getAttribute('id'),
1721
+ );
1722
+ expect(
1723
+ container.children[0].children[0].getAttribute('id'),
1724
+ ).not.toBeNull();
1725
+ });
1726
1763
- const container = document.createElement('div');
1764
- document.body.appendChild(container);
1727
+ // @gate experimental
1728
+ it('useOpaqueIdentifier warn when there is a hydration error', async () => {
1729
+ function Child({appId}) {
1730
+ return <div aria-labelledby={appId} />;
1731
+ }
1732
+ function App() {
1733
+ const id = useOpaqueIdentifier();
1734
+ return <Child appId={id} />;
1735
+ }
1736
1766
- // This is the wrong HTML string
1767
- container.innerHTML = '<span></span>';
1768
- ReactDOM.unstable_createRoot(container, {hydrate: true}).render(
1769
- <App />,
1770
- );
1771
- expect(() => Scheduler.unstable_flushAll()).toErrorDev(
1772
- [
1773
- 'Warning: The object passed back from useOpaqueIdentifier is meant to be passed through to attributes only. Do not read the value directly.',
1774
- 'Warning: Did not expect server HTML to contain a <span> in <div>.',
1775
- ],
1776
- {withoutStack: 1},
1777
- );
1778
- });
1737
+ const container = document.createElement('div');
1738
+ document.body.appendChild(container);
1739
1780
- it('useOpaqueIdentifier warns if you try to use the result as a string in a child component', async () => {
1781
- function Child({appId}) {
1782
- return <div aria-labelledby={appId + ''} />;
1783
- }
1784
- function App() {
1785
- const id = useOpaqueIdentifier();
1786
- return <Child appId={id} />;
1787
- }
1740
+ // This is the wrong HTML string
1741
+ container.innerHTML = '<span></span>';
1742
+ ReactDOM.unstable_createRoot(container, {hydrate: true}).render(<App />);
1743
+ expect(() => Scheduler.unstable_flushAll()).toErrorDev([
1744
+ 'Warning: Expected server HTML to contain a matching <div> in <div>.',
1745
+ ]);
1746
+ });
1747
1789
- const container = document.createElement('div');
1790
- document.body.appendChild(container);
1748
+ // @gate experimental
1749
+ it('useOpaqueIdentifier warns when there is a hydration error and we are using ID as a string', async () => {
1750
+ function Child({appId}) {
1751
+ return <div aria-labelledby={appId + ''} />;
1752
+ }
1753
+ function App() {
1754
+ const id = useOpaqueIdentifier();
1755
+ return <Child appId={id} />;
1756
+ }
1757
1792
- container.innerHTML = ReactDOMServer.renderToString(<App />);
1793
- ReactDOM.unstable_createRoot(container, {hydrate: true}).render(
1794
- <App />,
1795
- );
1796
- expect(() => Scheduler.unstable_flushAll()).toErrorDev(
1797
- [
1798
- 'Warning: The object passed back from useOpaqueIdentifier is meant to be passed through to attributes only. Do not read the value directly.',
1799
- 'Warning: Did not expect server HTML to contain a <div> in <div>.',
1800
- ],
1801
- {withoutStack: 1},
1802
- );
1803
- });
1758
+ const container = document.createElement('div');
1759
+ document.body.appendChild(container);
1760
+
1761
+ // This is the wrong HTML string
1762
+ container.innerHTML = '<span></span>';
1763
+ ReactDOM.unstable_createRoot(container, {hydrate: true}).render(<App />);
1764
+ expect(() => Scheduler.unstable_flushAll()).toErrorDev(
1765
+ [
1766
+ 'Warning: The object passed back from useOpaqueIdentifier is meant to be passed through to attributes only. Do not read the value directly.',
1767
+ 'Warning: Did not expect server HTML to contain a <span> in <div>.',
1768
+ ],
1769
+ {withoutStack: 1},
1770
+ );
1771
+ });
1772
1805
- it('useOpaqueIdentifier warns if you try to use the result as a string', async () => {
1806
- function App() {
1807
- const id = useOpaqueIdentifier();
1808
- return <div aria-labelledby={id + ''} />;
1809
- }
1773
+ // @gate experimental
1774
+ it('useOpaqueIdentifier warns when there is a hydration error and we are using ID as a string', async () => {
1775
+ function Child({appId}) {
1776
+ return <div aria-labelledby={appId + ''} />;
1777
+ }
1778
+ function App() {
1779
+ const id = useOpaqueIdentifier();
1780
+ return <Child appId={id} />;
1781
+ }
1782
1811
- const container = document.createElement('div');
1812
- document.body.appendChild(container);
1783
+ const container = document.createElement('div');
1784
+ document.body.appendChild(container);
1785
+
1786
+ // This is the wrong HTML string
1787
+ container.innerHTML = '<span></span>';
1788
+ ReactDOM.unstable_createRoot(container, {hydrate: true}).render(<App />);
1789
+ expect(() => Scheduler.unstable_flushAll()).toErrorDev(
1790
+ [
1791
+ 'Warning: The object passed back from useOpaqueIdentifier is meant to be passed through to attributes only. Do not read the value directly.',
1792
+ 'Warning: Did not expect server HTML to contain a <span> in <div>.',
1793
+ ],
1794
+ {withoutStack: 1},
1795
+ );
1796
+ });
1797
1814
- container.innerHTML = ReactDOMServer.renderToString(<App />);
1815
- ReactDOM.unstable_createRoot(container, {hydrate: true}).render(
1816
- <App />,
1817
- );
1818
- expect(() => Scheduler.unstable_flushAll()).toErrorDev(
1819
- [
1820
- 'Warning: The object passed back from useOpaqueIdentifier is meant to be passed through to attributes only. Do not read the value directly.',
1821
- 'Warning: Did not expect server HTML to contain a <div> in <div>.',
1822
- ],
1823
- {withoutStack: 1},
1824
- );
1825
- });
1798
+ // @gate experimental
1799
+ it('useOpaqueIdentifier warns if you try to use the result as a string in a child component', async () => {
1800
+ function Child({appId}) {
1801
+ return <div aria-labelledby={appId + ''} />;
1802
+ }
1803
+ function App() {
1804
+ const id = useOpaqueIdentifier();
1805
+ return <Child appId={id} />;
1806
+ }
1807
1827
- it('useOpaqueIdentifier warns if you try to use the result as a string in a child component wrapped in a Suspense', async () => {
1828
- function Child({appId}) {
1829
- return <div aria-labelledby={appId + ''} />;
1830
- }
1831
- function App() {
1832
- const id = useOpaqueIdentifier();
1833
- return (
1834
- <React.Suspense fallback={null}>
1835
- <Child appId={id} />
1836
- </React.Suspense>
1837
- );
1838
- }
1808
+ const container = document.createElement('div');
1809
+ document.body.appendChild(container);
1810
+
1811
+ container.innerHTML = ReactDOMServer.renderToString(<App />);
1812
+ ReactDOM.unstable_createRoot(container, {hydrate: true}).render(<App />);
1813
+ expect(() => Scheduler.unstable_flushAll()).toErrorDev(
1814
+ [
1815
+ 'Warning: The object passed back from useOpaqueIdentifier is meant to be passed through to attributes only. Do not read the value directly.',
1816
+ 'Warning: Did not expect server HTML to contain a <div> in <div>.',
1817
+ ],
1818
+ {withoutStack: 1},
1819
+ );
1820
+ });
1821
1840
- const container = document.createElement('div');
1841
- document.body.appendChild(container);
1822
+ // @gate experimental
1823
+ it('useOpaqueIdentifier warns if you try to use the result as a string', async () => {
1824
+ function App() {
1825
+ const id = useOpaqueIdentifier();
1826
+ return <div aria-labelledby={id + ''} />;
1827
+ }
1828
1843
- container.innerHTML = ReactDOMServer.renderToString(<App />);
1829
+ const container = document.createElement('div');
1830
+ document.body.appendChild(container);
1831
+
1832
+ container.innerHTML = ReactDOMServer.renderToString(<App />);
1833
+ ReactDOM.unstable_createRoot(container, {hydrate: true}).render(<App />);
1834
+ expect(() => Scheduler.unstable_flushAll()).toErrorDev(
1835
+ [
1836
+ 'Warning: The object passed back from useOpaqueIdentifier is meant to be passed through to attributes only. Do not read the value directly.',
1837
+ 'Warning: Did not expect server HTML to contain a <div> in <div>.',
1838
+ ],
1839
+ {withoutStack: 1},
1840
+ );
1841
+ });
1842
1845
- ReactDOM.unstable_createRoot(container, {hydrate: true}).render(
1846
- <App />,
1843
+ // @gate experimental
1844
+ it('useOpaqueIdentifier warns if you try to use the result as a string in a child component wrapped in a Suspense', async () => {
1845
+ function Child({appId}) {
1846
+ return <div aria-labelledby={appId + ''} />;
1847
+ }
1848
+ function App() {
1849
+ const id = useOpaqueIdentifier();
1850
+ return (
1851
+ <React.Suspense fallback={null}>
1852
+ <Child appId={id} />
1853
+ </React.Suspense>
1854
);
1855
+ }
1856
1849
- if (gate(flags => flags.deferRenderPhaseUpdateToNextBatch)) {
1850
- expect(() => Scheduler.unstable_flushAll()).toErrorDev([
1851
- 'The object passed back from useOpaqueIdentifier is meant to be passed through to attributes only. ' +
1852
- 'Do not read the value directly.',
1853
- ]);
1854
- } else {
1855
- // This error isn't surfaced to the user; only the warning is.
1856
- // The error is just the mechanism that restarts the render.
1857
- expect(() =>
1858
- expect(() => Scheduler.unstable_flushAll()).toThrow(
1859
- 'The object passed back from useOpaqueIdentifier is meant to be passed through to attributes only. ' +
1860
- 'Do not read the value directly.',
1861
- ),
1862
- ).toErrorDev([
1857
+ const container = document.createElement('div');
1858
+ document.body.appendChild(container);
1859
+
1860
+ container.innerHTML = ReactDOMServer.renderToString(<App />);
1861
+
1862
+ ReactDOM.unstable_createRoot(container, {hydrate: true}).render(<App />);
1863
+
1864
+ if (gate(flags => flags.deferRenderPhaseUpdateToNextBatch)) {
1865
+ expect(() => Scheduler.unstable_flushAll()).toErrorDev([
1866
+ 'The object passed back from useOpaqueIdentifier is meant to be passed through to attributes only. ' +
1867
+ 'Do not read the value directly.',
1868
+ ]);
1869
+ } else {
1870
+ // This error isn't surfaced to the user; only the warning is.
1871
+ // The error is just the mechanism that restarts the render.
1872
+ expect(() =>
1873
+ expect(() => Scheduler.unstable_flushAll()).toThrow(
1874
'The object passed back from useOpaqueIdentifier is meant to be passed through to attributes only. ' +
1875
'Do not read the value directly.',
1865
- ]);
1866
- }
1867
- });
1876
+ ),
1877
+ ).toErrorDev([
1878
+ 'The object passed back from useOpaqueIdentifier is meant to be passed through to attributes only. ' +
1879
+ 'Do not read the value directly.',
1880
+ ]);
1881
+ }
1882
+ });
1883
1869
- it('useOpaqueIdentifier warns if you try to add the result as a number in a child component wrapped in a Suspense', async () => {
1870
- function Child({appId}) {
1871
- return <div aria-labelledby={+appId} />;
1872
- }
1873
- function App() {
1874
- const [show] = useState(false);
1875
- const id = useOpaqueIdentifier();
1876
- return (
1877
- <React.Suspense fallback={null}>
1878
- {show && <div id={id} />}
1879
- <Child appId={id} />
1880
- </React.Suspense>
1881
- );
1882
- }
1884
+ // @gate experimental
1885
+ it('useOpaqueIdentifier warns if you try to add the result as a number in a child component wrapped in a Suspense', async () => {
1886
+ function Child({appId}) {
1887
+ return <div aria-labelledby={+appId} />;
1888
+ }
1889
+ function App() {
1890
+ const [show] = useState(false);
1891
+ const id = useOpaqueIdentifier();
1892
+ return (
1893
+ <React.Suspense fallback={null}>
1894
+ {show && <div id={id} />}
1895
+ <Child appId={id} />
1896
+ </React.Suspense>
1897
+ );
1898
+ }
1899
1884
- const container = document.createElement('div');
1885
- document.body.appendChild(container);
1900
+ const container = document.createElement('div');
1901
+ document.body.appendChild(container);
1902
1887
- container.innerHTML = ReactDOMServer.renderToString(<App />);
1903
+ container.innerHTML = ReactDOMServer.renderToString(<App />);
1904
1889
- ReactDOM.unstable_createRoot(container, {hydrate: true}).render(
1890
- <App />,
1891
- );
1905
+ ReactDOM.unstable_createRoot(container, {hydrate: true}).render(<App />);
1906
1893
- if (gate(flags => flags.deferRenderPhaseUpdateToNextBatch)) {
1894
- expect(() => Scheduler.unstable_flushAll()).toErrorDev([
1895
- 'The object passed back from useOpaqueIdentifier is meant to be passed through to attributes only. ' +
1896
- 'Do not read the value directly.',
1897
- ]);
1898
- } else {
1899
- // This error isn't surfaced to the user; only the warning is.
1900
- // The error is just the mechanism that restarts the render.
1901
- expect(() =>
1902
- expect(() => Scheduler.unstable_flushAll()).toThrow(
1903
- 'The object passed back from useOpaqueIdentifier is meant to be passed through to attributes only. ' +
1904
- 'Do not read the value directly.',
1905
- ),
1906
- ).toErrorDev([
1907
+ if (gate(flags => flags.deferRenderPhaseUpdateToNextBatch)) {
1908
+ expect(() => Scheduler.unstable_flushAll()).toErrorDev([
1909
+ 'The object passed back from useOpaqueIdentifier is meant to be passed through to attributes only. ' +
1910
+ 'Do not read the value directly.',
1911
+ ]);
1912
+ } else {
1913
+ // This error isn't surfaced to the user; only the warning is.
1914
+ // The error is just the mechanism that restarts the render.
1915
+ expect(() =>
1916
+ expect(() => Scheduler.unstable_flushAll()).toThrow(
1917
'The object passed back from useOpaqueIdentifier is meant to be passed through to attributes only. ' +
1918
'Do not read the value directly.',
1909
- ]);
1910
- }
1911
- });
1912
-
1913
- it('useOpaqueIdentifier with two opaque identifiers on the same page', () => {
1914
- let _setShow;
1919
+ ),
1920
+ ).toErrorDev([
1921
+ 'The object passed back from useOpaqueIdentifier is meant to be passed through to attributes only. ' +
1922
+ 'Do not read the value directly.',
1923
+ ]);
1924
+ }
1925
+ });
1926
1916
- function App() {
1917
- const id1 = useOpaqueIdentifier();
1918
- const id2 = useOpaqueIdentifier();
1919
- const [show, setShow] = useState(true);
1920
- _setShow = setShow;
1927
+ // @gate experimental
1928
+ it('useOpaqueIdentifier with two opaque identifiers on the same page', () => {
1929
+ let _setShow;
1930
1922
- return (
1923
- <div>
1924
- <React.Suspense fallback={null}>
1925
- {show ? (
1926
- <span id={id1}>{'Child'}</span>
1927
- ) : (
1928
- <span id={id2}>{'Child'}</span>
1929
- )}
1930
- </React.Suspense>
1931
- <span aria-labelledby={id1}>{'test'}</span>
1932
- </div>
1933
- );
1934
- }
1931
+ function App() {
1932
+ const id1 = useOpaqueIdentifier();
1933
+ const id2 = useOpaqueIdentifier();
1934
+ const [show, setShow] = useState(true);
1935
+ _setShow = setShow;
1936
1936
- const container = document.createElement('div');
1937
- document.body.appendChild(container);
1937
+ return (
1938
+ <div>
1939
+ <React.Suspense fallback={null}>
1940
+ {show ? (
1941
+ <span id={id1}>{'Child'}</span>
1942
+ ) : (
1943
+ <span id={id2}>{'Child'}</span>
1944
+ )}
1945
+ </React.Suspense>
1946
+ <span aria-labelledby={id1}>{'test'}</span>
1947
+ </div>
1948
+ );
1949
+ }
1950
1939
- container.innerHTML = ReactDOMServer.renderToString(<App />);
1951
+ const container = document.createElement('div');
1952
+ document.body.appendChild(container);
1953
1941
- const serverID = container
1942
- .getElementsByTagName('span')[0]
1943
- .getAttribute('id');
1944
- expect(serverID).not.toBeNull();
1945
- expect(
1946
- container
1947
- .getElementsByTagName('span')[1]
1948
- .getAttribute('aria-labelledby'),
1949
- ).toEqual(serverID);
1954
+ container.innerHTML = ReactDOMServer.renderToString(<App />);
1955
1951
- ReactDOM.unstable_createRoot(container, {hydrate: true}).render(
1952
- <App />,
1953
- );
1954
- jest.runAllTimers();
1955
- expect(Scheduler).toHaveYielded([]);
1956
- expect(Scheduler).toFlushAndYield([]);
1956
+ const serverID = container
1957
+ .getElementsByTagName('span')[0]
1958
+ .getAttribute('id');
1959
+ expect(serverID).not.toBeNull();
1960
+ expect(
1961
+ container
1962
+ .getElementsByTagName('span')[1]
1963
+ .getAttribute('aria-labelledby'),
1964
+ ).toEqual(serverID);
1965
1958
- ReactTestUtils.act(() => {
1959
- _setShow(false);
1960
- });
1966
+ ReactDOM.unstable_createRoot(container, {hydrate: true}).render(<App />);
1967
+ jest.runAllTimers();
1968
+ expect(Scheduler).toHaveYielded([]);
1969
+ expect(Scheduler).toFlushAndYield([]);
1970
1962
- expect(
1963
- container
1964
- .getElementsByTagName('span')[1]
1965
- .getAttribute('aria-labelledby'),
1966
- ).toEqual(serverID);
1967
- expect(
1968
- container.getElementsByTagName('span')[0].getAttribute('id'),
1969
- ).not.toEqual(serverID);
1970
- expect(
1971
- container.getElementsByTagName('span')[0].getAttribute('id'),
1972
- ).not.toBeNull();
1971
+ ReactTestUtils.act(() => {
1972
+ _setShow(false);
1973
});
1974
+
1975
+ expect(
1976
+ container
1977
+ .getElementsByTagName('span')[1]
1978
+ .getAttribute('aria-labelledby'),
1979
+ ).toEqual(serverID);
1980
+ expect(
1981
+ container.getElementsByTagName('span')[0].getAttribute('id'),
1982
+ ).not.toEqual(serverID);
1983
+ expect(
1984
+ container.getElementsByTagName('span')[0].getAttribute('id'),
1985
+ ).not.toBeNull();
1986
});
1975
- }
1987
+ });
1988
});
packages/react-dom/src/__tests__/utils/ReactDOMServerIntegrationTestUtils.js
+4
-2
@@ -144,9 +144,11 @@ module.exports = function(initModules) {
144
async function renderIntoStream(reactElement, errorCount = 0) {
145
return await expectErrors(
146
() =>
147
- new Promise(resolve => {
147
+ new Promise((resolve, reject) => {
148
const writable = new DrainWritable();
149
- ReactDOMServer.renderToNodeStream(reactElement).pipe(writable);
149
+ const s = ReactDOMServer.renderToNodeStream(reactElement);
150
+ s.on('error', e => reject(e));
151
+ s.pipe(writable);
152
writable.on('finish', () => resolve(writable.buffer));
153
}),
154
errorCount,