overlay={translate('quality_gates.conditions.warning.tooltip')} | overlay={translate('quality_gates.conditions.warning.tooltip')} | ||||
/> | /> | ||||
)} | )} | ||||
<Level level={project.qualityGate} /> | |||||
<Level aria-label={translate('quality_gates.status')} level={project.qualityGate} /> | |||||
</div> | </div> | ||||
)} | )} | ||||
</aside> | </aside> |
import MeasuresButtonLink from './MeasuresButtonLink'; | import MeasuresButtonLink from './MeasuresButtonLink'; | ||||
import RatingFreshness from './RatingFreshness'; | import RatingFreshness from './RatingFreshness'; | ||||
interface Props { | |||||
export interface MetricBoxProps { | |||||
component: string; | component: string; | ||||
measures: T.Dict<string | undefined>; | measures: T.Dict<string | undefined>; | ||||
metricKey: string; | metricKey: string; | ||||
} | } | ||||
export default function MetricBox({ component, measures, metricKey }: Props) { | |||||
export default function MetricBox({ component, measures, metricKey }: MetricBoxProps) { | |||||
const keys = METRICS_PER_TYPE[metricKey]; | const keys = METRICS_PER_TYPE[metricKey]; | ||||
const rating = measures[keys.rating]; | const rating = measures[keys.rating]; | ||||
const lastReliabilityChange = measures[keys.last_change]; | const lastReliabilityChange = measures[keys.last_change]; | ||||
? translate('project_singular') | ? translate('project_singular') | ||||
: translate('project_plural')} | : translate('project_plural')} | ||||
</span> | </span> | ||||
</Link>{' '} | |||||
<Level level="ERROR" small={true} /> | |||||
</Link> | |||||
<Level | |||||
aria-label={ | |||||
Number(effort) === 1 | |||||
? translate('portfolio.has_qg_status') | |||||
: translate('portfolio.have_qg_status') | |||||
} | |||||
className="little-spacer-left" | |||||
level="ERROR" | |||||
small={true} | |||||
/> | |||||
</div> | </div> | ||||
</> | </> | ||||
) | ) |
* along with this program; if not, write to the Free Software Foundation, | * along with this program; if not, write to the Free Software Foundation, | ||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||||
*/ | */ | ||||
/* eslint-disable sonarjs/no-duplicate-string */ | |||||
import { shallow } from 'enzyme'; | import { shallow } from 'enzyme'; | ||||
import * as React from 'react'; | import * as React from 'react'; | ||||
import MetricBox from '../MetricBox'; | |||||
import MetricBox, { MetricBoxProps } from '../MetricBox'; | |||||
it('should render correctly', () => { | it('should render correctly', () => { | ||||
const measures = { | |||||
reliability_rating: '3', | |||||
last_change_on_reliability_rating: '{"date":"2017-01-02T00:00:00.000Z","value":2}', | |||||
reliability_rating_effort: '{"rating":3,"projects":1}' | |||||
}; | |||||
expect( | |||||
shallow(<MetricBox component="foo" measures={measures} metricKey="reliability" />) | |||||
).toMatchSnapshot(); | |||||
expect(shallowRender({ metricKey: 'reliability' })).toMatchSnapshot(); | |||||
}); | }); | ||||
it('should render correctly for releasability', () => { | it('should render correctly for releasability', () => { | ||||
last_change_on_releasability_rating: '{"date":"2017-01-02T00:00:00.000Z","value":2}', | last_change_on_releasability_rating: '{"date":"2017-01-02T00:00:00.000Z","value":2}', | ||||
releasability_effort: '5' | releasability_effort: '5' | ||||
}; | }; | ||||
expect( | |||||
shallow(<MetricBox component="foo" measures={measures} metricKey="releasability" />) | |||||
).toMatchSnapshot(); | |||||
expect(shallowRender({ measures })).toMatchSnapshot(); | |||||
expect(shallowRender({ measures: { ...measures, releasability_effort: '1' } })).toMatchSnapshot(); | |||||
}); | }); | ||||
it('should render correctly when no effort', () => { | it('should render correctly when no effort', () => { | ||||
const measures = { | |||||
releasability_rating: '2', | |||||
last_change_on_releasability_rating: '{"date":"2017-01-02T00:00:00.000Z","value":2}', | |||||
releasability_effort: '0' | |||||
}; | |||||
expect( | expect( | ||||
shallow(<MetricBox component="foo" measures={measures} metricKey="releasability" />) | |||||
shallowRender({ | |||||
measures: { | |||||
releasability_rating: '2', | |||||
last_change_on_releasability_rating: '{"date":"2017-01-02T00:00:00.000Z","value":2}', | |||||
releasability_effort: '0' | |||||
} | |||||
}) | |||||
).toMatchSnapshot(); | ).toMatchSnapshot(); | ||||
}); | }); | ||||
function shallowRender(props: Partial<MetricBoxProps> = {}) { | |||||
return shallow( | |||||
<MetricBox | |||||
component="foo" | |||||
measures={{ | |||||
reliability_rating: '3', | |||||
last_change_on_reliability_rating: '{"date":"2017-01-02T00:00:00.000Z","value":2}', | |||||
reliability_rating_effort: '{"rating":3,"projects":1}' | |||||
}} | |||||
metricKey="releasability" | |||||
{...props} | |||||
/> | |||||
); | |||||
} |
project_plural | project_plural | ||||
</span> | </span> | ||||
</Link> | </Link> | ||||
<Level | <Level | ||||
aria-label="portfolio.have_qg_status" | |||||
className="little-spacer-left" | |||||
level="ERROR" | |||||
small={true} | |||||
/> | |||||
</div> | |||||
<div | |||||
className="portfolio-box-links" | |||||
> | |||||
<div> | |||||
<MeasuresButtonLink | |||||
component="foo" | |||||
metric="Releasability" | |||||
/> | |||||
</div> | |||||
<div> | |||||
<HistoryButtonLink | |||||
component="foo" | |||||
metric="releasability_rating" | |||||
/> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
`; | |||||
exports[`should render correctly for releasability 2`] = ` | |||||
<div | |||||
className="portfolio-box" | |||||
> | |||||
<h2 | |||||
className="portfolio-box-title" | |||||
> | |||||
metric_domain.Releasability | |||||
<HelpTooltip | |||||
className="little-spacer-left" | |||||
overlay="portfolio.metric_domain.releasability.help" | |||||
/> | |||||
</h2> | |||||
<MainRating | |||||
component="foo" | |||||
metric="releasability_rating" | |||||
value="2" | |||||
/> | |||||
<h3> | |||||
portfolio.metric_trend | |||||
</h3> | |||||
<RatingFreshness | |||||
lastChange="{\\"date\\":\\"2017-01-02T00:00:00.000Z\\",\\"value\\":2}" | |||||
rating="2" | |||||
/> | |||||
<h3> | |||||
portfolio.lowest_rated_projects | |||||
</h3> | |||||
<div | |||||
className="portfolio-effort" | |||||
> | |||||
<Link | |||||
onlyActiveOnIndex={false} | |||||
style={Object {}} | |||||
to={ | |||||
Object { | |||||
"pathname": "/component_measures", | |||||
"query": Object { | |||||
"id": "foo", | |||||
"metric": "alert_status", | |||||
}, | |||||
} | |||||
} | |||||
> | |||||
<span> | |||||
<Measure | |||||
className="little-spacer-right" | |||||
metricKey="projects" | |||||
metricType="SHORT_INT" | |||||
value={1} | |||||
/> | |||||
project_singular | |||||
</span> | |||||
</Link> | |||||
<Level | |||||
aria-label="portfolio.has_qg_status" | |||||
className="little-spacer-left" | |||||
level="ERROR" | level="ERROR" | ||||
small={true} | small={true} | ||||
/> | /> |
import { ResetButtonLink } from 'sonar-ui-common/components/controls/buttons'; | import { ResetButtonLink } from 'sonar-ui-common/components/controls/buttons'; | ||||
import DropdownIcon from 'sonar-ui-common/components/icons/DropdownIcon'; | import DropdownIcon from 'sonar-ui-common/components/icons/DropdownIcon'; | ||||
import Level from 'sonar-ui-common/components/ui/Level'; | import Level from 'sonar-ui-common/components/ui/Level'; | ||||
import { translate } from 'sonar-ui-common/helpers/l10n'; | |||||
import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n'; | |||||
import { getProjectUrl } from '../../../helpers/urls'; | import { getProjectUrl } from '../../../helpers/urls'; | ||||
export type RichQualityGateEvent = T.AnalysisEvent & Required<Pick<T.AnalysisEvent, 'qualityGate'>>; | export type RichQualityGateEvent = T.AnalysisEvent & Required<Pick<T.AnalysisEvent, 'qualityGate'>>; | ||||
<ul className="spacer-left spacer-top"> | <ul className="spacer-left spacer-top"> | ||||
{event.qualityGate.failing.map(project => ( | {event.qualityGate.failing.map(project => ( | ||||
<li className="display-flex-center spacer-top" key={project.key}> | <li className="display-flex-center spacer-top" key={project.key}> | ||||
<Level className="spacer-right" level={event.qualityGate.status} small={true} /> | |||||
<Level | |||||
aria-label={translate('quality_gates.status')} | |||||
className="spacer-right" | |||||
level={event.qualityGate.status} | |||||
small={true} | |||||
/> | |||||
<div className="flex-1 text-ellipsis"> | <div className="flex-1 text-ellipsis"> | ||||
<Link | <Link | ||||
onClick={this.stopPropagation} | onClick={this.stopPropagation} | ||||
title={project.name} | title={project.name} | ||||
to={getProjectUrl(project.key, project.branch)}> | to={getProjectUrl(project.key, project.branch)}> | ||||
{project.name} | |||||
<span aria-label={translateWithParameters('project_x', project.name)}> | |||||
{project.name} | |||||
</span> | |||||
</Link> | </Link> | ||||
</div> | </div> | ||||
</li> | </li> |
key="foo" | key="foo" | ||||
> | > | ||||
<Level | <Level | ||||
aria-label="quality_gates.status" | |||||
className="spacer-right" | className="spacer-right" | ||||
level="ERROR" | level="ERROR" | ||||
small={true} | small={true} | ||||
} | } | ||||
} | } | ||||
> | > | ||||
Foo | |||||
<span | |||||
aria-label="project_x.Foo" | |||||
> | |||||
Foo | |||||
</span> | |||||
</Link> | </Link> | ||||
</div> | </div> | ||||
</li> | </li> | ||||
key="bar" | key="bar" | ||||
> | > | ||||
<Level | <Level | ||||
aria-label="quality_gates.status" | |||||
className="spacer-right" | className="spacer-right" | ||||
level="ERROR" | level="ERROR" | ||||
small={true} | small={true} | ||||
} | } | ||||
} | } | ||||
> | > | ||||
Bar | |||||
<span | |||||
aria-label="project_x.Bar" | |||||
> | |||||
Bar | |||||
</span> | |||||
</Link> | </Link> | ||||
</div> | </div> | ||||
</li> | </li> |
<div className="project-card-quality-gate big-spacer-left"> | <div className="project-card-quality-gate big-spacer-left"> | ||||
<Tooltip overlay={tooltip}> | <Tooltip overlay={tooltip}> | ||||
<div className="project-card-measure-inner"> | <div className="project-card-measure-inner"> | ||||
<Level level={status} small={true} /> | |||||
<Level aria-label={translate('quality_gates.status')} level={status} small={true} /> | |||||
{status === 'WARN' && ( | {status === 'WARN' && ( | ||||
<HelpTooltip | <HelpTooltip | ||||
className="little-spacer-left" | className="little-spacer-left" |
className="project-card-measure-inner" | className="project-card-measure-inner" | ||||
> | > | ||||
<Level | <Level | ||||
aria-label="quality_gates.status" | |||||
level="ERROR" | level="ERROR" | ||||
small={true} | small={true} | ||||
/> | /> |
permalink=Permanent Link | permalink=Permanent Link | ||||
plugin=Plugin | plugin=Plugin | ||||
project=Project | project=Project | ||||
project_x=Project: {0} | |||||
projects=Projects | projects=Projects | ||||
projects_=project(s) | projects_=project(s) | ||||
project_singular=project | project_singular=project | ||||
quality_gates.built_in=Built-in | quality_gates.built_in=Built-in | ||||
quality_gates.built_in.description.1=This quality gate is provided by default. | quality_gates.built_in.description.1=This quality gate is provided by default. | ||||
quality_gates.built_in.description.2=It will automatically be updated with the latest recommendations. | quality_gates.built_in.description.2=It will automatically be updated with the latest recommendations. | ||||
quality_gates.status=Quality Gate status | |||||
#------------------------------------------------------------------------------ | #------------------------------------------------------------------------------ | ||||
portfolio.has_always_been_x=has always been {rating} | portfolio.has_always_been_x=has always been {rating} | ||||
portfolio.was_x_y=was {rating} {date} | portfolio.was_x_y=was {rating} {date} | ||||
portfolio.x_in_y={projects} in {rating} | portfolio.x_in_y={projects} in {rating} | ||||
portfolio.has_qg_status=Has Quality Gate Status | |||||
portfolio.have_qg_status=Have Quality Gate Status | |||||
portfolio.empty=This portfolio is empty. | portfolio.empty=This portfolio is empty. | ||||
portfolio.no_lines_of_code=All projects in this portfolio are empty | portfolio.no_lines_of_code=All projects in this portfolio are empty | ||||
portfolio.not_computed=This portfolio is not yet computed. | portfolio.not_computed=This portfolio is not yet computed. |