diff options
Diffstat (limited to 'server')
15 files changed, 634 insertions, 21 deletions
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<Editions> { .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<EditionStatus> { + 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<Props, State> { @@ -67,8 +69,11 @@ export default class EditionBoxes extends React.PureComponent<Props, State> { ); }; + 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<Props, State> { editionKey={key} editionStatus={this.props.editionStatus} key={key} + onInstall={this.handleOpenLicenseForm} /> )) )} + + {installEdition && ( + <LicenseEditionForm edition={installEdition} onClose={this.handleCloseLicenseForm} /> + )} </div> ); } 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( <EditionBoxes editionStatus={DEFAULT_STATUS} updateCenterActive={true} {...props} /> 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]} /> <EditionBox edition={ @@ -68,6 +69,7 @@ exports[`should display the edition boxes 2`] = ` "nextEditionKey": "", } } + onInstall={[Function]} /> </div> `; 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<Props> { + 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<Props> { {translate('marketplace.learn_more')} </a> {!isInstalled && ( - <button disabled={installInProgress}>{translate('marketplace.install')}</button> + <button disabled={installInProgress} onClick={this.handleInstall}> + {translate('marketplace.install')} + </button> )} {isInstalled && ( <button className="button-red" disabled={installInProgress}> 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<Props, State> { + 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<HTMLAnchorElement>) => { + event.preventDefault(); + this.props.onClose(); + }; + + handleConfirmClick = (event: React.SyntheticEvent<HTMLButtonElement>) => { + 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 ( + <Modal + isOpen={true} + contentLabel={header} + className="modal" + overlayClassName="modal-overlay" + onRequestClose={this.props.onClose}> + <header className="modal-head"> + <h2>{header}</h2> + </header> + + <LicenseEditionSet + className="modal-body" + edition={edition} + updateLicense={this.handleLicenseChange} + /> + + <footer className="modal-foot"> + {this.state.loading && <i className="spinner spacer-right" />} + {status && + ['NO_INSTALL', 'AUTOMATIC_INSTALL'].includes(status) && ( + <button className="js-confirm" onClick={this.handleConfirmClick}> + {status === 'NO_INSTALL' ? translate('save') : translate('marketplace.install')} + </button> + )} + <a className="js-modal-close" href="#" onClick={this.handleCancelClick}> + {translate('cancel')} + </a> + </footer> + </Modal> + ); + } +} 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<Props, State> { + 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<HTMLTextAreaElement>) => { + 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 ( + <div className={className}> + <label htmlFor="set-license"> + {translateWithParameters('marketplace.enter_license_for_x', edition.name)} + <em className="mandatory">*</em> + </label> + <textarea + autoFocus={true} + id="set-license" + className="spacer-top display-block" + cols={62} + onChange={this.handleLicenseChange} + required={true} + rows={6} + value={license} + /> + {previewStatus && ( + <p + className={classNames('alert spacer-top', { + 'alert-warning': previewStatus === 'AUTOMATIC_INSTALL', + 'alert-success': previewStatus === 'NO_INSTALL', + 'alert-danger': previewStatus === 'MANUAL_INSTALL' + })}> + {translateWithParameters( + 'marketplace.license_preview_status.' + previewStatus, + edition.name + )} + {previewStatus === 'MANUAL_INSTALL' && ( + <p className="spacer-top"> + <a + className="button" + download={`sonarqube-${edition.name}.zip`} + href={edition.download_link} + target="_blank"> + {translate('marketplace.download_package')} + </a> + <a + className="spacer-left" + href="https://redirect.sonarsource.com/doc/how-to-install-an-edition.html" + target="_blank"> + {translate('marketplace.how_to_install')} + </a> + </p> + )} + </p> + )} + <a + className="display-inline-block spacer-top" + href={edition.request_license_link} + target="_blank"> + {translate('marketplace.i_need_a_license')} + </a> + </div> + ); + } +} 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<any>; +const getStore = require('../../../../app/utils/getStore').default 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(() => { + 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(<LicenseEditionForm edition={DEFAULT_EDITION} onClose={jest.fn()} {...props} />); +} 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( + <LicenseEditionSet edition={DEFAULT_EDITION} updateLicense={jest.fn()} {...props} /> + ); +} + +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`] = ` </a> <button disabled={true} + onClick={[Function]} > marketplace.install </button> @@ -184,6 +185,7 @@ exports[`should display the edition 1`] = ` </a> <button disabled={false} + onClick={[Function]} > marketplace.install </button> 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`] = ` +<button + className="js-confirm" + onClick={[Function]} +> + save +</button> +`; + +exports[`should correctly change the button based on the status 2`] = ` +<button + className="js-confirm" + onClick={[Function]} +> + marketplace.install +</button> +`; + +exports[`should display correctly 1`] = ` +<Modal + ariaHideApp={true} + bodyOpenClassName="ReactModal__Body--open" + className="modal" + closeTimeoutMS={0} + contentLabel="marketplace.install_x.Foo" + isOpen={true} + onRequestClose={[Function]} + overlayClassName="modal-overlay" + parentSelector={[Function]} + portalClassName="ReactModalPortal" + shouldCloseOnOverlayClick={true} +> + <header + className="modal-head" + > + <h2> + marketplace.install_x.Foo + </h2> + </header> + <LicenseEditionSet + className="modal-body" + edition={ + Object { + "desc": "Foo desc", + "download_link": "download_url", + "more_link": "more_url", + "name": "Foo", + "request_license_link": "license_url", + } + } + updateLicense={[Function]} + /> + <footer + className="modal-foot" + > + <a + className="js-modal-close" + href="#" + onClick={[Function]} + > + cancel + </a> + </footer> +</Modal> +`; 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`] = ` +<p + className="alert spacer-top alert-success" +> + marketplace.license_preview_status.NO_INSTALL.Foo +</p> +`; + +exports[`should correctly display status message after checking license 2`] = ` +<p + className="alert spacer-top alert-warning" +> + marketplace.license_preview_status.AUTOMATIC_INSTALL.Foo +</p> +`; + +exports[`should correctly display status message after checking license 3`] = ` +<p + className="alert spacer-top alert-danger" +> + marketplace.license_preview_status.MANUAL_INSTALL.Foo + <p + className="spacer-top" + > + <a + className="button" + download="sonarqube-Foo.zip" + href="download_url" + target="_blank" + > + marketplace.download_package + </a> + <a + className="spacer-left" + href="https://redirect.sonarsource.com/doc/how-to-install-an-edition.html" + target="_blank" + > + marketplace.how_to_install + </a> + </p> +</p> +`; + +exports[`should display correctly 1`] = ` +<div> + <label + htmlFor="set-license" + > + marketplace.enter_license_for_x.Foo + <em + className="mandatory" + > + * + </em> + </label> + <textarea + autoFocus={true} + className="spacer-top display-block" + cols={62} + id="set-license" + onChange={[Function]} + required={true} + rows={6} + value="" + /> + <a + className="display-inline-block spacer-top" + href="license_url" + target="_blank" + > + marketplace.i_need_a_license + </a> +</div> +`; 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; } |