123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427 |
- import {mountWithTheme} from 'sentry-test/enzyme';
- import {ApiSource} from 'sentry/components/search/sources/apiSource';
- describe('ApiSource', function () {
- let wrapper;
- const org = TestStubs.Organization();
- let orgsMock;
- let projectsMock;
- let teamsMock;
- let membersMock;
- let shortIdMock;
- let eventIdMock;
- let allMocks;
- beforeEach(function () {
- MockApiClient.clearMockResponses();
- MockApiClient.addMockResponse({
- url: '/organizations/',
- query: 'test-1',
- body: [TestStubs.Organization({slug: 'test-org'})],
- });
- orgsMock = MockApiClient.addMockResponse({
- url: '/organizations/',
- query: 'foo',
- body: [TestStubs.Organization({slug: 'foo-org'})],
- });
- projectsMock = MockApiClient.addMockResponse({
- url: '/organizations/org-slug/projects/',
- query: 'foo',
- body: [TestStubs.Project({slug: 'foo-project'})],
- });
- teamsMock = MockApiClient.addMockResponse({
- url: '/organizations/org-slug/teams/',
- query: 'foo',
- body: [TestStubs.Team({slug: 'foo-team'})],
- });
- membersMock = MockApiClient.addMockResponse({
- url: '/organizations/org-slug/members/',
- query: 'foo',
- body: TestStubs.Members(),
- });
- shortIdMock = MockApiClient.addMockResponse({
- url: '/organizations/org-slug/shortids/test-1/',
- query: 'TEST-1',
- body: TestStubs.ShortIdQueryResult(),
- });
- eventIdMock = MockApiClient.addMockResponse({
- url: '/organizations/org-slug/eventids/12345678901234567890123456789012/',
- query: '12345678901234567890123456789012',
- body: TestStubs.EventIdQueryResult(),
- });
- MockApiClient.addMockResponse({
- url: '/organizations/org-slug/plugins/?plugins=_all',
- query: {plugins: '_all'},
- body: [],
- });
- MockApiClient.addMockResponse({
- url: '/organizations/org-slug/plugins/configs/',
- body: [],
- });
- MockApiClient.addMockResponse({
- url: '/organizations/org-slug/config/integrations/',
- body: [],
- });
- MockApiClient.addMockResponse({
- url: '/sentry-apps/?status=published',
- body: [],
- });
- MockApiClient.addMockResponse({
- url: '/doc-integrations/',
- body: [],
- });
- MockApiClient.addMockResponse({
- url: '/organizations/org-slug/shortids/foo-t/',
- body: [],
- });
- allMocks = {orgsMock, projectsMock, teamsMock, membersMock, shortIdMock, eventIdMock};
- });
- it('queries all API endpoints', function () {
- const mock = jest.fn().mockReturnValue(null);
- wrapper = mountWithTheme(
- <ApiSource params={{orgId: org.slug}} query="foo">
- {mock}
- </ApiSource>
- );
- expect(orgsMock).toHaveBeenCalled();
- expect(projectsMock).toHaveBeenCalled();
- expect(teamsMock).toHaveBeenCalled();
- expect(membersMock).toHaveBeenCalled();
- expect(shortIdMock).not.toHaveBeenCalled();
- expect(eventIdMock).not.toHaveBeenCalled();
- });
- it('only queries for shortids when query matches shortid format', async function () {
- const mock = jest.fn().mockReturnValue(null);
- wrapper = mountWithTheme(
- <ApiSource params={{orgId: org.slug}} query="test-">
- {mock}
- </ApiSource>
- );
- await tick();
- expect(shortIdMock).not.toHaveBeenCalled();
- // Reset all mocks
- Object.values(allMocks).forEach(m => m.mockReset);
- // This is a valid short id now
- wrapper.setProps({query: 'test-1'});
- await tick();
- wrapper.update();
- expect(shortIdMock).toHaveBeenCalled();
- // These may not be desired behavior in future, but lets specify the expectation regardless
- expect(orgsMock).toHaveBeenCalled();
- expect(projectsMock).toHaveBeenCalled();
- expect(teamsMock).toHaveBeenCalled();
- expect(membersMock).toHaveBeenCalled();
- expect(eventIdMock).not.toHaveBeenCalled();
- expect(mock).toHaveBeenLastCalledWith(
- expect.objectContaining({
- results: [
- {
- item: expect.objectContaining({
- title: 'group type',
- description: 'group description',
- sourceType: 'issue',
- resultType: 'issue',
- to: '/org-slug/project-slug/issues/1/',
- }),
- score: 1,
- refIndex: 0,
- },
- ],
- })
- );
- });
- it('only queries for eventids when query matches eventid format of 32 chars', async function () {
- const mock = jest.fn().mockReturnValue(null);
- wrapper = mountWithTheme(
- <ApiSource params={{orgId: org.slug}} query="1234567890123456789012345678901">
- {mock}
- </ApiSource>
- );
- await tick();
- expect(eventIdMock).not.toHaveBeenCalled();
- // Reset all mocks
- Object.values(allMocks).forEach(m => m.mockReset);
- // This is a valid short id now
- wrapper.setProps({query: '12345678901234567890123456789012'});
- wrapper.update();
- await tick();
- expect(eventIdMock).toHaveBeenCalled();
- // These may not be desired behavior in future, but lets specify the expectation regardless
- expect(orgsMock).toHaveBeenCalled();
- expect(projectsMock).toHaveBeenCalled();
- expect(teamsMock).toHaveBeenCalled();
- expect(membersMock).toHaveBeenCalled();
- expect(shortIdMock).not.toHaveBeenCalled();
- expect(mock).toHaveBeenLastCalledWith(
- expect.objectContaining({
- results: [
- {
- item: expect.objectContaining({
- title: 'event type',
- description: 'event description',
- sourceType: 'event',
- resultType: 'event',
- to: '/org-slug/project-slug/issues/1/events/12345678901234567890123456789012/',
- }),
- score: 1,
- refIndex: 0,
- },
- ],
- })
- );
- });
- it('only queries org endpoint if there is no org in context', function () {
- const mock = jest.fn().mockReturnValue(null);
- wrapper = mountWithTheme(
- <ApiSource params={{}} query="foo">
- {mock}
- </ApiSource>
- );
- expect(orgsMock).toHaveBeenCalled();
- expect(projectsMock).not.toHaveBeenCalled();
- expect(teamsMock).not.toHaveBeenCalled();
- expect(membersMock).not.toHaveBeenCalled();
- });
- it('render function is called with correct results', async function () {
- const mock = jest.fn().mockReturnValue(null);
- wrapper = mountWithTheme(
- <ApiSource params={{orgId: org.slug}} organization={org} query="foo">
- {mock}
- </ApiSource>
- );
- await tick();
- wrapper.update();
- expect(mock).toHaveBeenLastCalledWith({
- isLoading: false,
- results: expect.arrayContaining([
- expect.objectContaining({
- item: expect.objectContaining({
- model: expect.objectContaining({
- slug: 'foo-org',
- }),
- sourceType: 'organization',
- resultType: 'settings',
- to: '/settings/foo-org/',
- }),
- matches: expect.anything(),
- score: expect.anything(),
- }),
- expect.objectContaining({
- item: expect.objectContaining({
- model: expect.objectContaining({
- slug: 'foo-org',
- }),
- sourceType: 'organization',
- resultType: 'route',
- to: '/foo-org/',
- }),
- matches: expect.anything(),
- score: expect.anything(),
- }),
- expect.objectContaining({
- item: expect.objectContaining({
- model: expect.objectContaining({
- slug: 'foo-project',
- }),
- sourceType: 'project',
- resultType: 'route',
- to: '/organizations/org-slug/projects/foo-project/?project=2',
- }),
- matches: expect.anything(),
- score: expect.anything(),
- }),
- expect.objectContaining({
- item: expect.objectContaining({
- model: expect.objectContaining({
- slug: 'foo-project',
- }),
- sourceType: 'project',
- resultType: 'route',
- to: '/organizations/org-slug/alerts/rules/?project=2',
- }),
- matches: expect.anything(),
- score: expect.anything(),
- }),
- expect.objectContaining({
- item: expect.objectContaining({
- model: expect.objectContaining({
- slug: 'foo-project',
- }),
- sourceType: 'project',
- resultType: 'settings',
- to: '/settings/org-slug/projects/foo-project/',
- }),
- matches: expect.anything(),
- score: expect.anything(),
- }),
- expect.objectContaining({
- item: expect.objectContaining({
- model: expect.objectContaining({
- slug: 'foo-team',
- }),
- sourceType: 'team',
- resultType: 'settings',
- to: '/settings/org-slug/teams/foo-team/',
- }),
- matches: expect.anything(),
- score: expect.anything(),
- }),
- ]),
- });
- // The return values here are because of fuzzy search matching.
- // There are no members that match
- expect(mock.mock.calls[1][0].results).toHaveLength(6);
- });
- it('render function is called with correct results when API requests partially succeed', async function () {
- const mock = jest.fn().mockReturnValue(null);
- MockApiClient.addMockResponse({
- url: '/organizations/org-slug/projects/',
- query: 'foo',
- statusCode: 500,
- });
- wrapper = mountWithTheme(
- <ApiSource params={{orgId: org.slug}} query="foo">
- {mock}
- </ApiSource>
- );
- await tick();
- wrapper.update();
- expect(mock).toHaveBeenLastCalledWith({
- isLoading: false,
- results: expect.arrayContaining([
- expect.objectContaining({
- item: expect.objectContaining({
- model: expect.objectContaining({
- slug: 'foo-org',
- }),
- }),
- }),
- expect.objectContaining({
- item: expect.objectContaining({
- model: expect.objectContaining({
- slug: 'foo-org',
- }),
- }),
- }),
- expect.objectContaining({
- item: expect.objectContaining({
- model: expect.objectContaining({
- slug: 'foo-team',
- }),
- }),
- }),
- ]),
- });
- // The return values here are because of fuzzy search matching.
- // There are no members that match
- expect(mock.mock.calls[1][0].results).toHaveLength(3);
- });
- it('render function is updated as query changes', async function () {
- const mock = jest.fn().mockReturnValue(null);
- wrapper = mountWithTheme(
- <ApiSource params={{orgId: org.slug}} query="foo">
- {mock}
- </ApiSource>
- );
- await tick();
- wrapper.update();
- // The return values here are because of fuzzy search matching.
- // There are no members that match
- expect(mock.mock.calls[1][0].results).toHaveLength(6);
- expect(mock.mock.calls[1][0].results[0].item.model.slug).toBe('foo-org');
- mock.mockClear();
- wrapper.setProps({query: 'foo-t'});
- await tick();
- wrapper.update();
- // Still have 4 results, but is re-ordered
- expect(mock.mock.calls[0][0].results).toHaveLength(6);
- expect(mock.mock.calls[0][0].results[0].item.model.slug).toBe('foo-team');
- });
- describe('API queries', function () {
- let mock;
- beforeAll(function () {
- mock = jest.fn().mockReturnValue(null);
- wrapper = mountWithTheme(
- <ApiSource params={{orgId: org.slug}} query="">
- {mock}
- </ApiSource>
- );
- });
- it('does not call API with empty query string', function () {
- expect(projectsMock).not.toHaveBeenCalled();
- });
- it('calls API when query string length is 1 char', function () {
- wrapper.setProps({query: 'f'});
- wrapper.update();
- expect(projectsMock).toHaveBeenCalledTimes(1);
- });
- it('calls API when query string length increases from 1 -> 2', function () {
- wrapper.setProps({query: 'fo'});
- wrapper.update();
- expect(projectsMock).toHaveBeenCalledTimes(1);
- });
- it('does not query API when query string > 2 chars', function () {
- // Should not query API when query is > 2 chars
- wrapper.setProps({query: 'foo'});
- wrapper.update();
- expect(projectsMock).toHaveBeenCalledTimes(0);
- });
- it('does not query API when query string 3 -> 4 chars', function () {
- wrapper.setProps({query: 'foob'});
- wrapper.update();
- expect(projectsMock).toHaveBeenCalledTimes(0);
- });
- it('re-queries API if first 2 characters are different', function () {
- wrapper.setProps({query: 'ba'});
- wrapper.update();
- expect(projectsMock).toHaveBeenCalledTimes(1);
- });
- it('does not requery if query string is the same', function () {
- wrapper.setProps({query: 'ba'});
- wrapper.update();
- expect(projectsMock).toHaveBeenCalledTimes(0);
- });
- it('queries if we go from 2 chars -> 1 char', function () {
- wrapper.setProps({query: 'b'});
- wrapper.update();
- expect(projectsMock).toHaveBeenCalledTimes(1);
- });
- });
- });
|