qgStatuses | qgStatuses | ||||
} = this.state; | } = this.state; | ||||
const leakPeriod = component.qualifier === ComponentQualifier.Application ? appLeak : period; | |||||
const projectIsEmpty = | const projectIsEmpty = | ||||
loadingStatus === false && | loadingStatus === false && | ||||
(measures === undefined || | (measures === undefined || | ||||
return ( | return ( | ||||
<BranchOverviewRenderer | <BranchOverviewRenderer | ||||
analyses={analyses} | analyses={analyses} | ||||
appLeak={appLeak} | |||||
branchLike={branchLike} | branchLike={branchLike} | ||||
component={component} | component={component} | ||||
graph={graph} | graph={graph} | ||||
leakPeriod={leakPeriod} | |||||
loadingHistory={loadingHistory} | loadingHistory={loadingHistory} | ||||
loadingStatus={loadingStatus} | loadingStatus={loadingStatus} | ||||
measures={measures} | measures={measures} | ||||
measuresHistory={measuresHistory} | measuresHistory={measuresHistory} | ||||
metrics={metrics} | metrics={metrics} | ||||
onGraphChange={this.handleGraphChange} | onGraphChange={this.handleGraphChange} | ||||
period={period} | |||||
projectIsEmpty={projectIsEmpty} | projectIsEmpty={projectIsEmpty} | ||||
qgStatuses={qgStatuses} | qgStatuses={qgStatuses} | ||||
/> | /> |
import A11ySkipTarget from '../../../app/components/a11y/A11ySkipTarget'; | import A11ySkipTarget from '../../../app/components/a11y/A11ySkipTarget'; | ||||
import { ApplicationPeriod } from '../../../types/application'; | import { ApplicationPeriod } from '../../../types/application'; | ||||
import { BranchLike } from '../../../types/branch-like'; | import { BranchLike } from '../../../types/branch-like'; | ||||
import { ComponentQualifier } from '../../../types/component'; | |||||
import { GraphType, MeasureHistory } from '../../../types/project-activity'; | import { GraphType, MeasureHistory } from '../../../types/project-activity'; | ||||
import { QualityGateStatus } from '../../../types/quality-gates'; | import { QualityGateStatus } from '../../../types/quality-gates'; | ||||
import ActivityPanel from './ActivityPanel'; | import ActivityPanel from './ActivityPanel'; | ||||
export interface BranchOverviewRendererProps { | export interface BranchOverviewRendererProps { | ||||
analyses?: T.Analysis[]; | analyses?: T.Analysis[]; | ||||
appLeak?: ApplicationPeriod; | |||||
branchLike?: BranchLike; | branchLike?: BranchLike; | ||||
component: T.Component; | component: T.Component; | ||||
graph?: GraphType; | graph?: GraphType; | ||||
leakPeriod?: T.Period | ApplicationPeriod; | |||||
loadingHistory?: boolean; | loadingHistory?: boolean; | ||||
loadingStatus?: boolean; | loadingStatus?: boolean; | ||||
measures?: T.MeasureEnhanced[]; | measures?: T.MeasureEnhanced[]; | ||||
measuresHistory?: MeasureHistory[]; | measuresHistory?: MeasureHistory[]; | ||||
metrics?: T.Metric[]; | metrics?: T.Metric[]; | ||||
onGraphChange: (graph: GraphType) => void; | onGraphChange: (graph: GraphType) => void; | ||||
period?: T.Period; | |||||
projectIsEmpty?: boolean; | projectIsEmpty?: boolean; | ||||
qgStatuses?: QualityGateStatus[]; | qgStatuses?: QualityGateStatus[]; | ||||
} | } | ||||
export function BranchOverviewRenderer(props: BranchOverviewRendererProps) { | export function BranchOverviewRenderer(props: BranchOverviewRendererProps) { | ||||
const { | const { | ||||
analyses, | analyses, | ||||
appLeak, | |||||
branchLike, | branchLike, | ||||
component, | component, | ||||
graph, | graph, | ||||
leakPeriod, | |||||
loadingHistory, | loadingHistory, | ||||
loadingStatus, | loadingStatus, | ||||
measures, | measures, | ||||
measuresHistory = [], | measuresHistory = [], | ||||
metrics = [], | metrics = [], | ||||
onGraphChange, | onGraphChange, | ||||
period, | |||||
projectIsEmpty, | projectIsEmpty, | ||||
qgStatuses | qgStatuses | ||||
} = props; | } = props; | ||||
const leakPeriod = component.qualifier === ComponentQualifier.Application ? appLeak : period; | |||||
return ( | return ( | ||||
<div className="page page-limited"> | <div className="page page-limited"> | ||||
<div className="overview"> | <div className="overview"> | ||||
<div className="flex-1"> | <div className="flex-1"> | ||||
<div className="display-flex-column"> | <div className="display-flex-column"> | ||||
<MeasuresPanel | <MeasuresPanel | ||||
appLeak={appLeak} | |||||
branchLike={branchLike} | branchLike={branchLike} | ||||
component={component} | component={component} | ||||
leakPeriod={leakPeriod} | |||||
loading={loadingStatus} | loading={loadingStatus} | ||||
measures={measures} | measures={measures} | ||||
period={period} | |||||
/> | /> | ||||
<ActivityPanel | <ActivityPanel |
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||||
*/ | */ | ||||
import * as React from 'react'; | import * as React from 'react'; | ||||
import { FormattedMessage } from 'react-intl'; | |||||
import { Link } from 'react-router'; | |||||
import BoxedTabs from 'sonar-ui-common/components/controls/BoxedTabs'; | import BoxedTabs from 'sonar-ui-common/components/controls/BoxedTabs'; | ||||
import DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner'; | import DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner'; | ||||
import { translate } from 'sonar-ui-common/helpers/l10n'; | import { translate } from 'sonar-ui-common/helpers/l10n'; | ||||
import { isDiffMetric } from 'sonar-ui-common/helpers/measures'; | import { isDiffMetric } from 'sonar-ui-common/helpers/measures'; | ||||
import { getBaseUrl } from 'sonar-ui-common/helpers/urls'; | |||||
import { rawSizes } from '../../../app/theme'; | import { rawSizes } from '../../../app/theme'; | ||||
import { findMeasure } from '../../../helpers/measures'; | import { findMeasure } from '../../../helpers/measures'; | ||||
import { ApplicationPeriod } from '../../../types/application'; | import { ApplicationPeriod } from '../../../types/application'; | ||||
import { BranchLike } from '../../../types/branch-like'; | import { BranchLike } from '../../../types/branch-like'; | ||||
import { ComponentQualifier } from '../../../types/component'; | import { ComponentQualifier } from '../../../types/component'; | ||||
import { MetricKey } from '../../../types/metrics'; | import { MetricKey } from '../../../types/metrics'; | ||||
import IssueLabel from '../components/IssueLabel'; | |||||
import IssueRating from '../components/IssueRating'; | |||||
import MeasurementLabel from '../components/MeasurementLabel'; | import MeasurementLabel from '../components/MeasurementLabel'; | ||||
import { IssueType, MeasurementType } from '../utils'; | import { IssueType, MeasurementType } from '../utils'; | ||||
import DebtValue from './DebtValue'; | |||||
import { DrilldownMeasureValue } from './DrilldownMeasureValue'; | import { DrilldownMeasureValue } from './DrilldownMeasureValue'; | ||||
import { LeakPeriodInfo } from './LeakPeriodInfo'; | import { LeakPeriodInfo } from './LeakPeriodInfo'; | ||||
import SecurityHotspotsReviewed from './SecurityHotspotsReviewed'; | |||||
import MeasuresPanelIssueMeasureRow from './MeasuresPanelIssueMeasureRow'; | |||||
import MeasuresPanelNoNewCode from './MeasuresPanelNoNewCode'; | |||||
export interface MeasuresPanelProps { | export interface MeasuresPanelProps { | ||||
appLeak?: ApplicationPeriod; | |||||
branchLike?: BranchLike; | branchLike?: BranchLike; | ||||
component: T.Component; | component: T.Component; | ||||
leakPeriod?: T.Period | ApplicationPeriod; | |||||
loading?: boolean; | loading?: boolean; | ||||
measures?: T.MeasureEnhanced[]; | measures?: T.MeasureEnhanced[]; | ||||
period?: T.Period; | |||||
} | } | ||||
export enum MeasuresPanelTabs { | export enum MeasuresPanelTabs { | ||||
} | } | ||||
export function MeasuresPanel(props: MeasuresPanelProps) { | export function MeasuresPanel(props: MeasuresPanelProps) { | ||||
const { branchLike, component, loading, leakPeriod, measures = [] } = props; | |||||
const { appLeak, branchLike, component, loading, measures = [], period } = props; | |||||
const hasDiffMeasures = measures.some(m => isDiffMetric(m.metric.key)); | const hasDiffMeasures = measures.some(m => isDiffMetric(m.metric.key)); | ||||
const isApp = component.qualifier === ComponentQualifier.Application; | const isApp = component.qualifier === ComponentQualifier.Application; | ||||
const leakPeriod = isApp ? appLeak : period; | |||||
const [tab, selectTab] = React.useState(MeasuresPanelTabs.New); | const [tab, selectTab] = React.useState(MeasuresPanelTabs.New); | ||||
<div className="overview-panel-content flex-1 bordered"> | <div className="overview-panel-content flex-1 bordered"> | ||||
{!hasDiffMeasures && isNewCodeTab ? ( | {!hasDiffMeasures && isNewCodeTab ? ( | ||||
<div | |||||
className="display-flex-center display-flex-justify-center" | |||||
style={{ height: 500 }}> | |||||
<img | |||||
alt="" /* Make screen readers ignore this image; it's purely eye candy. */ | |||||
className="spacer-right" | |||||
height={52} | |||||
src={`${getBaseUrl()}/images/source-code.svg`} | |||||
/> | |||||
<div className="big-spacer-left text-muted" style={{ maxWidth: 500 }}> | |||||
<p className="spacer-bottom big-spacer-top big"> | |||||
{translate('overview.measures.empty_explanation')} | |||||
</p> | |||||
<p> | |||||
<FormattedMessage | |||||
defaultMessage={translate('overview.measures.empty_link')} | |||||
id="overview.measures.empty_link" | |||||
values={{ | |||||
learn_more_link: ( | |||||
<Link to="/documentation/user-guide/clean-as-you-code/"> | |||||
{translate('learn_more')} | |||||
</Link> | |||||
) | |||||
}} | |||||
/> | |||||
</p> | |||||
</div> | |||||
</div> | |||||
<MeasuresPanelNoNewCode | |||||
branchLike={branchLike} | |||||
component={component} | |||||
period={period} | |||||
/> | |||||
) : ( | ) : ( | ||||
<> | <> | ||||
{[ | {[ | ||||
IssueType.SecurityHotspot, | IssueType.SecurityHotspot, | ||||
IssueType.CodeSmell | IssueType.CodeSmell | ||||
].map((type: IssueType) => ( | ].map((type: IssueType) => ( | ||||
<div | |||||
className="display-flex-row overview-measures-row" | |||||
data-test={`overview__measures-${type.toString().toLowerCase()}`} | |||||
key={type}> | |||||
{type === IssueType.CodeSmell ? ( | |||||
<> | |||||
<div className="overview-panel-big-padded flex-1 small display-flex-center big-spacer-left"> | |||||
<DebtValue | |||||
branchLike={branchLike} | |||||
component={component} | |||||
measures={measures} | |||||
useDiffMetric={isNewCodeTab} | |||||
/> | |||||
</div> | |||||
<div className="flex-1 small display-flex-center"> | |||||
<IssueLabel | |||||
branchLike={branchLike} | |||||
component={component} | |||||
measures={measures} | |||||
type={type} | |||||
useDiffMetric={isNewCodeTab} | |||||
/> | |||||
</div> | |||||
</> | |||||
) : ( | |||||
<div className="overview-panel-big-padded flex-1 small display-flex-center big-spacer-left"> | |||||
<IssueLabel | |||||
branchLike={branchLike} | |||||
component={component} | |||||
docTooltip={ | |||||
type === IssueType.SecurityHotspot | |||||
? import( | |||||
/* webpackMode: "eager" */ 'Docs/tooltips/metrics/security-hotspots.md' | |||||
) | |||||
: undefined | |||||
} | |||||
measures={measures} | |||||
type={type} | |||||
useDiffMetric={isNewCodeTab} | |||||
/> | |||||
</div> | |||||
)} | |||||
{type === IssueType.SecurityHotspot && ( | |||||
<div className="flex-1 small display-flex-center"> | |||||
<SecurityHotspotsReviewed | |||||
measures={measures} | |||||
useDiffMetric={isNewCodeTab} | |||||
/> | |||||
</div> | |||||
)} | |||||
{(!isApp || tab === MeasuresPanelTabs.Overall) && ( | |||||
<div className="overview-panel-big-padded overview-measures-aside display-flex-center"> | |||||
<IssueRating | |||||
branchLike={branchLike} | |||||
component={component} | |||||
measures={measures} | |||||
type={type} | |||||
useDiffMetric={isNewCodeTab} | |||||
/> | |||||
</div> | |||||
)} | |||||
</div> | |||||
<MeasuresPanelIssueMeasureRow | |||||
branchLike={branchLike} | |||||
component={component} | |||||
isNewCodeTab={isNewCodeTab} | |||||
key={type} | |||||
measures={measures} | |||||
type={type} | |||||
/> | |||||
))} | ))} | ||||
<div className="display-flex-row overview-measures-row"> | <div className="display-flex-row overview-measures-row"> |
/* | |||||
* SonarQube | |||||
* Copyright (C) 2009-2020 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 { BranchLike } from '../../../types/branch-like'; | |||||
import { ComponentQualifier } from '../../../types/component'; | |||||
import IssueLabel from '../components/IssueLabel'; | |||||
import IssueRating from '../components/IssueRating'; | |||||
import { IssueType } from '../utils'; | |||||
import DebtValue from './DebtValue'; | |||||
import SecurityHotspotsReviewed from './SecurityHotspotsReviewed'; | |||||
export interface MeasuresPanelIssueMeasureRowProps { | |||||
branchLike?: BranchLike; | |||||
component: T.Component; | |||||
isNewCodeTab: boolean; | |||||
measures: T.MeasureEnhanced[]; | |||||
type: IssueType; | |||||
} | |||||
export default function MeasuresPanelIssueMeasureRow(props: MeasuresPanelIssueMeasureRowProps) { | |||||
const { branchLike, component, isNewCodeTab, measures, type } = props; | |||||
const isApp = component.qualifier === ComponentQualifier.Application; | |||||
return ( | |||||
<div | |||||
className="display-flex-row overview-measures-row" | |||||
data-test={`overview__measures-${type.toString().toLowerCase()}`}> | |||||
{type === IssueType.CodeSmell ? ( | |||||
<> | |||||
<div className="overview-panel-big-padded flex-1 small display-flex-center big-spacer-left"> | |||||
<DebtValue | |||||
branchLike={branchLike} | |||||
component={component} | |||||
measures={measures} | |||||
useDiffMetric={isNewCodeTab} | |||||
/> | |||||
</div> | |||||
<div className="flex-1 small display-flex-center"> | |||||
<IssueLabel | |||||
branchLike={branchLike} | |||||
component={component} | |||||
measures={measures} | |||||
type={type} | |||||
useDiffMetric={isNewCodeTab} | |||||
/> | |||||
</div> | |||||
</> | |||||
) : ( | |||||
<div className="overview-panel-big-padded flex-1 small display-flex-center big-spacer-left"> | |||||
<IssueLabel | |||||
branchLike={branchLike} | |||||
component={component} | |||||
docTooltip={ | |||||
type === IssueType.SecurityHotspot | |||||
? import(/* webpackMode: "eager" */ 'Docs/tooltips/metrics/security-hotspots.md') | |||||
: undefined | |||||
} | |||||
measures={measures} | |||||
type={type} | |||||
useDiffMetric={isNewCodeTab} | |||||
/> | |||||
</div> | |||||
)} | |||||
{type === IssueType.SecurityHotspot && ( | |||||
<div className="flex-1 small display-flex-center"> | |||||
<SecurityHotspotsReviewed measures={measures} useDiffMetric={isNewCodeTab} /> | |||||
</div> | |||||
)} | |||||
{(!isApp || !isNewCodeTab) && ( | |||||
<div className="overview-panel-big-padded overview-measures-aside display-flex-center"> | |||||
<IssueRating | |||||
branchLike={branchLike} | |||||
component={component} | |||||
measures={measures} | |||||
type={type} | |||||
useDiffMetric={isNewCodeTab} | |||||
/> | |||||
</div> | |||||
)} | |||||
</div> | |||||
); | |||||
} |
/* | |||||
* SonarQube | |||||
* Copyright (C) 2009-2020 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 { FormattedMessage } from 'react-intl'; | |||||
import { Link } from 'react-router'; | |||||
import { translate } from 'sonar-ui-common/helpers/l10n'; | |||||
import { getBaseUrl } from 'sonar-ui-common/helpers/urls'; | |||||
import { getBranchLikeQuery } from '../../../helpers/branch-like'; | |||||
import { BranchLike } from '../../../types/branch-like'; | |||||
import { ComponentQualifier } from '../../../types/component'; | |||||
export interface MeasuresPanelNoNewCodeProps { | |||||
branchLike?: BranchLike; | |||||
component: T.Component; | |||||
period?: T.Period; | |||||
} | |||||
export default function MeasuresPanelNoNewCode(props: MeasuresPanelNoNewCodeProps) { | |||||
const { branchLike, component, period } = props; | |||||
const isApp = component.qualifier === ComponentQualifier.Application; | |||||
/* | |||||
* If the period is "reference branch"-based, and if there's no date, it means | |||||
* that we're not lacking a second analysis, but that we'll never have new code because the | |||||
* selected reference branch is itself, or has disappeared for some reason. | |||||
* Makes no sense for Apps (project aggregate) | |||||
*/ | |||||
const hasBadNewCodeSetting = | |||||
!isApp && !!period && !period.date && period.mode === 'REFERENCE_BRANCH'; | |||||
const showSettingsLink = !!(component.configuration && component.configuration.showSettings); | |||||
return ( | |||||
<div className="display-flex-center display-flex-justify-center" style={{ height: 500 }}> | |||||
<img | |||||
alt="" /* Make screen readers ignore this image; it's purely eye candy. */ | |||||
className="spacer-right" | |||||
height={52} | |||||
src={`${getBaseUrl()}/images/source-code.svg`} | |||||
/> | |||||
<div className="big-spacer-left text-muted" style={{ maxWidth: 500 }}> | |||||
<p className="spacer-bottom big-spacer-top big"> | |||||
{hasBadNewCodeSetting | |||||
? translate('overview.measures.bad_setting.explanation') | |||||
: translate('overview.measures.empty_explanation')} | |||||
</p> | |||||
{hasBadNewCodeSetting ? ( | |||||
showSettingsLink && ( | |||||
<p> | |||||
<FormattedMessage | |||||
defaultMessage={translate('overview.measures.bad_setting.link')} | |||||
id="overview.measures.bad_setting.link" | |||||
values={{ | |||||
setting_link: ( | |||||
<Link | |||||
to={{ | |||||
pathname: '/project/baseline', | |||||
query: { id: component.key, ...getBranchLikeQuery(branchLike) } | |||||
}}> | |||||
{translate('settings.new_code_period.category')} | |||||
</Link> | |||||
) | |||||
}} | |||||
/> | |||||
</p> | |||||
) | |||||
) : ( | |||||
<p> | |||||
<FormattedMessage | |||||
defaultMessage={translate('overview.measures.empty_link')} | |||||
id="overview.measures.empty_link" | |||||
values={{ | |||||
learn_more_link: ( | |||||
<Link to="/documentation/user-guide/clean-as-you-code/"> | |||||
{translate('learn_more')} | |||||
</Link> | |||||
) | |||||
}} | |||||
/> | |||||
</p> | |||||
)} | |||||
</div> | |||||
</div> | |||||
); | |||||
} |
import { shallow } from 'enzyme'; | import { shallow } from 'enzyme'; | ||||
import * as React from 'react'; | import * as React from 'react'; | ||||
import BoxedTabs from 'sonar-ui-common/components/controls/BoxedTabs'; | import BoxedTabs from 'sonar-ui-common/components/controls/BoxedTabs'; | ||||
import { mockMainBranch } from '../../../../helpers/mocks/branch-like'; | |||||
import { mockComponent, mockMeasureEnhanced, mockMetric } from '../../../../helpers/testMocks'; | |||||
import { mockBranch, mockMainBranch } from '../../../../helpers/mocks/branch-like'; | |||||
import { | |||||
mockComponent, | |||||
mockMeasureEnhanced, | |||||
mockMetric, | |||||
mockPeriod | |||||
} from '../../../../helpers/testMocks'; | |||||
import { ComponentQualifier } from '../../../../types/component'; | import { ComponentQualifier } from '../../../../types/component'; | ||||
import { MetricKey } from '../../../../types/metrics'; | import { MetricKey } from '../../../../types/metrics'; | ||||
import { MeasuresPanel, MeasuresPanelProps, MeasuresPanelTabs } from '../MeasuresPanel'; | import { MeasuresPanel, MeasuresPanelProps, MeasuresPanelTabs } from '../MeasuresPanel'; | ||||
expect(wrapper).toMatchSnapshot(); | expect(wrapper).toMatchSnapshot(); | ||||
}); | }); | ||||
it('should render correctly if branch is misconfigured', () => { | |||||
const wrapper = shallowRender({ | |||||
branchLike: mockBranch({ name: 'own-reference' }), | |||||
measures: [ | |||||
mockMeasureEnhanced({ metric: mockMetric({ key: MetricKey.coverage }) }), | |||||
mockMeasureEnhanced({ metric: mockMetric({ key: MetricKey.bugs }) }) | |||||
], | |||||
period: mockPeriod({ date: undefined, mode: 'REFERENCE_BRANCH', parameter: 'own-reference' }) | |||||
}); | |||||
wrapper.find(BoxedTabs).prop<Function>('onSelect')(MeasuresPanelTabs.New); | |||||
expect(wrapper).toMatchSnapshot('hide settings'); | |||||
wrapper.setProps({ component: mockComponent({ configuration: { showSettings: true } }) }); | |||||
expect(wrapper).toMatchSnapshot('show settings'); | |||||
}); | |||||
it('should render correctly if there is no coverage', () => { | it('should render correctly if there is no coverage', () => { | ||||
expect( | expect( | ||||
shallowRender({ | shallowRender({ | ||||
}); | }); | ||||
function shallowRender(props: Partial<MeasuresPanelProps> = {}) { | function shallowRender(props: Partial<MeasuresPanelProps> = {}) { | ||||
return shallow( | |||||
return shallow<MeasuresPanelProps>( | |||||
<MeasuresPanel | <MeasuresPanel | ||||
branchLike={mockMainBranch()} | branchLike={mockMainBranch()} | ||||
component={mockComponent()} | component={mockComponent()} |
/* | |||||
* SonarQube | |||||
* Copyright (C) 2009-2020 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 { shallow } from 'enzyme'; | |||||
import * as React from 'react'; | |||||
import { mockMainBranch } from '../../../../helpers/mocks/branch-like'; | |||||
import { mockComponent, mockMeasureEnhanced, mockMetric } from '../../../../helpers/testMocks'; | |||||
import { ComponentQualifier } from '../../../../types/component'; | |||||
import { MetricKey } from '../../../../types/metrics'; | |||||
import { IssueType } from '../../utils'; | |||||
import MeasuresPanelIssueMeasureRow, { | |||||
MeasuresPanelIssueMeasureRowProps | |||||
} from '../MeasuresPanelIssueMeasureRow'; | |||||
it('should render correctly for projects', () => { | |||||
expect(shallowRender({ type: IssueType.Bug })).toMatchSnapshot('Bug'); | |||||
expect(shallowRender({ type: IssueType.CodeSmell })).toMatchSnapshot('Code Smell'); | |||||
expect(shallowRender({ type: IssueType.SecurityHotspot })).toMatchSnapshot('Hotspot'); | |||||
expect(shallowRender({ type: IssueType.Vulnerability })).toMatchSnapshot('Vulnerabilty'); | |||||
expect(shallowRender({ isNewCodeTab: false })).toMatchSnapshot('Overview'); | |||||
}); | |||||
it('should render correctly for apps', () => { | |||||
const app = mockComponent({ qualifier: ComponentQualifier.Application }); | |||||
expect(shallowRender({ component: app })).toMatchSnapshot('new code'); | |||||
expect(shallowRender({ component: app, isNewCodeTab: false })).toMatchSnapshot('overview'); | |||||
}); | |||||
function shallowRender(props: Partial<MeasuresPanelIssueMeasureRowProps> = {}) { | |||||
return shallow<MeasuresPanelIssueMeasureRowProps>( | |||||
<MeasuresPanelIssueMeasureRow | |||||
branchLike={mockMainBranch()} | |||||
component={mockComponent()} | |||||
isNewCodeTab={true} | |||||
measures={[ | |||||
mockMeasureEnhanced({ metric: mockMetric({ key: MetricKey.coverage }) }), | |||||
mockMeasureEnhanced({ metric: mockMetric({ key: MetricKey.new_coverage }) }), | |||||
mockMeasureEnhanced({ metric: mockMetric({ key: MetricKey.bugs }) }), | |||||
mockMeasureEnhanced({ metric: mockMetric({ key: MetricKey.new_bugs }) }) | |||||
]} | |||||
type={IssueType.Bug} | |||||
{...props} | |||||
/> | |||||
); | |||||
} |
/* | |||||
* SonarQube | |||||
* Copyright (C) 2009-2020 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 { shallow } from 'enzyme'; | |||||
import * as React from 'react'; | |||||
import { mockMainBranch } from '../../../../helpers/mocks/branch-like'; | |||||
import { mockComponent, mockPeriod } from '../../../../helpers/testMocks'; | |||||
import { ComponentQualifier } from '../../../../types/component'; | |||||
import MeasuresPanelNoNewCode, { MeasuresPanelNoNewCodeProps } from '../MeasuresPanelNoNewCode'; | |||||
it('should render the default message', () => { | |||||
const defaultMessage = ` | |||||
<div | |||||
className="display-flex-center display-flex-justify-center" | |||||
style={ | |||||
Object { | |||||
"height": 500, | |||||
} | |||||
} | |||||
> | |||||
<img | |||||
alt="" | |||||
className="spacer-right" | |||||
height={52} | |||||
src="/images/source-code.svg" | |||||
/> | |||||
<div | |||||
className="big-spacer-left text-muted" | |||||
style={ | |||||
Object { | |||||
"maxWidth": 500, | |||||
} | |||||
} | |||||
> | |||||
<p | |||||
className="spacer-bottom big-spacer-top big" | |||||
> | |||||
overview.measures.empty_explanation | |||||
</p> | |||||
<p> | |||||
<FormattedMessage | |||||
defaultMessage="overview.measures.empty_link" | |||||
id="overview.measures.empty_link" | |||||
values={ | |||||
Object { | |||||
"learn_more_link": <Link | |||||
onlyActiveOnIndex={false} | |||||
style={Object {}} | |||||
to="/documentation/user-guide/clean-as-you-code/" | |||||
> | |||||
learn_more | |||||
</Link>, | |||||
} | |||||
} | |||||
/> | |||||
</p> | |||||
</div> | |||||
</div> | |||||
`; | |||||
expect(shallowRender()).toMatchInlineSnapshot(defaultMessage); | |||||
expect( | |||||
shallowRender({ component: mockComponent({ qualifier: ComponentQualifier.Application }) }) | |||||
).toMatchInlineSnapshot(defaultMessage); | |||||
expect( | |||||
shallowRender({ period: mockPeriod({ date: '2018-05-23', mode: 'REFERENCE_BRANCH' }) }) | |||||
).toMatchInlineSnapshot(defaultMessage); | |||||
expect( | |||||
shallowRender({ period: mockPeriod({ date: '2018-05-23', mode: 'PREVIOUS_VERSION' }) }) | |||||
).toMatchInlineSnapshot(defaultMessage); | |||||
}); | |||||
it('should render "bad code setting" explanation', () => { | |||||
const period = mockPeriod({ date: undefined, mode: 'REFERENCE_BRANCH' }); | |||||
expect(shallowRender({ period })).toMatchSnapshot('no link'); | |||||
expect( | |||||
shallowRender({ component: mockComponent({ configuration: { showSettings: true } }), period }) | |||||
).toMatchSnapshot('with link'); | |||||
}); | |||||
function shallowRender(props: Partial<MeasuresPanelNoNewCodeProps> = {}) { | |||||
return shallow<MeasuresPanelNoNewCodeProps>( | |||||
<MeasuresPanelNoNewCode branchLike={mockMainBranch()} component={mockComponent()} {...props} /> | |||||
); | |||||
} |
}, | }, | ||||
] | ] | ||||
} | } | ||||
appLeak={ | |||||
Object { | |||||
"date": "2017-01-05", | |||||
"project": "foo", | |||||
"projectName": "Foo", | |||||
} | |||||
} | |||||
branchLike={ | branchLike={ | ||||
Object { | Object { | ||||
"analysisDate": "2018-01-01", | "analysisDate": "2018-01-01", | ||||
} | } | ||||
} | } | ||||
graph="coverage" | graph="coverage" | ||||
leakPeriod={ | |||||
Object { | |||||
"date": "2017-01-05", | |||||
"project": "foo", | |||||
"projectName": "Foo", | |||||
} | |||||
} | |||||
loadingHistory={false} | loadingHistory={false} | ||||
loadingStatus={false} | loadingStatus={false} | ||||
measures={ | measures={ | ||||
}, | }, | ||||
] | ] | ||||
} | } | ||||
appLeak={ | |||||
Object { | |||||
"date": "2017-01-05", | |||||
"project": "foo", | |||||
"projectName": "Foo", | |||||
} | |||||
} | |||||
branchLike={ | branchLike={ | ||||
Object { | Object { | ||||
"analysisDate": "2018-01-01", | "analysisDate": "2018-01-01", | ||||
} | } | ||||
} | } | ||||
graph="coverage" | graph="coverage" | ||||
leakPeriod={ | |||||
Object { | |||||
"date": "2017-01-05", | |||||
"project": "foo", | |||||
"projectName": "Foo", | |||||
} | |||||
} | |||||
loadingHistory={false} | loadingHistory={false} | ||||
loadingStatus={false} | loadingStatus={false} | ||||
measures={ | measures={ |
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||||
exports[`should render "bad code setting" explanation: no link 1`] = ` | |||||
<div | |||||
className="display-flex-center display-flex-justify-center" | |||||
style={ | |||||
Object { | |||||
"height": 500, | |||||
} | |||||
} | |||||
> | |||||
<img | |||||
alt="" | |||||
className="spacer-right" | |||||
height={52} | |||||
src="/images/source-code.svg" | |||||
/> | |||||
<div | |||||
className="big-spacer-left text-muted" | |||||
style={ | |||||
Object { | |||||
"maxWidth": 500, | |||||
} | |||||
} | |||||
> | |||||
<p | |||||
className="spacer-bottom big-spacer-top big" | |||||
> | |||||
overview.measures.bad_setting.explanation | |||||
</p> | |||||
</div> | |||||
</div> | |||||
`; | |||||
exports[`should render "bad code setting" explanation: with link 1`] = ` | |||||
<div | |||||
className="display-flex-center display-flex-justify-center" | |||||
style={ | |||||
Object { | |||||
"height": 500, | |||||
} | |||||
} | |||||
> | |||||
<img | |||||
alt="" | |||||
className="spacer-right" | |||||
height={52} | |||||
src="/images/source-code.svg" | |||||
/> | |||||
<div | |||||
className="big-spacer-left text-muted" | |||||
style={ | |||||
Object { | |||||
"maxWidth": 500, | |||||
} | |||||
} | |||||
> | |||||
<p | |||||
className="spacer-bottom big-spacer-top big" | |||||
> | |||||
overview.measures.bad_setting.explanation | |||||
</p> | |||||
<p> | |||||
<FormattedMessage | |||||
defaultMessage="overview.measures.bad_setting.link" | |||||
id="overview.measures.bad_setting.link" | |||||
values={ | |||||
Object { | |||||
"setting_link": <Link | |||||
onlyActiveOnIndex={false} | |||||
style={Object {}} | |||||
to={ | |||||
Object { | |||||
"pathname": "/project/baseline", | |||||
"query": Object { | |||||
"id": "my-project", | |||||
}, | |||||
} | |||||
} | |||||
> | |||||
settings.new_code_period.category | |||||
</Link>, | |||||
} | |||||
} | |||||
/> | |||||
</p> | |||||
</div> | |||||
</div> | |||||
`; |
overview.measures=Measures | overview.measures=Measures | ||||
overview.measures.empty_explanation=Measures on New Code will appear after the second analysis of this branch. | overview.measures.empty_explanation=Measures on New Code will appear after the second analysis of this branch. | ||||
overview.measures.empty_link={learn_more_link} about the Clean as You Code approach. | overview.measures.empty_link={learn_more_link} about the Clean as You Code approach. | ||||
overview.measures.bad_setting.explanation=This branch is configured to use itself as reference branch. It will never have New Code. | |||||
overview.measures.bad_setting.link=This can be fixed in the {setting_link} setting. | |||||
overview.measures.security_hotspots_reviewed=Reviewed | overview.measures.security_hotspots_reviewed=Reviewed | ||||
overview.project.no_lines_of_code=This project has no lines of code. | overview.project.no_lines_of_code=This project has no lines of code. | ||||
# New periods (MMF-1579) | # New periods (MMF-1579) | ||||
overview.period.number_of_days=From last {0} days | overview.period.number_of_days=From last {0} days | ||||
overview.period.specific_analysis=Since {0} | overview.period.specific_analysis=Since {0} | ||||
overview.period.reference_branch=Compared to {0} | |||||
overview.gate.ERROR=Failed | overview.gate.ERROR=Failed | ||||
overview.gate.WARN=Warning | overview.gate.WARN=Warning |