fetch-json.test.js 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. import { expect } from 'chai'
  2. import fetchMock from 'fetch-mock'
  3. import {
  4. deleteJSON,
  5. FetchError,
  6. getUserFacingMessage,
  7. getJSON,
  8. postJSON,
  9. putJSON,
  10. } from '../../../frontend/js/infrastructure/fetch-json'
  11. describe('fetchJSON', function () {
  12. before(function () {
  13. fetchMock.removeRoutes().clearHistory()
  14. })
  15. afterEach(function () {
  16. fetchMock.removeRoutes().clearHistory()
  17. })
  18. const headers = {
  19. Accept: 'application/json',
  20. 'Content-Type': 'application/json',
  21. }
  22. it('handles GET requests', function () {
  23. fetchMock.once(
  24. { method: 'GET', url: '/test', headers },
  25. { status: 200, body: { result: 'success' } }
  26. )
  27. return expect(getJSON('/test')).to.eventually.deep.equal({
  28. result: 'success',
  29. })
  30. })
  31. it('handles 4xx responses', function () {
  32. fetchMock.get('/test', {
  33. status: 400,
  34. body: { message: 'The request was invalid' },
  35. })
  36. return expect(getJSON('/test'))
  37. .to.eventually.be.rejectedWith('Bad Request')
  38. .and.be.an.instanceOf(FetchError)
  39. .to.nested.include({
  40. message: 'Bad Request',
  41. 'data.message': 'The request was invalid',
  42. 'response.status': 400,
  43. 'info.statusCode': 400,
  44. })
  45. })
  46. it('handles 5xx responses', async function () {
  47. fetchMock.get('/test', { status: 500 })
  48. return expect(getJSON('/test'))
  49. .to.eventually.be.rejectedWith('Internal Server Error')
  50. .and.be.an.instanceOf(FetchError)
  51. .to.nested.include({
  52. 'response.status': 500,
  53. 'info.statusCode': 500,
  54. })
  55. })
  56. it('handles JSON error responses', async function () {
  57. fetchMock.get('/test', {
  58. status: 500,
  59. headers: {
  60. 'Content-Type': 'application/json',
  61. },
  62. body: { message: 'lorem ipsum' },
  63. })
  64. return expect(getJSON('/test'))
  65. .to.eventually.be.rejectedWith('Internal Server Error')
  66. .and.be.an.instanceOf(FetchError)
  67. .to.nested.include({
  68. 'data.message': 'lorem ipsum',
  69. })
  70. })
  71. it('handles text error responses', async function () {
  72. fetchMock.get('/test', {
  73. status: 500,
  74. headers: {
  75. 'Content-Type': 'text/plain',
  76. },
  77. body: 'lorem ipsum',
  78. })
  79. return expect(getJSON('/test'))
  80. .to.eventually.be.rejectedWith('Internal Server Error')
  81. .and.be.an.instanceOf(FetchError)
  82. .to.nested.include({
  83. 'data.message': 'lorem ipsum',
  84. })
  85. })
  86. it('handles text error responses sent as HTML', async function () {
  87. fetchMock.get('/test', {
  88. status: 500,
  89. headers: {
  90. 'Content-Type': 'text/html',
  91. },
  92. body: 'lorem ipsum',
  93. })
  94. return expect(getJSON('/test'))
  95. .to.eventually.be.rejectedWith('Internal Server Error')
  96. .and.be.an.instanceOf(FetchError)
  97. .to.nested.include({
  98. 'data.message': 'lorem ipsum',
  99. })
  100. })
  101. it('handles (ignores) HTML error responses sent as HTML', async function () {
  102. fetchMock.get('/test', {
  103. status: 500,
  104. headers: {
  105. 'Content-Type': 'text/html',
  106. },
  107. body: '<!doctype html><html lang="en"><body><p>lorem ipsum</p></body></html>',
  108. })
  109. const promise = getJSON('/test')
  110. expect(promise)
  111. .to.eventually.be.rejectedWith('Internal Server Error')
  112. .and.be.an.instanceOf(FetchError)
  113. try {
  114. await promise
  115. } catch (error) {
  116. expect(error.data).to.eql({})
  117. }
  118. })
  119. it('handles 5xx responses without a status message', async function () {
  120. fetchMock.get('/test', { status: 599 })
  121. return expect(getJSON('/test'))
  122. .to.eventually.be.rejectedWith('Unexpected Error: 599')
  123. .and.be.an.instanceOf(FetchError)
  124. .to.nested.include({
  125. 'response.status': 599,
  126. 'info.statusCode': 599,
  127. message: 'Unexpected Error: 599',
  128. })
  129. })
  130. it('handles POST requests', function () {
  131. const body = { example: true }
  132. fetchMock.once(
  133. { method: 'POST', url: '/test', headers, body },
  134. { status: 200, body: { result: 'success' } }
  135. )
  136. return expect(postJSON('/test', { body })).to.eventually.deep.equal({
  137. result: 'success',
  138. })
  139. })
  140. it('handles PUT requests', function () {
  141. const body = { example: true }
  142. fetchMock.once(
  143. { method: 'PUT', url: '/test', headers, body },
  144. { status: 200, body: { result: 'success' } }
  145. )
  146. return expect(putJSON('/test', { body })).to.eventually.deep.equal({
  147. result: 'success',
  148. })
  149. })
  150. it('handles DELETE requests', function () {
  151. fetchMock.once({ method: 'DELETE', url: '/test', headers }, { status: 204 })
  152. return expect(deleteJSON('/test')).to.eventually.deep.equal({})
  153. })
  154. describe('getUserFacingMessage()', function () {
  155. it('returns the error facing message for FetchError instances', function () {
  156. const error = new FetchError(
  157. '403 error',
  158. 'http:/example.com',
  159. {},
  160. { status: 403 }
  161. )
  162. expect(getUserFacingMessage(error)).to.equal(
  163. 'Session error. Please check you have cookies enabled. If the problem persists, try clearing your cache and cookies.'
  164. )
  165. })
  166. it('returns `message` for Error instances different than FetchError', function () {
  167. const error = new Error('403 error')
  168. expect(getUserFacingMessage(error)).to.equal('403 error')
  169. })
  170. it('returns `undefined` for non-Error instances', function () {
  171. expect(getUserFacingMessage(undefined)).to.be.undefined
  172. expect(getUserFacingMessage(null)).to.be.undefined
  173. expect(getUserFacingMessage('error')).to.be.undefined
  174. })
  175. })
  176. })