@@ -403,8 +403,6 @@ export default class BranchOverview extends React.PureComponent<Props, State> { | |||
qgStatuses | |||
} = this.state; | |||
const leakPeriod = component.qualifier === ComponentQualifier.Application ? appLeak : period; | |||
const projectIsEmpty = | |||
loadingStatus === false && | |||
(measures === undefined || | |||
@@ -415,16 +413,17 @@ export default class BranchOverview extends React.PureComponent<Props, State> { | |||
return ( | |||
<BranchOverviewRenderer | |||
analyses={analyses} | |||
appLeak={appLeak} | |||
branchLike={branchLike} | |||
component={component} | |||
graph={graph} | |||
leakPeriod={leakPeriod} | |||
loadingHistory={loadingHistory} | |||
loadingStatus={loadingStatus} | |||
measures={measures} | |||
measuresHistory={measuresHistory} | |||
metrics={metrics} | |||
onGraphChange={this.handleGraphChange} | |||
period={period} | |||
projectIsEmpty={projectIsEmpty} | |||
qgStatuses={qgStatuses} | |||
/> |
@@ -22,6 +22,7 @@ import { parseDate } from 'sonar-ui-common/helpers/dates'; | |||
import A11ySkipTarget from '../../../app/components/a11y/A11ySkipTarget'; | |||
import { ApplicationPeriod } from '../../../types/application'; | |||
import { BranchLike } from '../../../types/branch-like'; | |||
import { ComponentQualifier } from '../../../types/component'; | |||
import { GraphType, MeasureHistory } from '../../../types/project-activity'; | |||
import { QualityGateStatus } from '../../../types/quality-gates'; | |||
import ActivityPanel from './ActivityPanel'; | |||
@@ -31,16 +32,17 @@ import QualityGatePanel from './QualityGatePanel'; | |||
export interface BranchOverviewRendererProps { | |||
analyses?: T.Analysis[]; | |||
appLeak?: ApplicationPeriod; | |||
branchLike?: BranchLike; | |||
component: T.Component; | |||
graph?: GraphType; | |||
leakPeriod?: T.Period | ApplicationPeriod; | |||
loadingHistory?: boolean; | |||
loadingStatus?: boolean; | |||
measures?: T.MeasureEnhanced[]; | |||
measuresHistory?: MeasureHistory[]; | |||
metrics?: T.Metric[]; | |||
onGraphChange: (graph: GraphType) => void; | |||
period?: T.Period; | |||
projectIsEmpty?: boolean; | |||
qgStatuses?: QualityGateStatus[]; | |||
} | |||
@@ -48,20 +50,23 @@ export interface BranchOverviewRendererProps { | |||
export function BranchOverviewRenderer(props: BranchOverviewRendererProps) { | |||
const { | |||
analyses, | |||
appLeak, | |||
branchLike, | |||
component, | |||
graph, | |||
leakPeriod, | |||
loadingHistory, | |||
loadingStatus, | |||
measures, | |||
measuresHistory = [], | |||
metrics = [], | |||
onGraphChange, | |||
period, | |||
projectIsEmpty, | |||
qgStatuses | |||
} = props; | |||
const leakPeriod = component.qualifier === ComponentQualifier.Application ? appLeak : period; | |||
return ( | |||
<div className="page page-limited"> | |||
<div className="overview"> | |||
@@ -82,11 +87,12 @@ export function BranchOverviewRenderer(props: BranchOverviewRendererProps) { | |||
<div className="flex-1"> | |||
<div className="display-flex-column"> | |||
<MeasuresPanel | |||
appLeak={appLeak} | |||
branchLike={branchLike} | |||
component={component} | |||
leakPeriod={leakPeriod} | |||
loading={loadingStatus} | |||
measures={measures} | |||
period={period} | |||
/> | |||
<ActivityPanel |
@@ -18,34 +18,30 @@ | |||
* 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 BoxedTabs from 'sonar-ui-common/components/controls/BoxedTabs'; | |||
import DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner'; | |||
import { translate } from 'sonar-ui-common/helpers/l10n'; | |||
import { isDiffMetric } from 'sonar-ui-common/helpers/measures'; | |||
import { getBaseUrl } from 'sonar-ui-common/helpers/urls'; | |||
import { rawSizes } from '../../../app/theme'; | |||
import { findMeasure } from '../../../helpers/measures'; | |||
import { ApplicationPeriod } from '../../../types/application'; | |||
import { BranchLike } from '../../../types/branch-like'; | |||
import { ComponentQualifier } from '../../../types/component'; | |||
import { MetricKey } from '../../../types/metrics'; | |||
import IssueLabel from '../components/IssueLabel'; | |||
import IssueRating from '../components/IssueRating'; | |||
import MeasurementLabel from '../components/MeasurementLabel'; | |||
import { IssueType, MeasurementType } from '../utils'; | |||
import DebtValue from './DebtValue'; | |||
import { DrilldownMeasureValue } from './DrilldownMeasureValue'; | |||
import { LeakPeriodInfo } from './LeakPeriodInfo'; | |||
import SecurityHotspotsReviewed from './SecurityHotspotsReviewed'; | |||
import MeasuresPanelIssueMeasureRow from './MeasuresPanelIssueMeasureRow'; | |||
import MeasuresPanelNoNewCode from './MeasuresPanelNoNewCode'; | |||
export interface MeasuresPanelProps { | |||
appLeak?: ApplicationPeriod; | |||
branchLike?: BranchLike; | |||
component: T.Component; | |||
leakPeriod?: T.Period | ApplicationPeriod; | |||
loading?: boolean; | |||
measures?: T.MeasureEnhanced[]; | |||
period?: T.Period; | |||
} | |||
export enum MeasuresPanelTabs { | |||
@@ -54,10 +50,11 @@ export enum MeasuresPanelTabs { | |||
} | |||
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 isApp = component.qualifier === ComponentQualifier.Application; | |||
const leakPeriod = isApp ? appLeak : period; | |||
const [tab, selectTab] = React.useState(MeasuresPanelTabs.New); | |||
@@ -109,34 +106,11 @@ export function MeasuresPanel(props: MeasuresPanelProps) { | |||
<div className="overview-panel-content flex-1 bordered"> | |||
{!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} | |||
/> | |||
) : ( | |||
<> | |||
{[ | |||
@@ -145,68 +119,14 @@ export function MeasuresPanel(props: MeasuresPanelProps) { | |||
IssueType.SecurityHotspot, | |||
IssueType.CodeSmell | |||
].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"> |
@@ -0,0 +1,100 @@ | |||
/* | |||
* 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> | |||
); | |||
} |
@@ -0,0 +1,102 @@ | |||
/* | |||
* 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> | |||
); | |||
} |
@@ -20,8 +20,13 @@ | |||
import { shallow } from 'enzyme'; | |||
import * as React from 'react'; | |||
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 { MetricKey } from '../../../../types/metrics'; | |||
import { MeasuresPanel, MeasuresPanelProps, MeasuresPanelTabs } from '../MeasuresPanel'; | |||
@@ -53,6 +58,22 @@ it('should render correctly if there is no new code measures', () => { | |||
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', () => { | |||
expect( | |||
shallowRender({ | |||
@@ -69,7 +90,7 @@ it('should render correctly if the data is still loading', () => { | |||
}); | |||
function shallowRender(props: Partial<MeasuresPanelProps> = {}) { | |||
return shallow( | |||
return shallow<MeasuresPanelProps>( | |||
<MeasuresPanel | |||
branchLike={mockMainBranch()} | |||
component={mockComponent()} |
@@ -0,0 +1,62 @@ | |||
/* | |||
* 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} | |||
/> | |||
); | |||
} |
@@ -0,0 +1,101 @@ | |||
/* | |||
* 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} /> | |||
); | |||
} |
@@ -36,6 +36,13 @@ exports[`application overview should fetch correctly other branch 1`] = ` | |||
}, | |||
] | |||
} | |||
appLeak={ | |||
Object { | |||
"date": "2017-01-05", | |||
"project": "foo", | |||
"projectName": "Foo", | |||
} | |||
} | |||
branchLike={ | |||
Object { | |||
"analysisDate": "2018-01-01", | |||
@@ -90,13 +97,6 @@ exports[`application overview should fetch correctly other branch 1`] = ` | |||
} | |||
} | |||
graph="coverage" | |||
leakPeriod={ | |||
Object { | |||
"date": "2017-01-05", | |||
"project": "foo", | |||
"projectName": "Foo", | |||
} | |||
} | |||
loadingHistory={false} | |||
loadingStatus={false} | |||
measures={ | |||
@@ -990,6 +990,13 @@ exports[`application overview should render correctly 1`] = ` | |||
}, | |||
] | |||
} | |||
appLeak={ | |||
Object { | |||
"date": "2017-01-05", | |||
"project": "foo", | |||
"projectName": "Foo", | |||
} | |||
} | |||
branchLike={ | |||
Object { | |||
"analysisDate": "2018-01-01", | |||
@@ -1044,13 +1051,6 @@ exports[`application overview should render correctly 1`] = ` | |||
} | |||
} | |||
graph="coverage" | |||
leakPeriod={ | |||
Object { | |||
"date": "2017-01-05", | |||
"project": "foo", | |||
"projectName": "Foo", | |||
} | |||
} | |||
loadingHistory={false} | |||
loadingStatus={false} | |||
measures={ |
@@ -0,0 +1,89 @@ | |||
// 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> | |||
`; |
@@ -2755,6 +2755,8 @@ overview.recent_activity=Recent Activity | |||
overview.measures=Measures | |||
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.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.project.no_lines_of_code=This project has no lines of code. | |||
@@ -2785,6 +2787,7 @@ overview.period.manual_baseline=Since {0} | |||
# New periods (MMF-1579) | |||
overview.period.number_of_days=From last {0} days | |||
overview.period.specific_analysis=Since {0} | |||
overview.period.reference_branch=Compared to {0} | |||
overview.gate.ERROR=Failed | |||
overview.gate.WARN=Warning |