issueAlertOptions.spec.tsx 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. import {OrganizationFixture} from 'sentry-fixture/organization';
  2. import {OrganizationIntegrationsFixture} from 'sentry-fixture/organizationIntegrations';
  3. import {
  4. MOCK_RESP_INCONSISTENT_INTERVALS,
  5. MOCK_RESP_INCONSISTENT_PLACEHOLDERS,
  6. MOCK_RESP_ONLY_IGNORED_CONDITIONS_INVALID,
  7. MOCK_RESP_VERBOSE,
  8. } from 'sentry-fixture/ruleConditions';
  9. import {render, screen, userEvent} from 'sentry-test/reactTestingLibrary';
  10. import selectEvent from 'sentry-test/selectEvent';
  11. import type {IssueAlertNotificationProps} from 'sentry/views/projectInstall/issueAlertNotificationOptions';
  12. import IssueAlertOptions from 'sentry/views/projectInstall/issueAlertOptions';
  13. describe('IssueAlertOptions', function () {
  14. const organization = OrganizationFixture({
  15. features: ['messaging-integration-onboarding-project-creation'],
  16. });
  17. const URL = `/projects/${organization.slug}/rule-conditions/`;
  18. const notificationProps: IssueAlertNotificationProps = {
  19. actions: [],
  20. channel: 'channel',
  21. integration: OrganizationIntegrationsFixture(),
  22. provider: 'slack',
  23. providersToIntegrations: {},
  24. querySuccess: true,
  25. shouldRenderSetupButton: false,
  26. setActions: jest.fn(),
  27. setChannel: jest.fn(),
  28. setIntegration: jest.fn(),
  29. setProvider: jest.fn(),
  30. };
  31. const props = {
  32. onChange: jest.fn(),
  33. organization,
  34. notificationProps,
  35. };
  36. const getComponent = () => <IssueAlertOptions {...props} {...notificationProps} />;
  37. beforeEach(() => {
  38. MockApiClient.addMockResponse({
  39. url: `/projects/${organization.slug}/rule-conditions/`,
  40. body: MOCK_RESP_VERBOSE,
  41. });
  42. });
  43. afterEach(() => {
  44. MockApiClient.clearMockResponses();
  45. jest.clearAllMocks();
  46. });
  47. it('should render only the `Default Rule` and `Create Later` option on empty response:[]', () => {
  48. MockApiClient.addMockResponse({
  49. url: URL,
  50. body: [],
  51. });
  52. render(getComponent(), {organization});
  53. expect(screen.getAllByRole('radio')).toHaveLength(2);
  54. });
  55. it('should render only the `Default Rule` and `Create Later` option on empty response:{}', () => {
  56. MockApiClient.addMockResponse({
  57. url: URL,
  58. body: {},
  59. });
  60. render(getComponent(), {organization});
  61. expect(screen.getAllByRole('radio')).toHaveLength(2);
  62. });
  63. it('should render only the `Default Rule` and `Create Later` option on responses with different allowable intervals', () => {
  64. MockApiClient.addMockResponse({
  65. url: URL,
  66. body: MOCK_RESP_INCONSISTENT_INTERVALS,
  67. });
  68. render(getComponent(), {organization});
  69. expect(screen.getAllByRole('radio')).toHaveLength(2);
  70. });
  71. it('should render all(three) options on responses with different placeholder values', () => {
  72. MockApiClient.addMockResponse({
  73. url: URL,
  74. body: MOCK_RESP_INCONSISTENT_PLACEHOLDERS,
  75. });
  76. render(getComponent(), {organization});
  77. expect(screen.getAllByRole('radio')).toHaveLength(3);
  78. });
  79. it('should ignore conditions that are not `sentry.rules.conditions.event_frequency.EventFrequencyCondition` and `sentry.rules.conditions.event_frequency.EventUniqueUserFrequencyCondition`', async () => {
  80. MockApiClient.addMockResponse({
  81. url: URL,
  82. body: MOCK_RESP_ONLY_IGNORED_CONDITIONS_INVALID,
  83. });
  84. render(getComponent(), {organization});
  85. expect(screen.getAllByRole('radio')).toHaveLength(3);
  86. await selectEvent.select(screen.getByText('Select...'), 'users affected by');
  87. expect(props.onChange).toHaveBeenCalledWith(
  88. expect.objectContaining({
  89. defaultRules: false,
  90. shouldCreateCustomRule: true,
  91. })
  92. );
  93. });
  94. it('should render all(three) options on a valid response', () => {
  95. MockApiClient.addMockResponse({
  96. url: URL,
  97. body: MOCK_RESP_VERBOSE,
  98. });
  99. render(getComponent());
  100. expect(screen.getAllByRole('radio')).toHaveLength(3);
  101. });
  102. it('should pre-populate fields from server response', async () => {
  103. MockApiClient.addMockResponse({
  104. url: URL,
  105. body: MOCK_RESP_VERBOSE,
  106. });
  107. render(getComponent());
  108. await selectEvent.select(screen.getByText('occurrences of'), 'users affected by');
  109. await selectEvent.select(screen.getByText('one minute'), '30 days');
  110. expect(props.onChange).toHaveBeenCalledWith(
  111. expect.objectContaining({
  112. defaultRules: false,
  113. shouldCreateCustomRule: true,
  114. })
  115. );
  116. });
  117. it('should pre-fill threshold value after a valid server response', () => {
  118. MockApiClient.addMockResponse({
  119. url: URL,
  120. body: MOCK_RESP_VERBOSE,
  121. });
  122. render(getComponent());
  123. expect(screen.getByTestId('range-input')).toHaveValue(10);
  124. });
  125. it('should provide fallthroughType with issue action', async () => {
  126. MockApiClient.addMockResponse({
  127. url: URL,
  128. body: MOCK_RESP_VERBOSE,
  129. });
  130. render(getComponent());
  131. await userEvent.click(screen.getByLabelText(/When there are more than/i));
  132. expect(props.onChange).toHaveBeenCalledWith(
  133. expect.objectContaining({
  134. actions: [
  135. {
  136. id: 'sentry.mail.actions.NotifyEmailAction',
  137. targetType: 'IssueOwners',
  138. fallthroughType: 'ActiveMembers',
  139. },
  140. ],
  141. })
  142. );
  143. });
  144. it('should render alert configuration if `Default` or `Custom` alerts are selected', async () => {
  145. MockApiClient.addMockResponse({
  146. url: URL,
  147. body: MOCK_RESP_VERBOSE,
  148. });
  149. render(getComponent());
  150. await screen.findByRole('checkbox', {name: 'Notify via email'});
  151. await screen.findByRole('checkbox', {
  152. name: 'Notify via integration (Slack, Discord, MS Teams, etc.)',
  153. });
  154. await selectEvent.select(screen.getByText('occurrences of'), 'users affected by');
  155. await screen.findByRole('checkbox', {name: 'Notify via email'});
  156. await screen.findByRole('checkbox', {
  157. name: 'Notify via integration (Slack, Discord, MS Teams, etc.)',
  158. });
  159. });
  160. it('should not render notification configuration if `Create Alerts Later` is selected', async () => {
  161. MockApiClient.addMockResponse({
  162. url: URL,
  163. body: MOCK_RESP_VERBOSE,
  164. });
  165. render(getComponent());
  166. await userEvent.click(screen.getByLabelText("I'll create my own alerts later"));
  167. expect(
  168. screen.queryByRole('checkbox', {name: 'Notify via email'})
  169. ).not.toBeInTheDocument();
  170. expect(
  171. screen.queryByRole('checkbox', {
  172. name: 'Notify via integration (Slack, Discord, MS Teams, etc.)',
  173. })
  174. ).not.toBeInTheDocument();
  175. });
  176. });