diff --git a/services/web/frontend/js/infrastructure/fetch-json.js b/services/web/frontend/js/infrastructure/fetch-json.js index f9d78657a4..fcc6cd92c6 100644 --- a/services/web/frontend/js/infrastructure/fetch-json.js +++ b/services/web/frontend/js/infrastructure/fetch-json.js @@ -49,35 +49,35 @@ function fetchJSON( return fetch(path, options) .then(parseResponseBody) - .then(({ responseBody, response }) => { + .then(({ data, response }) => { if (!response.ok) { throw new OError(response.statusText, { statusCode: response.status, - responseBody, + data, response }) } - return responseBody + return data }) } function parseResponseBody(response) { const contentType = response.headers.get('Content-Type') if (/application\/json/.test(contentType)) { - return response.json().then(json => { - return { responseBody: json, response } + return response.json().then(data => { + return { data, response } }) } else if ( /text\/plain/.test(contentType) || /text\/html/.test(contentType) ) { return response.text().then(text => { - return { responseBody: { message: text }, response } + return { data: { message: text }, response } }) } else { // response body ignored as content-type is either not set (e.g. 204 // responses) or unsupported - return Promise.resolve({ responseBody: {}, response }) + return Promise.resolve({ data: {}, response }) } } diff --git a/services/web/test/frontend/bootstrap.js b/services/web/test/frontend/bootstrap.js index 111ed01159..5bfe082d41 100644 --- a/services/web/test/frontend/bootstrap.js +++ b/services/web/test/frontend/bootstrap.js @@ -8,6 +8,7 @@ require('jsdom-global/register') // has a nicer failure messages const chai = require('chai') chai.use(require('sinon-chai')) +chai.use(require('chai-as-promised')) window.i18n = { currentLangCode: 'en' } require('../../frontend/js/i18n') diff --git a/services/web/test/frontend/infrastructure/fetch-json.test.js b/services/web/test/frontend/infrastructure/fetch-json.test.js new file mode 100644 index 0000000000..19edd15c6b --- /dev/null +++ b/services/web/test/frontend/infrastructure/fetch-json.test.js @@ -0,0 +1,93 @@ +import { expect } from 'chai' +import fetchMock from 'fetch-mock' +import OError from '@overleaf/o-error' +import { + deleteJSON, + getJSON, + postJSON, + putJSON +} from '../../../frontend/js/infrastructure/fetch-json' + +describe('fetchJSON', function() { + before(function() { + fetchMock.restore() + }) + + afterEach(function() { + fetchMock.restore() + }) + + const headers = { + Accept: 'application/json', + 'Content-Type': 'application/json' + } + + it('handles GET requests', function() { + fetchMock.once( + { method: 'GET', url: '/test', headers }, + { status: 200, body: { result: 'success' } } + ) + + return expect(getJSON('/test')).to.eventually.deep.equal({ + result: 'success' + }) + }) + + it('handles 4xx responses', function() { + fetchMock.get('/test', { + status: 400, + body: { message: 'The request was invalid' } + }) + + return expect(getJSON('/test')) + .to.eventually.be.rejectedWith('Bad Request') + .and.be.an.instanceOf(OError) + .to.nested.include({ + 'info.response.status': 400, + 'info.data.message': 'The request was invalid' + }) + }) + + it('handles 5xx responses', async function() { + fetchMock.get('/test', { status: 500 }) + + return expect(getJSON('/test')) + .to.eventually.be.rejectedWith('Internal Server Error') + .and.be.an.instanceOf(OError) + .to.nested.include({ + 'info.response.status': 500 + }) + }) + + it('handles POST requests', function() { + const body = { example: true } + + fetchMock.once( + { method: 'POST', url: '/test', headers, body }, + { status: 200, body: { result: 'success' } } + ) + + return expect(postJSON('/test', { body })).to.eventually.deep.equal({ + result: 'success' + }) + }) + + it('handles PUT requests', function() { + const body = { example: true } + + fetchMock.once( + { method: 'PUT', url: '/test', headers, body }, + { status: 200, body: { result: 'success' } } + ) + + return expect(putJSON('/test', { body })).to.eventually.deep.equal({ + result: 'success' + }) + }) + + it('handles DELETE requests', function() { + fetchMock.once({ method: 'DELETE', url: '/test', headers }, { status: 204 }) + + return expect(deleteJSON('/test')).to.eventually.deep.equal({}) + }) +})