Browse Source

fixup! SONAR-10471 Improve leak period description for specific date

tags/7.5
Grégoire Aubert 5 years ago
parent
commit
1629b3d84e

+ 43
- 31
server/sonar-web/src/main/js/apps/component-measures/components/LeakPeriodLegend.tsx View File

@@ -19,8 +19,9 @@
*/
import * as React from 'react';
import * as classNames from 'classnames';
import * as PropTypes from 'prop-types';
import DateFromNow from '../../../components/intl/DateFromNow';
import DateFormatter from '../../../components/intl/DateFormatter';
import DateFormatter, { longFormatterOption } from '../../../components/intl/DateFormatter';
import DateTimeFormatter from '../../../components/intl/DateTimeFormatter';
import Tooltip from '../../../components/controls/Tooltip';
import { getPeriodLabel, getPeriodDate, Period, PeriodMode } from '../../../helpers/periods';
@@ -34,39 +35,50 @@ interface Props {
period: Period;
}

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>;
}
export default class LeakPeriodLegend extends React.PureComponent<Props> {
static contextTypes = {
intl: PropTypes.object.isRequired
};

const leakPeriodLabel = getPeriodLabel(period);
if (!leakPeriodLabel) {
return null;
}
formatDate = (date: string) => {
return this.context.intl.formatDate(date, longFormatterOption);
};

const label = (
<div className={leakClass}>
{translateWithParameters('overview.new_code_period_x', leakPeriodLabel)}
</div>
);
render() {
const { className, component, period } = this.props;
const leakClass = classNames('domain-measures-leak-header', className);
if (component.qualifier === 'APP') {
return <div className={leakClass}>{translate('issues.new_code_period')}</div>;
}

if (period.mode === PeriodMode.days) {
return label;
}
const leakPeriodLabel = getPeriodLabel(period, this.formatDate);
if (!leakPeriodLabel) {
return null;
}

const date = getPeriodDate(period);
const tooltip = date && (
<div>
<DateFromNow date={date} />
{', '}
{differenceInDays(new Date(), date) < 1 ? (
<DateTimeFormatter date={date} />
) : (
<DateFormatter date={date} long={true} />
)}
</div>
);
const label = (
<div className={leakClass}>
{translateWithParameters('overview.new_code_period_x', leakPeriodLabel)}
</div>
);

return <Tooltip overlay={tooltip}>{label}</Tooltip>;
if (period.mode === PeriodMode.Days) {
return label;
}

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

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

+ 16
- 7
server/sonar-web/src/main/js/apps/component-measures/components/__tests__/LeakPeriodLegend-test.tsx View File

@@ -20,8 +20,9 @@
import * as React from 'react';
import { shallow } from 'enzyme';
import LeakPeriodLegend from '../LeakPeriodLegend';
import { PeriodMode } from '../../../../helpers/periods';
import { PeriodMode, Period } from '../../../../helpers/periods';
import { differenceInDays } from '../../../../helpers/dates';
import { ComponentMeasure } from '../../../../app/types';

jest.mock('../../../../helpers/dates', () => {
const dates = require.requireActual('../../../../helpers/dates');
@@ -44,27 +45,35 @@ const APP = {
const PERIOD = {
date: '2017-05-16T13:50:02+0200',
index: 1,
mode: PeriodMode.previousVersion,
mode: PeriodMode.PreviousVersion,
parameter: '6,4'
};

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

it('should render correctly', () => {
expect(shallow(<LeakPeriodLegend component={PROJECT} period={PERIOD} />)).toMatchSnapshot();
expect(shallow(<LeakPeriodLegend component={PROJECT} period={PERIOD_DAYS} />)).toMatchSnapshot();
expect(getWrapper(PROJECT, PERIOD)).toMatchSnapshot();
expect(getWrapper(PROJECT, PERIOD_DAYS)).toMatchSnapshot();
});

it('should render correctly for APP', () => {
expect(shallow(<LeakPeriodLegend component={APP} period={PERIOD} />)).toMatchSnapshot();
expect(getWrapper(APP, PERIOD)).toMatchSnapshot();
});

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

function getWrapper(component: ComponentMeasure, period: Period) {
return shallow(<LeakPeriodLegend component={component} period={period} />, {
context: {
intl: { formatDate: (date: string) => 'formatted.' + date }
}
});
}

+ 66
- 54
server/sonar-web/src/main/js/apps/overview/components/LeakPeriodLegend.tsx View File

@@ -18,8 +18,9 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
import * as PropTypes from 'prop-types';
import DateFromNow from '../../../components/intl/DateFromNow';
import DateFormatter from '../../../components/intl/DateFormatter';
import DateFormatter, { longFormatterOption } from '../../../components/intl/DateFormatter';
import DateTimeFormatter from '../../../components/intl/DateTimeFormatter';
import Tooltip from '../../../components/controls/Tooltip';
import { getPeriodDate, getPeriodLabel, Period, PeriodMode } from '../../../helpers/periods';
@@ -30,63 +31,74 @@ interface Props {
period: Period;
}

export default function LeakPeriodLegend({ period }: Props) {
const leakPeriodLabel = getPeriodLabel(period);
if (!leakPeriodLabel) {
return null;
}
export default class LeakPeriodLegend extends React.PureComponent<Props> {
static contextTypes = {
intl: PropTypes.object.isRequired
};

if (period.mode === PeriodMode.days) {
return (
<div className="overview-legend overview-legend-spaced-line">
{translateWithParameters('overview.new_code_period_x', leakPeriodLabel)}
</div>
);
}
formatDate = (date: string) => {
return this.context.intl.formatDate(date, longFormatterOption);
};

const leakPeriodDate = getPeriodDate(period);
if (!leakPeriodDate) {
return null;
}
render() {
const { period } = this.props;
const leakPeriodLabel = getPeriodLabel(period, this.formatDate);
if (!leakPeriodLabel) {
return null;
}

const formattedDateFunction = (formattedLeakPeriodDate: string) => (
<span>
{translateWithParameters(
period.mode === PeriodMode.previousAnalysis
? 'overview.previous_analysis_on_x'
: 'overview.started_on_x',
formattedLeakPeriodDate
)}
</span>
);
if (period.mode === PeriodMode.Days) {
return (
<div className="overview-legend overview-legend-spaced-line">
{translateWithParameters('overview.new_code_period_x', leakPeriodLabel)}
</div>
);
}

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

const formattedDateFunction = (formattedLeakPeriodDate: string) => (
<span>
{translateWithParameters(
period.mode === PeriodMode.PreviousAnalysis
? 'overview.previous_analysis_on_x'
: 'overview.started_on_x',
formattedLeakPeriodDate
)}
</span>
);

return (
<Tooltip overlay={tooltip}>
<div className="overview-legend">
{translateWithParameters('overview.new_code_period_x', leakPeriodLabel)}
<br />
<DateFromNow date={leakPeriodDate}>
{fromNow => (
<span className="note">
{translateWithParameters(
period.mode === PeriodMode.previousAnalysis
? 'overview.previous_analysis_x'
: 'overview.started_x',
fromNow
)}
</span>
)}
</DateFromNow>
</div>
</Tooltip>
);
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">
{translateWithParameters('overview.new_code_period_x', leakPeriodLabel)}
<br />
<DateFromNow date={leakPeriodDate}>
{fromNow => (
<span className="note">
{translateWithParameters(
period.mode === PeriodMode.PreviousAnalysis
? 'overview.previous_analysis_x'
: 'overview.started_x',
fromNow
)}
</span>
)}
</DateFromNow>
</div>
</Tooltip>
);
}
}

+ 54
- 40
server/sonar-web/src/main/js/apps/overview/components/__tests__/LeakPeriodLegend-test.tsx View File

@@ -20,7 +20,7 @@
import * as React from 'react';
import { shallow } from 'enzyme';
import LeakPeriodLegend from '../LeakPeriodLegend';
import { PeriodMode } from '../../../../helpers/periods';
import { PeriodMode, Period } from '../../../../helpers/periods';
import { differenceInDays } from '../../../../helpers/dates';

jest.mock('../../../../helpers/dates', () => {
@@ -30,59 +30,73 @@ jest.mock('../../../../helpers/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();
expect(
getWrapper({
date: '2013-09-22T00:00:00+0200',
index: 0,
mode: PeriodMode.Days,
parameter: '10'
})
).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();
expect(
getWrapper({
date: '2013-09-22T00:00:00+0200',
index: 0,
mode: PeriodMode.Date,
parameter: '2013-01-01'
})
).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();
expect(
getWrapper({
date: '2013-09-22T00:00:00+0200',
index: 0,
mode: PeriodMode.Version,
parameter: '0.1'
}).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();
expect(
getWrapper({
date: '2013-09-22T00:00:00+0200',
index: 0,
mode: PeriodMode.PreviousVersion
}).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();
expect(
getWrapper({
date: '2013-09-22T00:00:00+0200',
index: 0,
mode: PeriodMode.PreviousAnalysis
}).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();
expect(
getWrapper({
date: '2018-08-17T00:00:00+0200',
index: 0,
mode: PeriodMode.PreviousVersion
})
).toMatchSnapshot();
});

function getWrapper(period: Period) {
return shallow(<LeakPeriodLegend period={period} />, {
context: {
intl: { formatDate: (date: string) => 'formatted.' + date }
}
});
}

+ 1
- 1
server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/LeakPeriodLegend-test.tsx.snap View File

@@ -22,7 +22,7 @@ exports[`date 1`] = `
<div
className="overview-legend"
>
overview.new_code_period_x.overview.period.date.2013-01-01
overview.new_code_period_x.overview.period.date.formatted.2013-01-01
<br />
<DateFromNow
date={2013-09-21T22:00:00.000Z}

+ 16
- 13
server/sonar-web/src/main/js/helpers/periods.ts View File

@@ -21,11 +21,11 @@ import { translate, translateWithParameters } from './l10n';
import { parseDate } from './dates';

export enum PeriodMode {
days = 'days',
date = 'date',
version = 'version',
previousAnalysis = 'previous_analysis',
previousVersion = 'previous_version'
Days = 'days',
Date = 'date',
Version = 'version',
PreviousAnalysis = 'previous_analysis',
PreviousVersion = 'previous_version'
}

export interface Period {
@@ -36,7 +36,7 @@ export interface Period {
parameter?: string;
}

export function getPeriod(periods: Period[] | undefined, index: number) {
function getPeriod(periods: Period[] | undefined, index: number) {
if (!Array.isArray(periods)) {
return undefined;
}
@@ -48,23 +48,26 @@ export function getLeakPeriod(periods: Period[] | undefined) {
return getPeriod(periods, 1);
}

export function getPeriodLabel(period: Period | undefined) {
export function getPeriodLabel(
period: Period | undefined,
dateFormatter: (date: string) => string
) {
if (!period) {
return undefined;
}

const parameter = period.modeParam || period.parameter;
if (period.mode === 'previous_version' && !parameter) {
let parameter = period.modeParam || period.parameter;
if (period.mode === PeriodMode.PreviousVersion && !parameter) {
return translate('overview.period.previous_version_only_date');
}

if (period.mode === PeriodMode.Date && parameter) {
parameter = dateFormatter(parameter);
}

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

export function getPeriodDate(period?: { date?: string }): Date | undefined {
return period && period.date ? parseDate(period.date) : undefined;
}

export function getLeakPeriodLabel(periods: Period[]): string | undefined {
return getPeriodLabel(getLeakPeriod(periods));
}

+ 2
- 2
sonar-core/src/main/resources/org/sonar/l10n/core.properties View File

@@ -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 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.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 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,7 +2357,7 @@ 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.previous_analysis_x=Previous analysis {0}
overview.previous_analysis_x=Previous analysis was {0}
overview.started_on_x=Started on {0}
overview.previous_analysis_on_x=Previous analysis on {0}
overview.on_new_code=On New Code

Loading…
Cancel
Save