eventGraph.spec.tsx 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  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 {TagsFixture} from 'sentry-fixture/tags';
  8. import {render, screen, userEvent} from 'sentry-test/reactTestingLibrary';
  9. import PageFiltersStore from 'sentry/stores/pageFiltersStore';
  10. import ProjectsStore from 'sentry/stores/projectsStore';
  11. import {useLocation} from 'sentry/utils/useLocation';
  12. import {EventDetails} from 'sentry/views/issueDetails/streamline/eventDetails';
  13. jest.mock('sentry/utils/useLocation');
  14. jest.mock('sentry/components/events/suspectCommits');
  15. jest.mock('sentry/views/issueDetails/groupEventDetails/groupEventDetailsContent');
  16. jest.mock('sentry/views/issueDetails/streamline/issueContent');
  17. jest.mock('screenfull', () => ({
  18. enabled: true,
  19. isFullscreen: false,
  20. request: jest.fn(),
  21. exit: jest.fn(),
  22. on: jest.fn(),
  23. off: jest.fn(),
  24. }));
  25. const mockUseLocation = jest.mocked(useLocation);
  26. describe('EventGraph', () => {
  27. const organization = OrganizationFixture();
  28. const project = ProjectFixture({
  29. environments: ['production', 'staging', 'development'],
  30. });
  31. const group = GroupFixture();
  32. const event = EventFixture({id: 'event-id'});
  33. const persistantQuery = `issue:${group.shortId}`;
  34. const defaultProps = {project, group, event};
  35. let mockEventStats: jest.Mock;
  36. beforeEach(() => {
  37. mockUseLocation.mockReturnValue(LocationFixture());
  38. PageFiltersStore.init();
  39. PageFiltersStore.onInitializeUrlState(
  40. {
  41. projects: [],
  42. environments: [],
  43. datetime: {start: null, end: null, period: '14d', utc: null},
  44. },
  45. new Set(['environments'])
  46. );
  47. ProjectsStore.loadInitialData([project]);
  48. MockApiClient.clearMockResponses();
  49. MockApiClient.addMockResponse({
  50. url: `/projects/${organization.slug}/${project.slug}/events/${event.id}/actionable-items/`,
  51. body: {errors: []},
  52. method: 'GET',
  53. });
  54. MockApiClient.addMockResponse({
  55. url: `/organizations/${organization.slug}/issues/${group.id}/tags/`,
  56. body: TagsFixture(),
  57. method: 'GET',
  58. });
  59. mockEventStats = MockApiClient.addMockResponse({
  60. url: `/organizations/${organization.slug}/events-stats/`,
  61. body: {'count()': EventsStatsFixture(), 'count_unique(user)': EventsStatsFixture()},
  62. method: 'GET',
  63. });
  64. });
  65. it('displays allows toggling data sets', async function () {
  66. render(<EventDetails {...defaultProps} />, {organization});
  67. await screen.findByText(event.id);
  68. const count = EventsStatsFixture().data.reduce(
  69. (currentCount, item) => currentCount + item[1][0].count,
  70. 0
  71. );
  72. const eventsToggle = screen.getByRole('button', {name: `Events ${count}`});
  73. const usersToggle = screen.getByRole('button', {name: `Users ${count}`});
  74. // Defaults to events graph
  75. expect(eventsToggle).toBeDisabled();
  76. expect(usersToggle).toBeEnabled();
  77. // Switch to users graph
  78. await userEvent.click(usersToggle);
  79. expect(eventsToggle).toBeEnabled();
  80. expect(usersToggle).toBeDisabled();
  81. // Another click should do nothing
  82. await userEvent.click(usersToggle);
  83. expect(eventsToggle).toBeEnabled();
  84. expect(usersToggle).toBeDisabled();
  85. // Switch back to events
  86. await userEvent.click(eventsToggle);
  87. expect(eventsToggle).toBeDisabled();
  88. expect(usersToggle).toBeEnabled();
  89. });
  90. it('renders the graph using a discover event stats query', async function () {
  91. render(<EventDetails {...defaultProps} />, {organization});
  92. await screen.findByText(event.id);
  93. expect(mockEventStats).toHaveBeenCalledWith(
  94. '/organizations/org-slug/events-stats/',
  95. expect.objectContaining({
  96. query: {
  97. dataset: 'errors',
  98. environment: [],
  99. field: expect.anything(),
  100. partial: 1,
  101. interval: '12h',
  102. per_page: 50,
  103. project: [project.id],
  104. query: persistantQuery,
  105. referrer: 'issue_details.streamline_graph',
  106. statsPeriod: '14d',
  107. yAxis: ['count()', 'count_unique(user)'],
  108. },
  109. })
  110. );
  111. expect(screen.queryByLabelText('Open in Discover')).not.toBeInTheDocument();
  112. await userEvent.hover(screen.getByRole('figure'));
  113. const discoverButton = screen.getByLabelText('Open in Discover');
  114. expect(discoverButton).toBeInTheDocument();
  115. expect(discoverButton).toHaveAttribute(
  116. 'href',
  117. expect.stringContaining(`/organizations/${organization.slug}/discover/results/`)
  118. );
  119. });
  120. it('allows filtering by environment', async function () {
  121. render(<EventDetails {...defaultProps} />, {organization});
  122. await screen.findByText(event.id);
  123. await userEvent.click(screen.getByRole('button', {name: 'All Envs'}));
  124. await userEvent.click(screen.getByRole('row', {name: 'production'}));
  125. expect(mockEventStats).toHaveBeenCalledWith(
  126. '/organizations/org-slug/events-stats/',
  127. expect.objectContaining({
  128. query: expect.objectContaining({
  129. environment: ['production'],
  130. }),
  131. })
  132. );
  133. });
  134. it('updates query from location param change', async function () {
  135. const [tagKey, tagValue] = ['user.email', 'leander.rodrigues@sentry.io'];
  136. const locationQuery = {
  137. query: {
  138. query: `${tagKey}:${tagValue}`,
  139. },
  140. };
  141. mockUseLocation.mockReset();
  142. mockUseLocation.mockReturnValue(LocationFixture(locationQuery));
  143. render(<EventDetails {...defaultProps} />, {organization});
  144. await screen.findByText(event.id);
  145. expect(mockEventStats).toHaveBeenCalledWith(
  146. '/organizations/org-slug/events-stats/',
  147. expect.objectContaining({
  148. query: expect.objectContaining({
  149. query: [persistantQuery, locationQuery.query.query].join(' '),
  150. }),
  151. })
  152. );
  153. });
  154. it('allows filtering by date', async function () {
  155. render(<EventDetails {...defaultProps} />, {organization});
  156. await screen.findByText(event.id);
  157. await userEvent.click(screen.getByRole('button', {name: '14D'}));
  158. await userEvent.click(screen.getByRole('option', {name: 'Last 7 days'}));
  159. expect(mockEventStats).toHaveBeenCalledWith(
  160. '/organizations/org-slug/events-stats/',
  161. expect.objectContaining({
  162. query: expect.objectContaining({
  163. statsPeriod: '7d',
  164. }),
  165. })
  166. );
  167. });
  168. });