import {mountWithTheme} from 'sentry-test/enzyme'; import {mountGlobalModal} from 'sentry-test/modal'; import {act} from 'sentry-test/reactTestingLibrary'; import * as modals from 'sentry/actionCreators/modal'; import TeamStore from 'sentry/stores/teamStore'; import App from 'sentry/views/app'; import ProjectTeams from 'sentry/views/settings/project/projectTeams'; jest.unmock('sentry/actionCreators/modal'); describe('ProjectTeams', function () { let org; let project; const team1 = TestStubs.Team(); const team2 = TestStubs.Team({ id: '2', slug: 'team-slug-2', name: 'Team Name 2', hasAccess: true, }); beforeEach(function () { jest.spyOn(modals, 'openCreateTeamModal'); org = TestStubs.Organization(); project = TestStubs.ProjectDetails(); act(() => void TeamStore.loadInitialData([team1, team2])); MockApiClient.addMockResponse({ url: `/projects/${org.slug}/${project.slug}/`, method: 'GET', body: project, }); MockApiClient.addMockResponse({ url: `/projects/${org.slug}/${project.slug}/teams/`, method: 'GET', body: [team1], }); MockApiClient.addMockResponse({ url: `/organizations/${org.slug}/teams/`, method: 'GET', body: [team1, team2], }); }); afterEach(function () { MockApiClient.clearMockResponses(); modals.openCreateTeamModal.mockRestore(); }); it('renders', function () { const wrapper = mountWithTheme( ); // Wait for team list to fetch. wrapper.update(); expect(wrapper).toSnapshot(); }); it('can remove a team from project', async function () { MockApiClient.addMockResponse({ url: `/projects/${org.slug}/${project.slug}/teams/`, method: 'GET', body: [team1, team2], }); const endpoint = `/projects/${org.slug}/${project.slug}/teams/${team1.slug}/`; const mock = MockApiClient.addMockResponse({ url: endpoint, method: 'DELETE', statusCode: 200, }); const endpoint2 = `/projects/${org.slug}/${project.slug}/teams/${team2.slug}/`; const mock2 = MockApiClient.addMockResponse({ url: endpoint2, method: 'DELETE', statusCode: 200, }); const wrapper = mountWithTheme( ); // Wait for team list to fetch. wrapper.update(); expect(mock).not.toHaveBeenCalled(); // Click "Remove" wrapper.find('PanelBody Button').first().simulate('click'); expect(mock).toHaveBeenCalledWith( endpoint, expect.objectContaining({ method: 'DELETE', }) ); await tick(); // Remove second team wrapper.update().find('PanelBody Button').first().simulate('click'); // Modal opens because this is the last team in project // Click confirm const modal = await mountGlobalModal(); modal.find('Button[priority="primary"]').simulate('click'); expect(mock2).toHaveBeenCalledWith( endpoint2, expect.objectContaining({ method: 'DELETE', }) ); }); it('removes team from project when project team is not in org list', async function () { MockApiClient.clearMockResponses(); MockApiClient.addMockResponse({ url: `/projects/${org.slug}/${project.slug}/teams/`, method: 'GET', body: [team1, team2], }); const endpoint = `/projects/${org.slug}/${project.slug}/teams/${team1.slug}/`; const mock = MockApiClient.addMockResponse({ url: endpoint, method: 'DELETE', }); const endpoint2 = `/projects/${org.slug}/${project.slug}/teams/${team2.slug}/`; const mock2 = MockApiClient.addMockResponse({ url: endpoint2, method: 'DELETE', }); MockApiClient.addMockResponse({ url: `/organizations/${org.slug}/teams/`, method: 'GET', body: [ TestStubs.Team({ id: '3', slug: 'team-slug-3', name: 'Team Name 3', hasAccess: true, }), ], }); const wrapper = mountWithTheme( ); // Wait for team list to fetch. wrapper.update(); expect(mock).not.toHaveBeenCalled(); // Click "Remove" wrapper.find('PanelBody Button').first().simulate('click'); expect(mock).toHaveBeenCalledWith( endpoint, expect.objectContaining({ method: 'DELETE', }) ); await tick(); // Remove second team wrapper.update().find('PanelBody Button').first().simulate('click'); // Modal opens because this is the last team in project // Click confirm const modal = await mountGlobalModal(); modal.find('Button[priority="primary"]').simulate('click'); expect(mock2).toHaveBeenCalledWith( endpoint2, expect.objectContaining({ method: 'DELETE', }) ); }); it('can associate a team with project', function () { const endpoint = `/projects/${org.slug}/${project.slug}/teams/${team2.slug}/`; const mock = MockApiClient.addMockResponse({ url: endpoint, method: 'POST', statusCode: 200, }); const wrapper = mountWithTheme( ); // Wait for team list to fetch. wrapper.update(); expect(mock).not.toHaveBeenCalled(); // open dropdown wrapper.find('DropdownButton').simulate('click'); // click a team const el = wrapper.find('AutoCompleteItem').first(); el.simulate('click'); expect(mock).toHaveBeenCalledWith( endpoint, expect.objectContaining({ method: 'POST', }) ); }); it('creates a new team adds it to current project using the "create team modal" in dropdown', async function () { MockApiClient.addMockResponse({ url: '/internal/health/', body: {}, }); MockApiClient.addMockResponse({ url: '/assistant/', body: {}, }); MockApiClient.addMockResponse({ url: '/organizations/', body: [org], }); const addTeamToProject = MockApiClient.addMockResponse({ url: `/projects/${org.slug}/${project.slug}/teams/new-team/`, method: 'POST', }); const createTeam = MockApiClient.addMockResponse({ url: `/organizations/${org.slug}/teams/`, method: 'POST', body: {slug: 'new-team'}, }); const wrapper = mountWithTheme( ); // Wait for team list to fetch. wrapper.update(); // Open the dropdown wrapper.find('TeamSelect DropdownButton').simulate('click'); // Click "Create Team" inside of dropdown wrapper.find('TeamSelect StyledCreateTeamLink').simulate('click'); // action creator to open "create team modal" is called expect(modals.openCreateTeamModal).toHaveBeenCalledWith( expect.objectContaining({ project: expect.objectContaining({ slug: project.slug, }), organization: expect.objectContaining({ slug: org.slug, }), }) ); // Two ticks are required await act(tick); await act(tick); wrapper.update(); wrapper.find('input[name="slug"]').simulate('change', {target: {value: 'new-team'}}); wrapper.find('[data-test-id="create-team-form"] form').simulate('submit'); expect(createTeam).toHaveBeenCalledTimes(1); expect(createTeam).toHaveBeenCalledWith( '/organizations/org-slug/teams/', expect.objectContaining({ data: {slug: 'new-team'}, }) ); await tick(); expect(addTeamToProject).toHaveBeenCalledTimes(1); expect(addTeamToProject).toHaveBeenCalledWith( '/projects/org-slug/project-slug/teams/new-team/', expect.anything() ); }); });