import { ApolloLink, Observable } from '@apollo/client/core'; import { StartupJSLink } from '~/lib/utils/apollo_startup_js_link'; describe('StartupJSLink', () => { const FORWARDED_RESPONSE = { data: 'FORWARDED_RESPONSE' }; const STARTUP_JS_RESPONSE = { data: 'STARTUP_JS_RESPONSE' }; const OPERATION_NAME = 'startupJSQuery'; const STARTUP_JS_QUERY = `query ${OPERATION_NAME}($id: Int = 3){ name id }`; const STARTUP_JS_RESPONSE_TWO = { data: 'STARTUP_JS_RESPONSE_TWO' }; const OPERATION_NAME_TWO = 'startupJSQueryTwo'; const STARTUP_JS_QUERY_TWO = `query ${OPERATION_NAME_TWO}($id: Int = 3){ id name }`; const ERROR_RESPONSE = { data: { user: null, }, errors: [ { path: ['user'], locations: [{ line: 2, column: 3 }], extensions: { message: 'Object not found', type: 2, }, }, ], }; let startupLink; let link; function mockFetchCall(status = 200, response = STARTUP_JS_RESPONSE) { const p = { ok: status >= 200 && status < 300, status, headers: new Headers({ 'Content-Type': 'application/json' }), statusText: `MOCK-FETCH ${status}`, clone: () => p, json: () => Promise.resolve(response), }; return Promise.resolve(p); } function mockOperation({ operationName = OPERATION_NAME, variables = { id: 3 } } = {}) { return { operationName, variables, setContext: () => {} }; } const setupLink = () => { startupLink = new StartupJSLink(); link = ApolloLink.from([startupLink, new ApolloLink(() => Observable.of(FORWARDED_RESPONSE))]); }; it('forwards requests if no calls are set up', (done) => { setupLink(); link.request(mockOperation()).subscribe((result) => { expect(result).toEqual(FORWARDED_RESPONSE); expect(startupLink.startupCalls).toBe(null); expect(startupLink.request).toEqual(StartupJSLink.noopRequest); done(); }); }); it('forwards requests if the operation is not pre-loaded', (done) => { window.gl = { startup_graphql_calls: [ { fetchCall: mockFetchCall(), query: STARTUP_JS_QUERY, variables: { id: 3 }, }, ], }; setupLink(); link.request(mockOperation({ operationName: 'notLoaded' })).subscribe((result) => { expect(result).toEqual(FORWARDED_RESPONSE); expect(startupLink.startupCalls.size).toBe(1); done(); }); }); describe('variable match errors: ', () => { it('forwards requests if the variables are not matching', (done) => { window.gl = { startup_graphql_calls: [ { fetchCall: mockFetchCall(), query: STARTUP_JS_QUERY, variables: { id: 'NOT_MATCHING' }, }, ], }; setupLink(); link.request(mockOperation()).subscribe((result) => { expect(result).toEqual(FORWARDED_RESPONSE); expect(startupLink.startupCalls.size).toBe(0); done(); }); }); it('forwards requests if more variables are set in the operation', (done) => { window.gl = { startup_graphql_calls: [ { fetchCall: mockFetchCall(), query: STARTUP_JS_QUERY, }, ], }; setupLink(); link.request(mockOperation()).subscribe((result) => { expect(result).toEqual(FORWARDED_RESPONSE); expect(startupLink.startupCalls.size).toBe(0); done(); }); }); it('forwards requests if less variables are set in the operation', (done) => { window.gl = { startup_graphql_calls: [ { fetchCall: mockFetchCall(), query: STARTUP_JS_QUERY, variables: { id: 3, name: 'tanuki' }, }, ], }; setupLink(); link.request(mockOperation({ variables: { id: 3 } })).subscribe((result) => { expect(result).toEqual(FORWARDED_RESPONSE); expect(startupLink.startupCalls.size).toBe(0); done(); }); }); it('forwards requests if different variables are set', (done) => { window.gl = { startup_graphql_calls: [ { fetchCall: mockFetchCall(), query: STARTUP_JS_QUERY, variables: { name: 'tanuki' }, }, ], }; setupLink(); link.request(mockOperation({ variables: { id: 3 } })).subscribe((result) => { expect(result).toEqual(FORWARDED_RESPONSE); expect(startupLink.startupCalls.size).toBe(0); done(); }); }); it('forwards requests if array variables have a different order', (done) => { window.gl = { startup_graphql_calls: [ { fetchCall: mockFetchCall(), query: STARTUP_JS_QUERY, variables: { id: [3, 4] }, }, ], }; setupLink(); link.request(mockOperation({ variables: { id: [4, 3] } })).subscribe((result) => { expect(result).toEqual(FORWARDED_RESPONSE); expect(startupLink.startupCalls.size).toBe(0); done(); }); }); }); describe('error handling', () => { it('forwards the call if the fetchCall is failing with a HTTP Error', (done) => { window.gl = { startup_graphql_calls: [ { fetchCall: mockFetchCall(404), query: STARTUP_JS_QUERY, variables: { id: 3 }, }, ], }; setupLink(); link.request(mockOperation()).subscribe((result) => { expect(result).toEqual(FORWARDED_RESPONSE); expect(startupLink.startupCalls.size).toBe(0); done(); }); }); it('forwards the call if it errors (e.g. failing JSON)', (done) => { window.gl = { startup_graphql_calls: [ { fetchCall: Promise.reject(new Error('Parsing failed')), query: STARTUP_JS_QUERY, variables: { id: 3 }, }, ], }; setupLink(); link.request(mockOperation()).subscribe((result) => { expect(result).toEqual(FORWARDED_RESPONSE); expect(startupLink.startupCalls.size).toBe(0); done(); }); }); it('forwards the call if the response contains an error', (done) => { window.gl = { startup_graphql_calls: [ { fetchCall: mockFetchCall(200, ERROR_RESPONSE), query: STARTUP_JS_QUERY, variables: { id: 3 }, }, ], }; setupLink(); link.request(mockOperation()).subscribe((result) => { expect(result).toEqual(FORWARDED_RESPONSE); expect(startupLink.startupCalls.size).toBe(0); done(); }); }); it("forwards the call if the response doesn't contain a data object", (done) => { window.gl = { startup_graphql_calls: [ { fetchCall: mockFetchCall(200, { 'no-data': 'yay' }), query: STARTUP_JS_QUERY, variables: { id: 3 }, }, ], }; setupLink(); link.request(mockOperation()).subscribe((result) => { expect(result).toEqual(FORWARDED_RESPONSE); expect(startupLink.startupCalls.size).toBe(0); done(); }); }); }); it('resolves the request if the operation is matching', (done) => { window.gl = { startup_graphql_calls: [ { fetchCall: mockFetchCall(), query: STARTUP_JS_QUERY, variables: { id: 3 }, }, ], }; setupLink(); link.request(mockOperation()).subscribe((result) => { expect(result).toEqual(STARTUP_JS_RESPONSE); expect(startupLink.startupCalls.size).toBe(0); done(); }); }); it('resolves the request exactly once', (done) => { window.gl = { startup_graphql_calls: [ { fetchCall: mockFetchCall(), query: STARTUP_JS_QUERY, variables: { id: 3 }, }, ], }; setupLink(); link.request(mockOperation()).subscribe((result) => { expect(result).toEqual(STARTUP_JS_RESPONSE); expect(startupLink.startupCalls.size).toBe(0); link.request(mockOperation()).subscribe((result2) => { expect(result2).toEqual(FORWARDED_RESPONSE); done(); }); }); }); it('resolves the request if the variables have a different order', (done) => { window.gl = { startup_graphql_calls: [ { fetchCall: mockFetchCall(), query: STARTUP_JS_QUERY, variables: { id: 3, name: 'foo' }, }, ], }; setupLink(); link.request(mockOperation({ variables: { name: 'foo', id: 3 } })).subscribe((result) => { expect(result).toEqual(STARTUP_JS_RESPONSE); expect(startupLink.startupCalls.size).toBe(0); done(); }); }); it('resolves the request if the variables have undefined values', (done) => { window.gl = { startup_graphql_calls: [ { fetchCall: mockFetchCall(), query: STARTUP_JS_QUERY, variables: { name: 'foo' }, }, ], }; setupLink(); link .request(mockOperation({ variables: { name: 'foo', undef: undefined } })) .subscribe((result) => { expect(result).toEqual(STARTUP_JS_RESPONSE); expect(startupLink.startupCalls.size).toBe(0); done(); }); }); it('resolves the request if the variables are of an array format', (done) => { window.gl = { startup_graphql_calls: [ { fetchCall: mockFetchCall(), query: STARTUP_JS_QUERY, variables: { id: [3, 4] }, }, ], }; setupLink(); link.request(mockOperation({ variables: { id: [3, 4] } })).subscribe((result) => { expect(result).toEqual(STARTUP_JS_RESPONSE); expect(startupLink.startupCalls.size).toBe(0); done(); }); }); it('resolves multiple requests correctly', (done) => { window.gl = { startup_graphql_calls: [ { fetchCall: mockFetchCall(), query: STARTUP_JS_QUERY, variables: { id: 3 }, }, { fetchCall: mockFetchCall(200, STARTUP_JS_RESPONSE_TWO), query: STARTUP_JS_QUERY_TWO, variables: { id: 3 }, }, ], }; setupLink(); link.request(mockOperation({ operationName: OPERATION_NAME_TWO })).subscribe((result) => { expect(result).toEqual(STARTUP_JS_RESPONSE_TWO); expect(startupLink.startupCalls.size).toBe(1); link.request(mockOperation({ operationName: OPERATION_NAME })).subscribe((result2) => { expect(result2).toEqual(STARTUP_JS_RESPONSE); expect(startupLink.startupCalls.size).toBe(0); done(); }); }); }); });