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-10-20 21:16:53 +03:00
|
|
|
import * as querystring from 'querystring';
|
2016-07-03 08:42:59 -07:00
|
|
|
|
|
|
|
import {
|
|
|
|
GraphQLSchema,
|
|
|
|
GraphQLObjectType,
|
|
|
|
GraphQLString,
|
2017-01-23 09:21:22 +02:00
|
|
|
GraphQLInt,
|
2016-07-29 11:51:25 -07:00
|
|
|
GraphQLError,
|
2017-01-18 10:49:13 +02:00
|
|
|
GraphQLNonNull,
|
2016-10-17 07:18:31 -07:00
|
|
|
introspectionQuery,
|
2016-07-29 11:51:25 -07:00
|
|
|
BREAK,
|
2016-07-03 08:42:59 -07:00
|
|
|
} from 'graphql';
|
|
|
|
|
|
|
|
// tslint:disable-next-line
|
|
|
|
const request = require('supertest-as-promised');
|
|
|
|
|
2016-10-22 23:56:14 -07:00
|
|
|
import { GraphQLOptions } from 'graphql-server-core';
|
2016-10-18 09:36:07 +03:00
|
|
|
import * as GraphiQL from 'graphql-server-module-graphiql';
|
|
|
|
import { OperationStore } from 'graphql-server-module-operation-store';
|
2016-07-03 08:42:59 -07:00
|
|
|
|
2017-01-23 13:10:42 +02:00
|
|
|
const queryType = new GraphQLObjectType({
|
2016-07-03 08:42:59 -07:00
|
|
|
name: 'QueryType',
|
|
|
|
fields: {
|
|
|
|
testString: {
|
|
|
|
type: GraphQLString,
|
2016-07-29 11:36:22 -07:00
|
|
|
resolve() {
|
2016-07-03 08:42:59 -07:00
|
|
|
return 'it works';
|
|
|
|
},
|
|
|
|
},
|
2017-01-23 09:21:22 +02:00
|
|
|
testStringWithDelay: {
|
|
|
|
type: GraphQLString,
|
|
|
|
args: {
|
|
|
|
delay: { type: new GraphQLNonNull(GraphQLInt) },
|
|
|
|
},
|
|
|
|
resolve(root, args) {
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
setTimeout(() => resolve('it works'), args['delay']);
|
|
|
|
});
|
|
|
|
},
|
|
|
|
},
|
2016-07-29 11:36:22 -07:00
|
|
|
testContext: {
|
|
|
|
type: GraphQLString,
|
|
|
|
resolve(_, args, context) {
|
2016-10-20 21:52:42 -07:00
|
|
|
if (context.otherField) {
|
|
|
|
return 'unexpected';
|
|
|
|
}
|
|
|
|
context.otherField = true;
|
|
|
|
return context.testField;
|
2016-07-29 11:36:22 -07:00
|
|
|
},
|
|
|
|
},
|
|
|
|
testRootValue: {
|
|
|
|
type: GraphQLString,
|
|
|
|
resolve(rootValue) {
|
|
|
|
return rootValue;
|
|
|
|
},
|
|
|
|
},
|
2016-07-03 08:42:59 -07:00
|
|
|
testArgument: {
|
|
|
|
type: GraphQLString,
|
|
|
|
args: { echo: { type: GraphQLString } },
|
|
|
|
resolve(root, { echo }) {
|
|
|
|
return `hello ${echo}`;
|
|
|
|
},
|
|
|
|
},
|
2016-07-29 11:42:05 -07:00
|
|
|
testError: {
|
|
|
|
type: GraphQLString,
|
|
|
|
resolve() {
|
|
|
|
throw new Error('Secret error message');
|
|
|
|
},
|
|
|
|
},
|
2016-07-03 08:42:59 -07:00
|
|
|
},
|
|
|
|
});
|
|
|
|
|
2017-01-23 13:10:42 +02:00
|
|
|
const personType = new GraphQLObjectType({
|
2017-01-18 10:49:13 +02:00
|
|
|
name: 'PersonType',
|
|
|
|
fields: {
|
|
|
|
firstName: {
|
|
|
|
type: GraphQLString,
|
|
|
|
},
|
|
|
|
lastName: {
|
|
|
|
type: GraphQLString,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
2017-01-23 13:10:42 +02:00
|
|
|
const mutationType = new GraphQLObjectType({
|
2016-07-03 08:42:59 -07:00
|
|
|
name: 'MutationType',
|
|
|
|
fields: {
|
|
|
|
testMutation: {
|
|
|
|
type: GraphQLString,
|
|
|
|
args: { echo: { type: GraphQLString } },
|
|
|
|
resolve(root, { echo }) {
|
|
|
|
return `not really a mutation, but who cares: ${echo}`;
|
|
|
|
},
|
|
|
|
},
|
2017-01-18 10:49:13 +02:00
|
|
|
testPerson: {
|
2017-01-23 13:10:42 +02:00
|
|
|
type: personType,
|
2017-01-18 10:49:13 +02:00
|
|
|
args: {
|
|
|
|
firstName: {
|
|
|
|
type: new GraphQLNonNull(GraphQLString),
|
|
|
|
},
|
|
|
|
lastName: {
|
|
|
|
type: new GraphQLNonNull(GraphQLString),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
resolve(root, args) {
|
|
|
|
return args;
|
|
|
|
},
|
|
|
|
},
|
2016-07-03 08:42:59 -07:00
|
|
|
},
|
|
|
|
});
|
|
|
|
|
2017-01-23 13:10:42 +02:00
|
|
|
export const schema = new GraphQLSchema({
|
|
|
|
query: queryType,
|
|
|
|
mutation: mutationType,
|
2016-07-03 08:42:59 -07:00
|
|
|
});
|
|
|
|
|
|
|
|
export interface CreateAppOptions {
|
|
|
|
excludeParser?: boolean;
|
2016-10-22 23:56:14 -07:00
|
|
|
graphqlOptions?: GraphQLOptions | {(): GraphQLOptions | Promise<{}>};
|
2016-07-03 08:42:59 -07:00
|
|
|
graphiqlOptions?: GraphiQL.GraphiQLData;
|
|
|
|
}
|
|
|
|
|
|
|
|
export interface CreateAppFunc {
|
|
|
|
(options?: CreateAppOptions): void;
|
|
|
|
}
|
|
|
|
|
2016-07-28 20:05:05 +02:00
|
|
|
export interface DestroyAppFunc {
|
|
|
|
(app: any): void;
|
|
|
|
}
|
|
|
|
|
2016-07-28 22:56:34 +02:00
|
|
|
export default (createApp: CreateAppFunc, destroyApp?: DestroyAppFunc) => {
|
2016-07-07 08:46:10 -07:00
|
|
|
describe('apolloServer', () => {
|
2016-07-28 23:15:30 +02:00
|
|
|
let app;
|
|
|
|
|
|
|
|
afterEach(() => {
|
|
|
|
if (app) {
|
|
|
|
if (destroyApp) {
|
|
|
|
destroyApp(app);
|
|
|
|
} else {
|
|
|
|
app = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2016-07-18 21:13:57 -07:00
|
|
|
describe('graphqlHTTP', () => {
|
|
|
|
it('can be called with an options function', () => {
|
2017-01-23 13:10:42 +02:00
|
|
|
app = createApp({graphqlOptions: (): GraphQLOptions => ({schema})});
|
2016-07-18 21:13:57 -07:00
|
|
|
const expected = {
|
|
|
|
testString: 'it works',
|
|
|
|
};
|
|
|
|
const req = request(app)
|
|
|
|
.post('/graphql')
|
|
|
|
.send({
|
|
|
|
query: 'query test{ testString }',
|
|
|
|
});
|
|
|
|
return req.then((res) => {
|
|
|
|
expect(res.status).to.equal(200);
|
|
|
|
return expect(res.body.data).to.deep.equal(expected);
|
|
|
|
});
|
|
|
|
});
|
2016-07-03 08:42:59 -07:00
|
|
|
|
2016-07-18 21:13:57 -07:00
|
|
|
it('can be called with an options function that returns a promise', () => {
|
2016-10-22 23:52:32 -07:00
|
|
|
app = createApp({ graphqlOptions: () => {
|
2016-07-18 21:13:57 -07:00
|
|
|
return new Promise(resolve => {
|
2017-01-23 13:10:42 +02:00
|
|
|
resolve({schema});
|
2016-07-18 21:13:57 -07:00
|
|
|
});
|
|
|
|
}});
|
|
|
|
const expected = {
|
|
|
|
testString: 'it works',
|
|
|
|
};
|
|
|
|
const req = request(app)
|
|
|
|
.post('/graphql')
|
|
|
|
.send({
|
|
|
|
query: 'query test{ testString }',
|
|
|
|
});
|
|
|
|
return req.then((res) => {
|
|
|
|
expect(res.status).to.equal(200);
|
|
|
|
return expect(res.body.data).to.deep.equal(expected);
|
|
|
|
});
|
|
|
|
});
|
2016-07-05 14:19:14 -07:00
|
|
|
|
2016-07-18 21:13:57 -07:00
|
|
|
it('throws an error if options promise is rejected', () => {
|
2016-10-22 23:52:32 -07:00
|
|
|
app = createApp({ graphqlOptions: () => {
|
2016-10-22 23:56:14 -07:00
|
|
|
return Promise.reject({}) as any as GraphQLOptions;
|
2016-07-18 21:13:57 -07:00
|
|
|
}});
|
|
|
|
const expected = 'Invalid options';
|
|
|
|
const req = request(app)
|
|
|
|
.post('/graphql')
|
|
|
|
.send({
|
|
|
|
query: 'query test{ testString }',
|
|
|
|
});
|
|
|
|
return req.then((res) => {
|
|
|
|
expect(res.status).to.equal(500);
|
|
|
|
return expect(res.error.text).to.contain(expected);
|
|
|
|
});
|
|
|
|
});
|
2016-07-03 08:42:59 -07:00
|
|
|
|
2016-10-20 21:16:53 +03:00
|
|
|
it('rejects the request if the method is not POST or GET', () => {
|
2016-07-29 12:47:27 -07:00
|
|
|
app = createApp({excludeParser: true});
|
|
|
|
const req = request(app)
|
2016-10-20 21:16:53 +03:00
|
|
|
.head('/graphql')
|
2016-07-29 12:47:27 -07:00
|
|
|
.send();
|
|
|
|
return req.then((res) => {
|
2016-10-20 22:01:23 +03:00
|
|
|
expect(res.status).to.equal(405);
|
2017-01-13 13:49:25 +02:00
|
|
|
expect(res.headers['allow']).to.equal('GET, POST');
|
2016-07-29 12:47:27 -07:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2016-07-18 21:13:57 -07:00
|
|
|
it('throws an error if POST body is missing', () => {
|
2016-07-28 23:15:30 +02:00
|
|
|
app = createApp({excludeParser: true});
|
2016-07-18 21:13:57 -07:00
|
|
|
const req = request(app)
|
|
|
|
.post('/graphql')
|
2016-07-18 21:17:52 -07:00
|
|
|
.send();
|
2016-07-18 21:13:57 -07:00
|
|
|
return req.then((res) => {
|
|
|
|
expect(res.status).to.equal(500);
|
|
|
|
return expect(res.error.text).to.contain('POST body missing.');
|
|
|
|
});
|
|
|
|
});
|
2016-07-03 08:42:59 -07:00
|
|
|
|
2016-12-16 15:46:57 +02:00
|
|
|
it('throws an error if GET query is missing', () => {
|
|
|
|
app = createApp();
|
|
|
|
const req = request(app)
|
|
|
|
.get(`/graphql`);
|
|
|
|
return req.then((res) => {
|
|
|
|
expect(res.status).to.equal(400);
|
|
|
|
return expect(res.error.text).to.contain('GET query missing.');
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2016-10-20 21:16:53 +03:00
|
|
|
it('can handle a basic GET request', () => {
|
|
|
|
app = createApp();
|
|
|
|
const expected = {
|
|
|
|
testString: 'it works',
|
|
|
|
};
|
|
|
|
const query = {
|
|
|
|
query: 'query test{ testString }',
|
|
|
|
};
|
|
|
|
const req = request(app)
|
|
|
|
.get(`/graphql?${querystring.stringify(query)}`);
|
|
|
|
return req.then((res) => {
|
|
|
|
expect(res.status).to.equal(200);
|
|
|
|
return expect(res.body.data).to.deep.equal(expected);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2017-01-13 16:42:49 +02:00
|
|
|
it('can handle a basic implicit GET request', () => {
|
|
|
|
app = createApp();
|
|
|
|
const expected = {
|
|
|
|
testString: 'it works',
|
|
|
|
};
|
|
|
|
const query = {
|
|
|
|
query: '{ testString }',
|
|
|
|
};
|
|
|
|
const req = request(app)
|
|
|
|
.get(`/graphql?${querystring.stringify(query)}`);
|
|
|
|
return req.then((res) => {
|
|
|
|
expect(res.status).to.equal(200);
|
|
|
|
return expect(res.body.data).to.deep.equal(expected);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2017-01-18 10:49:13 +02:00
|
|
|
it('throws error if trying to use mutation using GET request', () => {
|
2017-01-13 16:25:37 +02:00
|
|
|
app = createApp();
|
|
|
|
const query = {
|
|
|
|
query: 'mutation test{ testMutation(echo: "ping") }',
|
|
|
|
};
|
|
|
|
const req = request(app)
|
|
|
|
.get(`/graphql?${querystring.stringify(query)}`);
|
|
|
|
return req.then((res) => {
|
|
|
|
expect(res.status).to.equal(405);
|
|
|
|
expect(res.headers['allow']).to.equal('POST');
|
|
|
|
return expect(res.error.text).to.contain('GET supports only query operation');
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2017-01-18 10:49:13 +02:00
|
|
|
it('throws error if trying to use mutation with fragment using GET request', () => {
|
|
|
|
app = createApp();
|
|
|
|
const query = {
|
|
|
|
query: `fragment PersonDetails on PersonType {
|
|
|
|
firstName
|
|
|
|
}
|
|
|
|
|
|
|
|
mutation test {
|
|
|
|
testPerson(firstName: "Test", lastName: "Me") {
|
|
|
|
...PersonDetails
|
|
|
|
}
|
|
|
|
}`,
|
|
|
|
};
|
|
|
|
const req = request(app)
|
|
|
|
.get(`/graphql?${querystring.stringify(query)}`);
|
|
|
|
return req.then((res) => {
|
|
|
|
expect(res.status).to.equal(405);
|
|
|
|
expect(res.headers['allow']).to.equal('POST');
|
|
|
|
return expect(res.error.text).to.contain('GET supports only query operation');
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2016-10-20 21:16:53 +03:00
|
|
|
it('can handle a GET request with variables', () => {
|
|
|
|
app = createApp();
|
|
|
|
const query = {
|
|
|
|
query: 'query test($echo: String){ testArgument(echo: $echo) }',
|
|
|
|
variables: JSON.stringify({ echo: 'world' }),
|
|
|
|
};
|
|
|
|
const expected = {
|
|
|
|
testArgument: 'hello world',
|
|
|
|
};
|
|
|
|
const req = request(app)
|
|
|
|
.get(`/graphql?${querystring.stringify(query)}`);
|
|
|
|
return req.then((res) => {
|
|
|
|
expect(res.status).to.equal(200);
|
|
|
|
return expect(res.body.data).to.deep.equal(expected);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2016-07-18 21:13:57 -07:00
|
|
|
it('can handle a basic request', () => {
|
2016-07-28 23:15:30 +02:00
|
|
|
app = createApp();
|
2016-07-18 21:13:57 -07:00
|
|
|
const expected = {
|
|
|
|
testString: 'it works',
|
|
|
|
};
|
|
|
|
const req = request(app)
|
|
|
|
.post('/graphql')
|
|
|
|
.send({
|
|
|
|
query: 'query test{ testString }',
|
|
|
|
});
|
|
|
|
return req.then((res) => {
|
|
|
|
expect(res.status).to.equal(200);
|
|
|
|
return expect(res.body.data).to.deep.equal(expected);
|
|
|
|
});
|
|
|
|
});
|
2016-07-03 08:42:59 -07:00
|
|
|
|
2016-07-18 21:13:57 -07:00
|
|
|
it('can handle a request with variables', () => {
|
2016-07-28 23:15:30 +02:00
|
|
|
app = createApp();
|
2016-07-18 21:13:57 -07:00
|
|
|
const expected = {
|
|
|
|
testArgument: 'hello world',
|
|
|
|
};
|
|
|
|
const req = request(app)
|
|
|
|
.post('/graphql')
|
|
|
|
.send({
|
|
|
|
query: 'query test($echo: String){ testArgument(echo: $echo) }',
|
|
|
|
variables: { echo: 'world' },
|
|
|
|
});
|
|
|
|
return req.then((res) => {
|
|
|
|
expect(res.status).to.equal(200);
|
|
|
|
return expect(res.body.data).to.deep.equal(expected);
|
|
|
|
});
|
|
|
|
});
|
2016-07-03 08:42:59 -07:00
|
|
|
|
2016-07-18 21:13:57 -07:00
|
|
|
it('can handle a request with variables as string', () => {
|
2016-07-28 23:15:30 +02:00
|
|
|
app = createApp();
|
2016-07-18 21:13:57 -07:00
|
|
|
const expected = {
|
|
|
|
testArgument: 'hello world',
|
|
|
|
};
|
|
|
|
const req = request(app)
|
|
|
|
.post('/graphql')
|
|
|
|
.send({
|
|
|
|
query: 'query test($echo: String!){ testArgument(echo: $echo) }',
|
|
|
|
variables: '{ "echo": "world" }',
|
|
|
|
});
|
|
|
|
return req.then((res) => {
|
|
|
|
expect(res.status).to.equal(200);
|
|
|
|
return expect(res.body.data).to.deep.equal(expected);
|
|
|
|
});
|
|
|
|
});
|
2016-07-03 08:42:59 -07:00
|
|
|
|
2016-09-09 20:30:27 -05:00
|
|
|
it('can handle a request with variables as an invalid string', () => {
|
|
|
|
app = createApp();
|
|
|
|
const req = request(app)
|
|
|
|
.post('/graphql')
|
|
|
|
.send({
|
|
|
|
query: 'query test($echo: String!){ testArgument(echo: $echo) }',
|
|
|
|
variables: '{ echo: "world" }',
|
|
|
|
});
|
|
|
|
return req.then((res) => {
|
|
|
|
expect(res.status).to.equal(400);
|
|
|
|
return expect(res.error.text).to.contain('Variables are invalid JSON.');
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2016-07-18 21:13:57 -07:00
|
|
|
it('can handle a request with operationName', () => {
|
2016-07-28 23:15:30 +02:00
|
|
|
app = createApp();
|
2016-07-18 21:13:57 -07:00
|
|
|
const expected = {
|
|
|
|
testString: 'it works',
|
|
|
|
};
|
|
|
|
const req = request(app)
|
|
|
|
.post('/graphql')
|
|
|
|
.send({
|
|
|
|
query: `
|
|
|
|
query test($echo: String){ testArgument(echo: $echo) }
|
|
|
|
query test2{ testString }`,
|
|
|
|
variables: { echo: 'world' },
|
|
|
|
operationName: 'test2',
|
|
|
|
});
|
|
|
|
return req.then((res) => {
|
|
|
|
expect(res.status).to.equal(200);
|
|
|
|
return expect(res.body.data).to.deep.equal(expected);
|
|
|
|
});
|
|
|
|
});
|
2016-07-03 08:42:59 -07:00
|
|
|
|
2016-10-17 07:18:31 -07:00
|
|
|
it('can handle introspection request', () => {
|
|
|
|
app = createApp();
|
|
|
|
const req = request(app)
|
|
|
|
.post('/graphql')
|
|
|
|
.send({query: introspectionQuery});
|
|
|
|
return req.then((res) => {
|
|
|
|
expect(res.status).to.equal(200);
|
|
|
|
return expect(res.body.data.__schema.types[0].fields[0].name).to.equal('testString');
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2016-07-18 21:13:57 -07:00
|
|
|
it('can handle batch requests', () => {
|
2016-07-28 23:15:30 +02:00
|
|
|
app = createApp();
|
2016-07-18 21:13:57 -07:00
|
|
|
const expected = [
|
|
|
|
{
|
|
|
|
data: {
|
|
|
|
testString: 'it works',
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
data: {
|
|
|
|
testArgument: 'hello yellow',
|
|
|
|
},
|
|
|
|
},
|
|
|
|
];
|
|
|
|
const req = request(app)
|
|
|
|
.post('/graphql')
|
|
|
|
.send([{
|
|
|
|
query: `
|
|
|
|
query test($echo: String){ testArgument(echo: $echo) }
|
|
|
|
query test2{ testString }`,
|
|
|
|
variables: { echo: 'world' },
|
|
|
|
operationName: 'test2',
|
|
|
|
},
|
|
|
|
{
|
|
|
|
query: `
|
|
|
|
query testX($echo: String){ testArgument(echo: $echo) }`,
|
|
|
|
variables: { echo: 'yellow' },
|
|
|
|
operationName: 'testX',
|
|
|
|
}]);
|
|
|
|
return req.then((res) => {
|
|
|
|
expect(res.status).to.equal(200);
|
|
|
|
return expect(res.body).to.deep.equal(expected);
|
|
|
|
});
|
|
|
|
});
|
2016-07-03 08:42:59 -07:00
|
|
|
|
2016-09-09 07:49:23 -05:00
|
|
|
it('can handle batch requests', () => {
|
|
|
|
app = createApp();
|
|
|
|
const expected = [
|
|
|
|
{
|
|
|
|
data: {
|
|
|
|
testString: 'it works',
|
|
|
|
},
|
|
|
|
},
|
|
|
|
];
|
|
|
|
const req = request(app)
|
|
|
|
.post('/graphql')
|
|
|
|
.send([{
|
|
|
|
query: `
|
|
|
|
query test($echo: String){ testArgument(echo: $echo) }
|
|
|
|
query test2{ testString }`,
|
|
|
|
variables: { echo: 'world' },
|
|
|
|
operationName: 'test2',
|
|
|
|
}]);
|
|
|
|
return req.then((res) => {
|
|
|
|
expect(res.status).to.equal(200);
|
|
|
|
return expect(res.body).to.deep.equal(expected);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2017-01-23 09:21:22 +02:00
|
|
|
it('can handle batch requests in parallel', function() {
|
|
|
|
// this test will fail due to timeout if running serially.
|
2017-01-23 10:43:53 +02:00
|
|
|
const parallels = 100;
|
|
|
|
const delayPerReq = 40;
|
|
|
|
this.timeout(2000);
|
2017-01-23 09:21:22 +02:00
|
|
|
|
|
|
|
app = createApp();
|
2017-01-23 10:43:53 +02:00
|
|
|
const expected = Array(parallels).fill({
|
2017-01-23 09:21:22 +02:00
|
|
|
data: { testStringWithDelay: 'it works' },
|
|
|
|
});
|
|
|
|
const req = request(app)
|
|
|
|
.post('/graphql')
|
2017-01-23 10:43:53 +02:00
|
|
|
.send(Array(parallels).fill({
|
2017-01-23 09:21:22 +02:00
|
|
|
query: `query test($delay: Int!) { testStringWithDelay(delay: $delay) }`,
|
|
|
|
operationName: 'test',
|
2017-01-23 10:43:53 +02:00
|
|
|
variables: { delay: delayPerReq },
|
2017-01-23 09:21:22 +02:00
|
|
|
}));
|
|
|
|
return req.then((res) => {
|
|
|
|
expect(res.status).to.equal(200);
|
|
|
|
return expect(res.body).to.deep.equal(expected);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2016-10-20 21:52:42 -07:00
|
|
|
it('clones batch context', () => {
|
2016-10-22 23:52:32 -07:00
|
|
|
app = createApp({graphqlOptions: {
|
2017-01-23 13:10:42 +02:00
|
|
|
schema,
|
2016-10-20 21:52:42 -07:00
|
|
|
context: {testField: 'expected'},
|
|
|
|
}});
|
|
|
|
const expected = [
|
|
|
|
{
|
|
|
|
data: {
|
|
|
|
testContext: 'expected',
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
data: {
|
|
|
|
testContext: 'expected',
|
|
|
|
},
|
|
|
|
},
|
|
|
|
];
|
|
|
|
const req = request(app)
|
|
|
|
.post('/graphql')
|
|
|
|
.send([{
|
|
|
|
query: 'query test{ testContext }',
|
|
|
|
}, {
|
|
|
|
query: 'query test{ testContext }',
|
|
|
|
}]);
|
|
|
|
return req.then((res) => {
|
|
|
|
expect(res.status).to.equal(200);
|
|
|
|
return expect(res.body).to.deep.equal(expected);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2016-07-18 21:13:57 -07:00
|
|
|
it('can handle a request with a mutation', () => {
|
2016-07-28 23:15:30 +02:00
|
|
|
app = createApp();
|
2016-07-18 21:13:57 -07:00
|
|
|
const expected = {
|
|
|
|
testMutation: 'not really a mutation, but who cares: world',
|
|
|
|
};
|
|
|
|
const req = request(app)
|
|
|
|
.post('/graphql')
|
|
|
|
.send({
|
|
|
|
query: 'mutation test($echo: String){ testMutation(echo: $echo) }',
|
|
|
|
variables: { echo: 'world' },
|
|
|
|
});
|
|
|
|
return req.then((res) => {
|
|
|
|
expect(res.status).to.equal(200);
|
|
|
|
return expect(res.body.data).to.deep.equal(expected);
|
|
|
|
});
|
|
|
|
});
|
2016-07-03 08:42:59 -07:00
|
|
|
|
|
|
|
it('applies the formatResponse function', () => {
|
2016-10-22 23:52:32 -07:00
|
|
|
app = createApp({graphqlOptions: {
|
2017-01-23 13:10:42 +02:00
|
|
|
schema,
|
2016-07-03 08:42:59 -07:00
|
|
|
formatResponse(response) {
|
|
|
|
response['extensions'] = { it: 'works' }; return response;
|
|
|
|
},
|
|
|
|
}});
|
|
|
|
const expected = { it: 'works' };
|
|
|
|
const req = request(app)
|
|
|
|
.post('/graphql')
|
|
|
|
.send({
|
|
|
|
query: 'mutation test($echo: String){ testMutation(echo: $echo) }',
|
|
|
|
variables: { echo: 'world' },
|
|
|
|
});
|
|
|
|
return req.then((res) => {
|
|
|
|
expect(res.status).to.equal(200);
|
|
|
|
return expect(res.body.extensions).to.deep.equal(expected);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2016-07-17 17:51:41 -07:00
|
|
|
it('passes the context to the resolver', () => {
|
2016-07-29 11:36:22 -07:00
|
|
|
const expected = 'context works';
|
2016-10-22 23:52:32 -07:00
|
|
|
app = createApp({graphqlOptions: {
|
2017-01-23 13:10:42 +02:00
|
|
|
schema,
|
2016-10-20 21:52:42 -07:00
|
|
|
context: {testField: expected},
|
2016-07-17 17:51:41 -07:00
|
|
|
}});
|
|
|
|
const req = request(app)
|
|
|
|
.post('/graphql')
|
|
|
|
.send({
|
2016-07-29 11:36:22 -07:00
|
|
|
query: 'query test{ testContext }',
|
|
|
|
});
|
|
|
|
return req.then((res) => {
|
|
|
|
expect(res.status).to.equal(200);
|
|
|
|
return expect(res.body.data.testContext).to.equal(expected);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('passes the rootValue to the resolver', () => {
|
|
|
|
const expected = 'it passes rootValue';
|
2016-10-22 23:52:32 -07:00
|
|
|
app = createApp({graphqlOptions: {
|
2017-01-23 13:10:42 +02:00
|
|
|
schema,
|
2016-07-29 11:36:22 -07:00
|
|
|
rootValue: expected,
|
|
|
|
}});
|
|
|
|
const req = request(app)
|
|
|
|
.post('/graphql')
|
|
|
|
.send({
|
|
|
|
query: 'query test{ testRootValue }',
|
2016-07-17 17:51:41 -07:00
|
|
|
});
|
|
|
|
return req.then((res) => {
|
|
|
|
expect(res.status).to.equal(200);
|
2016-07-29 11:36:22 -07:00
|
|
|
return expect(res.body.data.testRootValue).to.equal(expected);
|
2016-07-17 17:51:41 -07:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2016-07-29 11:42:05 -07:00
|
|
|
it('returns errors', () => {
|
|
|
|
const expected = 'Secret error message';
|
2016-10-22 23:52:32 -07:00
|
|
|
app = createApp({graphqlOptions: {
|
2017-01-23 13:10:42 +02:00
|
|
|
schema,
|
2016-07-29 11:42:05 -07:00
|
|
|
}});
|
|
|
|
const req = request(app)
|
|
|
|
.post('/graphql')
|
|
|
|
.send({
|
|
|
|
query: 'query test{ testError }',
|
|
|
|
});
|
|
|
|
return req.then((res) => {
|
|
|
|
expect(res.status).to.equal(200);
|
|
|
|
return expect(res.body.errors[0].message).to.equal(expected);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('applies formatError if provided', () => {
|
|
|
|
const expected = '--blank--';
|
2016-10-22 23:52:32 -07:00
|
|
|
app = createApp({graphqlOptions: {
|
2017-01-23 13:10:42 +02:00
|
|
|
schema,
|
2016-07-29 11:42:05 -07:00
|
|
|
formatError: (err) => ({ message: expected }),
|
|
|
|
}});
|
|
|
|
const req = request(app)
|
|
|
|
.post('/graphql')
|
|
|
|
.send({
|
|
|
|
query: 'query test{ testError }',
|
|
|
|
});
|
|
|
|
return req.then((res) => {
|
|
|
|
expect(res.status).to.equal(200);
|
|
|
|
return expect(res.body.errors[0].message).to.equal(expected);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2016-10-17 16:18:45 -03:00
|
|
|
it('sends internal server error when formatError fails', () => {
|
2016-10-22 23:52:32 -07:00
|
|
|
app = createApp({graphqlOptions: {
|
2017-01-23 13:10:42 +02:00
|
|
|
schema,
|
2016-10-17 13:55:15 -03:00
|
|
|
formatError: (err) => {
|
|
|
|
throw new Error('I should be catched');
|
|
|
|
},
|
|
|
|
}});
|
|
|
|
const req = request(app)
|
|
|
|
.post('/graphql')
|
|
|
|
.send({
|
|
|
|
query: 'query test{ testError }',
|
|
|
|
});
|
|
|
|
return req.then((res) => {
|
2016-10-17 16:17:23 -03:00
|
|
|
return expect(res.res.body.errors[0].message).to.equal('Internal server error');
|
2016-10-17 13:55:15 -03:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2016-09-12 15:02:41 -07:00
|
|
|
it('sends stack trace to error if debug mode is set', () => {
|
|
|
|
const expected = /at resolveOrError/;
|
|
|
|
const stackTrace = [];
|
|
|
|
const origError = console.error;
|
|
|
|
console.error = (...args) => stackTrace.push(args);
|
2016-10-22 23:52:32 -07:00
|
|
|
app = createApp({graphqlOptions: {
|
2017-01-23 13:10:42 +02:00
|
|
|
schema,
|
2016-09-12 15:02:41 -07:00
|
|
|
debug: true,
|
|
|
|
}});
|
|
|
|
const req = request(app)
|
|
|
|
.post('/graphql')
|
|
|
|
.send({
|
|
|
|
query: 'query test{ testError }',
|
|
|
|
});
|
|
|
|
return req.then((res) => {
|
|
|
|
console.error = origError;
|
|
|
|
return expect(stackTrace[0][0]).to.match(expected);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('sends stack trace to error log if debug mode is set', () => {
|
|
|
|
const logStub = stub(console, 'error');
|
|
|
|
const expected = /at resolveOrError/;
|
2016-10-22 23:52:32 -07:00
|
|
|
app = createApp({graphqlOptions: {
|
2017-01-23 13:10:42 +02:00
|
|
|
schema,
|
2016-09-12 15:02:41 -07:00
|
|
|
debug: true,
|
|
|
|
}});
|
|
|
|
const req = request(app)
|
|
|
|
.post('/graphql')
|
|
|
|
.send({
|
|
|
|
query: 'query test{ testError }',
|
|
|
|
});
|
|
|
|
return req.then((res) => {
|
|
|
|
logStub.restore();
|
|
|
|
expect(logStub.callCount).to.equal(1);
|
|
|
|
return expect(logStub.getCall(0).args[0]).to.match(expected);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2016-07-29 11:51:25 -07:00
|
|
|
it('applies additional validationRules', () => {
|
2017-01-23 13:10:42 +02:00
|
|
|
const expected = 'alwaysInvalidRule was really invalid!';
|
|
|
|
const alwaysInvalidRule = function (context) {
|
2016-07-29 11:51:25 -07:00
|
|
|
return {
|
|
|
|
enter() {
|
2017-01-23 13:10:42 +02:00
|
|
|
context.reportError(new GraphQLError(expected));
|
2016-07-29 11:51:25 -07:00
|
|
|
return BREAK;
|
|
|
|
},
|
|
|
|
};
|
|
|
|
};
|
2016-10-22 23:52:32 -07:00
|
|
|
app = createApp({graphqlOptions: {
|
2017-01-23 13:10:42 +02:00
|
|
|
schema,
|
|
|
|
validationRules: [alwaysInvalidRule],
|
2016-07-29 11:51:25 -07:00
|
|
|
}});
|
|
|
|
const req = request(app)
|
|
|
|
.post('/graphql')
|
|
|
|
.send({
|
|
|
|
query: 'query test{ testString }',
|
|
|
|
});
|
|
|
|
return req.then((res) => {
|
|
|
|
expect(res.status).to.equal(400);
|
|
|
|
return expect(res.body.errors[0].message).to.equal(expected);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2016-07-03 08:42:59 -07:00
|
|
|
});
|
|
|
|
|
2016-07-18 21:13:57 -07:00
|
|
|
describe('renderGraphiQL', () => {
|
|
|
|
it('presents GraphiQL when accepting HTML', () => {
|
2016-07-28 23:15:30 +02:00
|
|
|
app = createApp({graphiqlOptions: {
|
2016-07-18 21:13:57 -07:00
|
|
|
endpointURL: '/graphql',
|
|
|
|
}});
|
2016-07-03 08:42:59 -07:00
|
|
|
|
2016-07-18 21:13:57 -07:00
|
|
|
const req = request(app)
|
|
|
|
.get('/graphiql?query={test}')
|
|
|
|
.set('Accept', 'text/html');
|
|
|
|
return req.then((response) => {
|
|
|
|
expect(response.status).to.equal(200);
|
|
|
|
expect(response.type).to.equal('text/html');
|
|
|
|
expect(response.text).to.include('{test}');
|
|
|
|
expect(response.text).to.include('/graphql');
|
|
|
|
expect(response.text).to.include('graphiql.min.js');
|
|
|
|
});
|
|
|
|
});
|
2016-12-26 15:39:44 -06:00
|
|
|
|
|
|
|
it('presents options variables', () => {
|
|
|
|
app = createApp({graphiqlOptions: {
|
|
|
|
endpointURL: '/graphql',
|
|
|
|
variables: {key: 'optionsValue'},
|
|
|
|
}});
|
|
|
|
|
|
|
|
const req = request(app)
|
|
|
|
.get('/graphiql')
|
|
|
|
.set('Accept', 'text/html');
|
|
|
|
return req.then((response) => {
|
|
|
|
expect(response.status).to.equal(200);
|
|
|
|
expect(response.text.replace(/\s/g, '')).to.include('variables:"{\\n\\"key\\":\\"optionsValue\\"\\n}"');
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('presents query variables over options variables', () => {
|
|
|
|
app = createApp({graphiqlOptions: {
|
|
|
|
endpointURL: '/graphql',
|
|
|
|
variables: {key: 'optionsValue'},
|
|
|
|
}});
|
|
|
|
|
|
|
|
const req = request(app)
|
|
|
|
.get('/graphiql?variables={"key":"queryValue"}')
|
|
|
|
.set('Accept', 'text/html');
|
|
|
|
return req.then((response) => {
|
|
|
|
expect(response.status).to.equal(200);
|
|
|
|
expect(response.text.replace(/\s/g, '')).to.include('variables:"{\\n\\"key\\":\\"queryValue\\"\\n}"');
|
|
|
|
});
|
2016-07-18 21:13:57 -07:00
|
|
|
});
|
2016-07-03 08:42:59 -07:00
|
|
|
});
|
|
|
|
|
2016-07-18 21:13:57 -07:00
|
|
|
describe('stored queries', () => {
|
|
|
|
it('works with formatParams', () => {
|
2017-01-23 13:10:42 +02:00
|
|
|
const store = new OperationStore(schema);
|
2016-07-18 21:13:57 -07:00
|
|
|
store.put('query testquery{ testString }');
|
2016-10-22 23:52:32 -07:00
|
|
|
app = createApp({ graphqlOptions: {
|
2017-01-23 13:10:42 +02:00
|
|
|
schema,
|
2016-07-18 21:13:57 -07:00
|
|
|
formatParams(params) {
|
|
|
|
params['query'] = store.get(params.operationName);
|
|
|
|
return params;
|
|
|
|
},
|
|
|
|
}});
|
|
|
|
const expected = { testString: 'it works' };
|
|
|
|
const req = request(app)
|
|
|
|
.post('/graphql')
|
|
|
|
.send({
|
|
|
|
operationName: 'testquery',
|
|
|
|
});
|
|
|
|
return req.then((res) => {
|
|
|
|
expect(res.status).to.equal(200);
|
|
|
|
return expect(res.body.data).to.deep.equal(expected);
|
|
|
|
});
|
|
|
|
});
|
2016-07-03 08:42:59 -07:00
|
|
|
|
2016-07-18 21:13:57 -07:00
|
|
|
it('can reject non-whitelisted queries', () => {
|
2017-01-23 13:10:42 +02:00
|
|
|
const store = new OperationStore(schema);
|
2016-07-18 21:13:57 -07:00
|
|
|
store.put('query testquery{ testString }');
|
2016-10-22 23:52:32 -07:00
|
|
|
app = createApp({ graphqlOptions: {
|
2017-01-23 13:10:42 +02:00
|
|
|
schema,
|
2016-07-18 21:13:57 -07:00
|
|
|
formatParams(params) {
|
|
|
|
if (params.query) {
|
|
|
|
throw new Error('Must not provide query, only operationName');
|
|
|
|
}
|
|
|
|
params['query'] = store.get(params.operationName);
|
|
|
|
return params;
|
|
|
|
},
|
|
|
|
}});
|
|
|
|
const expected = [{
|
|
|
|
data: {
|
|
|
|
testString: 'it works',
|
|
|
|
},
|
|
|
|
}, {
|
|
|
|
errors: [{
|
|
|
|
message: 'Must not provide query, only operationName',
|
|
|
|
}],
|
|
|
|
}];
|
2016-07-03 08:42:59 -07:00
|
|
|
|
2016-07-18 21:13:57 -07:00
|
|
|
const req = request(app)
|
|
|
|
.post('/graphql')
|
|
|
|
.send([{
|
|
|
|
operationName: 'testquery',
|
|
|
|
}, {
|
|
|
|
query: '{ testString }',
|
|
|
|
}]);
|
|
|
|
return req.then((res) => {
|
|
|
|
expect(res.status).to.equal(200);
|
|
|
|
return expect(res.body).to.deep.equal(expected);
|
|
|
|
});
|
|
|
|
});
|
2016-07-03 08:42:59 -07:00
|
|
|
});
|
|
|
|
});
|
|
|
|
};
|