From 37d0e93c8dba39b01bf518a5d20a60b58d350d52 Mon Sep 17 00:00:00 2001 From: regexowl Date: Mon, 20 Jan 2025 12:35:48 +0100 Subject: [PATCH] Wizard: Add validation for kernel step This adds validation for the Kernel step. --- .../CreateImageWizard/CreateImageWizard.tsx | 9 ++++++++- .../steps/Kernel/components/KernelName.tsx | 12 +++++++++++ .../utilities/useValidation.tsx | 20 +++++++++++++++++++ .../CreateImageWizard/validators.ts | 11 ++++++++++ .../steps/Kernel/Kernel.test.tsx | 12 +++++++++++ 5 files changed, 63 insertions(+), 1 deletion(-) diff --git a/src/Components/CreateImageWizard/CreateImageWizard.tsx b/src/Components/CreateImageWizard/CreateImageWizard.tsx index 13295bbcf..0fd25fce6 100644 --- a/src/Components/CreateImageWizard/CreateImageWizard.tsx +++ b/src/Components/CreateImageWizard/CreateImageWizard.tsx @@ -40,6 +40,7 @@ import { useDetailsValidation, useRegistrationValidation, useHostnameValidation, + useKernelValidation, } from './utilities/useValidation'; import { isAwsAccountIdValid, @@ -223,6 +224,8 @@ const CreateImageWizard = ({ isEdit }: CreateImageWizardProps) => { const fileSystemValidation = useFilesystemValidation(); // Hostname const hostnameValidation = useHostnameValidation(); + // Kernel + const kernelValidation = useKernelValidation(); // Firstboot const firstBootValidation = useFirstBootValidation(); // Details @@ -510,8 +513,12 @@ const CreateImageWizard = ({ isEdit }: CreateImageWizardProps) => { key="wizard-kernel" navItem={customStatusNavItem} isHidden={!isKernelEnabled} + status={kernelValidation.disabledNext ? 'error' : 'default'} footer={ - + } > diff --git a/src/Components/CreateImageWizard/steps/Kernel/components/KernelName.tsx b/src/Components/CreateImageWizard/steps/Kernel/components/KernelName.tsx index d8492b2f6..e9ab48995 100644 --- a/src/Components/CreateImageWizard/steps/Kernel/components/KernelName.tsx +++ b/src/Components/CreateImageWizard/steps/Kernel/components/KernelName.tsx @@ -4,6 +4,8 @@ import { FormGroup } from '@patternfly/react-core'; import { Alert, Button, + HelperText, + HelperTextItem, MenuToggle, MenuToggleElement, Select, @@ -20,6 +22,7 @@ import { changeKernelName, selectKernel, } from '../../../../../store/wizardSlice'; +import { useKernelValidation } from '../../../utilities/useValidation'; const initialOptions = ['kernel', 'kernel-debug']; let kernelOptions = initialOptions; @@ -28,6 +31,8 @@ const KernelName = () => { const dispatch = useAppDispatch(); const kernel = useAppSelector(selectKernel).name; + const stepValidation = useKernelValidation(); + const [isOpen, setIsOpen] = useState(false); const [inputValue, setInputValue] = useState(''); const [filterValue, setFilterValue] = useState(''); @@ -166,6 +171,13 @@ const KernelName = () => { ))} + {stepValidation.errors.kernel && ( + + + {stepValidation.errors.kernel} + + + )} ); diff --git a/src/Components/CreateImageWizard/utilities/useValidation.tsx b/src/Components/CreateImageWizard/utilities/useValidation.tsx index 2307c6d2d..019ea596b 100644 --- a/src/Components/CreateImageWizard/utilities/useValidation.tsx +++ b/src/Components/CreateImageWizard/utilities/useValidation.tsx @@ -19,6 +19,7 @@ import { selectActivationKey, selectRegistrationType, selectHostname, + selectKernel, } from '../../../store/wizardSlice'; import { getDuplicateMountPoints, @@ -27,6 +28,7 @@ import { isMountpointMinSizeValid, isSnapshotValid, isHostnameValid, + isKernelNameValid, } from '../validators'; export type StepValidation = { @@ -41,6 +43,7 @@ export function useIsBlueprintValid(): boolean { const filesystem = useFilesystemValidation(); const snapshot = useSnapshotValidation(); const hostname = useHostnameValidation(); + const kernel = useKernelValidation(); const firstBoot = useFirstBootValidation(); const details = useDetailsValidation(); return ( @@ -48,6 +51,7 @@ export function useIsBlueprintValid(): boolean { !filesystem.disabledNext && !snapshot.disabledNext && !hostname.disabledNext && + !kernel.disabledNext && !firstBoot.disabledNext && !details.disabledNext ); @@ -155,6 +159,22 @@ export function useHostnameValidation(): StepValidation { return { errors: {}, disabledNext: false }; } +export function useKernelValidation(): StepValidation { + const kernel = useAppSelector(selectKernel); + + const errorMessage = 'Invalid format.'; + + if (!isKernelNameValid(kernel.name)) { + return { + errors: { + kernel: errorMessage, + }, + disabledNext: true, + }; + } + return { errors: {}, disabledNext: false }; +} + export function useDetailsValidation(): StepValidation { const name = useAppSelector(selectBlueprintName); const description = useAppSelector(selectBlueprintDescription); diff --git a/src/Components/CreateImageWizard/validators.ts b/src/Components/CreateImageWizard/validators.ts index f65d26fba..22988fa3f 100644 --- a/src/Components/CreateImageWizard/validators.ts +++ b/src/Components/CreateImageWizard/validators.ts @@ -104,6 +104,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) => { return /^(\d{1,5}|[a-z]{1,6})(-\d{1,5})?:[a-z]{1,6}$/.test(port); }; diff --git a/src/test/Components/CreateImageWizard/steps/Kernel/Kernel.test.tsx b/src/test/Components/CreateImageWizard/steps/Kernel/Kernel.test.tsx index 19398e9f5..775e932b9 100644 --- a/src/test/Components/CreateImageWizard/steps/Kernel/Kernel.test.tsx +++ b/src/test/Components/CreateImageWizard/steps/Kernel/Kernel.test.tsx @@ -8,6 +8,7 @@ import { clickBack, clickNext, enterBlueprintName, + getNextButton, interceptBlueprintRequest, openAndDismissSaveAndBuildModal, verifyCancelButton, @@ -121,6 +122,17 @@ describe('Step Kernel', () => { await openKernelNameOptions(kernelNameDropdown); await screen.findByText(CUSTOM_NAME); }); + + test('disable Next with invalid custom kernel name', async () => { + await renderCreateMode(); + await goToKernelStep(); + + await selectCustomKernelName('-----------'); + const nextButton = await getNextButton(); + expect(nextButton).toBeDisabled(); + await clearKernelName(); + expect(nextButton).toBeEnabled(); + }); }); describe('Kernel request generated correctly', () => {