eventNavigation.spec.tsx 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. import {EventFixture} from 'sentry-fixture/event';
  2. import {EventAttachmentFixture} from 'sentry-fixture/eventAttachment';
  3. import {GroupFixture} from 'sentry-fixture/group';
  4. import {LocationFixture} from 'sentry-fixture/locationFixture';
  5. import {RouterFixture} from 'sentry-fixture/routerFixture';
  6. import {initializeOrg} from 'sentry-test/initializeOrg';
  7. import {render, screen, userEvent, waitFor} from 'sentry-test/reactTestingLibrary';
  8. import * as useMedia from 'sentry/utils/useMedia';
  9. import {SectionKey, useEventDetails} from 'sentry/views/issueDetails/streamline/context';
  10. import {Tab, TabPaths} from 'sentry/views/issueDetails/types';
  11. import {IssueEventNavigation} from './eventNavigation';
  12. jest.mock('sentry/views/issueDetails/streamline/context');
  13. describe('EventNavigation', () => {
  14. const {organization, router} = initializeOrg();
  15. const group = GroupFixture({id: 'group-id'});
  16. const testEvent = EventFixture({
  17. id: 'event-id',
  18. size: 7,
  19. dateCreated: '2019-03-20T00:00:00.000Z',
  20. errors: [],
  21. entries: [],
  22. tags: [
  23. {key: 'environment', value: 'dev'},
  24. {key: 'replayId', value: 'replay-id'},
  25. ],
  26. previousEventID: 'prev-event-id',
  27. nextEventID: 'next-event-id',
  28. });
  29. const defaultProps: React.ComponentProps<typeof IssueEventNavigation> = {
  30. event: testEvent,
  31. group,
  32. query: undefined,
  33. };
  34. beforeEach(() => {
  35. jest.resetAllMocks();
  36. jest.mocked(useEventDetails).mockReturnValue({
  37. sectionData: {
  38. highlights: {key: SectionKey.HIGHLIGHTS},
  39. tags: {key: SectionKey.TAGS},
  40. replay: {key: SectionKey.REPLAY},
  41. },
  42. dispatch: jest.fn(),
  43. });
  44. MockApiClient.addMockResponse({
  45. url: `/organizations/${organization.slug}/issues/${group.id}/tags/`,
  46. body: [],
  47. });
  48. MockApiClient.addMockResponse({
  49. url: `/organizations/${organization.slug}/issues/${group.id}/attachments/`,
  50. body: [],
  51. });
  52. MockApiClient.addMockResponse({
  53. url: '/organizations/org-slug/replay-count/',
  54. body: {},
  55. });
  56. });
  57. describe('all events buttons', () => {
  58. it('renders the all events controls', () => {
  59. const allEventsRouter = RouterFixture({
  60. params: {groupId: group.id},
  61. routes: [{path: TabPaths[Tab.EVENTS]}],
  62. location: LocationFixture({
  63. pathname: `/organizations/${organization.slug}/issues/${group.id}/events/`,
  64. }),
  65. });
  66. render(<IssueEventNavigation {...defaultProps} />, {router: allEventsRouter});
  67. const discoverButton = screen.getByLabelText('Open in Discover');
  68. expect(discoverButton).toBeInTheDocument();
  69. expect(discoverButton).toHaveAttribute(
  70. 'href',
  71. expect.stringContaining(`/organizations/${organization.slug}/discover/results/`)
  72. );
  73. const closeButton = screen.getByRole('button', {name: 'Return to event details'});
  74. expect(closeButton).toBeInTheDocument();
  75. expect(closeButton).toHaveAttribute(
  76. 'href',
  77. expect.stringContaining(`/organizations/${organization.slug}/issues/${group.id}/`)
  78. );
  79. });
  80. });
  81. describe('recommended event tabs', () => {
  82. it('can navigate to the oldest event', async () => {
  83. jest.spyOn(useMedia, 'default').mockReturnValue(true);
  84. render(<IssueEventNavigation {...defaultProps} />, {router});
  85. await userEvent.click(await screen.findByRole('tab', {name: 'First'}));
  86. expect(router.push).toHaveBeenCalledWith({
  87. pathname: '/organizations/org-slug/issues/group-id/events/oldest/',
  88. query: {referrer: 'oldest-event'},
  89. });
  90. });
  91. it('can navigate to the latest event', async () => {
  92. jest.spyOn(useMedia, 'default').mockReturnValue(true);
  93. render(<IssueEventNavigation {...defaultProps} />, {router});
  94. await userEvent.click(await screen.findByRole('tab', {name: 'Last'}));
  95. expect(router.push).toHaveBeenCalledWith({
  96. pathname: '/organizations/org-slug/issues/group-id/events/latest/',
  97. query: {referrer: 'latest-event'},
  98. });
  99. });
  100. it('can navigate to the recommended event', async () => {
  101. jest.spyOn(useMedia, 'default').mockReturnValue(true);
  102. const recommendedEventRouter = RouterFixture({
  103. params: {eventId: 'latest'},
  104. location: LocationFixture({
  105. pathname: `/organizations/org-slug/issues/group-id/events/latest/`,
  106. }),
  107. });
  108. render(<IssueEventNavigation {...defaultProps} />, {
  109. router: recommendedEventRouter,
  110. });
  111. await userEvent.click(await screen.findByRole('tab', {name: 'Rec.'}));
  112. expect(recommendedEventRouter.push).toHaveBeenCalledWith({
  113. pathname: '/organizations/org-slug/issues/group-id/events/recommended/',
  114. query: {referrer: 'recommended-event'},
  115. });
  116. });
  117. });
  118. it('can navigate next/previous events', async () => {
  119. render(<IssueEventNavigation {...defaultProps} />);
  120. expect(await screen.findByRole('button', {name: 'Previous Event'})).toHaveAttribute(
  121. 'href',
  122. `/organizations/org-slug/issues/group-id/events/prev-event-id/?referrer=previous-event`
  123. );
  124. expect(screen.getByRole('button', {name: 'Next Event'})).toHaveAttribute(
  125. 'href',
  126. `/organizations/org-slug/issues/group-id/events/next-event-id/?referrer=next-event`
  127. );
  128. });
  129. it('can preload next/previous events', async () => {
  130. const event = EventFixture({
  131. nextEventID: 'next-event-id',
  132. previousEventID: 'prev-event-id',
  133. });
  134. const mockNextEvent = MockApiClient.addMockResponse({
  135. url: `/organizations/org-slug/issues/group-id/events/next-event-id/`,
  136. body: EventFixture(),
  137. });
  138. const mockPreviousEvent = MockApiClient.addMockResponse({
  139. url: `/organizations/org-slug/issues/group-id/events/prev-event-id/`,
  140. body: EventFixture(),
  141. });
  142. render(<IssueEventNavigation {...defaultProps} event={event} />);
  143. expect(mockNextEvent).not.toHaveBeenCalled();
  144. expect(mockPreviousEvent).not.toHaveBeenCalled();
  145. await userEvent.hover(await screen.findByRole('button', {name: 'Next Event'}));
  146. await waitFor(() => expect(mockNextEvent).toHaveBeenCalled());
  147. expect(mockPreviousEvent).not.toHaveBeenCalled();
  148. await userEvent.hover(screen.getByRole('button', {name: 'Previous Event'}));
  149. await waitFor(() => expect(mockPreviousEvent).toHaveBeenCalled());
  150. });
  151. describe('counts', () => {
  152. it('renders default counts', async () => {
  153. render(<IssueEventNavigation {...defaultProps} />);
  154. await userEvent.click(screen.getByRole('button', {name: 'Select issue content'}));
  155. expect(
  156. await screen.findByRole('menuitemradio', {name: 'Attachments 0'})
  157. ).toBeInTheDocument();
  158. expect(
  159. screen.getByRole('menuitemradio', {name: 'Events 327k'})
  160. ).toBeInTheDocument();
  161. expect(screen.getByRole('menuitemradio', {name: 'Replays 0'})).toBeInTheDocument();
  162. expect(screen.getByRole('menuitemradio', {name: 'Feedback 0'})).toBeInTheDocument();
  163. });
  164. it('renders 1 attachment', async () => {
  165. MockApiClient.addMockResponse({
  166. url: `/organizations/${organization.slug}/issues/${group.id}/attachments/`,
  167. body: [EventAttachmentFixture()],
  168. });
  169. render(<IssueEventNavigation {...defaultProps} />);
  170. await userEvent.click(screen.getByRole('button', {name: 'Select issue content'}));
  171. expect(
  172. await screen.findByRole('menuitemradio', {name: 'Attachments 1'})
  173. ).toBeInTheDocument();
  174. });
  175. it('renders 50+ attachments', async () => {
  176. MockApiClient.addMockResponse({
  177. url: `/organizations/${organization.slug}/issues/${group.id}/attachments/`,
  178. body: [EventAttachmentFixture()],
  179. headers: {
  180. // Assumes there is more than 50 attachments if there is a next page
  181. Link: '<https://sentry.io>; rel="previous"; results="false"; cursor="0:0:1", <https://sentry.io>; rel="next"; results="true"; cursor="0:20:0"',
  182. },
  183. });
  184. render(<IssueEventNavigation {...defaultProps} />);
  185. await userEvent.click(screen.getByRole('button', {name: 'Select issue content'}));
  186. expect(
  187. await screen.findByRole('menuitemradio', {name: 'Attachments 50+'})
  188. ).toBeInTheDocument();
  189. });
  190. });
  191. });