From 7ae08845cfe069d2a4df4d788e9713dc25900f3c Mon Sep 17 00:00:00 2001 From: Wouter Admiraal Date: Tue, 2 Apr 2019 10:22:14 +0200 Subject: [PATCH] SONAR-11875 Connect Measures to the branch redux store, and refresh branch status when updating an issue --- .../component-measures/components/App.tsx | 25 +++++++- .../components/MeasureContent.tsx | 7 ++- .../components/MeasureOverview.tsx | 7 ++- .../components/MeasureOverviewContainer.tsx | 2 + .../components/__tests__/App-test.tsx | 62 ++++++++++++------- .../__tests__/__snapshots__/App-test.tsx.snap | 25 ++++++++ 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; } interface State { @@ -203,6 +206,10 @@ export class App extends React.PureComponent { return metric; }; + handleIssueChange = (_: T.Issue) => { + this.refreshBranchStatus(); + }; + updateQuery = (newQuery: Partial) => { const query: Query = { ...parseQuery(this.props.location.query), ...newQuery }; @@ -225,6 +232,13 @@ export class App extends React.PureComponent { }); }; + 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 { 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 { 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 { } } -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; metrics: T.Dict; + onIssueChange?: (issue: T.Issue) => void; rootComponent: T.ComponentMeasure; router: InjectedRouter; selected?: string; @@ -361,7 +362,11 @@ export default class MeasureContent extends React.PureComponent { /> {isFile ? (
- +
) : ( 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; + onIssueChange?: (issue: T.Issue) => void; rootComponent: T.ComponentMeasure; updateLoading: (param: T.Dict) => void; updateSelected: (component: string) => void; @@ -117,7 +118,11 @@ export default class MeasureOverview extends React.PureComponent { if (isFileType(component)) { return (
- +
); } 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; + onIssueChange?: (issue: T.Issue) => void; rootComponent: T.ComponentMeasure; router: InjectedRouter; selected?: string; @@ -124,6 +125,7 @@ export default class MeasureOverviewContainer extends React.PureComponent ({ 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(); + 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( - - ); + 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(); + const wrapper = shallowRender(); await waitAndUpdate(wrapper); expect(wrapper).toMatchSnapshot(); }); it('should not render drilldown for estimated duplications', async () => { - const wrapper = shallow(); + 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 = {}) { + return shallow( + + ); +} 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="" -- 2.39.5