]> source.dussan.org Git - sonarqube.git/commitdiff
fixup! SONAR-10471 Improve leak period description for specific date
authorGrégoire Aubert <gregoire.aubert@sonarsource.com>
Mon, 20 Aug 2018 09:42:43 +0000 (11:42 +0200)
committerSonarTech <sonartech@sonarsource.com>
Mon, 20 Aug 2018 18:21:03 +0000 (20:21 +0200)
server/sonar-web/src/main/js/apps/component-measures/components/LeakPeriodLegend.tsx
server/sonar-web/src/main/js/apps/component-measures/components/__tests__/LeakPeriodLegend-test.tsx
server/sonar-web/src/main/js/apps/overview/components/LeakPeriodLegend.tsx
server/sonar-web/src/main/js/apps/overview/components/__tests__/LeakPeriodLegend-test.tsx
server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/LeakPeriodLegend-test.tsx.snap
server/sonar-web/src/main/js/helpers/periods.ts
sonar-core/src/main/resources/org/sonar/l10n/core.properties

index bed6f18b8b27723b97b47cebd8f2bc9263b227c4..8c61f4e87b71e40909998f2178032caa327b422a 100644 (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>;
+  }
 }
index 90ff902c1de9702629bade1840a67eadf95ef3cf..475b6e17bc6ee4148c8a815b87a341d5286c8b50 100644 (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 }
+    }
+  });
+}
index 090aaceea7d6ef4032a032c677699400c68630d8..90b9b56931183b793df2c49e182605611dda34e5 100644 (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>
+    );
+  }
 }
index 77d51c4b0df348f0aea50ec07612bd32b1399dce..fa6f4c952fe62b07056c0ba2e2e3fa8bf14b5eb4 100644 (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 }
+    }
+  });
+}
index 981c88be95dee1ff6381921b2b7eb4dcde259aff..b11608008df42f267bccca6abc695151161af500 100644 (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}
index 7583847872999b23134e7d062dc0a65095eee7e4..d278a904b9d49a0f7640fe75951b49b74158a7b6 100644 (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));
-}
index 796b4439143cd1bf5bde86b86598e5e2e3e659bc..7d23bdbf56a3744fb5e5a339c3e19bff9a64de04 100644 (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