@@ -2,6 +2,7 @@ import { describe, it } from 'mocha';
22
33import { expectJSON } from '../../__testUtils__/expectJSON' ;
44
5+ import { invariant } from '../../jsutils/invariant' ;
56import { isAsyncIterable } from '../../jsutils/isAsyncIterable' ;
67
78import type { DocumentNode } from '../../language/ast' ;
@@ -161,6 +162,37 @@ const query = new GraphQLObjectType({
161162 yield await Promise . resolve ( { string : friends [ 1 ] . name } ) ;
162163 } ,
163164 } ,
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+ } ,
164196 asyncIterableListDelayedClose : {
165197 type : new GraphQLList ( friendType ) ,
166198 async * resolve ( ) {
@@ -1188,4 +1220,181 @@ describe('Execute: stream directive', () => {
11881220 } ,
11891221 ] ) ;
11901222 } ) ;
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+ } ) ;
11911400} ) ;
0 commit comments