searchBar.spec.tsx 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. import {Tags} from 'sentry-fixture/tags';
  2. import {initializeOrg} from 'sentry-test/initializeOrg';
  3. import {render, screen, userEvent, waitFor} from 'sentry-test/reactTestingLibrary';
  4. import TagStore from 'sentry/stores/tagStore';
  5. import IssueListSearchBar from 'sentry/views/issueList/searchBar';
  6. describe('IssueListSearchBar', function () {
  7. let recentSearchMock;
  8. let defaultProps;
  9. const {routerContext, organization} = initializeOrg();
  10. beforeEach(function () {
  11. TagStore.reset();
  12. TagStore.loadTagsSuccess(Tags());
  13. defaultProps = {
  14. organization,
  15. query: '',
  16. onSearch: jest.fn(),
  17. };
  18. recentSearchMock = MockApiClient.addMockResponse({
  19. url: '/organizations/org-slug/recent-searches/',
  20. method: 'GET',
  21. body: [],
  22. });
  23. });
  24. afterEach(function () {
  25. MockApiClient.clearMockResponses();
  26. });
  27. describe('updateAutoCompleteItems()', function () {
  28. it('sets state with complete tag', async function () {
  29. const tagValuesMock = MockApiClient.addMockResponse({
  30. url: '/organizations/org-slug/tags/url/values/',
  31. method: 'GET',
  32. body: [],
  33. });
  34. render(<IssueListSearchBar {...defaultProps} />, {
  35. context: routerContext,
  36. });
  37. await userEvent.click(screen.getByRole('textbox'));
  38. await userEvent.paste('url:"fu"');
  39. expect(tagValuesMock).toHaveBeenLastCalledWith(
  40. expect.anything(),
  41. expect.objectContaining({
  42. query: expect.objectContaining({
  43. query: 'fu',
  44. }),
  45. })
  46. );
  47. expect(screen.getByTestId('smart-search-dropdown')).toBeInTheDocument();
  48. });
  49. it('sets state when value has colon', async function () {
  50. const tagValuesMock = MockApiClient.addMockResponse({
  51. url: '/organizations/org-slug/tags/url/values/',
  52. method: 'GET',
  53. body: [],
  54. });
  55. render(<IssueListSearchBar {...defaultProps} />, {
  56. context: routerContext,
  57. });
  58. await userEvent.click(screen.getByRole('textbox'));
  59. await userEvent.paste('url:', {delay: null});
  60. expect(tagValuesMock).toHaveBeenCalled();
  61. });
  62. it('does not request values when tag is `timesSeen`', async function () {
  63. const tagValuesMock = MockApiClient.addMockResponse({
  64. url: '/organizations/org-slug/tags/url/values/',
  65. method: 'GET',
  66. body: [],
  67. });
  68. render(<IssueListSearchBar {...defaultProps} />, {
  69. context: routerContext,
  70. });
  71. await userEvent.click(screen.getByRole('textbox'));
  72. await userEvent.paste('timesSeen:', {delay: null});
  73. expect(tagValuesMock).not.toHaveBeenCalled();
  74. });
  75. });
  76. describe('Recent Searches', function () {
  77. it('saves search query as a recent search', async function () {
  78. const tagValuesMock = MockApiClient.addMockResponse({
  79. url: '/organizations/org-slug/tags/url/values/',
  80. method: 'GET',
  81. body: [],
  82. });
  83. const saveRecentSearch = MockApiClient.addMockResponse({
  84. url: '/organizations/org-slug/recent-searches/',
  85. method: 'POST',
  86. body: {},
  87. });
  88. const onSearch = jest.fn();
  89. render(<IssueListSearchBar {...defaultProps} onSearch={onSearch} />, {
  90. context: routerContext,
  91. });
  92. await userEvent.click(screen.getByRole('textbox'));
  93. await userEvent.paste('url:"fu"');
  94. expect(tagValuesMock).toHaveBeenLastCalledWith(
  95. expect.anything(),
  96. expect.objectContaining({
  97. query: expect.objectContaining({
  98. query: 'fu',
  99. }),
  100. })
  101. );
  102. expect(screen.getByTestId('smart-search-dropdown')).toBeInTheDocument();
  103. await userEvent.keyboard('{Enter}');
  104. expect(onSearch).toHaveBeenCalledWith('url:"fu"');
  105. expect(saveRecentSearch).toHaveBeenCalledWith(
  106. expect.anything(),
  107. expect.objectContaining({
  108. data: {
  109. query: 'url:"fu"',
  110. type: 0,
  111. },
  112. })
  113. );
  114. });
  115. it('queries for recent searches', async function () {
  116. MockApiClient.addMockResponse({
  117. url: '/organizations/org-slug/tags/url/values/',
  118. method: 'GET',
  119. body: [],
  120. });
  121. render(<IssueListSearchBar {...defaultProps} />, {context: routerContext});
  122. await userEvent.click(screen.getByRole('textbox'));
  123. await userEvent.paste('is:', {delay: null});
  124. expect(recentSearchMock).toHaveBeenCalledWith(
  125. expect.anything(),
  126. expect.objectContaining({
  127. query: {
  128. query: 'is:',
  129. limit: 3,
  130. type: 0,
  131. },
  132. })
  133. );
  134. });
  135. // Flaky due to timeouts, see https://github.com/getsentry/sentry/issues/42898
  136. // eslint-disable-next-line jest/no-disabled-tests
  137. it.skip('cycles through keyboard navigation for selection', async function () {
  138. MockApiClient.addMockResponse({
  139. url: '/organizations/org-slug/tags/device.orientation/values/',
  140. method: 'GET',
  141. body: [],
  142. });
  143. render(<IssueListSearchBar {...defaultProps} />, {context: routerContext});
  144. const textarea = screen.getByRole('textbox');
  145. // Keyboard navigate to first item and select
  146. await userEvent.type(textarea, 't');
  147. await waitFor(() =>
  148. expect(screen.getAllByTestId('search-autocomplete-item')[0]).toBeInTheDocument()
  149. );
  150. await userEvent.keyboard('{ArrowDown}{Tab}');
  151. expect(textarea).not.toHaveValue('t');
  152. const firstItemValue = textarea.textContent;
  153. // Keyboard navigate to second item and select
  154. await userEvent.keyboard('{selectall}{backspace}t');
  155. await waitFor(() =>
  156. expect(screen.getAllByTestId('search-autocomplete-item')[0]).toBeInTheDocument()
  157. );
  158. await userEvent.keyboard('{ArrowDown}{ArrowDown}{Tab}');
  159. expect(textarea).not.toHaveValue(firstItemValue);
  160. // Keyboard navigate to second item, then back to first item and select
  161. await userEvent.keyboard('{selectall}{backspace}t');
  162. await waitFor(() =>
  163. expect(screen.getAllByTestId('search-autocomplete-item')[0]).toBeInTheDocument()
  164. );
  165. await userEvent.keyboard('{ArrowDown}{ArrowDown}{ArrowUp}{Tab}');
  166. expect(textarea).toHaveValue(firstItemValue);
  167. });
  168. });
  169. });