2016-07-03 08:42:59 -07:00
|
|
|
import {
|
|
|
|
expect,
|
|
|
|
} from 'chai';
|
|
|
|
|
|
|
|
import {
|
|
|
|
GraphQLSchema,
|
|
|
|
GraphQLObjectType,
|
|
|
|
GraphQLString,
|
|
|
|
} from 'graphql';
|
|
|
|
|
|
|
|
// tslint:disable-next-line
|
|
|
|
const request = require('supertest-as-promised');
|
|
|
|
|
2016-07-04 22:04:31 -07:00
|
|
|
import ApolloOptions from './apolloOptions';
|
|
|
|
|
2016-07-03 08:42:59 -07:00
|
|
|
import * as GraphiQL from '../modules/renderGraphiQL';
|
|
|
|
import { OperationStore } from '../modules/operationStore';
|
|
|
|
|
|
|
|
const QueryType = new GraphQLObjectType({
|
|
|
|
name: 'QueryType',
|
|
|
|
fields: {
|
|
|
|
testString: {
|
|
|
|
type: GraphQLString,
|
2016-07-17 17:51:41 -07:00
|
|
|
resolve(_, params, context) {
|
|
|
|
if (context) {
|
|
|
|
context();
|
|
|
|
}
|
2016-07-03 08:42:59 -07:00
|
|
|
return 'it works';
|
|
|
|
},
|
|
|
|
},
|
|
|
|
testArgument: {
|
|
|
|
type: GraphQLString,
|
|
|
|
args: { echo: { type: GraphQLString } },
|
|
|
|
resolve(root, { echo }) {
|
|
|
|
return `hello ${echo}`;
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
const MutationType = new GraphQLObjectType({
|
|
|
|
name: 'MutationType',
|
|
|
|
fields: {
|
|
|
|
testMutation: {
|
|
|
|
type: GraphQLString,
|
|
|
|
args: { echo: { type: GraphQLString } },
|
|
|
|
resolve(root, { echo }) {
|
|
|
|
return `not really a mutation, but who cares: ${echo}`;
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
2016-07-04 22:04:31 -07:00
|
|
|
export const Schema = new GraphQLSchema({
|
2016-07-03 08:42:59 -07:00
|
|
|
query: QueryType,
|
|
|
|
mutation: MutationType,
|
|
|
|
});
|
|
|
|
|
|
|
|
export interface CreateAppOptions {
|
|
|
|
excludeParser?: boolean;
|
2016-07-04 22:04:31 -07:00
|
|
|
apolloOptions?: ApolloOptions | {(): ApolloOptions | Promise<{}>};
|
2016-07-03 08:42:59 -07:00
|
|
|
graphiqlOptions?: GraphiQL.GraphiQLData;
|
|
|
|
}
|
|
|
|
|
|
|
|
export interface CreateAppFunc {
|
|
|
|
(options?: CreateAppOptions): void;
|
|
|
|
}
|
|
|
|
|
2016-07-07 08:46:10 -07:00
|
|
|
export default (createApp: CreateAppFunc) => {
|
|
|
|
describe('apolloServer', () => {
|
2016-07-18 21:13:57 -07:00
|
|
|
describe('graphqlHTTP', () => {
|
|
|
|
it('can be called with an options function', () => {
|
|
|
|
const app = createApp({apolloOptions: (): ApolloOptions => ({schema: Schema})});
|
|
|
|
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', () => {
|
|
|
|
const app = createApp({ apolloOptions: () => {
|
|
|
|
return new Promise(resolve => {
|
|
|
|
resolve({schema: Schema});
|
|
|
|
});
|
|
|
|
}});
|
|
|
|
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', () => {
|
|
|
|
const app = createApp({ apolloOptions: () => {
|
|
|
|
return Promise.reject({}) as any as ApolloOptions;
|
|
|
|
}});
|
|
|
|
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-07-18 21:13:57 -07:00
|
|
|
it('throws an error if POST body is missing', () => {
|
|
|
|
const app = createApp({excludeParser: true});
|
|
|
|
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('POST body missing.');
|
|
|
|
});
|
|
|
|
});
|
2016-07-03 08:42:59 -07:00
|
|
|
|
|
|
|
|
2016-07-18 21:13:57 -07:00
|
|
|
it('can handle a basic request', () => {
|
|
|
|
const app = createApp();
|
|
|
|
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', () => {
|
|
|
|
const app = createApp();
|
|
|
|
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', () => {
|
|
|
|
const app = createApp();
|
|
|
|
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 operationName', () => {
|
|
|
|
const app = createApp();
|
|
|
|
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-07-18 21:13:57 -07:00
|
|
|
it('can handle batch requests', () => {
|
|
|
|
const app = createApp();
|
|
|
|
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-07-18 21:13:57 -07:00
|
|
|
it('can handle a request with a mutation', () => {
|
|
|
|
const app = createApp();
|
|
|
|
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', () => {
|
|
|
|
const app = createApp({apolloOptions: {
|
|
|
|
schema: Schema,
|
|
|
|
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', () => {
|
|
|
|
let results;
|
|
|
|
const expected = 'it works';
|
|
|
|
const app = createApp({apolloOptions: {
|
|
|
|
schema: Schema,
|
|
|
|
context: () => results = expected,
|
|
|
|
}});
|
|
|
|
const req = request(app)
|
|
|
|
.post('/graphql')
|
|
|
|
.send({
|
|
|
|
query: 'query test{ testString }',
|
|
|
|
});
|
|
|
|
return req.then((res) => {
|
|
|
|
expect(res.status).to.equal(200);
|
|
|
|
return expect(results).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', () => {
|
|
|
|
const app = createApp({graphiqlOptions: {
|
|
|
|
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-07-03 08:42:59 -07:00
|
|
|
});
|
|
|
|
|
2016-07-18 21:13:57 -07:00
|
|
|
describe('stored queries', () => {
|
|
|
|
it('works with formatParams', () => {
|
|
|
|
const store = new OperationStore(Schema);
|
|
|
|
store.put('query testquery{ testString }');
|
|
|
|
const app = createApp({ apolloOptions: {
|
|
|
|
schema: Schema,
|
|
|
|
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', () => {
|
|
|
|
const store = new OperationStore(Schema);
|
|
|
|
store.put('query testquery{ testString }');
|
|
|
|
const app = createApp({ apolloOptions: {
|
|
|
|
schema: Schema,
|
|
|
|
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
|
|
|
});
|
|
|
|
});
|
|
|
|
};
|