|
| 1 | +import { assert } from 'chai'; |
1 | 2 | import { describe, it } from 'mocha';
|
2 | 3 |
|
3 | 4 | import { expectJSON } from '../../__testUtils__/expectJSON';
|
@@ -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 | + assert(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 | + assert(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 | + assert(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