]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-9936 Update edition notification to be scoped to page and not whole administration
authorGrégoire Aubert <gregoire.aubert@sonarsource.com>
Tue, 17 Oct 2017 14:17:11 +0000 (16:17 +0200)
committerGrégoire Aubert <gregoire.aubert@sonarsource.com>
Mon, 23 Oct 2017 15:01:13 +0000 (08:01 -0700)
20 files changed:
server/sonar-web/src/main/js/app/components/AdminContainer.tsx
server/sonar-web/src/main/js/app/components/nav/settings/SettingsEditionsNotif.tsx [deleted file]
server/sonar-web/src/main/js/app/components/nav/settings/SettingsNav.tsx
server/sonar-web/src/main/js/app/components/nav/settings/__tests__/SettingsEditionsNotif-test.tsx [deleted file]
server/sonar-web/src/main/js/app/components/nav/settings/__tests__/SettingsNav-test.tsx
server/sonar-web/src/main/js/app/components/nav/settings/__tests__/__snapshots__/SettingsEditionsNotif-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/app/components/nav/settings/__tests__/__snapshots__/SettingsNav-test.tsx.snap
server/sonar-web/src/main/js/apps/marketplace/App.tsx
server/sonar-web/src/main/js/apps/marketplace/AppContainer.tsx
server/sonar-web/src/main/js/apps/marketplace/EditionBoxes.tsx
server/sonar-web/src/main/js/apps/marketplace/PendingActions.tsx
server/sonar-web/src/main/js/apps/marketplace/__tests__/EditionBoxes-test.tsx
server/sonar-web/src/main/js/apps/marketplace/components/EditionsStatusNotif.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/apps/marketplace/components/LicenseEditionForm.tsx
server/sonar-web/src/main/js/apps/marketplace/components/__tests__/EditionsStatusNotif-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/apps/marketplace/components/__tests__/LicenseEditionForm-test.tsx
server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/EditionsStatusNotif-test.tsx.snap [new file with mode: 0644]
server/sonar-web/src/main/js/store/appState/duck.ts
server/sonar-web/src/main/less/components/alerts.less
sonar-core/src/main/resources/org/sonar/l10n/core.properties

index fec085b91f2556123edbced348478198f3902830..ea9c6f1301bf53f3c19d0540313cbb7a826acb41 100644 (file)
@@ -24,20 +24,17 @@ import { connect } from 'react-redux';
 import SettingsNav from './nav/settings/SettingsNav';
 import { getAppState } from '../../store/rootReducer';
 import { getSettingsNavigation } from '../../api/nav';
-import { EditionStatus, getEditionStatus } from '../../api/marketplace';
-import { setAdminPages, setEditionStatus } from '../../store/appState/duck';
+import { setAdminPages } from '../../store/appState/duck';
 import { translate } from '../../helpers/l10n';
 import { Extension } from '../types';
 
 interface Props {
   appState: {
     adminPages: Extension[];
-    editionStatus?: EditionStatus;
     organizationsEnabled: boolean;
   };
   location: {};
   setAdminPages: (adminPages: Extension[]) => void;
-  setEditionStatus: (editionStatus: EditionStatus) => void;
 }
 
 class AdminContainer extends React.PureComponent<Props> {
@@ -56,17 +53,16 @@ class AdminContainer extends React.PureComponent<Props> {
   }
 
   loadData() {
-    Promise.all([getSettingsNavigation(), getEditionStatus()]).then(
-      ([r, editionStatus]) => {
+    getSettingsNavigation().then(
+      r => {
         this.props.setAdminPages(r.extensions);
-        this.props.setEditionStatus(editionStatus);
       },
       () => {}
     );
   }
 
   render() {
-    const { adminPages, editionStatus, organizationsEnabled } = this.props.appState;
+    const { adminPages, organizationsEnabled } = this.props.appState;
 
     // Check that the adminPages are loaded
     if (!adminPages) {
@@ -80,7 +76,6 @@ class AdminContainer extends React.PureComponent<Props> {
         <Helmet defaultTitle={defaultTitle} titleTemplate={'%s - ' + defaultTitle} />
         <SettingsNav
           customOrganizations={organizationsEnabled}
-          editionStatus={editionStatus}
           extensions={adminPages}
           location={this.props.location}
         />
@@ -94,6 +89,6 @@ const mapStateToProps = (state: any) => ({
   appState: getAppState(state)
 });
 
-const mapDispatchToProps = { setAdminPages, setEditionStatus };
+const mapDispatchToProps = { setAdminPages };
 
 export default connect(mapStateToProps, mapDispatchToProps)(AdminContainer as any);
diff --git a/server/sonar-web/src/main/js/app/components/nav/settings/SettingsEditionsNotif.tsx b/server/sonar-web/src/main/js/app/components/nav/settings/SettingsEditionsNotif.tsx
deleted file mode 100644 (file)
index 5df886e..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * 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 NavBarNotif from '../../../../components/nav/NavBarNotif';
-import { EditionStatus } from '../../../../api/marketplace';
-import { translate } from '../../../../helpers/l10n';
-
-interface Props {
-  editionStatus: EditionStatus;
-}
-
-export default class SettingsEditionsNotif extends React.PureComponent<Props> {
-  render() {
-    const { editionStatus } = this.props;
-
-    if (editionStatus.installationStatus === 'AUTOMATIC_IN_PROGRESS') {
-      return (
-        <NavBarNotif className="alert alert-info">
-          <i className="spinner spacer-right text-bottom" />
-          <span>{translate('marketplace.status.AUTOMATIC_IN_PROGRESS')}</span>
-        </NavBarNotif>
-      );
-    } else if (editionStatus.installationStatus === 'AUTOMATIC_READY') {
-      return (
-        <NavBarNotif className="alert alert-success">
-          <span>{translate('marketplace.status.AUTOMATIC_READY')}</span>
-        </NavBarNotif>
-      );
-    } else if (
-      ['MANUAL_IN_PROGRESS', 'AUTOMATIC_FAILURE'].includes(editionStatus.installationStatus)
-    ) {
-      return (
-        <NavBarNotif className="alert 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>
-        </NavBarNotif>
-      );
-    }
-    return null;
-  }
-}
index b677146a39278d82a26fa9891de66183a10f9261..7f7632d66306285cc5c11344fde7a2cbabf365f9 100644 (file)
@@ -21,14 +21,11 @@ import * as React from 'react';
 import * as classNames from 'classnames';
 import { IndexLink, Link } from 'react-router';
 import ContextNavBar from '../../../../components/nav/ContextNavBar';
-import SettingsEditionsNotif from './SettingsEditionsNotif';
 import NavBarTabs from '../../../../components/nav/NavBarTabs';
-import { EditionStatus } from '../../../../api/marketplace';
 import { Extension } from '../../../types';
 import { translate } from '../../../../helpers/l10n';
 
 interface Props {
-  editionStatus?: EditionStatus;
   extensions: Extension[];
   customOrganizations: boolean;
   location: {};
@@ -75,7 +72,7 @@ export default class SettingsNav extends React.PureComponent<Props> {
   };
 
   render() {
-    const { customOrganizations, editionStatus, extensions } = this.props;
+    const { customOrganizations, extensions } = this.props;
     const isSecurity = this.isSecurityActive();
     const isProjects = this.isProjectsActive();
     const isSystem = this.isSystemActive();
@@ -94,15 +91,8 @@ export default class SettingsNav extends React.PureComponent<Props> {
 
     const hasSupportExtension = extensionsWithoutSupport.length < extensions.length;
 
-    let notifComponent;
-    if (editionStatus && editionStatus.installationStatus !== 'NONE') {
-      notifComponent = <SettingsEditionsNotif editionStatus={editionStatus} />;
-    }
     return (
-      <ContextNavBar
-        id="context-navigation"
-        height={notifComponent ? 95 : 65}
-        notif={notifComponent}>
+      <ContextNavBar id="context-navigation" height={65}>
         <h1 className="navbar-context-header">
           <strong>{translate('layout.settings')}</strong>
         </h1>
diff --git a/server/sonar-web/src/main/js/app/components/nav/settings/__tests__/SettingsEditionsNotif-test.tsx b/server/sonar-web/src/main/js/app/components/nav/settings/__tests__/SettingsEditionsNotif-test.tsx
deleted file mode 100644 (file)
index 55612c0..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * 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 SettingsEditionsNotif from '../SettingsEditionsNotif';
-
-it('should display an in progress notif', () => {
-  const wrapper = shallow(
-    <SettingsEditionsNotif editionStatus={{ installationStatus: 'AUTOMATIC_IN_PROGRESS' }} />
-  );
-  expect(wrapper).toMatchSnapshot();
-});
-
-it('should display an error notification', () => {
-  const wrapper = shallow(
-    <SettingsEditionsNotif editionStatus={{ installationStatus: 'AUTOMATIC_FAILURE' }} />
-  );
-  expect(wrapper).toMatchSnapshot();
-});
-
-it('should display a ready notification', () => {
-  const wrapper = shallow(
-    <SettingsEditionsNotif editionStatus={{ installationStatus: 'AUTOMATIC_READY' }} />
-  );
-  expect(wrapper).toMatchSnapshot();
-});
index f819af90c4931265e63b652987c6b8033c28be71..9650ec9f4a99823d0f5003f6b5dc1c21cb319b88 100644 (file)
@@ -24,24 +24,7 @@ import SettingsNav from '../SettingsNav';
 it('should work with extensions', () => {
   const extensions = [{ key: 'foo', name: 'Foo' }];
   const wrapper = shallow(
-    <SettingsNav
-      customOrganizations={false}
-      editionStatus={{ installationStatus: 'NONE' }}
-      extensions={extensions}
-      location={{}}
-    />
+    <SettingsNav customOrganizations={false} extensions={extensions} location={{}} />
   );
   expect(wrapper).toMatchSnapshot();
 });
-
-it('should display an edition notification', () => {
-  const wrapper = shallow(
-    <SettingsNav
-      customOrganizations={false}
-      editionStatus={{ installationStatus: 'AUTOMATIC_IN_PROGRESS' }}
-      extensions={[]}
-      location={{}}
-    />
-  );
-  expect({ ...wrapper.find('ContextNavBar').props(), children: [] }).toMatchSnapshot();
-});
diff --git a/server/sonar-web/src/main/js/app/components/nav/settings/__tests__/__snapshots__/SettingsEditionsNotif-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/settings/__tests__/__snapshots__/SettingsEditionsNotif-test.tsx.snap
deleted file mode 100644 (file)
index 030ff10..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should display a ready notification 1`] = `
-<NavBarNotif
-  className="alert alert-success"
->
-  <span>
-    marketplace.status.AUTOMATIC_READY
-  </span>
-</NavBarNotif>
-`;
-
-exports[`should display an error notification 1`] = `
-<NavBarNotif
-  className="alert alert-danger"
->
-  marketplace.status.AUTOMATIC_FAILURE
-  <a
-    className="little-spacer-left"
-    href="https://www.sonarsource.com"
-    target="_blank"
-  >
-    marketplace.how_to_install
-  </a>
-</NavBarNotif>
-`;
-
-exports[`should display an in progress notif 1`] = `
-<NavBarNotif
-  className="alert alert-info"
->
-  <i
-    className="spinner spacer-right text-bottom"
-  />
-  <span>
-    marketplace.status.AUTOMATIC_IN_PROGRESS
-  </span>
-</NavBarNotif>
-`;
index 9dd9e567752c32624356fbdfa313a4e2f6ce85a3..62c113f24bb16ade30727fac5ffa1be37d4d449f 100644 (file)
@@ -1,20 +1,5 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`should display an edition notification 1`] = `
-Object {
-  "children": Array [],
-  "height": 95,
-  "id": "context-navigation",
-  "notif": <SettingsEditionsNotif
-    editionStatus={
-        Object {
-            "installationStatus": "AUTOMATIC_IN_PROGRESS",
-          }
-    }
-/>,
-}
-`;
-
 exports[`should work with extensions 1`] = `
 <ContextNavBar
   height={65}
index 18fc8adaaeeddf709271419ebb8cd928c9f9c47c..02910f26c5e94cf00445eb17af651be27bcf7c62 100644 (file)
@@ -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
index 64794ebbda247ae0f4ae64e6bee24b88ce8afbe1..909f284fbdf3d409d8dca00dd286550bd01dcef0 100644 (file)
@@ -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
index 58ffe846c632dc2c0d7a3fb794085ca2cb70f143..e1f5f373f1f7b0c02fd9edfe3bc6ab737e4a11e0 100644 (file)
@@ -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>
     );
index 921afeef3da6f7947ba11c03b2a3be40c82b2b22..bc96da9bbf078ec2b33fe45010c10260ae76a566 100644 (file)
@@ -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 = () => {
index c3525b0e3044059d75220fbf7c5fc58c53d17836..e64c714577f59e5020b1355bcbe0a511608bf807 100644 (file)
@@ -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 (file)
index 0000000..1ef4aac
--- /dev/null
@@ -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;
+  }
+}
index 1f00178f113f3351bc6c368028ed59ad773f3e4f..fb513eba0bc58d2b2e4afa7bc8b063bd629d56ab 100644 (file)
 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 (file)
index 0000000..48a65f4
--- /dev/null
@@ -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();
+});
index 7dff71932d5f044fcb88239f82557efc251c0c79..a08b002a718a735b737a5225d66e794c2d9be233 100644 (file)
@@ -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 (file)
index 0000000..807a7c6
--- /dev/null
@@ -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>
+`;
index d783242272fc3bc70c0c4a3328f48bf08720fc89..abb05d0b3090b2f5c4e4a6653c90457a0252c991 100644 (file)
  */
 
 import { Extension } from '../../app/types';
-import { EditionStatus } from '../../api/marketplace';
 
 interface AppState {
   adminPages?: Extension[];
   authenticationError: boolean;
   authorizationError: boolean;
-  editionStatus?: EditionStatus;
   organizationsEnabled: boolean;
   qualifiers?: string[];
 }
@@ -40,20 +38,11 @@ interface SetAdminPagesAction {
   adminPages: Extension[];
 }
 
-interface SetEditionStatusAction {
-  type: 'SET_EDITION_STATUS';
-  editionStatus: EditionStatus;
-}
-
 interface RequireAuthorizationAction {
   type: 'REQUIRE_AUTHORIZATION';
 }
 
-export type Action =
-  | SetAppStateAction
-  | SetAdminPagesAction
-  | SetEditionStatusAction
-  | RequireAuthorizationAction;
+export type Action = SetAppStateAction | SetAdminPagesAction | RequireAuthorizationAction;
 
 export function setAppState(appState: AppState): SetAppStateAction {
   return {
@@ -70,10 +59,6 @@ export function requireAuthorization(): RequireAuthorizationAction {
   return { type: 'REQUIRE_AUTHORIZATION' };
 }
 
-export function setEditionStatus(editionStatus: EditionStatus): SetEditionStatusAction {
-  return { type: 'SET_EDITION_STATUS', editionStatus };
-}
-
 const defaultValue: AppState = {
   authenticationError: false,
   authorizationError: false,
@@ -89,10 +74,6 @@ export default function(state: AppState = defaultValue, action: Action): AppStat
     return { ...state, adminPages: action.adminPages };
   }
 
-  if (action.type === 'SET_EDITION_STATUS') {
-    return { ...state, editionStatus: action.editionStatus };
-  }
-
   if (action.type === 'REQUIRE_AUTHORIZATION') {
     return { ...state, authorizationError: true };
   }
index 656940462611b4383fe9aa2efdcb9645fa7556b0..ee471b96bbb6f2eb5df3001d262a31f0d3c3b3f1 100644 (file)
   vertical-align: middle;
 }
 
+.alert-page {
+  margin-bottom: 16px;
+  padding: 8px;
+}
+
 .modal-alert {
   margin: -10px -10px 16px;
   padding: 10px;
index 373b3504a627278e9325d3d85350a9d72a9363c0..9d717313a9bdc584b1e130138382e3b475b120d6 100644 (file)
@@ -2094,11 +2094,11 @@ marketplace.update_to_x=Update to {0}
 marketplace.uninstall=Uninstall
 marketplace.i_accept_the=I accept the
 marketplace.terms_and_conditions=Terms and Conditions
-marketplace.editions_unavailable=Explore our Editions: advanced feature packs brought to you by SonarSource on {url}
+marketplace.editions_unavailable=Explore our Commercial Editions: advanced feature packs brought to you by SonarSource on {url}
 marketplace.status.AUTOMATIC_IN_PROGRESS=Updating your installation... Please wait...
-marketplace.status.AUTOMATIC_READY=New installation complete. Please restart Server to benefit from it.
-marketplace.status.MANUAL_IN_PROGRESS=Can't install Developer Edition because of internet access issue. Please manually install the package in your SonarQube's plugins folder.
-marketplace.status.AUTOMATIC_FAILURE=Can't install Developer Edition. Please manually install the package in your SonarQube's plugins folder.
+marketplace.status.AUTOMATIC_READY=Commercial Edition successfully installed. Please restart Server to activate your new features.
+marketplace.status.MANUAL_IN_PROGRESS=Can't install Commercial Edition because of internet access issue. Please manually install the package in your SonarQube's plugins folder.
+marketplace.status.AUTOMATIC_FAILURE=Can't install Commercial Edition. Please manually install the package in your SonarQube's plugins folder.
 marketplace.how_to_install=How to install it?
 marketplace.enter_license_for_x=Enter your license key for {0}
 marketplace.i_need_a_license=I need a license key