From 8e2eb6188bb66cd35642b8c21f5677887fa972f7 Mon Sep 17 00:00:00 2001 From: Stas Vilchik Date: Tue, 29 Aug 2017 11:54:11 +0200 Subject: [PATCH] fix governance pages when using branches (#2446) --- ...ctContainer.tsx => ComponentContainer.tsx} | 21 +++++----- ...und.tsx => ComponentContainerNotFound.tsx} | 2 +- ...r-test.tsx => ComponentContainer-test.tsx} | 39 +++++++++++-------- ...iewDashboard.js => PortfolioDashboard.tsx} | 13 +++++-- .../components/nav/component/ComponentNav.tsx | 17 ++++---- .../nav/component/ComponentNavMenu.tsx | 39 +++++++++++++------ .../nav/component/ComponentNavMeta.tsx | 8 ++-- .../src/main/js/app/utils/startReactApp.js | 6 +-- .../src/main/js/apps/code/components/App.tsx | 19 ++++++--- .../apps/component-measures/components/App.js | 14 ++++--- .../main/js/apps/overview/components/App.js | 4 +- .../apps/overview/components/OverviewApp.js | 8 ++-- .../components/ProjectActivityAppContainer.js | 18 ++++++--- .../settings/components/DefinitionsList.js | 2 +- 14 files changed, 128 insertions(+), 82 deletions(-) rename server/sonar-web/src/main/js/app/components/{ProjectContainer.tsx => ComponentContainer.tsx} (91%) rename server/sonar-web/src/main/js/app/components/{ProjectContainerNotFound.tsx => ComponentContainerNotFound.tsx} (95%) rename server/sonar-web/src/main/js/app/components/__tests__/{ProjectContainer-test.tsx => ComponentContainer-test.tsx} (81%) rename server/sonar-web/src/main/js/app/components/extensions/{ViewDashboard.js => PortfolioDashboard.tsx} (81%) diff --git a/server/sonar-web/src/main/js/app/components/ProjectContainer.tsx b/server/sonar-web/src/main/js/app/components/ComponentContainer.tsx similarity index 91% rename from server/sonar-web/src/main/js/app/components/ProjectContainer.tsx rename to server/sonar-web/src/main/js/app/components/ComponentContainer.tsx index 804c420ee88..27ccf26396e 100644 --- a/server/sonar-web/src/main/js/app/components/ProjectContainer.tsx +++ b/server/sonar-web/src/main/js/app/components/ComponentContainer.tsx @@ -18,7 +18,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import * as React from 'react'; -import ProjectContainerNotFound from './ProjectContainerNotFound'; +import ComponentContainerNotFound from './ComponentContainerNotFound'; import ComponentNav from './nav/component/ComponentNav'; import { Branch, Component } from '../types'; import handleRequiredAuthorization from '../utils/handleRequiredAuthorization'; @@ -39,7 +39,7 @@ interface State { component: Component | null; } -export default class ProjectContainer extends React.PureComponent { +export default class ComponentContainer extends React.PureComponent { mounted: boolean; constructor(props: Props) { @@ -49,12 +49,12 @@ export default class ProjectContainer extends React.PureComponent componentDidMount() { this.mounted = true; - this.fetchProject(); + this.fetchComponent(); } componentDidUpdate(prevProps: Props) { if (prevProps.location.query.id !== this.props.location.query.id) { - this.fetchProject(); + this.fetchComponent(); } } @@ -67,7 +67,7 @@ export default class ProjectContainer extends React.PureComponent qualifier: component.breadcrumbs[component.breadcrumbs.length - 1].qualifier }); - fetchProject() { + fetchComponent() { const { branch, id } = this.props.location.query; this.setState({ loading: true }); @@ -96,7 +96,7 @@ export default class ProjectContainer extends React.PureComponent return project ? getBranches(project.key) : Promise.resolve([]); }; - handleProjectChange = (changes: {}) => { + handleComponentChange = (changes: {}) => { if (this.mounted) { this.setState(state => ({ component: { ...state.component, ...changes } })); } @@ -123,12 +123,11 @@ export default class ProjectContainer extends React.PureComponent return ; } - const branch = branches.find(b => (query.branch ? b.name === query.branch : b.isMain)); - - if (!component || !branch) { - return ; + if (!component) { + return ; } + const branch = branches.find(b => (query.branch ? b.name === query.branch : b.isMain)); const isFile = ['FIL', 'UTS'].includes(component.qualifier); const configuration = component.configuration || {}; @@ -147,7 +146,7 @@ export default class ProjectContainer extends React.PureComponent branches, component: component, onBranchesChange: this.handleBranchesChange, - onComponentChange: this.handleProjectChange + onComponentChange: this.handleComponentChange })} ); diff --git a/server/sonar-web/src/main/js/app/components/ProjectContainerNotFound.tsx b/server/sonar-web/src/main/js/app/components/ComponentContainerNotFound.tsx similarity index 95% rename from server/sonar-web/src/main/js/app/components/ProjectContainerNotFound.tsx rename to server/sonar-web/src/main/js/app/components/ComponentContainerNotFound.tsx index 1b5c4daadab..b404c5d1af8 100644 --- a/server/sonar-web/src/main/js/app/components/ProjectContainerNotFound.tsx +++ b/server/sonar-web/src/main/js/app/components/ComponentContainerNotFound.tsx @@ -21,7 +21,7 @@ import * as React from 'react'; import { Link } from 'react-router'; import { translate } from '../../helpers/l10n'; -export default class ProjectContainerNotFound extends React.PureComponent { +export default class ComponentContainerNotFound extends React.PureComponent { componentDidMount() { const html = document.querySelector('html'); if (html) { diff --git a/server/sonar-web/src/main/js/app/components/__tests__/ProjectContainer-test.tsx b/server/sonar-web/src/main/js/app/components/__tests__/ComponentContainer-test.tsx similarity index 81% rename from server/sonar-web/src/main/js/app/components/__tests__/ProjectContainer-test.tsx rename to server/sonar-web/src/main/js/app/components/__tests__/ComponentContainer-test.tsx index ee31dea0b0c..105931959f9 100644 --- a/server/sonar-web/src/main/js/app/components/__tests__/ProjectContainer-test.tsx +++ b/server/sonar-web/src/main/js/app/components/__tests__/ComponentContainer-test.tsx @@ -21,14 +21,21 @@ jest.mock('../../../api/branches', () => ({ getBranches: jest.fn() })); jest.mock('../../../api/components', () => ({ getComponentData: jest.fn() })); jest.mock('../../../api/nav', () => ({ getComponentNavigation: jest.fn() })); +// mock this, because some of its children are using redux store +jest.mock('../nav/component/ComponentNav', () => ({ + default: () => null +})); + import * as React from 'react'; import { shallow, mount } from 'enzyme'; -import ProjectContainer from '../ProjectContainer'; +import ComponentContainer from '../ComponentContainer'; import { getBranches } from '../../../api/branches'; import { getComponentData } from '../../../api/components'; import { getComponentNavigation } from '../../../api/nav'; import { doAsync } from '../../../helpers/testUtils'; +const Inner = () =>
; + beforeEach(() => { (getBranches as jest.Mock).mockClear(); (getComponentData as jest.Mock).mockClear(); @@ -36,14 +43,12 @@ beforeEach(() => { }); it('changes component', () => { - const Inner = () =>
; - const wrapper = shallow( - + - + ); - (wrapper.instance() as ProjectContainer).mounted = true; + (wrapper.instance() as ComponentContainer).mounted = true; wrapper.setState({ branches: [{ isMain: true }], component: { qualifier: 'TRK', visibility: 'public' }, @@ -67,9 +72,9 @@ it("loads branches for module's project", () => { ); mount( - -
- + + + ); return doAsync().then(() => { @@ -88,28 +93,28 @@ it("doesn't load branches portfolio", () => { }) ); - mount( - -
- + const wrapper = mount( + + + ); return doAsync().then(() => { expect(getBranches).not.toBeCalled(); expect(getComponentData).toBeCalledWith('portfolioKey', undefined); expect(getComponentNavigation).toBeCalledWith('portfolioKey'); + expect(wrapper.find(Inner).exists()).toBeTruthy(); }); }); it('updates branches on change', () => { (getBranches as jest.Mock).mockImplementation(() => Promise.resolve([])); - const Inner = () =>
; const wrapper = shallow( - + - + ); - (wrapper.instance() as ProjectContainer).mounted = true; + (wrapper.instance() as ComponentContainer).mounted = true; wrapper.setState({ branches: [{ isMain: true }], component: { breadcrumbs: [{ key: 'projectKey', name: 'project', qualifier: 'TRK' }] }, diff --git a/server/sonar-web/src/main/js/app/components/extensions/ViewDashboard.js b/server/sonar-web/src/main/js/app/components/extensions/PortfolioDashboard.tsx similarity index 81% rename from server/sonar-web/src/main/js/app/components/extensions/ViewDashboard.js rename to server/sonar-web/src/main/js/app/components/extensions/PortfolioDashboard.tsx index 050cab3594a..2bb640dae73 100644 --- a/server/sonar-web/src/main/js/app/components/extensions/ViewDashboard.js +++ b/server/sonar-web/src/main/js/app/components/extensions/PortfolioDashboard.tsx @@ -17,14 +17,19 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -// @flow -import React from 'react'; +import * as React from 'react'; import ProjectPageExtension from './ProjectPageExtension'; +import { Component } from '../../types'; -export default function ViewDashboard(props /*: Object */) { +interface Props { + component: Component; + location: { query: { id: string } }; +} + +export default function PortfolioDashboard(props: Props) { return ( ); diff --git a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.tsx b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.tsx index d1b95c1a06f..df4a432d5be 100644 --- a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.tsx +++ b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.tsx @@ -32,7 +32,7 @@ import './ComponentNav.css'; interface Props { branches: Branch[]; - currentBranch: Branch; + currentBranch?: Branch; component: Component; conf: ComponentConfiguration; location: {}; @@ -99,13 +99,14 @@ export default class ComponentNav extends React.PureComponent { breadcrumbs={this.props.component.breadcrumbs} /> - + {this.props.currentBranch && + } { } renderDashboardLink() { - if (isShortLivingBranch(this.props.branch)) { + if (this.props.branch && isShortLivingBranch(this.props.branch)) { return null; } @@ -85,7 +85,10 @@ export default class ComponentNavMenu extends React.PureComponent { {translate('overview.page')} @@ -104,7 +107,10 @@ export default class ComponentNavMenu extends React.PureComponent { {this.isView() || this.isApplication() @@ -120,7 +126,7 @@ export default class ComponentNavMenu extends React.PureComponent { return null; } - if (isShortLivingBranch(this.props.branch)) { + if (this.props.branch && isShortLivingBranch(this.props.branch)) { return null; } @@ -129,7 +135,10 @@ export default class ComponentNavMenu extends React.PureComponent { {translate('project_activity.page')} @@ -145,7 +154,7 @@ export default class ComponentNavMenu extends React.PureComponent { to={{ pathname: '/project/issues', query: { - branch: getBranchName(this.props.branch), + branch: this.props.branch && getBranchName(this.props.branch), id: this.props.component.key, resolved: 'false' } @@ -158,7 +167,7 @@ export default class ComponentNavMenu extends React.PureComponent { } renderComponentMeasuresLink() { - if (isShortLivingBranch(this.props.branch)) { + if (this.props.branch && isShortLivingBranch(this.props.branch)) { return null; } @@ -167,7 +176,10 @@ export default class ComponentNavMenu extends React.PureComponent { {translate('layout.measures')} @@ -177,7 +189,7 @@ export default class ComponentNavMenu extends React.PureComponent { } renderAdministration() { - if (isShortLivingBranch(this.props.branch)) { + if (this.props.branch && isShortLivingBranch(this.props.branch)) { return null; } @@ -205,7 +217,7 @@ export default class ComponentNavMenu extends React.PureComponent { } renderAdministrationLinks() { - return isLongLivingBranch(this.props.branch) + return this.props.branch && isLongLivingBranch(this.props.branch) ? [this.renderSettingsLink()] : [ this.renderSettingsLink(), @@ -231,7 +243,10 @@ export default class ComponentNavMenu extends React.PureComponent { {translate('project_settings.page')} diff --git a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMeta.tsx b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMeta.tsx index 07d5cbc5bbf..3fffc261d4c 100644 --- a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMeta.tsx +++ b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMeta.tsx @@ -28,7 +28,7 @@ import { translate, translateWithParameters } from '../../../../helpers/l10n'; import { isShortLivingBranch } from '../../../../helpers/branches'; interface Props { - branch: Branch; + branch?: Branch; component: Component; conf: ComponentConfiguration; incremental?: boolean; @@ -91,7 +91,7 @@ export default function ComponentNavMeta(props: Props) { ); } - if (props.component.analysisDate && props.branch.isMain) { + if (props.component.analysisDate && (!props.branch || props.branch.isMain)) { metaList.push(
  • @@ -99,7 +99,7 @@ export default function ComponentNavMeta(props: Props) { ); } - if (props.component.version && props.branch.isMain) { + if (props.component.version && (!props.branch || props.branch.isMain)) { metaList.push(
  • Version {props.component.version} @@ -115,7 +115,7 @@ export default function ComponentNavMeta(props: Props) { ); } - if (isShortLivingBranch(props.branch)) { + if (props.branch && isShortLivingBranch(props.branch)) { metaList.push(
  • diff --git a/server/sonar-web/src/main/js/app/utils/startReactApp.js b/server/sonar-web/src/main/js/app/utils/startReactApp.js index af23e36912b..cab8a95008e 100644 --- a/server/sonar-web/src/main/js/app/utils/startReactApp.js +++ b/server/sonar-web/src/main/js/app/utils/startReactApp.js @@ -32,7 +32,7 @@ import Landing from '../components/Landing'; import ProjectAdminContainer from '../components/ProjectAdminContainer'; import ProjectPageExtension from '../components/extensions/ProjectPageExtension'; import ProjectAdminPageExtension from '../components/extensions/ProjectAdminPageExtension'; -import ViewDashboard from '../components/extensions/ViewDashboard'; +import PortfolioDashboard from '../components/extensions/PortfolioDashboard'; import PortfoliosPage from '../components/extensions/PortfoliosPage'; import AdminContainer from '../components/AdminContainer'; import GlobalPageExtension from '../components/extensions/GlobalPageExtension'; @@ -170,7 +170,7 @@ const startReactApp = () => { - import('../components/ProjectContainer').then(i => i.default)}> + import('../components/ComponentContainer').then(i => i.default)}> @@ -194,7 +194,7 @@ const startReactApp = () => { {projectAdminRoutes} - + 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 0df83e23dde..7063fe085c8 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 @@ -39,7 +39,7 @@ import '../code.css'; import { Component, Branch } from '../../../app/types'; interface Props { - branch: Branch; + branch?: Branch; component: Component; location: { query: { [x: string]: string } }; } @@ -91,7 +91,7 @@ export default class App extends React.PureComponent { this.setState({ loading: true }); const isPortfolio = ['VW', 'SVW'].includes(component.qualifier); - retrieveComponentChildren(component.key, isPortfolio, getBranchName(branch)) + retrieveComponentChildren(component.key, isPortfolio, branch && getBranchName(branch)) .then(() => { addComponent(component); this.handleUpdate(); @@ -108,7 +108,11 @@ export default class App extends React.PureComponent { 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.branch && getBranchName(this.props.branch) + ) .then(r => { if (this.mounted) { if (['FIL', 'UTS'].includes(r.component.qualifier)) { @@ -154,7 +158,12 @@ export default class App extends React.PureComponent { 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.branch && getBranchName(this.props.branch) + ) .then(r => { if (this.mounted) { this.setState({ @@ -189,7 +198,7 @@ export default class App extends React.PureComponent { total, sourceViewer } = this.state; - const branchName = getBranchName(branch); + const branchName = branch && getBranchName(branch); const shouldShowBreadcrumbs = breadcrumbs.length > 1; 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 a8f6908e215..255d5b5732d 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 @@ -34,7 +34,7 @@ import { translate } from '../../../helpers/l10n'; import '../style.css'; /*:: type Props = {| - branch: {}, + branch?: {}, component: Component, currentUser: { isLoggedIn: boolean }, location: { pathname: string, query: RawQuery }, @@ -106,7 +106,7 @@ export default class App extends React.PureComponent { const filteredKeys = metricsKey.filter( key => !metrics[key].hidden && !['DATA', 'DISTRIB'].includes(metrics[key].type) ); - fetchMeasures(component.key, filteredKeys, getBranchName(branch)).then( + fetchMeasures(component.key, filteredKeys, branch && getBranchName(branch)).then( ({ measures, leakPeriod }) => { if (this.mounted) { this.setState({ @@ -127,7 +127,11 @@ export default class App extends React.PureComponent { }); this.props.router.push({ pathname: this.props.location.pathname, - query: { ...query, branch: getBranchName(this.props.branch), id: this.props.component.key } + query: { + ...query, + branch: this.props.branch && getBranchName(this.props.branch), + id: this.props.component.key + } }); }; @@ -160,7 +164,7 @@ export default class App extends React.PureComponent { {metric != null && - +
  • ); } diff --git a/server/sonar-web/src/main/js/apps/overview/components/OverviewApp.js b/server/sonar-web/src/main/js/apps/overview/components/OverviewApp.js index 8dfcd55c171..e3dd3b349a8 100644 --- a/server/sonar-web/src/main/js/apps/overview/components/OverviewApp.js +++ b/server/sonar-web/src/main/js/apps/overview/components/OverviewApp.js @@ -42,7 +42,7 @@ import '../styles.css'; /*:: type Props = { - branch: { name: string }, + branch?: { name: string }, component: Component, onComponentChange: {} => void }; @@ -98,7 +98,7 @@ export default class OverviewApp extends React.PureComponent { return getMeasuresAndMeta(component.key, METRICS, { additionalFields: 'metrics,periods', - branch: getBranchName(branch) + branch: branch && getBranchName(branch) }).then( r => { if (this.mounted) { @@ -128,7 +128,7 @@ export default class OverviewApp extends React.PureComponent { const metrics = uniq(HISTORY_METRICS_LIST.concat(graphMetrics)); return getAllTimeMachineData(component.key, metrics, { - branch: getBranchName(branch) + branch: branch && getBranchName(branch) }).then(r => { if (this.mounted) { const history /*: History */ = {}; @@ -166,7 +166,7 @@ export default class OverviewApp extends React.PureComponent { const leakPeriod = component.qualifier === 'APP' ? this.getApplicationLeakPeriod() : getLeakPeriod(periods); - const branchName = getBranchName(branch); + const branchName = branch && getBranchName(branch); const domainProps = { branch: branchName, component, 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 19db0c40c33..2abe61ee16d 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 @@ -43,7 +43,7 @@ import { /*:: type Props = { - branch: {}, + branch?: {}, location: { pathname: string, query: RawQuery }, component: { configuration?: { showHistory: boolean }, @@ -95,7 +95,10 @@ export default class ProjectActivityAppContainer extends React.PureComponent { } this.context.router.replace({ pathname: props.location.pathname, - query: { ...serializeUrlQuery(newQuery), branch: getBranchName(props.branch) } + query: { + ...serializeUrlQuery(newQuery), + branch: props.branch && getBranchName(props.branch) + } }); } } @@ -169,7 +172,12 @@ export default class ProjectActivityAppContainer extends React.PureComponent { [string]: string } */ ) => { - const parameters = { project, p, ps, branch: getBranchName(this.props.branch) }; + const parameters = { + project, + p, + ps, + branch: this.props.branch && getBranchName(this.props.branch) + }; return api .getProjectActivity({ ...parameters, ...additional }) .then(({ analyses, paging }) => ({ @@ -183,7 +191,7 @@ export default class ProjectActivityAppContainer extends React.PureComponent { return Promise.resolve([]); } return getAllTimeMachineData(this.props.component.key, metrics, { - branch: getBranchName(this.props.branch) + branch: this.props.branch && getBranchName(this.props.branch) }).then( ({ measures }) => measures.map(measure => ({ @@ -285,7 +293,7 @@ export default class ProjectActivityAppContainer extends React.PureComponent { pathname: this.props.location.pathname, query: { ...query, - branch: getBranchName(this.props.branch), + branch: this.props.branch && getBranchName(this.props.branch), id: this.props.component.key } }); diff --git a/server/sonar-web/src/main/js/apps/settings/components/DefinitionsList.js b/server/sonar-web/src/main/js/apps/settings/components/DefinitionsList.js index 2106bb54639..f8e6230a52b 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/DefinitionsList.js +++ b/server/sonar-web/src/main/js/apps/settings/components/DefinitionsList.js @@ -24,7 +24,7 @@ import Definition from './Definition'; export default class DefinitionsList extends React.PureComponent { static propTypes = { - branch: PropTypes.object, + branch: PropTypes.string, component: PropTypes.object, settings: PropTypes.array.isRequired }; -- 2.39.5