integrationCodeMappings.spec.tsx 6.1 KB

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