2016-09-12 15:02:41 -07:00
|
|
|
import { expect } from 'chai';
|
|
|
|
import { stub } from 'sinon';
|
2016-10-03 23:13:08 +03:00
|
|
|
import 'mocha';
|
2016-06-10 17:05:39 -07:00
|
|
|
|
|
|
|
import {
|
|
|
|
GraphQLSchema,
|
|
|
|
GraphQLObjectType,
|
2016-06-10 20:48:21 -04:00
|
|
|
GraphQLString,
|
2016-06-13 16:20:21 -07:00
|
|
|
GraphQLInt,
|
|
|
|
GraphQLNonNull,
|
|
|
|
parse,
|
2016-06-10 17:05:39 -07:00
|
|
|
} from 'graphql';
|
|
|
|
|
2016-09-10 17:28:38 -05:00
|
|
|
import {
|
|
|
|
runQuery,
|
|
|
|
LogAction,
|
|
|
|
LogStep,
|
|
|
|
} from './runQuery';
|
2016-06-10 17:05:39 -07:00
|
|
|
|
2016-08-14 03:19:31 -04:00
|
|
|
// Make the global Promise constructor Fiber-aware to simulate a Meteor
|
|
|
|
// environment.
|
|
|
|
import { makeCompatible } from 'meteor-promise';
|
|
|
|
import Fiber = require('fibers');
|
|
|
|
makeCompatible(Promise, Fiber);
|
|
|
|
|
2017-01-23 13:10:42 +02:00
|
|
|
const queryType = new GraphQLObjectType({
|
2016-06-10 17:05:39 -07:00
|
|
|
name: 'QueryType',
|
|
|
|
fields: {
|
|
|
|
testString: {
|
|
|
|
type: GraphQLString,
|
2016-06-10 20:48:21 -04:00
|
|
|
resolve() {
|
2016-06-10 17:05:39 -07:00
|
|
|
return 'it works';
|
|
|
|
},
|
|
|
|
},
|
2016-06-13 16:20:21 -07:00
|
|
|
testRootValue: {
|
|
|
|
type: GraphQLString,
|
|
|
|
resolve(root) {
|
|
|
|
return root + ' works';
|
|
|
|
},
|
|
|
|
},
|
|
|
|
testContextValue: {
|
|
|
|
type: GraphQLString,
|
|
|
|
resolve(root, args, context) {
|
|
|
|
return context + ' works';
|
|
|
|
},
|
|
|
|
},
|
|
|
|
testArgumentValue: {
|
|
|
|
type: GraphQLInt,
|
|
|
|
resolve(root, args, context) {
|
|
|
|
return args['base'] + 5;
|
|
|
|
},
|
|
|
|
args: {
|
|
|
|
base: { type: new GraphQLNonNull(GraphQLInt) },
|
|
|
|
},
|
|
|
|
},
|
2016-08-14 03:19:31 -04:00
|
|
|
testAwaitedValue: {
|
|
|
|
type: GraphQLString,
|
|
|
|
resolve(root) {
|
|
|
|
// Calling Promise.await is legal here even though this is
|
|
|
|
// not an async function, because we are guaranteed to be
|
|
|
|
// running in a Fiber.
|
|
|
|
return 'it ' + (<any>Promise).await('works');
|
|
|
|
},
|
|
|
|
},
|
2016-09-12 15:02:41 -07:00
|
|
|
testError: {
|
|
|
|
type: GraphQLString,
|
|
|
|
resolve() {
|
|
|
|
throw new Error('Secret error message');
|
|
|
|
},
|
|
|
|
},
|
2016-06-10 17:05:39 -07:00
|
|
|
},
|
|
|
|
});
|
|
|
|
|
2017-01-23 13:10:42 +02:00
|
|
|
const schema = new GraphQLSchema({
|
|
|
|
query: queryType,
|
2016-06-10 17:05:39 -07:00
|
|
|
});
|
|
|
|
|
|
|
|
describe('runQuery', () => {
|
2016-06-13 16:20:21 -07:00
|
|
|
it('returns the right result when query is a string', () => {
|
2016-06-10 17:05:39 -07:00
|
|
|
const query = `{ testString }`;
|
2016-06-13 15:27:08 -07:00
|
|
|
const expected = { testString: 'it works' };
|
2017-01-23 13:10:42 +02:00
|
|
|
return runQuery({ schema, query: query })
|
2016-06-13 16:20:21 -07:00
|
|
|
.then((res) => {
|
|
|
|
return expect(res.data).to.deep.equal(expected);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('returns the right result when query is a document', () => {
|
|
|
|
const query = parse(`{ testString }`);
|
|
|
|
const expected = { testString: 'it works' };
|
2017-01-23 13:10:42 +02:00
|
|
|
return runQuery({ schema, query: query })
|
2016-06-13 16:20:21 -07:00
|
|
|
.then((res) => {
|
|
|
|
return expect(res.data).to.deep.equal(expected);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2016-09-12 15:02:41 -07:00
|
|
|
it('returns a syntax error if the query string contains one', () => {
|
|
|
|
const query = `query { test `;
|
|
|
|
const expected = /Syntax Error GraphQL/;
|
|
|
|
return runQuery({
|
2017-01-23 13:10:42 +02:00
|
|
|
schema,
|
2016-09-12 15:02:41 -07:00
|
|
|
query: query,
|
|
|
|
variables: { base: 1 },
|
|
|
|
}).then((res) => {
|
|
|
|
expect(res.data).to.be.undefined;
|
|
|
|
expect(res.errors.length).to.equal(1);
|
|
|
|
return expect(res.errors[0].message).to.match(expected);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('sends stack trace to error if in an error occurs and debug mode is set', () => {
|
|
|
|
const query = `query { testError }`;
|
|
|
|
const expected = /at resolveOrError/;
|
|
|
|
const logStub = stub(console, 'error');
|
|
|
|
return runQuery({
|
2017-01-23 13:10:42 +02:00
|
|
|
schema,
|
2016-09-12 15:02:41 -07:00
|
|
|
query: query,
|
|
|
|
debug: true,
|
|
|
|
}).then((res) => {
|
|
|
|
logStub.restore();
|
|
|
|
expect(logStub.callCount).to.equal(1);
|
|
|
|
return expect(logStub.getCall(0).args[0]).to.match(expected);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('does not send stack trace if in an error occurs and not in debug mode', () => {
|
|
|
|
const query = `query { testError }`;
|
|
|
|
const logStub = stub(console, 'error');
|
|
|
|
return runQuery({
|
2017-01-23 13:10:42 +02:00
|
|
|
schema,
|
2016-09-12 15:02:41 -07:00
|
|
|
query: query,
|
|
|
|
debug: false,
|
|
|
|
}).then((res) => {
|
|
|
|
logStub.restore();
|
|
|
|
return expect(logStub.callCount).to.equal(0);
|
|
|
|
});
|
2016-06-13 17:16:42 -07:00
|
|
|
});
|
|
|
|
|
2016-06-13 16:20:21 -07:00
|
|
|
it('returns a validation error if the query string does not pass validation', () => {
|
|
|
|
const query = `query TestVar($base: String){ testArgumentValue(base: $base) }`;
|
|
|
|
const expected = 'Variable "$base" of type "String" used in position expecting type "Int!".';
|
|
|
|
return runQuery({
|
2017-01-23 13:10:42 +02:00
|
|
|
schema,
|
2016-06-13 16:20:21 -07:00
|
|
|
query: query,
|
|
|
|
variables: { base: 1 },
|
|
|
|
}).then((res) => {
|
|
|
|
expect(res.data).to.be.undefined;
|
|
|
|
expect(res.errors.length).to.equal(1);
|
|
|
|
return expect(res.errors[0].message).to.deep.equal(expected);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('does not run validation if the query is a document', () => {
|
|
|
|
// this would not pass validation, because $base ought to be Int!, not String
|
|
|
|
// what effecively happens is string concatentation, but it's returned as Int
|
|
|
|
const query = parse(`query TestVar($base: String){ testArgumentValue(base: $base) }`);
|
|
|
|
const expected = { testArgumentValue: 15 };
|
|
|
|
return runQuery({
|
2017-01-23 13:10:42 +02:00
|
|
|
schema,
|
2016-06-13 16:20:21 -07:00
|
|
|
query: query,
|
|
|
|
variables: { base: 1 },
|
|
|
|
}).then((res) => {
|
|
|
|
return expect(res.data).to.deep.equal(expected);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('correctly passes in the rootValue', () => {
|
|
|
|
const query = `{ testRootValue }`;
|
|
|
|
const expected = { testRootValue: 'it also works' };
|
2017-01-23 13:10:42 +02:00
|
|
|
return runQuery({ schema, query: query, rootValue: 'it also' })
|
2016-06-13 16:20:21 -07:00
|
|
|
.then((res) => {
|
|
|
|
return expect(res.data).to.deep.equal(expected);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('correctly passes in the context', () => {
|
|
|
|
const query = `{ testContextValue }`;
|
|
|
|
const expected = { testContextValue: 'it still works' };
|
2017-01-23 13:10:42 +02:00
|
|
|
return runQuery({ schema, query: query, context: 'it still' })
|
2016-06-13 16:20:21 -07:00
|
|
|
.then((res) => {
|
|
|
|
return expect(res.data).to.deep.equal(expected);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2016-08-05 11:42:46 -07:00
|
|
|
it('passes the options to formatResponse', () => {
|
|
|
|
const query = `{ testContextValue }`;
|
|
|
|
const expected = { testContextValue: 'it still works' };
|
|
|
|
return runQuery({
|
2017-01-23 13:10:42 +02:00
|
|
|
schema,
|
2016-08-05 11:42:46 -07:00
|
|
|
query: query,
|
|
|
|
context: 'it still',
|
|
|
|
formatResponse: (response, { context }) => {
|
|
|
|
response['extensions'] = context;
|
|
|
|
return response;
|
|
|
|
},
|
|
|
|
})
|
|
|
|
.then((res) => {
|
|
|
|
expect(res.data).to.deep.equal(expected);
|
|
|
|
return expect(res['extensions']).to.equal('it still');
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2016-06-13 16:20:21 -07:00
|
|
|
it('correctly passes in variables (and arguments)', () => {
|
|
|
|
const query = `query TestVar($base: Int!){ testArgumentValue(base: $base) }`;
|
|
|
|
const expected = { testArgumentValue: 6 };
|
|
|
|
return runQuery({
|
2017-01-23 13:10:42 +02:00
|
|
|
schema,
|
2016-06-13 16:20:21 -07:00
|
|
|
query: query,
|
|
|
|
variables: { base: 1 },
|
|
|
|
}).then((res) => {
|
|
|
|
return expect(res.data).to.deep.equal(expected);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2016-06-27 19:20:06 -04:00
|
|
|
it('throws an error if there are missing variables', () => {
|
2016-06-26 21:06:37 -04:00
|
|
|
const query = `query TestVar($base: Int!){ testArgumentValue(base: $base) }`;
|
|
|
|
const expected = 'Variable "$base" of required type "Int!" was not provided.';
|
|
|
|
return runQuery({
|
2017-01-23 13:10:42 +02:00
|
|
|
schema,
|
2016-06-26 21:06:37 -04:00
|
|
|
query: query,
|
|
|
|
}).then((res) => {
|
|
|
|
return expect(res.errors[0].message).to.deep.equal(expected);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2016-08-14 03:19:31 -04:00
|
|
|
it('supports yielding resolver functions', () => {
|
|
|
|
return runQuery({
|
2017-01-23 13:10:42 +02:00
|
|
|
schema,
|
2016-08-14 03:19:31 -04:00
|
|
|
query: `{ testAwaitedValue }`,
|
|
|
|
}).then((res) => {
|
|
|
|
expect(res.data).to.deep.equal({
|
|
|
|
testAwaitedValue: 'it works',
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
2016-06-26 21:06:37 -04:00
|
|
|
|
2016-06-13 16:20:21 -07:00
|
|
|
it('runs the correct operation when operationName is specified', () => {
|
2016-06-26 21:06:37 -04:00
|
|
|
const query = `
|
2016-06-13 16:20:21 -07:00
|
|
|
query Q1 {
|
|
|
|
testString
|
|
|
|
}
|
|
|
|
query Q2 {
|
|
|
|
testRootValue
|
|
|
|
}`;
|
2016-06-26 21:06:37 -04:00
|
|
|
const expected = {
|
|
|
|
testString: 'it works',
|
2016-06-13 16:20:21 -07:00
|
|
|
};
|
2017-01-23 13:10:42 +02:00
|
|
|
return runQuery({ schema, query: query, operationName: 'Q1' })
|
2016-06-26 21:06:37 -04:00
|
|
|
.then((res) => {
|
|
|
|
return expect(res.data).to.deep.equal(expected);
|
|
|
|
});
|
|
|
|
});
|
2016-08-05 11:14:56 -07:00
|
|
|
|
|
|
|
it('calls logFunction', () => {
|
|
|
|
const query = `
|
|
|
|
query Q1 {
|
|
|
|
testString
|
|
|
|
}`;
|
|
|
|
const logs = [];
|
2016-09-10 17:28:38 -05:00
|
|
|
const logFn = (obj) => logs.push(obj);
|
2016-08-05 11:14:56 -07:00
|
|
|
const expected = {
|
|
|
|
testString: 'it works',
|
|
|
|
};
|
|
|
|
return runQuery({
|
2017-01-23 13:10:42 +02:00
|
|
|
schema,
|
2016-08-05 11:14:56 -07:00
|
|
|
query: query,
|
|
|
|
operationName: 'Q1',
|
|
|
|
variables: { test: 123 },
|
|
|
|
logFunction: logFn,
|
|
|
|
})
|
|
|
|
.then((res) => {
|
|
|
|
expect(res.data).to.deep.equal(expected);
|
|
|
|
expect(logs.length).to.equals(11);
|
2016-09-10 17:28:38 -05:00
|
|
|
expect(logs[0]).to.deep.equals({action: LogAction.request, step: LogStep.start});
|
|
|
|
expect(logs[1]).to.deep.equals({action: LogAction.request, step: LogStep.status, key: 'query', data: query});
|
|
|
|
expect(logs[2]).to.deep.equals({action: LogAction.request, step: LogStep.status, key: 'variables', data: { test: 123 }});
|
|
|
|
expect(logs[3]).to.deep.equals({action: LogAction.request, step: LogStep.status, key: 'operationName', data: 'Q1'});
|
|
|
|
expect(logs[10]).to.deep.equals({action: LogAction.request, step: LogStep.end});
|
2016-08-05 11:14:56 -07:00
|
|
|
});
|
|
|
|
});
|
2016-06-10 20:48:21 -04:00
|
|
|
});
|