aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web/src/main/js/apps
diff options
context:
space:
mode:
authorTeryk Bellahsene <teryk.bellahsene@sonarsource.com>2018-03-12 12:06:11 +0100
committerTeryk Bellahsene <teryk@users.noreply.github.com>2018-03-13 14:05:36 +0100
commit913c82c8772fd4747626a1fbe665ccda2e5ca9f1 (patch)
treed48784851df80905ce125cc60ac8aec8570751a9 /server/sonar-web/src/main/js/apps
parent751e4000e40a4af66b80767d632b1bef64dc5647 (diff)
downloadsonarqube-913c82c8772fd4747626a1fbe665ccda2e5ca9f1.tar.gz
sonarqube-913c82c8772fd4747626a1fbe665ccda2e5ca9f1.zip
SONAR-10374 Support pull request in the web app
Diffstat (limited to 'server/sonar-web/src/main/js/apps')
-rw-r--r--server/sonar-web/src/main/js/apps/about/actions.js2
-rw-r--r--server/sonar-web/src/main/js/apps/account/notifications/__tests__/__snapshots__/ProjectNotifications-test.js.snap1
-rw-r--r--server/sonar-web/src/main/js/apps/account/organizations/actions.ts2
-rw-r--r--server/sonar-web/src/main/js/apps/background-tasks/components/TaskComponent.tsx34
-rw-r--r--server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/TaskComponent-test.tsx.snap2
-rw-r--r--server/sonar-web/src/main/js/apps/background-tasks/types.ts2
-rw-r--r--server/sonar-web/src/main/js/apps/code/components/App.tsx37
-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.tsx11
-rw-r--r--server/sonar-web/src/main/js/apps/code/components/ComponentLink.tsx9
-rw-r--r--server/sonar-web/src/main/js/apps/code/components/ComponentName.tsx10
-rw-r--r--server/sonar-web/src/main/js/apps/code/components/ComponentPin.tsx11
-rw-r--r--server/sonar-web/src/main/js/apps/code/components/Components.tsx9
-rw-r--r--server/sonar-web/src/main/js/apps/code/components/Search.tsx25
-rw-r--r--server/sonar-web/src/main/js/apps/code/utils.ts70
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/components/App.js20
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/components/AppContainer.js5
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/components/Breadcrumbs.js15
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/components/MeasureContent.js19
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/components/MeasureContentContainer.js10
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/components/MeasureFavoriteContainer.js2
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/components/MeasureHeader.js6
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverview.js18
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverviewContainer.js9
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/components/__tests__/MeasureHeader-test.js5
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/MeasureHeader-test.js.snap1
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/drilldown/CodeView.js6
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentCell.js11
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentsList.js6
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentsListRow.js6
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/drilldown/FilesView.js4
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/drilldown/TreeMapView.js8
-rw-r--r--server/sonar-web/src/main/js/apps/component/components/App.tsx21
-rw-r--r--server/sonar-web/src/main/js/apps/component/components/__tests__/__snapshots__/App-test.tsx.snap9
-rw-r--r--server/sonar-web/src/main/js/apps/issues/components/App.d.ts4
-rw-r--r--server/sonar-web/src/main/js/apps/issues/components/App.js33
-rw-r--r--server/sonar-web/src/main/js/apps/issues/components/ComponentBreadcrumbs.tsx21
-rw-r--r--server/sonar-web/src/main/js/apps/issues/components/IssuesList.js6
-rw-r--r--server/sonar-web/src/main/js/apps/issues/components/IssuesSourceViewer.js4
-rw-r--r--server/sonar-web/src/main/js/apps/issues/components/ListItem.js8
-rw-r--r--server/sonar-web/src/main/js/apps/issues/components/__tests__/ComponentBreadcrumbs-test.tsx11
-rw-r--r--server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/ComponentBreadcrumbs-test.tsx.snap26
-rw-r--r--server/sonar-web/src/main/js/apps/overview/badges/BadgesModal.tsx9
-rw-r--r--server/sonar-web/src/main/js/apps/overview/badges/__tests__/BadgesModal-test.tsx9
-rw-r--r--server/sonar-web/src/main/js/apps/overview/badges/utils.ts7
-rw-r--r--server/sonar-web/src/main/js/apps/overview/components/App.tsx22
-rw-r--r--server/sonar-web/src/main/js/apps/overview/components/OverviewApp.tsx55
-rw-r--r--server/sonar-web/src/main/js/apps/overview/components/__tests__/App-test.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/overview/events/AnalysesList.tsx20
-rw-r--r--server/sonar-web/src/main/js/apps/overview/events/__tests__/AnalysesList-test.tsx38
-rw-r--r--server/sonar-web/src/main/js/apps/overview/events/__tests__/__snapshots__/AnalysesList-test.tsx.snap18
-rw-r--r--server/sonar-web/src/main/js/apps/overview/main/BugsAndVulnerabilities.tsx6
-rw-r--r--server/sonar-web/src/main/js/apps/overview/main/CodeSmells.tsx10
-rw-r--r--server/sonar-web/src/main/js/apps/overview/main/Coverage.tsx10
-rw-r--r--server/sonar-web/src/main/js/apps/overview/main/Duplications.tsx12
-rw-r--r--server/sonar-web/src/main/js/apps/overview/main/enhance.tsx23
-rw-r--r--server/sonar-web/src/main/js/apps/overview/meta/Meta.tsx14
-rw-r--r--server/sonar-web/src/main/js/apps/overview/meta/MetaSize.tsx11
-rw-r--r--server/sonar-web/src/main/js/apps/overview/qualityGate/QualityGate.js10
-rw-r--r--server/sonar-web/src/main/js/apps/overview/qualityGate/QualityGateCondition.js13
-rw-r--r--server/sonar-web/src/main/js/apps/overview/qualityGate/QualityGateConditions.js10
-rw-r--r--server/sonar-web/src/main/js/apps/overview/qualityGate/__tests__/__snapshots__/ApplicationQualityGateProject-test.tsx.snap1
-rw-r--r--server/sonar-web/src/main/js/apps/overview/qualityGate/__tests__/__snapshots__/QualityGateCondition-test.js.snap7
-rw-r--r--server/sonar-web/src/main/js/apps/portfolio/components/Activity.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/portfolio/components/App.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/portfolio/components/__tests__/Activity-test.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/portfolio/components/__tests__/App-test.tsx22
-rw-r--r--server/sonar-web/src/main/js/apps/portfolio/components/__tests__/__snapshots__/Effort-test.tsx.snap1
-rw-r--r--server/sonar-web/src/main/js/apps/portfolio/components/__tests__/__snapshots__/HistoryButtonLink-test.tsx.snap1
-rw-r--r--server/sonar-web/src/main/js/apps/portfolio/components/__tests__/__snapshots__/MainRating-test.tsx.snap1
-rw-r--r--server/sonar-web/src/main/js/apps/portfolio/components/__tests__/__snapshots__/MeasuresButtonLink-test.tsx.snap1
-rw-r--r--server/sonar-web/src/main/js/apps/portfolio/components/__tests__/__snapshots__/ReleasabilityBox-test.tsx.snap2
-rw-r--r--server/sonar-web/src/main/js/apps/portfolio/components/__tests__/__snapshots__/Summary-test.tsx.snap2
-rw-r--r--server/sonar-web/src/main/js/apps/portfolio/components/__tests__/__snapshots__/WorstProjects-test.tsx.snap3
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAppContainer.js21
-rw-r--r--server/sonar-web/src/main/js/apps/projectBranches/components/App.tsx48
-rw-r--r--server/sonar-web/src/main/js/apps/projectBranches/components/BranchRow.tsx76
-rw-r--r--server/sonar-web/src/main/js/apps/projectBranches/components/DeleteBranchModal.tsx31
-rw-r--r--server/sonar-web/src/main/js/apps/projectBranches/components/LeakPeriodForm.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/projectBranches/components/LongBranchesPattern.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/projectBranches/components/RenameBranchModal.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/projectBranches/components/SettingForm.tsx25
-rw-r--r--server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/App-test.tsx26
-rw-r--r--server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/BranchRow-test.tsx34
-rw-r--r--server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/DeleteBranchModal-test.tsx73
-rw-r--r--server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/LongBranchesPattern-test.tsx5
-rw-r--r--server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/SettingForm-test.tsx6
-rw-r--r--server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/__snapshots__/App-test.tsx.snap54
-rw-r--r--server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/__snapshots__/BranchRow-test.tsx.snap59
-rw-r--r--server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Risk-test.tsx.snap1
-rw-r--r--server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/SimpleBubbleChart-test.tsx.snap1
-rw-r--r--server/sonar-web/src/main/js/apps/projectsManagement/ProjectRowActions.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/CreateProjectForm-test.tsx.snap1
-rw-r--r--server/sonar-web/src/main/js/apps/settings/store/actions.js12
94 files changed, 865 insertions, 494 deletions
diff --git a/server/sonar-web/src/main/js/apps/about/actions.js b/server/sonar-web/src/main/js/apps/about/actions.js
index 2c63a8553b9..a60d730c30a 100644
--- a/server/sonar-web/src/main/js/apps/about/actions.js
+++ b/server/sonar-web/src/main/js/apps/about/actions.js
@@ -23,7 +23,7 @@ import { receiveValues } from '../settings/store/values/actions';
export const fetchAboutPageSettings = () => dispatch => {
const keys = ['sonar.lf.aboutText'];
- return getValues(keys.join()).then(values => {
+ return getValues({ keys: keys.join() }).then(values => {
dispatch(receiveValues(values));
});
};
diff --git a/server/sonar-web/src/main/js/apps/account/notifications/__tests__/__snapshots__/ProjectNotifications-test.js.snap b/server/sonar-web/src/main/js/apps/account/notifications/__tests__/__snapshots__/ProjectNotifications-test.js.snap
index 2fb0f7c15d1..33c554244f1 100644
--- a/server/sonar-web/src/main/js/apps/account/notifications/__tests__/__snapshots__/ProjectNotifications-test.js.snap
+++ b/server/sonar-web/src/main/js/apps/account/notifications/__tests__/__snapshots__/ProjectNotifications-test.js.snap
@@ -23,7 +23,6 @@ exports[`should match snapshot 1`] = `
Object {
"pathname": "/dashboard",
"query": Object {
- "branch": undefined,
"id": "foo",
},
}
diff --git a/server/sonar-web/src/main/js/apps/account/organizations/actions.ts b/server/sonar-web/src/main/js/apps/account/organizations/actions.ts
index 89dbf7c9705..4361e0d0849 100644
--- a/server/sonar-web/src/main/js/apps/account/organizations/actions.ts
+++ b/server/sonar-web/src/main/js/apps/account/organizations/actions.ts
@@ -30,7 +30,7 @@ export const fetchMyOrganizations = () => (dispatch: Dispatch<any>) => {
};
export const fetchIfAnyoneCanCreateOrganizations = () => (dispatch: Dispatch<any>) => {
- return getValues('sonar.organizations.anyoneCanCreate').then(values => {
+ return getValues({ keys: 'sonar.organizations.anyoneCanCreate' }).then(values => {
dispatch(receiveValues(values, undefined));
});
};
diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/TaskComponent.tsx b/server/sonar-web/src/main/js/apps/background-tasks/components/TaskComponent.tsx
index 61fba1730da..a81919215b1 100644
--- a/server/sonar-web/src/main/js/apps/background-tasks/components/TaskComponent.tsx
+++ b/server/sonar-web/src/main/js/apps/background-tasks/components/TaskComponent.tsx
@@ -23,9 +23,16 @@ import TaskType from './TaskType';
import { Task } from '../types';
import QualifierIcon from '../../../components/shared/QualifierIcon';
import Organization from '../../../components/shared/Organization';
-import { getProjectUrl } from '../../../helpers/urls';
+import {
+ getProjectUrl,
+ getShortLivingBranchUrl,
+ getLongLivingBranchUrl,
+ getPullRequestUrl
+} from '../../../helpers/urls';
import ShortLivingBranchIcon from '../../../components/icons-components/ShortLivingBranchIcon';
import LongLivingBranchIcon from '../../../components/icons-components/LongLivingBranchIcon';
+import PullRequestIcon from '../../../components/icons-components/PullRequestIcon';
+import Tooltip from '../../../components/controls/Tooltip';
interface Props {
task: Task;
@@ -45,8 +52,10 @@ export default function TaskComponent({ task }: Props) {
<td>
{task.branchType === 'SHORT' && <ShortLivingBranchIcon className="little-spacer-right" />}
{task.branchType === 'LONG' && <LongLivingBranchIcon className="little-spacer-right" />}
+ {task.pullRequest !== undefined && <PullRequestIcon className="little-spacer-right" />}
{!task.branchType &&
+ !task.pullRequest &&
task.componentQualifier && (
<span className="little-spacer-right">
<QualifierIcon qualifier={task.componentQualifier} />
@@ -56,7 +65,7 @@ export default function TaskComponent({ task }: Props) {
{task.organization && <Organization organizationKey={task.organization} />}
{task.componentName && (
- <Link className="spacer-right" to={getProjectUrl(task.componentKey, task.branch)}>
+ <Link className="spacer-right" to={getTaskComponentUrl(task.componentKey, task)}>
{task.componentName}
{task.branch && (
@@ -65,6 +74,15 @@ export default function TaskComponent({ task }: Props) {
{task.branch}
</span>
)}
+
+ {task.pullRequest && (
+ <Tooltip overlay={task.pullRequestTitle}>
+ <span className="text-limited text-text-top">
+ <span style={{ marginLeft: 5, marginRight: 5 }}>/</span>
+ {task.pullRequest}
+ </span>
+ </Tooltip>
+ )}
</Link>
)}
@@ -72,3 +90,15 @@ export default function TaskComponent({ task }: Props) {
</td>
);
}
+
+function getTaskComponentUrl(componentKey: string, task: Task) {
+ if (task.branch && task.branchType === 'SHORT') {
+ return getShortLivingBranchUrl(componentKey, task.branchType);
+ } else if (task.branchType && task.branchType === 'LONG') {
+ return getLongLivingBranchUrl(componentKey, task.branchType);
+ } else if (task.pullRequest) {
+ return getPullRequestUrl(componentKey, task.pullRequest);
+ } else {
+ return getProjectUrl(componentKey);
+ }
+}
diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/TaskComponent-test.tsx.snap b/server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/TaskComponent-test.tsx.snap
index 93a63cdafd4..f71a484187b 100644
--- a/server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/TaskComponent-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/TaskComponent-test.tsx.snap
@@ -20,7 +20,6 @@ exports[`renders 1`] = `
Object {
"pathname": "/dashboard",
"query": Object {
- "branch": undefined,
"id": "foo",
},
}
@@ -67,7 +66,6 @@ exports[`renders 3`] = `
Object {
"pathname": "/dashboard",
"query": Object {
- "branch": "feature",
"id": "foo",
},
}
diff --git a/server/sonar-web/src/main/js/apps/background-tasks/types.ts b/server/sonar-web/src/main/js/apps/background-tasks/types.ts
index b52783299ce..239c4581d61 100644
--- a/server/sonar-web/src/main/js/apps/background-tasks/types.ts
+++ b/server/sonar-web/src/main/js/apps/background-tasks/types.ts
@@ -29,6 +29,8 @@ export interface Task {
hasScannerContext?: boolean;
id: string;
organization?: string;
+ pullRequest?: string;
+ pullRequestTitle?: string;
startedAt?: string;
status: string;
submittedAt: string;
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 b6941e56388..937a66ee751 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
@@ -26,16 +26,16 @@ 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 ListFooter from '../../../components/controls/ListFooter';
import SourceViewer from '../../../components/SourceViewer/SourceViewer';
-import { parseError } from '../../../helpers/request';
-import { getBranchName } from '../../../helpers/branches';
+import { isSameBranchLike } from '../../../helpers/branches';
import { translate } from '../../../helpers/l10n';
-import { Component, Branch } from '../../../app/types';
+import { parseError } from '../../../helpers/request';
import '../code.css';
interface Props {
- branch?: Branch;
+ branchLike?: BranchLike;
component: Component;
location: { query: { [x: string]: string } };
}
@@ -67,7 +67,10 @@ export default class App extends React.PureComponent<Props, State> {
}
componentDidUpdate(prevProps: Props) {
- if (prevProps.component !== this.props.component || prevProps.branch !== this.props.branch) {
+ if (
+ prevProps.component !== this.props.component ||
+ !isSameBranchLike(prevProps.branchLike, this.props.branchLike)
+ ) {
this.handleComponentChange();
} else if (prevProps.location !== this.props.location) {
this.handleUpdate();
@@ -80,14 +83,14 @@ export default class App extends React.PureComponent<Props, State> {
}
handleComponentChange() {
- const { branch, component } = this.props;
+ const { branchLike, component } = this.props;
// we already know component's breadcrumbs,
addComponentBreadcrumbs(component.key, component.breadcrumbs);
this.setState({ loading: true });
const isPortfolio = ['VW', 'SVW'].includes(component.qualifier);
- retrieveComponentChildren(component.key, isPortfolio, getBranchName(branch))
+ retrieveComponentChildren(component.key, isPortfolio, branchLike)
.then(() => {
addComponent(component);
if (this.mounted) {
@@ -106,7 +109,7 @@ export default class App extends React.PureComponent<Props, State> {
this.setState({ loading: true });
const isPortfolio = ['VW', 'SVW'].includes(this.props.component.qualifier);
- retrieveComponent(componentKey, isPortfolio, getBranchName(this.props.branch))
+ retrieveComponent(componentKey, isPortfolio, this.props.branchLike)
.then(r => {
if (this.mounted) {
if (['FIL', 'UTS'].includes(r.component.qualifier)) {
@@ -152,7 +155,7 @@ export default class App extends React.PureComponent<Props, State> {
return;
}
const isPortfolio = ['VW', 'SVW'].includes(this.props.component.qualifier);
- loadMoreChildren(baseComponent.key, page + 1, isPortfolio, getBranchName(this.props.branch))
+ loadMoreChildren(baseComponent.key, page + 1, isPortfolio, this.props.branchLike)
.then(r => {
if (this.mounted) {
this.setState({
@@ -177,7 +180,7 @@ export default class App extends React.PureComponent<Props, State> {
};
render() {
- const { branch, component, location } = this.props;
+ const { branchLike, component, location } = this.props;
const {
loading,
error,
@@ -187,8 +190,6 @@ export default class App extends React.PureComponent<Props, State> {
total,
sourceViewer
} = this.state;
- const branchName = getBranchName(branch);
-
const shouldShowBreadcrumbs = breadcrumbs.length > 1;
const componentsClassName = classNames('boxed-group', 'boxed-group-inner', 'spacer-top', {
@@ -202,7 +203,7 @@ export default class App extends React.PureComponent<Props, State> {
{error && <div className="alert alert-danger">{error}</div>}
<Search
- branch={branchName}
+ branchLike={branchLike}
component={component}
location={location}
onError={this.handleError}
@@ -210,7 +211,11 @@ export default class App extends React.PureComponent<Props, State> {
<div className="code-components">
{shouldShowBreadcrumbs && (
- <Breadcrumbs branch={branchName} breadcrumbs={breadcrumbs} rootComponent={component} />
+ <Breadcrumbs
+ branchLike={branchLike}
+ breadcrumbs={breadcrumbs}
+ rootComponent={component}
+ />
)}
{sourceViewer === undefined &&
@@ -218,7 +223,7 @@ export default class App extends React.PureComponent<Props, State> {
<div className={componentsClassName}>
<Components
baseComponent={baseComponent}
- branch={branchName}
+ branchLike={branchLike}
components={components}
rootComponent={component}
/>
@@ -232,7 +237,7 @@ export default class App extends React.PureComponent<Props, State> {
{sourceViewer !== undefined && (
<div className="spacer-top">
- <SourceViewer branch={branchName} component={sourceViewer.key} />
+ <SourceViewer branchLike={branchLike} component={sourceViewer.key} />
</div>
)}
</div>
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 783b7126fa5..7ea212cd7ee 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
@@ -20,20 +20,21 @@
import * as React from 'react';
import ComponentName from './ComponentName';
import { Component } from '../types';
+import { BranchLike } from '../../../app/types';
interface Props {
- branch?: string;
+ branchLike?: BranchLike;
breadcrumbs: Component[];
rootComponent: Component;
}
-export default function Breadcrumbs({ branch, breadcrumbs, rootComponent }: Props) {
+export default function Breadcrumbs({ branchLike, breadcrumbs, rootComponent }: Props) {
return (
<ul className="code-breadcrumbs">
{breadcrumbs.map((component, index) => (
<li key={component.key}>
<ComponentName
- branch={branch}
+ branchLike={branchLike}
canBrowse={index < breadcrumbs.length - 1}
component={component}
rootComponent={rootComponent}
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 841d96baee7..df873439506 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
@@ -24,12 +24,13 @@ import ComponentMeasure from './ComponentMeasure';
import ComponentLink from './ComponentLink';
import ComponentPin from './ComponentPin';
import { Component as IComponent } from '../types';
+import { BranchLike } from '../../../app/types';
const TOP_OFFSET = 200;
const BOTTOM_OFFSET = 10;
interface Props {
- branch?: string;
+ branchLike?: BranchLike;
canBrowse?: boolean;
component: IComponent;
previous?: IComponent;
@@ -73,7 +74,7 @@ export default class Component extends React.PureComponent<Props> {
render() {
const {
- branch,
+ branchLike,
component,
rootComponent,
selected = false,
@@ -89,10 +90,10 @@ export default class Component extends React.PureComponent<Props> {
switch (component.qualifier) {
case 'FIL':
case 'UTS':
- componentAction = <ComponentPin branch={branch} component={component} />;
+ componentAction = <ComponentPin branchLike={branchLike} component={component} />;
break;
default:
- componentAction = <ComponentLink branch={branch} component={component} />;
+ componentAction = <ComponentLink branchLike={branchLike} component={component} />;
}
}
@@ -121,7 +122,7 @@ export default class Component extends React.PureComponent<Props> {
</td>
<td className="code-name-cell">
<ComponentName
- branch={branch}
+ branchLike={branchLike}
component={component}
rootComponent={rootComponent}
previous={previous}
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 599dabdc412..60927947472 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
@@ -20,21 +20,22 @@
import * as React from 'react';
import { Link } from 'react-router';
import { Component } from '../types';
+import { BranchLike } from '../../../app/types';
import LinkIcon from '../../../components/icons-components/LinkIcon';
import { translate } from '../../../helpers/l10n';
-import { getProjectUrl } from '../../../helpers/urls';
+import { getBranchLikeUrl } from '../../../helpers/urls';
interface Props {
- branch?: string;
+ branchLike?: BranchLike;
component: Component;
}
-export default function ComponentLink({ component, branch }: Props) {
+export default function ComponentLink({ component, branchLike }: Props) {
return (
<Link
className="link-no-underline"
title={translate('code.open_component_page')}
- to={getProjectUrl(component.refKey || component.key, branch)}>
+ to={getBranchLikeUrl(component.refKey || component.key, branchLike)}>
<LinkIcon />
</Link>
);
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 7abb5e51a2d..0bd1290d662 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,9 +20,11 @@
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 QualifierIcon from '../../../components/shared/QualifierIcon';
-import { Component } from '../types';
+import { getBranchLikeQuery } from '../../../helpers/branches';
function getTooltip(component: Component) {
const isFile = component.qualifier === 'FIL' || component.qualifier === 'UTS';
@@ -49,7 +51,7 @@ function mostCommitPrefix(strings: string[]) {
}
interface Props {
- branch?: string;
+ branchLike?: BranchLike;
canBrowse?: boolean;
component: Component;
previous?: Component;
@@ -57,7 +59,7 @@ interface Props {
}
export default function ComponentName(props: Props) {
- const { branch, component, rootComponent, previous, canBrowse = false } = props;
+ const { branchLike, component, rootComponent, previous, canBrowse = false } = props;
const areBothDirs = component.qualifier === 'DIR' && previous && previous.qualifier === 'DIR';
const prefix =
areBothDirs && previous !== undefined
@@ -83,7 +85,7 @@ export default function ComponentName(props: Props) {
</Link>
);
} else if (canBrowse) {
- const query = { id: rootComponent.key, branch };
+ const query = { id: rootComponent.key, ...getBranchLikeQuery(branchLike) };
if (component.key !== rootComponent.key) {
Object.assign(query, { selected: component.key });
}
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 eb61f75da93..867f4bd476f 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
@@ -18,20 +18,21 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import Workspace from '../../../components/workspace/main';
+import { Component } from '../types';
+import { BranchLike } from '../../../app/types';
import PinIcon from '../../../components/shared/pin-icon';
+import Workspace from '../../../components/workspace/main';
import { translate } from '../../../helpers/l10n';
-import { Component } from '../types';
interface Props {
- branch?: string;
+ branchLike?: BranchLike;
component: Component;
}
-export default function ComponentPin({ branch, component }: Props) {
+export default function ComponentPin({ branchLike, component }: Props) {
const handleClick = (event: React.SyntheticEvent<HTMLAnchorElement>) => {
event.preventDefault();
- Workspace.openComponent({ branch, key: component.key });
+ Workspace.openComponent({ branchLike, key: component.key });
};
return (
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 25e99051994..98d3b569e71 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
@@ -22,24 +22,25 @@ import Component from './Component';
import ComponentsEmpty from './ComponentsEmpty';
import ComponentsHeader from './ComponentsHeader';
import { Component as IComponent } from '../types';
+import { BranchLike } from '../../../app/types';
interface Props {
baseComponent?: IComponent;
- branch?: string;
+ branchLike?: BranchLike;
components: IComponent[];
rootComponent: IComponent;
selected?: IComponent;
}
export default function Components(props: Props) {
- const { baseComponent, branch, components, rootComponent, selected } = props;
+ const { baseComponent, branchLike, components, rootComponent, selected } = props;
return (
<table className="data zebra">
<ComponentsHeader baseComponent={baseComponent} rootComponent={rootComponent} />
{baseComponent && (
<tbody>
<Component
- branch={branch}
+ branchLike={branchLike}
component={baseComponent}
key={baseComponent.key}
rootComponent={rootComponent}
@@ -53,7 +54,7 @@ export default function Components(props: Props) {
{components.length ? (
components.map((component, index, list) => (
<Component
- branch={branch}
+ branchLike={branchLike}
canBrowse={true}
component={component}
key={component.key}
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 4a5536ed3de..abeeacc511a 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,15 +21,17 @@ import * as React from 'react';
import * as PropTypes from 'prop-types';
import * as classNames from 'classnames';
import Components from './Components';
-import { getTree } from '../../../api/components';
-import { parseError } from '../../../helpers/request';
-import { getProjectUrl } from '../../../helpers/urls';
import { Component } from '../types';
+import { getTree } from '../../../api/components';
+import { BranchLike } 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 {
- branch?: string;
+ branchLike?: BranchLike;
component: Component;
location: {};
onError: (error: string) => void;
@@ -89,7 +91,7 @@ export default class Search extends React.PureComponent<Props, State> {
}
handleSelectCurrent() {
- const { branch, component } = this.props;
+ const { branchLike, component } = this.props;
const { results, selectedIndex } = this.state;
if (results != null && selectedIndex != null) {
const selected = results[selectedIndex];
@@ -99,7 +101,7 @@ export default class Search extends React.PureComponent<Props, State> {
} else {
this.context.router.push({
pathname: '/code',
- query: { branch, id: component.key, selected: selected.key }
+ query: { id: component.key, selected: selected.key, ...getBranchLikeQuery(branchLike) }
});
}
}
@@ -125,13 +127,18 @@ export default class Search extends React.PureComponent<Props, State> {
handleSearch = (query: string) => {
if (this.mounted) {
- const { branch, component, onError } = this.props;
+ const { branchLike, component, onError } = this.props;
this.setState({ loading: true });
const isPortfolio = ['VW', 'SVW', 'APP'].includes(component.qualifier);
const qualifiers = isPortfolio ? 'SVW,TRK' : 'BRC,UTS,FIL';
- getTree(component.key, { branch, q: query, s: 'qualifier,name', qualifiers })
+ getTree(component.key, {
+ q: query,
+ s: 'qualifier,name',
+ qualifiers,
+ ...getBranchLikeQuery(branchLike)
+ })
.then(r => {
if (this.mounted) {
this.setState({
@@ -184,7 +191,7 @@ export default class Search extends React.PureComponent<Props, State> {
{results != null && (
<div className="boxed-group boxed-group-inner spacer-top">
<Components
- branch={this.props.branch}
+ branchLike={this.props.branchLike}
components={results}
rootComponent={component}
selected={selected}
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 80fdbadea8c..75ef812c977 100644
--- a/server/sonar-web/src/main/js/apps/code/utils.ts
+++ b/server/sonar-web/src/main/js/apps/code/utils.ts
@@ -28,6 +28,8 @@ import {
} from './bucket';
import { Breadcrumb, Component } from './types';
import { getChildren, getComponent, getBreadcrumbs } from '../../api/components';
+import { BranchLike } from '../../app/types';
+import { getBranchLikeQuery } from '../../helpers/branches';
const METRICS = [
'ncloc',
@@ -54,11 +56,15 @@ function requestChildren(
componentKey: string,
metrics: string[],
page: number,
- branch?: string
+ branchLike?: BranchLike
): Promise<Component[]> {
- return getChildren(componentKey, metrics, { branch, p: page, ps: PAGE_SIZE }).then(r => {
+ return getChildren(componentKey, metrics, {
+ p: page,
+ ps: PAGE_SIZE,
+ ...getBranchLikeQuery(branchLike)
+ }).then(r => {
if (r.paging.total > r.paging.pageSize * r.paging.pageIndex) {
- return requestChildren(componentKey, metrics, page + 1, branch).then(moreComponents => {
+ return requestChildren(componentKey, metrics, page + 1, branchLike).then(moreComponents => {
return [...r.components, ...moreComponents];
});
}
@@ -69,9 +75,9 @@ function requestChildren(
function requestAllChildren(
componentKey: string,
metrics: string[],
- branch?: string
+ branchLike?: BranchLike
): Promise<Component[]> {
- return requestChildren(componentKey, metrics, 1, branch);
+ return requestChildren(componentKey, metrics, 1, branchLike);
}
interface Children {
@@ -84,13 +90,13 @@ interface ExpandRootDirFunc {
(children: Children): Promise<Children>;
}
-function expandRootDir(metrics: string[], branch?: string): ExpandRootDirFunc {
+function expandRootDir(metrics: string[], branchLike?: BranchLike): ExpandRootDirFunc {
return function({ components, total, ...other }) {
const rootDir = components.find(
(component: Component) => component.qualifier === 'DIR' && component.name === '/'
);
if (rootDir) {
- return requestAllChildren(rootDir.key, metrics, branch).then(rootDirComponents => {
+ return requestAllChildren(rootDir.key, metrics, branchLike).then(rootDirComponents => {
const nextComponents = without([...rootDirComponents, ...components], rootDir);
const nextTotal = total + rootDirComponents.length - /* root dir */ 1;
return { components: nextComponents, total: nextTotal, ...other };
@@ -133,7 +139,11 @@ function getMetrics(isPortfolio: boolean) {
return isPortfolio ? PORTFOLIO_METRICS : METRICS;
}
-function retrieveComponentBase(componentKey: string, isPortfolio: boolean, branch?: string) {
+function retrieveComponentBase(
+ componentKey: string,
+ isPortfolio: boolean,
+ branchLike?: BranchLike
+) {
const existing = getComponentFromBucket(componentKey);
if (existing) {
return Promise.resolve(existing);
@@ -141,7 +151,11 @@ function retrieveComponentBase(componentKey: string, isPortfolio: boolean, branc
const metrics = getMetrics(isPortfolio);
- return getComponent(componentKey, metrics, branch).then(component => {
+ return getComponent({
+ componentKey,
+ metricKeys: metrics.join(),
+ ...getBranchLikeQuery(branchLike)
+ }).then(component => {
addComponent(component);
return component;
});
@@ -150,7 +164,7 @@ function retrieveComponentBase(componentKey: string, isPortfolio: boolean, branc
export function retrieveComponentChildren(
componentKey: string,
isPortfolio: boolean,
- branch?: string
+ branchLike?: BranchLike
): Promise<{ components: Component[]; page: number; total: number }> {
const existing = getComponentChildren(componentKey);
if (existing) {
@@ -163,9 +177,13 @@ export function retrieveComponentChildren(
const metrics = getMetrics(isPortfolio);
- return getChildren(componentKey, metrics, { branch, ps: PAGE_SIZE, s: 'qualifier,name' })
+ return getChildren(componentKey, metrics, {
+ ps: PAGE_SIZE,
+ s: 'qualifier,name',
+ ...getBranchLikeQuery(branchLike)
+ })
.then(prepareChildren)
- .then(expandRootDir(metrics, branch))
+ .then(expandRootDir(metrics, branchLike))
.then(r => {
addComponentChildren(componentKey, r.components, r.total, r.page);
storeChildrenBase(r.components);
@@ -175,18 +193,18 @@ export function retrieveComponentChildren(
}
function retrieveComponentBreadcrumbs(
- componentKey: string,
- branch?: string
+ component: string,
+ branchLike?: BranchLike
): Promise<Breadcrumb[]> {
- const existing = getComponentBreadcrumbs(componentKey);
+ const existing = getComponentBreadcrumbs(component);
if (existing) {
return Promise.resolve(existing);
}
- return getBreadcrumbs(componentKey, branch)
+ return getBreadcrumbs({ component, ...getBranchLikeQuery(branchLike) })
.then(skipRootDir)
.then(breadcrumbs => {
- addComponentBreadcrumbs(componentKey, breadcrumbs);
+ addComponentBreadcrumbs(component, breadcrumbs);
return breadcrumbs;
});
}
@@ -194,7 +212,7 @@ function retrieveComponentBreadcrumbs(
export function retrieveComponent(
componentKey: string,
isPortfolio: boolean,
- branch?: string
+ branchLike?: BranchLike
): Promise<{
breadcrumbs: Component[];
component: Component;
@@ -203,9 +221,9 @@ export function retrieveComponent(
total: number;
}> {
return Promise.all([
- retrieveComponentBase(componentKey, isPortfolio, branch),
- retrieveComponentChildren(componentKey, isPortfolio, branch),
- retrieveComponentBreadcrumbs(componentKey, branch)
+ retrieveComponentBase(componentKey, isPortfolio, branchLike),
+ retrieveComponentChildren(componentKey, isPortfolio, branchLike),
+ retrieveComponentBreadcrumbs(componentKey, branchLike)
]).then(r => {
return {
component: r[0],
@@ -221,13 +239,17 @@ export function loadMoreChildren(
componentKey: string,
page: number,
isPortfolio: boolean,
- branch?: string
+ branchLike?: BranchLike
): Promise<Children> {
const metrics = getMetrics(isPortfolio);
- return getChildren(componentKey, metrics, { branch, ps: PAGE_SIZE, p: page })
+ return getChildren(componentKey, metrics, {
+ ps: PAGE_SIZE,
+ p: page,
+ ...getBranchLikeQuery(branchLike)
+ })
.then(prepareChildren)
- .then(expandRootDir(metrics, branch))
+ .then(expandRootDir(metrics, branchLike))
.then(r => {
addComponentChildren(componentKey, r.components, r.total, r.page);
storeChildrenBase(r.components);
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/App.js b/server/sonar-web/src/main/js/apps/component-measures/components/App.js
index 4198c6689fd..d357ff4c15a 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/components/App.js
+++ b/server/sonar-web/src/main/js/apps/component-measures/components/App.js
@@ -26,7 +26,7 @@ import MeasureOverviewContainer from './MeasureOverviewContainer';
import Sidebar from '../sidebar/Sidebar';
import ScreenPositionHelper from '../../../components/common/ScreenPositionHelper';
import { hasBubbleChart, parseQuery, serializeQuery } from '../utils';
-import { getBranchName } from '../../../helpers/branches';
+import { isSameBranchLike, getBranchLikeQuery } from '../../../helpers/branches';
import { translate } from '../../../helpers/l10n';
import { getDisplayMetrics } from '../../../helpers/measures';
/*:: import type { Component, Query, Period } from '../types'; */
@@ -36,14 +36,14 @@ import { getDisplayMetrics } from '../../../helpers/measures';
import '../style.css';
/*:: type Props = {|
- branch?: {},
+ branchLike?: { id?: string; name: string },
component: Component,
currentUser: { isLoggedIn: boolean },
location: { pathname: string, query: RawQuery },
fetchMeasures: (
component: string,
metricsKey: Array<string>,
- branch?: string
+ branchLike?: { id?: string; name: string }
) => Promise<{ component: Component, measures: Array<MeasureEnhanced>, leakPeriod: ?Period }>,
fetchMetrics: () => void,
metrics: { [string]: Metric },
@@ -88,7 +88,7 @@ export default class App extends React.PureComponent {
componentWillReceiveProps(nextProps /*: Props */) {
if (
- nextProps.branch !== this.props.branch ||
+ !isSameBranchLike(nextProps.branchLike, this.props.branchLike) ||
nextProps.component.key !== this.props.component.key ||
nextProps.metrics !== this.props.metrics
) {
@@ -107,10 +107,10 @@ export default class App extends React.PureComponent {
}
}
- fetchMeasures = ({ branch, component, fetchMeasures, metrics } /*: Props */) => {
+ fetchMeasures = ({ branchLike, component, fetchMeasures, metrics } /*: Props */) => {
this.setState({ loading: true });
const filteredKeys = getDisplayMetrics(Object.values(metrics)).map(metric => metric.key);
- fetchMeasures(component.key, filteredKeys, getBranchName(branch)).then(
+ fetchMeasures(component.key, filteredKeys, branchLike).then(
({ measures, leakPeriod }) => {
if (this.mounted) {
this.setState({
@@ -137,7 +137,7 @@ export default class App extends React.PureComponent {
pathname: this.props.location.pathname,
query: {
...query,
- branch: getBranchName(this.props.branch),
+ ...getBranchLikeQuery(this.props.branchLike),
id: this.props.component.key
}
});
@@ -148,7 +148,7 @@ export default class App extends React.PureComponent {
if (isLoading) {
return <i className="spinner spinner-margin" />;
}
- const { branch, component, fetchMeasures, metrics } = this.props;
+ const { branchLike, component, fetchMeasures, metrics } = this.props;
const { leakPeriod } = this.state;
const query = parseQuery(this.props.location.query);
const metric = metrics[query.metric];
@@ -174,7 +174,7 @@ export default class App extends React.PureComponent {
{metric != null && (
<MeasureContentContainer
- branch={getBranchName(branch)}
+ branchLike={branchLike}
className="layout-page-main"
currentUser={this.props.currentUser}
rootComponent={component}
@@ -191,7 +191,7 @@ export default class App extends React.PureComponent {
{metric == null &&
hasBubbleChart(query.metric) && (
<MeasureOverviewContainer
- branch={getBranchName(branch)}
+ branchLike={branchLike}
className="layout-page-main"
rootComponent={component}
currentUser={this.props.currentUser}
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/AppContainer.js b/server/sonar-web/src/main/js/apps/component-measures/components/AppContainer.js
index a9122702894..54d63a4dddd 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/components/AppContainer.js
+++ b/server/sonar-web/src/main/js/apps/component-measures/components/AppContainer.js
@@ -27,6 +27,7 @@ import { fetchMetrics } from '../../../store/rootActions';
import { getMeasuresAndMeta } from '../../../api/measures';
import { getLeakPeriod } from '../../../helpers/periods';
import { enhanceMeasure } from '../../../components/measure/utils';
+import { getBranchLikeQuery } from '../../../helpers/branches';
/*:: import type { Component, Period } from '../types'; */
/*:: import type { Measure, MeasureEnhanced } from '../../../components/measure/types'; */
@@ -50,7 +51,7 @@ function banQualityGate(component /*: Component */) /*: Array<Measure> */ {
const fetchMeasures = (
component /*: string */,
metricsKey /*: Array<string> */,
- branch /*: string | void */
+ branchLike /*: { id?: string; name: string } | void */
) => (dispatch, getState) => {
if (metricsKey.length <= 0) {
return Promise.resolve({ component: {}, measures: [], leakPeriod: null });
@@ -58,7 +59,7 @@ const fetchMeasures = (
return getMeasuresAndMeta(component, metricsKey, {
additionalFields: 'periods',
- branch
+ ...getBranchLikeQuery(branchLike)
}).then(r => {
const measures = banQualityGate(r.component).map(measure =>
enhanceMeasure(measure, getMetrics(getState()))
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/Breadcrumbs.js b/server/sonar-web/src/main/js/apps/component-measures/components/Breadcrumbs.js
index fedd4d52dfe..7709a31d257 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/components/Breadcrumbs.js
+++ b/server/sonar-web/src/main/js/apps/component-measures/components/Breadcrumbs.js
@@ -22,11 +22,12 @@ import React from 'react';
import key from 'keymaster';
import Breadcrumb from './Breadcrumb';
import { getBreadcrumbs } from '../../../api/components';
+import { getBranchLikeQuery } from '../../../helpers/branches';
/*:: import type { Component } from '../types'; */
/*:: type Props = {|
backToFirst: boolean,
- branch?: string,
+ branchLike?: { id?: string, name: string },
className?: string,
component: Component,
handleSelect: string => void,
@@ -76,7 +77,7 @@ export default class Breadcrumbs extends React.PureComponent {
key.unbind('left', 'measures-files');
}
- fetchBreadcrumbs = ({ branch, component, rootComponent } /*: Props */) => {
+ fetchBreadcrumbs = ({ branchLike, component, rootComponent } /*: Props */) => {
const isRoot = component.key === rootComponent.key;
if (isRoot) {
if (this.mounted) {
@@ -84,11 +85,13 @@ export default class Breadcrumbs extends React.PureComponent {
}
return;
}
- getBreadcrumbs(component.key, branch).then(breadcrumbs => {
- if (this.mounted) {
- this.setState({ breadcrumbs });
+ getBreadcrumbs({ component: component.key, ...getBranchLikeQuery(branchLike) }).then(
+ breadcrumbs => {
+ if (this.mounted) {
+ this.setState({ breadcrumbs });
+ }
}
- });
+ );
};
render() {
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContent.js b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContent.js
index a9d9f81c35d..8feb83e1167 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContent.js
+++ b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContent.js
@@ -34,6 +34,7 @@ import { complementary } from '../config/complementary';
import { enhanceComponent, isFileType, isViewType } from '../utils';
import { getProjectUrl } from '../../../helpers/urls';
import { isDiffMetric } from '../../../helpers/measures';
+import { isSameBranchLike, getBranchLikeQuery } from '../../../helpers/branches';
/*:: import type { Component, ComponentEnhanced, Paging, Period } from '../types'; */
/*:: import type { MeasureEnhanced } from '../../../components/measure/types'; */
/*:: import type { Metric } from '../../../store/metrics/actions'; */
@@ -42,7 +43,7 @@ import { isDiffMetric } from '../../../helpers/measures';
// https://github.com/facebook/flow/issues/3147
// router: { push: ({ pathname: string, query?: RawQuery }) => void }
/*:: type Props = {|
- branch?: string,
+ branchLike?: { id?: string; name: string },
className?: string,
component: Component,
currentUser: { isLoggedIn: boolean },
@@ -87,7 +88,7 @@ export default class MeasureContent extends React.PureComponent {
componentWillReceiveProps(nextProps /*: Props */) {
if (
- nextProps.branch !== this.props.branch ||
+ !isSameBranchLike(nextProps.branchLike, this.props.branchLike) ||
nextProps.component !== this.props.component ||
nextProps.metric !== this.props.metric
) {
@@ -115,7 +116,7 @@ export default class MeasureContent extends React.PureComponent {
const strategy = view === 'list' ? 'leaves' : 'children';
const metricKeys = [metric.key];
const opts /*: Object */ = {
- branch: this.props.branch,
+ ...getBranchLikeQuery(this.props.branchLike),
metricSortFilter: 'withMeasuresOnly'
};
const isDiff = isDiffMetric(metric.key);
@@ -225,7 +226,7 @@ export default class MeasureContent extends React.PureComponent {
return (
<div className="measure-details-viewer">
<CodeView
- branch={this.props.branch}
+ branchLike={this.props.branchLike}
component={this.props.component}
components={this.state.components}
leakPeriod={this.props.leakPeriod}
@@ -244,7 +245,7 @@ export default class MeasureContent extends React.PureComponent {
const selectedIdx = this.getSelectedIndex();
return (
<FilesView
- branch={this.props.branch}
+ branchLike={this.props.branchLike}
components={this.state.components}
fetchMore={this.fetchMoreComponents}
handleOpen={this.onOpenComponent}
@@ -261,7 +262,7 @@ export default class MeasureContent extends React.PureComponent {
if (view === 'treemap') {
return (
<TreeMapView
- branch={this.props.branch}
+ branchLike={this.props.branchLike}
components={this.state.components}
handleSelect={this.onOpenComponent}
metric={metric}
@@ -274,7 +275,7 @@ export default class MeasureContent extends React.PureComponent {
}
render() {
- const { branch, component, currentUser, measure, metric, rootComponent, view } = this.props;
+ const { branchLike, component, currentUser, measure, metric, rootComponent, view } = this.props;
const isLoggedIn = currentUser && currentUser.isLoggedIn;
const isFile = isFileType(component);
const selectedIdx = this.getSelectedIndex();
@@ -288,7 +289,7 @@ export default class MeasureContent extends React.PureComponent {
<div className="layout-page-main-inner">
<Breadcrumbs
backToFirst={view === 'list'}
- branch={branch}
+ branchLike={branchLike}
className="measure-breadcrumbs spacer-right text-ellipsis"
component={component}
handleSelect={this.onOpenComponent}
@@ -327,7 +328,7 @@ export default class MeasureContent extends React.PureComponent {
measure != null && (
<div className="layout-page-main-inner measure-details-content">
<MeasureHeader
- branch={branch}
+ branchLike={branchLike}
component={component}
components={this.state.components}
leakPeriod={this.props.leakPeriod}
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContentContainer.js b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContentContainer.js
index beaca1bd6e1..1998460996e 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContentContainer.js
+++ b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContentContainer.js
@@ -26,14 +26,14 @@ import MeasureContent from './MeasureContent';
/*:: import type { RawQuery } from '../../../helpers/query'; */
/*:: type Props = {|
- branch?: string,
+ branchLike?: { id?: string; name: string },
className?: string,
currentUser: { isLoggedIn: boolean },
rootComponent: Component,
fetchMeasures: (
component: string,
metricsKey: Array<string>,
- branch?: string
+ branchLike?: { id?: string; name: string }
) => Promise<{ component: Component, measures: Array<MeasureEnhanced> }>,
leakPeriod?: Period,
metric: Metric,
@@ -89,7 +89,7 @@ export default class MeasureContentContainer extends React.PureComponent {
this.mounted = false;
}
- fetchMeasure = ({ branch, rootComponent, fetchMeasures, metric, selected } /*: Props */) => {
+ fetchMeasure = ({ branchLike, rootComponent, fetchMeasures, metric, selected } /*: Props */) => {
this.updateLoading({ measure: true });
const metricKeys = [metric.key];
@@ -101,7 +101,7 @@ export default class MeasureContentContainer extends React.PureComponent {
metricKeys.push('file_complexity_distribution');
}
- fetchMeasures(selected || rootComponent.key, metricKeys, branch).then(
+ fetchMeasures(selected || rootComponent.key, metricKeys, branchLike).then(
({ component, measures }) => {
if (this.mounted) {
const measure = measures.find(measure => measure.metric.key === metric.key);
@@ -134,7 +134,7 @@ export default class MeasureContentContainer extends React.PureComponent {
return (
<MeasureContent
- branch={this.props.branch}
+ branchLike={this.props.branchLike}
className={this.props.className}
component={this.state.component}
currentUser={this.props.currentUser}
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureFavoriteContainer.js b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureFavoriteContainer.js
index 1690866ffde..f1aac5309b1 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureFavoriteContainer.js
+++ b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureFavoriteContainer.js
@@ -57,7 +57,7 @@ class MeasureFavoriteContainer extends React.PureComponent {
}
fetchComponentFavorite({ component, onReceiveComponent } /*: Props */) {
- getComponentForSourceViewer(component).then(component => {
+ getComponentForSourceViewer({ component }).then(component => {
this.setState({ component });
onReceiveComponent(component);
});
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureHeader.js b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureHeader.js
index 87c543aaa76..5c809374492 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureHeader.js
+++ b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureHeader.js
@@ -34,7 +34,7 @@ import { isDiffMetric } from '../../../helpers/measures';
/*:: import type { MeasureEnhanced } from '../../../components/measure/types'; */
/*:: type Props = {|
- branch?: string,
+ branchLike?: { id?: string; name: string },
component: Component,
components: Array<Component>,
leakPeriod?: Period,
@@ -43,7 +43,7 @@ import { isDiffMetric } from '../../../helpers/measures';
|}; */
export default function MeasureHeader(props /*: Props*/) {
- const { branch, component, leakPeriod, measure, secondaryMeasure } = props;
+ const { branchLike, component, leakPeriod, measure, secondaryMeasure } = props;
const { metric } = measure;
const isDiff = isDiffMetric(metric.key);
return (
@@ -72,7 +72,7 @@ export default function MeasureHeader(props /*: Props*/) {
overlay={translate('component_measures.show_metric_history')}>
<Link
className="js-show-history spacer-left button button-small"
- to={getMeasureHistoryUrl(component.key, metric.key, branch)}>
+ to={getMeasureHistoryUrl(component.key, metric.key, branchLike)}>
<HistoryIcon />
</Link>
</Tooltip>
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverview.js b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverview.js
index a5e79b7a134..0128f7b8b1f 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverview.js
+++ b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverview.js
@@ -27,11 +27,12 @@ import BubbleChart from '../drilldown/BubbleChart';
import SourceViewer from '../../../components/SourceViewer/SourceViewer';
import { getComponentLeaves } from '../../../api/components';
import { enhanceComponent, getBubbleMetrics, isFileType } from '../utils';
+import { getBranchLikeQuery } from '../../../helpers/branches';
/*:: import type { Component, ComponentEnhanced, Paging, Period } from '../types'; */
/*:: import type { Metric } from '../../../store/metrics/actions'; */
/*:: type Props = {|
- branch?: string,
+ branchLike?: { id?: string; name: string },
className?: string,
component: Component,
currentUser: { isLoggedIn: boolean },
@@ -79,9 +80,10 @@ export default class MeasureOverview extends React.PureComponent {
}
fetchComponents = (props /*: Props */) => {
- const { branch, component, domain, metrics } = props;
+ const { branchLike, component, domain, metrics } = props;
if (isFileType(component)) {
- return this.setState({ components: [], paging: null });
+ this.setState({ components: [], paging: null });
+ return;
}
const { x, y, size, colors } = getBubbleMetrics(domain, metrics);
const metricsKey = [x.key, y.key, size.key];
@@ -89,7 +91,7 @@ export default class MeasureOverview extends React.PureComponent {
metricsKey.push(colors.map(metric => metric.key));
}
const options = {
- branch,
+ ...getBranchLikeQuery(branchLike),
s: 'metric',
metricSort: size.key,
asc: false,
@@ -114,11 +116,11 @@ export default class MeasureOverview extends React.PureComponent {
};
renderContent() {
- const { branch, component } = this.props;
+ const { branchLike, component } = this.props;
if (isFileType(component)) {
return (
<div className="measure-details-viewer">
- <SourceViewer branch={branch} component={component.key} />
+ <SourceViewer branchLike={branchLike} component={component.key} />
</div>
);
}
@@ -135,7 +137,7 @@ export default class MeasureOverview extends React.PureComponent {
}
render() {
- const { branch, component, currentUser, leakPeriod, rootComponent } = this.props;
+ const { branchLike, component, currentUser, leakPeriod, rootComponent } = this.props;
const isLoggedIn = currentUser && currentUser.isLoggedIn;
const isFile = isFileType(component);
return (
@@ -145,7 +147,7 @@ export default class MeasureOverview extends React.PureComponent {
<div className="layout-page-main-inner">
<Breadcrumbs
backToFirst={true}
- branch={branch}
+ branchLike={branchLike}
className="measure-breadcrumbs spacer-right text-ellipsis"
component={component}
handleSelect={this.props.updateSelected}
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverviewContainer.js b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverviewContainer.js
index 7268b08f308..b69a9a8378f 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverviewContainer.js
+++ b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverviewContainer.js
@@ -23,12 +23,13 @@ import MeasureOverview from './MeasureOverview';
import { getComponentShow } from '../../../api/components';
import { getProjectUrl } from '../../../helpers/urls';
import { isViewType } from '../utils';
+import { getBranchLikeQuery } from '../../../helpers/branches';
/*:: import type { Component, Period, Query } from '../types'; */
/*:: import type { RawQuery } from '../../../helpers/query'; */
/*:: import type { Metric } from '../../../store/metrics/actions'; */
/*:: type Props = {|
- branch?: string,
+ branchLike?: { id?: string; name: string },
className?: string,
rootComponent: Component,
currentUser: { isLoggedIn: boolean },
@@ -81,14 +82,14 @@ export default class MeasureOverviewContainer extends React.PureComponent {
this.mounted = false;
}
- fetchComponent = ({ branch, rootComponent, selected } /*: Props */) => {
+ fetchComponent = ({ branchLike, rootComponent, selected } /*: Props */) => {
if (!selected || rootComponent.key === selected) {
this.setState({ component: rootComponent });
this.updateLoading({ component: false });
return;
}
this.updateLoading({ component: true });
- getComponentShow(selected, branch).then(
+ getComponentShow({ component: selected, ...getBranchLikeQuery(branchLike) }).then(
({ component }) => {
if (this.mounted) {
this.setState({ component });
@@ -122,7 +123,7 @@ export default class MeasureOverviewContainer extends React.PureComponent {
return (
<MeasureOverview
- branch={this.props.branch}
+ branchLike={this.props.branchLike}
className={this.props.className}
component={this.state.component}
currentUser={this.props.currentUser}
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/MeasureHeader-test.js b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/MeasureHeader-test.js
index dfa9a644f24..5b02e3d87fb 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/MeasureHeader-test.js
+++ b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/MeasureHeader-test.js
@@ -77,7 +77,10 @@ it('should render correctly for leak', () => {
});
it('should render with branch', () => {
- expect(shallow(<MeasureHeader branch="feature" {...PROPS} />).find('Link')).toMatchSnapshot();
+ const shortBranch = { isMain: false, name: 'feature', mergeBranch: '', type: 'SHORT' };
+ expect(
+ shallow(<MeasureHeader branchLike={shortBranch} {...PROPS} />).find('Link')
+ ).toMatchSnapshot();
});
it('should display secondary measure too', () => {
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/MeasureHeader-test.js.snap b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/MeasureHeader-test.js.snap
index d6c9bceb55a..8da497107c3 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/MeasureHeader-test.js.snap
+++ b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/MeasureHeader-test.js.snap
@@ -86,7 +86,6 @@ exports[`should render correctly 1`] = `
Object {
"pathname": "/project/activity",
"query": Object {
- "branch": undefined,
"custom_metrics": "reliability_rating",
"graph": "custom",
"id": "foo",
diff --git a/server/sonar-web/src/main/js/apps/component-measures/drilldown/CodeView.js b/server/sonar-web/src/main/js/apps/component-measures/drilldown/CodeView.js
index 5270c221842..1c0747851f1 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/drilldown/CodeView.js
+++ b/server/sonar-web/src/main/js/apps/component-measures/drilldown/CodeView.js
@@ -25,7 +25,7 @@ import SourceViewer from '../../../components/SourceViewer/SourceViewer';
/*:: import type { Metric } from '../../../store/metrics/actions'; */
/*:: type Props = {|
- branch?: string,
+ branchLike?: { id?: string; name: string },
component: ComponentEnhanced,
components: Array<ComponentEnhanced>,
leakPeriod?: Period,
@@ -81,7 +81,7 @@ export default class CodeView extends React.PureComponent {
};
render() {
- const { branch, component } = this.props;
- return <SourceViewer branch={branch} component={component.key} />;
+ const { branchLike, component } = this.props;
+ return <SourceViewer branchLike={branchLike} component={component.key} />;
}
}
diff --git a/server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentCell.js b/server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentCell.js
index 033a8bcf66b..248b6469e2b 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentCell.js
+++ b/server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentCell.js
@@ -23,11 +23,11 @@ import { Link } from 'react-router';
import LinkIcon from '../../../components/icons-components/LinkIcon';
import QualifierIcon from '../../../components/icons-components/QualifierIcon';
import { splitPath } from '../../../helpers/path';
-import { getPathUrlAsString, getProjectUrl } from '../../../helpers/urls';
+import { getPathUrlAsString, getBranchLikeUrl } from '../../../helpers/urls';
/*:: import type { ComponentEnhanced } from '../types'; */
/*:: type Props = {
- branch?: string,
+ branchLike?: { id?: string; name: string },
component: ComponentEnhanced,
onClick: string => void
}; */
@@ -65,15 +65,16 @@ export default class ComponentCell extends React.PureComponent {
}
render() {
- const { branch, component } = this.props;
+ const { branchLike, component } = this.props;
return (
<td className="measure-details-component-cell">
<div className="text-ellipsis">
+ {/* TODO make this <a> link a react-router <Link /> */}
{component.refKey == null ? (
<a
id={'component-measures-component-link-' + component.key}
className="link-no-underline"
- href={getPathUrlAsString(getProjectUrl(component.key, branch))}
+ href={getPathUrlAsString(getBranchLikeUrl(component.key, branchLike))}
onClick={this.handleClick}>
{this.renderInner()}
</a>
@@ -81,7 +82,7 @@ export default class ComponentCell extends React.PureComponent {
<Link
className="link-no-underline"
id={'component-measures-component-link-' + component.key}
- to={getProjectUrl(component.refKey, branch)}>
+ to={getBranchLikeUrl(component.refKey, branchLike)}>
<span className="big-spacer-right">
<LinkIcon />
</span>
diff --git a/server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentsList.js b/server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentsList.js
index f2b45b73878..0e29a704f0c 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentsList.js
+++ b/server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentsList.js
@@ -27,7 +27,7 @@ import { getLocalizedMetricName } from '../../../helpers/l10n';
/*:: import type { Metric } from '../../../store/metrics/actions'; */
/*:: type Props = {|
- branch?: string,
+ branchLike?: { id?: string; name: string },
components: Array<ComponentEnhanced>,
onClick: string => void,
metric: Metric,
@@ -36,7 +36,7 @@ import { getLocalizedMetricName } from '../../../helpers/l10n';
|}; */
export default function ComponentsList(
- { branch, components, onClick, metrics, metric, selectedComponent } /*: Props */
+ { branchLike, components, onClick, metrics, metric, selectedComponent } /*: Props */
) {
if (!components.length) {
return <EmptyResult />;
@@ -65,7 +65,7 @@ export default function ComponentsList(
{components.map(component => (
<ComponentsListRow
key={component.id}
- branch={branch}
+ branchLike={branchLike}
component={component}
otherMetrics={otherMetrics}
isSelected={component.key === selectedComponent}
diff --git a/server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentsListRow.js b/server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentsListRow.js
index e87b224967f..47a5d2bf29a 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentsListRow.js
+++ b/server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentsListRow.js
@@ -26,7 +26,7 @@ import MeasureCell from './MeasureCell';
/*:: import type { Metric } from '../../../store/metrics/actions'; */
/*:: type Props = {|
- branch?: string,
+ branchLike?: { id?: string; name: string },
component: ComponentEnhanced,
isSelected: boolean,
onClick: string => void,
@@ -35,7 +35,7 @@ import MeasureCell from './MeasureCell';
|}; */
export default function ComponentsListRow(props /*: Props */) {
- const { branch, component } = props;
+ const { branchLike, component } = props;
const otherMeasures = props.otherMetrics.map(metric => {
const measure = component.measures.find(measure => measure.metric.key === metric.key);
return { ...measure, metric };
@@ -45,7 +45,7 @@ export default function ComponentsListRow(props /*: Props */) {
});
return (
<tr className={rowClass}>
- <ComponentCell branch={branch} component={component} onClick={props.onClick} />
+ <ComponentCell branchLike={branchLike} component={component} onClick={props.onClick} />
<MeasureCell component={component} metric={props.metric} />
diff --git a/server/sonar-web/src/main/js/apps/component-measures/drilldown/FilesView.js b/server/sonar-web/src/main/js/apps/component-measures/drilldown/FilesView.js
index 9a0ffde8c05..2fd8d7feb27 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/drilldown/FilesView.js
+++ b/server/sonar-web/src/main/js/apps/component-measures/drilldown/FilesView.js
@@ -28,7 +28,7 @@ import { scrollToElement } from '../../../helpers/scrolling';
/*:: import type { Metric } from '../../../store/metrics/actions'; */
/*:: type Props = {|
- branch?: string,
+ branchLike?: { id?: string; name: string },
components: Array<ComponentEnhanced>,
fetchMore: () => void,
handleSelect: string => void,
@@ -123,7 +123,7 @@ export default class ListView extends React.PureComponent {
return (
<div ref={elem => (this.listContainer = elem)}>
<ComponentsList
- branch={this.props.branch}
+ branchLike={this.props.branchLike}
components={this.props.components}
metrics={this.props.metrics}
metric={this.props.metric}
diff --git a/server/sonar-web/src/main/js/apps/component-measures/drilldown/TreeMapView.js b/server/sonar-web/src/main/js/apps/component-measures/drilldown/TreeMapView.js
index 3892abf2689..1dd3fd68f26 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/drilldown/TreeMapView.js
+++ b/server/sonar-web/src/main/js/apps/component-measures/drilldown/TreeMapView.js
@@ -29,13 +29,13 @@ import QualifierIcon from '../../../components/icons-components/QualifierIcon';
import TreeMap from '../../../components/charts/TreeMap';
import { translate, translateWithParameters, getLocalizedMetricName } from '../../../helpers/l10n';
import { formatMeasure, isDiffMetric } from '../../../helpers/measures';
-import { getProjectUrl } from '../../../helpers/urls';
+import { getBranchLikeUrl } from '../../../helpers/urls';
/*:: import type { Metric } from '../../../store/metrics/actions'; */
/*:: import type { ComponentEnhanced } from '../types'; */
/*:: import type { TreeMapItem } from '../../../components/charts/TreeMap'; */
/*:: type Props = {|
- branch?: string,
+ branchLike?: { id?: string; name: string },
components: Array<ComponentEnhanced>,
handleSelect: string => void,
metric: Metric
@@ -64,7 +64,7 @@ export default class TreeMapView extends React.PureComponent {
}
}
- getTreemapComponents = ({ branch, components, metric } /*: Props */) => {
+ getTreemapComponents = ({ branchLike, components, metric } /*: Props */) => {
const colorScale = this.getColorScale(metric);
return components
.map(component => {
@@ -95,7 +95,7 @@ export default class TreeMapView extends React.PureComponent {
sizeValue
),
label: component.name,
- link: getProjectUrl(component.refKey || component.key, branch)
+ link: getBranchLikeUrl(component.refKey || component.key, branchLike)
};
})
.filter(Boolean);
diff --git a/server/sonar-web/src/main/js/apps/component/components/App.tsx b/server/sonar-web/src/main/js/apps/component/components/App.tsx
index 96ff380b906..71fc859ea2f 100644
--- a/server/sonar-web/src/main/js/apps/component/components/App.tsx
+++ b/server/sonar-web/src/main/js/apps/component/components/App.tsx
@@ -18,6 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
+import { PullRequest, BranchType, ShortLivingBranch } from '../../../app/types';
import SourceViewer from '../../../components/SourceViewer/SourceViewer';
interface Props {
@@ -26,6 +27,7 @@ interface Props {
branch?: string;
id: string;
line?: string;
+ pullRequest?: string;
};
};
}
@@ -45,15 +47,30 @@ export default class App extends React.PureComponent<Props> {
};
render() {
- const { branch, id, line } = this.props.location.query;
+ const { branch, id, line, pullRequest } = this.props.location.query;
const finalLine = line ? Number(line) : undefined;
+ // TODO find a way to avoid creating this fakeBranchLike
+ // probably the best way would be to drop this page completely
+ // and redirect to the Code page
+ let fakeBranchLike: ShortLivingBranch | PullRequest | undefined = undefined;
+ if (branch) {
+ fakeBranchLike = {
+ isMain: false,
+ mergeBranch: '',
+ name: branch,
+ type: BranchType.SHORT
+ } as ShortLivingBranch;
+ } else if (pullRequest) {
+ fakeBranchLike = { base: '', branch: '', key: pullRequest, title: '' } as PullRequest;
+ }
+
return (
<div className="page page-limited">
<SourceViewer
aroundLine={finalLine}
- branch={branch}
+ branchLike={fakeBranchLike}
component={id}
highlightedLine={finalLine}
onLoaded={this.scrollToLine}
diff --git a/server/sonar-web/src/main/js/apps/component/components/__tests__/__snapshots__/App-test.tsx.snap b/server/sonar-web/src/main/js/apps/component/components/__tests__/__snapshots__/App-test.tsx.snap
index 1cb5c653533..f5dd7e1a6ce 100644
--- a/server/sonar-web/src/main/js/apps/component/components/__tests__/__snapshots__/App-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/component/components/__tests__/__snapshots__/App-test.tsx.snap
@@ -6,7 +6,14 @@ exports[`renders 1`] = `
>
<Connect(SourceViewerBase)
aroundLine={7}
- branch="b"
+ branchLike={
+ Object {
+ "isMain": false,
+ "mergeBranch": "",
+ "name": "b",
+ "type": "SHORT",
+ }
+ }
component="foo"
highlightedLine={7}
onLoaded={[Function]}
diff --git a/server/sonar-web/src/main/js/apps/issues/components/App.d.ts b/server/sonar-web/src/main/js/apps/issues/components/App.d.ts
index 9bafb845242..211061e58bd 100644
--- a/server/sonar-web/src/main/js/apps/issues/components/App.d.ts
+++ b/server/sonar-web/src/main/js/apps/issues/components/App.d.ts
@@ -18,11 +18,11 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import { Component, CurrentUser } from '../../../app/types';
+import { Component, CurrentUser, BranchLike } from '../../../app/types';
import { RawQuery } from '../../../helpers/query';
interface Props {
- branch?: { name: string };
+ branchLike?: BranchLike;
component?: Component;
currentUser: CurrentUser;
fetchIssues: (query: RawQuery, requestOrganizations?: boolean) => Promise<any>;
diff --git a/server/sonar-web/src/main/js/apps/issues/components/App.js b/server/sonar-web/src/main/js/apps/issues/components/App.js
index bc6cf9826ff..85bf0fa458e 100644
--- a/server/sonar-web/src/main/js/apps/issues/components/App.js
+++ b/server/sonar-web/src/main/js/apps/issues/components/App.js
@@ -59,7 +59,12 @@ import ListFooter from '../../../components/controls/ListFooter';
import EmptySearch from '../../../components/common/EmptySearch';
import FiltersHeader from '../../../components/common/FiltersHeader';
import ScreenPositionHelper from '../../../components/common/ScreenPositionHelper';
-import { getBranchName, isShortLivingBranch } from '../../../helpers/branches';
+import {
+ isShortLivingBranch,
+ isSameBranchLike,
+ getBranchLikeQuery,
+ isPullRequest
+} from '../../../helpers/branches';
import { translate, translateWithParameters } from '../../../helpers/l10n';
import { scrollToElement } from '../../../helpers/scrolling';
import Checkbox from '../../../components/controls/Checkbox';
@@ -69,7 +74,7 @@ import '../styles.css';
/*::
export type Props = {
- branch?: { name: string },
+ branchLike?: { id?: string; name: string },
component?: Component,
currentUser: CurrentUser,
fetchIssues: (query: RawQuery, requestOrganizations?: boolean) => Promise<*>,
@@ -193,7 +198,7 @@ export default class App extends React.PureComponent {
const { query: prevQuery } = prevProps.location;
if (
prevProps.component !== this.props.component ||
- prevProps.branch !== this.props.branch ||
+ !isSameBranchLike(prevProps.branchLike, this.props.branchLike) ||
!areQueriesEqual(prevQuery, query) ||
areMyIssuesSelected(prevQuery) !== areMyIssuesSelected(query)
) {
@@ -337,7 +342,7 @@ export default class App extends React.PureComponent {
pathname: this.props.location.pathname,
query: {
...serializeQuery(this.state.query),
- branch: getBranchName(this.props.branch),
+ ...getBranchLikeQuery(this.props.branchLike),
id: this.props.component && this.props.component.key,
myIssues: this.state.myIssues ? 'true' : undefined,
open: issue
@@ -356,7 +361,7 @@ export default class App extends React.PureComponent {
pathname: this.props.location.pathname,
query: {
...serializeQuery(this.state.query),
- branch: getBranchName(this.props.branch),
+ ...getBranchLikeQuery(this.props.branchLike),
id: this.props.component && this.props.component.key,
myIssues: this.state.myIssues ? 'true' : undefined,
open: undefined
@@ -399,7 +404,7 @@ export default class App extends React.PureComponent {
: undefined;
const parameters = {
- branch: getBranchName(this.props.branch),
+ ...getBranchLikeQuery(this.props.branchLike),
componentKeys: component && component.key,
s: 'FILE_LINE',
...serializeQuery(query),
@@ -594,7 +599,7 @@ export default class App extends React.PureComponent {
pathname: this.props.location.pathname,
query: {
...serializeQuery({ ...this.state.query, ...changes }),
- branch: getBranchName(this.props.branch),
+ ...getBranchLikeQuery(this.props.branchLike),
id: this.props.component && this.props.component.key,
myIssues: this.state.myIssues ? 'true' : undefined
}
@@ -610,7 +615,7 @@ export default class App extends React.PureComponent {
pathname: this.props.location.pathname,
query: {
...serializeQuery({ ...this.state.query, assigned: true, assignees: [] }),
- branch: getBranchName(this.props.branch),
+ ...getBranchLikeQuery(this.props.branchLike),
id: this.props.component && this.props.component.key,
myIssues: myIssues ? 'true' : undefined
}
@@ -637,7 +642,7 @@ export default class App extends React.PureComponent {
pathname: this.props.location.pathname,
query: {
...DEFAULT_QUERY,
- branch: getBranchName(this.props.branch),
+ ...getBranchLikeQuery(this.props.branchLike),
id: this.props.component && this.props.component.key,
myIssues: this.state.myIssues ? 'true' : undefined
}
@@ -724,7 +729,7 @@ export default class App extends React.PureComponent {
handleReload = () => {
this.fetchFirstIssues();
- if (isShortLivingBranch(this.props.branch)) {
+ if (isShortLivingBranch(this.props.branchLike) || isPullRequest(this.props.branchLike)) {
this.props.onBranchesChange();
}
};
@@ -892,7 +897,7 @@ export default class App extends React.PureComponent {
}
renderList() {
- const { branch, component, currentUser, organization } = this.props;
+ const { branchLike, component, currentUser, organization } = this.props;
const { issues, openIssue, paging } = this.state;
const selectedIndex = this.getSelectedIndex();
const selectedIssue = selectedIndex != null ? issues[selectedIndex] : null;
@@ -905,7 +910,7 @@ export default class App extends React.PureComponent {
<div>
{paging.total > 0 && (
<IssuesList
- branch={getBranchName(branch)}
+ branchLike={branchLike}
checked={this.state.checked}
component={component}
issues={issues}
@@ -971,7 +976,7 @@ export default class App extends React.PureComponent {
{openIssue != null ? (
<div className="pull-left width-60">
<ComponentBreadcrumbs
- branch={getBranchName(this.props.branch)}
+ branchLike={this.props.branchLike}
component={component}
issue={openIssue}
organization={this.props.organization}
@@ -1000,7 +1005,7 @@ export default class App extends React.PureComponent {
<div>
{openIssue ? (
<IssuesSourceViewer
- branch={getBranchName(this.props.branch)}
+ branchLike={this.props.branchLike}
component={component}
openIssue={openIssue}
loadIssues={this.fetchIssuesForComponent}
diff --git a/server/sonar-web/src/main/js/apps/issues/components/ComponentBreadcrumbs.tsx b/server/sonar-web/src/main/js/apps/issues/components/ComponentBreadcrumbs.tsx
index 9da37298c41..c86ec19b057 100644
--- a/server/sonar-web/src/main/js/apps/issues/components/ComponentBreadcrumbs.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/components/ComponentBreadcrumbs.tsx
@@ -21,11 +21,11 @@ import * as React from 'react';
import { Link } from 'react-router';
import Organization from '../../../components/shared/Organization';
import { collapsePath, limitComponentName } from '../../../helpers/path';
-import { getProjectUrl } from '../../../helpers/urls';
-import { Component } from '../../../app/types';
+import { getBranchLikeUrl, getCodeUrl } from '../../../helpers/urls';
+import { Component, BranchLike } from '../../../app/types';
interface Props {
- branch?: string;
+ branchLike?: BranchLike;
component?: Component;
issue: {
component: string;
@@ -39,7 +39,12 @@ interface Props {
organization?: { key: string };
}
-export default function ComponentBreadcrumbs({ branch, component, issue, organization }: Props) {
+export default function ComponentBreadcrumbs({
+ branchLike,
+ component,
+ issue,
+ organization
+}: Props) {
const displayOrganization =
!organization && (component == null || ['VW', 'SVW'].includes(component.qualifier));
const displayProject = component == null || !['TRK', 'BRC', 'DIR'].includes(component.qualifier);
@@ -53,7 +58,7 @@ export default function ComponentBreadcrumbs({ branch, component, issue, organiz
{displayProject && (
<span title={issue.projectName}>
- <Link to={getProjectUrl(issue.project, branch)} className="link-no-underline">
+ <Link to={getBranchLikeUrl(issue.project, branchLike)} className="link-no-underline">
{limitComponentName(issue.projectName)}
</Link>
<span className="slash-separator" />
@@ -64,14 +69,16 @@ export default function ComponentBreadcrumbs({ branch, component, issue, organiz
issue.subProject !== undefined &&
issue.subProjectName !== undefined && (
<span title={issue.subProjectName}>
- <Link to={getProjectUrl(issue.subProject, branch)} className="link-no-underline">
+ <Link to={getBranchLikeUrl(issue.subProject, branchLike)} className="link-no-underline">
{limitComponentName(issue.subProjectName)}
</Link>
<span className="slash-separator" />
</span>
)}
- <Link to={getProjectUrl(issue.component, branch)} className="link-no-underline">
+ <Link
+ to={getCodeUrl(issue.project, branchLike, issue.component)}
+ className="link-no-underline">
<span title={issue.componentLongName}>{collapsePath(issue.componentLongName)}</span>
</Link>
</div>
diff --git a/server/sonar-web/src/main/js/apps/issues/components/IssuesList.js b/server/sonar-web/src/main/js/apps/issues/components/IssuesList.js
index 30506292162..c7d6bc2e650 100644
--- a/server/sonar-web/src/main/js/apps/issues/components/IssuesList.js
+++ b/server/sonar-web/src/main/js/apps/issues/components/IssuesList.js
@@ -25,7 +25,7 @@ import ListItem from './ListItem';
/*::
type Props = {|
- branch?: string,
+ branchLike?: { id?: string; name: string },
checked: Array<string>,
component?: Component,
issues: Array<Issue>,
@@ -44,13 +44,13 @@ export default class IssuesList extends React.PureComponent {
/*:: props: Props; */
render() {
- const { branch, checked, component, issues, openPopup, selectedIssue } = this.props;
+ const { branchLike, checked, component, issues, openPopup, selectedIssue } = this.props;
return (
<div>
{issues.map((issue, index) => (
<ListItem
- branch={branch}
+ branchLike={branchLike}
checked={checked.includes(issue.key)}
component={component}
key={issue.key}
diff --git a/server/sonar-web/src/main/js/apps/issues/components/IssuesSourceViewer.js b/server/sonar-web/src/main/js/apps/issues/components/IssuesSourceViewer.js
index 6f7422c2290..fb72e2eca3b 100644
--- a/server/sonar-web/src/main/js/apps/issues/components/IssuesSourceViewer.js
+++ b/server/sonar-web/src/main/js/apps/issues/components/IssuesSourceViewer.js
@@ -26,7 +26,7 @@ import { scrollToElement } from '../../../helpers/scrolling';
/*::
type Props = {|
- branch?: string,
+ branchLike?: { id?: string; name: string },
component: Component,
loadIssues: (string, number, number) => Promise<*>,
onIssueChange: Issue => void,
@@ -107,7 +107,7 @@ export default class IssuesSourceViewer extends React.PureComponent {
<div ref={node => (this.node = node)}>
<SourceViewer
aroundLine={aroundLine}
- branch={this.props.branch}
+ branchLike={this.props.branchLike}
component={openIssue.component}
displayAllIssues={true}
displayIssueLocationsCount={false}
diff --git a/server/sonar-web/src/main/js/apps/issues/components/ListItem.js b/server/sonar-web/src/main/js/apps/issues/components/ListItem.js
index 28c6ae69760..e4802fc64a3 100644
--- a/server/sonar-web/src/main/js/apps/issues/components/ListItem.js
+++ b/server/sonar-web/src/main/js/apps/issues/components/ListItem.js
@@ -26,7 +26,7 @@ import Issue from '../../../components/issue/Issue';
/*::
type Props = {|
- branch?: string,
+ branchLike?: { id?: string; name: string },
checked: boolean,
component?: Component,
issue: IssueType,
@@ -89,7 +89,7 @@ export default class ListItem extends React.PureComponent {
};
render() {
- const { branch, component, issue, previousIssue } = this.props;
+ const { branchLike, component, issue, previousIssue } = this.props;
const displayComponent = previousIssue == null || previousIssue.component !== issue.component;
@@ -98,7 +98,7 @@ export default class ListItem extends React.PureComponent {
{displayComponent && (
<div className="issues-workspace-list-component">
<ComponentBreadcrumbs
- branch={branch}
+ branchLike={branchLike}
component={component}
issue={this.props.issue}
organization={this.props.organization}
@@ -106,7 +106,7 @@ export default class ListItem extends React.PureComponent {
</div>
)}
<Issue
- branch={branch}
+ branchLike={branchLike}
checked={this.props.checked}
displayLocationsLink={false}
issue={issue}
diff --git a/server/sonar-web/src/main/js/apps/issues/components/__tests__/ComponentBreadcrumbs-test.tsx b/server/sonar-web/src/main/js/apps/issues/components/__tests__/ComponentBreadcrumbs-test.tsx
index 3bee431c81c..00c83d10dcd 100644
--- a/server/sonar-web/src/main/js/apps/issues/components/__tests__/ComponentBreadcrumbs-test.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/components/__tests__/ComponentBreadcrumbs-test.tsx
@@ -20,6 +20,7 @@
import * as React from 'react';
import { shallow } from 'enzyme';
import ComponentBreadcrumbs from '../ComponentBreadcrumbs';
+import { ShortLivingBranch, BranchType } from '../../../../app/types';
const baseIssue = {
component: 'comp',
@@ -40,5 +41,13 @@ it('renders with sub-project', () => {
it('renders with branch', () => {
const issue = { ...baseIssue, subProject: 'sub-proj', subProjectName: 'sub-proj-name' };
- expect(shallow(<ComponentBreadcrumbs branch="feature" issue={issue} />)).toMatchSnapshot();
+ const shortBranch: ShortLivingBranch = {
+ isMain: false,
+ mergeBranch: '',
+ name: 'feature',
+ type: BranchType.SHORT
+ };
+ expect(
+ shallow(<ComponentBreadcrumbs branchLike={shortBranch} issue={issue} />)
+ ).toMatchSnapshot();
});
diff --git a/server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/ComponentBreadcrumbs-test.tsx.snap b/server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/ComponentBreadcrumbs-test.tsx.snap
index 3861f4a0780..ac510c72824 100644
--- a/server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/ComponentBreadcrumbs-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/ComponentBreadcrumbs-test.tsx.snap
@@ -19,7 +19,6 @@ exports[`renders 1`] = `
Object {
"pathname": "/dashboard",
"query": Object {
- "branch": undefined,
"id": "proj",
},
}
@@ -37,10 +36,10 @@ exports[`renders 1`] = `
style={Object {}}
to={
Object {
- "pathname": "/dashboard",
+ "pathname": "/code",
"query": Object {
- "branch": undefined,
- "id": "comp",
+ "id": "proj",
+ "selected": "comp",
},
}
}
@@ -71,10 +70,11 @@ exports[`renders with branch 1`] = `
style={Object {}}
to={
Object {
- "pathname": "/dashboard",
+ "pathname": "/project/issues",
"query": Object {
"branch": "feature",
"id": "proj",
+ "resolved": "false",
},
}
}
@@ -94,10 +94,11 @@ exports[`renders with branch 1`] = `
style={Object {}}
to={
Object {
- "pathname": "/dashboard",
+ "pathname": "/project/issues",
"query": Object {
"branch": "feature",
"id": "sub-proj",
+ "resolved": "false",
},
}
}
@@ -114,10 +115,11 @@ exports[`renders with branch 1`] = `
style={Object {}}
to={
Object {
- "pathname": "/dashboard",
+ "pathname": "/code",
"query": Object {
"branch": "feature",
- "id": "comp",
+ "id": "proj",
+ "selected": "comp",
},
}
}
@@ -150,7 +152,6 @@ exports[`renders with sub-project 1`] = `
Object {
"pathname": "/dashboard",
"query": Object {
- "branch": undefined,
"id": "proj",
},
}
@@ -173,7 +174,6 @@ exports[`renders with sub-project 1`] = `
Object {
"pathname": "/dashboard",
"query": Object {
- "branch": undefined,
"id": "sub-proj",
},
}
@@ -191,10 +191,10 @@ exports[`renders with sub-project 1`] = `
style={Object {}}
to={
Object {
- "pathname": "/dashboard",
+ "pathname": "/code",
"query": Object {
- "branch": undefined,
- "id": "comp",
+ "id": "proj",
+ "selected": "comp",
},
}
}
diff --git a/server/sonar-web/src/main/js/apps/overview/badges/BadgesModal.tsx b/server/sonar-web/src/main/js/apps/overview/badges/BadgesModal.tsx
index 3592c4daea3..ebea71b75a6 100644
--- a/server/sonar-web/src/main/js/apps/overview/badges/BadgesModal.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/badges/BadgesModal.tsx
@@ -21,15 +21,16 @@ import * as React from 'react';
import BadgeButton from './BadgeButton';
import BadgeParams from './BadgeParams';
import { BadgeType, BadgeOptions, getBadgeUrl } from './utils';
-import { Metric } from '../../../app/types';
+import { Metric, BranchLike } from '../../../app/types';
import CodeSnippet from '../../../components/common/CodeSnippet';
import Modal from '../../../components/controls/Modal';
+import { getBranchLikeQuery } from '../../../helpers/branches';
import { translate } from '../../../helpers/l10n';
import './styles.css';
import { Button, ResetButtonLink } from '../../../components/ui/buttons';
interface Props {
- branch?: string;
+ branchLike?: BranchLike;
metrics: { [key: string]: Metric };
project: string;
}
@@ -64,10 +65,10 @@ export default class BadgesModal extends React.PureComponent<Props, State> {
};
render() {
- const { branch, project } = this.props;
+ const { branchLike, project } = this.props;
const { selectedType, badgeOptions } = this.state;
const header = translate('overview.badges.title');
- const fullBadgeOptions = { branch, project, ...badgeOptions };
+ const fullBadgeOptions = { project, ...badgeOptions, ...getBranchLikeQuery(branchLike) };
return (
<div className="overview-meta-card">
<Button className="js-project-badges" onClick={this.handleOpen}>
diff --git a/server/sonar-web/src/main/js/apps/overview/badges/__tests__/BadgesModal-test.tsx b/server/sonar-web/src/main/js/apps/overview/badges/__tests__/BadgesModal-test.tsx
index cea857f2e90..d77c077d6d9 100644
--- a/server/sonar-web/src/main/js/apps/overview/badges/__tests__/BadgesModal-test.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/badges/__tests__/BadgesModal-test.tsx
@@ -21,13 +21,20 @@ import * as React from 'react';
import { shallow } from 'enzyme';
import BadgesModal from '../BadgesModal';
import { click } from '../../../../helpers/testUtils';
+import { ShortLivingBranch, BranchType } from '../../../../app/types';
jest.mock('../../../../helpers/urls', () => ({
getHostUrl: () => 'host'
}));
it('should display the modal after click', () => {
- const wrapper = shallow(<BadgesModal branch="branch-6.6" metrics={{}} project="foo" />);
+ const shortBranch: ShortLivingBranch = {
+ isMain: false,
+ mergeBranch: '',
+ name: 'branch-6.6',
+ type: BranchType.SHORT
+ };
+ const wrapper = shallow(<BadgesModal branchLike={shortBranch} metrics={{}} project="foo" />);
expect(wrapper).toMatchSnapshot();
click(wrapper.find('Button'));
expect(wrapper.find('Modal')).toMatchSnapshot();
diff --git a/server/sonar-web/src/main/js/apps/overview/badges/utils.ts b/server/sonar-web/src/main/js/apps/overview/badges/utils.ts
index 39113f88945..5f300830c1b 100644
--- a/server/sonar-web/src/main/js/apps/overview/badges/utils.ts
+++ b/server/sonar-web/src/main/js/apps/overview/badges/utils.ts
@@ -28,6 +28,7 @@ export interface BadgeOptions {
color?: BadgeColors;
project?: string;
metric?: string;
+ pullRequest?: string;
}
export enum BadgeType {
@@ -38,19 +39,19 @@ export enum BadgeType {
export function getBadgeUrl(
type: BadgeType,
- { branch, project, color = 'white', metric = 'alert_status' }: BadgeOptions
+ { branch, project, color = 'white', metric = 'alert_status', pullRequest }: BadgeOptions
) {
switch (type) {
case BadgeType.marketing:
return `${getHostUrl()}/images/project_badges/sonarcloud-${color}.svg`;
case BadgeType.qualityGate:
return `${getHostUrl()}/api/project_badges/quality_gate?${stringify(
- omitNil({ branch, project })
+ omitNil({ branch, project, pullRequest })
)}`;
case BadgeType.measure:
default:
return `${getHostUrl()}/api/project_badges/measure?${stringify(
- omitNil({ branch, project, metric })
+ omitNil({ branch, project, metric, pullRequest })
)}`;
}
}
diff --git a/server/sonar-web/src/main/js/apps/overview/components/App.tsx b/server/sonar-web/src/main/js/apps/overview/components/App.tsx
index 03d898d4e9e..0d7caeb5d3e 100644
--- a/server/sonar-web/src/main/js/apps/overview/components/App.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/components/App.tsx
@@ -21,12 +21,12 @@ import * as React from 'react';
import * as PropTypes from 'prop-types';
import OverviewApp from './OverviewApp';
import EmptyOverview from './EmptyOverview';
-import { getBranchName, isShortLivingBranch } from '../../../helpers/branches';
-import { getProjectBranchUrl, getCodeUrl } from '../../../helpers/urls';
-import { Branch, Component } from '../../../app/types';
+import { Component, BranchLike } from '../../../app/types';
+import { isShortLivingBranch } from '../../../helpers/branches';
+import { getShortLivingBranchUrl, getCodeUrl } from '../../../helpers/urls';
interface Props {
- branch?: Branch;
+ branchLike?: BranchLike;
component: Component;
isInProgress?: boolean;
isPending?: boolean;
@@ -39,7 +39,7 @@ export default class App extends React.PureComponent<Props> {
};
componentDidMount() {
- const { branch, component } = this.props;
+ const { branchLike, component } = this.props;
if (this.isPortfolio()) {
this.context.router.replace({
@@ -48,10 +48,10 @@ export default class App extends React.PureComponent<Props> {
});
} else if (this.isFile()) {
this.context.router.replace(
- getCodeUrl(component.breadcrumbs[0].key, getBranchName(branch), component.key)
+ getCodeUrl(component.breadcrumbs[0].key, branchLike, component.key)
);
- } else if (isShortLivingBranch(branch)) {
- this.context.router.replace(getProjectBranchUrl(component.key, branch));
+ } else if (isShortLivingBranch(branchLike)) {
+ this.context.router.replace(getShortLivingBranchUrl(component.key, branchLike.name));
}
}
@@ -60,9 +60,9 @@ export default class App extends React.PureComponent<Props> {
isFile = () => ['FIL', 'UTS'].includes(this.props.component.qualifier);
render() {
- const { branch, component } = this.props;
+ const { branchLike, component } = this.props;
- if (this.isPortfolio() || this.isFile() || isShortLivingBranch(branch)) {
+ if (this.isPortfolio() || this.isFile() || isShortLivingBranch(branchLike)) {
return null;
}
@@ -77,7 +77,7 @@ export default class App extends React.PureComponent<Props> {
return (
<OverviewApp
- branch={branch}
+ branchLike={branchLike}
component={component}
onComponentChange={this.props.onComponentChange}
/>
diff --git a/server/sonar-web/src/main/js/apps/overview/components/OverviewApp.tsx b/server/sonar-web/src/main/js/apps/overview/components/OverviewApp.tsx
index 5bc3679f9df..091826a44a6 100644
--- a/server/sonar-web/src/main/js/apps/overview/components/OverviewApp.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/components/OverviewApp.tsx
@@ -36,14 +36,14 @@ import { getLeakPeriod, Period } from '../../../helpers/periods';
import { getCustomGraph, getGraph } from '../../../helpers/storage';
import { METRICS, HISTORY_METRICS_LIST } from '../utils';
import { DEFAULT_GRAPH, getDisplayedHistoryMetrics } from '../../projectActivity/utils';
-import { getBranchName } from '../../../helpers/branches';
+import { isSameBranchLike, getBranchLikeQuery } from '../../../helpers/branches';
import { fetchMetrics } from '../../../store/rootActions';
import { getMetrics } from '../../../store/rootReducer';
-import { Branch, Component, Metric } from '../../../app/types';
+import { BranchLike, Component, Metric } from '../../../app/types';
import '../styles.css';
interface OwnProps {
- branch?: Branch;
+ branchLike?: BranchLike;
component: Component;
onComponentChange: (changes: {}) => void;
}
@@ -79,7 +79,7 @@ export class OverviewApp extends React.PureComponent<Props, State> {
componentDidUpdate(prevProps: Props) {
if (
this.props.component.key !== prevProps.component.key ||
- this.props.branch !== prevProps.branch
+ !isSameBranchLike(this.props.branchLike, prevProps.branchLike)
) {
this.loadMeasures().then(this.loadHistory, () => {});
}
@@ -90,12 +90,12 @@ export class OverviewApp extends React.PureComponent<Props, State> {
}
loadMeasures() {
- const { branch, component } = this.props;
+ const { branchLike, component } = this.props;
this.setState({ loading: true });
return getMeasuresAndMeta(component.key, METRICS, {
additionalFields: 'metrics,periods',
- branch: getBranchName(branch)
+ ...getBranchLikeQuery(branchLike)
}).then(
r => {
if (this.mounted && r.metrics) {
@@ -116,7 +116,7 @@ export class OverviewApp extends React.PureComponent<Props, State> {
}
loadHistory = () => {
- const { branch, component } = this.props;
+ const { branchLike, component } = this.props;
let graphMetrics = getDisplayedHistoryMetrics(getGraph(), getCustomGraph());
if (!graphMetrics || graphMetrics.length <= 0) {
@@ -124,22 +124,24 @@ export class OverviewApp extends React.PureComponent<Props, State> {
}
const metrics = uniq(HISTORY_METRICS_LIST.concat(graphMetrics));
- return getAllTimeMachineData(component.key, metrics, { branch: getBranchName(branch) }).then(
- r => {
- if (this.mounted) {
- const history: History = {};
- r.measures.forEach(measure => {
- const measureHistory = measure.history.map(analysis => ({
- date: parseDate(analysis.date),
- value: analysis.value
- }));
- history[measure.metric] = measureHistory;
- });
- const historyStartDate = history[HISTORY_METRICS_LIST[0]][0].date;
- this.setState({ history, historyStartDate });
- }
+ return getAllTimeMachineData({
+ ...getBranchLikeQuery(branchLike),
+ component: component.key,
+ metrics: metrics.join()
+ }).then(r => {
+ if (this.mounted) {
+ const history: History = {};
+ r.measures.forEach(measure => {
+ const measureHistory = measure.history.map(analysis => ({
+ date: parseDate(analysis.date),
+ value: analysis.value
+ }));
+ history[measure.metric] = measureHistory;
+ });
+ const historyStartDate = history[HISTORY_METRICS_LIST[0]][0].date;
+ this.setState({ history, historyStartDate });
}
- );
+ });
};
getApplicationLeakPeriod = () =>
@@ -156,7 +158,7 @@ export class OverviewApp extends React.PureComponent<Props, State> {
}
render() {
- const { branch, component } = this.props;
+ const { branchLike, component } = this.props;
const { loading, measures, periods, history, historyStartDate } = this.state;
if (loading) {
@@ -165,9 +167,8 @@ export class OverviewApp extends React.PureComponent<Props, State> {
const leakPeriod =
component.qualifier === 'APP' ? this.getApplicationLeakPeriod() : getLeakPeriod(periods);
- const branchName = getBranchName(branch);
const domainProps = {
- branch: branchName,
+ branchLike,
component,
measures,
leakPeriod,
@@ -182,7 +183,7 @@ export class OverviewApp extends React.PureComponent<Props, State> {
{component.qualifier === 'APP' ? (
<ApplicationQualityGate component={component} />
) : (
- <QualityGate branch={branchName} component={component} measures={measures} />
+ <QualityGate branchLike={branchLike} component={component} measures={measures} />
)}
<div className="overview-domains-list">
@@ -195,7 +196,7 @@ export class OverviewApp extends React.PureComponent<Props, State> {
<div className="overview-sidebar page-sidebar-fixed">
<Meta
- branch={branchName}
+ branchLike={branchLike}
component={component}
history={history}
measures={measures}
diff --git a/server/sonar-web/src/main/js/apps/overview/components/__tests__/App-test.tsx b/server/sonar-web/src/main/js/apps/overview/components/__tests__/App-test.tsx
index 4edd63ffba8..f3eb8d43cba 100644
--- a/server/sonar-web/src/main/js/apps/overview/components/__tests__/App-test.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/components/__tests__/App-test.tsx
@@ -54,7 +54,7 @@ it('redirects on Code page for files', () => {
qualifier: 'FIL'
};
const replace = jest.fn();
- mount(<App branch={branch} component={newComponent} onComponentChange={jest.fn()} />, {
+ mount(<App branchLike={branch} component={newComponent} onComponentChange={jest.fn()} />, {
context: { router: { replace } }
});
expect(replace).toBeCalledWith({
diff --git a/server/sonar-web/src/main/js/apps/overview/events/AnalysesList.tsx b/server/sonar-web/src/main/js/apps/overview/events/AnalysesList.tsx
index 8380a53b0a5..d405662ace4 100644
--- a/server/sonar-web/src/main/js/apps/overview/events/AnalysesList.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/events/AnalysesList.tsx
@@ -23,11 +23,13 @@ import Analysis from './Analysis';
import { getProjectActivity, Analysis as IAnalysis } from '../../../api/projectActivity';
import PreviewGraph from '../../../components/preview-graph/PreviewGraph';
import { translate } from '../../../helpers/l10n';
-import { Metric, Component } from '../../../app/types';
+import { Metric, Component, BranchLike } from '../../../app/types';
import { History } from '../../../api/time-machine';
+import { getBranchLikeQuery } from '../../../helpers/branches';
+import { getActivityUrl } from '../../../helpers/urls';
interface Props {
- branch?: string;
+ branchLike?: BranchLike;
component: Component;
history?: History;
metrics: { [key: string]: Metric };
@@ -76,7 +78,7 @@ export default class AnalysesList extends React.PureComponent<Props, State> {
this.setState({ loading: true });
getProjectActivity({
- branch: this.props.branch,
+ ...getBranchLikeQuery(this.props.branchLike),
project: this.getTopLevelComponent(),
ps: PAGE_SIZE
}).then(
@@ -101,7 +103,7 @@ export default class AnalysesList extends React.PureComponent<Props, State> {
return (
<ul className="spacer-top">
{analyses.map(analysis => (
- <Analysis key={analysis.key} analysis={analysis} qualifier={this.props.qualifier} />
+ <Analysis analysis={analysis} key={analysis.key} qualifier={this.props.qualifier} />
))}
</ul>
);
@@ -121,20 +123,16 @@ export default class AnalysesList extends React.PureComponent<Props, State> {
</h4>
<PreviewGraph
- branch={this.props.branch}
+ branchLike={this.props.branchLike}
history={this.props.history}
- project={this.props.component.key}
metrics={this.props.metrics}
+ project={this.props.component.key}
/>
{this.renderList(analyses)}
<div className="spacer-top small">
- <Link
- to={{
- pathname: '/project/activity',
- query: { id: this.props.component.key, branch: this.props.branch }
- }}>
+ <Link to={getActivityUrl(this.props.component.key, this.props.branchLike)}>
{translate('show_more')}
</Link>
</div>
diff --git a/server/sonar-web/src/main/js/apps/overview/events/__tests__/AnalysesList-test.tsx b/server/sonar-web/src/main/js/apps/overview/events/__tests__/AnalysesList-test.tsx
new file mode 100644
index 00000000000..710f2608a55
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/overview/events/__tests__/AnalysesList-test.tsx
@@ -0,0 +1,38 @@
+/*
+ * 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 AnalysesList from '../AnalysesList';
+
+it('should render show more link', () => {
+ const branchLike = { analysisDate: '2018-03-08T09:49:22+0100', isMain: true, name: 'master' };
+ const component = {
+ breadcrumbs: [{ key: 'foo', name: 'foo', qualifier: 'TRK' }],
+ key: 'foo',
+ name: 'foo',
+ organization: 'org',
+ qualifier: 'TRK'
+ };
+ const wrapper = shallow(
+ <AnalysesList branchLike={branchLike} component={component} metrics={{}} qualifier="TRK" />
+ );
+ wrapper.setState({ loading: false });
+ expect(wrapper.find('Link')).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/apps/overview/events/__tests__/__snapshots__/AnalysesList-test.tsx.snap b/server/sonar-web/src/main/js/apps/overview/events/__tests__/__snapshots__/AnalysesList-test.tsx.snap
new file mode 100644
index 00000000000..2320d0d0c4f
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/overview/events/__tests__/__snapshots__/AnalysesList-test.tsx.snap
@@ -0,0 +1,18 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render show more link 1`] = `
+<Link
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/project/activity",
+ "query": Object {
+ "id": "foo",
+ },
+ }
+ }
+>
+ show_more
+</Link>
+`;
diff --git a/server/sonar-web/src/main/js/apps/overview/main/BugsAndVulnerabilities.tsx b/server/sonar-web/src/main/js/apps/overview/main/BugsAndVulnerabilities.tsx
index d6433a10e08..52fd2d5253b 100644
--- a/server/sonar-web/src/main/js/apps/overview/main/BugsAndVulnerabilities.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/main/BugsAndVulnerabilities.tsx
@@ -31,7 +31,7 @@ import { translate } from '../../../helpers/l10n';
export class BugsAndVulnerabilities extends React.PureComponent<ComposedProps> {
renderHeader() {
- const { branch, component } = this.props;
+ const { branchLike, component } = this.props;
return (
<div className="overview-card-header">
@@ -39,13 +39,13 @@ export class BugsAndVulnerabilities extends React.PureComponent<ComposedProps> {
<span>{translate('metric.bugs.name')}</span>
<Link
className="button button-small spacer-left text-text-bottom"
- to={getComponentDrilldownUrl(component.key, 'Reliability', branch)}>
+ to={getComponentDrilldownUrl(component.key, 'Reliability', branchLike)}>
<BubblesIcon size={14} />
</Link>
<span className="big-spacer-left">{translate('metric.vulnerabilities.name')}</span>
<Link
className="button button-small spacer-left text-text-bottom"
- to={getComponentDrilldownUrl(component.key, 'Security', branch)}>
+ to={getComponentDrilldownUrl(component.key, 'Security', branchLike)}>
<BubblesIcon size={14} />
</Link>
</div>
diff --git a/server/sonar-web/src/main/js/apps/overview/main/CodeSmells.tsx b/server/sonar-web/src/main/js/apps/overview/main/CodeSmells.tsx
index ac7fcd0c94c..bc632fae96a 100644
--- a/server/sonar-web/src/main/js/apps/overview/main/CodeSmells.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/main/CodeSmells.tsx
@@ -26,6 +26,7 @@ import { translate, translateWithParameters } from '../../../helpers/l10n';
import { formatMeasure, isDiffMetric } from '../../../helpers/measures';
import { getComponentIssuesUrl } from '../../../helpers/urls';
import CodeSmellIcon from '../../../components/icons-components/CodeSmellIcon';
+import { getBranchLikeQuery } from '../../../helpers/branches';
export class CodeSmells extends React.PureComponent<ComposedProps> {
renderHeader() {
@@ -33,10 +34,15 @@ export class CodeSmells extends React.PureComponent<ComposedProps> {
}
renderDebt(metric: string, type: string) {
- const { branch, measures, component } = this.props;
+ const { branchLike, measures, component } = this.props;
const measure = measures.find(measure => measure.metric.key === metric);
const value = measure ? this.props.getValue(measure) : undefined;
- const params = { branch, resolved: 'false', facetMode: 'effort', types: type };
+ const params = {
+ ...getBranchLikeQuery(branchLike),
+ resolved: 'false',
+ facetMode: 'effort',
+ types: type
+ };
if (isDiffMetric(metric)) {
Object.assign(params, { sinceLeakPeriod: 'true' });
diff --git a/server/sonar-web/src/main/js/apps/overview/main/Coverage.tsx b/server/sonar-web/src/main/js/apps/overview/main/Coverage.tsx
index cd1abc807ee..28e5a141ef7 100644
--- a/server/sonar-web/src/main/js/apps/overview/main/Coverage.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/main/Coverage.tsx
@@ -44,7 +44,7 @@ export class Coverage extends React.PureComponent<ComposedProps> {
}
renderCoverage() {
- const { branch, component } = this.props;
+ const { branchLike, component } = this.props;
const metric = 'coverage';
const coverage = this.getCoverage();
@@ -56,7 +56,7 @@ export class Coverage extends React.PureComponent<ComposedProps> {
<div className="display-inline-block text-middle">
<div className="overview-domain-measure-value">
- <DrilldownLink branch={branch} component={component.key} metric={metric}>
+ <DrilldownLink branchLike={branchLike} component={component.key} metric={metric}>
<span className="js-overview-main-coverage">
{formatMeasure(coverage, 'PERCENT')}
</span>
@@ -73,7 +73,7 @@ export class Coverage extends React.PureComponent<ComposedProps> {
}
renderNewCoverage() {
- const { branch, component, leakPeriod, measures } = this.props;
+ const { branchLike, component, leakPeriod, measures } = this.props;
if (!leakPeriod) {
return null;
}
@@ -85,7 +85,7 @@ export class Coverage extends React.PureComponent<ComposedProps> {
newCoverageMeasure && newCoverageValue !== undefined ? (
<div>
<DrilldownLink
- branch={branch}
+ branchLike={branchLike}
component={component.key}
metric={newCoverageMeasure.metric.key}>
<span className="js-overview-main-new-coverage">
@@ -106,7 +106,7 @@ export class Coverage extends React.PureComponent<ComposedProps> {
{translate('overview.coverage_on')}
<br />
<DrilldownLink
- branch={branch}
+ branchLike={branchLike}
className="spacer-right overview-domain-secondary-measure-value"
component={component.key}
metric={newLinesToCover.metric.key}>
diff --git a/server/sonar-web/src/main/js/apps/overview/main/Duplications.tsx b/server/sonar-web/src/main/js/apps/overview/main/Duplications.tsx
index d8a09caee40..49db5f74c36 100644
--- a/server/sonar-web/src/main/js/apps/overview/main/Duplications.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/main/Duplications.tsx
@@ -39,8 +39,7 @@ export class Duplications extends React.PureComponent<ComposedProps> {
}
renderDuplications() {
- const { branch, component, measures } = this.props;
-
+ const { branchLike, component, measures } = this.props;
const measure = measures.find(measure => measure.metric.key === 'duplicated_lines_density');
if (!measure) {
return null;
@@ -57,7 +56,7 @@ export class Duplications extends React.PureComponent<ComposedProps> {
<div className="display-inline-block text-middle">
<div className="overview-domain-measure-value">
<DrilldownLink
- branch={branch}
+ branchLike={branchLike}
component={component.key}
metric="duplicated_lines_density">
{formatMeasure(duplications, 'PERCENT')}
@@ -74,11 +73,10 @@ export class Duplications extends React.PureComponent<ComposedProps> {
}
renderNewDuplications() {
- const { branch, component, measures, leakPeriod } = this.props;
+ const { branchLike, component, measures, leakPeriod } = this.props;
if (!leakPeriod) {
return null;
}
-
const newDuplicationsMeasure = measures.find(
measure => measure.metric.key === 'new_duplicated_lines_density'
);
@@ -88,7 +86,7 @@ export class Duplications extends React.PureComponent<ComposedProps> {
newDuplicationsMeasure && newDuplicationsValue ? (
<div>
<DrilldownLink
- branch={branch}
+ branchLike={branchLike}
component={component.key}
metric={newDuplicationsMeasure.metric.key}>
<span className="js-overview-main-new-duplications">
@@ -108,7 +106,7 @@ export class Duplications extends React.PureComponent<ComposedProps> {
{translate('overview.duplications_on')}
<br />
<DrilldownLink
- branch={branch}
+ branchLike={branchLike}
className="spacer-right overview-domain-secondary-measure-value"
component={component.key}
metric={newLinesMeasure.metric.key}>
diff --git a/server/sonar-web/src/main/js/apps/overview/main/enhance.tsx b/server/sonar-web/src/main/js/apps/overview/main/enhance.tsx
index 2b07c3c9239..2251612e2e3 100644
--- a/server/sonar-web/src/main/js/apps/overview/main/enhance.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/main/enhance.tsx
@@ -40,11 +40,12 @@ import {
getComponentIssuesUrl,
getMeasureHistoryUrl
} from '../../../helpers/urls';
-import { Component } from '../../../app/types';
+import { Component, BranchLike } from '../../../app/types';
import { History } from '../../../api/time-machine';
+import { getBranchLikeQuery } from '../../../helpers/branches';
export interface EnhanceProps {
- branch?: string;
+ branchLike?: BranchLike;
component: Component;
measures: MeasureEnhanced[];
leakPeriod?: { index: number; date?: string };
@@ -77,14 +78,14 @@ export default function enhance(ComposedComponent: React.ComponentType<ComposedP
};
renderHeader = (domain: string, label: string) => {
- const { branch, component } = this.props;
+ const { branchLike, component } = this.props;
return (
<div className="overview-card-header">
<div className="overview-title">
<span>{label}</span>
<Link
className="button button-small spacer-left text-text-bottom"
- to={getComponentDrilldownUrl(component.key, domain, branch)}>
+ to={getComponentDrilldownUrl(component.key, domain, branchLike)}>
<BubblesIcon size={14} />
</Link>
</div>
@@ -93,7 +94,7 @@ export default function enhance(ComposedComponent: React.ComponentType<ComposedP
};
renderMeasure = (metricKey: string) => {
- const { branch, measures, component } = this.props;
+ const { branchLike, measures, component } = this.props;
const measure = measures.find(measure => measure.metric.key === metricKey);
if (!measure) {
return null;
@@ -102,7 +103,7 @@ export default function enhance(ComposedComponent: React.ComponentType<ComposedP
return (
<div className="overview-domain-measure">
<div className="overview-domain-measure-value">
- <DrilldownLink branch={branch} component={component.key} metric={metricKey}>
+ <DrilldownLink branchLike={branchLike} component={component.key} metric={metricKey}>
<span className="js-overview-main-tests">
{formatMeasure(measure.value, getShortType(measure.metric.type))}
</span>
@@ -118,7 +119,7 @@ export default function enhance(ComposedComponent: React.ComponentType<ComposedP
};
renderRating = (metricKey: string) => {
- const { branch, component, measures } = this.props;
+ const { branchLike, component, measures } = this.props;
const measure = measures.find(measure => measure.metric.key === metricKey);
if (!measure) {
return null;
@@ -130,7 +131,7 @@ export default function enhance(ComposedComponent: React.ComponentType<ComposedP
<Tooltip overlay={title} placement="top">
<div className="overview-domain-measure-sup">
<DrilldownLink
- branch={branch}
+ branchLike={branchLike}
className="link-no-underline"
component={component.key}
metric={metricKey}>
@@ -142,14 +143,14 @@ export default function enhance(ComposedComponent: React.ComponentType<ComposedP
};
renderIssues = (metric: string, type: string) => {
- const { branch, measures, component } = this.props;
+ const { branchLike, measures, component } = this.props;
const measure = measures.find(measure => measure.metric.key === metric);
if (!measure) {
return null;
}
const value = this.getValue(measure);
- const params = { branch, resolved: 'false', types: type };
+ const params = { ...getBranchLikeQuery(branchLike), resolved: 'false', types: type };
if (isDiffMetric(metric)) {
Object.assign(params, { sinceLeakPeriod: 'true' });
}
@@ -166,7 +167,7 @@ export default function enhance(ComposedComponent: React.ComponentType<ComposedP
return (
<Link
className={linkClass}
- to={getMeasureHistoryUrl(this.props.component.key, metricKey, this.props.branch)}>
+ to={getMeasureHistoryUrl(this.props.component.key, metricKey, this.props.branchLike)}>
<HistoryIcon />
</Link>
);
diff --git a/server/sonar-web/src/main/js/apps/overview/meta/Meta.tsx b/server/sonar-web/src/main/js/apps/overview/meta/Meta.tsx
index 4a55b5e29a4..edca9b4c4cb 100644
--- a/server/sonar-web/src/main/js/apps/overview/meta/Meta.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/meta/Meta.tsx
@@ -28,13 +28,13 @@ import MetaSize from './MetaSize';
import MetaTags from './MetaTags';
import BadgesModal from '../badges/BadgesModal';
import AnalysesList from '../events/AnalysesList';
-import { Visibility, Component, Metric } from '../../../app/types';
+import { Visibility, Component, Metric, BranchLike } from '../../../app/types';
import { History } from '../../../api/time-machine';
import { translate } from '../../../helpers/l10n';
import { MeasureEnhanced } from '../../../helpers/measures';
interface Props {
- branch?: string;
+ branchLike?: BranchLike;
component: Component;
history?: History;
measures: MeasureEnhanced[];
@@ -50,7 +50,7 @@ export default class Meta extends React.PureComponent<Props> {
render() {
const { onSonarCloud, organizationsEnabled } = this.context;
- const { branch, component, metrics } = this.props;
+ const { branchLike, component, metrics } = this.props;
const { qualifier, description, qualityProfiles, qualityGate, visibility } = component;
const isProject = qualifier === 'TRK';
@@ -66,11 +66,11 @@ export default class Meta extends React.PureComponent<Props> {
{isProject && (
<MetaTags component={component} onComponentChange={this.props.onComponentChange} />
)}
- <MetaSize branch={branch} component={component} measures={this.props.measures} />
+ <MetaSize branchLike={branchLike} component={component} measures={this.props.measures} />
</div>
<AnalysesList
- branch={branch}
+ branchLike={branchLike}
component={component}
history={this.props.history}
metrics={metrics}
@@ -106,7 +106,9 @@ export default class Meta extends React.PureComponent<Props> {
{onSonarCloud &&
isProject &&
- !isPrivate && <BadgesModal branch={branch} metrics={metrics} project={component.key} />}
+ !isPrivate && (
+ <BadgesModal branchLike={branchLike} metrics={metrics} project={component.key} />
+ )}
</div>
);
}
diff --git a/server/sonar-web/src/main/js/apps/overview/meta/MetaSize.tsx b/server/sonar-web/src/main/js/apps/overview/meta/MetaSize.tsx
index 335d0b48795..0fe760979f5 100644
--- a/server/sonar-web/src/main/js/apps/overview/meta/MetaSize.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/meta/MetaSize.tsx
@@ -25,10 +25,10 @@ import SizeRating from '../../../components/ui/SizeRating';
import { formatMeasure, MeasureEnhanced } from '../../../helpers/measures';
import { getMetricName } from '../helpers/metrics';
import { translate } from '../../../helpers/l10n';
-import { LightComponent } from '../../../app/types';
+import { LightComponent, BranchLike } from '../../../app/types';
interface Props {
- branch?: string;
+ branchLike?: BranchLike;
component: LightComponent;
measures: MeasureEnhanced[];
}
@@ -43,7 +43,10 @@ export default class MetaSize extends React.PureComponent<Props> {
<span className="spacer-right">
<SizeRating value={Number(ncloc.value)} />
</span>
- <DrilldownLink branch={this.props.branch} component={this.props.component.key} metric="ncloc">
+ <DrilldownLink
+ branchLike={this.props.branchLike}
+ component={this.props.component.key}
+ metric="ncloc">
{formatMeasure(ncloc.value, 'SHORT_INT')}
</DrilldownLink>
<div className="spacer-top text-muted">{getMetricName('ncloc')}</div>
@@ -71,7 +74,7 @@ export default class MetaSize extends React.PureComponent<Props> {
return projects ? (
<div id="overview-projects" className="overview-meta-size-ncloc is-half-width">
<DrilldownLink
- branch={this.props.branch}
+ branchLike={this.props.branchLike}
component={this.props.component.key}
metric="projects">
{formatMeasure(projects.value, 'SHORT_INT')}
diff --git a/server/sonar-web/src/main/js/apps/overview/qualityGate/QualityGate.js b/server/sonar-web/src/main/js/apps/overview/qualityGate/QualityGate.js
index 5c3125aedee..20ab7885146 100644
--- a/server/sonar-web/src/main/js/apps/overview/qualityGate/QualityGate.js
+++ b/server/sonar-web/src/main/js/apps/overview/qualityGate/QualityGate.js
@@ -38,13 +38,13 @@ function isProject(component /*: Component */) {
/*::
type Props = {
- branch?: string,
+ branchLike?: {id?: string; name: string },
component: Component,
measures: MeasuresList
};
*/
-export default function QualityGate({ branch, component, measures } /*: Props */) {
+export default function QualityGate({ branchLike, component, measures } /*: Props */) {
const statusMeasure = measures.find(measure => measure.metric.key === 'alert_status');
const detailsMeasure = measures.find(measure => measure.metric.key === 'quality_gate_details');
@@ -81,7 +81,11 @@ export default function QualityGate({ branch, component, measures } /*: Props */
)}
{conditions.length > 0 && (
- <QualityGateConditions branch={branch} component={component} conditions={conditions} />
+ <QualityGateConditions
+ branchLike={branchLike}
+ component={component}
+ conditions={conditions}
+ />
)}
</div>
);
diff --git a/server/sonar-web/src/main/js/apps/overview/qualityGate/QualityGateCondition.js b/server/sonar-web/src/main/js/apps/overview/qualityGate/QualityGateCondition.js
index 3301c33ef6f..ba6d12fa35d 100644
--- a/server/sonar-web/src/main/js/apps/overview/qualityGate/QualityGateCondition.js
+++ b/server/sonar-web/src/main/js/apps/overview/qualityGate/QualityGateCondition.js
@@ -27,12 +27,13 @@ import IssueTypeIcon from '../../../components/ui/IssueTypeIcon';
import { getPeriodValue, isDiffMetric, formatMeasure } from '../../../helpers/measures';
import { translate } from '../../../helpers/l10n';
import { getComponentIssuesUrl } from '../../../helpers/urls';
+import { getBranchLikeQuery } from '../../../helpers/branches';
/*:: import type { Component } from '../types'; */
/*:: import type { MeasureEnhanced } from '../../../components/measure/types'; */
export default class QualityGateCondition extends React.PureComponent {
/*:: props: {
- branch?: string,
+ branchLike?: { id?: string; name: string },
component: Component,
condition: {
level: string,
@@ -54,7 +55,11 @@ export default class QualityGateCondition extends React.PureComponent {
}
getIssuesUrl = (sinceLeakPeriod /*: boolean */, customQuery /*: {} */) => {
- const query /*: Object */ = { resolved: 'false', branch: this.props.branch, ...customQuery };
+ const query /*: Object */ = {
+ resolved: 'false',
+ ...getBranchLikeQuery(this.props.branchLike),
+ ...customQuery
+ };
if (sinceLeakPeriod) {
Object.assign(query, { sinceLeakPeriod: 'true' });
}
@@ -89,7 +94,7 @@ export default class QualityGateCondition extends React.PureComponent {
}
wrapWithLink(children /*: React.Element<*> */) {
- const { branch, component, condition } = this.props;
+ const { branchLike, component, condition } = this.props;
const className = classNames(
'overview-quality-gate-condition',
@@ -114,7 +119,7 @@ export default class QualityGateCondition extends React.PureComponent {
</Link>
) : (
<DrilldownLink
- branch={branch}
+ branchLike={branchLike}
className={className}
component={component.key}
metric={condition.measure.metric.key}
diff --git a/server/sonar-web/src/main/js/apps/overview/qualityGate/QualityGateConditions.js b/server/sonar-web/src/main/js/apps/overview/qualityGate/QualityGateConditions.js
index 8abd92619a1..57dc3002b4d 100644
--- a/server/sonar-web/src/main/js/apps/overview/qualityGate/QualityGateConditions.js
+++ b/server/sonar-web/src/main/js/apps/overview/qualityGate/QualityGateConditions.js
@@ -19,10 +19,12 @@
*/
import React from 'react';
import { sortBy } from 'lodash';
+import PropTypes from 'prop-types';
import QualityGateCondition from './QualityGateCondition';
import { ComponentType, ConditionsListType } from '../propTypes';
import { getMeasuresAndMeta } from '../../../api/measures';
import { enhanceMeasuresWithMetrics } from '../../../helpers/measures';
+import { isSameBranchLike, getBranchLikeQuery } from '../../../helpers/branches';
const LEVEL_ORDER = ['ERROR', 'WARN'];
@@ -35,7 +37,7 @@ function enhanceConditions(conditions, measures) {
export default class QualityGateConditions extends React.PureComponent {
static propTypes = {
- // branch
+ branchLike: PropTypes.object,
component: ComponentType.isRequired,
conditions: ConditionsListType.isRequired
};
@@ -51,7 +53,7 @@ export default class QualityGateConditions extends React.PureComponent {
componentDidUpdate(prevProps) {
if (
- prevProps.branch !== this.props.branch ||
+ !isSameBranchLike(prevProps.branchLike, this.props.branchLike) ||
prevProps.conditions !== this.props.conditions ||
prevProps.component !== this.props.component
) {
@@ -64,13 +66,13 @@ export default class QualityGateConditions extends React.PureComponent {
}
loadFailedMeasures() {
- const { branch, component, conditions } = this.props;
+ const { branchLike, component, conditions } = this.props;
const failedConditions = conditions.filter(c => c.level !== 'OK');
if (failedConditions.length > 0) {
const metrics = failedConditions.map(condition => condition.metric);
getMeasuresAndMeta(component.key, metrics, {
additionalFields: 'metrics',
- branch
+ ...getBranchLikeQuery(branchLike)
}).then(r => {
if (this.mounted) {
const measures = enhanceMeasuresWithMetrics(r.component.measures, r.metrics);
diff --git a/server/sonar-web/src/main/js/apps/overview/qualityGate/__tests__/__snapshots__/ApplicationQualityGateProject-test.tsx.snap b/server/sonar-web/src/main/js/apps/overview/qualityGate/__tests__/__snapshots__/ApplicationQualityGateProject-test.tsx.snap
index 64d2bf86cfb..4c5e4ee1a87 100644
--- a/server/sonar-web/src/main/js/apps/overview/qualityGate/__tests__/__snapshots__/ApplicationQualityGateProject-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/overview/qualityGate/__tests__/__snapshots__/ApplicationQualityGateProject-test.tsx.snap
@@ -9,7 +9,6 @@ exports[`renders 1`] = `
Object {
"pathname": "/dashboard",
"query": Object {
- "branch": undefined,
"id": "foo",
},
}
diff --git a/server/sonar-web/src/main/js/apps/overview/qualityGate/__tests__/__snapshots__/QualityGateCondition-test.js.snap b/server/sonar-web/src/main/js/apps/overview/qualityGate/__tests__/__snapshots__/QualityGateCondition-test.js.snap
index e286b3da99d..900cc34dce2 100644
--- a/server/sonar-web/src/main/js/apps/overview/qualityGate/__tests__/__snapshots__/QualityGateCondition-test.js.snap
+++ b/server/sonar-web/src/main/js/apps/overview/qualityGate/__tests__/__snapshots__/QualityGateCondition-test.js.snap
@@ -9,7 +9,6 @@ exports[`new_maintainability_rating 1`] = `
Object {
"pathname": "/project/issues",
"query": Object {
- "branch": undefined,
"id": "abcd-key",
"resolved": "false",
"sinceLeakPeriod": "true",
@@ -104,7 +103,6 @@ exports[`new_reliability_rating 1`] = `
Object {
"pathname": "/project/issues",
"query": Object {
- "branch": undefined,
"id": "abcd-key",
"resolved": "false",
"severities": "BLOCKER,CRITICAL,MAJOR,MINOR",
@@ -158,7 +156,6 @@ exports[`new_security_rating 1`] = `
Object {
"pathname": "/project/issues",
"query": Object {
- "branch": undefined,
"id": "abcd-key",
"resolved": "false",
"severities": "BLOCKER,CRITICAL,MAJOR,MINOR",
@@ -254,7 +251,6 @@ exports[`reliability_rating 1`] = `
Object {
"pathname": "/project/issues",
"query": Object {
- "branch": undefined,
"id": "abcd-key",
"resolved": "false",
"severities": "BLOCKER,CRITICAL,MAJOR,MINOR",
@@ -307,7 +303,6 @@ exports[`security_rating 1`] = `
Object {
"pathname": "/project/issues",
"query": Object {
- "branch": undefined,
"id": "abcd-key",
"resolved": "false",
"severities": "BLOCKER,CRITICAL,MAJOR,MINOR",
@@ -360,7 +355,6 @@ exports[`should work with branch 1`] = `
Object {
"pathname": "/project/issues",
"query": Object {
- "branch": "feature",
"id": "abcd-key",
"resolved": "false",
"sinceLeakPeriod": "true",
@@ -413,7 +407,6 @@ exports[`sqale_rating 1`] = `
Object {
"pathname": "/project/issues",
"query": Object {
- "branch": undefined,
"id": "abcd-key",
"resolved": "false",
"types": "CODE_SMELL",
diff --git a/server/sonar-web/src/main/js/apps/portfolio/components/Activity.tsx b/server/sonar-web/src/main/js/apps/portfolio/components/Activity.tsx
index ddba951e313..01d14bb1807 100644
--- a/server/sonar-web/src/main/js/apps/portfolio/components/Activity.tsx
+++ b/server/sonar-web/src/main/js/apps/portfolio/components/Activity.tsx
@@ -60,13 +60,13 @@ export default class Activity extends React.PureComponent<Props> {
fetchHistory = () => {
const { component } = this.props;
- let graphMetrics = getDisplayedHistoryMetrics(getGraph(), getCustomGraph());
+ let graphMetrics: string[] = getDisplayedHistoryMetrics(getGraph(), getCustomGraph());
if (!graphMetrics || graphMetrics.length <= 0) {
graphMetrics = getDisplayedHistoryMetrics(DEFAULT_GRAPH, []);
}
this.setState({ loading: true });
- return getAllTimeMachineData(component, graphMetrics).then(
+ return getAllTimeMachineData({ component, metrics: graphMetrics.join() }).then(
timeMachine => {
if (this.mounted) {
const history: History = {};
diff --git a/server/sonar-web/src/main/js/apps/portfolio/components/App.tsx b/server/sonar-web/src/main/js/apps/portfolio/components/App.tsx
index 27d4e2d957b..aa0fd5f0a16 100644
--- a/server/sonar-web/src/main/js/apps/portfolio/components/App.tsx
+++ b/server/sonar-web/src/main/js/apps/portfolio/components/App.tsx
@@ -81,7 +81,7 @@ export class App extends React.PureComponent<Props, State> {
fetchData() {
this.setState({ loading: true });
Promise.all([
- getMeasures(this.props.component.key, PORTFOLIO_METRICS),
+ getMeasures({ componentKey: this.props.component.key, metricKeys: PORTFOLIO_METRICS.join() }),
getChildren(this.props.component.key, SUB_COMPONENTS_METRICS, { ps: 20, s: 'qualifier' })
]).then(
([measures, subComponents]) => {
diff --git a/server/sonar-web/src/main/js/apps/portfolio/components/__tests__/Activity-test.tsx b/server/sonar-web/src/main/js/apps/portfolio/components/__tests__/Activity-test.tsx
index c8c2e81f295..0b5fb1f1584 100644
--- a/server/sonar-web/src/main/js/apps/portfolio/components/__tests__/Activity-test.tsx
+++ b/server/sonar-web/src/main/js/apps/portfolio/components/__tests__/Activity-test.tsx
@@ -67,5 +67,5 @@ it('renders', () => {
it('fetches history', () => {
mount(<Activity component="foo" metrics={{}} />);
- expect(getAllTimeMachineData).toBeCalledWith('foo', ['coverage']);
+ expect(getAllTimeMachineData).toBeCalledWith({ component: 'foo', metrics: 'coverage' });
});
diff --git a/server/sonar-web/src/main/js/apps/portfolio/components/__tests__/App-test.tsx b/server/sonar-web/src/main/js/apps/portfolio/components/__tests__/App-test.tsx
index 6cfe8d6890b..4abdc4e056b 100644
--- a/server/sonar-web/src/main/js/apps/portfolio/components/__tests__/App-test.tsx
+++ b/server/sonar-web/src/main/js/apps/portfolio/components/__tests__/App-test.tsx
@@ -77,23 +77,11 @@ it('fetches measures and children components', () => {
getMeasures.mockClear();
getChildren.mockClear();
mount(<App component={component} fetchMetrics={jest.fn()} metrics={{}} />);
- expect(getMeasures).toBeCalledWith('foo', [
- 'projects',
- 'ncloc',
- 'ncloc_language_distribution',
- 'releasability_rating',
- 'releasability_effort',
- 'sqale_rating',
- 'maintainability_rating_effort',
- 'reliability_rating',
- 'reliability_rating_effort',
- 'security_rating',
- 'security_rating_effort',
- 'last_change_on_releasability_rating',
- 'last_change_on_maintainability_rating',
- 'last_change_on_security_rating',
- 'last_change_on_reliability_rating'
- ]);
+ expect(getMeasures).toBeCalledWith({
+ componentKey: 'foo',
+ metricKeys:
+ 'projects,ncloc,ncloc_language_distribution,releasability_rating,releasability_effort,sqale_rating,maintainability_rating_effort,reliability_rating,reliability_rating_effort,security_rating,security_rating_effort,last_change_on_releasability_rating,last_change_on_maintainability_rating,last_change_on_security_rating,last_change_on_reliability_rating'
+ });
expect(getChildren).toBeCalledWith(
'foo',
[
diff --git a/server/sonar-web/src/main/js/apps/portfolio/components/__tests__/__snapshots__/Effort-test.tsx.snap b/server/sonar-web/src/main/js/apps/portfolio/components/__tests__/__snapshots__/Effort-test.tsx.snap
index 8918c9e4b99..b278210c01b 100644
--- a/server/sonar-web/src/main/js/apps/portfolio/components/__tests__/__snapshots__/Effort-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/portfolio/components/__tests__/__snapshots__/Effort-test.tsx.snap
@@ -16,7 +16,6 @@ exports[`renders 1`] = `
Object {
"pathname": "/component_measures",
"query": Object {
- "branch": undefined,
"id": "foo",
"metric": "security_rating",
},
diff --git a/server/sonar-web/src/main/js/apps/portfolio/components/__tests__/__snapshots__/HistoryButtonLink-test.tsx.snap b/server/sonar-web/src/main/js/apps/portfolio/components/__tests__/__snapshots__/HistoryButtonLink-test.tsx.snap
index e62b2d558c6..8f25ec024e1 100644
--- a/server/sonar-web/src/main/js/apps/portfolio/components/__tests__/__snapshots__/HistoryButtonLink-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/portfolio/components/__tests__/__snapshots__/HistoryButtonLink-test.tsx.snap
@@ -9,7 +9,6 @@ exports[`renders 1`] = `
Object {
"pathname": "/project/activity",
"query": Object {
- "branch": undefined,
"custom_metrics": "security_rating",
"graph": "custom",
"id": "foo",
diff --git a/server/sonar-web/src/main/js/apps/portfolio/components/__tests__/__snapshots__/MainRating-test.tsx.snap b/server/sonar-web/src/main/js/apps/portfolio/components/__tests__/__snapshots__/MainRating-test.tsx.snap
index d8cc0a6fd99..b90b5afa313 100644
--- a/server/sonar-web/src/main/js/apps/portfolio/components/__tests__/__snapshots__/MainRating-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/portfolio/components/__tests__/__snapshots__/MainRating-test.tsx.snap
@@ -9,7 +9,6 @@ exports[`renders 1`] = `
Object {
"pathname": "/component_measures",
"query": Object {
- "branch": undefined,
"id": "foo",
"metric": "security_rating",
"view": "treemap",
diff --git a/server/sonar-web/src/main/js/apps/portfolio/components/__tests__/__snapshots__/MeasuresButtonLink-test.tsx.snap b/server/sonar-web/src/main/js/apps/portfolio/components/__tests__/__snapshots__/MeasuresButtonLink-test.tsx.snap
index a97849bb45e..b4afa69ade1 100644
--- a/server/sonar-web/src/main/js/apps/portfolio/components/__tests__/__snapshots__/MeasuresButtonLink-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/portfolio/components/__tests__/__snapshots__/MeasuresButtonLink-test.tsx.snap
@@ -9,7 +9,6 @@ exports[`renders 1`] = `
Object {
"pathname": "/component_measures",
"query": Object {
- "branch": undefined,
"id": "foo",
"metric": "security_rating",
},
diff --git a/server/sonar-web/src/main/js/apps/portfolio/components/__tests__/__snapshots__/ReleasabilityBox-test.tsx.snap b/server/sonar-web/src/main/js/apps/portfolio/components/__tests__/__snapshots__/ReleasabilityBox-test.tsx.snap
index c2120360b56..2401a4dc09b 100644
--- a/server/sonar-web/src/main/js/apps/portfolio/components/__tests__/__snapshots__/ReleasabilityBox-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/portfolio/components/__tests__/__snapshots__/ReleasabilityBox-test.tsx.snap
@@ -17,7 +17,6 @@ exports[`renders 1`] = `
Object {
"pathname": "/component_measures",
"query": Object {
- "branch": undefined,
"id": "foo",
"metric": "alert_status",
},
@@ -41,7 +40,6 @@ exports[`renders 1`] = `
Object {
"pathname": "/component_measures",
"query": Object {
- "branch": undefined,
"id": "foo",
"metric": "alert_status",
},
diff --git a/server/sonar-web/src/main/js/apps/portfolio/components/__tests__/__snapshots__/Summary-test.tsx.snap b/server/sonar-web/src/main/js/apps/portfolio/components/__tests__/__snapshots__/Summary-test.tsx.snap
index 149c818b286..5c828719017 100644
--- a/server/sonar-web/src/main/js/apps/portfolio/components/__tests__/__snapshots__/Summary-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/portfolio/components/__tests__/__snapshots__/Summary-test.tsx.snap
@@ -24,7 +24,6 @@ exports[`renders 1`] = `
Object {
"pathname": "/component_measures",
"query": Object {
- "branch": undefined,
"id": "foo",
"metric": "projects",
},
@@ -55,7 +54,6 @@ exports[`renders 1`] = `
Object {
"pathname": "/component_measures",
"query": Object {
- "branch": undefined,
"id": "foo",
"metric": "ncloc",
},
diff --git a/server/sonar-web/src/main/js/apps/portfolio/components/__tests__/__snapshots__/WorstProjects-test.tsx.snap b/server/sonar-web/src/main/js/apps/portfolio/components/__tests__/__snapshots__/WorstProjects-test.tsx.snap
index 736ab248060..ceb66108e3c 100644
--- a/server/sonar-web/src/main/js/apps/portfolio/components/__tests__/__snapshots__/WorstProjects-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/portfolio/components/__tests__/__snapshots__/WorstProjects-test.tsx.snap
@@ -53,7 +53,6 @@ exports[`renders 1`] = `
Object {
"pathname": "/dashboard",
"query": Object {
- "branch": undefined,
"id": "foo",
},
}
@@ -141,7 +140,6 @@ exports[`renders 1`] = `
Object {
"pathname": "/dashboard",
"query": Object {
- "branch": undefined,
"id": "barbar",
},
}
@@ -229,7 +227,6 @@ exports[`renders 1`] = `
Object {
"pathname": "/dashboard",
"query": Object {
- "branch": undefined,
"id": "bazbaz",
},
}
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAppContainer.js b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAppContainer.js
index 51d28646604..54aa9929ecc 100644
--- a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAppContainer.js
+++ b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAppContainer.js
@@ -26,7 +26,7 @@ import { getAllTimeMachineData } from '../../../api/time-machine';
import { getAllMetrics } from '../../../api/metrics';
import * as api from '../../../api/projectActivity';
import * as actions from '../actions';
-import { getBranchName } from '../../../helpers/branches';
+import { getBranchLikeQuery } from '../../../helpers/branches';
import { parseDate } from '../../../helpers/dates';
import { getCustomGraph, getGraph } from '../../../helpers/storage';
import {
@@ -51,7 +51,7 @@ type Component = {
};
type Props = {
- branch?: {},
+ branchLike?: { id?: string; name: string },
location: { pathname: string, query: RawQuery },
component: Component
};
@@ -103,7 +103,7 @@ export default class ProjectActivityAppContainer extends React.PureComponent {
pathname: this.props.location.pathname,
query: {
...serializeUrlQuery(newQuery),
- branch: getBranchName(this.props.branch)
+ ...getBranchLikeQuery(this.props.branchLike)
}
});
} else {
@@ -169,12 +169,7 @@ export default class ProjectActivityAppContainer extends React.PureComponent {
[string]: string
} */
) => {
- const parameters = {
- project,
- p,
- ps,
- branch: getBranchName(this.props.branch)
- };
+ const parameters = { project, p, ps, ...getBranchLikeQuery(this.props.branchLike) };
return api
.getProjectActivity({ ...additional, ...parameters })
.then(({ analyses, paging }) => ({
@@ -187,8 +182,10 @@ export default class ProjectActivityAppContainer extends React.PureComponent {
if (metrics.length <= 0) {
return Promise.resolve([]);
}
- return getAllTimeMachineData(this.props.component.key, metrics, {
- branch: getBranchName(this.props.branch)
+ return getAllTimeMachineData({
+ component: this.props.component.key,
+ metrics: metrics.join(),
+ ...getBranchLikeQuery(this.props.branchLike)
}).then(
({ measures }) =>
measures.map(measure => ({
@@ -300,7 +297,7 @@ export default class ProjectActivityAppContainer extends React.PureComponent {
pathname: this.props.location.pathname,
query: {
...query,
- branch: getBranchName(this.props.branch),
+ ...getBranchLikeQuery(this.props.branchLike),
id: this.props.component.key
}
});
diff --git a/server/sonar-web/src/main/js/apps/projectBranches/components/App.tsx b/server/sonar-web/src/main/js/apps/projectBranches/components/App.tsx
index e4c886668fd..24dac728e91 100644
--- a/server/sonar-web/src/main/js/apps/projectBranches/components/App.tsx
+++ b/server/sonar-web/src/main/js/apps/projectBranches/components/App.tsx
@@ -22,14 +22,19 @@ import { FormattedMessage } from 'react-intl';
import { Link } from 'react-router';
import BranchRow from './BranchRow';
import LongBranchesPattern from './LongBranchesPattern';
-import { Branch } from '../../../app/types';
-import { sortBranchesAsTree } from '../../../helpers/branches';
+import { BranchLike } from '../../../app/types';
+import {
+ sortBranchesAsTree,
+ getBranchLikeKey,
+ isShortLivingBranch,
+ isPullRequest
+} from '../../../helpers/branches';
import { translate } from '../../../helpers/l10n';
import { getValues } from '../../../api/settings';
import { formatMeasure } from '../../../helpers/measures';
interface Props {
- branches: Branch[];
+ branchLikes: BranchLike[];
canAdmin?: boolean;
component: { key: string };
onBranchesChange: () => void;
@@ -57,7 +62,7 @@ export default class App extends React.PureComponent<Props, State> {
fetchPurgeSetting() {
this.setState({ loading: true });
- getValues(BRANCH_LIFETIME_SETTING).then(
+ getValues({ keys: BRANCH_LIFETIME_SETTING }).then(
settings => {
if (this.mounted) {
this.setState({
@@ -78,26 +83,29 @@ export default class App extends React.PureComponent<Props, State> {
return null;
}
- const messageKey = this.props.canAdmin
- ? 'project_branches.page.life_time.admin'
- : 'project_branches.page.life_time';
-
return (
<p className="page-description">
<FormattedMessage
- defaultMessage={translate(messageKey)}
- id={messageKey}
- values={{
- days: formatMeasure(this.state.branchLifeTime, 'INT'),
- settings: <Link to="/admin/settings">{translate('settings.page')}</Link>
- }}
+ defaultMessage={translate('project_branches.page.life_time')}
+ id="project_branches.page.life_time"
+ values={{ days: formatMeasure(this.state.branchLifeTime, 'INT') }}
/>
+ {this.props.canAdmin && (
+ <>
+ <br />
+ <FormattedMessage
+ defaultMessage={translate('project_branches.page.life_time.admin')}
+ id="project_branches.page.life_time.admin"
+ values={{ settings: <Link to="/admin/settings">{translate('settings.page')}</Link> }}
+ />
+ </>
+ )}
</p>
);
}
render() {
- const { branches, component, onBranchesChange } = this.props;
+ const { branchLikes, component, onBranchesChange } = this.props;
if (this.state.loading) {
return (
@@ -132,11 +140,15 @@ export default class App extends React.PureComponent<Props, State> {
</tr>
</thead>
<tbody>
- {sortBranchesAsTree(branches).map(branch => (
+ {sortBranchesAsTree(branchLikes).map(branchLike => (
<BranchRow
- branch={branch}
+ branchLike={branchLike}
component={component.key}
- key={branch.name}
+ isOrphan={
+ (isShortLivingBranch(branchLike) || isPullRequest(branchLike)) &&
+ branchLike.isOrphan
+ }
+ key={getBranchLikeKey(branchLike)}
onChange={onBranchesChange}
/>
))}
diff --git a/server/sonar-web/src/main/js/apps/projectBranches/components/BranchRow.tsx b/server/sonar-web/src/main/js/apps/projectBranches/components/BranchRow.tsx
index 723998c7049..0d5e440a372 100644
--- a/server/sonar-web/src/main/js/apps/projectBranches/components/BranchRow.tsx
+++ b/server/sonar-web/src/main/js/apps/projectBranches/components/BranchRow.tsx
@@ -22,10 +22,16 @@ import * as classNames from 'classnames';
import DeleteBranchModal from './DeleteBranchModal';
import LeakPeriodForm from './LeakPeriodForm';
import RenameBranchModal from './RenameBranchModal';
-import { Branch } from '../../../app/types';
+import { BranchLike } from '../../../app/types';
import BranchStatus from '../../../components/common/BranchStatus';
import BranchIcon from '../../../components/icons-components/BranchIcon';
-import { isShortLivingBranch, isLongLivingBranch } from '../../../helpers/branches';
+import {
+ isShortLivingBranch,
+ isLongLivingBranch,
+ isMainBranch,
+ getBranchLikeDisplayName,
+ isPullRequest
+} from '../../../helpers/branches';
import { translate } from '../../../helpers/l10n';
import DateFromNow from '../../../components/intl/DateFromNow';
import ActionsDropdown, {
@@ -34,8 +40,9 @@ import ActionsDropdown, {
} from '../../../components/controls/ActionsDropdown';
interface Props {
- branch: Branch;
+ branchLike: BranchLike;
component: string;
+ isOrphan?: boolean;
onChange: () => void;
}
@@ -91,19 +98,19 @@ export default class BranchRow extends React.PureComponent<Props, State> {
};
renderActions() {
- const { branch, component } = this.props;
+ const { branchLike, component } = this.props;
return (
<td className="thin nowrap text-right">
<ActionsDropdown className="ig-spacer-left">
- {isLongLivingBranch(branch) && (
+ {isLongLivingBranch(branchLike) && (
<ActionsDropdownItem
className="js-change-leak-period"
onClick={this.handleChangeLeakClick}>
{translate('branches.set_leak_period')}
</ActionsDropdownItem>
)}
- {isLongLivingBranch(branch) && !branch.isMain && <ActionsDropdownDivider />}
- {branch.isMain ? (
+ {isLongLivingBranch(branchLike) && <ActionsDropdownDivider />}
+ {isMainBranch(branchLike) ? (
<ActionsDropdownItem className="js-rename" onClick={this.handleRenameClick}>
{translate('branches.rename')}
</ActionsDropdownItem>
@@ -112,62 +119,65 @@ export default class BranchRow extends React.PureComponent<Props, State> {
className="js-delete"
destructive={true}
onClick={this.handleDeleteClick}>
- {translate('branches.delete')}
+ {translate(
+ isPullRequest(branchLike) ? 'branches.pull_request.delete' : 'branches.delete'
+ )}
</ActionsDropdownItem>
)}
</ActionsDropdown>
{this.state.deleting && (
<DeleteBranchModal
- branch={branch}
+ branchLike={branchLike}
component={component}
onClose={this.handleDeletingStop}
onDelete={this.handleChange}
/>
)}
- {this.state.renaming && (
- <RenameBranchModal
- branch={branch}
- component={component}
- onClose={this.handleRenamingStop}
- onRename={this.handleChange}
- />
- )}
+ {this.state.renaming &&
+ isMainBranch(branchLike) && (
+ <RenameBranchModal
+ branch={branchLike}
+ component={component}
+ onClose={this.handleRenamingStop}
+ onRename={this.handleChange}
+ />
+ )}
- {this.state.changingLeak && (
- <LeakPeriodForm
- branch={branch.name}
- onClose={this.handleChangingLeakStop}
- project={component}
- />
- )}
+ {this.state.changingLeak &&
+ isLongLivingBranch(branchLike) && (
+ <LeakPeriodForm
+ branch={branchLike.name}
+ onClose={this.handleChangingLeakStop}
+ project={component}
+ />
+ )}
</td>
);
}
render() {
- const { branch } = this.props;
+ const { branchLike, isOrphan } = this.props;
+ const indented = (isShortLivingBranch(branchLike) || isPullRequest(branchLike)) && !isOrphan;
return (
<tr>
<td>
<BranchIcon
- branch={branch}
- className={classNames('little-spacer-right', {
- 'big-spacer-left': isShortLivingBranch(branch) && !branch.isOrphan
- })}
+ branchLike={branchLike}
+ className={classNames('little-spacer-right', { 'big-spacer-left': indented })}
/>
- {branch.name}
- {branch.isMain && (
+ {getBranchLikeDisplayName(branchLike)}
+ {isMainBranch(branchLike) && (
<div className="outline-badge spacer-left">{translate('branches.main_branch')}</div>
)}
</td>
<td className="thin nowrap text-right">
- <BranchStatus branch={branch} />
+ <BranchStatus branchLike={branchLike} />
</td>
<td className="thin nowrap text-right">
- {branch.analysisDate && <DateFromNow date={branch.analysisDate} />}
+ {branchLike.analysisDate && <DateFromNow date={branchLike.analysisDate} />}
</td>
{this.renderActions()}
</tr>
diff --git a/server/sonar-web/src/main/js/apps/projectBranches/components/DeleteBranchModal.tsx b/server/sonar-web/src/main/js/apps/projectBranches/components/DeleteBranchModal.tsx
index 127b3781189..c98bb190f65 100644
--- a/server/sonar-web/src/main/js/apps/projectBranches/components/DeleteBranchModal.tsx
+++ b/server/sonar-web/src/main/js/apps/projectBranches/components/DeleteBranchModal.tsx
@@ -18,14 +18,15 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import { deleteBranch } from '../../../api/branches';
-import { Branch } from '../../../app/types';
+import { deleteBranch, deletePullRequest } from '../../../api/branches';
+import { BranchLike } from '../../../app/types';
import Modal from '../../../components/controls/Modal';
import { SubmitButton, ResetButtonLink } from '../../../components/ui/buttons';
import { translate, translateWithParameters } from '../../../helpers/l10n';
+import { isPullRequest, getBranchLikeDisplayName } from '../../../helpers/branches';
interface Props {
- branch: Branch;
+ branchLike: BranchLike;
component: string;
onClose: () => void;
onDelete: () => void;
@@ -50,7 +51,16 @@ export default class DeleteBranchModal extends React.PureComponent<Props, State>
handleSubmit = (event: React.SyntheticEvent<HTMLFormElement>) => {
event.preventDefault();
this.setState({ loading: true });
- deleteBranch(this.props.component, this.props.branch.name).then(
+ const request = isPullRequest(this.props.branchLike)
+ ? deletePullRequest({
+ project: this.props.component,
+ pullRequest: this.props.branchLike.key
+ })
+ : deleteBranch({
+ branch: this.props.branchLike.name,
+ project: this.props.component
+ });
+ request.then(
() => {
if (this.mounted) {
this.setState({ loading: false });
@@ -66,8 +76,10 @@ export default class DeleteBranchModal extends React.PureComponent<Props, State>
};
render() {
- const { branch } = this.props;
- const header = translate('branches.delete');
+ const { branchLike } = this.props;
+ const header = translate(
+ isPullRequest(branchLike) ? 'branches.pull_request.delete' : 'branches.delete'
+ );
return (
<Modal contentLabel={header} onRequestClose={this.props.onClose}>
@@ -76,7 +88,12 @@ export default class DeleteBranchModal extends React.PureComponent<Props, State>
</header>
<form onSubmit={this.handleSubmit}>
<div className="modal-body">
- {translateWithParameters('branches.delete.are_you_sure', branch.name)}
+ {translateWithParameters(
+ isPullRequest(branchLike)
+ ? 'branches.pull_request.delete.are_you_sure'
+ : 'branches.delete.are_you_sure',
+ getBranchLikeDisplayName(branchLike)
+ )}
</div>
<footer className="modal-foot">
{this.state.loading && <i className="spinner spacer-right" />}
diff --git a/server/sonar-web/src/main/js/apps/projectBranches/components/LeakPeriodForm.tsx b/server/sonar-web/src/main/js/apps/projectBranches/components/LeakPeriodForm.tsx
index 71d4deaa53f..3bb22c48aed 100644
--- a/server/sonar-web/src/main/js/apps/projectBranches/components/LeakPeriodForm.tsx
+++ b/server/sonar-web/src/main/js/apps/projectBranches/components/LeakPeriodForm.tsx
@@ -53,7 +53,7 @@ export default class LeakPeriodForm extends React.PureComponent<Props, State> {
fetchSetting() {
this.setState({ loading: true });
- getValues(LEAK_PERIOD, this.props.project, this.props.branch).then(
+ getValues({ keys: LEAK_PERIOD, component: this.props.project, branch: this.props.branch }).then(
settings => {
if (this.mounted) {
this.setState({ loading: false, setting: settings[0] });
diff --git a/server/sonar-web/src/main/js/apps/projectBranches/components/LongBranchesPattern.tsx b/server/sonar-web/src/main/js/apps/projectBranches/components/LongBranchesPattern.tsx
index e6d8b31d0f2..61c9e133c4c 100644
--- a/server/sonar-web/src/main/js/apps/projectBranches/components/LongBranchesPattern.tsx
+++ b/server/sonar-web/src/main/js/apps/projectBranches/components/LongBranchesPattern.tsx
@@ -48,7 +48,7 @@ export default class LongBranchesPattern extends React.PureComponent<Props, Stat
}
fetchSetting() {
- return getValues(LONG_BRANCH_PATTERN, this.props.project).then(
+ return getValues({ keys: LONG_BRANCH_PATTERN, component: this.props.project }).then(
settings => {
if (this.mounted) {
this.setState({ setting: settings[0] });
diff --git a/server/sonar-web/src/main/js/apps/projectBranches/components/RenameBranchModal.tsx b/server/sonar-web/src/main/js/apps/projectBranches/components/RenameBranchModal.tsx
index bc78f0395cb..2e8ee656d44 100644
--- a/server/sonar-web/src/main/js/apps/projectBranches/components/RenameBranchModal.tsx
+++ b/server/sonar-web/src/main/js/apps/projectBranches/components/RenameBranchModal.tsx
@@ -19,13 +19,13 @@
*/
import * as React from 'react';
import { renameBranch } from '../../../api/branches';
-import { Branch } from '../../../app/types';
+import { MainBranch } from '../../../app/types';
import Modal from '../../../components/controls/Modal';
import { SubmitButton, ResetButtonLink } from '../../../components/ui/buttons';
import { translate } from '../../../helpers/l10n';
interface Props {
- branch: Branch;
+ branch: MainBranch;
component: string;
onClose: () => void;
onRename: () => void;
diff --git a/server/sonar-web/src/main/js/apps/projectBranches/components/SettingForm.tsx b/server/sonar-web/src/main/js/apps/projectBranches/components/SettingForm.tsx
index 6c3b737dd4c..45533ab1da6 100644
--- a/server/sonar-web/src/main/js/apps/projectBranches/components/SettingForm.tsx
+++ b/server/sonar-web/src/main/js/apps/projectBranches/components/SettingForm.tsx
@@ -51,6 +51,12 @@ export default class SettingForm extends React.PureComponent<Props, State> {
this.mounted = false;
}
+ stopLoading = () => {
+ if (this.mounted) {
+ this.setState({ submitting: false });
+ }
+ };
+
handleSubmit = (event: React.SyntheticEvent<HTMLFormElement>) => {
event.preventDefault();
@@ -65,11 +71,7 @@ export default class SettingForm extends React.PureComponent<Props, State> {
component: this.props.project,
key: this.props.setting.key,
value
- }).then(this.props.onChange, () => {
- if (this.mounted) {
- this.setState({ submitting: false });
- }
- });
+ }).then(this.props.onChange, this.stopLoading);
};
handleValueChange = (event: React.SyntheticEvent<HTMLInputElement>) => {
@@ -78,14 +80,11 @@ export default class SettingForm extends React.PureComponent<Props, State> {
handleResetClick = () => {
this.setState({ submitting: true });
- resetSettingValue(this.props.setting.key, this.props.project, this.props.branch).then(
- this.props.onChange,
- () => {
- if (this.mounted) {
- this.setState({ submitting: false });
- }
- }
- );
+ resetSettingValue({
+ keys: this.props.setting.key,
+ component: this.props.project,
+ branch: this.props.branch
+ }).then(this.props.onChange, this.stopLoading);
};
render() {
diff --git a/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/App-test.tsx b/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/App-test.tsx
index e22b327bbc1..ee90b112e98 100644
--- a/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/App-test.tsx
+++ b/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/App-test.tsx
@@ -25,7 +25,13 @@ jest.mock('../../../../api/settings', () => ({
import * as React from 'react';
import { mount, shallow } from 'enzyme';
import App from '../App';
-import { BranchType, MainBranch, LongLivingBranch, ShortLivingBranch } from '../../../../app/types';
+import {
+ BranchType,
+ LongLivingBranch,
+ ShortLivingBranch,
+ MainBranch,
+ PullRequest
+} from '../../../../app/types';
const getValues = require('../../../../api/settings').getValues as jest.Mock<any>;
@@ -34,19 +40,27 @@ beforeEach(() => {
});
it('renders sorted list of branches', () => {
- const branches: [MainBranch, LongLivingBranch, ShortLivingBranch] = [
+ const branchLikes: [MainBranch, LongLivingBranch, ShortLivingBranch, PullRequest] = [
{ isMain: true, name: 'master' },
{ isMain: false, name: 'branch-1.0', type: BranchType.LONG },
- { isMain: false, name: 'branch-1.0', mergeBranch: 'master', type: BranchType.SHORT }
+ { isMain: false, mergeBranch: 'master', name: 'feature', type: BranchType.SHORT },
+ { base: 'master', branch: 'feature', key: '1234', title: 'Feature PR' }
];
const wrapper = shallow(
- <App branches={branches} component={{ key: 'foo' }} onBranchesChange={jest.fn()} />
+ <App
+ branchLikes={branchLikes}
+ canAdmin={true}
+ component={{ key: 'foo' }}
+ onBranchesChange={jest.fn()}
+ />
);
wrapper.setState({ branchLifeTime: '100', loading: false });
expect(wrapper).toMatchSnapshot();
});
it('fetches branch life time setting on mount', () => {
- mount(<App branches={[]} component={{ key: 'foo' }} onBranchesChange={jest.fn()} />);
- expect(getValues).toBeCalledWith('sonar.dbcleaner.daysBeforeDeletingInactiveShortLivingBranches');
+ mount(<App branchLikes={[]} component={{ key: 'foo' }} onBranchesChange={jest.fn()} />);
+ expect(getValues).toBeCalledWith({
+ keys: 'sonar.dbcleaner.daysBeforeDeletingInactiveShortLivingBranches'
+ });
});
diff --git a/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/BranchRow-test.tsx b/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/BranchRow-test.tsx
index 8d7532033cc..eac799c7f6f 100644
--- a/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/BranchRow-test.tsx
+++ b/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/BranchRow-test.tsx
@@ -20,7 +20,13 @@
import * as React from 'react';
import { shallow } from 'enzyme';
import BranchRow from '../BranchRow';
-import { MainBranch, ShortLivingBranch, BranchType } from '../../../../app/types';
+import {
+ MainBranch,
+ ShortLivingBranch,
+ BranchType,
+ PullRequest,
+ BranchLike
+} from '../../../../app/types';
import { click } from '../../../../helpers/testUtils';
const mainBranch: MainBranch = { isMain: true, name: 'master' };
@@ -33,6 +39,13 @@ const shortBranch: ShortLivingBranch = {
type: BranchType.SHORT
};
+const pullRequest: PullRequest = {
+ base: 'master',
+ branch: 'feature',
+ key: '1234',
+ title: 'Feature PR'
+};
+
it('renders main branch', () => {
expect(shallowRender(mainBranch)).toMatchSnapshot();
});
@@ -41,6 +54,10 @@ it('renders short-living branch', () => {
expect(shallowRender(shortBranch)).toMatchSnapshot();
});
+it('renders pull request', () => {
+ expect(shallowRender(pullRequest)).toMatchSnapshot();
+});
+
it('renames main branch', () => {
const onChange = jest.fn();
const wrapper = shallowRender(mainBranch, onChange);
@@ -59,8 +76,19 @@ it('deletes short-living branch', () => {
expect(onChange).toBeCalled();
});
-function shallowRender(branch: MainBranch | ShortLivingBranch, onChange: () => void = jest.fn()) {
- const wrapper = shallow(<BranchRow branch={branch} component="foo" onChange={onChange} />);
+it('deletes pull request', () => {
+ const onChange = jest.fn();
+ const wrapper = shallowRender(pullRequest, onChange);
+
+ click(wrapper.find('.js-delete'));
+ (wrapper.find('DeleteBranchModal').prop('onDelete') as Function)();
+ expect(onChange).toBeCalled();
+});
+
+function shallowRender(branchLike: BranchLike, onChange: () => void = jest.fn()) {
+ const wrapper = shallow(
+ <BranchRow branchLike={branchLike} component="foo" isOrphan={false} onChange={onChange} />
+ );
(wrapper.instance() as any).mounted = true;
return wrapper;
}
diff --git a/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/DeleteBranchModal-test.tsx b/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/DeleteBranchModal-test.tsx
index 88fd8dffca7..16d7dfaf5cf 100644
--- a/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/DeleteBranchModal-test.tsx
+++ b/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/DeleteBranchModal-test.tsx
@@ -18,42 +18,72 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/* eslint-disable import/first */
-jest.mock('../../../../api/branches', () => ({ deleteBranch: jest.fn() }));
+jest.mock('../../../../api/branches', () => ({
+ deleteBranch: jest.fn(),
+ deletePullRequest: jest.fn()
+}));
import * as React from 'react';
import { shallow, ShallowWrapper } from 'enzyme';
import DeleteBranchModal from '../DeleteBranchModal';
-import { ShortLivingBranch, BranchType } from '../../../../app/types';
+import { ShortLivingBranch, BranchType, BranchLike, PullRequest } from '../../../../app/types';
import { submit, doAsync, click, waitAndUpdate } from '../../../../helpers/testUtils';
-import { deleteBranch } from '../../../../api/branches';
+import { deleteBranch, deletePullRequest } from '../../../../api/branches';
+
+const branch: ShortLivingBranch = {
+ isMain: false,
+ name: 'feature',
+ mergeBranch: 'master',
+ type: BranchType.SHORT
+};
beforeEach(() => {
- (deleteBranch as jest.Mock<any>).mockClear();
+ (deleteBranch as jest.Mock).mockClear();
+ (deletePullRequest as jest.Mock).mockClear();
});
it('renders', () => {
- const wrapper = shallowRender();
+ const wrapper = shallowRender(branch);
expect(wrapper).toMatchSnapshot();
wrapper.setState({ loading: true });
expect(wrapper).toMatchSnapshot();
});
it('deletes branch', async () => {
- (deleteBranch as jest.Mock<any>).mockImplementation(() => Promise.resolve());
+ (deleteBranch as jest.Mock).mockImplementationOnce(() => Promise.resolve());
+ const onDelete = jest.fn();
+ const wrapper = shallowRender(branch, onDelete);
+
+ submitForm(wrapper);
+
+ await waitAndUpdate(wrapper);
+ expect(wrapper.state().loading).toBe(false);
+ expect(onDelete).toBeCalled();
+ expect(deleteBranch).toBeCalledWith({ branch: 'feature', project: 'foo' });
+});
+
+it('deletes pull request', async () => {
+ (deletePullRequest as jest.Mock).mockImplementationOnce(() => Promise.resolve());
+ const pullRequest: PullRequest = {
+ base: 'master',
+ branch: 'feature',
+ key: '1234',
+ title: 'Feature PR'
+ };
const onDelete = jest.fn();
- const wrapper = shallowRender(onDelete);
+ const wrapper = shallowRender(pullRequest, onDelete);
submitForm(wrapper);
await waitAndUpdate(wrapper);
expect(wrapper.state().loading).toBe(false);
expect(onDelete).toBeCalled();
- expect(deleteBranch).toBeCalledWith('foo', 'feature');
+ expect(deletePullRequest).toBeCalledWith({ project: 'foo', pullRequest: '1234' });
});
it('cancels', () => {
const onClose = jest.fn();
- const wrapper = shallowRender(jest.fn(), onClose);
+ const wrapper = shallowRender(branch, jest.fn(), onClose);
click(wrapper.find('ResetButtonLink'));
@@ -63,27 +93,30 @@ it('cancels', () => {
});
it('stops loading on WS error', async () => {
- (deleteBranch as jest.Mock<any>).mockImplementation(() => Promise.reject(null));
+ (deleteBranch as jest.Mock).mockImplementationOnce(() => Promise.reject(null));
const onDelete = jest.fn();
- const wrapper = shallowRender(onDelete);
+ const wrapper = shallowRender(branch, onDelete);
submitForm(wrapper);
await waitAndUpdate(wrapper);
expect(wrapper.state().loading).toBe(false);
expect(onDelete).not.toBeCalled();
- expect(deleteBranch).toBeCalledWith('foo', 'feature');
+ expect(deleteBranch).toBeCalledWith({ branch: 'feature', project: 'foo' });
});
-function shallowRender(onDelete: () => void = jest.fn(), onClose: () => void = jest.fn()) {
- const branch: ShortLivingBranch = {
- isMain: false,
- name: 'feature',
- mergeBranch: 'master',
- type: BranchType.SHORT
- };
+function shallowRender(
+ branchLike: BranchLike,
+ onDelete: () => void = jest.fn(),
+ onClose: () => void = jest.fn()
+) {
const wrapper = shallow(
- <DeleteBranchModal branch={branch} component="foo" onClose={onClose} onDelete={onDelete} />
+ <DeleteBranchModal
+ branchLike={branchLike}
+ component="foo"
+ onClose={onClose}
+ onDelete={onDelete}
+ />
);
(wrapper.instance() as any).mounted = true;
return wrapper;
diff --git a/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/LongBranchesPattern-test.tsx b/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/LongBranchesPattern-test.tsx
index 0e2a0bf65a7..5c19f9f989f 100644
--- a/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/LongBranchesPattern-test.tsx
+++ b/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/LongBranchesPattern-test.tsx
@@ -53,7 +53,10 @@ it('opens form', () => {
it('fetches setting value on mount', () => {
shallow(<LongBranchesPattern project="project" />);
- expect(getValues).lastCalledWith('sonar.branch.longLivedBranches.regex', 'project');
+ expect(getValues).lastCalledWith({
+ keys: 'sonar.branch.longLivedBranches.regex',
+ component: 'project'
+ });
});
it('fetches new setting value after change', () => {
diff --git a/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/SettingForm-test.tsx b/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/SettingForm-test.tsx
index 6a107645fc0..4989daa203d 100644
--- a/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/SettingForm-test.tsx
+++ b/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/SettingForm-test.tsx
@@ -76,7 +76,11 @@ it('resets value', async () => {
expect(wrapper).toMatchSnapshot();
click(wrapper.find('Button'));
- expect(resetSettingValue).toBeCalledWith('foo', 'project', undefined);
+ expect(resetSettingValue).toBeCalledWith({
+ keys: 'foo',
+ component: 'project',
+ branch: undefined
+ });
await new Promise(setImmediate);
expect(onChange).toBeCalled();
diff --git a/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/__snapshots__/App-test.tsx.snap b/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/__snapshots__/App-test.tsx.snap
index 1c14e27cdbe..a9d71179c0c 100644
--- a/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/__snapshots__/App-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/__snapshots__/App-test.tsx.snap
@@ -29,16 +29,27 @@ exports[`renders sorted list of branches 1`] = `
values={
Object {
"days": "100",
- "settings": <Link
- onlyActiveOnIndex={false}
- style={Object {}}
- to="/admin/settings"
- >
- settings.page
- </Link>,
}
}
/>
+ <React.Fragment>
+ <br />
+ <FormattedMessage
+ defaultMessage="project_branches.page.life_time.admin"
+ id="project_branches.page.life_time.admin"
+ values={
+ Object {
+ "settings": <Link
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to="/admin/settings"
+ >
+ settings.page
+ </Link>,
+ }
+ }
+ />
+ </React.Fragment>
</p>
</header>
<div
@@ -71,31 +82,45 @@ exports[`renders sorted list of branches 1`] = `
</thead>
<tbody>
<BranchRow
- branch={
+ branchLike={
Object {
"isMain": true,
"name": "master",
}
}
component="foo"
- key="master"
+ isOrphan={false}
+ key="branch-master"
+ onChange={[MockFunction]}
+ />
+ <BranchRow
+ branchLike={
+ Object {
+ "base": "master",
+ "branch": "feature",
+ "key": "1234",
+ "title": "Feature PR",
+ }
+ }
+ component="foo"
+ key="pull-request-1234"
onChange={[MockFunction]}
/>
<BranchRow
- branch={
+ branchLike={
Object {
"isMain": false,
"mergeBranch": "master",
- "name": "branch-1.0",
+ "name": "feature",
"type": "SHORT",
}
}
component="foo"
- key="branch-1.0"
+ key="branch-feature"
onChange={[MockFunction]}
/>
<BranchRow
- branch={
+ branchLike={
Object {
"isMain": false,
"name": "branch-1.0",
@@ -103,7 +128,8 @@ exports[`renders sorted list of branches 1`] = `
}
}
component="foo"
- key="branch-1.0"
+ isOrphan={false}
+ key="branch-branch-1.0"
onChange={[MockFunction]}
/>
</tbody>
diff --git a/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/__snapshots__/BranchRow-test.tsx.snap b/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/__snapshots__/BranchRow-test.tsx.snap
index 5a28f352855..d7e94ca3b2e 100644
--- a/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/__snapshots__/BranchRow-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/__snapshots__/BranchRow-test.tsx.snap
@@ -4,7 +4,7 @@ exports[`renders main branch 1`] = `
<tr>
<td>
<BranchIcon
- branch={
+ branchLike={
Object {
"isMain": true,
"name": "master",
@@ -23,7 +23,7 @@ exports[`renders main branch 1`] = `
className="thin nowrap text-right"
>
<BranchStatus
- branch={
+ branchLike={
Object {
"isMain": true,
"name": "master",
@@ -51,11 +51,62 @@ exports[`renders main branch 1`] = `
</tr>
`;
+exports[`renders pull request 1`] = `
+<tr>
+ <td>
+ <BranchIcon
+ branchLike={
+ Object {
+ "base": "master",
+ "branch": "feature",
+ "key": "1234",
+ "title": "Feature PR",
+ }
+ }
+ className="little-spacer-right big-spacer-left"
+ />
+ 1234 – Feature PR
+ </td>
+ <td
+ className="thin nowrap text-right"
+ >
+ <BranchStatus
+ branchLike={
+ Object {
+ "base": "master",
+ "branch": "feature",
+ "key": "1234",
+ "title": "Feature PR",
+ }
+ }
+ />
+ </td>
+ <td
+ className="thin nowrap text-right"
+ />
+ <td
+ className="thin nowrap text-right"
+ >
+ <ActionsDropdown
+ className="ig-spacer-left"
+ >
+ <ActionsDropdownItem
+ className="js-delete"
+ destructive={true}
+ onClick={[Function]}
+ >
+ branches.pull_request.delete
+ </ActionsDropdownItem>
+ </ActionsDropdown>
+ </td>
+</tr>
+`;
+
exports[`renders short-living branch 1`] = `
<tr>
<td>
<BranchIcon
- branch={
+ branchLike={
Object {
"analysisDate": "2017-09-27T00:05:19+0000",
"isMain": false,
@@ -72,7 +123,7 @@ exports[`renders short-living branch 1`] = `
className="thin nowrap text-right"
>
<BranchStatus
- branch={
+ branchLike={
Object {
"analysisDate": "2017-09-27T00:05:19+0000",
"isMain": false,
diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Risk-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Risk-test.tsx.snap
index 505f5276139..733d95af43f 100644
--- a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Risk-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Risk-test.tsx.snap
@@ -18,7 +18,6 @@ exports[`renders 1`] = `
"link": Object {
"pathname": "/dashboard",
"query": Object {
- "branch": undefined,
"id": "foo",
},
},
diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/SimpleBubbleChart-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/SimpleBubbleChart-test.tsx.snap
index afb4e501021..b6e9c87e444 100644
--- a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/SimpleBubbleChart-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/SimpleBubbleChart-test.tsx.snap
@@ -18,7 +18,6 @@ exports[`renders 1`] = `
"link": Object {
"pathname": "/dashboard",
"query": Object {
- "branch": undefined,
"id": "foo",
},
},
diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/ProjectRowActions.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/ProjectRowActions.tsx
index b43fd2d96fa..2c58557ac4f 100644
--- a/server/sonar-web/src/main/js/apps/projectsManagement/ProjectRowActions.tsx
+++ b/server/sonar-web/src/main/js/apps/projectsManagement/ProjectRowActions.tsx
@@ -57,8 +57,8 @@ export default class ProjectRowActions extends React.PureComponent<Props, State>
// call `getComponentNavigation` to check if user has the "Administer" permission
// call `getComponentShow` to check if user has the "Browse" permission
Promise.all([
- getComponentNavigation(this.props.project.key),
- getComponentShow(this.props.project.key)
+ getComponentNavigation({ componentKey: this.props.project.key }),
+ getComponentShow({ component: this.props.project.key })
]).then(
([navResponse]) => {
if (this.mounted) {
diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/CreateProjectForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/CreateProjectForm-test.tsx.snap
index deec1e2e7fd..b0b662adaae 100644
--- a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/CreateProjectForm-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/CreateProjectForm-test.tsx.snap
@@ -385,7 +385,6 @@ exports[`creates project 4`] = `
Object {
"pathname": "/dashboard",
"query": Object {
- "branch": undefined,
"id": "name",
},
}
diff --git a/server/sonar-web/src/main/js/apps/settings/store/actions.js b/server/sonar-web/src/main/js/apps/settings/store/actions.js
index 7865fd56313..dcbf19f63dd 100644
--- a/server/sonar-web/src/main/js/apps/settings/store/actions.js
+++ b/server/sonar-web/src/main/js/apps/settings/store/actions.js
@@ -50,10 +50,10 @@ export const fetchSettings = componentKey => dispatch => {
);
};
-export const fetchValues = (keys, componentKey) => dispatch =>
- getValues(keys, componentKey).then(
+export const fetchValues = (keys, component) => dispatch =>
+ getValues({ keys, component }).then(
settings => {
- dispatch(receiveValues(settings, componentKey));
+ dispatch(receiveValues(settings, component));
dispatch(closeAllGlobalMessages());
},
() => {}
@@ -73,7 +73,7 @@ export const saveValue = (key, componentKey) => (dispatch, getState) => {
}
return setSettingValue(definition, value, componentKey)
- .then(() => getValues(key, componentKey))
+ .then(() => getValues({ keys: key, component: componentKey }))
.then(values => {
dispatch(receiveValues(values, componentKey));
dispatch(cancelChange(key));
@@ -90,8 +90,8 @@ export const saveValue = (key, componentKey) => (dispatch, getState) => {
export const resetValue = (key, componentKey) => dispatch => {
dispatch(startLoading(key));
- return resetSettingValue(key, componentKey)
- .then(() => getValues(key, componentKey))
+ return resetSettingValue({ keys: key, component: componentKey })
+ .then(() => getValues({ keys: key, component: componentKey }))
.then(values => {
if (values.length > 0) {
dispatch(receiveValues(values, componentKey));