diff options
author | Philippe Perrin <philippe.perrin@sonarsource.com> | 2023-06-08 10:22:27 +0200 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2023-06-26 20:03:54 +0000 |
commit | df412e40611054143f326a2d364028b64f4a8bac (patch) | |
tree | dfe66bcdbde70544f68e26ee2c45a407108cd35f /server/sonar-web | |
parent | 50324c4ab7e55dfd8293dd9ac21afe751ac7e345 (diff) | |
download | sonarqube-df412e40611054143f326a2d364028b64f4a8bac.tar.gz sonarqube-df412e40611054143f326a2d364028b64f4a8bac.zip |
SONAR-19687 Non-compliant global new code definition value should not be proposed as default
Diffstat (limited to 'server/sonar-web')
36 files changed, 540 insertions, 377 deletions
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 0e8506433de..0d429a7af6a 100644 --- a/server/sonar-web/src/main/js/api/mocks/NewCodePeriodsServiceMock.ts +++ b/server/sonar-web/src/main/js/api/mocks/NewCodePeriodsServiceMock.ts @@ -18,8 +18,15 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import { cloneDeep } from 'lodash'; -import { mockNewCodePeriod, mockNewCodePeriodBranch } from '../../helpers/mocks/new-code-period'; -import { NewCodePeriod, NewCodePeriodBranch, NewCodePeriodSettingType } from '../../types/types'; +import { + mockNewCodePeriod, + mockNewCodePeriodBranch, +} from '../../helpers/mocks/new-code-definition'; +import { + NewCodeDefinition, + NewCodeDefinitionBranch, + NewCodeDefinitionType, +} from '../../types/new-code-definition'; import { getNewCodePeriod, listBranchesNewCodePeriod, @@ -34,13 +41,13 @@ export default class NewCodePeriodsServiceMock { mockNewCodePeriodBranch({ inherited: true, branchKey: 'main' }), mockNewCodePeriodBranch({ branchKey: 'feature', - type: NewCodePeriodSettingType.NUMBER_OF_DAYS, + type: NewCodeDefinitionType.NumberOfDays, value: '1', }), ]; - #newCodePeriod: NewCodePeriod; - #listBranchesNewCode: NewCodePeriodBranch[]; + #newCodePeriod: NewCodeDefinition; + #listBranchesNewCode: NewCodeDefinitionBranch[]; constructor() { this.#newCodePeriod = cloneDeep(this.#defaultNewCodePeriod); @@ -58,14 +65,14 @@ export default class NewCodePeriodsServiceMock { handleSetNewCodePeriod = (data: { project?: string; branch?: string; - type: NewCodePeriodSettingType; + type: NewCodeDefinitionType; value?: string; }) => { const { type, value, branch } = data; if (branch) { const branchNewCode = this.#listBranchesNewCode.find( (bNew) => bNew.branchKey === branch - ) as NewCodePeriodBranch; + ) as NewCodeDefinitionBranch; branchNewCode.type = type; branchNewCode.value = value; } else { @@ -93,11 +100,11 @@ export default class NewCodePeriodsServiceMock { return this.reply({ newCodePeriods: this.#listBranchesNewCode }); }; - setNewCodePeriod = (newCodePeriod: NewCodePeriod) => { + setNewCodePeriod = (newCodePeriod: NewCodeDefinition) => { this.#newCodePeriod = newCodePeriod; }; - setListBranchesNewCode = (listBranchesNewCode: NewCodePeriodBranch[]) => { + setListBranchesNewCode = (listBranchesNewCode: NewCodeDefinitionBranch[]) => { this.#listBranchesNewCode = listBranchesNewCode; }; diff --git a/server/sonar-web/src/main/js/api/newCodePeriod.ts b/server/sonar-web/src/main/js/api/newCodePeriod.ts index b930d715ef2..ca56e5f17ea 100644 --- a/server/sonar-web/src/main/js/api/newCodePeriod.ts +++ b/server/sonar-web/src/main/js/api/newCodePeriod.ts @@ -19,19 +19,23 @@ */ import { throwGlobalError } from '../helpers/error'; import { getJSON, post } from '../helpers/request'; -import { NewCodePeriod, NewCodePeriodBranch, NewCodePeriodSettingType } from '../types/types'; +import { + NewCodeDefinition, + NewCodeDefinitionBranch, + NewCodeDefinitionType, +} from '../types/new-code-definition'; export function getNewCodePeriod(data?: { project?: string; branch?: string; -}): Promise<Omit<NewCodePeriod, 'effectiveValue'>> { +}): Promise<Omit<NewCodeDefinition, 'effectiveValue'>> { return getJSON('/api/new_code_periods/show', data).catch(throwGlobalError); } export function setNewCodePeriod(data: { project?: string; branch?: string; - type: NewCodePeriodSettingType; + type: NewCodeDefinitionType; value?: string; }): Promise<void> { return post('/api/new_code_periods/set', data).catch(throwGlobalError); @@ -43,6 +47,6 @@ export function resetNewCodePeriod(data: { project?: string; branch?: string }): export function listBranchesNewCodePeriod(data: { project: string; -}): Promise<{ newCodePeriods: NewCodePeriodBranch[] }> { +}): Promise<{ newCodePeriods: NewCodeDefinitionBranch[] }> { return getJSON('/api/new_code_periods/list', data).catch(throwGlobalError); } diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/LeakPeriodLegend.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/LeakPeriodLegend.tsx index 93bd5e6911c..dc2b236b822 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/components/LeakPeriodLegend.tsx +++ b/server/sonar-web/src/main/js/apps/component-measures/components/LeakPeriodLegend.tsx @@ -27,9 +27,10 @@ import DateFormatter, { longFormatterOption } from '../../../components/intl/Dat import DateFromNow from '../../../components/intl/DateFromNow'; import DateTimeFormatter, { formatterOption } from '../../../components/intl/DateTimeFormatter'; import { translate, translateWithParameters } from '../../../helpers/l10n'; -import { getPeriodDate, getPeriodLabel } from '../../../helpers/periods'; +import { getNewCodePeriodDate, getNewCodePeriodLabel } from '../../../helpers/new-code-period'; import { ComponentQualifier } from '../../../types/component'; -import { ComponentMeasure, NewCodePeriodSettingType, Period } from '../../../types/types'; +import { NewCodeDefinitionType } from '../../../types/new-code-definition'; +import { ComponentMeasure, Period } from '../../../types/types'; export interface LeakPeriodLegendProps { component: ComponentMeasure; @@ -56,7 +57,7 @@ class LeakPeriodLegend extends React.PureComponent<LeakPeriodLegendProps & Wrapp ); } - const leakPeriodLabel = getPeriodLabel( + const leakPeriodLabel = getNewCodePeriodLabel( period, period.mode === 'manual_baseline' ? this.formatDateTime : this.formatDate ); @@ -68,11 +69,11 @@ class LeakPeriodLegend extends React.PureComponent<LeakPeriodLegendProps & Wrapp </LeakPeriodLabel> ); - if (period.mode === 'days' || period.mode === NewCodePeriodSettingType.NUMBER_OF_DAYS) { + if (period.mode === 'days' || period.mode === NewCodeDefinitionType.NumberOfDays) { return label; } - const date = getPeriodDate(period); + const date = getNewCodePeriodDate(period); const tooltip = date && ( <div> <DateFromNow date={date} /> diff --git a/server/sonar-web/src/main/js/apps/create/project/CreateProjectPage.tsx b/server/sonar-web/src/main/js/apps/create/project/CreateProjectPage.tsx index 7b2abaa73dd..1238d1e05dd 100644 --- a/server/sonar-web/src/main/js/apps/create/project/CreateProjectPage.tsx +++ b/server/sonar-web/src/main/js/apps/create/project/CreateProjectPage.tsx @@ -17,6 +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 classNames from 'classnames'; import * as React from 'react'; import { Helmet } from 'react-helmet-async'; import { FormattedMessage } from 'react-intl'; @@ -31,12 +32,13 @@ import { ButtonLink, SubmitButton } from '../../../components/controls/buttons'; import { Location, Router, withRouter } from '../../../components/hoc/withRouter'; import NewCodeDefinitionSelector from '../../../components/new-code-definition/NewCodeDefinitionSelector'; import DeferredSpinner from '../../../components/ui/DeferredSpinner'; +import { addGlobalSuccessMessage } from '../../../helpers/globalMessages'; import { translate } from '../../../helpers/l10n'; import { getProjectUrl } from '../../../helpers/urls'; import { AlmKeys, AlmSettingsInstance } from '../../../types/alm-settings'; import { AppState } from '../../../types/appstate'; import { Feature } from '../../../types/features'; -import { NewCodePeriodWithCompliance } from '../../../types/types'; +import { NewCodeDefinitiondWithCompliance } from '../../../types/new-code-definition'; import AlmBindingDefinitionForm from '../../settings/components/almIntegration/AlmBindingDefinitionForm'; import AzureProjectCreate from './Azure/AzureProjectCreate'; import BitbucketCloudProjectCreate from './BitbucketCloud/BitbucketCloudProjectCreate'; @@ -48,8 +50,6 @@ import CreateProjectPageHeader from './components/CreateProjectPageHeader'; import ManualProjectCreate from './manual/ManualProjectCreate'; import './style.css'; import { CreateProjectApiCallback, CreateProjectModes } from './types'; -import { addGlobalSuccessMessage } from '../../../helpers/globalMessages'; -import classNames from 'classnames'; export interface CreateProjectPageProps extends WithAvailableFeaturesProps { appState: AppState; @@ -66,7 +66,7 @@ interface State { loading: boolean; isProjectSetupDone: boolean; creatingAlmDefinition?: AlmKeys; - selectedNcd: NewCodePeriodWithCompliance | null; + selectedNcd: NewCodeDefinitiondWithCompliance | null; submitting: boolean; } @@ -177,7 +177,7 @@ export class CreateProjectPage extends React.PureComponent<CreateProjectPageProp } }; - handleNcdChanged = (ncd: NewCodePeriodWithCompliance) => { + handleNcdChanged = (ncd: NewCodeDefinitiondWithCompliance) => { this.setState({ selectedNcd: ncd, }); diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/Manual-it.tsx b/server/sonar-web/src/main/js/apps/create/project/__tests__/Manual-it.tsx index daffdc75925..1b22f0b085f 100644 --- a/server/sonar-web/src/main/js/apps/create/project/__tests__/Manual-it.tsx +++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/Manual-it.tsx @@ -27,7 +27,7 @@ import { mockProject } from '../../../../helpers/mocks/projects'; import { mockAppState } from '../../../../helpers/testMocks'; import { renderApp } from '../../../../helpers/testReactTestingUtils'; import { byRole, byText } from '../../../../helpers/testSelector'; -import { NewCodePeriodSettingType } from '../../../../types/types'; +import { NewCodeDefinitionType } from '../../../../types/new-code-definition'; import CreateProjectPage, { CreateProjectPageProps } from '../CreateProjectPage'; jest.mock('../../../../api/alm-settings'); @@ -124,7 +124,7 @@ it('should fill form and move to NCD selection and back', async () => { it('should select the global NCD when it is compliant', async () => { jest .mocked(getNewCodePeriod) - .mockResolvedValue({ type: NewCodePeriodSettingType.NUMBER_OF_DAYS, value: '30' }); + .mockResolvedValue({ type: NewCodeDefinitionType.NumberOfDays, value: '30' }); const user = userEvent.setup(); renderCreateProject(); await fillFormAndNext('test', user); @@ -142,7 +142,7 @@ it('should select the global NCD when it is compliant', async () => { it('global NCD option should be disabled if not compliant', async () => { jest .mocked(getNewCodePeriod) - .mockResolvedValue({ type: NewCodePeriodSettingType.NUMBER_OF_DAYS, value: '96' }); + .mockResolvedValue({ type: NewCodeDefinitionType.NumberOfDays, value: '96' }); const user = userEvent.setup(); renderCreateProject(); await fillFormAndNext('test', user); @@ -161,7 +161,7 @@ it.each([ async ({ canAdmin, message }) => { jest .mocked(getNewCodePeriod) - .mockResolvedValue({ type: NewCodePeriodSettingType.NUMBER_OF_DAYS, value: '96' }); + .mockResolvedValue({ type: NewCodeDefinitionType.NumberOfDays, value: '96' }); const user = userEvent.setup(); renderCreateProject({ appState: mockAppState({ canAdmin }) }); await fillFormAndNext('test', user); @@ -175,7 +175,7 @@ it.each([ui.ncdOptionRefBranchRadio, ui.ncdOptionPreviousVersionRadio])( async (option) => { jest .mocked(getNewCodePeriod) - .mockResolvedValue({ type: NewCodePeriodSettingType.NUMBER_OF_DAYS, value: '96' }); + .mockResolvedValue({ type: NewCodeDefinitionType.NumberOfDays, value: '96' }); const user = userEvent.setup(); renderCreateProject(); await fillFormAndNext('test', user); @@ -199,7 +199,7 @@ it.each([ui.ncdOptionRefBranchRadio, ui.ncdOptionPreviousVersionRadio])( it('number of days should show error message if value is not a number', async () => { jest .mocked(getNewCodePeriod) - .mockResolvedValue({ type: NewCodePeriodSettingType.NUMBER_OF_DAYS, value: '60' }); + .mockResolvedValue({ type: NewCodeDefinitionType.NumberOfDays, value: '60' }); const user = userEvent.setup(); renderCreateProject(); await fillFormAndNext('test', user); @@ -214,7 +214,7 @@ it('number of days should show error message if value is not a number', async () await user.click(ui.ncdOptionDaysRadio.get()); expect(ui.ncdOptionDaysInput.get()).toBeInTheDocument(); - expect(ui.ncdOptionDaysInput.get()).toHaveValue('30'); + expect(ui.ncdOptionDaysInput.get()).toHaveValue('60'); expect(ui.projectCreateButton.get()).toBeEnabled(); await user.click(ui.ncdOptionDaysInput.get()); @@ -232,7 +232,7 @@ it('number of days should show error message if value is not a number', async () }); it('the project onboarding page should be displayed when the project is created', async () => { - newCodePeriodHandler.setNewCodePeriod({ type: NewCodePeriodSettingType.NUMBER_OF_DAYS }); + newCodePeriodHandler.setNewCodePeriod({ type: NewCodeDefinitionType.NumberOfDays }); const user = userEvent.setup(); renderCreateProject(); await fillFormAndNext('testing', user); diff --git a/server/sonar-web/src/main/js/apps/create/project/types.ts b/server/sonar-web/src/main/js/apps/create/project/types.ts index ddd7c3700a4..6b578e4705e 100644 --- a/server/sonar-web/src/main/js/apps/create/project/types.ts +++ b/server/sonar-web/src/main/js/apps/create/project/types.ts @@ -18,7 +18,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import { ProjectBase } from '../../../api/components'; -import { NewCodePeriodSettingType } from '../../../types/types'; +import { NewCodeDefinitionType } from '../../../types/new-code-definition'; export enum CreateProjectModes { Manual = 'manual', @@ -30,6 +30,6 @@ export enum CreateProjectModes { } export type CreateProjectApiCallback = ( - newCodeDefinitionType?: NewCodePeriodSettingType, + newCodeDefinitionType?: NewCodeDefinitionType, newCodeDefinitionValue?: string ) => Promise<{ project: ProjectBase }>; diff --git a/server/sonar-web/src/main/js/apps/overview/branches/LeakPeriodInfo.tsx b/server/sonar-web/src/main/js/apps/overview/branches/LeakPeriodInfo.tsx index 533cf15b074..daf24ee685c 100644 --- a/server/sonar-web/src/main/js/apps/overview/branches/LeakPeriodInfo.tsx +++ b/server/sonar-web/src/main/js/apps/overview/branches/LeakPeriodInfo.tsx @@ -18,7 +18,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import * as React from 'react'; -import { isApplicationPeriod } from '../../../helpers/periods'; +import { isApplicationNewCodePeriod } from '../../../helpers/new-code-period'; import { ApplicationPeriod } from '../../../types/application'; import { Period } from '../../../types/types'; import ApplicationLeakPeriodInfo from './ApplicationLeakPeriodInfo'; @@ -29,7 +29,7 @@ export interface LeakPeriodInfoProps { } export function LeakPeriodInfo({ leakPeriod }: LeakPeriodInfoProps) { - if (isApplicationPeriod(leakPeriod)) { + if (isApplicationNewCodePeriod(leakPeriod)) { return <ApplicationLeakPeriodInfo leakPeriod={leakPeriod} />; } return <ProjectLeakPeriodInfo leakPeriod={leakPeriod} />; diff --git a/server/sonar-web/src/main/js/apps/overview/branches/MeasuresPanelNoNewCode.tsx b/server/sonar-web/src/main/js/apps/overview/branches/MeasuresPanelNoNewCode.tsx index 37d8c2ac37f..fad0d415731 100644 --- a/server/sonar-web/src/main/js/apps/overview/branches/MeasuresPanelNoNewCode.tsx +++ b/server/sonar-web/src/main/js/apps/overview/branches/MeasuresPanelNoNewCode.tsx @@ -27,7 +27,8 @@ import { getBaseUrl } from '../../../helpers/system'; import { queryToSearch } from '../../../helpers/urls'; import { Branch } from '../../../types/branch-like'; import { ComponentQualifier } from '../../../types/component'; -import { Component, NewCodePeriodSettingType, Period } from '../../../types/types'; +import { NewCodeDefinitionType } from '../../../types/new-code-definition'; +import { Component, Period } from '../../../types/types'; export interface MeasuresPanelNoNewCodeProps { branch?: Branch; @@ -41,7 +42,7 @@ export default function MeasuresPanelNoNewCode(props: MeasuresPanelNoNewCodeProp const isApp = component.qualifier === ComponentQualifier.Application; const hasBadReferenceBranch = - !isApp && !!period && !period.date && period.mode === NewCodePeriodSettingType.REFERENCE_BRANCH; + !isApp && !!period && !period.date && period.mode === NewCodeDefinitionType.ReferenceBranch; /* * If the period is "reference branch"-based, and if there's no date, it means * that we're not lacking a second analysis, but that we'll never have new code because the diff --git a/server/sonar-web/src/main/js/apps/overview/branches/ProjectLeakPeriodInfo.tsx b/server/sonar-web/src/main/js/apps/overview/branches/ProjectLeakPeriodInfo.tsx index 4a9025ba54c..ff14b007ff1 100644 --- a/server/sonar-web/src/main/js/apps/overview/branches/ProjectLeakPeriodInfo.tsx +++ b/server/sonar-web/src/main/js/apps/overview/branches/ProjectLeakPeriodInfo.tsx @@ -23,8 +23,9 @@ import { longFormatterOption } from '../../../components/intl/DateFormatter'; import DateFromNow from '../../../components/intl/DateFromNow'; import { formatterOption } from '../../../components/intl/DateTimeFormatter'; import { translateWithParameters } from '../../../helpers/l10n'; -import { getPeriodDate, getPeriodLabel } from '../../../helpers/periods'; -import { NewCodePeriodSettingType, Period } from '../../../types/types'; +import { getNewCodePeriodDate, getNewCodePeriodLabel } from '../../../helpers/new-code-period'; +import { NewCodeDefinitionType } from '../../../types/new-code-definition'; +import { Period } from '../../../types/types'; export interface ProjectLeakPeriodInfoProps extends WrappedComponentProps { leakPeriod: Period; @@ -36,9 +37,9 @@ export function ProjectLeakPeriodInfo(props: ProjectLeakPeriodInfoProps) { leakPeriod, } = props; - const leakPeriodLabel = getPeriodLabel( + const leakPeriodLabel = getNewCodePeriodLabel( leakPeriod, - ['manual_baseline', NewCodePeriodSettingType.SPECIFIC_ANALYSIS].includes(leakPeriod.mode) + ['manual_baseline', NewCodeDefinitionType.SpecificAnalysis].includes(leakPeriod.mode) ? (date: string) => formatTime(date, formatterOption) : (date: string) => formatDate(date, longFormatterOption) ); @@ -49,13 +50,13 @@ export function ProjectLeakPeriodInfo(props: ProjectLeakPeriodInfoProps) { if ( leakPeriod.mode === 'days' || - leakPeriod.mode === NewCodePeriodSettingType.NUMBER_OF_DAYS || - leakPeriod.mode === NewCodePeriodSettingType.REFERENCE_BRANCH + leakPeriod.mode === NewCodeDefinitionType.NumberOfDays || + leakPeriod.mode === NewCodeDefinitionType.ReferenceBranch ) { return <div>{leakPeriodLabel} </div>; } - const leakPeriodDate = getPeriodDate(leakPeriod); + const leakPeriodDate = getNewCodePeriodDate(leakPeriod); if (!leakPeriodDate) { return null; diff --git a/server/sonar-web/src/main/js/apps/overview/branches/__tests__/ProjectLeakPeriodInfo-test.tsx b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/ProjectLeakPeriodInfo-test.tsx index 516922ab183..e03094a7ac9 100644 --- a/server/sonar-web/src/main/js/apps/overview/branches/__tests__/ProjectLeakPeriodInfo-test.tsx +++ b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/ProjectLeakPeriodInfo-test.tsx @@ -23,7 +23,8 @@ import * as React from 'react'; import { IntlShape } from 'react-intl'; import { mockPeriod } from '../../../../helpers/testMocks'; import { renderComponent } from '../../../../helpers/testReactTestingUtils'; -import { NewCodePeriodSettingType, Period } from '../../../../types/types'; +import { NewCodeDefinitionType } from '../../../../types/new-code-definition'; +import { Period } from '../../../../types/types'; import { ProjectLeakPeriodInfo } from '../ProjectLeakPeriodInfo'; jest.mock('date-fns', () => { @@ -62,7 +63,7 @@ it('should render correctly for "previous_analysis"', async () => { it('should render correctly for "REFERENCE_BRANCH"', async () => { renderProjectLeakPeriodInfo({ - mode: NewCodePeriodSettingType.REFERENCE_BRANCH, + mode: NewCodeDefinitionType.ReferenceBranch, parameter: 'master', }); expect(await screen.findByText('overview.period.reference_branch.master')).toBeInTheDocument(); diff --git a/server/sonar-web/src/main/js/apps/overview/components/LeakPeriodLegend.tsx b/server/sonar-web/src/main/js/apps/overview/components/LeakPeriodLegend.tsx index 3209b00b9b2..a6076b28690 100644 --- a/server/sonar-web/src/main/js/apps/overview/components/LeakPeriodLegend.tsx +++ b/server/sonar-web/src/main/js/apps/overview/components/LeakPeriodLegend.tsx @@ -25,8 +25,9 @@ import DateFormatter, { longFormatterOption } from '../../../components/intl/Dat import DateFromNow from '../../../components/intl/DateFromNow'; import DateTimeFormatter, { formatterOption } from '../../../components/intl/DateTimeFormatter'; import { translateWithParameters } from '../../../helpers/l10n'; -import { getPeriodDate, getPeriodLabel } from '../../../helpers/periods'; -import { Dict, NewCodePeriodSettingType, Period } from '../../../types/types'; +import { getNewCodePeriodDate, getNewCodePeriodLabel } from '../../../helpers/new-code-period'; +import { NewCodeDefinitionType } from '../../../types/new-code-definition'; +import { Dict, Period } from '../../../types/types'; interface Props { period: Period; @@ -48,7 +49,7 @@ export class LeakPeriodLegend extends React.PureComponent<Props & WrappedCompone render() { const { period } = this.props; - const leakPeriodLabel = getPeriodLabel( + const leakPeriodLabel = getNewCodePeriodLabel( period, MODE_INCLUDES_TIME[period.mode] ? this.formatDateTime : this.formatDate ); @@ -56,7 +57,7 @@ export class LeakPeriodLegend extends React.PureComponent<Props & WrappedCompone return null; } - if (period.mode === 'days' || period.mode === NewCodePeriodSettingType.NUMBER_OF_DAYS) { + if (period.mode === 'days' || period.mode === NewCodeDefinitionType.NumberOfDays) { return ( <div className="overview-legend overview-legend-spaced-line"> {translateWithParameters('overview.new_code_period_x', leakPeriodLabel)} @@ -64,7 +65,7 @@ export class LeakPeriodLegend extends React.PureComponent<Props & WrappedCompone ); } - const leakPeriodDate = getPeriodDate(period); + const leakPeriodDate = getNewCodePeriodDate(period); if (!leakPeriodDate) { return null; } diff --git a/server/sonar-web/src/main/js/apps/projectBaseline/components/BaselineSettingAnalysis.tsx b/server/sonar-web/src/main/js/apps/projectBaseline/components/BaselineSettingAnalysis.tsx index ca5db89b7e0..d042123ca89 100644 --- a/server/sonar-web/src/main/js/apps/projectBaseline/components/BaselineSettingAnalysis.tsx +++ b/server/sonar-web/src/main/js/apps/projectBaseline/components/BaselineSettingAnalysis.tsx @@ -20,10 +20,10 @@ import * as React from 'react'; import RadioCard from '../../../components/controls/RadioCard'; import { translate } from '../../../helpers/l10n'; -import { NewCodePeriodSettingType } from '../../../types/types'; +import { NewCodeDefinitionType } from '../../../types/new-code-definition'; export interface Props { - onSelect: (selection: NewCodePeriodSettingType) => void; + onSelect: (selection: NewCodeDefinitionType) => void; selected: boolean; } @@ -32,7 +32,7 @@ export default function BaselineSettingAnalysis({ onSelect, selected }: Props) { <RadioCard noRadio disabled - onClick={() => onSelect(NewCodePeriodSettingType.SPECIFIC_ANALYSIS)} + onClick={() => onSelect(NewCodeDefinitionType.SpecificAnalysis)} selected={selected} title={translate('baseline.specific_analysis')} > diff --git a/server/sonar-web/src/main/js/apps/projectBaseline/components/BaselineSettingReferenceBranch.tsx b/server/sonar-web/src/main/js/apps/projectBaseline/components/BaselineSettingReferenceBranch.tsx index adf4c56df1b..4e9b28484af 100644 --- a/server/sonar-web/src/main/js/apps/projectBaseline/components/BaselineSettingReferenceBranch.tsx +++ b/server/sonar-web/src/main/js/apps/projectBaseline/components/BaselineSettingReferenceBranch.tsx @@ -26,14 +26,14 @@ import AlertErrorIcon from '../../../components/icons/AlertErrorIcon'; import MandatoryFieldMarker from '../../../components/ui/MandatoryFieldMarker'; import MandatoryFieldsExplanation from '../../../components/ui/MandatoryFieldsExplanation'; import { translate, translateWithParameters } from '../../../helpers/l10n'; -import { NewCodePeriodSettingType } from '../../../types/types'; +import { NewCodeDefinitionType } from '../../../types/new-code-definition'; export interface BaselineSettingReferenceBranchProps { branchList: BranchOption[]; className?: string; disabled?: boolean; onChangeReferenceBranch: (value: string) => void; - onSelect: (selection: NewCodePeriodSettingType) => void; + onSelect: (selection: NewCodeDefinitionType) => void; referenceBranch: string; selected: boolean; settingLevel: 'project' | 'branch'; @@ -98,7 +98,7 @@ export default function BaselineSettingReferenceBranch(props: BaselineSettingRef noRadio className={className} disabled={disabled} - onClick={() => props.onSelect(NewCodePeriodSettingType.REFERENCE_BRANCH)} + onClick={() => props.onSelect(NewCodeDefinitionType.ReferenceBranch)} selected={selected} title={translate('baseline.reference_branch')} > 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 c60deb34b08..063a19f0639 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 @@ -19,17 +19,18 @@ */ import * as React from 'react'; import { setNewCodePeriod } from '../../../api/newCodePeriod'; -import { ResetButtonLink, SubmitButton } from '../../../components/controls/buttons'; import Modal from '../../../components/controls/Modal'; +import { ResetButtonLink, SubmitButton } from '../../../components/controls/buttons'; import NewCodeDefinitionDaysOption from '../../../components/new-code-definition/NewCodeDefinitionDaysOption'; import NewCodeDefinitionPreviousVersionOption from '../../../components/new-code-definition/NewCodeDefinitionPreviousVersionOption'; 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'; +import { getNumberOfDaysDefaultValue } from '../../../helpers/new-code-definition'; import { Branch, BranchWithNewCodePeriod } from '../../../types/branch-like'; +import { NewCodeDefinition, NewCodeDefinitionType } from '../../../types/new-code-definition'; import { ParsedAnalysis } from '../../../types/project-activity'; -import { NewCodePeriod, NewCodePeriodSettingType } from '../../../types/types'; import { getSettingValue, validateSetting } from '../utils'; import BaselineSettingAnalysis from './BaselineSettingAnalysis'; import BaselineSettingReferenceBranch from './BaselineSettingReferenceBranch'; @@ -39,7 +40,9 @@ interface Props { branch: BranchWithNewCodePeriod; branchList: Branch[]; component: string; - onClose: (branch?: string, newSetting?: NewCodePeriod) => void; + onClose: (branch?: string, newSetting?: NewCodeDefinition) => void; + inheritedSetting: NewCodeDefinition; + generalSetting: NewCodeDefinition; } interface State { @@ -48,7 +51,7 @@ interface State { days: string; referenceBranch: string; saving: boolean; - selected?: NewCodePeriodSettingType; + selected?: NewCodeDefinitionType; } export default class BranchBaselineSettingModal extends React.PureComponent<Props, State> { @@ -57,16 +60,19 @@ export default class BranchBaselineSettingModal extends React.PureComponent<Prop constructor(props: Props) { super(props); - const otherBranches = props.branchList.filter((b) => b.name !== props.branch.name); + const { branch, branchList, inheritedSetting, generalSetting } = props; + const otherBranches = branchList.filter((b) => b.name !== branch.name); const defaultBranch = otherBranches.length > 0 ? otherBranches[0].name : ''; this.state = { - analysis: this.getValueFromProps(NewCodePeriodSettingType.SPECIFIC_ANALYSIS) || '', - days: this.getValueFromProps(NewCodePeriodSettingType.NUMBER_OF_DAYS) || '30', + analysis: this.getValueFromProps(NewCodeDefinitionType.SpecificAnalysis) || '', + days: + this.getValueFromProps(NewCodeDefinitionType.NumberOfDays) || + getNumberOfDaysDefaultValue(generalSetting, inheritedSetting), referenceBranch: - this.getValueFromProps(NewCodePeriodSettingType.REFERENCE_BRANCH) || defaultBranch, + this.getValueFromProps(NewCodeDefinitionType.ReferenceBranch) || defaultBranch, saving: false, - selected: this.props.branch.newCodePeriod && this.props.branch.newCodePeriod.type, + selected: branch.newCodePeriod?.type, }; } @@ -78,7 +84,7 @@ export default class BranchBaselineSettingModal extends React.PureComponent<Prop this.mounted = false; } - getValueFromProps(type: NewCodePeriodSettingType) { + getValueFromProps(type: NewCodeDefinitionType) { return this.props.branch.newCodePeriod && this.props.branch.newCodePeriod.type === type ? this.props.branch.newCodePeriod.value : null; @@ -108,19 +114,23 @@ export default class BranchBaselineSettingModal extends React.PureComponent<Prop branch: branch.name, }).then( () => { - this.setState({ - saving: false, - }); - this.props.onClose(branch.name, { - type, - value, - effectiveValue: analysisDate && toISO8601WithOffsetString(analysisDate), - }); + if (this.mounted) { + this.setState({ + saving: false, + }); + this.props.onClose(branch.name, { + type, + value, + effectiveValue: analysisDate && toISO8601WithOffsetString(analysisDate), + }); + } }, () => { - this.setState({ - saving: false, - }); + if (this.mounted) { + this.setState({ + saving: false, + }); + } } ); } @@ -135,7 +145,7 @@ export default class BranchBaselineSettingModal extends React.PureComponent<Prop handleSelectReferenceBranch = (referenceBranch: string) => this.setState({ referenceBranch }); - handleSelectSetting = (selected: NewCodePeriodSettingType) => this.setState({ selected }); + handleSelectSetting = (selected: NewCodeDefinitionType) => this.setState({ selected }); render() { const { branch, branchList } = this.props; @@ -143,8 +153,8 @@ export default class BranchBaselineSettingModal extends React.PureComponent<Prop const header = translateWithParameters('baseline.new_code_period_for_branch_x', branch.name); - const currentSetting = branch.newCodePeriod && branch.newCodePeriod.type; - const currentSettingValue = branch.newCodePeriod && branch.newCodePeriod.value; + const currentSetting = branch.newCodePeriod?.type; + const currentSettingValue = branch.newCodePeriod?.value; const { isChanged, isValid } = validateSetting({ analysis, @@ -173,7 +183,7 @@ export default class BranchBaselineSettingModal extends React.PureComponent<Prop <NewCodeDefinitionPreviousVersionOption isDefault={false} onSelect={this.handleSelectSetting} - selected={selected === NewCodePeriodSettingType.PREVIOUS_VERSION} + selected={selected === NewCodeDefinitionType.PreviousVersion} /> <NewCodeDefinitionDaysOption days={days} @@ -181,24 +191,24 @@ export default class BranchBaselineSettingModal extends React.PureComponent<Prop isValid={isValid} onChangeDays={this.handleSelectDays} onSelect={this.handleSelectSetting} - selected={selected === NewCodePeriodSettingType.NUMBER_OF_DAYS} + selected={selected === NewCodeDefinitionType.NumberOfDays} /> <BaselineSettingReferenceBranch branchList={branchList.map(this.branchToOption)} onChangeReferenceBranch={this.handleSelectReferenceBranch} onSelect={this.handleSelectSetting} referenceBranch={referenceBranch} - selected={selected === NewCodePeriodSettingType.REFERENCE_BRANCH} + selected={selected === NewCodeDefinitionType.ReferenceBranch} settingLevel="branch" /> - {currentSetting === NewCodePeriodSettingType.SPECIFIC_ANALYSIS && ( + {currentSetting === NewCodeDefinitionType.SpecificAnalysis && ( <BaselineSettingAnalysis onSelect={() => {}} - selected={selected === NewCodePeriodSettingType.SPECIFIC_ANALYSIS} + selected={selected === NewCodeDefinitionType.SpecificAnalysis} /> )} </div> - {selected === NewCodePeriodSettingType.SPECIFIC_ANALYSIS && ( + {selected === NewCodeDefinitionType.SpecificAnalysis && ( <BranchAnalysisList analysis={analysis} branch={branch.name} diff --git a/server/sonar-web/src/main/js/apps/projectBaseline/components/BranchList.tsx b/server/sonar-web/src/main/js/apps/projectBaseline/components/BranchList.tsx index 7d9943686e5..f8e5ab4c6ac 100644 --- a/server/sonar-web/src/main/js/apps/projectBaseline/components/BranchList.tsx +++ b/server/sonar-web/src/main/js/apps/projectBaseline/components/BranchList.tsx @@ -22,16 +22,18 @@ import { listBranchesNewCodePeriod, resetNewCodePeriod } from '../../../api/newC import DeferredSpinner from '../../../components/ui/DeferredSpinner'; import { isBranch, sortBranches } from '../../../helpers/branch-like'; import { translate } from '../../../helpers/l10n'; +import { DEFAULT_NEW_CODE_DEFINITION_TYPE } from '../../../helpers/new-code-definition'; import { Branch, BranchLike, BranchWithNewCodePeriod } from '../../../types/branch-like'; -import { Component, NewCodePeriod } from '../../../types/types'; -import { DEFAULT_GENERAL_SETTING_TYPE } from '../constants'; +import { NewCodeDefinition } from '../../../types/new-code-definition'; +import { Component } from '../../../types/types'; import BranchBaselineSettingModal from './BranchBaselineSettingModal'; import BranchListRow from './BranchListRow'; interface Props { branchList: Branch[]; component: Component; - inheritedSetting: NewCodePeriod; + inheritedSetting: NewCodeDefinition; + generalSetting: NewCodeDefinition; } interface State { @@ -75,7 +77,7 @@ export default class BranchList extends React.PureComponent<Props, State> { if (!newCodePeriod) { return b; } - const { type = DEFAULT_GENERAL_SETTING_TYPE, value, effectiveValue } = newCodePeriod; + const { type = DEFAULT_NEW_CODE_DEFINITION_TYPE, value, effectiveValue } = newCodePeriod; return { ...b, newCodePeriod: { type, value, effectiveValue }, @@ -90,7 +92,7 @@ export default class BranchList extends React.PureComponent<Props, State> { ); } - updateBranchNewCodePeriod = (branch: string, newSetting: NewCodePeriod | undefined) => { + updateBranchNewCodePeriod = (branch: string, newSetting: NewCodeDefinition | undefined) => { const { branches } = this.state; const updated = branches.find((b) => b.name === branch); @@ -104,7 +106,7 @@ export default class BranchList extends React.PureComponent<Props, State> { this.setState({ editedBranch: branch }); }; - closeEditModal = (branch?: string, newSetting?: NewCodePeriod) => { + closeEditModal = (branch?: string, newSetting?: NewCodeDefinition) => { if (branch) { this.setState({ branches: this.updateBranchNewCodePeriod(branch, newSetting), @@ -125,7 +127,7 @@ export default class BranchList extends React.PureComponent<Props, State> { }; render() { - const { branchList, inheritedSetting } = this.props; + const { branchList, inheritedSetting, generalSetting } = this.props; const { branches, editedBranch, loading } = this.state; if (branches.length < 1) { @@ -167,6 +169,8 @@ export default class BranchList extends React.PureComponent<Props, State> { branchList={branchList} component={this.props.component.key} onClose={this.closeEditModal} + inheritedSetting={inheritedSetting} + generalSetting={generalSetting} /> )} </> diff --git a/server/sonar-web/src/main/js/apps/projectBaseline/components/BranchListRow.tsx b/server/sonar-web/src/main/js/apps/projectBaseline/components/BranchListRow.tsx index 39197e19dd2..401c22eac16 100644 --- a/server/sonar-web/src/main/js/apps/projectBaseline/components/BranchListRow.tsx +++ b/server/sonar-web/src/main/js/apps/projectBaseline/components/BranchListRow.tsx @@ -24,21 +24,21 @@ import BranchLikeIcon from '../../../components/icons/BranchLikeIcon'; import WarningIcon from '../../../components/icons/WarningIcon'; import DateTimeFormatter from '../../../components/intl/DateTimeFormatter'; import { translate, translateWithParameters } from '../../../helpers/l10n'; -import { isNewCodeDefinitionCompliant } from '../../../helpers/periods'; +import { isNewCodeDefinitionCompliant } from '../../../helpers/new-code-definition'; import { BranchWithNewCodePeriod } from '../../../types/branch-like'; -import { NewCodePeriod, NewCodePeriodSettingType } from '../../../types/types'; +import { NewCodeDefinition, NewCodeDefinitionType } from '../../../types/new-code-definition'; export interface BranchListRowProps { branch: BranchWithNewCodePeriod; existingBranches: Array<string>; - inheritedSetting: NewCodePeriod; + inheritedSetting: NewCodeDefinition; onOpenEditModal: (branch: BranchWithNewCodePeriod) => void; onResetToDefault: (branchName: string) => void; } -function renderNewCodePeriodSetting(newCodePeriod: NewCodePeriod) { +function renderNewCodePeriodSetting(newCodePeriod: NewCodeDefinition) { switch (newCodePeriod.type) { - case NewCodePeriodSettingType.SPECIFIC_ANALYSIS: + case NewCodeDefinitionType.SpecificAnalysis: return ( <> {`${translate('baseline.specific_analysis')}: `} @@ -49,11 +49,11 @@ function renderNewCodePeriodSetting(newCodePeriod: NewCodePeriod) { )} </> ); - case NewCodePeriodSettingType.NUMBER_OF_DAYS: + case NewCodeDefinitionType.NumberOfDays: return `${translate('new_code_definition.number_days')}: ${newCodePeriod.value}`; - case NewCodePeriodSettingType.PREVIOUS_VERSION: + case NewCodeDefinitionType.PreviousVersion: return translate('new_code_definition.previous_version'); - case NewCodePeriodSettingType.REFERENCE_BRANCH: + case NewCodeDefinitionType.ReferenceBranch: return `${translate('baseline.reference_branch')}: ${newCodePeriod.value}`; default: return newCodePeriod.type; @@ -62,11 +62,11 @@ function renderNewCodePeriodSetting(newCodePeriod: NewCodePeriod) { function branchInheritsItselfAsReference( branch: BranchWithNewCodePeriod, - inheritedSetting: NewCodePeriod + inheritedSetting: NewCodeDefinition ) { return ( !branch.newCodePeriod && - inheritedSetting.type === NewCodePeriodSettingType.REFERENCE_BRANCH && + inheritedSetting.type === NewCodeDefinitionType.ReferenceBranch && branch.name === inheritedSetting.value ); } @@ -78,7 +78,7 @@ function referenceBranchDoesNotExist( return ( branch.newCodePeriod && branch.newCodePeriod.value && - branch.newCodePeriod.type === NewCodePeriodSettingType.REFERENCE_BRANCH && + branch.newCodePeriod.type === NewCodeDefinitionType.ReferenceBranch && !existingBranches.includes(branch.newCodePeriod.value) ); } diff --git a/server/sonar-web/src/main/js/apps/projectBaseline/components/ProjectBaselineApp.tsx b/server/sonar-web/src/main/js/apps/projectBaseline/components/ProjectBaselineApp.tsx index 0b3f8674abd..5d185dd51ca 100644 --- a/server/sonar-web/src/main/js/apps/projectBaseline/components/ProjectBaselineApp.tsx +++ b/server/sonar-web/src/main/js/apps/projectBaseline/components/ProjectBaselineApp.tsx @@ -32,12 +32,16 @@ import AlertSuccessIcon from '../../../components/icons/AlertSuccessIcon'; import DeferredSpinner from '../../../components/ui/DeferredSpinner'; import { isBranch, sortBranches } from '../../../helpers/branch-like'; import { translate } from '../../../helpers/l10n'; +import { + DEFAULT_NEW_CODE_DEFINITION_TYPE, + getNumberOfDaysDefaultValue, +} from '../../../helpers/new-code-definition'; import { AppState } from '../../../types/appstate'; import { Branch, BranchLike } from '../../../types/branch-like'; import { Feature } from '../../../types/features'; +import { NewCodeDefinition, NewCodeDefinitionType } from '../../../types/new-code-definition'; import { ParsedAnalysis } from '../../../types/project-activity'; -import { Component, NewCodePeriod, NewCodePeriodSettingType } from '../../../types/types'; -import { DEFAULT_GENERAL_SETTING_TYPE } from '../constants'; +import { Component } from '../../../types/types'; import '../styles.css'; import { getSettingValue } from '../utils'; import AppHeader from './AppHeader'; @@ -54,25 +58,23 @@ interface Props extends WithAvailableFeaturesProps { interface State { analysis?: string; branchList: Branch[]; - currentSetting?: NewCodePeriodSettingType; + currentSetting?: NewCodeDefinitionType; currentSettingValue?: string; days: string; - generalSetting?: NewCodePeriod; + generalSetting?: NewCodeDefinition; loading: boolean; overrideGeneralSetting?: boolean; referenceBranch?: string; saving: boolean; - selected?: NewCodePeriodSettingType; + selected?: NewCodeDefinitionType; success?: boolean; } -const DEFAULT_NUMBER_OF_DAYS = '30'; - class ProjectBaselineApp extends React.PureComponent<Props, State> { mounted = false; state: State = { branchList: [], - days: DEFAULT_NUMBER_OF_DAYS, + days: getNumberOfDaysDefaultValue(), loading: true, saving: false, }; @@ -97,16 +99,14 @@ class ProjectBaselineApp extends React.PureComponent<Props, State> { } getUpdatedState(params: { - currentSetting?: NewCodePeriodSettingType; + currentSetting?: NewCodeDefinitionType; currentSettingValue?: string; - generalSetting: NewCodePeriod; + generalSetting: NewCodeDefinition; }) { const { currentSetting, currentSettingValue, generalSetting } = params; const { referenceBranch } = this.state; - const defaultDays = - (generalSetting.type === NewCodePeriodSettingType.NUMBER_OF_DAYS && generalSetting.value) || - DEFAULT_NUMBER_OF_DAYS; + const defaultDays = getNumberOfDaysDefaultValue(generalSetting); return { loading: false, @@ -116,13 +116,12 @@ class ProjectBaselineApp extends React.PureComponent<Props, State> { selected: currentSetting || generalSetting.type, overrideGeneralSetting: Boolean(currentSetting), days: - (currentSetting === NewCodePeriodSettingType.NUMBER_OF_DAYS && currentSettingValue) || + (currentSetting === NewCodeDefinitionType.NumberOfDays && currentSettingValue) || defaultDays, analysis: - (currentSetting === NewCodePeriodSettingType.SPECIFIC_ANALYSIS && currentSettingValue) || - '', + (currentSetting === NewCodeDefinitionType.SpecificAnalysis && currentSettingValue) || '', referenceBranch: - (currentSetting === NewCodePeriodSettingType.REFERENCE_BRANCH && currentSettingValue) || + (currentSetting === NewCodeDefinitionType.ReferenceBranch && currentSettingValue) || referenceBranch, }; } @@ -147,12 +146,12 @@ class ProjectBaselineApp extends React.PureComponent<Props, State> { ([generalSetting, setting]) => { if (this.mounted) { if (!generalSetting.type) { - generalSetting = { type: DEFAULT_GENERAL_SETTING_TYPE }; + generalSetting = { type: DEFAULT_NEW_CODE_DEFINITION_TYPE }; } const currentSettingValue = setting.value; const currentSetting = setting.inherited ? undefined - : setting.type || DEFAULT_GENERAL_SETTING_TYPE; + : setting.type || DEFAULT_NEW_CODE_DEFINITION_TYPE; this.setState( this.getUpdatedState({ @@ -198,13 +197,13 @@ class ProjectBaselineApp extends React.PureComponent<Props, State> { handleCancel = () => this.setState( ({ - generalSetting = { type: DEFAULT_GENERAL_SETTING_TYPE }, + generalSetting = { type: DEFAULT_NEW_CODE_DEFINITION_TYPE }, currentSetting, currentSettingValue, }) => this.getUpdatedState({ generalSetting, currentSetting, currentSettingValue }) ); - handleSelectSetting = (selected?: NewCodePeriodSettingType) => this.setState({ selected }); + handleSelectSetting = (selected?: NewCodeDefinitionType) => this.setState({ selected }); handleToggleSpecificSetting = (overrideGeneralSetting: boolean) => this.setState({ overrideGeneralSetting }); @@ -322,6 +321,7 @@ class ProjectBaselineApp extends React.PureComponent<Props, State> { } : generalSetting } + generalSetting={generalSetting} /> </div> )} diff --git a/server/sonar-web/src/main/js/apps/projectBaseline/components/ProjectBaselineSelector.tsx b/server/sonar-web/src/main/js/apps/projectBaseline/components/ProjectBaselineSelector.tsx index f20cc30acfc..39f6c718421 100644 --- a/server/sonar-web/src/main/js/apps/projectBaseline/components/ProjectBaselineSelector.tsx +++ b/server/sonar-web/src/main/js/apps/projectBaseline/components/ProjectBaselineSelector.tsx @@ -29,10 +29,10 @@ import NewCodeDefinitionWarning from '../../../components/new-code-definition/Ne import { Alert } from '../../../components/ui/Alert'; import DeferredSpinner from '../../../components/ui/DeferredSpinner'; import { translate } from '../../../helpers/l10n'; -import { isNewCodeDefinitionCompliant } from '../../../helpers/periods'; +import { isNewCodeDefinitionCompliant } from '../../../helpers/new-code-definition'; import { Branch } from '../../../types/branch-like'; +import { NewCodeDefinition, NewCodeDefinitionType } from '../../../types/new-code-definition'; import { ParsedAnalysis } from '../../../types/project-activity'; -import { NewCodePeriod, NewCodePeriodSettingType } from '../../../types/types'; import { validateSetting } from '../utils'; import BaselineSettingAnalysis from './BaselineSettingAnalysis'; import BaselineSettingReferenceBranch from './BaselineSettingReferenceBranch'; @@ -45,20 +45,20 @@ export interface ProjectBaselineSelectorProps { branchesEnabled?: boolean; canAdmin: boolean | undefined; component: string; - currentSetting?: NewCodePeriodSettingType; + currentSetting?: NewCodeDefinitionType; currentSettingValue?: string; days: string; - generalSetting: NewCodePeriod; + generalSetting: NewCodeDefinition; onCancel: () => void; onSelectAnalysis: (analysis: ParsedAnalysis) => void; onSelectDays: (value: string) => void; onSelectReferenceBranch: (value: string) => void; - onSelectSetting: (value?: NewCodePeriodSettingType) => void; + onSelectSetting: (value?: NewCodeDefinitionType) => void; onSubmit: (e: React.SyntheticEvent<HTMLFormElement>) => void; onToggleSpecificSetting: (selection: boolean) => void; referenceBranch?: string; saving: boolean; - selected?: NewCodePeriodSettingType; + selected?: NewCodeDefinitionType; overrideGeneralSetting: boolean; } @@ -146,9 +146,7 @@ export default function ProjectBaselineSelector(props: ProjectBaselineSelectorPr <NewCodeDefinitionPreviousVersionOption disabled={!overrideGeneralSetting} onSelect={props.onSelectSetting} - selected={ - overrideGeneralSetting && selected === NewCodePeriodSettingType.PREVIOUS_VERSION - } + selected={overrideGeneralSetting && selected === NewCodeDefinitionType.PreviousVersion} /> <NewCodeDefinitionDaysOption days={days} @@ -157,9 +155,7 @@ export default function ProjectBaselineSelector(props: ProjectBaselineSelectorPr isValid={isValid} onChangeDays={props.onSelectDays} onSelect={props.onSelectSetting} - selected={ - overrideGeneralSetting && selected === NewCodePeriodSettingType.NUMBER_OF_DAYS - } + selected={overrideGeneralSetting && selected === NewCodeDefinitionType.NumberOfDays} /> {branchesEnabled && ( <BaselineSettingReferenceBranch @@ -169,23 +165,23 @@ export default function ProjectBaselineSelector(props: ProjectBaselineSelectorPr onSelect={props.onSelectSetting} referenceBranch={referenceBranch || ''} selected={ - overrideGeneralSetting && selected === NewCodePeriodSettingType.REFERENCE_BRANCH + overrideGeneralSetting && selected === NewCodeDefinitionType.ReferenceBranch } settingLevel="project" /> )} - {!branchesEnabled && currentSetting === NewCodePeriodSettingType.SPECIFIC_ANALYSIS && ( + {!branchesEnabled && currentSetting === NewCodeDefinitionType.SpecificAnalysis && ( <BaselineSettingAnalysis onSelect={() => {}} selected={ - overrideGeneralSetting && selected === NewCodePeriodSettingType.SPECIFIC_ANALYSIS + overrideGeneralSetting && selected === NewCodeDefinitionType.SpecificAnalysis } /> )} </div> {!branchesEnabled && overrideGeneralSetting && - selected === NewCodePeriodSettingType.SPECIFIC_ANALYSIS && ( + selected === NewCodeDefinitionType.SpecificAnalysis && ( <BranchAnalysisList analysis={analysis || ''} branch={branch.name} 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 b10faa7cdec..edcad7d025b 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 @@ -25,7 +25,7 @@ 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 { mockNewCodePeriodBranch } from '../../../../helpers/mocks/new-code-definition'; import { mockAppState } from '../../../../helpers/testMocks'; import { RenderContext, @@ -33,7 +33,7 @@ import { } from '../../../../helpers/testReactTestingUtils'; import { byRole, byText } from '../../../../helpers/testSelector'; import { Feature } from '../../../../types/features'; -import { NewCodePeriodSettingType } from '../../../../types/types'; +import { NewCodeDefinitionType } from '../../../../types/new-code-definition'; import routes from '../../routes'; jest.mock('../../../../api/newCodePeriod'); @@ -65,7 +65,7 @@ it('renders correctly without branch support feature', async () => { it('prevents selection of global setting if it is not compliant and warns non-admin about it', async () => { codePeriodsMock.setNewCodePeriod({ - type: NewCodePeriodSettingType.NUMBER_OF_DAYS, + type: NewCodeDefinitionType.NumberOfDays, value: '99', inherited: true, }); @@ -81,7 +81,7 @@ it('prevents selection of global setting if it is not compliant and warns non-ad it('prevents selection of global setting if it is not compliant and warns admin about it', async () => { codePeriodsMock.setNewCodePeriod({ - type: NewCodePeriodSettingType.NUMBER_OF_DAYS, + type: NewCodeDefinitionType.NumberOfDays, value: '99', inherited: true, }); @@ -177,7 +177,7 @@ it('can set reference branch specific setting', async () => { it('cannot set specific analysis setting', async () => { const { ui } = getPageObjects(); codePeriodsMock.setNewCodePeriod({ - type: NewCodePeriodSettingType.SPECIFIC_ANALYSIS, + type: NewCodeDefinitionType.SpecificAnalysis, value: 'analysis_id', }); renderProjectBaselineApp(); @@ -246,7 +246,7 @@ it('cannot set a specific analysis setting for branch', async () => { codePeriodsMock.setListBranchesNewCode([ mockNewCodePeriodBranch({ branchKey: 'main', - type: NewCodePeriodSettingType.SPECIFIC_ANALYSIS, + type: NewCodeDefinitionType.SpecificAnalysis, value: 'analysis_id', }), ]); 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 c9b2640b83c..1f50739f93c 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 @@ -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 { NewCodePeriodSettingType } from '../../../../types/types'; +import { NewCodeDefinitionType } from '../../../../types/new-code-definition'; import { getSettingValue, validateSetting } from '../../utils'; describe('getSettingValue', () => { @@ -28,25 +28,25 @@ describe('getSettingValue', () => { }; it('should work for Days', () => { - expect(getSettingValue({ ...state, type: NewCodePeriodSettingType.NUMBER_OF_DAYS })).toBe( + expect(getSettingValue({ ...state, type: NewCodeDefinitionType.NumberOfDays })).toBe( state.days ); }); it('should work for Analysis', () => { - expect(getSettingValue({ ...state, type: NewCodePeriodSettingType.SPECIFIC_ANALYSIS })).toBe( + expect(getSettingValue({ ...state, type: NewCodeDefinitionType.SpecificAnalysis })).toBe( state.analysis ); }); it('should work for Previous version', () => { expect( - getSettingValue({ ...state, type: NewCodePeriodSettingType.PREVIOUS_VERSION }) + getSettingValue({ ...state, type: NewCodeDefinitionType.PreviousVersion }) ).toBeUndefined(); }); it('should work for Reference branch', () => { - expect(getSettingValue({ ...state, type: NewCodePeriodSettingType.REFERENCE_BRANCH })).toBe( + expect(getSettingValue({ ...state, type: NewCodeDefinitionType.ReferenceBranch })).toBe( state.referenceBranch ); }); @@ -57,68 +57,68 @@ describe('validateSettings', () => { expect(validateSetting({ days: '' })).toEqual({ isChanged: false, isValid: false }); expect( validateSetting({ - currentSetting: NewCodePeriodSettingType.PREVIOUS_VERSION, + currentSetting: NewCodeDefinitionType.PreviousVersion, days: '12', - selected: NewCodePeriodSettingType.NUMBER_OF_DAYS, + selected: NewCodeDefinitionType.NumberOfDays, }) ).toEqual({ isChanged: true, isValid: true }); expect( validateSetting({ - currentSetting: NewCodePeriodSettingType.PREVIOUS_VERSION, + currentSetting: NewCodeDefinitionType.PreviousVersion, days: 'nope', - selected: NewCodePeriodSettingType.NUMBER_OF_DAYS, + selected: NewCodeDefinitionType.NumberOfDays, }) ).toEqual({ isChanged: true, isValid: false }); expect( validateSetting({ - currentSetting: NewCodePeriodSettingType.NUMBER_OF_DAYS, + currentSetting: NewCodeDefinitionType.NumberOfDays, currentSettingValue: '15', days: '15', - selected: NewCodePeriodSettingType.NUMBER_OF_DAYS, + selected: NewCodeDefinitionType.NumberOfDays, }) ).toEqual({ isChanged: false, isValid: true }); expect( validateSetting({ - currentSetting: NewCodePeriodSettingType.NUMBER_OF_DAYS, + currentSetting: NewCodeDefinitionType.NumberOfDays, currentSettingValue: '15', days: '13', - selected: NewCodePeriodSettingType.NUMBER_OF_DAYS, + selected: NewCodeDefinitionType.NumberOfDays, }) ).toEqual({ isChanged: true, isValid: true }); expect( validateSetting({ analysis: 'analysis1', - currentSetting: NewCodePeriodSettingType.SPECIFIC_ANALYSIS, + currentSetting: NewCodeDefinitionType.SpecificAnalysis, currentSettingValue: 'analysis1', days: '', - selected: NewCodePeriodSettingType.SPECIFIC_ANALYSIS, + selected: NewCodeDefinitionType.SpecificAnalysis, }) ).toEqual({ isChanged: false, isValid: false }); expect( validateSetting({ analysis: 'analysis2', - currentSetting: NewCodePeriodSettingType.SPECIFIC_ANALYSIS, + currentSetting: NewCodeDefinitionType.SpecificAnalysis, currentSettingValue: 'analysis1', days: '', - selected: NewCodePeriodSettingType.SPECIFIC_ANALYSIS, + selected: NewCodeDefinitionType.SpecificAnalysis, }) ).toEqual({ isChanged: true, isValid: false }); expect( validateSetting({ - currentSetting: NewCodePeriodSettingType.REFERENCE_BRANCH, + currentSetting: NewCodeDefinitionType.ReferenceBranch, currentSettingValue: 'master', days: '', referenceBranch: 'master', - selected: NewCodePeriodSettingType.REFERENCE_BRANCH, + selected: NewCodeDefinitionType.ReferenceBranch, }) ).toEqual({ isChanged: false, isValid: true }); expect( validateSetting({ - currentSetting: NewCodePeriodSettingType.REFERENCE_BRANCH, + currentSetting: NewCodeDefinitionType.ReferenceBranch, currentSettingValue: 'master', days: '', referenceBranch: '', - selected: NewCodePeriodSettingType.REFERENCE_BRANCH, + selected: NewCodeDefinitionType.ReferenceBranch, }) ).toEqual({ isChanged: true, isValid: false }); }); @@ -134,7 +134,7 @@ describe('validateSettings', () => { }); expect( validateSetting({ - currentSetting: NewCodePeriodSettingType.PREVIOUS_VERSION, + currentSetting: NewCodeDefinitionType.PreviousVersion, days: '', overrideGeneralSetting: false, }) 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 f1dc41c7056..6a1076de85b 100644 --- a/server/sonar-web/src/main/js/apps/projectBaseline/utils.ts +++ b/server/sonar-web/src/main/js/apps/projectBaseline/utils.ts @@ -17,8 +17,8 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { isNewCodeDefinitionCompliant } from '../../helpers/periods'; -import { NewCodePeriodSettingType } from '../../types/types'; +import { isNewCodeDefinitionCompliant } from '../../helpers/new-code-definition'; +import { NewCodeDefinitionType } from '../../types/new-code-definition'; export function getSettingValue({ analysis, @@ -29,14 +29,14 @@ export function getSettingValue({ analysis?: string; days?: string; referenceBranch?: string; - type?: NewCodePeriodSettingType; + type?: NewCodeDefinitionType; }) { switch (type) { - case NewCodePeriodSettingType.NUMBER_OF_DAYS: + case NewCodeDefinitionType.NumberOfDays: return days; - case NewCodePeriodSettingType.REFERENCE_BRANCH: + case NewCodeDefinitionType.ReferenceBranch: return referenceBranch; - case NewCodePeriodSettingType.SPECIFIC_ANALYSIS: + case NewCodeDefinitionType.SpecificAnalysis: return analysis; default: return undefined; @@ -45,12 +45,12 @@ export function getSettingValue({ export function validateSetting(state: { analysis?: string; - currentSetting?: NewCodePeriodSettingType; + currentSetting?: NewCodeDefinitionType; currentSettingValue?: string; days: string; overrideGeneralSetting?: boolean; referenceBranch?: string; - selected?: NewCodePeriodSettingType; + selected?: NewCodeDefinitionType; }) { const { analysis = '', @@ -69,10 +69,9 @@ export function validateSetting(state: { isChanged = overrideGeneralSetting === false || selected !== currentSetting || - (selected === NewCodePeriodSettingType.NUMBER_OF_DAYS && days !== currentSettingValue) || - (selected === NewCodePeriodSettingType.SPECIFIC_ANALYSIS && - analysis !== currentSettingValue) || - (selected === NewCodePeriodSettingType.REFERENCE_BRANCH && + (selected === NewCodeDefinitionType.NumberOfDays && days !== currentSettingValue) || + (selected === NewCodeDefinitionType.SpecificAnalysis && analysis !== currentSettingValue) || + (selected === NewCodeDefinitionType.ReferenceBranch && referenceBranch !== currentSettingValue); } @@ -83,8 +82,8 @@ export function validateSetting(state: { type: selected, value: days, }) && - (selected !== NewCodePeriodSettingType.SPECIFIC_ANALYSIS || analysis.length > 0) && - (selected !== NewCodePeriodSettingType.REFERENCE_BRANCH || referenceBranch.length > 0)); + (selected !== NewCodeDefinitionType.SpecificAnalysis || analysis.length > 0) && + (selected !== NewCodeDefinitionType.ReferenceBranch || 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 96498f2fd8a..7dd692db76f 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 @@ -28,16 +28,19 @@ import NewCodeDefinitionPreviousVersionOption from '../../../components/new-code 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 { + getNumberOfDaysDefaultValue, + isNewCodeDefinitionCompliant, +} from '../../../helpers/new-code-definition'; +import { NewCodeDefinitionType } from '../../../types/new-code-definition'; interface State { - currentSetting?: NewCodePeriodSettingType; + currentSetting?: NewCodeDefinitionType; days: string; loading: boolean; currentSettingValue?: string; saving: boolean; - selected?: NewCodePeriodSettingType; + selected?: NewCodeDefinitionType; success: boolean; } @@ -45,7 +48,7 @@ export default class NewCodePeriod extends React.PureComponent<{}, State> { mounted = false; state: State = { loading: true, - days: '30', + days: getNumberOfDaysDefaultValue(), saving: false, success: false, }; @@ -64,7 +67,7 @@ export default class NewCodePeriod extends React.PureComponent<{}, State> { .then(({ type, value }) => { this.setState(({ days }) => ({ currentSetting: type, - days: type === NewCodePeriodSettingType.NUMBER_OF_DAYS ? String(value) : days, + days: type === NewCodeDefinitionType.NumberOfDays ? String(value) : days, loading: false, currentSettingValue: value, selected: type, @@ -79,7 +82,7 @@ export default class NewCodePeriod extends React.PureComponent<{}, State> { this.setState({ days, success: false }); }; - onSelectSetting = (selected: NewCodePeriodSettingType) => { + onSelectSetting = (selected: NewCodeDefinitionType) => { this.setState({ selected, success: false }); }; @@ -87,9 +90,7 @@ export default class NewCodePeriod extends React.PureComponent<{}, State> { this.setState(({ currentSetting, currentSettingValue, days }) => ({ selected: currentSetting, days: - currentSetting === NewCodePeriodSettingType.NUMBER_OF_DAYS - ? String(currentSettingValue) - : days, + currentSetting === NewCodeDefinitionType.NumberOfDays ? String(currentSettingValue) : days, })); }; @@ -99,25 +100,29 @@ export default class NewCodePeriod extends React.PureComponent<{}, State> { const { days, selected } = this.state; const type = selected; - const value = type === NewCodePeriodSettingType.NUMBER_OF_DAYS ? days : undefined; + const value = type === NewCodeDefinitionType.NumberOfDays ? days : undefined; this.setState({ saving: true, success: false }); setNewCodePeriod({ - type: type as NewCodePeriodSettingType, + type: type as NewCodeDefinitionType, value, }).then( () => { - this.setState({ - saving: false, - currentSetting: type, - currentSettingValue: value || undefined, - success: true, - }); + if (this.mounted) { + this.setState({ + saving: false, + currentSetting: type, + currentSettingValue: value || undefined, + success: true, + }); + } }, () => { - this.setState({ - saving: false, - }); + if (this.mounted) { + this.setState({ + saving: false, + }); + } } ); }; @@ -128,12 +133,11 @@ export default class NewCodePeriod extends React.PureComponent<{}, State> { const isChanged = selected !== currentSetting || - (selected === NewCodePeriodSettingType.NUMBER_OF_DAYS && - String(days) !== currentSettingValue); + (selected === NewCodeDefinitionType.NumberOfDays && String(days) !== currentSettingValue); const isValid = - selected !== NewCodePeriodSettingType.NUMBER_OF_DAYS || - isNewCodeDefinitionCompliant({ type: NewCodePeriodSettingType.NUMBER_OF_DAYS, value: days }); + selected !== NewCodeDefinitionType.NumberOfDays || + isNewCodeDefinitionCompliant({ type: NewCodeDefinitionType.NumberOfDays, value: days }); return ( <> @@ -187,7 +191,7 @@ export default class NewCodePeriod extends React.PureComponent<{}, State> { <NewCodeDefinitionPreviousVersionOption isDefault onSelect={this.onSelectSetting} - selected={selected === NewCodePeriodSettingType.PREVIOUS_VERSION} + selected={selected === NewCodeDefinitionType.PreviousVersion} /> <NewCodeDefinitionDaysOption className="spacer-top sw-mb-4" @@ -196,7 +200,7 @@ export default class NewCodePeriod extends React.PureComponent<{}, State> { isValid={isValid} onChangeDays={this.onSelectDays} onSelect={this.onSelectSetting} - selected={selected === NewCodePeriodSettingType.NUMBER_OF_DAYS} + selected={selected === NewCodeDefinitionType.NumberOfDays} /> <NewCodeDefinitionWarning newCodeDefinitionType={currentSetting} 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 f8aba024f2e..e541c4e645f 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 @@ -23,7 +23,7 @@ import * as React from 'react'; import NewCodePeriodsServiceMock from '../../../../api/mocks/NewCodePeriodsServiceMock'; import { renderComponent } from '../../../../helpers/testReactTestingUtils'; import { byRole, byText } from '../../../../helpers/testSelector'; -import { NewCodePeriodSettingType } from '../../../../types/types'; +import { NewCodeDefinitionType } from '../../../../types/new-code-definition'; import NewCodePeriod from '../NewCodePeriod'; let newCodeMock: NewCodePeriodsServiceMock; @@ -93,7 +93,7 @@ it('renders and behaves as expected', async () => { it('renders and behaves properly when the current value is not compliant', async () => { const user = userEvent.setup(); - newCodeMock.setNewCodePeriod({ type: NewCodePeriodSettingType.NUMBER_OF_DAYS, value: '91' }); + newCodeMock.setNewCodePeriod({ type: NewCodeDefinitionType.NumberOfDays, value: '91' }); renderNewCodePeriod(); expect(await ui.newCodeTitle.find()).toBeInTheDocument(); diff --git a/server/sonar-web/src/main/js/components/new-code-definition/GlobalNewCodeDefinitionDescription.tsx b/server/sonar-web/src/main/js/components/new-code-definition/GlobalNewCodeDefinitionDescription.tsx index cf41e0236f6..b8d3ad3898c 100644 --- a/server/sonar-web/src/main/js/components/new-code-definition/GlobalNewCodeDefinitionDescription.tsx +++ b/server/sonar-web/src/main/js/components/new-code-definition/GlobalNewCodeDefinitionDescription.tsx @@ -20,12 +20,12 @@ import * as React from 'react'; import { FormattedMessage } from 'react-intl'; import { translate, translateWithParameters } from '../../helpers/l10n'; -import { NewCodePeriod, NewCodePeriodSettingType } from '../../types/types'; +import { NewCodeDefinition, NewCodeDefinitionType } from '../../types/new-code-definition'; import Link from '../common/Link'; import { Alert } from '../ui/Alert'; interface Props { - globalNcd: NewCodePeriod; + globalNcd: NewCodeDefinition; isGlobalNcdCompliant: boolean; canAdmin?: boolean; } @@ -38,7 +38,7 @@ export default function GlobalNewCodeDefinitionDescription({ let setting: string; let description: string; let useCase: string; - if (globalNcd.type === NewCodePeriodSettingType.NUMBER_OF_DAYS) { + if (globalNcd.type === NewCodeDefinitionType.NumberOfDays) { setting = `${translate('new_code_definition.number_days')} (${translateWithParameters( 'duration.days', globalNcd.value ?? '?' diff --git a/server/sonar-web/src/main/js/components/new-code-definition/NewCodeDefinitionDaysOption.tsx b/server/sonar-web/src/main/js/components/new-code-definition/NewCodeDefinitionDaysOption.tsx index 3d9511bbfc0..a3b52e9f642 100644 --- a/server/sonar-web/src/main/js/components/new-code-definition/NewCodeDefinitionDaysOption.tsx +++ b/server/sonar-web/src/main/js/components/new-code-definition/NewCodeDefinitionDaysOption.tsx @@ -19,8 +19,11 @@ */ import * as React from 'react'; import { translate, translateWithParameters } from '../../helpers/l10n'; -import { MAX_NUMBER_OF_DAYS, MIN_NUMBER_OF_DAYS } from '../../helpers/periods'; -import { NewCodePeriodSettingType } from '../../types/types'; +import { + NUMBER_OF_DAYS_MAX_VALUE, + NUMBER_OF_DAYS_MIN_VALUE, +} from '../../helpers/new-code-definition'; +import { NewCodeDefinitionType } from '../../types/new-code-definition'; import RadioCard from '../controls/RadioCard'; import ValidationInput, { ValidationInputErrorPlacement } from '../controls/ValidationInput'; import MandatoryFieldsExplanation from '../ui/MandatoryFieldsExplanation'; @@ -32,7 +35,7 @@ export interface Props { isChanged: boolean; isValid: boolean; onChangeDays: (value: string) => void; - onSelect: (selection: NewCodePeriodSettingType) => void; + onSelect: (selection: NewCodeDefinitionType) => void; selected: boolean; } @@ -44,7 +47,7 @@ export default function NewCodeDefinitionDaysOption(props: Props) { noRadio className={className} disabled={disabled} - onClick={() => onSelect(NewCodePeriodSettingType.NUMBER_OF_DAYS)} + onClick={() => onSelect(NewCodeDefinitionType.NumberOfDays)} selected={selected} title={translate('new_code_definition.number_days')} > @@ -64,8 +67,8 @@ export default function NewCodeDefinitionDaysOption(props: Props) { errorPlacement={ValidationInputErrorPlacement.Bottom} error={translateWithParameters( 'new_code_definition.number_days.invalid', - MIN_NUMBER_OF_DAYS, - MAX_NUMBER_OF_DAYS + NUMBER_OF_DAYS_MIN_VALUE, + NUMBER_OF_DAYS_MAX_VALUE )} label={translate('new_code_definition.number_days.specify_days')} required diff --git a/server/sonar-web/src/main/js/components/new-code-definition/NewCodeDefinitionPreviousVersionOption.tsx b/server/sonar-web/src/main/js/components/new-code-definition/NewCodeDefinitionPreviousVersionOption.tsx index ec113fb06a1..60141f592e7 100644 --- a/server/sonar-web/src/main/js/components/new-code-definition/NewCodeDefinitionPreviousVersionOption.tsx +++ b/server/sonar-web/src/main/js/components/new-code-definition/NewCodeDefinitionPreviousVersionOption.tsx @@ -19,13 +19,13 @@ */ import * as React from 'react'; import { translate } from '../../helpers/l10n'; -import { NewCodePeriodSettingType } from '../../types/types'; +import { NewCodeDefinitionType } from '../../types/new-code-definition'; import RadioCard from '../controls/RadioCard'; interface Props { disabled?: boolean; isDefault?: boolean; - onSelect: (selection: NewCodePeriodSettingType) => void; + onSelect: (selection: NewCodeDefinitionType) => void; selected: boolean; } @@ -39,7 +39,7 @@ export default function NewCodeDefinitionPreviousVersionOption({ <RadioCard noRadio disabled={disabled} - onClick={() => onSelect(NewCodePeriodSettingType.PREVIOUS_VERSION)} + onClick={() => onSelect(NewCodeDefinitionType.PreviousVersion)} selected={selected} title={ translate('new_code_definition.previous_version') + diff --git a/server/sonar-web/src/main/js/components/new-code-definition/NewCodeDefinitionSelector.tsx b/server/sonar-web/src/main/js/components/new-code-definition/NewCodeDefinitionSelector.tsx index ef28eeff0fe..4a12a6a042a 100644 --- a/server/sonar-web/src/main/js/components/new-code-definition/NewCodeDefinitionSelector.tsx +++ b/server/sonar-web/src/main/js/components/new-code-definition/NewCodeDefinitionSelector.tsx @@ -20,15 +20,17 @@ import { RadioButton } from 'design-system/lib'; import { noop } from 'lodash'; import * as React from 'react'; -import { useEffect } from 'react'; import { getNewCodePeriod } from '../../api/newCodePeriod'; import { translate } from '../../helpers/l10n'; -import { isNewCodeDefinitionCompliant } from '../../helpers/periods'; import { - NewCodePeriod, - NewCodePeriodSettingType, - NewCodePeriodWithCompliance, -} from '../../types/types'; + getNumberOfDaysDefaultValue, + isNewCodeDefinitionCompliant, +} from '../../helpers/new-code-definition'; +import { + NewCodeDefinition, + NewCodeDefinitionType, + NewCodeDefinitiondWithCompliance, +} from '../../types/new-code-definition'; import RadioCard from '../controls/RadioCard'; import Tooltip from '../controls/Tooltip'; import { Alert } from '../ui/Alert'; @@ -38,28 +40,31 @@ import NewCodeDefinitionPreviousVersionOption from './NewCodeDefinitionPreviousV interface Props { canAdmin: boolean | undefined; - onNcdChanged: (ncd: NewCodePeriodWithCompliance) => void; + onNcdChanged: (ncd: NewCodeDefinitiondWithCompliance) => void; } -const INITIAL_DAYS = '30'; - export default function NewCodeDefinitionSelector(props: Props) { const { canAdmin, onNcdChanged } = props; - const [globalNcd, setGlobalNcd] = React.useState<NewCodePeriod | null>(null); - const [selectedNcdType, setSelectedNcdType] = React.useState<NewCodePeriodSettingType | null>( - null - ); - const [days, setDays] = React.useState<string>(INITIAL_DAYS); + const [globalNcd, setGlobalNcd] = React.useState<NewCodeDefinition | null>(null); + const [selectedNcdType, setSelectedNcdType] = React.useState<NewCodeDefinitionType | null>(null); + const [days, setDays] = React.useState<string>(''); - const iGlobalNcdCompliant = React.useMemo( + const isGlobalNcdCompliant = React.useMemo( () => Boolean(globalNcd && isNewCodeDefinitionCompliant(globalNcd)), [globalNcd] ); + const initialNumberOfDays = React.useMemo(() => { + const numberOfDays = getNumberOfDaysDefaultValue(globalNcd); + setDays(numberOfDays); + + return numberOfDays; + }, [globalNcd]); + const isChanged = React.useMemo( - () => selectedNcdType === NewCodePeriodSettingType.NUMBER_OF_DAYS && days !== INITIAL_DAYS, - [selectedNcdType, days] + () => selectedNcdType === NewCodeDefinitionType.NumberOfDays && days !== initialNumberOfDays, + [selectedNcdType, days, initialNumberOfDays] ); const isCompliant = React.useMemo( @@ -72,7 +77,7 @@ export default function NewCodeDefinitionSelector(props: Props) { [selectedNcdType, days] ); - useEffect(() => { + React.useEffect(() => { function fetchGlobalNcd() { getNewCodePeriod().then(setGlobalNcd, noop); } @@ -80,11 +85,11 @@ export default function NewCodeDefinitionSelector(props: Props) { fetchGlobalNcd(); }, []); - useEffect(() => { + React.useEffect(() => { if (selectedNcdType) { const type = - selectedNcdType === NewCodePeriodSettingType.INHERITED ? undefined : selectedNcdType; - const value = selectedNcdType === NewCodePeriodSettingType.NUMBER_OF_DAYS ? days : undefined; + selectedNcdType === NewCodeDefinitionType.Inherited ? undefined : selectedNcdType; + const value = selectedNcdType === NewCodeDefinitionType.NumberOfDays ? days : undefined; onNcdChanged({ isCompliant, type, value }); } }, [selectedNcdType, days, isCompliant, onNcdChanged]); @@ -97,15 +102,15 @@ export default function NewCodeDefinitionSelector(props: Props) { <div className="big-spacer-top spacer-bottom" role="radiogroup"> <RadioButton aria-label={translate('new_code_definition.global_setting')} - checked={selectedNcdType === NewCodePeriodSettingType.INHERITED} + checked={selectedNcdType === NewCodeDefinitionType.Inherited} className="big-spacer-bottom" - disabled={!iGlobalNcdCompliant} - onCheck={() => setSelectedNcdType(NewCodePeriodSettingType.INHERITED)} + disabled={!isGlobalNcdCompliant} + onCheck={() => setSelectedNcdType(NewCodeDefinitionType.Inherited)} value="general" > <Tooltip overlay={ - iGlobalNcdCompliant + isGlobalNcdCompliant ? null : translate('new_code_definition.compliance.warning.title.global') } @@ -118,7 +123,7 @@ export default function NewCodeDefinitionSelector(props: Props) { {globalNcd && ( <GlobalNewCodeDefinitionDescription globalNcd={globalNcd} - isGlobalNcdCompliant={iGlobalNcdCompliant} + isGlobalNcdCompliant={isGlobalNcdCompliant} canAdmin={canAdmin} /> )} @@ -126,11 +131,9 @@ export default function NewCodeDefinitionSelector(props: Props) { <RadioButton aria-label={translate('new_code_definition.specific_setting')} - checked={Boolean( - selectedNcdType && selectedNcdType !== NewCodePeriodSettingType.INHERITED - )} + checked={Boolean(selectedNcdType && selectedNcdType !== NewCodeDefinitionType.Inherited)} className="huge-spacer-top" - onCheck={() => setSelectedNcdType(NewCodePeriodSettingType.PREVIOUS_VERSION)} + onCheck={() => setSelectedNcdType(NewCodeDefinitionType.PreviousVersion)} value="specific" > {translate('new_code_definition.specific_setting')} @@ -141,31 +144,31 @@ export default function NewCodeDefinitionSelector(props: Props) { <div className="display-flex-row big-spacer-bottom" role="radiogroup"> <NewCodeDefinitionPreviousVersionOption disabled={Boolean( - !selectedNcdType || selectedNcdType === NewCodePeriodSettingType.INHERITED + !selectedNcdType || selectedNcdType === NewCodeDefinitionType.Inherited )} onSelect={setSelectedNcdType} - selected={selectedNcdType === NewCodePeriodSettingType.PREVIOUS_VERSION} + selected={selectedNcdType === NewCodeDefinitionType.PreviousVersion} /> <NewCodeDefinitionDaysOption days={days} disabled={Boolean( - !selectedNcdType || selectedNcdType === NewCodePeriodSettingType.INHERITED + !selectedNcdType || selectedNcdType === NewCodeDefinitionType.Inherited )} isChanged={isChanged} isValid={isCompliant} onChangeDays={setDays} onSelect={setSelectedNcdType} - selected={selectedNcdType === NewCodePeriodSettingType.NUMBER_OF_DAYS} + selected={selectedNcdType === NewCodeDefinitionType.NumberOfDays} /> <RadioCard noRadio disabled={Boolean( - !selectedNcdType || selectedNcdType === NewCodePeriodSettingType.INHERITED + !selectedNcdType || selectedNcdType === NewCodeDefinitionType.Inherited )} - onClick={() => setSelectedNcdType(NewCodePeriodSettingType.REFERENCE_BRANCH)} - selected={selectedNcdType === NewCodePeriodSettingType.REFERENCE_BRANCH} + onClick={() => setSelectedNcdType(NewCodeDefinitionType.ReferenceBranch)} + selected={selectedNcdType === NewCodeDefinitionType.ReferenceBranch} title={translate('new_code_definition.reference_branch')} > <div> @@ -173,7 +176,7 @@ export default function NewCodeDefinitionSelector(props: Props) { {translate('new_code_definition.reference_branch.description')} </p> <p className="sw-mb-4">{translate('new_code_definition.reference_branch.usecase')}</p> - {selectedNcdType === NewCodePeriodSettingType.REFERENCE_BRANCH && ( + {selectedNcdType === NewCodeDefinitionType.ReferenceBranch && ( <Alert variant="info"> {translate('new_code_definition.reference_branch.notice')} </Alert> 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 index dfbc9c9f503..60e23dcecae 100644 --- 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 @@ -20,13 +20,13 @@ import * as React from 'react'; import { translate } from '../../helpers/l10n'; -import { isNewCodeDefinitionCompliant } from '../../helpers/periods'; -import { NewCodePeriodSettingType } from '../../types/types'; +import { isNewCodeDefinitionCompliant } from '../../helpers/new-code-definition'; +import { NewCodeDefinitionType } from '../../types/new-code-definition'; import DocLink from '../common/DocLink'; import { Alert } from '../ui/Alert'; export interface NewCodeDefinitionWarningProps { - newCodeDefinitionType: NewCodePeriodSettingType | undefined; + newCodeDefinitionType: NewCodeDefinitionType | undefined; newCodeDefinitionValue: string | undefined; isBranchSupportEnabled: boolean | undefined; level: 'branch' | 'project' | 'global'; @@ -45,7 +45,7 @@ export default function NewCodeDefinitionWarning({ return null; } - if (newCodeDefinitionType === NewCodePeriodSettingType.SPECIFIC_ANALYSIS) { + if (newCodeDefinitionType === NewCodeDefinitionType.SpecificAnalysis) { return ( <Alert variant="warning" className="sw-mb-4 sw-max-w-[800px]"> <p className="sw-mb-2 sw-font-bold"> @@ -64,7 +64,7 @@ export default function NewCodeDefinitionWarning({ ); } - if (newCodeDefinitionType === NewCodePeriodSettingType.NUMBER_OF_DAYS) { + if (newCodeDefinitionType === NewCodeDefinitionType.NumberOfDays) { return ( <Alert variant="warning" className="sw-mb-4 sw-max-w-[800px]"> <p className="sw-mb-2 sw-font-bold"> diff --git a/server/sonar-web/src/main/js/helpers/__tests__/new-code-definition-test.ts b/server/sonar-web/src/main/js/helpers/__tests__/new-code-definition-test.ts new file mode 100644 index 00000000000..7368b3c15a8 --- /dev/null +++ b/server/sonar-web/src/main/js/helpers/__tests__/new-code-definition-test.ts @@ -0,0 +1,84 @@ +/* + * 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 { NewCodeDefinition, NewCodeDefinitionType } from '../../types/new-code-definition'; +import { mockNewCodePeriod } from '../mocks/new-code-definition'; +import { + NUMBER_OF_DAYS_DEFAULT_VALUE, + getNumberOfDaysDefaultValue, + isNewCodeDefinitionCompliant, +} from '../new-code-definition'; + +describe('isNewCodeDefinitionCompliant', () => { + it.each([ + [mockNewCodePeriod({ type: NewCodeDefinitionType.NumberOfDays, value: '0' }), false], + [mockNewCodePeriod({ type: NewCodeDefinitionType.NumberOfDays, value: '15' }), true], + [mockNewCodePeriod({ type: NewCodeDefinitionType.NumberOfDays, value: '15.' }), false], + [mockNewCodePeriod({ type: NewCodeDefinitionType.NumberOfDays, value: '15.0' }), false], + [mockNewCodePeriod({ type: NewCodeDefinitionType.NumberOfDays, value: '15.3' }), false], + [mockNewCodePeriod({ type: NewCodeDefinitionType.NumberOfDays, value: '91' }), false], + [mockNewCodePeriod({ type: NewCodeDefinitionType.PreviousVersion }), true], + [mockNewCodePeriod({ type: NewCodeDefinitionType.ReferenceBranch }), true], + [mockNewCodePeriod({ type: NewCodeDefinitionType.SpecificAnalysis }), false], + ])( + 'should test for new code definition compliance properly %s', + (newCodePeriod: NewCodeDefinition, result: boolean) => { + expect(isNewCodeDefinitionCompliant(newCodePeriod)).toEqual(result); + } + ); +}); + +describe('getNumberOfDaysDefaultValue', () => { + it.each([ + [null, null, NUMBER_OF_DAYS_DEFAULT_VALUE.toString()], + [ + mockNewCodePeriod({ type: NewCodeDefinitionType.PreviousVersion }), + null, + NUMBER_OF_DAYS_DEFAULT_VALUE.toString(), + ], + [ + null, + mockNewCodePeriod({ type: NewCodeDefinitionType.PreviousVersion }), + NUMBER_OF_DAYS_DEFAULT_VALUE.toString(), + ], + [ + mockNewCodePeriod({ type: NewCodeDefinitionType.NumberOfDays, value: '91' }), + null, + NUMBER_OF_DAYS_DEFAULT_VALUE.toString(), + ], + [ + null, + mockNewCodePeriod({ type: NewCodeDefinitionType.NumberOfDays, value: '91' }), + NUMBER_OF_DAYS_DEFAULT_VALUE.toString(), + ], + [mockNewCodePeriod({ type: NewCodeDefinitionType.NumberOfDays, value: '90' }), null, '90'], + [null, mockNewCodePeriod({ type: NewCodeDefinitionType.NumberOfDays, value: '90' }), '90'], + ])( + 'should return the defaut number of days vale properly %s', + ( + globalNewCodeDefinition: NewCodeDefinition | null, + inheritedNewCodeDefinition: NewCodeDefinition | null, + result: string + ) => { + expect( + getNumberOfDaysDefaultValue(globalNewCodeDefinition, inheritedNewCodeDefinition) + ).toEqual(result); + } + ); +}); diff --git a/server/sonar-web/src/main/js/helpers/__tests__/periods-test.ts b/server/sonar-web/src/main/js/helpers/__tests__/new-code-period-test.ts index aca69741c93..52011866a7d 100644 --- a/server/sonar-web/src/main/js/helpers/__tests__/periods-test.ts +++ b/server/sonar-web/src/main/js/helpers/__tests__/new-code-period-test.ts @@ -17,8 +17,8 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { NewCodePeriod, NewCodePeriodSettingType } from '../../types/types'; -import { getPeriodLabel, isNewCodeDefinitionCompliant } from '../periods'; +import { NewCodeDefinitionType } from '../../types/new-code-definition'; +import { getNewCodePeriodLabel } from '../new-code-period'; import { mockPeriod } from '../testMocks'; const formatter = jest.fn((v) => v); @@ -29,19 +29,24 @@ beforeEach(() => { describe('getPeriodLabel', () => { it('should handle missing value', () => { - expect(getPeriodLabel(undefined, formatter)).toBeUndefined(); + expect(getNewCodePeriodLabel(undefined, formatter)).toBeUndefined(); }); it('should handle date', () => { - expect(getPeriodLabel(mockPeriod({ mode: 'date' }), formatter)).toBe('overview.period.date.'); + expect(getNewCodePeriodLabel(mockPeriod({ mode: 'date' }), formatter)).toBe( + 'overview.period.date.' + ); expect( - getPeriodLabel(mockPeriod({ mode: 'date', parameter: '2019-02-21T01:11:21+0100' }), formatter) + getNewCodePeriodLabel( + mockPeriod({ mode: 'date', parameter: '2019-02-21T01:11:21+0100' }), + formatter + ) ).toBe('overview.period.date.2019-02-21T01:11:21+0100'); expect(formatter).toHaveBeenCalledTimes(1); }); it('should handle days', () => { - expect(getPeriodLabel(mockPeriod({ mode: 'days', modeParam: '12' }), formatter)).toBe( + expect(getNewCodePeriodLabel(mockPeriod({ mode: 'days', modeParam: '12' }), formatter)).toBe( 'overview.period.days.12' ); expect(formatter).not.toHaveBeenCalled(); @@ -49,36 +54,42 @@ describe('getPeriodLabel', () => { it('should handle previous analysis', () => { expect( - getPeriodLabel(mockPeriod({ mode: 'previous_analysis', parameter: 'param' }), formatter) + getNewCodePeriodLabel( + mockPeriod({ mode: 'previous_analysis', parameter: 'param' }), + formatter + ) ).toBe('overview.period.previous_analysis.param'); expect(formatter).not.toHaveBeenCalled(); }); it('should handle previous version', () => { - expect(getPeriodLabel(mockPeriod({ mode: 'previous_version' }), formatter)).toBe( + expect(getNewCodePeriodLabel(mockPeriod({ mode: 'previous_version' }), formatter)).toBe( 'overview.period.previous_version_only_date' ); expect( - getPeriodLabel(mockPeriod({ mode: 'previous_version', parameter: '7.9' }), formatter) + getNewCodePeriodLabel(mockPeriod({ mode: 'previous_version', parameter: '7.9' }), formatter) ).toBe('overview.period.previous_version.7.9'); expect(formatter).not.toHaveBeenCalled(); }); it('should handle version', () => { - expect(getPeriodLabel(mockPeriod({ mode: 'version', modeParam: '7.2' }), formatter)).toBe( - 'overview.period.version.7.2' - ); expect( - getPeriodLabel(mockPeriod({ mode: 'previous_version', parameter: '7.9' }), formatter) + getNewCodePeriodLabel(mockPeriod({ mode: 'version', modeParam: '7.2' }), formatter) + ).toBe('overview.period.version.7.2'); + expect( + getNewCodePeriodLabel(mockPeriod({ mode: 'previous_version', parameter: '7.9' }), formatter) ).toBe('overview.period.previous_version.7.9'); expect(formatter).not.toHaveBeenCalled(); }); it('should handle manual baseline', () => { expect( - getPeriodLabel(mockPeriod({ mode: 'manual_baseline', modeParam: 'A658678DE' }), formatter) + getNewCodePeriodLabel( + mockPeriod({ mode: 'manual_baseline', modeParam: 'A658678DE' }), + formatter + ) ).toBe('overview.period.manual_baseline.A658678DE'); - expect(getPeriodLabel(mockPeriod({ mode: 'manual_baseline' }), formatter)).toBe( + expect(getNewCodePeriodLabel(mockPeriod({ mode: 'manual_baseline' }), formatter)).toBe( 'overview.period.manual_baseline.2019-04-23T02:12:32+0100' ); expect(formatter).toHaveBeenCalledTimes(1); @@ -86,46 +97,27 @@ describe('getPeriodLabel', () => { it('should handle SPECIFIC_ANALYSIS', () => { expect( - getPeriodLabel( - mockPeriod({ mode: NewCodePeriodSettingType.SPECIFIC_ANALYSIS, parameter: '7.1' }), + getNewCodePeriodLabel( + mockPeriod({ mode: NewCodeDefinitionType.SpecificAnalysis, parameter: '7.1' }), formatter ) ).toBe('overview.period.specific_analysis.2019-04-23T02:12:32+0100'); expect( - getPeriodLabel(mockPeriod({ mode: NewCodePeriodSettingType.SPECIFIC_ANALYSIS }), formatter) + getNewCodePeriodLabel(mockPeriod({ mode: NewCodeDefinitionType.SpecificAnalysis }), formatter) ).toBe('overview.period.specific_analysis.2019-04-23T02:12:32+0100'); expect(formatter).toHaveBeenCalledTimes(2); }); it('should handle PREVIOUS_VERSION', () => { expect( - getPeriodLabel( - mockPeriod({ mode: NewCodePeriodSettingType.PREVIOUS_VERSION, modeParam: 'A658678DE' }), + getNewCodePeriodLabel( + mockPeriod({ mode: NewCodeDefinitionType.PreviousVersion, modeParam: 'A658678DE' }), formatter ) ).toBe('overview.period.previous_version.A658678DE'); expect( - getPeriodLabel(mockPeriod({ mode: NewCodePeriodSettingType.PREVIOUS_VERSION }), formatter) + getNewCodePeriodLabel(mockPeriod({ mode: NewCodeDefinitionType.PreviousVersion }), formatter) ).toBe('overview.period.previous_version.2019-04-23T02:12:32+0100'); expect(formatter).toHaveBeenCalledTimes(1); }); }); - -describe('isNewCodeDefinitionCompliant', () => { - it.each([ - [{ type: NewCodePeriodSettingType.NUMBER_OF_DAYS, value: '0' }, false], - [{ type: NewCodePeriodSettingType.NUMBER_OF_DAYS, value: '15' }, true], - [{ type: NewCodePeriodSettingType.NUMBER_OF_DAYS, value: '15.' }, false], - [{ type: NewCodePeriodSettingType.NUMBER_OF_DAYS, value: '15.0' }, false], - [{ type: NewCodePeriodSettingType.NUMBER_OF_DAYS, value: '15.3' }, false], - [{ type: NewCodePeriodSettingType.NUMBER_OF_DAYS, value: '91' }, false], - [{ type: NewCodePeriodSettingType.PREVIOUS_VERSION }, true], - [{ type: NewCodePeriodSettingType.REFERENCE_BRANCH }, true], - [{ type: NewCodePeriodSettingType.SPECIFIC_ANALYSIS }, false], - ])( - 'should test for new code definition compliance properly %s', - (newCodePeriod: NewCodePeriod, result: boolean) => { - expect(isNewCodeDefinitionCompliant(newCodePeriod)).toEqual(result); - } - ); -}); diff --git a/server/sonar-web/src/main/js/helpers/mocks/new-code-period.ts b/server/sonar-web/src/main/js/helpers/mocks/new-code-definition.ts index a4372621720..72ecf4f8c61 100644 --- a/server/sonar-web/src/main/js/helpers/mocks/new-code-period.ts +++ b/server/sonar-web/src/main/js/helpers/mocks/new-code-definition.ts @@ -18,16 +18,22 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { NewCodePeriod, NewCodePeriodBranch, NewCodePeriodSettingType } from '../../types/types'; +import { + NewCodeDefinition, + NewCodeDefinitionBranch, + NewCodeDefinitionType, +} from '../../types/new-code-definition'; -export function mockNewCodePeriod(overrides: Partial<NewCodePeriod> = {}) { +export function mockNewCodePeriod(overrides: Partial<NewCodeDefinition> = {}): NewCodeDefinition { return { - type: NewCodePeriodSettingType.PREVIOUS_VERSION, + type: NewCodeDefinitionType.PreviousVersion, ...overrides, }; } -export function mockNewCodePeriodBranch(overrides: Partial<NewCodePeriodBranch> = {}) { +export function mockNewCodePeriodBranch( + overrides: Partial<NewCodeDefinitionBranch> = {} +): NewCodeDefinitionBranch { return { projectKey: 'pkey', branchKey: 'bKey', diff --git a/server/sonar-web/src/main/js/helpers/new-code-definition.ts b/server/sonar-web/src/main/js/helpers/new-code-definition.ts new file mode 100644 index 00000000000..d02b8742c62 --- /dev/null +++ b/server/sonar-web/src/main/js/helpers/new-code-definition.ts @@ -0,0 +1,70 @@ +/* + * 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 { NewCodeDefinition, NewCodeDefinitionType } from '../types/new-code-definition'; + +export const DEFAULT_NEW_CODE_DEFINITION_TYPE: NewCodeDefinitionType = + NewCodeDefinitionType.PreviousVersion; +export const NUMBER_OF_DAYS_MIN_VALUE = 1; +export const NUMBER_OF_DAYS_MAX_VALUE = 90; +export const NUMBER_OF_DAYS_DEFAULT_VALUE = 30; + +export function isNewCodeDefinitionCompliant(newCodePeriod: NewCodeDefinition) { + switch (newCodePeriod.type) { + case NewCodeDefinitionType.NumberOfDays: + if (!newCodePeriod.value) { + return false; + } + return ( + !/\D/.test(newCodePeriod.value) && + Number.isInteger(+newCodePeriod.value) && + NUMBER_OF_DAYS_MIN_VALUE <= +newCodePeriod.value && + +newCodePeriod.value <= NUMBER_OF_DAYS_MAX_VALUE + ); + case NewCodeDefinitionType.SpecificAnalysis: + return false; + default: + return true; + } +} + +export function getNumberOfDaysDefaultValue( + globalNewCodeDefinition?: NewCodeDefinition | null, + inheritedNewCodeDefinition?: NewCodeDefinition | null +) { + if ( + inheritedNewCodeDefinition && + isNewCodeDefinitionCompliant(inheritedNewCodeDefinition) && + inheritedNewCodeDefinition.type === NewCodeDefinitionType.NumberOfDays && + inheritedNewCodeDefinition.value + ) { + return inheritedNewCodeDefinition.value; + } + + if ( + globalNewCodeDefinition && + isNewCodeDefinitionCompliant(globalNewCodeDefinition) && + globalNewCodeDefinition.type === NewCodeDefinitionType.NumberOfDays && + globalNewCodeDefinition.value + ) { + return globalNewCodeDefinition.value; + } + + return NUMBER_OF_DAYS_DEFAULT_VALUE.toString(); +} diff --git a/server/sonar-web/src/main/js/helpers/periods.ts b/server/sonar-web/src/main/js/helpers/new-code-period.ts index 7e80ce8a8d2..7d92c9521ef 100644 --- a/server/sonar-web/src/main/js/helpers/periods.ts +++ b/server/sonar-web/src/main/js/helpers/new-code-period.ts @@ -17,12 +17,17 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { parseDate } from '../helpers/dates'; -import { translate, translateWithParameters } from '../helpers/l10n'; import { ApplicationPeriod } from '../types/application'; -import { NewCodePeriod, NewCodePeriodSettingType, Period } from '../types/types'; +import { NewCodeDefinitionType } from '../types/new-code-definition'; +import { Period } from '../types/types'; +import { parseDate } from './dates'; +import { translate, translateWithParameters } from './l10n'; -export function getPeriodLabel( +export function getNewCodePeriodDate(period?: { date?: string }): Date | undefined { + return period?.date ? parseDate(period.date) : undefined; +} + +export function getNewCodePeriodLabel( period: Period | undefined, dateFormatter: (date: string) => string ) { @@ -33,10 +38,10 @@ export function getPeriodLabel( let parameter = period.modeParam || period.parameter || ''; switch (period.mode) { - case NewCodePeriodSettingType.SPECIFIC_ANALYSIS: + case NewCodeDefinitionType.SpecificAnalysis: parameter = dateFormatter(period.date); break; - case NewCodePeriodSettingType.PREVIOUS_VERSION: + case NewCodeDefinitionType.PreviousVersion: parameter = parameter || dateFormatter(period.date); break; /* @@ -59,34 +64,8 @@ export function getPeriodLabel( return translateWithParameters(`overview.period.${period.mode.toLowerCase()}`, parameter); } -export function getPeriodDate(period?: { date?: string }): Date | undefined { - return period && period.date ? parseDate(period.date) : undefined; -} - -export function isApplicationPeriod( +export function isApplicationNewCodePeriod( period: Period | ApplicationPeriod ): period is ApplicationPeriod { return (period as ApplicationPeriod).project !== undefined; } - -export const MIN_NUMBER_OF_DAYS = 1; -export const MAX_NUMBER_OF_DAYS = 90; - -export function isNewCodeDefinitionCompliant(newCodePeriod: NewCodePeriod) { - switch (newCodePeriod.type) { - case NewCodePeriodSettingType.NUMBER_OF_DAYS: - if (!newCodePeriod.value) { - return false; - } - return ( - !/\D/.test(newCodePeriod.value) && - Number.isInteger(+newCodePeriod.value) && - MIN_NUMBER_OF_DAYS <= +newCodePeriod.value && - +newCodePeriod.value <= MAX_NUMBER_OF_DAYS - ); - case NewCodePeriodSettingType.SPECIFIC_ANALYSIS: - return false; - default: - return true; - } -} diff --git a/server/sonar-web/src/main/js/types/branch-like.ts b/server/sonar-web/src/main/js/types/branch-like.ts index f25c8a11693..6f1056dcf6b 100644 --- a/server/sonar-web/src/main/js/types/branch-like.ts +++ b/server/sonar-web/src/main/js/types/branch-like.ts @@ -17,8 +17,9 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import { NewCodeDefinition } from './new-code-definition'; import { QualityGateStatusCondition } from './quality-gates'; -import { NewCodePeriod, Status } from './types'; +import { Status } from './types'; export interface Branch { analysisDate?: string; @@ -61,7 +62,7 @@ export interface BranchLikeTree { export type BranchParameters = { branch?: string } | { pullRequest?: string }; export interface BranchWithNewCodePeriod extends Branch { - newCodePeriod?: NewCodePeriod; + newCodePeriod?: NewCodeDefinition; } export interface BranchStatusData { diff --git a/server/sonar-web/src/main/js/apps/projectBaseline/constants.ts b/server/sonar-web/src/main/js/types/new-code-definition.ts index 00c2c82e24b..9bda3b52432 100644 --- a/server/sonar-web/src/main/js/apps/projectBaseline/constants.ts +++ b/server/sonar-web/src/main/js/types/new-code-definition.ts @@ -18,7 +18,28 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { NewCodePeriodSettingType } from '../../types/types'; +export enum NewCodeDefinitionType { + PreviousVersion = 'PREVIOUS_VERSION', + NumberOfDays = 'NUMBER_OF_DAYS', + SpecificAnalysis = 'SPECIFIC_ANALYSIS', + ReferenceBranch = 'REFERENCE_BRANCH', + Inherited = 'INHERITED', +} -export const DEFAULT_GENERAL_SETTING_TYPE: NewCodePeriodSettingType = - NewCodePeriodSettingType.PREVIOUS_VERSION; +export interface NewCodeDefinition { + type: NewCodeDefinitionType; + value?: string; + effectiveValue?: string; + inherited?: boolean; +} + +export interface NewCodeDefinitiondWithCompliance { + type?: NewCodeDefinitionType; + value?: string; + isCompliant: boolean; +} + +export interface NewCodeDefinitionBranch extends NewCodeDefinition { + projectKey: string; + branchKey: string; +} diff --git a/server/sonar-web/src/main/js/types/types.ts b/server/sonar-web/src/main/js/types/types.ts index f31661e0c7f..a83e6bb36c8 100644 --- a/server/sonar-web/src/main/js/types/types.ts +++ b/server/sonar-web/src/main/js/types/types.ts @@ -20,6 +20,7 @@ import { RuleDescriptionSection } from '../apps/coding-rules/rule'; import { ComponentQualifier, Visibility } from './component'; import { MessageFormatting } from './issues'; +import { NewCodeDefinitionType } from './new-code-definition'; import { UserActive, UserBase } from './users'; export type Dict<T> = { [key: string]: T }; @@ -397,32 +398,6 @@ export interface MyProject { qualityGate?: string; } -export interface NewCodePeriod { - type: NewCodePeriodSettingType; - value?: string; - effectiveValue?: string; - inherited?: boolean; -} - -export interface NewCodePeriodWithCompliance { - type?: NewCodePeriodSettingType; - value?: string; - isCompliant: boolean; -} - -export interface NewCodePeriodBranch extends NewCodePeriod { - projectKey: string; - branchKey: string; -} - -export enum NewCodePeriodSettingType { - PREVIOUS_VERSION = 'PREVIOUS_VERSION', - NUMBER_OF_DAYS = 'NUMBER_OF_DAYS', - SPECIFIC_ANALYSIS = 'SPECIFIC_ANALYSIS', - REFERENCE_BRANCH = 'REFERENCE_BRANCH', - INHERITED = 'INHERITED', -} - export interface Paging { pageIndex: number; pageSize: number; @@ -432,7 +407,7 @@ export interface Paging { export interface Period { date: string; index?: number; - mode: PeriodMode | NewCodePeriodSettingType; + mode: PeriodMode | NewCodeDefinitionType; modeParam?: string; parameter?: string; } |