From 1629b3d84e8691b22cd20391fc4411915f854d2c Mon Sep 17 00:00:00 2001 From: =?utf8?q?Gr=C3=A9goire=20Aubert?= Date: Mon, 20 Aug 2018 11:42:43 +0200 Subject: [PATCH] fixup! SONAR-10471 Improve leak period description for specific date --- .../components/LeakPeriodLegend.tsx | 74 ++++++----- .../__tests__/LeakPeriodLegend-test.tsx | 23 +++- .../overview/components/LeakPeriodLegend.tsx | 120 ++++++++++-------- .../__tests__/LeakPeriodLegend-test.tsx | 94 ++++++++------ .../LeakPeriodLegend-test.tsx.snap | 2 +- .../sonar-web/src/main/js/helpers/periods.ts | 29 +++-- .../resources/org/sonar/l10n/core.properties | 4 +- 7 files changed, 198 insertions(+), 148 deletions(-) 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 bed6f18b8b2..8c61f4e87b7 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 @@ -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
{translate('issues.new_code_period')}
; - } +export default class LeakPeriodLegend extends React.PureComponent { + 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 = ( -
- {translateWithParameters('overview.new_code_period_x', leakPeriodLabel)} -
- ); + render() { + const { className, component, period } = this.props; + const leakClass = classNames('domain-measures-leak-header', className); + if (component.qualifier === 'APP') { + return
{translate('issues.new_code_period')}
; + } - if (period.mode === PeriodMode.days) { - return label; - } + const leakPeriodLabel = getPeriodLabel(period, this.formatDate); + if (!leakPeriodLabel) { + return null; + } - const date = getPeriodDate(period); - const tooltip = date && ( -
- - {', '} - {differenceInDays(new Date(), date) < 1 ? ( - - ) : ( - - )} -
- ); + const label = ( +
+ {translateWithParameters('overview.new_code_period_x', leakPeriodLabel)} +
+ ); - return {label}; + if (period.mode === PeriodMode.Days) { + return label; + } + + const date = getPeriodDate(period); + const tooltip = date && ( +
+ + {', '} + {differenceInDays(new Date(), date) < 1 ? ( + + ) : ( + + )} +
+ ); + + return {label}; + } } diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/LeakPeriodLegend-test.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/LeakPeriodLegend-test.tsx index 90ff902c1de..475b6e17bc6 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/LeakPeriodLegend-test.tsx +++ b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/LeakPeriodLegend-test.tsx @@ -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()).toMatchSnapshot(); - expect(shallow()).toMatchSnapshot(); + expect(getWrapper(PROJECT, PERIOD)).toMatchSnapshot(); + expect(getWrapper(PROJECT, PERIOD_DAYS)).toMatchSnapshot(); }); it('should render correctly for APP', () => { - expect(shallow()).toMatchSnapshot(); + expect(getWrapper(APP, PERIOD)).toMatchSnapshot(); }); it('should render a more precise date', () => { (differenceInDays as jest.Mock).mockReturnValueOnce(0); - expect(shallow()).toMatchSnapshot(); + expect(getWrapper(PROJECT, PERIOD)).toMatchSnapshot(); }); + +function getWrapper(component: ComponentMeasure, period: Period) { + return shallow(, { + context: { + intl: { formatDate: (date: string) => 'formatted.' + date } + } + }); +} 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 090aaceea7d..90b9b569311 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 @@ -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 { + static contextTypes = { + intl: PropTypes.object.isRequired + }; - if (period.mode === PeriodMode.days) { - return ( -
- {translateWithParameters('overview.new_code_period_x', leakPeriodLabel)} -
- ); - } + 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) => ( - - {translateWithParameters( - period.mode === PeriodMode.previousAnalysis - ? 'overview.previous_analysis_on_x' - : 'overview.started_on_x', - formattedLeakPeriodDate - )} - - ); + if (period.mode === PeriodMode.Days) { + return ( +
+ {translateWithParameters('overview.new_code_period_x', leakPeriodLabel)} +
+ ); + } - const tooltip = - differenceInDays(new Date(), leakPeriodDate) < 1 ? ( - {formattedDateFunction} - ) : ( - - {formattedDateFunction} - + const leakPeriodDate = getPeriodDate(period); + if (!leakPeriodDate) { + return null; + } + + const formattedDateFunction = (formattedLeakPeriodDate: string) => ( + + {translateWithParameters( + period.mode === PeriodMode.PreviousAnalysis + ? 'overview.previous_analysis_on_x' + : 'overview.started_on_x', + formattedLeakPeriodDate + )} + ); - return ( - -
- {translateWithParameters('overview.new_code_period_x', leakPeriodLabel)} -
- - {fromNow => ( - - {translateWithParameters( - period.mode === PeriodMode.previousAnalysis - ? 'overview.previous_analysis_x' - : 'overview.started_x', - fromNow - )} - - )} - -
-
- ); + const tooltip = + differenceInDays(new Date(), leakPeriodDate) < 1 ? ( + {formattedDateFunction} + ) : ( + + {formattedDateFunction} + + ); + + return ( + +
+ {translateWithParameters('overview.new_code_period_x', leakPeriodLabel)} +
+ + {fromNow => ( + + {translateWithParameters( + period.mode === PeriodMode.PreviousAnalysis + ? 'overview.previous_analysis_x' + : 'overview.started_x', + fromNow + )} + + )} + +
+
+ ); + } } diff --git a/server/sonar-web/src/main/js/apps/overview/components/__tests__/LeakPeriodLegend-test.tsx b/server/sonar-web/src/main/js/apps/overview/components/__tests__/LeakPeriodLegend-test.tsx index 77d51c4b0df..fa6f4c952fe 100644 --- a/server/sonar-web/src/main/js/apps/overview/components/__tests__/LeakPeriodLegend-test.tsx +++ b/server/sonar-web/src/main/js/apps/overview/components/__tests__/LeakPeriodLegend-test.tsx @@ -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()).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()).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().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().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().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).mockReturnValueOnce(0); - const period = { - date: '2018-08-17T00:00:00+0200', - index: 0, - mode: PeriodMode.previousVersion - }; - expect(shallow()).toMatchSnapshot(); + expect( + getWrapper({ + date: '2018-08-17T00:00:00+0200', + index: 0, + mode: PeriodMode.PreviousVersion + }) + ).toMatchSnapshot(); }); + +function getWrapper(period: Period) { + return shallow(, { + context: { + intl: { formatDate: (date: string) => 'formatted.' + date } + } + }); +} diff --git a/server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/LeakPeriodLegend-test.tsx.snap b/server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/LeakPeriodLegend-test.tsx.snap index 981c88be95d..b11608008df 100644 --- a/server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/LeakPeriodLegend-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/LeakPeriodLegend-test.tsx.snap @@ -22,7 +22,7 @@ exports[`date 1`] = `
- overview.new_code_period_x.overview.period.date.2013-01-01 + overview.new_code_period_x.overview.period.date.formatted.2013-01-01
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)); -} diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties index 796b4439143..7d23bdbf56a 100644 --- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties +++ b/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:
  • Number of days before analysis, for example 5.
  • A custom date. Format is yyyy-MM-dd, for example 2010-12-25
  • 'previous_version' to compare to the previous version in the project history
  • A version, for example '1.2' or 'BASELINE'

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).

Changing this property only takes effect after subsequent project inspections.

+property.sonar.leak.period.description=Period used to compare measures and track new issues. Values are:

  • Number of days before analysis, for example 5.
  • A custom date. Format is yyyy-MM-dd, for example 2010-12-25
  • 'previous_version' to compare to the previous version in the project history
  • A version, for example '1.2' or 'BASELINE'

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.

Changing this property only takes effect after subsequent project inspections.

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 -- 2.39.5