edit.spec.tsx 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. import {initializeOrg} from 'sentry-test/initializeOrg';
  2. import {render, screen, userEvent} from 'sentry-test/reactTestingLibrary';
  3. import {metric} from 'sentry/utils/analytics';
  4. import MetricRulesEdit from 'sentry/views/alerts/rules/metric/edit';
  5. import {AlertRuleTriggerType} from 'sentry/views/alerts/rules/metric/types';
  6. jest.mock('sentry/utils/analytics', () => ({
  7. metric: {
  8. startTransaction: jest.fn(() => ({
  9. setTag: jest.fn(),
  10. setData: jest.fn(),
  11. })),
  12. mark: jest.fn(),
  13. measure: jest.fn(),
  14. endTransaction: jest.fn(),
  15. },
  16. }));
  17. describe('MetricRulesEdit', function () {
  18. beforeEach(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. MockApiClient.addMockResponse({
  51. url: '/organizations/org-slug/members/',
  52. body: [TestStubs.Member()],
  53. });
  54. });
  55. afterEach(() => {
  56. MockApiClient.clearMockResponses();
  57. jest.clearAllMocks();
  58. });
  59. it('renders and edits trigger', async function () {
  60. const {organization, project} = initializeOrg();
  61. const rule = TestStubs.MetricRule();
  62. const onChangeTitleMock = jest.fn();
  63. const req = MockApiClient.addMockResponse({
  64. url: `/organizations/${organization.slug}/alert-rules/${rule.id}/`,
  65. body: rule,
  66. });
  67. const editRule = MockApiClient.addMockResponse({
  68. url: `/projects/${organization.slug}/project-slug/alert-rules/${rule.id}/`,
  69. method: 'PUT',
  70. body: rule,
  71. });
  72. render(
  73. <MetricRulesEdit
  74. {...TestStubs.routeComponentProps()}
  75. params={{
  76. projectId: project.slug,
  77. ruleId: rule.id,
  78. }}
  79. userTeamIds={[]}
  80. organization={organization}
  81. onChangeTitle={onChangeTitleMock}
  82. project={project}
  83. />
  84. );
  85. // has existing trigger
  86. expect(screen.getByTestId('critical-threshold')).toHaveValue('70');
  87. expect(screen.getByTestId('resolve-threshold')).toHaveValue('36');
  88. expect(req).toHaveBeenCalled();
  89. // Check correct rule name is called
  90. expect(onChangeTitleMock).toHaveBeenCalledWith(rule.name);
  91. await userEvent.clear(screen.getByTestId('resolve-threshold'));
  92. await userEvent.type(screen.getByTestId('resolve-threshold'), '7');
  93. // Create a new action
  94. await userEvent.click(screen.getByLabelText('Add Action'));
  95. // Save Trigger
  96. await userEvent.click(screen.getByLabelText('Save Rule'));
  97. expect(metric.startTransaction).toHaveBeenCalledWith({name: 'saveAlertRule'});
  98. expect(editRule).toHaveBeenCalledWith(
  99. expect.anything(),
  100. expect.objectContaining({
  101. data: expect.objectContaining({
  102. aggregation: 0,
  103. dataset: 'events',
  104. id: '4',
  105. name: 'My Incident Rule',
  106. projects: ['project-slug'],
  107. query: '',
  108. status: 0,
  109. timeWindow: 60,
  110. thresholdType: 0,
  111. resolveThreshold: 7,
  112. triggers: [
  113. expect.objectContaining({
  114. actions: [
  115. expect.objectContaining({
  116. integrationId: null,
  117. targetIdentifier: '',
  118. targetType: 'user',
  119. type: 'email',
  120. options: null,
  121. }),
  122. ],
  123. alertRuleId: '4',
  124. alertThreshold: 70,
  125. id: '1',
  126. }),
  127. ],
  128. }),
  129. method: 'PUT',
  130. })
  131. );
  132. // New Trigger should be in list
  133. // Has correct values
  134. expect(screen.getByTestId('critical-threshold')).toHaveValue('70');
  135. expect(screen.getByTestId('resolve-threshold')).toHaveValue('7');
  136. });
  137. it('removes warning trigger', async function () {
  138. const {organization, project} = initializeOrg();
  139. const rule = TestStubs.MetricRule();
  140. rule.triggers.push({
  141. label: AlertRuleTriggerType.WARNING,
  142. alertThreshold: 13,
  143. actions: [],
  144. });
  145. rule.resolveThreshold = 12;
  146. MockApiClient.addMockResponse({
  147. url: `/organizations/${organization.slug}/alert-rules/${rule.id}/`,
  148. body: rule,
  149. });
  150. const editRule = MockApiClient.addMockResponse({
  151. url: `/projects/${organization.slug}/project-slug/alert-rules/${rule.id}/`,
  152. method: 'PUT',
  153. body: rule,
  154. });
  155. render(
  156. <MetricRulesEdit
  157. {...TestStubs.routeComponentProps()}
  158. params={{
  159. projectId: project.slug,
  160. ruleId: rule.id,
  161. }}
  162. userTeamIds={[]}
  163. organization={organization}
  164. onChangeTitle={() => {}}
  165. project={project}
  166. />
  167. );
  168. // has existing trigger
  169. expect(screen.getByTestId('critical-threshold')).toHaveValue('70');
  170. expect(screen.getByTestId('warning-threshold')).toHaveValue('13');
  171. expect(screen.getByTestId('resolve-threshold')).toHaveValue('12');
  172. // Clear warning Trigger
  173. await userEvent.clear(screen.getByTestId('warning-threshold'));
  174. await userEvent.click(screen.getByLabelText('Save Rule'));
  175. expect(editRule).toHaveBeenCalledWith(
  176. expect.anything(),
  177. expect.objectContaining({
  178. data: expect.objectContaining({
  179. aggregation: 0,
  180. dataset: 'events',
  181. id: '4',
  182. name: 'My Incident Rule',
  183. projects: ['project-slug'],
  184. query: '',
  185. status: 0,
  186. timeWindow: 60,
  187. resolveThreshold: 12,
  188. thresholdType: 0,
  189. triggers: [
  190. expect.objectContaining({
  191. actions: [],
  192. alertRuleId: '4',
  193. alertThreshold: 70,
  194. id: '1',
  195. }),
  196. ],
  197. }),
  198. method: 'PUT',
  199. })
  200. );
  201. });
  202. it('renders 404', function () {
  203. const {organization, project} = initializeOrg();
  204. MockApiClient.addMockResponse({
  205. url: `/organizations/${organization.slug}/alert-rules/1234/`,
  206. statusCode: 404,
  207. body: {},
  208. });
  209. render(
  210. <MetricRulesEdit
  211. {...TestStubs.routeComponentProps()}
  212. userTeamIds={[]}
  213. onChangeTitle={() => {}}
  214. params={{
  215. projectId: project.slug,
  216. ruleId: '1234',
  217. }}
  218. organization={organization}
  219. project={project}
  220. />
  221. );
  222. expect(screen.getByText('This alert rule could not be found.')).toBeInTheDocument();
  223. });
  224. });