ticketRuleModal.spec.tsx 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  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 = (otherFields = {}) => {
  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. otherFields,
  66. ],
  67. },
  68. });
  69. };
  70. const renderComponent = (props: Partial<IssueAlertRuleAction> = {}) => {
  71. const {organization, routerContext} = initializeOrg();
  72. addMockConfigsAPICall({
  73. label: 'Reporter',
  74. required: true,
  75. choices: [['a', 'a']],
  76. type: 'select',
  77. name: 'reporter',
  78. });
  79. const body = styled(c => c.children);
  80. return render(
  81. <TicketRuleModal
  82. {...modalElements}
  83. CloseButton={makeCloseButton(() => {})}
  84. closeModal={closeModal}
  85. Body={body()}
  86. Footer={body()}
  87. formFields={{}}
  88. link=""
  89. ticketType=""
  90. instance={{...(props.data || {}), integration: 1}}
  91. index={0}
  92. onSubmitAction={() => {}}
  93. organization={organization}
  94. />,
  95. {context: routerContext}
  96. );
  97. };
  98. describe('Create Rule', function () {
  99. it('should render the Ticket Rule modal', function () {
  100. renderComponent();
  101. expect(screen.getByRole('button', {name: 'Apply Changes'})).toBeInTheDocument();
  102. expect(screen.getByRole('textbox', {name: 'Title'})).toBeInTheDocument();
  103. expect(screen.getByRole('textbox', {name: 'Description'})).toBeInTheDocument();
  104. });
  105. it('should save the modal data when "Apply Changes" is clicked with valid data', async function () {
  106. renderComponent();
  107. await selectEvent.select(screen.getByRole('textbox', {name: 'Reporter'}), 'a');
  108. await submitSuccess();
  109. });
  110. it('should raise validation errors when "Apply Changes" is clicked with invalid data', async function () {
  111. // This doesn't test anything TicketRules specific but I'm leaving it here as an example.
  112. renderComponent();
  113. await submitErrors(1);
  114. });
  115. it('should reload fields when an "updatesForm" field changes', async function () {
  116. renderComponent();
  117. await selectEvent.select(screen.getByRole('textbox', {name: 'Reporter'}), 'a');
  118. addMockConfigsAPICall({
  119. label: 'Assignee',
  120. required: true,
  121. choices: [['b', 'b']],
  122. type: 'select',
  123. name: 'assignee',
  124. });
  125. await selectEvent.select(screen.getByRole('textbox', {name: 'Issue Type'}), 'Epic');
  126. await selectEvent.select(screen.getByRole('textbox', {name: 'Assignee'}), 'b');
  127. await submitSuccess();
  128. });
  129. it('should persist values when the modal is reopened', async function () {
  130. renderComponent({data: {reporter: 'a'}});
  131. await submitSuccess();
  132. });
  133. it('should get async options from URL', async function () {
  134. renderComponent();
  135. addMockConfigsAPICall({
  136. label: 'Assignee',
  137. required: true,
  138. url: 'http://example.com',
  139. type: 'select',
  140. name: 'assignee',
  141. });
  142. await selectEvent.select(screen.getByRole('textbox', {name: 'Issue Type'}), 'Epic');
  143. // Component makes 1 request per character typed.
  144. let txt = '';
  145. for (const char of 'Joe') {
  146. txt += char;
  147. MockApiClient.addMockResponse({
  148. url: `http://example.com?field=assignee&issuetype=10001&project=10000&query=${txt}`,
  149. method: 'GET',
  150. body: [{label: 'Joe', value: 'Joe'}],
  151. });
  152. }
  153. const menu = screen.getByRole('textbox', {name: 'Assignee'});
  154. selectEvent.openMenu(menu);
  155. await userEvent.type(menu, 'Joe{Escape}');
  156. await selectEvent.select(menu, 'Joe');
  157. await submitSuccess();
  158. });
  159. });
  160. });