import {GitHubIntegrationFixture} from 'sentry-fixture/githubIntegration'; import {OrganizationFixture} from 'sentry-fixture/organization'; import {ProjectFixture} from 'sentry-fixture/project'; import {initializeOrg} from 'sentry-test/initializeOrg'; import { act, render, renderGlobalModal, screen, userEvent, waitFor, within, } from 'sentry-test/reactTestingLibrary'; import ConfigStore from 'sentry/stores/configStore'; import OrganizationsStore from 'sentry/stores/organizationsStore'; import ProjectsStore from 'sentry/stores/projectsStore'; import type {Config} from 'sentry/types/system'; import {trackAnalytics} from 'sentry/utils/analytics'; import {browserHistory} from 'sentry/utils/browserHistory'; import OrganizationGeneralSettings from 'sentry/views/settings/organizationGeneralSettings'; jest.mock('sentry/utils/analytics'); describe('OrganizationGeneralSettings', function () { const ENDPOINT = '/organizations/org-slug/'; const {organization, router} = initializeOrg(); let configState: Config; const defaultProps = { organization, router, location: router.location, params: {orgId: organization.slug}, routes: router.routes, route: {}, routeParams: router.params, }; beforeEach(function () { configState = ConfigStore.getState(); OrganizationsStore.addOrReplace(organization); MockApiClient.addMockResponse({ url: `/organizations/${organization.slug}/auth-provider/`, method: 'GET', }); MockApiClient.addMockResponse({ url: `/organizations/${organization.slug}/integrations/?provider_key=github`, method: 'GET', body: [GitHubIntegrationFixture()], }); }); afterEach(function () { act(function () { ConfigStore.loadInitialData(configState); }); }); it('can enable "early adopter"', async function () { render(); const mock = MockApiClient.addMockResponse({ url: ENDPOINT, method: 'PUT', }); await userEvent.click(screen.getByRole('checkbox', {name: /early adopter/i})); await waitFor(() => { expect(mock).toHaveBeenCalledWith( ENDPOINT, expect.objectContaining({ data: {isEarlyAdopter: true}, }) ); }); }); it('can enable "codecov access"', async function () { const organizationWithCodecovFeature = OrganizationFixture({ features: ['codecov-integration'], codecovAccess: false, }); render(, { organization: organizationWithCodecovFeature, }); const mock = MockApiClient.addMockResponse({ url: ENDPOINT, method: 'PUT', }); await userEvent.click( screen.getByRole('checkbox', {name: /Enable Code Coverage Insights/i}) ); await waitFor(() => { expect(mock).toHaveBeenCalledWith( ENDPOINT, expect.objectContaining({ data: {codecovAccess: true}, }) ); }); expect(trackAnalytics).toHaveBeenCalled(); }); it('changes org slug and redirects to new slug', async function () { render(); const mock = MockApiClient.addMockResponse({ url: ENDPOINT, method: 'PUT', body: {...organization, slug: 'new-slug'}, }); await userEvent.clear(screen.getByRole('textbox', {name: /slug/i})); await userEvent.type(screen.getByRole('textbox', {name: /slug/i}), 'new-slug'); await userEvent.click(screen.getByLabelText('Save')); await waitFor(() => { expect(mock).toHaveBeenCalledWith( ENDPOINT, expect.objectContaining({ data: {slug: 'new-slug'}, }) ); expect(browserHistory.replace).toHaveBeenCalledWith('/settings/new-slug/'); }); }); it('changes org slug and redirects to new customer-domain', async function () { ConfigStore.set('features', new Set(['system:multi-region'])); const org = OrganizationFixture(); const updateMock = MockApiClient.addMockResponse({ url: `/organizations/${organization.slug}/`, method: 'PUT', body: {...org, slug: 'acme', links: {organizationUrl: 'https://acme.sentry.io'}}, }); render(, {organization: org}); const input = screen.getByRole('textbox', {name: /slug/i}); await userEvent.clear(input); await userEvent.type(input, 'acme'); await userEvent.click(screen.getByLabelText('Save')); await waitFor(() => { expect(updateMock).toHaveBeenCalledWith( '/organizations/org-slug/', expect.objectContaining({ data: { slug: 'acme', }, }) ); expect(window.location.replace).toHaveBeenCalledWith( 'https://acme.sentry.io/settings/organization/' ); }); }); it('disables the entire form if user does not have write access', function () { const readOnlyOrg = OrganizationFixture({access: ['org:read']}); render(, { organization: readOnlyOrg, }); const formElements = [ ...screen.getAllByRole('textbox'), ...screen.getAllByRole('button'), ...screen.getAllByRole('checkbox'), ]; for (const formElement of formElements) { expect(formElement).toBeDisabled(); } expect( screen.getByText( 'These settings can only be edited by users with the organization owner or manager role.' ) ).toBeInTheDocument(); }); it('does not have remove organization button without org:admin permission', function () { render(, { organization: OrganizationFixture({ access: ['org:write'], }), }); expect( screen.queryByRole('button', {name: /remove organization/i}) ).not.toBeInTheDocument(); }); it('can remove organization when org admin', async function () { act(() => ProjectsStore.loadInitialData([ProjectFixture({slug: 'project'})])); render(, { organization: OrganizationFixture({access: ['org:admin']}), }); renderGlobalModal(); const mock = MockApiClient.addMockResponse({ url: ENDPOINT, method: 'DELETE', }); await userEvent.click(screen.getByRole('button', {name: /remove organization/i})); const modal = screen.getByRole('dialog'); expect( within(modal).getByText('This will also remove the following associated projects:') ).toBeInTheDocument(); expect(within(modal).getByText('project')).toBeInTheDocument(); await userEvent.click( within(modal).getByRole('button', {name: /remove organization/i}) ); await waitFor(() => { expect(mock).toHaveBeenCalledWith( ENDPOINT, expect.objectContaining({ method: 'DELETE', }) ); }); }); });