apollo-link-http
Version:
HTTP transport layer for GraphQL
278 lines (248 loc) • 8.06 kB
text/typescript
import { Observable, ApolloLink, execute } from 'apollo-link';
import gql from 'graphql-tag';
import * as fetchMock from 'fetch-mock';
import objectToQuery from 'object-to-querystring';
import { sharedHttpTest } from './sharedHttpTests';
import { HttpLink, createHttpLink } from '../httpLink';
const sampleQuery = gql`
query SampleQuery {
stub {
id
}
}
`;
const sampleMutation = gql`
mutation SampleMutation {
stub {
id
}
}
`;
const makeCallback = (done, body) => {
return (...args) => {
try {
body(...args);
done();
} catch (error) {
done.fail(error);
}
};
};
describe('HttpLink', () => {
describe('HttpLink-specific tests', () => {
it('does not need any constructor arguments', () => {
expect(() => new HttpLink()).not.toThrow();
});
const makePromise = res =>
new Promise((resolve, reject) => setTimeout(() => resolve(res)));
const data = { data: { hello: 'world' } };
beforeEach(() => {
fetchMock.restore();
fetchMock.post('begin:http://data/', makePromise(data));
fetchMock.get('begin:http://data/', makePromise(data));
});
afterEach(() => {
fetchMock.restore();
});
it('constructor creates link that can call next and then complete', done => {
const next = jest.fn();
const link = new HttpLink({ uri: 'http://data/' });
const observable = execute(link, {
query: sampleQuery,
});
observable.subscribe({
next,
error: error => expect(false),
complete: () => {
expect(next).toHaveBeenCalledTimes(1);
done();
},
});
});
it('supports using a GET request', done => {
const variables = { params: 'stub' };
const extensions = { myExtension: 'foo' };
const link = createHttpLink({
uri: 'http://data/',
fetchOptions: { method: 'GET' },
includeExtensions: true,
});
execute(link, { query: sampleQuery, variables, extensions }).subscribe({
next: makeCallback(done, result => {
const [uri, options] = fetchMock.lastCall();
const { method, body } = options;
expect(body).toBeUndefined();
expect(method).toBe('GET');
expect(uri).toBe(
'http://data/?query=query%20SampleQuery%20%7B%0A%20%20stub%20%7B%0A%20%20%20%20id%0A%20%20%7D%0A%7D%0A&operationName=SampleQuery&variables=%7B%22params%22%3A%22stub%22%7D&extensions=%7B%22myExtension%22%3A%22foo%22%7D',
);
}),
error: error => done.fail(error),
});
});
it('supports using a GET request with search and fragment', done => {
const variables = { params: 'stub' };
const link = createHttpLink({
uri: 'http://data/?foo=bar#frag',
fetchOptions: { method: 'GET' },
});
execute(link, { query: sampleQuery, variables }).subscribe({
next: makeCallback(done, result => {
const [uri, options] = fetchMock.lastCall();
const { method, body } = options;
expect(body).toBeUndefined();
expect(method).toBe('GET');
expect(uri).toBe(
'http://data/?foo=bar&query=query%20SampleQuery%20%7B%0A%20%20stub%20%7B%0A%20%20%20%20id%0A%20%20%7D%0A%7D%0A&operationName=SampleQuery&variables=%7B%22params%22%3A%22stub%22%7D#frag',
);
}),
error: error => done.fail(error),
});
});
it('supports using a GET request on the context', done => {
const variables = { params: 'stub' };
const link = createHttpLink({
uri: 'http://data/',
});
execute(link, {
query: sampleQuery,
variables,
context: {
fetchOptions: { method: 'GET' },
},
}).subscribe(
makeCallback(done, result => {
const [uri, options] = fetchMock.lastCall();
const { method, body } = options;
expect(body).toBeUndefined();
expect(method).toBe('GET');
expect(uri).toBe(
'http://data/?query=query%20SampleQuery%20%7B%0A%20%20stub%20%7B%0A%20%20%20%20id%0A%20%20%7D%0A%7D%0A&operationName=SampleQuery&variables=%7B%22params%22%3A%22stub%22%7D',
);
}),
);
});
it('uses GET with useGETForQueries', done => {
const variables = { params: 'stub' };
const link = createHttpLink({
uri: 'http://data/',
useGETForQueries: true,
});
execute(link, {
query: sampleQuery,
variables,
}).subscribe(
makeCallback(done, result => {
const [uri, options] = fetchMock.lastCall();
const { method, body } = options;
expect(body).toBeUndefined();
expect(method).toBe('GET');
expect(uri).toBe(
'http://data/?query=query%20SampleQuery%20%7B%0A%20%20stub%20%7B%0A%20%20%20%20id%0A%20%20%7D%0A%7D%0A&operationName=SampleQuery&variables=%7B%22params%22%3A%22stub%22%7D',
);
}),
);
});
it('uses POST for mutations with useGETForQueries', done => {
const variables = { params: 'stub' };
const link = createHttpLink({
uri: 'http://data/',
useGETForQueries: true,
});
execute(link, {
query: sampleMutation,
variables,
}).subscribe(
makeCallback(done, result => {
const [uri, options] = fetchMock.lastCall();
const { method, body } = options;
expect(body).toBeDefined();
expect(method).toBe('POST');
expect(uri).toBe('http://data/');
}),
);
});
it('should add client awareness settings to request headers', done => {
const variables = { params: 'stub' };
const link = createHttpLink({
uri: 'http://data/',
});
const clientAwareness = {
name: 'Some Client Name',
version: '1.0.1',
};
execute(link, {
query: sampleQuery,
variables,
context: {
clientAwareness,
},
}).subscribe(
makeCallback(done, result => {
const [uri, options] = fetchMock.lastCall();
const { headers } = options;
expect(headers['apollographql-client-name']).toBeDefined();
expect(headers['apollographql-client-name']).toEqual(
clientAwareness.name,
);
expect(headers['apollographql-client-version']).toBeDefined();
expect(headers['apollographql-client-version']).toEqual(
clientAwareness.version,
);
}),
);
});
});
it("throws for GET if the variables can't be stringified", done => {
const link = createHttpLink({
uri: 'http://data/',
useGETForQueries: true,
});
let b;
const a = { b };
b = { a };
a.b = b;
const variables = {
a,
b,
};
execute(link, { query: sampleQuery, variables }).subscribe(
result => {
done.fail('next should have been thrown from the link');
},
makeCallback(done, e => {
expect(e.message).toMatch(/Variables map is not serializable/);
expect(e.parseError.message).toMatch(
/Converting circular structure to JSON/,
);
}),
);
});
it("throws for GET if the extensions can't be stringified", done => {
const link = createHttpLink({
uri: 'http://data/',
useGETForQueries: true,
includeExtensions: true,
});
let b;
const a = { b };
b = { a };
a.b = b;
const extensions = {
a,
b,
};
execute(link, { query: sampleQuery, extensions }).subscribe(
result => {
done.fail('next should have been thrown from the link');
},
makeCallback(done, e => {
expect(e.message).toMatch(/Extensions map is not serializable/);
expect(e.parseError.message).toMatch(
/Converting circular structure to JSON/,
);
}),
);
});
sharedHttpTest('HttpLink', createHttpLink);
});