Wizard: Fix blueprint name update on Architecture/Distribution changes
This commit resolves an issue where the blueprint name did not update when the user changed the Architecture or Distribution. Additionally, it sets an initial value for blueprintName in the WizardSlice.
This commit is contained in:
parent
b2255de04e
commit
978237bf84
8 changed files with 96 additions and 64 deletions
|
|
@ -16,8 +16,8 @@ import {
|
|||
changeBlueprintName,
|
||||
selectBlueprintDescription,
|
||||
selectBlueprintName,
|
||||
setIsCustomName,
|
||||
} from '../../../../store/wizardSlice';
|
||||
import { useGenerateDefaultName } from '../../utilities/useGenerateDefaultName';
|
||||
import { useDetailsValidation } from '../../utilities/useValidation';
|
||||
import { ValidatedInputAndTextArea } from '../../ValidatedInput';
|
||||
|
||||
|
|
@ -26,13 +26,12 @@ const DetailsStep = () => {
|
|||
const blueprintName = useAppSelector(selectBlueprintName);
|
||||
const blueprintDescription = useAppSelector(selectBlueprintDescription);
|
||||
|
||||
useGenerateDefaultName();
|
||||
|
||||
const handleNameChange = (
|
||||
_event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>,
|
||||
name: string
|
||||
) => {
|
||||
dispatch(changeBlueprintName(name));
|
||||
dispatch(setIsCustomName());
|
||||
};
|
||||
|
||||
const handleDescriptionChange = (
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import React from 'react';
|
||||
import React, { useEffect } from 'react';
|
||||
|
||||
import { Text, Form, Title } from '@patternfly/react-core';
|
||||
|
||||
|
|
@ -8,12 +8,30 @@ import ReleaseLifecycle from './ReleaseLifecycle';
|
|||
import ReleaseSelect from './ReleaseSelect';
|
||||
import TargetEnvironment from './TargetEnvironment';
|
||||
|
||||
import { useAppSelector } from '../../../../store/hooks';
|
||||
import { selectDistribution } from '../../../../store/wizardSlice';
|
||||
import { useAppDispatch, useAppSelector } from '../../../../store/hooks';
|
||||
import {
|
||||
changeBlueprintName,
|
||||
selectArchitecture,
|
||||
selectBlueprintName,
|
||||
selectDistribution,
|
||||
selectIsCustomName,
|
||||
} from '../../../../store/wizardSlice';
|
||||
import DocumentationButton from '../../../sharedComponents/DocumentationButton';
|
||||
import { generateDefaultName } from '../../utilities/useGenerateDefaultName';
|
||||
|
||||
const ImageOutputStep = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const blueprintName = useAppSelector(selectBlueprintName);
|
||||
const distribution = useAppSelector(selectDistribution);
|
||||
const arch = useAppSelector(selectArchitecture);
|
||||
const isCustomName = useAppSelector(selectIsCustomName);
|
||||
|
||||
useEffect(() => {
|
||||
const defaultName = generateDefaultName(distribution, arch);
|
||||
if (!isCustomName && blueprintName !== defaultName) {
|
||||
dispatch(changeBlueprintName(defaultName));
|
||||
}
|
||||
}, [dispatch, distribution, arch, isCustomName]);
|
||||
|
||||
return (
|
||||
<Form>
|
||||
|
|
|
|||
|
|
@ -9,14 +9,11 @@ import {
|
|||
selectBlueprintDescription,
|
||||
selectBlueprintName,
|
||||
} from '../../../../store/wizardSlice';
|
||||
import { useGenerateDefaultName } from '../../utilities/useGenerateDefaultName';
|
||||
|
||||
const ReviewStep = () => {
|
||||
const blueprintName = useAppSelector(selectBlueprintName);
|
||||
const blueprintDescription = useAppSelector(selectBlueprintDescription);
|
||||
|
||||
useGenerateDefaultName();
|
||||
|
||||
return (
|
||||
<Form>
|
||||
<Title headingLevel="h1" size="xl">
|
||||
|
|
|
|||
|
|
@ -222,6 +222,7 @@ function commonRequestToState(
|
|||
return {
|
||||
details: {
|
||||
blueprintName: request.name || '',
|
||||
isCustomName: true,
|
||||
blueprintDescription: request.description || '',
|
||||
},
|
||||
users:
|
||||
|
|
|
|||
|
|
@ -1,15 +1,6 @@
|
|||
import { useEffect } from 'react';
|
||||
|
||||
import { useAppDispatch, useAppSelector } from '../../../store/hooks';
|
||||
import { Distributions, ImageRequest } from '../../../store/imageBuilderApi';
|
||||
import {
|
||||
changeBlueprintName,
|
||||
selectArchitecture,
|
||||
selectBlueprintName,
|
||||
selectDistribution,
|
||||
} from '../../../store/wizardSlice';
|
||||
|
||||
const generateDefaultName = (
|
||||
export const generateDefaultName = (
|
||||
distribution: Distributions,
|
||||
arch: ImageRequest['architecture']
|
||||
) => {
|
||||
|
|
@ -24,19 +15,3 @@ const generateDefaultName = (
|
|||
|
||||
return `${distribution}-${arch}-${dateTimeString}`;
|
||||
};
|
||||
|
||||
export const useGenerateDefaultName = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const blueprintName = useAppSelector(selectBlueprintName);
|
||||
const distribution = useAppSelector(selectDistribution);
|
||||
const arch = useAppSelector(selectArchitecture);
|
||||
|
||||
useEffect(() => {
|
||||
if (!blueprintName) {
|
||||
dispatch(changeBlueprintName(generateDefaultName(distribution, arch)));
|
||||
}
|
||||
// This useEffect hook should run *only* on mount and therefore has an empty
|
||||
// dependency array. eslint's exhaustive-deps rule does not support this use.
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ import type {
|
|||
GcpShareMethod,
|
||||
} from '../Components/CreateImageWizard/steps/TargetEnvironment/Gcp';
|
||||
import type { V1ListSourceResponseItem } from '../Components/CreateImageWizard/types';
|
||||
import { generateDefaultName } from '../Components/CreateImageWizard/utilities/useGenerateDefaultName';
|
||||
import { RHEL_9, X86_64 } from '../constants';
|
||||
|
||||
import type { RootState } from '.';
|
||||
|
|
@ -140,6 +141,7 @@ export type wizardState = {
|
|||
locale: Locale;
|
||||
details: {
|
||||
blueprintName: string;
|
||||
isCustomName: boolean;
|
||||
blueprintDescription: string;
|
||||
};
|
||||
timezone: Timezone;
|
||||
|
|
@ -226,7 +228,8 @@ export const initialState: wizardState = {
|
|||
keyboard: '',
|
||||
},
|
||||
details: {
|
||||
blueprintName: '',
|
||||
blueprintName: generateDefaultName(RHEL_9, X86_64),
|
||||
isCustomName: false,
|
||||
blueprintDescription: '',
|
||||
},
|
||||
timezone: {
|
||||
|
|
@ -424,6 +427,10 @@ export const selectBlueprintName = (state: RootState) => {
|
|||
return state.wizard.details.blueprintName;
|
||||
};
|
||||
|
||||
export const selectIsCustomName = (state: RootState) => {
|
||||
return state.wizard.details.isCustomName;
|
||||
};
|
||||
|
||||
export const selectMetadata = (state: RootState) => {
|
||||
return state.wizard.metadata;
|
||||
};
|
||||
|
|
@ -815,6 +822,9 @@ export const wizardSlice = createSlice({
|
|||
changeBlueprintName: (state, action: PayloadAction<string>) => {
|
||||
state.details.blueprintName = action.payload;
|
||||
},
|
||||
setIsCustomName: (state) => {
|
||||
state.details.isCustomName = true;
|
||||
},
|
||||
changeBlueprintDescription: (state, action: PayloadAction<string>) => {
|
||||
state.details.blueprintDescription = action.payload;
|
||||
},
|
||||
|
|
@ -1069,6 +1079,7 @@ export const {
|
|||
clearLanguages,
|
||||
changeKeyboard,
|
||||
changeBlueprintName,
|
||||
setIsCustomName,
|
||||
changeBlueprintDescription,
|
||||
loadWizardState,
|
||||
setFirstBootScript,
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import {
|
|||
renderEditMode,
|
||||
} from '../../wizardTestUtils';
|
||||
|
||||
const goToDetailsStep = async () => {
|
||||
export const goToDetailsStep = async () => {
|
||||
await clickNext(); // OpenSCAP
|
||||
await clickNext(); // File system configuration
|
||||
await clickNext(); // Repository snapshot
|
||||
|
|
|
|||
|
|
@ -36,10 +36,10 @@ import {
|
|||
imageRequest,
|
||||
interceptBlueprintRequest,
|
||||
interceptEditBlueprintRequest,
|
||||
openAndDismissSaveAndBuildModal,
|
||||
renderCreateMode,
|
||||
renderEditMode,
|
||||
} from '../../wizardTestUtils';
|
||||
import { goToDetailsStep } from '../Details/Details.test';
|
||||
|
||||
let router: RemixRouter | undefined = undefined;
|
||||
|
||||
|
|
@ -117,6 +117,14 @@ const selectGuestImageTarget = async () => {
|
|||
await waitFor(() => user.click(guestImageCheckBox));
|
||||
};
|
||||
|
||||
const verifyNameInReviewStep = async (name: string) => {
|
||||
const region = screen.getByRole('region', {
|
||||
name: /details revisit step/i,
|
||||
});
|
||||
const definition = within(region).getByRole('definition');
|
||||
expect(definition).toHaveTextContent(name);
|
||||
};
|
||||
|
||||
const selectVMwareTarget = async () => {
|
||||
const user = userEvent.setup();
|
||||
const vmwareImageCheckBox = await screen.findByTestId('checkbox-vmware');
|
||||
|
|
@ -128,21 +136,7 @@ const handleRegistration = async () => {
|
|||
await clickRegisterLater();
|
||||
};
|
||||
|
||||
const goToReviewStep = async () => {
|
||||
await clickNext(); // OpenSCAP
|
||||
await clickNext(); // File system customization
|
||||
await clickNext(); // Repository snapshot
|
||||
await clickNext(); // Custom repositories
|
||||
await clickNext(); // Additional packages
|
||||
await clickNext(); // Users
|
||||
await clickNext(); // Timezone
|
||||
await clickNext(); // Locale
|
||||
await clickNext(); // Hostname
|
||||
await clickNext(); // Kernel
|
||||
await clickNext(); // Firewall
|
||||
await clickNext(); // Services
|
||||
await clickNext(); // First boot
|
||||
await clickNext(); // Details
|
||||
const enterNameAndGoToReviewStep = async () => {
|
||||
await enterBlueprintName();
|
||||
await clickNext(); // Review
|
||||
};
|
||||
|
|
@ -308,10 +302,43 @@ describe('Step Image output', () => {
|
|||
await renderCreateMode();
|
||||
await selectGuestImageTarget();
|
||||
await handleRegistration();
|
||||
await goToReviewStep();
|
||||
await goToDetailsStep();
|
||||
await enterNameAndGoToReviewStep();
|
||||
await clickRevisitButton();
|
||||
await screen.findByRole('heading', { name: /Image output/ });
|
||||
});
|
||||
|
||||
test('change image type and check the update in Review step', async () => {
|
||||
await renderCreateMode();
|
||||
await selectGuestImageTarget();
|
||||
await handleRegistration();
|
||||
await goToDetailsStep();
|
||||
await clickNext(); // Review
|
||||
await clickRevisitButton();
|
||||
await selectRhel8();
|
||||
await selectAarch64();
|
||||
await selectGuestImageTarget();
|
||||
await handleRegistration();
|
||||
await goToDetailsStep();
|
||||
await clickNext(); // Review
|
||||
await verifyNameInReviewStep('rhel-8-aarch64');
|
||||
});
|
||||
|
||||
test('change blueprint name and image type, then verify the updated blueprint name in the Review step', async () => {
|
||||
await renderCreateMode();
|
||||
await selectGuestImageTarget();
|
||||
await handleRegistration();
|
||||
await goToDetailsStep();
|
||||
await enterNameAndGoToReviewStep();
|
||||
await clickRevisitButton();
|
||||
await selectRhel8();
|
||||
await selectAarch64();
|
||||
await selectGuestImageTarget();
|
||||
await handleRegistration();
|
||||
await goToDetailsStep();
|
||||
await clickNext(); // Review
|
||||
await verifyNameInReviewStep('Red Velvet');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Check that the target filtering is in accordance to mock content', () => {
|
||||
|
|
@ -568,7 +595,8 @@ describe('Set target using query parameter', () => {
|
|||
await renderCreateMode({ target: 'iso' });
|
||||
expect(await screen.findByTestId('checkbox-image-installer')).toBeChecked();
|
||||
await handleRegistration();
|
||||
await goToReviewStep();
|
||||
await goToDetailsStep();
|
||||
await enterNameAndGoToReviewStep();
|
||||
const targetExpandable = await screen.findByTestId(
|
||||
'target-environments-expandable'
|
||||
);
|
||||
|
|
@ -580,7 +608,8 @@ describe('Set target using query parameter', () => {
|
|||
await renderCreateMode({ target: 'qcow2' });
|
||||
expect(await screen.findByTestId('checkbox-guest-image')).toBeChecked();
|
||||
await handleRegistration();
|
||||
await goToReviewStep();
|
||||
await goToDetailsStep();
|
||||
await enterNameAndGoToReviewStep();
|
||||
const targetExpandable = await screen.findByTestId(
|
||||
'target-environments-expandable'
|
||||
);
|
||||
|
|
@ -599,10 +628,8 @@ describe('Distribution request generated correctly', () => {
|
|||
await selectRhel8();
|
||||
await selectGuestImageTarget();
|
||||
await handleRegistration();
|
||||
await goToReviewStep();
|
||||
// informational modal pops up in the first test only as it's tied
|
||||
// to a 'imageBuilder.saveAndBuildModalSeen' variable in localStorage
|
||||
await openAndDismissSaveAndBuildModal();
|
||||
await goToDetailsStep();
|
||||
await enterNameAndGoToReviewStep();
|
||||
const receivedRequest = await interceptBlueprintRequest(CREATE_BLUEPRINT);
|
||||
|
||||
const expectedRequest: CreateBlueprintRequest = {
|
||||
|
|
@ -618,7 +645,8 @@ describe('Distribution request generated correctly', () => {
|
|||
await selectRhel9();
|
||||
await selectGuestImageTarget();
|
||||
await handleRegistration();
|
||||
await goToReviewStep();
|
||||
await goToDetailsStep();
|
||||
await enterNameAndGoToReviewStep();
|
||||
const receivedRequest = await interceptBlueprintRequest(CREATE_BLUEPRINT);
|
||||
|
||||
const expectedRequest: CreateBlueprintRequest = {
|
||||
|
|
@ -633,7 +661,8 @@ describe('Distribution request generated correctly', () => {
|
|||
await renderCreateMode();
|
||||
await selectCentos9();
|
||||
await selectGuestImageTarget();
|
||||
await goToReviewStep();
|
||||
await goToDetailsStep();
|
||||
await enterNameAndGoToReviewStep();
|
||||
const receivedRequest = await interceptBlueprintRequest(CREATE_BLUEPRINT);
|
||||
|
||||
const expectedRequest: CreateBlueprintRequest = {
|
||||
|
|
@ -655,7 +684,8 @@ describe('Architecture request generated correctly', () => {
|
|||
await selectX86_64();
|
||||
await selectGuestImageTarget();
|
||||
await handleRegistration();
|
||||
await goToReviewStep();
|
||||
await goToDetailsStep();
|
||||
await enterNameAndGoToReviewStep();
|
||||
const receivedRequest = await interceptBlueprintRequest(CREATE_BLUEPRINT);
|
||||
|
||||
const expectedImageRequest: ImageRequest = {
|
||||
|
|
@ -675,7 +705,8 @@ describe('Architecture request generated correctly', () => {
|
|||
await selectAarch64();
|
||||
await selectGuestImageTarget();
|
||||
await handleRegistration();
|
||||
await goToReviewStep();
|
||||
await goToDetailsStep();
|
||||
await enterNameAndGoToReviewStep();
|
||||
const receivedRequest = await interceptBlueprintRequest(CREATE_BLUEPRINT);
|
||||
|
||||
const expectedImageRequest: ImageRequest = {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue