describe('parseQuery', () => { | describe('parseQuery', () => { | ||||
it('should correctly parse the url query', () => { | it('should correctly parse the url query', () => { | ||||
expect(utils.parseQuery({})).toEqual({ | expect(utils.parseQuery({})).toEqual({ | ||||
metric: 'project_overview', | |||||
metric: utils.DEFAULT_METRIC, | |||||
selected: '', | selected: '', | ||||
view: utils.DEFAULT_VIEW | view: utils.DEFAULT_VIEW | ||||
}); | }); |
import { InjectedRouter } from 'react-router'; | import { InjectedRouter } from 'react-router'; | ||||
import Helmet from 'react-helmet'; | import Helmet from 'react-helmet'; | ||||
import MeasureContentContainer from './MeasureContentContainer'; | import MeasureContentContainer from './MeasureContentContainer'; | ||||
import MeasuresEmpty from './MeasuresEmpty'; | |||||
import MeasureOverviewContainer from './MeasureOverviewContainer'; | import MeasureOverviewContainer from './MeasureOverviewContainer'; | ||||
import Sidebar from '../sidebar/Sidebar'; | import Sidebar from '../sidebar/Sidebar'; | ||||
import ScreenPositionHelper from '../../../components/common/ScreenPositionHelper'; | import ScreenPositionHelper from '../../../components/common/ScreenPositionHelper'; | ||||
import { isProjectOverview, hasBubbleChart, parseQuery, serializeQuery, Query } from '../utils'; | |||||
import { | |||||
isProjectOverview, | |||||
hasBubbleChart, | |||||
parseQuery, | |||||
serializeQuery, | |||||
Query, | |||||
hasFullMeasures, | |||||
getMeasuresPageMetricKeys, | |||||
groupByDomains, | |||||
sortMeasures | |||||
} from '../utils'; | |||||
import { isSameBranchLike, getBranchLikeQuery } from '../../../helpers/branches'; | import { isSameBranchLike, getBranchLikeQuery } from '../../../helpers/branches'; | ||||
import Suggestions from '../../../app/components/embed-docs-modal/Suggestions'; | import Suggestions from '../../../app/components/embed-docs-modal/Suggestions'; | ||||
import { | import { | ||||
translateWithParameters, | translateWithParameters, | ||||
translate | translate | ||||
} from '../../../helpers/l10n'; | } from '../../../helpers/l10n'; | ||||
import { getDisplayMetrics } from '../../../helpers/measures'; | |||||
import { RawQuery } from '../../../helpers/query'; | import { RawQuery } from '../../../helpers/query'; | ||||
import { | import { | ||||
BranchLike, | BranchLike, | ||||
ComponentMeasure, | ComponentMeasure, | ||||
CurrentUser, | |||||
MeasureEnhanced, | MeasureEnhanced, | ||||
Metric, | Metric, | ||||
CurrentUser, | |||||
Period | Period | ||||
} from '../../../app/types'; | } from '../../../app/types'; | ||||
import '../../../components/search-navigator.css'; | import '../../../components/search-navigator.css'; | ||||
fetchMeasures = ({ branchLike, component, fetchMeasures, metrics }: Props) => { | fetchMeasures = ({ branchLike, component, fetchMeasures, metrics }: Props) => { | ||||
this.setState({ loading: true }); | this.setState({ loading: true }); | ||||
const filteredKeys = getDisplayMetrics(Object.values(metrics)).map(metric => metric.key); | |||||
const filteredKeys = getMeasuresPageMetricKeys(metrics, branchLike); | |||||
fetchMeasures(component.key, filteredKeys, branchLike).then( | fetchMeasures(component.key, filteredKeys, branchLike).then( | ||||
({ measures, leakPeriod }) => { | ({ measures, leakPeriod }) => { | ||||
if (this.mounted) { | if (this.mounted) { | ||||
); | ); | ||||
}; | }; | ||||
getHelmetTitle = (query: Query, displayOverview: boolean, metric?: Metric) => { | |||||
if (displayOverview && query.metric) { | |||||
return isProjectOverview(query.metric) | |||||
? translate('component_measures.overview.project_overview.facet') | |||||
: translateWithParameters( | |||||
'component_measures.domain_x_overview', | |||||
getLocalizedMetricDomain(query.metric) | |||||
); | |||||
} | |||||
return metric ? metric.name : translate('layout.measures'); | |||||
}; | |||||
getSelectedMetric = (query: Query, displayOverview: boolean) => { | |||||
if (displayOverview) { | |||||
return undefined; | |||||
} | |||||
const metric = this.props.metrics[query.metric]; | |||||
if (!metric) { | |||||
const domainMeasures = groupByDomains(this.state.measures); | |||||
const firstMeasure = | |||||
domainMeasures[0] && sortMeasures(domainMeasures[0].name, domainMeasures[0].measures)[0]; | |||||
if (firstMeasure && typeof firstMeasure !== 'string') { | |||||
return firstMeasure.metric; | |||||
} | |||||
} | |||||
return metric; | |||||
}; | |||||
updateQuery = (newQuery: Partial<Query>) => { | updateQuery = (newQuery: Partial<Query>) => { | ||||
const query = serializeQuery({ | const query = serializeQuery({ | ||||
...parseQuery(this.props.location.query), | ...parseQuery(this.props.location.query), | ||||
}); | }); | ||||
}; | }; | ||||
getHelmetTitle = (metric?: Metric) => { | |||||
if (metric && hasBubbleChart(metric.key)) { | |||||
return isProjectOverview(metric.key) | |||||
? translate('component_measures.overview.project_overview.facet') | |||||
: translateWithParameters( | |||||
'component_measures.domain_x_overview', | |||||
getLocalizedMetricDomain(metric.key) | |||||
); | |||||
renderContent = (displayOverview: boolean, query: Query, metric?: Metric) => { | |||||
const { branchLike, component, fetchMeasures, metrics } = this.props; | |||||
const { leakPeriod, measures } = this.state; | |||||
if (measures.length === 0) { | |||||
return <MeasuresEmpty />; | |||||
} | } | ||||
return metric ? metric.name : translate('layout.measures'); | |||||
if (displayOverview) { | |||||
return ( | |||||
<MeasureOverviewContainer | |||||
branchLike={branchLike} | |||||
className="layout-page-main" | |||||
currentUser={this.props.currentUser} | |||||
domain={query.metric} | |||||
leakPeriod={leakPeriod} | |||||
metrics={metrics} | |||||
rootComponent={component} | |||||
router={this.props.router} | |||||
selected={query.selected} | |||||
updateQuery={this.updateQuery} | |||||
/> | |||||
); | |||||
} | |||||
if (!metric) { | |||||
return <MeasuresEmpty />; | |||||
} | |||||
return ( | |||||
<MeasureContentContainer | |||||
branchLike={branchLike} | |||||
className="layout-page-main" | |||||
currentUser={this.props.currentUser} | |||||
fetchMeasures={fetchMeasures} | |||||
leakPeriod={leakPeriod} | |||||
metric={metric} | |||||
metrics={metrics} | |||||
rootComponent={component} | |||||
router={this.props.router} | |||||
selected={query.selected} | |||||
updateQuery={this.updateQuery} | |||||
view={query.view} | |||||
/> | |||||
); | |||||
}; | }; | ||||
render() { | render() { | ||||
if (isLoading) { | if (isLoading) { | ||||
return <i className="spinner spinner-margin" />; | return <i className="spinner spinner-margin" />; | ||||
} | } | ||||
const { branchLike, component, fetchMeasures, metrics } = this.props; | |||||
const { leakPeriod } = this.state; | |||||
const { branchLike } = this.props; | |||||
const { measures } = this.state; | |||||
const query = parseQuery(this.props.location.query); | const query = parseQuery(this.props.location.query); | ||||
const metric = metrics[query.metric]; | |||||
const hasOverview = hasFullMeasures(branchLike); | |||||
const displayOverview = hasOverview && hasBubbleChart(query.metric); | |||||
const metric = this.getSelectedMetric(query, displayOverview); | |||||
return ( | return ( | ||||
<div className="layout-page" id="component-measures"> | <div className="layout-page" id="component-measures"> | ||||
<Suggestions suggestions="component_measures" /> | <Suggestions suggestions="component_measures" /> | ||||
<Helmet title={this.getHelmetTitle(metric)} /> | |||||
<Helmet title={this.getHelmetTitle(query, displayOverview, metric)} /> | |||||
<ScreenPositionHelper className="layout-page-side-outer"> | <ScreenPositionHelper className="layout-page-side-outer"> | ||||
{({ top }) => ( | {({ top }) => ( | ||||
<div className="layout-page-side-inner"> | <div className="layout-page-side-inner"> | ||||
<div className="layout-page-filters"> | <div className="layout-page-filters"> | ||||
<Sidebar | <Sidebar | ||||
measures={this.state.measures} | |||||
selectedMetric={query.metric} | |||||
hasOverview={hasOverview} | |||||
measures={measures} | |||||
selectedMetric={metric ? metric.key : query.metric} | |||||
updateQuery={this.updateQuery} | updateQuery={this.updateQuery} | ||||
/> | /> | ||||
</div> | </div> | ||||
)} | )} | ||||
</ScreenPositionHelper> | </ScreenPositionHelper> | ||||
{metric && ( | |||||
<MeasureContentContainer | |||||
branchLike={branchLike} | |||||
className="layout-page-main" | |||||
currentUser={this.props.currentUser} | |||||
fetchMeasures={fetchMeasures} | |||||
leakPeriod={leakPeriod} | |||||
metric={metric} | |||||
metrics={metrics} | |||||
rootComponent={component} | |||||
router={this.props.router} | |||||
selected={query.selected} | |||||
updateQuery={this.updateQuery} | |||||
view={query.view} | |||||
/> | |||||
)} | |||||
{!metric && | |||||
hasBubbleChart(query.metric) && ( | |||||
<MeasureOverviewContainer | |||||
branchLike={branchLike} | |||||
className="layout-page-main" | |||||
currentUser={this.props.currentUser} | |||||
domain={query.metric} | |||||
leakPeriod={leakPeriod} | |||||
metrics={metrics} | |||||
rootComponent={component} | |||||
router={this.props.router} | |||||
selected={query.selected} | |||||
updateQuery={this.updateQuery} | |||||
/> | |||||
)} | |||||
{this.renderContent(displayOverview, query, metric)} | |||||
</div> | </div> | ||||
); | ); | ||||
} | } |
import { getLocalizedMetricName, translate } from '../../../helpers/l10n'; | import { getLocalizedMetricName, translate } from '../../../helpers/l10n'; | ||||
import { getMeasureHistoryUrl } from '../../../helpers/urls'; | import { getMeasureHistoryUrl } from '../../../helpers/urls'; | ||||
import { isDiffMetric } from '../../../helpers/measures'; | import { isDiffMetric } from '../../../helpers/measures'; | ||||
import { MeasureEnhanced, Metric, ComponentMeasure, BranchLike, Period } from '../../../app/types'; | |||||
import { BranchLike, ComponentMeasure, MeasureEnhanced, Metric, Period } from '../../../app/types'; | |||||
import { hasFullMeasures } from '../utils'; | |||||
interface Props { | interface Props { | ||||
branchLike?: BranchLike; | branchLike?: BranchLike; | ||||
export default function MeasureHeader(props: Props) { | export default function MeasureHeader(props: Props) { | ||||
const { branchLike, component, leakPeriod, measure, metric, secondaryMeasure } = props; | const { branchLike, component, leakPeriod, measure, metric, secondaryMeasure } = props; | ||||
const isDiff = isDiffMetric(metric.key); | const isDiff = isDiffMetric(metric.key); | ||||
const hasHistory = component.qualifier !== 'FIL' && component.qualifier !== 'UTS'; | |||||
const hasHistory = | |||||
component.qualifier !== 'FIL' && component.qualifier !== 'UTS' && hasFullMeasures(branchLike); | |||||
const displayLeak = hasFullMeasures(branchLike); | |||||
return ( | return ( | ||||
<div className="measure-details-header big-spacer-bottom"> | <div className="measure-details-header big-spacer-bottom"> | ||||
<div className="measure-details-primary"> | <div className="measure-details-primary"> | ||||
)} | )} | ||||
</div> | </div> | ||||
<div className="measure-details-primary-actions"> | <div className="measure-details-primary-actions"> | ||||
{leakPeriod && ( | |||||
<LeakPeriodLegend className="spacer-left" component={component} period={leakPeriod} /> | |||||
)} | |||||
{displayLeak && | |||||
leakPeriod && ( | |||||
<LeakPeriodLegend className="spacer-left" component={component} period={leakPeriod} /> | |||||
)} | |||||
</div> | </div> | ||||
</div> | </div> | ||||
{secondaryMeasure && | {secondaryMeasure && |
/* | |||||
* SonarQube | |||||
* Copyright (C) 2009-2018 SonarSource SA | |||||
* mailto:info AT sonarsource DOT com | |||||
* | |||||
* This program is free software; you can redistribute it and/or | |||||
* modify it under the terms of the GNU Lesser General Public | |||||
* License as published by the Free Software Foundation; either | |||||
* version 3 of the License, or (at your option) any later version. | |||||
* | |||||
* This program is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||||
* Lesser General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU Lesser General Public License | |||||
* along with this program; if not, write to the Free Software Foundation, | |||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||||
*/ | |||||
import * as React from 'react'; | |||||
import { translate } from '../../../helpers/l10n'; | |||||
export default function MeasuresEmpty() { | |||||
return ( | |||||
<div className="layout-page-main"> | |||||
<div className="note text-center">{translate('component_measures.empty')}</div> | |||||
</div> | |||||
); | |||||
} |
import * as React from 'react'; | import * as React from 'react'; | ||||
import { shallow } from 'enzyme'; | import { shallow } from 'enzyme'; | ||||
import App from '../App'; | import App from '../App'; | ||||
import { waitAndUpdate } from '../../../../helpers/testUtils'; | |||||
const COMPONENT = { key: 'foo', name: 'Foo', qualifier: 'TRK' }; | const COMPONENT = { key: 'foo', name: 'Foo', qualifier: 'TRK' }; | ||||
component: COMPONENT, | component: COMPONENT, | ||||
currentUser: { isLoggedIn: false }, | currentUser: { isLoggedIn: false }, | ||||
location: { pathname: '/component_measures', query: { metric: 'coverage' } }, | location: { pathname: '/component_measures', query: { metric: 'coverage' } }, | ||||
fetchMeasures: jest.fn().mockResolvedValue({ component: COMPONENT, measures: [] }), | |||||
fetchMeasures: jest.fn().mockResolvedValue({ | |||||
component: COMPONENT, | |||||
measures: [{ metric: 'coverage', value: '80.0' }] | |||||
}), | |||||
fetchMetrics: jest.fn(), | fetchMetrics: jest.fn(), | ||||
metrics: METRICS, | metrics: METRICS, | ||||
metricsKey: ['lines_to_cover', 'coverage', 'duplicated_lines_density', 'new_bugs'], | metricsKey: ['lines_to_cover', 'coverage', 'duplicated_lines_density', 'new_bugs'], | ||||
router: { push: jest.fn() } as any | router: { push: jest.fn() } as any | ||||
}; | }; | ||||
it('should render correctly', () => { | |||||
it('should render correctly', async () => { | |||||
const wrapper = shallow(<App {...PROPS} />); | const wrapper = shallow(<App {...PROPS} />); | ||||
expect(wrapper.find('.spinner')).toHaveLength(1); | expect(wrapper.find('.spinner')).toHaveLength(1); | ||||
wrapper.setState({ loading: false }); | |||||
await waitAndUpdate(wrapper); | |||||
expect(wrapper).toMatchSnapshot(); | expect(wrapper).toMatchSnapshot(); | ||||
}); | }); | ||||
it('should render a measure overview', () => { | |||||
it('should render a measure overview', async () => { | |||||
const wrapper = shallow( | const wrapper = shallow( | ||||
<App | <App | ||||
{...PROPS} | {...PROPS} | ||||
/> | /> | ||||
); | ); | ||||
expect(wrapper.find('.spinner')).toHaveLength(1); | expect(wrapper.find('.spinner')).toHaveLength(1); | ||||
wrapper.setState({ loading: false }); | |||||
await waitAndUpdate(wrapper); | |||||
expect(wrapper.find('MeasureOverviewContainer')).toHaveLength(1); | expect(wrapper.find('MeasureOverviewContainer')).toHaveLength(1); | ||||
}); | }); |
).toMatchSnapshot(); | ).toMatchSnapshot(); | ||||
}); | }); | ||||
it('should render with branch', () => { | |||||
const shortBranch = { isMain: false, name: 'feature', mergeBranch: '', type: 'SHORT' }; | |||||
it('should render with long living branch', () => { | |||||
const longBranch = { isMain: false, name: 'branch-6.7', type: 'LONG' }; | |||||
expect( | expect( | ||||
shallow(<MeasureHeader branchLike={shortBranch} {...PROPS} />).find('Link') | |||||
shallow(<MeasureHeader branchLike={longBranch} {...PROPS} />).find('Link') | |||||
).toMatchSnapshot(); | |||||
}); | |||||
it('should render with short living branch', () => { | |||||
const shortBranch = { isMain: false, name: 'feature', mergeBranch: 'master', type: 'SHORT' }; | |||||
expect( | |||||
shallow( | |||||
<MeasureHeader | |||||
{...PROPS} | |||||
branchLike={shortBranch} | |||||
measure={LEAK_MEASURE} | |||||
metric={LEAK_METRIC} | |||||
/> | |||||
) | |||||
).toMatchSnapshot(); | ).toMatchSnapshot(); | ||||
}); | }); | ||||
</div> | </div> | ||||
`; | `; | ||||
exports[`should render with branch 1`] = ` | |||||
exports[`should render with long living branch 1`] = ` | |||||
<Link | <Link | ||||
className="js-show-history spacer-left button button-small" | className="js-show-history spacer-left button button-small" | ||||
onlyActiveOnIndex={false} | onlyActiveOnIndex={false} | ||||
Object { | Object { | ||||
"pathname": "/project/activity", | "pathname": "/project/activity", | ||||
"query": Object { | "query": Object { | ||||
"branch": "feature", | |||||
"branch": "branch-6.7", | |||||
"custom_metrics": "reliability_rating", | "custom_metrics": "reliability_rating", | ||||
"graph": "custom", | "graph": "custom", | ||||
"id": "foo", | "id": "foo", | ||||
</Link> | </Link> | ||||
`; | `; | ||||
exports[`should render with short living branch 1`] = ` | |||||
<div | |||||
className="measure-details-header big-spacer-bottom" | |||||
> | |||||
<div | |||||
className="measure-details-primary" | |||||
> | |||||
<div | |||||
className="measure-details-metric" | |||||
> | |||||
<IssueTypeIcon | |||||
className="little-spacer-right text-text-bottom" | |||||
query="new_reliability_rating" | |||||
/> | |||||
Reliability Rating on New Code | |||||
<span | |||||
className="measure-details-value spacer-left" | |||||
> | |||||
<strong> | |||||
<Measure | |||||
className="domain-measures-leak" | |||||
metricKey="new_reliability_rating" | |||||
metricType="RATING" | |||||
value="3.0" | |||||
/> | |||||
</strong> | |||||
</span> | |||||
</div> | |||||
<div | |||||
className="measure-details-primary-actions" | |||||
/> | |||||
</div> | |||||
</div> | |||||
`; | |||||
exports[`should work with measure without value 1`] = ` | exports[`should work with measure without value 1`] = ` | ||||
<div | <div | ||||
className="measure-details-header big-spacer-bottom" | className="measure-details-header big-spacer-bottom" |
interface Props { | interface Props { | ||||
domain: { name: string; measures: MeasureEnhanced[] }; | domain: { name: string; measures: MeasureEnhanced[] }; | ||||
hasOverview: boolean; | |||||
onChange: (metric: string) => void; | onChange: (metric: string) => void; | ||||
onToggle: (property: string) => void; | onToggle: (property: string) => void; | ||||
open: boolean; | open: boolean; | ||||
} | } | ||||
export default class DomainFacet extends React.PureComponent<Props> { | export default class DomainFacet extends React.PureComponent<Props> { | ||||
getValues = () => { | |||||
const { domain, selected } = this.props; | |||||
const measureSelected = domain.measures.find(measure => measure.metric.key === selected); | |||||
const overviewSelected = domain.name === selected && this.hasOverview(domain.name); | |||||
if (measureSelected) { | |||||
return [getLocalizedMetricName(measureSelected.metric)]; | |||||
} | |||||
return overviewSelected ? [translate('component_measures.domain_overview')] : []; | |||||
}; | |||||
handleHeaderClick = () => { | handleHeaderClick = () => { | ||||
this.props.onToggle(this.props.domain.name); | this.props.onToggle(this.props.domain.name); | ||||
}; | }; | ||||
hasFacetSelected = (domain: { name: string }, measures: MeasureEnhanced[], selected: string) => { | hasFacetSelected = (domain: { name: string }, measures: MeasureEnhanced[], selected: string) => { | ||||
const measureSelected = measures.find(measure => measure.metric.key === selected); | const measureSelected = measures.find(measure => measure.metric.key === selected); | ||||
const overviewSelected = domain.name === selected && hasBubbleChart(domain.name); | |||||
const overviewSelected = domain.name === selected && this.hasOverview(domain.name); | |||||
return measureSelected || overviewSelected; | return measureSelected || overviewSelected; | ||||
}; | }; | ||||
getValues = () => { | |||||
const { domain, selected } = this.props; | |||||
const measureSelected = domain.measures.find(measure => measure.metric.key === selected); | |||||
const overviewSelected = domain.name === selected && hasBubbleChart(domain.name); | |||||
if (measureSelected) { | |||||
return [getLocalizedMetricName(measureSelected.metric)]; | |||||
} | |||||
return overviewSelected ? [translate('component_measures.domain_overview')] : []; | |||||
hasOverview = (domain: string) => { | |||||
return this.props.hasOverview && hasBubbleChart(domain); | |||||
}; | }; | ||||
renderItemFacetStat = (item: MeasureEnhanced) => { | renderItemFacetStat = (item: MeasureEnhanced) => { | ||||
renderOverviewFacet = () => { | renderOverviewFacet = () => { | ||||
const { domain, selected } = this.props; | const { domain, selected } = this.props; | ||||
if (!hasBubbleChart(domain.name)) { | |||||
if (!this.hasOverview(domain.name)) { | |||||
return null; | return null; | ||||
} | } | ||||
return ( | return ( |
import { MeasureEnhanced } from '../../../app/types'; | import { MeasureEnhanced } from '../../../app/types'; | ||||
interface Props { | interface Props { | ||||
hasOverview: boolean; | |||||
measures: MeasureEnhanced[]; | measures: MeasureEnhanced[]; | ||||
selectedMetric: string; | selectedMetric: string; | ||||
updateQuery: (query: Query) => void; | |||||
updateQuery: (query: Partial<Query>) => void; | |||||
} | } | ||||
interface State { | interface State { | ||||
this.props.updateQuery({ metric, ...this.resetSelection(metric) }); | this.props.updateQuery({ metric, ...this.resetSelection(metric) }); | ||||
render() { | render() { | ||||
const { hasOverview } = this.props; | |||||
return ( | return ( | ||||
<div> | <div> | ||||
<ProjectOverviewFacet | |||||
onChange={this.changeMetric} | |||||
selected={this.props.selectedMetric} | |||||
value={PROJECT_OVERVEW} | |||||
/> | |||||
{hasOverview && ( | |||||
<ProjectOverviewFacet | |||||
onChange={this.changeMetric} | |||||
selected={this.props.selectedMetric} | |||||
value={PROJECT_OVERVEW} | |||||
/> | |||||
)} | |||||
{groupByDomains(this.props.measures).map(domain => ( | {groupByDomains(this.props.measures).map(domain => ( | ||||
<DomainFacet | <DomainFacet | ||||
domain={domain} | domain={domain} | ||||
hasOverview={hasOverview} | |||||
key={domain.name} | key={domain.name} | ||||
onChange={this.changeMetric} | onChange={this.changeMetric} | ||||
onToggle={this.toggleFacet} | onToggle={this.toggleFacet} |
}; | }; | ||||
const PROPS = { | const PROPS = { | ||||
domain: DOMAIN, | |||||
hasOverview: true, | |||||
onChange: () => {}, | onChange: () => {}, | ||||
onToggle: () => {}, | onToggle: () => {}, | ||||
open: true, | open: true, | ||||
domain: DOMAIN, | |||||
selected: 'foo' | selected: 'foo' | ||||
}; | }; | ||||
expect(wrapper.find('FacetItemsList')).toHaveLength(0); | expect(wrapper.find('FacetItemsList')).toHaveLength(0); | ||||
}); | }); | ||||
it('should render without overview', () => { | |||||
const wrapper = shallow(<DomainFacet {...PROPS} hasOverview={false} />); | |||||
expect( | |||||
wrapper | |||||
.find('FacetItem') | |||||
.filterWhere(node => node.getElement().key === 'Reliability') | |||||
.exists() | |||||
).toBe(false); | |||||
}); | |||||
it('should not display subtitles of new measures if there is none', () => { | it('should not display subtitles of new measures if there is none', () => { | ||||
const domain = { | const domain = { | ||||
name: 'Reliability', | name: 'Reliability', | ||||
] | ] | ||||
}; | }; | ||||
expect( | |||||
shallow( | |||||
<DomainFacet | |||||
domain={domain} | |||||
onChange={() => {}} | |||||
onToggle={() => {}} | |||||
open={true} | |||||
selected={'foo'} | |||||
/> | |||||
) | |||||
).toMatchSnapshot(); | |||||
expect(shallow(<DomainFacet {...PROPS} domain={domain} />)).toMatchSnapshot(); | |||||
}); | }); | ||||
it('should not display subtitles of new measures if there is none, even on last line', () => { | it('should not display subtitles of new measures if there is none, even on last line', () => { | ||||
] | ] | ||||
}; | }; | ||||
expect( | |||||
shallow( | |||||
<DomainFacet | |||||
domain={domain} | |||||
onChange={() => {}} | |||||
onToggle={() => {}} | |||||
open={true} | |||||
selected={'foo'} | |||||
/> | |||||
) | |||||
).toMatchSnapshot(); | |||||
expect(shallow(<DomainFacet {...PROPS} domain={domain} />)).toMatchSnapshot(); | |||||
}); | }); |
]; | ]; | ||||
const PROPS = { | const PROPS = { | ||||
hasOverview: true, | |||||
measures: MEASURES, | measures: MEASURES, | ||||
selectedMetric: 'duplicated_lines_density', | selectedMetric: 'duplicated_lines_density', | ||||
updateQuery: () => {} | updateQuery: () => {} |
"name": "Coverage", | "name": "Coverage", | ||||
} | } | ||||
} | } | ||||
hasOverview={true} | |||||
key="Coverage" | key="Coverage" | ||||
onChange={[Function]} | onChange={[Function]} | ||||
onToggle={[Function]} | onToggle={[Function]} | ||||
"name": "Duplications", | "name": "Duplications", | ||||
} | } | ||||
} | } | ||||
hasOverview={true} | |||||
key="Duplications" | key="Duplications" | ||||
onChange={[Function]} | onChange={[Function]} | ||||
onToggle={[Function]} | onToggle={[Function]} |
ComponentMeasure, | ComponentMeasure, | ||||
ComponentMeasureEnhanced, | ComponentMeasureEnhanced, | ||||
Metric, | Metric, | ||||
MeasureEnhanced | |||||
MeasureEnhanced, | |||||
BranchLike | |||||
} from '../../app/types'; | } from '../../app/types'; | ||||
import { enhanceMeasure } from '../../components/measure/utils'; | import { enhanceMeasure } from '../../components/measure/utils'; | ||||
import { cleanQuery, parseAsString, RawQuery, serializeString } from '../../helpers/query'; | import { cleanQuery, parseAsString, RawQuery, serializeString } from '../../helpers/query'; | ||||
import { isLongLivingBranch, isMainBranch } from '../../helpers/branches'; | |||||
import { getDisplayMetrics } from '../../helpers/measures'; | |||||
export const PROJECT_OVERVEW = 'project_overview'; | export const PROJECT_OVERVEW = 'project_overview'; | ||||
export const DEFAULT_VIEW = 'list'; | export const DEFAULT_VIEW = 'list'; | ||||
} | } | ||||
export function hasBubbleChart(domainName: string): boolean { | export function hasBubbleChart(domainName: string): boolean { | ||||
return bubbles[domainName] != null; | |||||
return bubbles[domainName] !== undefined; | |||||
} | } | ||||
export function hasFacetStat(metric: string): boolean { | export function hasFacetStat(metric: string): boolean { | ||||
return metric !== 'alert_status'; | return metric !== 'alert_status'; | ||||
} | } | ||||
export function hasFullMeasures(branch?: BranchLike) { | |||||
return !branch || isLongLivingBranch(branch) || isMainBranch(branch); | |||||
} | |||||
export function getMeasuresPageMetricKeys(metrics: { [key: string]: Metric }, branch?: BranchLike) { | |||||
if (!hasFullMeasures(branch)) { | |||||
return [ | |||||
'new_coverage', | |||||
'new_lines_to_cover', | |||||
'new_uncovered_lines', | |||||
'new_line_coverage', | |||||
'new_conditions_to_cover', | |||||
'new_uncovered_conditions', | |||||
'new_branch_coverage' | |||||
]; | |||||
} | |||||
return getDisplayMetrics(Object.values(metrics)).map(metric => metric.key); | |||||
} | |||||
export function getBubbleMetrics(domain: string, metrics: { [key: string]: Metric }) { | export function getBubbleMetrics(domain: string, metrics: { [key: string]: Metric }) { | ||||
const conf = bubbles[domain]; | const conf = bubbles[domain]; | ||||
return { | return { | ||||
}; | }; | ||||
export interface Query { | export interface Query { | ||||
metric?: string; | |||||
metric: string; | |||||
selected?: string; | selected?: string; | ||||
view: string; | view: string; | ||||
} | } |
component_measures.legend.worse_of_x_y=Worse of {0} and {1} | component_measures.legend.worse_of_x_y=Worse of {0} and {1} | ||||
component_measures.no_history=There is no historical data. | component_measures.no_history=There is no historical data. | ||||
component_measures.not_found=The requested measure was not found. | component_measures.not_found=The requested measure was not found. | ||||
component_measures.empty=No measures. | |||||
component_measures.to_select_files=to select files | component_measures.to_select_files=to select files | ||||
component_measures.to_navigate=to navigate | component_measures.to_navigate=to navigate | ||||
component_measures.to_navigate_files=to next/previous file | component_measures.to_navigate_files=to next/previous file |