diff --git a/src/execution/__tests__/union-interface-test.ts b/src/execution/__tests__/union-interface-test.ts index b7691fa689..6f8408c487 100644 --- a/src/execution/__tests__/union-interface-test.ts +++ b/src/execution/__tests__/union-interface-test.ts @@ -1,6 +1,8 @@ import { expect } from 'chai'; import { describe, it } from 'mocha'; +import { expectJSON } from '../../__testUtils__/expectJSON.js'; + import { parse } from '../../language/parser.js'; import { @@ -12,7 +14,7 @@ import { import { GraphQLBoolean, GraphQLString } from '../../type/scalars.js'; import { GraphQLSchema } from '../../type/schema.js'; -import { executeSync } from '../execute.js'; +import { execute, executeSync } from '../execute.js'; class Dog { name: string; @@ -42,19 +44,30 @@ class Cat { } } +class Plant { + name: string; + + constructor(name: string) { + this.name = name; + } +} + class Person { name: string; pets: ReadonlyArray | undefined; friends: ReadonlyArray | undefined; + responsibilities: ReadonlyArray | undefined; constructor( name: string, pets?: ReadonlyArray, friends?: ReadonlyArray, + responsibilities?: ReadonlyArray, ) { this.name = name; this.pets = pets; this.friends = friends; + this.responsibilities = responsibilities; } } @@ -108,6 +121,18 @@ const CatType: GraphQLObjectType = new GraphQLObjectType({ isTypeOf: (value) => value instanceof Cat, }); +const PlantType: GraphQLObjectType = new GraphQLObjectType({ + name: 'Plant', + interfaces: [NamedType], + fields: () => ({ + name: { type: GraphQLString }, + }), + // eslint-disable-next-line @typescript-eslint/require-await + isTypeOf: async () => { + throw new Error('Not sure if this is a plant'); + }, +}); + const PetType = new GraphQLUnionType({ name: 'Pet', types: [DogType, CatType], @@ -124,6 +149,11 @@ const PetType = new GraphQLUnionType({ }, }); +const PetOrPlantType = new GraphQLUnionType({ + name: 'PetOrPlantType', + types: [PlantType, DogType, CatType], +}); + const PersonType: GraphQLObjectType = new GraphQLObjectType({ name: 'Person', interfaces: [NamedType, MammalType, LifeType], @@ -131,6 +161,7 @@ const PersonType: GraphQLObjectType = new GraphQLObjectType({ name: { type: GraphQLString }, pets: { type: new GraphQLList(PetType) }, friends: { type: new GraphQLList(NamedType) }, + responsibilities: { type: new GraphQLList(PetOrPlantType) }, progeny: { type: new GraphQLList(PersonType) }, mother: { type: PersonType }, father: { type: PersonType }, @@ -151,8 +182,14 @@ const odie = new Dog('Odie', true); odie.mother = new Dog("Odie's Mom", true); odie.mother.progeny = [odie]; +const fern = new Plant('Fern'); const liz = new Person('Liz'); -const john = new Person('John', [garfield, odie], [liz, odie]); +const john = new Person( + 'John', + [garfield, odie], + [liz, odie], + [garfield, fern], +); describe('Execute: Union and intersection types', () => { it('can introspect on union and intersection types', () => { @@ -195,7 +232,12 @@ describe('Execute: Union and intersection types', () => { name: 'Named', fields: [{ name: 'name' }], interfaces: [], - possibleTypes: [{ name: 'Dog' }, { name: 'Cat' }, { name: 'Person' }], + possibleTypes: [ + { name: 'Dog' }, + { name: 'Cat' }, + { name: 'Person' }, + { name: 'Plant' }, + ], enumValues: null, inputFields: null, }, @@ -545,4 +587,50 @@ describe('Execute: Union and intersection types', () => { expect(encounteredRootValue).to.equal(rootValue); expect(encounteredContext).to.equal(contextValue); }); + + it('it handles rejections from isTypeOf after after an isTypeOf returns true', async () => { + const document = parse(` + { + responsibilities { + __typename + ... on Dog { + name + barks + } + ... on Cat { + name + meows + } + } + } + `); + + const rootValue = new Person('John', [], [liz], [garfield]); + const contextValue = { authToken: '123abc' }; + + /* c8 ignore next 4 */ + // eslint-disable-next-line no-undef + process.on('unhandledRejection', () => { + expect.fail('Unhandled rejection'); + }); + + const result = await execute({ + schema, + document, + rootValue, + contextValue, + }); + + expectJSON(result).toDeepEqual({ + data: { + responsibilities: [ + { + __typename: 'Cat', + meows: false, + name: 'Garfield', + }, + ], + }, + }); + }); }); diff --git a/src/execution/execute.ts b/src/execution/execute.ts index acc3f86040..97a8eb76ac 100644 --- a/src/execution/execute.ts +++ b/src/execution/execute.ts @@ -1823,6 +1823,12 @@ export const defaultTypeResolver: GraphQLTypeResolver = if (isPromise(isTypeOfResult)) { promisedIsTypeOfResults[i] = isTypeOfResult; } else if (isTypeOfResult) { + if (promisedIsTypeOfResults.length > 0) { + Promise.all(promisedIsTypeOfResults).then(undefined, () => { + /* ignore errors */ + }); + } + return type.name; } }