@@ -2,6 +2,7 @@ import { describe, it } from 'mocha';
2
2
3
3
import { expectJSON } from '../../__testUtils__/expectJSON' ;
4
4
5
+ import { invariant } from '../../jsutils/invariant' ;
5
6
import { isAsyncIterable } from '../../jsutils/isAsyncIterable' ;
6
7
7
8
import type { DocumentNode } from '../../language/ast' ;
@@ -161,6 +162,37 @@ const query = new GraphQLObjectType({
161
162
yield await Promise . resolve ( { string : friends [ 1 ] . name } ) ;
162
163
} ,
163
164
} ,
165
+ asyncIterableListDelayed : {
166
+ type : new GraphQLList ( friendType ) ,
167
+ async * resolve ( ) {
168
+ for ( const friend of friends ) {
169
+ // pause an additional ms before yielding to allow time
170
+ // for tests to return or throw before next value is processed.
171
+ // eslint-disable-next-line no-await-in-loop
172
+ await new Promise ( ( r ) => setTimeout ( r , 1 ) ) ;
173
+ yield friend ; /* c8 ignore start */
174
+ // Not reachable, early return
175
+ }
176
+ } /* c8 ignore stop */ ,
177
+ } ,
178
+ asyncIterableListNoReturn : {
179
+ type : new GraphQLList ( friendType ) ,
180
+ resolve ( ) {
181
+ let i = 0 ;
182
+ return {
183
+ [ Symbol . asyncIterator ] : ( ) => ( {
184
+ async next ( ) {
185
+ const friend = friends [ i ++ ] ;
186
+ if ( friend ) {
187
+ await new Promise ( ( r ) => setTimeout ( r , 1 ) ) ;
188
+ return { value : friend , done : false } ;
189
+ }
190
+ return { value : undefined , done : true } ;
191
+ } ,
192
+ } ) ,
193
+ } ;
194
+ } ,
195
+ } ,
164
196
asyncIterableListDelayedClose : {
165
197
type : new GraphQLList ( friendType ) ,
166
198
async * resolve ( ) {
@@ -1188,4 +1220,181 @@ describe('Execute: stream directive', () => {
1188
1220
} ,
1189
1221
] ) ;
1190
1222
} ) ;
1223
+ it ( 'Returns underlying async iterables when dispatcher is returned' , async ( ) => {
1224
+ const document = parse ( `
1225
+ query {
1226
+ asyncIterableListDelayed @stream(initialCount: 1) {
1227
+ name
1228
+ id
1229
+ }
1230
+ }
1231
+ ` ) ;
1232
+ const schema = new GraphQLSchema ( { query } ) ;
1233
+
1234
+ const executeResult = await execute ( { schema, document, rootValue : { } } ) ;
1235
+ invariant ( isAsyncIterable ( executeResult ) ) ;
1236
+ const iterator = executeResult [ Symbol . asyncIterator ] ( ) ;
1237
+
1238
+ const result1 = await iterator . next ( ) ;
1239
+ expectJSON ( result1 ) . toDeepEqual ( {
1240
+ done : false ,
1241
+ value : {
1242
+ data : {
1243
+ asyncIterableListDelayed : [
1244
+ {
1245
+ id : '1' ,
1246
+ name : 'Luke' ,
1247
+ } ,
1248
+ ] ,
1249
+ } ,
1250
+ hasNext : true ,
1251
+ } ,
1252
+ } ) ;
1253
+
1254
+ const returnPromise = iterator . return ( ) ;
1255
+
1256
+ // this result had started processing before return was called
1257
+ const result2 = await iterator . next ( ) ;
1258
+ expectJSON ( result2 ) . toDeepEqual ( {
1259
+ done : false ,
1260
+ value : {
1261
+ data : [
1262
+ {
1263
+ id : '2' ,
1264
+ name : 'Han' ,
1265
+ } ,
1266
+ ] ,
1267
+ hasNext : true ,
1268
+ path : [ 'asyncIterableListDelayed' , 1 ] ,
1269
+ } ,
1270
+ } ) ;
1271
+
1272
+ // third result is not returned because async iterator has returned
1273
+ const result3 = await iterator . next ( ) ;
1274
+ expectJSON ( result3 ) . toDeepEqual ( {
1275
+ done : true ,
1276
+ value : undefined ,
1277
+ } ) ;
1278
+ await returnPromise ;
1279
+ } ) ;
1280
+ it ( 'Can return async iterable when underlying iterable does not have a return method' , async ( ) => {
1281
+ const document = parse ( `
1282
+ query {
1283
+ asyncIterableListNoReturn @stream(initialCount: 1) {
1284
+ name
1285
+ id
1286
+ }
1287
+ }
1288
+ ` ) ;
1289
+ const schema = new GraphQLSchema ( { query } ) ;
1290
+
1291
+ const executeResult = await execute ( { schema, document, rootValue : { } } ) ;
1292
+ invariant ( isAsyncIterable ( executeResult ) ) ;
1293
+ const iterator = executeResult [ Symbol . asyncIterator ] ( ) ;
1294
+
1295
+ const result1 = await iterator . next ( ) ;
1296
+ expectJSON ( result1 ) . toDeepEqual ( {
1297
+ done : false ,
1298
+ value : {
1299
+ data : {
1300
+ asyncIterableListNoReturn : [
1301
+ {
1302
+ id : '1' ,
1303
+ name : 'Luke' ,
1304
+ } ,
1305
+ ] ,
1306
+ } ,
1307
+ hasNext : true ,
1308
+ } ,
1309
+ } ) ;
1310
+
1311
+ const returnPromise = iterator . return ( ) ;
1312
+
1313
+ // this result had started processing before return was called
1314
+ const result2 = await iterator . next ( ) ;
1315
+ expectJSON ( result2 ) . toDeepEqual ( {
1316
+ done : false ,
1317
+ value : {
1318
+ data : [
1319
+ {
1320
+ id : '2' ,
1321
+ name : 'Han' ,
1322
+ } ,
1323
+ ] ,
1324
+ hasNext : true ,
1325
+ path : [ 'asyncIterableListNoReturn' , 1 ] ,
1326
+ } ,
1327
+ } ) ;
1328
+
1329
+ // third result is not returned because async iterator has returned
1330
+ const result3 = await iterator . next ( ) ;
1331
+ expectJSON ( result3 ) . toDeepEqual ( {
1332
+ done : true ,
1333
+ value : undefined ,
1334
+ } ) ;
1335
+ await returnPromise ;
1336
+ } ) ;
1337
+ it ( 'Returns underlying async iterables when dispatcher is thrown' , async ( ) => {
1338
+ const document = parse ( `
1339
+ query {
1340
+ asyncIterableListDelayed @stream(initialCount: 1) {
1341
+ name
1342
+ id
1343
+ }
1344
+ }
1345
+ ` ) ;
1346
+ const schema = new GraphQLSchema ( { query } ) ;
1347
+
1348
+ const executeResult = await execute ( { schema, document, rootValue : { } } ) ;
1349
+ invariant ( isAsyncIterable ( executeResult ) ) ;
1350
+ const iterator = executeResult [ Symbol . asyncIterator ] ( ) ;
1351
+
1352
+ const result1 = await iterator . next ( ) ;
1353
+ expectJSON ( result1 ) . toDeepEqual ( {
1354
+ done : false ,
1355
+ value : {
1356
+ data : {
1357
+ asyncIterableListDelayed : [
1358
+ {
1359
+ id : '1' ,
1360
+ name : 'Luke' ,
1361
+ } ,
1362
+ ] ,
1363
+ } ,
1364
+ hasNext : true ,
1365
+ } ,
1366
+ } ) ;
1367
+
1368
+ const throwPromise = iterator . throw ( new Error ( 'bad' ) ) ;
1369
+
1370
+ // this result had started processing before return was called
1371
+ const result2 = await iterator . next ( ) ;
1372
+ expectJSON ( result2 ) . toDeepEqual ( {
1373
+ done : false ,
1374
+ value : {
1375
+ data : [
1376
+ {
1377
+ id : '2' ,
1378
+ name : 'Han' ,
1379
+ } ,
1380
+ ] ,
1381
+ hasNext : true ,
1382
+ path : [ 'asyncIterableListDelayed' , 1 ] ,
1383
+ } ,
1384
+ } ) ;
1385
+
1386
+ // third result is not returned because async iterator has returned
1387
+ const result3 = await iterator . next ( ) ;
1388
+ expectJSON ( result3 ) . toDeepEqual ( {
1389
+ done : true ,
1390
+ value : undefined ,
1391
+ } ) ;
1392
+ try {
1393
+ await throwPromise ; /* c8 ignore start */
1394
+ // Not reachable, always throws
1395
+ /* c8 ignore stop */
1396
+ } catch ( e ) {
1397
+ // ignore error
1398
+ }
1399
+ } ) ;
1191
1400
} ) ;
0 commit comments