@@ -2,11 +2,17 @@ import { expect } from 'chai';
2
2
import { describe , it } from 'mocha' ;
3
3
4
4
import { expectJSON } from '../../__testUtils__/expectJSON' ;
5
+ import type { PromiseOrValue } from '../../jsutils/PromiseOrValue' ;
5
6
6
7
import { parse } from '../../language/parser' ;
8
+ import type { GraphQLFieldResolver } from '../../type/definition' ;
9
+ import { GraphQLList , GraphQLObjectType } from '../../type/definition' ;
10
+ import { GraphQLString } from '../../type/scalars' ;
11
+ import { GraphQLSchema } from '../../type/schema' ;
7
12
8
13
import { buildSchema } from '../../utilities/buildASTSchema' ;
9
14
15
+ import type { ExecutionResult } from '../execute' ;
10
16
import { execute , executeSync } from '../execute' ;
11
17
12
18
describe ( 'Execute: Accepts any iterable as list value' , ( ) => {
@@ -66,6 +72,146 @@ describe('Execute: Accepts any iterable as list value', () => {
66
72
} ) ;
67
73
} ) ;
68
74
75
+ describe ( 'Execute: Accepts async iterables as list value' , ( ) => {
76
+ function complete ( rootValue : unknown ) {
77
+ return execute ( {
78
+ schema : buildSchema ( 'type Query { listField: [String] }' ) ,
79
+ document : parse ( '{ listField }' ) ,
80
+ rootValue,
81
+ } ) ;
82
+ }
83
+
84
+ function completeObjectList (
85
+ resolve : GraphQLFieldResolver < { index : number } , unknown > ,
86
+ ) : PromiseOrValue < ExecutionResult > {
87
+ const schema = new GraphQLSchema ( {
88
+ query : new GraphQLObjectType ( {
89
+ name : 'Query' ,
90
+ fields : {
91
+ listField : {
92
+ resolve : async function * listField ( ) {
93
+ yield await Promise . resolve ( { index : 0 } ) ;
94
+ yield await Promise . resolve ( { index : 1 } ) ;
95
+ yield await Promise . resolve ( { index : 2 } ) ;
96
+ } ,
97
+ type : new GraphQLList (
98
+ new GraphQLObjectType ( {
99
+ name : 'ObjectWrapper' ,
100
+ fields : {
101
+ index : {
102
+ type : GraphQLString ,
103
+ resolve,
104
+ } ,
105
+ } ,
106
+ } ) ,
107
+ ) ,
108
+ } ,
109
+ } ,
110
+ } ) ,
111
+ } ) ;
112
+ return execute ( {
113
+ schema,
114
+ document : parse ( '{ listField { index } }' ) ,
115
+ } ) ;
116
+ }
117
+
118
+ it ( 'Accepts an AsyncGenerator function as a List value' , async ( ) => {
119
+ async function * listField ( ) {
120
+ yield await Promise . resolve ( 'two' ) ;
121
+ yield await Promise . resolve ( 4 ) ;
122
+ yield await Promise . resolve ( false ) ;
123
+ }
124
+
125
+ expectJSON ( await complete ( { listField } ) ) . toDeepEqual ( {
126
+ data : { listField : [ 'two' , '4' , 'false' ] } ,
127
+ } ) ;
128
+ } ) ;
129
+
130
+ it ( 'Handles an AsyncGenerator function that throws' , async ( ) => {
131
+ async function * listField ( ) {
132
+ yield await Promise . resolve ( 'two' ) ;
133
+ yield await Promise . resolve ( 4 ) ;
134
+ throw new Error ( 'bad' ) ;
135
+ }
136
+
137
+ expectJSON ( await complete ( { listField } ) ) . toDeepEqual ( {
138
+ data : { listField : [ 'two' , '4' , null ] } ,
139
+ errors : [
140
+ {
141
+ message : 'bad' ,
142
+ locations : [ { line : 1 , column : 3 } ] ,
143
+ path : [ 'listField' , 2 ] ,
144
+ } ,
145
+ ] ,
146
+ } ) ;
147
+ } ) ;
148
+
149
+ it ( 'Handles an AsyncGenerator function where an intermediate value triggers an error' , async ( ) => {
150
+ async function * listField ( ) {
151
+ yield await Promise . resolve ( 'two' ) ;
152
+ yield await Promise . resolve ( { } ) ;
153
+ yield await Promise . resolve ( 4 ) ;
154
+ }
155
+
156
+ expectJSON ( await complete ( { listField } ) ) . toDeepEqual ( {
157
+ data : { listField : [ 'two' , null , '4' ] } ,
158
+ errors : [
159
+ {
160
+ message : 'String cannot represent value: {}' ,
161
+ locations : [ { line : 1 , column : 3 } ] ,
162
+ path : [ 'listField' , 1 ] ,
163
+ } ,
164
+ ] ,
165
+ } ) ;
166
+ } ) ;
167
+
168
+ it ( 'Handles errors from `completeValue` in AsyncIterables' , async ( ) => {
169
+ async function * listField ( ) {
170
+ yield await Promise . resolve ( 'two' ) ;
171
+ yield await Promise . resolve ( { } ) ;
172
+ }
173
+
174
+ expectJSON ( await complete ( { listField } ) ) . toDeepEqual ( {
175
+ data : { listField : [ 'two' , null ] } ,
176
+ errors : [
177
+ {
178
+ message : 'String cannot represent value: {}' ,
179
+ locations : [ { line : 1 , column : 3 } ] ,
180
+ path : [ 'listField' , 1 ] ,
181
+ } ,
182
+ ] ,
183
+ } ) ;
184
+ } ) ;
185
+
186
+ it ( 'Handles promises from `completeValue` in AsyncIterables' , async ( ) => {
187
+ expectJSON (
188
+ await completeObjectList ( ( { index } ) => Promise . resolve ( index ) ) ,
189
+ ) . toDeepEqual ( {
190
+ data : { listField : [ { index : '0' } , { index : '1' } , { index : '2' } ] } ,
191
+ } ) ;
192
+ } ) ;
193
+
194
+ it ( 'Handles rejected promises from `completeValue` in AsyncIterables' , async ( ) => {
195
+ expectJSON (
196
+ await completeObjectList ( ( { index } ) => {
197
+ if ( index === 2 ) {
198
+ return Promise . reject ( new Error ( 'bad' ) ) ;
199
+ }
200
+ return Promise . resolve ( index ) ;
201
+ } ) ,
202
+ ) . toDeepEqual ( {
203
+ data : { listField : [ { index : '0' } , { index : '1' } , { index : null } ] } ,
204
+ errors : [
205
+ {
206
+ message : 'bad' ,
207
+ locations : [ { line : 1 , column : 15 } ] ,
208
+ path : [ 'listField' , 2 , 'index' ] ,
209
+ } ,
210
+ ] ,
211
+ } ) ;
212
+ } ) ;
213
+ } ) ;
214
+
69
215
describe ( 'Execute: Handles list nullability' , ( ) => {
70
216
async function complete ( args : { listField : unknown ; as : string } ) {
71
217
const { listField, as } = args ;
0 commit comments