diff options
author | Grégoire Aubert <gregoire.aubert@sonarsource.com> | 2017-10-17 16:17:11 +0200 |
---|---|---|
committer | Grégoire Aubert <gregoire.aubert@sonarsource.com> | 2017-10-23 08:01:13 -0700 |
commit | bff95ad641af2546712ea61d38a259f0847343c5 (patch) | |
tree | 56a4e216e4068da8062a2b8b0fd40a3dbf85d2d0 /server/sonar-web/src/main/js/apps/marketplace | |
parent | 3979c1713989085fa72b7d355ed544197de5b283 (diff) | |
download | sonarqube-bff95ad641af2546712ea61d38a259f0847343c5.tar.gz sonarqube-bff95ad641af2546712ea61d38a259f0847343c5.zip |
SONAR-9936 Update edition notification to be scoped to page and not whole administration
Diffstat (limited to 'server/sonar-web/src/main/js/apps/marketplace')
10 files changed, 207 insertions, 22 deletions
diff --git a/server/sonar-web/src/main/js/apps/marketplace/App.tsx b/server/sonar-web/src/main/js/apps/marketplace/App.tsx index 18fc8adaaee..02910f26c5e 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/App.tsx +++ b/server/sonar-web/src/main/js/apps/marketplace/App.tsx @@ -22,6 +22,7 @@ import * as PropTypes from 'prop-types'; import { sortBy, uniqBy } from 'lodash'; import Helmet from 'react-helmet'; import Header from './Header'; +import EditionsStatusNotif from './components/EditionsStatusNotif'; import EditionBoxes from './EditionBoxes'; import Footer from './Footer'; import PendingActions from './PendingActions'; @@ -35,13 +36,12 @@ import { Plugin, PluginPending } from '../../api/plugins'; -import { EditionStatus } from '../../api/marketplace'; +import { EditionStatus, getEditionStatus } from '../../api/marketplace'; import { RawQuery } from '../../helpers/query'; import { translate } from '../../helpers/l10n'; import { filterPlugins, parseQuery, Query, serializeQuery } from './utils'; export interface Props { - editionStatus?: EditionStatus; editionsUrl: string; location: { pathname: string; query: RawQuery }; sonarqubeVersion: string; @@ -49,6 +49,7 @@ export interface Props { } interface State { + editionStatus?: EditionStatus; loading: boolean; pending: { installing: PluginPending[]; @@ -81,6 +82,7 @@ export default class App extends React.PureComponent<Props, State> { componentDidMount() { this.mounted = true; this.fetchPendingPlugins(); + this.fetchEditionStatus(); this.fetchQueryPlugins(); } @@ -148,6 +150,19 @@ export default class App extends React.PureComponent<Props, State> { () => {} ); + fetchEditionStatus = () => + getEditionStatus().then( + editionStatus => { + if (this.mounted) { + this.updateEditionStatus(editionStatus); + } + }, + () => {} + ); + + updateEditionStatus = (editionStatus: EditionStatus) => + this.setState({ editionStatus: editionStatus }); + updateQuery = (newQuery: Partial<Query>) => { const query = serializeQuery({ ...parseQuery(this.props.location.query), @@ -160,18 +175,20 @@ export default class App extends React.PureComponent<Props, State> { }; render() { - const { plugins, pending } = this.state; + const { editionStatus, plugins, pending } = this.state; const query = parseQuery(this.props.location.query); const filteredPlugins = query.search ? filterPlugins(plugins, query.search) : plugins; return ( <div className="page page-limited" id="marketplace-page"> <Helmet title={translate('marketplace.page')} /> + {editionStatus && <EditionsStatusNotif editionStatus={editionStatus} />} <Header /> <EditionBoxes - editionStatus={this.props.editionStatus} + editionStatus={editionStatus} editionsUrl={this.props.editionsUrl} sonarqubeVersion={this.props.sonarqubeVersion} updateCenterActive={this.props.updateCenterActive} + updateEditionStatus={this.updateEditionStatus} /> <PendingActions refreshPending={this.fetchPendingPlugins} pending={pending} /> <Search diff --git a/server/sonar-web/src/main/js/apps/marketplace/AppContainer.tsx b/server/sonar-web/src/main/js/apps/marketplace/AppContainer.tsx index 64794ebbda2..909f284fbdf 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/AppContainer.tsx +++ b/server/sonar-web/src/main/js/apps/marketplace/AppContainer.tsx @@ -23,7 +23,6 @@ import { getAppState, getGlobalSettingValue } from '../../store/rootReducer'; import './style.css'; const mapStateToProps = (state: any) => ({ - editionStatus: getAppState(state).editionStatus, editionsUrl: (getGlobalSettingValue(state, 'sonar.editions.jsonUrl') || {}).value, sonarqubeVersion: getAppState(state).version, updateCenterActive: (getGlobalSettingValue(state, 'sonar.updatecenter.activate') || {}).value diff --git a/server/sonar-web/src/main/js/apps/marketplace/EditionBoxes.tsx b/server/sonar-web/src/main/js/apps/marketplace/EditionBoxes.tsx index 58ffe846c63..e1f5f373f1f 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/EditionBoxes.tsx +++ b/server/sonar-web/src/main/js/apps/marketplace/EditionBoxes.tsx @@ -30,6 +30,7 @@ export interface Props { editionsUrl: string; sonarqubeVersion: string; updateCenterActive: boolean; + updateEditionStatus: (editionStatus: EditionStatus) => void; } interface State { @@ -108,7 +109,11 @@ export default class EditionBoxes extends React.PureComponent<Props, State> { )} {installEdition && ( - <LicenseEditionForm edition={installEdition} onClose={this.handleCloseLicenseForm} /> + <LicenseEditionForm + edition={installEdition} + onClose={this.handleCloseLicenseForm} + updateEditionStatus={this.props.updateEditionStatus} + /> )} </div> ); diff --git a/server/sonar-web/src/main/js/apps/marketplace/PendingActions.tsx b/server/sonar-web/src/main/js/apps/marketplace/PendingActions.tsx index 921afeef3da..bc96da9bbf0 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/PendingActions.tsx +++ b/server/sonar-web/src/main/js/apps/marketplace/PendingActions.tsx @@ -19,9 +19,9 @@ */ import * as React from 'react'; import { FormattedMessage } from 'react-intl'; -import { translate } from '../../helpers/l10n'; -import { cancelPendingPlugins, PluginPending } from '../../api/plugins'; import RestartForm from '../../components/common/RestartForm'; +import { cancelPendingPlugins, PluginPending } from '../../api/plugins'; +import { translate } from '../../helpers/l10n'; interface Props { pending: { @@ -40,7 +40,6 @@ export default class PendingActions extends React.PureComponent<Props, State> { state: State = { openRestart: false }; handleOpenRestart = () => this.setState({ openRestart: true }); - hanleCloseRestart = () => this.setState({ openRestart: false }); handleRevert = () => { diff --git a/server/sonar-web/src/main/js/apps/marketplace/__tests__/EditionBoxes-test.tsx b/server/sonar-web/src/main/js/apps/marketplace/__tests__/EditionBoxes-test.tsx index c3525b0e304..e64c714577f 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/__tests__/EditionBoxes-test.tsx +++ b/server/sonar-web/src/main/js/apps/marketplace/__tests__/EditionBoxes-test.tsx @@ -80,6 +80,7 @@ function getWrapper(props = {}) { editionsUrl="" sonarqubeVersion="6.7.5" updateCenterActive={true} + updateEditionStatus={jest.fn()} {...props} /> ); diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/EditionsStatusNotif.tsx b/server/sonar-web/src/main/js/apps/marketplace/components/EditionsStatusNotif.tsx new file mode 100644 index 00000000000..1ef4aacc308 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/marketplace/components/EditionsStatusNotif.tsx @@ -0,0 +1,72 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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 RestartForm from '../../../components/common/RestartForm'; +import { EditionStatus } from '../../../api/marketplace'; +import { translate } from '../../../helpers/l10n'; + +interface Props { + editionStatus: EditionStatus; +} + +interface State { + openRestart: boolean; +} + +export default class EditionsStatusNotif extends React.PureComponent<Props, State> { + state: State = { openRestart: false }; + + handleOpenRestart = () => this.setState({ openRestart: true }); + hanleCloseRestart = () => this.setState({ openRestart: false }); + + render() { + const { editionStatus } = this.props; + if (editionStatus.installationStatus === 'AUTOMATIC_IN_PROGRESS') { + return ( + <div className="alert alert-page alert-info"> + <i className="spinner spacer-right text-bottom" /> + <span>{translate('marketplace.status.AUTOMATIC_IN_PROGRESS')}</span> + </div> + ); + } else if (editionStatus.installationStatus === 'AUTOMATIC_READY') { + return ( + <div className="alert alert-page alert-success"> + <span>{translate('marketplace.status.AUTOMATIC_READY')}</span> + <button className="js-restart spacer-left" onClick={this.handleOpenRestart}> + {translate('marketplace.restart')} + </button> + {this.state.openRestart && <RestartForm onClose={this.hanleCloseRestart} />} + </div> + ); + } else if ( + ['MANUAL_IN_PROGRESS', 'AUTOMATIC_FAILURE'].includes(editionStatus.installationStatus) + ) { + return ( + <div className="alert alert-page alert-danger"> + {translate('marketplace.status', editionStatus.installationStatus)} + <a className="little-spacer-left" href="https://www.sonarsource.com" target="_blank"> + {translate('marketplace.how_to_install')} + </a> + </div> + ); + } + return null; + } +} diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/LicenseEditionForm.tsx b/server/sonar-web/src/main/js/apps/marketplace/components/LicenseEditionForm.tsx index 1f00178f113..fb513eba0bc 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/components/LicenseEditionForm.tsx +++ b/server/sonar-web/src/main/js/apps/marketplace/components/LicenseEditionForm.tsx @@ -20,14 +20,13 @@ import * as React from 'react'; import Modal from 'react-modal'; import LicenseEditionSet from './LicenseEditionSet'; -import getStore from '../../../app/utils/getStore'; -import { setEditionStatus } from '../../../store/appState/duck'; -import { Edition, applyLicense } from '../../../api/marketplace'; +import { Edition, EditionStatus, applyLicense } from '../../../api/marketplace'; import { translate, translateWithParameters } from '../../../helpers/l10n'; export interface Props { edition: Edition; onClose: () => void; + updateEditionStatus: (editionStatus: EditionStatus) => void; } interface State { @@ -66,7 +65,7 @@ export default class LicenseEditionForm extends React.PureComponent<Props, State this.setState({ loading: true }); applyLicense({ license }).then( editionStatus => { - getStore().dispatch(setEditionStatus(editionStatus)); + this.props.updateEditionStatus(editionStatus); this.props.onClose(); }, () => { diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/EditionsStatusNotif-test.tsx b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/EditionsStatusNotif-test.tsx new file mode 100644 index 00000000000..48a65f4b20f --- /dev/null +++ b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/EditionsStatusNotif-test.tsx @@ -0,0 +1,43 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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 EditionsStatusNotif from '../EditionsStatusNotif'; + +it('should display an in progress notif', () => { + const wrapper = shallow( + <EditionsStatusNotif editionStatus={{ installationStatus: 'AUTOMATIC_IN_PROGRESS' }} /> + ); + expect(wrapper).toMatchSnapshot(); +}); + +it('should display an error notification', () => { + const wrapper = shallow( + <EditionsStatusNotif editionStatus={{ installationStatus: 'AUTOMATIC_FAILURE' }} /> + ); + expect(wrapper).toMatchSnapshot(); +}); + +it('should display a ready notification', () => { + const wrapper = shallow( + <EditionsStatusNotif editionStatus={{ installationStatus: 'AUTOMATIC_READY' }} /> + ); + expect(wrapper).toMatchSnapshot(); +}); diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/LicenseEditionForm-test.tsx b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/LicenseEditionForm-test.tsx index 7dff71932d5..a08b002a718 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/LicenseEditionForm-test.tsx +++ b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/LicenseEditionForm-test.tsx @@ -22,10 +22,6 @@ import { shallow } from 'enzyme'; import { click } from '../../../../helpers/testUtils'; import LicenseEditionForm from '../LicenseEditionForm'; -jest.mock('../../../../app/utils/getStore', () => { - const dispatch = jest.fn(); - return { default: () => ({ dispatch }) }; -}); jest.mock('../../../../api/marketplace', () => ({ applyLicense: jest.fn(() => Promise.resolve({ nextEditionKey: 'foo', installationStatus: 'AUTOMATIC_IN_PROGRESS' }) @@ -33,7 +29,6 @@ jest.mock('../../../../api/marketplace', () => ({ })); const applyLicense = require('../../../../api/marketplace').applyLicense as jest.Mock<any>; -const getStore = require('../../../../app/utils/getStore').default as jest.Mock<any>; const DEFAULT_EDITION = { key: 'foo', @@ -46,7 +41,6 @@ const DEFAULT_EDITION = { beforeEach(() => { applyLicense.mockClear(); - getStore().dispatch.mockClear(); }); it('should display correctly', () => { @@ -65,16 +59,27 @@ it('should correctly change the button based on the status', () => { }); it('should update the edition status after install', async () => { - const wrapper = getWrapper(); + const updateEditionStatus = jest.fn(); + const wrapper = getWrapper({ updateEditionStatus }); const form = wrapper.instance() as LicenseEditionForm; form.mounted = true; form.handleLicenseChange('mylicense', 'AUTOMATIC_INSTALL'); click(wrapper.find('button')); expect(applyLicense).toHaveBeenCalledWith({ license: 'mylicense' }); await new Promise(setImmediate); - expect(getStore().dispatch).toHaveBeenCalled(); + expect(updateEditionStatus).toHaveBeenCalledWith({ + nextEditionKey: 'foo', + installationStatus: 'AUTOMATIC_IN_PROGRESS' + }); }); function getWrapper(props = {}) { - return shallow(<LicenseEditionForm edition={DEFAULT_EDITION} onClose={jest.fn()} {...props} />); + return shallow( + <LicenseEditionForm + edition={DEFAULT_EDITION} + onClose={jest.fn()} + updateEditionStatus={jest.fn()} + {...props} + /> + ); } diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/EditionsStatusNotif-test.tsx.snap b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/EditionsStatusNotif-test.tsx.snap new file mode 100644 index 00000000000..807a7c6714a --- /dev/null +++ b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/EditionsStatusNotif-test.tsx.snap @@ -0,0 +1,45 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should display a ready notification 1`] = ` +<div + className="alert alert-page alert-success" +> + <span> + marketplace.status.AUTOMATIC_READY + </span> + <button + className="js-restart spacer-left" + onClick={[Function]} + > + marketplace.restart + </button> +</div> +`; + +exports[`should display an error notification 1`] = ` +<div + className="alert alert-page alert-danger" +> + marketplace.status.AUTOMATIC_FAILURE + <a + className="little-spacer-left" + href="https://www.sonarsource.com" + target="_blank" + > + marketplace.how_to_install + </a> +</div> +`; + +exports[`should display an in progress notif 1`] = ` +<div + className="alert alert-page alert-info" +> + <i + className="spinner spacer-right text-bottom" + /> + <span> + marketplace.status.AUTOMATIC_IN_PROGRESS + </span> +</div> +`; |