ticketRuleModal.spec.tsx 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. import selectEvent from 'react-select-event';
  2. import styled from '@emotion/styled';
  3. import {initializeOrg} from 'sentry-test/initializeOrg';
  4. import {render, screen, userEvent} from 'sentry-test/reactTestingLibrary';
  5. import {addSuccessMessage} from 'sentry/actionCreators/indicator';
  6. import {makeCloseButton} from 'sentry/components/globalModal/components';
  7. import {IssueAlertRuleAction} from 'sentry/types/alerts';
  8. import TicketRuleModal from 'sentry/views/alerts/rules/issue/ticketRuleModal';
  9. jest.unmock('sentry/utils/recreateRoute');
  10. jest.mock('sentry/actionCreators/indicator');
  11. jest.mock('sentry/actionCreators/onboardingTasks');
  12. describe('ProjectAlerts -> TicketRuleModal', function () {
  13. const closeModal = jest.fn();
  14. const modalElements = {
  15. Header: p => p.children,
  16. Body: p => p.children,
  17. Footer: p => p.children,
  18. };
  19. afterEach(function () {
  20. closeModal.mockReset();
  21. MockApiClient.clearMockResponses();
  22. });
  23. const doSubmit = async () =>
  24. await userEvent.click(screen.getByRole('button', {name: 'Apply Changes'}));
  25. const submitSuccess = async () => {
  26. await doSubmit();
  27. expect(addSuccessMessage).toHaveBeenCalled();
  28. expect(closeModal).toHaveBeenCalled();
  29. };
  30. const submitErrors = async errorCount => {
  31. await doSubmit();
  32. expect(screen.getAllByText('Field is required')).toHaveLength(errorCount);
  33. expect(closeModal).toHaveBeenCalledTimes(0);
  34. };
  35. const addMockConfigsAPICall = (otherField = {}) => {
  36. return MockApiClient.addMockResponse({
  37. url: '/organizations/org-slug/integrations/1/?ignored=Sprint',
  38. method: 'GET',
  39. body: {
  40. createIssueConfig: [
  41. {
  42. name: 'project',
  43. label: 'Jira Project',
  44. choices: [['10000', 'TEST']],
  45. default: '10000',
  46. type: 'select',
  47. updatesForm: true,
  48. },
  49. {
  50. name: 'issuetype',
  51. label: 'Issue Type',
  52. default: '10001',
  53. type: 'select',
  54. choices: [
  55. ['10001', 'Improvement'],
  56. ['10002', 'Task'],
  57. ['10003', 'Sub-task'],
  58. ['10004', 'New Feature'],
  59. ['10005', 'Bug'],
  60. ['10000', 'Epic'],
  61. ],
  62. updatesForm: true,
  63. required: true,
  64. },
  65. otherField,
  66. ],
  67. },
  68. });
  69. };
  70. const renderComponent = (
  71. props: Partial<IssueAlertRuleAction> = {},
  72. otherField = {
  73. label: 'Reporter',
  74. required: true,
  75. choices: [['a', 'a']],
  76. type: 'select',
  77. name: 'reporter',
  78. }
  79. ) => {
  80. const {organization, routerContext} = initializeOrg();
  81. addMockConfigsAPICall(otherField);
  82. const body = styled(c => c.children);
  83. return render(
  84. <TicketRuleModal
  85. {...modalElements}
  86. CloseButton={makeCloseButton(() => {})}
  87. closeModal={closeModal}
  88. Body={body()}
  89. Footer={body()}
  90. formFields={{}}
  91. link=""
  92. ticketType=""
  93. instance={{...(props.data || {}), integration: 1}}
  94. index={0}
  95. onSubmitAction={() => {}}
  96. organization={organization}
  97. />,
  98. {context: routerContext}
  99. );
  100. };
  101. describe('Create Rule', function () {
  102. it('should render the Ticket Rule modal', function () {
  103. renderComponent();
  104. expect(screen.getByRole('button', {name: 'Apply Changes'})).toBeInTheDocument();
  105. expect(screen.getByRole('textbox', {name: 'Title'})).toBeInTheDocument();
  106. expect(screen.getByRole('textbox', {name: 'Description'})).toBeInTheDocument();
  107. });
  108. it('should save the modal data when "Apply Changes" is clicked with valid data', async function () {
  109. renderComponent();
  110. await selectEvent.select(screen.getByRole('textbox', {name: 'Reporter'}), 'a');
  111. await submitSuccess();
  112. });
  113. it('should raise validation errors when "Apply Changes" is clicked with invalid data', async function () {
  114. // This doesn't test anything TicketRules specific but I'm leaving it here as an example.
  115. renderComponent();
  116. await submitErrors(1);
  117. });
  118. it('should reload fields when an "updatesForm" field changes', async function () {
  119. renderComponent();
  120. await selectEvent.select(screen.getByRole('textbox', {name: 'Reporter'}), 'a');
  121. addMockConfigsAPICall({
  122. label: 'Assignee',
  123. required: true,
  124. choices: [['b', 'b']],
  125. type: 'select',
  126. name: 'assignee',
  127. });
  128. await selectEvent.select(screen.getByRole('textbox', {name: 'Issue Type'}), 'Epic');
  129. await selectEvent.select(screen.getByRole('textbox', {name: 'Assignee'}), 'b');
  130. await submitSuccess();
  131. });
  132. it('should ignore error checking when default is empty array', function () {
  133. renderComponent({
  134. otherField: {
  135. label: 'Labels',
  136. required: false,
  137. choices: [['bug', `bug`]],
  138. default: [],
  139. type: 'select',
  140. multiple: true,
  141. name: 'labels',
  142. },
  143. });
  144. expect(
  145. screen.queryAllByText(`Could not fetch saved option for Labels. Please reselect.`)
  146. ).toHaveLength(0);
  147. });
  148. it('should persist values when the modal is reopened', async function () {
  149. renderComponent({data: {reporter: 'a'}});
  150. await submitSuccess();
  151. });
  152. it('should get async options from URL', async function () {
  153. renderComponent();
  154. addMockConfigsAPICall({
  155. label: 'Assignee',
  156. required: true,
  157. url: 'http://example.com',
  158. type: 'select',
  159. name: 'assignee',
  160. });
  161. await selectEvent.select(screen.getByRole('textbox', {name: 'Issue Type'}), 'Epic');
  162. // Component makes 1 request per character typed.
  163. let txt = '';
  164. for (const char of 'Joe') {
  165. txt += char;
  166. MockApiClient.addMockResponse({
  167. url: `http://example.com?field=assignee&issuetype=10001&project=10000&query=${txt}`,
  168. method: 'GET',
  169. body: [{label: 'Joe', value: 'Joe'}],
  170. });
  171. }
  172. const menu = screen.getByRole('textbox', {name: 'Assignee'});
  173. selectEvent.openMenu(menu);
  174. await userEvent.type(menu, 'Joe{Escape}');
  175. await selectEvent.select(menu, 'Joe');
  176. await submitSuccess();
  177. });
  178. });
  179. });