浏览代码

SONAR-10486 Display badges for applications

tags/7.5
Pascal Mugnier 6 年前
父节点
当前提交
875989e8d4

+ 7
- 4
server/sonar-web/src/main/js/apps/overview/badges/BadgesModal.tsx 查看文件

metrics: { [key: string]: Metric }; metrics: { [key: string]: Metric };
onSonarCloud: boolean; onSonarCloud: boolean;
project: string; project: string;
qualifier: string;
} }


interface State { interface State {
}; };


render() { render() {
const { branchLike, project } = this.props;
const { branchLike, project, qualifier } = this.props;
const { selectedType, badgeOptions } = this.state; const { selectedType, badgeOptions } = this.state;
const header = translate('overview.badges.title'); const header = translate('overview.badges.title');
const fullBadgeOptions = { project, ...badgeOptions, ...getBranchLikeQuery(branchLike) }; const fullBadgeOptions = { project, ...badgeOptions, ...getBranchLikeQuery(branchLike) };
return ( return (
<div className="overview-meta-card"> <div className="overview-meta-card">
<Button className="js-project-badges" onClick={this.handleOpen}> <Button className="js-project-badges" onClick={this.handleOpen}>
{translate('overview.badges.get_badge')}
{translate('overview.badges.get_badge', qualifier)}
</Button> </Button>
{this.state.open && ( {this.state.open && (
<Modal contentLabel={header} onRequestClose={this.handleClose}> <Modal contentLabel={header} onRequestClose={this.handleClose}>
<h2>{header}</h2> <h2>{header}</h2>
</header> </header>
<div className="modal-body"> <div className="modal-body">
<p className="huge-spacer-bottom">{translate('overview.badges.description')}</p>
<p className="huge-spacer-bottom">
{translate('overview.badges.description', qualifier)}
</p>
<div className="badges-list spacer-bottom"> <div className="badges-list spacer-bottom">
{badges.map(type => ( {badges.map(type => (
<BadgeButton <BadgeButton
))} ))}
</div> </div>
<p className="text-center note huge-spacer-bottom"> <p className="text-center note huge-spacer-bottom">
{translate('overview.badges', selectedType, 'description')}
{translate('overview.badges', selectedType, 'description', qualifier)}
</p> </p>
<BadgeParams <BadgeParams
className="big-spacer-bottom" className="big-spacer-bottom"

+ 14
- 2
server/sonar-web/src/main/js/apps/overview/badges/__tests__/BadgesModal-test.tsx 查看文件



it('should display the modal after click on sonar cloud', () => { it('should display the modal after click on sonar cloud', () => {
const wrapper = shallow( const wrapper = shallow(
<BadgesModal branchLike={shortBranch} metrics={{}} onSonarCloud={true} project="foo" />
<BadgesModal
branchLike={shortBranch}
metrics={{}}
onSonarCloud={true}
project="foo"
qualifier="TRK"
/>
); );
expect(wrapper).toMatchSnapshot(); expect(wrapper).toMatchSnapshot();
click(wrapper.find('Button')); click(wrapper.find('Button'));


it('should display the modal after click on sonar qube', () => { it('should display the modal after click on sonar qube', () => {
const wrapper = shallow( const wrapper = shallow(
<BadgesModal branchLike={shortBranch} metrics={{}} onSonarCloud={false} project="foo" />
<BadgesModal
branchLike={shortBranch}
metrics={{}}
onSonarCloud={false}
project="foo"
qualifier="TRK"
/>
); );
expect(wrapper).toMatchSnapshot(); expect(wrapper).toMatchSnapshot();
click(wrapper.find('Button')); click(wrapper.find('Button'));

+ 6
- 6
server/sonar-web/src/main/js/apps/overview/badges/__tests__/__snapshots__/BadgesModal-test.tsx.snap 查看文件

className="js-project-badges" className="js-project-badges"
onClick={[Function]} onClick={[Function]}
> >
overview.badges.get_badge
overview.badges.get_badge.TRK
</Button> </Button>
</div> </div>
`; `;
<p <p
className="huge-spacer-bottom" className="huge-spacer-bottom"
> >
overview.badges.description
overview.badges.description.TRK
</p> </p>
<div <div
className="badges-list spacer-bottom" className="badges-list spacer-bottom"
<p <p
className="text-center note huge-spacer-bottom" className="text-center note huge-spacer-bottom"
> >
overview.badges.measure.description
overview.badges.measure.description.TRK
</p> </p>
<BadgeParams <BadgeParams
className="big-spacer-bottom" className="big-spacer-bottom"
className="js-project-badges" className="js-project-badges"
onClick={[Function]} onClick={[Function]}
> >
overview.badges.get_badge
overview.badges.get_badge.TRK
</Button> </Button>
</div> </div>
`; `;
<p <p
className="huge-spacer-bottom" className="huge-spacer-bottom"
> >
overview.badges.description
overview.badges.description.TRK
</p> </p>
<div <div
className="badges-list spacer-bottom" className="badges-list spacer-bottom"
<p <p
className="text-center note huge-spacer-bottom" className="text-center note huge-spacer-bottom"
> >
overview.badges.measure.description
overview.badges.measure.description.TRK
</p> </p>
<BadgeParams <BadgeParams
className="big-spacer-bottom" className="big-spacer-bottom"

+ 9
- 9
server/sonar-web/src/main/js/apps/overview/meta/Meta.tsx 查看文件

{organizationsEnabled && <MetaOrganizationKey organization={component.organization} />} {organizationsEnabled && <MetaOrganizationKey organization={component.organization} />}
</div> </div>


{isProject &&
!isPrivate && (
<BadgesModal
branchLike={branchLike}
metrics={metrics}
onSonarCloud={onSonarCloud}
project={component.key}
/>
)}
{!isPrivate && (
<BadgesModal
branchLike={branchLike}
metrics={metrics}
onSonarCloud={onSonarCloud}
project={component.key}
qualifier={component.qualifier}
/>
)}
</div> </div>
); );
} }

+ 17
- 6
server/sonar-web/src/main/js/apps/portfolio/components/App.tsx 查看文件

import { translate } from '../../../helpers/l10n'; import { translate } from '../../../helpers/l10n';
import { fetchMetrics } from '../../../store/rootActions'; import { fetchMetrics } from '../../../store/rootActions';
import { getMetrics } from '../../../store/rootReducer'; import { getMetrics } from '../../../store/rootReducer';
import { Metric } from '../../../app/types';
import { Metric, Component } from '../../../app/types';
import '../styles.css'; import '../styles.css';


interface OwnProps { interface OwnProps {
component: { key: string; name: string };
component: Component;
} }


interface StateToProps { interface StateToProps {
<div className="page-main">{this.renderMain()}</div> <div className="page-main">{this.renderMain()}</div>


<aside className="page-sidebar-fixed"> <aside className="page-sidebar-fixed">
{!this.isEmpty() &&
!this.isNotComputed() && <Summary component={component} measures={measures!} />}
<Activity component={component.key} metrics={this.props.metrics} />
<Report component={component} />
<div className="portfolio-meta-card">
<h4 className="portfolio-meta-header">
{translate('overview.about_this_portfolio')}
</h4>
{!this.isEmpty() &&
!this.isNotComputed() && <Summary component={component} measures={measures!} />}
</div>

<div className="portfolio-meta-card">
<Activity component={component.key} metrics={this.props.metrics} />
</div>

<div className="portfolio-meta-card">
<Report component={component} />
</div>
</aside> </aside>
</div> </div>
</div> </div>

+ 1
- 1
server/sonar-web/src/main/js/apps/portfolio/components/Summary.tsx 查看文件

const nclocDistribution = measures['ncloc_language_distribution']; const nclocDistribution = measures['ncloc_language_distribution'];


return ( return (
<section id="portfolio-summary" className="big-spacer-bottom">
<section className="big-spacer-bottom" id="portfolio-summary">
{component.description && <div className="big-spacer-bottom">{component.description}</div>} {component.description && <div className="big-spacer-bottom">{component.description}</div>}


<ul className="portfolio-grid"> <ul className="portfolio-grid">

+ 2
- 1
server/sonar-web/src/main/js/apps/portfolio/components/__tests__/App-test.tsx 查看文件

import * as React from 'react'; import * as React from 'react';
import { shallow, mount } from 'enzyme'; import { shallow, mount } from 'enzyme';
import { App } from '../App'; import { App } from '../App';
import { Component } from '../../../../app/types';


const getMeasures = require('../../../../api/measures').getMeasures as jest.Mock<any>; const getMeasures = require('../../../../api/measures').getMeasures as jest.Mock<any>;
const getChildren = require('../../../../api/components').getChildren as jest.Mock<any>; const getChildren = require('../../../../api/components').getChildren as jest.Mock<any>;


const component = { key: 'foo', name: 'Foo' };
const component = { key: 'foo', name: 'Foo', qualifier: 'TRK' } as Component;


it('renders', () => { it('renders', () => {
const wrapper = shallow(<App component={component} fetchMetrics={jest.fn()} metrics={{}} />); const wrapper = shallow(<App component={component} fetchMetrics={jest.fn()} metrics={{}} />);

+ 100
- 45
server/sonar-web/src/main/js/apps/portfolio/components/__tests__/__snapshots__/App-test.tsx.snap 查看文件

<aside <aside
className="page-sidebar-fixed" className="page-sidebar-fixed"
> >
<Summary
component={
Object {
"key": "foo",
"name": "Foo",
<div
className="portfolio-meta-card"
>
<h4
className="portfolio-meta-header"
>
overview.about_this_portfolio
</h4>
<Summary
component={
Object {
"key": "foo",
"name": "Foo",
"qualifier": "TRK",
}
} }
}
measures={
Object {
"ncloc": "173",
"reliability_rating": "1",
measures={
Object {
"ncloc": "173",
"reliability_rating": "1",
}
} }
}
/>
<Activity
component="foo"
metrics={Object {}}
/>
<Report
component={
Object {
"key": "foo",
"name": "Foo",
/>
</div>
<div
className="portfolio-meta-card"
>
<Activity
component="foo"
metrics={Object {}}
/>
</div>
<div
className="portfolio-meta-card"
>
<Report
component={
Object {
"key": "foo",
"name": "Foo",
"qualifier": "TRK",
}
} }
}
/>
/>
</div>
</aside> </aside>
</div> </div>
</div> </div>
<aside <aside
className="page-sidebar-fixed" className="page-sidebar-fixed"
> >
<Activity
component="foo"
metrics={Object {}}
/>
<Report
component={
Object {
"key": "foo",
"name": "Foo",
<div
className="portfolio-meta-card"
>
<h4
className="portfolio-meta-header"
>
overview.about_this_portfolio
</h4>
</div>
<div
className="portfolio-meta-card"
>
<Activity
component="foo"
metrics={Object {}}
/>
</div>
<div
className="portfolio-meta-card"
>
<Report
component={
Object {
"key": "foo",
"name": "Foo",
"qualifier": "TRK",
}
} }
}
/>
/>
</div>
</aside> </aside>
</div> </div>
</div> </div>
<aside <aside
className="page-sidebar-fixed" className="page-sidebar-fixed"
> >
<Activity
component="foo"
metrics={Object {}}
/>
<Report
component={
Object {
"key": "foo",
"name": "Foo",
<div
className="portfolio-meta-card"
>
<h4
className="portfolio-meta-header"
>
overview.about_this_portfolio
</h4>
</div>
<div
className="portfolio-meta-card"
>
<Activity
component="foo"
metrics={Object {}}
/>
</div>
<div
className="portfolio-meta-card"
>
<Report
component={
Object {
"key": "foo",
"name": "Foo",
"qualifier": "TRK",
}
} }
}
/>
/>
</div>
</aside> </aside>
</div> </div>
</div> </div>

+ 16
- 0
server/sonar-web/src/main/js/apps/portfolio/styles.css 查看文件

.portfolio-sub-components-cell { .portfolio-sub-components-cell {
width: 90px; width: 90px;
} }

.portfolio-meta-header {
margin-bottom: calc(0.5 * var(--gridSize));
color: var(--baseFontColor);
}

.portfolio-meta-card {
min-width: 200px;
box-sizing: border-box;
}

.portfolio-meta-card + .portfolio-meta-card {
margin-top: calc(2 * var(--gridSize));
padding-top: calc(2 * var(--gridSize) - 1px);
border-top: 1px solid var(--barBorderColor);
}

+ 6
- 2
server/sonar-web/src/main/js/apps/projectQualityGate/__tests__/App-test.tsx 查看文件

import * as React from 'react'; import * as React from 'react';
import { mount } from 'enzyme'; import { mount } from 'enzyme';
import App from '../App'; import App from '../App';
import { Component } from '../../../app/types';


const associateGateWithProject = require('../../../api/quality-gates') const associateGateWithProject = require('../../../api/quality-gates')
.associateGateWithProject as jest.Mock<any>; .associateGateWithProject as jest.Mock<any>;
organization: 'org', organization: 'org',
qualifier: 'TRK', qualifier: 'TRK',
version: '0.0.1' version: '0.0.1'
};
} as Component;


beforeEach(() => { beforeEach(() => {
associateGateWithProject.mockClear(); associateGateWithProject.mockClear();
it('checks permissions', () => { it('checks permissions', () => {
handleRequiredAuthorization.mockClear(); handleRequiredAuthorization.mockClear();
mount( mount(
<App component={{ ...component, configuration: undefined }} onComponentChange={jest.fn()} />
<App
component={{ ...component, configuration: undefined } as Component}
onComponentChange={jest.fn()}
/>
); );
expect(handleRequiredAuthorization).toBeCalled(); expect(handleRequiredAuthorization).toBeCalled();
}); });

+ 11
- 3
sonar-core/src/main/resources/org/sonar/l10n/core.properties 查看文件

overview.started_on_x=Started on {0} overview.started_on_x=Started on {0}
overview.last_analysis_on_x=Last analysis on {0} overview.last_analysis_on_x=Last analysis on {0}
overview.on_new_code=On New Code overview.on_new_code=On New Code
overview.about_this_portfolio=About This Portfolio
overview.about_this_project.APP=About This Application overview.about_this_project.APP=About This Application
overview.about_this_project.TRK=About This Project overview.about_this_project.TRK=About This Project
overview.about_this_project.BRC=About This Sub-Project overview.about_this_project.BRC=About This Sub-Project


overview.deprecated_profile=This quality profile uses {0} deprecated rules and should be updated. overview.deprecated_profile=This quality profile uses {0} deprecated rules and should be updated.


overview.badges.get_badge=Get project badges

overview.badges.get_badge.TRK=Get project badges
overview.badges.get_badge.VW=Get portfolio badges
overview.badges.get_badge.APP=Get application badges
overview.badges.title=Badges overview.badges.title=Badges
overview.badges.description=Show the status of your project metrics on your README or website. Pick your style:
overview.badges.description.TRK=Show the status of your project metrics on your README or website. Pick your style:
overview.badges.description.VW=Show the status of your portfolio metrics on your README or website. Pick your style:
overview.badges.description.APP=Show the status of your application metrics on your README or website. Pick your style:
overview.badges.metric=Metric overview.badges.metric=Metric
overview.badges.options.colors.white=White overview.badges.options.colors.white=White
overview.badges.options.colors.black=Black overview.badges.options.colors.black=Black
overview.badges.options.colors.orange=Orange overview.badges.options.colors.orange=Orange
overview.badges.measure.alt=Standard badge overview.badges.measure.alt=Standard badge
overview.badges.measure.description=This badge dynamically displays the current status of one metric of your project.
overview.badges.measure.description.TRK=This badge dynamically displays the current status of one metric of your project.
overview.badges.measure.description.VW=This badge dynamically displays the current status of one metric of your portfolio.
overview.badges.measure.description.APP=This badge dynamically displays the current status of one metric of your application.
overview.badges.marketing.alt=Scanned on SonarCloud badge overview.badges.marketing.alt=Scanned on SonarCloud badge
overview.badges.marketing.description=This badge lets you advertise that you're using SonarCloud for code quality. overview.badges.marketing.description=This badge lets you advertise that you're using SonarCloud for code quality.
overview.badges.quality_gate.alt=SonarCloud Quality Gate badge overview.badges.quality_gate.alt=SonarCloud Quality Gate badge

正在加载...
取消
保存