integrationCodeMappings.spec.tsx 6.4 KB

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