123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299 |
- import $ from 'jquery';
- import {Client, Request, paramsToQueryArgs} from 'app/api';
- import GroupActions from 'app/actions/groupActions';
- import {PROJECT_MOVED} from 'app/constants/apiErrorCodes';
- import * as Sentry from '@sentry/browser';
- jest.unmock('app/api');
- describe('api', function() {
- let api;
- beforeEach(function() {
- api = new Client();
- });
- describe('paramsToQueryArgs()', function() {
- it('should convert itemIds properties to id array', function() {
- expect(
- paramsToQueryArgs({
- itemIds: [1, 2, 3],
- query: 'is:unresolved', // itemIds takes precedence
- })
- ).toEqual({id: [1, 2, 3]});
- });
- it('should extract query property if no itemIds', function() {
- expect(
- paramsToQueryArgs({
- query: 'is:unresolved',
- foo: 'bar',
- })
- ).toEqual({query: 'is:unresolved'});
- });
- it('should convert params w/o itemIds or query to empty object', function() {
- expect(
- paramsToQueryArgs({
- foo: 'bar',
- bar: 'baz', // paramsToQueryArgs ignores these
- })
- ).toEqual({});
- });
- it('should keep environment when query is provided', function() {
- expect(
- paramsToQueryArgs({
- query: 'is:unresolved',
- environment: 'production',
- })
- ).toEqual({query: 'is:unresolved', environment: 'production'});
- });
- it('should exclude environment when it is null/undefined', function() {
- expect(
- paramsToQueryArgs({
- query: 'is:unresolved',
- environment: null,
- })
- ).toEqual({query: 'is:unresolved'});
- });
- it('should handle non-empty projects', function() {
- expect(
- paramsToQueryArgs({
- itemIds: [1, 2, 3],
- project: [1],
- })
- ).toEqual({id: [1, 2, 3], project: [1]});
- expect(
- paramsToQueryArgs({
- itemIds: [1, 2, 3],
- project: [],
- })
- ).toEqual({id: [1, 2, 3]});
- expect(
- paramsToQueryArgs({
- itemIds: [1, 2, 3],
- project: null,
- })
- ).toEqual({id: [1, 2, 3]});
- });
- });
- describe('Client', function() {
- beforeEach(function() {
- jest.spyOn($, 'ajax');
- });
- describe('cancel()', function() {
- it('should abort any open XHR requests', function() {
- const req1 = new Request({
- abort: jest.fn(),
- });
- const req2 = new Request({
- abort: jest.fn(),
- });
- api.activeRequests = {
- 1: req1,
- 2: req2,
- };
- api.clear();
- expect(req1.xhr.abort).toHaveBeenCalledTimes(1);
- expect(req2.xhr.abort).toHaveBeenCalledTimes(1);
- });
- });
- });
- it('does not call success callback if 302 was returned because of a project slug change', function() {
- const successCb = jest.fn();
- api.activeRequests = {id: {alive: true}};
- api.wrapCallback('id', successCb)({
- responseJSON: {
- detail: {
- code: PROJECT_MOVED,
- message: '...',
- extra: {
- slug: 'new-slug',
- },
- },
- },
- });
- expect(successCb).not.toHaveBeenCalled();
- });
- it('handles error callback', function() {
- jest.spyOn(api, 'wrapCallback').mockImplementation((id, func) => func);
- const errorCb = jest.fn();
- const args = ['test', true, 1];
- api.handleRequestError(
- {
- id: 'test',
- path: 'test',
- requestOptions: {error: errorCb},
- },
- ...args
- );
- expect(errorCb).toHaveBeenCalledWith(...args);
- });
- it('handles undefined error callback', function() {
- expect(() =>
- api.handleRequestError(
- {
- id: 'test',
- path: 'test',
- requestOptions: {},
- },
- {},
- {}
- )
- ).not.toThrow();
- });
- describe('bulkUpdate()', function() {
- beforeEach(function() {
- jest.spyOn(api, '_wrapRequest');
- jest.spyOn(GroupActions, 'update'); // stub GroupActions.update call from api.update
- });
- it('should use itemIds as query if provided', function() {
- api.bulkUpdate({
- orgId: '1337',
- projectId: '1337',
- itemIds: [1, 2, 3],
- data: {status: 'unresolved'},
- query: 'is:resolved',
- });
- expect(api._wrapRequest).toHaveBeenCalledTimes(1);
- expect(api._wrapRequest).toHaveBeenCalledWith(
- '/projects/1337/1337/issues/',
- expect.objectContaining({query: {id: [1, 2, 3]}}),
- undefined
- );
- });
- it('should use query as query if itemIds are absent', function() {
- api.bulkUpdate({
- orgId: '1337',
- projectId: '1337',
- itemIds: null,
- data: {status: 'unresolved'},
- query: 'is:resolved',
- });
- expect(api._wrapRequest).toHaveBeenCalledTimes(1);
- expect(api._wrapRequest).toHaveBeenCalledWith(
- '/projects/1337/1337/issues/',
- expect.objectContaining({query: {query: 'is:resolved'}}),
- undefined
- );
- });
- it('should apply project option', function() {
- api.bulkUpdate({
- orgId: '1337',
- project: [99],
- itemIds: [1, 2, 3],
- data: {status: 'unresolved'},
- });
- expect(api._wrapRequest).toHaveBeenCalledTimes(1);
- expect(api._wrapRequest).toHaveBeenCalledWith(
- '/organizations/1337/issues/',
- expect.objectContaining({query: {id: [1, 2, 3], project: [99]}}),
- undefined
- );
- });
- });
- describe('merge()', function() {
- // TODO: this is totally copypasta from the test above. We need to refactor
- // these API methods/tests.
- beforeEach(function() {
- jest.spyOn(api, '_wrapRequest');
- jest.spyOn(GroupActions, 'merge'); // stub GroupActions.merge call from api.merge
- });
- it('should use itemIds as query if provided', function() {
- api.merge({
- orgId: '1337',
- projectId: '1337',
- itemIds: [1, 2, 3],
- data: {status: 'unresolved'},
- query: 'is:resolved',
- });
- expect(api._wrapRequest).toHaveBeenCalledTimes(1);
- expect(api._wrapRequest).toHaveBeenCalledWith(
- '/projects/1337/1337/issues/',
- expect.objectContaining({query: {id: [1, 2, 3]}}),
- undefined
- );
- });
- it('should use query as query if itemIds are absent', function() {
- api.merge({
- orgId: '1337',
- projectId: '1337',
- itemIds: null,
- data: {status: 'unresolved'},
- query: 'is:resolved',
- });
- expect(api._wrapRequest).toHaveBeenCalledTimes(1);
- expect(api._wrapRequest).toHaveBeenCalledWith(
- '/projects/1337/1337/issues/',
- expect.objectContaining({query: {query: 'is:resolved'}}),
- undefined
- );
- });
- });
- describe('Sentry reporting', function() {
- beforeEach(function() {
- jest.spyOn($, 'ajax');
- $.ajax.mockReset();
- Sentry.captureException.mockClear();
- $.ajax.mockImplementation(async ({error}) => {
- await tick();
- error({
- status: 404,
- statusText: 'Not Found',
- responseJSON: {detail: 'Item was not found'},
- });
- return {};
- });
- });
- it('reports correct error and stacktrace to Sentry', async function() {
- api.request('/some/url/');
- await tick();
- const errorObjectSentryCalled = Sentry.captureException.mock.calls[0][0];
- expect(errorObjectSentryCalled.name).toBe('NotFoundError');
- expect(errorObjectSentryCalled.message).toBe('GET /some/url/ 404');
- // First line of stack should be this test case
- expect(errorObjectSentryCalled.stack.split('\n')[1]).toContain('api.spec.jsx');
- });
- it('reports correct error and stacktrace to Sentry when using promises', async function() {
- await expect(
- api.requestPromise('/some/url/')
- ).rejects.toThrowErrorMatchingInlineSnapshot('"GET /some/url/ 404"');
- expect(Sentry.captureException).toHaveBeenCalled();
- });
- });
- });
|