Skip to content

Commit

Permalink
add tests for size fields and update the validation
Browse files Browse the repository at this point in the history
  • Loading branch information
DaoDaoNoCode committed Jan 7, 2025
1 parent cdd305c commit 911c403
Show file tree
Hide file tree
Showing 14 changed files with 204 additions and 38 deletions.
2 changes: 1 addition & 1 deletion frontend/src/__mocks__/mockHardwareProfile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export const mockHardwareProfile = ({
displayName = 'Nvidia GPU',
identifiers = [
{
displayName: 'RAM',
displayName: 'Memory',
identifier: 'memory',
minCount: '2Gi',
maxCount: '5Gi',
Expand Down
28 changes: 28 additions & 0 deletions frontend/src/__tests__/cypress/cypress/pages/hardwareProfile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,34 @@ class NodeResourceModal extends Modal {
return this.find().findByTestId('resource-identifier-error');
}

findNodeResourceDefaultInput() {
return this.find().findByTestId('node-resource-size-default').findByLabelText('Input');
}

selectNodeResourceDefaultUnit(name: string) {
this.find()
.findByTestId('node-resource-size-default')
.findByTestId('value-unit-select')
.findDropdownItem(name)
.click();
}

findNodeResourceDefaultErrorMessage() {
return this.find().findByTestId('node-resource-size-default-error');
}

findNodeResourceMinInput() {
return this.find().findByTestId('node-resource-size-minimum-allowed').findByLabelText('Input');
}

findNodeResourceMinErrorMessage() {
return this.find().findByTestId('node-resource-size-minimum-allowed-error');
}

findNodeResourceMaxInput() {
return this.find().findByTestId('node-resource-size-maximum-allowed').findByLabelText('Input');
}

findNodeResourceSubmitButton() {
return this.find().findByTestId('modal-submit-button');
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const initIntercepts = ({ isPresent = true }: HandlersProps) => {
description: 'Test description',
identifiers: [
{
displayName: 'RAM',
displayName: 'Memory',
identifier: 'memory',
minCount: '2Gi',
maxCount: '5Gi',
Expand Down Expand Up @@ -77,7 +77,7 @@ describe('Manage Hardware Profile', () => {
createHardwareProfile.findSubmitButton().should('be.disabled');

// test required fields
createHardwareProfile.k8sNameDescription.findDisplayNameInput().fill('test-hardware-profile');
createHardwareProfile.k8sNameDescription.findDisplayNameInput().fill('Test hardware profile');
createHardwareProfile.findSubmitButton().should('be.enabled');

// test resource name validation
Expand All @@ -99,10 +99,33 @@ describe('Manage Hardware Profile', () => {
.clear()
.type('test-hardware-profile-name');
createHardwareProfile.findSubmitButton().should('be.enabled');
createHardwareProfile.k8sNameDescription.findDescriptionInput().fill('Test description');

cy.interceptK8s(
'POST',
{
model: HardwareProfileModel,
ns: 'opendatahub',
name: 'test-hardware-profile',
},
mockHardwareProfile({ name: 'test-hardware-profile', namespace: 'opendatahub' }),
).as('createHardwareProfile');
createHardwareProfile.findSubmitButton().click();

cy.wait('@createHardwareProfile').then((interception) => {
expect(interception.request.body.spec.displayName).to.be.eql('Test hardware profile');
expect(interception.request.body.spec.description).to.be.eql('Test description');
});
});

it('test node resources section', () => {
initIntercepts({});
createHardwareProfile.visit();
createHardwareProfile.k8sNameDescription.findDisplayNameInput().fill('test-hardware-profile');

// test node resource table
createHardwareProfile.findNodeResourceTable().should('exist');
// verify both CPU and RAM row exists and cannot be deleted
// verify both CPU and Memory rows exist and cannot be deleted
createHardwareProfile
.getNodeResourceTableRow('cpu')
.findDeleteAction()
Expand All @@ -125,23 +148,105 @@ describe('Manage Hardware Profile', () => {
createNodeResourceModal.findNodeResourceSubmitButton().click();
// test that values were added correctly
createHardwareProfile.getNodeResourceTableRow('test-gpu').shouldHaveResourceLabel('Test GPU');

// test edit node resource
// test cannot edit cpu or memory identifier
createHardwareProfile.getNodeResourceTableRow('cpu').findEditAction().click();
editNodeResourceModal.findNodeResourceIdentifierInput().should('be.disabled');
// test default value should be within min and max value
editNodeResourceModal.selectNodeResourceDefaultUnit('Milicores');
editNodeResourceModal.findNodeResourceDefaultErrorMessage().should('exist');
editNodeResourceModal.selectNodeResourceDefaultUnit('Cores');
editNodeResourceModal.findNodeResourceDefaultErrorMessage().should('not.exist');
// test min value should not exceed max value
editNodeResourceModal.findNodeResourceMinInput().type('3');
editNodeResourceModal.findNodeResourceMinErrorMessage().should('exist');
editNodeResourceModal.findNodeResourceMinInput().clear();
editNodeResourceModal.findNodeResourceMinErrorMessage().should('not.exist');
editNodeResourceModal.findCancelButton().click();

createHardwareProfile.getNodeResourceTableRow('memory').findEditAction().click();
editNodeResourceModal.findNodeResourceIdentifierInput().should('be.disabled');
// test default value should be within min and max value
editNodeResourceModal.selectNodeResourceDefaultUnit('MiB');
editNodeResourceModal.findNodeResourceDefaultErrorMessage().should('exist');
editNodeResourceModal.selectNodeResourceDefaultUnit('GiB');
editNodeResourceModal.findNodeResourceDefaultErrorMessage().should('not.exist');
// test min value should not exceed max value
editNodeResourceModal.findNodeResourceMinInput().type('3');
editNodeResourceModal.findNodeResourceMinErrorMessage().should('exist');
editNodeResourceModal.findNodeResourceMinInput().clear();
editNodeResourceModal.findNodeResourceMinErrorMessage().should('not.exist');
editNodeResourceModal.findCancelButton().click();

createHardwareProfile.getNodeResourceTableRow('test-gpu').findEditAction().click();
editNodeResourceModal.findNodeResourceLabelInput().fill('Test GPU Edited');
editNodeResourceModal.findNodeResourceIdentifierInput().fill('test-gpu-edited');
// test default value should be within min and max value
editNodeResourceModal.findNodeResourceDefaultInput().type('3');
editNodeResourceModal.findNodeResourceDefaultErrorMessage().should('exist');
editNodeResourceModal.findNodeResourceSubmitButton().should('be.disabled');
editNodeResourceModal.findNodeResourceDefaultInput().type('{backspace}');
editNodeResourceModal.findNodeResourceDefaultErrorMessage().should('not.exist');
editNodeResourceModal.findNodeResourceSubmitButton().should('be.enabled');
// test min value should not exceed max value
editNodeResourceModal.findNodeResourceMinInput().type('3');
editNodeResourceModal.findNodeResourceMinErrorMessage().should('exist');
editNodeResourceModal.findNodeResourceSubmitButton().should('be.disabled');
editNodeResourceModal.findNodeResourceMinInput().type('{backspace}');
editNodeResourceModal.findNodeResourceMinErrorMessage().should('not.exist');
editNodeResourceModal.findNodeResourceSubmitButton().should('be.enabled');
editNodeResourceModal.findNodeResourceMaxInput().type('3');
editNodeResourceModal.findNodeResourceSubmitButton().click();
createHardwareProfile
.getNodeResourceTableRow('test-gpu-edited')
.shouldHaveResourceLabel('Test GPU Edited')
.shouldHaveResourceIdentifier('test-gpu-edited');

cy.interceptK8s(
'POST',
{
model: HardwareProfileModel,
ns: 'opendatahub',
name: 'test-hardware-profile',
},
mockHardwareProfile({ name: 'test-hardware-profile', namespace: 'opendatahub' }),
).as('createHardwareProfile');
createHardwareProfile.findSubmitButton().click();

cy.wait('@createHardwareProfile').then((interception) => {
expect(interception.request.body.spec.identifiers).to.be.eql([
{
identifier: 'cpu',
displayName: 'CPU',
defaultCount: 2,
maxCount: 4,
minCount: 1,
},
{
identifier: 'memory',
displayName: 'Memory',
defaultCount: '4Gi',
minCount: '2Gi',
maxCount: '8Gi',
},
{
displayName: 'Test GPU Edited',
identifier: 'test-gpu-edited',
minCount: 1,
maxCount: 13,
defaultCount: 1,
},
]);
});
});

it('test node selectors section', () => {
initIntercepts({});
createHardwareProfile.visit();
createHardwareProfile.findSubmitButton().should('be.disabled');
createHardwareProfile.k8sNameDescription.findDisplayNameInput().fill('test-hardware-profile');

// test node selectors empty state
createHardwareProfile.findNodeSelectorTable().should('not.exist');
// open node selector modal
Expand Down Expand Up @@ -182,6 +287,30 @@ describe('Manage Hardware Profile', () => {
// delete the previous one
nodeSelectorTableRow.findDeleteAction().click();

cy.interceptK8s(
'POST',
{
model: HardwareProfileModel,
ns: 'opendatahub',
name: 'test-hardware-profile',
},
mockHardwareProfile({ name: 'test-hardware-profile', namespace: 'opendatahub' }),
).as('createHardwareProfile');
createHardwareProfile.findSubmitButton().click();

cy.wait('@createHardwareProfile').then((interception) => {
expect(interception.request.body.spec.nodeSelectors).to.be.eql([
{ key: 'new-test-node-selector', value: 'new-test-value' },
]);
});
});

it('test tolerations section', () => {
initIntercepts({});
createHardwareProfile.visit();
createHardwareProfile.findSubmitButton().should('be.disabled');
createHardwareProfile.k8sNameDescription.findDisplayNameInput().fill('test-hardware-profile');

// test tolerations empty state
createHardwareProfile.findTolerationTable().should('not.exist');
// open toleration modal
Expand Down Expand Up @@ -253,9 +382,6 @@ describe('Manage Hardware Profile', () => {

cy.wait('@createHardwareProfile').then((interception) => {
expect(interception.request.body.spec.tolerations).to.be.eql([]);
expect(interception.request.body.spec.nodeSelectors).to.be.eql([
{ key: 'new-test-node-selector', value: 'new-test-value' },
]);
});
});

Expand Down Expand Up @@ -310,7 +436,7 @@ describe('Manage Hardware Profile', () => {
expect(interception.request.body.spec).to.eql({
identifiers: [
{
displayName: 'RAM',
displayName: 'Memory',
identifier: 'memory',
minCount: '2Gi',
maxCount: '5Gi',
Expand Down Expand Up @@ -384,7 +510,7 @@ describe('Manage Hardware Profile', () => {
expect(interception.request.body.spec).to.eql({
identifiers: [
{
displayName: 'RAM',
displayName: 'Memory',
identifier: 'memory',
minCount: '2Gi',
maxCount: '5Gi',
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/api/k8s/__tests__/hardwareProfiles.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ const data: HardwareProfileKind['spec'] = {
displayName: 'test',
identifiers: [
{
displayName: 'RAM',
displayName: 'Memory',
identifier: 'memory',
minCount: '2Gi',
maxCount: '5Gi',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@ import * as React from 'react';
import { Navigate, Route, Routes } from 'react-router-dom';
import ManageHardwareProfile from '~/pages/hardwareProfiles/manage/ManageHardwareProfile';
import {
DuplicateHardWareProfile,
EditHardWareProfile,
DuplicateHardwareProfile,
EditHardwareProfile,
} from '~/pages/hardwareProfiles/manage/ManageHardwareProfileWrapper';
import HardwareProfiles from './HardwareProfiles';

const HardwareProfilesRoutes: React.FC = () => (
<Routes>
<Route path="/" element={<HardwareProfiles />} />
<Route path="/create" element={<ManageHardwareProfile />} />
<Route path="/edit/:hardwareProfileName" element={<EditHardWareProfile />} />
<Route path="/duplicate/:hardwareProfileName" element={<DuplicateHardWareProfile />} />
<Route path="/edit/:hardwareProfileName" element={<EditHardwareProfile />} />
<Route path="/duplicate/:hardwareProfileName" element={<DuplicateHardwareProfile />} />
<Route path="*" element={<Navigate to="." />} />
</Routes>
);
Expand Down
12 changes: 6 additions & 6 deletions frontend/src/pages/hardwareProfiles/const.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,16 +85,16 @@ export const DEFAULT_HARDWARE_PROFILE_SPEC: HardwareProfileKind['spec'] = {
{
identifier: 'cpu',
displayName: 'CPU',
defaultCount: 1,
maxCount: 2,
defaultCount: 2,
maxCount: 4,
minCount: 1,
},
{
identifier: 'memory',
displayName: 'RAM',
defaultCount: '1GiB',
minCount: '0.001GiB',
maxCount: '2GiB',
displayName: 'Memory',
defaultCount: '4Gi',
minCount: '2Gi',
maxCount: '8Gi',
},
],
};
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import ManageNodeSelectorSection from '~/pages/hardwareProfiles/manage/ManageNod
import ManageTolerationSection from '~/pages/hardwareProfiles/manage/ManageTolerationSection';
import ManageHardwareProfileFooter from '~/pages/hardwareProfiles/manage/ManageHardwareProfileFooter';
import ManageNodeResourceSection from '~/pages/hardwareProfiles/manage/ManageNodeResourceSection';
import { isNodeResourcesSectionValid } from '~/pages/hardwareProfiles/utils';
import { HardwareProfileFormData, ManageHardwareProfileSectionID } from './types';

type ManageHardwareProfileProps = {
Expand Down Expand Up @@ -69,8 +70,9 @@ const ManageHardwareProfile: React.FC<ManageHardwareProfileProps> = ({
[state, profileNameDesc],
);

// TODO: add valid func
const validFormData = isK8sNameDescriptionDataValid(profileNameDesc);
const validFormData =
isK8sNameDescriptionDataValid(profileNameDesc) &&
isNodeResourcesSectionValid(state.identifiers);

return (
<ApplicationsPage
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,13 @@ const ManageHardwareProfileWrapper: React.FC<ManageHardwareProfileWrapperProps>
return children(data);
};

export const EditHardWareProfile: React.FC = () => (
export const EditHardwareProfile: React.FC = () => (
<ManageHardwareProfileWrapper>
{(data) => <ManageHardwareProfile existingHardwareProfile={data} />}
</ManageHardwareProfileWrapper>
);

export const DuplicateHardWareProfile: React.FC = () => (
export const DuplicateHardwareProfile: React.FC = () => (
<ManageHardwareProfileWrapper>
{(data) => <ManageHardwareProfile duplicatedHardwareProfile={data} />}
</ManageHardwareProfileWrapper>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,12 @@ const CountFormField: React.FC<CountFormFieldProps> = ({
};

return (
<FormGroup label={label} fieldId={fieldId} data-testid={fieldId}>
<FormGroup label={label} fieldId={fieldId} data-testid={`node-resource-size-${fieldId}`}>
{renderInputField()}
{!isValid && errorMessage && (
<FormHelperText>
<HelperText>
<HelperTextItem data-testid={`${fieldId}-error`} variant="error">
<HelperTextItem data-testid={`node-resource-size-${fieldId}-error`} variant="error">
{errorMessage}
</HelperTextItem>
</HelperText>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,8 @@ const ManageNodeResourceModal: React.FC<ManageNodeResourceModalProps> = ({
}
}, [identifier]);

const isValidCounts = unitOptions
? validateDefaultCount(identifier, unitOptions) && validateMinCount(identifier, unitOptions)
: true;
const isValidCounts =
validateDefaultCount(identifier, unitOptions) && validateMinCount(identifier, unitOptions);

const isButtonDisabled =
!identifier.displayName || !identifier.identifier || !isUniqueIdentifier || !isValidCounts;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ const NodeResourceForm: React.FC<NodeResourceFormProps> = ({
identifier={identifier.identifier}
size={identifier.defaultCount}
setSize={(value) => setIdentifier('defaultCount', value)}
isValid={unitOptions ? validateDefaultCount(identifier, unitOptions) : true}
isValid={validateDefaultCount(identifier, unitOptions)}
errorMessage="Default must be equal to or between the minimum and maximum allowed limits."
/>

Expand All @@ -79,7 +79,7 @@ const NodeResourceForm: React.FC<NodeResourceFormProps> = ({
identifier={identifier.identifier}
size={identifier.minCount}
setSize={(value) => setIdentifier('minCount', value)}
isValid={unitOptions ? validateMinCount(identifier, unitOptions) : true}
isValid={validateMinCount(identifier, unitOptions)}
errorMessage="Minimum allowed value cannot exceed the maximum allowed value."
/>

Expand Down
Loading

0 comments on commit 911c403

Please sign in to comment.