@@ -12,7 +12,7 @@ import {
1212import { GraphQLBoolean , GraphQLString } from '../../type/scalars' ;
1313import { GraphQLSchema } from '../../type/schema' ;
1414
15- import { executeSync } from '../execute' ;
15+ import { execute , executeSync } from '../execute' ;
1616
1717class Dog {
1818 name : string ;
@@ -154,6 +154,77 @@ odie.mother.progeny = [odie];
154154const liz = new Person ( 'Liz' ) ;
155155const john = new Person ( 'John' , [ garfield , odie ] , [ liz , odie ] ) ;
156156
157+ const SearchableInterface = new GraphQLInterfaceType ( {
158+ name : 'Searchable' ,
159+ fields : {
160+ id : { type : GraphQLString } ,
161+ } ,
162+ } ) ;
163+
164+ const TypeA = new GraphQLObjectType ( {
165+ name : 'TypeA' ,
166+ interfaces : [ SearchableInterface ] ,
167+ fields : ( ) => ( {
168+ id : { type : GraphQLString } ,
169+ nameA : { type : GraphQLString } ,
170+ } ) ,
171+ isTypeOf : ( _value , _context , _info ) => {
172+ return new Promise ( ( _resolve , reject ) => {
173+ setTimeout ( ( ) => {
174+ reject ( new Error ( 'TypeA_isTypeOf_rejected' ) ) ;
175+ } , 10 ) ;
176+ } ) ;
177+ } ,
178+ } ) ;
179+
180+ const TypeB = new GraphQLObjectType ( {
181+ name : 'TypeB' ,
182+ interfaces : [ SearchableInterface ] ,
183+ fields : ( ) => ( {
184+ id : { type : GraphQLString } ,
185+ nameB : { type : GraphQLString } ,
186+ } ) ,
187+ isTypeOf : ( value : any , _context , _info ) => {
188+ return value . id === 'b' ;
189+ } ,
190+ } ) ;
191+
192+ const queryTypeWithSearchable = new GraphQLObjectType ( {
193+ name : 'Query' ,
194+ fields : {
195+ person : {
196+ type : PersonType ,
197+ resolve : ( ) => john ,
198+ } ,
199+ search : {
200+ type : SearchableInterface ,
201+ args : { id : { type : GraphQLString } } ,
202+ resolve : ( _source , { id } ) => {
203+ /* c8 ignore start */
204+ if ( id === 'a' ) {
205+ return { id : 'a' , nameA : 'Object A' } ;
206+ /* c8 ignore end */
207+ } else if ( id === 'b' ) {
208+ return { id : 'b' , nameB : 'Object B' } ;
209+ }
210+ } ,
211+ } ,
212+ } ,
213+ } ) ;
214+
215+ const schemaWithSearchable = new GraphQLSchema ( {
216+ query : queryTypeWithSearchable ,
217+ types : [
218+ PetType ,
219+ TypeA ,
220+ TypeB ,
221+ SearchableInterface ,
222+ PersonType ,
223+ DogType ,
224+ CatType ,
225+ ] ,
226+ } ) ;
227+
157228describe ( 'Execute: Union and intersection types' , ( ) => {
158229 it ( 'can introspect on union and intersection types' , ( ) => {
159230 const document = parse ( `
@@ -545,4 +616,50 @@ describe('Execute: Union and intersection types', () => {
545616 expect ( encounteredRootValue ) . to . equal ( rootValue ) ;
546617 expect ( encounteredContext ) . to . equal ( contextValue ) ;
547618 } ) ;
619+
620+ it ( 'handles promises from isTypeOf correctly when a later type matches synchronously' , async ( ) => {
621+ const document = parse ( `
622+ query TestSearch {
623+ search(id: "b") {
624+ __typename
625+ id
626+ ... on TypeA {
627+ nameA
628+ }
629+ ... on TypeB {
630+ nameB
631+ }
632+ }
633+ }
634+ ` ) ;
635+
636+ let unhandledRejection : any = null ;
637+ /* c8 ignore start */
638+ const unhandledRejectionListener = ( reason : any ) => {
639+ unhandledRejection = reason ;
640+ } ;
641+ process . on ( 'unhandledRejection' , unhandledRejectionListener ) ;
642+ /* c8 ignore end */
643+
644+ const result = await execute ( {
645+ schema : schemaWithSearchable ,
646+ document,
647+ } ) ;
648+
649+ expect ( result . errors ) . to . be . undefined ;
650+ expect ( result . data ) . to . deep . equal ( {
651+ search : {
652+ __typename : 'TypeB' ,
653+ id : 'b' ,
654+ nameB : 'Object B' ,
655+ } ,
656+ } ) ;
657+
658+ // Give the TypeA promise a chance to reject and the listener to fire
659+ await new Promise ( ( resolve ) => setTimeout ( resolve , 20 ) ) ;
660+
661+ process . removeListener ( 'unhandledRejection' , unhandledRejectionListener ) ;
662+
663+ expect ( unhandledRejection ) . to . be . null ;
664+ } ) ;
548665} ) ;
0 commit comments