From: Grégoire Aubert
Date: Mon, 16 Oct 2017 15:18:19 +0000 (+0200)
Subject: SONAR-9937 Create a form to apply the license and install/update an edition
X-Git-Tag: 6.7-RC1~96
X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=4b9c83214cb871d3f73d083411278266327c8cd4;p=sonarqube.git
SONAR-9937 Create a form to apply the license and install/update an edition
---
diff --git a/server/sonar-web/src/main/js/api/marketplace.ts b/server/sonar-web/src/main/js/api/marketplace.ts
index 282be5bc7b3..17ef7d367a3 100644
--- a/server/sonar-web/src/main/js/api/marketplace.ts
+++ b/server/sonar-web/src/main/js/api/marketplace.ts
@@ -17,7 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { checkStatus, corsRequest, getJSON, parseJSON } from '../helpers/request';
+import { checkStatus, corsRequest, getJSON, parseJSON, postJSON } from '../helpers/request';
import throwGlobalError from '../app/utils/throwGlobalError';
export interface Edition {
@@ -56,3 +56,16 @@ export function getEditionsList(): Promise {
.then(checkStatus)
.then(parseJSON);
}
+
+export function getLicensePreview(data: {
+ license: string;
+}): Promise<{
+ nextEditionKey: string;
+ previewStatus: 'NO_INSTALL' | 'AUTOMATIC_INSTALL' | 'MANUAL_INSTALL';
+}> {
+ return postJSON('/api/editions/preview', data).catch(throwGlobalError);
+}
+
+export function applyLicense(data: { license: string }): Promise {
+ return postJSON('/api/editions/apply_license', data).catch(throwGlobalError);
+}
diff --git a/server/sonar-web/src/main/js/app/utils/exposeLibraries.js b/server/sonar-web/src/main/js/app/utils/exposeLibraries.js
index b0d2a4fdd9f..b1c1238c9a6 100644
--- a/server/sonar-web/src/main/js/app/utils/exposeLibraries.js
+++ b/server/sonar-web/src/main/js/app/utils/exposeLibraries.js
@@ -29,6 +29,7 @@ import DateFromNow from '../../components/intl/DateFromNow';
import DateFormatter from '../../components/intl/DateFormatter';
import DateTimeFormatter from '../../components/intl/DateTimeFormatter';
import FavoriteContainer from '../../components/controls/FavoriteContainer';
+import LicenseEditionSet from '../../apps/marketplace/components/LicenseEditionSet';
import ListFooter from '../../components/controls/ListFooter';
import Tooltip from '../../components/controls/Tooltip';
import ModalForm from '../../components/common/modal-form';
@@ -48,6 +49,7 @@ const exposeLibraries = () => {
DateFormatter,
DateTimeFormatter,
FavoriteContainer,
+ LicenseEditionSet,
ListFooter,
Modal,
Tooltip,
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 5c74a1e8aa4..64290f13e9e 100644
--- a/server/sonar-web/src/main/js/apps/marketplace/EditionBoxes.tsx
+++ b/server/sonar-web/src/main/js/apps/marketplace/EditionBoxes.tsx
@@ -20,7 +20,8 @@
import * as React from 'react';
import { FormattedMessage } from 'react-intl';
import EditionBox from './components/EditionBox';
-import { Editions, EditionStatus, getEditionsList } from '../../api/marketplace';
+import LicenseEditionForm from './components/LicenseEditionForm';
+import { Edition, Editions, EditionStatus, getEditionsList } from '../../api/marketplace';
import { translate } from '../../helpers/l10n';
export interface Props {
@@ -32,6 +33,7 @@ interface State {
editions: Editions;
editionsError: boolean;
loading: boolean;
+ installEdition?: Edition;
}
export default class EditionBoxes extends React.PureComponent {
@@ -67,8 +69,11 @@ export default class EditionBoxes extends React.PureComponent {
);
};
+ handleOpenLicenseForm = (edition: Edition) => this.setState({ installEdition: edition });
+ handleCloseLicenseForm = () => this.setState({ installEdition: undefined });
+
render() {
- const { editions, loading } = this.state;
+ const { editions, loading, installEdition } = this.state;
if (loading) {
return null;
}
@@ -95,9 +100,14 @@ export default class EditionBoxes extends React.PureComponent {
editionKey={key}
editionStatus={this.props.editionStatus}
key={key}
+ onInstall={this.handleOpenLicenseForm}
/>
))
)}
+
+ {installEdition && (
+
+ )}
);
}
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 49ee1abdcde..ca32326f225 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
@@ -28,26 +28,28 @@ const DEFAULT_STATUS: EditionStatus = {
installationStatus: 'NONE'
};
+const DEFAULT_EDITIONS = {
+ foo: {
+ name: 'Foo',
+ desc: 'Foo desc',
+ download_link: 'download_url',
+ more_link: 'more_url',
+ request_license_link: 'license_url'
+ },
+ bar: {
+ name: 'Bar',
+ desc: 'Bar desc',
+ download_link: 'download_url',
+ more_link: 'more_url',
+ request_license_link: 'license_url'
+ }
+};
+
it('should display the edition boxes', () => {
const wrapper = getWrapper();
expect(wrapper).toMatchSnapshot();
wrapper.setState({
- editions: {
- foo: {
- name: 'Foo',
- desc: 'Foo desc',
- download_link: 'download_url',
- more_link: 'more_url',
- request_license_link: 'license_url'
- },
- bar: {
- name: 'Bar',
- desc: 'Bar desc',
- download_link: 'download_url',
- more_link: 'more_url',
- request_license_link: 'license_url'
- }
- },
+ editions: DEFAULT_EDITIONS,
loading: false
});
expect(wrapper).toMatchSnapshot();
@@ -59,6 +61,16 @@ it('should display an error message', () => {
expect(wrapper).toMatchSnapshot();
});
+it('should open the license form', () => {
+ const wrapper = getWrapper();
+ wrapper.setState({
+ editions: DEFAULT_EDITIONS,
+ loading: false
+ });
+ (wrapper.instance() as EditionBoxes).handleOpenLicenseForm(DEFAULT_EDITIONS.foo);
+ expect(wrapper.find('LicenseEditionForm').exists()).toBeTruthy();
+});
+
function getWrapper(props = {}) {
return shallow(
diff --git a/server/sonar-web/src/main/js/apps/marketplace/__tests__/__snapshots__/EditionBoxes-test.tsx.snap b/server/sonar-web/src/main/js/apps/marketplace/__tests__/__snapshots__/EditionBoxes-test.tsx.snap
index 7ee4e6b73e9..cfc6695810a 100644
--- a/server/sonar-web/src/main/js/apps/marketplace/__tests__/__snapshots__/EditionBoxes-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/marketplace/__tests__/__snapshots__/EditionBoxes-test.tsx.snap
@@ -49,6 +49,7 @@ exports[`should display the edition boxes 2`] = `
"nextEditionKey": "",
}
}
+ onInstall={[Function]}
/>
`;
diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/EditionBox.tsx b/server/sonar-web/src/main/js/apps/marketplace/components/EditionBox.tsx
index 3299504fdad..43dcd7ea577 100644
--- a/server/sonar-web/src/main/js/apps/marketplace/components/EditionBox.tsx
+++ b/server/sonar-web/src/main/js/apps/marketplace/components/EditionBox.tsx
@@ -22,13 +22,16 @@ import CheckIcon from '../../../components/icons-components/CheckIcon';
import { Edition, EditionStatus } from '../../../api/marketplace';
import { translate } from '../../../helpers/l10n';
-export interface Props {
+interface Props {
edition: Edition;
editionKey: string;
editionStatus?: EditionStatus;
+ onInstall: (edition: Edition) => void;
}
export default class EditionBox extends React.PureComponent {
+ handleInstall = () => this.props.onInstall(this.props.edition);
+
render() {
const { edition, editionKey, editionStatus } = this.props;
const isInstalled = editionStatus && editionStatus.currentEditionKey === editionKey;
@@ -58,7 +61,9 @@ export default class EditionBox extends React.PureComponent {
{translate('marketplace.learn_more')}
{!isInstalled && (
- {translate('marketplace.install')}
+
+ {translate('marketplace.install')}
+
)}
{isInstalled && (
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
new file mode 100644
index 00000000000..1f00178f113
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/marketplace/components/LicenseEditionForm.tsx
@@ -0,0 +1,117 @@
+/*
+ * 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 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 { translate, translateWithParameters } from '../../../helpers/l10n';
+
+export interface Props {
+ edition: Edition;
+ onClose: () => void;
+}
+
+interface State {
+ license: string;
+ loading: boolean;
+ status?: string;
+}
+
+export default class LicenseEditionForm extends React.PureComponent {
+ mounted: boolean;
+ state: State = { license: '', loading: false };
+
+ componentDidMount() {
+ this.mounted = true;
+ }
+
+ componentWillUnmount() {
+ this.mounted = false;
+ }
+
+ handleLicenseChange = (license: string, status?: string) => {
+ if (this.mounted) {
+ this.setState({ license, status });
+ }
+ };
+
+ handleCancelClick = (event: React.SyntheticEvent) => {
+ event.preventDefault();
+ this.props.onClose();
+ };
+
+ handleConfirmClick = (event: React.SyntheticEvent) => {
+ event.preventDefault();
+ const { license, status } = this.state;
+ if (license && status && ['AUTOMATIC_INSTALL', 'NO_INSTALL'].includes(status)) {
+ this.setState({ loading: true });
+ applyLicense({ license }).then(
+ editionStatus => {
+ getStore().dispatch(setEditionStatus(editionStatus));
+ this.props.onClose();
+ },
+ () => {
+ if (this.mounted) {
+ this.setState({ loading: false });
+ }
+ }
+ );
+ }
+ };
+
+ render() {
+ const { edition } = this.props;
+ const { status } = this.state;
+ const header = translateWithParameters('marketplace.install_x', edition.name);
+ return (
+
+
+
+
+
+
+ {this.state.loading && }
+ {status &&
+ ['NO_INSTALL', 'AUTOMATIC_INSTALL'].includes(status) && (
+
+ {status === 'NO_INSTALL' ? translate('save') : translate('marketplace.install')}
+
+ )}
+
+ {translate('cancel')}
+
+
+
+ );
+ }
+}
diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/LicenseEditionSet.tsx b/server/sonar-web/src/main/js/apps/marketplace/components/LicenseEditionSet.tsx
new file mode 100644
index 00000000000..7b74d6b6bcf
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/marketplace/components/LicenseEditionSet.tsx
@@ -0,0 +1,142 @@
+/*
+ * 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 * as classNames from 'classnames';
+import { debounce } from 'lodash';
+import { Edition, getLicensePreview } from '../../../api/marketplace';
+import { translate, translateWithParameters } from '../../../helpers/l10n';
+
+export interface Props {
+ className?: string;
+ edition: Edition;
+ updateLicense: (license?: string, status?: string) => void;
+}
+
+interface State {
+ license: string;
+ loading: boolean;
+ previewStatus?: string;
+}
+
+export default class LicenseEditionSet extends React.PureComponent {
+ mounted: boolean;
+
+ constructor(props: Props) {
+ super(props);
+ this.state = { license: '', loading: false };
+ this.fetchLicensePreview = debounce(this.fetchLicensePreview, 250);
+ }
+
+ componentDidMount() {
+ this.mounted = true;
+ }
+
+ componentWillUnmount() {
+ this.mounted = false;
+ }
+
+ fetchLicensePreview = (license: string) =>
+ getLicensePreview({ license }).then(
+ r => {
+ if (this.mounted) {
+ this.updateLicense(license, r.previewStatus);
+ }
+ },
+ () => {
+ if (this.mounted) {
+ this.updateLicense(license, undefined);
+ }
+ }
+ );
+
+ handleLicenseChange = (event: React.SyntheticEvent) => {
+ const license = event.currentTarget.value;
+ if (license) {
+ this.fetchLicensePreview(license);
+ this.setState({ license });
+ } else {
+ this.updateLicense(license, undefined);
+ }
+ };
+
+ updateLicense = (license: string, previewStatus?: string) => {
+ this.setState({ license, previewStatus });
+ this.props.updateLicense(license, previewStatus);
+ };
+
+ render() {
+ const { className, edition } = this.props;
+ const { license, previewStatus } = this.state;
+ return (
+
+ );
+ }
+}
diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/EditionBox-test.tsx b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/EditionBox-test.tsx
index ebb42f27489..e0faa1bc3a3 100644
--- a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/EditionBox-test.tsx
+++ b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/EditionBox-test.tsx
@@ -94,6 +94,7 @@ function getWrapper(props = {}) {
edition={DEFAULT_EDITION}
editionKey="foo"
editionStatus={DEFAULT_STATUS}
+ onInstall={jest.fn()}
{...props}
/>
);
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
new file mode 100644
index 00000000000..850d9afb411
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/LicenseEditionForm-test.tsx
@@ -0,0 +1,79 @@
+/*
+ * 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 { 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' })
+ )
+}));
+
+const applyLicense = require('../../../../api/marketplace').applyLicense as jest.Mock;
+const getStore = require('../../../../app/utils/getStore').default as jest.Mock;
+
+const DEFAULT_EDITION = {
+ name: 'Foo',
+ desc: 'Foo desc',
+ download_link: 'download_url',
+ more_link: 'more_url',
+ request_license_link: 'license_url'
+};
+
+beforeEach(() => {
+ applyLicense.mockClear();
+ getStore().dispatch.mockClear();
+});
+
+it('should display correctly', () => {
+ expect(getWrapper()).toMatchSnapshot();
+});
+
+it('should correctly change the button based on the status', () => {
+ const wrapper = getWrapper();
+ (wrapper.instance() as LicenseEditionForm).mounted = true;
+ wrapper.setState({ status: 'NO_INSTALL' });
+ expect(wrapper.find('button')).toMatchSnapshot();
+ wrapper.setState({ status: 'AUTOMATIC_INSTALL' });
+ expect(wrapper.find('button')).toMatchSnapshot();
+ wrapper.setState({ status: 'MANUAL_INSTALL' });
+ expect(wrapper.find('button').exists()).toBeFalsy();
+});
+
+it('should update the edition status after install', async () => {
+ const wrapper = getWrapper();
+ 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();
+});
+
+function getWrapper(props = {}) {
+ return shallow( );
+}
diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/LicenseEditionSet-test.tsx b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/LicenseEditionSet-test.tsx
new file mode 100644
index 00000000000..49c97b43027
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/LicenseEditionSet-test.tsx
@@ -0,0 +1,81 @@
+/*
+ * 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 { change } from '../../../../helpers/testUtils';
+import LicenseEditionSet from '../LicenseEditionSet';
+
+jest.mock('../../../../api/marketplace', () => ({
+ getLicensePreview: jest.fn(() =>
+ Promise.resolve({ nextEditionKey: 'foo', previewStatus: 'NO_INSTALL' })
+ )
+}));
+
+jest.mock('lodash', () => {
+ const lodash = require.requireActual('lodash');
+ lodash.debounce = (fn: Function) => (...args: any[]) => fn(args);
+ return lodash;
+});
+
+const getLicensePreview = require('../../../../api/marketplace').getLicensePreview as jest.Mock<
+ any
+>;
+
+const DEFAULT_EDITION = {
+ name: 'Foo',
+ desc: 'Foo desc',
+ download_link: 'download_url',
+ more_link: 'more_url',
+ request_license_link: 'license_url'
+};
+
+beforeEach(() => {
+ getLicensePreview.mockClear();
+});
+
+it('should display correctly', () => {
+ expect(getWrapper()).toMatchSnapshot();
+});
+
+it('should correctly display status message after checking license', async () => {
+ await testLicenseStatus('NO_INSTALL');
+ await testLicenseStatus('AUTOMATIC_INSTALL');
+ await testLicenseStatus('MANUAL_INSTALL');
+});
+
+function getWrapper(props = {}) {
+ return shallow(
+
+ );
+}
+
+async function testLicenseStatus(status: string) {
+ getLicensePreview.mockImplementation(() =>
+ Promise.resolve({ nextEditionKey: 'foo', previewStatus: status })
+ );
+ const updateLicense = jest.fn();
+ const wrapper = getWrapper({ updateLicense });
+ (wrapper.instance() as LicenseEditionSet).mounted = true;
+ change(wrapper.find('textarea'), 'mylicense');
+ expect(getLicensePreview).toHaveBeenCalled();
+ await new Promise(setImmediate);
+ expect(updateLicense).toHaveBeenCalled();
+ expect(wrapper.find('p.alert')).toMatchSnapshot();
+}
diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/EditionBox-test.tsx.snap b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/EditionBox-test.tsx.snap
index 6814875f5ba..e37bd585c1b 100644
--- a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/EditionBox-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/EditionBox-test.tsx.snap
@@ -72,6 +72,7 @@ exports[`should disable uninstall button 1`] = `
marketplace.install
@@ -184,6 +185,7 @@ exports[`should display the edition 1`] = `
marketplace.install
diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/LicenseEditionForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/LicenseEditionForm-test.tsx.snap
new file mode 100644
index 00000000000..492f821f630
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/LicenseEditionForm-test.tsx.snap
@@ -0,0 +1,67 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should correctly change the button based on the status 1`] = `
+
+ save
+
+`;
+
+exports[`should correctly change the button based on the status 2`] = `
+
+ marketplace.install
+
+`;
+
+exports[`should display correctly 1`] = `
+
+
+
+ marketplace.install_x.Foo
+
+
+
+
+
+`;
diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/LicenseEditionSet-test.tsx.snap b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/LicenseEditionSet-test.tsx.snap
new file mode 100644
index 00000000000..9b89e852330
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/LicenseEditionSet-test.tsx.snap
@@ -0,0 +1,76 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should correctly display status message after checking license 1`] = `
+
+ marketplace.license_preview_status.NO_INSTALL.Foo
+
+`;
+
+exports[`should correctly display status message after checking license 2`] = `
+
+ marketplace.license_preview_status.AUTOMATIC_INSTALL.Foo
+
+`;
+
+exports[`should correctly display status message after checking license 3`] = `
+
+ marketplace.license_preview_status.MANUAL_INSTALL.Foo
+
+
+ marketplace.download_package
+
+
+ marketplace.how_to_install
+
+
+
+`;
+
+exports[`should display correctly 1`] = `
+
+`;
diff --git a/server/sonar-web/src/main/less/init/misc.less b/server/sonar-web/src/main/less/init/misc.less
index cff3da4fdf0..b5f4e4f5ada 100644
--- a/server/sonar-web/src/main/less/init/misc.less
+++ b/server/sonar-web/src/main/less/init/misc.less
@@ -226,6 +226,10 @@ td.big-spacer-top {
pointer-events: none !important;
}
+.display-block {
+ display: block !important;
+}
+
.display-inline-block {
display: inline-block !important;
}
diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties
index a91bb089669..373b3504a62 100644
--- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties
+++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties
@@ -2068,6 +2068,7 @@ marketplace.restart=Restart
marketplace.revert=Revert
marketplace.system_upgrades=System Upgrades
marketplace.install=Install
+marketplace.install_x=Install {0}
marketplace.installed=Installed
marketplace.installing=Installing...
marketplace._installed=installed
@@ -2085,6 +2086,9 @@ marketplace.status.COMPATIBLE=Compatible
marketplace.status.INCOMPATIBLE=Incompatible
marketplace.status.REQUIRES_SYSTEM_UPGRADE=Requires system update
marketplace.status.DEPS_REQUIRE_SYSTEM_UPGRADE=Some of dependencies requires system update
+marketplace.license_preview_status.NO_INSTALL=No installation needed, your license will be updated directly.
+marketplace.license_preview_status.AUTOMATIC_INSTALL=After clicking on "Install", your {0} will be automatically downloaded and installed, and you'll need to restart your server once it's completed.
+marketplace.license_preview_status.MANUAL_INSTALL=Can't install {0} because of internet access issue. Please manually install the package in your SonarQube's plugins folder.
marketplace.installing_this_plugin_will_also_install_x=Installing this plugin will also install: {0}
marketplace.update_to_x=Update to {0}
marketplace.uninstall=Uninstall
@@ -2096,6 +2100,9 @@ marketplace.status.AUTOMATIC_READY=New installation complete. Please restart Ser
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.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
+marketplace.download_package=Download package
#------------------------------------------------------------------------------
#