@@ -1215,4 +1215,226 @@ describe('Execute: stream directive', () => {
12151215 done : true ,
12161216 } ) ;
12171217 } ) ;
1218+ it ( 'Returns underlying async iterables when returned generator is returned' , async ( ) => {
1219+ let returned = false ;
1220+ let index = 0 ;
1221+ const iterable = {
1222+ [ Symbol . asyncIterator ] : ( ) => ( {
1223+ next : ( ) => {
1224+ const friend = friends [ index ++ ] ;
1225+ if ( ! friend ) {
1226+ return Promise . resolve ( { done : true , value : undefined } ) ;
1227+ }
1228+ return Promise . resolve ( { done : false , value : friend } ) ;
1229+ } ,
1230+ return : ( ) => {
1231+ returned = true ;
1232+ } ,
1233+ } ) ,
1234+ } ;
1235+
1236+ const document = parse ( `
1237+ query {
1238+ friendList @stream(initialCount: 1) {
1239+ id
1240+ ... @defer {
1241+ name
1242+ }
1243+ }
1244+ }
1245+ ` ) ;
1246+
1247+ const executeResult = await execute ( {
1248+ schema,
1249+ document,
1250+ rootValue : {
1251+ friendList : iterable ,
1252+ } ,
1253+ } ) ;
1254+ assert ( isAsyncIterable ( executeResult ) ) ;
1255+ const iterator = executeResult [ Symbol . asyncIterator ] ( ) ;
1256+
1257+ const result1 = await iterator . next ( ) ;
1258+ expectJSON ( result1 ) . toDeepEqual ( {
1259+ done : false ,
1260+ value : {
1261+ data : {
1262+ friendList : [ { id : '1' } ] ,
1263+ } ,
1264+ hasNext : true ,
1265+ } ,
1266+ } ) ;
1267+ const returnPromise = iterator . return ( ) ;
1268+
1269+ // these results had started processing before return was called
1270+ const result2 = await iterator . next ( ) ;
1271+ expectJSON ( result2 ) . toDeepEqual ( {
1272+ done : false ,
1273+ value : {
1274+ incremental : [
1275+ {
1276+ data : { name : 'Luke' } ,
1277+ path : [ 'friendList' , 0 ] ,
1278+ } ,
1279+ ] ,
1280+ hasNext : true ,
1281+ } ,
1282+ } ) ;
1283+ const result3 = await iterator . next ( ) ;
1284+ expectJSON ( result3 ) . toDeepEqual ( {
1285+ done : true ,
1286+ value : undefined ,
1287+ } ) ;
1288+ await returnPromise ;
1289+ assert ( returned ) ;
1290+ } ) ;
1291+ it ( 'Can return async iterable when underlying iterable does not have a return method' , async ( ) => {
1292+ let index = 0 ;
1293+ const iterable = {
1294+ [ Symbol . asyncIterator ] : ( ) => ( {
1295+ next : ( ) => {
1296+ const friend = friends [ index ++ ] ;
1297+ if ( ! friend ) {
1298+ return Promise . resolve ( { done : true , value : undefined } ) ;
1299+ }
1300+ return Promise . resolve ( { done : false , value : friend } ) ;
1301+ } ,
1302+ } ) ,
1303+ } ;
1304+
1305+ const document = parse ( `
1306+ query {
1307+ friendList @stream(initialCount: 1) {
1308+ name
1309+ id
1310+ }
1311+ }
1312+ ` ) ;
1313+
1314+ const executeResult = await execute ( {
1315+ schema,
1316+ document,
1317+ rootValue : {
1318+ friendList : iterable ,
1319+ } ,
1320+ } ) ;
1321+ assert ( isAsyncIterable ( executeResult ) ) ;
1322+ const iterator = executeResult [ Symbol . asyncIterator ] ( ) ;
1323+
1324+ const result1 = await iterator . next ( ) ;
1325+ expectJSON ( result1 ) . toDeepEqual ( {
1326+ done : false ,
1327+ value : {
1328+ data : {
1329+ friendList : [ { id : '1' , name : 'Luke' } ] ,
1330+ } ,
1331+ hasNext : true ,
1332+ } ,
1333+ } ) ;
1334+
1335+ const returnPromise = iterator . return ( ) ;
1336+
1337+ // this result had started processing before return was called
1338+ const result2 = await iterator . next ( ) ;
1339+ expectJSON ( result2 ) . toDeepEqual ( {
1340+ done : false ,
1341+ value : {
1342+ incremental : [
1343+ {
1344+ items : [ { id : '2' , name : 'Han' } ] ,
1345+ path : [ 'friendList' , 1 ] ,
1346+ } ,
1347+ ] ,
1348+ hasNext : true ,
1349+ } ,
1350+ } ) ;
1351+
1352+ // third result is not returned because async iterator has returned
1353+ const result3 = await iterator . next ( ) ;
1354+ expectJSON ( result3 ) . toDeepEqual ( {
1355+ done : true ,
1356+ value : undefined ,
1357+ } ) ;
1358+ await returnPromise ;
1359+ } ) ;
1360+ it ( 'Returns underlying async iterables when returned generator is thrown' , async ( ) => {
1361+ let index = 0 ;
1362+ let returned = false ;
1363+ const iterable = {
1364+ [ Symbol . asyncIterator ] : ( ) => ( {
1365+ next : ( ) => {
1366+ const friend = friends [ index ++ ] ;
1367+ if ( ! friend ) {
1368+ return Promise . resolve ( { done : true , value : undefined } ) ;
1369+ }
1370+ return Promise . resolve ( { done : false , value : friend } ) ;
1371+ } ,
1372+ return : ( ) => {
1373+ returned = true ;
1374+ } ,
1375+ } ) ,
1376+ } ;
1377+ const document = parse ( `
1378+ query {
1379+ friendList @stream(initialCount: 1) {
1380+ ... @defer {
1381+ name
1382+ }
1383+ id
1384+ }
1385+ }
1386+ ` ) ;
1387+ const executeResult = await execute ( {
1388+ schema,
1389+ document,
1390+ rootValue : {
1391+ friendList : iterable ,
1392+ } ,
1393+ } ) ;
1394+ assert ( isAsyncIterable ( executeResult ) ) ;
1395+ const iterator = executeResult [ Symbol . asyncIterator ] ( ) ;
1396+
1397+ const result1 = await iterator . next ( ) ;
1398+ expectJSON ( result1 ) . toDeepEqual ( {
1399+ done : false ,
1400+ value : {
1401+ data : {
1402+ friendList : [ { id : '1' } ] ,
1403+ } ,
1404+ hasNext : true ,
1405+ } ,
1406+ } ) ;
1407+
1408+ const throwPromise = iterator . throw ( new Error ( 'bad' ) ) ;
1409+
1410+ // these results had started processing before return was called
1411+ const result2 = await iterator . next ( ) ;
1412+ expectJSON ( result2 ) . toDeepEqual ( {
1413+ done : false ,
1414+ value : {
1415+ incremental : [
1416+ {
1417+ data : { name : 'Luke' } ,
1418+ path : [ 'friendList' , 0 ] ,
1419+ } ,
1420+ ] ,
1421+ hasNext : true ,
1422+ } ,
1423+ } ) ;
1424+
1425+ // this result is not returned because async iterator has returned
1426+ const result3 = await iterator . next ( ) ;
1427+ expectJSON ( result3 ) . toDeepEqual ( {
1428+ done : true ,
1429+ value : undefined ,
1430+ } ) ;
1431+ try {
1432+ await throwPromise ; /* c8 ignore start */
1433+ // Not reachable, always throws
1434+ /* c8 ignore stop */
1435+ } catch ( e ) {
1436+ // ignore error
1437+ }
1438+ assert ( returned ) ;
1439+ } ) ;
12181440} ) ;
0 commit comments