releaseIssues.spec.tsx 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. import {initializeOrg} from 'sentry-test/initializeOrg';
  2. import {render, screen, userEvent} from 'sentry-test/reactTestingLibrary';
  3. import {textWithMarkupMatcher} from 'sentry-test/utils';
  4. import ReleaseIssues from 'sentry/views/releases/detail/overview/releaseIssues';
  5. import {getReleaseBounds} from 'sentry/views/releases/utils';
  6. describe('ReleaseIssues', function () {
  7. let newIssuesEndpoint,
  8. resolvedIssuesEndpoint,
  9. unhandledIssuesEndpoint,
  10. allIssuesEndpoint;
  11. const props = {
  12. orgId: 'org',
  13. organization: TestStubs.Organization(),
  14. version: '1.0.0',
  15. location: TestStubs.location({query: {}}),
  16. releaseBounds: getReleaseBounds(TestStubs.Release({version: '1.0.0'})),
  17. };
  18. beforeEach(function () {
  19. MockApiClient.clearMockResponses();
  20. MockApiClient.addMockResponse({
  21. url: `/organizations/${props.organization.slug}/users/`,
  22. body: [],
  23. });
  24. MockApiClient.addMockResponse({
  25. url: `/organizations/${props.organization.slug}/issues-count/?end=2020-03-24T02%3A04%3A59Z&query=first-release%3A%221.0.0%22%20is%3Aunresolved&query=release%3A%221.0.0%22%20is%3Aunresolved&query=error.handled%3A0%20release%3A%221.0.0%22%20is%3Aunresolved&query=regressed_in_release%3A%221.0.0%22&start=2020-03-23T01%3A02%3A00Z`,
  26. });
  27. MockApiClient.addMockResponse({
  28. url: `/organizations/${props.organization.slug}/issues-count/?query=first-release%3A%221.0.0%22%20is%3Aunresolved&query=release%3A%221.0.0%22%20is%3Aunresolved&query=error.handled%3A0%20release%3A%221.0.0%22%20is%3Aunresolved&query=regressed_in_release%3A%221.0.0%22&statsPeriod=24h`,
  29. });
  30. MockApiClient.addMockResponse({
  31. url: `/organizations/${props.organization.slug}/releases/1.0.0/resolved/`,
  32. });
  33. newIssuesEndpoint = MockApiClient.addMockResponse({
  34. url: `/organizations/${props.organization.slug}/issues/?end=2020-03-24T02%3A04%3A59Z&groupStatsPeriod=auto&limit=10&query=first-release%3A1.0.0%20is%3Aunresolved&sort=freq&start=2020-03-23T01%3A02%3A00Z`,
  35. body: [],
  36. });
  37. MockApiClient.addMockResponse({
  38. url: `/organizations/${props.organization.slug}/issues/?groupStatsPeriod=auto&limit=10&query=first-release%3A1.0.0%20is%3Aunresolved&sort=freq&statsPeriod=24h`,
  39. body: [],
  40. });
  41. resolvedIssuesEndpoint = MockApiClient.addMockResponse({
  42. url: `/organizations/${props.organization.slug}/releases/1.0.0/resolved/?end=2020-03-24T02%3A04%3A59Z&groupStatsPeriod=auto&limit=10&query=&sort=freq&start=2020-03-23T01%3A02%3A00Z`,
  43. body: [],
  44. });
  45. unhandledIssuesEndpoint = MockApiClient.addMockResponse({
  46. url: `/organizations/${props.organization.slug}/issues/?end=2020-03-24T02%3A04%3A59Z&groupStatsPeriod=auto&limit=10&query=release%3A1.0.0%20error.handled%3A0%20is%3Aunresolved&sort=freq&start=2020-03-23T01%3A02%3A00Z`,
  47. body: [],
  48. });
  49. MockApiClient.addMockResponse({
  50. url: `/organizations/${props.organization.slug}/issues/?groupStatsPeriod=auto&limit=10&query=release%3A1.0.0%20error.handled%3A0%20is%3Aunresolved&sort=freq&statsPeriod=24h`,
  51. body: [],
  52. });
  53. allIssuesEndpoint = MockApiClient.addMockResponse({
  54. url: `/organizations/${props.organization.slug}/issues/?end=2020-03-24T02%3A04%3A59Z&groupStatsPeriod=auto&limit=10&query=release%3A1.0.0%20is%3Aunresolved&sort=freq&start=2020-03-23T01%3A02%3A00Z`,
  55. body: [],
  56. });
  57. });
  58. it('shows an empty state', async function () {
  59. const {rerender} = render(<ReleaseIssues {...props} />);
  60. expect(await screen.findByText('No new issues in this release.')).toBeInTheDocument();
  61. await userEvent.click(screen.getByRole('radio', {name: 'Resolved 0'}));
  62. // Simulate query change
  63. rerender(
  64. <ReleaseIssues
  65. {...props}
  66. location={TestStubs.location({query: {issuesType: 'resolved'}})}
  67. />
  68. );
  69. expect(
  70. await screen.findByText('No resolved issues in this release.')
  71. ).toBeInTheDocument();
  72. });
  73. it('shows an empty sttate with stats period', async function () {
  74. const query = {pageStatsPeriod: '24h'};
  75. const {rerender} = render(
  76. <ReleaseIssues {...props} location={TestStubs.location({query})} />
  77. );
  78. expect(
  79. await screen.findByText(
  80. textWithMarkupMatcher('No new issues for the last 24 hours.')
  81. )
  82. ).toBeInTheDocument();
  83. await userEvent.click(screen.getByRole('radio', {name: 'Unhandled 0'}));
  84. // Simulate query change
  85. rerender(
  86. <ReleaseIssues
  87. {...props}
  88. location={TestStubs.location({query: {...query, issuesType: 'unhandled'}})}
  89. />
  90. );
  91. expect(
  92. await screen.findByText(
  93. textWithMarkupMatcher('No unhandled issues for the last 24 hours.')
  94. )
  95. ).toBeInTheDocument();
  96. });
  97. it('can switch issue filters', async function () {
  98. const {routerContext} = initializeOrg();
  99. const {rerender} = render(<ReleaseIssues {...props} />, {context: routerContext});
  100. // New
  101. expect(await screen.findByRole('radio', {name: 'New Issues 0'})).toBeChecked();
  102. expect(screen.getByRole('button', {name: 'Open in Issues'})).toHaveAttribute(
  103. 'href',
  104. '/organizations/org-slug/issues/?end=2020-03-24T02%3A04%3A59Z&groupStatsPeriod=auto&query=firstRelease%3A1.0.0&sort=freq&start=2020-03-23T01%3A02%3A00Z'
  105. );
  106. expect(newIssuesEndpoint).toHaveBeenCalledTimes(1);
  107. // Resolved
  108. await userEvent.click(screen.getByRole('radio', {name: 'Resolved 0'}));
  109. // Simulate query change
  110. rerender(
  111. <ReleaseIssues
  112. {...props}
  113. location={TestStubs.location({query: {issuesType: 'resolved'}})}
  114. />
  115. );
  116. expect(screen.getByRole('button', {name: 'Open in Issues'})).toHaveAttribute(
  117. 'href',
  118. '/organizations/org-slug/issues/?end=2020-03-24T02%3A04%3A59Z&groupStatsPeriod=auto&query=release%3A1.0.0&sort=freq&start=2020-03-23T01%3A02%3A00Z'
  119. );
  120. expect(resolvedIssuesEndpoint).toHaveBeenCalledTimes(1);
  121. // Unhandled
  122. await userEvent.click(screen.getByRole('radio', {name: 'Unhandled 0'}));
  123. rerender(
  124. <ReleaseIssues
  125. {...props}
  126. location={TestStubs.location({query: {issuesType: 'unhandled'}})}
  127. />
  128. );
  129. expect(screen.getByRole('button', {name: 'Open in Issues'})).toHaveAttribute(
  130. 'href',
  131. '/organizations/org-slug/issues/?end=2020-03-24T02%3A04%3A59Z&groupStatsPeriod=auto&query=release%3A1.0.0%20error.handled%3A0&sort=freq&start=2020-03-23T01%3A02%3A00Z'
  132. );
  133. expect(unhandledIssuesEndpoint).toHaveBeenCalledTimes(1);
  134. // All
  135. await userEvent.click(screen.getByRole('radio', {name: 'All Issues 0'}));
  136. rerender(
  137. <ReleaseIssues
  138. {...props}
  139. location={TestStubs.location({query: {issuesType: 'all'}})}
  140. />
  141. );
  142. expect(screen.getByRole('button', {name: 'Open in Issues'})).toHaveAttribute(
  143. 'href',
  144. '/organizations/org-slug/issues/?end=2020-03-24T02%3A04%3A59Z&groupStatsPeriod=auto&query=release%3A1.0.0&sort=freq&start=2020-03-23T01%3A02%3A00Z'
  145. );
  146. expect(allIssuesEndpoint).toHaveBeenCalledTimes(1);
  147. });
  148. it('includes release context when linking to issue', async function () {
  149. newIssuesEndpoint = MockApiClient.addMockResponse({
  150. url: `/organizations/${props.organization.slug}/issues/?end=2020-03-24T02%3A04%3A59Z&groupStatsPeriod=auto&limit=10&query=first-release%3A1.0.0%20is%3Aunresolved&sort=freq&start=2020-03-23T01%3A02%3A00Z`,
  151. body: [TestStubs.Group({id: '123'})],
  152. });
  153. const {routerContext} = initializeOrg();
  154. render(<ReleaseIssues {...props} />, {context: routerContext});
  155. await userEvent.click(screen.getByRole('radio', {name: /New Issues/}));
  156. const link = await screen.findByRole('link', {name: /RequestError/});
  157. // Should pass the query param `query` with value `release:1.0.0`
  158. expect(link).toHaveAttribute(
  159. 'href',
  160. '/organizations/org-slug/issues/123/?_allp=1&query=release%3A1.0.0&referrer=release-issue-stream'
  161. );
  162. });
  163. });