瀏覽代碼

SONAR-10471 Improve leak period description for specific date

tags/7.5
Grégoire Aubert 5 年之前
父節點
當前提交
02546fd9c8

server/sonar-web/src/main/js/apps/component-measures/components/LeakPeriodLegend.js → server/sonar-web/src/main/js/apps/component-measures/components/LeakPeriodLegend.tsx 查看文件

@@ -17,45 +17,56 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
// @flow
import React from 'react';
import classNames from 'classnames';
import * as React from 'react';
import * as classNames from 'classnames';
import DateFromNow from '../../../components/intl/DateFromNow';
import DateFormatter from '../../../components/intl/DateFormatter';
import DateTimeFormatter from '../../../components/intl/DateTimeFormatter';
import Tooltip from '../../../components/controls/Tooltip';
import { getPeriodLabel, getPeriodDate } from '../../../helpers/periods';
import { getPeriodLabel, getPeriodDate, Period, PeriodMode } from '../../../helpers/periods';
import { translate, translateWithParameters } from '../../../helpers/l10n';
/*:: import type { Component, Period } from '../types'; */
import { differenceInDays } from '../../../helpers/dates';
import { ComponentMeasure } from '../../../app/types';

/*:: type Props = {
className?: string,
component: Component,
period: Period
}; */
interface Props {
className?: string;
component: ComponentMeasure;
period: Period;
}

export default function LeakPeriodLegend({ className, component, period } /*: Props */) {
export default function LeakPeriodLegend({ className, component, period }: Props) {
const leakClass = classNames('domain-measures-leak-header', className);
if (component.qualifier === 'APP') {
return <div className={leakClass}>{translate('issues.new_code_period')}</div>;
}

const leakPeriodLabel = getPeriodLabel(period);
if (!leakPeriodLabel) {
return null;
}

const label = (
<div className={leakClass}>
{translateWithParameters('overview.new_code_period_x', getPeriodLabel(period))}
{translateWithParameters('overview.new_code_period_x', leakPeriodLabel)}
</div>
);

if (period.mode === 'days') {
if (period.mode === PeriodMode.days) {
return label;
}

const date = getPeriodDate(period);
const tooltip = (
const tooltip = date && (
<div>
<DateFromNow date={date} />
{', '}
<DateFormatter date={date} long={true} />
{differenceInDays(new Date(), date) < 1 ? (
<DateTimeFormatter date={date} />
) : (
<DateFormatter date={date} long={true} />
)}
</div>
);

return <Tooltip overlay={tooltip}>{label}</Tooltip>;
}

server/sonar-web/src/main/js/apps/component-measures/components/__tests__/LeakPeriodLegend-test.js → server/sonar-web/src/main/js/apps/component-measures/components/__tests__/LeakPeriodLegend-test.tsx 查看文件

@@ -17,31 +17,41 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import React from 'react';
import * as React from 'react';
import { shallow } from 'enzyme';
import LeakPeriodLegend from '../LeakPeriodLegend';
import { PeriodMode } from '../../../../helpers/periods';
import { differenceInDays } from '../../../../helpers/dates';

jest.mock('../../../../helpers/dates', () => {
const dates = require.requireActual('../../../../helpers/dates');
dates.differenceInDays = jest.fn().mockReturnValue(10);
return dates;
});

const PROJECT = {
key: 'foo',
name: 'Foo',
qualifier: 'TRK'
};

const APP = {
key: 'bar',
name: 'Bar',
qualifier: 'APP'
};

const PERIOD = {
date: '2017-05-16T13:50:02+0200',
index: 1,
mode: 'previous_version',
mode: PeriodMode.previousVersion,
parameter: '6,4'
};

const PERIOD_DAYS = {
date: '2017-05-16T13:50:02+0200',
index: 1,
mode: 'days',
mode: PeriodMode.days,
parameter: '18'
};

@@ -53,3 +63,8 @@ it('should render correctly', () => {
it('should render correctly for APP', () => {
expect(shallow(<LeakPeriodLegend component={APP} period={PERIOD} />)).toMatchSnapshot();
});

it('should render a more precise date', () => {
(differenceInDays as jest.Mock<any>).mockReturnValueOnce(0);
expect(shallow(<LeakPeriodLegend component={PROJECT} period={PERIOD} />)).toMatchSnapshot();
});

server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/LeakPeriodLegend-test.js.snap → server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/LeakPeriodLegend-test.tsx.snap 查看文件

@@ -1,5 +1,27 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should render a more precise date 1`] = `
<Tooltip
overlay={
<div>
<DateFromNow
date={2017-05-16T11:50:02.000Z}
/>
,
<DateTimeFormatter
date={2017-05-16T11:50:02.000Z}
/>
</div>
}
>
<div
className="domain-measures-leak-header"
>
overview.new_code_period_x.overview.period.previous_version.6,4
</div>
</Tooltip>
`;

exports[`should render correctly 1`] = `
<Tooltip
overlay={

server/sonar-web/src/main/js/apps/overview/components/LeakPeriodLegend.js → server/sonar-web/src/main/js/apps/overview/components/LeakPeriodLegend.tsx 查看文件

@@ -17,64 +17,26 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
// @flow
import React from 'react';
import * as React from 'react';
import DateFromNow from '../../../components/intl/DateFromNow';
import DateFormatter from '../../../components/intl/DateFormatter';
import DateTimeFormatter from '../../../components/intl/DateTimeFormatter';
import Tooltip from '../../../components/controls/Tooltip';
import { getPeriodDate, getPeriodLabel } from '../../../helpers/periods';
import { getPeriodDate, getPeriodLabel, Period, PeriodMode } from '../../../helpers/periods';
import { translateWithParameters } from '../../../helpers/l10n';
import { differenceInDays } from '../../../helpers/dates';

/*::
type DaysPeriod = {
date: string,
mode: 'days',
parameter: string
};
*/

/*::
type DatePeriod = {
date: string,
mode: 'date',
parameter: string
};
*/

/*::
type VersionPeriod = {
date: string,
mode: 'version',
parameter: string
};
*/

/*::
type PreviousAnalysisPeriod = {
date: string,
mode: 'previous_analysis'
};
*/

/*::
type PreviousVersionPeriod = {
date: string,
mode: 'previous_version'
};
*/

/*::
type Period =
| DaysPeriod
| DatePeriod
| VersionPeriod
| PreviousAnalysisPeriod
| PreviousVersionPeriod; */
interface Props {
period: Period;
}

export default function LeakPeriodLegend({ period } /*: { period: Period } */) {
export default function LeakPeriodLegend({ period }: Props) {
const leakPeriodLabel = getPeriodLabel(period);
if (!leakPeriodLabel) {
return null;
}

if (period.mode === 'days') {
if (period.mode === PeriodMode.days) {
return (
<div className="overview-legend overview-legend-spaced-line">
{translateWithParameters('overview.new_code_period_x', leakPeriodLabel)}
@@ -83,20 +45,30 @@ export default function LeakPeriodLegend({ period } /*: { period: Period } */) {
}

const leakPeriodDate = getPeriodDate(period);
const tooltip = (
<DateFormatter date={leakPeriodDate} long={true}>
{formattedLeakPeriodDate => (
<span>
{translateWithParameters(
['date'].includes(period.mode)
? 'overview.last_analysis_on_x'
: 'overview.started_on_x',
formattedLeakPeriodDate
)}
</span>
if (!leakPeriodDate) {
return null;
}
const formattedDateFunction = (formattedLeakPeriodDate: string) => (
<span>
{translateWithParameters(
period.mode === PeriodMode.previousAnalysis
? 'overview.previous_analysis_on_x'
: 'overview.started_on_x',
formattedLeakPeriodDate
)}
</DateFormatter>
</span>
);

const tooltip =
differenceInDays(new Date(), leakPeriodDate) < 1 ? (
<DateTimeFormatter date={leakPeriodDate}>{formattedDateFunction}</DateTimeFormatter>
) : (
<DateFormatter date={leakPeriodDate} long={true}>
{formattedDateFunction}
</DateFormatter>
);

return (
<Tooltip overlay={tooltip}>
<div className="overview-legend">
@@ -106,7 +78,9 @@ export default function LeakPeriodLegend({ period } /*: { period: Period } */) {
{fromNow => (
<span className="note">
{translateWithParameters(
['date'].includes(period.mode) ? 'overview.last_analysis_x' : 'overview.started_x',
period.mode === PeriodMode.previousAnalysis
? 'overview.previous_analysis_x'
: 'overview.started_x',
fromNow
)}
</span>

+ 1
- 1
server/sonar-web/src/main/js/apps/overview/components/OverviewApp.tsx 查看文件

@@ -160,7 +160,7 @@ export class OverviewApp extends React.PureComponent<Props, State> {

getApplicationLeakPeriod = () =>
this.state.measures.find(measure => measure.metric.key === 'new_bugs')
? { index: 1 }
? ({ index: 1 } as Period)
: undefined;

isEmpty = () =>

+ 0
- 67
server/sonar-web/src/main/js/apps/overview/components/__tests__/LeakPeriodLegend-test.js 查看文件

@@ -1,67 +0,0 @@
/*
* SonarQube
* Copyright (C) 2009-2018 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 React from 'react';
import { shallow } from 'enzyme';
import LeakPeriodLegend from '../LeakPeriodLegend';

describe('check note', () => {
it('10 days', () => {
const period = {
date: '2013-09-22T00:00:00+0200',
mode: 'days',
parameter: '10'
};
expect(shallow(<LeakPeriodLegend period={period} />)).toMatchSnapshot();
});

it('date', () => {
const period = {
date: '2013-09-22T00:00:00+0200',
mode: 'date',
parameter: '2013-01-01'
};
expect(shallow(<LeakPeriodLegend period={period} />).find('DateFromNow')).toMatchSnapshot();
});

it('version', () => {
const period = {
date: '2013-09-22T00:00:00+0200',
mode: 'version',
parameter: '0.1'
};
expect(shallow(<LeakPeriodLegend period={period} />).find('DateFromNow')).toMatchSnapshot();
});

it('previous_version', () => {
const period = {
date: '2013-09-22T00:00:00+0200',
mode: 'previous_version'
};
expect(shallow(<LeakPeriodLegend period={period} />).find('DateFromNow')).toHaveLength(1);
});

it('previous_analysis', () => {
const period = {
date: '2013-09-22T00:00:00+0200',
mode: 'previous_analysis'
};
expect(shallow(<LeakPeriodLegend period={period} />).find('DateFromNow')).toHaveLength(1);
});
});

+ 88
- 0
server/sonar-web/src/main/js/apps/overview/components/__tests__/LeakPeriodLegend-test.tsx 查看文件

@@ -0,0 +1,88 @@
/*
* SonarQube
* Copyright (C) 2009-2018 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
import { shallow } from 'enzyme';
import LeakPeriodLegend from '../LeakPeriodLegend';
import { PeriodMode } from '../../../../helpers/periods';
import { differenceInDays } from '../../../../helpers/dates';

jest.mock('../../../../helpers/dates', () => {
const dates = require.requireActual('../../../../helpers/dates');
dates.differenceInDays = jest.fn().mockReturnValue(10);
return dates;
});

it('10 days', () => {
const period = {
date: '2013-09-22T00:00:00+0200',
index: 0,
mode: PeriodMode.days,
parameter: '10'
};
expect(shallow(<LeakPeriodLegend period={period} />)).toMatchSnapshot();
});

it('date', () => {
const period = {
date: '2013-09-22T00:00:00+0200',
index: 0,
mode: PeriodMode.date,
parameter: '2013-01-01'
};
expect(shallow(<LeakPeriodLegend period={period} />)).toMatchSnapshot();
});

it('version', () => {
const period = {
date: '2013-09-22T00:00:00+0200',
index: 0,
mode: PeriodMode.version,
parameter: '0.1'
};
expect(shallow(<LeakPeriodLegend period={period} />).find('.overview-legend')).toMatchSnapshot();
});

it('previous_version', () => {
const period = {
date: '2013-09-22T00:00:00+0200',
index: 0,
mode: PeriodMode.previousVersion
};
expect(shallow(<LeakPeriodLegend period={period} />).find('.overview-legend')).toMatchSnapshot();
});

it('previous_analysis', () => {
const period = {
date: '2013-09-22T00:00:00+0200',
index: 0,
mode: PeriodMode.previousAnalysis
};
expect(shallow(<LeakPeriodLegend period={period} />).find('.overview-legend')).toMatchSnapshot();
});

it('should render a more precise date', () => {
(differenceInDays as jest.Mock<any>).mockReturnValueOnce(0);
const period = {
date: '2018-08-17T00:00:00+0200',
index: 0,
mode: PeriodMode.previousVersion
};
expect(shallow(<LeakPeriodLegend period={period} />)).toMatchSnapshot();
});

+ 0
- 21
server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/LeakPeriodLegend-test.js.snap 查看文件

@@ -1,21 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`check note 10 days 1`] = `
<div
className="overview-legend overview-legend-spaced-line"
>
overview.new_code_period_x.overview.period.days.10
</div>
`;

exports[`check note date 1`] = `
<DateFromNow
date={2013-09-21T22:00:00.000Z}
/>
`;

exports[`check note version 1`] = `
<DateFromNow
date={2013-09-21T22:00:00.000Z}
/>
`;

+ 90
- 0
server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/LeakPeriodLegend-test.tsx.snap 查看文件

@@ -0,0 +1,90 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`10 days 1`] = `
<div
className="overview-legend overview-legend-spaced-line"
>
overview.new_code_period_x.overview.period.days.10
</div>
`;

exports[`date 1`] = `
<Tooltip
overlay={
<DateFormatter
date={2013-09-21T22:00:00.000Z}
long={true}
>
[Function]
</DateFormatter>
}
>
<div
className="overview-legend"
>
overview.new_code_period_x.overview.period.date.2013-01-01
<br />
<DateFromNow
date={2013-09-21T22:00:00.000Z}
/>
</div>
</Tooltip>
`;

exports[`previous_analysis 1`] = `
<div
className="overview-legend"
>
overview.new_code_period_x.overview.period.previous_analysis.
<br />
<DateFromNow
date={2013-09-21T22:00:00.000Z}
/>
</div>
`;

exports[`previous_version 1`] = `
<div
className="overview-legend"
>
overview.new_code_period_x.overview.period.previous_version_only_date
<br />
<DateFromNow
date={2013-09-21T22:00:00.000Z}
/>
</div>
`;

exports[`should render a more precise date 1`] = `
<Tooltip
overlay={
<DateTimeFormatter
date={2018-08-16T22:00:00.000Z}
>
[Function]
</DateTimeFormatter>
}
>
<div
className="overview-legend"
>
overview.new_code_period_x.overview.period.previous_version_only_date
<br />
<DateFromNow
date={2018-08-16T22:00:00.000Z}
/>
</div>
</Tooltip>
`;

exports[`version 1`] = `
<div
className="overview-legend"
>
overview.new_code_period_x.overview.period.version.0.1
<br />
<DateFromNow
date={2013-09-21T22:00:00.000Z}
/>
</div>
`;

+ 2
- 2
server/sonar-web/src/main/js/apps/overview/main/enhance.tsx 查看文件

@@ -33,7 +33,7 @@ import {
getRatingTooltip
} from '../../../helpers/measures';
import { getLocalizedMetricName } from '../../../helpers/l10n';
import { getPeriodDate } from '../../../helpers/periods';
import { getPeriodDate, Period } from '../../../helpers/periods';
import {
getComponentDrilldownUrl,
getComponentIssuesUrl,
@@ -47,7 +47,7 @@ export interface EnhanceProps {
branchLike?: BranchLike;
component: Component;
measures: MeasureEnhanced[];
leakPeriod?: { index: number; date?: string };
leakPeriod?: Period;
history?: History;
historyStartDate?: Date;
}

+ 14
- 7
server/sonar-web/src/main/js/helpers/periods.ts 查看文件

@@ -20,15 +20,23 @@
import { translate, translateWithParameters } from './l10n';
import { parseDate } from './dates';

export enum PeriodMode {
days = 'days',
date = 'date',
version = 'version',
previousAnalysis = 'previous_analysis',
previousVersion = 'previous_version'
}

export interface Period {
date: string;
index: number;
mode: string;
mode: PeriodMode;
modeParam?: string;
parameter: string;
parameter?: string;
}

export function getPeriod(periods: Period[] | undefined, index: number): Period | undefined {
export function getPeriod(periods: Period[] | undefined, index: number) {
if (!Array.isArray(periods)) {
return undefined;
}
@@ -36,22 +44,21 @@ export function getPeriod(periods: Period[] | undefined, index: number): Period
return periods.find(period => period.index === index);
}

export function getLeakPeriod(periods: Period[] | undefined): Period | undefined {
export function getLeakPeriod(periods: Period[] | undefined) {
return getPeriod(periods, 1);
}

export function getPeriodLabel(period: Period | undefined): string | undefined {
export function getPeriodLabel(period: Period | undefined) {
if (!period) {
return undefined;
}

const parameter = period.modeParam || period.parameter;

if (period.mode === 'previous_version' && !parameter) {
return translate('overview.period.previous_version_only_date');
}

return translateWithParameters(`overview.period.${period.mode}`, parameter);
return translateWithParameters(`overview.period.${period.mode}`, parameter || '');
}

export function getPeriodDate(period?: { date?: string }): Date | undefined {

+ 4
- 4
sonar-core/src/main/resources/org/sonar/l10n/core.properties 查看文件

@@ -898,7 +898,7 @@ property.error.notFloat=Not a floating point number
property.error.notRegexp=Not a valid Java regular expression
property.error.notInOptions=Not a valid option
property.category.scm=SCM
property.sonar.leak.period.description=Period used to compare measures and track new issues. Values are:<ul class='bullet'><li>Number of days before analysis, for example 5.</li><li>A custom date. Format is yyyy-MM-dd, for example 2010-12-25</li><li>'previous_version' to compare to the previous version in the project history</li><li>A version, for example '1.2' or 'BASELINE'</li></ul><p>When specifying a number of days or a date, the snapshot selected for comparison is the first one available inside the corresponding time range. </p><p>Changing this property only takes effect after subsequent project inspections.<p/>
property.sonar.leak.period.description=Period used to compare measures and track new issues. Values are:<ul class='bullet'><li>Number of days before analysis, for example 5.</li><li>A custom date. Format is yyyy-MM-dd, for example 2010-12-25</li><li>'previous_version' to compare to the previous version in the project history</li><li>A version, for example '1.2' or 'BASELINE'</li></ul><p>When specifying a number of days or a date, the snapshot selected as the baseline for comparison is the first one available inside the corresponding time range. Specifically, the first analysis in the range is considered to be before the leak period(/new code period). </p><p>Changing this property only takes effect after subsequent project inspections.<p/>
property.sonar.branch.longLivedBranches.regex.description=Regular expression used to detect whether a branch is a long living branch (as opposed to short living branch), based on its name. This applies only during first analysis, the type of a branch cannot be changed later.


@@ -2357,9 +2357,9 @@ overview.quality_gate.ignored_conditions.tooltip=At the start of a new code peri
overview.quality_profiles=Quality Profiles
overview.new_code_period_x=New code: {0}
overview.started_x=started {0}
overview.last_analysis_x=last analysis {0}
overview.previous_analysis_x=Previous analysis {0}
overview.started_on_x=Started on {0}
overview.last_analysis_on_x=Last analysis on {0}
overview.previous_analysis_on_x=Previous analysis on {0}
overview.on_new_code=On New Code
overview.about_this_portfolio=About This Portfolio
overview.about_this_project.APP=About This Application
@@ -2407,7 +2407,7 @@ overview.period.previous_version_only_date=since previous version
overview.period.previous_analysis=since previous analysis
overview.period.days=last {0} days
overview.period.version=since {0}
overview.period.date=since {0}
overview.period.date=after {0}

overview.gate.ERROR=Failed
overview.gate.WARN=Warning

Loading…
取消
儲存