@@ -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,72 @@ 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+ /* c8 ignore start */
206+ if ( id === 'a' ) {
207+ return { id : 'a' , nameA : 'Object A' } ;
208+ /* c8 ignore end */
209+ } else if ( id === 'b' ) {
210+ return { id : 'b' , nameB : 'Object B' } ;
211+ }
212+ } ,
213+ } ,
214+ } ,
215+ } ) ;
216+
217+ const schemaWithSearchable = new GraphQLSchema ( {
218+ query : queryTypeWithSearchable ,
219+ types : [ PetType , TypeA , TypeB , SearchableInterface , PersonType , DogType , CatType ] , // Added new types
220+ } ) ;
221+ // End of new types
222+
157223describe ( 'Execute: Union and intersection types' , ( ) => {
158224 it ( 'can introspect on union and intersection types' , ( ) => {
159225 const document = parse ( `
@@ -545,4 +611,50 @@ describe('Execute: Union and intersection types', () => {
545611 expect ( encounteredRootValue ) . to . equal ( rootValue ) ;
546612 expect ( encounteredContext ) . to . equal ( contextValue ) ;
547613 } ) ;
614+
615+ it ( 'handles promises from isTypeOf correctly when a later type matches synchronously' , async ( ) => {
616+ const document = parse ( `
617+ query TestSearch {
618+ search(id: "b") {
619+ __typename
620+ id
621+ ... on TypeA {
622+ nameA
623+ }
624+ ... on TypeB {
625+ nameB
626+ }
627+ }
628+ }
629+ ` ) ;
630+
631+ let unhandledRejection : any = null ;
632+ /* c8 ignore start */
633+ const unhandledRejectionListener = ( reason : any ) => {
634+ unhandledRejection = reason ;
635+ } ;
636+ process . on ( 'unhandledRejection' , unhandledRejectionListener ) ;
637+ /* c8 ignore end */
638+
639+ const result = await execute ( {
640+ schema : schemaWithSearchable ,
641+ document,
642+ } ) ;
643+
644+ expect ( result . errors ) . to . be . undefined ;
645+ expect ( result . data ) . to . deep . equal ( {
646+ search : {
647+ __typename : 'TypeB' ,
648+ id : 'b' ,
649+ nameB : 'Object B' ,
650+ } ,
651+ } ) ;
652+
653+ // Give the TypeA promise a chance to reject and the listener to fire
654+ await new Promise ( ( resolve ) => setTimeout ( resolve , 20 ) ) ;
655+
656+ process . removeListener ( 'unhandledRejection' , unhandledRejectionListener ) ;
657+
658+ expect ( unhandledRejection ) . to . be . null ;
659+ } ) ;
548660} ) ;
0 commit comments