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