eventList.spec.tsx 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. import {EventFixture} from 'sentry-fixture/event';
  2. import {EventsStatsFixture} from 'sentry-fixture/events';
  3. import {GroupFixture} from 'sentry-fixture/group';
  4. import {LocationFixture} from 'sentry-fixture/locationFixture';
  5. import {OrganizationFixture} from 'sentry-fixture/organization';
  6. import {ProjectFixture} from 'sentry-fixture/project';
  7. import {RouterFixture} from 'sentry-fixture/routerFixture';
  8. import {TagsFixture} from 'sentry-fixture/tags';
  9. import {
  10. render,
  11. renderHook,
  12. screen,
  13. userEvent,
  14. waitFor,
  15. } from 'sentry-test/reactTestingLibrary';
  16. import PageFiltersStore from 'sentry/stores/pageFiltersStore';
  17. import ProjectsStore from 'sentry/stores/projectsStore';
  18. import {useEventColumns} from 'sentry/views/issueDetails/allEventsTable';
  19. import {EventDetails} from 'sentry/views/issueDetails/streamline/eventDetails';
  20. import {MOCK_EVENTS_TABLE_DATA} from 'sentry/views/performance/transactionSummary/transactionEvents/testUtils';
  21. jest.mock('sentry/components/events/suspectCommits');
  22. jest.mock('sentry/views/issueDetails/groupEventDetails/groupEventDetailsContent');
  23. jest.mock('sentry/views/issueDetails/streamline/issueContent');
  24. jest.mock('screenfull', () => ({
  25. enabled: true,
  26. isFullscreen: false,
  27. request: jest.fn(),
  28. exit: jest.fn(),
  29. on: jest.fn(),
  30. off: jest.fn(),
  31. }));
  32. describe('EventList', () => {
  33. const organization = OrganizationFixture();
  34. const project = ProjectFixture({
  35. environments: ['production', 'staging', 'development'],
  36. });
  37. const group = GroupFixture();
  38. const event = EventFixture({id: 'event-id'});
  39. const persistantQuery = `issue:${group.shortId}`;
  40. const totalCount = 100;
  41. let mockEventList: jest.Mock;
  42. let mockEventListMeta: jest.Mock;
  43. beforeEach(() => {
  44. PageFiltersStore.init();
  45. PageFiltersStore.onInitializeUrlState(
  46. {
  47. projects: [],
  48. environments: [],
  49. datetime: {start: null, end: null, period: '14d', utc: null},
  50. },
  51. new Set(['environments'])
  52. );
  53. ProjectsStore.loadInitialData([project]);
  54. MockApiClient.clearMockResponses();
  55. MockApiClient.addMockResponse({
  56. url: `/projects/${organization.slug}/${project.slug}/events/${event.id}/actionable-items/`,
  57. body: {errors: []},
  58. method: 'GET',
  59. });
  60. MockApiClient.addMockResponse({
  61. url: `/organizations/${organization.slug}/issues/${group.id}/tags/`,
  62. body: TagsFixture(),
  63. method: 'GET',
  64. });
  65. MockApiClient.addMockResponse({
  66. url: `/organizations/${organization.slug}/events-stats/`,
  67. body: {'count()': EventsStatsFixture(), 'count_unique(user)': EventsStatsFixture()},
  68. method: 'GET',
  69. });
  70. mockEventList = MockApiClient.addMockResponse({
  71. url: '/organizations/org-slug/events/',
  72. headers: {
  73. Link:
  74. `<http://localhost/api/0/organizations/${organization.slug}/events/?cursor=2:0:0>; rel="next"; results="true"; cursor="2:0:0",` +
  75. `<http://localhost/api/0/organizations/${organization.slug}/events/?cursor=1:0:0>; rel="previous"; results="false"; cursor="1:0:0"`,
  76. },
  77. body: {
  78. data: MOCK_EVENTS_TABLE_DATA,
  79. },
  80. match: [
  81. (_url, options) => {
  82. return options.query?.field?.includes('user.display');
  83. },
  84. ],
  85. });
  86. mockEventListMeta = MockApiClient.addMockResponse({
  87. url: '/organizations/org-slug/events/',
  88. headers: {
  89. Link:
  90. `<http://localhost/api/0/organizations/${organization.slug}/events/?cursor=2:0:0>; rel="next"; results="true"; cursor="2:0:0",` +
  91. `<http://localhost/api/0/organizations/${organization.slug}/events/?cursor=1:0:0>; rel="previous"; results="false"; cursor="1:0:0"`,
  92. },
  93. body: {
  94. data: [{'count()': totalCount}],
  95. },
  96. match: [
  97. (_url, options) => {
  98. return options.query?.field?.includes('count()');
  99. },
  100. ],
  101. });
  102. });
  103. function renderAllEvents() {
  104. render(<EventDetails event={event} group={group} project={project} />, {
  105. organization,
  106. router: RouterFixture({
  107. location: LocationFixture({
  108. pathname: `/organizations/${organization.slug}/issues/${group.id}/events/`,
  109. }),
  110. routes: [{name: '', path: 'events/'}],
  111. }),
  112. });
  113. }
  114. it('renders the list using a discover event query', async function () {
  115. renderAllEvents();
  116. const {result} = renderHook(() => useEventColumns(group, organization));
  117. expect(await screen.findByText('All Events')).toBeInTheDocument();
  118. expect(mockEventList).toHaveBeenCalledWith(
  119. '/organizations/org-slug/events/',
  120. expect.objectContaining({
  121. query: {
  122. dataset: 'errors',
  123. environment: [],
  124. field: result.current.fields,
  125. per_page: 50,
  126. project: [project.id],
  127. query: persistantQuery,
  128. referrer: 'issue_details.streamline_list',
  129. statsPeriod: '14d',
  130. },
  131. })
  132. );
  133. expect(mockEventListMeta).toHaveBeenCalled();
  134. expect(screen.getByRole('button', {name: 'Previous Page'})).toBeInTheDocument();
  135. expect(screen.getByRole('button', {name: 'Next Page'})).toBeInTheDocument();
  136. expect(screen.getByRole('button', {name: 'Close'})).toBeInTheDocument();
  137. expect(
  138. screen.getByText(`Showing 0-${MOCK_EVENTS_TABLE_DATA.length} of ${totalCount}`)
  139. ).toBeInTheDocument();
  140. // Returns minidump column, but we omit it as a custom column
  141. const columns = result.current.columnTitles.filter(t => t !== 'Minidump');
  142. for (const title of columns) {
  143. expect(screen.getByRole('columnheader', {name: title})).toBeInTheDocument();
  144. }
  145. });
  146. it('allows filtering by environment', async function () {
  147. renderAllEvents();
  148. await userEvent.click(screen.getByRole('button', {name: 'All Envs'}));
  149. await userEvent.click(screen.getByRole('row', {name: 'production'}));
  150. const expectedArgs = [
  151. '/organizations/org-slug/events/',
  152. expect.objectContaining({
  153. query: expect.objectContaining({
  154. environment: ['production'],
  155. }),
  156. }),
  157. ];
  158. expect(mockEventList).toHaveBeenCalledWith(...expectedArgs);
  159. expect(mockEventListMeta).toHaveBeenCalledWith(...expectedArgs);
  160. });
  161. it('updates query from location param change', async function () {
  162. const [tagKey, tagValue] = ['user.email', 'leander.rodrigues@sentry.io'];
  163. const locationQuery = {
  164. query: {
  165. query: `${tagKey}:${tagValue}`,
  166. },
  167. };
  168. render(<EventDetails event={event} group={group} project={project} />, {
  169. organization,
  170. router: RouterFixture({
  171. location: LocationFixture({
  172. pathname: `/organizations/${organization.slug}/issues/${group.id}/events/`,
  173. query: locationQuery.query,
  174. }),
  175. routes: [{name: '', path: 'events/'}],
  176. }),
  177. });
  178. const expectedArgs = [
  179. '/organizations/org-slug/events/',
  180. expect.objectContaining({
  181. query: expect.objectContaining({
  182. query: [persistantQuery, locationQuery.query.query].join(' '),
  183. }),
  184. }),
  185. ];
  186. await waitFor(() => {
  187. expect(mockEventList).toHaveBeenCalledWith(...expectedArgs);
  188. });
  189. expect(mockEventListMeta).toHaveBeenCalledWith(...expectedArgs);
  190. });
  191. it('allows filtering by date', async function () {
  192. renderAllEvents();
  193. await userEvent.click(screen.getByRole('button', {name: '14D'}));
  194. await userEvent.click(screen.getByRole('option', {name: 'Last 7 days'}));
  195. const expectedArgs = [
  196. '/organizations/org-slug/events/',
  197. expect.objectContaining({
  198. query: expect.objectContaining({
  199. statsPeriod: '7d',
  200. }),
  201. }),
  202. ];
  203. expect(mockEventList).toHaveBeenCalledWith(...expectedArgs);
  204. expect(mockEventListMeta).toHaveBeenCalledWith(...expectedArgs);
  205. });
  206. });