searchBar.spec.tsx 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. import {
  2. act,
  3. render,
  4. screen,
  5. userEvent,
  6. waitForElementToBeRemoved,
  7. } from 'sentry-test/reactTestingLibrary';
  8. import {textWithMarkupMatcher} from 'sentry-test/utils';
  9. import SearchBar, {SearchBarProps} from 'sentry/components/performance/searchBar';
  10. import EventView from 'sentry/utils/discover/eventView';
  11. // Jest's fake timers don't advance the debounce timer, so we need to mock it
  12. // with a different implementation. This could probably go in __mocks__ since
  13. // it's used in a few tests.
  14. jest.mock('lodash/debounce', () => {
  15. const debounceMap = new Map();
  16. const mockDebounce =
  17. (fn, timeout) =>
  18. (...args) => {
  19. if (debounceMap.has(fn)) {
  20. clearTimeout(debounceMap.get(fn));
  21. }
  22. debounceMap.set(
  23. fn,
  24. setTimeout(() => {
  25. fn.apply(fn, args);
  26. debounceMap.delete(fn);
  27. }, timeout)
  28. );
  29. };
  30. return mockDebounce;
  31. });
  32. describe('SearchBar', () => {
  33. let eventsMock;
  34. const organization = TestStubs.Organization();
  35. const testProps: SearchBarProps = {
  36. onSearch: jest.fn(),
  37. organization,
  38. eventView: EventView.fromSavedQuery({
  39. id: '',
  40. name: '',
  41. fields: [],
  42. projects: [],
  43. version: 2,
  44. }),
  45. query: '',
  46. };
  47. beforeAll(() => {
  48. jest.useFakeTimers();
  49. });
  50. beforeEach(() => {
  51. MockApiClient.clearMockResponses();
  52. jest.clearAllMocks();
  53. eventsMock = MockApiClient.addMockResponse({
  54. url: `/organizations/${organization.slug}/eventsv2/`,
  55. body: {data: []},
  56. });
  57. });
  58. afterAll(() => {
  59. jest.useRealTimers();
  60. });
  61. it('Sends user input as a transaction search and shows the results', async () => {
  62. eventsMock = MockApiClient.addMockResponse({
  63. url: `/organizations/${organization.slug}/eventsv2/`,
  64. body: {
  65. data: [{transaction: 'clients.call'}, {transaction: 'clients.fetch'}],
  66. },
  67. });
  68. render(<SearchBar {...testProps} />);
  69. userEvent.type(screen.getByRole('textbox'), 'proje');
  70. expect(screen.getByRole('textbox')).toHaveValue('proje');
  71. act(() => {
  72. jest.runAllTimers();
  73. });
  74. await waitForElementToBeRemoved(() => screen.getByTestId('loading-indicator'));
  75. expect(eventsMock).toHaveBeenCalledTimes(1);
  76. expect(eventsMock).toHaveBeenCalledWith(
  77. '/organizations/org-slug/eventsv2/',
  78. expect.objectContaining({
  79. query: expect.objectContaining({
  80. query: 'transaction:*proje* event.type:transaction',
  81. }),
  82. })
  83. );
  84. expect(screen.getByText(textWithMarkupMatcher('clients.call'))).toBeInTheDocument();
  85. expect(screen.getByText(textWithMarkupMatcher('clients.fetch'))).toBeInTheDocument();
  86. });
  87. it('Responds to keyboard navigation', async () => {
  88. const onSearch = jest.fn();
  89. eventsMock = MockApiClient.addMockResponse({
  90. url: `/organizations/${organization.slug}/eventsv2/`,
  91. body: {
  92. data: [
  93. {project_id: 1, transaction: 'clients.call'},
  94. {project_id: 1, transaction: 'clients.fetch'},
  95. ],
  96. },
  97. });
  98. render(<SearchBar {...testProps} onSearch={onSearch} />);
  99. userEvent.type(screen.getByRole('textbox'), 'proje');
  100. expect(screen.getByTestId('smart-search-dropdown')).toBeInTheDocument();
  101. act(() => {
  102. jest.runAllTimers();
  103. });
  104. await waitForElementToBeRemoved(() => screen.getByTestId('loading-indicator'));
  105. userEvent.keyboard('{Escape}');
  106. expect(screen.queryByTestId('smart-search-dropdown')).not.toBeInTheDocument();
  107. userEvent.type(screen.getByRole('textbox'), 'client');
  108. expect(screen.getByTestId('smart-search-dropdown')).toBeInTheDocument();
  109. act(() => {
  110. jest.runAllTimers();
  111. });
  112. await waitForElementToBeRemoved(() => screen.getByTestId('loading-indicator'));
  113. userEvent.keyboard('{ArrowDown}');
  114. userEvent.keyboard('{ArrowDown}');
  115. userEvent.keyboard('{Enter}');
  116. expect(screen.queryByTestId('smart-search-dropdown')).not.toBeInTheDocument();
  117. expect(onSearch).toHaveBeenCalledTimes(1);
  118. expect(onSearch).toHaveBeenCalledWith('transaction:clients.fetch');
  119. });
  120. });