import xhr from '../xhr'; import XhrMockBuilder from '../../../test/XhrMockBuilder'; import { NetworkResponse, Serializable } from '../../types'; const originalXhr = window.XMLHttpRequest; function xhrMock(): XhrMockBuilder { const builder = new XhrMockBuilder(); // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore window.XMLHttpRequest = builder.getXMLHttpRequest(); return builder; } function getResponse(result: NetworkResponse): Serializable | undefined { if (result.ok) { return result.response; } return undefined; } function getError(result: NetworkResponse): Serializable | undefined { if (result.ok === false) { return result.error; } return undefined; } afterEach(() => { window.XMLHttpRequest = originalXhr; }); describe('xhr transport utility', () => { it('should perform a simple request', async () => { const builder = xhrMock(); const response = { name: 'John Doe' }; const url = 'https://api.flow.io/users/1'; builder.status(200).response(response); const res = await builder.processRequest( xhr({ url }), ); expect(res.mock.open).toHaveBeenCalledWith('GET', url, true); expect(res.mock.send).toHaveBeenCalledWith(); expect(res.mock.setRequestHeader).not.toHaveBeenCalled(); expect(res.result.ok).toEqual(true); expect(res.result.status).toEqual(200); expect(getResponse(res.result)).toEqual(response); }); it('should send request headers', async () => { const builder = xhrMock(); const response = { name: 'John Doe' }; const url = 'https://api.flow.io/users/1'; builder.status(200).response(response); const { result, mock } = await builder.processRequest( xhr({ url, headers: { 'x-flow-token': 'token', 'x-flow-request-id': '27', }, }), ); expect(mock.setRequestHeader).toHaveBeenNthCalledWith(1, 'x-flow-token', 'token'); expect(mock.setRequestHeader).toHaveBeenNthCalledWith(2, 'x-flow-request-id', '27'); expect(result.ok).toEqual(true); expect(result.status).toEqual(200); expect(getResponse(result)).toEqual(response); }); it('should map data to query parameters for GET requests', async () => { const builder = xhrMock(); const data = { limit: 1, offset: 25 }; const response = [{ name: 'John Doe' }]; const url = 'https://api.flow.io/users'; builder.status(200).response(response); const { mock } = await builder.processRequest( xhr({ url, data, }), ); const requestUrl = `${url}?limit=1&offset=25`; expect(mock.open).toHaveBeenCalledWith('GET', requestUrl, true); expect(mock.setRequestHeader).not.toHaveBeenCalled(); }); it('should map data to query parameters for HEAD requests', async () => { const builder = xhrMock(); const data = { limit: 1, offset: 25 }; const url = 'https://api.flow.io/users'; builder.status(200); const { result, mock } = await builder.processRequest( xhr({ url, method: 'HEAD', data, }), ); const requestUrl = `${url}?limit=1&offset=25`; expect(result.ok).toEqual(true); expect(mock.open).toHaveBeenCalledWith('HEAD', requestUrl, true); expect(mock.setRequestHeader).not.toHaveBeenCalled(); expect(getResponse(result)).toEqual(''); }); it('should send data as JSON for POST requests', async () => { const builder = xhrMock(); const url = 'https://api.flow.io/users'; const requestBody = { firstName: 'John', lastName: 'Doe' }; const responseBody = { name: 'John Doe' }; builder.status(201).response(responseBody); const { result, mock } = await builder.processRequest( xhr({ url, method: 'POST', data: requestBody, }), ); expect(result.ok).toEqual(true); expect(mock.open).toHaveBeenCalledWith('POST', url, true); expect(mock.setRequestHeader).toHaveBeenCalledWith('Content-Type', 'application/json'); expect(mock.send).toHaveBeenCalledWith(JSON.stringify(requestBody)); expect(result.status).toEqual(201); expect(getResponse(result)).toEqual(responseBody); }); it('should decorate non 2XX responses', async () => { const builder = xhrMock(); const url = 'https://api.flow.io/users/1'; const responseBody = { code: 'ENOTFOUND', messages: ['Resource requested is not found'], }; builder.status(404).response(responseBody); const { result } = await builder.processRequest( xhr({ url, }), ); expect(result.ok).toEqual(false); expect(result.status).toEqual(404); expect(getError(result)).toEqual(responseBody); }); it('should send enveloped request with option.envelope', async () => { const builder = xhrMock(); const url = 'https://api.flow.io/users/1'; const responseBody = { id: '123124545', username: 'Maggie', }; const testHeaders = { blah: 'hell-yeah', }; builder.status(201).response(responseBody); const { result, mock } = await builder.processRequest( xhr({ url, envelope: true, method: 'PUT', data: { some: 'data' }, headers: testHeaders, }), ); expect(mock.open).toHaveBeenCalledWith('POST', `${url}?envelope=request`, true); expect(mock.send).toHaveBeenCalledWith(JSON.stringify({ method: 'PUT', headers: { blah: ['hell-yeah'], }, body: { some: 'data' }, })); expect(result.ok).toEqual(true); expect(result.status).toEqual(201); expect(getResponse(result)).toEqual(responseBody); }); });