diff --git a/src/Components/CreateImageWizard/CreateImageWizard.js b/src/Components/CreateImageWizard/CreateImageWizard.js index e2e388bd..2d35597b 100644 --- a/src/Components/CreateImageWizard/CreateImageWizard.js +++ b/src/Components/CreateImageWizard/CreateImageWizard.js @@ -50,7 +50,16 @@ const onSave = (values) => { customizations['payload_repositories'] = [...values['custom-repositories']]; } - if (values['register-system'] === 'register-now-insights') { + if (values['register-system'] === 'register-now-rhc') { + customizations.subscription = { + 'activation-key': values['subscription-activation-key'], + insights: true, + rhc: true, + organization: Number(values['subscription-organization-id']), + 'server-url': values['subscription-server-url'], + 'base-url': values['subscription-base-url'], + }; + } else if (values['register-system'] === 'register-now-insights') { customizations.subscription = { 'activation-key': values['subscription-activation-key'], insights: true, @@ -423,7 +432,9 @@ const requestToState = (composeRequest) => { // subscription const subscription = composeRequest?.customizations?.subscription; if (subscription) { - if (subscription.insights) { + if (subscription.rhc) { + formState['register-system'] = 'register-now-rhc'; + } else if (subscription.insights) { formState['register-system'] = 'register-now-insights'; } else { formState['register-system'] = 'register-now'; diff --git a/src/Components/CreateImageWizard/CreateImageWizard.scss b/src/Components/CreateImageWizard/CreateImageWizard.scss index cb979124..c736de31 100644 --- a/src/Components/CreateImageWizard/CreateImageWizard.scss +++ b/src/Components/CreateImageWizard/CreateImageWizard.scss @@ -52,3 +52,9 @@ .pf-u-max-width { --pf-u-max-width--MaxWidth: 26rem; } + +.pf-m-plain { + list-style: none; + padding-left: 0; + margin-left: 0; +} diff --git a/src/Components/CreateImageWizard/ImageCreator.js b/src/Components/CreateImageWizard/ImageCreator.js index b3040a15..7ff9ee3d 100644 --- a/src/Components/CreateImageWizard/ImageCreator.js +++ b/src/Components/CreateImageWizard/ImageCreator.js @@ -20,6 +20,7 @@ import { RedHatPackages, } from './formComponents/Packages'; import RadioWithPopover from './formComponents/RadioWithPopover'; +import Registration from './formComponents/Registration'; import RegistrationKeyInformation from './formComponents/RegistrationKeyInformation'; import Repositories from './formComponents/Repositories'; import Review from './formComponents/ReviewStep'; @@ -69,6 +70,7 @@ const ImageCreator = ({ 'aws-sources-select': AWSSourcesSelect, 'gallery-layout': GalleryLayout, 'field-listener': FieldListenerWrapper, + registration: Registration, ...customComponentMapper, }} onCancel={onClose} diff --git a/src/Components/CreateImageWizard/formComponents/Registration.js b/src/Components/CreateImageWizard/formComponents/Registration.js new file mode 100644 index 00000000..2a2be355 --- /dev/null +++ b/src/Components/CreateImageWizard/formComponents/Registration.js @@ -0,0 +1,278 @@ +import React, { useEffect, useState } from 'react'; + +import { FormSpy } from '@data-driven-forms/react-form-renderer'; +import useFieldApi from '@data-driven-forms/react-form-renderer/use-field-api'; +import useFormApi from '@data-driven-forms/react-form-renderer/use-form-api'; +import { + Button, + Checkbox, + FormGroup, + Popover, + Radio, + Text, + TextContent, +} from '@patternfly/react-core'; +import { HelpIcon, ExternalLinkAltIcon } from '@patternfly/react-icons'; +import PropTypes from 'prop-types'; + +const RHSMPopover = () => { + return ( + + + Registered systems are entitled to support services, as well as + errata, patches, and upgrades. + + + + } + > + + + ); +}; + +const InsightsPopover = () => { + return ( + + + Insights client is a tool which provides actionable intelligence + about your Red Hat Enterprise Linux environments, helping to + identify and address operational and vulnerability risks before an + issue results in downtime. + + + + } + > + + + ); +}; + +const RHCPopover = () => { + return ( + + + RHC is a tool that allows RHEL hosts to connect to the Insights + services. It is required for the Insights Remediations service. + + + + } + > + + + ); +}; + +const Registration = ({ label, ...props }) => { + const { change, getState } = useFormApi(); + const { input } = useFieldApi(props); + const [showOptions, setShowOptions] = useState(false); + const [insights, setInsights] = useState(true); + const [rhc, setRhc] = useState(true); + const registerSystem = getState()?.values?.['register-system']; + + useEffect(() => { + if (registerSystem === 'register-now-insights') { + setShowOptions(true); + setInsights(true); + setRhc(false); + } + + if (registerSystem === 'register-now') { + setShowOptions(true); + setInsights(false); + setRhc(false); + } + }, []); + + useEffect(() => { + if (insights && rhc) { + change(input.name, 'register-now-rhc'); + } else if (insights && !rhc) { + change(input.name, 'register-now-insights'); + } else if (!insights && !rhc) { + change(input.name, 'register-now'); + } + }, [insights, rhc]); + + return ( + + {() => ( + + + Monitor & manage subscriptions and access to Red Hat content + + + ) + } + data-testid="registration-radio-now" + name="register-system" + id="register-system-now" + checked={registerSystem.startsWith('register-now')} + onChange={() => { + // If rhc (or insights) was disabled previously, enable them again + if (!rhc) { + setInsights(true); + setRhc(true); + } else { + // useEffect[insights, rhc] won't be triggered if both insights and rhc were enabled + change(input.name, 'register-now-rhc'); + } + }} + description={ + !showOptions && ( + + ) + } + body={ + showOptions && ( + + Enable predictive analytics and management capabilities + + + } + data-testid="registration-checkbox-insights" + isChecked={insights} + onChange={(checked) => { + setInsights(checked); + // Uncheck rhc if insights is being unchecked + if (!checked) { + setRhc(!insights); + } + }} + id="register-system-now-insights" + name="register-system-insights" + body={ + + Enable remote remediations and system management with + automation + + + } + data-testid="registration-checkbox-rhc" + isChecked={rhc} + onChange={(checked) => { + setRhc(checked); + // Enable insights if not already enabled + if (!insights) { + setInsights(true); + } + }} + id="register-system-now-rhc" + name="register-system-rhc" + /> + } + /> + ) + } + /> + { + setShowOptions(false); + change(input.name, 'register-later'); + }} + /> + + )} + + ); +}; + +Registration.propTypes = { + label: PropTypes.node, +}; + +export default Registration; diff --git a/src/Components/CreateImageWizard/formComponents/ReviewStep.js b/src/Components/CreateImageWizard/formComponents/ReviewStep.js index 7138616d..7f811197 100644 --- a/src/Components/CreateImageWizard/formComponents/ReviewStep.js +++ b/src/Components/CreateImageWizard/formComponents/ReviewStep.js @@ -87,10 +87,7 @@ const ReviewStep = () => { useEffect(() => { const registerSystem = getState()?.values?.['register-system']; - if ( - registerSystem === 'register-now' || - registerSystem === 'register-now-insights' - ) { + if (registerSystem?.startsWith('register-now')) { (async () => { const userData = await insights?.chrome?.auth?.getUser(); const id = userData?.identity?.internal?.org_id; @@ -324,20 +321,44 @@ const ReviewStep = () => { )} - {(getState()?.values?.['register-system'] === 'register-now' || - getState()?.values?.['register-system'] === - 'register-now-insights') && ( + {getState()?.values?.['register-system']?.startsWith( + 'register-now' + ) && ( Registration type - - {getState()?.values?.['register-system'] === - 'register-now-insights' && - 'Register with Subscriptions and Red Hat Insights'} - {getState()?.values?.['register-system'] === - 'register-now' && 'Register with Subscriptions'} + + + {getState()?.values?.['register-system']?.startsWith( + 'register-now' + ) && ( + + Register with Red Hat Subscription Manager (RHSM) +
+
+ )} + {(getState()?.values?.['register-system'] === + 'register-now-insights' || + getState()?.values?.['register-system'] === + 'register-now-rhc') && ( + + Connect to Red Hat Insights +
+
+ )} + {getState()?.values?.['register-system'] === + 'register-now-rhc' && ( + + Use remote host configuration (RHC) utility +
+
+ )} +
Activation key diff --git a/src/Components/CreateImageWizard/steps/registration.js b/src/Components/CreateImageWizard/steps/registration.js index df5fde41..fcfba1a2 100644 --- a/src/Components/CreateImageWizard/steps/registration.js +++ b/src/Components/CreateImageWizard/steps/registration.js @@ -8,6 +8,7 @@ import { Text, TextContent, TextVariants, + Title, } from '@patternfly/react-core'; import { ExternalLinkAltIcon, HelpIcon } from '@patternfly/react-icons'; @@ -48,38 +49,27 @@ const PopoverActivation = () => { export default { StepTemplate, id: 'wizard-registration', - title: 'Registration', + title: 'Register', + customTitle: ( + + Register systems using this image + + ), name: 'registration', nextStep: 'File system configuration', buttons: CustomButtons, fields: [ { - component: componentTypes.RADIO, - label: 'Register images with Red Hat', + component: componentTypes.PLAIN_TEXT, + name: 'registration-general-description', + label: + 'Automatically Register your systems with Red Hat to enhance security and track your spending.', + }, + { name: 'register-system', - initialValue: 'register-now-insights', - options: [ - { - label: 'Register and connect image instances with Red Hat', - description: 'Includes Subscriptions and Red Hat Insights', - value: 'register-now-insights', - 'data-testid': 'radio-register-now-insights', - autoFocus: true, - }, - { - label: 'Register image instances only', - description: 'Includes Subscriptions only', - value: 'register-now', - className: 'pf-u-mt-sm', - 'data-testid': 'radio-register-now', - }, - { - label: 'Register later', - value: 'register-later', - className: 'pf-u-mt-sm', - 'data-testid': 'radio-register-later', - }, - ], + component: 'registration', + label: 'Registration method', + initialValue: 'register-now-rhc', }, { component: 'activation-keys', @@ -93,6 +83,7 @@ export default { ), condition: { or: [ + { when: 'register-system', is: 'register-now-rhc' }, { when: 'register-system', is: 'register-now-insights' }, { when: 'register-system', is: 'register-now' }, ], @@ -122,6 +113,7 @@ export default { ), condition: { or: [ + { when: 'register-system', is: 'register-now-rhc' }, { when: 'register-system', is: 'register-now-insights' }, { when: 'register-system', is: 'register-now' }, ], @@ -149,6 +141,13 @@ export default { name: 'subscription-activation-key-information', label: 'Selected activation key', valueReference: 'subscription-activation-key', + condition: { + or: [ + { when: 'register-system', is: 'register-now-rhc' }, + { when: 'register-system', is: 'register-now-insights' }, + { when: 'register-system', is: 'register-now' }, + ], + }, }, ], }; diff --git a/src/test/Components/CreateImageWizard/CreateImageWizard.beta.test.js b/src/test/Components/CreateImageWizard/CreateImageWizard.beta.test.js index a8e74445..0a405fdf 100644 --- a/src/test/Components/CreateImageWizard/CreateImageWizard.beta.test.js +++ b/src/test/Components/CreateImageWizard/CreateImageWizard.beta.test.js @@ -564,7 +564,7 @@ describe('Create Image Wizard', () => { screen.getByRole('heading', { name: /Create image/ }); screen.getByRole('button', { name: 'Image output' }); - screen.getByRole('button', { name: 'Registration' }); + screen.getByRole('button', { name: 'Register' }); screen.getByRole('button', { name: 'File system configuration' }); screen.getByRole('button', { name: 'Content' }); screen.getByRole('button', { name: 'Additional Red Hat packages' }); @@ -737,7 +737,7 @@ describe('Step Packages', () => { name: 'Select activation key', }); - const registerLaterRadio = screen.getByLabelText('Register later'); + const registerLaterRadio = screen.getByTestId('registration-radio-later'); userEvent.click(registerLaterRadio); getNextButton().click(); @@ -1216,11 +1216,6 @@ describe('Click through all steps', () => { return Promise.resolve(mockActivationKey[name]); }); - const registrationRadio = screen.getByLabelText( - 'Register and connect image instances with Red Hat' - ); - userEvent.click(registrationRadio); - const activationKeyDropdown = await screen.findByRole('textbox', { name: 'Select activation key', }); @@ -1306,7 +1301,10 @@ describe('Click through all steps', () => { await screen.findByText('VMWare'); await screen.findByText('Virtualization - Guest image'); await screen.findByText('Bare metal - Installer'); - await screen.findByText('Register with Subscriptions and Red Hat Insights'); + const review = screen.getByTestId('review-registration'); + expect(review).toHaveTextContent( + 'Use remote host configuration (RHC) utility' + ); await screen.findByText('MyImageName'); screen.getByTestId('tab-registration').click(); @@ -1361,6 +1359,7 @@ describe('Click through all steps', () => { subscription: { 'activation-key': 'name0', insights: true, + rhc: true, organization: 5, 'server-url': 'subscription.rhsm.redhat.com', 'base-url': 'https://cdn.redhat.com/', diff --git a/src/test/Components/CreateImageWizard/CreateImageWizard.test.js b/src/test/Components/CreateImageWizard/CreateImageWizard.test.js index 5d284490..83bc804f 100644 --- a/src/test/Components/CreateImageWizard/CreateImageWizard.test.js +++ b/src/test/Components/CreateImageWizard/CreateImageWizard.test.js @@ -159,7 +159,7 @@ describe('Create Image Wizard', () => { screen.getByRole('heading', { name: /Create image/ }); screen.getByRole('button', { name: 'Image output' }); - screen.getByRole('button', { name: 'Registration' }); + screen.getByRole('button', { name: 'Register' }); screen.getByRole('button', { name: 'File system configuration' }); screen.getByRole('button', { name: 'Content' }); screen.getByRole('button', { name: 'Additional Red Hat packages' }); @@ -327,7 +327,7 @@ describe('Step Upload to AWS', () => { name: 'Select activation key', }); - screen.getByText('Register images with Red Hat'); + screen.getByText('Automatically register and enable advanced capabilities'); }); test('clicking Back loads Release', () => { @@ -380,7 +380,7 @@ describe('Step Upload to Google', () => { name: 'Select activation key', }); - screen.getByText('Register images with Red Hat'); + screen.getByText('Automatically register and enable advanced capabilities'); }); test('clicking Back loads Release', () => { @@ -455,7 +455,7 @@ describe('Step Upload to Azure', () => { name: 'Select activation key', }); - screen.getByText('Register images with Red Hat'); + screen.getByText('Automatically register and enable advanced capabilities'); }); test('clicking Back loads Release', () => { @@ -514,7 +514,7 @@ describe('Step Registration', () => { test('clicking Next loads file system configuration', async () => { await setUp(); - const registerLaterRadio = screen.getByLabelText('Register later'); + const registerLaterRadio = screen.getByTestId('registration-radio-later'); userEvent.click(registerLaterRadio); getNextButton().click(); @@ -537,14 +537,9 @@ describe('Step Registration', () => { verifyCancelButton(cancel, history); }); - test('should allow registering with insights', async () => { + test('should allow registering with rhc', async () => { await setUp(); - const registrationRadio = screen.getByLabelText( - 'Register and connect image instances with Red Hat' - ); - userEvent.click(registrationRadio); - const activationKeyDropdown = await screen.findByRole('textbox', { name: 'Select activation key', }); @@ -559,19 +554,29 @@ describe('Step Registration', () => { screen.getByRole('button', { name: /Next/ }).click(); screen.getByRole('button', { name: /Next/ }).click(); screen.getByRole('button', { name: /Next/ }).click(); - await waitFor(() => { - screen.getByText('Register with Subscriptions and Red Hat Insights'); - screen.getAllByText('012345678901'); - }); + const review = screen.getByTestId('review-registration'); + expect(review).toHaveTextContent( + 'Register with Red Hat Subscription Manager (RHSM)' + ); + expect(review).toHaveTextContent('Connect to Red Hat Insights'); + expect(review).toHaveTextContent( + 'Use remote host configuration (RHC) utility' + ); + screen.getAllByText('012345678901'); }); - test('should allow registering without insights', async () => { + test('should allow registering without rhc', async () => { await setUp(); - const registrationRadio = screen.getByLabelText( - 'Register image instances only' - ); - userEvent.click(registrationRadio); + userEvent.click(screen.getByTestId('registration-additional-options')); + userEvent.click(screen.getByTestId('registration-checkbox-rhc')); + + // going back and forward when rhc isn't selected should keep additional options shown + screen.getByRole('button', { name: /Back/ }).click(); + await screen.findByTestId('aws-account-id'); + screen.getByRole('button', { name: /Next/ }).click(); + screen.getByTestId('registration-checkbox-insights'); + screen.getByTestId('registration-checkbox-rhc'); const activationKeyDropdown = await screen.findByRole('textbox', { name: 'Select activation key', @@ -587,27 +592,64 @@ describe('Step Registration', () => { screen.getByRole('button', { name: /Next/ }).click(); screen.getByRole('button', { name: /Next/ }).click(); screen.getByRole('button', { name: /Next/ }).click(); - await waitFor(() => { - screen.getByText('Register with Subscriptions'); - screen.getAllByText('012345678901'); + const review = screen.getByTestId('review-registration'); + expect(review).toHaveTextContent( + 'Register with Red Hat Subscription Manager (RHSM)' + ); + expect(review).toHaveTextContent('Connect to Red Hat Insights'); + screen.getAllByText('012345678901'); + expect(review).not.toHaveTextContent( + 'Use remote host configuration (RHC) utility' + ); + }); + + test('should allow registering without insights or rhc', async () => { + await setUp(); + + userEvent.click(screen.getByTestId('registration-additional-options')); + userEvent.click(screen.getByTestId('registration-checkbox-insights')); + + // going back and forward when neither rhc or insights is selected should keep additional options shown + screen.getByRole('button', { name: /Back/ }).click(); + await screen.findByTestId('aws-account-id'); + screen.getByRole('button', { name: /Next/ }).click(); + screen.getByTestId('registration-checkbox-insights'); + screen.getByTestId('registration-checkbox-rhc'); + + const activationKeyDropdown = await screen.findByRole('textbox', { + name: 'Select activation key', }); + userEvent.click(activationKeyDropdown); + const activationKey = await screen.findByRole('option', { + name: 'name0', + }); + userEvent.click(activationKey); + screen.getByDisplayValue('name0'); + + screen.getByRole('button', { name: /Next/ }).click(); + screen.getByRole('button', { name: /Next/ }).click(); + screen.getByRole('button', { name: /Next/ }).click(); + screen.getByRole('button', { name: /Next/ }).click(); + screen.getByTestId('tab-registration').click(); + const review = screen.getByTestId('review-registration'); + expect(review).toHaveTextContent( + 'Register with Red Hat Subscription Manager (RHSM)' + ); + screen.getAllByText('012345678901'); + expect(review).not.toHaveTextContent('Connect to Red Hat Insights'); + expect(review).not.toHaveTextContent( + 'Use remote host configuration (RHC) utility' + ); }); test('should hide input fields when clicking Register the system later', async () => { await setUp(); - // first check the other radio button which causes extra widgets to be shown - const registrationRadio = screen.getByLabelText( - 'Register and connect image instances with Red Hat' - ); - userEvent.click(registrationRadio); - const p1 = waitForElementToBeRemoved(() => [ screen.getByTestId('subscription-activation-key'), ]); - // then click the later radio button which should remove any input fields - const registerLaterRadio = screen.getByLabelText('Register later'); - userEvent.click(registerLaterRadio); + // click the later radio button which should remove any input fields + screen.getByTestId('registration-radio-later').click(); await p1; @@ -617,6 +659,17 @@ describe('Step Registration', () => { screen.getByRole('button', { name: /Next/ }).click(); screen.getByText('Register the system later'); }); + + test('registering with rhc implies registering with insights', async () => { + await setUp(); + userEvent.click(screen.getByTestId('registration-additional-options')); + + userEvent.click(screen.getByTestId('registration-checkbox-insights')); + expect(screen.getByTestId('registration-checkbox-rhc')).not.toBeChecked(); + + userEvent.click(screen.getByTestId('registration-checkbox-rhc')); + expect(screen.getByTestId('registration-checkbox-insights')).toBeChecked(); + }); }); describe('Step File system configuration', () => { @@ -636,7 +689,7 @@ describe('Step File system configuration', () => { name: 'Select activation key', }); - const registerLaterRadio = screen.getByLabelText('Register later'); + const registerLaterRadio = screen.getByTestId('registration-radio-later'); userEvent.click(registerLaterRadio); getNextButton().click(); }; @@ -703,7 +756,7 @@ describe('Step Packages', () => { name: 'Select activation key', }); - const registerLaterRadio = screen.getByLabelText('Register later'); + const registerLaterRadio = screen.getByTestId('registration-radio-later'); userEvent.click(registerLaterRadio); getNextButton().click(); @@ -1061,7 +1114,7 @@ describe('Step Details', () => { name: 'Select activation key', }); - const registerLaterRadio = screen.getByLabelText('Register later'); + const registerLaterRadio = screen.getByTestId('registration-radio-later'); userEvent.click(registerLaterRadio); getNextButton().click(); @@ -1108,7 +1161,7 @@ describe('Step Review', () => { }); // skip registration - const registerLaterRadio = screen.getByLabelText('Register later'); + const registerLaterRadio = screen.getByTestId('registration-radio-later'); userEvent.click(registerLaterRadio); getNextButton().click(); @@ -1362,9 +1415,7 @@ describe('Click through all steps', () => { return Promise.resolve(mockActivationKey[name]); }); - const registrationRadio = screen.getByLabelText( - 'Register and connect image instances with Red Hat' - ); + const registrationRadio = screen.getByTestId('registration-radio-now'); userEvent.click(registrationRadio); const activationKeyDropdown = await screen.findByRole('textbox', { @@ -1447,14 +1498,16 @@ describe('Click through all steps', () => { await screen.findByText('VMWare'); await screen.findByText('Virtualization - Guest image'); await screen.findByText('Bare metal - Installer'); - await screen.findByText('Register with Subscriptions and Red Hat Insights'); await screen.findByText('MyImageName'); screen.getByTestId('tab-registration').click(); await screen.findByText('name0'); await screen.findByText('Self-Support'); await screen.findByText('Production'); - + const review = screen.getByTestId('review-registration'); + expect(review).toHaveTextContent( + 'Use remote host configuration (RHC) utility' + ); screen.getByTestId('repositories-popover-button').click(); const repotbody = await screen.findByTestId( 'additional-repositories-table' @@ -1508,6 +1561,7 @@ describe('Click through all steps', () => { subscription: { 'activation-key': 'name0', insights: true, + rhc: true, organization: 5, 'server-url': 'subscription.rhsm.redhat.com', 'base-url': 'https://cdn.redhat.com/', @@ -1550,6 +1604,7 @@ describe('Click through all steps', () => { subscription: { 'activation-key': 'name0', insights: true, + rhc: true, organization: 5, 'server-url': 'subscription.rhsm.redhat.com', 'base-url': 'https://cdn.redhat.com/', @@ -1594,6 +1649,7 @@ describe('Click through all steps', () => { subscription: { 'activation-key': 'name0', insights: true, + rhc: true, organization: 5, 'server-url': 'subscription.rhsm.redhat.com', 'base-url': 'https://cdn.redhat.com/', @@ -1634,6 +1690,7 @@ describe('Click through all steps', () => { subscription: { 'activation-key': 'name0', insights: true, + rhc: true, organization: 5, 'server-url': 'subscription.rhsm.redhat.com', 'base-url': 'https://cdn.redhat.com/', @@ -1674,6 +1731,7 @@ describe('Click through all steps', () => { subscription: { 'activation-key': 'name0', insights: true, + rhc: true, organization: 5, 'server-url': 'subscription.rhsm.redhat.com', 'base-url': 'https://cdn.redhat.com/', @@ -1714,6 +1772,7 @@ describe('Click through all steps', () => { subscription: { 'activation-key': 'name0', insights: true, + rhc: true, organization: 5, 'server-url': 'subscription.rhsm.redhat.com', 'base-url': 'https://cdn.redhat.com/', @@ -1813,15 +1872,13 @@ describe('Keyboard accessibility', () => { clickNext(); // Registration - const registerRadio = screen.getByRole('radio', { - name: /register and connect image instances with red hat/i, - }); + const registerRadio = screen.getByTestId('registration-radio-now'); expect(registerRadio).toHaveFocus(); await screen.findByRole('textbox', { name: 'Select activation key', }); // skip registration - const registerLaterRadio = screen.getByLabelText('Register later'); + const registerLaterRadio = screen.getByTestId('registration-radio-later'); userEvent.click(registerLaterRadio); clickNext();