integrationCodeMappings.spec.tsx 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. import {GitHubIntegrationFixture} from 'sentry-fixture/githubIntegration';
  2. import {OrganizationFixture} from 'sentry-fixture/organization';
  3. import {ProjectFixture} from 'sentry-fixture/project';
  4. import {RepositoryFixture} from 'sentry-fixture/repository';
  5. import {RepositoryProjectPathConfigFixture} from 'sentry-fixture/repositoryProjectPathConfig';
  6. import {
  7. render,
  8. renderGlobalModal,
  9. screen,
  10. userEvent,
  11. waitFor,
  12. } from 'sentry-test/reactTestingLibrary';
  13. import selectEvent from 'sentry-test/selectEvent';
  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(
  103. screen.getByText('Choose Sentry project'),
  104. projects[1]!.slug
  105. );
  106. await selectEvent.select(screen.getByText('Choose repo'), repos[1]!.name);
  107. await userEvent.type(
  108. screen.getByRole('textbox', {name: 'Stack Trace Root'}),
  109. stackRoot
  110. );
  111. await userEvent.type(
  112. screen.getByRole('textbox', {name: 'Source Code Root'}),
  113. sourceRoot
  114. );
  115. await userEvent.clear(screen.getByRole('textbox', {name: 'Branch'}));
  116. await userEvent.type(screen.getByRole('textbox', {name: 'Branch'}), defaultBranch);
  117. await userEvent.click(screen.getByRole('button', {name: 'Save Changes'}));
  118. await waitForModalToHide();
  119. expect(createMock).toHaveBeenCalledWith(
  120. url,
  121. expect.objectContaining({
  122. data: expect.objectContaining({
  123. projectId: projects[1]!.id,
  124. repositoryId: repos[1]!.id,
  125. stackRoot,
  126. sourceRoot,
  127. defaultBranch,
  128. integrationId: integration.id,
  129. }),
  130. })
  131. );
  132. });
  133. it('edit existing config', async () => {
  134. const stackRoot = 'new/root';
  135. const sourceRoot = 'source/root';
  136. const defaultBranch = 'master';
  137. const url = `/organizations/${org.slug}/code-mappings/${pathConfig1.id}/`;
  138. const editMock = MockApiClient.addMockResponse({
  139. url,
  140. method: 'PUT',
  141. body: RepositoryProjectPathConfigFixture({
  142. project: projects[0]!,
  143. repo: repos[0]!,
  144. integration,
  145. stackRoot,
  146. sourceRoot,
  147. defaultBranch,
  148. }),
  149. });
  150. render(<IntegrationCodeMappings organization={org} integration={integration} />);
  151. const {waitForModalToHide} = renderGlobalModal();
  152. await userEvent.click(screen.getAllByRole('button', {name: 'edit'})[0]!);
  153. await userEvent.clear(screen.getByRole('textbox', {name: 'Stack Trace Root'}));
  154. await userEvent.type(
  155. screen.getByRole('textbox', {name: 'Stack Trace Root'}),
  156. stackRoot
  157. );
  158. await userEvent.click(screen.getByRole('button', {name: 'Save Changes'}));
  159. await waitForModalToHide();
  160. expect(editMock).toHaveBeenCalledWith(
  161. url,
  162. expect.objectContaining({
  163. data: expect.objectContaining({
  164. defaultBranch,
  165. projectId: '2',
  166. repositoryId: '4',
  167. sourceRoot,
  168. stackRoot,
  169. }),
  170. })
  171. );
  172. });
  173. it('switches default branch to the repo defaultBranch', async () => {
  174. MockApiClient.addMockResponse({
  175. url: `/organizations/${org.slug}/integrations/${integration.id}/repos/`,
  176. body: {
  177. repos: [
  178. {
  179. id: repos[0]!.id,
  180. identifier: repos[1]!.name,
  181. defaultBranch: 'main',
  182. },
  183. ],
  184. },
  185. });
  186. render(<IntegrationCodeMappings organization={org} integration={integration} />);
  187. renderGlobalModal();
  188. await userEvent.click(screen.getByRole('button', {name: 'Add Code Mapping'}));
  189. expect(screen.getByRole('textbox', {name: 'Branch'})).toHaveValue('master');
  190. await selectEvent.select(screen.getByText('Choose repo'), repos[1]!.name);
  191. await waitFor(() => {
  192. expect(screen.getByRole('textbox', {name: 'Branch'})).toHaveValue('main');
  193. });
  194. });
  195. });