All Downloads are FREE. Search and download functionalities are using the official Maven repository.

node_modules.apollo-codegen-scala.src.__tests__.codeGeneration.js Maven / Gradle / Ivy

There is a newer version: 3.3.1
Show newest version
import {
  parse,
  GraphQLString,
  GraphQLList,
  GraphQLNonNull,
  GraphQLEnumType
} from 'graphql';

import {
  generateSource,
  classDeclarationForOperation,
  caseClassDeclarationForFragment,
  caseClassDeclarationForSelectionSet,
  typeDeclarationForGraphQLType,
} from '../codeGeneration';

import { loadSchema } from 'apollo-codegen-core/lib/loading';
const schema = loadSchema(require.resolve('../../../common-test/fixtures/starwars/schema.json'));

import CodeGenerator from 'apollo-codegen-core/lib/utilities/CodeGenerator';

import { compileToLegacyIR } from 'apollo-codegen-core/lib/compiler/legacyIR';

describe('Scala code generation', function() {
  let generator;
  let resetGenerator;
  let compileFromSource;
  let addFragment;

  beforeEach(function() {

    resetGenerator = () => {
      const context = {
        schema: schema,
        operations: {},
        fragments: {},
        typesUsed: {}
      }
      generator = new CodeGenerator(context);
    };

    compileFromSource = (source, options = { generateOperationIds: false }) => {
      const document = parse(source);
      let context = compileToLegacyIR(schema, document);
      options.generateOperationIds && Object.assign(context.options, { generateOperationIds: true, operationIdsMap: {} });
      options.namespace && Object.assign(context.options, { namespace: options.namespace });
      generator.context = context;
      return context;
    };

    addFragment = (fragment) => {
      generator.context.fragments[fragment.fragmentName] = fragment;
    };

    resetGenerator();
  });

  describe('#generateSource()', function() {
    test(`should emit a package declaration when the namespace option is specified`, function() {
      const context = compileFromSource(`
        query HeroName($episode: Episode) {
          hero(episode: $episode) {
            name
          }
        }
      `, { namespace: "hello.world" });

      expect(generateSource(context)).toMatchSnapshot();
    });
  });

  describe('#classDeclarationForOperation()', function() {
    test(`should generate a class declaration for a query with variables`, function() {
      const { operations, fragments } = compileFromSource(`
        query HeroName($episode: Episode) {
          hero(episode: $episode) {
            name
          }
        }
      `);

      classDeclarationForOperation(generator, operations['HeroName'], Object.values(fragments));
      expect(generator.output).toMatchSnapshot();
    });

    test(`should generate a class declaration for a query with fragment spreads`, function() {
      const { operations, fragments } = compileFromSource(`
        query Hero {
          hero {
            ...HeroDetails
          }
        }

        fragment HeroDetails on Character {
          name
        }
      `);

      classDeclarationForOperation(generator, operations['Hero'], Object.values(fragments));
      expect(generator.output).toMatchSnapshot();
    });

    test(`should generate a class declaration for a query with conditional fragment spreads`, function() {
      const { operations, fragments } = compileFromSource(`
        query Hero {
          hero {
            ...DroidDetails
          }
        }

        fragment DroidDetails on Droid {
          primaryFunction
        }
      `);

      classDeclarationForOperation(generator, operations['Hero'], Object.values(fragments));
      expect(generator.output).toMatchSnapshot();
    });

    test(`should generate a class declaration for a query with a fragment spread nested in an inline fragment`, function() {
      const { operations, fragments } = compileFromSource(`
        query Hero {
          hero {
            ... on Droid {
              ...HeroDetails
            }
          }
        }

        fragment HeroDetails on Character {
          name
        }
      `);

      classDeclarationForOperation(generator, operations['Hero'], Object.values(fragments));

      expect(generator.output).toMatchSnapshot();
    });

    test(`should generate a class declaration for a mutation with variables`, function() {
      const { operations, fragments } = compileFromSource(`
        mutation CreateReview($episode: Episode) {
          createReview(episode: $episode, review: { stars: 5, commentary: "Wow!" }) {
            stars
            commentary
          }
        }
      `);

      classDeclarationForOperation(generator, operations['CreateReview'], Object.values(fragments));

      expect(generator.output).toMatchSnapshot();
    });

    describe(`when generateOperationIds is specified`, function() {
      let compileOptions = { generateOperationIds: true };

      test(`should generate a class declaration with an operationId property`, function() {
        const context = compileFromSource(`
          query Hero {
            hero {
              ...HeroDetails
            }
          }
          fragment HeroDetails on Character {
            name
          }
        `, compileOptions);

        classDeclarationForOperation(generator, context.operations['Hero'], Object.values(context.fragments));
        expect(generator.output).toMatchSnapshot();
      });

      test(`should generate different operation ids for different operations`, function() {
        const context1 = compileFromSource(`
          query Hero {
            hero {
              ...HeroDetails
            }
          }
          fragment HeroDetails on Character {
            name
          }
        `, compileOptions);

        classDeclarationForOperation(generator, context1.operations['Hero'], Object.values(context1.fragments));
        const output1 = generator.output;

        resetGenerator();
        const context2 = compileFromSource(`
          query Hero {
            hero {
              ...HeroDetails
            }
          }
          fragment HeroDetails on Character {
            appearsIn
          }
        `, compileOptions);

        classDeclarationForOperation(generator, context2.operations['Hero'], Object.values(context2.fragments));
        const output2 = generator.output;

        expect(output1).not.toBe(output2);
      });

      test(`should generate the same operation id regardless of operation formatting/commenting`, function() {
        const context1 = compileFromSource(`
          query HeroName($episode: Episode) {
            hero(episode: $episode) {
              name
            }
          }
        `, compileOptions);

        classDeclarationForOperation(generator, context1.operations['HeroName'], Object.values(context1.fragments));
        const output1 = generator.output;

        resetGenerator();
        const context2 = compileFromSource(`
          # Profound comment
          query HeroName($episode:Episode) { hero(episode: $episode) { name } }
          # Deeply meaningful comment
        `, compileOptions);

        classDeclarationForOperation(generator, context2.operations['HeroName'], Object.values(context2.fragments));
        const output2 = generator.output;

        expect(output1).toBe(output2);
      });

      test(`should generate the same operation id regardless of fragment order`, function() {
        const context1 = compileFromSource(`
          query Hero {
            hero {
              ...HeroName
              ...HeroAppearsIn
            }
          }
          fragment HeroName on Character {
            name
          }
          fragment HeroAppearsIn on Character {
            appearsIn
          }
        `, compileOptions);

        classDeclarationForOperation(generator, context1.operations['Hero'], Object.values(context1.fragments));
        const output1 = generator.output;

        resetGenerator();
        const context2 = compileFromSource(`
          query Hero {
            hero {
              ...HeroName
              ...HeroAppearsIn
            }
          }
          fragment HeroAppearsIn on Character {
            appearsIn
          }
          fragment HeroName on Character {
            name
          }
        `, compileOptions);

        classDeclarationForOperation(generator, context2.operations['Hero'], Object.values(context2.fragments));
        const output2 = generator.output;

        expect(output1).toBe(output2);
      });

      test(`should generate appropriate operation id mapping source when there are nested fragment references`, function() {
        const source = `
          query Hero {
            hero {
              ...HeroDetails
            }
          }
          fragment HeroName on Character {
            name
          }
          fragment HeroDetails on Character {
            ...HeroName
            appearsIn
          }
        `;
        const context = compileFromSource(source, true);
        expect(context.operations['Hero'].sourceWithFragments).toMatchSnapshot();
      });

    });
  });

  describe('#caseClassDeclarationForFragment()', function() {
    test(`should generate a caseClass declaration for a fragment with an abstract type condition`, function() {
      const { fragments } = compileFromSource(`
        fragment HeroDetails on Character {
          name
          appearsIn
        }
      `);

      caseClassDeclarationForFragment(generator, fragments['HeroDetails']);

      expect(generator.output).toMatchSnapshot();
    });

    test(`should generate a caseClass declaration for a fragment with a concrete type condition`, function() {
      const { fragments } = compileFromSource(`
        fragment DroidDetails on Droid {
          name
          primaryFunction
        }
      `);

      caseClassDeclarationForFragment(generator, fragments['DroidDetails']);

      expect(generator.output).toMatchSnapshot();
    });

    test(`should generate a caseClass declaration for a fragment with a subselection`, function() {
      const { fragments } = compileFromSource(`
        fragment HeroDetails on Character {
          name
          friends {
            name
          }
        }
      `);

      caseClassDeclarationForFragment(generator, fragments['HeroDetails']);

      expect(generator.output).toMatchSnapshot();
    });

    test(`should generate a caseClass declaration for a fragment that includes a fragment spread`, function() {
      const { fragments } = compileFromSource(`
        fragment HeroDetails on Character {
          name
          ...MoreHeroDetails
        }

        fragment MoreHeroDetails on Character {
          appearsIn
        }
      `);

      caseClassDeclarationForFragment(generator, fragments['HeroDetails']);

      expect(generator.output).toMatchSnapshot();
    });
  });

  describe('#caseClassDeclarationForSelectionSet()', function() {
    test(`should generate a caseClass declaration for a selection set`, function() {
      caseClassDeclarationForSelectionSet(generator, {
        caseClassName: 'Hero',
        parentType: schema.getType('Character'),
        fields: [
          {
            responseName: 'name',
            fieldName: 'name',
            type: GraphQLString
          }
        ]
      });

      expect(generator.output).toMatchSnapshot();
    });

    test(`should escape reserved keywords in a caseClass declaration for a selection set`, function() {
      caseClassDeclarationForSelectionSet(generator, {
        caseClassName: 'Hero',
        parentType: schema.getType('Character'),
        fields: [
          {
            responseName: 'private',
            fieldName: 'name',
            type: GraphQLString
          }
        ]
      });

      expect(generator.output).toMatchSnapshot();
    });

    test(`should generate a nested caseClass declaration for a selection set with subselections`, function() {
      caseClassDeclarationForSelectionSet(generator, {
        caseClassName: 'Hero',
        parentType: schema.getType('Character'),
        fields: [
          {
            responseName: 'friends',
            fieldName: 'friends',
            type: new GraphQLList(schema.getType('Character')),
            fields: [
              {
                responseName: 'name',
                fieldName: 'name',
                type: GraphQLString
              }
            ]
          }
        ]
      });

      expect(generator.output).toMatchSnapshot();
    });

    test(`should generate a caseClass declaration for a selection set with a fragment spread that matches the parent type`, function() {
      addFragment({
        fragmentName: 'HeroDetails',
        typeCondition: schema.getType('Character')
      });

      caseClassDeclarationForSelectionSet(generator, {
        caseClassName: 'Hero',
        parentType: schema.getType('Character'),
        fragmentSpreads: ['HeroDetails'],
        fields: [
          {
            responseName: 'name',
            fieldName: 'name',
            type: GraphQLString
          }
        ]
      });

      expect(generator.output).toMatchSnapshot();
    });

    test(`should generate a caseClass declaration for a selection set with a fragment spread with a more specific type condition`, function() {
      addFragment({
        fragmentName: 'DroidDetails',
        typeCondition: schema.getType('Droid')
      });

      caseClassDeclarationForSelectionSet(generator, {
        caseClassName: 'Hero',
        parentType: schema.getType('Character'),
        fragmentSpreads: ['DroidDetails'],
        fields: [
          {
            responseName: 'name',
            fieldName: 'name',
            type: GraphQLString
          }
        ]
      });

      expect(generator.output).toMatchSnapshot();
    });

    test(`should generate a caseClass declaration for a selection set with an inline fragment`, function() {
      caseClassDeclarationForSelectionSet(generator, {
        caseClassName: 'Hero',
        parentType: schema.getType('Character'),
        fields: [
          {
            responseName: 'name',
            fieldName: 'name',
            type: new GraphQLNonNull(GraphQLString)
          }
        ],
        inlineFragments: [
          {
            typeCondition: schema.getType('Droid'),
            possibleTypes: ['Droid'],
            fields: [
              {
                responseName: 'name',
                fieldName: 'name',
                type: new GraphQLNonNull(GraphQLString)
              },
              {
                responseName: 'primaryFunction',
                fieldName: 'primaryFunction',
                type: GraphQLString
              }
            ]
          }
        ]
      });

      expect(generator.output).toMatchSnapshot();
    });

    test(`should generate a caseClass declaration for a fragment spread nested in an inline fragment`, function() {
      addFragment({
        fragmentName: 'HeroDetails',
        typeCondition: schema.getType('Character')
      });

      caseClassDeclarationForSelectionSet(generator, {
        caseClassName: 'Hero',
        parentType: schema.getType('Character'),
        fields: [],
        inlineFragments: [
          {
            typeCondition: schema.getType('Droid'),
            possibleTypes: ['Droid'],
            fields: [],
            fragmentSpreads: ['HeroDetails'],
          }
        ]
      });

      expect(generator.output).toMatchSnapshot();
    });
  });

  describe('#typeDeclarationForGraphQLType()', function() {
    test('should generate an enum declaration for a GraphQLEnumType', function() {
      const generator = new CodeGenerator({options: {}});

      typeDeclarationForGraphQLType(generator, schema.getType('Episode'));

      expect(generator.output).toMatchSnapshot();
    });

    test('should escape identifiers in cases of enum declaration for a GraphQLEnumType', function() {
      const generator = new CodeGenerator({options: {}});

      const albumPrivaciesEnum = new GraphQLEnumType({
        name: 'AlbumPrivacies',
        values: { PUBLIC: { value: "PUBLIC" }, PRIVATE: { value: "PRIVATE" } }
      });

      typeDeclarationForGraphQLType(generator, albumPrivaciesEnum);

      expect(generator.output).toMatchSnapshot();
    });

    test('should generate a caseClass declaration for a GraphQLInputObjectType', function() {
      const generator = new CodeGenerator({options: {}});

      typeDeclarationForGraphQLType(generator, schema.getType('ReviewInput'));

      expect(generator.output).toMatchSnapshot();
    });
  });
});




© 2015 - 2025 Weber Informatics LLC | Privacy Policy