diff options
author | Jeremy Davis <jeremy.davis@sonarsource.com> | 2020-09-22 14:20:28 +0200 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2020-09-28 20:07:23 +0000 |
commit | 71cc0592365fc7ff36694c726c1a8ca4e1fcc6cd (patch) | |
tree | 79f66251286871397c898aca6ccb050c5f102f23 /server/sonar-web/src/main/js/apps/create/organization | |
parent | 7d546a463731e1410ba34225e2e1aa5d9586a3b2 (diff) | |
download | sonarqube-71cc0592365fc7ff36694c726c1a8ca4e1fcc6cd.tar.gz sonarqube-71cc0592365fc7ff36694c726c1a8ca4e1fcc6cd.zip |
SONAR-13880 internationalize tooltips
Diffstat (limited to 'server/sonar-web/src/main/js/apps/create/organization')
34 files changed, 0 insertions, 4260 deletions
diff --git a/server/sonar-web/src/main/js/apps/create/organization/AlmApplicationInstalling.tsx b/server/sonar-web/src/main/js/apps/create/organization/AlmApplicationInstalling.tsx deleted file mode 100644 index d6b9dcebf01..00000000000 --- a/server/sonar-web/src/main/js/apps/create/organization/AlmApplicationInstalling.tsx +++ /dev/null @@ -1,43 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2020 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 DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner'; -import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n'; -import { sanitizeAlmId } from '../../../helpers/almIntegrations'; - -export default function AlmApplicationInstalling({ almKey }: { almKey?: string }) { - return ( - <DeferredSpinner - customSpinner={ - <div className="sonarcloud page page-limited"> - <div className="huge-spacer-top text-center"> - <i className="spinner" /> - <p className="big-spacer-top"> - {translateWithParameters( - 'onboarding.import_organization.installing', - almKey ? translate(sanitizeAlmId(almKey)) : 'ALM' - )} - </p> - </div> - </div> - } - /> - ); -} diff --git a/server/sonar-web/src/main/js/apps/create/organization/AutoOrganizationBind.tsx b/server/sonar-web/src/main/js/apps/create/organization/AutoOrganizationBind.tsx deleted file mode 100644 index 38d3862aa7d..00000000000 --- a/server/sonar-web/src/main/js/apps/create/organization/AutoOrganizationBind.tsx +++ /dev/null @@ -1,115 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2020 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 { Link } from 'react-router'; -import { SubmitButton } from 'sonar-ui-common/components/controls/buttons'; -import { Alert } from 'sonar-ui-common/components/ui/Alert'; -import DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner'; -import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n'; -import { isGithub } from '../../../helpers/almIntegrations'; -import OrganizationSelect from '../components/OrganizationSelect'; - -interface Props { - almKey: string; - onBindOrganization: (organization: string) => Promise<void>; - unboundOrganizations: T.Organization[]; -} - -interface State { - organization: string; - submitting: boolean; -} - -export default class AutoOrganizationBind extends React.PureComponent<Props, State> { - mounted = false; - - constructor(props: Props) { - super(props); - this.state = { organization: this.getInitialSelectedOrganization(props), submitting: false }; - } - - componentDidMount() { - this.mounted = true; - } - - componentWillUnmount() { - this.mounted = false; - } - - getInitialSelectedOrganization(props: Props) { - if (props.unboundOrganizations.length === 1) { - return props.unboundOrganizations[0].key; - } - return ''; - } - - handleChange = ({ key }: T.Organization) => { - this.setState({ organization: key }); - }; - - handleSubmit = (event: React.FormEvent<HTMLFormElement>) => { - event.preventDefault(); - const { organization } = this.state; - if (organization) { - this.setState({ submitting: true }); - this.props.onBindOrganization(organization).then(this.stopSubmitting, this.stopSubmitting); - } - }; - - stopSubmitting = () => { - if (this.mounted) { - this.setState({ submitting: false }); - } - }; - - render() { - const { almKey } = this.props; - const { organization, submitting } = this.state; - return ( - <form id="bind-organization-form" onSubmit={this.handleSubmit}> - <OrganizationSelect - onChange={this.handleChange} - organization={organization} - organizations={this.props.unboundOrganizations} - /> - {isGithub(almKey) && ( - <Alert className="abs-width-400 big-spacer-top" display="block" variant="info"> - {translateWithParameters( - 'onboarding.import_organization.bind_members_not_sync_info_x', - translate('organization', almKey) - )} - <Link - className="spacer-left" - target="_blank" - to={{ pathname: '/documentation/organizations/manage-team/' }}> - {translate('learn_more')} - </Link> - </Alert> - )} - <div className="display-flex-center big-spacer-top"> - <SubmitButton disabled={submitting || !organization}> - {translate('onboarding.import_organization.bind')} - </SubmitButton> - {submitting && <DeferredSpinner className="spacer-left" />} - </div> - </form> - ); - } -} diff --git a/server/sonar-web/src/main/js/apps/create/organization/AutoOrganizationCreate.tsx b/server/sonar-web/src/main/js/apps/create/organization/AutoOrganizationCreate.tsx deleted file mode 100644 index 1f0542b429c..00000000000 --- a/server/sonar-web/src/main/js/apps/create/organization/AutoOrganizationCreate.tsx +++ /dev/null @@ -1,215 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2020 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 { FormattedMessage } from 'react-intl'; -import { ClearButton } from 'sonar-ui-common/components/controls/buttons'; -import RadioToggle from 'sonar-ui-common/components/controls/RadioToggle'; -import { Alert } from 'sonar-ui-common/components/ui/Alert'; -import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n'; -import { getBaseUrl } from 'sonar-ui-common/helpers/urls'; -import { bindAlmOrganization } from '../../../api/alm-integration'; -import { getAlmMembersUrl, isGithub, sanitizeAlmId } from '../../../helpers/almIntegrations'; -import AutoOrganizationBind from './AutoOrganizationBind'; -import OrganizationDetailsForm from './OrganizationDetailsForm'; -import OrganizationDetailsStep from './OrganizationDetailsStep'; -import PlanStep from './PlanStep'; -import { Step } from './utils'; - -enum Filters { - Bind = 'bind', - Create = 'create' -} - -interface Props { - almApplication: T.AlmApplication; - almInstallId: string; - almOrganization: T.AlmOrganization; - className?: string; - createOrganization: ( - organization: T.Organization & { installationId?: string } - ) => Promise<string>; - handleCancelImport: () => void; - handleOrgDetailsFinish: (organization: T.Organization) => Promise<void>; - handleOrgDetailsStepOpen: () => void; - onDone: () => void; - onOrgCreated: (organization: string) => void; - onUpgradeFail: () => void; - organization?: T.Organization; - step: Step; - subscriptionPlans?: T.SubscriptionPlan[]; - unboundOrganizations: T.Organization[]; -} - -interface State { - filter?: Filters; -} - -export default class AutoOrganizationCreate extends React.PureComponent<Props, State> { - constructor(props: Props) { - super(props); - this.state = { - filter: props.unboundOrganizations.length === 0 ? Filters.Create : undefined - }; - } - - handleBindOrganization = (organization: string) => { - return bindAlmOrganization({ - organization, - installationId: this.props.almInstallId - }).then(() => this.props.onOrgCreated(organization)); - }; - - handleCreateOrganization = () => { - const { almApplication, almOrganization, organization } = this.props; - if (!organization) { - return Promise.reject(); - } - return this.props.createOrganization({ - ...organization, - alm: { - key: almApplication.key, - membersSync: true, - personal: almOrganization.personal, - url: almOrganization.almUrl - }, - installationId: this.props.almInstallId - }); - }; - - handleOptionChange = (filter: Filters) => { - this.setState({ filter }); - }; - - render() { - const { - almApplication, - almOrganization, - className, - organization, - step, - subscriptionPlans, - unboundOrganizations - } = this.props; - const { filter } = this.state; - const hasUnboundOrgs = unboundOrganizations.length > 0; - const almKey = sanitizeAlmId(almApplication.key); - return ( - <div className={className}> - <OrganizationDetailsStep - finished={organization !== undefined} - onOpen={this.props.handleOrgDetailsStepOpen} - open={step === Step.OrganizationDetails} - organization={organization} - stepTitle={translate('onboarding.import_organization.import_org_details')}> - <div className="huge-spacer-bottom"> - <p className="display-flex-center big-spacer-bottom"> - <FormattedMessage - defaultMessage={translate('onboarding.import_organization_x')} - id="onboarding.import_organization_x" - values={{ - avatar: ( - <img - alt={almApplication.name} - className="little-spacer-left" - src={`${getBaseUrl()}/images/sonarcloud/${sanitizeAlmId( - almApplication.key - )}.svg`} - width={16} - /> - ), - name: <strong>{almOrganization.name}</strong> - }} - /> - <ClearButton className="little-spacer-left" onClick={this.props.handleCancelImport} /> - </p> - - {hasUnboundOrgs && ( - <RadioToggle - name="filter" - onCheck={this.handleOptionChange} - options={[ - { - label: translate('onboarding.import_organization.create_new'), - value: Filters.Create - }, - { - label: translate('onboarding.import_organization.bind_existing'), - value: Filters.Bind - } - ]} - value={filter} - /> - )} - </div> - - {filter === Filters.Create && ( - <OrganizationDetailsForm - infoBlock={ - isGithub(almKey) && ( - <Alert className="abs-width-600 big-spacer-top" display="block" variant="info"> - <p> - {translateWithParameters( - 'onboarding.import_organization.members_sync_info_x', - translate('organization', almKey), - almOrganization.name, - translate(almKey) - )} - </p> - <a - href={getAlmMembersUrl(almApplication.key, almOrganization.almUrl)} - rel="noopener noreferrer" - target="_blank"> - {translateWithParameters( - 'organization.members.see_all_members_on_x', - translate(almKey) - )} - </a> - </Alert> - ) - } - onContinue={this.props.handleOrgDetailsFinish} - organization={almOrganization} - submitText={translate('continue')} - /> - )} - {filter === Filters.Bind && ( - <AutoOrganizationBind - almKey={almKey} - onBindOrganization={this.handleBindOrganization} - unboundOrganizations={unboundOrganizations} - /> - )} - </OrganizationDetailsStep> - - {subscriptionPlans !== undefined && filter !== Filters.Bind && ( - <PlanStep - almApplication={this.props.almApplication} - almOrganization={this.props.almOrganization} - createOrganization={this.handleCreateOrganization} - onDone={this.props.onDone} - onUpgradeFail={this.props.onUpgradeFail} - open={step === Step.Plan} - subscriptionPlans={subscriptionPlans} - /> - )} - </div> - ); - } -} diff --git a/server/sonar-web/src/main/js/apps/create/organization/CreateOrganization.tsx b/server/sonar-web/src/main/js/apps/create/organization/CreateOrganization.tsx deleted file mode 100644 index ac6a60056f8..00000000000 --- a/server/sonar-web/src/main/js/apps/create/organization/CreateOrganization.tsx +++ /dev/null @@ -1,456 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2020 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 classNames from 'classnames'; -import * as differenceInMinutes from 'date-fns/difference_in_minutes'; -import { times } from 'lodash'; -import * as React from 'react'; -import { Helmet } from 'react-helmet-async'; -import { connect } from 'react-redux'; -import { withRouter, WithRouterProps } from 'react-router'; -import Tabs from 'sonar-ui-common/components/controls/Tabs'; -import DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner'; -import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n'; -import { addWhitePageClass, removeWhitePageClass } from 'sonar-ui-common/helpers/pages'; -import { get, remove } from 'sonar-ui-common/helpers/storage'; -import { slugify } from 'sonar-ui-common/helpers/strings'; -import { - bindAlmOrganization, - getAlmAppInfo, - getAlmOrganization, - GetAlmOrganizationResponse, - listUnboundApplications -} from '../../../api/alm-integration'; -import { getSubscriptionPlans } from '../../../api/billing'; -import * as api from '../../../api/organizations'; -import A11ySkipTarget from '../../../app/components/a11y/A11ySkipTarget'; -import addGlobalSuccessMessage from '../../../app/utils/addGlobalSuccessMessage'; -import { whenLoggedIn } from '../../../components/hoc/whenLoggedIn'; -import { withUserOrganizations } from '../../../components/hoc/withUserOrganizations'; -import { hasAdvancedALMIntegration, sanitizeAlmId } from '../../../helpers/almIntegrations'; -import { getOrganizationUrl } from '../../../helpers/urls'; -import { skipOnboarding } from '../../../store/users'; -import { deleteOrganization } from '../../organizations/actions'; -import { createOrganization } from './actions'; -import AlmApplicationInstalling from './AlmApplicationInstalling'; -import AutoOrganizationCreate from './AutoOrganizationCreate'; -import ManualOrganizationCreate from './ManualOrganizationCreate'; -import RemoteOrganizationChoose from './RemoteOrganizationChoose'; -import { - BIND_ORGANIZATION_KEY, - BIND_ORGANIZATION_REDIRECT_TO_ORG_TIMESTAMP, - ORGANIZATION_IMPORT_BINDING_IN_PROGRESS_TIMESTAMP, - ORGANIZATION_IMPORT_REDIRECT_TO_PROJECT_TIMESTAMP, - parseQuery, - Query, - serializeQuery, - Step -} from './utils'; - -interface Props { - createOrganization: ( - organization: T.Organization & { installationId?: string } - ) => Promise<string>; - currentUser: T.LoggedInUser; - deleteOrganization: (key: string) => Promise<void>; - userOrganizations: T.Organization[]; - skipOnboarding: () => void; -} - -interface State { - almApplication?: T.AlmApplication; - almOrganization?: T.AlmOrganization; - almOrgLoading: boolean; - almUnboundApplications: T.AlmUnboundApplication[]; - bindingExistingOrg: boolean; - boundOrganization?: T.OrganizationBase; - loading: boolean; - organization?: T.Organization; - step: Step; - subscriptionPlans?: T.SubscriptionPlan[]; -} - -type StateWithAutoImport = State & Required<Pick<State, 'almApplication'>>; - -type TabKeys = 'auto' | 'manual'; - -interface LocationState { - tab?: TabKeys; -} - -export class CreateOrganization extends React.PureComponent<Props & WithRouterProps, State> { - mounted = false; - state: State = { - almOrgLoading: false, - almUnboundApplications: [], - bindingExistingOrg: false, - loading: true, - step: Step.OrganizationDetails - }; - - componentDidMount() { - this.mounted = true; - addWhitePageClass(); - - const query = parseQuery(this.props.location.query); - - //highjack the process for the organization settings - if ( - hasAdvancedALMIntegration(this.props.currentUser) && - query.almInstallId && - this.isStoredTimestampValid(BIND_ORGANIZATION_REDIRECT_TO_ORG_TIMESTAMP) - ) { - this.bindAndRedirectToOrganizationSettings(query.almInstallId); - } else { - const initRequests = [this.fetchSubscriptionPlans()]; - if (hasAdvancedALMIntegration(this.props.currentUser)) { - initRequests.push(this.fetchAlmApplication()); - - if (query.almInstallId) { - this.fetchAlmOrganization(query.almInstallId); - } else { - initRequests.push(this.fetchAlmUnboundApplications()); - } - } - Promise.all(initRequests).then(this.stopLoading, this.stopLoading); - } - } - - componentDidUpdate(prevProps: WithRouterProps) { - const prevQuery = parseQuery(prevProps.location.query); - const query = parseQuery(this.props.location.query); - if (this.state.almApplication && prevQuery.almInstallId !== query.almInstallId) { - if (query.almInstallId) { - this.fetchAlmOrganization(query.almInstallId); - } else { - this.setState({ almOrganization: undefined, boundOrganization: undefined, loading: true }); - this.fetchAlmUnboundApplications().then(this.stopLoading, this.stopLoading); - } - } - } - - componentWillUnmount() { - this.mounted = false; - removeWhitePageClass(); - } - - deleteOrganization = () => { - if (this.state.organization) { - this.props.deleteOrganization(this.state.organization.key); - } - }; - - fetchAlmApplication = () => { - return getAlmAppInfo().then(({ application }) => { - if (this.mounted) { - this.setState({ almApplication: application }); - } - }); - }; - - fetchAlmOrganization = (installationId: string) => { - this.setState({ almOrgLoading: true }); - return getAlmOrganization({ installationId }) - .then(({ almOrganization, boundOrganization }) => { - if (boundOrganization) { - return { almOrganization, boundOrganization }; - } - return this.setValidOrgKey(almOrganization); - }) - .then( - ({ almOrganization, boundOrganization }: GetAlmOrganizationResponse) => { - if (this.mounted) { - if ( - boundOrganization && - boundOrganization.key && - !this.isStoredTimestampValid(ORGANIZATION_IMPORT_BINDING_IN_PROGRESS_TIMESTAMP) - ) { - this.props.router.push({ - pathname: getOrganizationUrl(boundOrganization.key) - }); - } else { - this.setState({ almOrganization, almOrgLoading: false, boundOrganization }); - } - } - }, - () => { - if (this.mounted) { - this.setState({ almOrgLoading: false }); - } - } - ); - }; - - fetchAlmUnboundApplications = () => { - return listUnboundApplications().then(almUnboundApplications => { - if (this.mounted) { - this.setState({ almUnboundApplications }); - } - }); - }; - - fetchSubscriptionPlans = () => { - return getSubscriptionPlans().then(subscriptionPlans => { - if (this.mounted) { - this.setState({ subscriptionPlans }); - } - }); - }; - - handleCancelImport = () => { - this.updateUrlQuery({ almInstallId: undefined, almKey: undefined }); - }; - - handleOrgCreated = (organization: string) => { - this.props.skipOnboarding(); - if (this.isStoredTimestampValid(ORGANIZATION_IMPORT_REDIRECT_TO_PROJECT_TIMESTAMP)) { - this.props.router.push({ - pathname: '/projects/create', - state: { organization, tab: this.state.almOrganization ? 'auto' : 'manual' } - }); - } else { - this.props.router.push({ pathname: getOrganizationUrl(organization) }); - } - }; - - handleOrgDetailsFinish = (organization: T.Organization) => { - this.setState({ organization, step: Step.Plan }); - return Promise.resolve(); - }; - - handleOrgDetailsStepOpen = () => { - this.setState({ step: Step.OrganizationDetails }); - }; - - handlePlanDone = () => { - if (this.state.organization) { - this.handleOrgCreated(this.state.organization.key); - } - }; - - hasAutoImport(state: State): state is StateWithAutoImport { - return Boolean(state.almApplication); - } - - isStoredTimestampValid = (timestampKey: string) => { - const storedTimestamp = get(timestampKey); - remove(timestampKey); - return storedTimestamp && differenceInMinutes(Date.now(), Number(storedTimestamp)) < 10; - }; - - onTabChange = (tab: TabKeys) => { - this.updateUrlState({ tab }); - }; - - bindAndRedirectToOrganizationSettings(installationId: string) { - const organizationKey = get(BIND_ORGANIZATION_KEY) || ''; - remove(BIND_ORGANIZATION_KEY); - - this.setState({ bindingExistingOrg: true }); - - bindAlmOrganization({ - installationId, - organization: organizationKey - }).then( - () => { - this.props.router.push({ - pathname: `/organizations/${organizationKey}` - }); - addGlobalSuccessMessage(translate('organization.bind.success')); - }, - () => {} - ); - } - - getHeader = (bindingExistingOrg: boolean) => { - if (bindingExistingOrg) { - return translate('onboarding.binding_organization'); - } else { - return translate('onboarding.create_organization.page.header'); - } - }; - - setValidOrgKey = (almOrganization: T.AlmOrganization) => { - const key = slugify(almOrganization.key); - const keys = [key, ...times(9, i => `${key}-${i + 1}`)]; - return api - .getOrganizations({ organizations: keys.join(',') }) - .then( - ({ organizations }) => { - const availableKey = keys.find(key => !organizations.find(o => o.key === key)); - return availableKey || `${key}-${Math.ceil(Math.random() * 1000) + 10}`; - }, - () => key - ) - .then(key => { - return { almOrganization: { ...almOrganization, key } }; - }); - }; - - stopLoading = () => { - if (this.mounted) { - this.setState({ loading: false }); - } - }; - - updateUrlQuery = (query: Partial<Query> = {}) => { - this.props.router.push({ - pathname: this.props.location.pathname, - query: serializeQuery({ ...parseQuery(this.props.location.query), ...query }), - state: this.props.location.state - }); - }; - - updateUrlState = (state: Partial<LocationState> = {}) => { - this.props.router.replace({ - pathname: this.props.location.pathname, - query: this.props.location.query, - state: { ...(this.props.location.state || {}), ...state } - }); - }; - - renderContent = (almInstallId?: string) => { - const { location } = this.props; - const { state } = this; - const { organization, step, subscriptionPlans } = state; - const { tab = 'auto' } = (location.state || {}) as LocationState; - - const commonProps = { - handleOrgDetailsFinish: this.handleOrgDetailsFinish, - handleOrgDetailsStepOpen: this.handleOrgDetailsStepOpen, - onDone: this.handlePlanDone, - organization, - step, - subscriptionPlans - }; - - if (!this.hasAutoImport(state)) { - return ( - <ManualOrganizationCreate - {...commonProps} - createOrganization={this.props.createOrganization} - onUpgradeFail={this.deleteOrganization} - organization={this.state.organization} - step={this.state.step} - /> - ); - } - - const { almApplication, almOrganization, boundOrganization } = state; - - return ( - <> - <Tabs<TabKeys> - onChange={this.onTabChange} - selected={tab || 'auto'} - tabs={[ - { - key: 'auto', - node: translateWithParameters( - 'onboarding.import_organization.import_from_x', - translate(sanitizeAlmId(almApplication.key)) - ) - }, - { - key: 'manual', - node: translate('onboarding.create_organization.create_manually') - } - ]} - /> - - <ManualOrganizationCreate - {...commonProps} - className={classNames({ hidden: tab !== 'manual' && this.hasAutoImport(state) })} - createOrganization={this.props.createOrganization} - onUpgradeFail={this.deleteOrganization} - /> - - {almInstallId && almOrganization && !boundOrganization ? ( - <AutoOrganizationCreate - {...commonProps} - almApplication={almApplication} - almInstallId={almInstallId} - almOrganization={almOrganization} - className={classNames({ hidden: tab !== 'auto' })} - createOrganization={this.props.createOrganization} - handleCancelImport={this.handleCancelImport} - onOrgCreated={this.handleOrgCreated} - onUpgradeFail={this.deleteOrganization} - unboundOrganizations={this.props.userOrganizations.filter( - ({ actions = {}, alm }) => !alm && actions.admin - )} - /> - ) : ( - <RemoteOrganizationChoose - almApplication={almApplication} - almInstallId={almInstallId} - almOrganization={almOrganization} - almUnboundApplications={state.almUnboundApplications} - boundOrganization={boundOrganization} - className={classNames({ hidden: tab !== 'auto' })} - /> - )} - </> - ); - }; - - render() { - const { location } = this.props; - const query = parseQuery(location.query); - - if (this.state.almOrgLoading) { - return <AlmApplicationInstalling almKey={query.almKey} />; - } - - const { bindingExistingOrg, subscriptionPlans } = this.state; - const header = this.getHeader(bindingExistingOrg); - const startedPrice = subscriptionPlans && subscriptionPlans[0] && subscriptionPlans[0].price; - - return ( - <> - <Helmet defer={false} title={header} titleTemplate="%s" /> - <div className="page page-limited huge-spacer-top huge-spacer-bottom"> - <A11ySkipTarget anchor="create_org_main" /> - - <header className="page-header huge-spacer-bottom"> - <h1 className="page-title huge big-spacer-bottom"> - <strong>{header}</strong> - </h1> - {startedPrice !== undefined && ( - <p className="page-description"> - {translate('onboarding.create_organization.page.description')} - </p> - )} - </header> - {this.state.loading ? <DeferredSpinner /> : this.renderContent(query.almInstallId)} - </div> - </> - ); - } -} - -const mapDispatchToProps = { - createOrganization: createOrganization as any, - deleteOrganization: deleteOrganization as any, - skipOnboarding: skipOnboarding as any -}; - -export default whenLoggedIn( - withUserOrganizations(withRouter(connect(null, mapDispatchToProps)(CreateOrganization))) -); diff --git a/server/sonar-web/src/main/js/apps/create/organization/ManualOrganizationCreate.tsx b/server/sonar-web/src/main/js/apps/create/organization/ManualOrganizationCreate.tsx deleted file mode 100644 index 157acec75a5..00000000000 --- a/server/sonar-web/src/main/js/apps/create/organization/ManualOrganizationCreate.tsx +++ /dev/null @@ -1,76 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2020 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 { translate } from 'sonar-ui-common/helpers/l10n'; -import OrganizationDetailsForm from './OrganizationDetailsForm'; -import OrganizationDetailsStep from './OrganizationDetailsStep'; -import PlanStep from './PlanStep'; -import { Step } from './utils'; - -interface Props { - createOrganization: (organization: T.Organization) => Promise<string>; - className?: string; - onUpgradeFail: () => void; - handleOrgDetailsFinish: (organization: T.Organization) => Promise<void>; - handleOrgDetailsStepOpen: () => void; - onDone: () => void; - organization?: T.Organization; - step: Step; - subscriptionPlans?: T.SubscriptionPlan[]; -} - -export default class ManualOrganizationCreate extends React.PureComponent<Props> { - handleCreateOrganization = () => { - const { organization } = this.props; - if (!organization) { - return Promise.reject(); - } - return this.props.createOrganization(organization); - }; - - render() { - const { className, organization, subscriptionPlans } = this.props; - return ( - <div className={className}> - <OrganizationDetailsStep - finished={organization !== undefined} - onOpen={this.props.handleOrgDetailsStepOpen} - open={this.props.step === Step.OrganizationDetails} - organization={organization}> - <OrganizationDetailsForm - onContinue={this.props.handleOrgDetailsFinish} - organization={organization} - submitText={translate('continue')} - /> - </OrganizationDetailsStep> - - {subscriptionPlans !== undefined && ( - <PlanStep - createOrganization={this.handleCreateOrganization} - onDone={this.props.onDone} - onUpgradeFail={this.props.onUpgradeFail} - open={this.props.step === Step.Plan} - subscriptionPlans={subscriptionPlans} - /> - )} - </div> - ); - } -} diff --git a/server/sonar-web/src/main/js/apps/create/organization/OrganizationDetailsForm.tsx b/server/sonar-web/src/main/js/apps/create/organization/OrganizationDetailsForm.tsx deleted file mode 100644 index 928efd8f0cc..00000000000 --- a/server/sonar-web/src/main/js/apps/create/organization/OrganizationDetailsForm.tsx +++ /dev/null @@ -1,205 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2020 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 { ResetButtonLink, SubmitButton } from 'sonar-ui-common/components/controls/buttons'; -import DropdownIcon from 'sonar-ui-common/components/icons/DropdownIcon'; -import DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner'; -import { translate } from 'sonar-ui-common/helpers/l10n'; -import OrganizationAvatarInput from '../components/OrganizationAvatarInput'; -import OrganizationKeyInput from '../components/OrganizationKeyInput'; -import OrganizationUrlInput from '../components/OrganizationUrlInput'; - -type RequiredOrganization = Required<T.OrganizationBase>; - -interface Props { - infoBlock?: React.ReactNode; - onContinue: (organization: T.Organization) => Promise<void>; - organization?: T.Organization; - submitText: string; -} - -interface State { - additional: boolean; - avatar?: string; - description?: string; - key?: string; - name?: string; - submitting: boolean; - url?: string; -} - -type ValidState = Pick<State, Exclude<keyof State, RequiredOrganization>> & RequiredOrganization; - -export default class OrganizationDetailsForm extends React.PureComponent<Props, State> { - mounted = false; - - constructor(props: Props) { - super(props); - const { organization } = props; - this.state = { - additional: false, - avatar: (organization && organization.avatar) || '', - description: (organization && organization.description) || '', - key: (organization && organization.key) || undefined, - name: (organization && organization.name) || '', - submitting: false, - url: (organization && organization.url) || '' - }; - } - - componentDidMount() { - this.mounted = true; - } - - componentWillUnmount() { - this.mounted = false; - } - - canSubmit(state: State): state is ValidState { - return Boolean( - state.key !== undefined && - state.name !== undefined && - state.description !== undefined && - state.avatar !== undefined && - state.url !== undefined - ); - } - - handleAdditionalClick = () => { - this.setState(state => ({ additional: !state.additional })); - }; - - handleAvatarUpdate = (avatar: string | undefined) => { - this.setState({ avatar }); - }; - - handleDescriptionUpdate = (event: React.ChangeEvent<HTMLTextAreaElement>) => { - this.setState({ description: event.currentTarget.value }); - }; - - handleKeyUpdate = (key: string | undefined) => { - this.setState({ key }); - }; - - handleNameUpdate = (event: React.ChangeEvent<HTMLInputElement>) => { - this.setState({ name: event.currentTarget.value }); - }; - - handleUrlUpdate = (url: string | undefined) => { - this.setState({ url }); - }; - - handleSubmit = (event: React.FormEvent) => { - event.preventDefault(); - const { state } = this; - if (this.canSubmit(state)) { - this.setState({ submitting: true }); - this.props - .onContinue({ - avatar: state.avatar, - description: state.description, - key: state.key, - name: state.name, - url: state.url - }) - .then(this.stopSubmitting, this.stopSubmitting); - } - }; - - stopSubmitting = () => { - if (this.mounted) { - this.setState({ submitting: false }); - } - }; - - render() { - const { submitting } = this.state; - const { infoBlock } = this.props; - return ( - <form id="organization-form" onSubmit={this.handleSubmit}> - <OrganizationKeyInput initialValue={this.state.key} onChange={this.handleKeyUpdate} /> - <div className="big-spacer-top"> - <ResetButtonLink onClick={this.handleAdditionalClick}> - {translate( - this.state.additional - ? 'onboarding.create_organization.hide_additional_info' - : 'onboarding.create_organization.add_additional_info' - )} - <DropdownIcon className="little-spacer-left" turned={this.state.additional} /> - </ResetButtonLink> - </div> - <div className="js-additional-info" hidden={!this.state.additional}> - <div className="big-spacer-top"> - <label htmlFor="organization-display-name"> - <strong>{translate('onboarding.create_organization.display_name')}</strong> - </label> - <div className="little-spacer-top"> - <input - className="input-super-large text-middle" - id="organization-display-name" - maxLength={255} - onChange={this.handleNameUpdate} - type="text" - value={this.state.name} - /> - </div> - <div className="note abs-width-400"> - {translate('onboarding.create_organization.display_name.description')} - </div> - </div> - <div className="big-spacer-top"> - <OrganizationAvatarInput - initialValue={this.state.avatar} - name={this.state.name} - onChange={this.handleAvatarUpdate} - /> - </div> - <div className="big-spacer-top"> - <label htmlFor="organization-description"> - <strong>{translate('onboarding.create_organization.description')}</strong> - </label> - <div className="little-spacer-top"> - <textarea - className="input-super-large text-middle" - id="organization-description" - maxLength={256} - onChange={this.handleDescriptionUpdate} - rows={3} - value={this.state.description} - /> - </div> - </div> - <div className="big-spacer-top"> - <OrganizationUrlInput initialValue={this.state.url} onChange={this.handleUrlUpdate} /> - </div> - </div> - - {infoBlock} - - <div className="display-flex-center big-spacer-top"> - <SubmitButton disabled={submitting || !this.canSubmit(this.state)}> - {this.props.submitText} - </SubmitButton> - {submitting && <DeferredSpinner className="spacer-left" />} - </div> - </form> - ); - } -} diff --git a/server/sonar-web/src/main/js/apps/create/organization/OrganizationDetailsStep.tsx b/server/sonar-web/src/main/js/apps/create/organization/OrganizationDetailsStep.tsx deleted file mode 100644 index 91b45c1e44f..00000000000 --- a/server/sonar-web/src/main/js/apps/create/organization/OrganizationDetailsStep.tsx +++ /dev/null @@ -1,63 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2020 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 AlertSuccessIcon from 'sonar-ui-common/components/icons/AlertSuccessIcon'; -import { translate } from 'sonar-ui-common/helpers/l10n'; -import Step from '../../../components/tutorials/components/Step'; - -interface Props { - children: React.ReactNode; - finished: boolean; - onOpen: () => void; - open: boolean; - organization?: T.Organization; - stepTitle?: string; -} -export default class OrganizationDetailsStep extends React.PureComponent<Props> { - renderForm = () => { - return <div className="boxed-group-inner">{this.props.children}</div>; - }; - - renderResult = () => { - const { organization } = this.props; - return organization ? ( - <div className="boxed-group-actions display-flex-center"> - <AlertSuccessIcon className="spacer-right" /> - <strong className="text-limited">{organization.key}</strong> - </div> - ) : null; - }; - - render() { - return ( - <Step - finished={this.props.finished} - onOpen={this.props.onOpen} - open={this.props.open} - renderForm={this.renderForm} - renderResult={this.renderResult} - stepNumber={1} - stepTitle={ - this.props.stepTitle || translate('onboarding.create_organization.enter_org_details') - } - /> - ); - } -} diff --git a/server/sonar-web/src/main/js/apps/create/organization/PlanSelect.tsx b/server/sonar-web/src/main/js/apps/create/organization/PlanSelect.tsx deleted file mode 100644 index 0c2460fd7d9..00000000000 --- a/server/sonar-web/src/main/js/apps/create/organization/PlanSelect.tsx +++ /dev/null @@ -1,81 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2020 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 { translate } from 'sonar-ui-common/helpers/l10n'; -import FreeCardPlan from '../components/FreeCardPlan'; -import PaidCardPlan from '../components/PaidCardPlan'; - -export enum Plan { - Free = 'free', - Paid = 'paid' -} - -interface Props { - almApplication?: T.AlmApplication; - almOrganization?: T.AlmOrganization; - onChange: (plan: Plan) => void; - plan: Plan; - startingPrice: number; -} - -export default class PlanSelect extends React.PureComponent<Props> { - handleFreePlanClick = () => { - this.props.onChange(Plan.Free); - }; - - handlePaidPlanClick = () => { - this.props.onChange(Plan.Paid); - }; - - render() { - const { almApplication, almOrganization, plan } = this.props; - const hasPrivateRepo = Boolean(almOrganization && almOrganization.privateRepos > 0); - const onlyPrivateRepo = Boolean( - hasPrivateRepo && almOrganization && almOrganization.publicRepos === 0 - ); - - const cards = [ - <PaidCardPlan - isRecommended={hasPrivateRepo} - key="paid" - onClick={this.handlePaidPlanClick} - selected={plan === Plan.Paid} - startingPrice={this.props.startingPrice} - />, - <FreeCardPlan - almName={almApplication && almApplication.name} - disabled={onlyPrivateRepo} - hasWarning={hasPrivateRepo && plan === Plan.Free} - key="free" - onClick={this.handleFreePlanClick} - selected={plan === Plan.Free} - /> - ]; - - return ( - <div - aria-label={translate('onboarding.create_organization.choose_plan')} - className="display-flex-row huge-spacer-bottom" - role="radiogroup"> - {hasPrivateRepo ? cards : cards.reverse()} - </div> - ); - } -} diff --git a/server/sonar-web/src/main/js/apps/create/organization/PlanStep.tsx b/server/sonar-web/src/main/js/apps/create/organization/PlanStep.tsx deleted file mode 100644 index 042210cee1e..00000000000 --- a/server/sonar-web/src/main/js/apps/create/organization/PlanStep.tsx +++ /dev/null @@ -1,165 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2020 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 { SubmitButton } from 'sonar-ui-common/components/controls/buttons'; -import DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner'; -import { translate } from 'sonar-ui-common/helpers/l10n'; -import { withCurrentUser } from '../../../components/hoc/withCurrentUser'; -import Step from '../../../components/tutorials/components/Step'; -import { getExtensionStart } from '../../../helpers/extensions'; -import BillingFormShim from '../components/BillingFormShim'; -import PlanSelect, { Plan } from './PlanSelect'; - -const BillingForm = withCurrentUser(BillingFormShim); - -interface Props { - almApplication?: T.AlmApplication; - almOrganization?: T.AlmOrganization; - createOrganization: () => Promise<string>; - onDone: () => void; - onUpgradeFail?: () => void; - open: boolean; - subscriptionPlans: T.SubscriptionPlan[]; -} - -interface State { - plan: Plan; - ready: boolean; - submitting: boolean; -} - -export default class PlanStep extends React.PureComponent<Props, State> { - mounted = false; - - constructor(props: Props) { - super(props); - this.state = { - plan: props.almOrganization && props.almOrganization.privateRepos > 0 ? Plan.Paid : Plan.Free, - ready: false, - submitting: false - }; - } - - componentDidMount() { - this.mounted = true; - getExtensionStart('billing/billing').then( - () => { - if (this.mounted) { - this.setState({ ready: true }); - } - }, - () => {} - ); - } - - componentWillUnmount() { - this.mounted = false; - } - - handlePlanChange = (plan: Plan) => { - this.setState({ plan }); - }; - - stopSubmitting = () => { - if (this.mounted) { - this.setState({ submitting: false }); - } - }; - - handleFreePlanSubmit = (event: React.FormEvent) => { - event.preventDefault(); - this.setState({ submitting: true }); - return this.props.createOrganization().then(() => { - this.props.onDone(); - this.stopSubmitting(); - }, this.stopSubmitting); - }; - - renderForm = () => { - const { submitting } = this.state; - const { subscriptionPlans } = this.props; - const startingPrice = subscriptionPlans && subscriptionPlans[0] && subscriptionPlans[0].price; - return ( - <div className="boxed-group-inner"> - {this.state.ready && ( - <> - <PlanSelect - almApplication={this.props.almApplication} - almOrganization={this.props.almOrganization} - onChange={this.handlePlanChange} - plan={this.state.plan} - startingPrice={startingPrice} - /> - - {this.state.plan === Plan.Paid ? ( - <BillingForm - onCommit={this.props.onDone} - onFailToUpgrade={this.props.onUpgradeFail} - organizationKey={this.props.createOrganization} - subscriptionPlans={this.props.subscriptionPlans}> - {({ onSubmit, renderFormFields, renderSubmitGroup }) => ( - <form id="organization-paid-plan-form" onSubmit={onSubmit}> - {renderFormFields()} - <div className="billing-input-large big-spacer-top"> - {renderSubmitGroup( - translate('onboarding.create_organization.create_and_upgrade') - )} - </div> - </form> - )} - </BillingForm> - ) : ( - <form - className="display-flex-center big-spacer-top" - id="organization-free-plan-form" - onSubmit={this.handleFreePlanSubmit}> - <SubmitButton disabled={submitting}> - {translate('my_account.create_organization')} - </SubmitButton> - {submitting && <DeferredSpinner className="spacer-left" />} - </form> - )} - </> - )} - </div> - ); - }; - - render() { - const { almOrganization } = this.props; - const stepTitle = translate( - almOrganization && almOrganization.privateRepos > 0 && almOrganization.publicRepos === 0 - ? 'onboarding.create_organization.enter_payment_details' - : 'onboarding.create_organization.choose_plan' - ); - - return ( - <Step - finished={false} - onOpen={() => {}} - open={this.props.open} - renderForm={this.renderForm} - renderResult={() => null} - stepNumber={2} - stepTitle={stepTitle} - /> - ); - } -} diff --git a/server/sonar-web/src/main/js/apps/create/organization/RemoteOrganizationChoose.tsx b/server/sonar-web/src/main/js/apps/create/organization/RemoteOrganizationChoose.tsx deleted file mode 100644 index 00b9744b7cb..00000000000 --- a/server/sonar-web/src/main/js/apps/create/organization/RemoteOrganizationChoose.tsx +++ /dev/null @@ -1,204 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2020 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 classNames from 'classnames'; -import { sortBy } from 'lodash'; -import * as React from 'react'; -import { FormattedMessage } from 'react-intl'; -import { withRouter, WithRouterProps } from 'react-router'; -import { SubmitButton } from 'sonar-ui-common/components/controls/buttons'; -import IdentityProviderLink from 'sonar-ui-common/components/controls/IdentityProviderLink'; -import Select from 'sonar-ui-common/components/controls/Select'; -import { Alert } from 'sonar-ui-common/components/ui/Alert'; -import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n'; -import { save } from 'sonar-ui-common/helpers/storage'; -import { getBaseUrl } from 'sonar-ui-common/helpers/urls'; -import OrganizationAvatar from '../../../components/common/OrganizationAvatar'; -import { sanitizeAlmId } from '../../../helpers/almIntegrations'; -import { ORGANIZATION_IMPORT_BINDING_IN_PROGRESS_TIMESTAMP, serializeQuery } from './utils'; - -interface Props { - almApplication: T.AlmApplication; - almInstallId?: string; - almOrganization?: T.AlmOrganization; - almUnboundApplications: T.AlmUnboundApplication[]; - boundOrganization?: T.OrganizationBase; - className?: string; -} - -interface State { - unboundInstallationId: string; -} - -export class RemoteOrganizationChoose extends React.PureComponent<Props & WithRouterProps, State> { - state: State = { unboundInstallationId: '' }; - - handleSubmit = (event: React.FormEvent<HTMLFormElement>) => { - event.preventDefault(); - - const { unboundInstallationId } = this.state; - if (unboundInstallationId) { - this.props.router.push({ - pathname: '/create-organization', - query: serializeQuery({ - almInstallId: unboundInstallationId, - almKey: this.props.almApplication.key - }) - }); - } - }; - - handleInstallAppClick = () => { - save(ORGANIZATION_IMPORT_BINDING_IN_PROGRESS_TIMESTAMP, Date.now().toString(10)); - }; - - handleInstallationChange = ({ installationId }: T.AlmUnboundApplication) => { - this.setState({ unboundInstallationId: installationId }); - }; - - renderOption = (organization: T.AlmUnboundApplication) => { - const { almApplication } = this.props; - return ( - <span> - <img - alt={almApplication.name} - className="spacer-right" - height={14} - src={`${getBaseUrl()}/images/sonarcloud/${sanitizeAlmId(almApplication.key)}.svg`} - /> - {organization.name} - </span> - ); - }; - - render() { - const { - almApplication, - almInstallId, - almOrganization, - almUnboundApplications, - boundOrganization, - className - } = this.props; - const { unboundInstallationId } = this.state; - return ( - <div className={classNames('boxed-group', className)}> - <div className="boxed-group-header"> - <h2>{translate('onboarding.import_organization.import_org_details')}</h2> - </div> - <div className="boxed-group-inner"> - {almInstallId && !almOrganization && ( - <Alert className="big-spacer-bottom width-60" variant="error"> - <div className="markdown"> - {translate('onboarding.import_organization.org_not_found')} - <ul> - <li>{translate('onboarding.import_organization.org_not_found.tips_1')}</li> - <li>{translate('onboarding.import_organization.org_not_found.tips_2')}</li> - </ul> - </div> - </Alert> - )} - {almOrganization && boundOrganization && ( - <Alert className="big-spacer-bottom width-60" variant="error"> - <FormattedMessage - defaultMessage={translate('onboarding.import_organization.already_bound_x')} - id="onboarding.import_organization.already_bound_x" - values={{ - avatar: ( - <img - alt={almApplication.name} - className="little-spacer-left" - src={`${getBaseUrl()}/images/sonarcloud/${sanitizeAlmId( - almApplication.key - )}.svg`} - width={16} - /> - ), - name: <strong>{almOrganization.name}</strong>, - boundAvatar: ( - <OrganizationAvatar - className="little-spacer-left" - organization={boundOrganization} - small={true} - /> - ), - boundName: <strong>{boundOrganization.name}</strong> - }} - /> - </Alert> - )} - <div className="display-flex-center"> - <div className="display-inline-block"> - <IdentityProviderLink - backgroundColor={almApplication.backgroundColor} - className="display-inline-block" - iconPath={almApplication.iconPath} - name={almApplication.name} - onClick={this.handleInstallAppClick} - small={true} - url={almApplication.installationUrl}> - {translate( - 'onboarding.import_organization.choose_organization_button', - almApplication.key - )} - </IdentityProviderLink> - </div> - {almUnboundApplications.length > 0 && ( - <div className="display-flex-stretch"> - <div className="vertical-pipe-separator"> - <div className="vertical-separator " /> - <span className="note">{translate('or')}</span> - <div className="vertical-separator" /> - </div> - <form className="big-spacer-top big-spacer-bottom" onSubmit={this.handleSubmit}> - <div className="form-field abs-width-400"> - <label className="text-normal" htmlFor="select-unbound-installation"> - {translateWithParameters( - 'onboarding.import_organization.choose_unbound_installation_x', - translate(sanitizeAlmId(almApplication.key)) - )} - </label> - <Select - className="input-super-large" - clearable={false} - id="select-unbound-installation" - labelKey="name" - onChange={this.handleInstallationChange} - optionRenderer={this.renderOption} - options={sortBy(almUnboundApplications, o => o.name.toLowerCase())} - placeholder={translate('onboarding.import_organization.choose_organization')} - value={unboundInstallationId} - valueKey="installationId" - valueRenderer={this.renderOption} - /> - </div> - <SubmitButton disabled={!unboundInstallationId}> - {translate('continue')} - </SubmitButton> - </form> - </div> - )} - </div> - </div> - </div> - ); - } -} - -export default withRouter(RemoteOrganizationChoose); diff --git a/server/sonar-web/src/main/js/apps/create/organization/__tests__/AlmApplicationInstalling-test.tsx b/server/sonar-web/src/main/js/apps/create/organization/__tests__/AlmApplicationInstalling-test.tsx deleted file mode 100644 index 352e72f3781..00000000000 --- a/server/sonar-web/src/main/js/apps/create/organization/__tests__/AlmApplicationInstalling-test.tsx +++ /dev/null @@ -1,27 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2020 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 { shallow } from 'enzyme'; -import * as React from 'react'; -import AlmApplicationInstalling from '../AlmApplicationInstalling'; - -it('should render correctly', () => { - expect(shallow(<AlmApplicationInstalling />)).toMatchSnapshot(); - expect(shallow(<AlmApplicationInstalling almKey="github" />)).toMatchSnapshot(); -}); diff --git a/server/sonar-web/src/main/js/apps/create/organization/__tests__/AutoOrganizationBind-test.tsx b/server/sonar-web/src/main/js/apps/create/organization/__tests__/AutoOrganizationBind-test.tsx deleted file mode 100644 index ecc8107689c..00000000000 --- a/server/sonar-web/src/main/js/apps/create/organization/__tests__/AutoOrganizationBind-test.tsx +++ /dev/null @@ -1,52 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2020 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 { shallow } from 'enzyme'; -import * as React from 'react'; -import { submit } from 'sonar-ui-common/helpers/testUtils'; -import { mockOrganization } from '../../../../helpers/testMocks'; -import AutoOrganizationBind from '../AutoOrganizationBind'; - -it('should render correctly', () => { - const onBindOrganization = jest.fn().mockResolvedValue({}); - const wrapper = shallowRender({ onBindOrganization }); - expect(wrapper).toMatchSnapshot(); - - submit(wrapper.find('form')); - expect(onBindOrganization).toHaveBeenCalled(); -}); - -it('should not show member sync info box for Bitbucket', () => { - expect( - shallowRender({ almKey: 'bitbucket' }) - .find('Alert') - .exists() - ).toBe(false); -}); - -function shallowRender(props: Partial<AutoOrganizationBind['props']> = {}) { - return shallow( - <AutoOrganizationBind - almKey="github" - onBindOrganization={jest.fn()} - unboundOrganizations={[mockOrganization()]} - {...props} - /> - ); -} 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 deleted file mode 100644 index c358824365d..00000000000 --- a/server/sonar-web/src/main/js/apps/create/organization/__tests__/AutoOrganizationCreate-test.tsx +++ /dev/null @@ -1,128 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2020 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 { shallow } from 'enzyme'; -import * as React from 'react'; -import { click, waitAndUpdate } from 'sonar-ui-common/helpers/testUtils'; -import { bindAlmOrganization } from '../../../../api/alm-integration'; -import { mockAlmApplication, mockAlmOrganization } from '../../../../helpers/testMocks'; -import AutoOrganizationCreate from '../AutoOrganizationCreate'; -import { Step } from '../utils'; - -jest.mock('../../../../api/alm-integration', () => ({ - bindAlmOrganization: jest.fn().mockResolvedValue({}) -})); - -const organization = mockAlmOrganization(); - -it('should render prefilled and create org', async () => { - const createOrganization = jest.fn().mockResolvedValue({ key: 'foo' }); - const handleOrgDetailsFinish = jest.fn(); - const almOrganization = mockAlmOrganization({ almUrl: 'http://github.com/thing' }); - const wrapper = shallowRender({ - almOrganization, - createOrganization, - handleOrgDetailsFinish - }); - - expect(wrapper).toMatchSnapshot(); - - wrapper.find('OrganizationDetailsForm').prop<Function>('onContinue')(organization); - await waitAndUpdate(wrapper); - expect(handleOrgDetailsFinish).toBeCalled(); - - wrapper.setProps({ organization }); - wrapper.find('PlanStep').prop<Function>('createOrganization')(); - - const alm: T.Organization['alm'] = { - key: 'github', - membersSync: true, - personal: false, - url: 'http://github.com/thing' - }; - expect(createOrganization).toBeCalledWith({ ...organization, alm, installationId: 'id-foo' }); -}); - -it('should allow to cancel org import', () => { - const handleCancelImport = jest.fn().mockResolvedValue({ key: 'foo' }); - const wrapper = shallowRender({ handleCancelImport }); - - click(wrapper.find('ClearButton')); - expect(handleCancelImport).toBeCalled(); -}); - -it('should display choice between import or creation', () => { - const wrapper = shallowRender({ unboundOrganizations: [organization] }); - expect(wrapper).toMatchSnapshot(); - - wrapper.find('RadioToggle').prop<Function>('onCheck')('create'); - wrapper.update(); - expect(wrapper.find('OrganizationDetailsForm').exists()).toBe(true); - - wrapper.find('RadioToggle').prop<Function>('onCheck')('bind'); - wrapper.update(); - expect(wrapper.find('AutoOrganizationBind').exists()).toBe(true); -}); - -it('should bind existing organization', async () => { - const onOrgCreated = jest.fn(); - const wrapper = shallowRender({ onOrgCreated, unboundOrganizations: [organization] }); - - wrapper.find('RadioToggle').prop<Function>('onCheck')('bind'); - wrapper.update(); - wrapper.find('AutoOrganizationBind').prop<Function>('onBindOrganization')('foo'); - expect(bindAlmOrganization as jest.Mock<any>).toHaveBeenCalledWith({ - installationId: 'id-foo', - organization: 'foo' - }); - await waitAndUpdate(wrapper); - expect(onOrgCreated).toHaveBeenCalledWith('foo'); -}); - -it('should not show member sync info box for Bitbucket', () => { - expect( - shallowRender({ almApplication: mockAlmApplication({ key: 'bitbucket-cloud' }) }) - .find('Alert') - .exists() - ).toBe(false); -}); - -function shallowRender(props: Partial<AutoOrganizationCreate['props']> = {}) { - return shallow( - <AutoOrganizationCreate - almApplication={mockAlmApplication()} - almInstallId="id-foo" - almOrganization={organization} - createOrganization={jest.fn()} - handleCancelImport={jest.fn()} - handleOrgDetailsFinish={jest.fn()} - handleOrgDetailsStepOpen={jest.fn()} - onDone={jest.fn()} - onOrgCreated={jest.fn()} - onUpgradeFail={jest.fn()} - step={Step.OrganizationDetails} - subscriptionPlans={[ - { maxNcloc: 100000, price: 10 }, - { maxNcloc: 250000, price: 75 } - ]} - unboundOrganizations={[]} - {...props} - /> - ); -} 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 deleted file mode 100644 index 1f1e0a03dad..00000000000 --- a/server/sonar-web/src/main/js/apps/create/organization/__tests__/CreateOrganization-test.tsx +++ /dev/null @@ -1,342 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2020 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 { mount, shallow } from 'enzyme'; -import { Location } from 'history'; -import { times } from 'lodash'; -import * as React from 'react'; -import { HelmetProvider } from 'react-helmet-async'; -import { get, remove } from 'sonar-ui-common/helpers/storage'; -import { waitAndUpdate } from 'sonar-ui-common/helpers/testUtils'; -import { - bindAlmOrganization, - getAlmAppInfo, - getAlmOrganization, - listUnboundApplications -} from '../../../../api/alm-integration'; -import { getSubscriptionPlans } from '../../../../api/billing'; -import { getOrganizations } from '../../../../api/organizations'; -import { - mockAlmOrganization, - mockLocation, - mockLoggedInUser, - mockOrganizationWithAdminActions, - mockOrganizationWithAlm, - mockRouter -} from '../../../../helpers/testMocks'; -import { CreateOrganization } from '../CreateOrganization'; - -jest.mock('../../../../api/billing', () => ({ - getSubscriptionPlans: jest.fn().mockResolvedValue([ - { maxNcloc: 100000, price: 10 }, - { maxNcloc: 250000, price: 75 } - ]) -})); - -jest.mock('../../../../api/alm-integration', () => ({ - getAlmAppInfo: jest.fn().mockResolvedValue({ - application: { - backgroundColor: 'blue', - iconPath: 'icon/path', - installationUrl: 'https://alm.installation.url', - key: 'github', - name: 'GitHub' - } - }), - getAlmOrganization: jest.fn().mockResolvedValue({ - almOrganization: { - avatar: 'my-avatar', - description: 'Continuous Code Quality', - key: 'sonarsource', - name: 'SonarSource', - privateRepos: 0, - publicRepos: 3, - url: 'https://www.sonarsource.com' - } - }), - listUnboundApplications: jest.fn().mockResolvedValue([]), - bindAlmOrganization: jest.fn().mockResolvedValue({}) -})); - -jest.mock('../../../../api/organizations', () => ({ - getOrganizations: jest.fn().mockResolvedValue({ organizations: [] }) -})); - -jest.mock('sonar-ui-common/helpers/storage', () => ({ - get: jest.fn().mockReturnValue(undefined), - remove: jest.fn() -})); - -const user = mockLoggedInUser(); -const fooAlmOrganization = mockAlmOrganization(); -const fooBarAlmOrganization = mockAlmOrganization({ - avatar: 'https://avatars3.githubusercontent.com/u/37629810?v=4', - key: 'Foo&Bar', - name: 'Foo & Bar' -}); - -const boundOrganization = { key: 'foobar', name: 'Foo & Bar' }; - -beforeEach(() => { - (getAlmAppInfo as jest.Mock<any>).mockClear(); - (getAlmOrganization as jest.Mock<any>).mockClear(); - (listUnboundApplications as jest.Mock<any>).mockClear(); - (getSubscriptionPlans as jest.Mock<any>).mockClear(); - (getOrganizations as jest.Mock<any>).mockClear(); - (get as jest.Mock<any>).mockClear(); - (remove as jest.Mock<any>).mockClear(); -}); - -it('should render with manual tab displayed', async () => { - const wrapper = shallowRender(); - await waitAndUpdate(wrapper); - expect(wrapper).toMatchSnapshot(); - expect(getSubscriptionPlans).toHaveBeenCalled(); - expect(getAlmAppInfo).not.toHaveBeenCalled(); -}); - -it('should render with auto tab displayed', async () => { - const wrapper = shallowRender({ currentUser: { ...user, externalProvider: 'github' } }); - await waitAndUpdate(wrapper); - expect(wrapper).toMatchSnapshot(); - expect(getAlmAppInfo).toHaveBeenCalled(); - expect(listUnboundApplications).toHaveBeenCalled(); -}); - -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 - }); - expect(wrapper).toMatchSnapshot(); - await waitAndUpdate(wrapper); - expect(wrapper).toMatchSnapshot(); - expect(getAlmAppInfo).toHaveBeenCalled(); - expect(getAlmOrganization).toHaveBeenCalled(); - expect(getOrganizations).toHaveBeenCalled(); -}); - -it('should render with organization bind page', async () => { - (getAlmOrganization as jest.Mock<any>).mockResolvedValueOnce({ - almOrganization: fooAlmOrganization - }); - const wrapper = shallowRender({ - currentUser: { ...user, externalProvider: 'github' }, - location: { query: { installation_id: 'foo' } } as Location - }); - expect(wrapper).toMatchSnapshot(); - await waitAndUpdate(wrapper); - expect(wrapper).toMatchSnapshot(); -}); - -it('should slugify and find a uniq organization key', async () => { - (getAlmOrganization as jest.Mock<any>).mockResolvedValueOnce({ - almOrganization: fooBarAlmOrganization - }); - (getOrganizations as jest.Mock<any>).mockResolvedValueOnce({ - organizations: [{ key: 'foo-and-bar' }, { key: 'foo-and-bar-1' }] - }); - const wrapper = shallowRender({ - currentUser: { ...user, externalProvider: 'github' }, - location: { query: { installation_id: 'foo' } } as Location - }); - await waitAndUpdate(wrapper); - expect(getOrganizations).toHaveBeenCalledWith({ - organizations: ['foo-and-bar', ...times(9, i => `foo-and-bar-${i + 1}`)].join(',') - }); - expect(wrapper.find('AutoOrganizationCreate').prop('almOrganization')).toMatchObject({ - key: 'foo-and-bar-2' - }); -}); - -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(); - - (wrapper.find('Tabs').prop('onChange') as Function)('manual'); - expect(wrapper.find('ManualOrganizationCreate').hasClass('hidden')).toBe(false); - expect(wrapper.find('withRouter(RemoteOrganizationChoose)').hasClass('hidden')).toBe(true); - (wrapper.find('Tabs').prop('onChange') as Function)('auto'); - expect(wrapper.find('withRouter(RemoteOrganizationChoose)').hasClass('hidden')).toBe(false); - expect(wrapper.find('ManualOrganizationCreate').hasClass('hidden')).toBe(true); -}); - -it('should reload the alm organization when the url query changes', async () => { - const wrapper = shallowRender({ currentUser: { ...user, externalProvider: 'github' } }); - await waitAndUpdate(wrapper); - expect(getAlmOrganization).not.toHaveBeenCalled(); - wrapper.setProps({ location: { query: { installation_id: 'foo' } } as Location }); - expect(getAlmOrganization).toHaveBeenCalledWith({ installationId: 'foo' }); - wrapper.setProps({ location: { query: {} } as Location }); - expect(wrapper.state('almOrganization')).toBeUndefined(); - expect(listUnboundApplications).toHaveBeenCalledTimes(2); -}); - -it('should redirect to organization page after creation', async () => { - const push = jest.fn(); - const wrapper = shallowRender({ router: mockRouter({ push }) }); - await waitAndUpdate(wrapper); - - wrapper.setState({ organization: boundOrganization }); - wrapper.instance().handleOrgCreated('foo'); - expect(push).toHaveBeenCalledWith({ pathname: '/organizations/foo' }); -}); - -it('should redirect to projects creation page after creation', async () => { - const push = jest.fn(); - const wrapper = shallowRender({ router: mockRouter({ push }) }); - await waitAndUpdate(wrapper); - - (get as jest.Mock<any>).mockReturnValueOnce(Date.now().toString()); - wrapper.instance().handleOrgCreated('foo'); - expect(get).toHaveBeenCalled(); - expect(remove).toHaveBeenCalled(); - expect(push).toHaveBeenCalledWith({ - pathname: '/projects/create', - state: { organization: 'foo', tab: 'manual' } - }); - - wrapper.setState({ almOrganization: fooAlmOrganization }); - (get as jest.Mock<any>).mockReturnValueOnce(Date.now().toString()); - wrapper.instance().handleOrgCreated('foo'); - expect(push).toHaveBeenCalledWith({ - pathname: '/projects/create', - state: { organization: 'foo', tab: 'auto' } - }); -}); - -it('should display AutoOrganizationCreate with already bound organization', async () => { - (getAlmOrganization as jest.Mock<any>).mockResolvedValueOnce({ - almOrganization: fooBarAlmOrganization, - boundOrganization - }); - (get as jest.Mock<any>) - .mockReturnValueOnce(undefined) // For BIND_ORGANIZATION_REDIRECT_TO_ORG_TIMESTAMP - .mockReturnValueOnce(Date.now().toString()); // For ORGANIZATION_IMPORT_BINDING_IN_PROGRESS_TIMESTAMP - const push = jest.fn(); - const wrapper = shallowRender({ - currentUser: { ...user, externalProvider: 'github' }, - location: { query: { installation_id: 'foo' } } as Location, - router: mockRouter({ push }) - }); - await waitAndUpdate(wrapper); - expect(get).toHaveBeenCalled(); - expect(remove).toHaveBeenCalled(); - expect(getAlmOrganization).toHaveBeenCalledWith({ installationId: 'foo' }); - expect(push).not.toHaveBeenCalled(); - expect(wrapper.find('withRouter(RemoteOrganizationChoose)').prop('boundOrganization')).toEqual({ - key: 'foobar', - name: 'Foo & Bar' - }); -}); - -it('should redirect to org page when already bound and no binding in progress', async () => { - (getAlmOrganization as jest.Mock<any>).mockResolvedValueOnce({ - almOrganization: fooBarAlmOrganization, - boundOrganization - }); - const push = jest.fn(); - const wrapper = shallowRender({ - currentUser: { ...user, externalProvider: 'github' }, - location: { query: { installation_id: 'foo' } } as Location, - router: mockRouter({ push }) - }); - await waitAndUpdate(wrapper); - expect(getAlmOrganization).toHaveBeenCalledWith({ installationId: 'foo' }); - expect(push).toHaveBeenCalledWith({ pathname: '/organizations/foobar' }); -}); - -it('should roll back after upgrade failure', async () => { - const deleteOrganization = jest.fn(); - const wrapper = shallowRender({ deleteOrganization }); - await waitAndUpdate(wrapper); - wrapper.setState({ organization: boundOrganization }); - wrapper.find('ManualOrganizationCreate').prop<Function>('onUpgradeFail')(); - expect(deleteOrganization).toBeCalled(); -}); - -it('should cancel imports', async () => { - const push = jest.fn(); - const wrapper = shallowRender({ router: mockRouter({ push }) }); - await waitAndUpdate(wrapper); - wrapper.instance().handleCancelImport(); - expect(push).toBeCalledWith({ pathname: '/path', query: {}, state: {} }); -}); - -it('should bind org and redirect to org home when coming from org binding', async () => { - const installation_id = '5328'; - const orgKey = 'org4test'; - const push = jest.fn(); - - (get as jest.Mock<any>) - .mockReturnValueOnce(Date.now().toString()) // For BIND_ORGANIZATION_REDIRECT_TO_ORG_TIMESTAMP - .mockReturnValueOnce(orgKey); // For BIND_ORGANIZATION_KEY - - const wrapper = mountRender({ - currentUser: mockLoggedInUser({ ...user, externalProvider: 'github' }), - location: mockLocation({ query: { installation_id } }), - router: mockRouter({ push }) - }); - await waitAndUpdate(wrapper); - - expect(bindAlmOrganization).toBeCalled(); - expect(getAlmOrganization).not.toBeCalled(); - expect(push).toBeCalledWith({ - pathname: `/organizations/${orgKey}` - }); -}); - -function mountRender(props: Partial<CreateOrganization['props']> = {}) { - return mount<CreateOrganization>(<HelmetProvider>{createComponent(props)}</HelmetProvider>); -} - -function shallowRender(props: Partial<CreateOrganization['props']> = {}) { - return shallow<CreateOrganization>(createComponent(props)); -} - -function createComponent(props: Partial<CreateOrganization['props']> = {}) { - return ( - <CreateOrganization - createOrganization={jest.fn()} - currentUser={user} - deleteOrganization={jest.fn()} - location={mockLocation()} - params={{}} - router={mockRouter()} - routes={[]} - skipOnboarding={jest.fn()} - userOrganizations={[ - mockOrganizationWithAdminActions(), - mockOrganizationWithAdminActions(mockOrganizationWithAlm({ key: 'bar', name: 'Bar' })), - mockOrganizationWithAdminActions({ key: 'baz', name: 'Baz' }, { admin: false }) - ]} - {...props} - /> - ); -} 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 deleted file mode 100644 index 46c1fab37f4..00000000000 --- a/server/sonar-web/src/main/js/apps/create/organization/__tests__/ManualOrganizationCreate-test.tsx +++ /dev/null @@ -1,59 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2020 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 { shallow } from 'enzyme'; -import * as React from 'react'; -import { waitAndUpdate } from 'sonar-ui-common/helpers/testUtils'; -import { mockOrganization } from '../../../../helpers/testMocks'; -import ManualOrganizationCreate from '../ManualOrganizationCreate'; -import { Step } from '../utils'; - -it('should render and create organization', async () => { - const createOrganization = jest.fn().mockResolvedValue({ key: 'foo' }); - const onDone = jest.fn(); - const handleOrgDetailsFinish = jest.fn(); - const wrapper = shallowRender({ createOrganization, handleOrgDetailsFinish, onDone }); - - await waitAndUpdate(wrapper); - expect(wrapper).toMatchSnapshot(); - - wrapper.find('OrganizationDetailsForm').prop<Function>('onContinue')(mockOrganization()); - await waitAndUpdate(wrapper); - expect(handleOrgDetailsFinish).toHaveBeenCalled(); - wrapper.setProps({ step: Step.Plan }); - expect(wrapper).toMatchSnapshot(); -}); - -function shallowRender(props: Partial<ManualOrganizationCreate['props']> = {}) { - return shallow( - <ManualOrganizationCreate - createOrganization={jest.fn()} - handleOrgDetailsFinish={jest.fn()} - handleOrgDetailsStepOpen={jest.fn()} - onDone={jest.fn()} - onUpgradeFail={jest.fn()} - step={Step.OrganizationDetails} - subscriptionPlans={[ - { maxNcloc: 100000, price: 10 }, - { maxNcloc: 250000, price: 75 } - ]} - {...props} - /> - ); -} diff --git a/server/sonar-web/src/main/js/apps/create/organization/__tests__/OrganizationDetailsForm-test.tsx b/server/sonar-web/src/main/js/apps/create/organization/__tests__/OrganizationDetailsForm-test.tsx deleted file mode 100644 index 548d59e4cec..00000000000 --- a/server/sonar-web/src/main/js/apps/create/organization/__tests__/OrganizationDetailsForm-test.tsx +++ /dev/null @@ -1,84 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2020 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 { shallow } from 'enzyme'; -import * as React from 'react'; -import { click, submit } from 'sonar-ui-common/helpers/testUtils'; -import OrganizationDetailsForm from '../OrganizationDetailsForm'; - -it('should render form', () => { - const wrapper = shallowRender(); - expect(wrapper).toMatchSnapshot(); - expect(wrapper.find('.js-additional-info').prop('hidden')).toBe(true); - - click(wrapper.find('ResetButtonLink')); - wrapper.update(); - expect(wrapper.find('.js-additional-info').prop('hidden')).toBe(false); -}); - -it('should validate before submit', () => { - const wrapper = shallowRender(); - const instance = wrapper.instance() as OrganizationDetailsForm; - - expect( - instance.canSubmit({ - additional: false, - avatar: '', - description: '', - name: '', - key: 'foo', - submitting: false, - url: '' - }) - ).toBe(true); - - expect( - instance.canSubmit({ - additional: false, - avatar: '', - description: '', - name: '', - key: undefined, - submitting: false, - url: '' - }) - ).toBe(false); - - expect( - instance.canSubmit({ - additional: false, - avatar: undefined, - description: '', - name: '', - key: 'foo', - submitting: false, - url: '' - }) - ).toBe(false); - - instance.canSubmit = jest.fn() as any; - submit(wrapper.find('form')); - expect(instance.canSubmit).toHaveBeenCalled(); -}); - -function shallowRender(props: Partial<OrganizationDetailsForm['props']> = {}) { - return shallow( - <OrganizationDetailsForm onContinue={jest.fn()} submitText="continue" {...props} /> - ); -} 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 deleted file mode 100644 index c88443b685d..00000000000 --- a/server/sonar-web/src/main/js/apps/create/organization/__tests__/OrganizationDetailsStep-test.tsx +++ /dev/null @@ -1,56 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2020 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 { shallow } from 'enzyme'; -import * as React from 'react'; -import OrganizationDetailsStep from '../OrganizationDetailsStep'; - -it('should render form', () => { - const wrapper = shallow( - <OrganizationDetailsStep finished={false} onOpen={jest.fn()} open={true}> - <form /> - </OrganizationDetailsStep> - ); - expect(wrapper).toMatchSnapshot(); - expect( - wrapper - .dive() - .find('form') - .exists() - ).toBe(true); -}); - -it('should render result', () => { - const wrapper = shallow( - <OrganizationDetailsStep - finished={true} - onOpen={jest.fn()} - open={false} - organization={{ avatar: '', description: '', key: 'org', name: 'Organization', url: '' }}> - <div /> - </OrganizationDetailsStep> - ); - expect(wrapper.dive().find('.boxed-group-actions')).toMatchSnapshot(); - expect( - wrapper - .dive() - .find('.hidden') - .exists() - ).toBe(true); -}); diff --git a/server/sonar-web/src/main/js/apps/create/organization/__tests__/PlanSelect-test.tsx b/server/sonar-web/src/main/js/apps/create/organization/__tests__/PlanSelect-test.tsx deleted file mode 100644 index 55fa395aead..00000000000 --- a/server/sonar-web/src/main/js/apps/create/organization/__tests__/PlanSelect-test.tsx +++ /dev/null @@ -1,62 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2020 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 { shallow } from 'enzyme'; -import * as React from 'react'; -import { click } from 'sonar-ui-common/helpers/testUtils'; -import { mockAlmOrganization } from '../../../../helpers/testMocks'; -import PlanSelect, { Plan } from '../PlanSelect'; - -it('should render and select', () => { - const onChange = jest.fn(); - const wrapper = shallowRender({ onChange }); - expect(wrapper).toMatchSnapshot(); - - click(wrapper.find('PaidCardPlan')); - expect(onChange).toBeCalledWith(Plan.Paid); - wrapper.setProps({ plan: Plan.Paid }); - expect(wrapper).toMatchSnapshot(); -}); - -it('should recommend paid plan', () => { - const wrapper = shallowRender({ - almOrganization: mockAlmOrganization({ privateRepos: 1, publicRepos: 5 }), - plan: Plan.Paid - }); - expect(wrapper.find('PaidCardPlan').prop('isRecommended')).toBe(true); - expect(wrapper.find('FreeCardPlan').prop('disabled')).toBe(false); - expect(wrapper.find('FreeCardPlan').prop('hasWarning')).toBe(false); - - wrapper.setProps({ plan: Plan.Free }); - expect(wrapper.find('FreeCardPlan').prop('hasWarning')).toBe(true); -}); - -it('should recommend paid plan and disable free plan', () => { - const wrapper = shallowRender({ - almOrganization: mockAlmOrganization({ privateRepos: 1, publicRepos: 0 }) - }); - expect(wrapper.find('PaidCardPlan').prop('isRecommended')).toBe(true); - expect(wrapper.find('FreeCardPlan').prop('disabled')).toBe(true); -}); - -function shallowRender(props: Partial<PlanSelect['props']> = {}) { - return shallow( - <PlanSelect onChange={jest.fn()} plan={Plan.Free} startingPrice={10} {...props} /> - ); -} diff --git a/server/sonar-web/src/main/js/apps/create/organization/__tests__/PlanStep-test.tsx b/server/sonar-web/src/main/js/apps/create/organization/__tests__/PlanStep-test.tsx deleted file mode 100644 index a306d1ccf0a..00000000000 --- a/server/sonar-web/src/main/js/apps/create/organization/__tests__/PlanStep-test.tsx +++ /dev/null @@ -1,93 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2020 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 { shallow } from 'enzyme'; -import * as React from 'react'; -import { submit, waitAndUpdate } from 'sonar-ui-common/helpers/testUtils'; -import { mockAlmOrganization } from '../../../../helpers/testMocks'; -import { Plan } from '../PlanSelect'; -import PlanStep from '../PlanStep'; - -jest.mock('../../../../helpers/extensions', () => ({ - getExtensionStart: jest.fn().mockResolvedValue(undefined) -})); -const subscriptionPlans = [{ maxNcloc: 1000, price: 100 }]; - -it('should render and use free plan', async () => { - const onDone = jest.fn(); - const createOrganization = jest.fn().mockResolvedValue('org'); - const wrapper = shallow( - <PlanStep - createOrganization={createOrganization} - onDone={onDone} - onUpgradeFail={jest.fn()} - open={true} - subscriptionPlans={subscriptionPlans} - /> - ); - await waitAndUpdate(wrapper); - expect(wrapper).toMatchSnapshot(); - expect(wrapper.dive()).toMatchSnapshot(); - - submit(wrapper.dive().find('form')); - await waitAndUpdate(wrapper); - expect(createOrganization).toBeCalled(); - expect(onDone).toBeCalled(); -}); - -it('should upgrade', async () => { - const onDone = jest.fn(); - const wrapper = shallow( - <PlanStep - createOrganization={jest.fn().mockResolvedValue('org')} - onDone={onDone} - onUpgradeFail={jest.fn()} - open={true} - subscriptionPlans={subscriptionPlans} - /> - ); - await waitAndUpdate(wrapper); - - wrapper - .dive() - .find('PlanSelect') - .prop<Function>('onChange')(Plan.Paid); - expect(wrapper.dive()).toMatchSnapshot(); - - wrapper - .dive() - .find('Connect(withCurrentUser(BillingFormShim))') - .prop<Function>('onCommit')(); - expect(onDone).toBeCalled(); -}); - -it('should preselect paid plan', async () => { - const wrapper = shallow( - <PlanStep - almOrganization={mockAlmOrganization({ privateRepos: 5, publicRepos: 0 })} - createOrganization={jest.fn()} - onDone={jest.fn()} - onUpgradeFail={jest.fn()} - open={true} - subscriptionPlans={subscriptionPlans} - /> - ); - await waitAndUpdate(wrapper); - expect(wrapper.dive()).toMatchSnapshot(); -}); diff --git a/server/sonar-web/src/main/js/apps/create/organization/__tests__/RemoteOrganizationChoose-test.tsx b/server/sonar-web/src/main/js/apps/create/organization/__tests__/RemoteOrganizationChoose-test.tsx deleted file mode 100644 index b19e2bfa6c3..00000000000 --- a/server/sonar-web/src/main/js/apps/create/organization/__tests__/RemoteOrganizationChoose-test.tsx +++ /dev/null @@ -1,77 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2020 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 { shallow } from 'enzyme'; -import * as React from 'react'; -import { submit } from 'sonar-ui-common/helpers/testUtils'; -import { mockAlmOrganization, mockRouter } from '../../../../helpers/testMocks'; -import { RemoteOrganizationChoose } from '../RemoteOrganizationChoose'; - -it('should render', () => { - expect(shallowRender()).toMatchSnapshot(); -}); - -it('should display an alert message', () => { - expect(shallowRender({ almInstallId: 'foo' }).find('Alert')).toMatchSnapshot(); -}); - -it('should display unbound installations', () => { - const installation = { installationId: '12345', key: 'foo', name: 'Foo' }; - const push = jest.fn(); - const wrapper = shallowRender({ - almUnboundApplications: [installation], - router: mockRouter({ push }) - }); - expect(wrapper).toMatchSnapshot(); - - wrapper.find('Select').prop<Function>('onChange')(installation); - submit(wrapper.find('form')); - expect(push).toHaveBeenCalledWith({ - pathname: '/create-organization', - query: { installation_id: installation.installationId } - }); -}); - -it('should display already bound alert message', () => { - expect( - shallowRender({ - almInstallId: 'foo', - almOrganization: mockAlmOrganization(), - boundOrganization: { avatar: 'bound-avatar', key: 'bound', name: 'Bound' } - }).find('Alert') - ).toMatchSnapshot(); -}); - -function shallowRender(props: Partial<RemoteOrganizationChoose['props']> = {}) { - return shallow( - // @ts-ignore avoid passing everything from WithRouterProps - <RemoteOrganizationChoose - almApplication={{ - backgroundColor: 'blue', - iconPath: 'icon/path', - installationUrl: 'https://alm.application.url', - key: 'github', - name: 'GitHub' - }} - almUnboundApplications={[]} - router={mockRouter()} - {...props} - /> - ); -} diff --git a/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/AlmApplicationInstalling-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/AlmApplicationInstalling-test.tsx.snap deleted file mode 100644 index 135829ce072..00000000000 --- a/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/AlmApplicationInstalling-test.tsx.snap +++ /dev/null @@ -1,47 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render correctly 1`] = ` -<DeferredSpinner - customSpinner={ - <div - className="sonarcloud page page-limited" - > - <div - className="huge-spacer-top text-center" - > - <i - className="spinner" - /> - <p - className="big-spacer-top" - > - onboarding.import_organization.installing.ALM - </p> - </div> - </div> - } -/> -`; - -exports[`should render correctly 2`] = ` -<DeferredSpinner - customSpinner={ - <div - className="sonarcloud page page-limited" - > - <div - className="huge-spacer-top text-center" - > - <i - className="spinner" - /> - <p - className="big-spacer-top" - > - onboarding.import_organization.installing.github - </p> - </div> - </div> - } -/> -`; diff --git a/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/AutoOrganizationBind-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/AutoOrganizationBind-test.tsx.snap deleted file mode 100644 index 7c32193e58c..00000000000 --- a/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/AutoOrganizationBind-test.tsx.snap +++ /dev/null @@ -1,50 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render correctly 1`] = ` -<form - id="bind-organization-form" - onSubmit={[Function]} -> - <OrganizationSelect - onChange={[Function]} - organization="foo" - organizations={ - Array [ - Object { - "key": "foo", - "name": "Foo", - }, - ] - } - /> - <Alert - className="abs-width-400 big-spacer-top" - display="block" - variant="info" - > - onboarding.import_organization.bind_members_not_sync_info_x.organization.github - <Link - className="spacer-left" - onlyActiveOnIndex={false} - style={Object {}} - target="_blank" - to={ - Object { - "pathname": "/documentation/organizations/manage-team/", - } - } - > - learn_more - </Link> - </Alert> - <div - className="display-flex-center big-spacer-top" - > - <SubmitButton - disabled={false} - > - onboarding.import_organization.bind - </SubmitButton> - </div> -</form> -`; 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 deleted file mode 100644 index 9b74fb68d47..00000000000 --- a/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/AutoOrganizationCreate-test.tsx.snap +++ /dev/null @@ -1,216 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should display choice between import or creation 1`] = ` -<div> - <OrganizationDetailsStep - finished={false} - onOpen={[MockFunction]} - open={true} - stepTitle="onboarding.import_organization.import_org_details" - > - <div - className="huge-spacer-bottom" - > - <p - className="display-flex-center big-spacer-bottom" - > - <FormattedMessage - defaultMessage="onboarding.import_organization_x" - id="onboarding.import_organization_x" - values={ - Object { - "avatar": <img - alt="GitHub" - className="little-spacer-left" - src="/images/sonarcloud/github.svg" - width={16} - />, - "name": <strong> - foo - </strong>, - } - } - /> - <ClearButton - className="little-spacer-left" - onClick={[MockFunction]} - /> - </p> - <RadioToggle - disabled={false} - name="filter" - onCheck={[Function]} - options={ - Array [ - Object { - "label": "onboarding.import_organization.create_new", - "value": "create", - }, - Object { - "label": "onboarding.import_organization.bind_existing", - "value": "bind", - }, - ] - } - value={null} - /> - </div> - </OrganizationDetailsStep> - <PlanStep - almApplication={ - Object { - "backgroundColor": "#444444", - "iconPath": "/images/sonarcloud/github-white.svg", - "installationUrl": "https://github.com/apps/greg-sonarcloud/installations/new", - "key": "github", - "name": "GitHub", - } - } - almOrganization={ - Object { - "almUrl": "https://github.com/foo", - "avatar": "http://example.com/avatar", - "description": "description-foo", - "key": "foo", - "name": "foo", - "personal": false, - "privateRepos": 0, - "publicRepos": 3, - "url": "http://example.com/foo", - } - } - createOrganization={[Function]} - onDone={[MockFunction]} - onUpgradeFail={[MockFunction]} - open={false} - subscriptionPlans={ - Array [ - Object { - "maxNcloc": 100000, - "price": 10, - }, - Object { - "maxNcloc": 250000, - "price": 75, - }, - ] - } - /> -</div> -`; - -exports[`should render prefilled and create org 1`] = ` -<div> - <OrganizationDetailsStep - finished={false} - onOpen={[MockFunction]} - open={true} - stepTitle="onboarding.import_organization.import_org_details" - > - <div - className="huge-spacer-bottom" - > - <p - className="display-flex-center big-spacer-bottom" - > - <FormattedMessage - defaultMessage="onboarding.import_organization_x" - id="onboarding.import_organization_x" - values={ - Object { - "avatar": <img - alt="GitHub" - className="little-spacer-left" - src="/images/sonarcloud/github.svg" - width={16} - />, - "name": <strong> - foo - </strong>, - } - } - /> - <ClearButton - className="little-spacer-left" - onClick={[MockFunction]} - /> - </p> - </div> - <OrganizationDetailsForm - infoBlock={ - <Alert - className="abs-width-600 big-spacer-top" - display="block" - variant="info" - > - <p> - onboarding.import_organization.members_sync_info_x.organization.github.foo.github - </p> - <a - href="http://github.com/orgs/thing/people" - rel="noopener noreferrer" - target="_blank" - > - organization.members.see_all_members_on_x.github - </a> - </Alert> - } - onContinue={[MockFunction]} - organization={ - Object { - "almUrl": "http://github.com/thing", - "avatar": "http://example.com/avatar", - "description": "description-foo", - "key": "foo", - "name": "foo", - "personal": false, - "privateRepos": 0, - "publicRepos": 3, - "url": "http://example.com/foo", - } - } - submitText="continue" - /> - </OrganizationDetailsStep> - <PlanStep - almApplication={ - Object { - "backgroundColor": "#444444", - "iconPath": "/images/sonarcloud/github-white.svg", - "installationUrl": "https://github.com/apps/greg-sonarcloud/installations/new", - "key": "github", - "name": "GitHub", - } - } - almOrganization={ - Object { - "almUrl": "http://github.com/thing", - "avatar": "http://example.com/avatar", - "description": "description-foo", - "key": "foo", - "name": "foo", - "personal": false, - "privateRepos": 0, - "publicRepos": 3, - "url": "http://example.com/foo", - } - } - createOrganization={[Function]} - onDone={[MockFunction]} - onUpgradeFail={[MockFunction]} - open={false} - subscriptionPlans={ - Array [ - Object { - "maxNcloc": 100000, - "price": 10, - }, - Object { - "maxNcloc": 250000, - "price": 75, - }, - ] - } - /> -</div> -`; 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 deleted file mode 100644 index 927b7d368f9..00000000000 --- a/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/CreateOrganization-test.tsx.snap +++ /dev/null @@ -1,489 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render with auto tab displayed 1`] = ` -<Fragment> - <Helmet - defer={false} - encodeSpecialCharacters={true} - title="onboarding.create_organization.page.header" - titleTemplate="%s" - /> - <div - className="page page-limited huge-spacer-top huge-spacer-bottom" - > - <A11ySkipTarget - anchor="create_org_main" - /> - <header - className="page-header huge-spacer-bottom" - > - <h1 - className="page-title huge big-spacer-bottom" - > - <strong> - onboarding.create_organization.page.header - </strong> - </h1> - <p - className="page-description" - > - onboarding.create_organization.page.description - </p> - </header> - <Tabs - onChange={[Function]} - selected="auto" - tabs={ - Array [ - Object { - "key": "auto", - "node": "onboarding.import_organization.import_from_x.github", - }, - Object { - "key": "manual", - "node": "onboarding.create_organization.create_manually", - }, - ] - } - /> - <ManualOrganizationCreate - className="hidden" - createOrganization={[MockFunction]} - handleOrgDetailsFinish={[Function]} - handleOrgDetailsStepOpen={[Function]} - onDone={[Function]} - onUpgradeFail={[Function]} - step={0} - subscriptionPlans={ - Array [ - Object { - "maxNcloc": 100000, - "price": 10, - }, - Object { - "maxNcloc": 250000, - "price": 75, - }, - ] - } - /> - <withRouter(RemoteOrganizationChoose) - almApplication={ - Object { - "backgroundColor": "blue", - "iconPath": "icon/path", - "installationUrl": "https://alm.installation.url", - "key": "github", - "name": "GitHub", - } - } - almUnboundApplications={Array []} - className="" - /> - </div> -</Fragment> -`; - -exports[`should render with auto tab selected and manual disabled 1`] = ` -<AlmApplicationInstalling - almKey="github" -/> -`; - -exports[`should render with auto tab selected and manual disabled 2`] = ` -<Fragment> - <Helmet - defer={false} - encodeSpecialCharacters={true} - title="onboarding.create_organization.page.header" - titleTemplate="%s" - /> - <div - className="page page-limited huge-spacer-top huge-spacer-bottom" - > - <A11ySkipTarget - anchor="create_org_main" - /> - <header - className="page-header huge-spacer-bottom" - > - <h1 - className="page-title huge big-spacer-bottom" - > - <strong> - onboarding.create_organization.page.header - </strong> - </h1> - <p - className="page-description" - > - onboarding.create_organization.page.description - </p> - </header> - <Tabs - onChange={[Function]} - selected="auto" - tabs={ - Array [ - Object { - "key": "auto", - "node": "onboarding.import_organization.import_from_x.github", - }, - Object { - "key": "manual", - "node": "onboarding.create_organization.create_manually", - }, - ] - } - /> - <ManualOrganizationCreate - className="hidden" - createOrganization={[MockFunction]} - handleOrgDetailsFinish={[Function]} - handleOrgDetailsStepOpen={[Function]} - onDone={[Function]} - onUpgradeFail={[Function]} - step={0} - subscriptionPlans={ - Array [ - Object { - "maxNcloc": 100000, - "price": 10, - }, - Object { - "maxNcloc": 250000, - "price": 75, - }, - ] - } - /> - <AutoOrganizationCreate - almApplication={ - Object { - "backgroundColor": "blue", - "iconPath": "icon/path", - "installationUrl": "https://alm.installation.url", - "key": "github", - "name": "GitHub", - } - } - almInstallId="foo" - almOrganization={ - Object { - "avatar": "my-avatar", - "description": "Continuous Code Quality", - "key": "sonarsource", - "name": "SonarSource", - "privateRepos": 0, - "publicRepos": 3, - "url": "https://www.sonarsource.com", - } - } - className="" - createOrganization={[MockFunction]} - handleCancelImport={[Function]} - handleOrgDetailsFinish={[Function]} - handleOrgDetailsStepOpen={[Function]} - onDone={[Function]} - onOrgCreated={[Function]} - onUpgradeFail={[Function]} - step={0} - subscriptionPlans={ - Array [ - Object { - "maxNcloc": 100000, - "price": 10, - }, - Object { - "maxNcloc": 250000, - "price": 75, - }, - ] - } - unboundOrganizations={ - Array [ - Object { - "actions": Object { - "admin": true, - }, - "key": "foo", - "name": "Foo", - }, - ] - } - /> - </div> -</Fragment> -`; - -exports[`should render with manual tab displayed 1`] = ` -<Fragment> - <Helmet - defer={false} - encodeSpecialCharacters={true} - title="onboarding.create_organization.page.header" - titleTemplate="%s" - /> - <div - className="page page-limited huge-spacer-top huge-spacer-bottom" - > - <A11ySkipTarget - anchor="create_org_main" - /> - <header - className="page-header huge-spacer-bottom" - > - <h1 - className="page-title huge big-spacer-bottom" - > - <strong> - onboarding.create_organization.page.header - </strong> - </h1> - <p - className="page-description" - > - onboarding.create_organization.page.description - </p> - </header> - <ManualOrganizationCreate - createOrganization={[MockFunction]} - handleOrgDetailsFinish={[Function]} - handleOrgDetailsStepOpen={[Function]} - onDone={[Function]} - onUpgradeFail={[Function]} - step={0} - subscriptionPlans={ - Array [ - Object { - "maxNcloc": 100000, - "price": 10, - }, - Object { - "maxNcloc": 250000, - "price": 75, - }, - ] - } - /> - </div> -</Fragment> -`; - -exports[`should render with organization bind page 1`] = ` -<AlmApplicationInstalling - almKey="github" -/> -`; - -exports[`should render with organization bind page 2`] = ` -<Fragment> - <Helmet - defer={false} - encodeSpecialCharacters={true} - title="onboarding.create_organization.page.header" - titleTemplate="%s" - /> - <div - className="page page-limited huge-spacer-top huge-spacer-bottom" - > - <A11ySkipTarget - anchor="create_org_main" - /> - <header - className="page-header huge-spacer-bottom" - > - <h1 - className="page-title huge big-spacer-bottom" - > - <strong> - onboarding.create_organization.page.header - </strong> - </h1> - <p - className="page-description" - > - onboarding.create_organization.page.description - </p> - </header> - <Tabs - onChange={[Function]} - selected="auto" - tabs={ - Array [ - Object { - "key": "auto", - "node": "onboarding.import_organization.import_from_x.github", - }, - Object { - "key": "manual", - "node": "onboarding.create_organization.create_manually", - }, - ] - } - /> - <ManualOrganizationCreate - className="hidden" - createOrganization={[MockFunction]} - handleOrgDetailsFinish={[Function]} - handleOrgDetailsStepOpen={[Function]} - onDone={[Function]} - onUpgradeFail={[Function]} - step={0} - subscriptionPlans={ - Array [ - Object { - "maxNcloc": 100000, - "price": 10, - }, - Object { - "maxNcloc": 250000, - "price": 75, - }, - ] - } - /> - <AutoOrganizationCreate - almApplication={ - Object { - "backgroundColor": "blue", - "iconPath": "icon/path", - "installationUrl": "https://alm.installation.url", - "key": "github", - "name": "GitHub", - } - } - almInstallId="foo" - almOrganization={ - Object { - "almUrl": "https://github.com/foo", - "avatar": "http://example.com/avatar", - "description": "description-foo", - "key": "foo", - "name": "foo", - "personal": false, - "privateRepos": 0, - "publicRepos": 3, - "url": "http://example.com/foo", - } - } - className="" - createOrganization={[MockFunction]} - handleCancelImport={[Function]} - handleOrgDetailsFinish={[Function]} - handleOrgDetailsStepOpen={[Function]} - onDone={[Function]} - onOrgCreated={[Function]} - onUpgradeFail={[Function]} - step={0} - subscriptionPlans={ - Array [ - Object { - "maxNcloc": 100000, - "price": 10, - }, - Object { - "maxNcloc": 250000, - "price": 75, - }, - ] - } - unboundOrganizations={ - Array [ - Object { - "actions": Object { - "admin": true, - }, - "key": "foo", - "name": "Foo", - }, - ] - } - /> - </div> -</Fragment> -`; - -exports[`should switch tabs 1`] = ` -<Fragment> - <Helmet - defer={false} - encodeSpecialCharacters={true} - title="onboarding.create_organization.page.header" - titleTemplate="%s" - /> - <div - className="page page-limited huge-spacer-top huge-spacer-bottom" - > - <A11ySkipTarget - anchor="create_org_main" - /> - <header - className="page-header huge-spacer-bottom" - > - <h1 - className="page-title huge big-spacer-bottom" - > - <strong> - onboarding.create_organization.page.header - </strong> - </h1> - <p - className="page-description" - > - onboarding.create_organization.page.description - </p> - </header> - <Tabs - onChange={[Function]} - selected="auto" - tabs={ - Array [ - Object { - "key": "auto", - "node": "onboarding.import_organization.import_from_x.github", - }, - Object { - "key": "manual", - "node": "onboarding.create_organization.create_manually", - }, - ] - } - /> - <ManualOrganizationCreate - className="hidden" - createOrganization={[MockFunction]} - handleOrgDetailsFinish={[Function]} - handleOrgDetailsStepOpen={[Function]} - onDone={[Function]} - onUpgradeFail={[Function]} - step={0} - subscriptionPlans={ - Array [ - Object { - "maxNcloc": 100000, - "price": 10, - }, - Object { - "maxNcloc": 250000, - "price": 75, - }, - ] - } - /> - <withRouter(RemoteOrganizationChoose) - almApplication={ - Object { - "backgroundColor": "blue", - "iconPath": "icon/path", - "installationUrl": "https://alm.installation.url", - "key": "github", - "name": "GitHub", - } - } - almUnboundApplications={Array []} - className="" - /> - </div> -</Fragment> -`; 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 deleted file mode 100644 index 9ede472a8d9..00000000000 --- a/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/ManualOrganizationCreate-test.tsx.snap +++ /dev/null @@ -1,84 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render and create organization 1`] = ` -<div> - <OrganizationDetailsStep - finished={false} - onOpen={[MockFunction]} - open={true} - > - <OrganizationDetailsForm - onContinue={[MockFunction]} - submitText="continue" - /> - </OrganizationDetailsStep> - <PlanStep - createOrganization={[Function]} - onDone={[MockFunction]} - onUpgradeFail={[MockFunction]} - open={false} - subscriptionPlans={ - Array [ - Object { - "maxNcloc": 100000, - "price": 10, - }, - Object { - "maxNcloc": 250000, - "price": 75, - }, - ] - } - /> -</div> -`; - -exports[`should render and create organization 2`] = ` -<div> - <OrganizationDetailsStep - finished={false} - onOpen={[MockFunction]} - open={false} - > - <OrganizationDetailsForm - onContinue={ - [MockFunction] { - "calls": Array [ - Array [ - Object { - "key": "foo", - "name": "Foo", - }, - ], - ], - "results": Array [ - Object { - "type": "return", - "value": undefined, - }, - ], - } - } - submitText="continue" - /> - </OrganizationDetailsStep> - <PlanStep - createOrganization={[Function]} - onDone={[MockFunction]} - onUpgradeFail={[MockFunction]} - open={true} - subscriptionPlans={ - Array [ - Object { - "maxNcloc": 100000, - "price": 10, - }, - Object { - "maxNcloc": 250000, - "price": 75, - }, - ] - } - /> -</div> -`; diff --git a/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/OrganizationDetailsForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/OrganizationDetailsForm-test.tsx.snap deleted file mode 100644 index b29f23245fe..00000000000 --- a/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/OrganizationDetailsForm-test.tsx.snap +++ /dev/null @@ -1,107 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render form 1`] = ` -<form - id="organization-form" - onSubmit={[Function]} -> - <OrganizationKeyInput - onChange={[Function]} - /> - <div - className="big-spacer-top" - > - <ResetButtonLink - onClick={[Function]} - > - onboarding.create_organization.add_additional_info - <DropdownIcon - className="little-spacer-left" - turned={false} - /> - </ResetButtonLink> - </div> - <div - className="js-additional-info" - hidden={true} - > - <div - className="big-spacer-top" - > - <label - htmlFor="organization-display-name" - > - <strong> - onboarding.create_organization.display_name - </strong> - </label> - <div - className="little-spacer-top" - > - <input - className="input-super-large text-middle" - id="organization-display-name" - maxLength={255} - onChange={[Function]} - type="text" - value="" - /> - </div> - <div - className="note abs-width-400" - > - onboarding.create_organization.display_name.description - </div> - </div> - <div - className="big-spacer-top" - > - <OrganizationAvatarInput - initialValue="" - name="" - onChange={[Function]} - /> - </div> - <div - className="big-spacer-top" - > - <label - htmlFor="organization-description" - > - <strong> - onboarding.create_organization.description - </strong> - </label> - <div - className="little-spacer-top" - > - <textarea - className="input-super-large text-middle" - id="organization-description" - maxLength={256} - onChange={[Function]} - rows={3} - value="" - /> - </div> - </div> - <div - className="big-spacer-top" - > - <OrganizationUrlInput - initialValue="" - onChange={[Function]} - /> - </div> - </div> - <div - className="display-flex-center big-spacer-top" - > - <SubmitButton - disabled={true} - > - continue - </SubmitButton> - </div> -</form> -`; diff --git a/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/OrganizationDetailsStep-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/OrganizationDetailsStep-test.tsx.snap deleted file mode 100644 index 3b15b770b40..00000000000 --- a/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/OrganizationDetailsStep-test.tsx.snap +++ /dev/null @@ -1,28 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render form 1`] = ` -<Step - finished={false} - onOpen={[MockFunction]} - open={true} - renderForm={[Function]} - renderResult={[Function]} - stepNumber={1} - stepTitle="onboarding.create_organization.enter_org_details" -/> -`; - -exports[`should render result 1`] = ` -<div - className="boxed-group-actions display-flex-center" -> - <AlertSuccessIcon - className="spacer-right" - /> - <strong - className="text-limited" - > - org - </strong> -</div> -`; diff --git a/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/PlanSelect-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/PlanSelect-test.tsx.snap deleted file mode 100644 index cb99c558c4a..00000000000 --- a/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/PlanSelect-test.tsx.snap +++ /dev/null @@ -1,47 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render and select 1`] = ` -<div - aria-label="onboarding.create_organization.choose_plan" - className="display-flex-row huge-spacer-bottom" - role="radiogroup" -> - <FreeCardPlan - disabled={false} - hasWarning={false} - key="free" - onClick={[Function]} - selected={true} - /> - <PaidCardPlan - isRecommended={false} - key="paid" - onClick={[Function]} - selected={false} - startingPrice={10} - /> -</div> -`; - -exports[`should render and select 2`] = ` -<div - aria-label="onboarding.create_organization.choose_plan" - className="display-flex-row huge-spacer-bottom" - role="radiogroup" -> - <FreeCardPlan - disabled={false} - hasWarning={false} - key="free" - onClick={[Function]} - selected={false} - /> - <PaidCardPlan - isRecommended={false} - key="paid" - onClick={[Function]} - selected={true} - startingPrice={10} - /> -</div> -`; diff --git a/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/PlanStep-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/PlanStep-test.tsx.snap deleted file mode 100644 index ca7cb711940..00000000000 --- a/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/PlanStep-test.tsx.snap +++ /dev/null @@ -1,163 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should preselect paid plan 1`] = ` -<div - className="boxed-group onboarding-step is-open" -> - <div - className="onboarding-step-number" - > - 2 - </div> - <div - className="boxed-group-header" - > - <h2> - onboarding.create_organization.enter_payment_details - </h2> - </div> - <div - className="" - > - <div - className="boxed-group-inner" - > - <PlanSelect - almOrganization={ - Object { - "almUrl": "https://github.com/foo", - "avatar": "http://example.com/avatar", - "description": "description-foo", - "key": "foo", - "name": "foo", - "personal": false, - "privateRepos": 5, - "publicRepos": 0, - "url": "http://example.com/foo", - } - } - onChange={[Function]} - plan="paid" - startingPrice={100} - /> - <Connect(withCurrentUser(BillingFormShim)) - onCommit={[MockFunction]} - onFailToUpgrade={[MockFunction]} - organizationKey={[MockFunction]} - subscriptionPlans={ - Array [ - Object { - "maxNcloc": 1000, - "price": 100, - }, - ] - } - > - <Component /> - </Connect(withCurrentUser(BillingFormShim))> - </div> - </div> -</div> -`; - -exports[`should render and use free plan 1`] = ` -<Step - finished={false} - onOpen={[Function]} - open={true} - renderForm={[Function]} - renderResult={[Function]} - stepNumber={2} - stepTitle="onboarding.create_organization.choose_plan" -/> -`; - -exports[`should render and use free plan 2`] = ` -<div - className="boxed-group onboarding-step is-open" -> - <div - className="onboarding-step-number" - > - 2 - </div> - <div - className="boxed-group-header" - > - <h2> - onboarding.create_organization.choose_plan - </h2> - </div> - <div - className="" - > - <div - className="boxed-group-inner" - > - <PlanSelect - onChange={[Function]} - plan="free" - startingPrice={100} - /> - <form - className="display-flex-center big-spacer-top" - id="organization-free-plan-form" - onSubmit={[Function]} - > - <SubmitButton - disabled={false} - > - my_account.create_organization - </SubmitButton> - </form> - </div> - </div> -</div> -`; - -exports[`should upgrade 1`] = ` -<div - className="boxed-group onboarding-step is-open" -> - <div - className="onboarding-step-number" - > - 2 - </div> - <div - className="boxed-group-header" - > - <h2> - onboarding.create_organization.choose_plan - </h2> - </div> - <div - className="" - > - <div - className="boxed-group-inner" - > - <PlanSelect - onChange={[Function]} - plan="paid" - startingPrice={100} - /> - <Connect(withCurrentUser(BillingFormShim)) - onCommit={[MockFunction]} - onFailToUpgrade={[MockFunction]} - organizationKey={[MockFunction]} - subscriptionPlans={ - Array [ - Object { - "maxNcloc": 1000, - "price": 100, - }, - ] - } - > - <Component /> - </Connect(withCurrentUser(BillingFormShim))> - </div> - </div> -</div> -`; diff --git a/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/RemoteOrganizationChoose-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/RemoteOrganizationChoose-test.tsx.snap deleted file mode 100644 index 946d551efa3..00000000000 --- a/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/RemoteOrganizationChoose-test.tsx.snap +++ /dev/null @@ -1,195 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should display already bound alert message 1`] = ` -<Alert - className="big-spacer-bottom width-60" - variant="error" -> - <FormattedMessage - defaultMessage="onboarding.import_organization.already_bound_x" - id="onboarding.import_organization.already_bound_x" - values={ - Object { - "avatar": <img - alt="GitHub" - className="little-spacer-left" - src="/images/sonarcloud/github.svg" - width={16} - />, - "boundAvatar": <OrganizationAvatar - className="little-spacer-left" - organization={ - Object { - "avatar": "bound-avatar", - "key": "bound", - "name": "Bound", - } - } - small={true} - />, - "boundName": <strong> - Bound - </strong>, - "name": <strong> - foo - </strong>, - } - } - /> -</Alert> -`; - -exports[`should display an alert message 1`] = ` -<Alert - className="big-spacer-bottom width-60" - variant="error" -> - <div - className="markdown" - > - onboarding.import_organization.org_not_found - <ul> - <li> - onboarding.import_organization.org_not_found.tips_1 - </li> - <li> - onboarding.import_organization.org_not_found.tips_2 - </li> - </ul> - </div> -</Alert> -`; - -exports[`should display unbound installations 1`] = ` -<div - className="boxed-group" -> - <div - className="boxed-group-header" - > - <h2> - onboarding.import_organization.import_org_details - </h2> - </div> - <div - className="boxed-group-inner" - > - <div - className="display-flex-center" - > - <div - className="display-inline-block" - > - <IdentityProviderLink - backgroundColor="blue" - className="display-inline-block" - iconPath="icon/path" - name="GitHub" - onClick={[Function]} - small={true} - url="https://alm.application.url" - > - onboarding.import_organization.choose_organization_button.github - </IdentityProviderLink> - </div> - <div - className="display-flex-stretch" - > - <div - className="vertical-pipe-separator" - > - <div - className="vertical-separator " - /> - <span - className="note" - > - or - </span> - <div - className="vertical-separator" - /> - </div> - <form - className="big-spacer-top big-spacer-bottom" - onSubmit={[Function]} - > - <div - className="form-field abs-width-400" - > - <label - className="text-normal" - htmlFor="select-unbound-installation" - > - onboarding.import_organization.choose_unbound_installation_x.github - </label> - <Select - className="input-super-large" - clearable={false} - id="select-unbound-installation" - labelKey="name" - onChange={[Function]} - optionRenderer={[Function]} - options={ - Array [ - Object { - "installationId": "12345", - "key": "foo", - "name": "Foo", - }, - ] - } - placeholder="onboarding.import_organization.choose_organization" - value="" - valueKey="installationId" - valueRenderer={[Function]} - /> - </div> - <SubmitButton - disabled={true} - > - continue - </SubmitButton> - </form> - </div> - </div> - </div> -</div> -`; - -exports[`should render 1`] = ` -<div - className="boxed-group" -> - <div - className="boxed-group-header" - > - <h2> - onboarding.import_organization.import_org_details - </h2> - </div> - <div - className="boxed-group-inner" - > - <div - className="display-flex-center" - > - <div - className="display-inline-block" - > - <IdentityProviderLink - backgroundColor="blue" - className="display-inline-block" - iconPath="icon/path" - name="GitHub" - onClick={[Function]} - small={true} - url="https://alm.application.url" - > - onboarding.import_organization.choose_organization_button.github - </IdentityProviderLink> - </div> - </div> - </div> -</div> -`; diff --git a/server/sonar-web/src/main/js/apps/create/organization/__tests__/actions-test.ts b/server/sonar-web/src/main/js/apps/create/organization/__tests__/actions-test.ts deleted file mode 100644 index 9248d38b46b..00000000000 --- a/server/sonar-web/src/main/js/apps/create/organization/__tests__/actions-test.ts +++ /dev/null @@ -1,79 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2020 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 { createOrganization, syncMembers } from '../../../../api/organizations'; -import { mockOrganization, mockOrganizationWithAlm } from '../../../../helpers/testMocks'; -import * as actions from '../actions'; - -jest.mock('../../../../api/alm-integration', () => ({ - bindAlmOrganization: jest.fn().mockResolvedValue({}) -})); - -jest.mock('../../../../api/organizations', () => ({ - createOrganization: jest.fn().mockResolvedValue({ key: 'foo', name: 'Foo' }), - updateOrganization: jest.fn().mockResolvedValue({}), - syncMembers: jest.fn() -})); - -const dispatch = jest.fn(); - -beforeEach(() => { - jest.clearAllMocks(); -}); - -describe('#createOrganization', () => { - it('should create and return an org key', async () => { - const org = mockOrganization(); - const promise = actions.createOrganization(org)(dispatch); - - expect(createOrganization).toHaveBeenCalledWith(org); - const returnValue = await promise; - expect(dispatch).toHaveBeenCalledWith({ organization: org, type: 'CREATE_ORGANIZATION' }); - expect(syncMembers).not.toBeCalled(); - expect(returnValue).toBe(org.key); - }); - - it('should create and sync members', async () => { - const { alm, ...org } = mockOrganizationWithAlm( - {}, - { key: 'github', membersSync: true, url: 'https://github.com/foo' } - ); - - (createOrganization as jest.Mock).mockResolvedValueOnce(org); - const promise = actions.createOrganization({ alm, ...org })(dispatch); - - expect(createOrganization).toHaveBeenCalledWith(org); - await promise; - expect(syncMembers).toHaveBeenCalledWith(org.key); - }); - - it('should not sync members for personal Github orgs', async () => { - const { alm, ...org } = mockOrganizationWithAlm( - {}, - { key: 'github', membersSync: true, personal: true, url: 'https://github.com/foo' } - ); - - (createOrganization as jest.Mock).mockResolvedValueOnce(org); - const promise = actions.createOrganization({ alm, ...org })(dispatch); - - expect(createOrganization).toHaveBeenCalledWith(org); - await promise; - expect(syncMembers).not.toBeCalled(); - }); -}); diff --git a/server/sonar-web/src/main/js/apps/create/organization/__tests__/utils-test.tsx b/server/sonar-web/src/main/js/apps/create/organization/__tests__/utils-test.tsx deleted file mode 100644 index 8ba2a9f1824..00000000000 --- a/server/sonar-web/src/main/js/apps/create/organization/__tests__/utils-test.tsx +++ /dev/null @@ -1,32 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2020 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 { formatPrice } from '../utils'; - -jest.mock('sonar-ui-common/helpers/urls', () => ({ - getHostUrl: () => 'http://host.url' -})); - -describe('#formatPrice', () => { - it('formats correctly', () => { - expect(formatPrice(10)).toBe('billing.price_format.10'); - expect(formatPrice(10000)).toBe('billing.price_format.10,000'); - expect(formatPrice(10000, true)).toBe('10,000'); - }); -}); diff --git a/server/sonar-web/src/main/js/apps/create/organization/actions.ts b/server/sonar-web/src/main/js/apps/create/organization/actions.ts deleted file mode 100644 index 4abaa682d0f..00000000000 --- a/server/sonar-web/src/main/js/apps/create/organization/actions.ts +++ /dev/null @@ -1,40 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2020 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 { Dispatch } from 'redux'; -import * as api from '../../../api/organizations'; -import { isGithub } from '../../../helpers/almIntegrations'; -import * as actions from '../../../store/organizations'; - -export function createOrganization({ - alm, - ...organization -}: T.Organization & { installationId?: string }) { - return (dispatch: Dispatch) => { - return api - .createOrganization({ ...organization, name: organization.name || organization.key }) - .then((newOrganization: T.Organization) => { - dispatch(actions.createOrganization({ ...newOrganization, alm })); - if (alm && alm.membersSync && !alm.personal && isGithub(alm.key)) { - api.syncMembers(newOrganization.key); - } - return newOrganization.key; - }); - }; -} diff --git a/server/sonar-web/src/main/js/apps/create/organization/utils.ts b/server/sonar-web/src/main/js/apps/create/organization/utils.ts deleted file mode 100644 index e0d5ca58124..00000000000 --- a/server/sonar-web/src/main/js/apps/create/organization/utils.ts +++ /dev/null @@ -1,80 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2020 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 { memoize } from 'lodash'; -import { translateWithParameters } from 'sonar-ui-common/helpers/l10n'; -import { formatMeasure } from 'sonar-ui-common/helpers/measures'; -import { cleanQuery, parseAsOptionalString, serializeString } from 'sonar-ui-common/helpers/query'; -import { decodeJwt } from 'sonar-ui-common/helpers/strings'; -import { isBitbucket, isGithub } from '../../../helpers/almIntegrations'; - -export const ORGANIZATION_IMPORT_BINDING_IN_PROGRESS_TIMESTAMP = - 'sonarcloud.import_org.binding_in_progress'; - -export const ORGANIZATION_IMPORT_REDIRECT_TO_PROJECT_TIMESTAMP = - 'sonarcloud.import_org.redirect_to_projects'; - -export const BIND_ORGANIZATION_KEY = 'sonarcloud.bind_org.key'; - -export const BIND_ORGANIZATION_REDIRECT_TO_ORG_TIMESTAMP = 'sonarcloud.bind_org.redirect_to_org'; - -export enum Step { - OrganizationDetails, - Plan -} - -export function formatPrice(price?: number, noSign?: boolean) { - const priceFormatted = formatMeasure(price, 'FLOAT') - .replace(/[.|,]0$/, '') - .replace(/([.|,]\d)$/, '$10'); - return noSign ? priceFormatted : translateWithParameters('billing.price_format', priceFormatted); -} - -export interface Query { - almInstallId?: string; - almKey?: string; -} - -export const parseQuery = memoize( - (urlQuery: T.RawQuery = {}): Query => { - let almInstallId = undefined; - let almKey = undefined; - - if (urlQuery['installation_id']) { - almKey = 'github'; - almInstallId = parseAsOptionalString(urlQuery['installation_id']); - } else if (urlQuery['clientKey']) { - almKey = 'bitbucket'; - almInstallId = parseAsOptionalString(urlQuery['clientKey']); - } else if (urlQuery['jwt']) { - const jwt = decodeJwt(urlQuery['jwt']); - if (jwt && jwt.iss) { - almKey = 'bitbucket'; - almInstallId = jwt.iss; - } - } - return { almInstallId, almKey }; - } -); - -export const serializeQuery = (query: Query): T.RawQuery => - cleanQuery({ - installation_id: isGithub(query.almKey) ? serializeString(query.almInstallId) : undefined, - clientKey: isBitbucket(query.almKey) ? serializeString(query.almInstallId) : undefined - }); |