From: Philippe Perrin Date: Thu, 8 Jun 2023 08:22:27 +0000 (+0200) Subject: SONAR-19687 Non-compliant global new code definition value should not be proposed... X-Git-Tag: 10.2.0.77647~536 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=df412e40611054143f326a2d364028b64f4a8bac;p=sonarqube.git SONAR-19687 Non-compliant global new code definition value should not be proposed as default --- 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> { +}): Promise> { 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 { 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 ); - 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 && (
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 { + 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 ; } return ; 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
{leakPeriodLabel}
; } - 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 {translateWithParameters('overview.new_code_period_x', leakPeriodLabel)} @@ -64,7 +65,7 @@ export class LeakPeriodLegend extends React.PureComponent void; + onSelect: (selection: NewCodeDefinitionType) => void; selected: boolean; } @@ -32,7 +32,7 @@ export default function BaselineSettingAnalysis({ onSelect, selected }: Props) { 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 { @@ -57,16 +60,19 @@ export default class BranchBaselineSettingModal extends React.PureComponent 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 { - 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 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 - {currentSetting === NewCodePeriodSettingType.SPECIFIC_ANALYSIS && ( + {currentSetting === NewCodeDefinitionType.SpecificAnalysis && ( {}} - selected={selected === NewCodePeriodSettingType.SPECIFIC_ANALYSIS} + selected={selected === NewCodeDefinitionType.SpecificAnalysis} /> )}
- {selected === NewCodePeriodSettingType.SPECIFIC_ANALYSIS && ( + {selected === NewCodeDefinitionType.SpecificAnalysis && ( { 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 { ); } - 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 { 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 { }; 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 { 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; - 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 { 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 { } 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 { 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 { ([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 { 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 { } : generalSetting } + generalSetting={generalSetting} /> )} 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) => 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 {branchesEnabled && ( )} - {!branchesEnabled && currentSetting === NewCodePeriodSettingType.SPECIFIC_ANALYSIS && ( + {!branchesEnabled && currentSetting === NewCodeDefinitionType.SpecificAnalysis && ( {}} selected={ - overrideGeneralSetting && selected === NewCodePeriodSettingType.SPECIFIC_ANALYSIS + overrideGeneralSetting && selected === NewCodeDefinitionType.SpecificAnalysis } /> )} {!branchesEnabled && overrideGeneralSetting && - selected === NewCodePeriodSettingType.SPECIFIC_ANALYSIS && ( + selected === NewCodeDefinitionType.SpecificAnalysis && ( { 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/constants.ts b/server/sonar-web/src/main/js/apps/projectBaseline/constants.ts deleted file mode 100644 index 00c2c82e24b..00000000000 --- a/server/sonar-web/src/main/js/apps/projectBaseline/constants.ts +++ /dev/null @@ -1,24 +0,0 @@ -/* - * 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 { NewCodePeriodSettingType } from '../../types/types'; - -export const DEFAULT_GENERAL_SETTING_TYPE: NewCodePeriodSettingType = - NewCodePeriodSettingType.PREVIOUS_VERSION; 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> { { isValid={isValid} onChangeDays={this.onSelectDays} onSelect={this.onSelectSetting} - selected={selected === NewCodePeriodSettingType.NUMBER_OF_DAYS} + selected={selected === NewCodeDefinitionType.NumberOfDays} /> { 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({ 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(null); - const [selectedNcdType, setSelectedNcdType] = React.useState( - null - ); - const [days, setDays] = React.useState(INITIAL_DAYS); + const [globalNcd, setGlobalNcd] = React.useState(null); + const [selectedNcdType, setSelectedNcdType] = React.useState(null); + const [days, setDays] = React.useState(''); - 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) {
setSelectedNcdType(NewCodePeriodSettingType.INHERITED)} + disabled={!isGlobalNcdCompliant} + onCheck={() => setSelectedNcdType(NewCodeDefinitionType.Inherited)} value="general" > )} @@ -126,11 +131,9 @@ export default function NewCodeDefinitionSelector(props: Props) { 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) {
setSelectedNcdType(NewCodePeriodSettingType.REFERENCE_BRANCH)} - selected={selectedNcdType === NewCodePeriodSettingType.REFERENCE_BRANCH} + onClick={() => setSelectedNcdType(NewCodeDefinitionType.ReferenceBranch)} + selected={selectedNcdType === NewCodeDefinitionType.ReferenceBranch} title={translate('new_code_definition.reference_branch')} >
@@ -173,7 +176,7 @@ export default function NewCodeDefinitionSelector(props: Props) { {translate('new_code_definition.reference_branch.description')}

{translate('new_code_definition.reference_branch.usecase')}

- {selectedNcdType === NewCodePeriodSettingType.REFERENCE_BRANCH && ( + {selectedNcdType === NewCodeDefinitionType.ReferenceBranch && ( {translate('new_code_definition.reference_branch.notice')} 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 (

@@ -64,7 +64,7 @@ export default function NewCodeDefinitionWarning({ ); } - if (newCodeDefinitionType === NewCodePeriodSettingType.NUMBER_OF_DAYS) { + if (newCodeDefinitionType === NewCodeDefinitionType.NumberOfDays) { return (

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__/new-code-period-test.ts b/server/sonar-web/src/main/js/helpers/__tests__/new-code-period-test.ts new file mode 100644 index 00000000000..52011866a7d --- /dev/null +++ b/server/sonar-web/src/main/js/helpers/__tests__/new-code-period-test.ts @@ -0,0 +1,123 @@ +/* + * 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 { NewCodeDefinitionType } from '../../types/new-code-definition'; +import { getNewCodePeriodLabel } from '../new-code-period'; +import { mockPeriod } from '../testMocks'; + +const formatter = jest.fn((v) => v); + +beforeEach(() => { + formatter.mockClear(); +}); + +describe('getPeriodLabel', () => { + it('should handle missing value', () => { + expect(getNewCodePeriodLabel(undefined, formatter)).toBeUndefined(); + }); + + it('should handle date', () => { + expect(getNewCodePeriodLabel(mockPeriod({ mode: 'date' }), formatter)).toBe( + 'overview.period.date.' + ); + expect( + 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(getNewCodePeriodLabel(mockPeriod({ mode: 'days', modeParam: '12' }), formatter)).toBe( + 'overview.period.days.12' + ); + expect(formatter).not.toHaveBeenCalled(); + }); + + it('should handle previous analysis', () => { + expect( + getNewCodePeriodLabel( + mockPeriod({ mode: 'previous_analysis', parameter: 'param' }), + formatter + ) + ).toBe('overview.period.previous_analysis.param'); + expect(formatter).not.toHaveBeenCalled(); + }); + + it('should handle previous version', () => { + expect(getNewCodePeriodLabel(mockPeriod({ mode: 'previous_version' }), formatter)).toBe( + 'overview.period.previous_version_only_date' + ); + expect( + 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( + 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( + getNewCodePeriodLabel( + mockPeriod({ mode: 'manual_baseline', modeParam: 'A658678DE' }), + formatter + ) + ).toBe('overview.period.manual_baseline.A658678DE'); + expect(getNewCodePeriodLabel(mockPeriod({ mode: 'manual_baseline' }), formatter)).toBe( + 'overview.period.manual_baseline.2019-04-23T02:12:32+0100' + ); + expect(formatter).toHaveBeenCalledTimes(1); + }); + + it('should handle SPECIFIC_ANALYSIS', () => { + expect( + getNewCodePeriodLabel( + mockPeriod({ mode: NewCodeDefinitionType.SpecificAnalysis, parameter: '7.1' }), + formatter + ) + ).toBe('overview.period.specific_analysis.2019-04-23T02:12:32+0100'); + expect( + 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( + getNewCodePeriodLabel( + mockPeriod({ mode: NewCodeDefinitionType.PreviousVersion, modeParam: 'A658678DE' }), + formatter + ) + ).toBe('overview.period.previous_version.A658678DE'); + expect( + getNewCodePeriodLabel(mockPeriod({ mode: NewCodeDefinitionType.PreviousVersion }), formatter) + ).toBe('overview.period.previous_version.2019-04-23T02:12:32+0100'); + expect(formatter).toHaveBeenCalledTimes(1); + }); +}); diff --git a/server/sonar-web/src/main/js/helpers/__tests__/periods-test.ts b/server/sonar-web/src/main/js/helpers/__tests__/periods-test.ts deleted file mode 100644 index aca69741c93..00000000000 --- a/server/sonar-web/src/main/js/helpers/__tests__/periods-test.ts +++ /dev/null @@ -1,131 +0,0 @@ -/* - * 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 { NewCodePeriod, NewCodePeriodSettingType } from '../../types/types'; -import { getPeriodLabel, isNewCodeDefinitionCompliant } from '../periods'; -import { mockPeriod } from '../testMocks'; - -const formatter = jest.fn((v) => v); - -beforeEach(() => { - formatter.mockClear(); -}); - -describe('getPeriodLabel', () => { - it('should handle missing value', () => { - expect(getPeriodLabel(undefined, formatter)).toBeUndefined(); - }); - - it('should handle date', () => { - expect(getPeriodLabel(mockPeriod({ mode: 'date' }), formatter)).toBe('overview.period.date.'); - expect( - getPeriodLabel(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( - 'overview.period.days.12' - ); - expect(formatter).not.toHaveBeenCalled(); - }); - - it('should handle previous analysis', () => { - expect( - getPeriodLabel(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( - 'overview.period.previous_version_only_date' - ); - expect( - getPeriodLabel(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) - ).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) - ).toBe('overview.period.manual_baseline.A658678DE'); - expect(getPeriodLabel(mockPeriod({ mode: 'manual_baseline' }), formatter)).toBe( - 'overview.period.manual_baseline.2019-04-23T02:12:32+0100' - ); - expect(formatter).toHaveBeenCalledTimes(1); - }); - - it('should handle SPECIFIC_ANALYSIS', () => { - expect( - getPeriodLabel( - mockPeriod({ mode: NewCodePeriodSettingType.SPECIFIC_ANALYSIS, parameter: '7.1' }), - formatter - ) - ).toBe('overview.period.specific_analysis.2019-04-23T02:12:32+0100'); - expect( - getPeriodLabel(mockPeriod({ mode: NewCodePeriodSettingType.SPECIFIC_ANALYSIS }), 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' }), - formatter - ) - ).toBe('overview.period.previous_version.A658678DE'); - expect( - getPeriodLabel(mockPeriod({ mode: NewCodePeriodSettingType.PREVIOUS_VERSION }), 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-definition.ts b/server/sonar-web/src/main/js/helpers/mocks/new-code-definition.ts new file mode 100644 index 00000000000..72ecf4f8c61 --- /dev/null +++ b/server/sonar-web/src/main/js/helpers/mocks/new-code-definition.ts @@ -0,0 +1,43 @@ +/* + * 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, + NewCodeDefinitionBranch, + NewCodeDefinitionType, +} from '../../types/new-code-definition'; + +export function mockNewCodePeriod(overrides: Partial = {}): NewCodeDefinition { + return { + type: NewCodeDefinitionType.PreviousVersion, + ...overrides, + }; +} + +export function mockNewCodePeriodBranch( + overrides: Partial = {} +): NewCodeDefinitionBranch { + return { + projectKey: 'pkey', + branchKey: 'bKey', + ...mockNewCodePeriod(), + ...overrides, + }; +} 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-period.ts deleted file mode 100644 index a4372621720..00000000000 --- a/server/sonar-web/src/main/js/helpers/mocks/new-code-period.ts +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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 { NewCodePeriod, NewCodePeriodBranch, NewCodePeriodSettingType } from '../../types/types'; - -export function mockNewCodePeriod(overrides: Partial = {}) { - return { - type: NewCodePeriodSettingType.PREVIOUS_VERSION, - ...overrides, - }; -} - -export function mockNewCodePeriodBranch(overrides: Partial = {}) { - return { - projectKey: 'pkey', - branchKey: 'bKey', - ...mockNewCodePeriod(), - ...overrides, - }; -} 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/new-code-period.ts b/server/sonar-web/src/main/js/helpers/new-code-period.ts new file mode 100644 index 00000000000..7d92c9521ef --- /dev/null +++ b/server/sonar-web/src/main/js/helpers/new-code-period.ts @@ -0,0 +1,71 @@ +/* + * 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 { ApplicationPeriod } from '../types/application'; +import { NewCodeDefinitionType } from '../types/new-code-definition'; +import { Period } from '../types/types'; +import { parseDate } from './dates'; +import { translate, translateWithParameters } from './l10n'; + +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 +) { + if (!period) { + return undefined; + } + + let parameter = period.modeParam || period.parameter || ''; + + switch (period.mode) { + case NewCodeDefinitionType.SpecificAnalysis: + parameter = dateFormatter(period.date); + break; + case NewCodeDefinitionType.PreviousVersion: + parameter = parameter || dateFormatter(period.date); + break; + /* + * Handle legacy period modes, that predate MMF-1579 + */ + case 'previous_version': + if (!parameter) { + return translate('overview.period.previous_version_only_date'); + } + break; + case 'date': + parameter = parameter && dateFormatter(parameter); + break; + case 'manual_baseline': + parameter = parameter || dateFormatter(period.date); + break; + default: // No change in the parameter + } + + return translateWithParameters(`overview.period.${period.mode.toLowerCase()}`, parameter); +} + +export function isApplicationNewCodePeriod( + period: Period | ApplicationPeriod +): period is ApplicationPeriod { + return (period as ApplicationPeriod).project !== undefined; +} diff --git a/server/sonar-web/src/main/js/helpers/periods.ts b/server/sonar-web/src/main/js/helpers/periods.ts deleted file mode 100644 index 7e80ce8a8d2..00000000000 --- a/server/sonar-web/src/main/js/helpers/periods.ts +++ /dev/null @@ -1,92 +0,0 @@ -/* - * 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 { parseDate } from '../helpers/dates'; -import { translate, translateWithParameters } from '../helpers/l10n'; -import { ApplicationPeriod } from '../types/application'; -import { NewCodePeriod, NewCodePeriodSettingType, Period } from '../types/types'; - -export function getPeriodLabel( - period: Period | undefined, - dateFormatter: (date: string) => string -) { - if (!period) { - return undefined; - } - - let parameter = period.modeParam || period.parameter || ''; - - switch (period.mode) { - case NewCodePeriodSettingType.SPECIFIC_ANALYSIS: - parameter = dateFormatter(period.date); - break; - case NewCodePeriodSettingType.PREVIOUS_VERSION: - parameter = parameter || dateFormatter(period.date); - break; - /* - * Handle legacy period modes, that predate MMF-1579 - */ - case 'previous_version': - if (!parameter) { - return translate('overview.period.previous_version_only_date'); - } - break; - case 'date': - parameter = parameter && dateFormatter(parameter); - break; - case 'manual_baseline': - parameter = parameter || dateFormatter(period.date); - break; - default: // No change in the parameter - } - - 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( - 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/types/new-code-definition.ts b/server/sonar-web/src/main/js/types/new-code-definition.ts new file mode 100644 index 00000000000..9bda3b52432 --- /dev/null +++ b/server/sonar-web/src/main/js/types/new-code-definition.ts @@ -0,0 +1,45 @@ +/* + * 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. + */ + +export enum NewCodeDefinitionType { + PreviousVersion = 'PREVIOUS_VERSION', + NumberOfDays = 'NUMBER_OF_DAYS', + SpecificAnalysis = 'SPECIFIC_ANALYSIS', + ReferenceBranch = 'REFERENCE_BRANCH', + Inherited = 'INHERITED', +} + +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 = { [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; }