diff options
Diffstat (limited to 'server/sonar-web/src/main/js/apps/component-measures/components')
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="" |