From 27e618aded47917f8122944baef8651a4e8d371d Mon Sep 17 00:00:00 2001 From: Philippe Perrin Date: Mon, 22 May 2023 18:34:42 +0200 Subject: [PATCH] SONAR-19294 Prevent "specific analysis" NCD option selection and warn about it --- .../js/api/mocks/NewCodePeriodsServiceMock.ts | 4 + .../projectBaseline/components/AppHeader.tsx | 14 +-- .../components/BaselineSettingAnalysis.tsx | 5 +- .../components/BaselineSettingDays.tsx | 49 +-------- .../components/BranchAnalysisListRenderer.tsx | 4 +- .../components/BranchBaselineSettingModal.tsx | 20 ++-- .../components/ProjectBaselineSelector.tsx | 101 ++++++++++-------- .../__tests__/ProjectBaselineApp-it.tsx | 89 ++++++++------- .../components/__tests__/utils-test.ts | 4 +- .../main/js/apps/projectBaseline/styles.css | 4 +- .../src/main/js/apps/projectBaseline/utils.ts | 11 +- .../settings/components/NewCodePeriod.tsx | 16 +-- .../components/__tests__/NewCodePeriod-it.tsx | 2 - .../NewCodeDefinitionWarning.tsx | 91 ++++++++++++++++ .../main/js/helpers/__tests__/periods-test.ts | 2 +- .../sonar-web/src/main/js/helpers/periods.ts | 2 + .../resources/org/sonar/l10n/core.properties | 14 ++- 17 files changed, 254 insertions(+), 178 deletions(-) create mode 100644 server/sonar-web/src/main/js/components/new-code-definition/NewCodeDefinitionWarning.tsx diff --git a/server/sonar-web/src/main/js/api/mocks/NewCodePeriodsServiceMock.ts b/server/sonar-web/src/main/js/api/mocks/NewCodePeriodsServiceMock.ts index 43a20156098..0e8506433de 100644 --- a/server/sonar-web/src/main/js/api/mocks/NewCodePeriodsServiceMock.ts +++ b/server/sonar-web/src/main/js/api/mocks/NewCodePeriodsServiceMock.ts @@ -97,6 +97,10 @@ export default class NewCodePeriodsServiceMock { this.#newCodePeriod = newCodePeriod; }; + setListBranchesNewCode = (listBranchesNewCode: NewCodePeriodBranch[]) => { + this.#listBranchesNewCode = listBranchesNewCode; + }; + reset = () => { this.#newCodePeriod = cloneDeep(this.#defaultNewCodePeriod); this.#listBranchesNewCode = cloneDeep(this.#defaultListBranchesNewCode); diff --git a/server/sonar-web/src/main/js/apps/projectBaseline/components/AppHeader.tsx b/server/sonar-web/src/main/js/apps/projectBaseline/components/AppHeader.tsx index 5e20a0b36c7..dbb6c2063aa 100644 --- a/server/sonar-web/src/main/js/apps/projectBaseline/components/AppHeader.tsx +++ b/server/sonar-web/src/main/js/apps/projectBaseline/components/AppHeader.tsx @@ -34,19 +34,7 @@ export default function AppHeader(props: AppHeaderProps) {

{translate('project_baseline.page')}

{translate('project_baseline.page.description')}

-

- - {translate('settings.new_code_period.description1.link')} - - ), - }} - /> -

+

{translate('settings.new_code_period.description1')}

{canAdmin && ( void; selected: boolean; } -export default function BaselineSettingAnalysis({ disabled, onSelect, selected }: Props) { +export default function BaselineSettingAnalysis({ onSelect, selected }: Props) { return ( onSelect(NewCodePeriodSettingType.SPECIFIC_ANALYSIS)} selected={selected} title={translate('baseline.specific_analysis')} diff --git a/server/sonar-web/src/main/js/apps/projectBaseline/components/BaselineSettingDays.tsx b/server/sonar-web/src/main/js/apps/projectBaseline/components/BaselineSettingDays.tsx index f6f2e1ae084..91c02599ee1 100644 --- a/server/sonar-web/src/main/js/apps/projectBaseline/components/BaselineSettingDays.tsx +++ b/server/sonar-web/src/main/js/apps/projectBaseline/components/BaselineSettingDays.tsx @@ -18,21 +18,16 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import * as React from 'react'; -import withAvailableFeatures, { - WithAvailableFeaturesProps, -} from '../../../app/components/available-features/withAvailableFeatures'; import RadioCard from '../../../components/controls/RadioCard'; import ValidationInput, { ValidationInputErrorPlacement, } from '../../../components/controls/ValidationInput'; -import { Alert } from '../../../components/ui/Alert'; import MandatoryFieldsExplanation from '../../../components/ui/MandatoryFieldsExplanation'; import { translate, translateWithParameters } from '../../../helpers/l10n'; import { MAX_NUMBER_OF_DAYS, MIN_NUMBER_OF_DAYS } from '../../../helpers/periods'; -import { Feature } from '../../../types/features'; import { NewCodePeriodSettingType } from '../../../types/types'; -export interface Props extends WithAvailableFeaturesProps { +export interface Props { className?: string; days: string; disabled?: boolean; @@ -41,28 +36,10 @@ export interface Props extends WithAvailableFeaturesProps { onChangeDays: (value: string) => void; onSelect: (selection: NewCodePeriodSettingType) => void; selected: boolean; - settingLevel: BaselineSettingDaysSettingLevel; } -export enum BaselineSettingDaysSettingLevel { - Global = 'global', - Project = 'project', - Branch = 'branch', -} - -function BaselineSettingDays(props: Props) { - const { - className, - days, - disabled, - isChanged, - isValid, - onChangeDays, - onSelect, - selected, - settingLevel, - } = props; - const isBranchSupportEnabled = props.hasFeature(Feature.BranchSupport); +export default function BaselineSettingDays(props: Props) { + const { className, days, disabled, isChanged, isValid, onChangeDays, onSelect, selected } = props; return ( - - {!isChanged && !isValid && ( - -

- {translate('baseline.number_days.compliance_warning.title')} -

-

- {translate( - `baseline.number_days.compliance_warning.content.${settingLevel}${ - isBranchSupportEnabled && - settingLevel === BaselineSettingDaysSettingLevel.Project - ? '.with_branch_support' - : '' - }` - )} -

- - )} )} ); } - -export default withAvailableFeatures(BaselineSettingDays); diff --git a/server/sonar-web/src/main/js/apps/projectBaseline/components/BranchAnalysisListRenderer.tsx b/server/sonar-web/src/main/js/apps/projectBaseline/components/BranchAnalysisListRenderer.tsx index 94aa4bcf902..7232e40a23c 100644 --- a/server/sonar-web/src/main/js/apps/projectBaseline/components/BranchAnalysisListRenderer.tsx +++ b/server/sonar-web/src/main/js/apps/projectBaseline/components/BranchAnalysisListRenderer.tsx @@ -51,7 +51,7 @@ function renderAnalysis(args: { selectedAnalysisKey: string; intl: IntlShape; }) { - const { analysis, isFirst, onSelectAnalysis, selectedAnalysisKey, intl } = args; + const { analysis, isFirst, selectedAnalysisKey, intl } = args; return (
  • onSelectAnalysis(analysis)} >
    @@ -86,6 +85,7 @@ function renderAnalysis(args: { )} onCheck={() => {}} value="" + disabled={true} />
  • diff --git a/server/sonar-web/src/main/js/apps/projectBaseline/components/BranchBaselineSettingModal.tsx b/server/sonar-web/src/main/js/apps/projectBaseline/components/BranchBaselineSettingModal.tsx index e2d8067e774..94d349b5245 100644 --- a/server/sonar-web/src/main/js/apps/projectBaseline/components/BranchBaselineSettingModal.tsx +++ b/server/sonar-web/src/main/js/apps/projectBaseline/components/BranchBaselineSettingModal.tsx @@ -21,6 +21,7 @@ import * as React from 'react'; import { setNewCodePeriod } from '../../../api/newCodePeriod'; import Modal from '../../../components/controls/Modal'; import { ResetButtonLink, SubmitButton } from '../../../components/controls/buttons'; +import NewCodeDefinitionWarning from '../../../components/new-code-definition/NewCodeDefinitionWarning'; import DeferredSpinner from '../../../components/ui/DeferredSpinner'; import { toISO8601WithOffsetString } from '../../../helpers/dates'; import { translate, translateWithParameters } from '../../../helpers/l10n'; @@ -29,7 +30,7 @@ import { ParsedAnalysis } from '../../../types/project-activity'; import { NewCodePeriod, NewCodePeriodSettingType } from '../../../types/types'; import { getSettingValue, validateSetting } from '../utils'; import BaselineSettingAnalysis from './BaselineSettingAnalysis'; -import BaselineSettingDays, { BaselineSettingDaysSettingLevel } from './BaselineSettingDays'; +import BaselineSettingDays from './BaselineSettingDays'; import BaselineSettingPreviousVersion from './BaselineSettingPreviousVersion'; import BaselineSettingReferenceBranch from './BaselineSettingReferenceBranch'; import BranchAnalysisList from './BranchAnalysisList'; @@ -162,6 +163,12 @@ export default class BranchBaselineSettingModal extends React.PureComponent

    {translate('baseline.new_code_period_for_branch_x.question')}

    +
    - + {currentSetting === NewCodePeriodSettingType.SPECIFIC_ANALYSIS && ( + {}} + selected={selected === NewCodePeriodSettingType.SPECIFIC_ANALYSIS} + /> + )}
    {selected === NewCodePeriodSettingType.SPECIFIC_ANALYSIS && ( {translate('project_baseline.global_setting')} -
    {renderGeneralSetting(generalSetting)}
    - {!isGeneralSettingCompliant && ( - -

    - {translate('project_baseline.compliance.warning.title.global')} -

    -

    - {canAdmin ? ( - - {translate('project_baseline.warning.explanation.action.admin.link')} - - ), - }} - /> - ) : ( - translate('project_baseline.compliance.warning.explanation') - )} -

    -
    - )} + +
    + {!isGeneralSettingCompliant && ( + +

    + {translate('project_baseline.compliance.warning.title.global')} +

    +

    + {canAdmin ? ( + + {translate('project_baseline.warning.explanation.action.admin.link')} + + ), + }} + /> + ) : ( + translate('project_baseline.compliance.warning.explanation') + )} +

    +
    + )} + {renderGeneralSetting(generalSetting)} +
    +
    - {branchesEnabled ? ( + {branchesEnabled && ( - ) : ( + )} + {!branchesEnabled && currentSetting === NewCodePeriodSettingType.SPECIFIC_ANALYSIS && ( {}} selected={ overrideGeneralSetting && selected === NewCodePeriodSettingType.SPECIFIC_ANALYSIS } /> )}
    - {selected === NewCodePeriodSettingType.SPECIFIC_ANALYSIS && ( - - )} + {!branchesEnabled && + overrideGeneralSetting && + selected === NewCodePeriodSettingType.SPECIFIC_ANALYSIS && ( + + )}
    diff --git a/server/sonar-web/src/main/js/apps/projectBaseline/components/__tests__/ProjectBaselineApp-it.tsx b/server/sonar-web/src/main/js/apps/projectBaseline/components/__tests__/ProjectBaselineApp-it.tsx index 577aa5be783..6cf0cc88d26 100644 --- a/server/sonar-web/src/main/js/apps/projectBaseline/components/__tests__/ProjectBaselineApp-it.tsx +++ b/server/sonar-web/src/main/js/apps/projectBaseline/components/__tests__/ProjectBaselineApp-it.tsx @@ -26,14 +26,15 @@ import NewCodePeriodsServiceMock from '../../../../api/mocks/NewCodePeriodsServi import { ProjectActivityServiceMock } from '../../../../api/mocks/ProjectActivityServiceMock'; import { mockBranch } from '../../../../helpers/mocks/branch-like'; import { mockComponent } from '../../../../helpers/mocks/component'; +import { mockNewCodePeriodBranch } from '../../../../helpers/mocks/new-code-period'; import { mockAppState } from '../../../../helpers/testMocks'; import { - renderAppWithComponentContext, RenderContext, + renderAppWithComponentContext, } from '../../../../helpers/testReactTestingUtils'; import { Feature } from '../../../../types/features'; -import routes from '../../routes'; import { NewCodePeriodSettingType } from '../../../../types/types'; +import routes from '../../routes'; jest.mock('../../../../api/newCodePeriod'); jest.mock('../../../../api/projectActivity'); @@ -52,7 +53,7 @@ it('renders correctly without branch support feature', async () => { await ui.appIsLoaded(); expect(ui.generalSettingRadio.get()).toBeChecked(); - expect(ui.specificAnalysisRadio.get()).toBeInTheDocument(); + expect(ui.specificAnalysisRadio.query()).not.toBeInTheDocument(); // User is not admin expect(ui.generalSettingsLink.query()).not.toBeInTheDocument(); @@ -173,19 +174,35 @@ it('can set reference branch specific setting', async () => { expect(ui.saved.get()).toBeInTheDocument(); }); -it('can set specific analysis setting', async () => { - const { ui, user } = getPageObjects(); +it('cannot set specific analysis setting', async () => { + const { ui } = getPageObjects(); + codePeriodsMock.setNewCodePeriod({ + type: NewCodePeriodSettingType.SPECIFIC_ANALYSIS, + value: 'analysis_id', + }); renderProjectBaselineApp(); await ui.appIsLoaded(); - expect(ui.specificAnalysisRadio.get()).toHaveClass('disabled'); - await ui.setSpecificAnalysisSetting(); expect(ui.specificAnalysisRadio.get()).toBeChecked(); + expect(ui.specificAnalysisRadio.get()).toHaveClass('disabled'); + expect(ui.specificAnalysisWarning.get()).toBeInTheDocument(); - // Save changes - await user.click(ui.saveButton.get()); + await selectEvent.select(ui.analysisFromSelect.get(), 'baseline.branch_analyses.ranges.allTime'); - expect(ui.saved.get()).toBeInTheDocument(); + expect(first(ui.analysisListItem.getAll())).toHaveClass('disabled'); + expect(ui.saveButton.get()).toBeDisabled(); +}); + +it('renders correctly branch modal', async () => { + const { ui } = getPageObjects(); + renderProjectBaselineApp({ + featureList: [Feature.BranchSupport], + }); + await ui.appIsLoaded(); + + await ui.openBranchSettingModal('main'); + + expect(ui.specificAnalysisRadio.query()).not.toBeInTheDocument(); }); it('can set a previous version setting for branch', async () => { @@ -220,16 +237,30 @@ it('can set a number of days setting for branch', async () => { expect(within(byRole('table').get()).getByText('baseline.number_days: 15')).toBeInTheDocument(); }); -it('can set a specific analysis setting for branch', async () => { +it('cannot set a specific analysis setting for branch', async () => { const { ui } = getPageObjects(); + codePeriodsMock.setListBranchesNewCode([ + mockNewCodePeriodBranch({ + branchKey: 'main', + type: NewCodePeriodSettingType.SPECIFIC_ANALYSIS, + value: 'analysis_id', + }), + ]); renderProjectBaselineApp({ featureList: [Feature.BranchSupport], }); await ui.appIsLoaded(); - await ui.setBranchSpecificAnalysisSetting('main'); + await ui.openBranchSettingModal('main'); - expect(within(byRole('table').get()).getByText(/baseline.specific_analysis/)).toBeInTheDocument(); + expect(ui.specificAnalysisRadio.get()).toBeChecked(); + expect(ui.specificAnalysisRadio.get()).toHaveClass('disabled'); + expect(ui.specificAnalysisWarning.get()).toBeInTheDocument(); + + await selectEvent.select(ui.analysisFromSelect.get(), 'baseline.branch_analyses.ranges.allTime'); + + expect(first(ui.analysisListItem.getAll())).toHaveClass('disabled'); + expect(last(ui.saveButton.getAll())).toBeDisabled(); }); it('can set a reference branch setting for branch', async () => { @@ -270,6 +301,7 @@ function getPageObjects() { referenceBranchRadio: byRole('radio', { name: /baseline.reference_branch.description/ }), chooseBranchSelect: byRole('combobox', { name: 'baseline.reference_branch.choose' }), specificAnalysisRadio: byRole('radio', { name: /baseline.specific_analysis.description/ }), + specificAnalysisWarning: byText('baseline.specific_analysis.compliance_warning.title'), analysisFromSelect: byRole('combobox', { name: 'baseline.analysis_from' }), analysisListItem: byRole('radio', { name: /baseline.branch_analyses.analysis_for_x/ }), saveButton: byRole('button', { name: 'save' }), @@ -293,8 +325,7 @@ function getPageObjects() { } async function setBranchPreviousVersionSetting(branch: string) { - await user.click(await ui.branchActionsButton(branch).find()); - await user.click(ui.editButton.get()); + await openBranchSettingModal(branch); await user.click(last(ui.previousVersionRadio.getAll()) as HTMLElement); await user.click(last(ui.saveButton.getAll()) as HTMLElement); } @@ -307,8 +338,7 @@ function getPageObjects() { } async function setBranchNumberOfDaysSetting(branch: string, value: string) { - await user.click(await ui.branchActionsButton(branch).find()); - await user.click(ui.editButton.get()); + await openBranchSettingModal(branch); await user.click(last(ui.numberDaysRadio.getAll()) as HTMLElement); await user.clear(ui.numberDaysInput.get()); await user.type(ui.numberDaysInput.get(), value); @@ -322,33 +352,15 @@ function getPageObjects() { } async function setBranchReferenceToBranchSetting(branch: string, branchRef: string) { - await user.click(await ui.branchActionsButton(branch).find()); - await user.click(ui.editButton.get()); + await openBranchSettingModal(branch); await user.click(last(ui.referenceBranchRadio.getAll()) as HTMLElement); await selectEvent.select(ui.chooseBranchSelect.get(), branchRef); await user.click(last(ui.saveButton.getAll()) as HTMLElement); } - async function setSpecificAnalysisSetting() { - await user.click(ui.specificSettingRadio.get()); - await user.click(ui.specificAnalysisRadio.get()); - await selectEvent.select( - ui.analysisFromSelect.get(), - 'baseline.branch_analyses.ranges.allTime' - ); - await user.click(first(ui.analysisListItem.getAll()) as HTMLElement); - } - - async function setBranchSpecificAnalysisSetting(branch: string) { + async function openBranchSettingModal(branch: string) { await user.click(await ui.branchActionsButton(branch).find()); await user.click(ui.editButton.get()); - await user.click(last(ui.specificAnalysisRadio.getAll()) as HTMLElement); - await selectEvent.select( - ui.analysisFromSelect.get(), - 'baseline.branch_analyses.ranges.allTime' - ); - await user.click(first(ui.analysisListItem.getAll()) as HTMLElement); - await user.click(last(ui.saveButton.getAll()) as HTMLElement); } return { @@ -358,11 +370,10 @@ function getPageObjects() { setNumberDaysSetting, setPreviousVersionSetting, setReferenceBranchSetting, - setSpecificAnalysisSetting, setBranchPreviousVersionSetting, setBranchNumberOfDaysSetting, - setBranchSpecificAnalysisSetting, setBranchReferenceToBranchSetting, + openBranchSettingModal, }, user, }; diff --git a/server/sonar-web/src/main/js/apps/projectBaseline/components/__tests__/utils-test.ts b/server/sonar-web/src/main/js/apps/projectBaseline/components/__tests__/utils-test.ts index 2d53227ef9f..c9b2640b83c 100644 --- a/server/sonar-web/src/main/js/apps/projectBaseline/components/__tests__/utils-test.ts +++ b/server/sonar-web/src/main/js/apps/projectBaseline/components/__tests__/utils-test.ts @@ -93,7 +93,7 @@ describe('validateSettings', () => { days: '', selected: NewCodePeriodSettingType.SPECIFIC_ANALYSIS, }) - ).toEqual({ isChanged: false, isValid: true }); + ).toEqual({ isChanged: false, isValid: false }); expect( validateSetting({ analysis: 'analysis2', @@ -102,7 +102,7 @@ describe('validateSettings', () => { days: '', selected: NewCodePeriodSettingType.SPECIFIC_ANALYSIS, }) - ).toEqual({ isChanged: true, isValid: true }); + ).toEqual({ isChanged: true, isValid: false }); expect( validateSetting({ currentSetting: NewCodePeriodSettingType.REFERENCE_BRANCH, diff --git a/server/sonar-web/src/main/js/apps/projectBaseline/styles.css b/server/sonar-web/src/main/js/apps/projectBaseline/styles.css index 3cbb5847c02..9967cf27b7f 100644 --- a/server/sonar-web/src/main/js/apps/projectBaseline/styles.css +++ b/server/sonar-web/src/main/js/apps/projectBaseline/styles.css @@ -75,10 +75,10 @@ .branch-analysis { display: flex; justify-content: space-between; - cursor: pointer; padding: var(--gridSize); border-top: 1px solid var(--barBorderColor); border-bottom: 1px solid var(--barBorderColor); + cursor: not-allowed; } .branch-analysis + .branch-analysis { @@ -86,7 +86,7 @@ } .branch-analysis:hover { - background-color: var(--lightBlue); + background-color: var(--disableGrayBg); } .branch-analysis > .project-activity-events { diff --git a/server/sonar-web/src/main/js/apps/projectBaseline/utils.ts b/server/sonar-web/src/main/js/apps/projectBaseline/utils.ts index e40c4d89670..f1dc41c7056 100644 --- a/server/sonar-web/src/main/js/apps/projectBaseline/utils.ts +++ b/server/sonar-web/src/main/js/apps/projectBaseline/utils.ts @@ -78,14 +78,13 @@ export function validateSetting(state: { const isValid = overrideGeneralSetting === false || - selected === NewCodePeriodSettingType.PREVIOUS_VERSION || - (selected === NewCodePeriodSettingType.SPECIFIC_ANALYSIS && analysis.length > 0) || - (selected === NewCodePeriodSettingType.NUMBER_OF_DAYS && + (!!selected && isNewCodeDefinitionCompliant({ - type: NewCodePeriodSettingType.NUMBER_OF_DAYS, + type: selected, value: days, - })) || - (selected === NewCodePeriodSettingType.REFERENCE_BRANCH && referenceBranch.length > 0); + }) && + (selected !== NewCodePeriodSettingType.SPECIFIC_ANALYSIS || analysis.length > 0) && + (selected !== NewCodePeriodSettingType.REFERENCE_BRANCH || referenceBranch.length > 0)); return { isChanged, isValid }; } diff --git a/server/sonar-web/src/main/js/apps/settings/components/NewCodePeriod.tsx b/server/sonar-web/src/main/js/apps/settings/components/NewCodePeriod.tsx index fbac9119d87..6ca8b1d3695 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/NewCodePeriod.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/NewCodePeriod.tsx @@ -23,20 +23,19 @@ import { getNewCodePeriod, setNewCodePeriod } from '../../../api/newCodePeriod'; import DocLink from '../../../components/common/DocLink'; import { ResetButtonLink, SubmitButton } from '../../../components/controls/buttons'; import AlertSuccessIcon from '../../../components/icons/AlertSuccessIcon'; +import NewCodeDefinitionWarning from '../../../components/new-code-definition/NewCodeDefinitionWarning'; import DeferredSpinner from '../../../components/ui/DeferredSpinner'; import { translate } from '../../../helpers/l10n'; import { isNewCodeDefinitionCompliant } from '../../../helpers/periods'; import { NewCodePeriodSettingType } from '../../../types/types'; -import BaselineSettingDays, { - BaselineSettingDaysSettingLevel, -} from '../../projectBaseline/components/BaselineSettingDays'; +import BaselineSettingDays from '../../projectBaseline/components/BaselineSettingDays'; import BaselineSettingPreviousVersion from '../../projectBaseline/components/BaselineSettingPreviousVersion'; interface State { currentSetting?: NewCodePeriodSettingType; days: string; loading: boolean; - currentSettingValue?: string | number; + currentSettingValue?: string; saving: boolean; selected?: NewCodePeriodSettingType; success: boolean; @@ -191,14 +190,19 @@ export default class NewCodePeriod extends React.PureComponent<{}, State> { selected={selected === NewCodePeriodSettingType.PREVIOUS_VERSION} /> + {isChanged && (
    diff --git a/server/sonar-web/src/main/js/apps/settings/components/__tests__/NewCodePeriod-it.tsx b/server/sonar-web/src/main/js/apps/settings/components/__tests__/NewCodePeriod-it.tsx index bc0bcedaa36..2d8d3f4035c 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/__tests__/NewCodePeriod-it.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/__tests__/NewCodePeriod-it.tsx @@ -101,7 +101,6 @@ it('renders and behaves properly when the current value is not compliant', async expect(ui.daysInput.get()).toHaveValue('91'); // Should warn about non compliant value - expect(ui.daysNumberErrorMessage.get()).toBeInTheDocument(); expect(screen.getByRole('alert')).toHaveTextContent( 'baseline.number_days.compliance_warning.title' ); @@ -109,7 +108,6 @@ it('renders and behaves properly when the current value is not compliant', async await user.clear(ui.daysInput.get()); await user.type(ui.daysInput.get(), '92'); - expect(screen.queryByRole('alert')).not.toBeInTheDocument(); expect(ui.daysNumberErrorMessage.get()).toBeInTheDocument(); }); diff --git a/server/sonar-web/src/main/js/components/new-code-definition/NewCodeDefinitionWarning.tsx b/server/sonar-web/src/main/js/components/new-code-definition/NewCodeDefinitionWarning.tsx new file mode 100644 index 00000000000..dfbc9c9f503 --- /dev/null +++ b/server/sonar-web/src/main/js/components/new-code-definition/NewCodeDefinitionWarning.tsx @@ -0,0 +1,91 @@ +/* + * SonarQube + * Copyright (C) 2009-2023 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import * as React from 'react'; +import { translate } from '../../helpers/l10n'; +import { isNewCodeDefinitionCompliant } from '../../helpers/periods'; +import { NewCodePeriodSettingType } from '../../types/types'; +import DocLink from '../common/DocLink'; +import { Alert } from '../ui/Alert'; + +export interface NewCodeDefinitionWarningProps { + newCodeDefinitionType: NewCodePeriodSettingType | undefined; + newCodeDefinitionValue: string | undefined; + isBranchSupportEnabled: boolean | undefined; + level: 'branch' | 'project' | 'global'; +} + +export default function NewCodeDefinitionWarning({ + newCodeDefinitionType, + newCodeDefinitionValue, + isBranchSupportEnabled, + level, +}: NewCodeDefinitionWarningProps) { + if ( + newCodeDefinitionType === undefined || + isNewCodeDefinitionCompliant({ type: newCodeDefinitionType, value: newCodeDefinitionValue }) + ) { + return null; + } + + if (newCodeDefinitionType === NewCodePeriodSettingType.SPECIFIC_ANALYSIS) { + return ( + +

    + {translate('baseline.specific_analysis.compliance_warning.title')} +

    +

    + {translate('baseline.specific_analysis.compliance_warning.explanation')} +

    +

    + {translate('learn_more')}:  + + {translate('baseline.specific_analysis.compliance_warning.link')} + +

    +
    + ); + } + + if (newCodeDefinitionType === NewCodePeriodSettingType.NUMBER_OF_DAYS) { + return ( + +

    + {translate('baseline.number_days.compliance_warning.title')} +

    +

    + {translate( + `baseline.number_days.compliance_warning.content.${level}${ + isBranchSupportEnabled && level === 'project' ? '.with_branch_support' : '' + }` + )} +

    +

    + {translate('learn_more')}:  + + {translate('baseline.number_days.compliance_warning.link')} + +

    +
    + ); + } + + return null; +} diff --git a/server/sonar-web/src/main/js/helpers/__tests__/periods-test.ts b/server/sonar-web/src/main/js/helpers/__tests__/periods-test.ts index c6283e473ae..ae1f3b3c22f 100644 --- a/server/sonar-web/src/main/js/helpers/__tests__/periods-test.ts +++ b/server/sonar-web/src/main/js/helpers/__tests__/periods-test.ts @@ -119,7 +119,7 @@ describe('isNewCodeDefinitionCompliant', () => { [{ type: NewCodePeriodSettingType.NUMBER_OF_DAYS, value: '91' }, false], [{ type: NewCodePeriodSettingType.PREVIOUS_VERSION }, true], [{ type: NewCodePeriodSettingType.REFERENCE_BRANCH }, true], - [{ type: NewCodePeriodSettingType.SPECIFIC_ANALYSIS }, true], + [{ type: NewCodePeriodSettingType.SPECIFIC_ANALYSIS }, false], ])( 'should test for new code definition compliance properly', (newCodePeriod: NewCodePeriod, result: boolean) => { diff --git a/server/sonar-web/src/main/js/helpers/periods.ts b/server/sonar-web/src/main/js/helpers/periods.ts index c973948b58f..b79b47bd844 100644 --- a/server/sonar-web/src/main/js/helpers/periods.ts +++ b/server/sonar-web/src/main/js/helpers/periods.ts @@ -83,6 +83,8 @@ export function isNewCodeDefinitionCompliant(newCodePeriod: NewCodePeriod) { MIN_NUMBER_OF_DAYS <= +newCodePeriod.value && +newCodePeriod.value <= MAX_NUMBER_OF_DAYS ); + case NewCodePeriodSettingType.SPECIFIC_ANALYSIS: + return false; default: return true; } diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties index 7eeb7acf95e..da4911599fe 100644 --- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties +++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties @@ -649,13 +649,17 @@ baseline.number_days=Number of days baseline.number_days.usecase=Recommended for projects following continuous delivery. baseline.number_days.description=Any code that has changed in the last x days is considered new code. If no action is taken on a new issue after x days, this issue will become part of the overall code. baseline.number_days.invalid=Please provide a whole number between {0} and {1} -baseline.number_days.compliance_warning.title=Update the number of days to benefit from the Clean as You Code methodology -baseline.number_days.compliance_warning.content.global=We recommend that you update this New Code definition so that new projects and existing projects that do not use a specific New Code definition benefit from the Clean as You Code methodology by default. -baseline.number_days.compliance_warning.content.project=We recommend that you update this New Code definition so that your project benefits from the Clean as You Code methodology. -baseline.number_days.compliance_warning.content.project.with_branch_support=We recommend that you update this New Code definition so that new branches and existing branches that do not use a specific New Code definition benefit from the Clean as You Code methodology by default. -baseline.number_days.compliance_warning.content.branch=We recommend that you update this New Code definition so that your branch benefits from the Clean as You Code methodology. +baseline.number_days.compliance_warning.title=Your new code definition is not compliant with the Clean as You Code methodology +baseline.number_days.compliance_warning.content.global=We recommend that you update this new code definition so that new projects and existing projects that do not use a specific New Code definition benefit from the Clean as You Code methodology by default. +baseline.number_days.compliance_warning.content.project=We recommend that you update this new code definition so that your project benefits from the Clean as You Code methodology. +baseline.number_days.compliance_warning.content.project.with_branch_support=We recommend that you update this new code definition so that new branches and existing branches that do not use a specific New Code definition benefit from the Clean as You Code methodology by default. +baseline.number_days.compliance_warning.content.branch=We recommend that you update this new code definition so that your branch benefits from the Clean as You Code methodology. +baseline.number_days.compliance_warning.link=Defining New Code baseline.specific_analysis=Specific analysis baseline.specific_analysis.description=Choose an analysis as the baseline for the new code. +baseline.specific_analysis.compliance_warning.title=Choosing the "Specific analysis" option from the SonarQube UI is not compliant with the Clean as You Code methodology +baseline.specific_analysis.compliance_warning.explanation=It has been deprecated. The option remains available through the Web API. +baseline.specific_analysis.compliance_warning.link=Defining New Code baseline.reference_branch=Reference branch baseline.reference_branch.description=Choose a branch as the baseline for the new code. -- 2.39.5