diff options
author | Philippe Perrin <philippe.perrin@sonarsource.com> | 2021-06-21 15:09:32 +0200 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2021-06-28 20:03:21 +0000 |
commit | c243a3e78070ab9a1d1038248212151e1b9dd11f (patch) | |
tree | 6fc7a9d506869ef3fd26945dd6a2c973718e07ec /server/sonar-web/src/main/js | |
parent | 0863e93ba1f4d9d20acb6b4ef3934fbc53e8d615 (diff) | |
download | sonarqube-c243a3e78070ab9a1d1038248212151e1b9dd11f.tar.gz sonarqube-c243a3e78070ab9a1d1038248212151e1b9dd11f.zip |
SONAR-14932 Make alm config modal standalone
Diffstat (limited to 'server/sonar-web/src/main/js')
56 files changed, 3114 insertions, 3673 deletions
diff --git a/server/sonar-web/src/main/js/api/alm-settings.ts b/server/sonar-web/src/main/js/api/alm-settings.ts index c294553d0b2..e3c50cb9ce3 100644 --- a/server/sonar-web/src/main/js/api/alm-settings.ts +++ b/server/sonar-web/src/main/js/api/alm-settings.ts @@ -31,10 +31,10 @@ import { AlmSettingsInstance, AzureBindingDefinition, AzureProjectAlmBindingParams, - BitbucketBindingDefinition, BitbucketCloudBindingDefinition, BitbucketCloudProjectAlmBindingParams, BitbucketProjectAlmBindingParams, + BitbucketServerBindingDefinition, GithubBindingDefinition, GithubProjectAlmBindingParams, GitlabBindingDefinition, @@ -85,12 +85,12 @@ export function updateAzureConfiguration(data: AzureBindingDefinition & { newKey return post('/api/alm_settings/update_azure', data).catch(throwGlobalError); } -export function createBitbucketConfiguration(data: BitbucketBindingDefinition) { +export function createBitbucketServerConfiguration(data: BitbucketServerBindingDefinition) { return post('/api/alm_settings/create_bitbucket', data).catch(throwGlobalError); } -export function updateBitbucketConfiguration( - data: BitbucketBindingDefinition & { newKey: string } +export function updateBitbucketServerConfiguration( + data: BitbucketServerBindingDefinition & { newKey: string } ) { return post('/api/alm_settings/update_bitbucket', data).catch(throwGlobalError); } diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmBindingDefinitionBox.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmBindingDefinitionBox.tsx index 031d7ccee56..73489a02c3d 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmBindingDefinitionBox.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmBindingDefinitionBox.tsx @@ -32,7 +32,7 @@ import { translate } from 'sonar-ui-common/helpers/l10n'; import { ALM_DOCUMENTATION_PATHS, IMPORT_COMPATIBLE_ALMS } from '../../../../helpers/constants'; import { getEdition, getEditionUrl } from '../../../../helpers/editions'; import { - AlmBindingDefinition, + AlmBindingDefinitionBase, AlmKeys, AlmSettingsBindingStatus, AlmSettingsBindingStatusType @@ -42,7 +42,7 @@ import { EditionKey } from '../../../../types/editions'; export interface AlmBindingDefinitionBoxProps { alm: AlmKeys; branchesEnabled: boolean; - definition: AlmBindingDefinition; + definition: AlmBindingDefinitionBase; multipleDefinitions: boolean; onCheck: (definitionKey: string) => void; onDelete: (definitionKey: string) => void; @@ -105,7 +105,7 @@ function getPRDecorationFeatureStatus( function getImportFeatureStatus( alm: AlmKeys, - definition: AlmBindingDefinition, + definition: AlmBindingDefinitionBase, multipleDefinitions: boolean, type: AlmSettingsBindingStatusType.Success | AlmSettingsBindingStatusType.Failure ) { diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmBindingDefinitionForm.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmBindingDefinitionForm.tsx index fa0a1886544..4d8a17c2d17 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmBindingDefinitionForm.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmBindingDefinitionForm.tsx @@ -17,67 +17,130 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { isEqual, omit } from 'lodash'; import * as React from 'react'; -import { AlmBindingDefinition } from '../../../../types/alm-settings'; -import AlmBindingDefinitionFormModalRenderer from './AlmBindingDefinitionFormModalRenderer'; - -export interface AlmBindingDefinitionFormChildrenProps<B> { - formData: B; - onFieldChange: (fieldId: keyof B, value: string) => void; +import { + createAzureConfiguration, + createBitbucketCloudConfiguration, + createBitbucketServerConfiguration, + createGithubConfiguration, + createGitlabConfiguration, + updateAzureConfiguration, + updateBitbucketCloudConfiguration, + updateBitbucketServerConfiguration, + updateGithubConfiguration, + updateGitlabConfiguration +} from '../../../../api/alm-settings'; +import { + AlmBindingDefinition, + AlmBindingDefinitionBase, + AlmKeys, + AzureBindingDefinition, + BitbucketCloudBindingDefinition, + BitbucketServerBindingDefinition, + GithubBindingDefinition, + GitlabBindingDefinition, + isBitbucketCloudBindingDefinition +} from '../../../../types/alm-settings'; +import AlmBindingDefinitionFormRenderer from './AlmBindingDefinitionFormRenderer'; + +export interface AlmBindingDefinitionFormChildrenProps { + formData: AlmBindingDefinition; + onFieldChange: (fieldId: string, value: string) => void; } -interface Props<B> { - bindingDefinition: B; - children: (props: AlmBindingDefinitionFormChildrenProps<B>) => React.ReactNode; - help?: React.ReactNode; - isSecondInstance?: boolean; - onCancel?: () => void; +interface Props { + alm: AlmKeys; + bindingDefinition?: AlmBindingDefinition; + alreadyHaveInstanceConfigured: boolean; onDelete?: (definitionKey: string) => void; onEdit?: (definitionKey: string) => void; - onSubmit: (data: B, originalKey: string) => void; - optionalFields?: Array<keyof B>; + onCancel?: () => void; + afterSubmit?: (data: AlmBindingDefinitionBase) => void; } -interface State<B> { - formData: B; +interface State { + formData: AlmBindingDefinition; touched: boolean; + submitting: boolean; + bitbucketVariant?: AlmKeys.BitbucketServer | AlmKeys.BitbucketCloud; } -export default class AlmBindingDefinitionForm< - B extends AlmBindingDefinition -> extends React.PureComponent<Props<B>, State<B>> { - constructor(props: Props<B>) { - super(props); - this.state = { formData: props.bindingDefinition, touched: false }; +const BINDING_PER_ALM = { + [AlmKeys.Azure]: { + createApi: createAzureConfiguration, + updateApi: updateAzureConfiguration, + defaultBinding: { key: '', personalAccessToken: '', url: '' } as AzureBindingDefinition + }, + [AlmKeys.GitHub]: { + createApi: createGithubConfiguration, + updateApi: updateGithubConfiguration, + defaultBinding: { + key: '', + appId: '', + clientId: '', + clientSecret: '', + url: '', + privateKey: '' + } as GithubBindingDefinition + }, + [AlmKeys.GitLab]: { + createApi: createGitlabConfiguration, + updateApi: updateGitlabConfiguration, + defaultBinding: { key: '', personalAccessToken: '', url: '' } as GitlabBindingDefinition + }, + [AlmKeys.BitbucketServer]: { + createApi: createBitbucketServerConfiguration, + updateApi: updateBitbucketServerConfiguration, + defaultBinding: { + key: '', + url: '', + personalAccessToken: '' + } as BitbucketServerBindingDefinition + }, + [AlmKeys.BitbucketCloud]: { + createApi: createBitbucketCloudConfiguration, + updateApi: updateBitbucketCloudConfiguration, + defaultBinding: { + key: '', + clientId: '', + clientSecret: '', + workspace: '' + } as BitbucketCloudBindingDefinition } +}; - componentDidUpdate(prevProps: Props<B>) { - if (!isEqual(prevProps.bindingDefinition, this.props.bindingDefinition)) { - this.setState({ formData: this.props.bindingDefinition, touched: false }); - } - } +export default class AlmBindingDefinitionForm extends React.PureComponent<Props, State> { + mounted = false; + constructor(props: Props) { + super(props); - handleCancel = () => { - this.setState({ formData: this.props.bindingDefinition, touched: false }); - if (this.props.onCancel) { - this.props.onCancel(); - } - }; + let bitbucketVariant: AlmKeys.BitbucketServer | AlmKeys.BitbucketCloud | undefined = undefined; - handleDelete = () => { - if (this.props.onDelete) { - this.props.onDelete(this.props.bindingDefinition.key); + if (props.bindingDefinition && props.alm === AlmKeys.BitbucketServer) { + bitbucketVariant = isBitbucketCloudBindingDefinition(props.bindingDefinition) + ? AlmKeys.BitbucketCloud + : AlmKeys.BitbucketServer; } - }; - handleEdit = () => { - if (this.props.onEdit) { - this.props.onEdit(this.props.bindingDefinition.key); - } - }; + const alm = bitbucketVariant || props.alm; - handleFieldChange = (fieldId: keyof B, value: string) => { + this.state = { + formData: props.bindingDefinition ?? BINDING_PER_ALM[alm].defaultBinding, + touched: false, + submitting: false, + bitbucketVariant + }; + } + + componentDidMount() { + this.mounted = true; + } + + componentWillUnmount() { + this.mounted = false; + } + + handleFieldChange = (fieldId: string, value: string) => { this.setState(({ formData }) => ({ formData: { ...formData, @@ -87,42 +150,69 @@ export default class AlmBindingDefinitionForm< })); }; - handleFormSubmit = () => { - this.props.onSubmit(this.state.formData, this.props.bindingDefinition.key); + handleFormSubmit = async () => { + const { alm } = this.props; + const { formData, bitbucketVariant } = this.state; + const apiAlm = bitbucketVariant ?? alm; + + const apiMethod = this.props.bindingDefinition?.key + ? BINDING_PER_ALM[apiAlm].updateApi({ + newKey: formData.key, + ...formData, + key: this.props.bindingDefinition.key + } as any) + : BINDING_PER_ALM[apiAlm].createApi({ ...formData } as any); + + this.setState({ submitting: true }); + + try { + await apiMethod; + + if (this.props.afterSubmit) { + this.props.afterSubmit(formData); + } + } finally { + if (this.mounted) { + this.setState({ submitting: false }); + } + } + }; + + handleBitbucketVariantChange = ( + bitbucketVariant: AlmKeys.BitbucketServer | AlmKeys.BitbucketCloud + ) => { + this.setState({ + bitbucketVariant, + formData: { ...BINDING_PER_ALM[bitbucketVariant].defaultBinding } + }); }; canSubmit = () => { - const { optionalFields } = this.props; const { formData, touched } = this.state; - let values = { ...formData }; - - if (optionalFields && optionalFields.length > 0) { - values = omit(values, optionalFields) as B; - } - - return touched && !Object.values(values).some(v => !v); + return touched && !Object.values(formData).some(v => !v); }; render() { - const { bindingDefinition, children, help, isSecondInstance } = this.props; - const { formData } = this.state; + const { alm, bindingDefinition, alreadyHaveInstanceConfigured } = this.props; + const { formData, submitting, bitbucketVariant } = this.state; - const action = bindingDefinition.key ? 'edit' : 'create'; + const isUpdate = !!bindingDefinition; return ( - <AlmBindingDefinitionFormModalRenderer - action={action} - canSubmit={this.canSubmit} - help={help} - isSecondInstance={Boolean(isSecondInstance)} - onCancel={this.handleCancel} - onSubmit={this.handleFormSubmit}> - {children({ - formData, - onFieldChange: this.handleFieldChange - })} - </AlmBindingDefinitionFormModalRenderer> + <AlmBindingDefinitionFormRenderer + alm={alm} + isUpdate={isUpdate} + canSubmit={this.canSubmit()} + alreadyHaveInstanceConfigured={alreadyHaveInstanceConfigured} + onCancel={() => this.props.onCancel && this.props.onCancel()} + onSubmit={this.handleFormSubmit} + onFieldChange={this.handleFieldChange} + formData={formData} + submitting={submitting} + bitbucketVariant={bitbucketVariant} + onBitbucketVariantChange={this.handleBitbucketVariantChange} + /> ); } } diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmBindingDefinitionFormField.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmBindingDefinitionFormField.tsx index de5693da0f3..bba6481cf22 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmBindingDefinitionFormField.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmBindingDefinitionFormField.tsx @@ -22,9 +22,9 @@ import { ButtonLink } from 'sonar-ui-common/components/controls/buttons'; import HelpTooltip from 'sonar-ui-common/components/controls/HelpTooltip'; import MandatoryFieldMarker from 'sonar-ui-common/components/ui/MandatoryFieldMarker'; import { translate } from 'sonar-ui-common/helpers/l10n'; -import { AlmBindingDefinition } from '../../../../types/alm-settings'; +import { AlmBindingDefinitionBase } from '../../../../types/alm-settings'; -export interface AlmBindingDefinitionFormFieldProps<B extends AlmBindingDefinition> { +export interface AlmBindingDefinitionFormFieldProps<B extends AlmBindingDefinitionBase> { autoFocus?: boolean; help?: React.ReactNode; id: string; @@ -37,7 +37,7 @@ export interface AlmBindingDefinitionFormFieldProps<B extends AlmBindingDefiniti value: string; } -export function AlmBindingDefinitionFormField<B extends AlmBindingDefinition>( +export function AlmBindingDefinitionFormField<B extends AlmBindingDefinitionBase>( props: AlmBindingDefinitionFormFieldProps<B> ) { const { diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmBindingDefinitionFormModalRenderer.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmBindingDefinitionFormModalRenderer.tsx deleted file mode 100644 index f6ec74d3180..00000000000 --- a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmBindingDefinitionFormModalRenderer.tsx +++ /dev/null @@ -1,87 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2021 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 Modal from 'sonar-ui-common/components/controls/Modal'; -import { Alert } from 'sonar-ui-common/components/ui/Alert'; -import { translate } from 'sonar-ui-common/helpers/l10n'; - -export interface AlmBindingDefinitionFormModalProps { - action: 'edit' | 'create'; - canSubmit: () => boolean; - children: React.ReactNode; - help?: React.ReactNode; - isSecondInstance: boolean; - onCancel: () => void; - onSubmit: () => void | Promise<void | Response>; -} - -export default function AlmBindingDefinitionFormModalRenderer( - props: AlmBindingDefinitionFormModalProps -) { - const { action, children, help, isSecondInstance } = props; - const header = translate('settings.almintegration.form.header', action); - - const handleSubmit = (event: React.SyntheticEvent<HTMLFormElement>) => { - event.preventDefault(); - props.onSubmit(); - }; - - return ( - <Modal - contentLabel={header} - onRequestClose={props.onCancel} - shouldCloseOnOverlayClick={false} - size="medium"> - <form className="views-form" onSubmit={handleSubmit}> - <div className="modal-head"> - <h2>{header}</h2> - </div> - - <div className="modal-body modal-container"> - {isSecondInstance && action === 'create' && ( - <Alert className="big-spacer-bottom" variant="warning"> - {translate('settings.almintegration.form.second_instance_warning')} - </Alert> - )} - - <div className="display-flex-start"> - <div className="flex-1">{children}</div> - - {help ? ( - <Alert className="huge-spacer-left flex-1" variant="info"> - {help} - </Alert> - ) : ( - <div className="flex-1" /> - )} - </div> - </div> - - <div className="modal-foot"> - <SubmitButton disabled={!props.canSubmit()}> - {translate('settings.almintegration.form.save')} - </SubmitButton> - <ResetButtonLink onClick={props.onCancel}>{translate('cancel')}</ResetButtonLink> - </div> - </form> - </Modal> - ); -} diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmBindingDefinitionFormRenderer.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmBindingDefinitionFormRenderer.tsx new file mode 100644 index 00000000000..c50f5d2ed4e --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmBindingDefinitionFormRenderer.tsx @@ -0,0 +1,141 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 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 Modal from 'sonar-ui-common/components/controls/Modal'; +import { Alert } from 'sonar-ui-common/components/ui/Alert'; +import DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner'; +import { translate } from 'sonar-ui-common/helpers/l10n'; +import { + AlmBindingDefinition, + AlmKeys, + AzureBindingDefinition, + BitbucketCloudBindingDefinition, + BitbucketServerBindingDefinition, + GithubBindingDefinition, + GitlabBindingDefinition +} from '../../../../types/alm-settings'; +import AzureForm from './AzureForm'; +import BitbucketForm from './BitbucketForm'; +import GithubForm from './GithubForm'; +import GitlabForm from './GitlabForm'; + +export interface AlmBindingDefinitionFormProps { + alm: AlmKeys; + isUpdate: boolean; + canSubmit: boolean; + alreadyHaveInstanceConfigured: boolean; + onCancel: () => void; + onSubmit: () => void; + onFieldChange: (fieldId: keyof AlmBindingDefinition, value: string) => void; + formData: AlmBindingDefinition; + submitting: boolean; + bitbucketVariant?: AlmKeys.BitbucketServer | AlmKeys.BitbucketCloud; + onBitbucketVariantChange: ( + bitbucketVariant: AlmKeys.BitbucketServer | AlmKeys.BitbucketCloud + ) => void; +} + +export default class AlmBindingDefinitionFormRenderer extends React.PureComponent< + AlmBindingDefinitionFormProps +> { + renderForm = () => { + const { alm, formData, isUpdate, bitbucketVariant } = this.props; + + switch (alm) { + case AlmKeys.GitLab: + return ( + <GitlabForm + onFieldChange={this.props.onFieldChange} + formData={formData as GitlabBindingDefinition} + /> + ); + case AlmKeys.Azure: + return ( + <AzureForm + onFieldChange={this.props.onFieldChange} + formData={formData as AzureBindingDefinition} + /> + ); + case AlmKeys.GitHub: + return ( + <GithubForm + onFieldChange={this.props.onFieldChange} + formData={formData as GithubBindingDefinition} + /> + ); + case AlmKeys.BitbucketServer: + return ( + <BitbucketForm + onFieldChange={this.props.onFieldChange} + formData={ + formData as BitbucketServerBindingDefinition | BitbucketCloudBindingDefinition + } + isUpdate={isUpdate} + variant={bitbucketVariant} + onVariantChange={this.props.onBitbucketVariantChange} + /> + ); + default: + return null; + } + }; + + render() { + const { isUpdate, alreadyHaveInstanceConfigured, canSubmit, submitting } = this.props; + const header = translate('settings.almintegration.form.header', isUpdate ? 'edit' : 'create'); + + const handleSubmit = (event: React.SyntheticEvent<HTMLFormElement>) => { + event.preventDefault(); + this.props.onSubmit(); + }; + + return ( + <Modal + contentLabel={header} + onRequestClose={this.props.onCancel} + shouldCloseOnOverlayClick={false} + size="medium"> + <form className="views-form" onSubmit={handleSubmit}> + <div className="modal-head"> + <h2>{header}</h2> + </div> + + <div className="modal-body modal-container"> + {alreadyHaveInstanceConfigured && !isUpdate && ( + <Alert className="big-spacer-bottom" variant="warning"> + {translate('settings.almintegration.form.second_instance_warning')} + </Alert> + )} + {this.renderForm()} + </div> + + <div className="modal-foot"> + <SubmitButton disabled={!canSubmit || submitting}> + {translate('settings.almintegration.form.save')} + <DeferredSpinner className="spacer-left" loading={submitting} /> + </SubmitButton> + <ResetButtonLink onClick={this.props.onCancel}>{translate('cancel')}</ResetButtonLink> + </div> + </form> + </Modal> + ); + } +} diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmIntegration.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmIntegration.tsx index 8a17bcfa442..d1570b2a4c4 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmIntegration.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmIntegration.tsx @@ -28,22 +28,22 @@ import { import { withAppState } from '../../../../components/hoc/withAppState'; import { withRouter } from '../../../../components/hoc/withRouter'; import { - AlmBindingDefinition, + AlmBindingDefinitionBase, AlmKeys, AlmSettingsBindingDefinitions, AlmSettingsBindingStatus, AlmSettingsBindingStatusType } from '../../../../types/alm-settings'; import AlmIntegrationRenderer from './AlmIntegrationRenderer'; -import { ALM_KEY_LIST } from './utils'; interface Props extends Pick<WithRouterProps, 'location'> { appState: Pick<T.AppState, 'branchesEnabled' | 'multipleAlmEnabled'>; - component?: T.Component; } +export type AlmTabs = AlmKeys.Azure | AlmKeys.GitHub | AlmKeys.GitLab | AlmKeys.BitbucketServer; + interface State { - currentAlm: AlmKeys; + currentAlmTab: AlmTabs; definitionKeyForDeletion?: string; definitions: AlmSettingsBindingDefinitions; definitionStatus: T.Dict<AlmSettingsBindingStatus>; @@ -59,13 +59,13 @@ export class AlmIntegration extends React.PureComponent<Props, State> { constructor(props: Props) { super(props); - let currentAlm = props.location.query.alm || AlmKeys.GitHub; - if (currentAlm === AlmKeys.BitbucketCloud) { - currentAlm = AlmKeys.BitbucketServer; + let currentAlmTab = props.location.query.alm || AlmKeys.GitHub; + if (currentAlmTab === AlmKeys.BitbucketCloud) { + currentAlmTab = AlmKeys.BitbucketServer; } this.state = { - currentAlm, + currentAlmTab, definitions: { [AlmKeys.Azure]: [], [AlmKeys.BitbucketServer]: [], @@ -84,8 +84,14 @@ export class AlmIntegration extends React.PureComponent<Props, State> { return this.fetchPullRequestDecorationSetting().then(definitions => { if (definitions) { // Validate all alms on load: - ALM_KEY_LIST.forEach(alm => { - this.state.definitions[alm].forEach((def: AlmBindingDefinition) => + [ + AlmKeys.Azure, + AlmKeys.BitbucketCloud, + AlmKeys.BitbucketServer, + AlmKeys.GitHub, + AlmKeys.GitLab + ].forEach(alm => { + this.state.definitions[alm].forEach((def: AlmBindingDefinitionBase) => this.handleCheck(def.key, false) ); }); @@ -97,7 +103,7 @@ export class AlmIntegration extends React.PureComponent<Props, State> { this.mounted = false; } - deleteConfiguration = (definitionKey: string) => { + handleConfirmDelete = (definitionKey: string) => { return deleteConfiguration(definitionKey) .then(() => { if (this.mounted) { @@ -118,7 +124,6 @@ export class AlmIntegration extends React.PureComponent<Props, State> { }); return definitions; } - return undefined; }) .catch(() => { if (this.mounted) { @@ -127,11 +132,11 @@ export class AlmIntegration extends React.PureComponent<Props, State> { }); }; - handleSelectAlm = (currentAlm: AlmKeys) => { - this.setState({ currentAlm }); + handleSelectAlm = (currentAlmTab: AlmTabs) => { + this.setState({ currentAlmTab }); }; - handleCancel = () => { + handleCancelDelete = () => { this.setState({ definitionKeyForDeletion: undefined, projectCount: undefined }); }; @@ -191,21 +196,35 @@ export class AlmIntegration extends React.PureComponent<Props, State> { render() { const { - appState: { branchesEnabled, multipleAlmEnabled }, - component + appState: { branchesEnabled, multipleAlmEnabled } } = this.props; + const { + currentAlmTab, + definitionKeyForDeletion, + definitions, + definitionStatus, + loadingAlmDefinitions, + loadingProjectCount, + projectCount + } = this.state; + return ( <AlmIntegrationRenderer branchesEnabled={Boolean(branchesEnabled)} - component={component} multipleAlmEnabled={Boolean(multipleAlmEnabled)} - onCancel={this.handleCancel} - onConfirmDelete={this.deleteConfiguration} - onCheck={this.handleCheck} + onCancelDelete={this.handleCancelDelete} + onConfirmDelete={this.handleConfirmDelete} + onCheckConfiguration={this.handleCheck} onDelete={this.handleDelete} - onSelectAlm={this.handleSelectAlm} + onSelectAlmTab={this.handleSelectAlm} onUpdateDefinitions={this.fetchPullRequestDecorationSetting} - {...this.state} + currentAlmTab={currentAlmTab} + definitionKeyForDeletion={definitionKeyForDeletion} + definitions={definitions} + definitionStatus={definitionStatus} + loadingAlmDefinitions={loadingAlmDefinitions} + loadingProjectCount={loadingProjectCount} + projectCount={projectCount} /> ); } diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmIntegrationRenderer.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmIntegrationRenderer.tsx index 92ced87796c..f0289cb6318 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmIntegrationRenderer.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmIntegrationRenderer.tsx @@ -26,27 +26,24 @@ import { AlmSettingsBindingDefinitions, AlmSettingsBindingStatus } from '../../../../types/alm-settings'; -import AzureTab from './AzureTab'; -import BitbucketTab from './BitbucketTab'; +import { AlmTabs } from './AlmIntegration'; +import AlmTab from './AlmTab'; import DeleteModal from './DeleteModal'; -import GithubTab from './GithubTab'; -import GitlabTab from './GitlabTab'; export interface AlmIntegrationRendererProps { branchesEnabled: boolean; - component?: T.Component; - currentAlm: AlmKeys; + currentAlmTab: AlmTabs; definitionKeyForDeletion?: string; definitions: AlmSettingsBindingDefinitions; definitionStatus: T.Dict<AlmSettingsBindingStatus>; loadingAlmDefinitions: boolean; loadingProjectCount: boolean; multipleAlmEnabled: boolean; - onCancel: () => void; - onCheck: (definitionKey: string) => void; + onCancelDelete: () => void; + onCheckConfiguration: (definitionKey: string) => void; onConfirmDelete: (definitionKey: string) => void; onDelete: (definitionKey: string) => void; - onSelectAlm: (alm: AlmKeys) => void; + onSelectAlmTab: (alm: AlmTabs) => void; onUpdateDefinitions: () => void; projectCount?: number; } @@ -64,8 +61,7 @@ const tabs = [ /> GitHub </> - ), - requiresBranchesEnabled: false + ) }, { key: AlmKeys.BitbucketServer, @@ -79,8 +75,7 @@ const tabs = [ /> Bitbucket </> - ), - requiresBranchesEnabled: false + ) }, { key: AlmKeys.Azure, @@ -94,8 +89,7 @@ const tabs = [ /> Azure DevOps </> - ), - requiresBranchesEnabled: false + ) }, { key: AlmKeys.GitLab, @@ -109,18 +103,16 @@ const tabs = [ /> GitLab </> - ), - requiresBranchesEnabled: false + ) } ]; export default function AlmIntegrationRenderer(props: AlmIntegrationRendererProps) { const { - component, definitionKeyForDeletion, definitions, definitionStatus, - currentAlm, + currentAlmTab, loadingAlmDefinitions, loadingProjectCount, branchesEnabled, @@ -128,6 +120,13 @@ export default function AlmIntegrationRenderer(props: AlmIntegrationRendererProp projectCount } = props; + const bindingDefinitions = { + [AlmKeys.Azure]: definitions.azure, + [AlmKeys.GitLab]: definitions.gitlab, + [AlmKeys.GitHub]: definitions.github, + [AlmKeys.BitbucketServer]: [...definitions.bitbucket, ...definitions.bitbucketcloud] + }; + return ( <> <header className="page-header"> @@ -137,70 +136,26 @@ export default function AlmIntegrationRenderer(props: AlmIntegrationRendererProp <div className="markdown small spacer-top big-spacer-bottom"> {translate('settings.almintegration.description')} </div> - <BoxedTabs - onSelect={props.onSelectAlm} - selected={currentAlm} - tabs={tabs.filter(tab => !(tab.requiresBranchesEnabled && !branchesEnabled))} - /> - {currentAlm === AlmKeys.Azure && ( - <AzureTab - branchesEnabled={branchesEnabled} - definitions={definitions.azure} - definitionStatus={definitionStatus} - loadingAlmDefinitions={loadingAlmDefinitions} - loadingProjectCount={loadingProjectCount} - multipleAlmEnabled={multipleAlmEnabled} - onCheck={props.onCheck} - onDelete={props.onDelete} - onUpdateDefinitions={props.onUpdateDefinitions} - /> - )} - {currentAlm === AlmKeys.BitbucketServer && ( - <BitbucketTab - branchesEnabled={branchesEnabled} - definitions={[...definitions.bitbucket, ...definitions.bitbucketcloud]} - definitionStatus={definitionStatus} - loadingAlmDefinitions={loadingAlmDefinitions} - loadingProjectCount={loadingProjectCount} - multipleAlmEnabled={multipleAlmEnabled} - onCheck={props.onCheck} - onDelete={props.onDelete} - onUpdateDefinitions={props.onUpdateDefinitions} - /> - )} - {currentAlm === AlmKeys.GitHub && ( - <GithubTab - branchesEnabled={branchesEnabled} - component={component} - definitions={definitions.github} - definitionStatus={definitionStatus} - loadingAlmDefinitions={loadingAlmDefinitions} - loadingProjectCount={loadingProjectCount} - multipleAlmEnabled={multipleAlmEnabled} - onCheck={props.onCheck} - onDelete={props.onDelete} - onUpdateDefinitions={props.onUpdateDefinitions} - /> - )} - {currentAlm === AlmKeys.GitLab && ( - <GitlabTab - branchesEnabled={branchesEnabled} - definitions={definitions.gitlab} - definitionStatus={definitionStatus} - loadingAlmDefinitions={loadingAlmDefinitions} - loadingProjectCount={loadingProjectCount} - multipleAlmEnabled={multipleAlmEnabled} - onCheck={props.onCheck} - onDelete={props.onDelete} - onUpdateDefinitions={props.onUpdateDefinitions} - /> - )} + <BoxedTabs onSelect={props.onSelectAlmTab} selected={currentAlmTab} tabs={tabs} /> + + <AlmTab + almTab={currentAlmTab} + branchesEnabled={branchesEnabled} + definitions={bindingDefinitions[currentAlmTab]} + definitionStatus={definitionStatus} + loadingAlmDefinitions={loadingAlmDefinitions} + loadingProjectCount={loadingProjectCount} + multipleAlmEnabled={multipleAlmEnabled} + onCheck={props.onCheckConfiguration} + onDelete={props.onDelete} + onUpdateDefinitions={props.onUpdateDefinitions} + /> {definitionKeyForDeletion && ( <DeleteModal id={definitionKeyForDeletion} - onCancel={props.onCancel} + onCancel={props.onCancelDelete} onDelete={props.onConfirmDelete} projectCount={projectCount} /> diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmTab.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmTab.tsx index 6e54d53897d..1dd264c83b7 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmTab.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmTab.tsx @@ -20,43 +20,33 @@ import * as React from 'react'; import { AlmBindingDefinition, - AlmKeys, + AlmBindingDefinitionBase, AlmSettingsBindingStatus } from '../../../../types/alm-settings'; -import { AlmBindingDefinitionFormChildrenProps } from './AlmBindingDefinitionForm'; +import { AlmTabs } from './AlmIntegration'; import AlmTabRenderer from './AlmTabRenderer'; -interface Props<B> { - alm: AlmKeys; +interface Props { + almTab: AlmTabs; branchesEnabled: boolean; - createConfiguration: (data: B) => Promise<void>; - defaultBinding: B; - definitions: B[]; + definitions: AlmBindingDefinition[]; definitionStatus: T.Dict<AlmSettingsBindingStatus>; - form: (props: AlmBindingDefinitionFormChildrenProps<B>) => React.ReactNode; - help: React.ReactNode; loadingAlmDefinitions: boolean; loadingProjectCount: boolean; multipleAlmEnabled: boolean; onCheck: (definitionKey: string) => void; onDelete: (definitionKey: string) => void; onUpdateDefinitions: () => void; - optionalFields?: Array<keyof B>; - updateConfiguration: (data: B & { newKey?: string }) => Promise<void>; } -interface State<B> { - editedDefinition?: B; - submitting: boolean; - success: boolean; +interface State { + editedDefinition?: AlmBindingDefinition; + editDefinition?: boolean; } -export default class AlmTab<B extends AlmBindingDefinition> extends React.PureComponent< - Props<B>, - State<B> -> { +export default class AlmTab extends React.PureComponent<Props, State> { + state: State = {}; mounted = false; - state: State<B> = { submitting: false, success: false }; componentDidMount() { this.mounted = true; @@ -67,81 +57,60 @@ export default class AlmTab<B extends AlmBindingDefinition> extends React.PureCo } handleCancel = () => { - this.setState({ editedDefinition: undefined, success: false }); + this.setState({ editDefinition: false, editedDefinition: undefined }); }; handleCreate = () => { - this.setState({ editedDefinition: this.props.defaultBinding, success: false }); + this.setState({ editDefinition: true, editedDefinition: undefined }); }; handleEdit = (definitionKey: string) => { const editedDefinition = this.props.definitions.find(d => d.key === definitionKey); - this.setState({ editedDefinition, success: false }); + this.setState({ editDefinition: true, editedDefinition }); }; - handleSubmit = (config: B, originalKey: string) => { - const call = originalKey - ? this.props.updateConfiguration({ newKey: config.key, ...config, key: originalKey }) - : this.props.createConfiguration({ ...config }); - - this.setState({ submitting: true }); - return call - .then(() => { - if (this.mounted) { - this.setState({ - editedDefinition: undefined, - submitting: false, - success: true - }); - } - }) - .then(this.props.onUpdateDefinitions) - .then(() => { - this.props.onCheck(config.key); - }) - .catch(() => { - if (this.mounted) { - this.setState({ submitting: false, success: false }); - } + handleAfterSubmit = (config: AlmBindingDefinitionBase) => { + if (this.mounted) { + this.setState({ + editDefinition: false, + editedDefinition: undefined }); + } + + this.props.onUpdateDefinitions(); + + this.props.onCheck(config.key); }; render() { const { - alm, + almTab, branchesEnabled, definitions, definitionStatus, - form, - help, loadingAlmDefinitions, loadingProjectCount, - multipleAlmEnabled, - optionalFields + multipleAlmEnabled } = this.props; - const { editedDefinition, submitting, success } = this.state; + const { editDefinition, editedDefinition } = this.state; return ( <AlmTabRenderer - alm={alm} + almTab={almTab} branchesEnabled={branchesEnabled} definitions={definitions} definitionStatus={definitionStatus} + editDefinition={editDefinition} editedDefinition={editedDefinition} - form={form} - help={help} loadingAlmDefinitions={loadingAlmDefinitions} loadingProjectCount={loadingProjectCount} multipleAlmEnabled={multipleAlmEnabled} - onCancel={this.handleCancel} onCheck={this.props.onCheck} onCreate={this.handleCreate} onDelete={this.props.onDelete} onEdit={this.handleEdit} - onSubmit={this.handleSubmit} - optionalFields={optionalFields} - submitting={submitting} - success={success} + onCancel={this.handleCancel} + afterSubmit={this.handleAfterSubmit} /> ); } diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmTabRenderer.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmTabRenderer.tsx index 2e8380d91d4..667059ef4fb 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmTabRenderer.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmTabRenderer.tsx @@ -23,24 +23,25 @@ import DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner'; import { translate } from 'sonar-ui-common/helpers/l10n'; import { AlmBindingDefinition, + AlmBindingDefinitionBase, AlmKeys, AlmSettingsBindingStatus, isBitbucketCloudBindingDefinition } from '../../../../types/alm-settings'; +import { ALM_INTEGRATION } from '../AdditionalCategoryKeys'; +import CategoryDefinitionsList from '../CategoryDefinitionsList'; import AlmBindingDefinitionBox from './AlmBindingDefinitionBox'; -import AlmBindingDefinitionForm, { - AlmBindingDefinitionFormChildrenProps -} from './AlmBindingDefinitionForm'; +import AlmBindingDefinitionForm from './AlmBindingDefinitionForm'; +import { AlmTabs } from './AlmIntegration'; import CreationTooltip from './CreationTooltip'; -export interface AlmTabRendererProps<B> { - alm: AlmKeys; +export interface AlmTabRendererProps { + almTab: AlmTabs; branchesEnabled: boolean; definitionStatus: T.Dict<AlmSettingsBindingStatus>; - editedDefinition?: B; - definitions: B[]; - form: (props: AlmBindingDefinitionFormChildrenProps<B>) => React.ReactNode; - help: React.ReactNode; + editDefinition?: boolean; + editedDefinition?: AlmBindingDefinition; + definitions: AlmBindingDefinition[]; loadingAlmDefinitions: boolean; loadingProjectCount: boolean; multipleAlmEnabled: boolean; @@ -49,74 +50,73 @@ export interface AlmTabRendererProps<B> { onCreate: () => void; onDelete: (definitionKey: string) => void; onEdit: (definitionKey: string) => void; - onSubmit: (config: B, originalKey: string) => void; - optionalFields?: Array<keyof B>; - submitting: boolean; - success: boolean; + afterSubmit: (config: AlmBindingDefinitionBase) => void; } -export default function AlmTabRenderer<B extends AlmBindingDefinition>( - props: AlmTabRendererProps<B> -) { +export default function AlmTabRenderer(props: AlmTabRendererProps) { const { - alm, + almTab, branchesEnabled, definitions, definitionStatus, + editDefinition, editedDefinition, - form, loadingAlmDefinitions, loadingProjectCount, - multipleAlmEnabled, - optionalFields, - help + multipleAlmEnabled } = props; const preventCreation = loadingProjectCount || (!multipleAlmEnabled && definitions.length > 0); return ( - <div className="big-padded"> - <DeferredSpinner loading={loadingAlmDefinitions}> - {definitions.length === 0 && ( - <p className="spacer-top">{translate('settings.almintegration.empty', alm)}</p> - )} + <div className="bordered"> + <div className="big-padded"> + <DeferredSpinner loading={loadingAlmDefinitions}> + {definitions.length === 0 && ( + <p className="spacer-top">{translate('settings.almintegration.empty', almTab)}</p> + )} - <div className={definitions.length > 0 ? 'spacer-bottom text-right' : 'big-spacer-top'}> - <CreationTooltip alm={alm} preventCreation={preventCreation}> - <Button - data-test="settings__alm-create" - disabled={preventCreation} - onClick={props.onCreate}> - {translate('settings.almintegration.create')} - </Button> - </CreationTooltip> - </div> - {definitions.map(def => ( - <AlmBindingDefinitionBox - alm={isBitbucketCloudBindingDefinition(def) ? AlmKeys.BitbucketCloud : alm} - branchesEnabled={branchesEnabled} - definition={def} - key={def.key} - multipleDefinitions={definitions.length > 1} - onCheck={props.onCheck} - onDelete={props.onDelete} - onEdit={props.onEdit} - status={definitionStatus[def.key]} - /> - ))} + <div className={definitions.length > 0 ? 'spacer-bottom text-right' : 'big-spacer-top'}> + <CreationTooltip alm={almTab} preventCreation={preventCreation}> + <Button + data-test="settings__alm-create" + disabled={preventCreation} + onClick={props.onCreate}> + {translate('settings.almintegration.create')} + </Button> + </CreationTooltip> + </div> + {definitions.map(def => ( + <AlmBindingDefinitionBox + alm={isBitbucketCloudBindingDefinition(def) ? AlmKeys.BitbucketCloud : almTab} + branchesEnabled={branchesEnabled} + definition={def} + key={def.key} + multipleDefinitions={definitions.length > 1} + onCheck={props.onCheck} + onDelete={props.onDelete} + onEdit={props.onEdit} + status={definitionStatus[def.key]} + /> + ))} - {editedDefinition && ( - <AlmBindingDefinitionForm - bindingDefinition={editedDefinition} - help={help} - isSecondInstance={definitions.length === 1} - onCancel={props.onCancel} - onSubmit={props.onSubmit} - optionalFields={optionalFields}> - {form} - </AlmBindingDefinitionForm> - )} - </DeferredSpinner> + {editDefinition && ( + <AlmBindingDefinitionForm + alm={almTab} + bindingDefinition={editedDefinition} + alreadyHaveInstanceConfigured={definitions.length > 0} + onCancel={props.onCancel} + afterSubmit={props.afterSubmit} + /> + )} + </DeferredSpinner> + </div> + + <div className="huge-spacer-top huge-spacer-bottom bordered-top" /> + + <div className="big-padded"> + <CategoryDefinitionsList category={ALM_INTEGRATION} subCategory={almTab} /> + </div> </div> ); } diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AzureForm.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AzureForm.tsx index 24eacd2886a..f6638de1817 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AzureForm.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AzureForm.tsx @@ -18,8 +18,12 @@ * 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 { Alert } from 'sonar-ui-common/components/ui/Alert'; import { translate } from 'sonar-ui-common/helpers/l10n'; -import { AzureBindingDefinition } from '../../../../types/alm-settings'; +import { ALM_DOCUMENTATION_PATHS } from '../../../../helpers/constants'; +import { AlmKeys, AzureBindingDefinition } from '../../../../types/alm-settings'; import { AlmBindingDefinitionFormField } from './AlmBindingDefinitionFormField'; export interface AzureFormProps { @@ -31,43 +35,58 @@ export default function AzureForm(props: AzureFormProps) { const { formData, onFieldChange } = props; return ( - <> - <AlmBindingDefinitionFormField - autoFocus={true} - help={translate('settings.almintegration.form.name.azure.help')} - id="name.azure" - onFieldChange={onFieldChange} - propKey="key" - value={formData.key} - /> - <AlmBindingDefinitionFormField - help={ - <> - {translate('settings.almintegration.form.url.azure.help1')} - <br /> - <em>https://ado.your-company.com/your_collection</em> - <br /> - <br /> - {translate('settings.almintegration.form.url.azure.help2')} - <br /> - <em>https://dev.azure.com/your_organization</em> - </> - } - id="url.azure" - maxLength={2000} - onFieldChange={onFieldChange} - propKey="url" - value={formData.url || ''} - /> - <AlmBindingDefinitionFormField - help={translate('settings.almintegration.form.personal_access_token.azure.help')} - id="personal_access_token" - isTextArea={true} - onFieldChange={onFieldChange} - overwriteOnly={Boolean(formData.key)} - propKey="personalAccessToken" - value={formData.personalAccessToken} - /> - </> + <div className="display-flex-start"> + <div className="flex-1"> + <AlmBindingDefinitionFormField + autoFocus={true} + help={translate('settings.almintegration.form.name.azure.help')} + id="name.azure" + onFieldChange={onFieldChange} + propKey="key" + value={formData.key} + /> + <AlmBindingDefinitionFormField + help={ + <> + {translate('settings.almintegration.form.url.azure.help1')} + <br /> + <em>https://ado.your-company.com/your_collection</em> + <br /> + <br /> + {translate('settings.almintegration.form.url.azure.help2')} + <br /> + <em>https://dev.azure.com/your_organization</em> + </> + } + id="url.azure" + maxLength={2000} + onFieldChange={onFieldChange} + propKey="url" + value={formData.url || ''} + /> + <AlmBindingDefinitionFormField + help={translate('settings.almintegration.form.personal_access_token.azure.help')} + id="personal_access_token" + isTextArea={true} + onFieldChange={onFieldChange} + overwriteOnly={Boolean(formData.key)} + propKey="personalAccessToken" + value={formData.personalAccessToken} + /> + </div> + <Alert className="huge-spacer-left flex-1" variant="info"> + <FormattedMessage + defaultMessage={translate(`settings.almintegration.azure.info`)} + id="settings.almintegration.azure.info" + values={{ + link: ( + <Link target="_blank" to={ALM_DOCUMENTATION_PATHS[AlmKeys.Azure]}> + {translate('learn_more')} + </Link> + ) + }} + /> + </Alert> + </div> ); } diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AzureTab.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AzureTab.tsx deleted file mode 100644 index 5be30a1be4b..00000000000 --- a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AzureTab.tsx +++ /dev/null @@ -1,89 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2021 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 { translate } from 'sonar-ui-common/helpers/l10n'; -import { createAzureConfiguration, updateAzureConfiguration } from '../../../../api/alm-settings'; -import { ALM_DOCUMENTATION_PATHS } from '../../../../helpers/constants'; -import { - AlmKeys, - AlmSettingsBindingStatus, - AzureBindingDefinition -} from '../../../../types/alm-settings'; -import AlmTab from './AlmTab'; -import AzureForm from './AzureForm'; - -export interface AzureTabProps { - branchesEnabled: boolean; - definitions: AzureBindingDefinition[]; - definitionStatus: T.Dict<AlmSettingsBindingStatus>; - loadingAlmDefinitions: boolean; - loadingProjectCount: boolean; - multipleAlmEnabled: boolean; - onCheck: (definitionKey: string) => void; - onDelete: (definitionKey: string) => void; - onUpdateDefinitions: () => void; -} - -export default function AzureTab(props: AzureTabProps) { - const { - branchesEnabled, - multipleAlmEnabled, - definitions, - definitionStatus, - loadingAlmDefinitions, - loadingProjectCount - } = props; - - return ( - <div className="bordered"> - <AlmTab - alm={AlmKeys.Azure} - branchesEnabled={branchesEnabled} - createConfiguration={createAzureConfiguration} - defaultBinding={{ key: '', personalAccessToken: '', url: '' }} - definitions={definitions} - definitionStatus={definitionStatus} - form={childProps => <AzureForm {...childProps} />} - help={ - <FormattedMessage - defaultMessage={translate(`settings.almintegration.azure.info`)} - id="settings.almintegration.azure.info" - values={{ - link: ( - <Link target="_blank" to={ALM_DOCUMENTATION_PATHS[AlmKeys.Azure]}> - {translate('learn_more')} - </Link> - ) - }} - /> - } - loadingAlmDefinitions={loadingAlmDefinitions} - loadingProjectCount={loadingProjectCount} - multipleAlmEnabled={multipleAlmEnabled} - onCheck={props.onCheck} - onDelete={props.onDelete} - onUpdateDefinitions={props.onUpdateDefinitions} - updateConfiguration={updateAzureConfiguration} - /> - </div> - ); -} diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/BitbucketCloudForm.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/BitbucketCloudForm.tsx new file mode 100644 index 00000000000..c1ec4713ff5 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/BitbucketCloudForm.tsx @@ -0,0 +1,85 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 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 { translate } from 'sonar-ui-common/helpers/l10n'; +import { BitbucketCloudBindingDefinition } from '../../../../types/alm-settings'; +import { AlmBindingDefinitionFormField } from './AlmBindingDefinitionFormField'; + +export interface BitbucketCloudFormProps { + formData: BitbucketCloudBindingDefinition; + onFieldChange: (fieldId: keyof BitbucketCloudBindingDefinition, value: string) => void; +} + +export default function BitbucketCloudForm(props: BitbucketCloudFormProps) { + const { formData } = props; + + return ( + <> + <AlmBindingDefinitionFormField + autoFocus={true} + help={translate('settings.almintegration.form.name.bitbucketcloud.help')} + id="name.bitbucket" + maxLength={100} + onFieldChange={props.onFieldChange} + propKey="key" + value={formData.key || ''} + /> + <AlmBindingDefinitionFormField + help={ + <FormattedMessage + defaultMessage={translate('settings.almintegration.form.workspace.bitbucketcloud.help')} + id="settings.almintegration.form.workspace.bitbucketcloud.help" + values={{ + example: ( + <> + {'https://bitbucket.org/'} + <strong>{'{workspace}'}</strong> + {'/{repository}'} + </> + ) + }} + /> + } + id="workspace.bitbucketcloud" + maxLength={2000} + onFieldChange={props.onFieldChange} + propKey="workspace" + value={formData.workspace || ''} + /> + <AlmBindingDefinitionFormField + id="client_id.bitbucketcloud" + help={translate('settings.almintegration.form.oauth_key.bitbucketcloud.help')} + onFieldChange={props.onFieldChange} + propKey="clientId" + value={formData.clientId || ''} + /> + <AlmBindingDefinitionFormField + id="client_secret.bitbucketcloud" + help={translate('settings.almintegration.form.oauth_secret.bitbucketcloud.help')} + onFieldChange={props.onFieldChange} + overwriteOnly={Boolean(formData.key)} + propKey="clientSecret" + value={formData.clientSecret || ''} + /> + </> + ); +} diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/BitbucketForm.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/BitbucketForm.tsx index 3fd09b75508..08612473b58 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/BitbucketForm.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/BitbucketForm.tsx @@ -19,40 +19,42 @@ */ import * as React from 'react'; import { FormattedMessage } from 'react-intl'; +import { Link } from 'react-router'; import RadioToggle from 'sonar-ui-common/components/controls/RadioToggle'; +import { Alert } from 'sonar-ui-common/components/ui/Alert'; import { translate } from 'sonar-ui-common/helpers/l10n'; +import { ALM_DOCUMENTATION_PATHS } from '../../../../helpers/constants'; import { AlmKeys, - BitbucketBindingDefinition, BitbucketCloudBindingDefinition, - isBitbucketBindingDefinition, - isBitbucketCloudBindingDefinition + BitbucketServerBindingDefinition } from '../../../../types/alm-settings'; -import { AlmBindingDefinitionFormField } from './AlmBindingDefinitionFormField'; +import BitbucketCloudForm from './BitbucketCloudForm'; +import BitbucketServerForm from './BitbucketServerForm'; export interface BitbucketFormProps { - formData: BitbucketBindingDefinition | BitbucketCloudBindingDefinition; - isCreating: boolean; + formData: BitbucketServerBindingDefinition | BitbucketCloudBindingDefinition; + isUpdate: boolean; onFieldChange: ( - fieldId: keyof (BitbucketBindingDefinition & BitbucketCloudBindingDefinition), + fieldId: keyof (BitbucketServerBindingDefinition & BitbucketCloudBindingDefinition), value: string ) => void; - onSelectVariant: (variant: AlmKeys.BitbucketServer | AlmKeys.BitbucketCloud) => void; variant?: AlmKeys.BitbucketServer | AlmKeys.BitbucketCloud; + onVariantChange: (variant: AlmKeys.BitbucketServer | AlmKeys.BitbucketCloud) => void; } export default function BitbucketForm(props: BitbucketFormProps) { - const { formData, isCreating, variant } = props; + const { isUpdate, formData, variant } = props; return ( - <div> - {isCreating && ( - <> + <> + {!isUpdate && ( + <div className="display-flex-column"> <strong>{translate('settings.almintegration.form.choose_bitbucket_variant')}</strong> <RadioToggle className="little-spacer-top big-spacer-bottom" name="variant" - onCheck={props.onSelectVariant} + onCheck={props.onVariantChange} options={[ { label: 'Bitbucket Server', @@ -62,117 +64,67 @@ export default function BitbucketForm(props: BitbucketFormProps) { ]} value={variant} /> - </> - )} - - {variant === AlmKeys.BitbucketServer && isBitbucketBindingDefinition(formData) && ( - <div> - <AlmBindingDefinitionFormField - autoFocus={true} - help={translate('settings.almintegration.form.name.bitbucket.help')} - id="name.bitbucket" - maxLength={100} - onFieldChange={props.onFieldChange} - propKey="key" - value={formData.key} - /> - <AlmBindingDefinitionFormField - help={ - <FormattedMessage - defaultMessage={translate('settings.almintegration.form.url.bitbucket.help')} - id="settings.almintegration.form.url.bitbucket.help" - values={{ example: 'https://bitbucket-server.your-company.com' }} - /> - } - id="url.bitbucket" - maxLength={2000} - onFieldChange={props.onFieldChange} - propKey="url" - value={formData.url} - /> - <AlmBindingDefinitionFormField - help={ - <FormattedMessage - defaultMessage={translate( - 'settings.almintegration.form.personal_access_token.bitbucket.help' - )} - id="settings.almintegration.form.personal_access_token.bitbucket.help" - values={{ - pat: ( - <a - href="https://confluence.atlassian.com/bitbucketserver0515/personal-access-tokens-961275199.html" - rel="noopener noreferrer" - target="_blank"> - {translate( - 'settings.almintegration.form.personal_access_token.bitbucket.help.url' - )} - </a> - ) - }} - /> - } - id="personal_access_token" - isTextArea={true} - onFieldChange={props.onFieldChange} - overwriteOnly={Boolean(formData.key)} - propKey="personalAccessToken" - value={formData.personalAccessToken} - /> </div> )} - {variant === AlmKeys.BitbucketCloud && isBitbucketCloudBindingDefinition(formData) && ( - <div> - <AlmBindingDefinitionFormField - autoFocus={true} - help={translate('settings.almintegration.form.name.bitbucketcloud.help')} - id="name.bitbucket" - maxLength={100} - onFieldChange={props.onFieldChange} - propKey="key" - value={formData.key} - /> - <AlmBindingDefinitionFormField - help={ - <FormattedMessage - defaultMessage={translate( - 'settings.almintegration.form.workspace.bitbucketcloud.help' - )} - id="settings.almintegration.form.workspace.bitbucketcloud.help" - values={{ - example: ( - <> - {'https://bitbucket.org/'} - <strong>{'{workspace}'}</strong> - {'/{repository}'} - </> - ) - }} - /> - } - id="workspace.bitbucketcloud" - maxLength={2000} - onFieldChange={props.onFieldChange} - propKey="workspace" - value={formData.workspace} - /> - <AlmBindingDefinitionFormField - help={translate('settings.almintegration.form.oauth_key.bitbucketcloud.help')} - id="client_id.bitbucketcloud" - onFieldChange={props.onFieldChange} - propKey="clientId" - value={formData.clientId} - /> - <AlmBindingDefinitionFormField - help={translate('settings.almintegration.form.oauth_secret.bitbucketcloud.help')} - id="client_secret.bitbucketcloud" - onFieldChange={props.onFieldChange} - overwriteOnly={Boolean(formData.key)} - propKey="clientSecret" - value={formData.clientSecret} - /> - </div> + {variant !== undefined && ( + <> + {variant === AlmKeys.BitbucketServer && ( + <div className="display-flex-start"> + <div className="flex-1"> + <BitbucketServerForm + onFieldChange={props.onFieldChange} + formData={formData as BitbucketServerBindingDefinition} + /> + </div> + <Alert className="huge-spacer-left flex-1" variant="info"> + <> + <h3>{translate('onboarding.create_project.pat_help.title')}</h3> + + <p className="big-spacer-top"> + {translate('settings.almintegration.bitbucket.help_1')} + </p> + + <ul className="big-spacer-top list-styled"> + <li>{translate('settings.almintegration.bitbucket.help_2')}</li> + <li>{translate('settings.almintegration.bitbucket.help_3')}</li> + </ul> + + <p className="big-spacer-top big-spacer-bottom"> + <Link target="_blank" to={ALM_DOCUMENTATION_PATHS[AlmKeys.BitbucketServer]}> + {translate('learn_more')} + </Link> + </p> + </> + </Alert> + </div> + )} + + {variant === AlmKeys.BitbucketCloud && ( + <div className="display-flex-start"> + <div className="flex-1"> + <BitbucketCloudForm + onFieldChange={props.onFieldChange} + formData={formData as BitbucketCloudBindingDefinition} + /> + </div> + <Alert className="huge-spacer-left flex-1" variant="info"> + <FormattedMessage + defaultMessage={translate(`settings.almintegration.bitbucketcloud.info`)} + id="settings.almintegration.bitbucketcloud.info" + values={{ + link: ( + <Link target="_blank" to={ALM_DOCUMENTATION_PATHS[AlmKeys.BitbucketCloud]}> + {translate('learn_more')} + </Link> + ) + }} + /> + </Alert> + </div> + )} + </> )} - </div> + </> ); } diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/BitbucketServerForm.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/BitbucketServerForm.tsx new file mode 100644 index 00000000000..d2ac778e29a --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/BitbucketServerForm.tsx @@ -0,0 +1,91 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 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 { translate } from 'sonar-ui-common/helpers/l10n'; +import { BitbucketServerBindingDefinition } from '../../../../types/alm-settings'; +import { AlmBindingDefinitionFormField } from './AlmBindingDefinitionFormField'; + +export interface BitbucketServerFormProps { + formData: BitbucketServerBindingDefinition; + onFieldChange: (fieldId: keyof BitbucketServerBindingDefinition, value: string) => void; +} + +export default function BitbucketServerForm(props: BitbucketServerFormProps) { + const { formData } = props; + + return ( + <> + {' '} + <AlmBindingDefinitionFormField + autoFocus={true} + help={translate('settings.almintegration.form.name.bitbucket.help')} + id="name.bitbucket" + maxLength={100} + onFieldChange={props.onFieldChange} + propKey="key" + value={formData.key || ''} + /> + <AlmBindingDefinitionFormField + help={ + <FormattedMessage + defaultMessage={translate('settings.almintegration.form.url.bitbucket.help')} + id="settings.almintegration.form.url.bitbucket.help" + values={{ example: 'https://bitbucket-server.your-company.com' }} + /> + } + id="url.bitbucket" + maxLength={2000} + onFieldChange={props.onFieldChange} + propKey="url" + value={formData.url || ''} + /> + <AlmBindingDefinitionFormField + id="personal_access_token" + help={ + <FormattedMessage + defaultMessage={translate( + 'settings.almintegration.form.personal_access_token.bitbucket.help' + )} + id="settings.almintegration.form.personal_access_token.bitbucket.help" + values={{ + pat: ( + <a + href="https://confluence.atlassian.com/bitbucketserver0515/personal-access-tokens-961275199.html" + rel="noopener noreferrer" + target="_blank"> + {translate( + 'settings.almintegration.form.personal_access_token.bitbucket.help.url' + )} + </a> + ) + }} + /> + } + isTextArea={true} + onFieldChange={props.onFieldChange} + overwriteOnly={Boolean(formData.key)} + propKey="personalAccessToken" + value={formData.personalAccessToken || ''} + /> + </> + ); +} diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/BitbucketTab.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/BitbucketTab.tsx deleted file mode 100644 index df05024ece7..00000000000 --- a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/BitbucketTab.tsx +++ /dev/null @@ -1,186 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2021 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 { - createBitbucketCloudConfiguration, - createBitbucketConfiguration, - updateBitbucketCloudConfiguration, - updateBitbucketConfiguration -} from '../../../../api/alm-settings'; -import { - AlmKeys, - AlmSettingsBindingStatus, - BitbucketBindingDefinition, - BitbucketCloudBindingDefinition, - isBitbucketBindingDefinition -} from '../../../../types/alm-settings'; -import BitbucketTabRenderer from './BitbucketTabRenderer'; - -interface Props { - branchesEnabled: boolean; - definitions: Array<BitbucketBindingDefinition | BitbucketCloudBindingDefinition>; - definitionStatus: T.Dict<AlmSettingsBindingStatus>; - loadingAlmDefinitions: boolean; - loadingProjectCount: boolean; - multipleAlmEnabled: boolean; - onCheck: (definitionKey: string) => void; - onDelete: (definitionKey: string) => void; - onUpdateDefinitions: () => void; -} - -interface State { - editedDefinition?: BitbucketBindingDefinition | BitbucketCloudBindingDefinition; - isCreating: boolean; - submitting: boolean; - success: boolean; - variant?: AlmKeys.BitbucketServer | AlmKeys.BitbucketCloud; -} - -export const DEFAULT_SERVER_BINDING = { key: '', url: '', personalAccessToken: '' }; -export const DEFAULT_CLOUD_BINDING = { key: '', clientId: '', clientSecret: '', workspace: '' }; - -export default class BitbucketTab extends React.PureComponent<Props, State> { - mounted = false; - state: State = { isCreating: false, submitting: false, success: false }; - - componentDidMount() { - this.mounted = true; - } - - componentWillUnmount() { - this.mounted = false; - } - - handleCancel = () => { - this.setState({ - editedDefinition: undefined, - isCreating: false, - success: false, - variant: undefined - }); - }; - - handleCreate = () => { - this.setState({ - editedDefinition: DEFAULT_SERVER_BINDING, // Default to Bitbucket Server. - isCreating: true, - success: false, - variant: undefined - }); - }; - - handleSelectVariant = (variant: AlmKeys.BitbucketServer | AlmKeys.BitbucketCloud) => { - this.setState({ - variant, - editedDefinition: - variant === AlmKeys.BitbucketServer ? DEFAULT_SERVER_BINDING : DEFAULT_CLOUD_BINDING - }); - }; - - handleEdit = (definitionKey: string) => { - const editedDefinition = this.props.definitions.find(d => d.key === definitionKey); - const variant = isBitbucketBindingDefinition(editedDefinition) - ? AlmKeys.BitbucketServer - : AlmKeys.BitbucketCloud; - this.setState({ editedDefinition, variant, success: false }); - }; - - handleSubmit = ( - config: BitbucketBindingDefinition | BitbucketCloudBindingDefinition, - originalKey: string - ) => { - const call = originalKey - ? this.updateConfiguration({ newKey: config.key, ...config, key: originalKey }) - : this.createConfiguration({ ...config }); - - this.setState({ submitting: true }); - return call - .then(() => { - if (this.mounted) { - this.setState({ - editedDefinition: undefined, - isCreating: false, - submitting: false, - success: true - }); - } - }) - .then(this.props.onUpdateDefinitions) - .then(() => { - this.props.onCheck(config.key); - }) - .catch(() => { - if (this.mounted) { - this.setState({ submitting: false, success: false }); - } - }); - }; - - updateConfiguration = ( - config: (BitbucketBindingDefinition | BitbucketCloudBindingDefinition) & { newKey: string } - ) => { - if (isBitbucketBindingDefinition(config)) { - return updateBitbucketConfiguration(config); - } - return updateBitbucketCloudConfiguration(config); - }; - - createConfiguration = (config: BitbucketBindingDefinition | BitbucketCloudBindingDefinition) => { - if (isBitbucketBindingDefinition(config)) { - return createBitbucketConfiguration(config); - } - return createBitbucketCloudConfiguration(config); - }; - - render() { - const { - branchesEnabled, - definitions, - definitionStatus, - loadingAlmDefinitions, - loadingProjectCount, - multipleAlmEnabled - } = this.props; - const { editedDefinition, isCreating, submitting, success, variant } = this.state; - - return ( - <BitbucketTabRenderer - branchesEnabled={branchesEnabled} - definitions={definitions} - definitionStatus={definitionStatus} - editedDefinition={editedDefinition} - isCreating={isCreating} - loadingAlmDefinitions={loadingAlmDefinitions} - loadingProjectCount={loadingProjectCount} - multipleAlmEnabled={multipleAlmEnabled} - onCancel={this.handleCancel} - onCheck={this.props.onCheck} - onCreate={this.handleCreate} - onDelete={this.props.onDelete} - onEdit={this.handleEdit} - onSelectVariant={this.handleSelectVariant} - onSubmit={this.handleSubmit} - submitting={submitting} - success={success} - variant={variant} - /> - ); - } -} diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/BitbucketTabRenderer.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/BitbucketTabRenderer.tsx deleted file mode 100644 index e586f2b8d36..00000000000 --- a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/BitbucketTabRenderer.tsx +++ /dev/null @@ -1,140 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2021 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 { translate } from 'sonar-ui-common/helpers/l10n'; -import { ALM_DOCUMENTATION_PATHS } from '../../../../helpers/constants'; -import { - AlmKeys, - AlmSettingsBindingStatus, - BitbucketBindingDefinition, - BitbucketCloudBindingDefinition -} from '../../../../types/alm-settings'; -import AlmTabRenderer from './AlmTabRenderer'; -import BitbucketForm from './BitbucketForm'; - -export interface BitbucketTabRendererProps { - branchesEnabled: boolean; - definitionStatus: T.Dict<AlmSettingsBindingStatus>; - editedDefinition?: BitbucketBindingDefinition | BitbucketCloudBindingDefinition; - definitions: Array<BitbucketBindingDefinition | BitbucketCloudBindingDefinition>; - isCreating: boolean; - loadingAlmDefinitions: boolean; - loadingProjectCount: boolean; - multipleAlmEnabled: boolean; - onCancel: () => void; - onCheck: (definitionKey: string) => void; - onCreate: () => void; - onDelete: (definitionKey: string) => void; - onEdit: (definitionKey: string) => void; - onSelectVariant: (variant: AlmKeys.BitbucketServer | AlmKeys.BitbucketCloud) => void; - onSubmit: ( - config: BitbucketBindingDefinition | BitbucketCloudBindingDefinition, - originalKey: string - ) => void; - submitting: boolean; - success: boolean; - variant?: AlmKeys.BitbucketServer | AlmKeys.BitbucketCloud; -} - -export default function BitbucketTabRenderer(props: BitbucketTabRendererProps) { - const { - branchesEnabled, - editedDefinition, - definitions, - definitionStatus, - isCreating, - loadingAlmDefinitions, - loadingProjectCount, - multipleAlmEnabled, - submitting, - success, - variant - } = props; - - let help; - if (variant === AlmKeys.BitbucketServer) { - help = ( - <> - <h3>{translate('onboarding.create_project.pat_help.title')}</h3> - - <p className="big-spacer-top">{translate('settings.almintegration.bitbucket.help_1')}</p> - - <ul className="big-spacer-top list-styled"> - <li>{translate('settings.almintegration.bitbucket.help_2')}</li> - <li>{translate('settings.almintegration.bitbucket.help_3')}</li> - </ul> - - <p className="big-spacer-top big-spacer-bottom"> - <Link target="_blank" to={ALM_DOCUMENTATION_PATHS[AlmKeys.BitbucketServer]}> - {translate('learn_more')} - </Link> - </p> - </> - ); - } else if (variant === AlmKeys.BitbucketCloud) { - help = ( - <FormattedMessage - defaultMessage={translate(`settings.almintegration.bitbucketcloud.info`)} - id="settings.almintegration.bitbucketcloud.info" - values={{ - link: ( - <Link target="_blank" to={ALM_DOCUMENTATION_PATHS[AlmKeys.BitbucketCloud]}> - {translate('learn_more')} - </Link> - ) - }} - /> - ); - } - - return ( - <div className="bordered"> - <AlmTabRenderer - branchesEnabled={branchesEnabled} - alm={AlmKeys.BitbucketServer} // Always use Bitbucket Server for the translation keys. - definitions={definitions} - definitionStatus={definitionStatus} - editedDefinition={editedDefinition} - form={childProps => ( - <BitbucketForm - isCreating={isCreating} - onSelectVariant={props.onSelectVariant} - variant={variant} - {...childProps} - /> - )} - help={help} - loadingAlmDefinitions={loadingAlmDefinitions} - loadingProjectCount={loadingProjectCount} - multipleAlmEnabled={multipleAlmEnabled} - onCancel={props.onCancel} - onCheck={props.onCheck} - onCreate={props.onCreate} - onDelete={props.onDelete} - onEdit={props.onEdit} - onSubmit={props.onSubmit} - submitting={submitting} - success={success} - /> - </div> - ); -} diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/GithubForm.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/GithubForm.tsx index a1a782a2241..bf75e18c801 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/GithubForm.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/GithubForm.tsx @@ -18,8 +18,12 @@ * 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 { Alert } from 'sonar-ui-common/components/ui/Alert'; import { translate } from 'sonar-ui-common/helpers/l10n'; -import { GithubBindingDefinition } from '../../../../types/alm-settings'; +import { ALM_DOCUMENTATION_PATHS } from '../../../../helpers/constants'; +import { AlmKeys, GithubBindingDefinition } from '../../../../types/alm-settings'; import { AlmBindingDefinitionFormField } from './AlmBindingDefinitionFormField'; export interface GithubFormProps { @@ -31,68 +35,83 @@ export default function GithubForm(props: GithubFormProps) { const { formData, onFieldChange } = props; return ( - <> - <AlmBindingDefinitionFormField - autoFocus={true} - help={translate('settings.almintegration.form.name.github.help')} - id="name.github" - onFieldChange={onFieldChange} - propKey="key" - value={formData.key} - /> - <AlmBindingDefinitionFormField - help={ - <> - {translate('settings.almintegration.form.url.github.help1')} - <br /> - <em>https://github.company.com/api/v3</em> - <br /> - <br /> - {translate('settings.almintegration.form.url.github.help2')} - <br /> - <em>https://api.github.com/</em> - </> - } - id="url.github" - maxLength={2000} - onFieldChange={onFieldChange} - propKey="url" - value={formData.url} - /> - <AlmBindingDefinitionFormField - help={translate('settings.almintegration.form.app_id.github.help')} - id="app_id" - maxLength={80} - onFieldChange={onFieldChange} - propKey="appId" - value={formData.appId} - /> - <AlmBindingDefinitionFormField - help={translate('settings.almintegration.form.client_id.github.help')} - id="client_id.github" - maxLength={80} - onFieldChange={onFieldChange} - propKey="clientId" - value={formData.clientId} - /> - <AlmBindingDefinitionFormField - help={translate('settings.almintegration.form.client_secret.github.help')} - id="client_secret.github" - maxLength={80} - onFieldChange={onFieldChange} - overwriteOnly={Boolean(formData.key)} - propKey="clientSecret" - value={formData.clientSecret} - /> - <AlmBindingDefinitionFormField - help={translate('settings.almintegration.form.private_key.github.help')} - id="private_key" - isTextArea={true} - onFieldChange={onFieldChange} - overwriteOnly={Boolean(formData.key)} - propKey="privateKey" - value={formData.privateKey} - /> - </> + <div className="display-flex-start"> + <div className="flex-1"> + <AlmBindingDefinitionFormField + autoFocus={true} + help={translate('settings.almintegration.form.name.github.help')} + id="name.github" + onFieldChange={onFieldChange} + propKey="key" + value={formData.key} + /> + <AlmBindingDefinitionFormField + help={ + <> + {translate('settings.almintegration.form.url.github.help1')} + <br /> + <em>https://github.company.com/api/v3</em> + <br /> + <br /> + {translate('settings.almintegration.form.url.github.help2')} + <br /> + <em>https://api.github.com/</em> + </> + } + id="url.github" + maxLength={2000} + onFieldChange={onFieldChange} + propKey="url" + value={formData.url} + /> + <AlmBindingDefinitionFormField + id="app_id" + help={translate('settings.almintegration.form.app_id.github.help')} + maxLength={80} + onFieldChange={onFieldChange} + propKey="appId" + value={formData.appId} + /> + <AlmBindingDefinitionFormField + id="client_id.github" + help={translate('settings.almintegration.form.client_id.github.help')} + maxLength={80} + onFieldChange={onFieldChange} + propKey="clientId" + value={formData.clientId} + /> + <AlmBindingDefinitionFormField + id="client_secret.github" + help={translate('settings.almintegration.form.client_secret.github.help')} + maxLength={80} + onFieldChange={onFieldChange} + overwriteOnly={Boolean(formData.key)} + propKey="clientSecret" + value={formData.clientSecret} + /> + <AlmBindingDefinitionFormField + id="private_key" + help={translate('settings.almintegration.form.private_key.github.help')} + isTextArea={true} + onFieldChange={onFieldChange} + overwriteOnly={Boolean(formData.key)} + propKey="privateKey" + value={formData.privateKey} + /> + </div> + <Alert className="huge-spacer-left flex-1" variant="info"> + <FormattedMessage + defaultMessage={translate(`settings.almintegration.github.info`)} + id="settings.almintegration.github.info" + values={{ + link: ( + <Link target="_blank" to={ALM_DOCUMENTATION_PATHS[AlmKeys.GitHub]}> + {translate('learn_more')} + </Link> + ) + }} + /> + </Alert> + </div> ); } diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/GithubTab.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/GithubTab.tsx deleted file mode 100644 index 5a2371242da..00000000000 --- a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/GithubTab.tsx +++ /dev/null @@ -1,110 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2021 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 { translate } from 'sonar-ui-common/helpers/l10n'; -import { createGithubConfiguration, updateGithubConfiguration } from '../../../../api/alm-settings'; -import { ALM_DOCUMENTATION_PATHS } from '../../../../helpers/constants'; -import { - AlmKeys, - AlmSettingsBindingStatus, - GithubBindingDefinition -} from '../../../../types/alm-settings'; -import { ALM_INTEGRATION } from '../AdditionalCategoryKeys'; -import CategoryDefinitionsList from '../CategoryDefinitionsList'; -import AlmTab from './AlmTab'; -import GithubForm from './GithubForm'; - -export interface GithubTabProps { - branchesEnabled: boolean; - component?: T.Component; - definitions: GithubBindingDefinition[]; - definitionStatus: T.Dict<AlmSettingsBindingStatus>; - loadingAlmDefinitions: boolean; - loadingProjectCount: boolean; - multipleAlmEnabled: boolean; - onCheck: (definitionKey: string) => void; - onDelete: (definitionKey: string) => void; - onUpdateDefinitions: () => void; -} - -export default function GithubTab(props: GithubTabProps) { - const { - branchesEnabled, - component, - multipleAlmEnabled, - definitions, - definitionStatus, - loadingAlmDefinitions, - loadingProjectCount - } = props; - - return ( - <div className="bordered"> - <AlmTab - alm={AlmKeys.GitHub} - branchesEnabled={branchesEnabled} - createConfiguration={createGithubConfiguration} - defaultBinding={{ - key: '', - appId: '', - clientId: '', - clientSecret: '', - url: '', - privateKey: '' - }} - definitions={definitions} - definitionStatus={definitionStatus} - form={childProps => <GithubForm {...childProps} />} - help={ - <FormattedMessage - defaultMessage={translate(`settings.almintegration.github.info`)} - id="settings.almintegration.github.info" - values={{ - link: ( - <Link target="_blank" to={ALM_DOCUMENTATION_PATHS[AlmKeys.GitHub]}> - {translate('learn_more')} - </Link> - ) - }} - /> - } - loadingAlmDefinitions={loadingAlmDefinitions} - loadingProjectCount={loadingProjectCount} - multipleAlmEnabled={multipleAlmEnabled} - onCheck={props.onCheck} - onDelete={props.onDelete} - onUpdateDefinitions={props.onUpdateDefinitions} - updateConfiguration={updateGithubConfiguration} - /> - - <div className="huge-spacer-top huge-spacer-bottom bordered-top" /> - - <div className="big-padded"> - <CategoryDefinitionsList - category={ALM_INTEGRATION} - component={component} - subCategory={AlmKeys.GitHub} - /> - </div> - </div> - ); -} diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/GitlabForm.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/GitlabForm.tsx index 62b8daf8db7..34406e9ae7c 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/GitlabForm.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/GitlabForm.tsx @@ -18,8 +18,12 @@ * 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 { Alert } from 'sonar-ui-common/components/ui/Alert'; import { translate } from 'sonar-ui-common/helpers/l10n'; -import { GitlabBindingDefinition } from '../../../../types/alm-settings'; +import { ALM_DOCUMENTATION_PATHS } from '../../../../helpers/constants'; +import { AlmKeys, GitlabBindingDefinition } from '../../../../types/alm-settings'; import { AlmBindingDefinitionFormField } from './AlmBindingDefinitionFormField'; export interface GitlabFormProps { @@ -31,38 +35,53 @@ export default function GitlabForm(props: GitlabFormProps) { const { formData, onFieldChange } = props; return ( - <> - <AlmBindingDefinitionFormField - autoFocus={true} - help={translate('settings.almintegration.form.name.gitlab.help')} - id="name.gitlab" - onFieldChange={onFieldChange} - propKey="key" - value={formData.key} - /> - <AlmBindingDefinitionFormField - help={ - <> - {translate('settings.almintegration.form.url.gitlab.help')} - <br /> - <em>https://gitlab.com/api/v4</em> - </> - } - id="url.gitlab" - maxLength={2000} - onFieldChange={onFieldChange} - propKey="url" - value={formData.url || ''} - /> - <AlmBindingDefinitionFormField - help={translate('settings.almintegration.form.personal_access_token.gitlab.help')} - id="personal_access_token" - isTextArea={true} - onFieldChange={onFieldChange} - overwriteOnly={Boolean(formData.key)} - propKey="personalAccessToken" - value={formData.personalAccessToken} - /> - </> + <div className="display-flex-start"> + <div className="flex-1"> + <AlmBindingDefinitionFormField + autoFocus={true} + help={translate('settings.almintegration.form.name.gitlab.help')} + id="name.gitlab" + onFieldChange={onFieldChange} + propKey="key" + value={formData.key} + /> + <AlmBindingDefinitionFormField + help={ + <> + {translate('settings.almintegration.form.url.gitlab.help')} + <br /> + <em>https://gitlab.com/api/v4</em> + </> + } + id="url.gitlab" + maxLength={2000} + onFieldChange={onFieldChange} + propKey="url" + value={formData.url || ''} + /> + <AlmBindingDefinitionFormField + help={translate('settings.almintegration.form.personal_access_token.gitlab.help')} + id="personal_access_token" + isTextArea={true} + onFieldChange={onFieldChange} + overwriteOnly={Boolean(formData.key)} + propKey="personalAccessToken" + value={formData.personalAccessToken} + /> + </div> + <Alert className="huge-spacer-left flex-1" variant="info"> + <FormattedMessage + defaultMessage={translate(`settings.almintegration.gitlab.info`)} + id="settings.almintegration.gitlab.info" + values={{ + link: ( + <Link target="_blank" to={ALM_DOCUMENTATION_PATHS[AlmKeys.GitLab]}> + {translate('learn_more')} + </Link> + ) + }} + /> + </Alert> + </div> ); } diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/GitlabTab.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/GitlabTab.tsx deleted file mode 100644 index 993b6214b70..00000000000 --- a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/GitlabTab.tsx +++ /dev/null @@ -1,103 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2021 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 { translate } from 'sonar-ui-common/helpers/l10n'; -import { createGitlabConfiguration, updateGitlabConfiguration } from '../../../../api/alm-settings'; -import { ALM_DOCUMENTATION_PATHS } from '../../../../helpers/constants'; -import { - AlmKeys, - AlmSettingsBindingStatus, - GitlabBindingDefinition -} from '../../../../types/alm-settings'; -import { ALM_INTEGRATION } from '../AdditionalCategoryKeys'; -import CategoryDefinitionsList from '../CategoryDefinitionsList'; -import AlmTab from './AlmTab'; -import GitlabForm from './GitlabForm'; - -export interface GitlabTabProps { - branchesEnabled: boolean; - component?: T.Component; - definitions: GitlabBindingDefinition[]; - definitionStatus: T.Dict<AlmSettingsBindingStatus>; - loadingAlmDefinitions: boolean; - loadingProjectCount: boolean; - multipleAlmEnabled: boolean; - onCheck: (definitionKey: string) => void; - onDelete: (definitionKey: string) => void; - onUpdateDefinitions: () => void; -} - -export default function GitlabTab(props: GitlabTabProps) { - const { - branchesEnabled, - component, - multipleAlmEnabled, - definitions, - definitionStatus, - loadingAlmDefinitions, - loadingProjectCount - } = props; - - return ( - <div className="bordered"> - <AlmTab - alm={AlmKeys.GitLab} - branchesEnabled={branchesEnabled} - createConfiguration={createGitlabConfiguration} - defaultBinding={{ key: '', personalAccessToken: '', url: '' }} - definitions={definitions} - definitionStatus={definitionStatus} - form={childProps => <GitlabForm {...childProps} />} - help={ - <FormattedMessage - defaultMessage={translate(`settings.almintegration.gitlab.info`)} - id="settings.almintegration.gitlab.info" - values={{ - link: ( - <Link target="_blank" to={ALM_DOCUMENTATION_PATHS[AlmKeys.GitLab]}> - {translate('learn_more')} - </Link> - ) - }} - /> - } - loadingAlmDefinitions={loadingAlmDefinitions} - loadingProjectCount={loadingProjectCount} - multipleAlmEnabled={multipleAlmEnabled} - onCheck={props.onCheck} - onDelete={props.onDelete} - onUpdateDefinitions={props.onUpdateDefinitions} - updateConfiguration={updateGitlabConfiguration} - /> - - <div className="huge-spacer-top huge-spacer-bottom bordered-top" /> - - <div className="big-padded"> - <CategoryDefinitionsList - category={ALM_INTEGRATION} - component={component} - subCategory={AlmKeys.GitLab} - /> - </div> - </div> - ); -} diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmBindingDefinitionForm-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmBindingDefinitionForm-test.tsx index 6c2d2acb59f..09f5f58c8a3 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmBindingDefinitionForm-test.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmBindingDefinitionForm-test.tsx @@ -20,41 +20,57 @@ import { shallow } from 'enzyme'; import * as React from 'react'; import { waitAndUpdate } from 'sonar-ui-common/helpers/testUtils'; -import { mockGithubBindingDefinition } from '../../../../../helpers/mocks/alm-settings'; -import { GithubBindingDefinition } from '../../../../../types/alm-settings'; +import { + createAzureConfiguration, + createBitbucketCloudConfiguration, + createBitbucketServerConfiguration, + createGithubConfiguration, + createGitlabConfiguration, + updateAzureConfiguration, + updateBitbucketCloudConfiguration, + updateBitbucketServerConfiguration, + updateGithubConfiguration, + updateGitlabConfiguration +} from '../../../../../api/alm-settings'; +import { + mockAzureBindingDefinition, + mockBitbucketCloudBindingDefinition, + mockBitbucketServerBindingDefinition, + mockGithubBindingDefinition, + mockGitlabBindingDefinition +} from '../../../../../helpers/mocks/alm-settings'; +import { AlmBindingDefinition, AlmKeys } from '../../../../../types/alm-settings'; import AlmBindingDefinitionForm from '../AlmBindingDefinitionForm'; +import AlmBindingDefinitionFormRenderer from '../AlmBindingDefinitionFormRenderer'; + +jest.mock('../../../../../api/alm-settings', () => ({ + createAzureConfiguration: jest.fn().mockResolvedValue({}), + createBitbucketCloudConfiguration: jest.fn().mockResolvedValue({}), + createBitbucketServerConfiguration: jest.fn().mockResolvedValue({}), + createGithubConfiguration: jest.fn().mockResolvedValue({}), + createGitlabConfiguration: jest.fn().mockResolvedValue({}), + updateAzureConfiguration: jest.fn().mockResolvedValue({}), + updateBitbucketCloudConfiguration: jest.fn().mockResolvedValue({}), + updateBitbucketServerConfiguration: jest.fn().mockResolvedValue({}), + updateGithubConfiguration: jest.fn().mockResolvedValue({}), + updateGitlabConfiguration: jest.fn().mockResolvedValue({}) +})); + +beforeEach(() => { + jest.clearAllMocks(); +}); it('should render correctly', () => { - expect(shallowRender()).toMatchSnapshot('create'); - + expect(shallowRender({ bindingDefinition: undefined })).toMatchSnapshot('create'); expect(shallowRender({ bindingDefinition: mockGithubBindingDefinition() })).toMatchSnapshot( 'edit' ); }); -it('should reset if the props change', () => { - const bindingDefinition = mockGithubBindingDefinition(); - const wrapper = shallowRender({ bindingDefinition }); - - wrapper.setState({ formData: { ...bindingDefinition, appId: 'newAppId' }, touched: true }); - wrapper.setProps({ bindingDefinition: { ...bindingDefinition } }); - expect(wrapper.state('touched')).toBe(true); - - wrapper.setProps({ bindingDefinition: mockGithubBindingDefinition({ key: 'diffKey' }) }); - expect(wrapper.state('touched')).toBe(false); -}); - it('should handle field changes', () => { - const wrapper = shallowRender(); + const formData = mockGithubBindingDefinition(); - const formData = { - key: 'github - example', - url: 'http://github.com', - appId: '34812568251', - clientId: 'cid', - clientSecret: 'csecret', - privateKey: 'gs7df9g7d9fsg7x9df7g9xdg' - }; + const wrapper = shallowRender(); wrapper.instance().handleFieldChange('key', formData.key); wrapper.instance().handleFieldChange('url', formData.url); @@ -63,69 +79,81 @@ it('should handle field changes', () => { wrapper.instance().handleFieldChange('clientSecret', formData.clientSecret); wrapper.instance().handleFieldChange('privateKey', formData.privateKey); expect(wrapper.state().formData).toEqual(formData); + expect(wrapper.state().touched).toBe(true); }); it('should handle form submit', async () => { - const onSubmit = jest.fn(); - const wrapper = shallowRender({ - onSubmit, - bindingDefinition: { - key: 'originalKey', - appId: '', - clientId: '', - clientSecret: '', - privateKey: '', - url: '' - } - }); - const formData = { - key: 'github instance', - url: 'http://github.enterprise.com', - appId: '34812568251', - clientId: 'client1234', - clientSecret: 'secret', - privateKey: 'gs7df9g7d9fsg7x9df7g9xdg' - }; - wrapper.setState({ formData }); - await waitAndUpdate(wrapper); + const afterSubmit = jest.fn(); + const formData = mockGithubBindingDefinition(); - wrapper.instance().handleFormSubmit(); + const wrapper = shallowRender({ afterSubmit }); - expect(onSubmit).toHaveBeenCalledWith(formData, 'originalKey'); + wrapper.instance().setState({ formData }); + await waitAndUpdate(wrapper); + await wrapper.instance().handleFormSubmit(); + await waitAndUpdate(wrapper); + expect(afterSubmit).toHaveBeenCalledWith(formData); }); -it('should handle cancelling', () => { - const onCancel = jest.fn(); - const bindingDefinition = { - appId: 'foo', - clientId: 'cid', - clientSecret: 'cs', - key: 'bar', - privateKey: 'baz', - url: 'http://github.enterprise.com' - }; - const wrapper = shallowRender({ - bindingDefinition, - onCancel - }); +it.each([ + [AlmKeys.Azure, undefined, createAzureConfiguration], + [AlmKeys.Azure, mockAzureBindingDefinition(), updateAzureConfiguration], + [AlmKeys.GitLab, undefined, createGitlabConfiguration], + [AlmKeys.GitLab, mockGitlabBindingDefinition(), updateGitlabConfiguration], + [AlmKeys.GitHub, undefined, createGithubConfiguration], + [AlmKeys.GitHub, mockGithubBindingDefinition(), updateGithubConfiguration], + [AlmKeys.BitbucketServer, undefined, createBitbucketServerConfiguration], + [ + AlmKeys.BitbucketServer, + mockBitbucketServerBindingDefinition(), + updateBitbucketServerConfiguration + ] +])( + 'should call the proper api on submit for %s | %s', + async ( + alm: AlmKeys, + bindingDefinition: AlmBindingDefinition | undefined, + api: (def: AlmBindingDefinition) => any + ) => { + const wrapper = shallowRender({ alm, bindingDefinition }); + + await waitAndUpdate(wrapper); + await wrapper.instance().handleFormSubmit(); + expect(api).toHaveBeenCalled(); + } +); + +it('should call the proper api for BBC', async () => { + const wrapper = shallowRender({ alm: AlmKeys.BitbucketServer, bindingDefinition: undefined }); + + wrapper.instance().handleBitbucketVariantChange(AlmKeys.BitbucketCloud); - wrapper.setState({ formData: mockGithubBindingDefinition() }); - wrapper.instance().handleCancel(); + await waitAndUpdate(wrapper); + await wrapper.instance().handleFormSubmit(); + expect(createBitbucketCloudConfiguration).toHaveBeenCalled(); - expect(wrapper.state().formData).toBe(bindingDefinition); - expect(onCancel).toHaveBeenCalled(); + wrapper.setProps({ bindingDefinition: mockBitbucketCloudBindingDefinition() }); + await wrapper.instance().handleFormSubmit(); + expect(updateBitbucketCloudConfiguration).toHaveBeenCalled(); }); -it('should handle deleting', () => { - const onDelete = jest.fn(); - const bindingDefinition = mockGithubBindingDefinition(); - const wrapper = shallowRender({ - bindingDefinition, - onDelete - }); +it('should store bitbucket variant', async () => { + const wrapper = shallowRender(); + + wrapper + .find(AlmBindingDefinitionFormRenderer) + .props() + .onBitbucketVariantChange(AlmKeys.BitbucketCloud); - wrapper.instance().handleDelete(); - expect(onDelete).toHaveBeenCalledWith(bindingDefinition.key); + await waitAndUpdate(wrapper); + + expect(wrapper.state().bitbucketVariant).toBe(AlmKeys.BitbucketCloud); + expect(wrapper.state().formData).toEqual({ + clientId: '', + clientSecret: '', + key: '', + workspace: '' + }); }); it('should (dis)allow submit by validating its state', () => { @@ -134,29 +162,17 @@ it('should (dis)allow submit by validating its state', () => { wrapper.setState({ formData: mockGithubBindingDefinition(), touched: true }); expect(wrapper.instance().canSubmit()).toBe(true); - - wrapper.setState({ formData: mockGithubBindingDefinition({ url: '' }), touched: true }); - wrapper.setProps({ optionalFields: ['url'] }); - expect(wrapper.instance().canSubmit()).toBe(true); }); -function shallowRender( - props: Partial<AlmBindingDefinitionForm<GithubBindingDefinition>['props']> = {} -) { - return shallow<AlmBindingDefinitionForm<GithubBindingDefinition>>( +function shallowRender(props: Partial<AlmBindingDefinitionForm['props']> = {}) { + return shallow<AlmBindingDefinitionForm>( <AlmBindingDefinitionForm - bindingDefinition={{ - appId: '', - clientId: '', - clientSecret: '', - key: '', - privateKey: '', - url: '' - }} + alm={AlmKeys.GitHub} + bindingDefinition={mockGithubBindingDefinition()} + alreadyHaveInstanceConfigured={false} onCancel={jest.fn()} - onSubmit={jest.fn()} - {...props}> - {() => null} - </AlmBindingDefinitionForm> + afterSubmit={jest.fn()} + {...props} + /> ); } diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmBindingDefinitionFormField-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmBindingDefinitionFormField-test.tsx index 1137b35be28..ceccda088f0 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmBindingDefinitionFormField-test.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmBindingDefinitionFormField-test.tsx @@ -21,7 +21,7 @@ import { shallow } from 'enzyme'; import * as React from 'react'; import { ButtonLink } from 'sonar-ui-common/components/controls/buttons'; import { click } from 'sonar-ui-common/helpers/testUtils'; -import { AlmBindingDefinition } from '../../../../../types/alm-settings'; +import { AlmBindingDefinitionBase } from '../../../../../types/alm-settings'; import { AlmBindingDefinitionFormField, AlmBindingDefinitionFormFieldProps @@ -60,7 +60,7 @@ it('should correctly toggle visibility for secret fields', () => { }); function shallowRender( - props: Partial<AlmBindingDefinitionFormFieldProps<AlmBindingDefinition>> = {} + props: Partial<AlmBindingDefinitionFormFieldProps<AlmBindingDefinitionBase>> = {} ) { return shallow( <AlmBindingDefinitionFormField diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmBindingDefinitionFormModalRenderer-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmBindingDefinitionFormModalRenderer-test.tsx deleted file mode 100644 index 6201ec060dc..00000000000 --- a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmBindingDefinitionFormModalRenderer-test.tsx +++ /dev/null @@ -1,57 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2021 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 { mockEvent } from '../../../../../helpers/testMocks'; -import AlmBindingDefinitionFormModalRenderer, { - AlmBindingDefinitionFormModalProps -} from '../AlmBindingDefinitionFormModalRenderer'; - -it('should render correctly', () => { - expect(shallowRender()).toMatchSnapshot(); - expect(shallowRender({ help: <span>Help me</span> })).toMatchSnapshot('with help'); - expect(shallowRender({ isSecondInstance: true })).toMatchSnapshot('second instance'); -}); - -it('should submit properly', () => { - const onSubmit = jest.fn().mockResolvedValue({}); - const wrapper = shallowRender({ onSubmit }); - - const event: React.SyntheticEvent<HTMLFormElement> = mockEvent({ preventDefault: jest.fn() }); - - wrapper.find('form').simulate('submit', event); - - expect(event.preventDefault).toBeCalled(); - expect(onSubmit).toBeCalled(); -}); - -function shallowRender(props: Partial<AlmBindingDefinitionFormModalProps> = {}) { - return shallow( - <AlmBindingDefinitionFormModalRenderer - action="create" - canSubmit={jest.fn()} - isSecondInstance={false} - onCancel={jest.fn()} - onSubmit={jest.fn()} - {...props}> - {() => null} - </AlmBindingDefinitionFormModalRenderer> - ); -} diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmBindingDefinitionFormRenderer-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmBindingDefinitionFormRenderer-test.tsx new file mode 100644 index 00000000000..a28503cd58c --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmBindingDefinitionFormRenderer-test.tsx @@ -0,0 +1,95 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 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 { ResetButtonLink } from 'sonar-ui-common/components/controls/buttons'; +import { click } from 'sonar-ui-common/helpers/testUtils'; +import { mockGithubBindingDefinition } from '../../../../../helpers/mocks/alm-settings'; +import { mockEvent } from '../../../../../helpers/testMocks'; +import { AlmKeys } from '../../../../../types/alm-settings'; +import AlmBindingDefinitionFormRenderer, { + AlmBindingDefinitionFormProps +} from '../AlmBindingDefinitionFormRenderer'; +import GithubForm from '../GithubForm'; + +it('should render correctly', () => { + expect(shallowRender()).toMatchSnapshot(); + expect(shallowRender({ alreadyHaveInstanceConfigured: true })).toMatchSnapshot('second instance'); + expect(shallowRender({ submitting: true })).toMatchSnapshot('submitting'); + expect(shallowRender({ isUpdate: true })).toMatchSnapshot('editing'); +}); + +it.each([[AlmKeys.Azure], [AlmKeys.GitHub], [AlmKeys.GitLab], [AlmKeys.BitbucketServer]])( + 'should render correctly for %s', + alm => { + expect(shallowRender({ alm })).toMatchSnapshot(); + } +); + +it('should cancel properly', () => { + const onCancel = jest.fn(); + const wrapper = shallowRender({ onCancel }); + + click(wrapper.find(ResetButtonLink)); + expect(onCancel).toHaveBeenCalled(); +}); + +it('should submit properly', () => { + const onSubmit = jest.fn(); + const wrapper = shallowRender({ onSubmit }); + + const event: React.SyntheticEvent<HTMLFormElement> = mockEvent({ preventDefault: jest.fn() }); + + wrapper.find('form').simulate('submit', event); + + expect(event.preventDefault).toBeCalled(); + expect(onSubmit).toHaveBeenCalled(); +}); + +it('should handle field change', () => { + const onFieldChange = jest.fn(); + const wrapper = shallowRender({ onFieldChange }); + + wrapper + .find(GithubForm) + .props() + .onFieldChange('key', 'test'); + + expect(onFieldChange).toHaveBeenCalledWith('key', 'test'); +}); + +function shallowRender(props: Partial<AlmBindingDefinitionFormProps> = {}) { + return shallow( + <AlmBindingDefinitionFormRenderer + alm={AlmKeys.GitHub} + isUpdate={false} + canSubmit={false} + alreadyHaveInstanceConfigured={false} + submitting={false} + formData={mockGithubBindingDefinition()} + onCancel={jest.fn()} + onSubmit={jest.fn()} + onFieldChange={jest.fn()} + bitbucketVariant={AlmKeys.BitbucketServer} + onBitbucketVariantChange={jest.fn()} + {...props} + /> + ); +} diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmIntegration-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmIntegration-test.tsx index c4853b9ed18..110e538ff99 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmIntegration-test.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmIntegration-test.tsx @@ -29,6 +29,7 @@ import { import { mockLocation } from '../../../../../helpers/testMocks'; import { AlmKeys, AlmSettingsBindingStatusType } from '../../../../../types/alm-settings'; import { AlmIntegration } from '../AlmIntegration'; +import AlmIntegrationRenderer from '../AlmIntegrationRenderer'; jest.mock('../../../../../api/alm-settings', () => ({ countBindedProjects: jest.fn().mockResolvedValue(0), @@ -72,13 +73,13 @@ it('should validate existing configurations', async () => { it('should handle alm selection', async () => { const wrapper = shallowRender(); - wrapper.setState({ currentAlm: AlmKeys.Azure }); + wrapper.setState({ currentAlmTab: AlmKeys.Azure }); wrapper.instance().handleSelectAlm(AlmKeys.GitHub); await waitAndUpdate(wrapper); - expect(wrapper.state().currentAlm).toBe(AlmKeys.GitHub); + expect(wrapper.state().currentAlmTab).toBe(AlmKeys.GitHub); }); it('should handle delete', async () => { @@ -86,17 +87,27 @@ it('should handle delete', async () => { (countBindedProjects as jest.Mock).mockResolvedValueOnce(7); const wrapper = shallowRender(); - wrapper.instance().handleDelete(toBeDeleted); + wrapper + .find(AlmIntegrationRenderer) + .props() + .onDelete(toBeDeleted); await waitAndUpdate(wrapper); - expect(wrapper.state().projectCount).toBe(7); expect(wrapper.state().definitionKeyForDeletion).toBe(toBeDeleted); + + wrapper + .find(AlmIntegrationRenderer) + .props() + .onCancelDelete(); + await waitAndUpdate(wrapper); + expect(wrapper.state().projectCount).toBeUndefined(); + expect(wrapper.state().definitionKeyForDeletion).toBeUndefined(); }); it('should delete configuration', async () => { (deleteConfiguration as jest.Mock).mockResolvedValueOnce(undefined); const wrapper = shallowRender(); - wrapper.instance().deleteConfiguration('8345678'); + wrapper.instance().handleConfirmDelete('8345678'); await waitAndUpdate(wrapper); expect(wrapper.state().projectCount).toBeUndefined(); @@ -164,10 +175,10 @@ it('should fetch settings', async () => { it('should detect the current ALM from the query', () => { let wrapper = shallowRender({ location: mockLocation() }); - expect(wrapper.state().currentAlm).toBe(AlmKeys.GitHub); + expect(wrapper.state().currentAlmTab).toBe(AlmKeys.GitHub); wrapper = shallowRender({ location: mockLocation({ query: { alm: AlmKeys.BitbucketCloud } }) }); - expect(wrapper.state().currentAlm).toBe(AlmKeys.BitbucketServer); + expect(wrapper.state().currentAlmTab).toBe(AlmKeys.BitbucketServer); }); function shallowRender(props: Partial<AlmIntegration['props']> = {}) { diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmIntegrationRenderer-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmIntegrationRenderer-test.tsx index 6b33832d728..e6c3a7b7353 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmIntegrationRenderer-test.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmIntegrationRenderer-test.tsx @@ -30,27 +30,26 @@ it('should render correctly', () => { expect(shallowRender({ definitionKeyForDeletion: 'keyToDelete' })).toMatchSnapshot( 'delete modal' ); - expect(shallowRender({ currentAlm: AlmKeys.Azure })).toMatchSnapshot('azure'); - expect(shallowRender({ currentAlm: AlmKeys.BitbucketServer })).toMatchSnapshot('bitbucket'); - expect(shallowRender({ currentAlm: AlmKeys.BitbucketCloud })).toMatchSnapshot('bitbucketcloud'); - expect(shallowRender({ currentAlm: AlmKeys.GitLab })).toMatchSnapshot('gitlab'); + expect(shallowRender({ currentAlmTab: AlmKeys.Azure })).toMatchSnapshot('azure'); + expect(shallowRender({ currentAlmTab: AlmKeys.BitbucketServer })).toMatchSnapshot('bitbucket'); + expect(shallowRender({ currentAlmTab: AlmKeys.GitLab })).toMatchSnapshot('gitlab'); }); function shallowRender(props: Partial<AlmIntegrationRendererProps> = {}) { return shallow( <AlmIntegrationRenderer branchesEnabled={true} - currentAlm={AlmKeys.GitHub} + currentAlmTab={AlmKeys.GitHub} definitions={{ azure: [], bitbucket: [], bitbucketcloud: [], github: [], gitlab: [] }} definitionStatus={{}} loadingAlmDefinitions={false} loadingProjectCount={false} multipleAlmEnabled={false} - onCancel={jest.fn()} - onCheck={jest.fn()} + onCancelDelete={jest.fn()} + onCheckConfiguration={jest.fn()} onConfirmDelete={jest.fn()} onDelete={jest.fn()} - onSelectAlm={jest.fn()} + onSelectAlmTab={jest.fn()} onUpdateDefinitions={jest.fn()} {...props} /> diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmTab-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmTab-test.tsx index dbabfdadf5b..c66da1da1aa 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmTab-test.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmTab-test.tsx @@ -21,15 +21,9 @@ import { shallow } from 'enzyme'; import * as React from 'react'; import { waitAndUpdate } from 'sonar-ui-common/helpers/testUtils'; import { mockAzureBindingDefinition } from '../../../../../helpers/mocks/alm-settings'; -import { AlmKeys, AzureBindingDefinition } from '../../../../../types/alm-settings'; +import { AlmKeys } from '../../../../../types/alm-settings'; import AlmTab from '../AlmTab'; -const DEFAULT_BINDING = { - key: '', - personalAccessToken: '', - url: undefined -}; - it('should render correctly', () => { expect(shallowRender()).toMatchSnapshot(); }); @@ -51,64 +45,47 @@ it('should handle cancel', async () => { it('should handle edit', async () => { const config = mockAzureBindingDefinition(); const wrapper = shallowRender({ definitions: [config] }); + wrapper.instance().handleEdit(config.key); await waitAndUpdate(wrapper); expect(wrapper.state().editedDefinition).toEqual(config); }); -it('should create config', async () => { - const onUpdateDefinitions = jest.fn(); - const createConfiguration = jest.fn(() => Promise.resolve()); - const config = mockAzureBindingDefinition(); - const wrapper = shallowRender({ createConfiguration, onUpdateDefinitions }); +it('should handle create', async () => { + const wrapper = shallowRender(); wrapper.instance().handleCreate(); - expect(wrapper.state().editedDefinition).toBe(DEFAULT_BINDING); - - wrapper.setState({ editedDefinition: config }); - await wrapper.instance().handleSubmit(config, ''); - - expect(createConfiguration).toBeCalledWith(config); - expect(onUpdateDefinitions).toBeCalled(); + await waitAndUpdate(wrapper); expect(wrapper.state().editedDefinition).toBeUndefined(); }); -it('should update config', async () => { +it('should handle afterSubmit', async () => { const onUpdateDefinitions = jest.fn(); - const updateConfiguration = jest.fn(() => Promise.resolve()); - const config = mockAzureBindingDefinition(); - const wrapper = shallowRender({ onUpdateDefinitions, updateConfiguration }); - wrapper.setState({ editedDefinition: config }); + const onCheck = jest.fn(); + const binding = mockAzureBindingDefinition(); - await wrapper.instance().handleSubmit(config, 'originalKey'); + const wrapper = shallowRender({ onUpdateDefinitions, onCheck }); - expect(updateConfiguration).toBeCalledWith({ - newKey: 'key', - ...config, - key: 'originalKey' - }); - expect(onUpdateDefinitions).toBeCalled(); + wrapper.instance().handleAfterSubmit(binding); + await waitAndUpdate(wrapper); expect(wrapper.state().editedDefinition).toBeUndefined(); + expect(onUpdateDefinitions).toHaveBeenCalled(); + expect(onCheck).toHaveBeenCalledWith(binding.key); }); -function shallowRender(props: Partial<AlmTab<AzureBindingDefinition>['props']> = {}) { - return shallow<AlmTab<AzureBindingDefinition>>( +function shallowRender(props: Partial<AlmTab['props']> = {}) { + return shallow<AlmTab>( <AlmTab - alm={AlmKeys.Azure} + almTab={AlmKeys.Azure} branchesEnabled={true} - createConfiguration={jest.fn()} - defaultBinding={DEFAULT_BINDING} definitions={[mockAzureBindingDefinition()]} definitionStatus={{}} - form={jest.fn()} - help={<div />} loadingAlmDefinitions={false} loadingProjectCount={false} multipleAlmEnabled={true} onCheck={jest.fn()} onDelete={jest.fn()} onUpdateDefinitions={jest.fn()} - updateConfiguration={jest.fn()} {...props} /> ); diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmTabRenderer-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmTabRenderer-test.tsx index 8d35c9a6bb8..e1afdbd2aa5 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmTabRenderer-test.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmTabRenderer-test.tsx @@ -24,11 +24,7 @@ import { mockBitbucketCloudBindingDefinition, mockGithubBindingDefinition } from '../../../../../helpers/mocks/alm-settings'; -import { - AlmBindingDefinition, - AlmKeys, - AzureBindingDefinition -} from '../../../../../types/alm-settings'; +import { AlmKeys } from '../../../../../types/alm-settings'; import AlmTabRenderer, { AlmTabRendererProps } from '../AlmTabRenderer'; it('should render correctly for multi-ALM binding', () => { @@ -38,8 +34,7 @@ it('should render correctly for multi-ALM binding', () => { expect(shallowRenderAzure({ loadingProjectCount: true })).toMatchSnapshot( 'loading project count' ); - expect(shallowRenderAzure({ submitting: true })).toMatchSnapshot('submitting'); - expect(shallowRenderAzure()).toMatchSnapshot('loaded'); + expect(shallowRenderAzure({})).toMatchSnapshot('loaded'); expect(shallowRenderAzure({ editedDefinition: mockAzureBindingDefinition() })).toMatchSnapshot( 'editing a definition' ); @@ -80,30 +75,26 @@ it('should render correctly with validation', () => { expect( shallowRender({ - alm: AlmKeys.BitbucketServer, // BitbucketServer will be passed for both Bitbucket variants. + almTab: AlmKeys.BitbucketServer, // BitbucketServer will be passed for both Bitbucket variants. definitions: [mockBitbucketCloudBindingDefinition()] }) ).toMatchSnapshot('pass the correct key for bitbucket cloud'); }); -function shallowRenderAzure(props: Partial<AlmTabRendererProps<AzureBindingDefinition>> = {}) { +function shallowRenderAzure(props: Partial<AlmTabRendererProps>) { return shallowRender({ definitions: [mockAzureBindingDefinition()], ...props }); } -function shallowRender<B extends AlmBindingDefinition>( - props: Partial<AlmTabRendererProps<B>> = {} -) { +function shallowRender(props: Partial<AlmTabRendererProps> = {}) { return shallow( <AlmTabRenderer - alm={AlmKeys.Azure} + almTab={AlmKeys.Azure} branchesEnabled={true} definitions={[]} definitionStatus={{}} - form={jest.fn()} - help={<div />} loadingAlmDefinitions={false} loadingProjectCount={false} multipleAlmEnabled={true} @@ -112,9 +103,7 @@ function shallowRender<B extends AlmBindingDefinition>( onCreate={jest.fn()} onDelete={jest.fn()} onEdit={jest.fn()} - onSubmit={jest.fn()} - submitting={true} - success={false} + afterSubmit={jest.fn()} {...props} /> ); diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AzureTab-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/BitbucketCloudForm-test.tsx index ab62239c83a..01aaf0e10ce 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AzureTab-test.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/BitbucketCloudForm-test.tsx @@ -17,27 +17,22 @@ * 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 { mockAzureBindingDefinition } from '../../../../../helpers/mocks/alm-settings'; -import AzureTab, { AzureTabProps } from '../AzureTab'; +import { mockBitbucketCloudBindingDefinition } from '../../../../../helpers/mocks/alm-settings'; +import BitbucketCloudForm, { BitbucketCloudFormProps } from '../BitbucketCloudForm'; it('should render correctly', () => { - expect(shallowRender()).toMatchSnapshot(); + const wrapper = shallowRender(); + expect(wrapper).toMatchSnapshot(); }); -function shallowRender(props: Partial<AzureTabProps> = {}) { - return shallow( - <AzureTab - branchesEnabled={true} - definitions={[mockAzureBindingDefinition()]} - definitionStatus={{}} - loadingAlmDefinitions={false} - loadingProjectCount={false} - multipleAlmEnabled={true} - onCheck={jest.fn()} - onDelete={jest.fn()} - onUpdateDefinitions={jest.fn()} +function shallowRender(props: Partial<BitbucketCloudFormProps> = {}) { + return shallow<BitbucketCloudFormProps>( + <BitbucketCloudForm + onFieldChange={jest.fn()} + formData={mockBitbucketCloudBindingDefinition()} {...props} /> ); diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/BitbucketForm-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/BitbucketForm-test.tsx index daa8f83f497..21bf6410a6e 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/BitbucketForm-test.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/BitbucketForm-test.tsx @@ -19,41 +19,62 @@ */ import { shallow } from 'enzyme'; import * as React from 'react'; +import RadioToggle from 'sonar-ui-common/components/controls/RadioToggle'; import { - mockBitbucketBindingDefinition, - mockBitbucketCloudBindingDefinition + mockBitbucketCloudBindingDefinition, + mockBitbucketServerBindingDefinition } from '../../../../../helpers/mocks/alm-settings'; import { AlmKeys } from '../../../../../types/alm-settings'; import BitbucketForm, { BitbucketFormProps } from '../BitbucketForm'; it('should render correctly', () => { - expect(shallowRender({ isCreating: true })).toMatchSnapshot('variant select'); - expect(shallowRender()).toMatchSnapshot('bitbucket server, empty'); - expect(shallowRender({ formData: mockBitbucketBindingDefinition() })).toMatchSnapshot( - 'bitbucket server, edit' - ); - expect( - shallowRender({ - formData: { key: '', clientId: '', clientSecret: '', workspace: '' }, - variant: AlmKeys.BitbucketCloud - }) - ).toMatchSnapshot('bitbucket cloud, empty'); - expect( - shallowRender({ - variant: AlmKeys.BitbucketCloud, - formData: mockBitbucketCloudBindingDefinition() - }) - ).toMatchSnapshot('bitbucket cloud, edit'); + let wrapper = shallowRender({ + variant: AlmKeys.BitbucketServer, + formData: mockBitbucketServerBindingDefinition() + }); + expect(wrapper).toMatchSnapshot('bbs'); + + wrapper = shallowRender({ + variant: AlmKeys.BitbucketCloud, + formData: mockBitbucketCloudBindingDefinition() + }); + expect(wrapper).toMatchSnapshot('bbc'); + + wrapper = shallowRender({ + isUpdate: true, + variant: AlmKeys.BitbucketServer, + formData: mockBitbucketServerBindingDefinition() + }); + expect(wrapper).toMatchSnapshot('update bbs'); + + wrapper = shallowRender({ + isUpdate: true, + variant: AlmKeys.BitbucketCloud, + formData: mockBitbucketCloudBindingDefinition() + }); + expect(wrapper).toMatchSnapshot('update bbc'); +}); + +it('should render propagete variant properly', () => { + const onVariantChange = jest.fn(); + const wrapper = shallowRender({ onVariantChange }); + + wrapper + .find(RadioToggle) + .props() + .onCheck(AlmKeys.BitbucketServer); + + expect(onVariantChange).toHaveBeenCalledWith(AlmKeys.BitbucketServer); }); function shallowRender(props: Partial<BitbucketFormProps> = {}) { return shallow( <BitbucketForm - formData={{ key: '', personalAccessToken: '', url: '' }} - isCreating={false} + formData={mockBitbucketServerBindingDefinition()} + isUpdate={false} onFieldChange={jest.fn()} - onSelectVariant={jest.fn()} variant={AlmKeys.BitbucketServer} + onVariantChange={jest.fn()} {...props} /> ); diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/GithubTab-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/BitbucketServerForm-test.tsx index 3785bb78608..1bedcf6ba53 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/GithubTab-test.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/BitbucketServerForm-test.tsx @@ -17,27 +17,22 @@ * 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 { mockGithubBindingDefinition } from '../../../../../helpers/mocks/alm-settings'; -import GithubTab, { GithubTabProps } from '../GithubTab'; +import { mockBitbucketServerBindingDefinition } from '../../../../../helpers/mocks/alm-settings'; +import BitbucketServerForm, { BitbucketServerFormProps } from '../BitbucketServerForm'; it('should render correctly', () => { - expect(shallowRender()).toMatchSnapshot('with branch support'); + const wrapper = shallowRender(); + expect(wrapper).toMatchSnapshot(); }); -function shallowRender(props: Partial<GithubTabProps> = {}) { - return shallow( - <GithubTab - branchesEnabled={true} - definitions={[mockGithubBindingDefinition()]} - definitionStatus={{}} - loadingAlmDefinitions={false} - loadingProjectCount={false} - multipleAlmEnabled={true} - onCheck={jest.fn()} - onDelete={jest.fn()} - onUpdateDefinitions={jest.fn()} +function shallowRender(props: Partial<BitbucketServerFormProps> = {}) { + return shallow<BitbucketServerFormProps>( + <BitbucketServerForm + onFieldChange={jest.fn()} + formData={mockBitbucketServerBindingDefinition()} {...props} /> ); diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/BitbucketTab-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/BitbucketTab-test.tsx deleted file mode 100644 index 5a95099ac5f..00000000000 --- a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/BitbucketTab-test.tsx +++ /dev/null @@ -1,152 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2021 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 { - createBitbucketCloudConfiguration, - createBitbucketConfiguration, - updateBitbucketCloudConfiguration, - updateBitbucketConfiguration -} from '../../../../../api/alm-settings'; -import { - mockBitbucketBindingDefinition, - mockBitbucketCloudBindingDefinition -} from '../../../../../helpers/mocks/alm-settings'; -import { AlmKeys } from '../../../../../types/alm-settings'; -import BitbucketTab, { DEFAULT_CLOUD_BINDING, DEFAULT_SERVER_BINDING } from '../BitbucketTab'; - -jest.mock('../../../../../api/alm-settings', () => ({ - createBitbucketConfiguration: jest.fn().mockResolvedValue(null), - createBitbucketCloudConfiguration: jest.fn().mockResolvedValue(null), - updateBitbucketConfiguration: jest.fn().mockResolvedValue(null), - updateBitbucketCloudConfiguration: jest.fn().mockResolvedValue(null) -})); - -it('should render correctly', () => { - expect(shallowRender()).toMatchSnapshot(); -}); - -it('should handle cancel', async () => { - const wrapper = shallowRender(); - - wrapper.setState({ - editedDefinition: mockBitbucketBindingDefinition() - }); - - wrapper.instance().handleCancel(); - - await waitAndUpdate(wrapper); - - expect(wrapper.state().editedDefinition).toBeUndefined(); -}); - -it('should handle edit', async () => { - const config = mockBitbucketBindingDefinition(); - const wrapper = shallowRender({ definitions: [config] }); - wrapper.instance().handleEdit(config.key); - await waitAndUpdate(wrapper); - expect(wrapper.state().editedDefinition).toEqual(config); -}); - -it('should create config for Bitbucket Server', async () => { - const onUpdateDefinitions = jest.fn(); - const config = mockBitbucketBindingDefinition(); - const wrapper = shallowRender({ onUpdateDefinitions }); - - wrapper.instance().handleCreate(); - wrapper.instance().handleSelectVariant(AlmKeys.BitbucketServer); - expect(wrapper.state().editedDefinition).toBe(DEFAULT_SERVER_BINDING); - - wrapper.setState({ editedDefinition: config }); - await wrapper.instance().handleSubmit(config, ''); - - expect(createBitbucketConfiguration).toBeCalledWith(config); - expect(onUpdateDefinitions).toBeCalled(); - expect(wrapper.state().editedDefinition).toBeUndefined(); -}); - -it('should create config for Bitbucket Cloud', async () => { - const onUpdateDefinitions = jest.fn(); - const config = mockBitbucketCloudBindingDefinition(); - const wrapper = shallowRender({ onUpdateDefinitions }); - - wrapper.instance().handleCreate(); - wrapper.instance().handleSelectVariant(AlmKeys.BitbucketCloud); - expect(wrapper.state().editedDefinition).toBe(DEFAULT_CLOUD_BINDING); - - wrapper.setState({ editedDefinition: config }); - await wrapper.instance().handleSubmit(config, ''); - - expect(createBitbucketCloudConfiguration).toBeCalledWith(config); - expect(onUpdateDefinitions).toBeCalled(); - expect(wrapper.state().editedDefinition).toBeUndefined(); -}); - -it('should update config for Bitbucket Server', async () => { - const onUpdateDefinitions = jest.fn(); - const config = mockBitbucketBindingDefinition(); - const wrapper = shallowRender({ onUpdateDefinitions }); - wrapper.setState({ editedDefinition: config }); - - await wrapper.instance().handleSubmit(config, 'originalKey'); - - expect(updateBitbucketConfiguration).toBeCalledWith({ - newKey: 'key', - ...config, - key: 'originalKey' - }); - expect(onUpdateDefinitions).toBeCalled(); - expect(wrapper.state().editedDefinition).toBeUndefined(); -}); - -it('should update config for Bitbucket Cloud', async () => { - const onUpdateDefinitions = jest.fn(); - const config = mockBitbucketCloudBindingDefinition(); - const wrapper = shallowRender({ onUpdateDefinitions }); - wrapper.setState({ editedDefinition: config }); - - await wrapper.instance().handleSubmit(config, 'originalKey'); - - expect(updateBitbucketCloudConfiguration).toBeCalledWith({ - newKey: 'key', - ...config, - key: 'originalKey' - }); - expect(onUpdateDefinitions).toBeCalled(); - expect(wrapper.state().editedDefinition).toBeUndefined(); -}); - -function shallowRender(props: Partial<BitbucketTab['props']> = {}) { - return shallow<BitbucketTab>( - <BitbucketTab - branchesEnabled={true} - definitions={[mockBitbucketBindingDefinition()]} - definitionStatus={{}} - loadingAlmDefinitions={false} - loadingProjectCount={false} - multipleAlmEnabled={true} - onCheck={jest.fn()} - onDelete={jest.fn()} - onUpdateDefinitions={jest.fn()} - {...props} - /> - ); -} diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/BitbucketTabRenderer-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/BitbucketTabRenderer-test.tsx deleted file mode 100644 index 09ac75ce188..00000000000 --- a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/BitbucketTabRenderer-test.tsx +++ /dev/null @@ -1,63 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2021 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 { mockBitbucketBindingDefinition } from '../../../../../helpers/mocks/alm-settings'; -import { AlmKeys, BitbucketBindingDefinition } from '../../../../../types/alm-settings'; -import AlmTabRenderer, { AlmTabRendererProps } from '../AlmTabRenderer'; -import BitbucketTabRenderer, { BitbucketTabRendererProps } from '../BitbucketTabRenderer'; - -it('should render correctly', () => { - expect(shallowRender()).toMatchSnapshot('default'); - expect(shallowRender({ variant: AlmKeys.BitbucketServer })).toMatchSnapshot('bitbucket server'); - expect(shallowRender({ variant: AlmKeys.BitbucketCloud })).toMatchSnapshot('bitbucket cloud'); - - const almTab = shallowRender().find<AlmTabRendererProps<BitbucketBindingDefinition>>( - AlmTabRenderer - ); - expect( - almTab.props().form({ formData: mockBitbucketBindingDefinition(), onFieldChange: jest.fn() }) - ).toMatchSnapshot('bitbucket form'); -}); - -function shallowRender(props: Partial<BitbucketTabRendererProps> = {}) { - return shallow<BitbucketTabRendererProps>( - <BitbucketTabRenderer - branchesEnabled={true} - definitions={[]} - definitionStatus={{}} - isCreating={false} - loadingAlmDefinitions={false} - loadingProjectCount={false} - multipleAlmEnabled={true} - onCancel={jest.fn()} - onCheck={jest.fn()} - onCreate={jest.fn()} - onDelete={jest.fn()} - onEdit={jest.fn()} - onSelectVariant={jest.fn()} - onSubmit={jest.fn()} - submitting={true} - success={false} - variant={undefined} - {...props} - /> - ); -} diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/GitlabTab-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/GitlabTab-test.tsx deleted file mode 100644 index a3a9b6e80a3..00000000000 --- a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/GitlabTab-test.tsx +++ /dev/null @@ -1,54 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2021 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 { mockGitlabBindingDefinition } from '../../../../../helpers/mocks/alm-settings'; -import GitlabTab, { GitlabTabProps } from '../GitlabTab'; - -it('should render correctly', () => { - expect(shallowRender()).toMatchSnapshot('with branch support'); - expect( - shallowRender({ - definitions: [mockGitlabBindingDefinition({ url: 'https://gitlab.com/api/v4' })] - }) - ).toMatchSnapshot('with URL'); - expect( - shallowRender({ - definitions: [] - }) - ).toMatchSnapshot('with no definitions'); -}); - -function shallowRender(props: Partial<GitlabTabProps> = {}) { - return shallow( - <GitlabTab - branchesEnabled={true} - definitions={[mockGitlabBindingDefinition()]} - definitionStatus={{}} - loadingAlmDefinitions={false} - loadingProjectCount={false} - multipleAlmEnabled={true} - onCheck={jest.fn()} - onDelete={jest.fn()} - onUpdateDefinitions={jest.fn()} - {...props} - /> - ); -} diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AlmBindingDefinitionForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AlmBindingDefinitionForm-test.tsx.snap index 525258ed31d..edd60829d06 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AlmBindingDefinitionForm-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AlmBindingDefinitionForm-test.tsx.snap @@ -1,21 +1,49 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`should render correctly: create 1`] = ` -<AlmBindingDefinitionFormModalRenderer - action="create" - canSubmit={[Function]} - isSecondInstance={false} +<AlmBindingDefinitionFormRenderer + alm="github" + alreadyHaveInstanceConfigured={false} + canSubmit={false} + formData={ + Object { + "appId": "", + "clientId": "", + "clientSecret": "", + "key": "", + "privateKey": "", + "url": "", + } + } + isUpdate={false} + onBitbucketVariantChange={[Function]} onCancel={[Function]} + onFieldChange={[Function]} onSubmit={[Function]} + submitting={false} /> `; exports[`should render correctly: edit 1`] = ` -<AlmBindingDefinitionFormModalRenderer - action="edit" - canSubmit={[Function]} - isSecondInstance={false} +<AlmBindingDefinitionFormRenderer + alm="github" + alreadyHaveInstanceConfigured={false} + canSubmit={false} + formData={ + Object { + "appId": "123456", + "clientId": "client1", + "clientSecret": "**clientsecret**", + "key": "key", + "privateKey": "asdf1234", + "url": "http://github.enterprise.com", + } + } + isUpdate={true} + onBitbucketVariantChange={[Function]} onCancel={[Function]} + onFieldChange={[Function]} onSubmit={[Function]} + submitting={false} /> `; diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AlmBindingDefinitionFormModalRenderer-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AlmBindingDefinitionFormModalRenderer-test.tsx.snap deleted file mode 100644 index a5410979b1b..00000000000 --- a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AlmBindingDefinitionFormModalRenderer-test.tsx.snap +++ /dev/null @@ -1,168 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render correctly 1`] = ` -<Modal - contentLabel="settings.almintegration.form.header.create" - onRequestClose={[MockFunction]} - shouldCloseOnOverlayClick={false} - size="medium" -> - <form - className="views-form" - onSubmit={[Function]} - > - <div - className="modal-head" - > - <h2> - settings.almintegration.form.header.create - </h2> - </div> - <div - className="modal-body modal-container" - > - <div - className="display-flex-start" - > - <div - className="flex-1" - > - <Component /> - </div> - <div - className="flex-1" - /> - </div> - </div> - <div - className="modal-foot" - > - <SubmitButton - disabled={true} - > - settings.almintegration.form.save - </SubmitButton> - <ResetButtonLink - onClick={[MockFunction]} - > - cancel - </ResetButtonLink> - </div> - </form> -</Modal> -`; - -exports[`should render correctly: second instance 1`] = ` -<Modal - contentLabel="settings.almintegration.form.header.create" - onRequestClose={[MockFunction]} - shouldCloseOnOverlayClick={false} - size="medium" -> - <form - className="views-form" - onSubmit={[Function]} - > - <div - className="modal-head" - > - <h2> - settings.almintegration.form.header.create - </h2> - </div> - <div - className="modal-body modal-container" - > - <Alert - className="big-spacer-bottom" - variant="warning" - > - settings.almintegration.form.second_instance_warning - </Alert> - <div - className="display-flex-start" - > - <div - className="flex-1" - > - <Component /> - </div> - <div - className="flex-1" - /> - </div> - </div> - <div - className="modal-foot" - > - <SubmitButton - disabled={true} - > - settings.almintegration.form.save - </SubmitButton> - <ResetButtonLink - onClick={[MockFunction]} - > - cancel - </ResetButtonLink> - </div> - </form> -</Modal> -`; - -exports[`should render correctly: with help 1`] = ` -<Modal - contentLabel="settings.almintegration.form.header.create" - onRequestClose={[MockFunction]} - shouldCloseOnOverlayClick={false} - size="medium" -> - <form - className="views-form" - onSubmit={[Function]} - > - <div - className="modal-head" - > - <h2> - settings.almintegration.form.header.create - </h2> - </div> - <div - className="modal-body modal-container" - > - <div - className="display-flex-start" - > - <div - className="flex-1" - > - <Component /> - </div> - <Alert - className="huge-spacer-left flex-1" - variant="info" - > - <span> - Help me - </span> - </Alert> - </div> - </div> - <div - className="modal-foot" - > - <SubmitButton - disabled={true} - > - settings.almintegration.form.save - </SubmitButton> - <ResetButtonLink - onClick={[MockFunction]} - > - cancel - </ResetButtonLink> - </div> - </form> -</Modal> -`; diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AlmBindingDefinitionFormRenderer-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AlmBindingDefinitionFormRenderer-test.tsx.snap new file mode 100644 index 00000000000..ef718f37281 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AlmBindingDefinitionFormRenderer-test.tsx.snap @@ -0,0 +1,466 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly 1`] = ` +<Modal + contentLabel="settings.almintegration.form.header.create" + onRequestClose={[MockFunction]} + shouldCloseOnOverlayClick={false} + size="medium" +> + <form + className="views-form" + onSubmit={[Function]} + > + <div + className="modal-head" + > + <h2> + settings.almintegration.form.header.create + </h2> + </div> + <div + className="modal-body modal-container" + > + <GithubForm + formData={ + Object { + "appId": "123456", + "clientId": "client1", + "clientSecret": "**clientsecret**", + "key": "key", + "privateKey": "asdf1234", + "url": "http://github.enterprise.com", + } + } + onFieldChange={[MockFunction]} + /> + </div> + <div + className="modal-foot" + > + <SubmitButton + disabled={true} + > + settings.almintegration.form.save + <DeferredSpinner + className="spacer-left" + loading={false} + /> + </SubmitButton> + <ResetButtonLink + onClick={[MockFunction]} + > + cancel + </ResetButtonLink> + </div> + </form> +</Modal> +`; + +exports[`should render correctly for azure 1`] = ` +<Modal + contentLabel="settings.almintegration.form.header.create" + onRequestClose={[MockFunction]} + shouldCloseOnOverlayClick={false} + size="medium" +> + <form + className="views-form" + onSubmit={[Function]} + > + <div + className="modal-head" + > + <h2> + settings.almintegration.form.header.create + </h2> + </div> + <div + className="modal-body modal-container" + > + <AzureForm + formData={ + Object { + "appId": "123456", + "clientId": "client1", + "clientSecret": "**clientsecret**", + "key": "key", + "privateKey": "asdf1234", + "url": "http://github.enterprise.com", + } + } + onFieldChange={[MockFunction]} + /> + </div> + <div + className="modal-foot" + > + <SubmitButton + disabled={true} + > + settings.almintegration.form.save + <DeferredSpinner + className="spacer-left" + loading={false} + /> + </SubmitButton> + <ResetButtonLink + onClick={[MockFunction]} + > + cancel + </ResetButtonLink> + </div> + </form> +</Modal> +`; + +exports[`should render correctly for bitbucket 1`] = ` +<Modal + contentLabel="settings.almintegration.form.header.create" + onRequestClose={[MockFunction]} + shouldCloseOnOverlayClick={false} + size="medium" +> + <form + className="views-form" + onSubmit={[Function]} + > + <div + className="modal-head" + > + <h2> + settings.almintegration.form.header.create + </h2> + </div> + <div + className="modal-body modal-container" + > + <BitbucketForm + formData={ + Object { + "appId": "123456", + "clientId": "client1", + "clientSecret": "**clientsecret**", + "key": "key", + "privateKey": "asdf1234", + "url": "http://github.enterprise.com", + } + } + isUpdate={false} + onFieldChange={[MockFunction]} + onVariantChange={[MockFunction]} + variant="bitbucket" + /> + </div> + <div + className="modal-foot" + > + <SubmitButton + disabled={true} + > + settings.almintegration.form.save + <DeferredSpinner + className="spacer-left" + loading={false} + /> + </SubmitButton> + <ResetButtonLink + onClick={[MockFunction]} + > + cancel + </ResetButtonLink> + </div> + </form> +</Modal> +`; + +exports[`should render correctly for github 1`] = ` +<Modal + contentLabel="settings.almintegration.form.header.create" + onRequestClose={[MockFunction]} + shouldCloseOnOverlayClick={false} + size="medium" +> + <form + className="views-form" + onSubmit={[Function]} + > + <div + className="modal-head" + > + <h2> + settings.almintegration.form.header.create + </h2> + </div> + <div + className="modal-body modal-container" + > + <GithubForm + formData={ + Object { + "appId": "123456", + "clientId": "client1", + "clientSecret": "**clientsecret**", + "key": "key", + "privateKey": "asdf1234", + "url": "http://github.enterprise.com", + } + } + onFieldChange={[MockFunction]} + /> + </div> + <div + className="modal-foot" + > + <SubmitButton + disabled={true} + > + settings.almintegration.form.save + <DeferredSpinner + className="spacer-left" + loading={false} + /> + </SubmitButton> + <ResetButtonLink + onClick={[MockFunction]} + > + cancel + </ResetButtonLink> + </div> + </form> +</Modal> +`; + +exports[`should render correctly for gitlab 1`] = ` +<Modal + contentLabel="settings.almintegration.form.header.create" + onRequestClose={[MockFunction]} + shouldCloseOnOverlayClick={false} + size="medium" +> + <form + className="views-form" + onSubmit={[Function]} + > + <div + className="modal-head" + > + <h2> + settings.almintegration.form.header.create + </h2> + </div> + <div + className="modal-body modal-container" + > + <GitlabForm + formData={ + Object { + "appId": "123456", + "clientId": "client1", + "clientSecret": "**clientsecret**", + "key": "key", + "privateKey": "asdf1234", + "url": "http://github.enterprise.com", + } + } + onFieldChange={[MockFunction]} + /> + </div> + <div + className="modal-foot" + > + <SubmitButton + disabled={true} + > + settings.almintegration.form.save + <DeferredSpinner + className="spacer-left" + loading={false} + /> + </SubmitButton> + <ResetButtonLink + onClick={[MockFunction]} + > + cancel + </ResetButtonLink> + </div> + </form> +</Modal> +`; + +exports[`should render correctly: editing 1`] = ` +<Modal + contentLabel="settings.almintegration.form.header.edit" + onRequestClose={[MockFunction]} + shouldCloseOnOverlayClick={false} + size="medium" +> + <form + className="views-form" + onSubmit={[Function]} + > + <div + className="modal-head" + > + <h2> + settings.almintegration.form.header.edit + </h2> + </div> + <div + className="modal-body modal-container" + > + <GithubForm + formData={ + Object { + "appId": "123456", + "clientId": "client1", + "clientSecret": "**clientsecret**", + "key": "key", + "privateKey": "asdf1234", + "url": "http://github.enterprise.com", + } + } + onFieldChange={[MockFunction]} + /> + </div> + <div + className="modal-foot" + > + <SubmitButton + disabled={true} + > + settings.almintegration.form.save + <DeferredSpinner + className="spacer-left" + loading={false} + /> + </SubmitButton> + <ResetButtonLink + onClick={[MockFunction]} + > + cancel + </ResetButtonLink> + </div> + </form> +</Modal> +`; + +exports[`should render correctly: second instance 1`] = ` +<Modal + contentLabel="settings.almintegration.form.header.create" + onRequestClose={[MockFunction]} + shouldCloseOnOverlayClick={false} + size="medium" +> + <form + className="views-form" + onSubmit={[Function]} + > + <div + className="modal-head" + > + <h2> + settings.almintegration.form.header.create + </h2> + </div> + <div + className="modal-body modal-container" + > + <Alert + className="big-spacer-bottom" + variant="warning" + > + settings.almintegration.form.second_instance_warning + </Alert> + <GithubForm + formData={ + Object { + "appId": "123456", + "clientId": "client1", + "clientSecret": "**clientsecret**", + "key": "key", + "privateKey": "asdf1234", + "url": "http://github.enterprise.com", + } + } + onFieldChange={[MockFunction]} + /> + </div> + <div + className="modal-foot" + > + <SubmitButton + disabled={true} + > + settings.almintegration.form.save + <DeferredSpinner + className="spacer-left" + loading={false} + /> + </SubmitButton> + <ResetButtonLink + onClick={[MockFunction]} + > + cancel + </ResetButtonLink> + </div> + </form> +</Modal> +`; + +exports[`should render correctly: submitting 1`] = ` +<Modal + contentLabel="settings.almintegration.form.header.create" + onRequestClose={[MockFunction]} + shouldCloseOnOverlayClick={false} + size="medium" +> + <form + className="views-form" + onSubmit={[Function]} + > + <div + className="modal-head" + > + <h2> + settings.almintegration.form.header.create + </h2> + </div> + <div + className="modal-body modal-container" + > + <GithubForm + formData={ + Object { + "appId": "123456", + "clientId": "client1", + "clientSecret": "**clientsecret**", + "key": "key", + "privateKey": "asdf1234", + "url": "http://github.enterprise.com", + } + } + onFieldChange={[MockFunction]} + /> + </div> + <div + className="modal-foot" + > + <SubmitButton + disabled={true} + > + settings.almintegration.form.save + <DeferredSpinner + className="spacer-left" + loading={true} + /> + </SubmitButton> + <ResetButtonLink + onClick={[MockFunction]} + > + cancel + </ResetButtonLink> + </div> + </form> +</Modal> +`; diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AlmIntegration-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AlmIntegration-test.tsx.snap index 2494c7dca20..193dfaf0360 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AlmIntegration-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AlmIntegration-test.tsx.snap @@ -3,7 +3,7 @@ exports[`should render correctly 1`] = ` <AlmIntegrationRenderer branchesEnabled={true} - currentAlm="github" + currentAlmTab="github" definitionStatus={Object {}} definitions={ Object { @@ -17,11 +17,11 @@ exports[`should render correctly 1`] = ` loadingAlmDefinitions={true} loadingProjectCount={false} multipleAlmEnabled={false} - onCancel={[Function]} - onCheck={[Function]} + onCancelDelete={[Function]} + onCheckConfiguration={[Function]} onConfirmDelete={[Function]} onDelete={[Function]} - onSelectAlm={[Function]} + onSelectAlmTab={[Function]} onUpdateDefinitions={[Function]} /> `; diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AlmIntegrationRenderer-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AlmIntegrationRenderer-test.tsx.snap index 14df1c80a7a..dbbe5ea825c 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AlmIntegrationRenderer-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AlmIntegrationRenderer-test.tsx.snap @@ -32,7 +32,6 @@ exports[`should render correctly: azure 1`] = ` /> GitHub </React.Fragment>, - "requiresBranchesEnabled": false, }, Object { "key": "bitbucket", @@ -45,7 +44,6 @@ exports[`should render correctly: azure 1`] = ` /> Bitbucket </React.Fragment>, - "requiresBranchesEnabled": false, }, Object { "key": "azure", @@ -58,7 +56,6 @@ exports[`should render correctly: azure 1`] = ` /> Azure DevOps </React.Fragment>, - "requiresBranchesEnabled": false, }, Object { "key": "gitlab", @@ -71,12 +68,12 @@ exports[`should render correctly: azure 1`] = ` /> GitLab </React.Fragment>, - "requiresBranchesEnabled": false, }, ] } /> - <AzureTab + <AlmTab + almTab="azure" branchesEnabled={true} definitionStatus={Object {}} definitions={Array []} @@ -122,7 +119,6 @@ exports[`should render correctly: bitbucket 1`] = ` /> GitHub </React.Fragment>, - "requiresBranchesEnabled": false, }, Object { "key": "bitbucket", @@ -135,7 +131,6 @@ exports[`should render correctly: bitbucket 1`] = ` /> Bitbucket </React.Fragment>, - "requiresBranchesEnabled": false, }, Object { "key": "azure", @@ -148,7 +143,6 @@ exports[`should render correctly: bitbucket 1`] = ` /> Azure DevOps </React.Fragment>, - "requiresBranchesEnabled": false, }, Object { "key": "gitlab", @@ -161,12 +155,12 @@ exports[`should render correctly: bitbucket 1`] = ` /> GitLab </React.Fragment>, - "requiresBranchesEnabled": false, }, ] } /> - <BitbucketTab + <AlmTab + almTab="bitbucket" branchesEnabled={true} definitionStatus={Object {}} definitions={Array []} @@ -180,85 +174,6 @@ exports[`should render correctly: bitbucket 1`] = ` </Fragment> `; -exports[`should render correctly: bitbucketcloud 1`] = ` -<Fragment> - <header - className="page-header" - > - <h1 - className="page-title" - > - settings.almintegration.title - </h1> - </header> - <div - className="markdown small spacer-top big-spacer-bottom" - > - settings.almintegration.description - </div> - <BoxedTabs - onSelect={[MockFunction]} - selected="bitbucketcloud" - tabs={ - Array [ - Object { - "key": "github", - "label": <React.Fragment> - <img - alt="github" - className="spacer-right" - height={16} - src="/images/alm/github.svg" - /> - GitHub - </React.Fragment>, - "requiresBranchesEnabled": false, - }, - Object { - "key": "bitbucket", - "label": <React.Fragment> - <img - alt="bitbucket" - className="spacer-right" - height={16} - src="/images/alm/bitbucket.svg" - /> - Bitbucket - </React.Fragment>, - "requiresBranchesEnabled": false, - }, - Object { - "key": "azure", - "label": <React.Fragment> - <img - alt="azure" - className="spacer-right" - height={16} - src="/images/alm/azure.svg" - /> - Azure DevOps - </React.Fragment>, - "requiresBranchesEnabled": false, - }, - Object { - "key": "gitlab", - "label": <React.Fragment> - <img - alt="gitlab" - className="spacer-right" - height={16} - src="/images/alm/gitlab.svg" - /> - GitLab - </React.Fragment>, - "requiresBranchesEnabled": false, - }, - ] - } - /> -</Fragment> -`; - exports[`should render correctly: default 1`] = ` <Fragment> <header @@ -291,7 +206,6 @@ exports[`should render correctly: default 1`] = ` /> GitHub </React.Fragment>, - "requiresBranchesEnabled": false, }, Object { "key": "bitbucket", @@ -304,7 +218,6 @@ exports[`should render correctly: default 1`] = ` /> Bitbucket </React.Fragment>, - "requiresBranchesEnabled": false, }, Object { "key": "azure", @@ -317,7 +230,6 @@ exports[`should render correctly: default 1`] = ` /> Azure DevOps </React.Fragment>, - "requiresBranchesEnabled": false, }, Object { "key": "gitlab", @@ -330,12 +242,12 @@ exports[`should render correctly: default 1`] = ` /> GitLab </React.Fragment>, - "requiresBranchesEnabled": false, }, ] } /> - <GithubTab + <AlmTab + almTab="github" branchesEnabled={true} definitionStatus={Object {}} definitions={Array []} @@ -381,7 +293,6 @@ exports[`should render correctly: delete modal 1`] = ` /> GitHub </React.Fragment>, - "requiresBranchesEnabled": false, }, Object { "key": "bitbucket", @@ -394,7 +305,6 @@ exports[`should render correctly: delete modal 1`] = ` /> Bitbucket </React.Fragment>, - "requiresBranchesEnabled": false, }, Object { "key": "azure", @@ -407,7 +317,6 @@ exports[`should render correctly: delete modal 1`] = ` /> Azure DevOps </React.Fragment>, - "requiresBranchesEnabled": false, }, Object { "key": "gitlab", @@ -420,12 +329,12 @@ exports[`should render correctly: delete modal 1`] = ` /> GitLab </React.Fragment>, - "requiresBranchesEnabled": false, }, ] } /> - <GithubTab + <AlmTab + almTab="github" branchesEnabled={true} definitionStatus={Object {}} definitions={Array []} @@ -476,7 +385,6 @@ exports[`should render correctly: gitlab 1`] = ` /> GitHub </React.Fragment>, - "requiresBranchesEnabled": false, }, Object { "key": "bitbucket", @@ -489,7 +397,6 @@ exports[`should render correctly: gitlab 1`] = ` /> Bitbucket </React.Fragment>, - "requiresBranchesEnabled": false, }, Object { "key": "azure", @@ -502,7 +409,6 @@ exports[`should render correctly: gitlab 1`] = ` /> Azure DevOps </React.Fragment>, - "requiresBranchesEnabled": false, }, Object { "key": "gitlab", @@ -515,12 +421,12 @@ exports[`should render correctly: gitlab 1`] = ` /> GitLab </React.Fragment>, - "requiresBranchesEnabled": false, }, ] } /> - <GitlabTab + <AlmTab + almTab="gitlab" branchesEnabled={true} definitionStatus={Object {}} definitions={Array []} @@ -566,7 +472,6 @@ exports[`should render correctly: loading 1`] = ` /> GitHub </React.Fragment>, - "requiresBranchesEnabled": false, }, Object { "key": "bitbucket", @@ -579,7 +484,6 @@ exports[`should render correctly: loading 1`] = ` /> Bitbucket </React.Fragment>, - "requiresBranchesEnabled": false, }, Object { "key": "azure", @@ -592,7 +496,6 @@ exports[`should render correctly: loading 1`] = ` /> Azure DevOps </React.Fragment>, - "requiresBranchesEnabled": false, }, Object { "key": "gitlab", @@ -605,12 +508,12 @@ exports[`should render correctly: loading 1`] = ` /> GitLab </React.Fragment>, - "requiresBranchesEnabled": false, }, ] } /> - <GithubTab + <AlmTab + almTab="github" branchesEnabled={true} definitionStatus={Object {}} definitions={Array []} diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AlmTab-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AlmTab-test.tsx.snap index 9822401deaf..ae9342d08d0 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AlmTab-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AlmTab-test.tsx.snap @@ -2,7 +2,8 @@ exports[`should render correctly 1`] = ` <AlmTabRenderer - alm="azure" + afterSubmit={[Function]} + almTab="azure" branchesEnabled={true} definitionStatus={Object {}} definitions={ @@ -13,8 +14,6 @@ exports[`should render correctly 1`] = ` }, ] } - form={[MockFunction]} - help={<div />} loadingAlmDefinitions={false} loadingProjectCount={false} multipleAlmEnabled={true} @@ -23,8 +22,5 @@ exports[`should render correctly 1`] = ` onCreate={[Function]} onDelete={[MockFunction]} onEdit={[Function]} - onSubmit={[Function]} - submitting={false} - success={false} /> `; diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AlmTabRenderer-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AlmTabRenderer-test.tsx.snap index f0ff2da8675..a6d97e1ac03 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AlmTabRenderer-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AlmTabRenderer-test.tsx.snap @@ -2,586 +2,674 @@ exports[`should render correctly for multi-ALM binding: editing a definition 1`] = ` <div - className="big-padded" + className="bordered" > - <DeferredSpinner - loading={false} + <div + className="big-padded" > - <div - className="spacer-bottom text-right" + <DeferredSpinner + loading={false} > - <Connect(withAppState(CreationTooltip)) - alm="azure" - preventCreation={false} + <div + className="spacer-bottom text-right" > - <Button - data-test="settings__alm-create" - disabled={false} - onClick={[MockFunction]} + <Connect(withAppState(CreationTooltip)) + alm="azure" + preventCreation={false} > - settings.almintegration.create - </Button> - </Connect(withAppState(CreationTooltip))> - </div> - <AlmBindingDefinitionBox - alm="azure" - branchesEnabled={true} - definition={ - Object { - "key": "key", - "personalAccessToken": "asdf1234", + <Button + data-test="settings__alm-create" + disabled={false} + onClick={[MockFunction]} + > + settings.almintegration.create + </Button> + </Connect(withAppState(CreationTooltip))> + </div> + <AlmBindingDefinitionBox + alm="azure" + branchesEnabled={true} + definition={ + Object { + "key": "key", + "personalAccessToken": "asdf1234", + } } - } - key="key" - multipleDefinitions={false} - onCheck={[MockFunction]} - onDelete={[MockFunction]} - onEdit={[MockFunction]} + key="key" + multipleDefinitions={false} + onCheck={[MockFunction]} + onDelete={[MockFunction]} + onEdit={[MockFunction]} + /> + </DeferredSpinner> + </div> + <div + className="huge-spacer-top huge-spacer-bottom bordered-top" + /> + <div + className="big-padded" + > + <Connect(SubCategoryDefinitionsList) + category="almintegration" + subCategory="azure" /> - <AlmBindingDefinitionForm - bindingDefinition={ - Object { - "key": "key", - "personalAccessToken": "asdf1234", - } - } - help={<div />} - isSecondInstance={true} - onCancel={[MockFunction]} - onSubmit={[MockFunction]} - > - <Component /> - </AlmBindingDefinitionForm> - </DeferredSpinner> + </div> </div> `; exports[`should render correctly for multi-ALM binding: loaded 1`] = ` <div - className="big-padded" + className="bordered" > - <DeferredSpinner - loading={false} + <div + className="big-padded" > - <div - className="spacer-bottom text-right" + <DeferredSpinner + loading={false} > - <Connect(withAppState(CreationTooltip)) - alm="azure" - preventCreation={false} + <div + className="spacer-bottom text-right" > - <Button - data-test="settings__alm-create" - disabled={false} - onClick={[MockFunction]} + <Connect(withAppState(CreationTooltip)) + alm="azure" + preventCreation={false} > - settings.almintegration.create - </Button> - </Connect(withAppState(CreationTooltip))> - </div> - <AlmBindingDefinitionBox - alm="azure" - branchesEnabled={true} - definition={ - Object { - "key": "key", - "personalAccessToken": "asdf1234", + <Button + data-test="settings__alm-create" + disabled={false} + onClick={[MockFunction]} + > + settings.almintegration.create + </Button> + </Connect(withAppState(CreationTooltip))> + </div> + <AlmBindingDefinitionBox + alm="azure" + branchesEnabled={true} + definition={ + Object { + "key": "key", + "personalAccessToken": "asdf1234", + } } - } - key="key" - multipleDefinitions={false} - onCheck={[MockFunction]} - onDelete={[MockFunction]} - onEdit={[MockFunction]} + key="key" + multipleDefinitions={false} + onCheck={[MockFunction]} + onDelete={[MockFunction]} + onEdit={[MockFunction]} + /> + </DeferredSpinner> + </div> + <div + className="huge-spacer-top huge-spacer-bottom bordered-top" + /> + <div + className="big-padded" + > + <Connect(SubCategoryDefinitionsList) + category="almintegration" + subCategory="azure" /> - </DeferredSpinner> + </div> </div> `; exports[`should render correctly for multi-ALM binding: loading ALM definitions 1`] = ` <div - className="big-padded" + className="bordered" > - <DeferredSpinner - loading={true} + <div + className="big-padded" > - <div - className="spacer-bottom text-right" + <DeferredSpinner + loading={true} > - <Connect(withAppState(CreationTooltip)) - alm="azure" - preventCreation={false} + <div + className="spacer-bottom text-right" > - <Button - data-test="settings__alm-create" - disabled={false} - onClick={[MockFunction]} + <Connect(withAppState(CreationTooltip)) + alm="azure" + preventCreation={false} > - settings.almintegration.create - </Button> - </Connect(withAppState(CreationTooltip))> - </div> - <AlmBindingDefinitionBox - alm="azure" - branchesEnabled={true} - definition={ - Object { - "key": "key", - "personalAccessToken": "asdf1234", + <Button + data-test="settings__alm-create" + disabled={false} + onClick={[MockFunction]} + > + settings.almintegration.create + </Button> + </Connect(withAppState(CreationTooltip))> + </div> + <AlmBindingDefinitionBox + alm="azure" + branchesEnabled={true} + definition={ + Object { + "key": "key", + "personalAccessToken": "asdf1234", + } } - } - key="key" - multipleDefinitions={false} - onCheck={[MockFunction]} - onDelete={[MockFunction]} - onEdit={[MockFunction]} + key="key" + multipleDefinitions={false} + onCheck={[MockFunction]} + onDelete={[MockFunction]} + onEdit={[MockFunction]} + /> + </DeferredSpinner> + </div> + <div + className="huge-spacer-top huge-spacer-bottom bordered-top" + /> + <div + className="big-padded" + > + <Connect(SubCategoryDefinitionsList) + category="almintegration" + subCategory="azure" /> - </DeferredSpinner> + </div> </div> `; exports[`should render correctly for multi-ALM binding: loading project count 1`] = ` <div - className="big-padded" + className="bordered" > - <DeferredSpinner - loading={false} + <div + className="big-padded" > - <div - className="spacer-bottom text-right" + <DeferredSpinner + loading={false} > - <Connect(withAppState(CreationTooltip)) - alm="azure" - preventCreation={true} + <div + className="spacer-bottom text-right" > - <Button - data-test="settings__alm-create" - disabled={true} - onClick={[MockFunction]} + <Connect(withAppState(CreationTooltip)) + alm="azure" + preventCreation={true} > - settings.almintegration.create - </Button> - </Connect(withAppState(CreationTooltip))> - </div> - <AlmBindingDefinitionBox - alm="azure" - branchesEnabled={true} - definition={ - Object { - "key": "key", - "personalAccessToken": "asdf1234", - } - } - key="key" - multipleDefinitions={false} - onCheck={[MockFunction]} - onDelete={[MockFunction]} - onEdit={[MockFunction]} - /> - </DeferredSpinner> -</div> -`; - -exports[`should render correctly for multi-ALM binding: submitting 1`] = ` -<div - className="big-padded" -> - <DeferredSpinner - loading={false} - > - <div - className="spacer-bottom text-right" - > - <Connect(withAppState(CreationTooltip)) + <Button + data-test="settings__alm-create" + disabled={true} + onClick={[MockFunction]} + > + settings.almintegration.create + </Button> + </Connect(withAppState(CreationTooltip))> + </div> + <AlmBindingDefinitionBox alm="azure" - preventCreation={false} - > - <Button - data-test="settings__alm-create" - disabled={false} - onClick={[MockFunction]} - > - settings.almintegration.create - </Button> - </Connect(withAppState(CreationTooltip))> - </div> - <AlmBindingDefinitionBox - alm="azure" - branchesEnabled={true} - definition={ - Object { - "key": "key", - "personalAccessToken": "asdf1234", + branchesEnabled={true} + definition={ + Object { + "key": "key", + "personalAccessToken": "asdf1234", + } } - } - key="key" - multipleDefinitions={false} - onCheck={[MockFunction]} - onDelete={[MockFunction]} - onEdit={[MockFunction]} + key="key" + multipleDefinitions={false} + onCheck={[MockFunction]} + onDelete={[MockFunction]} + onEdit={[MockFunction]} + /> + </DeferredSpinner> + </div> + <div + className="huge-spacer-top huge-spacer-bottom bordered-top" + /> + <div + className="big-padded" + > + <Connect(SubCategoryDefinitionsList) + category="almintegration" + subCategory="azure" /> - </DeferredSpinner> + </div> </div> `; exports[`should render correctly for single-ALM binding 1`] = ` <div - className="big-padded" + className="bordered" > - <DeferredSpinner - loading={true} + <div + className="big-padded" > - <div - className="spacer-bottom text-right" + <DeferredSpinner + loading={true} > - <Connect(withAppState(CreationTooltip)) - alm="azure" - preventCreation={true} + <div + className="spacer-bottom text-right" > - <Button - data-test="settings__alm-create" - disabled={true} - onClick={[MockFunction]} + <Connect(withAppState(CreationTooltip)) + alm="azure" + preventCreation={true} > - settings.almintegration.create - </Button> - </Connect(withAppState(CreationTooltip))> - </div> - <AlmBindingDefinitionBox - alm="azure" - branchesEnabled={true} - definition={ - Object { - "key": "key", - "personalAccessToken": "asdf1234", + <Button + data-test="settings__alm-create" + disabled={true} + onClick={[MockFunction]} + > + settings.almintegration.create + </Button> + </Connect(withAppState(CreationTooltip))> + </div> + <AlmBindingDefinitionBox + alm="azure" + branchesEnabled={true} + definition={ + Object { + "key": "key", + "personalAccessToken": "asdf1234", + } } - } - key="key" - multipleDefinitions={false} - onCheck={[MockFunction]} - onDelete={[MockFunction]} - onEdit={[MockFunction]} + key="key" + multipleDefinitions={false} + onCheck={[MockFunction]} + onDelete={[MockFunction]} + onEdit={[MockFunction]} + /> + </DeferredSpinner> + </div> + <div + className="huge-spacer-top huge-spacer-bottom bordered-top" + /> + <div + className="big-padded" + > + <Connect(SubCategoryDefinitionsList) + category="almintegration" + subCategory="azure" /> - </DeferredSpinner> + </div> </div> `; exports[`should render correctly for single-ALM binding 2`] = ` <div - className="big-padded" + className="bordered" > - <DeferredSpinner - loading={false} + <div + className="big-padded" > - <div - className="spacer-bottom text-right" + <DeferredSpinner + loading={false} > - <Connect(withAppState(CreationTooltip)) - alm="azure" - preventCreation={true} + <div + className="spacer-bottom text-right" > - <Button - data-test="settings__alm-create" - disabled={true} - onClick={[MockFunction]} + <Connect(withAppState(CreationTooltip)) + alm="azure" + preventCreation={true} > - settings.almintegration.create - </Button> - </Connect(withAppState(CreationTooltip))> - </div> - <AlmBindingDefinitionBox - alm="azure" - branchesEnabled={true} - definition={ - Object { - "key": "key", - "personalAccessToken": "asdf1234", + <Button + data-test="settings__alm-create" + disabled={true} + onClick={[MockFunction]} + > + settings.almintegration.create + </Button> + </Connect(withAppState(CreationTooltip))> + </div> + <AlmBindingDefinitionBox + alm="azure" + branchesEnabled={true} + definition={ + Object { + "key": "key", + "personalAccessToken": "asdf1234", + } } - } - key="key" - multipleDefinitions={false} - onCheck={[MockFunction]} - onDelete={[MockFunction]} - onEdit={[MockFunction]} + key="key" + multipleDefinitions={false} + onCheck={[MockFunction]} + onDelete={[MockFunction]} + onEdit={[MockFunction]} + /> + </DeferredSpinner> + </div> + <div + className="huge-spacer-top huge-spacer-bottom bordered-top" + /> + <div + className="big-padded" + > + <Connect(SubCategoryDefinitionsList) + category="almintegration" + subCategory="azure" /> - </DeferredSpinner> + </div> </div> `; exports[`should render correctly for single-ALM binding 3`] = ` <div - className="big-padded" + className="bordered" > - <DeferredSpinner - loading={false} + <div + className="big-padded" > - <div - className="spacer-bottom text-right" + <DeferredSpinner + loading={false} > - <Connect(withAppState(CreationTooltip)) - alm="azure" - preventCreation={true} + <div + className="spacer-bottom text-right" > - <Button - data-test="settings__alm-create" - disabled={true} - onClick={[MockFunction]} + <Connect(withAppState(CreationTooltip)) + alm="azure" + preventCreation={true} > - settings.almintegration.create - </Button> - </Connect(withAppState(CreationTooltip))> - </div> - <AlmBindingDefinitionBox - alm="azure" - branchesEnabled={true} - definition={ - Object { - "key": "key", - "personalAccessToken": "asdf1234", + <Button + data-test="settings__alm-create" + disabled={true} + onClick={[MockFunction]} + > + settings.almintegration.create + </Button> + </Connect(withAppState(CreationTooltip))> + </div> + <AlmBindingDefinitionBox + alm="azure" + branchesEnabled={true} + definition={ + Object { + "key": "key", + "personalAccessToken": "asdf1234", + } } - } - key="key" - multipleDefinitions={false} - onCheck={[MockFunction]} - onDelete={[MockFunction]} - onEdit={[MockFunction]} + key="key" + multipleDefinitions={false} + onCheck={[MockFunction]} + onDelete={[MockFunction]} + onEdit={[MockFunction]} + /> + </DeferredSpinner> + </div> + <div + className="huge-spacer-top huge-spacer-bottom bordered-top" + /> + <div + className="big-padded" + > + <Connect(SubCategoryDefinitionsList) + category="almintegration" + subCategory="azure" /> - </DeferredSpinner> + </div> </div> `; exports[`should render correctly with validation: create a first 1`] = ` <div - className="big-padded" + className="bordered" > - <DeferredSpinner - loading={false} + <div + className="big-padded" > - <p - className="spacer-top" - > - settings.almintegration.empty.github - </p> - <div - className="big-spacer-top" + <DeferredSpinner + loading={false} > - <Connect(withAppState(CreationTooltip)) - alm="github" - preventCreation={false} + <p + className="spacer-top" + > + settings.almintegration.empty.azure + </p> + <div + className="big-spacer-top" > - <Button - data-test="settings__alm-create" - disabled={false} - onClick={[MockFunction]} + <Connect(withAppState(CreationTooltip)) + alm="azure" + preventCreation={false} > - settings.almintegration.create - </Button> - </Connect(withAppState(CreationTooltip))> - </div> - <AlmBindingDefinitionForm - bindingDefinition={ - Object { - "appId": "123456", - "clientId": "client1", - "clientSecret": "**clientsecret**", - "key": "key", - "privateKey": "asdf1234", - "url": "http://github.enterprise.com", - } - } - help={<div />} - isSecondInstance={false} - onCancel={[MockFunction]} - onSubmit={[MockFunction]} - > - <Component /> - </AlmBindingDefinitionForm> - </DeferredSpinner> + <Button + data-test="settings__alm-create" + disabled={false} + onClick={[MockFunction]} + > + settings.almintegration.create + </Button> + </Connect(withAppState(CreationTooltip))> + </div> + </DeferredSpinner> + </div> + <div + className="huge-spacer-top huge-spacer-bottom bordered-top" + /> + <div + className="big-padded" + > + <Connect(SubCategoryDefinitionsList) + category="almintegration" + subCategory="azure" + /> + </div> </div> `; exports[`should render correctly with validation: create a second 1`] = ` <div - className="big-padded" + className="bordered" > - <DeferredSpinner - loading={false} + <div + className="big-padded" > - <div - className="spacer-bottom text-right" + <DeferredSpinner + loading={false} > - <Connect(withAppState(CreationTooltip)) - alm="github" - preventCreation={false} + <div + className="spacer-bottom text-right" > - <Button - data-test="settings__alm-create" - disabled={false} - onClick={[MockFunction]} + <Connect(withAppState(CreationTooltip)) + alm="azure" + preventCreation={false} > - settings.almintegration.create - </Button> - </Connect(withAppState(CreationTooltip))> - </div> - <AlmBindingDefinitionBox - alm="github" - branchesEnabled={true} - definition={ - Object { - "appId": "123456", - "clientId": "client1", - "clientSecret": "**clientsecret**", - "key": "key", - "privateKey": "asdf1234", - "url": "http://github.enterprise.com", + <Button + data-test="settings__alm-create" + disabled={false} + onClick={[MockFunction]} + > + settings.almintegration.create + </Button> + </Connect(withAppState(CreationTooltip))> + </div> + <AlmBindingDefinitionBox + alm="azure" + branchesEnabled={true} + definition={ + Object { + "appId": "123456", + "clientId": "client1", + "clientSecret": "**clientsecret**", + "key": "key", + "privateKey": "asdf1234", + "url": "http://github.enterprise.com", + } } - } - key="key" - multipleDefinitions={false} - onCheck={[MockFunction]} - onDelete={[MockFunction]} - onEdit={[MockFunction]} + key="key" + multipleDefinitions={false} + onCheck={[MockFunction]} + onDelete={[MockFunction]} + onEdit={[MockFunction]} + /> + </DeferredSpinner> + </div> + <div + className="huge-spacer-top huge-spacer-bottom bordered-top" + /> + <div + className="big-padded" + > + <Connect(SubCategoryDefinitionsList) + category="almintegration" + subCategory="azure" /> - <AlmBindingDefinitionForm - bindingDefinition={ - Object { - "appId": "123456", - "clientId": "client1", - "clientSecret": "**clientsecret**", - "key": "key", - "privateKey": "asdf1234", - "url": "http://github.enterprise.com", - } - } - help={<div />} - isSecondInstance={true} - onCancel={[MockFunction]} - onSubmit={[MockFunction]} - > - <Component /> - </AlmBindingDefinitionForm> - </DeferredSpinner> + </div> </div> `; exports[`should render correctly with validation: default 1`] = ` <div - className="big-padded" + className="bordered" > - <DeferredSpinner - loading={false} + <div + className="big-padded" > - <div - className="spacer-bottom text-right" + <DeferredSpinner + loading={false} > - <Connect(withAppState(CreationTooltip)) - alm="github" - preventCreation={false} + <div + className="spacer-bottom text-right" > - <Button - data-test="settings__alm-create" - disabled={false} - onClick={[MockFunction]} + <Connect(withAppState(CreationTooltip)) + alm="azure" + preventCreation={false} > - settings.almintegration.create - </Button> - </Connect(withAppState(CreationTooltip))> - </div> - <AlmBindingDefinitionBox - alm="github" - branchesEnabled={true} - definition={ - Object { - "appId": "123456", - "clientId": "client1", - "clientSecret": "**clientsecret**", - "key": "key", - "privateKey": "asdf1234", - "url": "http://github.enterprise.com", + <Button + data-test="settings__alm-create" + disabled={false} + onClick={[MockFunction]} + > + settings.almintegration.create + </Button> + </Connect(withAppState(CreationTooltip))> + </div> + <AlmBindingDefinitionBox + alm="azure" + branchesEnabled={true} + definition={ + Object { + "appId": "123456", + "clientId": "client1", + "clientSecret": "**clientsecret**", + "key": "key", + "privateKey": "asdf1234", + "url": "http://github.enterprise.com", + } } - } - key="key" - multipleDefinitions={false} - onCheck={[MockFunction]} - onDelete={[MockFunction]} - onEdit={[MockFunction]} + key="key" + multipleDefinitions={false} + onCheck={[MockFunction]} + onDelete={[MockFunction]} + onEdit={[MockFunction]} + /> + </DeferredSpinner> + </div> + <div + className="huge-spacer-top huge-spacer-bottom bordered-top" + /> + <div + className="big-padded" + > + <Connect(SubCategoryDefinitionsList) + category="almintegration" + subCategory="azure" /> - </DeferredSpinner> + </div> </div> `; exports[`should render correctly with validation: empty 1`] = ` <div - className="big-padded" + className="bordered" > - <DeferredSpinner - loading={false} + <div + className="big-padded" > - <p - className="spacer-top" - > - settings.almintegration.empty.github - </p> - <div - className="big-spacer-top" + <DeferredSpinner + loading={false} > - <Connect(withAppState(CreationTooltip)) - alm="github" - preventCreation={false} + <p + className="spacer-top" + > + settings.almintegration.empty.azure + </p> + <div + className="big-spacer-top" > - <Button - data-test="settings__alm-create" - disabled={false} - onClick={[MockFunction]} + <Connect(withAppState(CreationTooltip)) + alm="azure" + preventCreation={false} > - settings.almintegration.create - </Button> - </Connect(withAppState(CreationTooltip))> - </div> - </DeferredSpinner> + <Button + data-test="settings__alm-create" + disabled={false} + onClick={[MockFunction]} + > + settings.almintegration.create + </Button> + </Connect(withAppState(CreationTooltip))> + </div> + </DeferredSpinner> + </div> + <div + className="huge-spacer-top huge-spacer-bottom bordered-top" + /> + <div + className="big-padded" + > + <Connect(SubCategoryDefinitionsList) + category="almintegration" + subCategory="azure" + /> + </div> </div> `; exports[`should render correctly with validation: pass the correct key for bitbucket cloud 1`] = ` <div - className="big-padded" + className="bordered" > - <DeferredSpinner - loading={false} + <div + className="big-padded" > - <div - className="spacer-bottom text-right" + <DeferredSpinner + loading={false} > - <Connect(withAppState(CreationTooltip)) - alm="bitbucket" - preventCreation={false} + <div + className="spacer-bottom text-right" > - <Button - data-test="settings__alm-create" - disabled={false} - onClick={[MockFunction]} + <Connect(withAppState(CreationTooltip)) + alm="bitbucket" + preventCreation={false} > - settings.almintegration.create - </Button> - </Connect(withAppState(CreationTooltip))> - </div> - <AlmBindingDefinitionBox - alm="bitbucketcloud" - branchesEnabled={true} - definition={ - Object { - "clientId": "client1", - "clientSecret": "**clientsecret**", - "key": "key", - "workspace": "workspace", + <Button + data-test="settings__alm-create" + disabled={false} + onClick={[MockFunction]} + > + settings.almintegration.create + </Button> + </Connect(withAppState(CreationTooltip))> + </div> + <AlmBindingDefinitionBox + alm="bitbucketcloud" + branchesEnabled={true} + definition={ + Object { + "clientId": "client1", + "clientSecret": "**clientsecret**", + "key": "key", + "workspace": "workspace", + } } - } - key="key" - multipleDefinitions={false} - onCheck={[MockFunction]} - onDelete={[MockFunction]} - onEdit={[MockFunction]} + key="key" + multipleDefinitions={false} + onCheck={[MockFunction]} + onDelete={[MockFunction]} + onEdit={[MockFunction]} + /> + </DeferredSpinner> + </div> + <div + className="huge-spacer-top huge-spacer-bottom bordered-top" + /> + <div + className="big-padded" + > + <Connect(SubCategoryDefinitionsList) + category="almintegration" + subCategory="bitbucket" /> - </DeferredSpinner> + </div> </div> `; diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AzureForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AzureForm-test.tsx.snap index 69f49790db3..433536d09a7 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AzureForm-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AzureForm-test.tsx.snap @@ -1,91 +1,145 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`should render correctly: create 1`] = ` -<Fragment> - <AlmBindingDefinitionFormField - autoFocus={true} - help="settings.almintegration.form.name.azure.help" - id="name.azure" - onFieldChange={[MockFunction]} - propKey="key" - value="" - /> - <AlmBindingDefinitionFormField - help={ - <React.Fragment> - settings.almintegration.form.url.azure.help1 - <br /> - <em> - https://ado.your-company.com/your_collection - </em> - <br /> - <br /> - settings.almintegration.form.url.azure.help2 - <br /> - <em> - https://dev.azure.com/your_organization - </em> - </React.Fragment> - } - id="url.azure" - maxLength={2000} - onFieldChange={[MockFunction]} - propKey="url" - value="" - /> - <AlmBindingDefinitionFormField - help="settings.almintegration.form.personal_access_token.azure.help" - id="personal_access_token" - isTextArea={true} - onFieldChange={[MockFunction]} - overwriteOnly={false} - propKey="personalAccessToken" - value="" - /> -</Fragment> +<div + className="display-flex-start" +> + <div + className="flex-1" + > + <AlmBindingDefinitionFormField + autoFocus={true} + help="settings.almintegration.form.name.azure.help" + id="name.azure" + onFieldChange={[MockFunction]} + propKey="key" + value="" + /> + <AlmBindingDefinitionFormField + help={ + <React.Fragment> + settings.almintegration.form.url.azure.help1 + <br /> + <em> + https://ado.your-company.com/your_collection + </em> + <br /> + <br /> + settings.almintegration.form.url.azure.help2 + <br /> + <em> + https://dev.azure.com/your_organization + </em> + </React.Fragment> + } + id="url.azure" + maxLength={2000} + onFieldChange={[MockFunction]} + propKey="url" + value="" + /> + <AlmBindingDefinitionFormField + help="settings.almintegration.form.personal_access_token.azure.help" + id="personal_access_token" + isTextArea={true} + onFieldChange={[MockFunction]} + overwriteOnly={false} + propKey="personalAccessToken" + value="" + /> + </div> + <Alert + className="huge-spacer-left flex-1" + variant="info" + > + <FormattedMessage + defaultMessage="settings.almintegration.azure.info" + id="settings.almintegration.azure.info" + values={ + Object { + "link": <Link + onlyActiveOnIndex={false} + style={Object {}} + target="_blank" + to="/documentation/analysis/azuredevops-integration/" + > + learn_more + </Link>, + } + } + /> + </Alert> +</div> `; exports[`should render correctly: edit 1`] = ` -<Fragment> - <AlmBindingDefinitionFormField - autoFocus={true} - help="settings.almintegration.form.name.azure.help" - id="name.azure" - onFieldChange={[MockFunction]} - propKey="key" - value="key" - /> - <AlmBindingDefinitionFormField - help={ - <React.Fragment> - settings.almintegration.form.url.azure.help1 - <br /> - <em> - https://ado.your-company.com/your_collection - </em> - <br /> - <br /> - settings.almintegration.form.url.azure.help2 - <br /> - <em> - https://dev.azure.com/your_organization - </em> - </React.Fragment> - } - id="url.azure" - maxLength={2000} - onFieldChange={[MockFunction]} - propKey="url" - value="" - /> - <AlmBindingDefinitionFormField - help="settings.almintegration.form.personal_access_token.azure.help" - id="personal_access_token" - isTextArea={true} - onFieldChange={[MockFunction]} - overwriteOnly={true} - propKey="personalAccessToken" - value="asdf1234" - /> -</Fragment> +<div + className="display-flex-start" +> + <div + className="flex-1" + > + <AlmBindingDefinitionFormField + autoFocus={true} + help="settings.almintegration.form.name.azure.help" + id="name.azure" + onFieldChange={[MockFunction]} + propKey="key" + value="key" + /> + <AlmBindingDefinitionFormField + help={ + <React.Fragment> + settings.almintegration.form.url.azure.help1 + <br /> + <em> + https://ado.your-company.com/your_collection + </em> + <br /> + <br /> + settings.almintegration.form.url.azure.help2 + <br /> + <em> + https://dev.azure.com/your_organization + </em> + </React.Fragment> + } + id="url.azure" + maxLength={2000} + onFieldChange={[MockFunction]} + propKey="url" + value="" + /> + <AlmBindingDefinitionFormField + help="settings.almintegration.form.personal_access_token.azure.help" + id="personal_access_token" + isTextArea={true} + onFieldChange={[MockFunction]} + overwriteOnly={true} + propKey="personalAccessToken" + value="asdf1234" + /> + </div> + <Alert + className="huge-spacer-left flex-1" + variant="info" + > + <FormattedMessage + defaultMessage="settings.almintegration.azure.info" + id="settings.almintegration.azure.info" + values={ + Object { + "link": <Link + onlyActiveOnIndex={false} + style={Object {}} + target="_blank" + to="/documentation/analysis/azuredevops-integration/" + > + learn_more + </Link>, + } + } + /> + </Alert> +</div> `; diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AzureTab-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AzureTab-test.tsx.snap deleted file mode 100644 index b282c815654..00000000000 --- a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AzureTab-test.tsx.snap +++ /dev/null @@ -1,55 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render correctly 1`] = ` -<div - className="bordered" -> - <AlmTab - alm="azure" - branchesEnabled={true} - createConfiguration={[Function]} - defaultBinding={ - Object { - "key": "", - "personalAccessToken": "", - "url": "", - } - } - definitionStatus={Object {}} - definitions={ - Array [ - Object { - "key": "key", - "personalAccessToken": "asdf1234", - }, - ] - } - form={[Function]} - help={ - <FormattedMessage - defaultMessage="settings.almintegration.azure.info" - id="settings.almintegration.azure.info" - values={ - Object { - "link": <Link - onlyActiveOnIndex={false} - style={Object {}} - target="_blank" - to="/documentation/analysis/azuredevops-integration/" - > - learn_more - </Link>, - } - } - /> - } - loadingAlmDefinitions={false} - loadingProjectCount={false} - multipleAlmEnabled={true} - onCheck={[MockFunction]} - onDelete={[MockFunction]} - onUpdateDefinitions={[MockFunction]} - updateConfiguration={[Function]} - /> -</div> -`; diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/BitbucketCloudForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/BitbucketCloudForm-test.tsx.snap new file mode 100644 index 00000000000..7ead8a21fcc --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/BitbucketCloudForm-test.tsx.snap @@ -0,0 +1,54 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly 1`] = ` +<Fragment> + <AlmBindingDefinitionFormField + autoFocus={true} + help="settings.almintegration.form.name.bitbucketcloud.help" + id="name.bitbucket" + maxLength={100} + onFieldChange={[MockFunction]} + propKey="key" + value="key" + /> + <AlmBindingDefinitionFormField + help={ + <FormattedMessage + defaultMessage="settings.almintegration.form.workspace.bitbucketcloud.help" + id="settings.almintegration.form.workspace.bitbucketcloud.help" + values={ + Object { + "example": <React.Fragment> + https://bitbucket.org/ + <strong> + {workspace} + </strong> + /{repository} + </React.Fragment>, + } + } + /> + } + id="workspace.bitbucketcloud" + maxLength={2000} + onFieldChange={[MockFunction]} + propKey="workspace" + value="workspace" + /> + <AlmBindingDefinitionFormField + help="settings.almintegration.form.oauth_key.bitbucketcloud.help" + id="client_id.bitbucketcloud" + onFieldChange={[MockFunction]} + propKey="clientId" + value="client1" + /> + <AlmBindingDefinitionFormField + help="settings.almintegration.form.oauth_secret.bitbucketcloud.help" + id="client_secret.bitbucketcloud" + onFieldChange={[MockFunction]} + overwriteOnly={true} + propKey="clientSecret" + value="**clientsecret**" + /> +</Fragment> +`; diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/BitbucketForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/BitbucketForm-test.tsx.snap index 5a7f57de670..d8914665ebc 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/BitbucketForm-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/BitbucketForm-test.tsx.snap @@ -1,310 +1,259 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`should render correctly: bitbucket cloud, edit 1`] = ` -<div> - <div> - <AlmBindingDefinitionFormField - autoFocus={true} - help="settings.almintegration.form.name.bitbucketcloud.help" - id="name.bitbucket" - maxLength={100} - onFieldChange={[MockFunction]} - propKey="key" - value="key" - /> - <AlmBindingDefinitionFormField - help={ - <FormattedMessage - defaultMessage="settings.almintegration.form.workspace.bitbucketcloud.help" - id="settings.almintegration.form.workspace.bitbucketcloud.help" - values={ - Object { - "example": <React.Fragment> - https://bitbucket.org/ - <strong> - {workspace} - </strong> - /{repository} - </React.Fragment>, - } - } - /> +exports[`should render correctly: bbc 1`] = ` +<Fragment> + <div + className="display-flex-column" + > + <strong> + settings.almintegration.form.choose_bitbucket_variant + </strong> + <RadioToggle + className="little-spacer-top big-spacer-bottom" + disabled={false} + name="variant" + onCheck={[MockFunction]} + options={ + Array [ + Object { + "label": "Bitbucket Server", + "value": "bitbucket", + }, + Object { + "label": "Bitbucket Cloud", + "value": "bitbucketcloud", + }, + ] } - id="workspace.bitbucketcloud" - maxLength={2000} - onFieldChange={[MockFunction]} - propKey="workspace" - value="workspace" - /> - <AlmBindingDefinitionFormField - help="settings.almintegration.form.oauth_key.bitbucketcloud.help" - id="client_id.bitbucketcloud" - onFieldChange={[MockFunction]} - propKey="clientId" - value="client1" - /> - <AlmBindingDefinitionFormField - help="settings.almintegration.form.oauth_secret.bitbucketcloud.help" - id="client_secret.bitbucketcloud" - onFieldChange={[MockFunction]} - overwriteOnly={true} - propKey="clientSecret" - value="**clientsecret**" + value="bitbucketcloud" /> </div> -</div> -`; - -exports[`should render correctly: bitbucket cloud, empty 1`] = ` -<div> - <div> - <AlmBindingDefinitionFormField - autoFocus={true} - help="settings.almintegration.form.name.bitbucketcloud.help" - id="name.bitbucket" - maxLength={100} - onFieldChange={[MockFunction]} - propKey="key" - value="" - /> - <AlmBindingDefinitionFormField - help={ - <FormattedMessage - defaultMessage="settings.almintegration.form.workspace.bitbucketcloud.help" - id="settings.almintegration.form.workspace.bitbucketcloud.help" - values={ - Object { - "example": <React.Fragment> - https://bitbucket.org/ - <strong> - {workspace} - </strong> - /{repository} - </React.Fragment>, - } + <div + className="display-flex-start" + > + <div + className="flex-1" + > + <BitbucketCloudForm + formData={ + Object { + "clientId": "client1", + "clientSecret": "**clientsecret**", + "key": "key", + "workspace": "workspace", } - /> - } - id="workspace.bitbucketcloud" - maxLength={2000} - onFieldChange={[MockFunction]} - propKey="workspace" - value="" - /> - <AlmBindingDefinitionFormField - help="settings.almintegration.form.oauth_key.bitbucketcloud.help" - id="client_id.bitbucketcloud" - onFieldChange={[MockFunction]} - propKey="clientId" - value="" - /> - <AlmBindingDefinitionFormField - help="settings.almintegration.form.oauth_secret.bitbucketcloud.help" - id="client_secret.bitbucketcloud" - onFieldChange={[MockFunction]} - overwriteOnly={false} - propKey="clientSecret" - value="" - /> + } + onFieldChange={[MockFunction]} + /> + </div> + <Alert + className="huge-spacer-left flex-1" + variant="info" + > + <FormattedMessage + defaultMessage="settings.almintegration.bitbucketcloud.info" + id="settings.almintegration.bitbucketcloud.info" + values={ + Object { + "link": <Link + onlyActiveOnIndex={false} + style={Object {}} + target="_blank" + to="/documentation/analysis/bitbucket-cloud-integration/" + > + learn_more + </Link>, + } + } + /> + </Alert> </div> -</div> +</Fragment> `; -exports[`should render correctly: bitbucket server, edit 1`] = ` -<div> - <div> - <AlmBindingDefinitionFormField - autoFocus={true} - help="settings.almintegration.form.name.bitbucket.help" - id="name.bitbucket" - maxLength={100} - onFieldChange={[MockFunction]} - propKey="key" - value="key" - /> - <AlmBindingDefinitionFormField - help={ - <FormattedMessage - defaultMessage="settings.almintegration.form.url.bitbucket.help" - id="settings.almintegration.form.url.bitbucket.help" - values={ - Object { - "example": "https://bitbucket-server.your-company.com", - } - } - /> +exports[`should render correctly: bbs 1`] = ` +<Fragment> + <div + className="display-flex-column" + > + <strong> + settings.almintegration.form.choose_bitbucket_variant + </strong> + <RadioToggle + className="little-spacer-top big-spacer-bottom" + disabled={false} + name="variant" + onCheck={[MockFunction]} + options={ + Array [ + Object { + "label": "Bitbucket Server", + "value": "bitbucket", + }, + Object { + "label": "Bitbucket Cloud", + "value": "bitbucketcloud", + }, + ] } - id="url.bitbucket" - maxLength={2000} - onFieldChange={[MockFunction]} - propKey="url" - value="http://bbs.enterprise.com" + value="bitbucket" /> - <AlmBindingDefinitionFormField - help={ - <FormattedMessage - defaultMessage="settings.almintegration.form.personal_access_token.bitbucket.help" - id="settings.almintegration.form.personal_access_token.bitbucket.help" - values={ - Object { - "pat": <a - href="https://confluence.atlassian.com/bitbucketserver0515/personal-access-tokens-961275199.html" - rel="noopener noreferrer" - target="_blank" - > - settings.almintegration.form.personal_access_token.bitbucket.help.url - </a>, - } + </div> + <div + className="display-flex-start" + > + <div + className="flex-1" + > + <BitbucketServerForm + formData={ + Object { + "key": "key", + "personalAccessToken": "asdf1234", + "url": "http://bbs.enterprise.com", } - /> - } - id="personal_access_token" - isTextArea={true} - onFieldChange={[MockFunction]} - overwriteOnly={true} - propKey="personalAccessToken" - value="asdf1234" - /> + } + onFieldChange={[MockFunction]} + /> + </div> + <Alert + className="huge-spacer-left flex-1" + variant="info" + > + <h3> + onboarding.create_project.pat_help.title + </h3> + <p + className="big-spacer-top" + > + settings.almintegration.bitbucket.help_1 + </p> + <ul + className="big-spacer-top list-styled" + > + <li> + settings.almintegration.bitbucket.help_2 + </li> + <li> + settings.almintegration.bitbucket.help_3 + </li> + </ul> + <p + className="big-spacer-top big-spacer-bottom" + > + <Link + onlyActiveOnIndex={false} + style={Object {}} + target="_blank" + to="/documentation/analysis/bitbucket-integration/" + > + learn_more + </Link> + </p> + </Alert> </div> -</div> +</Fragment> `; -exports[`should render correctly: bitbucket server, empty 1`] = ` -<div> - <div> - <AlmBindingDefinitionFormField - autoFocus={true} - help="settings.almintegration.form.name.bitbucket.help" - id="name.bitbucket" - maxLength={100} - onFieldChange={[MockFunction]} - propKey="key" - value="" - /> - <AlmBindingDefinitionFormField - help={ - <FormattedMessage - defaultMessage="settings.almintegration.form.url.bitbucket.help" - id="settings.almintegration.form.url.bitbucket.help" - values={ - Object { - "example": "https://bitbucket-server.your-company.com", - } +exports[`should render correctly: update bbc 1`] = ` +<Fragment> + <div + className="display-flex-start" + > + <div + className="flex-1" + > + <BitbucketCloudForm + formData={ + Object { + "clientId": "client1", + "clientSecret": "**clientsecret**", + "key": "key", + "workspace": "workspace", } - /> - } - id="url.bitbucket" - maxLength={2000} - onFieldChange={[MockFunction]} - propKey="url" - value="" - /> - <AlmBindingDefinitionFormField - help={ - <FormattedMessage - defaultMessage="settings.almintegration.form.personal_access_token.bitbucket.help" - id="settings.almintegration.form.personal_access_token.bitbucket.help" - values={ - Object { - "pat": <a - href="https://confluence.atlassian.com/bitbucketserver0515/personal-access-tokens-961275199.html" - rel="noopener noreferrer" - target="_blank" - > - settings.almintegration.form.personal_access_token.bitbucket.help.url - </a>, - } + } + onFieldChange={[MockFunction]} + /> + </div> + <Alert + className="huge-spacer-left flex-1" + variant="info" + > + <FormattedMessage + defaultMessage="settings.almintegration.bitbucketcloud.info" + id="settings.almintegration.bitbucketcloud.info" + values={ + Object { + "link": <Link + onlyActiveOnIndex={false} + style={Object {}} + target="_blank" + to="/documentation/analysis/bitbucket-cloud-integration/" + > + learn_more + </Link>, } - /> - } - id="personal_access_token" - isTextArea={true} - onFieldChange={[MockFunction]} - overwriteOnly={false} - propKey="personalAccessToken" - value="" - /> + } + /> + </Alert> </div> -</div> +</Fragment> `; -exports[`should render correctly: variant select 1`] = ` -<div> - <strong> - settings.almintegration.form.choose_bitbucket_variant - </strong> - <RadioToggle - className="little-spacer-top big-spacer-bottom" - disabled={false} - name="variant" - onCheck={[MockFunction]} - options={ - Array [ - Object { - "label": "Bitbucket Server", - "value": "bitbucket", - }, - Object { - "label": "Bitbucket Cloud", - "value": "bitbucketcloud", - }, - ] - } - value="bitbucket" - /> - <div> - <AlmBindingDefinitionFormField - autoFocus={true} - help="settings.almintegration.form.name.bitbucket.help" - id="name.bitbucket" - maxLength={100} - onFieldChange={[MockFunction]} - propKey="key" - value="" - /> - <AlmBindingDefinitionFormField - help={ - <FormattedMessage - defaultMessage="settings.almintegration.form.url.bitbucket.help" - id="settings.almintegration.form.url.bitbucket.help" - values={ - Object { - "example": "https://bitbucket-server.your-company.com", - } +exports[`should render correctly: update bbs 1`] = ` +<Fragment> + <div + className="display-flex-start" + > + <div + className="flex-1" + > + <BitbucketServerForm + formData={ + Object { + "key": "key", + "personalAccessToken": "asdf1234", + "url": "http://bbs.enterprise.com", } - /> - } - id="url.bitbucket" - maxLength={2000} - onFieldChange={[MockFunction]} - propKey="url" - value="" - /> - <AlmBindingDefinitionFormField - help={ - <FormattedMessage - defaultMessage="settings.almintegration.form.personal_access_token.bitbucket.help" - id="settings.almintegration.form.personal_access_token.bitbucket.help" - values={ - Object { - "pat": <a - href="https://confluence.atlassian.com/bitbucketserver0515/personal-access-tokens-961275199.html" - rel="noopener noreferrer" - target="_blank" - > - settings.almintegration.form.personal_access_token.bitbucket.help.url - </a>, - } - } - /> - } - id="personal_access_token" - isTextArea={true} - onFieldChange={[MockFunction]} - overwriteOnly={false} - propKey="personalAccessToken" - value="" - /> + } + onFieldChange={[MockFunction]} + /> + </div> + <Alert + className="huge-spacer-left flex-1" + variant="info" + > + <h3> + onboarding.create_project.pat_help.title + </h3> + <p + className="big-spacer-top" + > + settings.almintegration.bitbucket.help_1 + </p> + <ul + className="big-spacer-top list-styled" + > + <li> + settings.almintegration.bitbucket.help_2 + </li> + <li> + settings.almintegration.bitbucket.help_3 + </li> + </ul> + <p + className="big-spacer-top big-spacer-bottom" + > + <Link + onlyActiveOnIndex={false} + style={Object {}} + target="_blank" + to="/documentation/analysis/bitbucket-integration/" + > + learn_more + </Link> + </p> + </Alert> </div> -</div> +</Fragment> `; diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/BitbucketServerForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/BitbucketServerForm-test.tsx.snap new file mode 100644 index 00000000000..718829ab486 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/BitbucketServerForm-test.tsx.snap @@ -0,0 +1,59 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly 1`] = ` +<Fragment> + + <AlmBindingDefinitionFormField + autoFocus={true} + help="settings.almintegration.form.name.bitbucket.help" + id="name.bitbucket" + maxLength={100} + onFieldChange={[MockFunction]} + propKey="key" + value="key" + /> + <AlmBindingDefinitionFormField + help={ + <FormattedMessage + defaultMessage="settings.almintegration.form.url.bitbucket.help" + id="settings.almintegration.form.url.bitbucket.help" + values={ + Object { + "example": "https://bitbucket-server.your-company.com", + } + } + /> + } + id="url.bitbucket" + maxLength={2000} + onFieldChange={[MockFunction]} + propKey="url" + value="http://bbs.enterprise.com" + /> + <AlmBindingDefinitionFormField + help={ + <FormattedMessage + defaultMessage="settings.almintegration.form.personal_access_token.bitbucket.help" + id="settings.almintegration.form.personal_access_token.bitbucket.help" + values={ + Object { + "pat": <a + href="https://confluence.atlassian.com/bitbucketserver0515/personal-access-tokens-961275199.html" + rel="noopener noreferrer" + target="_blank" + > + settings.almintegration.form.personal_access_token.bitbucket.help.url + </a>, + } + } + /> + } + id="personal_access_token" + isTextArea={true} + onFieldChange={[MockFunction]} + overwriteOnly={true} + propKey="personalAccessToken" + value="asdf1234" + /> +</Fragment> +`; diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/BitbucketTab-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/BitbucketTab-test.tsx.snap deleted file mode 100644 index 8100d78442d..00000000000 --- a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/BitbucketTab-test.tsx.snap +++ /dev/null @@ -1,30 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render correctly 1`] = ` -<BitbucketTabRenderer - branchesEnabled={true} - definitionStatus={Object {}} - definitions={ - Array [ - Object { - "key": "key", - "personalAccessToken": "asdf1234", - "url": "http://bbs.enterprise.com", - }, - ] - } - isCreating={false} - loadingAlmDefinitions={false} - loadingProjectCount={false} - multipleAlmEnabled={true} - onCancel={[Function]} - onCheck={[MockFunction]} - onCreate={[Function]} - onDelete={[MockFunction]} - onEdit={[Function]} - onSelectVariant={[Function]} - onSubmit={[Function]} - submitting={false} - success={false} -/> -`; diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/BitbucketTabRenderer-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/BitbucketTabRenderer-test.tsx.snap deleted file mode 100644 index e742c9c3f31..00000000000 --- a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/BitbucketTabRenderer-test.tsx.snap +++ /dev/null @@ -1,143 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render correctly: bitbucket cloud 1`] = ` -<div - className="bordered" -> - <AlmTabRenderer - alm="bitbucket" - branchesEnabled={true} - definitionStatus={Object {}} - definitions={Array []} - form={[Function]} - help={ - <FormattedMessage - defaultMessage="settings.almintegration.bitbucketcloud.info" - id="settings.almintegration.bitbucketcloud.info" - values={ - Object { - "link": <Link - onlyActiveOnIndex={false} - style={Object {}} - target="_blank" - to="/documentation/analysis/bitbucket-cloud-integration/" - > - learn_more - </Link>, - } - } - /> - } - loadingAlmDefinitions={false} - loadingProjectCount={false} - multipleAlmEnabled={true} - onCancel={[MockFunction]} - onCheck={[MockFunction]} - onCreate={[MockFunction]} - onDelete={[MockFunction]} - onEdit={[MockFunction]} - onSubmit={[MockFunction]} - submitting={true} - success={false} - /> -</div> -`; - -exports[`should render correctly: bitbucket form 1`] = ` -<BitbucketForm - formData={ - Object { - "key": "key", - "personalAccessToken": "asdf1234", - "url": "http://bbs.enterprise.com", - } - } - isCreating={false} - onFieldChange={[MockFunction]} - onSelectVariant={[MockFunction]} -/> -`; - -exports[`should render correctly: bitbucket server 1`] = ` -<div - className="bordered" -> - <AlmTabRenderer - alm="bitbucket" - branchesEnabled={true} - definitionStatus={Object {}} - definitions={Array []} - form={[Function]} - help={ - <React.Fragment> - <h3> - onboarding.create_project.pat_help.title - </h3> - <p - className="big-spacer-top" - > - settings.almintegration.bitbucket.help_1 - </p> - <ul - className="big-spacer-top list-styled" - > - <li> - settings.almintegration.bitbucket.help_2 - </li> - <li> - settings.almintegration.bitbucket.help_3 - </li> - </ul> - <p - className="big-spacer-top big-spacer-bottom" - > - <Link - onlyActiveOnIndex={false} - style={Object {}} - target="_blank" - to="/documentation/analysis/bitbucket-integration/" - > - learn_more - </Link> - </p> - </React.Fragment> - } - loadingAlmDefinitions={false} - loadingProjectCount={false} - multipleAlmEnabled={true} - onCancel={[MockFunction]} - onCheck={[MockFunction]} - onCreate={[MockFunction]} - onDelete={[MockFunction]} - onEdit={[MockFunction]} - onSubmit={[MockFunction]} - submitting={true} - success={false} - /> -</div> -`; - -exports[`should render correctly: default 1`] = ` -<div - className="bordered" -> - <AlmTabRenderer - alm="bitbucket" - branchesEnabled={true} - definitionStatus={Object {}} - definitions={Array []} - form={[Function]} - loadingAlmDefinitions={false} - loadingProjectCount={false} - multipleAlmEnabled={true} - onCancel={[MockFunction]} - onCheck={[MockFunction]} - onCreate={[MockFunction]} - onDelete={[MockFunction]} - onEdit={[MockFunction]} - onSubmit={[MockFunction]} - submitting={true} - success={false} - /> -</div> -`; diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/GithubForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/GithubForm-test.tsx.snap index 5727233d11d..86fe6664f75 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/GithubForm-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/GithubForm-test.tsx.snap @@ -1,141 +1,195 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`should render correctly 1`] = ` -<Fragment> - <AlmBindingDefinitionFormField - autoFocus={true} - help="settings.almintegration.form.name.github.help" - id="name.github" - onFieldChange={[MockFunction]} - propKey="key" - value="" - /> - <AlmBindingDefinitionFormField - help={ - <React.Fragment> - settings.almintegration.form.url.github.help1 - <br /> - <em> - https://github.company.com/api/v3 - </em> - <br /> - <br /> - settings.almintegration.form.url.github.help2 - <br /> - <em> - https://api.github.com/ - </em> - </React.Fragment> - } - id="url.github" - maxLength={2000} - onFieldChange={[MockFunction]} - propKey="url" - value="" - /> - <AlmBindingDefinitionFormField - help="settings.almintegration.form.app_id.github.help" - id="app_id" - maxLength={80} - onFieldChange={[MockFunction]} - propKey="appId" - value="" - /> - <AlmBindingDefinitionFormField - help="settings.almintegration.form.client_id.github.help" - id="client_id.github" - maxLength={80} - onFieldChange={[MockFunction]} - propKey="clientId" - value="" - /> - <AlmBindingDefinitionFormField - help="settings.almintegration.form.client_secret.github.help" - id="client_secret.github" - maxLength={80} - onFieldChange={[MockFunction]} - overwriteOnly={false} - propKey="clientSecret" - value="" - /> - <AlmBindingDefinitionFormField - help="settings.almintegration.form.private_key.github.help" - id="private_key" - isTextArea={true} - onFieldChange={[MockFunction]} - overwriteOnly={false} - propKey="privateKey" - value="" - /> -</Fragment> +<div + className="display-flex-start" +> + <div + className="flex-1" + > + <AlmBindingDefinitionFormField + autoFocus={true} + help="settings.almintegration.form.name.github.help" + id="name.github" + onFieldChange={[MockFunction]} + propKey="key" + value="" + /> + <AlmBindingDefinitionFormField + help={ + <React.Fragment> + settings.almintegration.form.url.github.help1 + <br /> + <em> + https://github.company.com/api/v3 + </em> + <br /> + <br /> + settings.almintegration.form.url.github.help2 + <br /> + <em> + https://api.github.com/ + </em> + </React.Fragment> + } + id="url.github" + maxLength={2000} + onFieldChange={[MockFunction]} + propKey="url" + value="" + /> + <AlmBindingDefinitionFormField + help="settings.almintegration.form.app_id.github.help" + id="app_id" + maxLength={80} + onFieldChange={[MockFunction]} + propKey="appId" + value="" + /> + <AlmBindingDefinitionFormField + help="settings.almintegration.form.client_id.github.help" + id="client_id.github" + maxLength={80} + onFieldChange={[MockFunction]} + propKey="clientId" + value="" + /> + <AlmBindingDefinitionFormField + help="settings.almintegration.form.client_secret.github.help" + id="client_secret.github" + maxLength={80} + onFieldChange={[MockFunction]} + overwriteOnly={false} + propKey="clientSecret" + value="" + /> + <AlmBindingDefinitionFormField + help="settings.almintegration.form.private_key.github.help" + id="private_key" + isTextArea={true} + onFieldChange={[MockFunction]} + overwriteOnly={false} + propKey="privateKey" + value="" + /> + </div> + <Alert + className="huge-spacer-left flex-1" + variant="info" + > + <FormattedMessage + defaultMessage="settings.almintegration.github.info" + id="settings.almintegration.github.info" + values={ + Object { + "link": <Link + onlyActiveOnIndex={false} + style={Object {}} + target="_blank" + to="/documentation/analysis/github-integration/" + > + learn_more + </Link>, + } + } + /> + </Alert> +</div> `; exports[`should render correctly 2`] = ` -<Fragment> - <AlmBindingDefinitionFormField - autoFocus={true} - help="settings.almintegration.form.name.github.help" - id="name.github" - onFieldChange={[MockFunction]} - propKey="key" - value="key" - /> - <AlmBindingDefinitionFormField - help={ - <React.Fragment> - settings.almintegration.form.url.github.help1 - <br /> - <em> - https://github.company.com/api/v3 - </em> - <br /> - <br /> - settings.almintegration.form.url.github.help2 - <br /> - <em> - https://api.github.com/ - </em> - </React.Fragment> - } - id="url.github" - maxLength={2000} - onFieldChange={[MockFunction]} - propKey="url" - value="http://github.enterprise.com" - /> - <AlmBindingDefinitionFormField - help="settings.almintegration.form.app_id.github.help" - id="app_id" - maxLength={80} - onFieldChange={[MockFunction]} - propKey="appId" - value="123456" - /> - <AlmBindingDefinitionFormField - help="settings.almintegration.form.client_id.github.help" - id="client_id.github" - maxLength={80} - onFieldChange={[MockFunction]} - propKey="clientId" - value="client1" - /> - <AlmBindingDefinitionFormField - help="settings.almintegration.form.client_secret.github.help" - id="client_secret.github" - maxLength={80} - onFieldChange={[MockFunction]} - overwriteOnly={true} - propKey="clientSecret" - value="**clientsecret**" - /> - <AlmBindingDefinitionFormField - help="settings.almintegration.form.private_key.github.help" - id="private_key" - isTextArea={true} - onFieldChange={[MockFunction]} - overwriteOnly={true} - propKey="privateKey" - value="asdf1234" - /> -</Fragment> +<div + className="display-flex-start" +> + <div + className="flex-1" + > + <AlmBindingDefinitionFormField + autoFocus={true} + help="settings.almintegration.form.name.github.help" + id="name.github" + onFieldChange={[MockFunction]} + propKey="key" + value="key" + /> + <AlmBindingDefinitionFormField + help={ + <React.Fragment> + settings.almintegration.form.url.github.help1 + <br /> + <em> + https://github.company.com/api/v3 + </em> + <br /> + <br /> + settings.almintegration.form.url.github.help2 + <br /> + <em> + https://api.github.com/ + </em> + </React.Fragment> + } + id="url.github" + maxLength={2000} + onFieldChange={[MockFunction]} + propKey="url" + value="http://github.enterprise.com" + /> + <AlmBindingDefinitionFormField + help="settings.almintegration.form.app_id.github.help" + id="app_id" + maxLength={80} + onFieldChange={[MockFunction]} + propKey="appId" + value="123456" + /> + <AlmBindingDefinitionFormField + help="settings.almintegration.form.client_id.github.help" + id="client_id.github" + maxLength={80} + onFieldChange={[MockFunction]} + propKey="clientId" + value="client1" + /> + <AlmBindingDefinitionFormField + help="settings.almintegration.form.client_secret.github.help" + id="client_secret.github" + maxLength={80} + onFieldChange={[MockFunction]} + overwriteOnly={true} + propKey="clientSecret" + value="**clientsecret**" + /> + <AlmBindingDefinitionFormField + help="settings.almintegration.form.private_key.github.help" + id="private_key" + isTextArea={true} + onFieldChange={[MockFunction]} + overwriteOnly={true} + propKey="privateKey" + value="asdf1234" + /> + </div> + <Alert + className="huge-spacer-left flex-1" + variant="info" + > + <FormattedMessage + defaultMessage="settings.almintegration.github.info" + id="settings.almintegration.github.info" + values={ + Object { + "link": <Link + onlyActiveOnIndex={false} + style={Object {}} + target="_blank" + to="/documentation/analysis/github-integration/" + > + learn_more + </Link>, + } + } + /> + </Alert> +</div> `; diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/GithubTab-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/GithubTab-test.tsx.snap deleted file mode 100644 index 7ad5b337f71..00000000000 --- a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/GithubTab-test.tsx.snap +++ /dev/null @@ -1,73 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render correctly: with branch support 1`] = ` -<div - className="bordered" -> - <AlmTab - alm="github" - branchesEnabled={true} - createConfiguration={[Function]} - defaultBinding={ - Object { - "appId": "", - "clientId": "", - "clientSecret": "", - "key": "", - "privateKey": "", - "url": "", - } - } - definitionStatus={Object {}} - definitions={ - Array [ - Object { - "appId": "123456", - "clientId": "client1", - "clientSecret": "**clientsecret**", - "key": "key", - "privateKey": "asdf1234", - "url": "http://github.enterprise.com", - }, - ] - } - form={[Function]} - help={ - <FormattedMessage - defaultMessage="settings.almintegration.github.info" - id="settings.almintegration.github.info" - values={ - Object { - "link": <Link - onlyActiveOnIndex={false} - style={Object {}} - target="_blank" - to="/documentation/analysis/github-integration/" - > - learn_more - </Link>, - } - } - /> - } - loadingAlmDefinitions={false} - loadingProjectCount={false} - multipleAlmEnabled={true} - onCheck={[MockFunction]} - onDelete={[MockFunction]} - onUpdateDefinitions={[MockFunction]} - updateConfiguration={[Function]} - /> - <div - className="huge-spacer-top huge-spacer-bottom bordered-top" - /> - <div - className="big-padded" - > - <Connect(SubCategoryDefinitionsList) - category="almintegration" - subCategory="github" - /> - </div> -</div> -`; diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/GitlabForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/GitlabForm-test.tsx.snap index 10b9473011a..d68ef50b519 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/GitlabForm-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/GitlabForm-test.tsx.snap @@ -1,77 +1,131 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`should render correctly 1`] = ` -<Fragment> - <AlmBindingDefinitionFormField - autoFocus={true} - help="settings.almintegration.form.name.gitlab.help" - id="name.gitlab" - onFieldChange={[MockFunction]} - propKey="key" - value="" - /> - <AlmBindingDefinitionFormField - help={ - <React.Fragment> - settings.almintegration.form.url.gitlab.help - <br /> - <em> - https://gitlab.com/api/v4 - </em> - </React.Fragment> - } - id="url.gitlab" - maxLength={2000} - onFieldChange={[MockFunction]} - propKey="url" - value="" - /> - <AlmBindingDefinitionFormField - help="settings.almintegration.form.personal_access_token.gitlab.help" - id="personal_access_token" - isTextArea={true} - onFieldChange={[MockFunction]} - overwriteOnly={false} - propKey="personalAccessToken" - value="" - /> -</Fragment> +<div + className="display-flex-start" +> + <div + className="flex-1" + > + <AlmBindingDefinitionFormField + autoFocus={true} + help="settings.almintegration.form.name.gitlab.help" + id="name.gitlab" + onFieldChange={[MockFunction]} + propKey="key" + value="" + /> + <AlmBindingDefinitionFormField + help={ + <React.Fragment> + settings.almintegration.form.url.gitlab.help + <br /> + <em> + https://gitlab.com/api/v4 + </em> + </React.Fragment> + } + id="url.gitlab" + maxLength={2000} + onFieldChange={[MockFunction]} + propKey="url" + value="" + /> + <AlmBindingDefinitionFormField + help="settings.almintegration.form.personal_access_token.gitlab.help" + id="personal_access_token" + isTextArea={true} + onFieldChange={[MockFunction]} + overwriteOnly={false} + propKey="personalAccessToken" + value="" + /> + </div> + <Alert + className="huge-spacer-left flex-1" + variant="info" + > + <FormattedMessage + defaultMessage="settings.almintegration.gitlab.info" + id="settings.almintegration.gitlab.info" + values={ + Object { + "link": <Link + onlyActiveOnIndex={false} + style={Object {}} + target="_blank" + to="/documentation/analysis/gitlab-integration/" + > + learn_more + </Link>, + } + } + /> + </Alert> +</div> `; exports[`should render correctly 2`] = ` -<Fragment> - <AlmBindingDefinitionFormField - autoFocus={true} - help="settings.almintegration.form.name.gitlab.help" - id="name.gitlab" - onFieldChange={[MockFunction]} - propKey="key" - value="foo" - /> - <AlmBindingDefinitionFormField - help={ - <React.Fragment> - settings.almintegration.form.url.gitlab.help - <br /> - <em> - https://gitlab.com/api/v4 - </em> - </React.Fragment> - } - id="url.gitlab" - maxLength={2000} - onFieldChange={[MockFunction]} - propKey="url" - value="" - /> - <AlmBindingDefinitionFormField - help="settings.almintegration.form.personal_access_token.gitlab.help" - id="personal_access_token" - isTextArea={true} - onFieldChange={[MockFunction]} - overwriteOnly={true} - propKey="personalAccessToken" - value="foobar" - /> -</Fragment> +<div + className="display-flex-start" +> + <div + className="flex-1" + > + <AlmBindingDefinitionFormField + autoFocus={true} + help="settings.almintegration.form.name.gitlab.help" + id="name.gitlab" + onFieldChange={[MockFunction]} + propKey="key" + value="foo" + /> + <AlmBindingDefinitionFormField + help={ + <React.Fragment> + settings.almintegration.form.url.gitlab.help + <br /> + <em> + https://gitlab.com/api/v4 + </em> + </React.Fragment> + } + id="url.gitlab" + maxLength={2000} + onFieldChange={[MockFunction]} + propKey="url" + value="" + /> + <AlmBindingDefinitionFormField + help="settings.almintegration.form.personal_access_token.gitlab.help" + id="personal_access_token" + isTextArea={true} + onFieldChange={[MockFunction]} + overwriteOnly={true} + propKey="personalAccessToken" + value="foobar" + /> + </div> + <Alert + className="huge-spacer-left flex-1" + variant="info" + > + <FormattedMessage + defaultMessage="settings.almintegration.gitlab.info" + id="settings.almintegration.gitlab.info" + values={ + Object { + "link": <Link + onlyActiveOnIndex={false} + style={Object {}} + target="_blank" + to="/documentation/analysis/gitlab-integration/" + > + learn_more + </Link>, + } + } + /> + </Alert> +</div> `; diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/GitlabTab-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/GitlabTab-test.tsx.snap deleted file mode 100644 index dfbd667ff1c..00000000000 --- a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/GitlabTab-test.tsx.snap +++ /dev/null @@ -1,190 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render correctly: with URL 1`] = ` -<div - className="bordered" -> - <AlmTab - alm="gitlab" - branchesEnabled={true} - createConfiguration={[Function]} - defaultBinding={ - Object { - "key": "", - "personalAccessToken": "", - "url": "", - } - } - definitionStatus={Object {}} - definitions={ - Array [ - Object { - "key": "foo", - "personalAccessToken": "foobar", - "url": "https://gitlab.com/api/v4", - }, - ] - } - form={[Function]} - help={ - <FormattedMessage - defaultMessage="settings.almintegration.gitlab.info" - id="settings.almintegration.gitlab.info" - values={ - Object { - "link": <Link - onlyActiveOnIndex={false} - style={Object {}} - target="_blank" - to="/documentation/analysis/gitlab-integration/" - > - learn_more - </Link>, - } - } - /> - } - loadingAlmDefinitions={false} - loadingProjectCount={false} - multipleAlmEnabled={true} - onCheck={[MockFunction]} - onDelete={[MockFunction]} - onUpdateDefinitions={[MockFunction]} - updateConfiguration={[Function]} - /> - <div - className="huge-spacer-top huge-spacer-bottom bordered-top" - /> - <div - className="big-padded" - > - <Connect(SubCategoryDefinitionsList) - category="almintegration" - subCategory="gitlab" - /> - </div> -</div> -`; - -exports[`should render correctly: with branch support 1`] = ` -<div - className="bordered" -> - <AlmTab - alm="gitlab" - branchesEnabled={true} - createConfiguration={[Function]} - defaultBinding={ - Object { - "key": "", - "personalAccessToken": "", - "url": "", - } - } - definitionStatus={Object {}} - definitions={ - Array [ - Object { - "key": "foo", - "personalAccessToken": "foobar", - }, - ] - } - form={[Function]} - help={ - <FormattedMessage - defaultMessage="settings.almintegration.gitlab.info" - id="settings.almintegration.gitlab.info" - values={ - Object { - "link": <Link - onlyActiveOnIndex={false} - style={Object {}} - target="_blank" - to="/documentation/analysis/gitlab-integration/" - > - learn_more - </Link>, - } - } - /> - } - loadingAlmDefinitions={false} - loadingProjectCount={false} - multipleAlmEnabled={true} - onCheck={[MockFunction]} - onDelete={[MockFunction]} - onUpdateDefinitions={[MockFunction]} - updateConfiguration={[Function]} - /> - <div - className="huge-spacer-top huge-spacer-bottom bordered-top" - /> - <div - className="big-padded" - > - <Connect(SubCategoryDefinitionsList) - category="almintegration" - subCategory="gitlab" - /> - </div> -</div> -`; - -exports[`should render correctly: with no definitions 1`] = ` -<div - className="bordered" -> - <AlmTab - alm="gitlab" - branchesEnabled={true} - createConfiguration={[Function]} - defaultBinding={ - Object { - "key": "", - "personalAccessToken": "", - "url": "", - } - } - definitionStatus={Object {}} - definitions={Array []} - form={[Function]} - help={ - <FormattedMessage - defaultMessage="settings.almintegration.gitlab.info" - id="settings.almintegration.gitlab.info" - values={ - Object { - "link": <Link - onlyActiveOnIndex={false} - style={Object {}} - target="_blank" - to="/documentation/analysis/gitlab-integration/" - > - learn_more - </Link>, - } - } - /> - } - loadingAlmDefinitions={false} - loadingProjectCount={false} - multipleAlmEnabled={true} - onCheck={[MockFunction]} - onDelete={[MockFunction]} - onUpdateDefinitions={[MockFunction]} - updateConfiguration={[Function]} - /> - <div - className="huge-spacer-top huge-spacer-bottom bordered-top" - /> - <div - className="big-padded" - > - <Connect(SubCategoryDefinitionsList) - category="almintegration" - subCategory="gitlab" - /> - </div> -</div> -`; diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/utils.ts b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/utils.ts deleted file mode 100644 index 5e118230c4b..00000000000 --- a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/utils.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2021 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 { AlmKeys } from '../../../../types/alm-settings'; - -export const ALM_KEY_LIST = [ - AlmKeys.Azure, - AlmKeys.BitbucketServer, - AlmKeys.BitbucketCloud, - AlmKeys.GitHub, - AlmKeys.GitLab -]; diff --git a/server/sonar-web/src/main/js/helpers/mocks/alm-settings.ts b/server/sonar-web/src/main/js/helpers/mocks/alm-settings.ts index bd7d454f3a2..4325859aee7 100644 --- a/server/sonar-web/src/main/js/helpers/mocks/alm-settings.ts +++ b/server/sonar-web/src/main/js/helpers/mocks/alm-settings.ts @@ -23,8 +23,8 @@ import { AlmSettingsBindingStatusType, AlmSettingsInstance, AzureBindingDefinition, - BitbucketBindingDefinition, BitbucketCloudBindingDefinition, + BitbucketServerBindingDefinition, GithubBindingDefinition, GitlabBindingDefinition, ProjectAlmBindingConfigurationErrors, @@ -67,9 +67,9 @@ export function mockAzureBindingDefinition( }; } -export function mockBitbucketBindingDefinition( - overrides: Partial<BitbucketBindingDefinition> = {} -): BitbucketBindingDefinition { +export function mockBitbucketServerBindingDefinition( + overrides: Partial<BitbucketServerBindingDefinition> = {} +): BitbucketServerBindingDefinition { return { key: 'key', personalAccessToken: 'asdf1234', diff --git a/server/sonar-web/src/main/js/types/alm-settings.ts b/server/sonar-web/src/main/js/types/alm-settings.ts index 9982b80d3a8..03254c50e42 100644 --- a/server/sonar-web/src/main/js/types/alm-settings.ts +++ b/server/sonar-web/src/main/js/types/alm-settings.ts @@ -25,28 +25,35 @@ export const enum AlmKeys { GitLab = 'gitlab' } -export interface AlmBindingDefinition { +export type AlmBindingDefinition = + | AzureBindingDefinition + | GithubBindingDefinition + | GitlabBindingDefinition + | BitbucketServerBindingDefinition + | BitbucketCloudBindingDefinition; + +export interface AlmBindingDefinitionBase { key: string; url?: string; } -export interface AzureBindingDefinition extends AlmBindingDefinition { +export interface AzureBindingDefinition extends AlmBindingDefinitionBase { personalAccessToken: string; url?: string; } -export interface BitbucketBindingDefinition extends AlmBindingDefinition { +export interface BitbucketServerBindingDefinition extends AlmBindingDefinitionBase { personalAccessToken: string; url: string; } -export interface BitbucketCloudBindingDefinition extends AlmBindingDefinition { +export interface BitbucketCloudBindingDefinition extends AlmBindingDefinitionBase { clientId: string; clientSecret: string; workspace: string; } -export interface GithubBindingDefinition extends AlmBindingDefinition { +export interface GithubBindingDefinition extends AlmBindingDefinitionBase { appId: string; clientId: string; clientSecret: string; @@ -54,7 +61,7 @@ export interface GithubBindingDefinition extends AlmBindingDefinition { url: string; } -export interface GitlabBindingDefinition extends AlmBindingDefinition { +export interface GitlabBindingDefinition extends AlmBindingDefinitionBase { personalAccessToken: string; url?: string; } @@ -134,7 +141,7 @@ export interface AlmSettingsInstance { export interface AlmSettingsBindingDefinitions { [AlmKeys.Azure]: AzureBindingDefinition[]; - [AlmKeys.BitbucketServer]: BitbucketBindingDefinition[]; + [AlmKeys.BitbucketServer]: BitbucketServerBindingDefinition[]; [AlmKeys.BitbucketCloud]: BitbucketCloudBindingDefinition[]; [AlmKeys.GitHub]: GithubBindingDefinition[]; [AlmKeys.GitLab]: GitlabBindingDefinition[]; @@ -195,25 +202,25 @@ export function isProjectAzureBindingResponse( } export function isBitbucketBindingDefinition( - binding?: AlmBindingDefinition & { url?: string } -): binding is BitbucketBindingDefinition { + binding?: AlmBindingDefinitionBase & { url?: string } +): binding is BitbucketServerBindingDefinition { return binding !== undefined && binding.url !== undefined; } export function isBitbucketCloudBindingDefinition( - binding?: AlmBindingDefinition & { clientId?: string; workspace?: string } + binding?: AlmBindingDefinitionBase & { clientId?: string; workspace?: string } ): binding is BitbucketCloudBindingDefinition { return binding !== undefined && binding.clientId !== undefined && binding.workspace !== undefined; } export function isGithubBindingDefinition( - binding?: AlmBindingDefinition & { appId?: string; url?: string } + binding?: AlmBindingDefinitionBase & { appId?: string; url?: string } ): binding is GithubBindingDefinition { return binding !== undefined && binding.appId !== undefined && binding.url !== undefined; } export function isGitLabBindingDefinition( - binding?: AlmBindingDefinition | GithubBindingDefinition | BitbucketCloudBindingDefinition + binding?: AlmBindingDefinitionBase | GithubBindingDefinition | BitbucketCloudBindingDefinition ): binding is GitlabBindingDefinition { // There's too much overlap with the others. We must not only validate that certain fields are // present, we must also validate that others are NOT present. And even so, we cannot be 100% |