@@ -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,70 @@ odie.mother.progeny = [odie];
154154const liz = new Person ( 'Liz' ) ;
155155const john = new Person ( 'John' , [ garfield , odie ] , [ liz , odie ] ) ;
156156
157+ // New types for the unhandled rejection test
158+ const SearchableInterface = new GraphQLInterfaceType ( {
159+ name : 'Searchable' ,
160+ fields : {
161+ id : { type : GraphQLString } ,
162+ } ,
163+ // Deliberately no resolveType, to use isTypeOf from concrete types
164+ } ) ;
165+
166+ const TypeA = new GraphQLObjectType ( {
167+ name : 'TypeA' ,
168+ interfaces : [ SearchableInterface ] ,
169+ fields : ( ) => ( {
170+ id : { type : GraphQLString } ,
171+ nameA : { type : GraphQLString } ,
172+ } ) ,
173+ isTypeOf : ( _value , _context , _info ) => {
174+ return new Promise ( ( _resolve , reject ) => {
175+ setTimeout ( ( ) => {
176+ reject ( new Error ( 'TypeA_isTypeOf_rejected' ) ) ;
177+ } , 10 ) ;
178+ } ) ;
179+ } ,
180+ } ) ;
181+
182+ const TypeB = new GraphQLObjectType ( {
183+ name : 'TypeB' ,
184+ interfaces : [ SearchableInterface ] ,
185+ fields : ( ) => ( {
186+ id : { type : GraphQLString } ,
187+ nameB : { type : GraphQLString } ,
188+ } ) ,
189+ isTypeOf : ( value : any , _context , _info ) => {
190+ return value . id === 'b' ;
191+ } ,
192+ } ) ;
193+
194+ const queryTypeWithSearchable = new GraphQLObjectType ( {
195+ name : 'Query' ,
196+ fields : {
197+ person : {
198+ type : PersonType ,
199+ resolve : ( ) => john ,
200+ } ,
201+ search : {
202+ type : SearchableInterface ,
203+ args : { id : { type : GraphQLString } } ,
204+ resolve : ( _source , { id } ) => {
205+ if ( id === 'a' ) {
206+ return { id : 'a' , nameA : 'Object A' } ;
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 : [ PetType , TypeA , TypeB , SearchableInterface , PersonType , DogType , CatType ] , // Added new types
218+ } ) ;
219+ // End of new types
220+
157221describe ( 'Execute: Union and intersection types' , ( ) => {
158222 it ( 'can introspect on union and intersection types' , ( ) => {
159223 const document = parse ( `
@@ -545,4 +609,48 @@ describe('Execute: Union and intersection types', () => {
545609 expect ( encounteredRootValue ) . to . equal ( rootValue ) ;
546610 expect ( encounteredContext ) . to . equal ( contextValue ) ;
547611 } ) ;
612+
613+ it ( 'handles promises from isTypeOf correctly when a later type matches synchronously' , async ( ) => {
614+ const document = parse ( `
615+ query TestSearch {
616+ search(id: "b") {
617+ __typename
618+ id
619+ ... on TypeA {
620+ nameA
621+ }
622+ ... on TypeB {
623+ nameB
624+ }
625+ }
626+ }
627+ ` ) ;
628+
629+ let unhandledRejection : any = null ;
630+ const unhandledRejectionListener = ( reason : any ) => {
631+ unhandledRejection = reason ;
632+ } ;
633+ process . on ( 'unhandledRejection' , unhandledRejectionListener ) ;
634+
635+ const result = await execute ( {
636+ schema : schemaWithSearchable ,
637+ document,
638+ } ) ;
639+
640+ expect ( result . errors ) . to . be . undefined ;
641+ expect ( result . data ) . to . deep . equal ( {
642+ search : {
643+ __typename : 'TypeB' ,
644+ id : 'b' ,
645+ nameB : 'Object B' ,
646+ } ,
647+ } ) ;
648+
649+ // Give the TypeA promise a chance to reject and the listener to fire
650+ await new Promise ( ( resolve ) => setTimeout ( resolve , 20 ) ) ;
651+
652+ process . removeListener ( 'unhandledRejection' , unhandledRejectionListener ) ;
653+
654+ expect ( unhandledRejection ) . to . be . null ;
655+ } ) ;
548656} ) ;
0 commit comments