V2 Wizard: Add File System Configuration Step (HMS-2781)
The FSC step is added to the wizard and takes full advantage of Redux for state management. This is still a work in progress. Supported features: 1. Select partition mountpoint prefix (e.g. /var, /home) 2. Edit partition mountpoint suffix (e.g. /home/videogames) 3. Change displayed units (KiB, MiB, GiB) Supported but buggy features: 1. Edit partition size Unsupported features: 1. Add partitions 2. Remove partitions 3. Validation
This commit is contained in:
parent
d063279b79
commit
430ea83df0
20 changed files with 751 additions and 111 deletions
7
package-lock.json
generated
7
package-lock.json
generated
|
|
@ -48,6 +48,7 @@
|
||||||
"@types/react": "18.2.64",
|
"@types/react": "18.2.64",
|
||||||
"@types/react-dom": "18.2.21",
|
"@types/react-dom": "18.2.21",
|
||||||
"@types/react-redux": "7.1.33",
|
"@types/react-redux": "7.1.33",
|
||||||
|
"@types/uuid": "^9.0.1",
|
||||||
"@typescript-eslint/eslint-plugin": "7.1.0",
|
"@typescript-eslint/eslint-plugin": "7.1.0",
|
||||||
"@typescript-eslint/parser": "7.1.1",
|
"@typescript-eslint/parser": "7.1.1",
|
||||||
"babel-jest": "29.7.0",
|
"babel-jest": "29.7.0",
|
||||||
|
|
@ -4861,6 +4862,12 @@
|
||||||
"version": "0.0.3",
|
"version": "0.0.3",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/uuid": {
|
||||||
|
"version": "9.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.1.tgz",
|
||||||
|
"integrity": "sha512-rFT3ak0/2trgvp4yYZo5iKFEPsET7vKydKF+VRCxlQ9bpheehyAJH89dAkaLEq/j/RZXJIqcgsmPJKUP1Z28HA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/@types/webpack": {
|
"node_modules/@types/webpack": {
|
||||||
"version": "4.41.34",
|
"version": "4.41.34",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,7 @@
|
||||||
"@types/react": "18.2.64",
|
"@types/react": "18.2.64",
|
||||||
"@types/react-dom": "18.2.21",
|
"@types/react-dom": "18.2.21",
|
||||||
"@types/react-redux": "7.1.33",
|
"@types/react-redux": "7.1.33",
|
||||||
|
"@types/uuid": "^9.0.1",
|
||||||
"@typescript-eslint/eslint-plugin": "7.1.0",
|
"@typescript-eslint/eslint-plugin": "7.1.0",
|
||||||
"@typescript-eslint/parser": "7.1.1",
|
"@typescript-eslint/parser": "7.1.1",
|
||||||
"babel-jest": "29.7.0",
|
"babel-jest": "29.7.0",
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import {
|
||||||
import { useNavigate, useSearchParams } from 'react-router-dom';
|
import { useNavigate, useSearchParams } from 'react-router-dom';
|
||||||
|
|
||||||
import DetailsStep from './steps/Details';
|
import DetailsStep from './steps/Details';
|
||||||
|
import FileSystemStep from './steps/FileSystem';
|
||||||
import ImageOutputStep from './steps/ImageOutput';
|
import ImageOutputStep from './steps/ImageOutput';
|
||||||
import OscapStep from './steps/Oscap';
|
import OscapStep from './steps/Oscap';
|
||||||
import PackagesStep from './steps/Packages';
|
import PackagesStep from './steps/Packages';
|
||||||
|
|
@ -131,7 +132,6 @@ const CreateImageWizard = ({ startStepIndex = 1 }: CreateImageWizardProps) => {
|
||||||
selectAzureResourceGroup(state)
|
selectAzureResourceGroup(state)
|
||||||
);
|
);
|
||||||
const azureSource = useAppSelector((state) => selectAzureSource(state));
|
const azureSource = useAppSelector((state) => selectAzureSource(state));
|
||||||
|
|
||||||
const registrationType = useAppSelector((state) =>
|
const registrationType = useAppSelector((state) =>
|
||||||
selectRegistrationType(state)
|
selectRegistrationType(state)
|
||||||
);
|
);
|
||||||
|
|
@ -249,6 +249,13 @@ const CreateImageWizard = ({ startStepIndex = 1 }: CreateImageWizardProps) => {
|
||||||
>
|
>
|
||||||
<OscapStep />
|
<OscapStep />
|
||||||
</WizardStep>
|
</WizardStep>
|
||||||
|
<WizardStep
|
||||||
|
name="File system configuration"
|
||||||
|
id="step-file-system"
|
||||||
|
footer={<CustomWizardFooter disableNext={false} />}
|
||||||
|
>
|
||||||
|
<FileSystemStep />
|
||||||
|
</WizardStep>
|
||||||
<WizardStep
|
<WizardStep
|
||||||
name="Content"
|
name="Content"
|
||||||
id="step-content"
|
id="step-content"
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ const EditImageWizard = ({ blueprintId }: EditImageWizardProps) => {
|
||||||
navigate(resolveRelPath(''));
|
navigate(resolveRelPath(''));
|
||||||
}
|
}
|
||||||
}, [error, navigate]);
|
}, [error, navigate]);
|
||||||
return <CreateImageWizard startStepIndex={12} />;
|
return <CreateImageWizard startStepIndex={13} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default EditImageWizard;
|
export default EditImageWizard;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import { Alert } from '@patternfly/react-core';
|
||||||
|
|
||||||
|
const UsrSubDirectoriesDisabled = () => {
|
||||||
|
return (
|
||||||
|
<Alert
|
||||||
|
variant="warning"
|
||||||
|
title="Sub-directories for the /usr mount point are no longer supported"
|
||||||
|
isInline
|
||||||
|
>
|
||||||
|
Please note that including sub-directories in the /usr path is no longer
|
||||||
|
supported. Previously included mount points with /usr sub-directory are
|
||||||
|
replaced by /usr when recreating an image.
|
||||||
|
</Alert>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default UsrSubDirectoriesDisabled;
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Text,
|
||||||
|
TextContent,
|
||||||
|
TextVariants,
|
||||||
|
} from '@patternfly/react-core';
|
||||||
|
import { ExternalLinkAltIcon } from '@patternfly/react-icons';
|
||||||
|
|
||||||
|
const FileSystemAutomaticPartition = () => {
|
||||||
|
return (
|
||||||
|
<TextContent>
|
||||||
|
<Text component={TextVariants.h3}>Automatic partitioning</Text>
|
||||||
|
<Text>
|
||||||
|
Red Hat will automatically partition your image to what is best,
|
||||||
|
depending on the target environment(s).
|
||||||
|
</Text>
|
||||||
|
<Text>
|
||||||
|
The target environment sometimes dictates the partitioning scheme or
|
||||||
|
parts of it, and sometimes the target environment is unknown (e.g., for
|
||||||
|
the .qcow2 generic cloud image).
|
||||||
|
</Text>
|
||||||
|
<Text>
|
||||||
|
Using automatic partitioning will apply the most current supported
|
||||||
|
configuration.
|
||||||
|
<br></br>
|
||||||
|
<Button
|
||||||
|
component="a"
|
||||||
|
target="_blank"
|
||||||
|
variant="link"
|
||||||
|
icon={<ExternalLinkAltIcon />}
|
||||||
|
iconPosition="right"
|
||||||
|
href="https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html/creating_customized_images_by_using_insights_image_builder/customizing-file-systems-during-the-image-creation"
|
||||||
|
className="pf-u-pl-0"
|
||||||
|
>
|
||||||
|
Customizing file systems during the image creation
|
||||||
|
</Button>
|
||||||
|
</Text>
|
||||||
|
</TextContent>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FileSystemAutomaticPartition;
|
||||||
|
|
@ -0,0 +1,329 @@
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Popover,
|
||||||
|
Text,
|
||||||
|
TextContent,
|
||||||
|
TextInput,
|
||||||
|
TextVariants,
|
||||||
|
} from '@patternfly/react-core';
|
||||||
|
import { Select, SelectOption } from '@patternfly/react-core/deprecated';
|
||||||
|
import {
|
||||||
|
HelpIcon,
|
||||||
|
MinusCircleIcon,
|
||||||
|
PlusCircleIcon,
|
||||||
|
} from '@patternfly/react-icons';
|
||||||
|
import { ExternalLinkAltIcon } from '@patternfly/react-icons';
|
||||||
|
import { Table, Tbody, Td, Th, Thead, Tr } from '@patternfly/react-table';
|
||||||
|
|
||||||
|
import { UNIT_GIB, UNIT_KIB, UNIT_MIB } from '../../../../constants';
|
||||||
|
import { useAppDispatch, useAppSelector } from '../../../../store/hooks';
|
||||||
|
import {
|
||||||
|
changePartitionMinSize,
|
||||||
|
changePartitionMountpoint,
|
||||||
|
selectPartitions,
|
||||||
|
} from '../../../../store/wizardSlice';
|
||||||
|
import UsrSubDirectoriesDisabled from '../../UsrSubDirectoriesDisabled';
|
||||||
|
import { ValidatedTextInput } from '../../ValidatedTextInput';
|
||||||
|
|
||||||
|
export type Partition = {
|
||||||
|
id: string;
|
||||||
|
mountpoint: string;
|
||||||
|
min_size: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const FileSystemConfiguration = () => {
|
||||||
|
const partitions = useAppSelector((state) => selectPartitions(state));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<TextContent>
|
||||||
|
<Text component={TextVariants.h3}>Configure partitions</Text>
|
||||||
|
</TextContent>
|
||||||
|
{partitions?.find((partition) =>
|
||||||
|
partition?.mountpoint?.includes('/usr')
|
||||||
|
) && <UsrSubDirectoriesDisabled />}
|
||||||
|
<TextContent>
|
||||||
|
<Text>
|
||||||
|
Create partitions for your image by defining mount points and minimum
|
||||||
|
sizes. Image builder creates partitions with a logical volume (LVM)
|
||||||
|
device type.
|
||||||
|
</Text>
|
||||||
|
<Text>
|
||||||
|
The order of partitions may change when the image is installed in
|
||||||
|
order to conform to best practices and ensure functionality.
|
||||||
|
<br></br>
|
||||||
|
<Button
|
||||||
|
component="a"
|
||||||
|
target="_blank"
|
||||||
|
variant="link"
|
||||||
|
icon={<ExternalLinkAltIcon />}
|
||||||
|
iconPosition="right"
|
||||||
|
href="https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html/creating_customized_images_by_using_insights_image_builder/customizing-file-systems-during-the-image-creation"
|
||||||
|
className="pf-u-pl-0"
|
||||||
|
>
|
||||||
|
Read more about manual configuration here
|
||||||
|
</Button>
|
||||||
|
</Text>
|
||||||
|
</TextContent>
|
||||||
|
<Table aria-label="File system table" variant="compact">
|
||||||
|
<Thead>
|
||||||
|
<Tr>
|
||||||
|
<Th />
|
||||||
|
<Th>Mount point</Th>
|
||||||
|
<Th></Th>
|
||||||
|
<Th>Type</Th>
|
||||||
|
<Th>
|
||||||
|
Minimum size
|
||||||
|
<Popover
|
||||||
|
hasAutoWidth
|
||||||
|
bodyContent={
|
||||||
|
<TextContent>
|
||||||
|
<Text>
|
||||||
|
Image Builder may extend this size based on requirements,
|
||||||
|
selected packages, and configurations.
|
||||||
|
</Text>
|
||||||
|
</TextContent>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
variant="plain"
|
||||||
|
aria-label="File system configuration info"
|
||||||
|
aria-describedby="file-system-configuration-info"
|
||||||
|
className="pf-c-form__group-label-help"
|
||||||
|
>
|
||||||
|
<HelpIcon />
|
||||||
|
</Button>
|
||||||
|
</Popover>
|
||||||
|
</Th>
|
||||||
|
<Th />
|
||||||
|
<Th />
|
||||||
|
</Tr>
|
||||||
|
</Thead>
|
||||||
|
<Tbody data-testid="file-system-configuration-tbody">
|
||||||
|
{partitions &&
|
||||||
|
partitions.map((partition) => (
|
||||||
|
<Row key={partition.id} partition={partition} />
|
||||||
|
))}
|
||||||
|
</Tbody>
|
||||||
|
</Table>
|
||||||
|
<TextContent>
|
||||||
|
<Button
|
||||||
|
ouiaId="add-partition"
|
||||||
|
data-testid="file-system-add-partition"
|
||||||
|
className="pf-u-text-align-left"
|
||||||
|
variant="link"
|
||||||
|
icon={<PlusCircleIcon />}
|
||||||
|
onClick={() => {}}
|
||||||
|
>
|
||||||
|
Add partition
|
||||||
|
</Button>
|
||||||
|
</TextContent>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
type RowPropTypes = {
|
||||||
|
partition: Partition;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getPrefix = (mountpoint: string) => {
|
||||||
|
return mountpoint.split('/')[1] ? '/' + mountpoint.split('/')[1] : '/';
|
||||||
|
};
|
||||||
|
const getSuffix = (mountpoint: string) => {
|
||||||
|
const prefix = getPrefix(mountpoint);
|
||||||
|
return mountpoint.substring(prefix.length);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Row = ({ partition }: RowPropTypes) => {
|
||||||
|
const [units, setUnits] = useState<Units>('MiB');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tr>
|
||||||
|
<Td />
|
||||||
|
<Td className="pf-m-width-15">
|
||||||
|
<MountpointPrefix partition={partition} />
|
||||||
|
</Td>
|
||||||
|
<Td className="pf-m-width-15">
|
||||||
|
<MountpointSuffix partition={partition} />
|
||||||
|
</Td>
|
||||||
|
<Td className="pf-m-width-20">xfs</Td>
|
||||||
|
<Td className="pf-m-width-30">
|
||||||
|
<MinimumSize partition={partition} units={units} />
|
||||||
|
</Td>
|
||||||
|
<Td className="pf-m-width-30">
|
||||||
|
<SizeUnit units={units} setUnits={setUnits} />
|
||||||
|
</Td>
|
||||||
|
<Td className="pf-m-width-10">
|
||||||
|
<Button
|
||||||
|
variant="link"
|
||||||
|
icon={<MinusCircleIcon />}
|
||||||
|
onClick={() => {}}
|
||||||
|
data-testid="remove-mount-point"
|
||||||
|
isDisabled={true}
|
||||||
|
/>
|
||||||
|
</Td>
|
||||||
|
</Tr>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const mountpointPrefixes = [
|
||||||
|
'/app',
|
||||||
|
'/boot',
|
||||||
|
'/data',
|
||||||
|
'/home',
|
||||||
|
'/opt',
|
||||||
|
'/srv',
|
||||||
|
'/tmp',
|
||||||
|
'/usr',
|
||||||
|
'/var',
|
||||||
|
];
|
||||||
|
|
||||||
|
type MountpointPrefixPropTypes = {
|
||||||
|
partition: Partition;
|
||||||
|
};
|
||||||
|
|
||||||
|
const MountpointPrefix = ({ partition }: MountpointPrefixPropTypes) => {
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
const prefix = getPrefix(partition.mountpoint);
|
||||||
|
const suffix = getSuffix(partition.mountpoint);
|
||||||
|
|
||||||
|
const onToggle = (isOpen: boolean) => {
|
||||||
|
setIsOpen(isOpen);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSelect = (event: React.MouseEvent, selection: string) => {
|
||||||
|
setIsOpen(false);
|
||||||
|
const mountpoint = selection + suffix;
|
||||||
|
dispatch(
|
||||||
|
changePartitionMountpoint({ id: partition.id, mountpoint: mountpoint })
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Select
|
||||||
|
ouiaId="mount-point"
|
||||||
|
isOpen={isOpen}
|
||||||
|
onToggle={(_event, isOpen) => onToggle(isOpen)}
|
||||||
|
onSelect={onSelect}
|
||||||
|
selections={prefix}
|
||||||
|
>
|
||||||
|
{mountpointPrefixes.map((prefix, index) => {
|
||||||
|
return <SelectOption key={index} value={prefix} />;
|
||||||
|
})}
|
||||||
|
</Select>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
type MountpointSuffixPropTypes = {
|
||||||
|
partition: Partition;
|
||||||
|
};
|
||||||
|
|
||||||
|
const MountpointSuffix = ({ partition }: MountpointSuffixPropTypes) => {
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const prefix = getPrefix(partition.mountpoint);
|
||||||
|
const suffix = getSuffix(partition.mountpoint);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TextInput
|
||||||
|
value={suffix}
|
||||||
|
type="text"
|
||||||
|
onChange={(event: React.FormEvent, suffix) => {
|
||||||
|
const mountpoint = prefix + suffix;
|
||||||
|
dispatch(
|
||||||
|
changePartitionMountpoint({
|
||||||
|
id: partition.id,
|
||||||
|
mountpoint: mountpoint,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
aria-label="text input example"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
type MinimumSizePropTypes = {
|
||||||
|
partition: Partition;
|
||||||
|
units: Units;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Units = 'KiB' | 'MiB' | 'GiB';
|
||||||
|
|
||||||
|
const getConversionFactor = (units: Units) => {
|
||||||
|
switch (units) {
|
||||||
|
case 'KiB':
|
||||||
|
return UNIT_KIB;
|
||||||
|
case 'MiB':
|
||||||
|
return UNIT_MIB;
|
||||||
|
case 'GiB':
|
||||||
|
return UNIT_GIB;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const MinimumSize = ({ partition, units }: MinimumSizePropTypes) => {
|
||||||
|
const conversionFactor = getConversionFactor(units);
|
||||||
|
|
||||||
|
const convertToDisplayUnits = (minSize: string) => {
|
||||||
|
return (parseInt(minSize) * conversionFactor).toString();
|
||||||
|
};
|
||||||
|
|
||||||
|
const convertToBytes = (minSize: string) => {
|
||||||
|
return (parseInt(minSize) / conversionFactor).toString();
|
||||||
|
};
|
||||||
|
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ValidatedTextInput
|
||||||
|
ariaLabel="minimum partition size"
|
||||||
|
helperText=""
|
||||||
|
validator={() => true}
|
||||||
|
value={convertToDisplayUnits(partition.min_size)}
|
||||||
|
type="text"
|
||||||
|
onChange={(event, minSize) => {
|
||||||
|
dispatch(
|
||||||
|
changePartitionMinSize({
|
||||||
|
id: partition.id,
|
||||||
|
min_size: convertToBytes(minSize),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
type SizeUnitPropTypes = {
|
||||||
|
units: Units;
|
||||||
|
setUnits: React.Dispatch<React.SetStateAction<Units>>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const SizeUnit = ({ units, setUnits }: SizeUnitPropTypes) => {
|
||||||
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
|
||||||
|
const onToggle = (isOpen: boolean) => {
|
||||||
|
setIsOpen(isOpen);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSelect = (event: React.MouseEvent, selection: Units) => {
|
||||||
|
setUnits(selection);
|
||||||
|
setIsOpen(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Select
|
||||||
|
ouiaId="mount-point"
|
||||||
|
isOpen={isOpen}
|
||||||
|
onToggle={(_event, isOpen) => onToggle(isOpen)}
|
||||||
|
onSelect={onSelect}
|
||||||
|
selections={units}
|
||||||
|
>
|
||||||
|
<SelectOption value={'KiB'} />
|
||||||
|
<SelectOption value={'MiB'} />
|
||||||
|
<SelectOption value={'GiB'} />
|
||||||
|
</Select>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FileSystemConfiguration;
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import { FormGroup, Label, Radio } from '@patternfly/react-core';
|
||||||
|
|
||||||
|
import { useAppDispatch, useAppSelector } from '../../../../store/hooks';
|
||||||
|
import {
|
||||||
|
changeFileSystemPartitionMode,
|
||||||
|
selectFileSystemPartitionMode,
|
||||||
|
} from '../../../../store/wizardSlice';
|
||||||
|
|
||||||
|
const FileSystemPartition = () => {
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const fileSystemPartition = useAppSelector((state) =>
|
||||||
|
selectFileSystemPartitionMode(state)
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<FormGroup>
|
||||||
|
<Radio
|
||||||
|
id="automatic file system config radio"
|
||||||
|
label={
|
||||||
|
<>
|
||||||
|
<Label isCompact color="blue">
|
||||||
|
Recommended
|
||||||
|
</Label>{' '}
|
||||||
|
Use automatic partitioning
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
name="sc-radio-automatic"
|
||||||
|
description="Automatically partition your image to what is best, depending on the target environment(s)"
|
||||||
|
isChecked={fileSystemPartition === 'automatic'}
|
||||||
|
onChange={() => {
|
||||||
|
dispatch(changeFileSystemPartitionMode('automatic'));
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Radio
|
||||||
|
id="manual file system config radio"
|
||||||
|
label="Manually configure partitions"
|
||||||
|
name="fsc-radio-manual"
|
||||||
|
description="Manually configure the file system of your image by adding, removing, and editing partitions"
|
||||||
|
isChecked={fileSystemPartition === 'manual'}
|
||||||
|
onChange={() => {
|
||||||
|
dispatch(changeFileSystemPartitionMode('manual'));
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FileSystemPartition;
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import { Text, Form, Title } from '@patternfly/react-core';
|
||||||
|
|
||||||
|
import FileSystemAutomaticPartition from './FileSystemAutomaticPartitionInformation';
|
||||||
|
import FileSystemConfiguration from './FileSystemConfiguration';
|
||||||
|
import FileSystemPartition from './FileSystemPartition';
|
||||||
|
|
||||||
|
import { useAppSelector } from '../../../../store/hooks';
|
||||||
|
import { selectFileSystemPartitionMode } from '../../../../store/wizardSlice';
|
||||||
|
export type FileSystemPartitionMode = 'automatic' | 'manual';
|
||||||
|
|
||||||
|
const FileSystemStep = () => {
|
||||||
|
const fileSystemPartitionMode = useAppSelector((state) =>
|
||||||
|
selectFileSystemPartitionMode(state)
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Form>
|
||||||
|
<Title headingLevel="h2">File system configuration</Title>
|
||||||
|
<Text>Define the partitioning of the image</Text>
|
||||||
|
{fileSystemPartitionMode === 'automatic' ? (
|
||||||
|
<>
|
||||||
|
<FileSystemPartition />
|
||||||
|
<FileSystemAutomaticPartition />
|
||||||
|
</>
|
||||||
|
) : fileSystemPartitionMode === 'manual' ? (
|
||||||
|
<>
|
||||||
|
<FileSystemPartition />
|
||||||
|
<FileSystemConfiguration />
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
fileSystemPartitionMode === 'oscap' && <FileSystemConfiguration />
|
||||||
|
)}
|
||||||
|
</Form>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FileSystemStep;
|
||||||
|
|
@ -64,6 +64,7 @@ export const FSReviewTable = () => {
|
||||||
<Th>Minimum size</Th>
|
<Th>Minimum size</Th>
|
||||||
</Tr>
|
</Tr>
|
||||||
</Thead>
|
</Thead>
|
||||||
|
<Tbody data-testid="file-system-configuration-tbody-review"></Tbody>
|
||||||
</Table>
|
</Table>
|
||||||
</PanelMain>
|
</PanelMain>
|
||||||
</Panel>
|
</Panel>
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ import { useChrome } from '@redhat-cloud-services/frontend-components/useChrome'
|
||||||
|
|
||||||
import ActivationKeyInformation from './../Registration/ActivationKeyInformation';
|
import ActivationKeyInformation from './../Registration/ActivationKeyInformation';
|
||||||
import { PackagesTable, RepositoriesTable } from './ReviewStepTables';
|
import { PackagesTable, RepositoriesTable } from './ReviewStepTables';
|
||||||
|
import { FSReviewTable } from './ReviewStepTables';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
RELEASES,
|
RELEASES,
|
||||||
|
|
@ -50,6 +51,7 @@ import {
|
||||||
selectGcpShareMethod,
|
selectGcpShareMethod,
|
||||||
selectPackages,
|
selectPackages,
|
||||||
selectRegistrationType,
|
selectRegistrationType,
|
||||||
|
selectFileSystemPartitionMode,
|
||||||
} from '../../../../store/wizardSlice';
|
} from '../../../../store/wizardSlice';
|
||||||
import { toMonthAndYear } from '../../../../Utilities/time';
|
import { toMonthAndYear } from '../../../../Utilities/time';
|
||||||
import { MajorReleasesLifecyclesChart } from '../ImageOutput/ReleaseLifecycle';
|
import { MajorReleasesLifecyclesChart } from '../ImageOutput/ReleaseLifecycle';
|
||||||
|
|
@ -103,13 +105,85 @@ export const ImageOutputList = () => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
export const FSCList = () => {
|
export const FSCList = () => {
|
||||||
|
const fileSystemPartitionMode = useAppSelector((state) =>
|
||||||
|
selectFileSystemPartitionMode(state)
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TextContent>
|
<TextContent>
|
||||||
|
<TextList component={TextListVariants.dl}>
|
||||||
|
<TextListItem
|
||||||
|
component={TextListItemVariants.dt}
|
||||||
|
className="pf-u-min-width"
|
||||||
|
>
|
||||||
|
Configuration type
|
||||||
|
</TextListItem>
|
||||||
|
<TextListItem
|
||||||
|
component={TextListItemVariants.dd}
|
||||||
|
data-testid="partitioning-auto-manual"
|
||||||
|
>
|
||||||
|
{fileSystemPartitionMode === 'manual' ? 'Manual' : 'Automatic'}
|
||||||
|
{fileSystemPartitionMode === 'manual' && (
|
||||||
|
<>
|
||||||
|
{' '}
|
||||||
|
<Popover
|
||||||
|
position="bottom"
|
||||||
|
headerContent="Partitions"
|
||||||
|
hasAutoWidth
|
||||||
|
minWidth="30rem"
|
||||||
|
bodyContent={<FSReviewTable />}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
data-testid="file-system-configuration-popover"
|
||||||
|
variant="link"
|
||||||
|
aria-label="File system configuration info"
|
||||||
|
aria-describedby="file-system-configuration-info"
|
||||||
|
className="pf-u-pt-0 pf-u-pb-0"
|
||||||
|
>
|
||||||
|
View partitions
|
||||||
|
</Button>
|
||||||
|
</Popover>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</TextListItem>
|
||||||
|
{fileSystemPartitionMode === 'manual' && (
|
||||||
|
<>
|
||||||
|
<TextListItem component={TextListItemVariants.dt}>
|
||||||
|
Image size (minimum)
|
||||||
|
<Popover
|
||||||
|
hasAutoWidth
|
||||||
|
bodyContent={
|
||||||
|
<TextContent>
|
||||||
|
<Text>
|
||||||
|
Image Builder may extend this size based on requirements,
|
||||||
|
selected packages, and configurations.
|
||||||
|
</Text>
|
||||||
|
</TextContent>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
variant="plain"
|
||||||
|
aria-label="File system configuration info"
|
||||||
|
aria-describedby="file-system-configuration-info"
|
||||||
|
className="pf-c-form__group-label-help"
|
||||||
|
>
|
||||||
|
<HelpIcon />
|
||||||
|
</Button>
|
||||||
|
</Popover>
|
||||||
|
</TextListItem>
|
||||||
|
<MinSize />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</TextList>
|
||||||
<br />
|
<br />
|
||||||
</TextContent>
|
</TextContent>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const MinSize = () => {
|
||||||
|
return <TextListItem component={TextListItemVariants.dd} />;
|
||||||
|
};
|
||||||
|
|
||||||
export const TargetEnvAWSList = () => {
|
export const TargetEnvAWSList = () => {
|
||||||
const { isSuccess } = useGetSourceListQuery({
|
const { isSuccess } = useGetSourceListQuery({
|
||||||
provider: 'aws',
|
provider: 'aws',
|
||||||
|
|
|
||||||
|
|
@ -108,6 +108,11 @@ export const mapRequestToState = (request: BlueprintResponse): wizardState => {
|
||||||
enabled: [],
|
enabled: [],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
fileSystem: {
|
||||||
|
mode: 'automatic',
|
||||||
|
partitions: [],
|
||||||
|
},
|
||||||
|
|
||||||
architecture: request.image_requests[0].architecture,
|
architecture: request.image_requests[0].architecture,
|
||||||
distribution: request.distribution,
|
distribution: request.distribution,
|
||||||
imageTypes: request.image_requests.map((image) => image.image_type),
|
imageTypes: request.image_requests.map((image) => image.image_type),
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,8 @@ import {
|
||||||
} from './imageBuilderApi';
|
} from './imageBuilderApi';
|
||||||
import { ActivationKeys } from './rhsmApi';
|
import { ActivationKeys } from './rhsmApi';
|
||||||
|
|
||||||
|
import { FileSystemPartitionMode } from '../Components/CreateImageWizardV2/steps/FileSystem';
|
||||||
|
import { Partition } from '../Components/CreateImageWizardV2/steps/FileSystem/FileSystemConfiguration';
|
||||||
import { IBPackageWithRepositoryInfo } from '../Components/CreateImageWizardV2/steps/Packages/Packages';
|
import { IBPackageWithRepositoryInfo } from '../Components/CreateImageWizardV2/steps/Packages/Packages';
|
||||||
import { AwsShareMethod } from '../Components/CreateImageWizardV2/steps/TargetEnvironment/Aws';
|
import { AwsShareMethod } from '../Components/CreateImageWizardV2/steps/TargetEnvironment/Aws';
|
||||||
import { AzureShareMethod } from '../Components/CreateImageWizardV2/steps/TargetEnvironment/Azure';
|
import { AzureShareMethod } from '../Components/CreateImageWizardV2/steps/TargetEnvironment/Azure';
|
||||||
|
|
@ -68,7 +70,10 @@ export type wizardState = {
|
||||||
enabled: string[] | undefined;
|
enabled: string[] | undefined;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
fileSystem: {
|
||||||
|
mode: FileSystemPartitionMode;
|
||||||
|
partitions: Partition[];
|
||||||
|
};
|
||||||
repositories: {
|
repositories: {
|
||||||
customRepositories: CustomRepository[];
|
customRepositories: CustomRepository[];
|
||||||
payloadRepositories: Repository[];
|
payloadRepositories: Repository[];
|
||||||
|
|
@ -119,6 +124,14 @@ const initialState: wizardState = {
|
||||||
enabled: [],
|
enabled: [],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
fileSystem: {
|
||||||
|
mode: 'automatic',
|
||||||
|
partitions: [
|
||||||
|
{ id: '1', mountpoint: '/', min_size: '500' },
|
||||||
|
{ id: '2', mountpoint: '/home', min_size: '500' },
|
||||||
|
{ id: '3', mountpoint: '/home/var', min_size: '500' },
|
||||||
|
],
|
||||||
|
},
|
||||||
repositories: {
|
repositories: {
|
||||||
customRepositories: [],
|
customRepositories: [],
|
||||||
payloadRepositories: [],
|
payloadRepositories: [],
|
||||||
|
|
@ -218,6 +231,14 @@ export const selectEnabledServices = (state: RootState) => {
|
||||||
return state.wizard.openScap.services.enabled;
|
return state.wizard.openScap.services.enabled;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const selectFileSystemPartitionMode = (state: RootState) => {
|
||||||
|
return state.wizard.fileSystem.mode;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const selectPartitions = (state: RootState) => {
|
||||||
|
return state.wizard.fileSystem.partitions;
|
||||||
|
};
|
||||||
|
|
||||||
export const selectCustomRepositories = (state: RootState) => {
|
export const selectCustomRepositories = (state: RootState) => {
|
||||||
return state.wizard.repositories.customRepositories;
|
return state.wizard.repositories.customRepositories;
|
||||||
};
|
};
|
||||||
|
|
@ -353,6 +374,36 @@ export const wizardSlice = createSlice({
|
||||||
) => {
|
) => {
|
||||||
state.openScap.services.enabled = action.payload;
|
state.openScap.services.enabled = action.payload;
|
||||||
},
|
},
|
||||||
|
changeFileSystemPartitionMode: (
|
||||||
|
state,
|
||||||
|
action: PayloadAction<FileSystemPartitionMode>
|
||||||
|
) => {
|
||||||
|
state.fileSystem.mode = action.payload;
|
||||||
|
},
|
||||||
|
changePartitionMountpoint: (
|
||||||
|
state,
|
||||||
|
action: PayloadAction<{ id: string; mountpoint: string }>
|
||||||
|
) => {
|
||||||
|
const { id, mountpoint } = action.payload;
|
||||||
|
const partitionIndex = state.fileSystem.partitions.findIndex(
|
||||||
|
(partition) => partition.id === id
|
||||||
|
);
|
||||||
|
if (partitionIndex !== -1) {
|
||||||
|
state.fileSystem.partitions[partitionIndex].mountpoint = mountpoint;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
changePartitionMinSize: (
|
||||||
|
state,
|
||||||
|
action: PayloadAction<{ id: string; min_size: string }>
|
||||||
|
) => {
|
||||||
|
const { id, min_size } = action.payload;
|
||||||
|
const partitionIndex = state.fileSystem.partitions.findIndex(
|
||||||
|
(partition) => partition.id === id
|
||||||
|
);
|
||||||
|
if (partitionIndex !== -1) {
|
||||||
|
state.fileSystem.partitions[partitionIndex].min_size = min_size;
|
||||||
|
}
|
||||||
|
},
|
||||||
changeCustomRepositories: (
|
changeCustomRepositories: (
|
||||||
state,
|
state,
|
||||||
action: PayloadAction<CustomRepository[]>
|
action: PayloadAction<CustomRepository[]>
|
||||||
|
|
@ -409,6 +460,9 @@ export const {
|
||||||
changeKernel,
|
changeKernel,
|
||||||
changeDisabledServices,
|
changeDisabledServices,
|
||||||
changeEnabledServices,
|
changeEnabledServices,
|
||||||
|
changeFileSystemPartitionMode,
|
||||||
|
changePartitionMountpoint,
|
||||||
|
changePartitionMinSize,
|
||||||
changeCustomRepositories,
|
changeCustomRepositories,
|
||||||
changePayloadRepositories,
|
changePayloadRepositories,
|
||||||
addPackage,
|
addPackage,
|
||||||
|
|
|
||||||
|
|
@ -144,7 +144,7 @@ describe('Step Packages', () => {
|
||||||
// skip Repositories
|
// skip Repositories
|
||||||
await clickNext();
|
await clickNext();
|
||||||
// skip fsc
|
// skip fsc
|
||||||
//await clickNext();
|
await clickNext();
|
||||||
};
|
};
|
||||||
|
|
||||||
test('clicking Next loads Image name', async () => {
|
test('clicking Next loads Image name', async () => {
|
||||||
|
|
@ -365,7 +365,10 @@ describe('Step Custom repositories', () => {
|
||||||
// skip OpenSCAP
|
// skip OpenSCAP
|
||||||
await clickNext();
|
await clickNext();
|
||||||
// skip fsc
|
// skip fsc
|
||||||
//await clickNext();
|
|
||||||
|
await clickNext();
|
||||||
|
// // skip packages
|
||||||
|
// await clickNext();
|
||||||
};
|
};
|
||||||
|
|
||||||
test('selected repositories stored in and retrieved from form state', async () => {
|
test('selected repositories stored in and retrieved from form state', async () => {
|
||||||
|
|
|
||||||
|
|
@ -121,7 +121,7 @@ describe('Create Image Wizard', () => {
|
||||||
|
|
||||||
await screen.findByRole('button', { name: 'Image output' });
|
await screen.findByRole('button', { name: 'Image output' });
|
||||||
await screen.findByRole('button', { name: 'Register' });
|
await screen.findByRole('button', { name: 'Register' });
|
||||||
// await screen.findByRole('button', { name: 'File system configuration' });
|
await screen.findByRole('button', { name: 'File system configuration' });
|
||||||
await screen.findByRole('button', { name: 'Content' });
|
await screen.findByRole('button', { name: 'Content' });
|
||||||
await screen.findByRole('button', { name: 'Custom repositories' });
|
await screen.findByRole('button', { name: 'Custom repositories' });
|
||||||
await screen.findByRole('button', { name: 'Additional packages' });
|
await screen.findByRole('button', { name: 'Additional packages' });
|
||||||
|
|
@ -430,6 +430,8 @@ describe('Step Upload to AWS', () => {
|
||||||
await clickNext();
|
await clickNext();
|
||||||
await clickNext();
|
await clickNext();
|
||||||
await clickNext();
|
await clickNext();
|
||||||
|
await clickNext();
|
||||||
|
await clickNext();
|
||||||
await enterBlueprintName();
|
await enterBlueprintName();
|
||||||
await clickNext();
|
await clickNext();
|
||||||
|
|
||||||
|
|
@ -556,18 +558,21 @@ describe('Step Registration', () => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// test('clicking Next loads file system configuration', async () => {
|
test('clicking Next loads file system configuration', async () => {
|
||||||
// await setUp();
|
await setUp();
|
||||||
|
|
||||||
// const registerLaterRadio = await screen.findByTestId('registration-radio-later');
|
const registerLaterRadio = await screen.findByTestId(
|
||||||
// await user.click(registerLaterRadio);
|
'registration-radio-later'
|
||||||
|
);
|
||||||
|
await user.click(registerLaterRadio);
|
||||||
|
|
||||||
// await clickNext();
|
await clickNext();
|
||||||
|
await clickNext();
|
||||||
|
|
||||||
// await screen.findByRole('heading', {
|
await screen.findByRole('heading', {
|
||||||
// name: 'File system configuration',
|
name: 'File system configuration',
|
||||||
// });
|
});
|
||||||
// });
|
});
|
||||||
|
|
||||||
test('clicking Back loads Upload to AWS', async () => {
|
test('clicking Back loads Upload to AWS', async () => {
|
||||||
await setUp();
|
await setUp();
|
||||||
|
|
@ -619,6 +624,7 @@ describe('Step Registration', () => {
|
||||||
await clickNext();
|
await clickNext();
|
||||||
await clickNext();
|
await clickNext();
|
||||||
await clickNext();
|
await clickNext();
|
||||||
|
await clickNext();
|
||||||
await enterBlueprintName();
|
await enterBlueprintName();
|
||||||
await clickNext();
|
await clickNext();
|
||||||
const review = await screen.findByTestId('review-registration');
|
const review = await screen.findByTestId('review-registration');
|
||||||
|
|
@ -663,6 +669,8 @@ describe('Step Registration', () => {
|
||||||
await clickNext();
|
await clickNext();
|
||||||
await clickNext();
|
await clickNext();
|
||||||
await clickNext();
|
await clickNext();
|
||||||
|
await clickNext();
|
||||||
|
await clickNext();
|
||||||
await enterBlueprintName();
|
await enterBlueprintName();
|
||||||
await clickNext();
|
await clickNext();
|
||||||
const review = await screen.findByTestId('review-registration');
|
const review = await screen.findByTestId('review-registration');
|
||||||
|
|
@ -709,6 +717,7 @@ describe('Step Registration', () => {
|
||||||
await clickNext();
|
await clickNext();
|
||||||
await clickNext();
|
await clickNext();
|
||||||
await clickNext();
|
await clickNext();
|
||||||
|
await clickNext();
|
||||||
await enterBlueprintName();
|
await enterBlueprintName();
|
||||||
await clickNext();
|
await clickNext();
|
||||||
const review = await screen.findByTestId('review-registration');
|
const review = await screen.findByTestId('review-registration');
|
||||||
|
|
@ -737,6 +746,8 @@ describe('Step Registration', () => {
|
||||||
await clickNext();
|
await clickNext();
|
||||||
await clickNext();
|
await clickNext();
|
||||||
await clickNext();
|
await clickNext();
|
||||||
|
await clickNext();
|
||||||
|
await clickNext();
|
||||||
await enterBlueprintName();
|
await enterBlueprintName();
|
||||||
await clickNext();
|
await clickNext();
|
||||||
await screen.findByText('Register the system later');
|
await screen.findByText('Register the system later');
|
||||||
|
|
@ -762,86 +773,77 @@ describe('Step Registration', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// describe('Step File system configuration', () => {
|
describe('Step File system configuration', () => {
|
||||||
// const user = userEvent.setup();
|
// const user = userEvent.setup();
|
||||||
// const setUp = async () => {
|
// const setUp = async () => {
|
||||||
// ({ router } = await renderCustomRoutesWithReduxRouter(
|
// ({ router } = await renderCustomRoutesWithReduxRouter(
|
||||||
// 'imagewizard',
|
// 'imagewizard',
|
||||||
// {},
|
// {},
|
||||||
// routes
|
// routes
|
||||||
// ));
|
// ));
|
||||||
|
// // select aws as upload destination
|
||||||
// // select aws as upload destination
|
// await waitFor(
|
||||||
// await waitFor(
|
// async () => await user.click(await screen.findByTestId('upload-aws'))
|
||||||
// async () => await user.click(await screen.findByTestId('upload-aws'))
|
// );
|
||||||
// );
|
// await clickNext();
|
||||||
// await clickNext();
|
// // aws step
|
||||||
|
// await switchToAWSManual();
|
||||||
// // aws step
|
// await user.type(
|
||||||
// await switchToAWSManual();
|
// screen.getByRole('textbox', {
|
||||||
// await user.type(
|
// name: /aws account id/i,
|
||||||
// await screen.findByTestId('aws-account-id'),
|
// }),
|
||||||
// '012345678901'
|
// '012345678901'
|
||||||
// );
|
// );
|
||||||
// await clickNext();
|
// await clickNext();
|
||||||
// // skip registration
|
// // skip registration
|
||||||
// await screen.findByRole('textbox', {
|
// await screen.findByRole('textbox', {
|
||||||
// name: 'Select activation key',
|
// name: 'Select activation key',
|
||||||
// });
|
// });
|
||||||
|
// const registerLaterRadio = await screen.findByTestId(
|
||||||
// const registerLaterRadio = await screen.findByTestId('registration-radio-later');
|
// 'registration-radio-later'
|
||||||
// await user.click(registerLaterRadio);
|
// );
|
||||||
// await clickNext();
|
// await user.click(registerLaterRadio);
|
||||||
// };
|
// await clickNext();
|
||||||
|
// await clickNext();
|
||||||
// test('Error validation occurs upon clicking next button', async () => {
|
// };
|
||||||
// await setUp();
|
//test('Error validation occurs upon clicking next button', async () => {
|
||||||
|
// await setUp();
|
||||||
// const manuallyConfigurePartitions = await screen.findByText(
|
// const manuallyConfigurePartitions = await screen.findByText(
|
||||||
// /manually configure partitions/i
|
// /manually configure partitions/i
|
||||||
// );
|
// );
|
||||||
// await user.click(manuallyConfigurePartitions);
|
// await user.click(manuallyConfigurePartitions);
|
||||||
|
// const addPartition = await screen.findByTestId('file-system-add-partition');
|
||||||
// const addPartition = await screen.findByTestId('file-system-add-partition');
|
// // Create duplicate partitions
|
||||||
|
// await user.click(addPartition);
|
||||||
// // Create duplicate partitions
|
// await user.click(addPartition);
|
||||||
// await user.click(addPartition);
|
// expect(await getNextButton()).toBeDisabled();
|
||||||
// await user.click(addPartition);
|
// // Clicking next causes errors to appear
|
||||||
|
// await clickNext();
|
||||||
// expect(await getNextButton()).toBeEnabled();
|
// const mountPointWarning = await screen.findByRole('heading', {
|
||||||
|
// name: /danger alert: duplicate mount points: all mount points must be unique\. remove the duplicate or choose a new mount point\./i,
|
||||||
// // Clicking next causes errors to appear
|
// hidden: true,
|
||||||
// await clickNext();
|
// });
|
||||||
|
// const mountPointAlerts = screen.getAllByRole('heading', {
|
||||||
// const mountPointWarning = await screen.findByRole('heading', {
|
// name: /danger alert: duplicate mount point\./i,
|
||||||
// name: /danger alert: duplicate mount points: all mount points must be unique\. remove the duplicate or choose a new mount point\./i,
|
// });
|
||||||
// hidden: true,
|
// const tbody = await screen.findByTestId('file-system-configuration-tbody');
|
||||||
// });
|
// const rows = within(tbody).getAllByRole('row');
|
||||||
|
// expect(rows).toHaveLength(3);
|
||||||
// const mountPointAlerts = screen.getAllByRole('heading', {
|
// //Change mountpoint of final row to /var, resolving errors
|
||||||
// name: /danger alert: duplicate mount point\./i,
|
// const mountPointOptions = within(rows[2]).getAllByRole('button', {
|
||||||
// });
|
// name: 'Options menu',
|
||||||
|
// })[0];
|
||||||
// const tbody = await screen.findByTestId('file-system-configuration-tbody');
|
// await user.click(mountPointOptions);
|
||||||
// const rows = within(tbody).getAllByRole('row');
|
// const varButton = await within(rows[2]).findByRole('option', {
|
||||||
// expect(rows).toHaveLength(3);
|
// name: '/var',
|
||||||
|
// });
|
||||||
// // Change mountpoint of final row to /var, resolving errors
|
// await user.click(varButton);
|
||||||
// const mountPointOptions = within(rows[2]).getAllByRole('button', {
|
// // await waitFor(() => expect(mountPointWarning).not.toBeInTheDocument());
|
||||||
// name: 'Options menu',
|
// // await waitFor(() => expect(mountPointAlerts[0]).not.toBeInTheDocument());
|
||||||
// })[0];
|
// // await waitFor(() => expect(mountPointAlerts[1]).not.toBeInTheDocument());
|
||||||
// await user.click(mountPointOptions);
|
// expect(await getNextButton()).toBeEnabled();
|
||||||
// const varButton = await within(rows[2]).findByRole('option', {
|
//});
|
||||||
// name: '/var',
|
});
|
||||||
// });
|
|
||||||
// await user.click(varButton);
|
|
||||||
|
|
||||||
// await waitFor(() => expect(mountPointWarning).not.toBeInTheDocument());
|
|
||||||
// await waitFor(() => expect(mountPointAlerts[0]).not.toBeInTheDocument());
|
|
||||||
// await waitFor(() => expect(mountPointAlerts[1]).not.toBeInTheDocument());
|
|
||||||
// expect(await getNextButton()).toBeEnabled();
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
|
|
||||||
describe('Step Details', () => {
|
describe('Step Details', () => {
|
||||||
const user = userEvent.setup();
|
const user = userEvent.setup();
|
||||||
|
|
@ -883,7 +885,7 @@ describe('Step Details', () => {
|
||||||
// skip packages
|
// skip packages
|
||||||
await clickNext();
|
await clickNext();
|
||||||
// skip fsc
|
// skip fsc
|
||||||
//await clickNext();
|
await clickNext();
|
||||||
};
|
};
|
||||||
|
|
||||||
test('image name invalid for more than 63 chars', async () => {
|
test('image name invalid for more than 63 chars', async () => {
|
||||||
|
|
@ -962,6 +964,7 @@ describe('Step Review', () => {
|
||||||
await clickNext();
|
await clickNext();
|
||||||
// skip packages
|
// skip packages
|
||||||
await clickNext();
|
await clickNext();
|
||||||
|
await clickNext();
|
||||||
// skip Details
|
// skip Details
|
||||||
const blueprintName = await screen.findByRole('textbox', {
|
const blueprintName = await screen.findByRole('textbox', {
|
||||||
name: /blueprint name/i,
|
name: /blueprint name/i,
|
||||||
|
|
@ -1023,7 +1026,7 @@ describe('Step Review', () => {
|
||||||
await clickNext();
|
await clickNext();
|
||||||
// skip repositories
|
// skip repositories
|
||||||
await clickNext();
|
await clickNext();
|
||||||
// skip Details
|
await clickNext();
|
||||||
const blueprintName = await screen.findByRole('textbox', {
|
const blueprintName = await screen.findByRole('textbox', {
|
||||||
name: /blueprint name/i,
|
name: /blueprint name/i,
|
||||||
});
|
});
|
||||||
|
|
@ -1061,7 +1064,9 @@ describe('Step Review', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
const contentExpandable = await screen.findByTestId('content-expandable');
|
const contentExpandable = await screen.findByTestId('content-expandable');
|
||||||
// const fscExpandable = screen.getByText(/file system configuration/i);
|
const fscExpandable = screen.getByTestId(
|
||||||
|
'file-system-configuration-expandable'
|
||||||
|
);
|
||||||
|
|
||||||
await user.click(targetExpandable);
|
await user.click(targetExpandable);
|
||||||
await screen.findByText('AWS');
|
await screen.findByText('AWS');
|
||||||
|
|
@ -1071,19 +1076,17 @@ describe('Step Review', () => {
|
||||||
|
|
||||||
await within(contentExpandable).findByText('Custom repositories');
|
await within(contentExpandable).findByText('Custom repositories');
|
||||||
await within(contentExpandable).findByText('Additional packages');
|
await within(contentExpandable).findByText('Additional packages');
|
||||||
// await user.click(fscExpandable);
|
await user.click(fscExpandable);
|
||||||
// await screen.findByText('Configuration type');
|
await screen.findByText('Configuration type');
|
||||||
});
|
});
|
||||||
test('has no Registration expandable for centos', async () => {
|
test('has no Registration expandable for centos', async () => {
|
||||||
await setUpCentOS();
|
await setUpCentOS();
|
||||||
const targetExpandable = screen.getByText(/target environments/i);
|
const targetExpandable = screen.getByText(/target environments/i);
|
||||||
const contentExpandable = await screen.findByTestId('content-expandable');
|
const contentExpandable = await screen.findByTestId('content-expandable');
|
||||||
|
|
||||||
//const fscExpandable = await screen.findByTestId(
|
const fscExpandable = await screen.findByTestId(
|
||||||
// 'file-system-configuration-expandable'
|
'file-system-configuration-expandable'
|
||||||
// );
|
);
|
||||||
// });
|
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
screen.queryByTestId('registration-expandable')
|
screen.queryByTestId('registration-expandable')
|
||||||
).not.toBeInTheDocument();
|
).not.toBeInTheDocument();
|
||||||
|
|
@ -1093,12 +1096,11 @@ describe('Step Review', () => {
|
||||||
await user.click(contentExpandable);
|
await user.click(contentExpandable);
|
||||||
await within(contentExpandable).findByText('Custom repositories');
|
await within(contentExpandable).findByText('Custom repositories');
|
||||||
await within(contentExpandable).findByText('Additional packages');
|
await within(contentExpandable).findByText('Additional packages');
|
||||||
|
|
||||||
|
await user.click(fscExpandable);
|
||||||
|
await screen.findByText('Configuration type');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
// await user.click(fscExpandable);
|
|
||||||
// await screen.findByText('Configuration type');
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
|
|
||||||
describe('Keyboard accessibility', () => {
|
describe('Keyboard accessibility', () => {
|
||||||
const user = userEvent.setup();
|
const user = userEvent.setup();
|
||||||
|
|
@ -1199,8 +1201,8 @@ describe('Keyboard accessibility', () => {
|
||||||
// TODO: Focus on textbox on OpenSCAP step
|
// TODO: Focus on textbox on OpenSCAP step
|
||||||
await clickNext();
|
await clickNext();
|
||||||
|
|
||||||
// File system configuration
|
//File system configuration
|
||||||
// await clickNext();
|
await clickNext();
|
||||||
|
|
||||||
// TODO: Focus on textbox on Custom Repos step
|
// TODO: Focus on textbox on Custom Repos step
|
||||||
await clickNext();
|
await clickNext();
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ const goToDetailsStep = async () => {
|
||||||
await clickNext();
|
await clickNext();
|
||||||
await clickNext();
|
await clickNext();
|
||||||
await clickNext();
|
await clickNext();
|
||||||
|
await clickNext();
|
||||||
};
|
};
|
||||||
|
|
||||||
const enterBlueprintDescription = async () => {
|
const enterBlueprintDescription = async () => {
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@ const goToPackagesStep = async () => {
|
||||||
await clickNext(); // Registration
|
await clickNext(); // Registration
|
||||||
await clickRegisterLater();
|
await clickRegisterLater();
|
||||||
await clickNext(); // OpenSCAP
|
await clickNext(); // OpenSCAP
|
||||||
|
await clickNext(); // File System
|
||||||
await clickNext(); // Custom repositories
|
await clickNext(); // Custom repositories
|
||||||
await clickNext(); // Additional packages
|
await clickNext(); // Additional packages
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,7 @@ const goToReviewStep = async () => {
|
||||||
await clickNext();
|
await clickNext();
|
||||||
await clickNext();
|
await clickNext();
|
||||||
await clickNext();
|
await clickNext();
|
||||||
|
await clickNext();
|
||||||
await enterBlueprintName();
|
await enterBlueprintName();
|
||||||
await clickNext();
|
await clickNext();
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -43,11 +43,13 @@ const goToRepositoriesStep = async () => {
|
||||||
await clickNext(); // Registration
|
await clickNext(); // Registration
|
||||||
await clickRegisterLater();
|
await clickRegisterLater();
|
||||||
await clickNext(); // OpenSCAP
|
await clickNext(); // OpenSCAP
|
||||||
|
await clickNext(); // File System
|
||||||
await clickNext(); // Custom repositories
|
await clickNext(); // Custom repositories
|
||||||
};
|
};
|
||||||
|
|
||||||
const goToReviewStep = async () => {
|
const goToReviewStep = async () => {
|
||||||
await clickNext(); // Additional packages
|
await clickNext(); // Additional packages
|
||||||
|
await clickNext();
|
||||||
await clickNext(); // Details
|
await clickNext(); // Details
|
||||||
await enterBlueprintName();
|
await enterBlueprintName();
|
||||||
await clickNext(); // Review
|
await clickNext(); // Review
|
||||||
|
|
|
||||||
|
|
@ -119,6 +119,7 @@ const goToReviewStep = async () => {
|
||||||
await goToRegistrationStep(); // Register
|
await goToRegistrationStep(); // Register
|
||||||
await clickRegisterLater();
|
await clickRegisterLater();
|
||||||
await clickNext(); // OpenSCAP
|
await clickNext(); // OpenSCAP
|
||||||
|
await clickNext(); // File system customization
|
||||||
await clickNext(); // Custom repositories
|
await clickNext(); // Custom repositories
|
||||||
await clickNext(); // Additional packages
|
await clickNext(); // Additional packages
|
||||||
await clickNext(); // Details
|
await clickNext(); // Details
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue