diff options
author | Jeremy Davis <jeremy.davis@sonarsource.com> | 2020-10-20 17:39:33 +0200 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2020-11-04 20:05:48 +0000 |
commit | b06ff168ea25343711b696e3ceabf8cfcb97d094 (patch) | |
tree | fa551fe070a546de159b9638b60e108c86a3b4a7 /server/sonar-web | |
parent | e083e2e0ff54828b484cf3534330cf8a186e1f24 (diff) | |
download | sonarqube-b06ff168ea25343711b696e3ceabf8cfcb97d094.tar.gz sonarqube-b06ff168ea25343711b696e3ceabf8cfcb97d094.zip |
SONAR-13988 Display validation status for GitHub integration
Diffstat (limited to 'server/sonar-web')
31 files changed, 1169 insertions, 159 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 56691948c8c..aa193c73b05 100644 --- a/server/sonar-web/src/main/js/api/alm-settings.ts +++ b/server/sonar-web/src/main/js/api/alm-settings.ts @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { getJSON, post } from 'sonar-ui-common/helpers/request'; +import { get, getJSON, HttpStatus, parseError, post } from 'sonar-ui-common/helpers/request'; import throwGlobalError from '../app/utils/throwGlobalError'; import { AlmSettingsBindingDefinitions, @@ -47,6 +47,20 @@ export function getAlmSettings(project?: string): Promise<AlmSettingsInstance[]> .catch(throwGlobalError); } +export function validateAlmSettings(key: string): Promise<string> { + return get('/api/alm_settings/validate', { key }) + .then(() => { + return ''; + }) + .catch((response: Response) => { + if (response.status === HttpStatus.BadRequest) { + return parseError(response); + } else { + return throwGlobalError(response); + } + }); +} + export function createGithubConfiguration(data: GithubBindingDefinition) { return post('/api/alm_settings/create_github', 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 new file mode 100644 index 00000000000..e335ab9ebbe --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmBindingDefinitionBox.tsx @@ -0,0 +1,123 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import * as React from 'react'; +import { Button } from 'sonar-ui-common/components/controls/buttons'; +import HelpTooltip from 'sonar-ui-common/components/controls/HelpTooltip'; +import AlertErrorIcon from 'sonar-ui-common/components/icons/AlertErrorIcon'; +import AlertSuccessIcon from 'sonar-ui-common/components/icons/AlertSuccessIcon'; +import DeleteIcon from 'sonar-ui-common/components/icons/DeleteIcon'; +import EditIcon from 'sonar-ui-common/components/icons/EditIcon'; +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, AlmSettingsBindingStatus } from '../../../../types/alm-settings'; + +export interface AlmBindingDefinitionBoxProps { + definition: AlmBindingDefinition; + multipleDefinitions: boolean; + onCheck: (definitionKey: string) => void; + onDelete: (definitionKey: string) => void; + onEdit: (definitionKey: string) => void; + status?: AlmSettingsBindingStatus; +} + +const DEFAULT_STATUS = { alert: false, errorMessage: '', validating: true }; + +function getImportFeatureStatus(multipleDefinitions: boolean, error: boolean) { + if (multipleDefinitions) { + return ( + <div className="display-inline-flex-center"> + <strong className="spacer-left"> + {translate('settings.almintegration.feature.alm_repo_import.disabled')} + </strong> + <HelpTooltip + className="little-spacer-left" + overlay={translate('settings.almintegration.feature.alm_repo_import.help')} + /> + </div> + ); + } else { + return error ? ( + <AlertErrorIcon className="spacer-left" /> + ) : ( + <AlertSuccessIcon className="spacer-left" /> + ); + } +} + +export default function AlmBindingDefinitionBox(props: AlmBindingDefinitionBoxProps) { + const { definition, multipleDefinitions, status = DEFAULT_STATUS } = props; + + return ( + <div className="boxed-group-inner bordered spacer-top spacer-bottom it__alm-binding-definition"> + <div className="actions pull-right"> + <Button onClick={() => props.onEdit(definition.key)}> + <EditIcon className="spacer-right" /> + {translate('edit')} + </Button> + <Button className="button-red spacer-left" onClick={() => props.onDelete(definition.key)}> + <DeleteIcon className="spacer-right" /> + {translate('delete')} + </Button> + </div> + + <div className="big-spacer-bottom"> + <h3>{definition.key}</h3> + {definition.url && <span>{definition.url}</span>} + </div> + + <DeferredSpinner + customSpinner={ + <div> + <i className="deferred-spinner spacer-right" /> + {translate('settings.almintegration.checking_configuration')} + </div> + } + loading={status.validating}> + <div className="display-flex-row spacer-bottom"> + <div className="huge-spacer-right"> + {translate('settings.almintegration.feature.pr_decoration.title')} + {status.errorMessage ? ( + <AlertErrorIcon className="spacer-left" /> + ) : ( + <AlertSuccessIcon className="spacer-left" /> + )} + </div> + <div> + {translate('settings.almintegration.feature.alm_repo_import.title')} + {getImportFeatureStatus(multipleDefinitions, Boolean(status.errorMessage))} + </div> + </div> + + {status.alert && ( + <div className="width-50"> + <Alert variant={status.errorMessage ? 'error' : 'success'}> + {status.errorMessage || translate('settings.almintegration.configuration_valid')} + </Alert> + </div> + )} + + <Button className="big-spacer-top" onClick={() => props.onCheck(definition.key)}> + {translate('settings.almintegration.check_configuration')} + </Button> + </DeferredSpinner> + </div> + ); +} 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 247abe17d20..a985b48f733 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 @@ -21,11 +21,18 @@ import * as React from 'react'; import { countBindedProjects, deleteConfiguration, - getAlmDefinitions + getAlmDefinitions, + validateAlmSettings } from '../../../../api/alm-settings'; import { withAppState } from '../../../../components/hoc/withAppState'; -import { AlmKeys, AlmSettingsBindingDefinitions } from '../../../../types/alm-settings'; +import { + AlmBindingDefinition, + AlmKeys, + AlmSettingsBindingDefinitions, + AlmSettingsBindingStatus +} from '../../../../types/alm-settings'; import AlmIntegrationRenderer from './AlmIntegrationRenderer'; +import { VALIDATED_ALMS } from './utils'; interface Props { appState: Pick<T.AppState, 'branchesEnabled' | 'multipleAlmEnabled'>; @@ -36,6 +43,7 @@ interface State { currentAlm: AlmKeys; definitionKeyForDeletion?: string; definitions: AlmSettingsBindingDefinitions; + definitionStatus: T.Dict<AlmSettingsBindingStatus>; loadingAlmDefinitions: boolean; loadingProjectCount: boolean; projectCount?: number; @@ -51,13 +59,23 @@ export class AlmIntegration extends React.PureComponent<Props, State> { [AlmKeys.GitHub]: [], [AlmKeys.GitLab]: [] }, + definitionStatus: {}, loadingAlmDefinitions: true, loadingProjectCount: false }; componentDidMount() { this.mounted = true; - this.fetchPullRequestDecorationSetting(); + return this.fetchPullRequestDecorationSetting().then(definitions => { + if (definitions) { + // Validate all alms on load: + VALIDATED_ALMS.forEach(alm => { + this.state.definitions[alm].forEach((def: AlmBindingDefinition) => + this.handleCheck(def.key, false) + ); + }); + } + }); } componentWillUnmount() { @@ -91,7 +109,9 @@ export class AlmIntegration extends React.PureComponent<Props, State> { definitions, loadingAlmDefinitions: false }); + return definitions; } + return undefined; }) .catch(() => { if (this.mounted) { @@ -127,6 +147,31 @@ export class AlmIntegration extends React.PureComponent<Props, State> { }); }; + handleCheck = async (definitionKey: string, alertSuccess = true) => { + this.setState(({ definitionStatus }) => { + definitionStatus[definitionKey] = { + ...definitionStatus[definitionKey], + validating: true + }; + + return { definitionStatus: { ...definitionStatus } }; + }); + + const errorMessage = await validateAlmSettings(definitionKey).catch(() => undefined); + + if (this.mounted && errorMessage !== undefined) { + this.setState(({ definitionStatus }) => { + definitionStatus[definitionKey] = { + alert: alertSuccess || Boolean(errorMessage), + errorMessage, + validating: false + }; + + return { definitionStatus: { ...definitionStatus } }; + }); + } + }; + render() { const { appState: { branchesEnabled, multipleAlmEnabled }, @@ -139,6 +184,7 @@ export class AlmIntegration extends React.PureComponent<Props, State> { multipleAlmEnabled={Boolean(multipleAlmEnabled)} onCancel={this.handleCancel} onConfirmDelete={this.deleteConfiguration} + onCheck={this.handleCheck} onDelete={this.handleDelete} onSelectAlm={this.handleSelectAlm} onUpdateDefinitions={this.fetchPullRequestDecorationSetting} 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 16e8f95b1e5..5d4c9ce2e66 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 @@ -21,7 +21,11 @@ import * as React from 'react'; import BoxedTabs from 'sonar-ui-common/components/controls/BoxedTabs'; import { translate } from 'sonar-ui-common/helpers/l10n'; import { getBaseUrl } from 'sonar-ui-common/helpers/urls'; -import { AlmKeys, AlmSettingsBindingDefinitions } from '../../../../types/alm-settings'; +import { + AlmKeys, + AlmSettingsBindingDefinitions, + AlmSettingsBindingStatus +} from '../../../../types/alm-settings'; import AzureTab from './AzureTab'; import BitbucketTab from './BitbucketTab'; import DeleteModal from './DeleteModal'; @@ -34,10 +38,12 @@ export interface AlmIntegrationRendererProps { currentAlm: AlmKeys; definitionKeyForDeletion?: string; definitions: AlmSettingsBindingDefinitions; + definitionStatus: T.Dict<AlmSettingsBindingStatus>; loadingAlmDefinitions: boolean; loadingProjectCount: boolean; multipleAlmEnabled: boolean; onCancel: () => void; + onCheck: (definitionKey: string) => void; onConfirmDelete: (definitionKey: string) => void; onDelete: (definitionKey: string) => void; onSelectAlm: (alm: AlmKeys) => void; @@ -111,6 +117,7 @@ export default function AlmIntegrationRenderer(props: AlmIntegrationRendererProp component, definitionKeyForDeletion, definitions, + definitionStatus, currentAlm, loadingAlmDefinitions, loadingProjectCount, @@ -137,9 +144,11 @@ export default function AlmIntegrationRenderer(props: AlmIntegrationRendererProp {currentAlm === AlmKeys.Azure && ( <AzureTab definitions={definitions.azure} + definitionStatus={definitionStatus} loadingAlmDefinitions={loadingAlmDefinitions} loadingProjectCount={loadingProjectCount} multipleAlmEnabled={multipleAlmEnabled} + onCheck={props.onCheck} onDelete={props.onDelete} onUpdateDefinitions={props.onUpdateDefinitions} /> @@ -147,9 +156,11 @@ export default function AlmIntegrationRenderer(props: AlmIntegrationRendererProp {currentAlm === AlmKeys.Bitbucket && ( <BitbucketTab definitions={definitions.bitbucket} + definitionStatus={definitionStatus} loadingAlmDefinitions={loadingAlmDefinitions} loadingProjectCount={loadingProjectCount} multipleAlmEnabled={multipleAlmEnabled} + onCheck={props.onCheck} onDelete={props.onDelete} onUpdateDefinitions={props.onUpdateDefinitions} /> @@ -159,9 +170,11 @@ export default function AlmIntegrationRenderer(props: AlmIntegrationRendererProp branchesEnabled={branchesEnabled} component={component} definitions={definitions.github} + definitionStatus={definitionStatus} loadingAlmDefinitions={loadingAlmDefinitions} loadingProjectCount={loadingProjectCount} multipleAlmEnabled={multipleAlmEnabled} + onCheck={props.onCheck} onDelete={props.onDelete} onUpdateDefinitions={props.onUpdateDefinitions} /> @@ -170,9 +183,11 @@ export default function AlmIntegrationRenderer(props: AlmIntegrationRendererProp <GitlabTab branchesEnabled={branchesEnabled} definitions={definitions.gitlab} + definitionStatus={definitionStatus} loadingAlmDefinitions={loadingAlmDefinitions} loadingProjectCount={loadingProjectCount} multipleAlmEnabled={multipleAlmEnabled} + onCheck={props.onCheck} onDelete={props.onDelete} onUpdateDefinitions={props.onUpdateDefinitions} /> 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 ff6783180ab..d603bd64027 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 @@ -18,7 +18,11 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import * as React from 'react'; -import { AlmBindingDefinition, AlmKeys } from '../../../../types/alm-settings'; +import { + AlmBindingDefinition, + AlmKeys, + AlmSettingsBindingStatus +} from '../../../../types/alm-settings'; import { AlmBindingDefinitionFormChildrenProps } from './AlmBindingDefinitionForm'; import { AlmIntegrationFeatureBoxProps } from './AlmIntegrationFeatureBox'; import AlmTabRenderer from './AlmTabRenderer'; @@ -31,12 +35,14 @@ interface Props<B> { createConfiguration: (data: B) => Promise<void>; defaultBinding: B; definitions: B[]; + definitionStatus: T.Dict<AlmSettingsBindingStatus>; features?: AlmIntegrationFeatureBoxProps[]; 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>; @@ -85,7 +91,7 @@ export default class AlmTab<B extends AlmBindingDefinition> extends React.PureCo ? this.props.updateConfiguration({ newKey: config.key, ...config, key: originalKey }) : // If there's no support for multi-ALM binding, the key will be an empty string. // Set a default. - this.props.createConfiguration(config.key ? config : { ...config, key: this.props.alm }); + this.props.createConfiguration({ ...config, key: config.key || this.props.alm }); this.setState({ submitting: true }); return call @@ -95,6 +101,7 @@ export default class AlmTab<B extends AlmBindingDefinition> extends React.PureCo } }) .then(this.props.onUpdateDefinitions) + .then(() => this.props.onCheck(config.key)) .catch(() => { if (this.mounted) { this.setState({ submitting: false, success: false }); @@ -110,6 +117,7 @@ export default class AlmTab<B extends AlmBindingDefinition> extends React.PureCo alm, defaultBinding, definitions, + definitionStatus, features, form, help, @@ -128,6 +136,7 @@ export default class AlmTab<B extends AlmBindingDefinition> extends React.PureCo alm={alm} defaultBinding={defaultBinding} definitions={definitions} + definitionStatus={definitionStatus} editedDefinition={editedDefinition} features={features} form={form} @@ -136,6 +145,7 @@ export default class AlmTab<B extends AlmBindingDefinition> extends React.PureCo loadingProjectCount={loadingProjectCount} multipleAlmEnabled={multipleAlmEnabled} onCancel={this.handleCancel} + onCheck={this.props.onCheck} onCreate={this.handleCreate} onDelete={this.props.onDelete} onEdit={this.handleEdit} 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 7e946452002..2ed43e96d57 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 @@ -20,9 +20,15 @@ import * as React from 'react'; import { FormattedMessage } from 'react-intl'; import { Link } from 'react-router'; +import { Button } from 'sonar-ui-common/components/controls/buttons'; import DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner'; import { translate } from 'sonar-ui-common/helpers/l10n'; -import { AlmBindingDefinition, AlmKeys } from '../../../../types/alm-settings'; +import { + AlmBindingDefinition, + AlmKeys, + AlmSettingsBindingStatus +} from '../../../../types/alm-settings'; +import AlmBindingDefinitionBox from './AlmBindingDefinitionBox'; import AlmBindingDefinitionForm, { AlmBindingDefinitionFormChildrenProps } from './AlmBindingDefinitionForm'; @@ -30,12 +36,14 @@ import AlmBindingDefinitionsTable from './AlmBindingDefinitionsTable'; import AlmIntegrationFeatureBox, { AlmIntegrationFeatureBoxProps } from './AlmIntegrationFeatureBox'; +import { VALIDATED_ALMS } from './utils'; export interface AlmTabRendererProps<B> { additionalColumnsHeaders: string[]; additionalColumnsKeys: Array<keyof B>; additionalTableInfo?: React.ReactNode; alm: AlmKeys; + definitionStatus: T.Dict<AlmSettingsBindingStatus>; editedDefinition?: B; defaultBinding: B; definitions: B[]; @@ -46,6 +54,7 @@ export interface AlmTabRendererProps<B> { loadingProjectCount: boolean; multipleAlmEnabled: boolean; onCancel: () => void; + onCheck: (definitionKey: string) => void; onCreate: () => void; onDelete: (definitionKey: string) => void; onEdit: (definitionKey: string) => void; @@ -55,14 +64,70 @@ export interface AlmTabRendererProps<B> { success: boolean; } -export default function AlmTabRenderer<B extends AlmBindingDefinition>( - props: AlmTabRendererProps<B> -) { +function renderListOfDefinitions<B extends AlmBindingDefinition>(props: AlmTabRendererProps<B>) { const { additionalColumnsHeaders, additionalColumnsKeys, additionalTableInfo, alm, + definitions, + definitionStatus, + loadingProjectCount + } = props; + + if (VALIDATED_ALMS.includes(alm)) { + return ( + <> + <div className="spacer-bottom text-right"> + <Button + data-test="settings__alm-create" + disabled={loadingProjectCount} + onClick={props.onCreate}> + {translate('settings.almintegration.table.create')} + </Button> + </div> + {definitions.map(def => ( + <AlmBindingDefinitionBox + definition={def} + key={def.key} + multipleDefinitions={definitions.length > 1} + onCheck={props.onCheck} + onDelete={props.onDelete} + onEdit={props.onEdit} + status={definitionStatus[def.key]} + /> + ))} + </> + ); + } + + const mappedDefinitions = definitions.map(({ key, ...properties }) => { + const additionalColumns = additionalColumnsKeys.map(k => (properties as any)[k]); + return { + key, + additionalColumns + }; + }); + + return ( + <AlmBindingDefinitionsTable + additionalColumnsHeaders={additionalColumnsHeaders} + additionalTableInfo={additionalTableInfo} + alm={alm} + definitions={mappedDefinitions} + loading={loadingProjectCount} + onCreate={props.onCreate} + onDelete={props.onDelete} + onEdit={props.onEdit} + /> + ); +} + +export default function AlmTabRenderer<B extends AlmBindingDefinition>( + props: AlmTabRendererProps<B> +) { + const { + alm, defaultBinding, definitions, editedDefinition, @@ -90,7 +155,6 @@ export default function AlmTabRenderer<B extends AlmBindingDefinition>( } = props; let definition: B | undefined; - let mappedDefinitions: Array<{ key: string; additionalColumns: string[] }> = []; let showEdit: boolean | undefined; if (!multipleAlmEnabled) { @@ -99,30 +163,13 @@ export default function AlmTabRenderer<B extends AlmBindingDefinition>( definition = definitions[0]; } showEdit = definition && editedDefinition === undefined; - } else { - mappedDefinitions = definitions.map(({ key, ...properties }) => { - const additionalColumns = additionalColumnsKeys.map(k => (properties as any)[k]); - return { - key, - additionalColumns - }; - }); } return ( <div className="big-padded"> {multipleAlmEnabled ? ( <DeferredSpinner loading={loadingAlmDefinitions}> - <AlmBindingDefinitionsTable - additionalColumnsHeaders={additionalColumnsHeaders} - additionalTableInfo={additionalTableInfo} - alm={alm} - definitions={mappedDefinitions} - loading={loadingProjectCount} - onCreate={props.onCreate} - onDelete={props.onDelete} - onEdit={props.onEdit} - /> + {renderListOfDefinitions(props)} {editedDefinition && ( <AlmBindingDefinitionForm @@ -153,7 +200,7 @@ export default function AlmTabRenderer<B extends AlmBindingDefinition>( </AlmBindingDefinitionForm> )} - {features.length > 0 && ( + {!VALIDATED_ALMS.includes(alm) && features.length > 0 && ( <div className="big-spacer-top big-padded-top bordered-top"> <h3 className="big-spacer-bottom">{translate('settings.almintegration.features')}</h3> 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 index b2d211ba787..f727d695c2d 100644 --- 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 @@ -20,21 +20,33 @@ import * as React from 'react'; import { translate } from 'sonar-ui-common/helpers/l10n'; import { createAzureConfiguration, updateAzureConfiguration } from '../../../../api/alm-settings'; -import { AlmKeys, AzureBindingDefinition } from '../../../../types/alm-settings'; +import { + AlmKeys, + AlmSettingsBindingStatus, + AzureBindingDefinition +} from '../../../../types/alm-settings'; import AlmTab from './AlmTab'; import AzureForm from './AzureForm'; export interface AzureTabProps { 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 { multipleAlmEnabled, definitions, loadingAlmDefinitions, loadingProjectCount } = props; + const { + multipleAlmEnabled, + definitions, + definitionStatus, + loadingAlmDefinitions, + loadingProjectCount + } = props; return ( <div className="bordered"> @@ -43,6 +55,7 @@ export default function AzureTab(props: AzureTabProps) { createConfiguration={createAzureConfiguration} defaultBinding={{ key: '', personalAccessToken: '' }} definitions={definitions} + definitionStatus={definitionStatus} features={[ { name: translate('settings.almintegration.feature.pr_decoration.title'), @@ -55,6 +68,7 @@ export default function AzureTab(props: AzureTabProps) { loadingAlmDefinitions={loadingAlmDefinitions} loadingProjectCount={loadingProjectCount} multipleAlmEnabled={multipleAlmEnabled} + onCheck={props.onCheck} onDelete={props.onDelete} onUpdateDefinitions={props.onUpdateDefinitions} updateConfiguration={updateAzureConfiguration} 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 index 05010d67d83..4fc261cfb92 100644 --- 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 @@ -26,21 +26,33 @@ import { createBitbucketConfiguration, updateBitbucketConfiguration } from '../../../../api/alm-settings'; -import { AlmKeys, BitbucketBindingDefinition } from '../../../../types/alm-settings'; +import { + AlmKeys, + AlmSettingsBindingStatus, + BitbucketBindingDefinition +} from '../../../../types/alm-settings'; import AlmTab from './AlmTab'; import BitbucketForm from './BitbucketForm'; export interface BitbucketTabProps { definitions: BitbucketBindingDefinition[]; + definitionStatus: T.Dict<AlmSettingsBindingStatus>; loadingAlmDefinitions: boolean; loadingProjectCount: boolean; multipleAlmEnabled: boolean; + onCheck: (definitionKey: string) => void; onDelete: (definitionKey: string) => void; onUpdateDefinitions: () => void; } export default function BitbucketTab(props: BitbucketTabProps) { - const { multipleAlmEnabled, definitions, loadingAlmDefinitions, loadingProjectCount } = props; + const { + multipleAlmEnabled, + definitions, + definitionStatus, + loadingAlmDefinitions, + loadingProjectCount + } = props; return ( <div className="bordered"> @@ -66,6 +78,7 @@ export default function BitbucketTab(props: BitbucketTabProps) { createConfiguration={createBitbucketConfiguration} defaultBinding={{ key: '', url: '', personalAccessToken: '' }} definitions={definitions} + definitionStatus={definitionStatus} features={[ { name: translate('settings.almintegration.feature.pr_decoration.title'), @@ -107,6 +120,7 @@ export default function BitbucketTab(props: BitbucketTabProps) { loadingAlmDefinitions={loadingAlmDefinitions} loadingProjectCount={loadingProjectCount} multipleAlmEnabled={multipleAlmEnabled} + onCheck={props.onCheck} onDelete={props.onDelete} onUpdateDefinitions={props.onUpdateDefinitions} updateConfiguration={updateBitbucketConfiguration} 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 index 82f4298b289..a44d2e8813e 100644 --- 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 @@ -23,7 +23,11 @@ import WarningIcon from 'sonar-ui-common/components/icons/WarningIcon'; import { Alert } from 'sonar-ui-common/components/ui/Alert'; import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n'; import { createGithubConfiguration, updateGithubConfiguration } from '../../../../api/alm-settings'; -import { AlmKeys, GithubBindingDefinition } from '../../../../types/alm-settings'; +import { + AlmKeys, + AlmSettingsBindingStatus, + GithubBindingDefinition +} from '../../../../types/alm-settings'; import { ALM_INTEGRATION } from '../AdditionalCategoryKeys'; import CategoryDefinitionsList from '../CategoryDefinitionsList'; import AlmTab from './AlmTab'; @@ -33,9 +37,11 @@ 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; } @@ -46,6 +52,7 @@ export default function GithubTab(props: GithubTabProps) { component, multipleAlmEnabled, definitions, + definitionStatus, loadingAlmDefinitions, loadingProjectCount } = props; @@ -86,6 +93,7 @@ export default function GithubTab(props: GithubTabProps) { privateKey: '' }} definitions={definitions} + definitionStatus={definitionStatus} features={[ { name: translate('settings.almintegration.feature.pr_decoration.title'), @@ -129,6 +137,7 @@ export default function GithubTab(props: GithubTabProps) { loadingAlmDefinitions={loadingAlmDefinitions} loadingProjectCount={loadingProjectCount} multipleAlmEnabled={multipleAlmEnabled} + onCheck={props.onCheck} onDelete={props.onDelete} onUpdateDefinitions={props.onUpdateDefinitions} updateConfiguration={updateGithubConfiguration} 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 index a50d7809bde..db7558480d0 100644 --- 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 @@ -22,7 +22,11 @@ import { FormattedMessage } from 'react-intl'; import { Alert } from 'sonar-ui-common/components/ui/Alert'; import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n'; import { createGitlabConfiguration, updateGitlabConfiguration } from '../../../../api/alm-settings'; -import { AlmKeys, GitlabBindingDefinition } from '../../../../types/alm-settings'; +import { + AlmKeys, + AlmSettingsBindingStatus, + GitlabBindingDefinition +} from '../../../../types/alm-settings'; import { ALM_INTEGRATION } from '../AdditionalCategoryKeys'; import CategoryDefinitionsList from '../CategoryDefinitionsList'; import AlmTab from './AlmTab'; @@ -32,9 +36,11 @@ 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; } @@ -45,6 +51,7 @@ export default function GitlabTab(props: GitlabTabProps) { component, multipleAlmEnabled, definitions, + definitionStatus, loadingAlmDefinitions, loadingProjectCount } = props; @@ -79,6 +86,7 @@ export default function GitlabTab(props: GitlabTabProps) { createConfiguration={createGitlabConfiguration} defaultBinding={{ key: '', personalAccessToken: '', url: '' }} definitions={definitions} + definitionStatus={definitionStatus} features={[ { name: translate('settings.almintegration.feature.mr_decoration.title'), @@ -107,6 +115,7 @@ export default function GitlabTab(props: GitlabTabProps) { loadingAlmDefinitions={loadingAlmDefinitions} loadingProjectCount={loadingProjectCount} multipleAlmEnabled={multipleAlmEnabled} + onCheck={props.onCheck} onDelete={props.onDelete} onUpdateDefinitions={props.onUpdateDefinitions} updateConfiguration={updateGitlabConfiguration} diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmBindingDefinitionBox-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmBindingDefinitionBox-test.tsx new file mode 100644 index 00000000000..d8c9cbb93d4 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmBindingDefinitionBox-test.tsx @@ -0,0 +1,70 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import { shallow } from 'enzyme'; +import * as React from 'react'; +import { + mockAlmSettingsBindingStatus, + mockGithubBindingDefinition +} from '../../../../../helpers/mocks/alm-settings'; +import AlmBindingDefinitionBox, { AlmBindingDefinitionBoxProps } from '../AlmBindingDefinitionBox'; + +it('should render correctly', () => { + expect(shallowRender()).toMatchSnapshot('default'); + expect(shallowRender({ multipleDefinitions: true })).toMatchSnapshot('multiple definitions'); + expect( + shallowRender({ status: mockAlmSettingsBindingStatus({ errorMessage: '', validating: false }) }) + ).toMatchSnapshot('success'); + expect( + shallowRender({ + status: mockAlmSettingsBindingStatus({ + errorMessage: 'Oops, something went wrong', + validating: false + }) + }) + ).toMatchSnapshot('error'); + + expect( + shallowRender({ + status: mockAlmSettingsBindingStatus({ alert: true, errorMessage: '', validating: false }) + }) + ).toMatchSnapshot('success with alert'); + expect( + shallowRender({ + status: mockAlmSettingsBindingStatus({ + alert: true, + errorMessage: 'Oops, something went wrong', + validating: false + }) + }) + ).toMatchSnapshot('error with alert'); +}); + +function shallowRender(props: Partial<AlmBindingDefinitionBoxProps> = {}) { + return shallow( + <AlmBindingDefinitionBox + definition={mockGithubBindingDefinition()} + multipleDefinitions={false} + onCheck={jest.fn()} + onDelete={jest.fn()} + onEdit={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 4940b4370ab..d165dbaa9af 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 @@ -23,7 +23,8 @@ import { waitAndUpdate } from 'sonar-ui-common/helpers/testUtils'; import { countBindedProjects, deleteConfiguration, - getAlmDefinitions + getAlmDefinitions, + validateAlmSettings } from '../../../../../api/alm-settings'; import { AlmKeys } from '../../../../../types/alm-settings'; import { AlmIntegration } from '../AlmIntegration'; @@ -31,7 +32,8 @@ import { AlmIntegration } from '../AlmIntegration'; jest.mock('../../../../../api/alm-settings', () => ({ countBindedProjects: jest.fn().mockResolvedValue(0), deleteConfiguration: jest.fn().mockResolvedValue(undefined), - getAlmDefinitions: jest.fn().mockResolvedValue({ github: [] }) + getAlmDefinitions: jest.fn().mockResolvedValue({ github: [] }), + validateAlmSettings: jest.fn().mockResolvedValue('') })); beforeEach(() => { @@ -42,6 +44,23 @@ it('should render correctly', () => { expect(shallowRender()).toMatchSnapshot(); }); +it('should validate existing configurations', async () => { + (getAlmDefinitions as jest.Mock).mockResolvedValueOnce({ + [AlmKeys.Azure]: [{ key: 'a1' }], + [AlmKeys.Bitbucket]: [{ key: 'b1' }], + [AlmKeys.GitHub]: [{ key: 'gh1' }, { key: 'gh2' }], + [AlmKeys.GitLab]: [{ key: 'gl1' }] + }); + + const wrapper = shallowRender(); + + await waitAndUpdate(wrapper); + + expect(validateAlmSettings).toBeCalledTimes(2); + expect(validateAlmSettings).toBeCalledWith('gh1'); + expect(validateAlmSettings).toBeCalledWith('gh2'); +}); + it('should handle alm selection', async () => { const wrapper = shallowRender(); @@ -76,17 +95,51 @@ it('should delete configuration', async () => { expect(wrapper.state().definitionKeyForDeletion).toBeUndefined(); }); +it('should validate a configuration', async () => { + const definitionKey = 'validated-key'; + const errorMessage = 'an error occured'; + + const wrapper = shallowRender(); + await waitAndUpdate(wrapper); + + (validateAlmSettings as jest.Mock) + .mockResolvedValueOnce(undefined) + .mockResolvedValueOnce(errorMessage) + .mockResolvedValueOnce(''); + + await wrapper.instance().handleCheck(definitionKey); + + expect(wrapper.state().definitionStatus[definitionKey]).toEqual( + expect.objectContaining({ + validating: true + }) + ); + + await wrapper.instance().handleCheck(definitionKey); + + expect(wrapper.state().definitionStatus[definitionKey]).toEqual({ + alert: true, + errorMessage, + validating: false + }); + + await wrapper.instance().handleCheck(definitionKey); + + expect(wrapper.state().definitionStatus[definitionKey]).toEqual({ + alert: true, + errorMessage: '', + validating: false + }); +}); + it('should fetch settings', async () => { const wrapper = shallowRender(); - await wrapper - .instance() - .fetchPullRequestDecorationSetting() - .then(() => { - expect(getAlmDefinitions).toBeCalled(); - expect(wrapper.state().definitions).toEqual({ github: [] }); - expect(wrapper.state().loadingAlmDefinitions).toBe(false); - }); + await wrapper.instance().fetchPullRequestDecorationSetting(); + + expect(getAlmDefinitions).toBeCalled(); + expect(wrapper.state().definitions).toEqual({ github: [] }); + expect(wrapper.state().loadingAlmDefinitions).toBe(false); }); function shallowRender() { 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 c23e364d9c4..fff40776838 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 @@ -41,10 +41,12 @@ function shallowRender(props: Partial<AlmIntegrationRendererProps> = {}) { branchesEnabled={true} currentAlm={AlmKeys.GitHub} definitions={{ azure: [], bitbucket: [], github: [], gitlab: [] }} + definitionStatus={{}} loadingAlmDefinitions={false} loadingProjectCount={false} multipleAlmEnabled={false} onCancel={jest.fn()} + onCheck={jest.fn()} onConfirmDelete={jest.fn()} onDelete={jest.fn()} onSelectAlm={jest.fn()} 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 2b1a6c45835..acdc39b415a 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 @@ -26,7 +26,8 @@ import AlmTab from '../AlmTab'; const DEFAULT_BINDING = { key: '', - personalAccessToken: '' + personalAccessToken: '', + url: undefined }; it('should render correctly', () => { @@ -97,10 +98,12 @@ function shallowRender(props: Partial<AlmTab<AzureBindingDefinition>['props']> = createConfiguration={jest.fn()} defaultBinding={DEFAULT_BINDING} definitions={[mockAzureBindingDefinition()]} + definitionStatus={{}} form={jest.fn()} loadingAlmDefinitions={false} loadingProjectCount={false} multipleAlmEnabled={true} + onCheck={jest.fn()} onDelete={jest.fn()} onUpdateDefinitions={jest.fn()} updateConfiguration={jest.fn()} 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 63b1a2b0258..6f352ed8489 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 @@ -19,20 +19,31 @@ */ import { shallow } from 'enzyme'; import * as React from 'react'; -import { mockGithubBindingDefinition } from '../../../../../helpers/mocks/alm-settings'; -import { AlmKeys, GithubBindingDefinition } from '../../../../../types/alm-settings'; +import { + mockAzureBindingDefinition, + mockGithubBindingDefinition +} from '../../../../../helpers/mocks/alm-settings'; +import { + AlmBindingDefinition, + AlmKeys, + AzureBindingDefinition +} from '../../../../../types/alm-settings'; import AlmTabRenderer, { AlmTabRendererProps } from '../AlmTabRenderer'; it('should render correctly for multi-ALM binding', () => { - expect(shallowRender({ loadingAlmDefinitions: true })).toMatchSnapshot('loading ALM definitions'); - expect(shallowRender({ loadingProjectCount: true })).toMatchSnapshot('loading project count'); - expect(shallowRender({ submitting: true })).toMatchSnapshot('submitting'); - expect(shallowRender()).toMatchSnapshot('loaded'); - expect(shallowRender({ editedDefinition: mockGithubBindingDefinition() })).toMatchSnapshot( + expect(shallowRenderAzure({ loadingAlmDefinitions: true })).toMatchSnapshot( + 'loading ALM definitions' + ); + expect(shallowRenderAzure({ loadingProjectCount: true })).toMatchSnapshot( + 'loading project count' + ); + expect(shallowRenderAzure({ submitting: true })).toMatchSnapshot('submitting'); + expect(shallowRenderAzure()).toMatchSnapshot('loaded'); + expect(shallowRenderAzure({ editedDefinition: mockAzureBindingDefinition() })).toMatchSnapshot( 'editing a definition' ); expect( - shallowRender({ + shallowRenderAzure({ features: [ { active: true, @@ -51,27 +62,49 @@ it('should render correctly for multi-ALM binding', () => { it('should render correctly for single-ALM binding', () => { expect( - shallowRender({ loadingAlmDefinitions: true, multipleAlmEnabled: false }) + shallowRenderAzure({ loadingAlmDefinitions: true, multipleAlmEnabled: false }) ).toMatchSnapshot(); - expect(shallowRender({ multipleAlmEnabled: false })).toMatchSnapshot(); + expect(shallowRenderAzure({ multipleAlmEnabled: false })).toMatchSnapshot(); expect( - shallowRender({ definitions: [mockGithubBindingDefinition()], multipleAlmEnabled: false }) + shallowRenderAzure({ definitions: [mockAzureBindingDefinition()], multipleAlmEnabled: false }) ).toMatchSnapshot(); }); -function shallowRender(props: Partial<AlmTabRendererProps<GithubBindingDefinition>> = {}) { +it('should render correctly with validation', () => { + const githubProps = { + alm: AlmKeys.GitHub, + defaultBinding: mockGithubBindingDefinition(), + definitions: [mockGithubBindingDefinition()] + }; + expect(shallowRender(githubProps)).toMatchSnapshot(); + expect(shallowRender({ ...githubProps, definitions: [] })).toMatchSnapshot('empty'); +}); + +function shallowRenderAzure(props: Partial<AlmTabRendererProps<AzureBindingDefinition>> = {}) { + return shallowRender({ + defaultBinding: mockAzureBindingDefinition(), + definitions: [mockAzureBindingDefinition()], + ...props + }); +} + +function shallowRender<B extends AlmBindingDefinition>( + props: Partial<AlmTabRendererProps<B>> = {} +) { return shallow( <AlmTabRenderer - additionalColumnsHeaders={['url', 'app_id']} - additionalColumnsKeys={['url', 'appId']} - alm={AlmKeys.GitHub} - defaultBinding={mockGithubBindingDefinition()} - definitions={[mockGithubBindingDefinition()]} + additionalColumnsHeaders={[]} + additionalColumnsKeys={[]} + alm={AlmKeys.Azure} + defaultBinding={{} as any} + definitions={[]} + definitionStatus={{}} form={jest.fn()} loadingAlmDefinitions={false} loadingProjectCount={false} multipleAlmEnabled={true} onCancel={jest.fn()} + onCheck={jest.fn()} onCreate={jest.fn()} onDelete={jest.fn()} onEdit={jest.fn()} 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__/AzureTab-test.tsx index ef7fe85ee3d..a2b0d7ce073 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__/AzureTab-test.tsx @@ -30,9 +30,11 @@ function shallowRender(props: Partial<AzureTabProps> = {}) { return shallow( <AzureTab definitions={[mockAzureBindingDefinition()]} + 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__/BitbucketTab-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/BitbucketTab-test.tsx index 6f2854c2eef..ee9acbec452 100644 --- 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 @@ -30,9 +30,11 @@ function shallowRender(props: Partial<BitbucketTabProps> = {}) { return shallow( <BitbucketTab 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__/GithubTab-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/GithubTab-test.tsx index 8aca94e7bd8..11e94059617 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__/GithubTab-test.tsx @@ -32,9 +32,11 @@ function shallowRender(props: Partial<GithubTabProps> = {}) { <GithubTab branchesEnabled={true} definitions={[mockGithubBindingDefinition()]} + 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__/GitlabTab-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/GitlabTab-test.tsx index cd3a792d8c1..f6461a33810 100644 --- 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 @@ -42,9 +42,11 @@ function shallowRender(props: Partial<GitlabTabProps> = {}) { <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__/AlmBindingDefinitionBox-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AlmBindingDefinitionBox-test.tsx.snap new file mode 100644 index 00000000000..0629f4af3c2 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AlmBindingDefinitionBox-test.tsx.snap @@ -0,0 +1,473 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly: default 1`] = ` +<div + className="boxed-group-inner bordered spacer-top spacer-bottom it__alm-binding-definition" +> + <div + className="actions pull-right" + > + <Button + onClick={[Function]} + > + <EditIcon + className="spacer-right" + /> + edit + </Button> + <Button + className="button-red spacer-left" + onClick={[Function]} + > + <DeleteIcon + className="spacer-right" + /> + delete + </Button> + </div> + <div + className="big-spacer-bottom" + > + <h3> + key + </h3> + <span> + http://github.enterprise.com + </span> + </div> + <DeferredSpinner + customSpinner={ + <div> + <i + className="deferred-spinner spacer-right" + /> + settings.almintegration.checking_configuration + </div> + } + loading={true} + > + <div + className="display-flex-row spacer-bottom" + > + <div + className="huge-spacer-right" + > + settings.almintegration.feature.pr_decoration.title + <AlertSuccessIcon + className="spacer-left" + /> + </div> + <div> + settings.almintegration.feature.alm_repo_import.title + <AlertSuccessIcon + className="spacer-left" + /> + </div> + </div> + <Button + className="big-spacer-top" + onClick={[Function]} + > + settings.almintegration.check_configuration + </Button> + </DeferredSpinner> +</div> +`; + +exports[`should render correctly: error 1`] = ` +<div + className="boxed-group-inner bordered spacer-top spacer-bottom it__alm-binding-definition" +> + <div + className="actions pull-right" + > + <Button + onClick={[Function]} + > + <EditIcon + className="spacer-right" + /> + edit + </Button> + <Button + className="button-red spacer-left" + onClick={[Function]} + > + <DeleteIcon + className="spacer-right" + /> + delete + </Button> + </div> + <div + className="big-spacer-bottom" + > + <h3> + key + </h3> + <span> + http://github.enterprise.com + </span> + </div> + <DeferredSpinner + customSpinner={ + <div> + <i + className="deferred-spinner spacer-right" + /> + settings.almintegration.checking_configuration + </div> + } + loading={false} + > + <div + className="display-flex-row spacer-bottom" + > + <div + className="huge-spacer-right" + > + settings.almintegration.feature.pr_decoration.title + <AlertErrorIcon + className="spacer-left" + /> + </div> + <div> + settings.almintegration.feature.alm_repo_import.title + <AlertErrorIcon + className="spacer-left" + /> + </div> + </div> + <Button + className="big-spacer-top" + onClick={[Function]} + > + settings.almintegration.check_configuration + </Button> + </DeferredSpinner> +</div> +`; + +exports[`should render correctly: error with alert 1`] = ` +<div + className="boxed-group-inner bordered spacer-top spacer-bottom it__alm-binding-definition" +> + <div + className="actions pull-right" + > + <Button + onClick={[Function]} + > + <EditIcon + className="spacer-right" + /> + edit + </Button> + <Button + className="button-red spacer-left" + onClick={[Function]} + > + <DeleteIcon + className="spacer-right" + /> + delete + </Button> + </div> + <div + className="big-spacer-bottom" + > + <h3> + key + </h3> + <span> + http://github.enterprise.com + </span> + </div> + <DeferredSpinner + customSpinner={ + <div> + <i + className="deferred-spinner spacer-right" + /> + settings.almintegration.checking_configuration + </div> + } + loading={false} + > + <div + className="display-flex-row spacer-bottom" + > + <div + className="huge-spacer-right" + > + settings.almintegration.feature.pr_decoration.title + <AlertErrorIcon + className="spacer-left" + /> + </div> + <div> + settings.almintegration.feature.alm_repo_import.title + <AlertErrorIcon + className="spacer-left" + /> + </div> + </div> + <div + className="width-50" + > + <Alert + variant="error" + > + Oops, something went wrong + </Alert> + </div> + <Button + className="big-spacer-top" + onClick={[Function]} + > + settings.almintegration.check_configuration + </Button> + </DeferredSpinner> +</div> +`; + +exports[`should render correctly: multiple definitions 1`] = ` +<div + className="boxed-group-inner bordered spacer-top spacer-bottom it__alm-binding-definition" +> + <div + className="actions pull-right" + > + <Button + onClick={[Function]} + > + <EditIcon + className="spacer-right" + /> + edit + </Button> + <Button + className="button-red spacer-left" + onClick={[Function]} + > + <DeleteIcon + className="spacer-right" + /> + delete + </Button> + </div> + <div + className="big-spacer-bottom" + > + <h3> + key + </h3> + <span> + http://github.enterprise.com + </span> + </div> + <DeferredSpinner + customSpinner={ + <div> + <i + className="deferred-spinner spacer-right" + /> + settings.almintegration.checking_configuration + </div> + } + loading={true} + > + <div + className="display-flex-row spacer-bottom" + > + <div + className="huge-spacer-right" + > + settings.almintegration.feature.pr_decoration.title + <AlertSuccessIcon + className="spacer-left" + /> + </div> + <div> + settings.almintegration.feature.alm_repo_import.title + <div + className="display-inline-flex-center" + > + <strong + className="spacer-left" + > + settings.almintegration.feature.alm_repo_import.disabled + </strong> + <HelpTooltip + className="little-spacer-left" + overlay="settings.almintegration.feature.alm_repo_import.help" + /> + </div> + </div> + </div> + <Button + className="big-spacer-top" + onClick={[Function]} + > + settings.almintegration.check_configuration + </Button> + </DeferredSpinner> +</div> +`; + +exports[`should render correctly: success 1`] = ` +<div + className="boxed-group-inner bordered spacer-top spacer-bottom it__alm-binding-definition" +> + <div + className="actions pull-right" + > + <Button + onClick={[Function]} + > + <EditIcon + className="spacer-right" + /> + edit + </Button> + <Button + className="button-red spacer-left" + onClick={[Function]} + > + <DeleteIcon + className="spacer-right" + /> + delete + </Button> + </div> + <div + className="big-spacer-bottom" + > + <h3> + key + </h3> + <span> + http://github.enterprise.com + </span> + </div> + <DeferredSpinner + customSpinner={ + <div> + <i + className="deferred-spinner spacer-right" + /> + settings.almintegration.checking_configuration + </div> + } + loading={false} + > + <div + className="display-flex-row spacer-bottom" + > + <div + className="huge-spacer-right" + > + settings.almintegration.feature.pr_decoration.title + <AlertSuccessIcon + className="spacer-left" + /> + </div> + <div> + settings.almintegration.feature.alm_repo_import.title + <AlertSuccessIcon + className="spacer-left" + /> + </div> + </div> + <Button + className="big-spacer-top" + onClick={[Function]} + > + settings.almintegration.check_configuration + </Button> + </DeferredSpinner> +</div> +`; + +exports[`should render correctly: success with alert 1`] = ` +<div + className="boxed-group-inner bordered spacer-top spacer-bottom it__alm-binding-definition" +> + <div + className="actions pull-right" + > + <Button + onClick={[Function]} + > + <EditIcon + className="spacer-right" + /> + edit + </Button> + <Button + className="button-red spacer-left" + onClick={[Function]} + > + <DeleteIcon + className="spacer-right" + /> + delete + </Button> + </div> + <div + className="big-spacer-bottom" + > + <h3> + key + </h3> + <span> + http://github.enterprise.com + </span> + </div> + <DeferredSpinner + customSpinner={ + <div> + <i + className="deferred-spinner spacer-right" + /> + settings.almintegration.checking_configuration + </div> + } + loading={false} + > + <div + className="display-flex-row spacer-bottom" + > + <div + className="huge-spacer-right" + > + settings.almintegration.feature.pr_decoration.title + <AlertSuccessIcon + className="spacer-left" + /> + </div> + <div> + settings.almintegration.feature.alm_repo_import.title + <AlertSuccessIcon + className="spacer-left" + /> + </div> + </div> + <div + className="width-50" + > + <Alert + variant="success" + > + settings.almintegration.configuration_valid + </Alert> + </div> + <Button + className="big-spacer-top" + onClick={[Function]} + > + settings.almintegration.check_configuration + </Button> + </DeferredSpinner> +</div> +`; 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 c3ce1fad8ad..647758269cb 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 @@ -4,6 +4,7 @@ exports[`should render correctly 1`] = ` <AlmIntegrationRenderer branchesEnabled={true} currentAlm="github" + definitionStatus={Object {}} definitions={ Object { "azure": Array [], @@ -16,6 +17,7 @@ exports[`should render correctly 1`] = ` loadingProjectCount={false} multipleAlmEnabled={false} onCancel={[Function]} + onCheck={[Function]} onConfirmDelete={[Function]} onDelete={[Function]} onSelectAlm={[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 8be88f64c3b..2a887a2e77c 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 @@ -75,10 +75,12 @@ exports[`should render correctly: azure 1`] = ` } /> <AzureTab + definitionStatus={Object {}} definitions={Array []} loadingAlmDefinitions={false} loadingProjectCount={false} multipleAlmEnabled={false} + onCheck={[MockFunction]} onDelete={[MockFunction]} onUpdateDefinitions={[MockFunction]} /> @@ -160,10 +162,12 @@ exports[`should render correctly: bitbucket 1`] = ` } /> <BitbucketTab + definitionStatus={Object {}} definitions={Array []} loadingAlmDefinitions={false} loadingProjectCount={false} multipleAlmEnabled={false} + onCheck={[MockFunction]} onDelete={[MockFunction]} onUpdateDefinitions={[MockFunction]} /> @@ -246,10 +250,12 @@ exports[`should render correctly: default 1`] = ` /> <GithubTab branchesEnabled={true} + definitionStatus={Object {}} definitions={Array []} loadingAlmDefinitions={false} loadingProjectCount={false} multipleAlmEnabled={false} + onCheck={[MockFunction]} onDelete={[MockFunction]} onUpdateDefinitions={[MockFunction]} /> @@ -332,10 +338,12 @@ exports[`should render correctly: delete modal 1`] = ` /> <GithubTab branchesEnabled={true} + definitionStatus={Object {}} definitions={Array []} loadingAlmDefinitions={false} loadingProjectCount={false} multipleAlmEnabled={false} + onCheck={[MockFunction]} onDelete={[MockFunction]} onUpdateDefinitions={[MockFunction]} /> @@ -423,10 +431,12 @@ exports[`should render correctly: gitlab 1`] = ` /> <GitlabTab branchesEnabled={true} + definitionStatus={Object {}} definitions={Array []} loadingAlmDefinitions={false} loadingProjectCount={false} multipleAlmEnabled={false} + onCheck={[MockFunction]} onDelete={[MockFunction]} onUpdateDefinitions={[MockFunction]} /> @@ -509,10 +519,12 @@ exports[`should render correctly: loading 1`] = ` /> <GithubTab branchesEnabled={true} + definitionStatus={Object {}} definitions={Array []} loadingAlmDefinitions={true} loadingProjectCount={true} multipleAlmEnabled={false} + onCheck={[MockFunction]} onDelete={[MockFunction]} onUpdateDefinitions={[MockFunction]} /> 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 e0e95f11057..68d1d8350fa 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 @@ -9,8 +9,10 @@ exports[`should render correctly 1`] = ` Object { "key": "", "personalAccessToken": "", + "url": undefined, } } + definitionStatus={Object {}} definitions={ Array [ Object { @@ -24,6 +26,7 @@ exports[`should render correctly 1`] = ` loadingProjectCount={false} multipleAlmEnabled={true} onCancel={[Function]} + onCheck={[MockFunction]} onCreate={[Function]} onDelete={[MockFunction]} onEdit={[Function]} 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 4ef84a57232..ffed975f0b5 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 @@ -8,20 +8,12 @@ exports[`should render correctly for multi-ALM binding: editing a definition 1`] loading={false} > <AlmBindingDefinitionsTable - additionalColumnsHeaders={ - Array [ - "url", - "app_id", - ] - } - alm="github" + additionalColumnsHeaders={Array []} + alm="azure" definitions={ Array [ Object { - "additionalColumns": Array [ - "http://github.enterprise.com", - "123456", - ], + "additionalColumns": Array [], "key": "key", }, ] @@ -34,18 +26,14 @@ exports[`should render correctly for multi-ALM binding: editing a definition 1`] <AlmBindingDefinitionForm bindingDefinition={ Object { - "appId": "123456", - "clientId": "client1", - "clientSecret": "**clientsecret**", "key": "key", - "privateKey": "asdf1234", - "url": "http://github.enterprise.com", + "personalAccessToken": "asdf1234", } } help={ <FormattedMessage - defaultMessage="settings.almintegration.github.info" - id="settings.almintegration.github.info" + defaultMessage="settings.almintegration.azure.info" + id="settings.almintegration.azure.info" values={ Object { "link": <Link @@ -78,20 +66,12 @@ exports[`should render correctly for multi-ALM binding: loaded 1`] = ` loading={false} > <AlmBindingDefinitionsTable - additionalColumnsHeaders={ - Array [ - "url", - "app_id", - ] - } - alm="github" + additionalColumnsHeaders={Array []} + alm="azure" definitions={ Array [ Object { - "additionalColumns": Array [ - "http://github.enterprise.com", - "123456", - ], + "additionalColumns": Array [], "key": "key", }, ] @@ -113,20 +93,12 @@ exports[`should render correctly for multi-ALM binding: loading ALM definitions loading={true} > <AlmBindingDefinitionsTable - additionalColumnsHeaders={ - Array [ - "url", - "app_id", - ] - } - alm="github" + additionalColumnsHeaders={Array []} + alm="azure" definitions={ Array [ Object { - "additionalColumns": Array [ - "http://github.enterprise.com", - "123456", - ], + "additionalColumns": Array [], "key": "key", }, ] @@ -148,20 +120,12 @@ exports[`should render correctly for multi-ALM binding: loading project count 1` loading={false} > <AlmBindingDefinitionsTable - additionalColumnsHeaders={ - Array [ - "url", - "app_id", - ] - } - alm="github" + additionalColumnsHeaders={Array []} + alm="azure" definitions={ Array [ Object { - "additionalColumns": Array [ - "http://github.enterprise.com", - "123456", - ], + "additionalColumns": Array [], "key": "key", }, ] @@ -183,20 +147,12 @@ exports[`should render correctly for multi-ALM binding: submitting 1`] = ` loading={false} > <AlmBindingDefinitionsTable - additionalColumnsHeaders={ - Array [ - "url", - "app_id", - ] - } - alm="github" + additionalColumnsHeaders={Array []} + alm="azure" definitions={ Array [ Object { - "additionalColumns": Array [ - "http://github.enterprise.com", - "123456", - ], + "additionalColumns": Array [], "key": "key", }, ] @@ -218,20 +174,12 @@ exports[`should render correctly for multi-ALM binding: with features 1`] = ` loading={false} > <AlmBindingDefinitionsTable - additionalColumnsHeaders={ - Array [ - "url", - "app_id", - ] - } - alm="github" + additionalColumnsHeaders={Array []} + alm="azure" definitions={ Array [ Object { - "additionalColumns": Array [ - "http://github.enterprise.com", - "123456", - ], + "additionalColumns": Array [], "key": "key", }, ] @@ -277,18 +225,14 @@ exports[`should render correctly for single-ALM binding 1`] = ` <AlmBindingDefinitionForm bindingDefinition={ Object { - "appId": "123456", - "clientId": "client1", - "clientSecret": "**clientsecret**", "key": "key", - "privateKey": "asdf1234", - "url": "http://github.enterprise.com", + "personalAccessToken": "asdf1234", } } help={ <FormattedMessage - defaultMessage="settings.almintegration.github.info" - id="settings.almintegration.github.info" + defaultMessage="settings.almintegration.azure.info" + id="settings.almintegration.azure.info" values={ Object { "link": <Link @@ -324,18 +268,14 @@ exports[`should render correctly for single-ALM binding 2`] = ` <AlmBindingDefinitionForm bindingDefinition={ Object { - "appId": "123456", - "clientId": "client1", - "clientSecret": "**clientsecret**", "key": "key", - "privateKey": "asdf1234", - "url": "http://github.enterprise.com", + "personalAccessToken": "asdf1234", } } help={ <FormattedMessage - defaultMessage="settings.almintegration.github.info" - id="settings.almintegration.github.info" + defaultMessage="settings.almintegration.azure.info" + id="settings.almintegration.azure.info" values={ Object { "link": <Link @@ -371,18 +311,14 @@ exports[`should render correctly for single-ALM binding 3`] = ` <AlmBindingDefinitionForm bindingDefinition={ Object { - "appId": "123456", - "clientId": "client1", - "clientSecret": "**clientsecret**", "key": "key", - "privateKey": "asdf1234", - "url": "http://github.enterprise.com", + "personalAccessToken": "asdf1234", } } help={ <FormattedMessage - defaultMessage="settings.almintegration.github.info" - id="settings.almintegration.github.info" + defaultMessage="settings.almintegration.azure.info" + id="settings.almintegration.azure.info" values={ Object { "link": <Link @@ -410,3 +346,64 @@ exports[`should render correctly for single-ALM binding 3`] = ` </AlmBindingDefinitionForm> </div> `; + +exports[`should render correctly with validation 1`] = ` +<div + className="big-padded" +> + <DeferredSpinner + loading={false} + > + <div + className="spacer-bottom text-right" + > + <Button + data-test="settings__alm-create" + disabled={false} + onClick={[MockFunction]} + > + settings.almintegration.table.create + </Button> + </div> + <AlmBindingDefinitionBox + 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]} + /> + </DeferredSpinner> +</div> +`; + +exports[`should render correctly with validation: empty 1`] = ` +<div + className="big-padded" +> + <DeferredSpinner + loading={false} + > + <div + className="spacer-bottom text-right" + > + <Button + data-test="settings__alm-create" + disabled={false} + onClick={[MockFunction]} + > + settings.almintegration.table.create + </Button> + </div> + </DeferredSpinner> +</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 index 35b9a582a23..0ed28e44289 100644 --- 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 @@ -13,6 +13,7 @@ exports[`should render correctly 1`] = ` "personalAccessToken": "", } } + definitionStatus={Object {}} definitions={ Array [ Object { @@ -35,6 +36,7 @@ exports[`should render correctly 1`] = ` loadingAlmDefinitions={false} loadingProjectCount={false} multipleAlmEnabled={true} + onCheck={[MockFunction]} onDelete={[MockFunction]} onUpdateDefinitions={[MockFunction]} updateConfiguration={[Function]} 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 index d86ec2f33cd..6dc5847e249 100644 --- 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 @@ -42,6 +42,7 @@ exports[`should render correctly 1`] = ` "url": "", } } + definitionStatus={Object {}} definitions={ Array [ Object { @@ -105,6 +106,7 @@ exports[`should render correctly 1`] = ` loadingAlmDefinitions={false} loadingProjectCount={false} multipleAlmEnabled={true} + onCheck={[MockFunction]} onDelete={[MockFunction]} onUpdateDefinitions={[MockFunction]} updateConfiguration={[Function]} 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 index 60db1f5eb30..0ab619e3f37 100644 --- 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 @@ -47,6 +47,7 @@ exports[`should render correctly: with branch support 1`] = ` "url": "", } } + definitionStatus={Object {}} definitions={ Array [ Object { @@ -97,6 +98,7 @@ exports[`should render correctly: with branch support 1`] = ` loadingAlmDefinitions={false} loadingProjectCount={false} multipleAlmEnabled={true} + onCheck={[MockFunction]} onDelete={[MockFunction]} onUpdateDefinitions={[MockFunction]} updateConfiguration={[Function]} 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 index 1ab95326119..2ef3faa1a0d 100644 --- 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 @@ -42,6 +42,7 @@ exports[`should render correctly: with URL 1`] = ` "url": "", } } + definitionStatus={Object {}} definitions={ Array [ Object { @@ -71,6 +72,7 @@ exports[`should render correctly: with URL 1`] = ` loadingAlmDefinitions={false} loadingProjectCount={false} multipleAlmEnabled={true} + onCheck={[MockFunction]} onDelete={[MockFunction]} onUpdateDefinitions={[MockFunction]} updateConfiguration={[Function]} @@ -131,6 +133,7 @@ exports[`should render correctly: with branch support 1`] = ` "url": "", } } + definitionStatus={Object {}} definitions={ Array [ Object { @@ -159,6 +162,7 @@ exports[`should render correctly: with branch support 1`] = ` loadingAlmDefinitions={false} loadingProjectCount={false} multipleAlmEnabled={true} + onCheck={[MockFunction]} onDelete={[MockFunction]} onUpdateDefinitions={[MockFunction]} updateConfiguration={[Function]} @@ -219,6 +223,7 @@ exports[`should render correctly: with no definitions 1`] = ` "url": "", } } + definitionStatus={Object {}} definitions={Array []} features={ Array [ @@ -240,6 +245,7 @@ exports[`should render correctly: with no definitions 1`] = ` loadingAlmDefinitions={false} loadingProjectCount={false} multipleAlmEnabled={true} + onCheck={[MockFunction]} onDelete={[MockFunction]} onUpdateDefinitions={[MockFunction]} updateConfiguration={[Function]} 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 new file mode 100644 index 00000000000..77af2d35efc --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/utils.ts @@ -0,0 +1,22 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import { AlmKeys } from '../../../../types/alm-settings'; + +export const VALIDATED_ALMS = [AlmKeys.GitHub]; 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 c5bcc318785..b2bd99f0432 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 @@ -19,6 +19,7 @@ */ import { AlmKeys, + AlmSettingsBindingStatus, AlmSettingsInstance, AzureBindingDefinition, BitbucketBindingDefinition, @@ -129,3 +130,14 @@ export function mockProjectGitLabBindingResponse( ...overrides }; } + +export function mockAlmSettingsBindingStatus( + overrides: Partial<AlmSettingsBindingStatus> +): AlmSettingsBindingStatus { + return { + alert: false, + errorMessage: '', + validating: true, + ...overrides + }; +} 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 887d227f881..8cc38476792 100644 --- a/server/sonar-web/src/main/js/types/alm-settings.ts +++ b/server/sonar-web/src/main/js/types/alm-settings.ts @@ -26,6 +26,7 @@ export const enum AlmKeys { export interface AlmBindingDefinition { key: string; + url?: string; } export interface AzureBindingDefinition extends AlmBindingDefinition { @@ -108,3 +109,9 @@ export interface AlmSettingsBindingDefinitions { [AlmKeys.GitHub]: GithubBindingDefinition[]; [AlmKeys.GitLab]: GitlabBindingDefinition[]; } + +export interface AlmSettingsBindingStatus { + alert: boolean; + errorMessage: string; + validating: boolean; +} |