<div className="boxed-group">
<h2>{translate('about_page.fix_the_leak')}</h2>
<div className="boxed-group-inner">
- <p className="about-page-text">{translate('about_page.fix_the_leak.text')}</p>
+ <p className="about-page-text">{translate('about_page.fix_the_leak_on_new_code.text')}</p>
<ReadMore link={link} />
</div>
</div>
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.leak_period')}</div>;
+ return <div className={leakClass}>{translate('issues.new_code_period')}</div>;
}
const label = (
<div className={leakClass}>
- {translateWithParameters('overview.leak_period_x', getPeriodLabel(period))}
+ {translateWithParameters('overview.new_code_period_x', getPeriodLabel(period))}
</div>
);
<div
className="domain-measures-leak-header"
>
- overview.leak_period_x.overview.period.previous_version.6,4
+ overview.new_code_period_x.overview.period.previous_version.6,4
</div>
</Tooltip>
`;
<div
className="domain-measures-leak-header"
>
- overview.leak_period_x.overview.period.days.18
+ overview.new_code_period_x.overview.period.days.18
</div>
`;
<div
className="domain-measures-leak-header"
>
- issues.leak_period
+ issues.new_code_period
</div>
`;
values.push(translate('issues.facet.createdAt.last_year'));
}
if (sinceLeakPeriod) {
- values.push(translate('issues.leak_period'));
+ values.push(translate('issues.new_code_period'));
}
return values;
}
<FacetItem
active={sinceLeakPeriod}
loading={this.props.loading}
- name={translate('issues.leak_period')}
+ name={translate('issues.new_code')}
onClick={this.handleLeakPeriodClick}
value=""
/>
return (
<Tooltip overlay={this.renderOverlay()}>
<div className={classNames('overview-legend', { 'overview-legend-spaced-line': !leak })}>
- {translate('issues.max_leak_period')}:{' '}
+ {translate('issues.max_new_code_period')}:{' '}
{leak && (
<>
<DateFromNow date={leak.date}>
if (period.mode === 'days') {
return (
<div className="overview-legend overview-legend-spaced-line">
- {translateWithParameters('overview.leak_period_x', leakPeriodLabel)}
+ {translateWithParameters('overview.new_code_period_x', leakPeriodLabel)}
</div>
);
}
return (
<Tooltip overlay={tooltip}>
<div className="overview-legend">
- {translateWithParameters('overview.leak_period_x', leakPeriodLabel)}
+ {translateWithParameters('overview.new_code_period_x', leakPeriodLabel)}
<br />
<DateFromNow date={leakPeriodDate}>
{fromNow => (
<div
className="overview-legend overview-legend-spaced-line"
>
- issues.max_leak_period
+ issues.max_new_code_period
:
</div>
<div
className="overview-legend"
>
- issues.max_leak_period
+ issues.max_new_code_period
:
<React.Fragment>
<div
className="overview-legend overview-legend-spaced-line"
>
- overview.leak_period_x.overview.period.days.10
+ overview.new_code_period_x.overview.period.days.10
</div>
`;
<li className={classNames({ 'is-on-leak': isDiff })} key={condition.metric}>
<span className="text-limited">
<strong>{formatMeasure(condition.value, metric.type)}</strong> {metricName}
- {!isDiff && condition.onLeak && ' ' + translate('quality_gates.conditions.leak')}
+ {!isDiff && condition.onLeak && ' ' + translate('quality_gates.conditions.new_code')}
</span>
<span
className={classNames('big-spacer-left', {
{!isDiff &&
condition.period != null && (
<div className="overview-quality-gate-condition-period">
- {translate('quality_gates.conditions.leak')}
+ {translate('quality_gates.conditions.new_code')}
</div>
)}
<div className="overview-quality-gate-threshold">
</strong>
Bugs
- quality_gates.conditions.leak
+ quality_gates.conditions.new_code
</span>
<span
className="big-spacer-left text-danger"
<ActionsDropdownItem
className="js-change-leak-period"
onClick={this.handleChangeLeakClick}>
- {translate('branches.set_leak_period')}
+ {translate('branches.set_new_code_period')}
</ActionsDropdownItem>
)}
{isLongLivingBranch(branchLike) && <ActionsDropdownDivider />}
render() {
const { setting } = this.state;
- const header = translate('branches.set_leak_period');
+ const header = translate('branches.set_new_code_period');
return (
<Modal contentLabel={header} onRequestClose={this.props.onClose}>
expect(utils.parseSorting('-size')).toEqual({ sortDesc: true, sortValue: 'size' });
});
});
+
+describe('formatDuration', () => {
+ const ONE_MINUTE = 60000;
+ const ONE_HOUR = 60 * ONE_MINUTE;
+ const ONE_DAY = 24 * ONE_HOUR;
+ const ONE_MONTH = 30 * ONE_DAY;
+ const ONE_YEAR = 12 * ONE_MONTH;
+ it('render years and months only', () => {
+ expect(utils.formatDuration(ONE_YEAR * 4 + ONE_MONTH * 2 + ONE_DAY * 10)).toEqual(
+ 'duration.years.4 duration.months.2 '
+ );
+ });
+
+ it('render years only', () => {
+ expect(utils.formatDuration(ONE_YEAR * 4 + ONE_DAY * 10)).toEqual('duration.years.4 ');
+ });
+
+ it('render hours and minutes', () => {
+ expect(utils.formatDuration(ONE_HOUR * 4 + ONE_MINUTE * 10)).toEqual(
+ 'duration.hours.4 duration.minutes.10 '
+ );
+ });
+
+ it('render days only', () => {
+ expect(utils.formatDuration(ONE_DAY * 4 + ONE_MINUTE * 10)).toEqual('duration.days.4 ');
+ });
+
+ it('render less than a minute', () => {
+ expect(utils.formatDuration(1000)).toEqual('duration.seconds');
+ });
+});
const options = [
...VIEWS.map(opt => ({
type: 'view',
- value: opt,
- label: translate('projects.view', opt)
+ value: opt.value,
+ label: translate('projects.view', opt.label)
})),
...VISUALIZATIONS.map(opt => ({
type: 'visualization',
import ProjectCardLeakMeasures from './ProjectCardLeakMeasures';
import ProjectCardOrganizationContainer from './ProjectCardOrganizationContainer';
import Favorite from '../../../components/controls/Favorite';
-import DateFromNow from '../../../components/intl/DateFromNow';
import DateTimeFormatter from '../../../components/intl/DateTimeFormatter';
import TagsList from '../../../components/tags/TagsList';
import PrivacyBadgeContainer from '../../../components/common/PrivacyBadgeContainer';
import { translate, translateWithParameters } from '../../../helpers/l10n';
import { Project } from '../types';
import { Organization } from '../../../app/types';
+import { formatDuration } from '../utils';
interface Props {
height: number;
export default function ProjectCardLeak({ height, organization, project }: Props) {
const { measures } = project;
const hasTags = project.tags.length > 0;
-
+ const period = project.leakPeriodDate
+ ? new Date().getTime() - new Date(project.leakPeriodDate).getTime()
+ : 0;
return (
<div className="boxed-group project-card" data-key={project.key} style={{ height }}>
<div className="boxed-group-header clearfix">
{project.analysisDate &&
project.leakPeriodDate && (
<div className="project-card-dates note text-right pull-right">
- <DateFromNow date={project.leakPeriodDate}>
- {fromNow => (
- <span className="project-card-leak-date pull-right">
- {translateWithParameters('projects.leak_period_x', fromNow)}
- </span>
- )}
- </DateFromNow>
+ <span className="project-card-leak-date pull-right">
+ {translateWithParameters('projects.new_code_period_x', formatDuration(period))}
+ </span>
<DateTimeFormatter date={project.analysisDate}>
{formattedDate => (
<span>
<div className="boxed-group-inner">
<div className="note project-card-not-analyzed">
{project.analysisDate
- ? translate('projects.no_leak_period')
+ ? translate('projects.no_new_code_period')
: translate('projects.not_analyzed')}
</div>
</div>
it('should display analysis date and leak start date', () => {
const card = shallow(<ProjectCardLeak height={100} organization={undefined} project={PROJECT} />);
expect(card.find('.project-card-dates').exists()).toBeTruthy();
- expect(card.find('.project-card-dates').find('DateFromNow')).toHaveLength(1);
+ expect(card.find('.project-card-dates').find('.project-card-leak-date')).toHaveLength(1);
expect(card.find('.project-card-dates').find('DateTimeFormatter')).toHaveLength(1);
});
"value": "overall",
},
Object {
- "label": "projects.view.leak",
+ "label": "projects.view.new_code",
"type": "view",
"value": "leak",
},
"value": "overall",
},
Object {
- "label": "projects.view.leak",
+ "label": "projects.view.new_code",
"type": "view",
"value": "leak",
},
<div
className="project-card-dates note text-right pull-right"
>
- <DateFromNow
- date="2016-12-01"
- />
+ <span
+ className="project-card-leak-date pull-right"
+ >
+ projects.new_code_period_x.duration.years.1 duration.months.8
+ </span>
<DateTimeFormatter
date="2017-01-01"
/>
/* eslint-disable camelcase */
import { uniq } from 'lodash';
import { Query, convertToFilter } from './query';
-import { translate } from '../../helpers/l10n';
+import { translate, translateWithParameters } from '../../helpers/l10n';
import { RequestData } from '../../helpers/request';
import { getOrganizations } from '../../api/organizations';
import { searchProjects, Facet } from '../../api/components';
new_lines: 'size'
};
-export const VIEWS = ['overall', 'leak'];
+export const VIEWS = [{ value: 'overall', label: 'overall' }, { value: 'leak', label: 'new_code' }];
export const VISUALIZATIONS = [
'risk',
};
return map[metricKey];
}
+
+const ONE_MINUTE = 60000;
+const ONE_HOUR = 60 * ONE_MINUTE;
+const ONE_DAY = 24 * ONE_HOUR;
+const ONE_MONTH = 30 * ONE_DAY;
+const ONE_YEAR = 12 * ONE_MONTH;
+
+function format(periods: Array<{ value: number; label: string }>) {
+ let result = '';
+ let count = 0;
+ let lastId = -1;
+ for (let i = 0; i < periods.length && count < 2; i++) {
+ if (periods[i].value > 0) {
+ count++;
+ if (lastId < 0 || lastId + 1 === i) {
+ lastId = i;
+ result += translateWithParameters(periods[i].label, periods[i].value) + ' ';
+ }
+ }
+ }
+ return result;
+}
+
+export function formatDuration(value: number) {
+ if(value < ONE_MINUTE) {
+ return translate('duration.seconds');
+ }
+ const years = Math.floor(value / ONE_YEAR);
+ value -= years * ONE_YEAR;
+ const months = Math.floor(value / ONE_MONTH);
+ value -= months * ONE_MONTH;
+ const days = Math.floor(value / ONE_DAY);
+ value -= days * ONE_DAY;
+ const hours = Math.floor(value / ONE_HOUR);
+ value -= hours * ONE_HOUR;
+ const minutes = Math.floor(value / ONE_MINUTE);
+ return format([
+ { value: years, label: 'duration.years' },
+ { value: months, label: 'duration.months' },
+ { value: days, label: 'duration.days' },
+ { value: hours, label: 'duration.hours' },
+ { value: minutes, label: 'duration.minutes' }
+ ]);
+}
{metric && (
<>
<div className="modal-field">
- <label>{translate('quality_gates.conditions.leak')}</label>
+ <label>{translate('quality_gates.conditions.new_code')}</label>
<Period
canEdit={true}
metric={metric}
/>
</div>
</th>
- <th className="thin nowrap">{translate('quality_gates.conditions.leak')}</th>
+ <th className="thin nowrap">{translate('quality_gates.conditions.new_code')}</th>
<th className="thin nowrap">{translate('quality_gates.conditions.operator')}</th>
<th className="thin nowrap">{translate('quality_gates.conditions.warning')}</th>
<th className="thin nowrap">{translate('quality_gates.conditions.error')}</th>
// SCANNER
PropertyDefinition.builder(LEAK_PERIOD)
- .name("Leak Period")
+ .name("New Code Period")
.deprecatedKey("sonar.timemachine.period1")
.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>" +
issues.to_navigate=to navigate
issues.to_navigate_issue_locations=to navigate issue locations
issues.to_switch_flows=to switch flows
-issues.leak_period=Leak Period
-issues.max_leak_period=Max Leak Period
+issues.new_code=New code
+issues.new_code_period=New Code Period
+issues.max_new_code_period=Max New Code Period
issues.my_issues=My Issues
issues.no_my_issues=There are no issues assigned to you.
issues.no_issues=No Issues. Hooray!
projects.no_favorite_projects.favorite_public_projects=Favorite public projects
projects.explore_projects=Explore Projects
projects.not_analyzed=Project is not analyzed yet.
-projects.no_leak_period=Project has no leak data yet.
-projects.leak_period_x=Leak Period started: {0}
+projects.no_new_code_period=Project has no new code data yet.
+projects.new_code_period_x=New code: last {0}
projects.last_analysis_on_x=Last analysis: {0}
projects.search=Search by project name or key
projects.perspective=Perspective
projects.sorting.new_duplications=Duplications
projects.sorting.new_lines=New Lines
projects.view.overall=Overall Status
-projects.view.leak=Leak
+projects.view.new_code=New Code
projects.worse_of_reliablity_and_security=Worse of Reliability and Security
projects.visualization.risk=Risk
projects.visualization.risk.description=Get quick insights into the operational risks in your projects. Any color but green indicates immediate risks: Bugs or Vulnerabilities that should be examined. A position at the top or right of the graph means that the longer-term health of the project may be at risk. Green bubbles at the bottom-left are best.
property.category.general=General
property.category.general.email=Email
property.category.general.duplications=Duplications
-property.category.general.differentialViews=Leak
+property.category.general.differentialViews=New Code
property.category.general.localization=Localization
property.category.general.databaseCleaner=Database Cleaner
property.category.general.looknfeel=Look & Feel
property.category.security=Security
property.category.security.encryption=Encryption
property.category.java=Java
-property.category.differentialViews=Leak
+property.category.differentialViews=New Code
property.category.codeCoverage=Code Coverage
property.category.duplications=Duplications
property.category.localization=Localization
quality_gates.condition.leak.no=No
quality_gates.condition.leak.unconditional=Always
quality_gates.conditions.metric=Metric
-quality_gates.conditions.leak=Over Leak Period
+quality_gates.conditions.new_code=On New Code
quality_gates.conditions.operator=Operator
quality_gates.conditions.warning=Warning
quality_gates.conditions.error=Error
overview.quality_gate_failed_with_x=with {0} errors
overview.you_should_define_quality_gate=You should define a quality gate on this project.
overview.quality_gate.ignored_conditions=Some Quality Gate conditions on New Code were ignored because of the small number of New Lines
-overview.quality_gate.ignored_conditions.tooltip=At the start of a leak period, if very few lines have been added or modified, it might be difficult to reach the desired level of code coverage or duplications. To prevent Quality Gate failure when there's little that can be done about it, Quality Gate conditions about duplications in new code and coverage on new code are ignored until the number of new lines is at least 20.
+overview.quality_gate.ignored_conditions.tooltip=At the start of a new code period, if very few lines have been added or modified, it might be difficult to reach the desired level of code coverage or duplications. To prevent Quality Gate failure when there's little that can be done about it, Quality Gate conditions about duplications in new code and coverage on new code are ignored until the number of new lines is at least 20.
overview.quality_profiles=Quality Profiles
-overview.leak_period_x=Leak Period: {0}
+overview.new_code_period_x=New code: {0}
overview.started_x=started {0}
overview.last_analysis_x=last analysis {0}
overview.started_on_x=Started on {0}
about_page.clean_code.text=By fixing new issues as they appear in code, you create and maintain a clean code base. Even on legacy projects, focusing on keeping new code clean will eventually yield a code base you can be proud of.
about_page.fix_the_leak=Fix The Leak
-about_page.fix_the_leak.text=The water leak paradigm and the default Quality Gate are based on the leak period - the recent period against which you're tracking issues. For some previous_version makes the most sense, for others the last 30 days is a good option.
+about_page.fix_the_leak_on_new_code.text=The water leak paradigm and the default Quality Gate are based on the new code period - the recent period against which you're tracking issues. For some previous_version makes the most sense, for others the last 30 days is a good option.
about_page.quality_gates=Enforce Quality Gate
about_page.quality_gates.text=Your project's Quality Gate is the set of conditions the project must meet before it can be released into production. The Quality Gate is designed to ensure that the next version's quality will be better than the last.
branches.long_living_branches_pattern=Long living branches pattern
branches.detection_of_long_living_branches=Detection of long living branches
branches.detection_of_long_living_branches.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.
-branches.set_leak_period=Set Leak Period
+branches.set_new_code_period=Set New Code Period
branches.last_analysis_date=Last Analysis Date
branches.search_for_branches=Search for branches...
branches.pull_requests=Pull Requests