diff options
author | Grégoire Aubert <gregoire.aubert@sonarsource.com> | 2017-11-06 12:09:19 +0100 |
---|---|---|
committer | Grégoire Aubert <gregoire.aubert@sonarsource.com> | 2017-11-06 16:18:50 +0100 |
commit | 76accff06136fe38497ab843476134bb00855f34 (patch) | |
tree | 8d6260d0314308eba8068fee2a729ba0ba1690f9 | |
parent | 703c0bcc517272f73fc4a597b2cd190a1d78fef4 (diff) | |
download | sonarqube-76accff06136fe38497ab843476134bb00855f34.tar.gz sonarqube-76accff06136fe38497ab843476134bb00855f34.zip |
SONAR-9937 Add TOS checkbox when installing an edition in the marketplace
6 files changed, 143 insertions, 104 deletions
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 abab7c84ed4..778c63fb457 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 @@ -81,7 +81,7 @@ export default class LicenseEditionForm extends React.PureComponent<Props, State render() { const { edition, isDowngrade } = this.props; - const { submitting, status } = this.state; + const { license, submitting, status } = this.state; const header = isDowngrade ? translateWithParameters('marketplace.downgrade_to_x', edition.name) @@ -107,7 +107,10 @@ export default class LicenseEditionForm extends React.PureComponent<Props, State <footer className="modal-foot"> {submitting && <i className="spinner spacer-right" />} {status && ( - <button className="js-confirm" onClick={this.handleConfirmClick} disabled={submitting}> + <button + className="js-confirm" + onClick={this.handleConfirmClick} + disabled={!license || submitting}> {status === 'AUTOMATIC_INSTALL' ? ( translate('marketplace.install') ) : ( 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 index edcd13a3d03..2053ea87e50 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/components/LicenseEditionSet.tsx +++ b/server/sonar-web/src/main/js/apps/marketplace/components/LicenseEditionSet.tsx @@ -17,10 +17,11 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import { stringify } from 'querystring'; import * as React from 'react'; import * as classNames from 'classnames'; +import Checkbox from '../../../components/controls/Checkbox'; import { FormattedMessage } from 'react-intl'; -import { stringify } from 'querystring'; import { debounce } from 'lodash'; import DeferredSpinner from '../../../components/common/DeferredSpinner'; import { omitNil } from '../../../helpers/request'; @@ -35,6 +36,7 @@ export interface Props { } interface State { + acceptTerms: boolean; license: string; licenseEdition?: Edition; loading: boolean; @@ -50,7 +52,7 @@ export default class LicenseEditionSet extends React.PureComponent<Props, State> constructor(props: Props) { super(props); - this.state = { license: '', loading: false }; + this.state = { acceptTerms: false, license: '', loading: false }; this.fetchLicensePreview = debounce(this.fetchLicensePreview, 100); } @@ -116,57 +118,93 @@ export default class LicenseEditionSet extends React.PureComponent<Props, State> } }; + handleTermsCheck = (checked: boolean) => + this.setState({ acceptTerms: checked }, () => + this.updateLicense(this.state.license, this.state.licenseEdition, this.state.previewStatus) + ); + updateLicense = (license: string, licenseEdition?: Edition, previewStatus?: string) => { this.setState({ license, licenseEdition, loading: false, previewStatus }); - this.props.updateLicense(license, previewStatus); + this.props.updateLicense( + previewStatus !== 'NO_INSTALL' && !this.state.acceptTerms ? undefined : license, + previewStatus + ); }; renderAlert() { const { licenseEdition, previewStatus } = this.state; if (!previewStatus) { const { edition } = this.props; - if (edition && licenseEdition && edition.key !== licenseEdition.key) { - return ( - <p className="alert alert-danger spacer-top"> - {translateWithParameters('marketplace.wrong_license_type_x', edition.name)} - </p> - ); + if (!edition) { + return undefined; } - return undefined; + return ( + <div className="spacer-top"> + {licenseEdition !== undefined && + edition.key !== licenseEdition.key && ( + <p className="alert alert-danger"> + {translateWithParameters('marketplace.wrong_license_type_x', edition.name)} + </p> + )} + <a href={this.getLicenseFormUrl(edition)} target="_blank"> + {translate('marketplace.i_need_a_license')} + </a> + </div> + ); } return ( - <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, - licenseEdition ? licenseEdition.name : translate('marketplace.commercial_edition') - )} - {licenseEdition && - licenseEdition.key === 'datacenter' && - previewStatus !== 'NO_INSTALL' && ( - <span className="little-spacer-left"> - <FormattedMessage - defaultMessage={translate('marketplace.how_to_setup_cluster_url')} - id="marketplace.how_to_setup_cluster_url" - values={{ - url: ( - <a - href="https://redirect.sonarsource.com/doc/data-center-edition.html" - target="_blank"> - {licenseEdition.name} - </a> - ) - }} - /> + <div className="spacer-top"> + <p + className={classNames('alert', { + 'alert-warning': previewStatus === 'AUTOMATIC_INSTALL', + 'alert-success': previewStatus === 'NO_INSTALL', + 'alert-danger': previewStatus === 'MANUAL_INSTALL' + })}> + {translateWithParameters( + 'marketplace.license_preview_status.' + previewStatus, + licenseEdition ? licenseEdition.name : translate('marketplace.commercial_edition') + )} + {licenseEdition && + licenseEdition.key === 'datacenter' && + previewStatus !== 'NO_INSTALL' && ( + <span className="little-spacer-left"> + <FormattedMessage + defaultMessage={translate('marketplace.how_to_setup_cluster_url')} + id="marketplace.how_to_setup_cluster_url" + values={{ + url: ( + <a + href="https://redirect.sonarsource.com/doc/data-center-edition.html" + target="_blank"> + {licenseEdition.name} + </a> + ) + }} + /> + </span> + )} + </p> + {previewStatus !== 'NO_INSTALL' && ( + <span className="js-edition-tos"> + <Checkbox + checked={this.state.acceptTerms} + id="edition-terms" + onCheck={this.handleTermsCheck}> + <label className="little-spacer-left" htmlFor="edition-terms"> + {translate('marketplace.i_accept_the')} + </label> + </Checkbox> + <a + className="nowrap little-spacer-left" + href="http://dist.sonarsource.com/SonarSource_Terms_And_Conditions.pdf" + target="_blank"> + {translate('marketplace.terms_and_conditions')} + </a> </span> )} - </p> + </div> ); } @@ -194,7 +232,6 @@ export default class LicenseEditionSet extends React.PureComponent<Props, State> /> <DeferredSpinner - className="spacer-top" loading={loading} customSpinner={ <p className="spacer-top"> @@ -204,15 +241,6 @@ export default class LicenseEditionSet extends React.PureComponent<Props, State> }> {this.renderAlert()} </DeferredSpinner> - - {edition && ( - <a - className="display-inline-block spacer-top" - href={this.getLicenseFormUrl(edition)} - target="_blank"> - {translate('marketplace.i_need_a_license')} - </a> - )} </div> ); } 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 745c8308a9b..7db7c0c0b5d 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 @@ -47,15 +47,28 @@ it('should display correctly', () => { expect(getWrapper()).toMatchSnapshot(); }); -it('should correctly change the button based on the status', () => { +it('should correctly change the button based on the status and license', () => { const wrapper = getWrapper(); + let button; (wrapper.instance() as LicenseEditionForm).mounted = true; - wrapper.setState({ status: 'NO_INSTALL' }); - expect(wrapper.find('button')).toMatchSnapshot(); + + wrapper.setState({ license: 'mylicense', status: 'NO_INSTALL' }); + button = wrapper.find('button'); + expect(button.text()).toBe('save'); + expect(button.prop('disabled')).toBeFalsy(); + + wrapper.setState({ license: undefined, status: 'MANUAL_INSTALL' }); + button = wrapper.find('button'); + expect(button.text()).toBe('save'); + expect(button.prop('disabled')).toBeTruthy(); + wrapper.setState({ status: 'AUTOMATIC_INSTALL' }); - expect(wrapper.find('button')).toMatchSnapshot(); - wrapper.setState({ status: 'MANUAL_INSTALL' }); - expect(wrapper.find('button')).toMatchSnapshot(); + button = wrapper.find('button'); + expect(button.text()).toContain('install'); + expect(button.prop('disabled')).toBeTruthy(); + + wrapper.setState({ license: 'mylicense' }); + expect(wrapper.find('button').prop('disabled')).toBeFalsy(); }); it('should update the edition status after install', async () => { 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 index ce68f4c2d3e..b0be56873fd 100644 --- 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 @@ -30,7 +30,7 @@ jest.mock('../../../../api/marketplace', () => ({ jest.mock('lodash', () => { const lodash = require.requireActual('lodash'); - lodash.debounce = (fn: Function) => (...args: any[]) => fn(args); + lodash.debounce = (fn: Function) => (...args: any[]) => fn(...args); return lodash; }); @@ -56,9 +56,32 @@ it('should display correctly', () => { }); it('should correctly display status message after checking license', async () => { - await testLicenseStatus('NO_INSTALL'); - await testLicenseStatus('AUTOMATIC_INSTALL'); - await testLicenseStatus('MANUAL_INSTALL'); + let wrapper = await testLicenseStatus('NO_INSTALL', jest.fn()); + expect(wrapper.find('p.alert')).toMatchSnapshot(); + wrapper = await testLicenseStatus('AUTOMATIC_INSTALL', jest.fn()); + expect(wrapper.find('p.alert')).toMatchSnapshot(); + wrapper = await testLicenseStatus('MANUAL_INSTALL', jest.fn()); + expect(wrapper.find('p.alert')).toMatchSnapshot(); +}); + +it('should display terms of license checkbox', async () => { + let updateLicense = jest.fn(); + let wrapper = await testLicenseStatus('NO_INSTALL', updateLicense); + expect(wrapper.find('.js-edition-tos').exists()).toBeFalsy(); + expect(updateLicense).toHaveBeenCalledWith('mylicense', 'NO_INSTALL'); + + updateLicense = jest.fn(); + wrapper = await testLicenseStatus('AUTOMATIC_INSTALL', updateLicense); + let tosCheckbox = wrapper.find('.js-edition-tos'); + expect(tosCheckbox.find('a').exists()).toBeTruthy(); + expect(updateLicense).toHaveBeenLastCalledWith(undefined, 'AUTOMATIC_INSTALL'); + (tosCheckbox.find('Checkbox').prop('onCheck') as Function)(true); + expect(updateLicense).toHaveBeenLastCalledWith('mylicense', 'AUTOMATIC_INSTALL'); + + updateLicense = jest.fn(); + wrapper = await testLicenseStatus('MANUAL_INSTALL', updateLicense); + expect(wrapper.find('.js-edition-tos').exists()).toBeTruthy(); + expect(updateLicense).toHaveBeenLastCalledWith(undefined, 'MANUAL_INSTALL'); }); function getWrapper(props = {}) { @@ -72,16 +95,15 @@ function getWrapper(props = {}) { ); } -async function testLicenseStatus(status: string) { +async function testLicenseStatus(status: string, updateLicense: jest.Mock<any>) { 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(); + return wrapper; } 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 index d178e5134f4..49d105b88f5 100644 --- 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 @@ -1,35 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`should correctly change the button based on the status 1`] = ` -<button - className="js-confirm" - disabled={false} - onClick={[Function]} -> - save -</button> -`; - -exports[`should correctly change the button based on the status 2`] = ` -<button - className="js-confirm" - disabled={false} - onClick={[Function]} -> - marketplace.install -</button> -`; - -exports[`should correctly change the button based on the status 3`] = ` -<button - className="js-confirm" - disabled={false} - onClick={[Function]} -> - save -</button> -`; - exports[`should display correctly 1`] = ` <Modal ariaHideApp={true} 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 index b990a23a26c..19750aefb8b 100644 --- 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 @@ -2,7 +2,7 @@ exports[`should correctly display status message after checking license 1`] = ` <p - className="alert spacer-top alert-success" + className="alert alert-success" > marketplace.license_preview_status.NO_INSTALL.Foo </p> @@ -10,7 +10,7 @@ exports[`should correctly display status message after checking license 1`] = ` exports[`should correctly display status message after checking license 2`] = ` <p - className="alert spacer-top alert-warning" + className="alert alert-warning" > marketplace.license_preview_status.AUTOMATIC_INSTALL.Foo </p> @@ -18,7 +18,7 @@ exports[`should correctly display status message after checking license 2`] = ` exports[`should correctly display status message after checking license 3`] = ` <p - className="alert spacer-top alert-danger" + className="alert alert-danger" > marketplace.license_preview_status.MANUAL_INSTALL.Foo </p> @@ -52,7 +52,6 @@ exports[`should display correctly 1`] = ` value="" /> <DeferredSpinner - className="spacer-top" customSpinner={ <p className="spacer-top" @@ -65,13 +64,17 @@ exports[`should display correctly 1`] = ` } loading={false} timeout={100} - /> - <a - className="display-inline-block spacer-top" - href="license_url" - target="_blank" > - marketplace.i_need_a_license - </a> + <div + className="spacer-top" + > + <a + href="license_url" + target="_blank" + > + marketplace.i_need_a_license + </a> + </div> + </DeferredSpinner> </div> `; |