addModal.spec.tsx 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. import sortBy from 'lodash/sortBy';
  2. import {mountWithTheme} from 'sentry-test/enzyme';
  3. import {openModal} from 'sentry/actionCreators/modal';
  4. import GlobalModal from 'sentry/components/globalModal';
  5. import convertRelayPiiConfig from 'sentry/views/settings/components/dataScrubbing/convertRelayPiiConfig';
  6. import Add from 'sentry/views/settings/components/dataScrubbing/modals/add';
  7. import {MethodType, RuleType} from 'sentry/views/settings/components/dataScrubbing/types';
  8. import {
  9. getMethodLabel,
  10. getRuleLabel,
  11. } from 'sentry/views/settings/components/dataScrubbing/utils';
  12. const relayPiiConfig = TestStubs.DataScrubbingRelayPiiConfig();
  13. const stringRelayPiiConfig = JSON.stringify(relayPiiConfig);
  14. const organizationSlug = 'sentry';
  15. const convertedRules = convertRelayPiiConfig(stringRelayPiiConfig);
  16. const rules = convertedRules;
  17. const successfullySaved = jest.fn();
  18. const projectId = 'foo';
  19. const endpoint = `/projects/${organizationSlug}/${projectId}/`;
  20. const api = new MockApiClient();
  21. async function renderComponent() {
  22. const wrapper = mountWithTheme(<GlobalModal />);
  23. openModal(modalProps => (
  24. <Add
  25. {...modalProps}
  26. projectId={projectId}
  27. savedRules={rules}
  28. api={api}
  29. endpoint={endpoint}
  30. orgSlug={organizationSlug}
  31. onSubmitSuccess={successfullySaved}
  32. />
  33. ));
  34. await tick();
  35. wrapper.update();
  36. return wrapper;
  37. }
  38. describe('Add Modal', () => {
  39. it('open Add Rule Modal', async () => {
  40. const wrapper = await renderComponent();
  41. expect(wrapper.find('Header').text()).toEqual('Add an advanced data scrubbing rule');
  42. const fieldGroup = wrapper.find('FieldGroup');
  43. expect(fieldGroup).toHaveLength(2);
  44. // Method Field
  45. const methodGroup = fieldGroup.at(0).find('Field');
  46. expect(methodGroup.find('FieldLabel').text()).toEqual('Method');
  47. const methodFieldHelp = 'What to do';
  48. expect(methodGroup.find('QuestionTooltip').prop('title')).toEqual(methodFieldHelp);
  49. expect(methodGroup.find('Tooltip').prop('title')).toEqual(methodFieldHelp);
  50. const methodField = methodGroup.find('SelectField');
  51. expect(methodField.exists()).toBe(true);
  52. const methodFieldProps = methodField.props();
  53. expect(methodFieldProps.value).toEqual(MethodType.MASK);
  54. const methodFieldOptions = sortBy(Object.values(MethodType)).map(value => ({
  55. ...getMethodLabel(value),
  56. value,
  57. }));
  58. expect(methodFieldProps.options).toEqual(methodFieldOptions);
  59. // Type Field
  60. const typeGroup = fieldGroup.at(1).find('Field');
  61. expect(typeGroup.find('FieldLabel').text()).toEqual('Data Type');
  62. const typeFieldHelp =
  63. 'What to look for. Use an existing pattern or define your own using regular expressions.';
  64. expect(typeGroup.find('QuestionTooltip').prop('title')).toEqual(typeFieldHelp);
  65. expect(typeGroup.find('Tooltip').prop('title')).toEqual(typeFieldHelp);
  66. const typeField = typeGroup.find('SelectField');
  67. expect(typeField.exists()).toBe(true);
  68. const typeFieldProps = typeField.props();
  69. expect(typeFieldProps.value).toEqual(RuleType.CREDITCARD);
  70. const typeFieldOptions = sortBy(Object.values(RuleType)).map(value => ({
  71. label: getRuleLabel(value),
  72. value,
  73. }));
  74. expect(typeFieldProps.options).toEqual(typeFieldOptions);
  75. // Event ID
  76. expect(wrapper.find('Toggle').text()).toEqual('Use event ID for auto-completion');
  77. // Source Field
  78. const sourceGroup = wrapper.find('SourceGroup');
  79. expect(sourceGroup.exists()).toBe(true);
  80. expect(sourceGroup.find('EventIdField')).toHaveLength(0);
  81. const sourceField = sourceGroup.find('Field');
  82. expect(sourceField.find('FieldLabel').text()).toEqual('Source');
  83. const sourceFieldHelp =
  84. 'Where to look. In the simplest case this can be an attribute name.';
  85. expect(sourceField.find('QuestionTooltip').prop('title')).toEqual(sourceFieldHelp);
  86. expect(sourceField.find('Tooltip').prop('title')).toEqual(sourceFieldHelp);
  87. // Close Modal
  88. const cancelButton = wrapper.find('[aria-label="Cancel"]').hostNodes();
  89. expect(cancelButton.exists()).toBe(true);
  90. cancelButton.simulate('click');
  91. await tick();
  92. wrapper.update();
  93. expect(wrapper.find('GlobalModal[visible=true]').exists()).toBe(false);
  94. });
  95. it('Display placeholder field', async () => {
  96. const wrapper = await renderComponent();
  97. const fieldGroup = wrapper.find('FieldGroup');
  98. expect(fieldGroup).toHaveLength(2);
  99. // Method Field
  100. const methodGroup = fieldGroup.at(0).find('Field');
  101. expect(methodGroup).toHaveLength(1);
  102. const methodField = methodGroup.find('[data-test-id="method-field"]');
  103. const methodFieldInput = methodField.find('input').at(1);
  104. methodFieldInput.simulate('keyDown', {key: 'ArrowDown'});
  105. const methodFieldMenuOptions = wrapper.find(
  106. '[data-test-id="method-field"] MenuList Option Wrapper'
  107. );
  108. expect(methodFieldMenuOptions).toHaveLength(4);
  109. const replaceOption = methodFieldMenuOptions.at(3);
  110. expect(replaceOption.find('[data-test-id="label"]').text()).toEqual('Replace');
  111. expect(replaceOption.find('Description').text()).toEqual(
  112. '(Replace with Placeholder)'
  113. );
  114. // After the click the placeholder field MUST be in the DOM
  115. replaceOption.simulate('click');
  116. wrapper.update();
  117. expect(
  118. wrapper.find('[data-test-id="method-field"] input').at(1).prop('value')
  119. ).toEqual(MethodType.REPLACE);
  120. const updatedMethodGroup = wrapper.find('FieldGroup').at(0).find('Field');
  121. expect(updatedMethodGroup).toHaveLength(2);
  122. const placeholderField = updatedMethodGroup.at(1);
  123. expect(placeholderField.find('FieldLabel').text()).toEqual(
  124. 'Custom Placeholder (Optional)'
  125. );
  126. const placeholderFieldHelp = 'It will replace the default placeholder [Filtered]';
  127. expect(placeholderField.find('QuestionTooltip').prop('title')).toEqual(
  128. placeholderFieldHelp
  129. );
  130. expect(placeholderField.find('Tooltip').prop('title')).toEqual(placeholderFieldHelp);
  131. // After the click, the placeholder field MUST NOT be in the DOM
  132. wrapper
  133. .find('[data-test-id="method-field"]')
  134. .find('input')
  135. .at(1)
  136. .simulate('keyDown', {key: 'ArrowDown'});
  137. const hashOption = wrapper
  138. .find('[data-test-id="method-field"] MenuList Option Wrapper')
  139. .at(0);
  140. hashOption.simulate('click');
  141. expect(wrapper.find('[data-test-id="method-field"] input').at(1).prop('value')).toBe(
  142. MethodType.HASH
  143. );
  144. expect(wrapper.find('FieldGroup').at(0).find('Field')).toHaveLength(1);
  145. });
  146. it('Display regex field', async () => {
  147. const wrapper = await renderComponent();
  148. const fieldGroup = wrapper.find('FieldGroup');
  149. expect(fieldGroup).toHaveLength(2);
  150. // Type Field
  151. const typeGroup = fieldGroup.at(1).find('Field');
  152. expect(typeGroup).toHaveLength(1);
  153. const typeField = typeGroup.find('[data-test-id="type-field"]');
  154. const typeFieldInput = typeField.find('input').at(1);
  155. typeFieldInput.simulate('keyDown', {key: 'ArrowDown'});
  156. const typeFieldMenuOptions = wrapper.find(
  157. '[data-test-id="type-field"] MenuList Option Wrapper'
  158. );
  159. expect(typeFieldMenuOptions).toHaveLength(13);
  160. const regexOption = typeFieldMenuOptions.at(7);
  161. expect(regexOption.find('[data-test-id="label"]').text()).toEqual('Regex matches');
  162. // After the click, the regex matches field MUST be in the DOM
  163. regexOption.simulate('click');
  164. wrapper.update();
  165. expect(wrapper.find('[data-test-id="type-field"] input').at(1).prop('value')).toEqual(
  166. RuleType.PATTERN
  167. );
  168. const updatedTypeGroup = wrapper.find('FieldGroup').at(1).find('Field');
  169. expect(updatedTypeGroup).toHaveLength(2);
  170. const regexField = updatedTypeGroup.at(1);
  171. expect(regexField.find('FieldLabel').text()).toEqual('Regex matches');
  172. const regexFieldHelp = 'Custom regular expression (see documentation)';
  173. expect(regexField.find('QuestionTooltip').prop('title')).toEqual(regexFieldHelp);
  174. expect(regexField.find('Tooltip').prop('title')).toEqual(regexFieldHelp);
  175. // After the click, the regex matches field MUST NOT be in the DOM
  176. wrapper
  177. .find('[data-test-id="type-field"]')
  178. .find('input')
  179. .at(1)
  180. .simulate('keyDown', {key: 'ArrowDown'});
  181. const creditCardOption = wrapper
  182. .find('[data-test-id="type-field"] MenuList Option Wrapper')
  183. .at(1);
  184. creditCardOption.simulate('click');
  185. expect(wrapper.find('[data-test-id="type-field"] input').at(1).prop('value')).toBe(
  186. RuleType.CREDITCARD
  187. );
  188. expect(wrapper.find('FieldGroup').at(1).find('Field')).toHaveLength(1);
  189. });
  190. it('Display Event Id', async () => {
  191. const eventId = '12345678901234567890123456789012';
  192. MockApiClient.addMockResponse({
  193. url: `/organizations/${organizationSlug}/data-scrubbing-selector-suggestions/`,
  194. body: {
  195. suggestions: [
  196. {type: 'value', examples: ['34359738368'], value: "extra.'system.cpu.memory'"},
  197. {type: 'value', value: '$frame.abs_path'},
  198. ],
  199. },
  200. });
  201. const wrapper = await renderComponent();
  202. const eventIdToggle = wrapper.find('Toggle');
  203. eventIdToggle.simulate('click');
  204. const eventIdFieldInput = wrapper.find('[data-test-id="event-id-field"] input');
  205. eventIdFieldInput.simulate('change', {
  206. target: {value: eventId},
  207. });
  208. eventIdFieldInput.simulate('blur');
  209. await tick();
  210. // Loading
  211. expect(wrapper.find('[data-test-id="event-id-field"] FormSpinner')).toHaveLength(1);
  212. wrapper.update();
  213. // Fetched new suggestions successfully through the informed event ID
  214. expect(wrapper.find('[data-test-id="event-id-field"] IconCheckmark')).toHaveLength(1);
  215. });
  216. });