integrationExternalMappingForm.spec.tsx 7.3 KB

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