From: Wouter Admiraal Date: Tue, 18 Dec 2018 10:36:11 +0000 (+0100) Subject: SONARCLOUD-235 Hide QG and QP links on Overview for non-members X-Git-Tag: 7.6~200 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=772a9e2f8ee92d98fc6f0114bcea700d8c3e8670;p=sonarqube.git SONARCLOUD-235 Hide QG and QP links on Overview for non-members --- diff --git a/server/sonar-web/src/main/js/app/components/ComponentContainer.tsx b/server/sonar-web/src/main/js/app/components/ComponentContainer.tsx index a031c478169..3cde92f9731 100644 --- a/server/sonar-web/src/main/js/app/components/ComponentContainer.tsx +++ b/server/sonar-web/src/main/js/app/components/ComponentContainer.tsx @@ -29,7 +29,7 @@ import { getTasksForComponent, getAnalysisStatus } from '../../api/ce'; import { getComponentData } from '../../api/components'; import { getMeasures } from '../../api/measures'; import { getComponentNavigation } from '../../api/nav'; -import { fetchOrganizations } from '../../store/rootActions'; +import { fetchOrganization } from '../../store/rootActions'; import { STATUSES } from '../../apps/background-tasks/constants'; import { isPullRequest, @@ -44,7 +44,7 @@ import { Store, getAppState } from '../../store/rootReducer'; interface Props { appState: Pick; children: any; - fetchOrganizations: (organizations: string[]) => void; + fetchOrganization: (organization: string) => void; location: { query: { branch?: string; id: string; pullRequest?: string }; }; @@ -116,7 +116,7 @@ export class ComponentContainer extends React.PureComponent { const component = this.addQualifier({ ...nav, ...data }); if (this.props.appState.organizationsEnabled) { - this.props.fetchOrganizations([component.organization]); + this.props.fetchOrganization(component.organization); } return component; }) @@ -379,7 +379,7 @@ const mapStateToProps = (state: Store) => ({ appState: getAppState(state) }); -const mapDispatchToProps = { fetchOrganizations }; +const mapDispatchToProps = { fetchOrganization }; export default connect( mapStateToProps, diff --git a/server/sonar-web/src/main/js/app/components/__tests__/ComponentContainer-test.tsx b/server/sonar-web/src/main/js/app/components/__tests__/ComponentContainer-test.tsx index 34d513e1b17..a98614a257a 100644 --- a/server/sonar-web/src/main/js/app/components/__tests__/ComponentContainer-test.tsx +++ b/server/sonar-web/src/main/js/app/components/__tests__/ComponentContainer-test.tsx @@ -78,7 +78,7 @@ it('changes component', () => { const wrapper = shallow( @@ -105,7 +105,7 @@ it("loads branches for module's project", async () => { mount( @@ -122,7 +122,7 @@ it("doesn't load branches portfolio", async () => { const wrapper = mount( @@ -141,7 +141,7 @@ it('updates branches on change', () => { const wrapper = shallow( @@ -169,7 +169,7 @@ it('updates the branch measures', async () => { const wrapper = shallow( @@ -195,18 +195,18 @@ it('updates the branch measures', async () => { it('loads organization', async () => { (getComponentData as jest.Mock).mockResolvedValueOnce({ organization: 'org' }); - const fetchOrganizations = jest.fn(); + const fetchOrganization = jest.fn(); mount( ); await new Promise(setImmediate); - expect(fetchOrganizations).toBeCalledWith(['org']); + expect(fetchOrganization).toBeCalledWith('org'); }); it('fetches status', async () => { @@ -215,7 +215,7 @@ it('fetches status', async () => { mount( @@ -229,7 +229,7 @@ it('filters correctly the pending tasks for a main branch', () => { const wrapper = shallow( @@ -297,7 +297,7 @@ it('reload component after task progress finished', async () => { const wrapper = shallow( diff --git a/server/sonar-web/src/main/js/app/components/extensions/OrganizationPageExtension.tsx b/server/sonar-web/src/main/js/app/components/extensions/OrganizationPageExtension.tsx index ac53b37e041..b9abb6d7188 100644 --- a/server/sonar-web/src/main/js/app/components/extensions/OrganizationPageExtension.tsx +++ b/server/sonar-web/src/main/js/app/components/extensions/OrganizationPageExtension.tsx @@ -22,7 +22,7 @@ import { connect } from 'react-redux'; import Extension from './Extension'; import NotFound from '../NotFound'; import { getOrganizationByKey, Store } from '../../../store/rootReducer'; -import { fetchOrganization } from '../../../apps/organizations/actions'; +import { fetchOrganization } from '../../../store/rootActions'; interface StateToProps { organization?: T.Organization; diff --git a/server/sonar-web/src/main/js/apps/organizations/actions.ts b/server/sonar-web/src/main/js/apps/organizations/actions.ts index 6d72a0b9318..846b0b2e632 100644 --- a/server/sonar-web/src/main/js/apps/organizations/actions.ts +++ b/server/sonar-web/src/main/js/apps/organizations/actions.ts @@ -23,17 +23,6 @@ import * as actions from '../../store/organizations'; import { addGlobalSuccessMessage } from '../../store/globalMessages'; import { translate, translateWithParameters } from '../../helpers/l10n'; -export const fetchOrganization = (key: string) => (dispatch: Dispatch) => { - return Promise.all([api.getOrganization(key), api.getOrganizationNavigation(key)]).then( - ([organization, navigation]) => { - if (organization) { - const organizationWithPermissions = { ...organization, ...navigation }; - dispatch(actions.receiveOrganizations([organizationWithPermissions])); - } - } - ); -}; - export const createOrganization = (organization: T.OrganizationBase) => ( dispatch: Dispatch ) => { diff --git a/server/sonar-web/src/main/js/apps/organizations/components/OrganizationPage.tsx b/server/sonar-web/src/main/js/apps/organizations/components/OrganizationPage.tsx index e74b3a8b651..47a503bf371 100644 --- a/server/sonar-web/src/main/js/apps/organizations/components/OrganizationPage.tsx +++ b/server/sonar-web/src/main/js/apps/organizations/components/OrganizationPage.tsx @@ -22,7 +22,6 @@ import Helmet from 'react-helmet'; import { connect } from 'react-redux'; import { Location } from 'history'; import OrganizationNavigation from '../navigation/OrganizationNavigation'; -import { fetchOrganization } from '../actions'; import NotFound from '../../../app/components/NotFound'; import Suggestions from '../../../app/components/embed-docs-modal/Suggestions'; import { @@ -31,6 +30,7 @@ import { getMyOrganizations, Store } from '../../../store/rootReducer'; +import { fetchOrganization } from '../../../store/rootActions'; interface OwnProps { children?: React.ReactNode; 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 611b3f5087f..edee5a58263 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 @@ -261,7 +261,9 @@ export class OverviewApp extends React.PureComponent { } } -const mapDispatchToProps: DispatchToProps = { fetchMetrics }; +const mapDispatchToProps: DispatchToProps = { + fetchMetrics +}; const mapStateToProps = (state: Store): StateToProps => ({ metrics: getMetrics(state) diff --git a/server/sonar-web/src/main/js/apps/overview/meta/__tests__/MetaContainer-test.tsx b/server/sonar-web/src/main/js/apps/overview/meta/__tests__/MetaContainer-test.tsx new file mode 100644 index 00000000000..9117279f2b1 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/overview/meta/__tests__/MetaContainer-test.tsx @@ -0,0 +1,68 @@ +/* + * 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 { Meta } from '../MetaContainer'; +import { + mockAppState, + mockCurrentUser, + mockOrganization, + mockComponent +} from '../../../../helpers/testUtils'; + +it('should render correctly', () => { + const wrapper = shallowRender(); + expect(wrapper).toMatchSnapshot(); + expect(metaQualityGateRendered(wrapper)).toBe(true); +}); + +it('should hide QG and QP links if the organization has a paid plan, and the user is not a member', () => { + const wrapper = shallowRender({ + organization: mockOrganization({ key: 'other_key', subscription: 'PAID' }) + }); + expect(wrapper).toMatchSnapshot(); + expect(metaQualityGateRendered(wrapper)).toBe(false); +}); + +it('should show QG and QP links if the organization has a paid plan, and the user is a member', () => { + const wrapper = shallowRender({ + organization: mockOrganization({ subscription: 'PAID' }) + }); + expect(wrapper).toMatchSnapshot(); + expect(metaQualityGateRendered(wrapper)).toBe(true); +}); + +function metaQualityGateRendered(wrapper: any) { + return wrapper.find('#overview-meta-quality-gate').exists(); +} + +function shallowRender(props: Partial = {}) { + return shallow( + + ); +} diff --git a/server/sonar-web/src/main/js/apps/overview/meta/__tests__/__snapshots__/MetaContainer-test.tsx.snap b/server/sonar-web/src/main/js/apps/overview/meta/__tests__/__snapshots__/MetaContainer-test.tsx.snap new file mode 100644 index 00000000000..fc95c357f57 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/overview/meta/__tests__/__snapshots__/MetaContainer-test.tsx.snap @@ -0,0 +1,293 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should hide QG and QP links if the organization has a paid plan, and the user is not a member 1`] = ` +
+
+

+ overview.about_this_project.TRK +

+ +
+ +
+ + +
+
+`; + +exports[`should render correctly 1`] = ` +
+
+

+ overview.about_this_project.TRK +

+ +
+
+ + +
+ +
+ + +
+
+`; + +exports[`should show QG and QP links if the organization has a paid plan, and the user is a member 1`] = ` +
+
+

+ overview.about_this_project.TRK +

+ +
+
+ + +
+ +
+ + +
+
+`; diff --git a/server/sonar-web/src/main/js/apps/permissions/project/components/AppContainer.ts b/server/sonar-web/src/main/js/apps/permissions/project/components/AppContainer.ts index ac9900803c7..baf32b46814 100644 --- a/server/sonar-web/src/main/js/apps/permissions/project/components/AppContainer.ts +++ b/server/sonar-web/src/main/js/apps/permissions/project/components/AppContainer.ts @@ -20,7 +20,7 @@ import { connect } from 'react-redux'; import App from './App'; import { getCurrentUser, getOrganizationByKey, Store } from '../../../../store/rootReducer'; -import { fetchOrganization } from '../../../organizations/actions'; +import { fetchOrganization } from '../../../../store/rootActions'; interface OwnProps { component: T.Component; diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/AppContainer.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/AppContainer.tsx index 35419fc25b6..a3f04d6ac86 100644 --- a/server/sonar-web/src/main/js/apps/projectsManagement/AppContainer.tsx +++ b/server/sonar-web/src/main/js/apps/projectsManagement/AppContainer.tsx @@ -24,7 +24,7 @@ import forSingleOrganization from '../organizations/forSingleOrganization'; import { getAppState, getOrganizationByKey, getCurrentUser, Store } from '../../store/rootReducer'; import { receiveOrganizations } from '../../store/organizations'; import { changeProjectDefaultVisibility } from '../../api/permissions'; -import { fetchOrganization } from '../organizations/actions'; +import { fetchOrganization } from '../../store/rootActions'; interface StateProps { appState: { defaultOrganization: string; qualifiers: string[] }; diff --git a/server/sonar-web/src/main/js/store/rootActions.ts b/server/sonar-web/src/main/js/store/rootActions.ts index adb81802919..4247ac33462 100644 --- a/server/sonar-web/src/main/js/store/rootActions.ts +++ b/server/sonar-web/src/main/js/store/rootActions.ts @@ -25,7 +25,7 @@ import { receiveOrganizations } from './organizations'; import * as auth from '../api/auth'; import { getLanguages } from '../api/languages'; import { getAllMetrics } from '../api/metrics'; -import { getOrganizations } from '../api/organizations'; +import { getOrganizations, getOrganization, getOrganizationNavigation } from '../api/organizations'; export function fetchLanguages() { return (dispatch: Dispatch) => { @@ -48,6 +48,17 @@ export function fetchOrganizations(organizations: string[]) { }; } +export const fetchOrganization = (key: string) => (dispatch: Dispatch) => { + return Promise.all([getOrganization(key), getOrganizationNavigation(key)]).then( + ([organization, navigation]) => { + if (organization) { + const organizationWithPermissions = { ...organization, ...navigation }; + dispatch(receiveOrganizations([organizationWithPermissions])); + } + } + ); +}; + export function doLogin(login: string, password: string) { return (dispatch: Dispatch) => auth.login(login, password).then(