searchBar.spec.tsx 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. import {TagsFixture} 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 type {Tag, TagValue} from 'sentry/types/group';
  6. import {IsFieldValues} from 'sentry/utils/fields';
  7. import IssueListSearchBar from 'sentry/views/issueList/searchBar';
  8. describe('IssueListSearchBar', function () {
  9. let recentSearchMock;
  10. let defaultProps;
  11. const {router, organization} = initializeOrg();
  12. beforeEach(function () {
  13. TagStore.reset();
  14. TagStore.loadTagsSuccess(TagsFixture());
  15. defaultProps = {
  16. organization,
  17. query: '',
  18. onSearch: jest.fn(),
  19. };
  20. recentSearchMock = MockApiClient.addMockResponse({
  21. url: '/organizations/org-slug/recent-searches/',
  22. method: 'GET',
  23. body: [],
  24. });
  25. });
  26. afterEach(function () {
  27. MockApiClient.clearMockResponses();
  28. });
  29. describe('updateAutoCompleteItems()', function () {
  30. it('sets state with complete tag', async function () {
  31. const tagValuesMock = MockApiClient.addMockResponse({
  32. url: '/organizations/org-slug/tags/url/values/',
  33. method: 'GET',
  34. body: [],
  35. });
  36. render(<IssueListSearchBar {...defaultProps} />, {
  37. router,
  38. });
  39. await userEvent.click(screen.getByRole('textbox'));
  40. await userEvent.paste('url:"fu"');
  41. expect(tagValuesMock).toHaveBeenLastCalledWith(
  42. expect.anything(),
  43. expect.objectContaining({
  44. query: expect.objectContaining({
  45. query: 'fu',
  46. }),
  47. })
  48. );
  49. expect(screen.getByTestId('smart-search-dropdown')).toBeInTheDocument();
  50. });
  51. it('sets state when value has colon', async function () {
  52. const tagValuesMock = MockApiClient.addMockResponse({
  53. url: '/organizations/org-slug/tags/url/values/',
  54. method: 'GET',
  55. body: [],
  56. });
  57. render(<IssueListSearchBar {...defaultProps} />, {
  58. router,
  59. });
  60. await userEvent.click(screen.getByRole('textbox'));
  61. await userEvent.paste('url:', {delay: null});
  62. expect(tagValuesMock).toHaveBeenCalled();
  63. });
  64. it('does not request values when tag is `timesSeen`', async function () {
  65. const tagValuesMock = MockApiClient.addMockResponse({
  66. url: '/organizations/org-slug/tags/url/values/',
  67. method: 'GET',
  68. body: [],
  69. });
  70. render(<IssueListSearchBar {...defaultProps} />, {
  71. router,
  72. });
  73. await userEvent.click(screen.getByRole('textbox'));
  74. await userEvent.paste('timesSeen:', {delay: null});
  75. expect(tagValuesMock).not.toHaveBeenCalled();
  76. });
  77. });
  78. describe('Recent Searches', function () {
  79. it('saves search query as a recent search', async function () {
  80. const tagValuesMock = MockApiClient.addMockResponse({
  81. url: '/organizations/org-slug/tags/url/values/',
  82. method: 'GET',
  83. body: [],
  84. });
  85. const saveRecentSearch = MockApiClient.addMockResponse({
  86. url: '/organizations/org-slug/recent-searches/',
  87. method: 'POST',
  88. body: {},
  89. });
  90. const onSearch = jest.fn();
  91. render(<IssueListSearchBar {...defaultProps} onSearch={onSearch} />, {
  92. router,
  93. });
  94. await userEvent.click(screen.getByRole('textbox'));
  95. await userEvent.paste('url:"fu"');
  96. expect(tagValuesMock).toHaveBeenLastCalledWith(
  97. expect.anything(),
  98. expect.objectContaining({
  99. query: expect.objectContaining({
  100. query: 'fu',
  101. }),
  102. })
  103. );
  104. expect(screen.getByTestId('smart-search-dropdown')).toBeInTheDocument();
  105. await userEvent.keyboard('{Enter}');
  106. expect(onSearch).toHaveBeenCalledWith('url:"fu"');
  107. expect(saveRecentSearch).toHaveBeenCalledWith(
  108. expect.anything(),
  109. expect.objectContaining({
  110. data: {
  111. query: 'url:"fu"',
  112. type: 0,
  113. },
  114. })
  115. );
  116. });
  117. it('queries for recent searches', async function () {
  118. MockApiClient.addMockResponse({
  119. url: '/organizations/org-slug/tags/url/values/',
  120. method: 'GET',
  121. body: [],
  122. });
  123. render(<IssueListSearchBar {...defaultProps} />, {router});
  124. await userEvent.click(screen.getByRole('textbox'));
  125. await userEvent.paste('is:', {delay: null});
  126. expect(recentSearchMock).toHaveBeenCalledWith(
  127. expect.anything(),
  128. expect.objectContaining({
  129. query: {
  130. query: 'is:',
  131. limit: 3,
  132. type: 0,
  133. },
  134. })
  135. );
  136. });
  137. });
  138. describe('Tags and Fields', function () {
  139. const {router: routerWithFlag, organization: orgWithFlag} = initializeOrg();
  140. orgWithFlag.features = ['issue-stream-search-query-builder'];
  141. const newDefaultProps = {
  142. organization: orgWithFlag,
  143. query: '',
  144. statsPeriod: '7d',
  145. onSearch: jest.fn(),
  146. };
  147. it('displays the correct options for the is tag', async function () {
  148. MockApiClient.addMockResponse({
  149. url: '/organizations/org-slug/tags/',
  150. body: [],
  151. });
  152. render(<IssueListSearchBar {...newDefaultProps} />, {
  153. router: routerWithFlag,
  154. });
  155. await userEvent.click(screen.getByRole('combobox', {name: 'Add a search term'}));
  156. await userEvent.paste('is:', {delay: null});
  157. await userEvent.click(
  158. await screen.findByRole('button', {name: 'Edit value for filter: is'})
  159. );
  160. Object.values(IsFieldValues).forEach(value => {
  161. expect(screen.getByRole('option', {name: value})).toBeInTheDocument();
  162. });
  163. });
  164. it('displays the correct options under Event Tags', async function () {
  165. MockApiClient.addMockResponse({
  166. url: '/organizations/org-slug/tags/',
  167. body: [{key: 'someTag', name: 'Some Tag'}],
  168. });
  169. render(<IssueListSearchBar {...newDefaultProps} />, {
  170. router: routerWithFlag,
  171. });
  172. await userEvent.click(screen.getByRole('combobox', {name: 'Add a search term'}));
  173. await userEvent.click(screen.getByRole('button', {name: 'Event Tags'}));
  174. expect(await screen.findByRole('option', {name: 'someTag'})).toBeInTheDocument();
  175. });
  176. it('displays tags in the has filter', async function () {
  177. MockApiClient.addMockResponse({
  178. url: '/organizations/org-slug/tags/',
  179. body: [{key: 'someTag', name: 'Some Tag'}],
  180. });
  181. defaultProps.organization.features = ['issue-stream-search-query-builder'];
  182. render(<IssueListSearchBar {...newDefaultProps} />, {
  183. router: routerWithFlag,
  184. });
  185. await userEvent.click(screen.getByRole('combobox', {name: 'Add a search term'}));
  186. await userEvent.paste('has:', {delay: null});
  187. await userEvent.click(
  188. await screen.findByRole('button', {name: 'Edit value for filter: has'})
  189. );
  190. expect(await screen.findByRole('option', {name: 'someTag'})).toBeInTheDocument();
  191. });
  192. });
  193. describe('Tag Values', function () {
  194. const {router: routerWithFlag, organization: orgWithFlag} = initializeOrg();
  195. orgWithFlag.features = ['issue-stream-search-query-builder'];
  196. const newDefaultProps = {
  197. organization: orgWithFlag,
  198. query: '',
  199. statsPeriod: '7d',
  200. onSearch: jest.fn(),
  201. };
  202. it('displays the correct tag values for a key', async () => {
  203. const tagKey = 'random';
  204. const tagValue = 'randomValue';
  205. const tagValueResponse: TagValue[] = [
  206. {
  207. name: tagValue,
  208. value: tagValue,
  209. count: 1,
  210. firstSeen: '2021-01-01T00:00:00Z',
  211. lastSeen: '2021-01-01T00:00:00Z',
  212. email: 'a@sentry.io',
  213. username: 'a',
  214. id: '1',
  215. ip_address: '1',
  216. },
  217. ];
  218. const tag: Tag = {
  219. key: tagKey,
  220. name: tagKey,
  221. };
  222. MockApiClient.addMockResponse({
  223. url: '/organizations/org-slug/tags/',
  224. method: 'GET',
  225. body: [tag],
  226. });
  227. const tagValueMock = MockApiClient.addMockResponse({
  228. url: `/organizations/org-slug/tags/${tagKey}/values/`,
  229. method: 'GET',
  230. body: tagValueResponse,
  231. });
  232. render(<IssueListSearchBar {...newDefaultProps} />, {
  233. router: routerWithFlag,
  234. });
  235. await userEvent.click(screen.getByRole('combobox', {name: 'Add a search term'}));
  236. await userEvent.paste(tagKey, {delay: null});
  237. await userEvent.click(screen.getByRole('option', {name: tagKey}));
  238. expect(await screen.findByRole('option', {name: tagValue})).toBeInTheDocument();
  239. await waitFor(() => {
  240. // Expected twice since we make one request for values in events dataset
  241. // and another for values in IssuePlatform dataset.
  242. expect(tagValueMock).toHaveBeenCalledTimes(2);
  243. });
  244. });
  245. });
  246. });