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, useIssueDetails} 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. };
  33. beforeEach(() => {
  34. jest.resetAllMocks();
  35. jest.mocked(useIssueDetails).mockReturnValue({
  36. sectionData: {
  37. highlights: {key: SectionKey.HIGHLIGHTS},
  38. tags: {key: SectionKey.TAGS},
  39. replay: {key: SectionKey.REPLAY},
  40. },
  41. eventCount: 0,
  42. isSidebarOpen: true,
  43. navScrollMargin: 0,
  44. dispatch: jest.fn(),
  45. });
  46. MockApiClient.addMockResponse({
  47. url: `/organizations/${organization.slug}/issues/${group.id}/tags/`,
  48. body: [],
  49. });
  50. MockApiClient.addMockResponse({
  51. url: `/organizations/${organization.slug}/issues/${group.id}/attachments/`,
  52. body: [],
  53. });
  54. MockApiClient.addMockResponse({
  55. url: '/organizations/org-slug/replay-count/',
  56. body: {},
  57. });
  58. });
  59. describe('all events buttons', () => {
  60. it('renders the all events controls', () => {
  61. const allEventsRouter = RouterFixture({
  62. params: {groupId: group.id},
  63. routes: [{path: TabPaths[Tab.EVENTS]}],
  64. location: LocationFixture({
  65. pathname: `/organizations/${organization.slug}/issues/${group.id}/events/`,
  66. }),
  67. });
  68. render(<IssueEventNavigation {...defaultProps} />, {router: allEventsRouter});
  69. const discoverButton = screen.getByLabelText('Open in Discover');
  70. expect(discoverButton).toBeInTheDocument();
  71. expect(discoverButton).toHaveAttribute(
  72. 'href',
  73. expect.stringContaining(`/organizations/${organization.slug}/discover/results/`)
  74. );
  75. const closeButton = screen.getByRole('button', {name: 'Return to event details'});
  76. expect(closeButton).toBeInTheDocument();
  77. expect(closeButton).toHaveAttribute(
  78. 'href',
  79. expect.stringContaining(`/organizations/${organization.slug}/issues/${group.id}/`)
  80. );
  81. });
  82. });
  83. describe('recommended event tabs', () => {
  84. it('can navigate to the oldest event', async () => {
  85. jest.spyOn(useMedia, 'default').mockReturnValue(true);
  86. render(<IssueEventNavigation {...defaultProps} />, {router});
  87. await userEvent.click(await screen.findByRole('tab', {name: 'First'}));
  88. expect(router.push).toHaveBeenCalledWith({
  89. pathname: '/organizations/org-slug/issues/group-id/events/oldest/',
  90. query: {referrer: 'oldest-event'},
  91. });
  92. });
  93. it('can navigate to the latest event', async () => {
  94. jest.spyOn(useMedia, 'default').mockReturnValue(true);
  95. render(<IssueEventNavigation {...defaultProps} />, {router});
  96. await userEvent.click(await screen.findByRole('tab', {name: 'Last'}));
  97. expect(router.push).toHaveBeenCalledWith({
  98. pathname: '/organizations/org-slug/issues/group-id/events/latest/',
  99. query: {referrer: 'latest-event'},
  100. });
  101. });
  102. it('can navigate to the recommended event', async () => {
  103. jest.spyOn(useMedia, 'default').mockReturnValue(true);
  104. const recommendedEventRouter = RouterFixture({
  105. params: {eventId: 'latest'},
  106. location: LocationFixture({
  107. pathname: `/organizations/org-slug/issues/group-id/events/latest/`,
  108. }),
  109. });
  110. render(<IssueEventNavigation {...defaultProps} />, {
  111. router: recommendedEventRouter,
  112. });
  113. await userEvent.click(await screen.findByRole('tab', {name: 'Rec.'}));
  114. expect(recommendedEventRouter.push).toHaveBeenCalledWith({
  115. pathname: '/organizations/org-slug/issues/group-id/events/recommended/',
  116. query: {referrer: 'recommended-event'},
  117. });
  118. });
  119. });
  120. it('can navigate next/previous events', async () => {
  121. render(<IssueEventNavigation {...defaultProps} />);
  122. expect(await screen.findByRole('button', {name: 'Previous Event'})).toHaveAttribute(
  123. 'href',
  124. `/organizations/org-slug/issues/group-id/events/prev-event-id/?referrer=previous-event`
  125. );
  126. expect(screen.getByRole('button', {name: 'Next Event'})).toHaveAttribute(
  127. 'href',
  128. `/organizations/org-slug/issues/group-id/events/next-event-id/?referrer=next-event`
  129. );
  130. });
  131. it('can preload next/previous events', async () => {
  132. const event = EventFixture({
  133. nextEventID: 'next-event-id',
  134. previousEventID: 'prev-event-id',
  135. });
  136. const mockNextEvent = MockApiClient.addMockResponse({
  137. url: `/organizations/org-slug/issues/group-id/events/next-event-id/`,
  138. body: EventFixture(),
  139. });
  140. const mockPreviousEvent = MockApiClient.addMockResponse({
  141. url: `/organizations/org-slug/issues/group-id/events/prev-event-id/`,
  142. body: EventFixture(),
  143. });
  144. render(<IssueEventNavigation {...defaultProps} event={event} />);
  145. expect(mockNextEvent).not.toHaveBeenCalled();
  146. expect(mockPreviousEvent).not.toHaveBeenCalled();
  147. await userEvent.hover(await screen.findByRole('button', {name: 'Next Event'}));
  148. await waitFor(() => expect(mockNextEvent).toHaveBeenCalled());
  149. expect(mockPreviousEvent).not.toHaveBeenCalled();
  150. await userEvent.hover(screen.getByRole('button', {name: 'Previous Event'}));
  151. await waitFor(() => expect(mockPreviousEvent).toHaveBeenCalled());
  152. });
  153. describe('counts', () => {
  154. it('renders default counts', async () => {
  155. render(<IssueEventNavigation {...defaultProps} />);
  156. await userEvent.click(screen.getByRole('button', {name: 'Select issue content'}));
  157. expect(
  158. await screen.findByRole('menuitemradio', {name: 'Attachments 0'})
  159. ).toBeInTheDocument();
  160. expect(screen.getByRole('menuitemradio', {name: 'Events 0'})).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. });