V2Wizard: Clean up for a fresh start
To make code reviews easier, these files are being removed for now. They will be added back over time in a way that results in clearer diffs.
This commit is contained in:
parent
247c436b90
commit
e1f66dd444
11 changed files with 0 additions and 1419 deletions
|
|
@ -1,70 +0,0 @@
|
|||
.pf-v5-c-wizard__nav-list {
|
||||
padding-right: 0px;
|
||||
}
|
||||
|
||||
.pf-v5-c-wizard__nav {
|
||||
overflow-y: unset;
|
||||
}
|
||||
|
||||
.pf-c-popover[data-popper-reference-hidden="true"] {
|
||||
font-weight: initial;
|
||||
visibility: initial;
|
||||
pointer-events: initial;
|
||||
}
|
||||
|
||||
.pf-v5-c-dual-list-selector {
|
||||
--pf-v5-c-dual-list-selector__menu--MinHeight: 18rem;
|
||||
--pf-v5-c-dual-list-selector--GridTemplateColumns--pane--MinMax--max: 100vw;
|
||||
}
|
||||
|
||||
.pf-c-form {
|
||||
--pf-c-form--GridGap: var(--pf-global--spacer--md);
|
||||
}
|
||||
|
||||
.pf-c-form__group-label {
|
||||
--pf-c-form__group-label--PaddingBottom: var(--pf-global--spacer--xs);
|
||||
}
|
||||
|
||||
.tiles {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.tile {
|
||||
flex: 1 0 0px;
|
||||
max-width: 250px;
|
||||
}
|
||||
|
||||
.pf-c-tile:focus {
|
||||
--pf-c-tile__title--Color: var(--pf-c-tile__title--Color);
|
||||
--pf-c-tile__icon--Color: var(---pf-global--Color--100);
|
||||
--pf-c-tile--before--BorderWidth: var(--pf-global--BorderWidth--sm);
|
||||
--pf-c-tile--before--BorderColor: var(--pf-global--BorderColor--100);
|
||||
}
|
||||
|
||||
.pf-c-tile.pf-m-selected:focus {
|
||||
--pf-c-tile__title--Color: var(--pf-c-tile--focus__title--Color);
|
||||
--pf-c-tile__icon--Color: var(--pf-c-tile--focus__icon--Color);
|
||||
}
|
||||
|
||||
.provider-icon {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
}
|
||||
|
||||
.pf-u-min-width {
|
||||
--pf-u-min-width--MinWidth: 18ch;
|
||||
}
|
||||
|
||||
.pf-u-max-width {
|
||||
--pf-u-max-width--MaxWidth: 26rem;
|
||||
}
|
||||
|
||||
ul.pf-m-plain {
|
||||
list-style: none;
|
||||
padding-left: 0;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
button.pf-v5-c-menu-toggle {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
|
@ -1,152 +0,0 @@
|
|||
import React, { useState } from 'react';
|
||||
|
||||
import {
|
||||
Button,
|
||||
Wizard,
|
||||
WizardFooterWrapper,
|
||||
WizardStep,
|
||||
useWizardContext,
|
||||
} from '@patternfly/react-core';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import {
|
||||
EnvironmentStateType,
|
||||
filterEnvironment,
|
||||
hasUserSelectedAtLeastOneEnv,
|
||||
useGetAllowedTargets,
|
||||
} from './steps/ImageOutput/Environment';
|
||||
import ImageOutputStep from './steps/ImageOutput/ImageOutput';
|
||||
import ReviewStep from './steps/Review/ReviewStep';
|
||||
|
||||
import { RHEL_9, X86_64 } from '../../constants';
|
||||
import './CreateImageWizard.scss';
|
||||
import { ArchitectureItem, Distributions } from '../../store/imageBuilderApi';
|
||||
import { resolveRelPath } from '../../Utilities/path';
|
||||
import { ImageBuilderHeader } from '../sharedComponents/ImageBuilderHeader';
|
||||
|
||||
/**
|
||||
* @return true if the array in prevAllowedTargets is equivalent to the array
|
||||
* allowedTargets, false otherwise
|
||||
*/
|
||||
const isIdenticalToPrev = (
|
||||
prevAllowedTargets: string[],
|
||||
allowedTargets: string[]
|
||||
) => {
|
||||
let identicalToPrev = true;
|
||||
if (allowedTargets.length === prevAllowedTargets.length) {
|
||||
allowedTargets.forEach((elem) => {
|
||||
if (!prevAllowedTargets.includes(elem)) {
|
||||
identicalToPrev = false;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
identicalToPrev = false;
|
||||
}
|
||||
return identicalToPrev;
|
||||
};
|
||||
|
||||
type CustomWizardFooterPropType = {
|
||||
isNextDisabled: boolean;
|
||||
};
|
||||
/**
|
||||
* The custom wizard footer is only switching the order of the buttons compared
|
||||
* to the default wizard footer from the PF5 library.
|
||||
*/
|
||||
const CustomWizardFooter = ({ isNextDisabled }: CustomWizardFooterPropType) => {
|
||||
const { goToNextStep, goToPrevStep, close } = useWizardContext();
|
||||
return (
|
||||
<WizardFooterWrapper>
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={goToNextStep}
|
||||
isDisabled={isNextDisabled}
|
||||
>
|
||||
Next
|
||||
</Button>
|
||||
<Button variant="secondary" onClick={goToPrevStep}>
|
||||
Back
|
||||
</Button>
|
||||
<Button variant="link" onClick={close}>
|
||||
Cancel
|
||||
</Button>
|
||||
</WizardFooterWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
const CreateImageWizard = () => {
|
||||
const navigate = useNavigate();
|
||||
// Image output step states
|
||||
const [release, setRelease] = useState<Distributions>(RHEL_9);
|
||||
const [arch, setArch] = useState<ArchitectureItem['arch']>(X86_64);
|
||||
const {
|
||||
data: allowedTargets,
|
||||
isFetching,
|
||||
isSuccess,
|
||||
isError,
|
||||
} = useGetAllowedTargets({
|
||||
architecture: arch,
|
||||
release: release,
|
||||
});
|
||||
const [environment, setEnvironment] = useState<EnvironmentStateType>(
|
||||
filterEnvironment(
|
||||
{
|
||||
aws: { selected: false, authorized: false },
|
||||
azure: { selected: false, authorized: false },
|
||||
gcp: { selected: false, authorized: false },
|
||||
oci: { selected: false, authorized: false },
|
||||
'vsphere-ova': { selected: false, authorized: false },
|
||||
vsphere: { selected: false, authorized: false },
|
||||
'guest-image': { selected: false, authorized: false },
|
||||
'image-installer': { selected: false, authorized: false },
|
||||
wsl: { selected: false, authorized: false },
|
||||
},
|
||||
allowedTargets
|
||||
)
|
||||
);
|
||||
// Update of the environment when the architecture and release are changed.
|
||||
// This pattern prevents the usage of a useEffect See https://react.dev/learn/you-might-not-need-an-effect#adjusting-some-state-when-a-prop-changes
|
||||
const [prevAllowedTargets, setPrevAllowedTargets] = useState(allowedTargets);
|
||||
if (!isIdenticalToPrev(prevAllowedTargets, allowedTargets)) {
|
||||
setPrevAllowedTargets(allowedTargets);
|
||||
setEnvironment(filterEnvironment(environment, allowedTargets));
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<ImageBuilderHeader />
|
||||
<section className="pf-l-page__main-section pf-c-page__main-section">
|
||||
<Wizard onClose={() => navigate(resolveRelPath(''))} isVisitRequired>
|
||||
<WizardStep
|
||||
name="Image output"
|
||||
id="step-image-output"
|
||||
footer={
|
||||
<CustomWizardFooter
|
||||
isNextDisabled={!hasUserSelectedAtLeastOneEnv(environment)}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<ImageOutputStep
|
||||
release={release}
|
||||
setRelease={setRelease}
|
||||
arch={arch}
|
||||
setArch={setArch}
|
||||
environment={environment}
|
||||
setEnvironment={setEnvironment}
|
||||
isFetching={isFetching}
|
||||
isError={isError}
|
||||
isSuccess={isSuccess}
|
||||
/>
|
||||
</WizardStep>
|
||||
<WizardStep
|
||||
name="Review"
|
||||
id="step-review"
|
||||
footer={<CustomWizardFooter isNextDisabled={true} />}
|
||||
>
|
||||
<ReviewStep release={release} arch={arch} />
|
||||
</WizardStep>
|
||||
</Wizard>
|
||||
</section>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default CreateImageWizard;
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
import React, { Dispatch, FormEvent, SetStateAction } from 'react';
|
||||
|
||||
import {
|
||||
FormGroup,
|
||||
FormSelect,
|
||||
FormSelectOption,
|
||||
} from '@patternfly/react-core';
|
||||
|
||||
import { ARCHS } from '../../../../constants';
|
||||
import { ArchitectureItem } from '../../../../store/imageBuilderApi';
|
||||
|
||||
type ArchSelectType = {
|
||||
setArch: Dispatch<SetStateAction<ArchitectureItem['arch']>>;
|
||||
arch: ArchitectureItem['arch'];
|
||||
};
|
||||
|
||||
/**
|
||||
* Allows the user to pick the architecture to build
|
||||
*/
|
||||
const ArchSelect = ({ setArch, arch }: ArchSelectType) => {
|
||||
const onChange = (_event: FormEvent<HTMLSelectElement>, value: string) => {
|
||||
setArch(value);
|
||||
};
|
||||
|
||||
return (
|
||||
<FormGroup
|
||||
isRequired={true}
|
||||
label="Architecture"
|
||||
data-testid="architecture-select"
|
||||
>
|
||||
<FormSelect
|
||||
value={arch}
|
||||
onChange={onChange}
|
||||
aria-label="Architecture"
|
||||
ouiaId="arch_select"
|
||||
>
|
||||
{ARCHS.map((arch, index) => (
|
||||
<FormSelectOption key={index} value={arch} label={arch} />
|
||||
))}
|
||||
</FormSelect>
|
||||
</FormGroup>
|
||||
);
|
||||
};
|
||||
|
||||
export default ArchSelect;
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
import React from 'react';
|
||||
|
||||
import { Alert, Button } from '@patternfly/react-core';
|
||||
import { ExternalLinkAltIcon } from '@patternfly/react-icons';
|
||||
|
||||
const DeveloperProgramButton = () => {
|
||||
return (
|
||||
<Button
|
||||
component="a"
|
||||
target="_blank"
|
||||
variant="link"
|
||||
icon={<ExternalLinkAltIcon />}
|
||||
iconPosition="right"
|
||||
isInline
|
||||
href={'https://developers.redhat.com/about'}
|
||||
>
|
||||
Red Hat Developer Program
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
const CentOSAcknowledgement = () => {
|
||||
return (
|
||||
<Alert
|
||||
variant="info"
|
||||
isPlain
|
||||
isInline
|
||||
title={
|
||||
<>
|
||||
CentOS Stream builds are intended for the development of future
|
||||
versions of RHEL and are not supported for production workloads or
|
||||
other use cases.
|
||||
</>
|
||||
}
|
||||
>
|
||||
<p>
|
||||
Join the <DeveloperProgramButton /> to learn about paid and no-cost RHEL
|
||||
subscription options.
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
};
|
||||
|
||||
export default CentOSAcknowledgement;
|
||||
|
|
@ -1,406 +0,0 @@
|
|||
import React, { Dispatch, SetStateAction } from 'react';
|
||||
|
||||
import {
|
||||
Checkbox,
|
||||
FormGroup,
|
||||
Popover,
|
||||
Radio,
|
||||
Text,
|
||||
TextContent,
|
||||
TextVariants,
|
||||
Tile,
|
||||
} from '@patternfly/react-core';
|
||||
import { HelpIcon } from '@patternfly/react-icons';
|
||||
|
||||
import {
|
||||
useGetArchitecturesQuery,
|
||||
Distributions,
|
||||
ArchitectureItem,
|
||||
} from '../../../../store/imageBuilderApi';
|
||||
import { provisioningApi } from '../../../../store/provisioningApi';
|
||||
import { useGetEnvironment } from '../../../../Utilities/useGetEnvironment';
|
||||
|
||||
type useGetAllowedTargetsPropType = {
|
||||
architecture: ArchitectureItem['arch'];
|
||||
release: Distributions;
|
||||
};
|
||||
|
||||
/**
|
||||
* Contacts the backend to get a list of valid targets based on the user
|
||||
* requirements (release & architecture)
|
||||
*
|
||||
* @return an array of strings which contains the names of the authorized
|
||||
* targets. Alongside the array, a couple of flags indicate the status of the
|
||||
* request. isFetching stays true while the data are in transit. isError is set
|
||||
* to true if anything wrong happened. isSuccess is set to true otherwise.
|
||||
*
|
||||
* @param architecture the selected arch (x86_64 or aarch64)
|
||||
* @param release the selected release (see RELEASES in constants)
|
||||
*/
|
||||
export const useGetAllowedTargets = ({
|
||||
architecture,
|
||||
release,
|
||||
}: useGetAllowedTargetsPropType) => {
|
||||
const { data, isFetching, isSuccess, isError } = useGetArchitecturesQuery({
|
||||
distribution: release,
|
||||
});
|
||||
|
||||
let image_types: string[] = [];
|
||||
if (isSuccess && data) {
|
||||
data.forEach((elem) => {
|
||||
if (elem.arch === architecture) {
|
||||
image_types = elem.image_types;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
data: image_types,
|
||||
isFetching: isFetching,
|
||||
isSuccess: isSuccess,
|
||||
isError: isError,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Type to represent the state of a target.
|
||||
* A target can be selected and/or authorized. An authorized target means the
|
||||
* target can be displayed to the user, selected means the user has selected
|
||||
* the target.
|
||||
*/
|
||||
type TargetType = {
|
||||
selected: boolean;
|
||||
authorized: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Defines all the possible targets a user can build.
|
||||
*/
|
||||
export type EnvironmentStateType = {
|
||||
aws: TargetType;
|
||||
azure: TargetType;
|
||||
gcp: TargetType;
|
||||
oci: TargetType;
|
||||
'vsphere-ova': TargetType;
|
||||
vsphere: TargetType;
|
||||
'guest-image': TargetType;
|
||||
'image-installer': TargetType;
|
||||
wsl: TargetType;
|
||||
};
|
||||
|
||||
/**
|
||||
* Takes an environment, a list of allowedTargets and updates the authorized
|
||||
* status of each targets in the environment accordingly.
|
||||
*
|
||||
* @param environment the environment to update
|
||||
* @param allowedTargets the list of targets authorized to get built
|
||||
* @return an updated environment
|
||||
*/
|
||||
export const filterEnvironment = (
|
||||
environment: EnvironmentStateType,
|
||||
allowedTargets: string[]
|
||||
) => {
|
||||
const newEnv = { ...environment };
|
||||
Object.keys(environment).forEach((target) => {
|
||||
newEnv[target as keyof EnvironmentStateType].authorized =
|
||||
allowedTargets.includes(target);
|
||||
});
|
||||
return newEnv;
|
||||
};
|
||||
|
||||
/**
|
||||
* @return true if at least one target has both its flags selected and
|
||||
* authorized set to true
|
||||
* @param env the environment to scan
|
||||
*/
|
||||
export const hasUserSelectedAtLeastOneEnv = (
|
||||
env: EnvironmentStateType
|
||||
): boolean => {
|
||||
let atLeastOne = false;
|
||||
Object.values(env).forEach(({ selected, authorized }) => {
|
||||
atLeastOne = atLeastOne || (selected && authorized);
|
||||
});
|
||||
return atLeastOne;
|
||||
};
|
||||
|
||||
type EnvironmentPropType = {
|
||||
environment: EnvironmentStateType;
|
||||
setEnvironment: Dispatch<SetStateAction<EnvironmentStateType>>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Displays a component that allows the user to pick the target they want
|
||||
* to build on.
|
||||
*/
|
||||
const Environment = ({ setEnvironment, environment }: EnvironmentPropType) => {
|
||||
const prefetchSources = provisioningApi.usePrefetch('getSourceList');
|
||||
const { isBeta } = useGetEnvironment();
|
||||
|
||||
const handleSetEnvironment = (env: string, checked: boolean) =>
|
||||
setEnvironment((prevEnv) => {
|
||||
const newEnv: EnvironmentStateType = {
|
||||
...prevEnv,
|
||||
};
|
||||
newEnv[env as keyof EnvironmentStateType].selected = checked;
|
||||
return newEnv;
|
||||
});
|
||||
|
||||
// each item the user can select is depending on what's compatible with the
|
||||
// architecture and the distribution they previously selected. That's why
|
||||
// every sub parts are conditional to the `authorized` status of its
|
||||
// corresponding key in the state.
|
||||
return (
|
||||
<FormGroup
|
||||
isRequired={true}
|
||||
label="Select target environments"
|
||||
data-testid="target-select"
|
||||
>
|
||||
<FormGroup
|
||||
label={<Text component={TextVariants.small}>Public cloud</Text>}
|
||||
data-testid="target-public"
|
||||
>
|
||||
<div className="tiles">
|
||||
{environment['aws'].authorized && (
|
||||
<Tile
|
||||
className="tile pf-u-mr-sm"
|
||||
data-testid="upload-aws"
|
||||
title="Amazon Web Services"
|
||||
icon={
|
||||
<img
|
||||
className="provider-icon"
|
||||
src={'/apps/frontend-assets/partners-icons/aws.svg'}
|
||||
alt="Amazon Web Services logo"
|
||||
/>
|
||||
}
|
||||
onClick={() =>
|
||||
handleSetEnvironment('aws', !environment.aws.selected)
|
||||
}
|
||||
onMouseEnter={() => prefetchSources({ provider: 'aws' })}
|
||||
isSelected={environment.aws.selected}
|
||||
isStacked
|
||||
isDisplayLarge
|
||||
/>
|
||||
)}
|
||||
{environment['gcp'].authorized && (
|
||||
<Tile
|
||||
className="tile pf-u-mr-sm"
|
||||
data-testid="upload-google"
|
||||
title="Google Cloud Platform"
|
||||
icon={
|
||||
<img
|
||||
className="provider-icon"
|
||||
src={
|
||||
'/apps/frontend-assets/partners-icons/google-cloud-short.svg'
|
||||
}
|
||||
alt="Google Cloud Platform logo"
|
||||
/>
|
||||
}
|
||||
onClick={() =>
|
||||
handleSetEnvironment('gcp', !environment.gcp.selected)
|
||||
}
|
||||
isSelected={environment.gcp.selected}
|
||||
onMouseEnter={() => prefetchSources({ provider: 'gcp' })}
|
||||
isStacked
|
||||
isDisplayLarge
|
||||
/>
|
||||
)}
|
||||
{environment['azure'].authorized && (
|
||||
<Tile
|
||||
className="tile pf-u-mr-sm"
|
||||
data-testid="upload-azure"
|
||||
title="Microsoft Azure"
|
||||
icon={
|
||||
<img
|
||||
className="provider-icon"
|
||||
src={
|
||||
'/apps/frontend-assets/partners-icons/microsoft-azure-short.svg'
|
||||
}
|
||||
alt="Microsoft Azure logo"
|
||||
/>
|
||||
}
|
||||
onClick={() =>
|
||||
handleSetEnvironment('azure', !environment.azure.selected)
|
||||
}
|
||||
onMouseEnter={() => prefetchSources({ provider: 'azure' })}
|
||||
isSelected={environment.azure.selected}
|
||||
isStacked
|
||||
isDisplayLarge
|
||||
/>
|
||||
)}
|
||||
{environment.oci.authorized && isBeta() && (
|
||||
<Tile
|
||||
className="tile pf-u-mr-sm"
|
||||
data-testid="upload-oci"
|
||||
title="Oracle Cloud Infrastructure"
|
||||
icon={
|
||||
<img
|
||||
className="provider-icon"
|
||||
src={'/apps/frontend-assets/partners-icons/oracle-short.svg'}
|
||||
alt="Oracle Cloud Infrastructure logo"
|
||||
/>
|
||||
}
|
||||
onClick={() =>
|
||||
handleSetEnvironment('oci', !environment.oci.selected)
|
||||
}
|
||||
isSelected={environment.oci.selected}
|
||||
isStacked
|
||||
isDisplayLarge
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</FormGroup>
|
||||
{environment['vsphere'].authorized && (
|
||||
<FormGroup
|
||||
label={<Text component={TextVariants.small}>Private cloud</Text>}
|
||||
className="pf-u-mt-sm"
|
||||
data-testid="target-private"
|
||||
>
|
||||
<Checkbox
|
||||
label="VMware vSphere"
|
||||
isChecked={
|
||||
environment.vsphere.selected ||
|
||||
environment['vsphere-ova'].selected
|
||||
}
|
||||
onChange={(_event, checked) => {
|
||||
handleSetEnvironment('vsphere-ova', checked);
|
||||
handleSetEnvironment('vsphere', false);
|
||||
}}
|
||||
aria-label="VMware checkbox"
|
||||
id="checkbox-vmware"
|
||||
name="VMware"
|
||||
data-testid="checkbox-vmware"
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
{environment['vsphere'].authorized && (
|
||||
<FormGroup
|
||||
className="pf-u-mt-sm pf-u-mb-sm pf-u-ml-xl"
|
||||
data-testid="target-private-vsphere-radio"
|
||||
>
|
||||
{environment['vsphere-ova'].authorized && (
|
||||
<Radio
|
||||
name="vsphere-radio"
|
||||
aria-label="VMware vSphere radio button OVA"
|
||||
id="vsphere-radio-ova"
|
||||
label={
|
||||
<>
|
||||
Open virtualization format (.ova)
|
||||
<Popover
|
||||
maxWidth="30rem"
|
||||
position="right"
|
||||
bodyContent={
|
||||
<TextContent>
|
||||
<Text>
|
||||
An OVA file is a virtual appliance used by
|
||||
virtualization platforms such as VMware vSphere. It is
|
||||
a package that contains files used to describe a
|
||||
virtual machine, which includes a VMDK image, OVF
|
||||
descriptor file and a manifest file.
|
||||
</Text>
|
||||
</TextContent>
|
||||
}
|
||||
>
|
||||
<HelpIcon className="pf-u-ml-sm" />
|
||||
</Popover>
|
||||
</>
|
||||
}
|
||||
onChange={(_event, checked) => {
|
||||
handleSetEnvironment('vsphere-ova', checked);
|
||||
handleSetEnvironment('vsphere', !checked);
|
||||
}}
|
||||
isChecked={environment['vsphere-ova'].selected}
|
||||
isDisabled={
|
||||
!(
|
||||
environment.vsphere.selected ||
|
||||
environment['vsphere-ova'].selected
|
||||
)
|
||||
}
|
||||
/>
|
||||
)}
|
||||
<Radio
|
||||
className="pf-u-mt-sm"
|
||||
name="vsphere-radio"
|
||||
aria-label="VMware vSphere radio button VMDK"
|
||||
id="vsphere-radio-vmdk"
|
||||
label={
|
||||
<>
|
||||
Virtual disk (.vmdk)
|
||||
<Popover
|
||||
maxWidth="30rem"
|
||||
position="right"
|
||||
bodyContent={
|
||||
<TextContent>
|
||||
<Text>
|
||||
A VMDK file is a virtual disk that stores the contents
|
||||
of a virtual machine. This disk has to be imported into
|
||||
vSphere using govc import.vmdk, use the OVA version when
|
||||
using the vSphere UI.
|
||||
</Text>
|
||||
</TextContent>
|
||||
}
|
||||
>
|
||||
<HelpIcon className="pf-u-ml-sm" />
|
||||
</Popover>
|
||||
</>
|
||||
}
|
||||
onChange={(_event, checked) => {
|
||||
handleSetEnvironment('vsphere-ova', !checked);
|
||||
handleSetEnvironment('vsphere', checked);
|
||||
}}
|
||||
isChecked={environment.vsphere.selected}
|
||||
isDisabled={
|
||||
!(
|
||||
environment.vsphere.selected ||
|
||||
environment['vsphere-ova'].selected
|
||||
)
|
||||
}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
<FormGroup
|
||||
label={<Text component={TextVariants.small}>Other</Text>}
|
||||
data-testid="target-other"
|
||||
>
|
||||
{environment['guest-image'].authorized && (
|
||||
<Checkbox
|
||||
label="Virtualization - Guest image (.qcow2)"
|
||||
isChecked={environment['guest-image'].selected}
|
||||
onChange={(_event, checked) =>
|
||||
handleSetEnvironment('guest-image', checked)
|
||||
}
|
||||
aria-label="Virtualization guest image checkbox"
|
||||
id="checkbox-guest-image"
|
||||
name="Virtualization guest image"
|
||||
data-testid="checkbox-guest-image"
|
||||
/>
|
||||
)}
|
||||
{environment['image-installer'].authorized && (
|
||||
<Checkbox
|
||||
label="Bare metal - Installer (.iso)"
|
||||
isChecked={environment['image-installer'].selected}
|
||||
onChange={(_event, checked) =>
|
||||
handleSetEnvironment('image-installer', checked)
|
||||
}
|
||||
aria-label="Bare metal installer checkbox"
|
||||
id="checkbox-image-installer"
|
||||
name="Bare metal installer"
|
||||
data-testid="checkbox-image-installer"
|
||||
/>
|
||||
)}
|
||||
{environment['wsl'].authorized && isBeta() && (
|
||||
<Checkbox
|
||||
label="WSL - Windows Subsystem for Linux (.tar.gz)"
|
||||
isChecked={environment['wsl'].selected}
|
||||
onChange={(_event, checked) => handleSetEnvironment('wsl', checked)}
|
||||
aria-label="windows subsystem for linux checkbox"
|
||||
id="checkbox-wsl"
|
||||
name="WSL"
|
||||
data-testid="checkbox-wsl"
|
||||
/>
|
||||
)}
|
||||
</FormGroup>
|
||||
</FormGroup>
|
||||
);
|
||||
};
|
||||
|
||||
export default Environment;
|
||||
|
|
@ -1,90 +0,0 @@
|
|||
import React, { Dispatch, SetStateAction } from 'react';
|
||||
|
||||
import {
|
||||
Text,
|
||||
Alert,
|
||||
Bullseye,
|
||||
Form,
|
||||
Spinner,
|
||||
Title,
|
||||
} from '@patternfly/react-core';
|
||||
|
||||
import ArchSelect from './ArchSelect';
|
||||
import CentOSAcknowledgement from './CentOSAcknowledgement';
|
||||
import Environment, { EnvironmentStateType } from './Environment';
|
||||
import ReleaseSelect from './ReleaseSelect';
|
||||
|
||||
import {
|
||||
ArchitectureItem,
|
||||
Distributions,
|
||||
} from '../../../../store/imageBuilderApi';
|
||||
import DocumentationButton from '../../../sharedComponents/DocumentationButton';
|
||||
|
||||
type ImageOutputPropTypes = {
|
||||
release: Distributions;
|
||||
setRelease: Dispatch<SetStateAction<Distributions>>;
|
||||
arch: ArchitectureItem['arch'];
|
||||
setArch: Dispatch<SetStateAction<ArchitectureItem['arch']>>;
|
||||
environment: EnvironmentStateType;
|
||||
setEnvironment: Dispatch<SetStateAction<EnvironmentStateType>>;
|
||||
isFetching: boolean;
|
||||
isError: boolean;
|
||||
isSuccess: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Manages the form for the image output step by providing the user with a
|
||||
* choice for:
|
||||
* - a distribution
|
||||
* - a release
|
||||
* - a set of environments
|
||||
*/
|
||||
const ImageOutputStep = ({
|
||||
release,
|
||||
setRelease,
|
||||
arch,
|
||||
setArch,
|
||||
setEnvironment,
|
||||
environment,
|
||||
isFetching,
|
||||
isError,
|
||||
isSuccess,
|
||||
}: ImageOutputPropTypes) => {
|
||||
return (
|
||||
<Form>
|
||||
<Title headingLevel="h2">Image output</Title>
|
||||
<Text>
|
||||
Image builder allows you to create a custom image and push it to target
|
||||
environments.
|
||||
<br />
|
||||
<DocumentationButton />
|
||||
</Text>
|
||||
<ReleaseSelect setRelease={setRelease} release={release} />
|
||||
<ArchSelect setArch={setArch} arch={arch} />
|
||||
{release.match('centos-*') && <CentOSAcknowledgement />}
|
||||
{isFetching && (
|
||||
<Bullseye>
|
||||
<Spinner size="lg" />
|
||||
</Bullseye>
|
||||
)}
|
||||
{isError && (
|
||||
<Alert
|
||||
variant={'danger'}
|
||||
isPlain
|
||||
isInline
|
||||
title={'Environments unavailable'}
|
||||
>
|
||||
API cannot be reached, try again later.
|
||||
</Alert>
|
||||
)}
|
||||
{isSuccess && !isFetching && (
|
||||
<Environment
|
||||
setEnvironment={setEnvironment}
|
||||
environment={environment}
|
||||
/>
|
||||
)}
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
|
||||
export default ImageOutputStep;
|
||||
|
|
@ -1,117 +0,0 @@
|
|||
import React, {
|
||||
Dispatch,
|
||||
ReactElement,
|
||||
SetStateAction,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react';
|
||||
|
||||
import {
|
||||
Select,
|
||||
SelectOption,
|
||||
SelectList,
|
||||
MenuToggle,
|
||||
FormGroup,
|
||||
} from '@patternfly/react-core';
|
||||
|
||||
import { RELEASES } from '../../../../constants';
|
||||
import { Distributions } from '../../../../store/imageBuilderApi';
|
||||
import isRhel from '../../../../Utilities/isRhel';
|
||||
|
||||
type ReleaseSelectType = {
|
||||
setRelease: Dispatch<SetStateAction<Distributions>>;
|
||||
release: Distributions;
|
||||
};
|
||||
|
||||
/**
|
||||
* Allows the user to choose the release they want to build.
|
||||
* Follows the PF5 pattern: https://www.patternfly.org/components/menus/select#view-more
|
||||
*/
|
||||
const ReleaseSelect = ({ setRelease, release }: ReleaseSelectType) => {
|
||||
// By default the component doesn't show the Centos releases and only the RHEL
|
||||
// ones. The user has the option to click on a button to make them appear.
|
||||
const [showDevelopmentOptions, setShowDevelopmentOptions] = useState(false);
|
||||
const releaseOptions = () => {
|
||||
const options: ReactElement[] = [];
|
||||
const filteredRhel = new Map<string, string>();
|
||||
RELEASES.forEach((value, key) => {
|
||||
// Only show non-RHEL distros if expanded
|
||||
if (showDevelopmentOptions || isRhel(key)) {
|
||||
filteredRhel.set(key, value);
|
||||
}
|
||||
});
|
||||
filteredRhel.forEach((value, key) => {
|
||||
if (value && key) {
|
||||
options.push(
|
||||
<SelectOption key={value} value={key} label={key}>
|
||||
{RELEASES.get(key)}
|
||||
</SelectOption>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return options;
|
||||
};
|
||||
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
const onToggleClick = () => {
|
||||
setIsOpen(!isOpen);
|
||||
};
|
||||
|
||||
const viewMoreRef = useRef<HTMLLIElement>(null);
|
||||
const toggleRef = useRef<HTMLButtonElement>(null);
|
||||
const onSelect = (
|
||||
_event: React.MouseEvent<Element, MouseEvent> | undefined,
|
||||
value: string | number | undefined
|
||||
) => {
|
||||
if (value !== 'loader') {
|
||||
if (typeof value === 'string') {
|
||||
setRelease(value as Distributions);
|
||||
}
|
||||
setIsOpen(false);
|
||||
toggleRef?.current?.focus(); // Only focus the toggle when a non-loader option is selected
|
||||
}
|
||||
};
|
||||
|
||||
const toggle = (
|
||||
<MenuToggle
|
||||
ref={toggleRef}
|
||||
onClick={onToggleClick}
|
||||
isExpanded={isOpen}
|
||||
isFullWidth
|
||||
>
|
||||
{RELEASES.get(release)}
|
||||
</MenuToggle>
|
||||
);
|
||||
|
||||
return (
|
||||
<FormGroup isRequired={true} label="Release" data-testid="release-select">
|
||||
<Select
|
||||
ouiaId="release_select"
|
||||
id="release_select"
|
||||
isOpen={isOpen}
|
||||
selected={release}
|
||||
onSelect={onSelect}
|
||||
onOpenChange={(isOpen) => setIsOpen(isOpen)}
|
||||
toggle={{ toggleNode: toggle, toggleRef }}
|
||||
>
|
||||
<SelectList>
|
||||
{releaseOptions()}
|
||||
<SelectOption
|
||||
{...(!showDevelopmentOptions && { isLoadButton: true })}
|
||||
onClick={() => setShowDevelopmentOptions(true)}
|
||||
value="loader"
|
||||
ref={viewMoreRef}
|
||||
>
|
||||
{!showDevelopmentOptions
|
||||
? 'Show options for further development of RHEL'
|
||||
: undefined}
|
||||
</SelectOption>
|
||||
</SelectList>
|
||||
</Select>
|
||||
</FormGroup>
|
||||
);
|
||||
};
|
||||
|
||||
export default ReleaseSelect;
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
import React, { useState } from 'react';
|
||||
|
||||
import { ExpandableSection, Form, Title } from '@patternfly/react-core';
|
||||
|
||||
import { ImageOutputList } from './imageOutput';
|
||||
|
||||
import {
|
||||
ArchitectureItem,
|
||||
Distributions,
|
||||
} from '../../../../store/imageBuilderApi';
|
||||
|
||||
type ReviewStepPropTypes = {
|
||||
release: Distributions;
|
||||
arch: ArchitectureItem['arch'];
|
||||
};
|
||||
|
||||
const ReviewStep = ({ release, arch }: ReviewStepPropTypes) => {
|
||||
const [isExpandedImageOutput, setIsExpandedImageOutput] = useState(false);
|
||||
|
||||
const onToggleImageOutput = (isExpandedImageOutput: boolean) =>
|
||||
setIsExpandedImageOutput(isExpandedImageOutput);
|
||||
return (
|
||||
<>
|
||||
<Form>
|
||||
<Title headingLevel="h2">Review</Title>
|
||||
<ExpandableSection
|
||||
toggleContent={'Image output'}
|
||||
onToggle={(_event, isExpandedImageOutput) =>
|
||||
onToggleImageOutput(isExpandedImageOutput)
|
||||
}
|
||||
isExpanded={isExpandedImageOutput}
|
||||
isIndented
|
||||
data-testid="image-output-expandable"
|
||||
>
|
||||
<ImageOutputList release={release} arch={arch} />
|
||||
</ExpandableSection>
|
||||
</Form>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ReviewStep;
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
import React from 'react';
|
||||
|
||||
import {
|
||||
TextContent,
|
||||
TextList,
|
||||
TextListItem,
|
||||
TextListVariants,
|
||||
TextListItemVariants,
|
||||
} from '@patternfly/react-core';
|
||||
|
||||
import { RELEASES } from '../../../../constants';
|
||||
import {
|
||||
ArchitectureItem,
|
||||
Distributions,
|
||||
} from '../../../../store/imageBuilderApi';
|
||||
|
||||
type ImageOutputListPropTypes = {
|
||||
release: Distributions;
|
||||
arch: ArchitectureItem['arch'];
|
||||
};
|
||||
|
||||
export const ImageOutputList = ({
|
||||
release,
|
||||
arch,
|
||||
}: ImageOutputListPropTypes) => {
|
||||
return (
|
||||
<TextContent>
|
||||
<TextList component={TextListVariants.dl}>
|
||||
<TextListItem
|
||||
component={TextListItemVariants.dt}
|
||||
className="pf-u-min-width"
|
||||
>
|
||||
Release
|
||||
</TextListItem>
|
||||
<TextListItem component={TextListItemVariants.dd}>
|
||||
{RELEASES.get(release)}
|
||||
</TextListItem>
|
||||
<TextListItem component={TextListItemVariants.dt}>
|
||||
Architecture
|
||||
</TextListItem>
|
||||
<TextListItem component={TextListItemVariants.dd}>{arch}</TextListItem>
|
||||
</TextList>
|
||||
<br />
|
||||
</TextContent>
|
||||
);
|
||||
};
|
||||
|
|
@ -1,145 +0,0 @@
|
|||
import React from 'react';
|
||||
|
||||
import '@testing-library/jest-dom';
|
||||
|
||||
import { screen, within } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
|
||||
import CreateImageWizard from '../../../Components/CreateImageWizardV2/CreateImageWizard';
|
||||
import ShareImageModal from '../../../Components/ShareImageModal/ShareImageModal';
|
||||
import { server } from '../../mocks/server.js';
|
||||
import {
|
||||
clickNext,
|
||||
renderCustomRoutesWithReduxRouter,
|
||||
verifyCancelButton,
|
||||
} from '../../testUtils';
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: 'insights/image-builder/*',
|
||||
element: <div />,
|
||||
},
|
||||
{
|
||||
path: 'insights/image-builder/imagewizard/:composeId?',
|
||||
element: <CreateImageWizard />,
|
||||
},
|
||||
{
|
||||
path: 'insights/image-builder/share /:composeId',
|
||||
element: <ShareImageModal />,
|
||||
},
|
||||
];
|
||||
|
||||
jest.mock('@redhat-cloud-services/frontend-components/useChrome', () => ({
|
||||
useChrome: () => ({
|
||||
auth: {
|
||||
getUser: () => {
|
||||
return {
|
||||
identity: {
|
||||
internal: {
|
||||
org_id: 5,
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
isBeta: () => true,
|
||||
isProd: () => true,
|
||||
getEnvironment: () => 'prod',
|
||||
}),
|
||||
}));
|
||||
|
||||
jest.mock('@unleash/proxy-client-react', () => ({
|
||||
useUnleashContext: () => jest.fn(),
|
||||
useFlag: jest.fn((flag) =>
|
||||
flag === 'image-builder.enable-content-sources' ? true : false
|
||||
),
|
||||
}));
|
||||
|
||||
beforeAll(() => {
|
||||
// scrollTo is not defined in jsdom
|
||||
window.HTMLElement.prototype.scrollTo = function () {};
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
server.resetHandlers();
|
||||
});
|
||||
|
||||
describe('Create Image Wizard', () => {
|
||||
test('renders component', () => {
|
||||
renderCustomRoutesWithReduxRouter('imagewizard', {}, routes);
|
||||
// check heading
|
||||
screen.getByRole('heading', { name: /Image Builder/ });
|
||||
|
||||
screen.getByRole('button', { name: 'Image output' });
|
||||
});
|
||||
});
|
||||
|
||||
describe('Step Image output', () => {
|
||||
test('clicking Next loads the review step with correct information about the image output', async () => {
|
||||
const user = userEvent.setup();
|
||||
await renderCustomRoutesWithReduxRouter('imagewizard', {}, routes);
|
||||
|
||||
// select aws as upload destination
|
||||
await user.click(await screen.findByTestId('upload-aws'));
|
||||
await screen.findByRole('heading', { name: 'Image output' });
|
||||
|
||||
await clickNext();
|
||||
|
||||
await screen.findByRole('heading', { name: 'Review' });
|
||||
const view = screen.getByTestId('image-output-expandable');
|
||||
await user.click(await within(view).findByText(/image output/i));
|
||||
expect(await screen.findByText(/x86_64/i)).not.toBeNaN();
|
||||
expect(
|
||||
await screen.findByText(/red hat enterprise linux \(rhel\) 9/i)
|
||||
).not.toBeNaN();
|
||||
});
|
||||
|
||||
test('selecting rhel8 and aarch64 shows accordingly in the review step', async () => {
|
||||
const user = userEvent.setup();
|
||||
await renderCustomRoutesWithReduxRouter('imagewizard', {}, routes);
|
||||
|
||||
// select rhel8
|
||||
const releaseMenu = screen.getAllByRole('button', {
|
||||
name: /Red Hat Enterprise Linux \(RHEL\) 9/,
|
||||
})[0];
|
||||
await user.click(releaseMenu);
|
||||
await user.click(
|
||||
await screen.findByRole('option', {
|
||||
name: /Red Hat Enterprise Linux \(RHEL\) 8/,
|
||||
})
|
||||
);
|
||||
|
||||
// Change to aarch
|
||||
await user.selectOptions(
|
||||
await screen.findByRole('combobox', {
|
||||
name: /architecture/i,
|
||||
}),
|
||||
'aarch64'
|
||||
);
|
||||
|
||||
// select aws as upload destination
|
||||
await user.click(await screen.findByTestId('upload-aws'));
|
||||
|
||||
await clickNext();
|
||||
|
||||
await screen.findByRole('heading', { name: 'Review' });
|
||||
const view = screen.getByTestId('image-output-expandable');
|
||||
await user.click(await within(view).findByText(/image output/i));
|
||||
expect(await screen.findByText(/aarch64/i)).not.toBeNaN();
|
||||
expect(
|
||||
await screen.findByText(/red hat enterprise linux \(rhel\) 8/i)
|
||||
).not.toBeNaN();
|
||||
});
|
||||
|
||||
test('clicking Cancel loads landing page', async () => {
|
||||
const { router } = await renderCustomRoutesWithReduxRouter(
|
||||
'imagewizard',
|
||||
{},
|
||||
routes
|
||||
);
|
||||
await clickNext();
|
||||
|
||||
await verifyCancelButton(router);
|
||||
});
|
||||
});
|
||||
|
|
@ -1,262 +0,0 @@
|
|||
import React from 'react';
|
||||
import '@testing-library/jest-dom';
|
||||
|
||||
import { screen, waitFor } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
|
||||
import CreateImageWizard from '../../../../../Components/CreateImageWizardV2/CreateImageWizard';
|
||||
import { AARCH64, RHEL_8, RHEL_9, X86_64 } from '../../../../../constants';
|
||||
import { mockArchitecturesByDistro } from '../../../../fixtures/architectures';
|
||||
import { server } from '../../../../mocks/server';
|
||||
import { renderCustomRoutesWithReduxRouter } from '../../../../testUtils';
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: 'insights/image-builder/imagewizard/:composeId?',
|
||||
element: <CreateImageWizard />,
|
||||
},
|
||||
];
|
||||
jest.mock('@redhat-cloud-services/frontend-components/useChrome', () => ({
|
||||
useChrome: () => ({
|
||||
auth: {
|
||||
getUser: () => {
|
||||
return {
|
||||
identity: {
|
||||
internal: {
|
||||
org_id: 5,
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
isBeta: () => true,
|
||||
isProd: () => true,
|
||||
getEnvironment: () => 'prod',
|
||||
}),
|
||||
}));
|
||||
|
||||
beforeAll(() => {
|
||||
// scrollTo is not defined in jsdom
|
||||
window.HTMLElement.prototype.scrollTo = function () {};
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
server.resetHandlers();
|
||||
});
|
||||
|
||||
describe('Check that the target filtering is in accordance to mock content', () => {
|
||||
test('rhel9 x86_64', async () => {
|
||||
await renderCustomRoutesWithReduxRouter('imagewizard', {}, routes);
|
||||
|
||||
// make sure this test is in SYNC with the mocks
|
||||
let images_types: string[] = [];
|
||||
mockArchitecturesByDistro(RHEL_9).forEach((elem) => {
|
||||
if (elem.arch === X86_64) {
|
||||
images_types = elem.image_types;
|
||||
}
|
||||
});
|
||||
expect(images_types).toContain('aws');
|
||||
expect(images_types).toContain('gcp');
|
||||
expect(images_types).toContain('azure');
|
||||
expect(images_types).toContain('guest-image');
|
||||
expect(images_types).toContain('image-installer');
|
||||
expect(images_types).toContain('vsphere');
|
||||
expect(images_types).toContain('vsphere-ova');
|
||||
expect(images_types).not.toContain('wsl');
|
||||
// make sure the UX conforms to the mocks
|
||||
await waitFor(async () => await screen.findByTestId('upload-aws'));
|
||||
await screen.findByTestId('upload-google');
|
||||
await screen.findByTestId('upload-azure');
|
||||
await screen.findByTestId('checkbox-guest-image');
|
||||
await screen.findByTestId('checkbox-image-installer');
|
||||
await screen.findByText(/vmware vsphere/i);
|
||||
await screen.findByText(/open virtualization format \(\.ova\)/i);
|
||||
expect(
|
||||
screen.queryByText(/wsl - windows subsystem for linux \(\.tar\.gz\)/i)
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('rhel8 x86_64', async () => {
|
||||
const user = userEvent.setup();
|
||||
await renderCustomRoutesWithReduxRouter('imagewizard', {}, routes);
|
||||
|
||||
// select rhel8
|
||||
const releaseMenu = screen.getAllByRole('button', {
|
||||
name: /Red Hat Enterprise Linux \(RHEL\) 9/,
|
||||
})[0];
|
||||
await user.click(releaseMenu);
|
||||
await user.click(
|
||||
await screen.findByRole('option', {
|
||||
name: /Red Hat Enterprise Linux \(RHEL\) 8/,
|
||||
})
|
||||
);
|
||||
|
||||
// make sure this test is in SYNC with the mocks
|
||||
let images_types: string[] = [];
|
||||
mockArchitecturesByDistro(RHEL_8).forEach((elem) => {
|
||||
if (elem.arch === X86_64) {
|
||||
images_types = elem.image_types;
|
||||
}
|
||||
});
|
||||
expect(images_types).toContain('aws');
|
||||
expect(images_types).toContain('gcp');
|
||||
expect(images_types).toContain('azure');
|
||||
expect(images_types).toContain('guest-image');
|
||||
expect(images_types).toContain('image-installer');
|
||||
expect(images_types).toContain('vsphere');
|
||||
expect(images_types).toContain('vsphere-ova');
|
||||
expect(images_types).toContain('wsl');
|
||||
// make sure the UX conforms to the mocks
|
||||
await waitFor(async () => await screen.findByTestId('upload-aws'));
|
||||
await screen.findByTestId('upload-google');
|
||||
await screen.findByTestId('upload-azure');
|
||||
await screen.findByTestId('checkbox-guest-image');
|
||||
await screen.findByTestId('checkbox-image-installer');
|
||||
await screen.findByText(/vmware vsphere/i);
|
||||
await screen.findByText(/open virtualization format \(\.ova\)/i);
|
||||
await screen.findByText(/wsl - windows subsystem for linux \(\.tar\.gz\)/i);
|
||||
});
|
||||
|
||||
test('rhel9 aarch64', async () => {
|
||||
const user = userEvent.setup();
|
||||
await renderCustomRoutesWithReduxRouter('imagewizard', {}, routes);
|
||||
|
||||
// select aarch64
|
||||
await user.selectOptions(
|
||||
await screen.findByRole('combobox', {
|
||||
name: /architecture/i,
|
||||
}),
|
||||
'aarch64'
|
||||
);
|
||||
|
||||
// make sure this test is in SYNC with the mocks
|
||||
let images_types: string[] = [];
|
||||
mockArchitecturesByDistro(RHEL_9).forEach((elem) => {
|
||||
if (elem.arch === AARCH64) {
|
||||
images_types = elem.image_types;
|
||||
}
|
||||
});
|
||||
expect(images_types).toContain('aws');
|
||||
expect(images_types).not.toContain('gcp');
|
||||
expect(images_types).not.toContain('azure');
|
||||
expect(images_types).toContain('guest-image');
|
||||
expect(images_types).toContain('image-installer');
|
||||
expect(images_types).not.toContain('vsphere');
|
||||
expect(images_types).not.toContain('vsphere-ova');
|
||||
expect(images_types).not.toContain('wsl');
|
||||
// make sure the UX conforms to the mocks
|
||||
await waitFor(async () => await screen.findByTestId('upload-aws'));
|
||||
expect(screen.queryByTestId('upload-google')).not.toBeInTheDocument();
|
||||
expect(screen.queryByTestId('upload-azure')).not.toBeInTheDocument();
|
||||
await screen.findByTestId('checkbox-guest-image');
|
||||
await screen.findByTestId('checkbox-image-installer');
|
||||
expect(screen.queryByText(/vmware vsphere/i)).not.toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByText(/open virtualization format \(\.ova\)/i)
|
||||
).not.toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByText(/wsl - windows subsystem for linux \(\.tar\.gz\)/i)
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('rhel8 aarch64', async () => {
|
||||
const user = userEvent.setup();
|
||||
await renderCustomRoutesWithReduxRouter('imagewizard', {}, routes);
|
||||
|
||||
// select rhel8
|
||||
const releaseMenu = screen.getAllByRole('button', {
|
||||
name: /Red Hat Enterprise Linux \(RHEL\) 9/,
|
||||
})[0];
|
||||
await user.click(releaseMenu);
|
||||
await user.click(
|
||||
await screen.findByRole('option', {
|
||||
name: /Red Hat Enterprise Linux \(RHEL\) 8/,
|
||||
})
|
||||
);
|
||||
|
||||
// select aarch64
|
||||
await user.selectOptions(
|
||||
await screen.findByRole('combobox', {
|
||||
name: /architecture/i,
|
||||
}),
|
||||
'aarch64'
|
||||
);
|
||||
|
||||
// make sure this test is in SYNC with the mocks
|
||||
let images_types: string[] = [];
|
||||
mockArchitecturesByDistro(RHEL_8).forEach((elem) => {
|
||||
if (elem.arch === AARCH64) {
|
||||
images_types = elem.image_types;
|
||||
}
|
||||
});
|
||||
expect(images_types).toContain('aws');
|
||||
expect(images_types).not.toContain('gcp');
|
||||
expect(images_types).not.toContain('azure');
|
||||
expect(images_types).toContain('guest-image');
|
||||
expect(images_types).toContain('image-installer');
|
||||
expect(images_types).not.toContain('vsphere');
|
||||
expect(images_types).not.toContain('vsphere-ova');
|
||||
expect(images_types).not.toContain('wsl');
|
||||
// make sure the UX conforms to the mocks
|
||||
await waitFor(async () => await screen.findByTestId('upload-aws'));
|
||||
expect(screen.queryByTestId('upload-google')).not.toBeInTheDocument();
|
||||
expect(screen.queryByTestId('upload-azure')).not.toBeInTheDocument();
|
||||
await screen.findByTestId('checkbox-guest-image');
|
||||
await screen.findByTestId('checkbox-image-installer');
|
||||
expect(screen.queryByText(/vmware vsphere/i)).not.toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByText(/open virtualization format \(\.ova\)/i)
|
||||
).not.toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByText(/wsl - windows subsystem for linux \(\.tar\.gz\)/i)
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Check step consistency', () => {
|
||||
test('going back and forth with selected options only keeps the one compatible', async () => {
|
||||
const user = userEvent.setup();
|
||||
await renderCustomRoutesWithReduxRouter('imagewizard', {}, routes);
|
||||
|
||||
// select x86_64
|
||||
await user.selectOptions(
|
||||
await screen.findByRole('combobox', {
|
||||
name: /architecture/i,
|
||||
}),
|
||||
'x86_64'
|
||||
);
|
||||
await waitFor(async () => await screen.findByTestId('upload-aws'));
|
||||
// select GCP, it's available for x86_64
|
||||
await user.click(await screen.findByTestId('upload-google'));
|
||||
await waitFor(async () =>
|
||||
expect(await screen.findByRole('button', { name: /Next/ })).toBeEnabled()
|
||||
);
|
||||
// Change to aarch
|
||||
await user.selectOptions(
|
||||
await screen.findByRole('combobox', {
|
||||
name: /architecture/i,
|
||||
}),
|
||||
'aarch64'
|
||||
);
|
||||
// GCP not being compatible with arch, the next button is disabled
|
||||
await waitFor(async () =>
|
||||
expect(await screen.findByRole('button', { name: /Next/ })).toBeDisabled()
|
||||
);
|
||||
// clicking on AWS the user can go next
|
||||
await user.click(await screen.findByTestId('upload-aws'));
|
||||
await waitFor(async () =>
|
||||
expect(await screen.findByRole('button', { name: /Next/ })).toBeEnabled()
|
||||
);
|
||||
// and going back to x86_64 the user should keep the next button visible
|
||||
await user.selectOptions(
|
||||
await screen.findByRole('combobox', {
|
||||
name: /architecture/i,
|
||||
}),
|
||||
'x86_64'
|
||||
);
|
||||
await waitFor(async () =>
|
||||
expect(await screen.findByRole('button', { name: /Next/ })).toBeEnabled()
|
||||
);
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue