edit.spec.jsx 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. import {Fragment} from 'react';
  2. import {initializeOrg} from 'sentry-test/initializeOrg';
  3. import {render, screen, userEvent, waitFor} from 'sentry-test/reactTestingLibrary';
  4. import GlobalModal from 'sentry/components/globalModal';
  5. import {metric} from 'sentry/utils/analytics';
  6. import MetricRulesEdit from 'sentry/views/alerts/rules/metric/edit';
  7. import {AlertRuleTriggerType} from 'sentry/views/alerts/rules/metric/types';
  8. jest.mock('sentry/utils/analytics', () => ({
  9. metric: {
  10. startTransaction: jest.fn(() => ({
  11. setTag: jest.fn(),
  12. setData: jest.fn(),
  13. })),
  14. endTransaction: jest.fn(),
  15. },
  16. }));
  17. describe('MetricRulesEdit', function () {
  18. beforeAll(function () {
  19. MockApiClient.addMockResponse({
  20. url: '/organizations/org-slug/users/',
  21. body: [],
  22. });
  23. MockApiClient.addMockResponse({
  24. url: '/organizations/org-slug/tags/',
  25. body: [],
  26. });
  27. MockApiClient.addMockResponse({
  28. url: '/projects/org-slug/project-slug/environments/',
  29. body: [],
  30. });
  31. MockApiClient.addMockResponse({
  32. url: '/organizations/org-slug/events-stats/',
  33. body: null,
  34. });
  35. MockApiClient.addMockResponse({
  36. url: '/organizations/org-slug/events-meta/',
  37. body: {count: 5},
  38. });
  39. MockApiClient.addMockResponse({
  40. url: '/organizations/org-slug/alert-rules/available-actions/',
  41. body: [
  42. {
  43. allowedTargetTypes: ['user', 'team'],
  44. integrationName: null,
  45. type: 'email',
  46. integrationId: null,
  47. },
  48. ],
  49. });
  50. });
  51. it('renders and edits trigger', async function () {
  52. const {organization, project} = initializeOrg();
  53. const rule = TestStubs.MetricRule();
  54. const onChangeTitleMock = jest.fn();
  55. const req = MockApiClient.addMockResponse({
  56. url: `/organizations/${organization.slug}/alert-rules/${rule.id}/`,
  57. body: rule,
  58. });
  59. MockApiClient.addMockResponse({
  60. url: '/organizations/org-slug/members/',
  61. body: [TestStubs.Member()],
  62. });
  63. const editRule = MockApiClient.addMockResponse({
  64. url: `/projects/${organization.slug}/project-slug/alert-rules/${rule.id}/`,
  65. method: 'PUT',
  66. body: rule,
  67. });
  68. render(
  69. <Fragment>
  70. <GlobalModal />
  71. <MetricRulesEdit
  72. params={{
  73. projectId: project.slug,
  74. ruleId: rule.id,
  75. }}
  76. organization={organization}
  77. onChangeTitle={onChangeTitleMock}
  78. project={project}
  79. />
  80. </Fragment>
  81. );
  82. // has existing trigger
  83. expect(screen.getByTestId('critical-threshold')).toHaveValue('70');
  84. expect(screen.getByTestId('resolve-threshold')).toHaveValue('36');
  85. expect(req).toHaveBeenCalled();
  86. // Check correct rule name is called
  87. expect(onChangeTitleMock).toHaveBeenCalledWith(rule.name);
  88. await userEvent.clear(screen.getByTestId('warning-threshold'));
  89. await userEvent.type(screen.getByTestId('warning-threshold'), '13');
  90. await userEvent.clear(screen.getByTestId('resolve-threshold'));
  91. await userEvent.type(screen.getByTestId('resolve-threshold'), '12');
  92. // Create a new action
  93. await userEvent.click(screen.getByLabelText('Add Action'));
  94. // Save Trigger
  95. await userEvent.click(screen.getByLabelText('Save Rule'));
  96. expect(metric.startTransaction).toHaveBeenCalledWith({name: 'saveAlertRule'});
  97. expect(editRule).toHaveBeenCalledWith(
  98. expect.anything(),
  99. expect.objectContaining({
  100. data: expect.objectContaining({
  101. aggregation: 0,
  102. dataset: 'events',
  103. id: '4',
  104. name: 'My Incident Rule',
  105. projects: ['project-slug'],
  106. query: '',
  107. status: 0,
  108. timeWindow: 60,
  109. thresholdType: 0,
  110. resolveThreshold: 12,
  111. triggers: [
  112. expect.objectContaining({
  113. actions: [
  114. expect.objectContaining({
  115. integrationId: null,
  116. targetIdentifier: '',
  117. targetType: 'user',
  118. type: 'email',
  119. options: null,
  120. }),
  121. ],
  122. alertRuleId: '4',
  123. alertThreshold: 70,
  124. id: '1',
  125. }),
  126. expect.objectContaining({
  127. actions: [],
  128. alertThreshold: 13,
  129. }),
  130. ],
  131. }),
  132. method: 'PUT',
  133. })
  134. );
  135. // New Trigger should be in list
  136. // Has correct values
  137. expect(screen.getByTestId('critical-threshold')).toHaveValue('70');
  138. expect(screen.getByTestId('warning-threshold')).toHaveValue('13');
  139. expect(screen.getByTestId('resolve-threshold')).toHaveValue('12');
  140. });
  141. it('clears trigger', async function () {
  142. const {organization, project} = initializeOrg();
  143. const rule = TestStubs.MetricRule();
  144. rule.triggers.push({
  145. label: AlertRuleTriggerType.WARNING,
  146. alertThreshold: 13,
  147. actions: [],
  148. });
  149. rule.resolveThreshold = 12;
  150. const onChangeTitleMock = jest.fn();
  151. const req = MockApiClient.addMockResponse({
  152. url: `/organizations/${organization.slug}/alert-rules/${rule.id}/`,
  153. body: rule,
  154. });
  155. MockApiClient.addMockResponse({
  156. url: '/organizations/org-slug/members/',
  157. body: [TestStubs.Member()],
  158. });
  159. const editRule = MockApiClient.addMockResponse({
  160. url: `/projects/${organization.slug}/project-slug/alert-rules/${rule.id}/`,
  161. method: 'PUT',
  162. body: rule,
  163. });
  164. render(
  165. <Fragment>
  166. <GlobalModal />
  167. <MetricRulesEdit
  168. params={{
  169. orgId: organization.slug,
  170. projectId: project.slug,
  171. ruleId: rule.id,
  172. }}
  173. organization={organization}
  174. onChangeTitle={onChangeTitleMock}
  175. project={project}
  176. />
  177. </Fragment>
  178. );
  179. // has existing trigger
  180. expect(screen.getByTestId('critical-threshold')).toHaveValue('70');
  181. expect(screen.getByTestId('warning-threshold')).toHaveValue('13');
  182. expect(screen.getByTestId('resolve-threshold')).toHaveValue('12');
  183. expect(req).toHaveBeenCalled();
  184. // Check correct rule name is called
  185. expect(onChangeTitleMock).toHaveBeenCalledWith(rule.name);
  186. await userEvent.click(screen.getByLabelText('Add Action'));
  187. // Clear warning Trigger
  188. await userEvent.clear(screen.getByTestId('warning-threshold'));
  189. await waitFor(() => expect(screen.getByLabelText('Save Rule')).toBeEnabled());
  190. // Save Trigger
  191. await userEvent.click(screen.getByLabelText('Save Rule'));
  192. expect(editRule).toHaveBeenCalledWith(
  193. expect.anything(),
  194. expect.objectContaining({
  195. data: expect.objectContaining({
  196. aggregation: 0,
  197. dataset: 'events',
  198. id: '4',
  199. name: 'My Incident Rule',
  200. projects: ['project-slug'],
  201. query: '',
  202. status: 0,
  203. timeWindow: 60,
  204. resolveThreshold: 12,
  205. thresholdType: 0,
  206. triggers: [
  207. expect.objectContaining({
  208. actions: [
  209. expect.objectContaining({
  210. integrationId: null,
  211. targetIdentifier: '',
  212. targetType: 'user',
  213. type: 'email',
  214. options: null,
  215. }),
  216. ],
  217. alertRuleId: '4',
  218. alertThreshold: 70,
  219. id: '1',
  220. }),
  221. ],
  222. }),
  223. method: 'PUT',
  224. })
  225. );
  226. });
  227. it('renders 404', function () {
  228. const {organization, project} = initializeOrg();
  229. MockApiClient.addMockResponse({
  230. url: `/organizations/${organization.slug}/alert-rules/1234/`,
  231. statusCode: 404,
  232. body: {},
  233. });
  234. render(
  235. <MetricRulesEdit
  236. params={{
  237. orgId: organization.slug,
  238. projectId: project.slug,
  239. ruleId: '1234',
  240. }}
  241. organization={organization}
  242. project={project}
  243. />
  244. );
  245. expect(screen.getByText('This alert rule could not be found.')).toBeInTheDocument();
  246. });
  247. });