@@ -3,10 +3,18 @@ import { describe, it } from 'mocha';
33
44import { expectJSON } from '../../__testUtils__/expectJSON' ;
55
6+ import type { PromiseOrValue } from '../../jsutils/PromiseOrValue' ;
7+
68import { parse } from '../../language/parser' ;
79
10+ import type { GraphQLFieldResolver } from '../../type/definition' ;
11+ import { GraphQLList , GraphQLObjectType } from '../../type/definition' ;
12+ import { GraphQLString } from '../../type/scalars' ;
13+ import { GraphQLSchema } from '../../type/schema' ;
14+
815import { buildSchema } from '../../utilities/buildASTSchema' ;
916
17+ import type { ExecutionResult } from '../execute' ;
1018import { execute , executeSync } from '../execute' ;
1119
1220describe ( 'Execute: Accepts any iterable as list value' , ( ) => {
@@ -66,6 +74,175 @@ describe('Execute: Accepts any iterable as list value', () => {
6674 } ) ;
6775} ) ;
6876
77+ describe ( 'Execute: Accepts async iterables as list value' , ( ) => {
78+ function complete ( rootValue : unknown , as : string = '[String]' ) {
79+ return execute ( {
80+ schema : buildSchema ( `type Query { listField: ${ as } }` ) ,
81+ document : parse ( '{ listField }' ) ,
82+ rootValue,
83+ } ) ;
84+ }
85+
86+ function completeObjectList (
87+ resolve : GraphQLFieldResolver < { index : number } , unknown > ,
88+ ) : PromiseOrValue < ExecutionResult > {
89+ const schema = new GraphQLSchema ( {
90+ query : new GraphQLObjectType ( {
91+ name : 'Query' ,
92+ fields : {
93+ listField : {
94+ resolve : async function * listField ( ) {
95+ yield await Promise . resolve ( { index : 0 } ) ;
96+ yield await Promise . resolve ( { index : 1 } ) ;
97+ yield await Promise . resolve ( { index : 2 } ) ;
98+ } ,
99+ type : new GraphQLList (
100+ new GraphQLObjectType ( {
101+ name : 'ObjectWrapper' ,
102+ fields : {
103+ index : {
104+ type : GraphQLString ,
105+ resolve,
106+ } ,
107+ } ,
108+ } ) ,
109+ ) ,
110+ } ,
111+ } ,
112+ } ) ,
113+ } ) ;
114+ return execute ( {
115+ schema,
116+ document : parse ( '{ listField { index } }' ) ,
117+ } ) ;
118+ }
119+
120+ it ( 'Accepts an AsyncGenerator function as a List value' , async ( ) => {
121+ async function * listField ( ) {
122+ yield await Promise . resolve ( 'two' ) ;
123+ yield await Promise . resolve ( 4 ) ;
124+ yield await Promise . resolve ( false ) ;
125+ }
126+
127+ expectJSON ( await complete ( { listField } ) ) . toDeepEqual ( {
128+ data : { listField : [ 'two' , '4' , 'false' ] } ,
129+ } ) ;
130+ } ) ;
131+
132+ it ( 'Handles an AsyncGenerator function that throws' , async ( ) => {
133+ async function * listField ( ) {
134+ yield await Promise . resolve ( 'two' ) ;
135+ yield await Promise . resolve ( 4 ) ;
136+ throw new Error ( 'bad' ) ;
137+ }
138+
139+ expectJSON ( await complete ( { listField } ) ) . toDeepEqual ( {
140+ data : { listField : [ 'two' , '4' , null ] } ,
141+ errors : [
142+ {
143+ message : 'bad' ,
144+ locations : [ { line : 1 , column : 3 } ] ,
145+ path : [ 'listField' , 2 ] ,
146+ } ,
147+ ] ,
148+ } ) ;
149+ } ) ;
150+
151+ it ( 'Handles an AsyncGenerator function where an intermediate value triggers an error' , async ( ) => {
152+ async function * listField ( ) {
153+ yield await Promise . resolve ( 'two' ) ;
154+ yield await Promise . resolve ( { } ) ;
155+ yield await Promise . resolve ( 4 ) ;
156+ }
157+
158+ expectJSON ( await complete ( { listField } ) ) . toDeepEqual ( {
159+ data : { listField : [ 'two' , null , '4' ] } ,
160+ errors : [
161+ {
162+ message : 'String cannot represent value: {}' ,
163+ locations : [ { line : 1 , column : 3 } ] ,
164+ path : [ 'listField' , 1 ] ,
165+ } ,
166+ ] ,
167+ } ) ;
168+ } ) ;
169+
170+ it ( 'Handles errors from `completeValue` in AsyncIterables' , async ( ) => {
171+ async function * listField ( ) {
172+ yield await Promise . resolve ( 'two' ) ;
173+ yield await Promise . resolve ( { } ) ;
174+ }
175+
176+ expectJSON ( await complete ( { listField } ) ) . toDeepEqual ( {
177+ data : { listField : [ 'two' , null ] } ,
178+ errors : [
179+ {
180+ message : 'String cannot represent value: {}' ,
181+ locations : [ { line : 1 , column : 3 } ] ,
182+ path : [ 'listField' , 1 ] ,
183+ } ,
184+ ] ,
185+ } ) ;
186+ } ) ;
187+
188+ it ( 'Handles promises from `completeValue` in AsyncIterables' , async ( ) => {
189+ expectJSON (
190+ await completeObjectList ( ( { index } ) => Promise . resolve ( index ) ) ,
191+ ) . toDeepEqual ( {
192+ data : { listField : [ { index : '0' } , { index : '1' } , { index : '2' } ] } ,
193+ } ) ;
194+ } ) ;
195+
196+ it ( 'Handles rejected promises from `completeValue` in AsyncIterables' , async ( ) => {
197+ expectJSON (
198+ await completeObjectList ( ( { index } ) => {
199+ if ( index === 2 ) {
200+ return Promise . reject ( new Error ( 'bad' ) ) ;
201+ }
202+ return Promise . resolve ( index ) ;
203+ } ) ,
204+ ) . toDeepEqual ( {
205+ data : { listField : [ { index : '0' } , { index : '1' } , { index : null } ] } ,
206+ errors : [
207+ {
208+ message : 'bad' ,
209+ locations : [ { line : 1 , column : 15 } ] ,
210+ path : [ 'listField' , 2 , 'index' ] ,
211+ } ,
212+ ] ,
213+ } ) ;
214+ } ) ;
215+ it ( 'Handles nulls yielded by async generator' , async ( ) => {
216+ async function * listField ( ) {
217+ yield await Promise . resolve ( 1 ) ;
218+ yield await Promise . resolve ( null ) ;
219+ yield await Promise . resolve ( 2 ) ;
220+ }
221+ const errors = [
222+ {
223+ message : 'Cannot return null for non-nullable field Query.listField.' ,
224+ locations : [ { line : 1 , column : 3 } ] ,
225+ path : [ 'listField' , 1 ] ,
226+ } ,
227+ ] ;
228+
229+ expect ( await complete ( { listField } , '[Int]' ) ) . to . deep . equal ( {
230+ data : { listField : [ 1 , null , 2 ] } ,
231+ } ) ;
232+ expect ( await complete ( { listField } , '[Int]!' ) ) . to . deep . equal ( {
233+ data : { listField : [ 1 , null , 2 ] } ,
234+ } ) ;
235+ expectJSON ( await complete ( { listField } , '[Int!]' ) ) . toDeepEqual ( {
236+ data : { listField : null } ,
237+ errors,
238+ } ) ;
239+ expectJSON ( await complete ( { listField } , '[Int!]!' ) ) . toDeepEqual ( {
240+ data : null ,
241+ errors,
242+ } ) ;
243+ } ) ;
244+ } ) ;
245+
69246describe ( 'Execute: Handles list nullability' , ( ) => {
70247 async function complete ( args : { listField : unknown ; as : string } ) {
71248 const { listField, as } = args ;
0 commit comments