integrationCodeMappings.spec.tsx 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. import selectEvent from 'react-select-event';
  2. import {Organization} from 'sentry-fixture/organization';
  3. import {Repository} from 'sentry-fixture/repository';
  4. import {RepositoryProjectPathConfig} from 'sentry-fixture/repositoryProjectPathConfig';
  5. import {
  6. render,
  7. renderGlobalModal,
  8. screen,
  9. userEvent,
  10. waitFor,
  11. } from 'sentry-test/reactTestingLibrary';
  12. import ModalStore from 'sentry/stores/modalStore';
  13. import ProjectsStore from 'sentry/stores/projectsStore';
  14. import IntegrationCodeMappings from 'sentry/views/settings/organizationIntegrations/integrationCodeMappings';
  15. describe('IntegrationCodeMappings', function () {
  16. const projects = [
  17. TestStubs.Project(),
  18. TestStubs.Project({
  19. id: '3',
  20. slug: 'some-project',
  21. name: 'Some Project',
  22. }),
  23. ];
  24. const org = Organization();
  25. const integration = TestStubs.GitHubIntegration();
  26. const repos = [
  27. Repository({
  28. integrationId: integration.id,
  29. }),
  30. Repository({
  31. integrationId: integration.id,
  32. id: '5',
  33. name: 'example/hello-there',
  34. }),
  35. ];
  36. const pathConfig1 = RepositoryProjectPathConfig({
  37. project: projects[0],
  38. repo: repos[0],
  39. integration,
  40. stackRoot: 'stack/root',
  41. sourceRoot: 'source/root',
  42. });
  43. const pathConfig2 = RepositoryProjectPathConfig({
  44. project: projects[1],
  45. repo: repos[1],
  46. integration,
  47. id: '12',
  48. stackRoot: 'one/path',
  49. sourceRoot: 'another/root',
  50. });
  51. beforeEach(() => {
  52. ModalStore.init();
  53. ProjectsStore.loadInitialData(projects);
  54. MockApiClient.addMockResponse({
  55. url: `/organizations/${org.slug}/code-mappings/`,
  56. body: [pathConfig1, pathConfig2],
  57. });
  58. MockApiClient.addMockResponse({
  59. url: `/organizations/${org.slug}/repos/`,
  60. body: repos,
  61. });
  62. MockApiClient.addMockResponse({
  63. url: `/organizations/${org.slug}/integrations/${integration.id}/repos/`,
  64. body: {repos: []},
  65. });
  66. });
  67. afterEach(() => {
  68. // Clear the fields from the GlobalModal after every test
  69. ModalStore.reset();
  70. ProjectsStore.reset();
  71. MockApiClient.clearMockResponses();
  72. });
  73. it('shows the paths', () => {
  74. render(<IntegrationCodeMappings organization={org} integration={integration} />);
  75. for (const repo of repos) {
  76. expect(screen.getByText(repo.name)).toBeInTheDocument();
  77. }
  78. });
  79. it('create new config', async () => {
  80. const stackRoot = 'my/root';
  81. const sourceRoot = 'hey/dude';
  82. const defaultBranch = 'release';
  83. const url = `/organizations/${org.slug}/code-mappings/`;
  84. const createMock = MockApiClient.addMockResponse({
  85. url,
  86. method: 'POST',
  87. body: RepositoryProjectPathConfig({
  88. project: projects[1],
  89. repo: repos[1],
  90. integration,
  91. stackRoot,
  92. sourceRoot,
  93. defaultBranch,
  94. }),
  95. });
  96. render(<IntegrationCodeMappings organization={org} integration={integration} />);
  97. const {waitForModalToHide} = renderGlobalModal();
  98. await userEvent.click(screen.getByRole('button', {name: 'Add Code Mapping'}));
  99. expect(screen.getByRole('dialog')).toBeInTheDocument();
  100. await selectEvent.select(screen.getByText('Choose Sentry project'), projects[1].slug);
  101. await selectEvent.select(screen.getByText('Choose repo'), repos[1].name);
  102. await userEvent.type(
  103. screen.getByRole('textbox', {name: 'Stack Trace Root'}),
  104. stackRoot
  105. );
  106. await userEvent.type(
  107. screen.getByRole('textbox', {name: 'Source Code Root'}),
  108. sourceRoot
  109. );
  110. await userEvent.clear(screen.getByRole('textbox', {name: 'Branch'}));
  111. await userEvent.type(screen.getByRole('textbox', {name: 'Branch'}), defaultBranch);
  112. await userEvent.click(screen.getByRole('button', {name: 'Save Changes'}));
  113. await waitForModalToHide();
  114. expect(createMock).toHaveBeenCalledWith(
  115. url,
  116. expect.objectContaining({
  117. data: expect.objectContaining({
  118. projectId: projects[1].id,
  119. repositoryId: repos[1].id,
  120. stackRoot,
  121. sourceRoot,
  122. defaultBranch,
  123. integrationId: integration.id,
  124. }),
  125. })
  126. );
  127. });
  128. it('edit existing config', async () => {
  129. const stackRoot = 'new/root';
  130. const sourceRoot = 'source/root';
  131. const defaultBranch = 'master';
  132. const url = `/organizations/${org.slug}/code-mappings/${pathConfig1.id}/`;
  133. const editMock = MockApiClient.addMockResponse({
  134. url,
  135. method: 'PUT',
  136. body: RepositoryProjectPathConfig({
  137. project: projects[0],
  138. repo: repos[0],
  139. integration,
  140. stackRoot,
  141. sourceRoot,
  142. defaultBranch,
  143. }),
  144. });
  145. render(<IntegrationCodeMappings organization={org} integration={integration} />);
  146. const {waitForModalToHide} = renderGlobalModal();
  147. await userEvent.click(screen.getAllByRole('button', {name: 'edit'})[0]);
  148. await userEvent.clear(screen.getByRole('textbox', {name: 'Stack Trace Root'}));
  149. await userEvent.type(
  150. screen.getByRole('textbox', {name: 'Stack Trace Root'}),
  151. stackRoot
  152. );
  153. await userEvent.click(screen.getByRole('button', {name: 'Save Changes'}));
  154. await waitForModalToHide();
  155. expect(editMock).toHaveBeenCalledWith(
  156. url,
  157. expect.objectContaining({
  158. data: expect.objectContaining({
  159. defaultBranch,
  160. projectId: '2',
  161. repositoryId: '4',
  162. sourceRoot,
  163. stackRoot,
  164. }),
  165. })
  166. );
  167. });
  168. it('switches default branch to the repo defaultBranch', async () => {
  169. MockApiClient.addMockResponse({
  170. url: `/organizations/${org.slug}/integrations/${integration.id}/repos/`,
  171. body: {
  172. repos: [
  173. {
  174. id: repos[0].id,
  175. identifier: repos[1].name,
  176. defaultBranch: 'main',
  177. },
  178. ],
  179. },
  180. });
  181. render(<IntegrationCodeMappings organization={org} integration={integration} />);
  182. renderGlobalModal();
  183. await userEvent.click(screen.getByRole('button', {name: 'Add Code Mapping'}));
  184. expect(screen.getByRole('textbox', {name: 'Branch'})).toHaveValue('master');
  185. await selectEvent.select(screen.getByText('Choose repo'), repos[1].name);
  186. await waitFor(() => {
  187. expect(screen.getByRole('textbox', {name: 'Branch'})).toHaveValue('main');
  188. });
  189. });
  190. });