Wizard: Add validation for kernel step

This adds validation for the Kernel step.
This commit is contained in:
regexowl 2025-01-20 12:35:48 +01:00 committed by Klara Simickova
parent 2d011c52e1
commit bfef28999d
5 changed files with 64 additions and 1 deletions

View file

@ -40,6 +40,7 @@ import {
useDetailsValidation, useDetailsValidation,
useRegistrationValidation, useRegistrationValidation,
useHostnameValidation, useHostnameValidation,
useKernelValidation,
} from './utilities/useValidation'; } from './utilities/useValidation';
import { import {
isAwsAccountIdValid, isAwsAccountIdValid,
@ -223,6 +224,8 @@ const CreateImageWizard = ({ isEdit }: CreateImageWizardProps) => {
const fileSystemValidation = useFilesystemValidation(); const fileSystemValidation = useFilesystemValidation();
// Hostname // Hostname
const hostnameValidation = useHostnameValidation(); const hostnameValidation = useHostnameValidation();
// Kernel
const kernelValidation = useKernelValidation();
// Firstboot // Firstboot
const firstBootValidation = useFirstBootValidation(); const firstBootValidation = useFirstBootValidation();
// Details // Details
@ -510,8 +513,12 @@ const CreateImageWizard = ({ isEdit }: CreateImageWizardProps) => {
key="wizard-kernel" key="wizard-kernel"
navItem={customStatusNavItem} navItem={customStatusNavItem}
isHidden={!isKernelEnabled} isHidden={!isKernelEnabled}
status={kernelValidation.disabledNext ? 'error' : 'default'}
footer={ footer={
<CustomWizardFooter disableNext={false} optional={true} /> <CustomWizardFooter
disableNext={kernelValidation.disabledNext}
optional={true}
/>
} }
> >
<KernelStep /> <KernelStep />

View file

@ -4,6 +4,8 @@ import { FormGroup } from '@patternfly/react-core';
import { import {
Alert, Alert,
Button, Button,
HelperText,
HelperTextItem,
MenuToggle, MenuToggle,
MenuToggleElement, MenuToggleElement,
Select, Select,
@ -20,6 +22,7 @@ import {
changeKernelName, changeKernelName,
selectKernel, selectKernel,
} from '../../../../../store/wizardSlice'; } from '../../../../../store/wizardSlice';
import { useKernelValidation } from '../../../utilities/useValidation';
const initialOptions = ['kernel', 'kernel-debug']; const initialOptions = ['kernel', 'kernel-debug'];
let kernelOptions = initialOptions; let kernelOptions = initialOptions;
@ -28,6 +31,8 @@ const KernelName = () => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const kernel = useAppSelector(selectKernel).name; const kernel = useAppSelector(selectKernel).name;
const stepValidation = useKernelValidation();
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
const [inputValue, setInputValue] = useState<string>(''); const [inputValue, setInputValue] = useState<string>('');
const [filterValue, setFilterValue] = useState<string>(''); const [filterValue, setFilterValue] = useState<string>('');
@ -166,6 +171,13 @@ const KernelName = () => {
))} ))}
</SelectList> </SelectList>
</Select> </Select>
{stepValidation.errors.kernel && (
<HelperText>
<HelperTextItem variant={'error'}>
{stepValidation.errors.kernel}
</HelperTextItem>
</HelperText>
)}
</FormGroup> </FormGroup>
</> </>
); );

View file

@ -19,6 +19,7 @@ import {
selectActivationKey, selectActivationKey,
selectRegistrationType, selectRegistrationType,
selectHostname, selectHostname,
selectKernel,
} from '../../../store/wizardSlice'; } from '../../../store/wizardSlice';
import { import {
getDuplicateMountPoints, getDuplicateMountPoints,
@ -27,6 +28,7 @@ import {
isMountpointMinSizeValid, isMountpointMinSizeValid,
isSnapshotValid, isSnapshotValid,
isHostnameValid, isHostnameValid,
isKernelNameValid,
} from '../validators'; } from '../validators';
export type StepValidation = { export type StepValidation = {
@ -41,6 +43,7 @@ export function useIsBlueprintValid(): boolean {
const filesystem = useFilesystemValidation(); const filesystem = useFilesystemValidation();
const snapshot = useSnapshotValidation(); const snapshot = useSnapshotValidation();
const hostname = useHostnameValidation(); const hostname = useHostnameValidation();
const kernel = useKernelValidation();
const firstBoot = useFirstBootValidation(); const firstBoot = useFirstBootValidation();
const details = useDetailsValidation(); const details = useDetailsValidation();
return ( return (
@ -48,6 +51,7 @@ export function useIsBlueprintValid(): boolean {
!filesystem.disabledNext && !filesystem.disabledNext &&
!snapshot.disabledNext && !snapshot.disabledNext &&
!hostname.disabledNext && !hostname.disabledNext &&
!kernel.disabledNext &&
!firstBoot.disabledNext && !firstBoot.disabledNext &&
!details.disabledNext !details.disabledNext
); );
@ -155,6 +159,20 @@ export function useHostnameValidation(): StepValidation {
return { errors: {}, disabledNext: false }; return { errors: {}, disabledNext: false };
} }
export function useKernelValidation(): StepValidation {
const kernel = useAppSelector(selectKernel);
if (!isKernelNameValid(kernel.name)) {
return {
errors: {
kernel: 'Invalid format.',
},
disabledNext: true,
};
}
return { errors: {}, disabledNext: false };
}
export function useDetailsValidation(): StepValidation { export function useDetailsValidation(): StepValidation {
const name = useAppSelector(selectBlueprintName); const name = useAppSelector(selectBlueprintName);
const description = useAppSelector(selectBlueprintDescription); const description = useAppSelector(selectBlueprintDescription);

View file

@ -91,6 +91,17 @@ export const isHostnameValid = (hostname: string) => {
); );
}; };
export const isKernelNameValid = (kernelName: string) => {
if (!kernelName) {
return true;
}
return (
kernelName.length < 65 &&
/^[a-z0-9]|[a-z0-9][a-z0-9-_.+]*[a-z0-9]$/.test(kernelName)
);
};
export const isPortValid = (port: string) => { export const isPortValid = (port: string) => {
return /^(\d{1,5}|[a-z]{1,6})(-\d{1,5})?:[a-z]{1,6}$/.test(port); return /^(\d{1,5}|[a-z]{1,6})(-\d{1,5})?:[a-z]{1,6}$/.test(port);
}; };

View file

@ -8,6 +8,7 @@ import {
clickBack, clickBack,
clickNext, clickNext,
enterBlueprintName, enterBlueprintName,
getNextButton,
interceptBlueprintRequest, interceptBlueprintRequest,
openAndDismissSaveAndBuildModal, openAndDismissSaveAndBuildModal,
verifyCancelButton, verifyCancelButton,
@ -121,6 +122,20 @@ describe('Step Kernel', () => {
await openKernelNameOptions(kernelNameDropdown); await openKernelNameOptions(kernelNameDropdown);
await screen.findByText(CUSTOM_NAME); await screen.findByText(CUSTOM_NAME);
}); });
test('disable Next with invalid custom kernel name', async () => {
await renderCreateMode();
await goToKernelStep();
await selectCustomKernelName('-----------');
await screen.findByText(/Invalid format/);
const nextButton = await getNextButton();
expect(nextButton).toBeDisabled();
await clearKernelName();
expect(screen.queryByText(/Invalid format/)).not.toBeInTheDocument();
expect(nextButton).toBeEnabled();
});
}); });
describe('Kernel request generated correctly', () => { describe('Kernel request generated correctly', () => {