sentryAppRuleModal.spec.jsx 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. import {render, screen, userEvent, waitFor} from 'sentry-test/reactTestingLibrary';
  2. import SentryAppRuleModal from 'sentry/views/alerts/rules/issue/sentryAppRuleModal';
  3. describe('SentryAppRuleModal', function () {
  4. const modalElements = {
  5. Header: p => p.children,
  6. Body: p => p.children,
  7. Footer: p => p.children,
  8. };
  9. let sentryApp;
  10. let sentryAppInstallation;
  11. beforeEach(function () {
  12. sentryApp = TestStubs.SentryApp();
  13. sentryAppInstallation = TestStubs.SentryAppInstallation({sentryApp});
  14. });
  15. const _submit = () => {
  16. userEvent.click(screen.getByText('Save Changes'));
  17. return screen.queryAllByText('Field is required');
  18. };
  19. const submitSuccess = () => {
  20. const errors = _submit();
  21. expect(errors).toHaveLength(0);
  22. };
  23. const submitErrors = errorCount => {
  24. const errors = _submit();
  25. expect(errors).toHaveLength(errorCount);
  26. };
  27. const defaultConfig = {
  28. uri: '/integration/test/',
  29. required_fields: [
  30. {
  31. type: 'text',
  32. label: 'Alert Title',
  33. name: 'title',
  34. },
  35. {
  36. type: 'textarea',
  37. label: 'Alert Description',
  38. name: 'description',
  39. },
  40. {
  41. type: 'select',
  42. label: 'Team Channel',
  43. name: 'channel',
  44. choices: [
  45. ['valor', 'valor'],
  46. ['mystic', 'mystic'],
  47. ['instinct', 'instinct'],
  48. ],
  49. },
  50. ],
  51. optional_fields: [
  52. {
  53. type: 'text',
  54. label: 'Extra Details',
  55. name: 'extra',
  56. },
  57. {
  58. type: 'select',
  59. label: 'Assignee',
  60. name: 'assignee',
  61. uri: '/link/assignee/',
  62. skip_load_on_open: 'true',
  63. },
  64. {
  65. type: 'select',
  66. label: 'Workspace',
  67. name: 'workspace',
  68. uri: '/link/workspace/',
  69. },
  70. ],
  71. };
  72. const resetValues = {
  73. settings: [
  74. {
  75. name: 'extra',
  76. value: 'saved details from last edit',
  77. },
  78. {
  79. name: 'assignee',
  80. value: 'edna-mode',
  81. label: 'Edna Mode',
  82. },
  83. ],
  84. };
  85. const createWrapper = (props = {}) => {
  86. return render(
  87. <SentryAppRuleModal
  88. {...modalElements}
  89. sentryAppInstallationUuid={sentryAppInstallation.uuid}
  90. appName={sentryApp.name}
  91. config={defaultConfig}
  92. action="create"
  93. onSubmitSuccess={() => {}}
  94. resetValues={resetValues}
  95. {...props}
  96. />
  97. );
  98. };
  99. describe('Create UI Alert Rule', function () {
  100. it('should render the Alert Rule modal with the config fields', function () {
  101. createWrapper();
  102. const {required_fields, optional_fields} = defaultConfig;
  103. const allFields = [...required_fields, ...optional_fields];
  104. allFields.forEach(field => {
  105. expect(screen.getByText(field.label)).toBeInTheDocument();
  106. });
  107. });
  108. it('should raise validation errors when "Save Changes" is clicked with invalid data', function () {
  109. createWrapper();
  110. submitErrors(3);
  111. });
  112. it('should submit when "Save Changes" is clicked with valid data', async function () {
  113. createWrapper();
  114. const titleInput = screen.getByTestId('title');
  115. userEvent.type(titleInput, 'some title');
  116. const descriptionInput = screen.getByTestId('description');
  117. userEvent.type(descriptionInput, 'some description');
  118. const channelInput = screen.getAllByText('Type to search')[0];
  119. userEvent.type(channelInput, '{keyDown}');
  120. userEvent.click(screen.getByText('valor'));
  121. // Ensure text fields are persisted on edit
  122. const savedExtraDetailsInput = screen.getByDisplayValue(
  123. resetValues.settings[0].value
  124. );
  125. expect(savedExtraDetailsInput).toBeInTheDocument();
  126. // Ensure select fields are persisted with labels on edit
  127. const savedAssigneeInput = screen.getByText(resetValues.settings[1].label);
  128. expect(savedAssigneeInput).toBeInTheDocument();
  129. // Ensure async select fields filter correctly
  130. const workspaceChoices = [
  131. ['WS0', 'Primary Workspace'],
  132. ['WS1', 'Secondary Workspace'],
  133. ];
  134. const workspaceResponse = MockApiClient.addMockResponse({
  135. url: `/sentry-app-installations/${sentryAppInstallation.uuid}/external-requests/`,
  136. body: {choices: workspaceChoices},
  137. });
  138. const workspaceInput = screen.getByText('Type to search');
  139. // Search by value
  140. userEvent.type(workspaceInput, workspaceChoices[1][0]);
  141. await waitFor(() => expect(workspaceResponse).toHaveBeenCalled());
  142. // Select by label
  143. userEvent.click(screen.getByText(workspaceChoices[1][1]));
  144. submitSuccess();
  145. });
  146. });
  147. });