@@ -3,6 +3,7 @@ import { describe, it } from 'mocha';
33import { expectJSON } from '../../__testUtils__/expectJSON' ;
44import { resolveOnNextTick } from '../../__testUtils__/resolveOnNextTick' ;
55
6+ import { invariant } from '../../jsutils/invariant' ;
67import { isAsyncIterable } from '../../jsutils/isAsyncIterable' ;
78
89import type { DocumentNode } from '../../language/ast' ;
@@ -162,6 +163,37 @@ const query = new GraphQLObjectType({
162163 yield await Promise . resolve ( { string : friends [ 1 ] . name } ) ;
163164 } ,
164165 } ,
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+ } ,
165197 asyncIterableListDelayedClose : {
166198 type : new GraphQLList ( friendType ) ,
167199 async * resolve ( ) {
@@ -1189,4 +1221,181 @@ describe('Execute: stream directive', () => {
11891221 } ,
11901222 ] ) ;
11911223 } ) ;
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+ } ) ;
11921401} ) ;
0 commit comments