@@ -3,10 +3,18 @@ import { describe, it } from 'mocha';
3
3
4
4
import { expectJSON } from '../../__testUtils__/expectJSON' ;
5
5
6
+ import type { PromiseOrValue } from '../../jsutils/PromiseOrValue' ;
7
+
6
8
import { parse } from '../../language/parser' ;
7
9
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
+
8
15
import { buildSchema } from '../../utilities/buildASTSchema' ;
9
16
17
+ import type { ExecutionResult } from '../execute' ;
10
18
import { execute , executeSync } from '../execute' ;
11
19
12
20
describe ( 'Execute: Accepts any iterable as list value' , ( ) => {
@@ -66,6 +74,175 @@ describe('Execute: Accepts any iterable as list value', () => {
66
74
} ) ;
67
75
} ) ;
68
76
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
+
69
246
describe ( 'Execute: Handles list nullability' , ( ) => {
70
247
async function complete ( args : { listField : unknown ; as : string } ) {
71
248
const { listField, as } = args ;
0 commit comments