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 | |
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')
65 files changed, 0 insertions, 6088 deletions
diff --git a/server/sonar-web/src/main/js/apps/create/components/BillingFormShim.tsx b/server/sonar-web/src/main/js/apps/create/components/BillingFormShim.tsx deleted file mode 100644 index bc3cf8b9874..00000000000 --- a/server/sonar-web/src/main/js/apps/create/components/BillingFormShim.tsx +++ /dev/null @@ -1,47 +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'; - -interface ChildrenProps { - onSubmit: React.FormEventHandler; - processingUpgrade: boolean; - renderFormFields: () => React.ReactNode; - renderNextCharge: (className?: string) => React.ReactNode; - renderRecap: () => React.ReactNode; - renderSubmitButton: (submitText?: string) => React.ReactNode; - renderSubmitGroup: (submitText?: string) => React.ReactNode; -} - -interface Props { - children: (props: ChildrenProps) => React.ReactNode; - initialCountry?: string; - currentUser: T.CurrentUser; - onCommit: () => void | Promise<void>; - onFailToUpgrade?: () => void; - organizationKey: string | (() => Promise<string>); - subscriptionPlans: T.SubscriptionPlan[]; -} - -export default class BillingFormShim extends React.Component<Props> { - render() { - const { BillingForm } = (window as any).SonarBilling; - return <BillingForm {...this.props} />; - } -} diff --git a/server/sonar-web/src/main/js/apps/create/components/FreeCardPlan.tsx b/server/sonar-web/src/main/js/apps/create/components/FreeCardPlan.tsx deleted file mode 100644 index 6d7f0aaab3e..00000000000 --- a/server/sonar-web/src/main/js/apps/create/components/FreeCardPlan.tsx +++ /dev/null @@ -1,66 +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 RadioCard, { RadioCardProps } from 'sonar-ui-common/components/controls/RadioCard'; -import { Alert } from 'sonar-ui-common/components/ui/Alert'; -import { translate } from 'sonar-ui-common/helpers/l10n'; -import { formatPrice } from '../organization/utils'; - -interface Props extends RadioCardProps { - almName?: string; - hasWarning: boolean; -} - -export default function FreeCardPlan({ almName, hasWarning, ...props }: Props) { - const showInfo = almName && props.disabled; - const showWarning = almName && hasWarning && !props.disabled; - - return ( - <RadioCard title={translate('billing.free_plan.title')} titleInfo={formatPrice(0)} {...props}> - <div className="spacer-left"> - <ul className="big-spacer-left note"> - <li className="little-spacer-bottom"> - {translate('billing.free_plan.all_projects_analyzed_public')} - </li> - <li>{translate('billing.free_plan.anyone_can_browse_source_code')}</li> - </ul> - </div> - {showWarning && ( - <Alert variant="warning"> - <FormattedMessage - defaultMessage={translate('billing.free_plan.private_repo_warning')} - id="billing.free_plan.private_repo_warning" - values={{ alm: almName }} - /> - </Alert> - )} - {showInfo && ( - <Alert variant="info"> - <FormattedMessage - defaultMessage={translate('billing.free_plan.not_available_info')} - id="billing.free_plan.not_available_info" - values={{ alm: almName }} - /> - </Alert> - )} - </RadioCard> - ); -} diff --git a/server/sonar-web/src/main/js/apps/create/components/OrganizationAvatarInput.tsx b/server/sonar-web/src/main/js/apps/create/components/OrganizationAvatarInput.tsx deleted file mode 100644 index 78495b31f6a..00000000000 --- a/server/sonar-web/src/main/js/apps/create/components/OrganizationAvatarInput.tsx +++ /dev/null @@ -1,112 +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 React from 'react'; -import ValidationInput from 'sonar-ui-common/components/controls/ValidationInput'; -import { translate } from 'sonar-ui-common/helpers/l10n'; -import { isWebUri } from 'valid-url'; -import OrganizationAvatar from '../../../components/common/OrganizationAvatar'; - -interface Props { - initialValue?: string; - name?: string; - onChange: (value: string | undefined) => void; -} - -interface State { - editing: boolean; - error?: string; - touched: boolean; - value: string; -} - -export default class OrganizationAvatarInput extends React.PureComponent<Props, State> { - state: State = { error: undefined, editing: false, touched: false, value: '' }; - - componentDidMount() { - if (this.props.initialValue) { - const value = this.props.initialValue; - const error = this.validateUrl(value); - this.setState({ error, touched: Boolean(error), value }); - } - } - - handleChange = (event: React.ChangeEvent<HTMLInputElement>) => { - const value = event.currentTarget.value.trim(); - const error = this.validateUrl(value); - this.setState({ error, touched: true, value }); - this.props.onChange(error === undefined ? value : undefined); - }; - - handleBlur = () => { - this.setState({ editing: false }); - }; - - handleFocus = () => { - this.setState({ editing: true }); - }; - - validateUrl(url: string) { - if (url.length > 0 && !isWebUri(url)) { - return translate('onboarding.create_organization.url.error'); - } - return undefined; - } - - render() { - const isInvalid = this.state.touched && !this.state.editing && this.state.error !== undefined; - const isValidUrl = this.state.error === undefined && this.state.value !== ''; - const isValid = this.state.touched && isValidUrl; - return ( - <ValidationInput - description={translate('onboarding.create_organization.avatar.description')} - error={this.state.error} - id="organization-avatar" - isInvalid={isInvalid} - isValid={isValid} - label={translate('onboarding.create_organization.avatar')}> - <> - {(isValidUrl || this.props.name) && ( - <OrganizationAvatar - className="display-block spacer-bottom" - organization={{ - avatar: isValidUrl ? this.state.value : undefined, - name: this.props.name || '' - }} - /> - )} - <input - className={classNames('input-super-large', 'text-middle', { - 'is-invalid': isInvalid, - 'is-valid': isValid - })} - id="organization-avatar" - onBlur={this.handleBlur} - onChange={this.handleChange} - onFocus={this.handleFocus} - placeholder={translate('onboarding.create_organization.avatar.placeholder')} - type="text" - value={this.state.value} - /> - </> - </ValidationInput> - ); - } -} diff --git a/server/sonar-web/src/main/js/apps/create/components/OrganizationKeyInput.tsx b/server/sonar-web/src/main/js/apps/create/components/OrganizationKeyInput.tsx deleted file mode 100644 index f1f88be187b..00000000000 --- a/server/sonar-web/src/main/js/apps/create/components/OrganizationKeyInput.tsx +++ /dev/null @@ -1,135 +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 { debounce } from 'lodash'; -import * as React from 'react'; -import ValidationInput from 'sonar-ui-common/components/controls/ValidationInput'; -import { translate } from 'sonar-ui-common/helpers/l10n'; -import { getHostUrl } from 'sonar-ui-common/helpers/urls'; -import { getOrganization } from '../../../api/organizations'; - -interface Props { - initialValue?: string; - onChange: (value: string | undefined) => void; -} - -interface State { - error?: string; - touched: boolean; - validating: boolean; - value: string; -} - -export default class OrganizationKeyInput extends React.PureComponent<Props, State> { - mounted = false; - constructor(props: Props) { - super(props); - this.state = { error: undefined, touched: false, validating: false, value: '' }; - this.checkFreeKey = debounce(this.checkFreeKey, 250); - } - - componentDidMount() { - this.mounted = true; - if (this.props.initialValue !== undefined) { - this.setState({ value: this.props.initialValue }); - this.validateKey(this.props.initialValue); - } - } - - componentWillUnmount() { - this.mounted = false; - } - - checkFreeKey = (key: string) => { - this.setState({ validating: true }); - return getOrganization(key) - .then(organization => { - if (this.mounted) { - if (organization === undefined) { - this.setState({ error: undefined, validating: false }); - this.props.onChange(key); - } else { - this.setState({ - error: translate('onboarding.create_organization.organization_name.taken'), - touched: true, - validating: false - }); - this.props.onChange(undefined); - } - } - }) - .catch(() => { - if (this.mounted) { - this.setState({ error: undefined, validating: false }); - this.props.onChange(key); - } - }); - }; - - handleChange = (event: React.ChangeEvent<HTMLInputElement>) => { - const { value } = event.currentTarget; - this.setState({ touched: true, value }); - this.validateKey(value); - }; - - validateKey(key: string) { - if (key.length > 255 || !/^[a-z0-9][a-z0-9-]*[a-z0-9]?$/.test(key)) { - this.setState({ - error: translate('onboarding.create_organization.organization_name.error'), - touched: true - }); - this.props.onChange(undefined); - } else { - this.checkFreeKey(key); - } - } - - render() { - const isInvalid = this.state.touched && this.state.error !== undefined; - const isValid = this.state.touched && !this.state.validating && this.state.error === undefined; - return ( - <ValidationInput - error={this.state.error} - id="organization-key" - isInvalid={isInvalid} - isValid={isValid} - label={translate('onboarding.create_organization.organization_name')} - required={true}> - <div className="display-inline-flex-baseline"> - <span className="little-spacer-right"> - {getHostUrl().replace(/https*:\/\//, '') + '/organizations/'} - </span> - <input - autoFocus={true} - className={classNames('input-super-large', { - 'is-invalid': isInvalid, - 'is-valid': isValid - })} - id="organization-key" - maxLength={255} - onChange={this.handleChange} - type="text" - value={this.state.value} - /> - </div> - </ValidationInput> - ); - } -} diff --git a/server/sonar-web/src/main/js/apps/create/components/OrganizationSelect.tsx b/server/sonar-web/src/main/js/apps/create/components/OrganizationSelect.tsx deleted file mode 100644 index 34baa319fc5..00000000000 --- a/server/sonar-web/src/main/js/apps/create/components/OrganizationSelect.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 { sortBy } from 'lodash'; -import * as React from 'react'; -import Select from 'sonar-ui-common/components/controls/Select'; -import { translate } from 'sonar-ui-common/helpers/l10n'; -import { getBaseUrl } from 'sonar-ui-common/helpers/urls'; -import { sanitizeAlmId } from '../../../helpers/almIntegrations'; - -interface Props { - hideIcons?: boolean; - onChange: (organization: T.Organization) => void; - organization: string; - organizations: T.Organization[]; -} - -export default function OrganizationSelect({ - hideIcons, - onChange, - organization, - organizations -}: Props) { - const optionRenderer = getOptionRenderer(hideIcons); - return ( - <Select - autoFocus={!organization} - className="input-super-large" - clearable={false} - id="select-organization" - labelKey="name" - onChange={onChange} - optionRenderer={optionRenderer} - options={sortBy(organizations, o => o.name.toLowerCase())} - placeholder={translate('onboarding.import_organization.choose_organization')} - required={true} - value={organization} - valueKey="key" - valueRenderer={optionRenderer} - /> - ); -} - -export function getOptionRenderer(hideIcons?: boolean) { - return function optionRenderer(organization: T.Organization) { - const icon = organization.alm - ? `sonarcloud/${sanitizeAlmId(organization.alm.key)}` - : 'sonarcloud-square-logo'; - const isPaidOrg = organization.subscription === 'PAID'; - return ( - <div className="display-flex-space-between"> - <span className="text-ellipsis flex-1"> - {!hideIcons && ( - <img - alt={organization.alm ? organization.alm.key : 'SonarCloud'} - className="little-spacer-right" - height={14} - src={`${getBaseUrl()}/images/${icon}.svg`} - /> - )} - {organization.name} - <span className="note little-spacer-left">{organization.key}</span> - </span> - {isPaidOrg && <div className="badge">{translate('organization.paid_plan.badge')}</div>} - </div> - ); - }; -} diff --git a/server/sonar-web/src/main/js/apps/create/components/OrganizationUrlInput.tsx b/server/sonar-web/src/main/js/apps/create/components/OrganizationUrlInput.tsx deleted file mode 100644 index 5f716947f02..00000000000 --- a/server/sonar-web/src/main/js/apps/create/components/OrganizationUrlInput.tsx +++ /dev/null @@ -1,96 +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 React from 'react'; -import ValidationInput from 'sonar-ui-common/components/controls/ValidationInput'; -import { translate } from 'sonar-ui-common/helpers/l10n'; -import { isWebUri } from 'valid-url'; - -interface Props { - initialValue?: string; - onChange: (value: string | undefined) => void; -} - -interface State { - editing: boolean; - error?: string; - touched: boolean; - value: string; -} - -export default class OrganizationUrlInput extends React.PureComponent<Props, State> { - state: State = { error: undefined, editing: false, touched: false, value: '' }; - - componentDidMount() { - if (this.props.initialValue) { - const value = this.props.initialValue; - const error = this.validateUrl(value); - this.setState({ error, touched: Boolean(error), value }); - } - } - - handleChange = (event: React.ChangeEvent<HTMLInputElement>) => { - const value = event.currentTarget.value.trim(); - const error = this.validateUrl(value); - this.setState({ error, touched: true, value }); - this.props.onChange(error === undefined ? value : undefined); - }; - - handleBlur = () => { - this.setState({ editing: false }); - }; - - handleFocus = () => { - this.setState({ editing: true }); - }; - - validateUrl(url: string) { - if (url.length > 0 && !isWebUri(url)) { - return translate('onboarding.create_organization.url.error'); - } - return undefined; - } - - render() { - const isInvalid = this.state.touched && !this.state.editing && this.state.error !== undefined; - const isValid = this.state.touched && this.state.error === undefined && this.state.value !== ''; - return ( - <ValidationInput - error={this.state.error} - id="organization-url" - isInvalid={isInvalid} - isValid={isValid} - label={translate('onboarding.create_organization.url')}> - <input - className={classNames('input-super-large', 'text-middle', { - 'is-invalid': isInvalid, - 'is-valid': isValid - })} - id="organization-url" - onBlur={this.handleBlur} - onChange={this.handleChange} - onFocus={this.handleFocus} - type="text" - value={this.state.value} - /> - </ValidationInput> - ); - } -} diff --git a/server/sonar-web/src/main/js/apps/create/components/PaidCardPlan.tsx b/server/sonar-web/src/main/js/apps/create/components/PaidCardPlan.tsx deleted file mode 100644 index a8150a08a85..00000000000 --- a/server/sonar-web/src/main/js/apps/create/components/PaidCardPlan.tsx +++ /dev/null @@ -1,58 +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 { Link } from 'react-router'; -import RadioCard, { RadioCardProps } from 'sonar-ui-common/components/controls/RadioCard'; -import { translate } from 'sonar-ui-common/helpers/l10n'; -import { formatPrice } from '../organization/utils'; -import UpgradeOrganizationAdvantages from './UpgradeOrganizationAdvantages'; - -interface Props extends RadioCardProps { - isRecommended: boolean; - startingPrice?: number; -} - -export default function PaidCardPlan({ isRecommended, startingPrice, ...props }: Props) { - return ( - <RadioCard - recommended={isRecommended ? translate('billing.paid_plan.recommended') : undefined} - title={translate('billing.paid_plan.title')} - titleInfo={ - startingPrice !== undefined && ( - <FormattedMessage - defaultMessage={translate('billing.price_from_x')} - id="billing.price_from_x" - values={{ - price: <span className="big">{formatPrice(startingPrice)}</span> - }} - /> - ) - } - {...props}> - <UpgradeOrganizationAdvantages /> - <div className="big-spacer-left"> - <Link className="spacer-left" target="_blank" to="/about/pricing"> - {translate('billing.pricing.learn_more')} - </Link> - </div> - </RadioCard> - ); -} diff --git a/server/sonar-web/src/main/js/apps/create/components/UpgradeOrganizationAdvantages.tsx b/server/sonar-web/src/main/js/apps/create/components/UpgradeOrganizationAdvantages.tsx deleted file mode 100644 index 6cfe7cf1526..00000000000 --- a/server/sonar-web/src/main/js/apps/create/components/UpgradeOrganizationAdvantages.tsx +++ /dev/null @@ -1,49 +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 CheckIcon from 'sonar-ui-common/components/icons/CheckIcon'; -import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n'; -import { colors } from '../../../app/theme'; - -const TRIAL_DURATION_DAYS = 14; - -export default function UpgradeOrganizationAdvantages() { - return ( - <ul className="note"> - <Advantage>{translate('billing.upgrade_box.unlimited_private_projects')}</Advantage> - <Advantage>{translate('billing.upgrade_box.strict_control_private_data')}</Advantage> - <Advantage>{translate('billing.upgrade_box.cancel_anytime')}</Advantage> - <Advantage> - <strong> - {translateWithParameters('billing.upgrade_box.free_trial_x', TRIAL_DURATION_DAYS)} - </strong> - </Advantage> - </ul> - ); -} - -export function Advantage({ children }: { children: React.ReactNode }) { - return ( - <li className="display-flex-center little-spacer-bottom"> - <CheckIcon className="spacer-right" fill={colors.lightGreen} /> - {children} - </li> - ); -} diff --git a/server/sonar-web/src/main/js/apps/create/components/UpgradeOrganizationBox.tsx b/server/sonar-web/src/main/js/apps/create/components/UpgradeOrganizationBox.tsx deleted file mode 100644 index c5466166692..00000000000 --- a/server/sonar-web/src/main/js/apps/create/components/UpgradeOrganizationBox.tsx +++ /dev/null @@ -1,127 +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 { Link } from 'react-router'; -import { Button } from 'sonar-ui-common/components/controls/buttons'; -import RadioCard from 'sonar-ui-common/components/controls/RadioCard'; -import { hasMessage, translate } from 'sonar-ui-common/helpers/l10n'; -import { getSubscriptionPlans } from '../../../api/billing'; -import { formatPrice } from '../organization/utils'; -import UpgradeOrganizationAdvantages from './UpgradeOrganizationAdvantages'; -import UpgradeOrganizationModal from './UpgradeOrganizationModal'; - -interface Props { - className?: string; - insideModal?: boolean; - onOrganizationUpgrade: () => void; - organization: T.Organization; -} - -interface State { - subscriptionPlans: T.SubscriptionPlan[]; - upgradeOrganizationModal: boolean; -} - -export default class UpgradeOrganizationBox extends React.PureComponent<Props, State> { - mounted = false; - state: State = { subscriptionPlans: [], upgradeOrganizationModal: false }; - - componentDidMount() { - this.mounted = true; - this.fetchSubscriptionPlans(); - } - - componentWillUnmount() { - this.mounted = false; - } - - fetchSubscriptionPlans = () => { - return getSubscriptionPlans().then(subscriptionPlans => { - if (this.mounted) { - this.setState({ subscriptionPlans }); - } - }); - }; - - handleUpgradeClick = () => { - this.setState({ upgradeOrganizationModal: true }); - }; - - handleUpgradeOrganizationModalClose = () => { - if (this.mounted) { - this.setState({ upgradeOrganizationModal: false }); - } - }; - - handleOrganizationUpgrade = () => { - this.props.onOrganizationUpgrade(); - this.handleUpgradeOrganizationModalClose(); - }; - - render() { - if (!hasMessage('billing.upgrade_box.header')) { - return null; - } - - const { subscriptionPlans, upgradeOrganizationModal } = this.state; - const startingPrice = subscriptionPlans[0] && subscriptionPlans[0].price; - - return ( - <> - <RadioCard - className={this.props.className} - title={translate('billing.upgrade_box.header')} - titleInfo={ - startingPrice !== undefined && ( - <FormattedMessage - defaultMessage={translate('billing.price_from_x')} - id="billing.price_from_x" - values={{ - price: <span className="big">{formatPrice(startingPrice)}</span> - }} - /> - ) - }> - <> - <UpgradeOrganizationAdvantages /> - <div className="big-spacer-left"> - <Button className="js-upgrade-organization" onClick={this.handleUpgradeClick}> - {translate('billing.paid_plan.upgrade')} - </Button> - <Link className="spacer-left" target="_blank" to="/about/pricing"> - {translate('billing.pricing.learn_more')} - </Link> - </div> - </> - </RadioCard> - {upgradeOrganizationModal && ( - <UpgradeOrganizationModal - insideModal={this.props.insideModal} - onClose={this.handleUpgradeOrganizationModalClose} - onUpgradeDone={this.handleOrganizationUpgrade} - organization={this.props.organization} - subscriptionPlans={subscriptionPlans} - /> - )} - </> - ); - } -} diff --git a/server/sonar-web/src/main/js/apps/create/components/UpgradeOrganizationModal.tsx b/server/sonar-web/src/main/js/apps/create/components/UpgradeOrganizationModal.tsx deleted file mode 100644 index 7e1b63ae176..00000000000 --- a/server/sonar-web/src/main/js/apps/create/components/UpgradeOrganizationModal.tsx +++ /dev/null @@ -1,127 +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 { ResetButtonLink } from 'sonar-ui-common/components/controls/buttons'; -import Modal from 'sonar-ui-common/components/controls/Modal'; -import DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner'; -import { translate } from 'sonar-ui-common/helpers/l10n'; -import { withCurrentUser } from '../../../components/hoc/withCurrentUser'; -import { getExtensionStart } from '../../../helpers/extensions'; -import BillingFormShim from './BillingFormShim'; -import UpgradeOrganizationAdvantages from './UpgradeOrganizationAdvantages'; - -const BillingForm = withCurrentUser(BillingFormShim); - -interface Props { - insideModal?: boolean; - onUpgradeDone: () => void; - onClose: () => void; - organization: T.Organization; - subscriptionPlans: T.SubscriptionPlan[]; -} - -interface State { - ready: boolean; -} - -export default class UpgradeOrganizationModal extends React.PureComponent<Props, State> { - mounted = false; - state: State = { ready: false }; - - componentDidMount() { - this.mounted = true; - getExtensionStart('billing/billing').then( - () => { - if (this.mounted) { - this.setState({ ready: true }); - } - }, - () => {} - ); - } - - componentWillUnmount() { - this.mounted = false; - } - - render() { - const header = translate('billing.upgrade_box.upgrade_to_paid_plan'); - - if (!this.state.ready) { - return null; - } - - return ( - <Modal - contentLabel={header} - noBackdrop={this.props.insideModal} - onRequestClose={this.props.onClose} - shouldCloseOnOverlayClick={false} - size="medium"> - <div className="modal-head"> - <h2>{header}</h2> - </div> - <BillingForm - onCommit={this.props.onUpgradeDone} - organizationKey={this.props.organization.key} - subscriptionPlans={this.props.subscriptionPlans}> - {({ - onSubmit, - processingUpgrade, - renderFormFields, - renderNextCharge, - renderRecap, - renderSubmitButton - }) => ( - <form id="organization-paid-plan-form" onSubmit={onSubmit}> - <div className="modal-body modal-container"> - <div className="huge-spacer-bottom"> - <p className="spacer-bottom"> - <FormattedMessage - defaultMessage={translate('billing.upgrade.org_x_advantages')} - id="billing.coupon.description" - values={{ - org: <strong>{this.props.organization.name}</strong> - }} - /> - </p> - <UpgradeOrganizationAdvantages /> - </div> - {renderFormFields()} - <div className="big-spacer-top">{renderRecap()}</div> - </div> - <footer className="modal-foot display-flex-center display-flex-space-between"> - {renderNextCharge() || <span />} - <div> - <DeferredSpinner loading={processingUpgrade} /> - {renderSubmitButton()} - <ResetButtonLink onClick={this.props.onClose}> - {translate('cancel')} - </ResetButtonLink> - </div> - </footer> - </form> - )} - </BillingForm> - </Modal> - ); - } -} diff --git a/server/sonar-web/src/main/js/apps/create/components/__mocks__/BillingFormShim.tsx b/server/sonar-web/src/main/js/apps/create/components/__mocks__/BillingFormShim.tsx deleted file mode 100644 index ce16888ffaf..00000000000 --- a/server/sonar-web/src/main/js/apps/create/components/__mocks__/BillingFormShim.tsx +++ /dev/null @@ -1,38 +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'; - -export default class BillingFormShim extends React.Component<{ children: any }> { - render() { - return ( - <div id="BillingFormShim"> - {this.props.children({ - onSubmit: jest.fn(), - processingUpgrade: true, - renderFormFields: () => <div id="form-fields" />, - renderNextCharge: () => <div id="form-next-charge" />, - renderRecap: () => <div id="form-recap" />, - renderSubmitButton: () => <div id="form-submit" />, - renderSubmitGroup: () => <div id="submit-group" /> - })} - </div> - ); - } -} diff --git a/server/sonar-web/src/main/js/apps/create/components/__tests__/BillingFormShim-test.tsx b/server/sonar-web/src/main/js/apps/create/components/__tests__/BillingFormShim-test.tsx deleted file mode 100644 index 1fb03480e3f..00000000000 --- a/server/sonar-web/src/main/js/apps/create/components/__tests__/BillingFormShim-test.tsx +++ /dev/null @@ -1,48 +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 BillingFormShim from '../BillingFormShim'; - -beforeAll(() => { - function BillingForm() { - return <div id="billing-form" />; - } - - (window as any).SonarBilling = { BillingForm }; -}); - -afterAll(() => { - delete (window as any).SonarBilling; -}); - -it('should render', () => { - expect( - shallow( - <BillingFormShim - currentUser={{ isLoggedIn: false }} - onCommit={jest.fn()} - organizationKey="org" - subscriptionPlans={[]}> - {() => <div id="inner-billing-form" />} - </BillingFormShim> - ) - ).toMatchSnapshot(); -}); diff --git a/server/sonar-web/src/main/js/apps/create/components/__tests__/FreeCardPlan-test.tsx b/server/sonar-web/src/main/js/apps/create/components/__tests__/FreeCardPlan-test.tsx deleted file mode 100644 index f3bcba95619..00000000000 --- a/server/sonar-web/src/main/js/apps/create/components/__tests__/FreeCardPlan-test.tsx +++ /dev/null @@ -1,38 +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 FreeCardPlan from '../FreeCardPlan'; - -it('should render', () => { - expect(shallow(<FreeCardPlan hasWarning={false} />)).toMatchSnapshot(); -}); - -it('should render with warning', () => { - expect( - shallow(<FreeCardPlan almName="GitHub" hasWarning={true} selected={true} />) - ).toMatchSnapshot(); -}); - -it('should render disabled with info', () => { - expect( - shallow(<FreeCardPlan almName="GitHub" disabled={true} hasWarning={false} />) - ).toMatchSnapshot(); -}); diff --git a/server/sonar-web/src/main/js/apps/create/components/__tests__/OrganizationAvatarInput-test.tsx b/server/sonar-web/src/main/js/apps/create/components/__tests__/OrganizationAvatarInput-test.tsx deleted file mode 100644 index 55a2ca81896..00000000000 --- a/server/sonar-web/src/main/js/apps/create/components/__tests__/OrganizationAvatarInput-test.tsx +++ /dev/null @@ -1,45 +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 OrganizationAvatarInput from '../OrganizationAvatarInput'; - -it('should render correctly', () => { - const wrapper = shallow( - <OrganizationAvatarInput initialValue="https://my.avatar" onChange={jest.fn()} /> - ); - expect(wrapper).toMatchSnapshot(); - wrapper.setState({ touched: true }); - expect(wrapper.find('ValidationInput').prop('isValid')).toMatchSnapshot(); -}); - -it('should have an error when the avatar url is not valid', () => { - expect( - shallow(<OrganizationAvatarInput initialValue="whatever" onChange={jest.fn()} />) - .find('ValidationInput') - .prop('isInvalid') - ).toBe(true); -}); - -it('should display the fallback avatar when there is no url', () => { - expect( - shallow(<OrganizationAvatarInput initialValue="" name="Luke Skywalker" onChange={jest.fn()} />) - ).toMatchSnapshot(); -}); diff --git a/server/sonar-web/src/main/js/apps/create/components/__tests__/OrganizationKeyInput-test.tsx b/server/sonar-web/src/main/js/apps/create/components/__tests__/OrganizationKeyInput-test.tsx deleted file mode 100644 index 667114a0da0..00000000000 --- a/server/sonar-web/src/main/js/apps/create/components/__tests__/OrganizationKeyInput-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 { waitAndUpdate } from 'sonar-ui-common/helpers/testUtils'; -import { getOrganization } from '../../../../api/organizations'; -import OrganizationKeyInput from '../OrganizationKeyInput'; - -jest.mock('../../../../api/organizations', () => ({ - getOrganization: jest.fn().mockResolvedValue(undefined) -})); - -beforeEach(() => { - (getOrganization as jest.Mock<any>).mockClear(); -}); - -it('should render correctly', async () => { - const wrapper = shallow(<OrganizationKeyInput initialValue="key" onChange={jest.fn()} />); - expect(wrapper).toMatchSnapshot(); - wrapper.setState({ touched: true }); - await waitAndUpdate(wrapper); - expect(wrapper.find('ValidationInput').prop('isValid')).toBe(true); -}); - -it('should not display any status when the key is not defined', async () => { - const wrapper = shallow(<OrganizationKeyInput onChange={jest.fn()} />); - await waitAndUpdate(wrapper); - expect(wrapper.find('ValidationInput').prop('isInvalid')).toBe(false); - expect(wrapper.find('ValidationInput').prop('isValid')).toBe(false); -}); - -it('should have an error when the key is invalid', async () => { - const wrapper = shallow( - <OrganizationKeyInput initialValue="KEy-with#speci@l_char" onChange={jest.fn()} /> - ); - await waitAndUpdate(wrapper); - expect(wrapper.find('ValidationInput').prop('isInvalid')).toBe(true); -}); - -it('should have an error when the key already exists', async () => { - (getOrganization as jest.Mock<any>).mockResolvedValue({}); - const wrapper = shallow(<OrganizationKeyInput initialValue="" onChange={jest.fn()} />); - await waitAndUpdate(wrapper); - expect(wrapper.find('ValidationInput').prop('isInvalid')).toBe(true); -}); diff --git a/server/sonar-web/src/main/js/apps/create/components/__tests__/OrganizationSelect-test.tsx b/server/sonar-web/src/main/js/apps/create/components/__tests__/OrganizationSelect-test.tsx deleted file mode 100644 index f248defd48b..00000000000 --- a/server/sonar-web/src/main/js/apps/create/components/__tests__/OrganizationSelect-test.tsx +++ /dev/null @@ -1,39 +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 { mockOrganization, mockOrganizationWithAlm } from '../../../../helpers/testMocks'; -import OrganizationSelect, { getOptionRenderer } from '../OrganizationSelect'; - -const organizations = [mockOrganization(), mockOrganizationWithAlm({ key: 'bar', name: 'Bar' })]; - -it('should render correctly', () => { - expect( - shallow( - <OrganizationSelect onChange={jest.fn()} organization="bar" organizations={organizations} /> - ) - ).toMatchSnapshot(); -}); - -it('should render options correctly', () => { - expect(shallow(getOptionRenderer()(organizations[0]))).toMatchSnapshot(); - expect(shallow(getOptionRenderer()(organizations[1]))).toMatchSnapshot(); - expect(shallow(getOptionRenderer(true)(organizations[0]))).toMatchSnapshot(); -}); diff --git a/server/sonar-web/src/main/js/apps/create/components/__tests__/OrganizationUrlInput-test.tsx b/server/sonar-web/src/main/js/apps/create/components/__tests__/OrganizationUrlInput-test.tsx deleted file mode 100644 index bda1bd73d4a..00000000000 --- a/server/sonar-web/src/main/js/apps/create/components/__tests__/OrganizationUrlInput-test.tsx +++ /dev/null @@ -1,39 +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 OrganizationUrlInput from '../OrganizationUrlInput'; - -it('should render correctly', () => { - const wrapper = shallow( - <OrganizationUrlInput initialValue="http://my.website" onChange={jest.fn()} /> - ); - expect(wrapper).toMatchSnapshot(); - wrapper.setState({ touched: true }); - expect(wrapper.find('ValidationInput').prop('isValid')).toMatchSnapshot(); -}); - -it('should have an error when the url is invalid', () => { - expect( - shallow(<OrganizationUrlInput initialValue="whatever" onChange={jest.fn()} />) - .find('ValidationInput') - .prop('isInvalid') - ).toBe(true); -}); diff --git a/server/sonar-web/src/main/js/apps/create/components/__tests__/PaidCardPlan-test.tsx b/server/sonar-web/src/main/js/apps/create/components/__tests__/PaidCardPlan-test.tsx deleted file mode 100644 index 5e6e7f55256..00000000000 --- a/server/sonar-web/src/main/js/apps/create/components/__tests__/PaidCardPlan-test.tsx +++ /dev/null @@ -1,26 +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 PaidCardPlan from '../PaidCardPlan'; - -it('should render correctly', () => { - expect(shallow(<PaidCardPlan isRecommended={true} startingPrice={10} />)).toMatchSnapshot(); -}); diff --git a/server/sonar-web/src/main/js/apps/create/components/__tests__/UpgradeOrganizationAdvantages-test.tsx b/server/sonar-web/src/main/js/apps/create/components/__tests__/UpgradeOrganizationAdvantages-test.tsx deleted file mode 100644 index 388b00f408b..00000000000 --- a/server/sonar-web/src/main/js/apps/create/components/__tests__/UpgradeOrganizationAdvantages-test.tsx +++ /dev/null @@ -1,26 +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 UpgradeOrganizationAdvantages from '../UpgradeOrganizationAdvantages'; - -it('should render correctly', () => { - expect(shallow(<UpgradeOrganizationAdvantages />)).toMatchSnapshot(); -}); diff --git a/server/sonar-web/src/main/js/apps/create/components/__tests__/UpgradeOrganizationBox-test.tsx b/server/sonar-web/src/main/js/apps/create/components/__tests__/UpgradeOrganizationBox-test.tsx deleted file mode 100644 index ad1bab841e8..00000000000 --- a/server/sonar-web/src/main/js/apps/create/components/__tests__/UpgradeOrganizationBox-test.tsx +++ /dev/null @@ -1,61 +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 { hasMessage } from 'sonar-ui-common/helpers/l10n'; -import { click, waitAndUpdate } from 'sonar-ui-common/helpers/testUtils'; -import { getSubscriptionPlans } from '../../../../api/billing'; -import UpgradeOrganizationBox from '../UpgradeOrganizationBox'; - -jest.mock('sonar-ui-common/helpers/l10n', () => ({ - ...jest.requireActual('sonar-ui-common/helpers/l10n'), - hasMessage: jest.fn().mockReturnValue(true) -})); - -jest.mock('../../../../api/billing', () => ({ - getSubscriptionPlans: jest.fn().mockResolvedValue([{ maxNcloc: 100000, price: 10 }]) -})); - -const organization = { key: 'foo', name: 'Foo' }; - -beforeEach(() => { - (hasMessage as jest.Mock<any>).mockClear(); - (getSubscriptionPlans as jest.Mock<any>).mockClear(); -}); - -it('should not render', () => { - (hasMessage as jest.Mock<any>).mockReturnValueOnce(false); - expect( - shallow( - <UpgradeOrganizationBox onOrganizationUpgrade={jest.fn()} organization={organization} /> - ).type() - ).toBeNull(); -}); - -it('should render correctly', async () => { - const wrapper = shallow( - <UpgradeOrganizationBox onOrganizationUpgrade={jest.fn()} organization={organization} /> - ); - await waitAndUpdate(wrapper); - expect(getSubscriptionPlans).toHaveBeenCalled(); - expect(wrapper).toMatchSnapshot(); - click(wrapper.find('Button')); - expect(wrapper.find('UpgradeOrganizationModal').exists()).toBe(true); -}); diff --git a/server/sonar-web/src/main/js/apps/create/components/__tests__/UpgradeOrganizationModal-test.tsx b/server/sonar-web/src/main/js/apps/create/components/__tests__/UpgradeOrganizationModal-test.tsx deleted file mode 100644 index b68be4d410d..00000000000 --- a/server/sonar-web/src/main/js/apps/create/components/__tests__/UpgradeOrganizationModal-test.tsx +++ /dev/null @@ -1,44 +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 { getExtensionStart } from '../../../../helpers/extensions'; -import UpgradeOrganizationModal from '../UpgradeOrganizationModal'; - -jest.mock('../../../../helpers/extensions', () => ({ - getExtensionStart: jest.fn().mockResolvedValue(undefined) -})); - -const organization = { key: 'foo', name: 'Foo' }; - -it('should render correctly', async () => { - const wrapper = shallow( - <UpgradeOrganizationModal - onClose={jest.fn()} - onUpgradeDone={jest.fn()} - organization={organization} - subscriptionPlans={[]} - /> - ); - await waitAndUpdate(wrapper); - expect(getExtensionStart).toHaveBeenCalled(); - expect(wrapper).toMatchSnapshot(); -}); diff --git a/server/sonar-web/src/main/js/apps/create/components/__tests__/__snapshots__/BillingFormShim-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/components/__tests__/__snapshots__/BillingFormShim-test.tsx.snap deleted file mode 100644 index fc6fb48982e..00000000000 --- a/server/sonar-web/src/main/js/apps/create/components/__tests__/__snapshots__/BillingFormShim-test.tsx.snap +++ /dev/null @@ -1,16 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render 1`] = ` -<BillingForm - currentUser={ - Object { - "isLoggedIn": false, - } - } - onCommit={[MockFunction]} - organizationKey="org" - subscriptionPlans={Array []} -> - <Component /> -</BillingForm> -`; diff --git a/server/sonar-web/src/main/js/apps/create/components/__tests__/__snapshots__/FreeCardPlan-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/components/__tests__/__snapshots__/FreeCardPlan-test.tsx.snap deleted file mode 100644 index 096ad74b503..00000000000 --- a/server/sonar-web/src/main/js/apps/create/components/__tests__/__snapshots__/FreeCardPlan-test.tsx.snap +++ /dev/null @@ -1,101 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render 1`] = ` -<RadioCard - title="billing.free_plan.title" - titleInfo="billing.price_format.0" -> - <div - className="spacer-left" - > - <ul - className="big-spacer-left note" - > - <li - className="little-spacer-bottom" - > - billing.free_plan.all_projects_analyzed_public - </li> - <li> - billing.free_plan.anyone_can_browse_source_code - </li> - </ul> - </div> -</RadioCard> -`; - -exports[`should render disabled with info 1`] = ` -<RadioCard - disabled={true} - title="billing.free_plan.title" - titleInfo="billing.price_format.0" -> - <div - className="spacer-left" - > - <ul - className="big-spacer-left note" - > - <li - className="little-spacer-bottom" - > - billing.free_plan.all_projects_analyzed_public - </li> - <li> - billing.free_plan.anyone_can_browse_source_code - </li> - </ul> - </div> - <Alert - variant="info" - > - <FormattedMessage - defaultMessage="billing.free_plan.not_available_info" - id="billing.free_plan.not_available_info" - values={ - Object { - "alm": "GitHub", - } - } - /> - </Alert> -</RadioCard> -`; - -exports[`should render with warning 1`] = ` -<RadioCard - selected={true} - title="billing.free_plan.title" - titleInfo="billing.price_format.0" -> - <div - className="spacer-left" - > - <ul - className="big-spacer-left note" - > - <li - className="little-spacer-bottom" - > - billing.free_plan.all_projects_analyzed_public - </li> - <li> - billing.free_plan.anyone_can_browse_source_code - </li> - </ul> - </div> - <Alert - variant="warning" - > - <FormattedMessage - defaultMessage="billing.free_plan.private_repo_warning" - id="billing.free_plan.private_repo_warning" - values={ - Object { - "alm": "GitHub", - } - } - /> - </Alert> -</RadioCard> -`; diff --git a/server/sonar-web/src/main/js/apps/create/components/__tests__/__snapshots__/OrganizationAvatarInput-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/components/__tests__/__snapshots__/OrganizationAvatarInput-test.tsx.snap deleted file mode 100644 index a2f801bc5d4..00000000000 --- a/server/sonar-web/src/main/js/apps/create/components/__tests__/__snapshots__/OrganizationAvatarInput-test.tsx.snap +++ /dev/null @@ -1,63 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should display the fallback avatar when there is no url 1`] = ` -<ValidationInput - description="onboarding.create_organization.avatar.description" - id="organization-avatar" - isInvalid={false} - isValid={false} - label="onboarding.create_organization.avatar" -> - <OrganizationAvatar - className="display-block spacer-bottom" - organization={ - Object { - "avatar": undefined, - "name": "Luke Skywalker", - } - } - /> - <input - className="input-super-large text-middle" - id="organization-avatar" - onBlur={[Function]} - onChange={[Function]} - onFocus={[Function]} - placeholder="onboarding.create_organization.avatar.placeholder" - type="text" - value="" - /> -</ValidationInput> -`; - -exports[`should render correctly 1`] = ` -<ValidationInput - description="onboarding.create_organization.avatar.description" - id="organization-avatar" - isInvalid={false} - isValid={false} - label="onboarding.create_organization.avatar" -> - <OrganizationAvatar - className="display-block spacer-bottom" - organization={ - Object { - "avatar": "https://my.avatar", - "name": "", - } - } - /> - <input - className="input-super-large text-middle" - id="organization-avatar" - onBlur={[Function]} - onChange={[Function]} - onFocus={[Function]} - placeholder="onboarding.create_organization.avatar.placeholder" - type="text" - value="https://my.avatar" - /> -</ValidationInput> -`; - -exports[`should render correctly 2`] = `true`; diff --git a/server/sonar-web/src/main/js/apps/create/components/__tests__/__snapshots__/OrganizationKeyInput-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/components/__tests__/__snapshots__/OrganizationKeyInput-test.tsx.snap deleted file mode 100644 index 5a4f352f826..00000000000 --- a/server/sonar-web/src/main/js/apps/create/components/__tests__/__snapshots__/OrganizationKeyInput-test.tsx.snap +++ /dev/null @@ -1,30 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render correctly 1`] = ` -<ValidationInput - id="organization-key" - isInvalid={false} - isValid={false} - label="onboarding.create_organization.organization_name" - required={true} -> - <div - className="display-inline-flex-baseline" - > - <span - className="little-spacer-right" - > - localhost/organizations/ - </span> - <input - autoFocus={true} - className="input-super-large" - id="organization-key" - maxLength={255} - onChange={[Function]} - type="text" - value="key" - /> - </div> -</ValidationInput> -`; diff --git a/server/sonar-web/src/main/js/apps/create/components/__tests__/__snapshots__/OrganizationSelect-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/components/__tests__/__snapshots__/OrganizationSelect-test.tsx.snap deleted file mode 100644 index 46e9dbfcba1..00000000000 --- a/server/sonar-web/src/main/js/apps/create/components/__tests__/__snapshots__/OrganizationSelect-test.tsx.snap +++ /dev/null @@ -1,99 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render correctly 1`] = ` -<Select - autoFocus={false} - className="input-super-large" - clearable={false} - id="select-organization" - labelKey="name" - onChange={[MockFunction]} - optionRenderer={[Function]} - options={ - Array [ - Object { - "alm": Object { - "key": "github", - "membersSync": false, - "personal": false, - "url": "https://github.com/foo", - }, - "key": "bar", - "name": "Bar", - }, - Object { - "key": "foo", - "name": "Foo", - }, - ] - } - placeholder="onboarding.import_organization.choose_organization" - required={true} - value="bar" - valueKey="key" - valueRenderer={[Function]} -/> -`; - -exports[`should render options correctly 1`] = ` -<div - className="display-flex-space-between" -> - <span - className="text-ellipsis flex-1" - > - <img - alt="SonarCloud" - className="little-spacer-right" - height={14} - src="/images/sonarcloud-square-logo.svg" - /> - Foo - <span - className="note little-spacer-left" - > - foo - </span> - </span> -</div> -`; - -exports[`should render options correctly 2`] = ` -<div - className="display-flex-space-between" -> - <span - className="text-ellipsis flex-1" - > - <img - alt="github" - className="little-spacer-right" - height={14} - src="/images/sonarcloud/github.svg" - /> - Bar - <span - className="note little-spacer-left" - > - bar - </span> - </span> -</div> -`; - -exports[`should render options correctly 3`] = ` -<div - className="display-flex-space-between" -> - <span - className="text-ellipsis flex-1" - > - Foo - <span - className="note little-spacer-left" - > - foo - </span> - </span> -</div> -`; diff --git a/server/sonar-web/src/main/js/apps/create/components/__tests__/__snapshots__/OrganizationUrlInput-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/components/__tests__/__snapshots__/OrganizationUrlInput-test.tsx.snap deleted file mode 100644 index d3f571b4db8..00000000000 --- a/server/sonar-web/src/main/js/apps/create/components/__tests__/__snapshots__/OrganizationUrlInput-test.tsx.snap +++ /dev/null @@ -1,22 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render correctly 1`] = ` -<ValidationInput - id="organization-url" - isInvalid={false} - isValid={false} - label="onboarding.create_organization.url" -> - <input - className="input-super-large text-middle" - id="organization-url" - onBlur={[Function]} - onChange={[Function]} - onFocus={[Function]} - type="text" - value="http://my.website" - /> -</ValidationInput> -`; - -exports[`should render correctly 2`] = `true`; diff --git a/server/sonar-web/src/main/js/apps/create/components/__tests__/__snapshots__/PaidCardPlan-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/components/__tests__/__snapshots__/PaidCardPlan-test.tsx.snap deleted file mode 100644 index 94ac5a89244..00000000000 --- a/server/sonar-web/src/main/js/apps/create/components/__tests__/__snapshots__/PaidCardPlan-test.tsx.snap +++ /dev/null @@ -1,38 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render correctly 1`] = ` -<RadioCard - recommended="billing.paid_plan.recommended" - title="billing.paid_plan.title" - titleInfo={ - <FormattedMessage - defaultMessage="billing.price_from_x" - id="billing.price_from_x" - values={ - Object { - "price": <span - className="big" - > - billing.price_format.10 - </span>, - } - } - /> - } -> - <UpgradeOrganizationAdvantages /> - <div - className="big-spacer-left" - > - <Link - className="spacer-left" - onlyActiveOnIndex={false} - style={Object {}} - target="_blank" - to="/about/pricing" - > - billing.pricing.learn_more - </Link> - </div> -</RadioCard> -`; diff --git a/server/sonar-web/src/main/js/apps/create/components/__tests__/__snapshots__/UpgradeOrganizationAdvantages-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/components/__tests__/__snapshots__/UpgradeOrganizationAdvantages-test.tsx.snap deleted file mode 100644 index dce3b0b46e6..00000000000 --- a/server/sonar-web/src/main/js/apps/create/components/__tests__/__snapshots__/UpgradeOrganizationAdvantages-test.tsx.snap +++ /dev/null @@ -1,22 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render correctly 1`] = ` -<ul - className="note" -> - <Advantage> - billing.upgrade_box.unlimited_private_projects - </Advantage> - <Advantage> - billing.upgrade_box.strict_control_private_data - </Advantage> - <Advantage> - billing.upgrade_box.cancel_anytime - </Advantage> - <Advantage> - <strong> - billing.upgrade_box.free_trial_x.14 - </strong> - </Advantage> -</ul> -`; diff --git a/server/sonar-web/src/main/js/apps/create/components/__tests__/__snapshots__/UpgradeOrganizationBox-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/components/__tests__/__snapshots__/UpgradeOrganizationBox-test.tsx.snap deleted file mode 100644 index e389049612a..00000000000 --- a/server/sonar-web/src/main/js/apps/create/components/__tests__/__snapshots__/UpgradeOrganizationBox-test.tsx.snap +++ /dev/null @@ -1,45 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render correctly 1`] = ` -<Fragment> - <RadioCard - title="billing.upgrade_box.header" - titleInfo={ - <FormattedMessage - defaultMessage="billing.price_from_x" - id="billing.price_from_x" - values={ - Object { - "price": <span - className="big" - > - billing.price_format.10 - </span>, - } - } - /> - } - > - <UpgradeOrganizationAdvantages /> - <div - className="big-spacer-left" - > - <Button - className="js-upgrade-organization" - onClick={[Function]} - > - billing.paid_plan.upgrade - </Button> - <Link - className="spacer-left" - onlyActiveOnIndex={false} - style={Object {}} - target="_blank" - to="/about/pricing" - > - billing.pricing.learn_more - </Link> - </div> - </RadioCard> -</Fragment> -`; diff --git a/server/sonar-web/src/main/js/apps/create/components/__tests__/__snapshots__/UpgradeOrganizationModal-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/components/__tests__/__snapshots__/UpgradeOrganizationModal-test.tsx.snap deleted file mode 100644 index 4d294fc1497..00000000000 --- a/server/sonar-web/src/main/js/apps/create/components/__tests__/__snapshots__/UpgradeOrganizationModal-test.tsx.snap +++ /dev/null @@ -1,25 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render correctly 1`] = ` -<Modal - contentLabel="billing.upgrade_box.upgrade_to_paid_plan" - onRequestClose={[MockFunction]} - shouldCloseOnOverlayClick={false} - size="medium" -> - <div - className="modal-head" - > - <h2> - billing.upgrade_box.upgrade_to_paid_plan - </h2> - </div> - <Connect(withCurrentUser(BillingFormShim)) - onCommit={[MockFunction]} - organizationKey="foo" - subscriptionPlans={Array []} - > - <Component /> - </Connect(withCurrentUser(BillingFormShim))> -</Modal> -`; 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 - }); |