edit.spec.tsx 7.0 KB

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