aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web/src/main/js/apps/component-measures
diff options
context:
space:
mode:
authorWouter Admiraal <wouter.admiraal@sonarsource.com>2019-04-02 10:22:14 +0200
committerSonarTech <sonartech@sonarsource.com>2019-05-29 20:21:13 +0200
commit7ae08845cfe069d2a4df4d788e9713dc25900f3c (patch)
tree737c65a3b4461220a529fe020cae5e7143a5c8db /server/sonar-web/src/main/js/apps/component-measures
parent0c1596629ea6ca26f207058953f0b58093e0f611 (diff)
downloadsonarqube-7ae08845cfe069d2a4df4d788e9713dc25900f3c.tar.gz
sonarqube-7ae08845cfe069d2a4df4d788e9713dc25900f3c.zip
SONAR-11875 Connect Measures to the branch redux store, and refresh branch status when updating an issue
Diffstat (limited to 'server/sonar-web/src/main/js/apps/component-measures')
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/components/App.tsx25
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/components/MeasureContent.tsx7
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverview.tsx7
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverviewContainer.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/components/__tests__/App-test.tsx62
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/App-test.tsx.snap25
6 files changed, 103 insertions, 25 deletions
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/App.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/App.tsx
index c55fae9ab8c..1b44f045888 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/components/App.tsx
+++ b/server/sonar-web/src/main/js/apps/component-measures/components/App.tsx
@@ -19,6 +19,7 @@
*/
import * as React from 'react';
import * as key from 'keymaster';
+import { connect } from 'react-redux';
import { withRouter, WithRouterProps } from 'react-router';
import Helmet from 'react-helmet';
import { keyBy } from 'lodash';
@@ -64,10 +65,12 @@ import { getAllMetrics } from '../../../api/metrics';
import { getMeasuresAndMeta } from '../../../api/measures';
import { enhanceMeasure } from '../../../components/measure/utils';
import { getLeakPeriod } from '../../../helpers/periods';
+import { fetchBranchStatus } from '../../../store/rootActions';
interface Props extends WithRouterProps {
branchLike?: T.BranchLike;
component: T.ComponentMeasure;
+ fetchBranchStatus: (branchLike: T.BranchLike, projectKey: string) => Promise<void>;
}
interface State {
@@ -203,6 +206,10 @@ export class App extends React.PureComponent<Props, State> {
return metric;
};
+ handleIssueChange = (_: T.Issue) => {
+ this.refreshBranchStatus();
+ };
+
updateQuery = (newQuery: Partial<Query>) => {
const query: Query = { ...parseQuery(this.props.location.query), ...newQuery };
@@ -225,6 +232,13 @@ export class App extends React.PureComponent<Props, State> {
});
};
+ refreshBranchStatus = () => {
+ const { branchLike, component } = this.props;
+ if (branchLike && component && (isPullRequest(branchLike) || isShortLivingBranch(branchLike))) {
+ this.props.fetchBranchStatus(branchLike, component.key);
+ }
+ };
+
renderContent = (displayOverview: boolean, query: Query, metric?: T.Metric) => {
const { branchLike, component } = this.props;
const { leakPeriod } = this.state;
@@ -236,6 +250,7 @@ export class App extends React.PureComponent<Props, State> {
domain={query.metric}
leakPeriod={leakPeriod}
metrics={this.state.metrics}
+ onIssueChange={this.handleIssueChange}
rootComponent={component}
router={this.props.router}
selected={query.selected}
@@ -267,6 +282,7 @@ export class App extends React.PureComponent<Props, State> {
branchLike={branchLike}
leakPeriod={leakPeriod}
metrics={this.state.metrics}
+ onIssueChange={this.handleIssueChange}
requestedMetric={metric}
rootComponent={component}
router={this.props.router}
@@ -322,4 +338,11 @@ export class App extends React.PureComponent<Props, State> {
}
}
-export default withRouter(App);
+const mapDispatchToProps = { fetchBranchStatus: fetchBranchStatus as any };
+
+export default withRouter(
+ connect(
+ null,
+ mapDispatchToProps
+ )(App)
+);
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContent.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContent.tsx
index ff9052a42d4..7505f27e422 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContent.tsx
+++ b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContent.tsx
@@ -43,6 +43,7 @@ interface Props {
leakPeriod?: T.Period;
requestedMetric: Pick<T.Metric, 'key' | 'direction'>;
metrics: T.Dict<T.Metric>;
+ onIssueChange?: (issue: T.Issue) => void;
rootComponent: T.ComponentMeasure;
router: InjectedRouter;
selected?: string;
@@ -361,7 +362,11 @@ export default class MeasureContent extends React.PureComponent<Props, State> {
/>
{isFile ? (
<div className="measure-details-viewer">
- <SourceViewer branchLike={branchLike} component={baseComponent.key} />
+ <SourceViewer
+ branchLike={branchLike}
+ component={baseComponent.key}
+ onIssueChange={this.props.onIssueChange}
+ />
</div>
) : (
this.renderMeasure()
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverview.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverview.tsx
index 53fc28de992..b53ebb6112e 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverview.tsx
+++ b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverview.tsx
@@ -38,6 +38,7 @@ interface Props {
leakPeriod?: T.Period;
loading: boolean;
metrics: T.Dict<T.Metric>;
+ onIssueChange?: (issue: T.Issue) => void;
rootComponent: T.ComponentMeasure;
updateLoading: (param: T.Dict<boolean>) => void;
updateSelected: (component: string) => void;
@@ -117,7 +118,11 @@ export default class MeasureOverview extends React.PureComponent<Props, State> {
if (isFileType(component)) {
return (
<div className="measure-details-viewer">
- <SourceViewer branchLike={branchLike} component={component.key} />
+ <SourceViewer
+ branchLike={branchLike}
+ component={component.key}
+ onIssueChange={this.props.onIssueChange}
+ />
</div>
);
}
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverviewContainer.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverviewContainer.tsx
index 4808cc21741..647f9f838c0 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverviewContainer.tsx
+++ b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverviewContainer.tsx
@@ -31,6 +31,7 @@ interface Props {
domain: string;
leakPeriod?: T.Period;
metrics: T.Dict<T.Metric>;
+ onIssueChange?: (issue: T.Issue) => void;
rootComponent: T.ComponentMeasure;
router: InjectedRouter;
selected?: string;
@@ -124,6 +125,7 @@ export default class MeasureOverviewContainer extends React.PureComponent<Props,
leakPeriod={this.props.leakPeriod}
loading={this.state.loading.component || this.state.loading.bubbles}
metrics={this.props.metrics}
+ onIssueChange={this.props.onIssueChange}
rootComponent={this.props.rootComponent}
updateLoading={this.updateLoading}
updateSelected={this.updateSelected}
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/App-test.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/App-test.tsx
index 108c9a45d0b..277aab23023 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/App-test.tsx
+++ b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/App-test.tsx
@@ -19,11 +19,17 @@
*/
import * as React from 'react';
import { shallow } from 'enzyme';
-import { Location } from 'history';
import { App } from '../App';
import { waitAndUpdate } from '../../../../helpers/testUtils';
import { getMeasuresAndMeta } from '../../../../api/measures';
-import { mockPullRequest, mockMainBranch } from '../../../../helpers/testMocks';
+import {
+ mockPullRequest,
+ mockMainBranch,
+ mockRouter,
+ mockLocation,
+ mockComponent,
+ mockIssue
+} from '../../../../helpers/testMocks';
jest.mock('../../../../api/metrics', () => ({
getAllMetrics: jest.fn().mockResolvedValue([
@@ -62,17 +68,6 @@ jest.mock('../../../../api/measures', () => ({
getMeasuresAndMeta: jest.fn()
}));
-const COMPONENT = { key: 'foo', name: 'Foo', qualifier: 'TRK' };
-
-const PROPS: App['props'] = {
- branchLike: mockMainBranch(),
- component: COMPONENT,
- location: { pathname: '/component_measures', query: { metric: 'coverage' } } as Location,
- params: {},
- router: { push: jest.fn() } as any,
- routes: []
-};
-
beforeEach(() => {
(getMeasuresAndMeta as jest.Mock).mockResolvedValue({
component: { measures: [{ metric: 'coverage', value: '80.0' }] },
@@ -81,19 +76,16 @@ beforeEach(() => {
});
it('should render correctly', async () => {
- const wrapper = shallow(<App {...PROPS} />);
+ const wrapper = shallowRender();
expect(wrapper.find('.spinner')).toHaveLength(1);
await waitAndUpdate(wrapper);
expect(wrapper).toMatchSnapshot();
});
it('should render a measure overview', async () => {
- const wrapper = shallow(
- <App
- {...PROPS}
- location={{ pathname: '/component_measures', query: { metric: 'Reliability' } } as Location}
- />
- );
+ const wrapper = shallowRender({
+ location: mockLocation({ pathname: '/component_measures', query: { metric: 'Reliability' } })
+ });
expect(wrapper.find('.spinner')).toHaveLength(1);
await waitAndUpdate(wrapper);
expect(wrapper.find('MeasureOverviewContainer')).toHaveLength(1);
@@ -104,13 +96,39 @@ it('should render a message when there are no measures', async () => {
component: { measures: [] },
periods: [{ index: '1' }]
});
- const wrapper = shallow(<App {...PROPS} />);
+ const wrapper = shallowRender();
await waitAndUpdate(wrapper);
expect(wrapper).toMatchSnapshot();
});
it('should not render drilldown for estimated duplications', async () => {
- const wrapper = shallow(<App {...PROPS} branchLike={mockPullRequest({ title: '' })} />);
+ const wrapper = shallowRender({ branchLike: mockPullRequest({ title: '' }) });
await waitAndUpdate(wrapper);
expect(wrapper).toMatchSnapshot();
});
+
+it('should refresh branch status if issues are updated', async () => {
+ const fetchBranchStatus = jest.fn();
+ const branchLike = mockPullRequest();
+ const wrapper = shallowRender({ branchLike, fetchBranchStatus });
+ const instance = wrapper.instance();
+ await waitAndUpdate(wrapper);
+
+ instance.handleIssueChange(mockIssue());
+ expect(fetchBranchStatus).toBeCalledWith(branchLike, 'foo');
+});
+
+function shallowRender(props: Partial<App['props']> = {}) {
+ return shallow<App>(
+ <App
+ branchLike={mockMainBranch()}
+ component={mockComponent({ key: 'foo', name: 'Foo' })}
+ fetchBranchStatus={jest.fn()}
+ location={mockLocation({ pathname: '/component_measures', query: { metric: 'coverage' } })}
+ params={{}}
+ router={mockRouter()}
+ routes={[]}
+ {...props}
+ />
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/App-test.tsx.snap b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/App-test.tsx.snap
index 2de9484e8fd..8b6cc89cb9a 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/App-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/App-test.tsx.snap
@@ -113,6 +113,7 @@ exports[`should render correctly 1`] = `
},
}
}
+ onIssueChange={[Function]}
requestedMetric={
Object {
"domain": "Coverage",
@@ -124,14 +125,38 @@ exports[`should render correctly 1`] = `
}
rootComponent={
Object {
+ "breadcrumbs": Array [],
"key": "foo",
"name": "Foo",
+ "organization": "foo",
"qualifier": "TRK",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
}
}
router={
Object {
+ "createHref": [MockFunction],
+ "createPath": [MockFunction],
+ "go": [MockFunction],
+ "goBack": [MockFunction],
+ "goForward": [MockFunction],
+ "isActive": [MockFunction],
"push": [MockFunction],
+ "replace": [MockFunction],
+ "setRouteLeaveHook": [MockFunction],
}
}
selected=""