text: import { expect } from 'chai';
  import { describe, it } from 'mocha';
  
  import invariant from '../../jsutils/invariant';
  
  import { GraphQLSchema } from '../../type/schema';
  import { GraphQLString, GraphQLBoolean } from '../../type/scalars';
  import {
    GraphQLList,
    GraphQLObjectType,
    GraphQLInterfaceType,
    GraphQLUnionType,
  } from '../../type/definition';
  
  import { graphql } from '../../graphql';
  
  class Dog {
    name: string;
    woofs: boolean;
  
    constructor(name: string, woofs: boolean) {
      this.name = name;
      this.woofs = woofs;
    }
  }
  
  class Cat {
    name: string;
    meows: boolean;
  
    constructor(name: string, meows: boolean) {
      this.name = name;
      this.meows = meows;
    }
  }
  
  class Human {
    name: string;
  
    constructor(name: string) {
      this.name = name;
    }
  }
  
  describe('Execute: Handles execution of abstract types with promises', () => {
    it('isTypeOf used to resolve runtime type for Interface', async () => {
      const PetType = new GraphQLInterfaceType({
        name: 'Pet',
        fields: {
          name: { type: GraphQLString },
        },
      });
  
      const DogType = new GraphQLObjectType({
        name: 'Dog',
        interfaces: [PetType],
        isTypeOf: (obj) => Promise.resolve(obj instanceof Dog),
        fields: {
          name: { type: GraphQLString },
          woofs: { type: GraphQLBoolean },
        },
      });
  
      const CatType = new GraphQLObjectType({
        name: 'Cat',
        interfaces: [PetType],
        isTypeOf: (obj) => Promise.resolve(obj instanceof Cat),
        fields: {
          name: { type: GraphQLString },
          meows: { type: GraphQLBoolean },
        },
      });
  
      const schema = new GraphQLSchema({
        query: new GraphQLObjectType({
          name: 'Query',
          fields: {
            pets: {
              type: new GraphQLList(PetType),
              resolve() {
                return [new Dog('Odie', true), new Cat('Garfield', false)];
              },
            },
          },
        }),
        types: [CatType, DogType],
      });
  
      const source = `
        {
          pets {
            name
            ... on Dog {
              woofs
            }
            ... on Cat {
              meows
            }
          }
        }
      `;
  
      const result = await graphql({ schema, source });
      expect(result).to.deep.equal({
        data: {
          pets: [
            {
              name: 'Odie',
              woofs: true,
            },
            {
              name: 'Garfield',
              meows: false,
            },
          ],
        },
      });
    });
  
    it('isTypeOf can be rejected', async () => {
      const PetType = new GraphQLInterfaceType({
        name: 'Pet',
        fields: {
          name: { type: GraphQLString },
        },
      });
  
      const DogType = new GraphQLObjectType({
        name: 'Dog',
        interfaces: [PetType],
        isTypeOf: () => Promise.reject(new Error('We are testing this error')),
        fields: {
          name: { type: GraphQLString },
          woofs: { type: GraphQLBoolean },
        },
      });
  
      const CatType = new GraphQLObjectType({
        name: 'Cat',
        interfaces: [PetType],
        isTypeOf: (obj) => Promise.resolve(obj instanceof Cat),
        fields: {
          name: { type: GraphQLString },
          meows: { type: GraphQLBoolean },
        },
      });
  
      const schema = new GraphQLSchema({
        query: new GraphQLObjectType({
          name: 'Query',
          fields: {
            pets: {
              type: new GraphQLList(PetType),
              resolve() {
                return [new Dog('Odie', true), new Cat('Garfield', false)];
              },
            },
          },
        }),
        types: [CatType, DogType],
      });
  
      const source = `
        {
          pets {
            name
            ... on Dog {
              woofs
            }
            ... on Cat {
              meows
            }
          }
        }
      `;
  
      const result = await graphql({ schema, source });
      expect(result).to.deep.equal({
        data: {
          pets: [null, null],
        },
        errors: [
          {
            message: 'We are testing this error',
            locations: [{ line: 3, column: 9 }],
            path: ['pets', 0],
          },
          {
            message: 'We are testing this error',
            locations: [{ line: 3, column: 9 }],
            path: ['pets', 1],
          },
        ],
      });
    });
  
    it('isTypeOf used to resolve runtime type for Union', async () => {
      const DogType = new GraphQLObjectType({
        name: 'Dog',
        isTypeOf: (obj) => Promise.resolve(obj instanceof Dog),
        fields: {
          name: { type: GraphQLString },
          woofs: { type: GraphQLBoolean },
        },
      });
  
      const CatType = new GraphQLObjectType({
        name: 'Cat',
        isTypeOf: (obj) => Promise.resolve(obj instanceof Cat),
        fields: {
          name: { type: GraphQLString },
          meows: { type: GraphQLBoolean },
        },
      });
  
      const PetType = new GraphQLUnionType({
        name: 'Pet',
        types: [DogType, CatType],
      });
  
      const schema = new GraphQLSchema({
        query: new GraphQLObjectType({
          name: 'Query',
          fields: {
            pets: {
              type: new GraphQLList(PetType),
              resolve() {
                return [new Dog('Odie', true), new Cat('Garfield', false)];
              },
            },
          },
        }),
      });
  
      const source = `
        {
          pets {
            ... on Dog {
              name
              woofs
            }
            ... on Cat {
              name
              meows
            }
          }
        }
      `;
  
      const result = await graphql({ schema, source });
      expect(result).to.deep.equal({
        data: {
          pets: [
            {
              name: 'Odie',
              woofs: true,
            },
            {
              name: 'Garfield',
              meows: false,
            },
          ],
        },
      });
    });
  
    it('resolveType on Interface yields useful error', async () => {
      const PetType = new GraphQLInterfaceType({
        name: 'Pet',
        resolveType(obj) {
          if (obj instanceof Dog) {
            return Promise.resolve(DogType);
          }
          if (obj instanceof Cat) {
            return Promise.resolve(CatType);
          }
          // istanbul ignore else (See: 'https://github.com/graphql/graphql-js/issues/2618')
          if (obj instanceof Human) {
            return Promise.resolve(HumanType);
          }
  
          // istanbul ignore next (Not reachable. All possible types have been considered)
          invariant(false);
        },
        fields: {
          name: { type: GraphQLString },
        },
      });
  
      const HumanType = new GraphQLObjectType({
        name: 'Human',
        fields: {
          name: { type: GraphQLString },
        },
      });
  
      const DogType = new GraphQLObjectType({
        name: 'Dog',
        interfaces: [PetType],
        fields: {
          name: { type: GraphQLString },
          woofs: { type: GraphQLBoolean },
        },
      });
  
      const CatType = new GraphQLObjectType({
        name: 'Cat',
        interfaces: [PetType],
        fields: {
          name: { type: GraphQLString },
          meows: { type: GraphQLBoolean },
        },
      });
  
      const schema = new GraphQLSchema({
        query: new GraphQLObjectType({
          name: 'Query',
          fields: {
            pets: {
              type: new GraphQLList(PetType),
              resolve() {
                return Promise.resolve([
                  new Dog('Odie', true),
                  new Cat('Garfield', false),
                  new Human('Jon'),
                ]);
              },
            },
          },
        }),
        types: [CatType, DogType],
      });
  
      const source = `
        {
          pets {
            name
            ... on Dog {
              woofs
            }
            ... on Cat {
              meows
            }
          }
        }
      `;
  
      const result = await graphql({ schema, source });
      expect(result).to.deep.equal({
        data: {
          pets: [
            {
              name: 'Odie',
              woofs: true,
            },
            {
              name: 'Garfield',
              meows: false,
            },
            null,
          ],
        },
        errors: [
          {
            message:
              'Runtime Object type "Human" is not a possible type for "Pet".',
            locations: [{ line: 3, column: 9 }],
            path: ['pets', 2],
          },
        ],
      });
    });
  
    it('resolveType on Union yields useful error', async () => {
      const HumanType = new GraphQLObjectType({
        name: 'Human',
        fields: {
          name: { type: GraphQLString },
        },
      });
  
      const DogType = new GraphQLObjectType({
        name: 'Dog',
        fields: {
          name: { type: GraphQLString },
          woofs: { type: GraphQLBoolean },
        },
      });
  
      const CatType = new GraphQLObjectType({
        name: 'Cat',
        fields: {
          name: { type: GraphQLString },
          meows: { type: GraphQLBoolean },
        },
      });
  
      const PetType = new GraphQLUnionType({
        name: 'Pet',
        resolveType(obj) {
          if (obj instanceof Dog) {
            return Promise.resolve(DogType);
          }
          if (obj instanceof Cat) {
            return Promise.resolve(CatType);
          }
          // istanbul ignore else (See: 'https://github.com/graphql/graphql-js/issues/2618')
          if (obj instanceof Human) {
            return Promise.resolve(HumanType);
          }
  
          // istanbul ignore next (Not reachable. All possible types have been considered)
          invariant(false);
        },
        types: [DogType, CatType],
      });
  
      const schema = new GraphQLSchema({
        query: new GraphQLObjectType({
          name: 'Query',
          fields: {
            pets: {
              type: new GraphQLList(PetType),
              resolve() {
                return [
                  new Dog('Odie', true),
                  new Cat('Garfield', false),
                  new Human('Jon'),
                ];
              },
            },
          },
        }),
      });
  
      const source = `
        {
          pets {
            ... on Dog {
              name
              woofs
            }
            ... on Cat {
              name
              meows
            }
          }
        }
      `;
  
      const result = await graphql({ schema, source });
      expect(result).to.deep.equal({
        data: {
          pets: [
            {
              name: 'Odie',
              woofs: true,
            },
            {
              name: 'Garfield',
              meows: false,
            },
            null,
          ],
        },
        errors: [
          {
            message:
              'Runtime Object type "Human" is not a possible type for "Pet".',
            locations: [{ line: 3, column: 9 }],
            path: ['pets', 2],
          },
        ],
      });
    });
  
    it('resolveType allows resolving with type name', async () => {
      const PetType = new GraphQLInterfaceType({
        name: 'Pet',
        resolveType(obj) {
          if (obj instanceof Dog) {
            return Promise.resolve('Dog');
          }
          // istanbul ignore else (See: 'https://github.com/graphql/graphql-js/issues/2618')
          if (obj instanceof Cat) {
            return Promise.resolve('Cat');
          }
  
          // istanbul ignore next (Not reachable. All possible types have been considered)
          invariant(false);
        },
        fields: {
          name: { type: GraphQLString },
        },
      });
  
      const DogType = new GraphQLObjectType({
        name: 'Dog',
        interfaces: [PetType],
        fields: {
          name: { type: GraphQLString },
          woofs: { type: GraphQLBoolean },
        },
      });
  
      const CatType = new GraphQLObjectType({
        name: 'Cat',
        interfaces: [PetType],
        fields: {
          name: { type: GraphQLString },
          meows: { type: GraphQLBoolean },
        },
      });
  
      const schema = new GraphQLSchema({
        query: new GraphQLObjectType({
          name: 'Query',
          fields: {
            pets: {
              type: new GraphQLList(PetType),
              resolve() {
                return [new Dog('Odie', true), new Cat('Garfield', false)];
              },
            },
          },
        }),
        types: [CatType, DogType],
      });
  
      const source = `
        {
          pets {
            name
            ... on Dog {
              woofs
            }
            ... on Cat {
              meows
            }
          }
        }
      `;
  
      const result = await graphql({ schema, source });
      expect(result).to.deep.equal({
        data: {
          pets: [
            {
              name: 'Odie',
              woofs: true,
            },
            {
              name: 'Garfield',
              meows: false,
            },
          ],
        },
      });
    });
  
    it('resolveType can be caught', async () => {
      const PetType = new GraphQLInterfaceType({
        name: 'Pet',
        resolveType: () => Promise.reject(new Error('We are testing this error')),
        fields: {
          name: { type: GraphQLString },
        },
      });
  
      const DogType = new GraphQLObjectType({
        name: 'Dog',
        interfaces: [PetType],
        fields: {
          name: { type: GraphQLString },
          woofs: { type: GraphQLBoolean },
        },
      });
  
      const CatType = new GraphQLObjectType({
        name: 'Cat',
        interfaces: [PetType],
        fields: {
          name: { type: GraphQLString },
          meows: { type: GraphQLBoolean },
        },
      });
  
      const schema = new GraphQLSchema({
        query: new GraphQLObjectType({
          name: 'Query',
          fields: {
            pets: {
              type: new GraphQLList(PetType),
              resolve() {
                return [new Dog('Odie', true), new Cat('Garfield', false)];
              },
            },
          },
        }),
        types: [CatType, DogType],
      });
  
      const source = `
        {
          pets {
            name
            ... on Dog {
              woofs
            }
            ... on Cat {
              meows
            }
          }
        }
      `;
  
      const result = await graphql({ schema, source });
      expect(result).to.deep.equal({
        data: {
          pets: [null, null],
        },
        errors: [
          {
            message: 'We are testing this error',
            locations: [{ line: 3, column: 9 }],
            path: ['pets', 0],
          },
          {
            message: 'We are testing this error',
            locations: [{ line: 3, column: 9 }],
            path: ['pets', 1],
          },
        ],
      });
    });
  });
  