123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330 |
- import {mountWithTheme} from 'sentry-test/enzyme';
- import {initializeOrg} from 'sentry-test/initializeOrg';
- import TagStore from 'sentry/stores/tagStore';
- import IssueListSearchBar from 'sentry/views/issueList/searchBar';
- describe('IssueListSearchBar', function () {
- let tagValuePromise;
- let supportedTags;
- let recentSearchMock;
- const {routerContext, organization} = initializeOrg({
- organization: {access: [], features: []},
- });
- const mockCursorPosition = (wrapper, pos) => {
- const component = wrapper.find('SmartSearchBar').instance();
- delete component.cursorPosition;
- Object.defineProperty(component, 'cursorPosition', {
- get: jest.fn().mockReturnValue(pos),
- configurable: true,
- });
- };
- beforeEach(function () {
- TagStore.reset();
- TagStore.loadTagsSuccess(TestStubs.Tags());
- supportedTags = TagStore.getAllTags();
- // Add a tag that is preseeded with values.
- supportedTags.is = {
- key: 'is',
- name: 'is',
- values: ['assigned', 'unresolved', 'ignored'],
- predefined: true,
- };
- tagValuePromise = Promise.resolve([]);
- recentSearchMock = MockApiClient.addMockResponse({
- url: '/organizations/org-slug/recent-searches/',
- method: 'GET',
- body: [],
- });
- });
- afterEach(function () {
- MockApiClient.clearMockResponses();
- });
- describe('updateAutoCompleteItems()', function () {
- it('sets state with complete tag', async () => {
- const loader = (key, value) => {
- expect(key).toEqual('url');
- expect(value).toEqual('fu');
- return tagValuePromise;
- };
- const props = {
- organization,
- query: 'url:"fu"',
- tagValueLoader: loader,
- supportedTags,
- onSearch: jest.fn(),
- };
- const searchBar = mountWithTheme(<IssueListSearchBar {...props} />, routerContext);
- mockCursorPosition(searchBar, 5);
- searchBar.find('textarea').simulate('click');
- searchBar.find('textarea').simulate('focus');
- await tick();
- expect(searchBar.find('SearchDropdown').prop('searchSubstring')).toEqual('"fu"');
- expect(searchBar.find('SearchDropdown').prop('items')).toEqual([]);
- });
- it('sets state when value has colon', async () => {
- const loader = (key, value) => {
- expect(key).toEqual('url');
- expect(value).toEqual('http://example.com');
- return tagValuePromise;
- };
- const props = {
- organization,
- projectId: '456',
- query: 'url:"http://example.com"',
- tagValueLoader: loader,
- supportedTags,
- onSearch: jest.fn(),
- };
- const searchBar = mountWithTheme(<IssueListSearchBar {...props} />, routerContext);
- mockCursorPosition(searchBar, 5);
- searchBar.find('textarea').simulate('click');
- searchBar.find('textarea').simulate('focus');
- await tick();
- expect(searchBar.state.searchTerm).toEqual();
- expect(searchBar.find('SearchDropdown').prop('searchSubstring')).toEqual(
- '"http://example.com"'
- );
- expect(searchBar.find('SearchDropdown').prop('items')).toEqual([]);
- });
- it('does not request values when tag is `timesSeen`', async () => {
- // This should never get called
- const loader = jest.fn(x => x);
- const props = {
- organization,
- projectId: '456',
- query: 'timesSeen:',
- tagValueLoader: loader,
- supportedTags,
- onSearch: jest.fn(),
- };
- const searchBar = mountWithTheme(<IssueListSearchBar {...props} />, routerContext);
- searchBar.find('textarea').simulate('click');
- searchBar.find('textarea').simulate('focus');
- await tick();
- expect(loader).not.toHaveBeenCalled();
- });
- });
- describe('Recent Searches', function () {
- it('saves search query as a recent search', async function () {
- const saveRecentSearch = MockApiClient.addMockResponse({
- url: '/organizations/org-slug/recent-searches/',
- method: 'POST',
- body: {},
- });
- const loader = (key, value) => {
- expect(key).toEqual('url');
- expect(value).toEqual('fu');
- return tagValuePromise;
- };
- const onSearch = jest.fn();
- const props = {
- organization,
- query: 'url:"fu"',
- onSearch,
- tagValueLoader: loader,
- supportedTags,
- };
- const searchBar = mountWithTheme(<IssueListSearchBar {...props} />, routerContext);
- mockCursorPosition(searchBar, 5);
- searchBar.find('textarea').simulate('focus');
- searchBar.find('textarea').simulate('click');
- await tick();
- expect(searchBar.find('SearchDropdown').prop('searchSubstring')).toEqual('"fu"');
- expect(searchBar.find('SearchDropdown').prop('items')).toEqual([]);
- jest.useRealTimers();
- searchBar.find('form').simulate('submit');
- expect(onSearch).toHaveBeenCalledWith('url:"fu"');
- await tick();
- searchBar.update();
- expect(saveRecentSearch).toHaveBeenCalledWith(
- expect.anything(),
- expect.objectContaining({
- data: {
- query: 'url:"fu"',
- type: 0,
- },
- })
- );
- });
- it('queries for recent searches', async function () {
- const props = {
- organization,
- query: 'timesSeen:',
- tagValueLoader: () => {},
- savedSearchType: 0,
- displayRecentSearches: true,
- supportedTags,
- };
- jest.useRealTimers();
- const wrapper = mountWithTheme(<IssueListSearchBar {...props} />, routerContext);
- wrapper.find('textarea').simulate('focus');
- wrapper.find('textarea').simulate('change', {target: {value: 'is:'}});
- await tick();
- wrapper.update();
- expect(recentSearchMock).toHaveBeenCalledWith(
- expect.anything(),
- expect.objectContaining({
- query: {
- query: 'is:',
- limit: 3,
- type: 0,
- },
- })
- );
- });
- it('cycles through keyboard navigation for selection', async function () {
- const props = {
- organization,
- query: 'timesSeen:',
- tagValueLoader: () => {},
- savedSearchType: 0,
- displayRecentSearches: true,
- supportedTags,
- };
- jest.useRealTimers();
- const wrapper = mountWithTheme(<IssueListSearchBar {...props} />, routerContext);
- wrapper.find('textarea').simulate('change', {target: {value: 'is:'}});
- await tick();
- wrapper.update();
- expect(
- wrapper.find('SearchListItem').at(0).find('li').prop('className')
- ).not.toContain('active');
- wrapper.find('textarea').simulate('keyDown', {key: 'ArrowDown'});
- expect(wrapper.find('SearchListItem').at(0).find('li').prop('className')).toContain(
- 'active'
- );
- wrapper.find('textarea').simulate('keyDown', {key: 'ArrowDown'});
- expect(wrapper.find('SearchListItem').at(1).find('li').prop('className')).toContain(
- 'active'
- );
- wrapper.find('textarea').simulate('keyDown', {key: 'ArrowUp'});
- wrapper.find('textarea').simulate('keyDown', {key: 'ArrowUp'});
- expect(
- wrapper.find('SearchListItem').last().find('li').prop('className')
- ).toContain('active');
- });
- });
- describe('Pinned Searches', function () {
- let pinSearch;
- let unpinSearch;
- beforeEach(function () {
- MockApiClient.clearMockResponses();
- pinSearch = MockApiClient.addMockResponse({
- url: '/organizations/org-slug/pinned-searches/',
- method: 'PUT',
- body: {},
- });
- unpinSearch = MockApiClient.addMockResponse({
- url: '/organizations/org-slug/pinned-searches/',
- method: 'DELETE',
- body: {},
- });
- MockApiClient.addMockResponse({
- url: '/organizations/org-slug/recent-searches/',
- method: 'GET',
- body: [],
- });
- });
- it('has pin icon', function () {
- const props = {
- query: 'url:"fu"',
- onSearch: jest.fn(),
- tagValueLoader: () => Promise.resolve([]),
- supportedTags,
- organization,
- };
- const searchBar = mountWithTheme(<IssueListSearchBar {...props} />, routerContext);
- expect(searchBar.find('ActionButton[data-test-id="pin-icon"]')).toHaveLength(1);
- });
- it('pins a search from the searchbar', function () {
- const props = {
- query: 'url:"fu"',
- onSearch: jest.fn(),
- tagValueLoader: () => Promise.resolve([]),
- supportedTags,
- organization,
- };
- const searchBar = mountWithTheme(<IssueListSearchBar {...props} />, routerContext);
- searchBar.find('ActionButton[data-test-id="pin-icon"] button').simulate('click');
- expect(pinSearch).toHaveBeenLastCalledWith(
- expect.anything(),
- expect.objectContaining({
- method: 'PUT',
- data: {
- query: 'url:"fu"',
- type: 0,
- },
- })
- );
- });
- it('unpins a search from the searchbar', function () {
- const props = {
- query: 'url:"fu"',
- onSearch: jest.fn(),
- tagValueLoader: () => Promise.resolve([]),
- supportedTags,
- organization,
- savedSearch: {id: '1', isPinned: true, query: 'url:"fu"'},
- };
- const searchBar = mountWithTheme(<IssueListSearchBar {...props} />, routerContext);
- searchBar
- .find('ActionButton[aria-label="Unpin this search"] button')
- .simulate('click');
- expect(unpinSearch).toHaveBeenLastCalledWith(
- expect.anything(),
- expect.objectContaining({
- method: 'DELETE',
- data: {
- type: 0,
- },
- })
- );
- });
- });
- });
|