From f61d654f7d74036f50f2a1ca6b380a437ec911a2 Mon Sep 17 00:00:00 2001 From: Julien Lancelot Date: Mon, 15 Oct 2018 11:55:35 +0200 Subject: [PATCH] SONAR-11321 Create organization from GitHub organization or BitBucket team * Create api/alm_integration/show_organization and handle only GitHub installation * Add import from ALM tab in Create Org page * Do not show error while validating detail input * Add step to create organization from ALM * Display a warning if the installation id was not found * Add Alm link to remote organization in org context * Create GET api/alm_integration/show_app_info --- .../org/sonar/db/alm/AlmAppInstallDao.java | 5 + .../org/sonar/db/alm/AlmAppInstallMapper.java | 3 + .../org/sonar/db/alm/AlmAppInstallMapper.xml | 10 + .../sonar/db/alm/AlmAppInstallDaoTest.java | 13 +- .../server/user/AbstractUserSession.java | 20 +- .../org/sonar/server/user/UserSession.java | 20 +- .../src/main/js/api/alm-integration.ts | 16 +- .../src/main/js/api/organizations.ts | 6 +- .../src/main/js/app/components/Landing.tsx | 3 +- .../main/js/app/components/StartupModal.tsx | 3 +- .../nav/component/ComponentNavMeta.tsx | 2 +- .../app/components/nav/global/GlobalNav.tsx | 3 +- .../components/nav/global/GlobalNavMenu.tsx | 3 +- .../components/nav/global/GlobalNavPlus.tsx | 9 +- .../components/nav/global/GlobalNavUser.tsx | 3 +- .../main/js/app/styles/components/menu.css | 2 +- .../src/main/js/app/styles/init/forms.css | 6 +- .../src/main/js/app/styles/init/icons.css | 4 +- .../src/main/js/app/styles/init/misc.css | 5 + .../src/main/js/app/styles/sonarcloud.css | 36 --- server/sonar-web/src/main/js/app/types.ts | 14 +- .../js/apps/about/sonarcloud/AsAService.tsx | 2 +- .../js/apps/about/sonarcloud/AzureDevOps.tsx | 2 +- .../apps/about/sonarcloud/BranchAnalysis.tsx | 2 +- .../main/js/apps/about/sonarcloud/Contact.tsx | 3 +- .../main/js/apps/about/sonarcloud/SQHome.tsx | 2 +- .../about/sonarcloud/SonarLintIntegration.tsx | 2 +- .../components/MeasureContent.tsx | 2 +- .../organization/AutoOrganizationCreate.tsx | 104 ++++++++ .../ChooseRemoteOrganizationStep.tsx | 76 ++++++ .../organization/CreateOrganization.tsx | 222 +++++++++------- .../organization/ManualOrganizationCreate.tsx | 133 ++++++++++ .../organization/OrganizationDetailsInput.tsx | 5 +- .../organization/OrganizationDetailsStep.tsx | 38 ++- .../__tests__/AutoOrganizationCreate-test.tsx | 74 ++++++ .../ChooseRemoteOrganizationStep-test.tsx | 45 ++++ .../__tests__/CreateOrganization-test.tsx | 122 +++++---- .../ManualOrganizationCreate-test.tsx | 86 +++++++ .../OrganizationDetailsInput-test.tsx | 1 + .../OrganizationDetailsStep-test.tsx | 65 +++-- .../AutoOrganizationCreate-test.tsx.snap | 58 +++++ ...ChooseRemoteOrganizationStep-test.tsx.snap | 60 +++++ .../CreateOrganization-test.tsx.snap | 241 +++++++++++++++--- .../ManualOrganizationCreate-test.tsx.snap | 74 ++++++ .../OrganizationDetailsInput-test.tsx.snap | 4 +- .../OrganizationDetailsStep-test.tsx.snap | 5 + .../organization/__tests__/utils-test.tsx | 32 +++ .../main/js/apps/create/organization/utils.ts | 16 ++ .../apps/create/organization/whenLoggedIn.tsx | 3 +- .../js/apps/issues/IssuesPageSelector.tsx | 3 +- .../issues/components/BulkChangeModal.tsx | 3 +- .../OrganizationAccessContainer.tsx | 3 +- .../OrganizationNavigationHeader.tsx | 17 ++ .../OrganizationNavigationHeader-test.tsx | 13 + ...OrganizationNavigationHeader-test.tsx.snap | 37 +++ .../components/SonarCloudEmptyOverview.tsx | 3 +- .../portfolio/components/Subscription.tsx | 3 +- .../apps/projects/components/AllProjects.tsx | 7 +- .../components/DefaultPageSelector.tsx | 3 +- .../projects/components/EmptyInstance.tsx | 3 +- .../projects/components/FavoriteFilter.tsx | 3 +- .../apps/projects/components/PageHeader.tsx | 3 +- .../projects/create/CreateProjectPage.tsx | 77 +++--- .../__tests__/CreateProjectPage-test.tsx | 7 +- .../CreateProjectPage-test.tsx.snap | 116 ++++----- .../tutorials/onboarding/OnboardingModal.tsx | 3 +- .../projectOnboarding/ProjectOnboarding.tsx | 3 +- .../js/components/controls/HomePageSelect.tsx | 3 +- .../src/main/js/components/controls/Tabs.css | 60 +++++ .../src/main/js/components/controls/Tabs.tsx | 77 ++++++ .../controls/__tests__/Tabs-test.tsx | 75 ++++++ .../__snapshots__/Tabs-test.tsx.snap | 52 ++++ .../issue/popups/SetAssigneePopup.tsx | 3 +- .../src/main/js/helpers/almIntegrations.ts | 8 + .../src/main/js/helpers/organizations.ts | 3 +- server/sonar-web/src/main/js/helpers/users.ts | 31 +++ server/sonar-web/src/main/js/store/users.ts | 14 +- .../resources/org/sonar/l10n/core.properties | 12 +- 78 files changed, 1892 insertions(+), 418 deletions(-) create mode 100644 server/sonar-web/src/main/js/apps/create/organization/AutoOrganizationCreate.tsx create mode 100644 server/sonar-web/src/main/js/apps/create/organization/ChooseRemoteOrganizationStep.tsx create mode 100644 server/sonar-web/src/main/js/apps/create/organization/ManualOrganizationCreate.tsx create mode 100644 server/sonar-web/src/main/js/apps/create/organization/__tests__/AutoOrganizationCreate-test.tsx create mode 100644 server/sonar-web/src/main/js/apps/create/organization/__tests__/ChooseRemoteOrganizationStep-test.tsx create mode 100644 server/sonar-web/src/main/js/apps/create/organization/__tests__/ManualOrganizationCreate-test.tsx create mode 100644 server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/AutoOrganizationCreate-test.tsx.snap create mode 100644 server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/ChooseRemoteOrganizationStep-test.tsx.snap create mode 100644 server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/ManualOrganizationCreate-test.tsx.snap create mode 100644 server/sonar-web/src/main/js/apps/create/organization/__tests__/utils-test.tsx create mode 100644 server/sonar-web/src/main/js/components/controls/Tabs.css create mode 100644 server/sonar-web/src/main/js/components/controls/Tabs.tsx create mode 100644 server/sonar-web/src/main/js/components/controls/__tests__/Tabs-test.tsx create mode 100644 server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/Tabs-test.tsx.snap create mode 100644 server/sonar-web/src/main/js/helpers/users.ts diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/alm/AlmAppInstallDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/alm/AlmAppInstallDao.java index 03361e5b586..04302fe6f06 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/alm/AlmAppInstallDao.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/alm/AlmAppInstallDao.java @@ -52,6 +52,11 @@ public class AlmAppInstallDao implements Dao { return Optional.ofNullable(mapper.selectByOwner(alm.getId(), ownerId)); } + public Optional getOwerId(DbSession dbSession, ALM alm, String installationId) { + AlmAppInstallMapper mapper = getMapper(dbSession); + return Optional.ofNullable(mapper.selectOwnerId(alm.getId(), installationId)); + } + public List findAllWithNoOwnerType(DbSession dbSession) { return getMapper(dbSession).selectAllWithNoOwnerType(); } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/alm/AlmAppInstallMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/alm/AlmAppInstallMapper.java index 809a054ca26..cb979c2217f 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/alm/AlmAppInstallMapper.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/alm/AlmAppInstallMapper.java @@ -29,6 +29,9 @@ public interface AlmAppInstallMapper { @CheckForNull AlmAppInstallDto selectByOwner(@Param("almId") String almId, @Param("ownerId") String ownerId); + @CheckForNull + String selectOwnerId(@Param("almId") String almId, @Param("installId") String installId); + List selectAllWithNoOwnerType(); void insert(@Param("uuid") String uuid, @Param("almId") String almId, @Param("ownerId") String ownerId, diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/alm/AlmAppInstallMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/alm/AlmAppInstallMapper.xml index 2454efde08c..d96a33dfe13 100644 --- a/server/sonar-db-dao/src/main/resources/org/sonar/db/alm/AlmAppInstallMapper.xml +++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/alm/AlmAppInstallMapper.xml @@ -22,6 +22,16 @@ and owner_id = #{ownerId, jdbcType=VARCHAR} + + } + {props => ( +
+ + {getHostUrl().replace(/https*:\/\//, '') + '/organizations/'} + + +
+ )}
@@ -170,7 +187,19 @@ export default class OrganizationDetailsStep extends React.PureComponent - {props => } + {props => ( + <> + {values.avatar && ( + + )} + + + )}
@@ -199,7 +228,7 @@ export default class OrganizationDetailsStep extends React.PureComponent
- {translate('continue')} + {this.props.submitText}
); @@ -208,6 +237,7 @@ export default class OrganizationDetailsStep extends React.PureComponent { return (
+ {this.props.description} initialValues={this.getInitialValues()} isInitialValid={this.props.organization !== undefined} diff --git a/server/sonar-web/src/main/js/apps/create/organization/__tests__/AutoOrganizationCreate-test.tsx b/server/sonar-web/src/main/js/apps/create/organization/__tests__/AutoOrganizationCreate-test.tsx new file mode 100644 index 00000000000..8beb62e897c --- /dev/null +++ b/server/sonar-web/src/main/js/apps/create/organization/__tests__/AutoOrganizationCreate-test.tsx @@ -0,0 +1,74 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import * as React from 'react'; +import { shallow } from 'enzyme'; +import AutoOrganizationCreate from '../AutoOrganizationCreate'; +import { waitAndUpdate } from '../../../../helpers/testUtils'; + +const organization = { + avatar: 'http://example.com/avatar', + description: 'description-foo', + key: 'key-foo', + name: 'name-foo', + url: 'http://example.com/foo' +}; + +it('should render with import org button', () => { + expect(shallowRender()).toMatchSnapshot(); +}); + +it('should render prefilled and create org', async () => { + const createOrganization = jest.fn().mockResolvedValue({ key: 'foo' }); + const onOrgCreated = jest.fn(); + const wrapper = shallowRender({ + almInstallId: 'id-foo', + almOrganization: { + ...organization, + type: 'ORGANIZATION' + }, + createOrganization, + onOrgCreated + }); + + expect(wrapper).toMatchSnapshot(); + + wrapper.find('OrganizationDetailsStep').prop('onContinue')(organization); + await waitAndUpdate(wrapper); + + expect(createOrganization).toBeCalledWith({ ...organization, installId: 'id-foo' }); + expect(onOrgCreated).toBeCalledWith('foo'); +}); + +function shallowRender(props: Partial = {}) { + return shallow( + + ); +} diff --git a/server/sonar-web/src/main/js/apps/create/organization/__tests__/ChooseRemoteOrganizationStep-test.tsx b/server/sonar-web/src/main/js/apps/create/organization/__tests__/ChooseRemoteOrganizationStep-test.tsx new file mode 100644 index 00000000000..a86ab8dfd11 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/create/organization/__tests__/ChooseRemoteOrganizationStep-test.tsx @@ -0,0 +1,45 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import * as React from 'react'; +import { shallow } from 'enzyme'; +import ChooseRemoteOrganizationStep from '../ChooseRemoteOrganizationStep'; + +it('should render', () => { + expect(shallowRender()).toMatchSnapshot(); +}); + +it('should display a warning message', () => { + expect(shallowRender({ almInstallId: 'foo' }).find('.alert-warning')).toMatchSnapshot(); +}); + +function shallowRender(props: Partial = {}) { + return shallow( + + ).dive(); +} diff --git a/server/sonar-web/src/main/js/apps/create/organization/__tests__/CreateOrganization-test.tsx b/server/sonar-web/src/main/js/apps/create/organization/__tests__/CreateOrganization-test.tsx index 6adb8f9d520..9c1e58367fb 100644 --- a/server/sonar-web/src/main/js/apps/create/organization/__tests__/CreateOrganization-test.tsx +++ b/server/sonar-web/src/main/js/apps/create/organization/__tests__/CreateOrganization-test.tsx @@ -18,9 +18,11 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import * as React from 'react'; +import { Location } from 'history'; import { shallow } from 'enzyme'; import { CreateOrganization } from '../CreateOrganization'; import { mockRouter, waitAndUpdate } from '../../../../helpers/testUtils'; +import { LoggedInUser } from '../../../../app/types'; jest.mock('../../../../api/billing', () => ({ getSubscriptionPlans: jest @@ -28,73 +30,93 @@ jest.mock('../../../../api/billing', () => ({ .mockResolvedValue([{ maxNcloc: 100000, price: 10 }, { maxNcloc: 250000, price: 75 }]) })); -const organization = { - avatar: 'http://example.com/avatar', - description: 'description-foo', - key: 'key-foo', - name: 'name-foo', - url: 'http://example.com/foo' +jest.mock('../../../../api/alm-integration', () => ({ + getAlmAppInfo: jest.fn().mockResolvedValue({ + application: { + installationUrl: 'https://alm.installation.url', + backgroundColor: 'blue', + iconPath: 'icon/path', + key: 'github', + name: 'GitHub' + } + }), + getAlmOrganization: jest.fn().mockResolvedValue({ + key: 'sonarsource', + name: 'SonarSource', + description: 'Continuous Code Quality', + url: 'https://www.sonarsource.com', + avatar: 'https://avatars3.githubusercontent.com/u/37629810?v=4', + type: 'ORGANIZATION' + }) +})); + +const user: LoggedInUser = { + groups: [], + isLoggedIn: true, + login: 'luke', + name: 'Skywalker', + scmAccounts: [], + showOnboardingTutorial: false }; -it('should render and create organization', async () => { - const createOrganization = jest.fn().mockResolvedValue({ key: 'foo' }); - const router = mockRouter(); - const wrapper = shallow( - // @ts-ignore avoid passing everything from WithRouterProps - - ); +it('should render with manual tab displayed', async () => { + const wrapper = shallowRender(); await waitAndUpdate(wrapper); expect(wrapper).toMatchSnapshot(); +}); - wrapper.find('OrganizationDetailsStep').prop('onContinue')(organization); +it('should preselect paid plan on manual creation', async () => { + const location = { state: { paid: true } }; + // @ts-ignore avoid passing everything from WithRouterProps + const wrapper = shallowRender({ location }); await waitAndUpdate(wrapper); - expect(wrapper).toMatchSnapshot(); + expect(wrapper.find('ManualOrganizationCreate').prop('onlyPaid')).toBe(true); +}); - wrapper.find('PlanStep').prop('onFreePlanChoose')(); +it('should render with auto tab displayed', async () => { + const wrapper = shallowRender({ currentUser: { ...user, externalProvider: 'github' } }); await waitAndUpdate(wrapper); - expect(createOrganization).toBeCalledWith(organization); - expect(router.push).toBeCalledWith({ - pathname: '/organizations/foo', - state: { justCreated: true } - }); + expect(wrapper).toMatchSnapshot(); }); -it('should preselect paid plan', async () => { - const router = mockRouter(); - const location = { state: { paid: true } }; - const wrapper = shallow( - // @ts-ignore avoid passing everything from WithRouterProps - - ); +it('should render with auto tab selected and manual disabled', async () => { + const wrapper = shallowRender({ + currentUser: { ...user, externalProvider: 'github' }, + location: { query: { installation_id: 'foo' } } as Location // eslint-disable-line camelcase + }); await waitAndUpdate(wrapper); - wrapper.find('OrganizationDetailsStep').prop('onContinue')(organization); + expect(wrapper).toMatchSnapshot(); +}); + +it('should switch tabs', async () => { + const replace = jest.fn(); + const wrapper = shallowRender({ + currentUser: { ...user, externalProvider: 'github' }, + router: mockRouter({ replace }) + }); + + replace.mockImplementation(location => { + wrapper.setProps({ location }).update(); + }); + await waitAndUpdate(wrapper); + expect(wrapper).toMatchSnapshot(); - expect(wrapper.find('PlanStep').prop('onlyPaid')).toBe(true); + (wrapper.find('Tabs').prop('onChange') as Function)('manual'); + expect(wrapper.find('ManualOrganizationCreate').exists()).toBeTruthy(); + (wrapper.find('Tabs').prop('onChange') as Function)('auto'); + expect(wrapper.find('AutoOrganizationCreate').exists()).toBeTruthy(); }); -it('should roll back after upgrade failure', async () => { - const createOrganization = jest.fn().mockResolvedValue({ key: 'foo' }); - const deleteOrganization = jest.fn().mockResolvedValue(undefined); - const router = mockRouter(); - const wrapper = shallow( +function shallowRender(props: Partial = {}) { + return shallow( ); - await waitAndUpdate(wrapper); - - wrapper.find('OrganizationDetailsStep').prop('onContinue')(organization); - await waitAndUpdate(wrapper); - - wrapper.find('PlanStep').prop('createOrganization')(); - expect(createOrganization).toBeCalledWith(organization); - - wrapper.find('PlanStep').prop('deleteOrganization')(); - expect(deleteOrganization).toBeCalledWith(organization.key); -}); +} diff --git a/server/sonar-web/src/main/js/apps/create/organization/__tests__/ManualOrganizationCreate-test.tsx b/server/sonar-web/src/main/js/apps/create/organization/__tests__/ManualOrganizationCreate-test.tsx new file mode 100644 index 00000000000..6226f8173b7 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/create/organization/__tests__/ManualOrganizationCreate-test.tsx @@ -0,0 +1,86 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import * as React from 'react'; +import { shallow } from 'enzyme'; +import ManualOrganizationCreate from '../ManualOrganizationCreate'; +import { waitAndUpdate } from '../../../../helpers/testUtils'; + +const organization = { + avatar: 'http://example.com/avatar', + description: 'description-foo', + key: 'key-foo', + name: 'name-foo', + url: 'http://example.com/foo' +}; + +it('should render and create organization', async () => { + const createOrganization = jest.fn().mockResolvedValue({ key: 'foo' }); + const onOrgCreated = jest.fn(); + const wrapper = shallowRender({ createOrganization, onOrgCreated }); + + await waitAndUpdate(wrapper); + expect(wrapper).toMatchSnapshot(); + + wrapper.find('OrganizationDetailsStep').prop('onContinue')(organization); + await waitAndUpdate(wrapper); + expect(wrapper).toMatchSnapshot(); + + wrapper.find('PlanStep').prop('onFreePlanChoose')(); + await waitAndUpdate(wrapper); + expect(createOrganization).toBeCalledWith(organization); + expect(onOrgCreated).toBeCalledWith('foo'); +}); + +it('should preselect paid plan', async () => { + const wrapper = shallowRender({ onlyPaid: true }); + + await waitAndUpdate(wrapper); + wrapper.find('OrganizationDetailsStep').prop('onContinue')(organization); + await waitAndUpdate(wrapper); + expect(wrapper.find('PlanStep').prop('onlyPaid')).toBe(true); +}); + +it('should roll back after upgrade failure', async () => { + const createOrganization = jest.fn().mockResolvedValue({ key: 'foo' }); + const deleteOrganization = jest.fn().mockResolvedValue(undefined); + const wrapper = shallowRender({ createOrganization, deleteOrganization }); + await waitAndUpdate(wrapper); + + wrapper.find('OrganizationDetailsStep').prop('onContinue')(organization); + await waitAndUpdate(wrapper); + + wrapper.find('PlanStep').prop('createOrganization')(); + expect(createOrganization).toBeCalledWith(organization); + + wrapper.find('PlanStep').prop('deleteOrganization')(); + expect(deleteOrganization).toBeCalledWith(organization.key); +}); + +function shallowRender(props: Partial = {}) { + return shallow( + + ); +} diff --git a/server/sonar-web/src/main/js/apps/create/organization/__tests__/OrganizationDetailsInput-test.tsx b/server/sonar-web/src/main/js/apps/create/organization/__tests__/OrganizationDetailsInput-test.tsx index 38629f87699..4ddeac5d913 100644 --- a/server/sonar-web/src/main/js/apps/create/organization/__tests__/OrganizationDetailsInput-test.tsx +++ b/server/sonar-web/src/main/js/apps/create/organization/__tests__/OrganizationDetailsInput-test.tsx @@ -30,6 +30,7 @@ it('should render', () => { error="This field is bad!" id="field" isSubmitting={true} + isValidating={false} label="Label" name="field" onBlur={jest.fn()} diff --git a/server/sonar-web/src/main/js/apps/create/organization/__tests__/OrganizationDetailsStep-test.tsx b/server/sonar-web/src/main/js/apps/create/organization/__tests__/OrganizationDetailsStep-test.tsx index aad43c0a619..f8748b45aec 100644 --- a/server/sonar-web/src/main/js/apps/create/organization/__tests__/OrganizationDetailsStep-test.tsx +++ b/server/sonar-web/src/main/js/apps/create/organization/__tests__/OrganizationDetailsStep-test.tsx @@ -38,6 +38,7 @@ it('should render form', () => { onContinue={jest.fn()} onOpen={jest.fn()} open={true} + submitText="continue" /> ); expect(wrapper).toMatchSnapshot(); @@ -58,22 +59,29 @@ it('should render form', () => { ).toBe(false); }); -it('should validate', () => { +it('should validate', async () => { const wrapper = shallow( ); const instance = wrapper.instance() as OrganizationDetailsStep; - expect( - instance.handleValidate({ avatar: '', description: '', name: '', key: 'foo', url: '' }) + await expect( + instance.handleValidate({ + avatar: '', + description: '', + name: '', + key: 'foo', + url: '' + }) ).resolves.toEqual({}); - expect( + await expect( instance.handleValidate({ avatar: '', description: '', @@ -81,13 +89,21 @@ it('should validate', () => { key: 'x'.repeat(256), url: '' }) - ).rejects.toEqual({ key: 'onboarding.create_organization.organization_name.error' }); + ).rejects.toEqual({ + key: 'onboarding.create_organization.organization_name.error' + }); - expect( - instance.handleValidate({ avatar: 'bla', description: '', name: '', key: 'foo', url: '' }) + await expect( + instance.handleValidate({ + avatar: 'bla', + description: '', + name: '', + key: 'foo', + url: '' + }) ).rejects.toEqual({ avatar: 'onboarding.create_organization.avatar.error' }); - expect( + await expect( instance.handleValidate({ avatar: '', description: '', @@ -95,16 +111,34 @@ it('should validate', () => { key: 'foo', url: '' }) - ).rejects.toEqual({ name: 'onboarding.create_organization.display_name.error' }); + ).rejects.toEqual({ + name: 'onboarding.create_organization.display_name.error' + }); - expect( - instance.handleValidate({ avatar: '', description: '', name: '', key: 'foo', url: 'bla' }) - ).rejects.toEqual({ url: 'onboarding.create_organization.url.error' }); + await expect( + instance.handleValidate({ + avatar: '', + description: '', + name: '', + key: 'foo', + url: 'bla' + }) + ).rejects.toEqual({ + url: 'onboarding.create_organization.url.error' + }); (getOrganization as jest.Mock).mockResolvedValue({}); - expect( - instance.handleValidate({ avatar: '', description: '', name: '', key: 'foo', url: '' }) - ).rejects.toEqual({ key: 'onboarding.create_organization.organization_name.taken' }); + await expect( + instance.handleValidate({ + avatar: '', + description: '', + name: '', + key: 'foo', + url: '' + }) + ).rejects.toEqual({ + key: 'onboarding.create_organization.organization_name.taken' + }); }); it('should render result', () => { @@ -115,6 +149,7 @@ it('should render result', () => { onOpen={jest.fn()} open={false} organization={{ avatar: '', description: '', key: 'org', name: 'Organization', url: '' }} + submitText="continue" /> ); expect(wrapper.dive()).toMatchSnapshot(); diff --git a/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/AutoOrganizationCreate-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/AutoOrganizationCreate-test.tsx.snap new file mode 100644 index 00000000000..a57042c6f50 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/AutoOrganizationCreate-test.tsx.snap @@ -0,0 +1,58 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render prefilled and create org 1`] = ` + + , + "name": + name-foo + , + } + } + /> +

+ } + finished={false} + onContinue={[Function]} + onOpen={[Function]} + open={true} + organization={ + Object { + "avatar": "http://example.com/avatar", + "description": "description-foo", + "key": "key-foo", + "name": "name-foo", + "type": "ORGANIZATION", + "url": "http://example.com/foo", + } + } + submitText="my_account.create_organization" +/> +`; + +exports[`should render with import org button 1`] = ` + +`; diff --git a/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/ChooseRemoteOrganizationStep-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/ChooseRemoteOrganizationStep-test.tsx.snap new file mode 100644 index 00000000000..ec99dad98ea --- /dev/null +++ b/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/ChooseRemoteOrganizationStep-test.tsx.snap @@ -0,0 +1,60 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should display a warning message 1`] = ` + + onboarding.create_organization.import_org_not_found +
    +
  • + onboarding.create_organization.import_org_not_found.tips_1 +
  • +
  • + onboarding.create_organization.import_org_not_found.tips_2 +
  • +
+
+`; + +exports[`should render 1`] = ` +
+
+ 1 +
+
+

+ onboarding.create_organization.import_org_details +

+
+
+
+ + onboarding.create_organization.choose_organization_button.github + +
+
+
+`; diff --git a/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/CreateOrganization-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/CreateOrganization-test.tsx.snap index 867e8c9f4a0..f2da7e6a62c 100644 --- a/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/CreateOrganization-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/CreateOrganization-test.tsx.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`should render and create organization 1`] = ` +exports[`should render with auto tab displayed 1`] = `

- - + onboarding.create_organization.import_organization.github + + beta + + , }, Object { - "maxNcloc": 250000, - "price": 75, + "disabled": false, + "key": "manual", + "node": "onboarding.create_organization.create_manually", }, ] } /> +
`; -exports[`should render and create organization 2`] = ` +exports[`should render with auto tab selected and manual disabled 1`] = `

- + onboarding.create_organization.import_organization.github + + beta + + , + }, + Object { + "disabled": true, + "key": "manual", + "node": "onboarding.create_organization.create_manually", + }, + ] + } + /> + - +
+`; + +exports[`should render with manual tab displayed 1`] = ` + + +
+
+

+ onboarding.create_organization.page.header +

+

+ , + "more": + learn_more + , + "price": "billing.price_format.10", + } + } + /> +

+
+ `; + +exports[`should switch tabs 1`] = ` + + +
+
+

+ onboarding.create_organization.page.header +

+

+ , + "more": + learn_more + , + "price": "billing.price_format.10", + } + } + /> +

+
+ + onboarding.create_organization.import_organization.github + + beta + + , + }, + Object { + "disabled": false, + "key": "manual", + "node": "onboarding.create_organization.create_manually", + }, + ] + } + /> + +
+
+`; diff --git a/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/ManualOrganizationCreate-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/ManualOrganizationCreate-test.tsx.snap new file mode 100644 index 00000000000..be548a156d6 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/ManualOrganizationCreate-test.tsx.snap @@ -0,0 +1,74 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render and create organization 1`] = ` + + + + +`; + +exports[`should render and create organization 2`] = ` + + + + +`; diff --git a/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/OrganizationDetailsInput-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/OrganizationDetailsInput-test.tsx.snap index 4142791c488..b8bd98adf5b 100644 --- a/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/OrganizationDetailsInput-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/OrganizationDetailsInput-test.tsx.snap @@ -5,7 +5,9 @@ exports[`should render 1`] = `