integrationExternalMappingForm.spec.tsx 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. import {GitHubIntegrationFixture} from 'sentry-fixture/githubIntegration';
  2. import {render, screen, userEvent, waitFor} from 'sentry-test/reactTestingLibrary';
  3. import IntegrationExternalMappingForm from './integrationExternalMappingForm';
  4. describe('IntegrationExternalMappingForm', function () {
  5. const dataEndpoint = '/test/dataEndpoint/';
  6. const baseProps = {
  7. integration: GitHubIntegrationFixture(),
  8. dataEndpoint,
  9. getBaseFormEndpoint: jest.fn(_mapping => dataEndpoint),
  10. sentryNamesMapper: mappings => mappings,
  11. };
  12. const MOCK_USER_MAPPING = {
  13. id: '1',
  14. userId: '1',
  15. externalName: '@gwen',
  16. sentryName: 'gwen@mcu.org',
  17. };
  18. const MOCK_TEAM_MAPPING = {
  19. id: '1',
  20. teamId: '1',
  21. externalName: '@getsentry/animals',
  22. sentryName: '#zoo',
  23. };
  24. const DEFAULT_OPTIONS = [
  25. {id: '1', name: 'option1'},
  26. {id: '2', name: 'option2'},
  27. {id: '3', name: 'option3'},
  28. ];
  29. let getResponse, postResponse, putResponse;
  30. beforeEach(() => {
  31. jest.clearAllMocks();
  32. MockApiClient.clearMockResponses();
  33. getResponse = MockApiClient.addMockResponse({
  34. url: dataEndpoint,
  35. method: 'GET',
  36. body: DEFAULT_OPTIONS,
  37. });
  38. postResponse = MockApiClient.addMockResponse({
  39. url: dataEndpoint,
  40. method: 'POST',
  41. body: {},
  42. });
  43. putResponse = MockApiClient.addMockResponse({
  44. url: `${dataEndpoint}1/`,
  45. method: 'PUT',
  46. body: {},
  47. });
  48. });
  49. // No mapping provided (e.g. Create a new mapping)
  50. it('renders with no mapping provided as a form', async function () {
  51. render(<IntegrationExternalMappingForm type="user" {...baseProps} />);
  52. expect(await screen.findByPlaceholderText('@username')).toBeInTheDocument();
  53. expect(screen.getByText('Select Sentry User')).toBeInTheDocument();
  54. expect(screen.getByTestId('form-submit')).toBeInTheDocument();
  55. });
  56. it('renders with no mapping as an inline field', async function () {
  57. render(<IntegrationExternalMappingForm isInline type="user" {...baseProps} />);
  58. expect(await screen.findByText('Select Sentry User')).toBeInTheDocument();
  59. expect(screen.queryByPlaceholderText('@username')).not.toBeInTheDocument();
  60. expect(screen.queryByTestId('form-submit')).not.toBeInTheDocument();
  61. });
  62. // Full mapping provided (e.g. Update an existing mapping)
  63. it('renders with a full mapping provided as a form', async function () {
  64. render(
  65. <IntegrationExternalMappingForm
  66. type="user"
  67. mapping={MOCK_USER_MAPPING}
  68. {...baseProps}
  69. />
  70. );
  71. expect(
  72. await screen.findByDisplayValue(MOCK_USER_MAPPING.externalName)
  73. ).toBeInTheDocument();
  74. expect(
  75. await screen.findByText(`option${MOCK_USER_MAPPING.userId}`)
  76. ).toBeInTheDocument();
  77. expect(screen.getByTestId('form-submit')).toBeInTheDocument();
  78. });
  79. it('renders with a full mapping provided as an inline field', async function () {
  80. render(
  81. <IntegrationExternalMappingForm
  82. isInline
  83. type="user"
  84. mapping={MOCK_USER_MAPPING}
  85. {...baseProps}
  86. />
  87. );
  88. expect(
  89. await screen.findByText(`option${MOCK_USER_MAPPING.userId}`)
  90. ).toBeInTheDocument();
  91. expect(
  92. screen.queryByDisplayValue(MOCK_USER_MAPPING.externalName)
  93. ).not.toBeInTheDocument();
  94. expect(screen.queryByTestId('form-submit')).not.toBeInTheDocument();
  95. });
  96. // Suggested mapping provided (e.g. Create new mapping from suggested external name)
  97. it('renders with a suggested mapping provided as a form', async function () {
  98. render(
  99. <IntegrationExternalMappingForm
  100. type="team"
  101. mapping={{externalName: MOCK_TEAM_MAPPING.externalName}}
  102. {...baseProps}
  103. />
  104. );
  105. expect(
  106. await screen.findByDisplayValue(MOCK_TEAM_MAPPING.externalName)
  107. ).toBeInTheDocument();
  108. expect(screen.getByText('Select Sentry Team')).toBeInTheDocument();
  109. expect(screen.getByTestId('form-submit')).toBeInTheDocument();
  110. });
  111. it('renders with a suggested mapping provided as an inline field', async function () {
  112. render(
  113. <IntegrationExternalMappingForm
  114. isInline
  115. type="team"
  116. mapping={{externalName: MOCK_TEAM_MAPPING.externalName}}
  117. {...baseProps}
  118. />
  119. );
  120. expect(await screen.findByText('Select Sentry Team')).toBeInTheDocument();
  121. expect(
  122. screen.queryByDisplayValue(MOCK_TEAM_MAPPING.externalName)
  123. ).not.toBeInTheDocument();
  124. expect(screen.queryByTestId('form-submit')).not.toBeInTheDocument();
  125. });
  126. it('updates the model when submitting', async function () {
  127. render(
  128. <IntegrationExternalMappingForm
  129. type="user"
  130. mapping={{externalName: MOCK_USER_MAPPING.externalName}}
  131. {...baseProps}
  132. />
  133. );
  134. expect(baseProps.getBaseFormEndpoint).not.toHaveBeenCalled();
  135. expect(postResponse).not.toHaveBeenCalled();
  136. await userEvent.type(screen.getByText('Select Sentry User'), 'option2');
  137. await userEvent.click(screen.getAllByText('option2')[1]);
  138. await userEvent.click(screen.getByTestId('form-submit'));
  139. await waitFor(() => {
  140. expect(baseProps.getBaseFormEndpoint).toHaveBeenCalledWith({
  141. externalName: MOCK_USER_MAPPING.externalName,
  142. integrationId: baseProps.integration.id,
  143. provider: baseProps.integration.provider.name.toLowerCase(),
  144. // From option2 selection
  145. userId: '2',
  146. });
  147. });
  148. expect(postResponse).toHaveBeenCalled();
  149. expect(putResponse).not.toHaveBeenCalled();
  150. });
  151. it('submits on blur when used as an inline field', async function () {
  152. render(
  153. <IntegrationExternalMappingForm
  154. isInline
  155. type="team"
  156. mapping={MOCK_TEAM_MAPPING}
  157. {...baseProps}
  158. />
  159. );
  160. expect(await screen.findByText('option1')).toBeInTheDocument();
  161. expect(baseProps.getBaseFormEndpoint).not.toHaveBeenCalled();
  162. expect(putResponse).not.toHaveBeenCalled();
  163. await userEvent.type(screen.getByRole('textbox'), 'option3');
  164. expect(await screen.findAllByText('option3')).toHaveLength(2);
  165. await userEvent.click(screen.getAllByText('option3')[1]);
  166. await waitFor(() => {
  167. expect(baseProps.getBaseFormEndpoint).toHaveBeenCalledWith({
  168. ...MOCK_TEAM_MAPPING,
  169. integrationId: baseProps.integration.id,
  170. provider: baseProps.integration.provider.name.toLowerCase(),
  171. // From option3 selection
  172. teamId: '3',
  173. });
  174. });
  175. expect(putResponse).toHaveBeenCalled();
  176. expect(postResponse).not.toHaveBeenCalled();
  177. });
  178. it('allows defaultOptions to be provided', async function () {
  179. render(
  180. <IntegrationExternalMappingForm
  181. type="user"
  182. mapping={MOCK_USER_MAPPING}
  183. defaultOptions={DEFAULT_OPTIONS.map(({id, name}) => ({value: id, label: name}))}
  184. {...baseProps}
  185. />
  186. );
  187. const sentryNameField = screen.getByText(`option${MOCK_USER_MAPPING.userId}`);
  188. // Don't query for results on load
  189. expect(await screen.findByText('option1')).toBeInTheDocument();
  190. expect(sentryNameField).toBeInTheDocument();
  191. expect(getResponse).not.toHaveBeenCalled();
  192. // Now that the user types, query for results
  193. await userEvent.type(sentryNameField, 'option2');
  194. await userEvent.click(screen.getAllByText('option2')[1]);
  195. await waitFor(() => expect(getResponse).toHaveBeenCalled());
  196. });
  197. });