ruleForm.spec.jsx 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. import React from 'react';
  2. import {mountWithTheme} from 'sentry-test/enzyme';
  3. import {initializeOrg} from 'sentry-test/initializeOrg';
  4. import {addErrorMessage} from 'app/actionCreators/indicator';
  5. import {metric} from 'app/utils/analytics';
  6. import FormModel from 'app/views/settings/components/forms/model';
  7. import RuleFormContainer from 'app/views/settings/incidentRules/ruleForm';
  8. jest.mock('app/actionCreators/indicator');
  9. jest.mock('app/utils/analytics', () => ({
  10. metric: {
  11. startTransaction: jest.fn(() => ({
  12. setTag: jest.fn(),
  13. setData: jest.fn(),
  14. })),
  15. endTransaction: jest.fn(),
  16. },
  17. }));
  18. describe('Incident Rules Form', function () {
  19. const {organization, project, routerContext} = initializeOrg();
  20. const createWrapper = props =>
  21. mountWithTheme(
  22. <RuleFormContainer
  23. params={{orgId: organization.slug, projectId: project.slug}}
  24. organization={organization}
  25. project={project}
  26. {...props}
  27. />,
  28. routerContext
  29. );
  30. beforeEach(function () {
  31. MockApiClient.clearMockResponses();
  32. MockApiClient.addMockResponse({
  33. url: '/organizations/org-slug/tags/',
  34. body: [],
  35. });
  36. MockApiClient.addMockResponse({
  37. url: '/organizations/org-slug/users/',
  38. body: [],
  39. });
  40. MockApiClient.addMockResponse({
  41. url: '/projects/org-slug/project-slug/environments/',
  42. body: [],
  43. });
  44. MockApiClient.addMockResponse({
  45. url: '/organizations/org-slug/events-stats/',
  46. body: TestStubs.EventsStats(),
  47. });
  48. MockApiClient.addMockResponse({
  49. url: '/organizations/org-slug/events-meta/',
  50. body: {count: 5},
  51. });
  52. MockApiClient.addMockResponse({
  53. url: '/organizations/org-slug/alert-rules/available-actions/',
  54. body: [
  55. {
  56. allowedTargetTypes: ['user', 'team'],
  57. integrationName: null,
  58. type: 'email',
  59. integrationId: null,
  60. },
  61. ],
  62. });
  63. });
  64. describe('Creating a new rule', function () {
  65. let createRule;
  66. beforeEach(function () {
  67. createRule = MockApiClient.addMockResponse({
  68. url: '/projects/org-slug/project-slug/alert-rules/',
  69. method: 'POST',
  70. });
  71. metric.startTransaction.mockClear();
  72. });
  73. /**
  74. * Note this isn't necessarily the desired behavior, as it is just documenting the behavior
  75. */
  76. it('creates a rule', async function () {
  77. const rule = TestStubs.IncidentRule();
  78. const wrapper = createWrapper({
  79. rule: {
  80. ...rule,
  81. id: undefined,
  82. eventTypes: ['default'],
  83. },
  84. });
  85. // Enter in name so we can submit
  86. wrapper
  87. .find('input[name="name"]')
  88. .simulate('change', {target: {value: 'Incident Rule'}});
  89. wrapper.find('form').simulate('submit');
  90. expect(createRule).toHaveBeenCalledWith(
  91. expect.anything(),
  92. expect.objectContaining({
  93. data: expect.objectContaining({
  94. name: 'Incident Rule',
  95. projects: ['project-slug'],
  96. eventTypes: ['default'],
  97. }),
  98. })
  99. );
  100. expect(metric.startTransaction).toHaveBeenCalledWith({name: 'saveAlertRule'});
  101. });
  102. describe('Slack async lookup', () => {
  103. const uuid = 'xxxx-xxxx-xxxx';
  104. let model;
  105. beforeEach(() => {
  106. jest.useFakeTimers();
  107. model = new FormModel();
  108. });
  109. afterEach(() => {
  110. jest.clearAllTimers();
  111. });
  112. it('success status updates the rule', async () => {
  113. const endpoint = `/projects/org-slug/project-slug/alert-rule-task/${uuid}/`;
  114. const alertRule = TestStubs.IncidentRule({name: 'Slack Alert Rule'});
  115. MockApiClient.addMockResponse({
  116. url: endpoint,
  117. body: {
  118. status: 'success',
  119. alertRule,
  120. },
  121. });
  122. const onSubmitSuccess = jest.fn();
  123. const wrapper = createWrapper({
  124. ruleId: alertRule.id,
  125. rule: alertRule,
  126. onSubmitSuccess,
  127. });
  128. const ruleFormContainer = wrapper.find('RuleFormContainer');
  129. ruleFormContainer.setState({uuid, loading: true});
  130. await Promise.resolve();
  131. ruleFormContainer.update();
  132. ruleFormContainer.instance().fetchStatus(model);
  133. jest.runOnlyPendingTimers();
  134. await Promise.resolve();
  135. ruleFormContainer.update();
  136. expect(ruleFormContainer.state('loading')).toBe(false);
  137. expect(onSubmitSuccess).toHaveBeenCalledWith(
  138. expect.objectContaining({
  139. id: alertRule.id,
  140. name: alertRule.name,
  141. }),
  142. expect.anything()
  143. );
  144. });
  145. it('pending status keeps loading true', async () => {
  146. const endpoint = `/projects/org-slug/project-slug/alert-rule-task/${uuid}/`;
  147. const alertRule = TestStubs.IncidentRule({name: 'Slack Alert Rule'});
  148. MockApiClient.addMockResponse({
  149. url: endpoint,
  150. body: {
  151. status: 'pending',
  152. },
  153. });
  154. const onSubmitSuccess = jest.fn();
  155. const wrapper = createWrapper({
  156. ruleId: alertRule.id,
  157. rule: alertRule,
  158. onSubmitSuccess,
  159. });
  160. const ruleFormContainer = wrapper.find('RuleFormContainer');
  161. ruleFormContainer.setState({uuid, loading: true});
  162. await Promise.resolve();
  163. ruleFormContainer.update();
  164. ruleFormContainer.instance().fetchStatus(model);
  165. jest.runOnlyPendingTimers();
  166. await Promise.resolve();
  167. ruleFormContainer.update();
  168. expect(ruleFormContainer.state('loading')).toBe(true);
  169. expect(onSubmitSuccess).not.toHaveBeenCalled();
  170. });
  171. it('failed status renders error message', async () => {
  172. const endpoint = `/projects/org-slug/project-slug/alert-rule-task/${uuid}/`;
  173. const alertRule = TestStubs.IncidentRule({name: 'Slack Alert Rule'});
  174. MockApiClient.addMockResponse({
  175. url: endpoint,
  176. body: {
  177. status: 'failed',
  178. error: 'An error occurred',
  179. },
  180. });
  181. const onSubmitSuccess = jest.fn();
  182. const wrapper = createWrapper({
  183. ruleId: alertRule.id,
  184. rule: alertRule,
  185. onSubmitSuccess,
  186. });
  187. const ruleFormContainer = wrapper.find('RuleFormContainer');
  188. ruleFormContainer.setState({uuid, loading: true});
  189. await Promise.resolve();
  190. ruleFormContainer.update();
  191. ruleFormContainer.instance().fetchStatus(model);
  192. jest.runOnlyPendingTimers();
  193. await Promise.resolve();
  194. ruleFormContainer.update();
  195. expect(ruleFormContainer.state('loading')).toBe(false);
  196. expect(onSubmitSuccess).not.toHaveBeenCalled();
  197. expect(addErrorMessage).toHaveBeenCalledWith('An error occurred');
  198. });
  199. });
  200. });
  201. describe('Editing a rule', function () {
  202. let editRule;
  203. let editTrigger;
  204. const rule = TestStubs.IncidentRule();
  205. beforeEach(function () {
  206. editRule = MockApiClient.addMockResponse({
  207. url: `/projects/org-slug/project-slug/alert-rules/${rule.id}/`,
  208. method: 'PUT',
  209. body: rule,
  210. });
  211. editTrigger = MockApiClient.addMockResponse({
  212. url: `/organizations/org-slug/alert-rules/${rule.id}/triggers/1/`,
  213. method: 'PUT',
  214. body: TestStubs.IncidentTrigger({id: 1}),
  215. });
  216. });
  217. afterEach(function () {
  218. editRule.mockReset();
  219. editTrigger.mockReset();
  220. });
  221. it('edits metric', async function () {
  222. const wrapper = createWrapper({
  223. ruleId: rule.id,
  224. rule,
  225. });
  226. wrapper
  227. .find('input[name="name"]')
  228. .simulate('change', {target: {value: 'new name'}});
  229. wrapper.find('form').simulate('submit');
  230. expect(editRule).toHaveBeenLastCalledWith(
  231. expect.anything(),
  232. expect.objectContaining({
  233. data: expect.objectContaining({
  234. name: 'new name',
  235. }),
  236. })
  237. );
  238. });
  239. });
  240. });