pact-foundation/pact-js

View on GitHub
src/dsl/graphql.spec.ts

Summary

Maintainability
A
0 mins
Test Coverage
import chai from 'chai';
import chaiAsPromised from 'chai-as-promised';
import { GraphQLInteraction } from './graphql';
import { isMatcher } from './matchers';

chai.use(chaiAsPromised);
const { expect } = chai;

describe('GraphQLInteraction', () => {
  let interaction: GraphQLInteraction;

  beforeEach(() => {
    interaction = new GraphQLInteraction();
  });

  describe('#withOperation', () => {
    describe('when given a valid operation', () => {
      it('creates a GraphQL Interaction', () => {
        interaction.uponReceiving('a request');
        interaction.withRequest({
          path: '/graphql',
          method: 'POST',
        });
        interaction.withOperation('query');
        interaction.withQuery('{ hello }');
        interaction.willRespondWith({
          status: 200,
          body: { data: {} },
        });

        const json: any = interaction.json();
        expect(json.request.body.operationName).to.eq('query');
      });
    });
    describe('when given an invalid operation', () => {
      it('fails with an error', () => {
        expect(interaction.withOperation.bind('aoeu')).to.throw(Error);
      });
    });
    describe('when given a null operation', () => {
      it('creates a GrphQL Interaction', () => {
        interaction.uponReceiving('a request');
        interaction.withRequest({
          path: '/graphql',
          method: 'POST',
        });
        interaction.withOperation(null);
        interaction.withQuery('{ hello }');
        interaction.willRespondWith({
          status: 200,
          body: { data: {} },
        });

        const json: any = interaction.json();
        expect(json.request.body.operationName).to.eq(null);
      });
    });
  });

  describe('#withVariables', () => {
    describe('when given a set of variables', () => {
      it('adds the variables to the payload', () => {
        interaction.uponReceiving('a request');
        interaction.withRequest({
          path: '/graphql',
          method: 'POST',
        });
        interaction.withOperation('query');
        interaction.withQuery('{ hello }');
        interaction.withVariables({
          foo: 'bar',
        });
        interaction.willRespondWith({
          status: 200,
          body: { data: {} },
        });

        const json: any = interaction.json();
        expect(json.request.body.variables).to.deep.eq({ foo: 'bar' });
      });
    });
    describe('when no variables are provided', () => {
      it('does not add the variables property to the payload', () => {
        interaction.uponReceiving('a request');
        interaction.withRequest({
          path: '/graphql',
          method: 'POST',
        });
        interaction.withOperation('query');
        interaction.withQuery('{ hello }');
        interaction.willRespondWith({
          status: 200,
          body: { data: {} },
        });

        const json: any = interaction.json();
        expect(json.request.body).to.not.have.property('variables');
      });
    });
    describe('when an empty variables object is presented', () => {
      it('adds the variables property to the payload', () => {
        interaction.uponReceiving('a request');
        interaction.withRequest({
          path: '/graphql',
          method: 'POST',
        });
        interaction.withOperation('query');
        interaction.withQuery('{ hello }');
        interaction.withVariables({});
        interaction.willRespondWith({
          status: 200,
          body: { data: {} },
        });

        const json: any = interaction.json();
        expect(json.request.body).to.have.property('variables');
      });
    });
  });

  describe('#withQuery', () => {
    beforeEach(() => {
      interaction.uponReceiving('a request');
      interaction.withRequest({
        path: '/graphql',
        method: 'POST',
      });
      interaction.withOperation('query');
      interaction.withQuery('{ hello }');
      interaction.withVariables({
        foo: 'bar',
      });
      interaction.willRespondWith({
        status: 200,
        body: { data: {} },
      });
    });

    describe('when given an empty query', () => {
      it('fails with an error', () => {
        expect(() => interaction.withQuery(null as any)).to.throw();
      });
    });

    describe('when given an invalid query', () => {
      it('fails with an error', () => {
        expect(() =>
          interaction.withQuery('{ not properly terminated')
        ).to.throw(Error);
      });
    });

    describe('when given a valid query', () => {
      describe('without variables', () => {
        it('adds regular expressions for the whitespace in the query', () => {
          interaction.uponReceiving('a request');
          interaction.withRequest({
            path: '/graphql',
            method: 'POST',
          });
          interaction.withOperation('query');
          interaction.withQuery('{ hello }');
          interaction.willRespondWith({
            status: 200,
            body: { data: {} },
          });

          const json: any = interaction.json();

          expect(isMatcher(json.request.body.query)).to.eq(true);
          const r = new RegExp(json.request.body.query.regex, 'g');
          const lotsOfWhitespace = `{             hello

        }`;
          expect(r.test(lotsOfWhitespace)).to.eq(true);
        });
      });

      describe('and variables', () => {
        it('adds regular expressions for the whitespace in the query', () => {
          interaction.withQuery(`{
            Hello(id: $id) {
              name
            }
          }`);
          interaction.withVariables({
            name: 'bar',
          });
          const json: any = interaction.json();

          expect(isMatcher(json.request.body.query)).to.eq(true);
          const r = new RegExp(json.request.body.query.regex, 'g');
          const lotsOfWhitespace = `{             Hello(id: $id) { name    } }`;
          expect(r.test(lotsOfWhitespace)).to.eq(true);
        });
      });
    });
  });

  describe('#json', () => {
    context('when query is empty', () => {
      it('fails with an error', () => {
        expect(() => interaction.json()).to.throw();
      });
    });

    context('when description is empty', () => {
      it('fails with an error', () => {
        interaction.withQuery('{ hello }');

        return expect(() => interaction.json()).to.throw();
      });
    });

    describe('when no operation is provided', () => {
      it('does not be present in unmarshaled body', () => {
        interaction.uponReceiving('a request');
        interaction.withRequest({
          path: '/graphql',
          method: 'POST',
        });
        interaction.withQuery('{ hello }');
        interaction.willRespondWith({
          status: 200,
          body: { data: {} },
        });

        const json: any = interaction.json();
        expect(json.request.body).to.not.have.property('operationName');
      });
    });
  });

  context('when given a valid query', () => {
    it('marshals the query to JSON', () => {
      interaction.uponReceiving('a request');
      interaction.withRequest({
        path: '/graphql',
        method: 'POST',
      });
      interaction.withOperation('query');
      interaction.withQuery('{ hello }');
      interaction.willRespondWith({
        status: 200,
        body: { data: {} },
      });

      const json: any = interaction.json();
      expect(isMatcher(json.request.body.query)).to.eq(true);
      expect(json.request.body.query.getValue()).to.eq('{ hello }');
    });
  });

  context('headers are not duplicated', () => {
    describe('headers are properly cased', () => {
      it('content-type header is properly cased', () => {
        interaction.uponReceiving('a request');
        interaction.withRequest({
          path: '/graphql',
          method: 'POST',
        });
        interaction.withOperation('query');
        interaction.withQuery('{ hello }');
        interaction.willRespondWith({
          status: 200,
          body: { data: {} },
        });

        const json: any = interaction.json();
        expect(json.request.headers).to.deep.eq({
          'Content-Type': 'application/json',
        });
      });
    });
  });
});