aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web/src/main/js
diff options
context:
space:
mode:
authorGrégoire Aubert <gregoire.aubert@sonarsource.com>2018-08-22 15:19:20 +0200
committersonartech <sonartech@sonarsource.com>2018-09-19 10:51:41 +0200
commitbc4d2e6948669842ff325c789f570a4f46f0af41 (patch)
tree29cc763e5604e2d577800dba0860c5750fedb9b5 /server/sonar-web/src/main/js
parent1b6dcc529ae3ef54db0cfe7ac80c926d20799d56 (diff)
downloadsonarqube-bc4d2e6948669842ff325c789f570a4f46f0af41.tar.gz
sonarqube-bc4d2e6948669842ff325c789f570a4f46f0af41.zip
SONAR-11159 SONAR-11163 Update Code page for pull requests and short living branches with Coverage
Diffstat (limited to 'server/sonar-web/src/main/js')
-rw-r--r--server/sonar-web/src/main/js/apps/code/__tests__/buckets-test.tsx (renamed from server/sonar-web/src/main/js/apps/code/components/__tests__/buckets-test.tsx)15
-rw-r--r--server/sonar-web/src/main/js/apps/code/bucket.ts14
-rw-r--r--server/sonar-web/src/main/js/apps/code/components/App.tsx136
-rw-r--r--server/sonar-web/src/main/js/apps/code/components/Breadcrumbs.tsx7
-rw-r--r--server/sonar-web/src/main/js/apps/code/components/Component.tsx49
-rw-r--r--server/sonar-web/src/main/js/apps/code/components/ComponentLink.tsx5
-rw-r--r--server/sonar-web/src/main/js/apps/code/components/ComponentMeasure.tsx20
-rw-r--r--server/sonar-web/src/main/js/apps/code/components/ComponentName.tsx11
-rw-r--r--server/sonar-web/src/main/js/apps/code/components/ComponentPin.tsx5
-rw-r--r--server/sonar-web/src/main/js/apps/code/components/Components.tsx19
-rw-r--r--server/sonar-web/src/main/js/apps/code/components/ComponentsHeader.tsx67
-rw-r--r--server/sonar-web/src/main/js/apps/code/components/Search.tsx27
-rw-r--r--server/sonar-web/src/main/js/apps/code/components/__tests__/App-test.tsx10
-rw-r--r--server/sonar-web/src/main/js/apps/code/components/__tests__/ComponentMeasure-test.tsx56
-rw-r--r--server/sonar-web/src/main/js/apps/code/components/__tests__/Components-test.tsx60
-rw-r--r--server/sonar-web/src/main/js/apps/code/components/__tests__/ComponentsHeader-test.tsx (renamed from server/sonar-web/src/main/js/apps/code/types.ts)39
-rw-r--r--server/sonar-web/src/main/js/apps/code/components/__tests__/__snapshots__/ComponentMeasure-test.tsx.snap19
-rw-r--r--server/sonar-web/src/main/js/apps/code/components/__tests__/__snapshots__/Components-test.tsx.snap160
-rw-r--r--server/sonar-web/src/main/js/apps/code/components/__tests__/__snapshots__/ComponentsHeader-test.tsx.snap94
-rw-r--r--server/sonar-web/src/main/js/apps/code/utils.ts84
20 files changed, 641 insertions, 256 deletions
diff --git a/server/sonar-web/src/main/js/apps/code/components/__tests__/buckets-test.tsx b/server/sonar-web/src/main/js/apps/code/__tests__/buckets-test.tsx
index 9ccf00c799d..5eeb0c80896 100644
--- a/server/sonar-web/src/main/js/apps/code/components/__tests__/buckets-test.tsx
+++ b/server/sonar-web/src/main/js/apps/code/__tests__/buckets-test.tsx
@@ -17,22 +17,17 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { Component } from '../../types';
-import {
- addComponent,
- getComponent,
- addComponentChildren,
- getComponentChildren
-} from '../../bucket';
+import { addComponent, getComponent, addComponentChildren, getComponentChildren } from '../bucket';
+import { ComponentMeasure } from '../../../app/types';
-const component: Component = { key: 'frodo', name: 'frodo', qualifier: 'frodo' };
+const component: ComponentMeasure = { key: 'frodo', name: 'frodo', qualifier: 'frodo' };
const componentKey: string = 'foo';
-const childrenA: Component[] = [
+const childrenA: ComponentMeasure[] = [
{ key: 'foo', name: 'foo', qualifier: 'foo' },
{ key: 'bar', name: 'bar', qualifier: 'bar' }
];
-const childrenB: Component[] = [
+const childrenB: ComponentMeasure[] = [
{ key: 'bart', name: 'bart', qualifier: 'bart' },
{ key: 'simpson', name: 'simpson', qualifier: 'simpson' }
];
diff --git a/server/sonar-web/src/main/js/apps/code/bucket.ts b/server/sonar-web/src/main/js/apps/code/bucket.ts
index dd172ca8eef..3e1600fbde7 100644
--- a/server/sonar-web/src/main/js/apps/code/bucket.ts
+++ b/server/sonar-web/src/main/js/apps/code/bucket.ts
@@ -17,29 +17,29 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { Breadcrumb, Component } from './types';
+import { ComponentMeasure, Breadcrumb } from '../../app/types';
-let bucket: { [key: string]: Component } = {};
+let bucket: { [key: string]: ComponentMeasure } = {};
let childrenBucket: {
[key: string]: {
- children: Component[];
+ children: ComponentMeasure[];
page: number;
total: number;
};
} = {};
let breadcrumbsBucket: { [key: string]: Breadcrumb[] } = {};
-export function addComponent(component: Component): void {
+export function addComponent(component: ComponentMeasure): void {
bucket[component.key] = component;
}
-export function getComponent(componentKey: string): Component {
+export function getComponent(componentKey: string): ComponentMeasure {
return bucket[componentKey];
}
export function addComponentChildren(
componentKey: string,
- children: Component[],
+ children: ComponentMeasure[],
total: number,
page: number
): void {
@@ -53,7 +53,7 @@ export function addComponentChildren(
export function getComponentChildren(
componentKey: string
): {
- children: Component[];
+ children: ComponentMeasure[];
page: number;
total: number;
} {
diff --git a/server/sonar-web/src/main/js/apps/code/components/App.tsx b/server/sonar-web/src/main/js/apps/code/components/App.tsx
index f7a7354b0e6..e6579501d88 100644
--- a/server/sonar-web/src/main/js/apps/code/components/App.tsx
+++ b/server/sonar-web/src/main/js/apps/code/components/App.tsx
@@ -17,43 +17,53 @@
* 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 classNames from 'classnames';
import * as React from 'react';
+import * as classNames from 'classnames';
+import { connect } from 'react-redux';
import Helmet from 'react-helmet';
import Components from './Components';
import Breadcrumbs from './Breadcrumbs';
import Search from './Search';
import { addComponent, addComponentBreadcrumbs, clearBucket } from '../bucket';
-import { Component as CodeComponent } from '../types';
import { retrieveComponentChildren, retrieveComponent, loadMoreChildren } from '../utils';
-import { Component, BranchLike } from '../../../app/types';
+import { Breadcrumb, Component, ComponentMeasure, BranchLike, Metric } from '../../../app/types';
import ListFooter from '../../../components/controls/ListFooter';
import SourceViewer from '../../../components/SourceViewer/SourceViewer';
import Suggestions from '../../../app/components/embed-docs-modal/Suggestions';
+import { fetchMetrics } from '../../../store/rootActions';
+import { getMetrics } from '../../../store/rootReducer';
import { isSameBranchLike } from '../../../helpers/branches';
import { translate } from '../../../helpers/l10n';
-import { parseError } from '../../../helpers/request';
import '../code.css';
-interface Props {
+interface StateToProps {
+ metrics: { [metric: string]: Metric };
+}
+
+interface DispatchToProps {
+ fetchMetrics: () => void;
+}
+
+interface OwnProps {
branchLike?: BranchLike;
component: Component;
location: { query: { [x: string]: string } };
}
+type Props = StateToProps & DispatchToProps & OwnProps;
+
interface State {
- baseComponent?: CodeComponent;
- breadcrumbs: Array<CodeComponent>;
- components?: Array<CodeComponent>;
- error?: string;
+ baseComponent?: ComponentMeasure;
+ breadcrumbs: Breadcrumb[];
+ components?: ComponentMeasure[];
loading: boolean;
page: number;
- searchResults?: Array<CodeComponent>;
- sourceViewer?: CodeComponent;
+ searchResults?: ComponentMeasure[];
+ sourceViewer?: ComponentMeasure;
total: number;
}
-export default class App extends React.PureComponent<Props, State> {
+export class App extends React.PureComponent<Props, State> {
mounted = false;
state: State = {
loading: true,
@@ -64,6 +74,7 @@ export default class App extends React.PureComponent<Props, State> {
componentDidMount() {
this.mounted = true;
+ this.props.fetchMetrics();
this.handleComponentChange();
}
@@ -90,28 +101,19 @@ export default class App extends React.PureComponent<Props, State> {
addComponentBreadcrumbs(component.key, component.breadcrumbs);
this.setState({ loading: true });
- const isPortfolio = ['VW', 'SVW'].includes(component.qualifier);
- retrieveComponentChildren(component.key, isPortfolio, branchLike)
- .then(() => {
- addComponent(component);
- if (this.mounted) {
- this.handleUpdate();
- }
- })
- .catch(e => {
- if (this.mounted) {
- this.setState({ loading: false });
- parseError(e).then(this.handleError);
- }
- });
+ retrieveComponentChildren(component.key, component.qualifier, branchLike).then(() => {
+ addComponent(component);
+ if (this.mounted) {
+ this.handleUpdate();
+ }
+ }, this.stopLoading);
}
loadComponent(componentKey: string) {
this.setState({ loading: true });
- const isPortfolio = ['VW', 'SVW'].includes(this.props.component.qualifier);
- retrieveComponent(componentKey, isPortfolio, this.props.branchLike)
- .then(r => {
+ retrieveComponent(componentKey, this.props.component.qualifier, this.props.branchLike).then(
+ r => {
if (this.mounted) {
if (['FIL', 'UTS'].includes(r.component.qualifier)) {
this.setState({
@@ -133,13 +135,9 @@ export default class App extends React.PureComponent<Props, State> {
});
}
}
- })
- .catch(e => {
- if (this.mounted) {
- this.setState({ loading: false });
- parseError(e).then(this.handleError);
- }
- });
+ },
+ this.stopLoading
+ );
}
handleUpdate() {
@@ -155,42 +153,31 @@ export default class App extends React.PureComponent<Props, State> {
if (!baseComponent || !components) {
return;
}
- const isPortfolio = ['VW', 'SVW'].includes(this.props.component.qualifier);
- loadMoreChildren(baseComponent.key, page + 1, isPortfolio, this.props.branchLike)
- .then(r => {
- if (this.mounted) {
- this.setState({
- components: [...components, ...r.components],
- page: r.page,
- total: r.total
- });
- }
- })
- .catch(e => {
- if (this.mounted) {
- this.setState({ loading: false });
- parseError(e).then(this.handleError);
- }
- });
+ loadMoreChildren(
+ baseComponent.key,
+ page + 1,
+ this.props.component.qualifier,
+ this.props.branchLike
+ ).then(r => {
+ if (this.mounted) {
+ this.setState({
+ components: [...components, ...r.components],
+ page: r.page,
+ total: r.total
+ });
+ }
+ }, this.stopLoading);
};
- handleError = (error: string) => {
+ stopLoading = () => {
if (this.mounted) {
- this.setState({ error });
+ this.setState({ loading: false });
}
};
render() {
const { branchLike, component, location } = this.props;
- const {
- loading,
- error,
- baseComponent,
- components,
- breadcrumbs,
- total,
- sourceViewer
- } = this.state;
+ const { loading, baseComponent, components, breadcrumbs, total, sourceViewer } = this.state;
const shouldShowBreadcrumbs = breadcrumbs.length > 1;
const componentsClassName = classNames('boxed-group', 'boxed-group-inner', 'spacer-top', {
@@ -207,14 +194,7 @@ export default class App extends React.PureComponent<Props, State> {
<Suggestions suggestions="code" />
<Helmet title={sourceViewer !== undefined ? sourceViewer.name : defaultTitle} />
- {error && <div className="alert alert-danger">{error}</div>}
-
- <Search
- branchLike={branchLike}
- component={component}
- location={location}
- onError={this.handleError}
- />
+ <Search branchLike={branchLike} component={component} location={location} />
<div className="code-components">
{shouldShowBreadcrumbs && (
@@ -232,6 +212,7 @@ export default class App extends React.PureComponent<Props, State> {
baseComponent={baseComponent}
branchLike={branchLike}
components={components}
+ metrics={this.props.metrics}
rootComponent={component}
/>
</div>
@@ -252,3 +233,14 @@ export default class App extends React.PureComponent<Props, State> {
);
}
}
+
+const mapStateToProps = (state: any): StateToProps => ({
+ metrics: getMetrics(state)
+});
+
+const mapDispatchToProps: DispatchToProps = { fetchMetrics };
+
+export default connect<StateToProps, DispatchToProps, Props>(
+ mapStateToProps,
+ mapDispatchToProps
+)(App);
diff --git a/server/sonar-web/src/main/js/apps/code/components/Breadcrumbs.tsx b/server/sonar-web/src/main/js/apps/code/components/Breadcrumbs.tsx
index 7ea212cd7ee..c1fca8d200b 100644
--- a/server/sonar-web/src/main/js/apps/code/components/Breadcrumbs.tsx
+++ b/server/sonar-web/src/main/js/apps/code/components/Breadcrumbs.tsx
@@ -19,13 +19,12 @@
*/
import * as React from 'react';
import ComponentName from './ComponentName';
-import { Component } from '../types';
-import { BranchLike } from '../../../app/types';
+import { BranchLike, Breadcrumb, ComponentMeasure } from '../../../app/types';
interface Props {
branchLike?: BranchLike;
- breadcrumbs: Component[];
- rootComponent: Component;
+ breadcrumbs: Breadcrumb[];
+ rootComponent: ComponentMeasure;
}
export default function Breadcrumbs({ branchLike, breadcrumbs, rootComponent }: Props) {
diff --git a/server/sonar-web/src/main/js/apps/code/components/Component.tsx b/server/sonar-web/src/main/js/apps/code/components/Component.tsx
index c12248a826a..e62b377faa7 100644
--- a/server/sonar-web/src/main/js/apps/code/components/Component.tsx
+++ b/server/sonar-web/src/main/js/apps/code/components/Component.tsx
@@ -23,9 +23,7 @@ import ComponentName from './ComponentName';
import ComponentMeasure from './ComponentMeasure';
import ComponentLink from './ComponentLink';
import ComponentPin from './ComponentPin';
-import { Component as IComponent } from '../types';
-import { BranchLike } from '../../../app/types';
-import { isShortLivingBranch, isPullRequest } from '../../../helpers/branches';
+import { BranchLike, Metric, ComponentMeasure as IComponentMeasure } from '../../../app/types';
const TOP_OFFSET = 200;
const BOTTOM_OFFSET = 10;
@@ -33,9 +31,10 @@ const BOTTOM_OFFSET = 10;
interface Props {
branchLike?: BranchLike;
canBrowse?: boolean;
- component: IComponent;
- previous?: IComponent;
- rootComponent: IComponent;
+ component: IComponentMeasure;
+ metrics: Metric[];
+ previous?: IComponentMeasure;
+ rootComponent: IComponentMeasure;
selected?: boolean;
}
@@ -76,15 +75,13 @@ export default class Component extends React.PureComponent<Props> {
render() {
const {
branchLike,
+ canBrowse = false,
component,
- rootComponent,
- selected = false,
+ metrics,
previous,
- canBrowse = false
+ rootComponent,
+ selected = false
} = this.props;
- const isPortfolio = ['VW', 'SVW'].includes(rootComponent.qualifier);
- const isApplication = rootComponent.qualifier === 'APP';
- const hideCoverageAndDuplicates = isShortLivingBranch(branchLike) || isPullRequest(branchLike);
let componentAction = null;
@@ -99,24 +96,6 @@ export default class Component extends React.PureComponent<Props> {
}
}
- const columns = isPortfolio
- ? [
- { metric: 'releasability_rating', type: 'RATING' },
- { metric: 'reliability_rating', type: 'RATING' },
- { metric: 'security_rating', type: 'RATING' },
- { metric: 'sqale_rating', type: 'RATING' },
- { metric: 'ncloc', type: 'SHORT_INT' }
- ]
- : ([
- isApplication && { metric: 'alert_status', type: 'LEVEL' },
- { metric: 'ncloc', type: 'SHORT_INT' },
- { metric: 'bugs', type: 'SHORT_INT' },
- { metric: 'vulnerabilities', type: 'SHORT_INT' },
- { metric: 'code_smells', type: 'SHORT_INT' },
- !hideCoverageAndDuplicates && { metric: 'coverage', type: 'PERCENT' },
- !hideCoverageAndDuplicates && { metric: 'duplicated_lines_density', type: 'PERCENT' }
- ].filter(Boolean) as Array<{ metric: string; type: string }>);
-
return (
<tr className={classNames({ selected })} ref={node => (this.node = node)}>
<td className="thin nowrap">
@@ -132,14 +111,10 @@ export default class Component extends React.PureComponent<Props> {
/>
</td>
- {columns.map(column => (
- <td className="thin nowrap text-right" key={column.metric}>
+ {metrics.map(metric => (
+ <td className="thin nowrap text-right" key={metric.key}>
<div className="code-components-cell">
- <ComponentMeasure
- component={component}
- metricKey={column.metric}
- metricType={column.type}
- />
+ <ComponentMeasure component={component} metric={metric} />
</div>
</td>
))}
diff --git a/server/sonar-web/src/main/js/apps/code/components/ComponentLink.tsx b/server/sonar-web/src/main/js/apps/code/components/ComponentLink.tsx
index 60927947472..eb6e931c945 100644
--- a/server/sonar-web/src/main/js/apps/code/components/ComponentLink.tsx
+++ b/server/sonar-web/src/main/js/apps/code/components/ComponentLink.tsx
@@ -19,15 +19,14 @@
*/
import * as React from 'react';
import { Link } from 'react-router';
-import { Component } from '../types';
-import { BranchLike } from '../../../app/types';
+import { BranchLike, ComponentMeasure } from '../../../app/types';
import LinkIcon from '../../../components/icons-components/LinkIcon';
import { translate } from '../../../helpers/l10n';
import { getBranchLikeUrl } from '../../../helpers/urls';
interface Props {
branchLike?: BranchLike;
- component: Component;
+ component: ComponentMeasure;
}
export default function ComponentLink({ component, branchLike }: Props) {
diff --git a/server/sonar-web/src/main/js/apps/code/components/ComponentMeasure.tsx b/server/sonar-web/src/main/js/apps/code/components/ComponentMeasure.tsx
index bb3cbe506a2..e33ff7e5d68 100644
--- a/server/sonar-web/src/main/js/apps/code/components/ComponentMeasure.tsx
+++ b/server/sonar-web/src/main/js/apps/code/components/ComponentMeasure.tsx
@@ -19,20 +19,21 @@
*/
import * as React from 'react';
import Measure from '../../../components/measure/Measure';
-import { Component } from '../types';
+import { Metric, ComponentMeasure as IComponentMeasure } from '../../../app/types';
+import { isDiffMetric } from '../../../helpers/measures';
+import { getLeakValue } from '../../../components/measure/utils';
interface Props {
- component: Component;
- metricKey: string;
- metricType: string;
+ component: IComponentMeasure;
+ metric: Metric;
}
-export default function ComponentMeasure({ component, metricKey, metricType }: Props) {
+export default function ComponentMeasure({ component, metric }: Props) {
const isProject = component.qualifier === 'TRK';
- const isReleasability = metricKey === 'releasability_rating';
+ const isReleasability = metric.key === 'releasability_rating';
- const finalMetricKey = isProject && isReleasability ? 'alert_status' : metricKey;
- const finalMetricType = isProject && isReleasability ? 'LEVEL' : metricType;
+ const finalMetricKey = isProject && isReleasability ? 'alert_status' : metric.key;
+ const finalMetricType = isProject && isReleasability ? 'LEVEL' : metric.type;
const measure =
Array.isArray(component.measures) &&
@@ -42,5 +43,6 @@ export default function ComponentMeasure({ component, metricKey, metricType }: P
return <span />;
}
- return <Measure metricKey={finalMetricKey} metricType={finalMetricType} value={measure.value} />;
+ const value = isDiffMetric(metric.key) ? getLeakValue(measure) : measure.value;
+ return <Measure metricKey={finalMetricKey} metricType={finalMetricType} value={value} />;
}
diff --git a/server/sonar-web/src/main/js/apps/code/components/ComponentName.tsx b/server/sonar-web/src/main/js/apps/code/components/ComponentName.tsx
index 391c1862c04..e303cffef93 100644
--- a/server/sonar-web/src/main/js/apps/code/components/ComponentName.tsx
+++ b/server/sonar-web/src/main/js/apps/code/components/ComponentName.tsx
@@ -20,15 +20,14 @@
import * as React from 'react';
import { Link } from 'react-router';
import Truncated from './Truncated';
-import { Component } from '../types';
import * as theme from '../../../app/theme';
-import { BranchLike } from '../../../app/types';
+import { BranchLike, ComponentMeasure } from '../../../app/types';
import QualifierIcon from '../../../components/icons-components/QualifierIcon';
import { getBranchLikeQuery } from '../../../helpers/branches';
import LongLivingBranchIcon from '../../../components/icons-components/LongLivingBranchIcon';
import { translate } from '../../../helpers/l10n';
-function getTooltip(component: Component) {
+function getTooltip(component: ComponentMeasure) {
const isFile = component.qualifier === 'FIL' || component.qualifier === 'UTS';
if (isFile && component.path) {
return component.path + '\n\n' + component.key;
@@ -55,9 +54,9 @@ function mostCommitPrefix(strings: string[]) {
interface Props {
branchLike?: BranchLike;
canBrowse?: boolean;
- component: Component;
- previous?: Component;
- rootComponent: Component;
+ component: ComponentMeasure;
+ previous?: ComponentMeasure;
+ rootComponent: ComponentMeasure;
}
export default function ComponentName(props: Props) {
diff --git a/server/sonar-web/src/main/js/apps/code/components/ComponentPin.tsx b/server/sonar-web/src/main/js/apps/code/components/ComponentPin.tsx
index 18d77d03f67..24ef4d65621 100644
--- a/server/sonar-web/src/main/js/apps/code/components/ComponentPin.tsx
+++ b/server/sonar-web/src/main/js/apps/code/components/ComponentPin.tsx
@@ -19,15 +19,14 @@
*/
import * as React from 'react';
import * as PropTypes from 'prop-types';
-import { Component } from '../types';
-import { BranchLike } from '../../../app/types';
+import { BranchLike, ComponentMeasure } from '../../../app/types';
import PinIcon from '../../../components/icons-components/PinIcon';
import { WorkspaceContext } from '../../../components/workspace/context';
import { translate } from '../../../helpers/l10n';
interface Props {
branchLike?: BranchLike;
- component: Component;
+ component: ComponentMeasure;
}
export default class ComponentPin extends React.PureComponent<Props> {
diff --git a/server/sonar-web/src/main/js/apps/code/components/Components.tsx b/server/sonar-web/src/main/js/apps/code/components/Components.tsx
index 59b017c40b6..030bdfd188b 100644
--- a/server/sonar-web/src/main/js/apps/code/components/Components.tsx
+++ b/server/sonar-web/src/main/js/apps/code/components/Components.tsx
@@ -21,24 +21,27 @@ import * as React from 'react';
import Component from './Component';
import ComponentsEmpty from './ComponentsEmpty';
import ComponentsHeader from './ComponentsHeader';
-import { Component as IComponent } from '../types';
-import { BranchLike } from '../../../app/types';
+import { BranchLike, ComponentMeasure, Metric } from '../../../app/types';
+import { getCodeMetrics } from '../utils';
interface Props {
- baseComponent?: IComponent;
+ baseComponent?: ComponentMeasure;
branchLike?: BranchLike;
- components: IComponent[];
- rootComponent: IComponent;
- selected?: IComponent;
+ components: ComponentMeasure[];
+ metrics: { [metric: string]: Metric };
+ rootComponent: ComponentMeasure;
+ selected?: ComponentMeasure;
}
export default function Components(props: Props) {
const { baseComponent, branchLike, components, rootComponent, selected } = props;
+ const metricKeys = getCodeMetrics(rootComponent.qualifier, branchLike);
+ const metrics = metricKeys.map(metric => props.metrics[metric]).filter(Boolean);
return (
<table className="data zebra">
<ComponentsHeader
baseComponent={baseComponent}
- branchLike={branchLike}
+ metrics={metricKeys}
rootComponent={rootComponent}
/>
{baseComponent && (
@@ -47,6 +50,7 @@ export default function Components(props: Props) {
branchLike={branchLike}
component={baseComponent}
key={baseComponent.key}
+ metrics={metrics}
rootComponent={rootComponent}
/>
<tr className="blank">
@@ -62,6 +66,7 @@ export default function Components(props: Props) {
canBrowse={true}
component={component}
key={component.key}
+ metrics={metrics}
previous={index > 0 ? list[index - 1] : undefined}
rootComponent={rootComponent}
selected={component === selected}
diff --git a/server/sonar-web/src/main/js/apps/code/components/ComponentsHeader.tsx b/server/sonar-web/src/main/js/apps/code/components/ComponentsHeader.tsx
index 3455f7972fc..2aeb89071a9 100644
--- a/server/sonar-web/src/main/js/apps/code/components/ComponentsHeader.tsx
+++ b/server/sonar-web/src/main/js/apps/code/components/ComponentsHeader.tsx
@@ -20,53 +20,48 @@
import * as React from 'react';
import * as classNames from 'classnames';
import { translate } from '../../../helpers/l10n';
-import { Component } from '../types';
-import { isShortLivingBranch, isPullRequest } from '../../../helpers/branches';
-import { BranchLike } from '../../../app/types';
+import { ComponentMeasure } from '../../../app/types';
interface Props {
- branchLike?: BranchLike;
- baseComponent?: Component;
- rootComponent: Component;
+ baseComponent?: ComponentMeasure;
+ metrics: string[];
+ rootComponent: ComponentMeasure;
}
-export default function ComponentsHeader({ baseComponent, branchLike, rootComponent }: Props) {
- const isPortfolio = rootComponent.qualifier === 'VW' || rootComponent.qualifier === 'SVW';
- const isApplication = rootComponent.qualifier === 'APP';
- const hideCoverageAndDuplicates = isShortLivingBranch(branchLike) || isPullRequest(branchLike);
+const SHORT_NAME_METRICS = ['duplicated_lines_density'];
- const columns = isPortfolio
- ? [
- translate('metric_domain.Releasability'),
- translate('metric_domain.Reliability'),
- translate('metric_domain.Security'),
- translate('metric_domain.Maintainability'),
- translate('metric', 'ncloc', 'name')
- ]
- : ([
- isApplication && translate('metric.alert_status.name'),
- translate('metric', 'ncloc', 'name'),
- translate('metric', 'bugs', 'name'),
- translate('metric', 'vulnerabilities', 'name'),
- translate('metric', 'code_smells', 'name'),
- !hideCoverageAndDuplicates && translate('metric', 'coverage', 'name'),
- !hideCoverageAndDuplicates && translate('metric', 'duplicated_lines_density', 'short_name')
- ].filter(Boolean) as string[]);
+export default function ComponentsHeader({ baseComponent, metrics, rootComponent }: Props) {
+ const isPortfolio = ['VW', 'SVW'].includes(rootComponent.qualifier);
+ let columns: string[] = [];
+ if (isPortfolio) {
+ columns = [
+ translate('metric_domain.Releasability'),
+ translate('metric_domain.Reliability'),
+ translate('metric_domain.Security'),
+ translate('metric_domain.Maintainability'),
+ translate('metric', 'ncloc', 'name')
+ ];
+ } else {
+ columns = metrics.map(metric =>
+ translate('metric', metric, SHORT_NAME_METRICS.includes(metric) ? 'short_name' : 'name')
+ );
+ }
return (
<thead>
<tr className="code-components-header">
<th className="thin nowrap">&nbsp;</th>
<th>&nbsp;</th>
- {columns.map((column, index) => (
- <th
- className={classNames('thin', 'nowrap', 'text-right', {
- 'code-components-cell': index > 0
- })}
- key={column}>
- {baseComponent && column}
- </th>
- ))}
+ {baseComponent &&
+ columns.map((column, index) => (
+ <th
+ className={classNames('thin', 'nowrap', 'text-right', {
+ 'code-components-cell': index > 0
+ })}
+ key={column}>
+ {column}
+ </th>
+ ))}
</tr>
</thead>
);
diff --git a/server/sonar-web/src/main/js/apps/code/components/Search.tsx b/server/sonar-web/src/main/js/apps/code/components/Search.tsx
index 7cdd3af90ac..8129b592c16 100644
--- a/server/sonar-web/src/main/js/apps/code/components/Search.tsx
+++ b/server/sonar-web/src/main/js/apps/code/components/Search.tsx
@@ -21,26 +21,23 @@ import * as React from 'react';
import * as PropTypes from 'prop-types';
import * as classNames from 'classnames';
import Components from './Components';
-import { Component } from '../types';
import { getTree } from '../../../api/components';
-import { BranchLike } from '../../../app/types';
+import { BranchLike, ComponentMeasure } from '../../../app/types';
import SearchBox from '../../../components/controls/SearchBox';
import { getBranchLikeQuery } from '../../../helpers/branches';
import { translate } from '../../../helpers/l10n';
-import { parseError } from '../../../helpers/request';
import { getProjectUrl } from '../../../helpers/urls';
interface Props {
branchLike?: BranchLike;
- component: Component;
+ component: ComponentMeasure;
location: {};
- onError: (error: string) => void;
}
interface State {
query: string;
loading: boolean;
- results?: Component[];
+ results?: ComponentMeasure[];
selectedIndex?: number;
}
@@ -78,14 +75,14 @@ export default class Search extends React.PureComponent<Props, State> {
handleSelectNext() {
const { selectedIndex, results } = this.state;
- if (results != null && selectedIndex != null && selectedIndex < results.length - 1) {
+ if (results && selectedIndex !== undefined && selectedIndex < results.length - 1) {
this.setState({ selectedIndex: selectedIndex + 1 });
}
}
handleSelectPrevious() {
const { selectedIndex, results } = this.state;
- if (results != null && selectedIndex != null && selectedIndex > 0) {
+ if (results && selectedIndex !== undefined && selectedIndex > 0) {
this.setState({ selectedIndex: selectedIndex - 1 });
}
}
@@ -93,7 +90,7 @@ export default class Search extends React.PureComponent<Props, State> {
handleSelectCurrent() {
const { branchLike, component } = this.props;
const { results, selectedIndex } = this.state;
- if (results != null && selectedIndex != null) {
+ if (results && selectedIndex !== undefined) {
const selected = results[selectedIndex];
if (selected.refKey) {
@@ -127,7 +124,7 @@ export default class Search extends React.PureComponent<Props, State> {
handleSearch = (query: string) => {
if (this.mounted) {
- const { branchLike, component, onError } = this.props;
+ const { branchLike, component } = this.props;
this.setState({ loading: true });
const isPortfolio = ['VW', 'SVW', 'APP'].includes(component.qualifier);
@@ -149,10 +146,9 @@ export default class Search extends React.PureComponent<Props, State> {
});
}
})
- .catch(e => {
+ .catch(() => {
if (this.mounted) {
this.setState({ loading: false });
- parseError(e).then(onError);
}
});
}
@@ -170,9 +166,9 @@ export default class Search extends React.PureComponent<Props, State> {
render() {
const { component } = this.props;
const { loading, selectedIndex, results } = this.state;
- const selected = selectedIndex != null && results != null ? results[selectedIndex] : undefined;
+ const selected = selectedIndex !== undefined && results ? results[selectedIndex] : undefined;
const containerClassName = classNames('code-search', {
- 'code-search-with-results': results != null
+ 'code-search-with-results': Boolean(results)
});
const isPortfolio = ['VW', 'SVW', 'APP'].includes(component.qualifier);
@@ -189,11 +185,12 @@ export default class Search extends React.PureComponent<Props, State> {
/>
{loading && <i className="spinner spacer-left" />}
- {results != null && (
+ {results && (
<div className="boxed-group boxed-group-inner spacer-top">
<Components
branchLike={this.props.branchLike}
components={results}
+ metrics={{}}
rootComponent={component}
selected={selected}
/>
diff --git a/server/sonar-web/src/main/js/apps/code/components/__tests__/App-test.tsx b/server/sonar-web/src/main/js/apps/code/components/__tests__/App-test.tsx
index 0171b0bca0f..42b5af7bb2f 100644
--- a/server/sonar-web/src/main/js/apps/code/components/__tests__/App-test.tsx
+++ b/server/sonar-web/src/main/js/apps/code/components/__tests__/App-test.tsx
@@ -17,9 +17,10 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+/* eslint-disable camelcase */
import * as React from 'react';
import { shallow } from 'enzyme';
-import App from '../App';
+import { App } from '../App';
import { waitAndUpdate } from '../../../../helpers/testUtils';
import { retrieveComponent } from '../../utils';
@@ -34,6 +35,11 @@ jest.mock('../../utils', () => ({
retrieveComponentChildren: () => Promise.resolve()
}));
+const METRICS = {
+ coverage: { id: '2', key: 'coverage', type: 'PERCENT', name: 'Coverage', domain: 'Coverage' },
+ new_bugs: { id: '4', key: 'new_bugs', type: 'INT', name: 'New Bugs', domain: 'Reliability' }
+};
+
beforeEach(() => {
(retrieveComponent as jest.Mock<any>).mockClear();
});
@@ -80,7 +86,9 @@ const getWrapper = () => {
organization: 'foo',
qualifier: 'FOO'
}}
+ fetchMetrics={jest.fn()}
location={{ query: { branch: 'b', id: 'foo', line: '7' } }}
+ metrics={METRICS}
/>
);
};
diff --git a/server/sonar-web/src/main/js/apps/code/components/__tests__/ComponentMeasure-test.tsx b/server/sonar-web/src/main/js/apps/code/components/__tests__/ComponentMeasure-test.tsx
new file mode 100644
index 00000000000..c317d15d70c
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/code/components/__tests__/ComponentMeasure-test.tsx
@@ -0,0 +1,56 @@
+/*
+ * 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 { shallow } from 'enzyme';
+import ComponentMeasure from '../ComponentMeasure';
+
+const METRIC = { id: '1', key: 'coverage', type: 'PERCENT', name: 'Coverage' };
+const LEAK_METRIC = { id: '2', key: 'new_coverage', type: 'PERCENT', name: 'Coverage on New Code' };
+const COMPONENT = { key: 'foo', name: 'Foo', qualifier: 'TRK' };
+const COMPONENT_MEASURE = {
+ ...COMPONENT,
+ measures: [{ value: '3.0', periods: [{ index: 1, value: '10.0' }], metric: METRIC.key }]
+};
+
+it('renders correctly', () => {
+ expect(
+ shallow(<ComponentMeasure component={COMPONENT_MEASURE} metric={METRIC} />)
+ ).toMatchSnapshot();
+});
+
+it('renders correctly for leak values', () => {
+ expect(
+ shallow(
+ <ComponentMeasure
+ component={{
+ ...COMPONENT,
+ measures: [
+ { value: '3.0', periods: [{ index: 1, value: '10.0' }], metric: LEAK_METRIC.key }
+ ]
+ }}
+ metric={LEAK_METRIC}
+ />
+ )
+ ).toMatchSnapshot();
+});
+
+it('renders correctly when no measure found', () => {
+ expect(shallow(<ComponentMeasure component={COMPONENT} metric={METRIC} />)).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/apps/code/components/__tests__/Components-test.tsx b/server/sonar-web/src/main/js/apps/code/components/__tests__/Components-test.tsx
new file mode 100644
index 00000000000..8d0dfbe21c5
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/code/components/__tests__/Components-test.tsx
@@ -0,0 +1,60 @@
+/*
+ * 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 { shallow } from 'enzyme';
+import Components from '../Components';
+
+const COMPONENT = { key: 'foo', name: 'Foo', qualifier: 'TRK' };
+const PORTFOLIO = { key: 'bar', name: 'Bar', qualifier: 'VW' };
+const METRICS = { coverage: { id: '1', key: 'coverage', type: 'PERCENT', name: 'Coverage' } };
+
+it('renders correctly', () => {
+ expect(
+ shallow(
+ <Components
+ baseComponent={COMPONENT}
+ components={[COMPONENT]}
+ metrics={METRICS}
+ rootComponent={COMPONENT}
+ />
+ )
+ ).toMatchSnapshot();
+});
+
+it('renders correctly for a search', () => {
+ expect(
+ shallow(<Components components={[COMPONENT]} metrics={METRICS} rootComponent={COMPONENT} />)
+ ).toMatchSnapshot();
+});
+
+it('handle no components correctly', () => {
+ expect(
+ shallow(
+ <Components
+ baseComponent={PORTFOLIO}
+ components={[]}
+ metrics={METRICS}
+ rootComponent={PORTFOLIO}
+ />
+ )
+ .find('ComponentsEmpty')
+ .exists()
+ ).toBe(true);
+});
diff --git a/server/sonar-web/src/main/js/apps/code/types.ts b/server/sonar-web/src/main/js/apps/code/components/__tests__/ComponentsHeader-test.tsx
index f8fbc5af202..4c7ab9f2414 100644
--- a/server/sonar-web/src/main/js/apps/code/types.ts
+++ b/server/sonar-web/src/main/js/apps/code/components/__tests__/ComponentsHeader-test.tsx
@@ -17,17 +17,32 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { Measure } from '../../app/types';
+import * as React from 'react';
+import { shallow } from 'enzyme';
+import ComponentsHeader from '../ComponentsHeader';
-export interface Component extends Breadcrumb {
- branch?: string;
- measures?: Measure[];
- path?: string;
- refKey?: string;
-}
+const COMPONENT = { key: 'foo', name: 'Foo', qualifier: 'TRK' };
+const PORTFOLIO = { key: 'bar', name: 'Bar', qualifier: 'VW' };
+const METRICS = ['foo', 'bar'];
-export interface Breadcrumb {
- key: string;
- name: string;
- qualifier: string;
-}
+it('renders correctly for projects', () => {
+ expect(
+ shallow(
+ <ComponentsHeader baseComponent={COMPONENT} metrics={METRICS} rootComponent={COMPONENT} />
+ )
+ ).toMatchSnapshot();
+});
+
+it('renders correctly for portfolios', () => {
+ expect(
+ shallow(
+ <ComponentsHeader baseComponent={PORTFOLIO} metrics={METRICS} rootComponent={PORTFOLIO} />
+ )
+ ).toMatchSnapshot();
+});
+
+it('renders correctly for a search', () => {
+ expect(
+ shallow(<ComponentsHeader metrics={METRICS} rootComponent={COMPONENT} />)
+ ).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/apps/code/components/__tests__/__snapshots__/ComponentMeasure-test.tsx.snap b/server/sonar-web/src/main/js/apps/code/components/__tests__/__snapshots__/ComponentMeasure-test.tsx.snap
new file mode 100644
index 00000000000..824a3eb07a0
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/code/components/__tests__/__snapshots__/ComponentMeasure-test.tsx.snap
@@ -0,0 +1,19 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders correctly 1`] = `
+<Measure
+ metricKey="coverage"
+ metricType="PERCENT"
+ value="3.0"
+/>
+`;
+
+exports[`renders correctly for leak values 1`] = `
+<Measure
+ metricKey="new_coverage"
+ metricType="PERCENT"
+ value="10.0"
+/>
+`;
+
+exports[`renders correctly when no measure found 1`] = `<span />`;
diff --git a/server/sonar-web/src/main/js/apps/code/components/__tests__/__snapshots__/Components-test.tsx.snap b/server/sonar-web/src/main/js/apps/code/components/__tests__/__snapshots__/Components-test.tsx.snap
new file mode 100644
index 00000000000..5c20c57810a
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/code/components/__tests__/__snapshots__/Components-test.tsx.snap
@@ -0,0 +1,160 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders correctly 1`] = `
+<table
+ className="data zebra"
+>
+ <ComponentsHeader
+ baseComponent={
+ Object {
+ "key": "foo",
+ "name": "Foo",
+ "qualifier": "TRK",
+ }
+ }
+ metrics={
+ Array [
+ "ncloc",
+ "bugs",
+ "vulnerabilities",
+ "code_smells",
+ "coverage",
+ "duplicated_lines_density",
+ ]
+ }
+ rootComponent={
+ Object {
+ "key": "foo",
+ "name": "Foo",
+ "qualifier": "TRK",
+ }
+ }
+ />
+ <tbody>
+ <Component
+ component={
+ Object {
+ "key": "foo",
+ "name": "Foo",
+ "qualifier": "TRK",
+ }
+ }
+ key="foo"
+ metrics={
+ Array [
+ Object {
+ "id": "1",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ ]
+ }
+ rootComponent={
+ Object {
+ "key": "foo",
+ "name": "Foo",
+ "qualifier": "TRK",
+ }
+ }
+ />
+ <tr
+ className="blank"
+ >
+ <td
+ colSpan={8}
+ >
+  
+ </td>
+ </tr>
+ </tbody>
+ <tbody>
+ <Component
+ canBrowse={true}
+ component={
+ Object {
+ "key": "foo",
+ "name": "Foo",
+ "qualifier": "TRK",
+ }
+ }
+ key="foo"
+ metrics={
+ Array [
+ Object {
+ "id": "1",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ ]
+ }
+ rootComponent={
+ Object {
+ "key": "foo",
+ "name": "Foo",
+ "qualifier": "TRK",
+ }
+ }
+ selected={false}
+ />
+ </tbody>
+</table>
+`;
+
+exports[`renders correctly for a search 1`] = `
+<table
+ className="data zebra"
+>
+ <ComponentsHeader
+ metrics={
+ Array [
+ "ncloc",
+ "bugs",
+ "vulnerabilities",
+ "code_smells",
+ "coverage",
+ "duplicated_lines_density",
+ ]
+ }
+ rootComponent={
+ Object {
+ "key": "foo",
+ "name": "Foo",
+ "qualifier": "TRK",
+ }
+ }
+ />
+ <tbody>
+ <Component
+ canBrowse={true}
+ component={
+ Object {
+ "key": "foo",
+ "name": "Foo",
+ "qualifier": "TRK",
+ }
+ }
+ key="foo"
+ metrics={
+ Array [
+ Object {
+ "id": "1",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ ]
+ }
+ rootComponent={
+ Object {
+ "key": "foo",
+ "name": "Foo",
+ "qualifier": "TRK",
+ }
+ }
+ selected={false}
+ />
+ </tbody>
+</table>
+`;
diff --git a/server/sonar-web/src/main/js/apps/code/components/__tests__/__snapshots__/ComponentsHeader-test.tsx.snap b/server/sonar-web/src/main/js/apps/code/components/__tests__/__snapshots__/ComponentsHeader-test.tsx.snap
new file mode 100644
index 00000000000..0d0a7b3c83d
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/code/components/__tests__/__snapshots__/ComponentsHeader-test.tsx.snap
@@ -0,0 +1,94 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders correctly for a search 1`] = `
+<thead>
+ <tr
+ className="code-components-header"
+ >
+ <th
+ className="thin nowrap"
+ >
+  
+ </th>
+ <th>
+  
+ </th>
+ </tr>
+</thead>
+`;
+
+exports[`renders correctly for portfolios 1`] = `
+<thead>
+ <tr
+ className="code-components-header"
+ >
+ <th
+ className="thin nowrap"
+ >
+  
+ </th>
+ <th>
+  
+ </th>
+ <th
+ className="thin nowrap text-right"
+ key="metric_domain.Releasability"
+ >
+ metric_domain.Releasability
+ </th>
+ <th
+ className="thin nowrap text-right code-components-cell"
+ key="metric_domain.Reliability"
+ >
+ metric_domain.Reliability
+ </th>
+ <th
+ className="thin nowrap text-right code-components-cell"
+ key="metric_domain.Security"
+ >
+ metric_domain.Security
+ </th>
+ <th
+ className="thin nowrap text-right code-components-cell"
+ key="metric_domain.Maintainability"
+ >
+ metric_domain.Maintainability
+ </th>
+ <th
+ className="thin nowrap text-right code-components-cell"
+ key="metric.ncloc.name"
+ >
+ metric.ncloc.name
+ </th>
+ </tr>
+</thead>
+`;
+
+exports[`renders correctly for projects 1`] = `
+<thead>
+ <tr
+ className="code-components-header"
+ >
+ <th
+ className="thin nowrap"
+ >
+  
+ </th>
+ <th>
+  
+ </th>
+ <th
+ className="thin nowrap text-right"
+ key="metric.foo.name"
+ >
+ metric.foo.name
+ </th>
+ <th
+ className="thin nowrap text-right code-components-cell"
+ key="metric.bar.name"
+ >
+ metric.bar.name
+ </th>
+ </tr>
+</thead>
+`;
diff --git a/server/sonar-web/src/main/js/apps/code/utils.ts b/server/sonar-web/src/main/js/apps/code/utils.ts
index 75ef812c977..cd429f3d6fa 100644
--- a/server/sonar-web/src/main/js/apps/code/utils.ts
+++ b/server/sonar-web/src/main/js/apps/code/utils.ts
@@ -26,30 +26,37 @@ import {
addComponentBreadcrumbs,
getComponentBreadcrumbs
} from './bucket';
-import { Breadcrumb, Component } from './types';
import { getChildren, getComponent, getBreadcrumbs } from '../../api/components';
-import { BranchLike } from '../../app/types';
-import { getBranchLikeQuery } from '../../helpers/branches';
+import { BranchLike, ComponentMeasure, Breadcrumb } from '../../app/types';
+import { getBranchLikeQuery, isShortLivingBranch, isPullRequest } from '../../helpers/branches';
const METRICS = [
'ncloc',
- 'code_smells',
'bugs',
'vulnerabilities',
+ 'code_smells',
'coverage',
- 'duplicated_lines_density',
- 'alert_status'
+ 'duplicated_lines_density'
];
+const APPLICATION_METRICS = ['alert_status', ...METRICS];
+
const PORTFOLIO_METRICS = [
'releasability_rating',
- 'alert_status',
'reliability_rating',
'security_rating',
'sqale_rating',
'ncloc'
];
+const LEAK_METRICS = [
+ 'new_lines',
+ 'new_bugs',
+ 'new_vulnerabilities',
+ 'new_code_smells',
+ 'new_coverage'
+];
+
const PAGE_SIZE = 100;
function requestChildren(
@@ -57,7 +64,7 @@ function requestChildren(
metrics: string[],
page: number,
branchLike?: BranchLike
-): Promise<Component[]> {
+): Promise<ComponentMeasure[]> {
return getChildren(componentKey, metrics, {
p: page,
ps: PAGE_SIZE,
@@ -76,12 +83,12 @@ function requestAllChildren(
componentKey: string,
metrics: string[],
branchLike?: BranchLike
-): Promise<Component[]> {
+): Promise<ComponentMeasure[]> {
return requestChildren(componentKey, metrics, 1, branchLike);
}
interface Children {
- components: Component[];
+ components: ComponentMeasure[];
page: number;
total: number;
}
@@ -93,7 +100,7 @@ interface ExpandRootDirFunc {
function expandRootDir(metrics: string[], branchLike?: BranchLike): ExpandRootDirFunc {
return function({ components, total, ...other }) {
const rootDir = components.find(
- (component: Component) => component.qualifier === 'DIR' && component.name === '/'
+ (component: ComponentMeasure) => component.qualifier === 'DIR' && component.name === '/'
);
if (rootDir) {
return requestAllChildren(rootDir.key, metrics, branchLike).then(rootDirComponents => {
@@ -107,6 +114,10 @@ function expandRootDir(metrics: string[], branchLike?: BranchLike): ExpandRootDi
};
}
+function showLeakMeasure(branchLike?: BranchLike) {
+ return isShortLivingBranch(branchLike) || isPullRequest(branchLike);
+}
+
function prepareChildren(r: any): Children {
return {
components: r.components,
@@ -115,13 +126,13 @@ function prepareChildren(r: any): Children {
};
}
-function skipRootDir(breadcrumbs: Component[]) {
+function skipRootDir(breadcrumbs: ComponentMeasure[]) {
return breadcrumbs.filter(component => {
return !(component.qualifier === 'DIR' && component.name === '/');
});
}
-function storeChildrenBase(children: Component[]) {
+function storeChildrenBase(children: ComponentMeasure[]) {
children.forEach(addComponent);
}
@@ -135,21 +146,26 @@ function storeChildrenBreadcrumbs(parentComponentKey: string, children: Breadcru
}
}
-function getMetrics(isPortfolio: boolean) {
- return isPortfolio ? PORTFOLIO_METRICS : METRICS;
+export function getCodeMetrics(qualifier: string, branchLike?: BranchLike) {
+ if (['VW', 'SVW'].includes(qualifier)) {
+ return PORTFOLIO_METRICS;
+ }
+ if (qualifier === 'APP') {
+ return APPLICATION_METRICS;
+ }
+ if (showLeakMeasure(branchLike)) {
+ return LEAK_METRICS;
+ }
+ return METRICS;
}
-function retrieveComponentBase(
- componentKey: string,
- isPortfolio: boolean,
- branchLike?: BranchLike
-) {
+function retrieveComponentBase(componentKey: string, qualifier: string, branchLike?: BranchLike) {
const existing = getComponentFromBucket(componentKey);
if (existing) {
return Promise.resolve(existing);
}
- const metrics = getMetrics(isPortfolio);
+ const metrics = getCodeMetrics(qualifier, branchLike);
return getComponent({
componentKey,
@@ -163,9 +179,9 @@ function retrieveComponentBase(
export function retrieveComponentChildren(
componentKey: string,
- isPortfolio: boolean,
+ qualifier: string,
branchLike?: BranchLike
-): Promise<{ components: Component[]; page: number; total: number }> {
+): Promise<{ components: ComponentMeasure[]; page: number; total: number }> {
const existing = getComponentChildren(componentKey);
if (existing) {
return Promise.resolve({
@@ -175,7 +191,7 @@ export function retrieveComponentChildren(
});
}
- const metrics = getMetrics(isPortfolio);
+ const metrics = getCodeMetrics(qualifier, branchLike);
return getChildren(componentKey, metrics, {
ps: PAGE_SIZE,
@@ -211,26 +227,26 @@ function retrieveComponentBreadcrumbs(
export function retrieveComponent(
componentKey: string,
- isPortfolio: boolean,
+ qualifier: string,
branchLike?: BranchLike
): Promise<{
- breadcrumbs: Component[];
- component: Component;
- components: Component[];
+ breadcrumbs: Breadcrumb[];
+ component: ComponentMeasure;
+ components: ComponentMeasure[];
page: number;
total: number;
}> {
return Promise.all([
- retrieveComponentBase(componentKey, isPortfolio, branchLike),
- retrieveComponentChildren(componentKey, isPortfolio, branchLike),
+ retrieveComponentBase(componentKey, qualifier, branchLike),
+ retrieveComponentChildren(componentKey, qualifier, branchLike),
retrieveComponentBreadcrumbs(componentKey, branchLike)
]).then(r => {
return {
+ breadcrumbs: r[2],
component: r[0],
components: r[1].components,
- total: r[1].total,
page: r[1].page,
- breadcrumbs: r[2]
+ total: r[1].total
};
});
}
@@ -238,10 +254,10 @@ export function retrieveComponent(
export function loadMoreChildren(
componentKey: string,
page: number,
- isPortfolio: boolean,
+ qualifier: string,
branchLike?: BranchLike
): Promise<Children> {
- const metrics = getMetrics(isPortfolio);
+ const metrics = getCodeMetrics(qualifier, branchLike);
return getChildren(componentKey, metrics, {
ps: PAGE_SIZE,