projectLatestAlerts.spec.tsx 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. import {initializeOrg} from 'sentry-test/initializeOrg';
  2. import {render, screen} from 'sentry-test/reactTestingLibrary';
  3. import {textWithMarkupMatcher} from 'sentry-test/utils';
  4. import ProjectLatestAlerts from 'sentry/views/projectDetail/projectLatestAlerts';
  5. describe('ProjectDetail > ProjectLatestAlerts', function () {
  6. let endpointMock: jest.Mock;
  7. let rulesEndpointMock: jest.Mock;
  8. const {organization, project, router, routerContext} = initializeOrg();
  9. beforeEach(function () {
  10. endpointMock = MockApiClient.addMockResponse({
  11. url: `/organizations/${organization.slug}/incidents/`,
  12. body: [
  13. TestStubs.Incident({id: 1, status: 20}), // critical
  14. TestStubs.Incident({id: 2, status: 10}), // warning
  15. TestStubs.Incident({id: 3, status: 2}), // closed
  16. ],
  17. });
  18. rulesEndpointMock = MockApiClient.addMockResponse({
  19. url: `/organizations/${organization.slug}/alert-rules/`,
  20. body: [TestStubs.MetricRule()],
  21. });
  22. });
  23. afterEach(function () {
  24. MockApiClient.clearMockResponses();
  25. });
  26. it('renders a list', function () {
  27. render(
  28. <ProjectLatestAlerts
  29. organization={organization}
  30. projectSlug={project.slug}
  31. location={router.location}
  32. isProjectStabilized
  33. />,
  34. {context: routerContext}
  35. );
  36. expect(endpointMock).toHaveBeenCalledTimes(2); // one for closed, one for open
  37. expect(rulesEndpointMock).toHaveBeenCalledTimes(0);
  38. expect(endpointMock).toHaveBeenCalledWith(
  39. expect.anything(),
  40. expect.objectContaining({
  41. query: {per_page: 3, status: expect.anything()},
  42. })
  43. );
  44. expect(screen.getByText('Latest Alerts')).toBeInTheDocument();
  45. expect(screen.getAllByText('Too many Chrome errors')).toHaveLength(3);
  46. expect(
  47. screen.getAllByRole('link', {name: 'Too many Chrome errors'})[0]
  48. ).toHaveAttribute(
  49. 'href',
  50. '/organizations/org-slug/alerts/123/'
  51. );
  52. expect(
  53. screen.getAllByText(textWithMarkupMatcher('Triggered 2 years ago'))
  54. ).toHaveLength(2);
  55. expect(
  56. screen.getByText(textWithMarkupMatcher('Resolved a year ago'))
  57. ).toBeInTheDocument();
  58. expect(screen.getByLabelText('Critical')).toBeInTheDocument();
  59. expect(screen.getByLabelText('Warning')).toBeInTheDocument();
  60. expect(screen.getByLabelText('Resolved')).toBeInTheDocument();
  61. });
  62. it('shows the empty state', async function () {
  63. MockApiClient.addMockResponse({
  64. url: `/organizations/${organization.slug}/incidents/`,
  65. body: [],
  66. });
  67. render(
  68. <ProjectLatestAlerts
  69. organization={organization}
  70. projectSlug={project.slug}
  71. location={router.location}
  72. isProjectStabilized
  73. />
  74. );
  75. expect(rulesEndpointMock).toHaveBeenCalledWith(
  76. expect.anything(),
  77. expect.objectContaining({
  78. query: {per_page: 1},
  79. })
  80. );
  81. expect(await screen.findByText('No alerts found')).toBeInTheDocument();
  82. });
  83. it('shows configure alerts buttons', async function () {
  84. MockApiClient.addMockResponse({
  85. url: `/organizations/${organization.slug}/incidents/`,
  86. body: [],
  87. });
  88. MockApiClient.addMockResponse({
  89. url: `/organizations/${organization.slug}/alert-rules/`,
  90. body: [],
  91. });
  92. render(
  93. <ProjectLatestAlerts
  94. organization={organization}
  95. projectSlug={project.slug}
  96. location={router.location}
  97. isProjectStabilized
  98. />,
  99. {context: routerContext}
  100. );
  101. expect(await screen.findByRole('button', {name: 'Create Alert'})).toHaveAttribute(
  102. 'href',
  103. `/organizations/${organization.slug}/alerts/wizard/?referrer=project_detail&project=project-slug`
  104. );
  105. expect(screen.getByRole('button', {name: 'Learn More'})).toHaveAttribute(
  106. 'href',
  107. 'https://docs.sentry.io/product/alerts-notifications/metric-alerts/'
  108. );
  109. });
  110. it('calls API with the right params', function () {
  111. render(
  112. <ProjectLatestAlerts
  113. organization={organization}
  114. projectSlug={project.slug}
  115. location={TestStubs.location({
  116. query: {statsPeriod: '7d', environment: 'staging', somethingBad: 'nope'},
  117. })}
  118. isProjectStabilized
  119. />
  120. );
  121. expect(endpointMock).toHaveBeenCalledWith(
  122. expect.anything(),
  123. expect.objectContaining({
  124. query: {per_page: 3, statsPeriod: '7d', environment: 'staging', status: 'open'},
  125. })
  126. );
  127. });
  128. it('handles null dateClosed with resolved alerts', function () {
  129. MockApiClient.addMockResponse({
  130. url: `/organizations/${organization.slug}/incidents/`,
  131. body: [
  132. TestStubs.Incident({id: 1, status: 20}), // critical
  133. TestStubs.Incident({id: 2, status: 10}), // warning
  134. TestStubs.Incident({id: 3, status: 2, dateClosed: null}), // closed with null dateClosed
  135. ],
  136. });
  137. render(
  138. <ProjectLatestAlerts
  139. organization={organization}
  140. projectSlug={project.slug}
  141. location={router.location}
  142. isProjectStabilized
  143. />
  144. );
  145. expect(screen.getByText('Resolved')).toBeInTheDocument();
  146. });
  147. it('does not call API if project is not stabilized yet', function () {
  148. render(
  149. <ProjectLatestAlerts
  150. organization={organization}
  151. projectSlug={project.slug}
  152. location={router.location}
  153. isProjectStabilized={false}
  154. />
  155. );
  156. expect(endpointMock).toHaveBeenCalledTimes(0);
  157. });
  158. });