Skip to content

Commit

Permalink
Merge pull request #3 from PabloSzx/newfeatures
Browse files Browse the repository at this point in the history
setOptions after creation and more specific types
  • Loading branch information
vitorbal committed Dec 12, 2019
2 parents fd62367 + 535622d commit 7f2133f
Show file tree
Hide file tree
Showing 4 changed files with 239 additions and 162 deletions.
35 changes: 31 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,9 @@ const UPDATE_USER = `
}
`;

const mutationResult = await mutate(
UPDATE_USER,
{ variables: { id: 1, email: 'nancy@foo.co' } }
);
const mutationResult = await mutate(UPDATE_USER, {
variables: { id: 1, email: 'nancy@foo.co' }
});

expect(mutationResult).toEqual({
data: {
Expand Down Expand Up @@ -85,6 +84,34 @@ This is useful when your apollo server `context` option is a callback that opera

As mentioned above, if you don't pass an `extendMockRequest` to `createTestClient`, we provide a default request mock object for you. See https://github.com/howardabrams/node-mocks-http#createrequest for all the default values that are included in that mock.

### setOptions

You can also set the `request` and `response` mocking options **after** the creation of the `test client`, which is a **cleaner** and **faster** way due not needing to create a new instance for **any** change you might want to do the `request` or `response`.

```js
const { query, setOptions } = createTestClient({
apolloServer
});

setOptions({
// If "request" or "response" is not specified, it's not modified
request: {
headers: {
cookie: 'csrf=blablabla',
referer: ''
}
},
response: {
locals: {
user: {
isAuthenticated: false
}
}
}
});
```


## Why not use `apollo-server-testing`?

You can't really write _real_ integration tests with `apollo-server-testing`, because it doesn't support servers which rely on the `context` option being a function that uses the `req` object ([see this issue for more information](https://github.com/apollographql/apollo-server/issues/2277)).
Expand Down
10 changes: 6 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,13 @@
"dev": "tsc --watch"
},
"dependencies": {
"apollo-server-core": "^2.8.1",
"apollo-server-express": "^2.8.1",
"apollo-server-core": "^2.9.13",
"apollo-server-express": "^2.9.13",
"express": "^4.17.1",
"node-mocks-http": "^1.7.6",
"typescript": "^3.5.3"
"node-mocks-http": "^1.8.0"
},
"devDependencies": {
"typescript": "^3.7.3"
},
"peerDependencies": {
"graphql": "^0.12.0 || ^0.13.0 || ^14.0.0"
Expand Down
76 changes: 56 additions & 20 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,24 @@
// @flow

import express from 'express';
import httpMocks from 'node-mocks-http';
import { print } from 'graphql';
import { convertNodeHttpToRequest, runHttpQuery } from 'apollo-server-core';

import { ApolloServer } from 'apollo-server-express';
import { DocumentNode } from 'graphql';
import express from 'express';
import { DocumentNode, print } from 'graphql';
import httpMocks, { RequestOptions, ResponseOptions } from 'node-mocks-http';

const mockRequest = (options = {}) =>
const mockRequest = (options: RequestOptions = {}) =>
httpMocks.createRequest({
method: 'POST',
...options
});

const mockResponse = (options = {}) => httpMocks.createResponse(options);
const mockResponse = (options: ResponseOptions = {}) =>
httpMocks.createResponse(options);

type StringOrAst = string | DocumentNode;
type Options = { variables?: object};
export type StringOrAst = string | DocumentNode;
export type Options<T extends object> = { variables?: T };

type TestClientConfig = {
export type TestClientConfig = {
// The ApolloServer instance that will be used for handling the queries you run in your tests.
// Must be an instance of the ApolloServer class from `apollo-server-express` (or a compatible subclass).
apolloServer: ApolloServer;
Expand All @@ -28,15 +27,25 @@ type TestClientConfig = {
// and you want to inject data into that `req` object.
// If you don't pass anything here, we provide a default request mock object for you.
// See https://github.com/howardabrams/node-mocks-http#createrequest for all the default values that are included.
extendMockRequest?: {};
extendMockRequest?: RequestOptions;
// Extends the mocked Response object with additional keys.
// Useful when your apolloServer `context` option is a callback that operates on the passed in `res` key,
// and you want to inject data into that `res` object (such as `res.locals`).
// If you don't pass anything here, we provide a default response mock object for you.
// See https://www.npmjs.com/package/node-mocks-http#createresponse for all the default values that are included.
extendMockResponse?: {};
extendMockResponse?: ResponseOptions;
};

export type TestQuery = <T extends object = {}, V extends object = {}>(
operation: StringOrAst,
{ variables }?: Options<V>
) => Promise<T>;

export type TestSetOptions = (options: {
request?: RequestOptions;
response?: ResponseOptions;
}) => void;

// This function takes in an apollo server instance and returns a function that you can use to run operations
// against your schema, and assert on the results.
//
Expand All @@ -59,17 +68,43 @@ type TestClientConfig = {
// }
// });
// ```
export const createTestClient = ({
export function createTestClient({
apolloServer,
extendMockRequest = {},
extendMockResponse = {}
}: TestClientConfig) => {
}: TestClientConfig) {
const app = express();
apolloServer.applyMiddleware({ app });

const test = async (operation: StringOrAst, {variables}: Options = {}) => {
const req = mockRequest(extendMockRequest);
const res = mockResponse(extendMockResponse);
let mockRequestOptions = extendMockRequest;
let mockResponseOptions = extendMockResponse;

/**
* Set the options after TestClient creation
* Useful when you don't want to create a new instance just for a specific change in the request or response.
* */

const setOptions: TestSetOptions = ({
request,
response
}: {
request?: RequestOptions;
response?: ResponseOptions;
}) => {
if (request) {
mockRequestOptions = request;
}
if (response) {
mockResponseOptions = response;
}
};

const test: TestQuery = async <T extends object = {}, V extends object = {}>(
operation: StringOrAst,
{ variables }: Options<V> = {}
) => {
const req = mockRequest(mockRequestOptions);
const res = mockResponse(mockResponseOptions);

const graphQLOptions = await apolloServer.createGraphQLServerOptions(
req,
Expand All @@ -87,11 +122,12 @@ export const createTestClient = ({
request: convertNodeHttpToRequest(req)
});

return JSON.parse(graphqlResponse);
return JSON.parse(graphqlResponse) as T;
};

return {
query: test,
mutate: test
mutate: test,
setOptions
};
};
}
Loading

0 comments on commit 7f2133f

Please sign in to comment.