aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web/src/main/js/apps/marketplace
diff options
context:
space:
mode:
authorGrégoire Aubert <gregoire.aubert@sonarsource.com>2017-10-17 16:17:11 +0200
committerGrégoire Aubert <gregoire.aubert@sonarsource.com>2017-10-23 08:01:13 -0700
commitbff95ad641af2546712ea61d38a259f0847343c5 (patch)
tree56a4e216e4068da8062a2b8b0fd40a3dbf85d2d0 /server/sonar-web/src/main/js/apps/marketplace
parent3979c1713989085fa72b7d355ed544197de5b283 (diff)
downloadsonarqube-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')
-rw-r--r--server/sonar-web/src/main/js/apps/marketplace/App.tsx25
-rw-r--r--server/sonar-web/src/main/js/apps/marketplace/AppContainer.tsx1
-rw-r--r--server/sonar-web/src/main/js/apps/marketplace/EditionBoxes.tsx7
-rw-r--r--server/sonar-web/src/main/js/apps/marketplace/PendingActions.tsx5
-rw-r--r--server/sonar-web/src/main/js/apps/marketplace/__tests__/EditionBoxes-test.tsx1
-rw-r--r--server/sonar-web/src/main/js/apps/marketplace/components/EditionsStatusNotif.tsx72
-rw-r--r--server/sonar-web/src/main/js/apps/marketplace/components/LicenseEditionForm.tsx7
-rw-r--r--server/sonar-web/src/main/js/apps/marketplace/components/__tests__/EditionsStatusNotif-test.tsx43
-rw-r--r--server/sonar-web/src/main/js/apps/marketplace/components/__tests__/LicenseEditionForm-test.tsx23
-rw-r--r--server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/EditionsStatusNotif-test.tsx.snap45
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>
+`;