handleXhrErrorResponse.spec.tsx 3.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. import * as Sentry from '@sentry/react';
  2. import {handleXhrErrorResponse} from 'sentry/utils/handleXhrErrorResponse';
  3. import RequestError from 'sentry/utils/requestError/requestError';
  4. describe('handleXhrErrorResponse', function () {
  5. const stringError = new RequestError('GET', '/api/0', new Error('dead'), {
  6. status: 400,
  7. responseText: 'Error message',
  8. statusText: 'Bad Request',
  9. getResponseHeader: () => 'application/json',
  10. responseJSON: {detail: 'Error message'},
  11. });
  12. const objError = new RequestError('GET', '/api/0', new Error('dead'), {
  13. status: 400,
  14. responseText: 'Error message',
  15. statusText: 'Bad Request',
  16. getResponseHeader: () => 'application/json',
  17. responseJSON: {detail: {code: 'api-err-code', message: 'Error message'}},
  18. });
  19. beforeEach(function () {
  20. jest.clearAllMocks();
  21. });
  22. it('does nothing if we have invalid response', function () {
  23. // cast to invalid type on purpose
  24. handleXhrErrorResponse('', null as any);
  25. expect(Sentry.captureException).not.toHaveBeenCalled();
  26. // cast to invalid type on purpose
  27. handleXhrErrorResponse('', undefined as any);
  28. expect(Sentry.captureException).not.toHaveBeenCalled();
  29. });
  30. it('captures an exception to sdk when `resp.detail` is a string', function () {
  31. handleXhrErrorResponse('String error', stringError);
  32. expect(Sentry.captureException).toHaveBeenCalledWith(new Error('String error'));
  33. });
  34. it('captures an exception to sdk when `resp.detail` is an object', function () {
  35. handleXhrErrorResponse('Object error', objError);
  36. expect(Sentry.captureException).toHaveBeenCalledWith(new Error('Object error'));
  37. });
  38. it('ignores `sudo-required` errors', function () {
  39. handleXhrErrorResponse(
  40. 'Sudo required error',
  41. new RequestError('GET', '/api/0', new Error('dead'), {
  42. status: 401,
  43. responseText: 'Sudo required',
  44. statusText: 'Unauthorized',
  45. getResponseHeader: () => 'application/json',
  46. responseJSON: {
  47. detail: {
  48. code: 'sudo-required',
  49. detail: 'Sudo required',
  50. },
  51. },
  52. })
  53. );
  54. expect(Sentry.captureException).not.toHaveBeenCalled();
  55. });
  56. it('adds data to the scope', () => {
  57. const status = 404;
  58. const responseJSON = {
  59. detail: {
  60. code: 'distracted-by-squirrel',
  61. detail: 'Got distracted by a squirrel',
  62. },
  63. };
  64. const err = new RequestError('GET', '/ball', new Error('API error'), {
  65. status,
  66. getResponseHeader: () => 'application/json',
  67. statusText: 'Not Found',
  68. responseText: 'distraced-by-squirrel: Got distracted by a squirrel',
  69. responseJSON,
  70. });
  71. const mockScope = new Sentry.Scope();
  72. const setExtrasSpy = jest.spyOn(mockScope, 'setExtras');
  73. const setTagsSpy = jest.spyOn(mockScope, 'setTags');
  74. const hub = Sentry.getCurrentHub();
  75. jest.spyOn(hub, 'pushScope').mockReturnValueOnce(mockScope);
  76. handleXhrErrorResponse("Can't fetch ball", err);
  77. expect(setExtrasSpy).toHaveBeenCalledWith({status, responseJSON});
  78. expect(setTagsSpy).toHaveBeenCalledWith({
  79. responseStatus: status,
  80. endpoint: 'GET /ball',
  81. });
  82. });
  83. });