import selectEvent from 'react-select-event'; import {GitHubIntegrationFixture} from 'sentry-fixture/githubIntegration'; import {OrganizationFixture} from 'sentry-fixture/organization'; import {ProjectFixture} from 'sentry-fixture/project'; import {RepositoryFixture} from 'sentry-fixture/repository'; import {RepositoryProjectPathConfigFixture} from 'sentry-fixture/repositoryProjectPathConfig'; import { render, renderGlobalModal, screen, userEvent, waitFor, } from 'sentry-test/reactTestingLibrary'; import ModalStore from 'sentry/stores/modalStore'; import ProjectsStore from 'sentry/stores/projectsStore'; import IntegrationCodeMappings from 'sentry/views/settings/organizationIntegrations/integrationCodeMappings'; describe('IntegrationCodeMappings', function () { const projects = [ ProjectFixture(), ProjectFixture({ id: '3', slug: 'some-project', name: 'Some Project', }), ]; const org = OrganizationFixture(); const integration = GitHubIntegrationFixture(); const repos = [ RepositoryFixture({ integrationId: integration.id, }), RepositoryFixture({ integrationId: integration.id, id: '5', name: 'example/hello-there', }), ]; const pathConfig1 = RepositoryProjectPathConfigFixture({ project: projects[0], repo: repos[0], integration, stackRoot: 'stack/root', sourceRoot: 'source/root', }); const pathConfig2 = RepositoryProjectPathConfigFixture({ project: projects[1], repo: repos[1], integration, id: '12', stackRoot: 'one/path', sourceRoot: 'another/root', }); beforeEach(() => { ModalStore.init(); ProjectsStore.loadInitialData(projects); MockApiClient.addMockResponse({ url: `/organizations/${org.slug}/code-mappings/`, body: [pathConfig1, pathConfig2], }); MockApiClient.addMockResponse({ url: `/organizations/${org.slug}/repos/`, body: repos, }); MockApiClient.addMockResponse({ url: `/organizations/${org.slug}/integrations/${integration.id}/repos/`, body: {repos: []}, }); }); afterEach(() => { // Clear the fields from the GlobalModal after every test ModalStore.reset(); ProjectsStore.reset(); MockApiClient.clearMockResponses(); }); it('shows the paths', () => { render(); for (const repo of repos) { expect(screen.getByText(repo.name)).toBeInTheDocument(); } }); it('create new config', async () => { const stackRoot = 'my/root'; const sourceRoot = 'hey/dude'; const defaultBranch = 'release'; const url = `/organizations/${org.slug}/code-mappings/`; const createMock = MockApiClient.addMockResponse({ url, method: 'POST', body: RepositoryProjectPathConfigFixture({ project: projects[1], repo: repos[1], integration, stackRoot, sourceRoot, defaultBranch, }), }); render(); const {waitForModalToHide} = renderGlobalModal(); await userEvent.click(screen.getByRole('button', {name: 'Add Code Mapping'})); expect(screen.getByRole('dialog')).toBeInTheDocument(); await selectEvent.select(screen.getByText('Choose Sentry project'), projects[1].slug); await selectEvent.select(screen.getByText('Choose repo'), repos[1].name); await userEvent.type( screen.getByRole('textbox', {name: 'Stack Trace Root'}), stackRoot ); await userEvent.type( screen.getByRole('textbox', {name: 'Source Code Root'}), sourceRoot ); await userEvent.clear(screen.getByRole('textbox', {name: 'Branch'})); await userEvent.type(screen.getByRole('textbox', {name: 'Branch'}), defaultBranch); await userEvent.click(screen.getByRole('button', {name: 'Save Changes'})); await waitForModalToHide(); expect(createMock).toHaveBeenCalledWith( url, expect.objectContaining({ data: expect.objectContaining({ projectId: projects[1].id, repositoryId: repos[1].id, stackRoot, sourceRoot, defaultBranch, integrationId: integration.id, }), }) ); }); it('edit existing config', async () => { const stackRoot = 'new/root'; const sourceRoot = 'source/root'; const defaultBranch = 'master'; const url = `/organizations/${org.slug}/code-mappings/${pathConfig1.id}/`; const editMock = MockApiClient.addMockResponse({ url, method: 'PUT', body: RepositoryProjectPathConfigFixture({ project: projects[0], repo: repos[0], integration, stackRoot, sourceRoot, defaultBranch, }), }); render(); const {waitForModalToHide} = renderGlobalModal(); await userEvent.click(screen.getAllByRole('button', {name: 'edit'})[0]); await userEvent.clear(screen.getByRole('textbox', {name: 'Stack Trace Root'})); await userEvent.type( screen.getByRole('textbox', {name: 'Stack Trace Root'}), stackRoot ); await userEvent.click(screen.getByRole('button', {name: 'Save Changes'})); await waitForModalToHide(); expect(editMock).toHaveBeenCalledWith( url, expect.objectContaining({ data: expect.objectContaining({ defaultBranch, projectId: '2', repositoryId: '4', sourceRoot, stackRoot, }), }) ); }); it('switches default branch to the repo defaultBranch', async () => { MockApiClient.addMockResponse({ url: `/organizations/${org.slug}/integrations/${integration.id}/repos/`, body: { repos: [ { id: repos[0].id, identifier: repos[1].name, defaultBranch: 'main', }, ], }, }); render(); renderGlobalModal(); await userEvent.click(screen.getByRole('button', {name: 'Add Code Mapping'})); expect(screen.getByRole('textbox', {name: 'Branch'})).toHaveValue('master'); await selectEvent.select(screen.getByText('Choose repo'), repos[1].name); await waitFor(() => { expect(screen.getByRole('textbox', {name: 'Branch'})).toHaveValue('main'); }); }); });