Repository: opendatahub-io/odh-dashboard
Pull request #3959: Create mocked cypress tests migration cases for hardware profiles
Author: login: caponetto
Labels: name: lgtm
name: approved
Created: 2025-03-20T13:27:18Z | Merged: 2025-03-25T20:33:30Z

<!--- If this is a non-code change, this template is not required; reference any issues or top-level descriptions as needed -->
<!--- All code change PRs should relate to an issue, reference it here; see example below -->
<!--- https://issues.redhat.com/browse/RHOAIENG-123456 -->

## Description
<!--- Describe your changes in detail; the what, the why, any findings, etc -->
<!--- Include any screenshots of changed UI; Include any gifs if it was a flow / UX change -->

See https://issues.redhat.com/browse/RHOAIENG-21522

## How Has This Been Tested?
<!--- Please describe in detail how you tested your changes. -->
<!--- Include details of your testing environment, and the tests you ran to -->
<!--- see how your change affects other areas of the code, etc. -->
This PR only adds tests.

## Test Impact
<!--- What tests have you done to cover the implemented functionality -->
<!--- If tests are not applicable, explain why here -->
This PR only adds tests.

## Request review criteria:
<!--- This PR will be merged by any repository approver when it meets all the points in the checklist -->
<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->

Self checklist (all need to be checked):
- [x] The developer has manually tested the changes and verified that the changes work
- [x] Testing instructions have been added in the PR body (for PRs involving changes that are not immediately obvious).
- [x] The developer has added tests or explained why testing cannot be added (unit or cypress tests for related changes)

If you have UI changes: 
<!--- You can ignore these if you are doing manifest, backend, internal logic, etc changes; aka non-UI / visual changes -->
- [ ] Included any necessary screenshots or gifs if it was a UI change.
- [ ] Included tags to the UX team if it was a UI/UX change.

After the PR is posted & before it merges:
- [ ] The developer has tested their solution on a cluster by using the image produced by the PR to `main`



Diff:
diff --git a/frontend/src/__tests__/cypress/cypress/pages/components/MigrationModal.ts b/frontend/src/__tests__/cypress/cypress/pages/components/MigrationModal.ts
new file mode 100644
index 0000000000..6ef26f72ea
--- /dev/null
+++ b/frontend/src/__tests__/cypress/cypress/pages/components/MigrationModal.ts
@@ -0,0 +1,14 @@
+import type { ByRoleOptions } from '@testing-library/react';
+import { Modal } from '~/__tests__/cypress/cypress/pages/components/Modal';
+
+export class MigrationModal extends Modal {
+  constructor(title: ByRoleOptions['name'] = /Migrate hardware profile/) {
+    super(title);
+  }
+
+  findSubmitButton(options?: ByRoleOptions | undefined): Cypress.Chainable<JQuery<HTMLElement>> {
+    return this.findFooter().findByRole('button', { name: /Migrate/, hidden: true, ...options });
+  }
+}
+
+export const migrationModal = new MigrationModal();
diff --git a/frontend/src/__tests__/cypress/cypress/pages/hardwareProfile.ts b/frontend/src/__tests__/cypress/cypress/pages/hardwareProfile.ts
index 9553b0a0ea..9c7b51a6c0 100644
--- a/frontend/src/__tests__/cypress/cypress/pages/hardwareProfile.ts
+++ b/frontend/src/__tests__/cypress/cypress/pages/hardwareProfile.ts
@@ -88,6 +88,31 @@ class HardwareProfile {
     return appChrome.findNavItem('Hardware profiles', 'Settings');
   }
 
+  getCell(rowIndex: number, columnIndex: number) {
+    return this.findTable()
+      .children('tbody')
+      .eq(rowIndex)
+      .find('tr')
+      .eq(0)
+      .find('td')
+      .eq(columnIndex);
+  }
+
+  getLabelsFromCell(rowIndex: number, columnIndex: number) {
+    const labels: string[] = [];
+
+    return this.getCell(rowIndex, columnIndex)
+      .find('[data-testid^="label-"]')
+      .each((el) => {
+        labels.push(el.text().trim());
+      })
+      .then(() => labels);
+  }
+
+  getFeatureLabels(rowIndex: number) {
+    return this.getLabelsFromCell(rowIndex, 3);
+  }
+
   private wait() {
     this.findAppPage();
     cy.testA11y();
@@ -105,7 +130,7 @@ class HardwareProfile {
     return new HardwareProfileWarningBanner(() => this.findHardwareProfileDisabledBanner());
   }
 
-  private findTable() {
+  findTable() {
     return cy.findByTestId('hardware-profile-table');
   }
 
@@ -294,6 +319,10 @@ class EditHardwareProfile extends ManageHardwareProfile {
     cy.testA11y();
   }
 
+  findMigrationAlert() {
+    return cy.findByTestId('migration-alert');
+  }
+
   findErrorText() {
     return cy.findByTestId('problem-loading-hardware-profile');
   }
@@ -450,6 +479,20 @@ class NodeResourceModal extends Modal {
   }
 }
 
+class LegacyHardwareProfile extends HardwareProfile {
+  findSection() {
+    return cy.findByTestId('migrated-hardware-profiles-section');
+  }
+
+  findExpandButton() {
+    return this.findSection().findByRole('button');
+  }
+
+  findTable() {
+    return this.findSection().findByTestId('hardware-profile-table');
+  }
+}
+
 export const hardwareProfile = new HardwareProfile();
 export const createHardwareProfile = new CreateHardwareProfile();
 export const createTolerationModal = new TolerationModal(false);
@@ -460,3 +503,4 @@ export const createNodeResourceModal = new NodeResourceModal(false);
 export const editNodeResourceModal = new NodeResourceModal(true);
 export const editHardwareProfile = new EditHardwareProfile();
 export const duplicateHardwareProfile = new DuplicateHardwareProfile();
+export const legacyHardwareProfile = new LegacyHardwareProfile();
diff --git a/frontend/src/__tests__/cypress/cypress/pages/notebookImageSettings.ts b/frontend/src/__tests__/cypress/cypress/pages/notebookImageSettings.ts
index e214780f2e..dbf9f4b4d2 100644
--- a/frontend/src/__tests__/cypress/cypress/pages/notebookImageSettings.ts
+++ b/frontend/src/__tests__/cypress/cypress/pages/notebookImageSettings.ts
@@ -109,6 +109,20 @@ class ImportUpdateNotebookImageModal extends Modal {
     return this.k8sNameDescription.findDescriptionInput();
   }
 
+  findHardwareProfileSelect() {
+    return this.find().findByTestId('hardware-profile-identifier-multiselect');
+  }
+
+  findHardwareProfileSelectOptionValues() {
+    return cy
+      .findAllByRole('option')
+      .then((options) => [...options].map((option) => option.textContent?.trim()));
+  }
+
+  findHardwareProfileSelectOption(option: string) {
+    return cy.findByRole('option', { name: option, hidden: true });
+  }
+
   // Software tab
   findSoftwareTab() {
     return this.find().findByTestId('displayed-content-software-tab');
diff --git a/frontend/src/__tests__/cypress/cypress/pages/notebookServer.ts b/frontend/src/__tests__/cypress/cypress/pages/notebookServer.ts
index b2edee1f08..6e1a3115e1 100644
--- a/frontend/src/__tests__/cypress/cypress/pages/notebookServer.ts
+++ b/frontend/src/__tests__/cypress/cypress/pages/notebookServer.ts
@@ -76,6 +76,17 @@ class NotebookServer {
     return cy.findByTestId('accelerator-profile-select');
   }
 
+  findHardwareProfileSelect() {
+    return cy.findByTestId('hardware-profile-select');
+  }
+
+  findHardwareProfileSelectOptionValues() {
+    return cy.findAllByRole('option').then((options) => {
+      const values = [...options].map((option) => option.textContent?.trim());
+      return cy.wrap(values);
+    });
+  }
+
   findOpenInNewTabButton() {
     return cy.findByRole('button', { name: 'Open in new tab' });
   }
diff --git a/frontend/src/__tests__/cypress/cypress/tests/mocked/applications/notebookServer.cy.ts b/frontend/src/__tests__/cypress/cypress/tests/mocked/applications/notebookServer.cy.ts
index e67c254538..72449a04cb 100644
--- a/frontend/src/__tests__/cypress/cypress/tests/mocked/applications/notebookServer.cy.ts
+++ b/frontend/src/__tests__/cypress/cypress/tests/mocked/applications/notebookServer.cy.ts
@@ -22,7 +22,7 @@ import {
   StorageClassModel,
 } from '~/__tests__/cypress/cypress/utils/models';
 import { mockAcceleratorProfile } from '~/__mocks__/mockAcceleratorProfile';
-import type { EnvironmentVariable } from '~/types';
+import type { EnvironmentVariable, NotebookData } from '~/types';
 import { mockConfigMap } from '~/__mocks__/mockConfigMap';
 
 const groupSubjects: RoleBindingSubject[] = [
@@ -182,6 +182,54 @@ describe('NotebookServer', () => {
     notebookServer.findEnvVarValue(1).should('contain.value', 'test2');
   });
 
+  it('should start notebook server with hardware profile', () => {
+    cy.interceptOdh(
+      'GET /api/config',
+      mockDashboardConfig({
+        disableHardwareProfiles: false,
+      }),
+    );
+
+    notebookServer.visit();
+    notebookServer.findHardwareProfileSelect().click();
+    notebookServer
+      .findHardwareProfileSelectOptionValues()
+      .should('not.satisfy', (arr: string[]) => arr.some((item) => item.includes('Test GPU')));
+    notebookServer
+      .findHardwareProfileSelect()
+      .findSelectOption('Large CPU: Request = 7; Limit = 7; Memory: Request = 56Gi; Limit = 56Gi')
+      .click();
+    notebookServer.findHardwareProfileSelect().should('contain', 'Large');
+    notebookServer.findStartServerButton().should('be.visible');
+    notebookServer.findStartServerButton().click();
+
+    cy.wait('@startNotebookServer').then((interception) => {
+      const { podSpecOptions } = interception.request.body as NotebookData;
+
+      expect(podSpecOptions.selectedAcceleratorProfile).to.eq(undefined);
+      expect(podSpecOptions.selectedHardwareProfile).not.to.eq(undefined);
+      expect(podSpecOptions.selectedHardwareProfile?.spec.displayName).to.eq('Large');
+      expect(podSpecOptions.selectedHardwareProfile?.spec.identifiers).to.eql([
+        {
+          displayName: 'CPU',
+          resourceType: 'CPU',
+          identifier: 'cpu',
+          minCount: '7',
+          maxCount: '14',
+          defaultCount: '7',
+        },
+        {
+          displayName: 'Memory',
+          resourceType: 'Memory',
+          identifier: 'memory',
+          minCount: '56Gi',
+          maxCount: '56Gi',
+          defaultCount: '56Gi',
+        },
+      ]);
+    });
+  });
+
   it('should start notebook server with accelerator profile', () => {
     notebookServer.visit();
     notebookServer.findAcceleratorProfileSelect().click();
diff --git a/frontend/src/__tests__/cypress/cypress/tests/mocked/hardwareProfiles/legacyHardwareProfiles.cy.ts b/frontend/src/__tests__/cypress/cypress/tests/mocked/hardwareProfiles/legacyHardwareProfiles.cy.ts
new file mode 100644
index 0000000000..ba8527abc0
--- /dev/null
+++ b/frontend/src/__tests__/cypress/cypress/tests/mocked/hardwareProfiles/legacyHardwareProfiles.cy.ts
@@ -0,0 +1,896 @@
+import {
+  editHardwareProfile,
+  legacyHardwareProfile,
+} from '~/__tests__/cypress/cypress/pages/hardwareProfile';
+import { mockHardwareProfile } from '~/__mocks__/mockHardwareProfile';
+import {
+  AcceleratorProfileModel,
+  HardwareProfileModel,
+  ODHDashboardConfigModel,
+} from '~/__tests__/cypress/cypress/utils/models';
+import { deleteModal } from '~/__tests__/cypress/cypress/pages/components/DeleteModal';
+import { migrationModal } from '~/__tests__/cypress/cypress/pages/components/MigrationModal';
+import { mock200Status, mockDashboardConfig, mockK8sResourceList } from '~/__mocks__';
+import { be } from '~/__tests__/cypress/cypress/utils/should';
+import { asProductAdminUser } from '~/__tests__/cypress/cypress/utils/mockUsers';
+import { testPagination } from '~/__tests__/cypress/cypress/utils/pagination';
+import { mockAcceleratorProfile } from '~/__mocks__/mockAcceleratorProfile';
+import { TolerationEffect, TolerationOperator } from '~/types';
+
+const initIntercepts = () => {
+  cy.interceptOdh(
+    'GET /api/config',
+    mockDashboardConfig({
+      modelServerSizes: [],
+      notebookSizes: [],
+    }),
+  );
+};
+
+describe('legacy profiles table', () => {
+  const testNamespace = 'opendatahub';
+
+  beforeEach(() => {
+    asProductAdminUser();
+    initIntercepts();
+  });
+  describe('Table', () => {
+    it('should not show the table when there are no items', () => {
+      legacyHardwareProfile.visit();
+      legacyHardwareProfile.findSection().should('not.exist');
+    });
+
+    it('should not show the table when there are no legacy items', () => {
+      cy.interceptK8sList(
+        { model: HardwareProfileModel, ns: testNamespace },
+        mockK8sResourceList([
+          mockHardwareProfile({
+            name: 'test-accelerator-profile',
+            displayName: 'Test Accelerator Profile',
+          }),
+        ]),
+      );
+
+      legacyHardwareProfile.visit();
+      legacyHardwareProfile.findSection().should('not.exist');
+    });
+
+    it('should initially hide the table when there are legacy items', () => {
+      cy.interceptK8sList(
+        { model: AcceleratorProfileModel, ns: testNamespace },
+        mockK8sResourceList([
+          mockAcceleratorProfile({
+            name: 'test-accelerator-profile',
+            displayName: 'Test Accelerator Profile',
+          }),
+        ]),
+      );
+
+      legacyHardwareProfile.visit();
+      legacyHardwareProfile.findSection().should('exist');
+      legacyHardwareProfile.findTable().should('not.be.visible');
+    });
+
+    it('should expand the section to visualize the table', () => {
+      cy.interceptK8sList(
+        { model: AcceleratorProfileModel, ns: testNamespace },
+        mockK8sResourceList([
+          mockAcceleratorProfile({
+            name: 'test-accelerator-profile',
+            displayName: 'Test Accelerator Profile',
+          }),
+        ]),
+      );
+
+      legacyHardwareProfile.visit();
+      legacyHardwareProfile.findTable().should('not.be.visible');
+      legacyHardwareProfile.findExpandButton().click();
+      legacyHardwareProfile.findTable().should('be.visible');
+    });
+
+    it('should show the Source column for legacy items', () => {
+      cy.interceptK8sList(
+        { model: AcceleratorProfileModel, ns: testNamespace },
+        mockK8sResourceList([
+          mockAcceleratorProfile({
+            name: 'test-accelerator-profile',
+            displayName: 'Test Accelerator Profile',
+          }),
+        ]),
+      );
+
+      legacyHardwareProfile.visit();
+      legacyHardwareProfile.findExpandButton().click();
+      legacyHardwareProfile.findTable().contains('th', 'Source').should('exist');
+    });
+
+    it('should show two entries in the table for each Accelerator Profile', () => {
+      const totalItems = 3;
+
+      cy.interceptK8sList(
+        { model: AcceleratorProfileModel, ns: testNamespace },
+        mockK8sResourceList(
+          Array.from({ length: totalItems }, (_, i) =>
+            mockAcceleratorProfile({
+              name: `test-accelerator-profile-${i}`,
+              displayName: `Test Accelerator Profile - ${i}`,
+            }),
+          ),
+        ),
+      );
+
+      legacyHardwareProfile.visit();
+      legacyHardwareProfile.findExpandButton().click();
+      legacyHardwareProfile.findRows().should('have.length', totalItems * 2);
+    });
+
+    it('should filter legacy profiles', () => {
+      const testAcceleratorProfileDisplayName = 'Test Accelerator Profile';
+      const testNotebookSizeProfileName = 'test-notebook-size-profile';
+      const testModelServerSizeProfileName = 'test-model-server-size-profile';
+
+      cy.interceptOdh(
+        'GET /api/config',
+        mockDashboardConfig({
+          modelServerSizes: [
+            {
+              name: testModelServerSizeProfileName,
+              resources: { requests: { cpu: '1', memory: '1Gi' } },
+            },
+          ],
+          notebookSizes: [
+            {
+              name: testNotebookSizeProfileName,
+              resources: { requests: { cpu: '1', memory: '1Gi' } },
+            },
+          ],
+        }),
+      );
+
+      cy.interceptK8sList(
+        { model: AcceleratorProfileModel, ns: testNamespace },
+        mockK8sResourceList([
+          mockAcceleratorProfile({
+            name: 'test-accelerator-profile',
+            displayName: testAcceleratorProfileDisplayName,
+            enabled: false,
+          }),
+        ]),
+      );
+
+      legacyHardwareProfile.visit();
+      legacyHardwareProfile.findExpandButton().click();
+      legacyHardwareProfile.findTable().should('be.visible');
+      legacyHardwareProfile.findRows().should('have.length', 4);
+
+      const tableToolbar = legacyHardwareProfile.getTableToolbar();
+
+      tableToolbar.findSearchInput().type(testAcceleratorProfileDisplayName);
+      legacyHardwareProfile.findRows().should('have.length', 2);
+      legacyHardwareProfile.getRow(testAcceleratorProfileDisplayName).find().should('exist');
+
+      tableToolbar.findFilterInput('name').clear();
+      legacyHardwareProfile.findRows().should('have.length', 4);
+
+      tableToolbar.findFilterMenuOption('filter-toolbar-dropdown', 'Enabled').click();
+      tableToolbar.selectEnableFilter('Enabled');
+      legacyHardwareProfile.findRows().should('have.length', 2);
+      legacyHardwareProfile.getRow(testNotebookSizeProfileName).find().should('exist');
+
+      tableToolbar.selectEnableFilter('Disabled');
+      legacyHardwareProfile.findRows().should('have.length', 2);
+      legacyHardwareProfile.getRow(testAcceleratorProfileDisplayName).find().should('exist');
+
+      tableToolbar.findFilterMenuOption('filter-toolbar-dropdown', 'Name').click();
+      tableToolbar.findFilterInput('name').type('No match');
+      legacyHardwareProfile.findRows().should('have.length', 0);
+
+      legacyHardwareProfile.findClearFiltersButton().click();
+      legacyHardwareProfile.findRows().should('have.length', 4);
+    });
+
+    it('should paginate legacy profiles', () => {
+      const itemCount = 25;
+      const totalItems = itemCount * 2;
+      const testModelServerProfileNamePrefix = 'test-model-server-size-profile';
+      const testNotebookSizeProfileNamePrefix = 'test-notebook-size-profile';
+
+      cy.interceptOdh(
+        'GET /api/config',
+        mockDashboardConfig({
+          modelServerSizes: Array.from({ length: itemCount }, (_, i) => ({
+            name: `${testModelServerProfileNamePrefix}-${i}`,
+            resources: { requests: { cpu: '1', memory: '1Gi' } },
+          })),
+          notebookSizes: Array.from({ length: itemCount }, (_, i) => ({
+            name: `${testNotebookSizeProfileNamePrefix}-${i}`,
+            resources: { requests: { cpu: '1', memory: '1Gi' } },
+          })),
+        }),
+      );
+
+      legacyHardwareProfile.visit();
+      legacyHardwareProfile.findExpandButton().click();
+      legacyHardwareProfile.findTable().should('be.visible');
+
+      testPagination({
+        totalItems,
+        firstElement: `${testModelServerProfileNamePrefix}-0`,
+        paginationVariant: 'top',
+      });
+
+      testPagination({
+        totalItems,
+        firstElement: `${testModelServerProfileNamePrefix}-0`,
+        paginationVariant: 'bottom',
+      });
+
+      legacyHardwareProfile.findTableHeaderButton('Name').click();
+      legacyHardwareProfile.findTableHeaderButton('Name').should(be.sortDescending);
+      legacyHardwareProfile.findTableHeaderButton('Name').click();
+      legacyHardwareProfile.findTableHeaderButton('Name').should(be.sortAscending);
+
+      legacyHardwareProfile.findTableHeaderButton('Last modified').click();
+      legacyHardwareProfile.findTableHeaderButton('Last modified').should(be.sortAscending);
+      legacyHardwareProfile.findTableHeaderButton('Last modified').click();
+      legacyHardwareProfile.findTableHeaderButton('Last modified').should(be.sortDescending);
+    });
+
+    it('should show the expandable section for items', () => {
+      const testModelServerProfileName = 'test-model-server-size-profile';
+      const testAcceleratorProfileDisplayName = 'test-accelerator-profile';
+
+      cy.interceptOdh(
+        'GET /api/config',
+        mockDashboardConfig({
+          modelServerSizes: [
+            {
+              name: testModelServerProfileName,
+              resources: { requests: { cpu: '1', memory: '1Gi' } },
+            },
+          ],
+          notebookSizes: [],
+        }),
+      );
+
+      cy.interceptK8sList(
+        { model: AcceleratorProfileModel, ns: testNamespace },
+        mockK8sResourceList([
+          mockAcceleratorProfile({
+            name: 'test-accelerator-profile',
+            displayName: testAcceleratorProfileDisplayName,
+            enabled: false,
+            tolerations: [
+              {
+                key: 'nvidia.com/gpu',
+                operator: TolerationOperator.EXISTS,
+                effect: TolerationEffect.NO_SCHEDULE,
+              },
+            ],
+          }),
+        ]),
+      );
+
+      legacyHardwareProfile.visit();
+      legacyHardwareProfile.findExpandButton().click();
+      legacyHardwareProfile.findTable().should('be.visible');
+
+      const row1 = legacyHardwareProfile.getRow(testModelServerProfileName);
+      row1.findExpandButton().click();
+      row1.findNodeResourceTable().should('exist');
+      row1.findTolerationTable().should('not.exist');
+
+      const row2 = legacyHardwareProfile.getRow(testAcceleratorProfileDisplayName);
+      row2.findExpandButton().click();
+      row2.findNodeResourceTable().should('exist');
+      row2.findTolerationTable().should('exist');
+    });
+
+    it('should not allow changes in the Enabled button', () => {
+      const testProfileDisplayNameEnabled = 'Test Accelerator Profile - Enabled';
+      const testProfileDisplayNameDisabled = 'Test Accelerator Profile - Disabled';
+
+      cy.interceptK8sList(
+        { model: AcceleratorProfileModel, ns: testNamespace },
+        mockK8sResourceList([
+          mockAcceleratorProfile({
+            name: 'test-accelerator-profile-enabled',
+            displayName: testProfileDisplayNameEnabled,
+            enabled: true,
+          }),
+          mockAcceleratorProfile({
+            name: 'test-accelerator-profile-disabled',
+            displayName: testProfileDisplayNameDisabled,
+            enabled: false,
+          }),
+        ]),
+      );
+
+      legacyHardwareProfile.visit();
+      legacyHardwareProfile.findExpandButton().click();
+      legacyHardwareProfile.findTable().should('be.visible');
+
+      const enabledRow = legacyHardwareProfile.getRow(testProfileDisplayNameEnabled);
+      enabledRow.findEnabled().should('be.checked');
+      enabledRow.findEnabled().should('be.disabled');
+
+      const disabledRow = legacyHardwareProfile.getRow(testProfileDisplayNameDisabled);
+      disabledRow.findEnabled().should('not.be.checked');
+      disabledRow.findEnabled().should('be.disabled');
+    });
+  });
+
+  describe('Accelerator profiles', () => {
+    const testProfileName = 'test-accelerator-profile';
+    const testProfileDisplayName = 'Test Accelerator Profile';
+
+    beforeEach(() => {
+      cy.interceptK8sList(
+        { model: AcceleratorProfileModel, ns: testNamespace },
+        mockK8sResourceList([
+          mockAcceleratorProfile({
+            name: testProfileName,
+            displayName: testProfileDisplayName,
+            namespace: testNamespace,
+          }),
+        ]),
+      );
+    });
+
+    it('should delete', () => {
+      cy.interceptK8s(
+        'DELETE',
+        {
+          model: AcceleratorProfileModel,
+          ns: testNamespace,
+          name: testProfileName,
+        },
+        mock200Status({}),
+      ).as('delete');
+
+      legacyHardwareProfile.visit();
+      legacyHardwareProfile.findExpandButton().click();
+      legacyHardwareProfile.getRow(testProfileDisplayName).findKebabAction('Delete').click();
+
+      deleteModal.findSubmitButton().should('be.disabled');
+      deleteModal.findInput().fill('Delete 3 resources');
+      deleteModal.findSubmitButton().should('be.enabled').click();
+
+      cy.wait('@delete');
+    });
+
+    it('should duplicate', () => {
+      legacyHardwareProfile.visit();
+      legacyHardwareProfile.findExpandButton().click();
+      legacyHardwareProfile.getRow(testProfileDisplayName).findKebabAction('Duplicate').click();
+
+      cy.url().should('include', `/hardwareProfiles/duplicate/${testProfileName}`);
+    });
+
+    it('should edit', () => {
+      legacyHardwareProfile.visit();
+      legacyHardwareProfile.findExpandButton().click();
+      legacyHardwareProfile.getRow(testProfileDisplayName).findKebabAction('Edit').click();
+
+      cy.url().should('include', `/hardwareProfiles/edit/${testProfileName}`);
+
+      editHardwareProfile.findMigrationAlert().should('be.visible');
+    });
+
+    it('should migrate', () => {
+      cy.interceptK8s(
+        'DELETE',
+        {
+          model: AcceleratorProfileModel,
+          ns: testNamespace,
+          name: testProfileName,
+        },
+        mock200Status({}),
+      ).as('deleteSource');
+
+      cy.interceptK8s(
+        'POST',
+        { model: HardwareProfileModel },
+        mockHardwareProfile({
+          name: testProfileName,
+          displayName: testProfileDisplayName,
+        }),
+      ).as('createTarget');
+
+      legacyHardwareProfile.visit();
+      legacyHardwareProfile.findExpandButton().click();
+      legacyHardwareProfile.getRow(testProfileDisplayName).findKebabAction('Migrate').click();
+      migrationModal.findSubmitButton().should('be.enabled').click();
+
+      cy.wait('@deleteSource');
+      cy.wait('@createTarget').then((interception) => {
+        // Assert individually the properties that include random values
+        const { name, annotations } = interception.request.body.metadata;
+        expect(annotations).to.have.property('opendatahub.io/modified-date');
+        expect(name).to.include(testProfileName);
+
+        const actual = Cypress._.omit(
+          interception.request.body,
+          'metadata.annotations["opendatahub.io/modified-date"]',
+          'metadata.name',
+        );
+
+        expect(actual).to.eql({
+          apiVersion: 'dashboard.opendatahub.io/v1alpha1',
+          kind: 'HardwareProfile',
+          metadata: {
+            namespace: 'opendatahub',
+            annotations: {
+              'opendatahub.io/dashboard-feature-visibility': '["workbench"]',
+            },
+          },
+          spec: {
+            displayName: 'Test Accelerator Profile',
+            description: '',
+            enabled: true,
+            tolerations: [
+              { key: 'NotebooksOnlyChange', effect: 'NoSchedule', operator: 'Exists' },
+              { key: 'nvidia.com/gpu', operator: 'Exists', effect: 'NoSchedule' },
+            ],
+            identifiers: [
+              {
+                displayName: 'CPU',
+                resourceType: 'CPU',
+                identifier: 'cpu',
+                minCount: '1',
+                defaultCount: '1',
+              },
+              {
+                displayName: 'Memory',
+                resourceType: 'Memory',
+                identifier: 'memory',
+                minCount: '1Mi',
+                defaultCount: '1Mi',
+              },
+              {
+                identifier: 'nvidia.com/gpu',
+                displayName: 'nvidia.com/gpu',
+                minCount: 0,
+                defaultCount: 1,
+              },
+            ],
+          },
+        });
+      });
+      cy.wait('@deleteSource');
+      cy.wait('@createTarget').then((interception) => {
+        // Assert individually the properties that include random values
+        const { name, annotations } = interception.request.body.metadata;
+        expect(annotations).to.have.property('opendatahub.io/modified-date');
+        expect(name).to.include(testProfileName);
+
+        const actual = Cypress._.omit(
+          interception.request.body,
+          'metadata.annotations["opendatahub.io/modified-date"]',
+          'metadata.name',
+        );
+
+        expect(actual).to.eql({
+          apiVersion: 'dashboard.opendatahub.io/v1alpha1',
+          kind: 'HardwareProfile',
+          metadata: {
+            namespace: 'opendatahub',
+            annotations: {
+              'opendatahub.io/dashboard-feature-visibility': '["workbench"]',
+            },
+          },
+          spec: {
+            displayName: 'Test Accelerator Profile',
+            description: '',
+            enabled: true,
+            tolerations: [
+              { key: 'NotebooksOnlyChange', effect: 'NoSchedule', operator: 'Exists' },
+              { key: 'nvidia.com/gpu', operator: 'Exists', effect: 'NoSchedule' },
+            ],
+            identifiers: [
+              {
+                displayName: 'CPU',
+                resourceType: 'CPU',
+                identifier: 'cpu',
+                minCount: '1',
+                defaultCount: '1',
+              },
+              {
+                displayName: 'Memory',
+                resourceType: 'Memory',
+                identifier: 'memory',
+                minCount: '1Mi',
+                defaultCount: '1Mi',
+              },
+              {
+                identifier: 'nvidia.com/gpu',
+                displayName: 'nvidia.com/gpu',
+                minCount: 0,
+                defaultCount: 1,
+              },
+            ],
+          },
+        });
+      });
+    });
+
+    it('should show correct Visibility labels', () => {
+      legacyHardwareProfile.visit();
+      legacyHardwareProfile.findExpandButton().click();
+
+      legacyHardwareProfile.getFeatureLabels(0).then((labels) => {
+        expect(labels).to.have.members(['Workbenches']);
+      });
+
+      legacyHardwareProfile.getFeatureLabels(1).then((labels) => {
+        expect(labels).to.have.members(['Model serving', 'Data science pipelines']);
+      });
+    });
+
+    it('should show correct Source', () => {
+      legacyHardwareProfile.visit();
+      legacyHardwareProfile.findExpandButton().click();
+
+      legacyHardwareProfile.getCell(0, 2).should('have.text', 'Accelerator profile');
+      legacyHardwareProfile.getCell(1, 2).should('have.text', 'Accelerator profile');
+    });
+  });
+
+  describe('Notebook size profiles', () => {
+    const testProfileName = 'test-notebook-size-profile';
+
+    beforeEach(() => {
+      cy.interceptOdh(
+        'GET /api/config',
+        mockDashboardConfig({
+          modelServerSizes: [],
+          notebookSizes: [
+            {
+              name: testProfileName,
+              resources: { requests: { cpu: '1', memory: '1Gi' } },
+            },
+          ],
+        }),
+      );
+    });
+
+    it('should delete', () => {
+      cy.interceptK8s('PATCH', ODHDashboardConfigModel, mockDashboardConfig({})).as('delete');
+
+      legacyHardwareProfile.visit();
+      legacyHardwareProfile.findExpandButton().click();
+      legacyHardwareProfile.getRow(testProfileName).findKebabAction('Delete').click();
+
+      deleteModal.findSubmitButton().should('be.disabled');
+      deleteModal.findInput().fill('Delete 2 resources');
+      deleteModal.findSubmitButton().should('be.enabled').click();
+
+      cy.wait('@delete');
+    });
+
+    it('should duplicate', () => {
+      legacyHardwareProfile.visit();
+      legacyHardwareProfile.findExpandButton().click();
+      legacyHardwareProfile.getRow(testProfileName).findKebabAction('Duplicate').click();
+
+      cy.url().should('include', `/hardwareProfiles/duplicate/${testProfileName}`);
+    });
+
+    it('should edit', () => {
+      legacyHardwareProfile.visit();
+      legacyHardwareProfile.findExpandButton().click();
+      legacyHardwareProfile.getRow(testProfileName).findKebabAction('Edit').click();
+
+      cy.url().should('include', `/hardwareProfiles/edit/${testProfileName}`);
+
+      editHardwareProfile.findMigrationAlert().should('be.visible');
+    });
+
+    it('should migrate', () => {
+      cy.interceptK8s('PATCH', ODHDashboardConfigModel, mockDashboardConfig({})).as('deleteSource');
+
+      cy.interceptK8s(
+        'POST',
+        { model: HardwareProfileModel },
+        mockHardwareProfile({
+          name: testProfileName,
+          displayName: testProfileName,
+        }),
+      ).as('createTarget');
+
+      legacyHardwareProfile.visit();
+      legacyHardwareProfile.findExpandButton().click();
+      legacyHardwareProfile.getRow(testProfileName).findKebabAction('Migrate').click();
+      migrationModal.findSubmitButton().should('be.enabled').click();
+
+      cy.wait('@deleteSource');
+      cy.wait('@createTarget').then((interception) => {
+        // Assert individually the properties that include random values
+        const { name, annotations } = interception.request.body.metadata;
+        expect(annotations).to.have.property('opendatahub.io/modified-date');
+        expect(name).to.include(`${testProfileName}-notebooks`);
+
+        const actual = Cypress._.omit(
+          interception.request.body,
+          'metadata.annotations["opendatahub.io/modified-date"]',
+          'metadata.name',
+        );
+
+        expect(actual).to.eql({
+          apiVersion: 'dashboard.opendatahub.io/v1alpha1',
+          kind: 'HardwareProfile',
+          metadata: {
+            namespace: 'opendatahub',
+            annotations: {
+              'opendatahub.io/dashboard-feature-visibility': '["workbench"]',
+            },
+          },
+          spec: {
+            displayName: 'test-notebook-size-profile',
+            enabled: true,
+            identifiers: [
+              {
+                displayName: 'CPU',
+                resourceType: 'CPU',
+                identifier: 'cpu',
+                minCount: '1',
+                maxCount: '1',
+                defaultCount: '1',
+              },
+              {
+                displayName: 'Memory',
+                resourceType: 'Memory',
+                identifier: 'memory',
+                minCount: '1Gi',
+                maxCount: '1Gi',
+                defaultCount: '1Gi',
+              },
+            ],
+            tolerations: [{ key: 'NotebooksOnlyChange', effect: 'NoSchedule', operator: 'Exists' }],
+          },
+        });
+      });
+      cy.wait('@deleteSource');
+      cy.wait('@createTarget').then((interception) => {
+        // Assert individually the properties that include random values
+        const { name, annotations } = interception.request.body.metadata;
+        expect(annotations).to.have.property('opendatahub.io/modified-date');
+        expect(name).to.include(`${testProfileName}-notebooks`);
+
+        const actual = Cypress._.omit(
+          interception.request.body,
+          'metadata.annotations["opendatahub.io/modified-date"]',
+          'metadata.name',
+        );
+
+        expect(actual).to.eql({
+          apiVersion: 'dashboard.opendatahub.io/v1alpha1',
+          kind: 'HardwareProfile',
+          metadata: {
+            namespace: 'opendatahub',
+            annotations: {
+              'opendatahub.io/dashboard-feature-visibility': '["workbench"]',
+            },
+          },
+          spec: {
+            displayName: testProfileName,
+            enabled: true,
+            identifiers: [
+              {
+                displayName: 'CPU',
+                resourceType: 'CPU',
+                identifier: 'cpu',
+                minCount: '1',
+                maxCount: '1',
+                defaultCount: '1',
+              },
+              {
+                displayName: 'Memory',
+                resourceType: 'Memory',
+                identifier: 'memory',
+                minCount: '1Gi',
+                maxCount: '1Gi',
+                defaultCount: '1Gi',
+              },
+            ],
+            tolerations: [{ key: 'NotebooksOnlyChange', effect: 'NoSchedule', operator: 'Exists' }],
+          },
+        });
+      });
+    });
+
+    it('should show correct Visibility labels', () => {
+      legacyHardwareProfile.visit();
+      legacyHardwareProfile.findExpandButton().click();
+
+      legacyHardwareProfile.getFeatureLabels(0).then((labels) => {
+        expect(labels).to.have.members(['Workbenches']);
+      });
+    });
+
+    it('should show correct Source', () => {
+      legacyHardwareProfile.visit();
+      legacyHardwareProfile.findExpandButton().click();
+
+      legacyHardwareProfile.getCell(0, 2).should('have.text', 'Workbench container size');
+    });
+  });
+
+  describe('Model server size profiles', () => {
+    const testProfileName = 'test-model-server-size-profile';
+
+    beforeEach(() => {
+      cy.interceptOdh(
+        'GET /api/config',
+        mockDashboardConfig({
+          modelServerSizes: [
+            {
+              name: testProfileName,
+              resources: { requests: { cpu: '1', memory: '1Gi' } },
+            },
+          ],
+          notebookSizes: [],
+        }),
+      );
+    });
+
+    it('should delete', () => {
+      cy.interceptK8s('PATCH', ODHDashboardConfigModel, mockDashboardConfig({})).as('delete');
+
+      legacyHardwareProfile.visit();
+      legacyHardwareProfile.findExpandButton().click();
+      legacyHardwareProfile.getRow(testProfileName).findKebabAction('Delete').click();
+
+      deleteModal.findSubmitButton().should('be.disabled');
+      deleteModal.findInput().fill('Delete 2 resources');
+      deleteModal.findSubmitButton().should('be.enabled').click();
+
+      cy.wait('@delete');
+    });
+
+    it('should duplicate', () => {
+      legacyHardwareProfile.visit();
+      legacyHardwareProfile.findExpandButton().click();
+      legacyHardwareProfile.getRow(testProfileName).findKebabAction('Duplicate').click();
+
+      cy.url().should('include', `/hardwareProfiles/duplicate/${testProfileName}`);
+    });
+
+    it('should edit', () => {
+      legacyHardwareProfile.visit();
+      legacyHardwareProfile.findExpandButton().click();
+      legacyHardwareProfile.getRow(testProfileName).findKebabAction('Edit').click();
+
+      cy.url().should('include', `/hardwareProfiles/edit/${testProfileName}`);
+
+      editHardwareProfile.findMigrationAlert().should('be.visible');
+    });
+
+    it('should migrate', () => {
+      cy.interceptK8s('PATCH', ODHDashboardConfigModel, mockDashboardConfig({})).as('deleteSource');
+
+      cy.interceptK8s(
+        'POST',
+        { model: HardwareProfileModel },
+        mockHardwareProfile({
+          name: testProfileName,
+          displayName: testProfileName,
+        }),
+      ).as('createTarget');
+
+      legacyHardwareProfile.visit();
+      legacyHardwareProfile.findExpandButton().click();
+      legacyHardwareProfile.getRow(testProfileName).findKebabAction('Migrate').click();
+      migrationModal.findSubmitButton().should('be.enabled').click();
+
+      cy.wait('@deleteSource');
+      cy.wait('@createTarget').then((interception) => {
+        // Assert individually the properties that include random values
+        const { name, annotations } = interception.request.body.metadata;
+        expect(annotations).to.have.property('opendatahub.io/modified-date');
+        expect(name).to.include(`${testProfileName}-serving`);
+
+        const actual = Cypress._.omit(
+          interception.request.body,
+          'metadata.annotations["opendatahub.io/modified-date"]',
+          'metadata.name',
+        );
+
+        expect(actual).to.eql({
+          apiVersion: 'dashboard.opendatahub.io/v1alpha1',
+          kind: 'HardwareProfile',
+          metadata: {
+            namespace: 'opendatahub',
+            annotations: {
+              'opendatahub.io/dashboard-feature-visibility': '["model-serving"]',
+            },
+          },
+          spec: {
+            displayName: 'test-model-server-size-profile',
+            enabled: true,
+            identifiers: [
+              {
+                displayName: 'CPU',
+                resourceType: 'CPU',
+                identifier: 'cpu',
+                minCount: '1',
+                maxCount: '1',
+                defaultCount: '1',
+              },
+              {
+                displayName: 'Memory',
+                resourceType: 'Memory',
+                identifier: 'memory',
+                minCount: '1Gi',
+                maxCount: '1Gi',
+                defaultCount: '1Gi',
+              },
+            ],
+          },
+        });
+      });
+      cy.wait('@deleteSource');
+      cy.wait('@createTarget').then((interception) => {
+        // Assert individually the properties that include random values
+        const { name, annotations } = interception.request.body.metadata;
+        expect(annotations).to.have.property('opendatahub.io/modified-date');
+        expect(name).to.include(`${testProfileName}-serving`);
+
+        const actual = Cypress._.omit(
+          interception.request.body,
+          'metadata.annotations["opendatahub.io/modified-date"]',
+          'metadata.name',
+        );
+
+        expect(actual).to.eql({
+          apiVersion: 'dashboard.opendatahub.io/v1alpha1',
+          kind: 'HardwareProfile',
+          metadata: {
+            namespace: 'opendatahub',
+            annotations: {
+              'opendatahub.io/dashboard-feature-visibility': '["model-serving"]',
+            },
+          },
+          spec: {
+            displayName: 'test-model-server-size-profile',
+            enabled: true,
+            identifiers: [
+              {
+                displayName: 'CPU',
+                resourceType: 'CPU',
+                identifier: 'cpu',
+                minCount: '1',
+                maxCount: '1',
+                defaultCount: '1',
+              },
+              {
+                displayName: 'Memory',
+                resourceType: 'Memory',
+                identifier: 'memory',
+                minCount: '1Gi',
+                maxCount: '1Gi',
+                defaultCount: '1Gi',
+              },
+            ],
+          },
+        });
+      });
+    });
+
+    it('should show correct Visibility labels', () => {
+      legacyHardwareProfile.visit();
+      legacyHardwareProfile.findExpandButton().click();
+
+      legacyHardwareProfile.getFeatureLabels(0).then((labels) => {
+        expect(labels).to.have.members(['Model serving']);
+      });
+    });
+
+    it('should show correct Source', () => {
+      legacyHardwareProfile.visit();
+      legacyHardwareProfile.findExpandButton().click();
+
+      legacyHardwareProfile.getCell(0, 2).should('have.text', 'Model serving container size');
+    });
+  });
+});
diff --git a/frontend/src/__tests__/cypress/cypress/tests/mocked/notebookImageSettings/notebookImageSettings.cy.ts b/frontend/src/__tests__/cypress/cypress/tests/mocked/notebookImageSettings/notebookImageSettings.cy.ts
index 929b91bcd8..9c76a15b35 100644
--- a/frontend/src/__tests__/cypress/cypress/tests/mocked/notebookImageSettings/notebookImageSettings.cy.ts
+++ b/frontend/src/__tests__/cypress/cypress/tests/mocked/notebookImageSettings/notebookImageSettings.cy.ts
@@ -14,6 +14,15 @@ import {
   asProjectAdminUser,
 } from '~/__tests__/cypress/cypress/utils/mockUsers';
 import { testPagination } from '~/__tests__/cypress/cypress/utils/pagination';
+import {
+  AcceleratorProfileModel,
+  HardwareProfileModel,
+} from '~/__tests__/cypress/cypress/utils/models';
+import { mockDashboardConfig, mockK8sResourceList } from '~/__mocks__';
+import { mockHardwareProfile } from '~/__mocks__/mockHardwareProfile';
+import { mockAcceleratorProfile } from '~/__mocks__/mockAcceleratorProfile';
+import { IdentifierResourceType } from '~/types';
+import { HardwareProfileFeatureVisibility } from '~/k8sTypes';
 
 it('Notebook image settings should not be available for non product admins', () => {
   asProjectAdminUser();
@@ -189,12 +198,89 @@ describe('Notebook image settings', () => {
 
   it('Edit form fields match', () => {
     cy.interceptOdh('GET /api/images/byon', mockByon([{ url: 'test-image:latest' }]));
+    cy.interceptOdh(
+      'GET /api/config',
+      mockDashboardConfig({
+        disableHardwareProfiles: false,
+      }),
+    );
     cy.interceptOdh(
       'PUT /api/images/:image',
       { path: { image: 'byon-123' } },
       { success: true },
     ).as('editNotebookImage');
 
+    cy.interceptK8sList(
+      { model: AcceleratorProfileModel, ns: 'opendatahub' },
+      mockK8sResourceList([
+        mockAcceleratorProfile({
+          name: 'test-accelerator-profile',
+          displayName: 'Test Accelerator Profile',
+          identifier: 'test-accelerator-profile',
+        }),
+      ]),
+    );
+
+    cy.interceptK8sList(
+      { model: HardwareProfileModel, ns: 'opendatahub' },
+      mockK8sResourceList([
+        mockHardwareProfile({
+          name: 'test-hardware-profile-visible',
+          displayName: 'Test Hardware Profile Visible',
+          annotations: {
+            'opendatahub.io/dashboard-feature-visibility': JSON.stringify([
+              HardwareProfileFeatureVisibility.WORKBENCH,
+            ]),
+          },
+          identifiers: [
+            {
+              displayName: 'hwp1',
+              identifier: 'hwp1',
+              minCount: '2Gi',
+              maxCount: '5Gi',
+              defaultCount: '2Gi',
+              resourceType: IdentifierResourceType.MEMORY,
+            },
+            {
+              displayName: 'hwp2',
+              identifier: 'hwp2',
+              minCount: '1',
+              maxCount: '2',
+              defaultCount: '1',
+              resourceType: IdentifierResourceType.CPU,
+            },
+          ],
+        }),
+        mockHardwareProfile({
+          name: 'test-hardware-profile-not-visible',
+          displayName: 'Test Hardware Profile Not Visible',
+          annotations: {
+            'opendatahub.io/dashboard-feature-visibility': JSON.stringify([
+              HardwareProfileFeatureVisibility.PIPELINES,
+            ]),
+          },
+          identifiers: [
+            {
+              displayName: 'hwp3',
+              identifier: 'hwp3',
+              minCount: '2Gi',
+              maxCount: '5Gi',
+              defaultCount: '2Gi',
+              resourceType: IdentifierResourceType.MEMORY,
+            },
+            {
+              displayName: 'hwp4',
+              identifier: 'hwp4',
+              minCount: '1',
+              maxCount: '2',
+              defaultCount: '1',
+              resourceType: IdentifierResourceType.CPU,
+            },
+          ],
+        }),
+      ]),
+    );
+
     notebookImageSettings.visit();
 
     // open edit form
@@ -205,6 +291,15 @@ describe('Notebook image settings', () => {
     updateNotebookImageModal.findNameInput().should('have.value', 'Testing Custom Image');
     updateNotebookImageModal.findDescriptionInput().should('have.value', 'A custom notebook image');
 
+    // test hardware profile
+    updateNotebookImageModal.findHardwareProfileSelect().click();
+    updateNotebookImageModal
+      .findHardwareProfileSelectOptionValues()
+      .should('deep.equal', ['hwp1', 'hwp2']);
+    updateNotebookImageModal.findHardwareProfileSelectOption('hwp1').click();
+    updateNotebookImageModal.findHardwareProfileSelectOption('hwp2').click();
+    updateNotebookImageModal.findHardwareProfileSelect().click();
+
     // test software and packages have correct values
     let notebookImageTabRow = importNotebookImageModal.getSoftwareRow('test-software', 0);
     notebookImageTabRow.shouldHaveVersionColumn('2.0');
@@ -226,7 +321,7 @@ describe('Notebook image settings', () => {
         /* eslint-disable-next-line camelcase */
         display_name: 'Updated custom image',
         description: 'A custom notebook image',
-        recommendedAcceleratorIdentifiers: [],
+        recommendedAcceleratorIdentifiers: ['hwp1', 'hwp2'],
         packages: [{ name: 'test-package', version: '1.0', visible: true }],
         software: [{ name: 'test-software', version: '2.0', visible: true }],
       });
diff --git a/frontend/src/pages/BYONImages/BYONImageModal/HardwareProfileIdentifierMultiselect.tsx b/frontend/src/pages/BYONImages/BYONImageModal/HardwareProfileIdentifierMultiselect.tsx
index 1c0954df11..07a944eedb 100644
--- a/frontend/src/pages/BYONImages/BYONImageModal/HardwareProfileIdentifierMultiselect.tsx
+++ b/frontend/src/pages/BYONImages/BYONImageModal/HardwareProfileIdentifierMultiselect.tsx
@@ -40,6 +40,7 @@ export const HardwareProfileIdentifierMultiselect: React.FC<
       }))}
       setValue={(newState) => setData(newState.filter((n) => n.selected).map((n) => String(n.id)))}
       placeholder="Example, nvidia.com/gpu"
+      toggleTestId="hardware-profile-identifier-multiselect"
       isCreatable
       createOptionMessage={(newValue) => `Create new option "${newValue}"`}
     />
diff --git a/frontend/src/pages/hardwareProfiles/HardwareProfiles.tsx b/frontend/src/pages/hardwareProfiles/HardwareProfiles.tsx
index 8f4cae0832..0db6d72666 100644
--- a/frontend/src/pages/hardwareProfiles/HardwareProfiles.tsx
+++ b/frontend/src/pages/hardwareProfiles/HardwareProfiles.tsx
@@ -147,6 +147,7 @@ const HardwareProfiles: React.FC = () => {
             </StackItem>
             <StackItem>
               <ExpandableSection
+                data-testid="migrated-hardware-profiles-section"
                 toggleTextExpanded={`Hide legacy profiles (${migratedHardwareProfiles.length})`}
                 toggleTextCollapsed={`Show legacy profiles (${migratedHardwareProfiles.length})`}
               >
diff --git a/frontend/src/pages/hardwareProfiles/HardwareProfilesTableRow.tsx b/frontend/src/pages/hardwareProfiles/HardwareProfilesTableRow.tsx
index 88bd490c9c..e5982dcd2a 100644
--- a/frontend/src/pages/hardwareProfiles/HardwareProfilesTableRow.tsx
+++ b/frontend/src/pages/hardwareProfiles/HardwareProfilesTableRow.tsx
@@ -135,7 +135,9 @@ const HardwareProfilesTableRow: React.FC<HardwareProfilesTableRowProps> = ({
           ) : (
             <LabelGroup>
               {useCases.map((v) => (
-                <Label key={v}>{HardwareProfileFeatureVisibilityTitles[v]}</Label>
+                <Label key={v} data-testid={`label-${v}`}>
+                  {HardwareProfileFeatureVisibilityTitles[v]}
+                </Label>
               ))}
             </LabelGroup>
           )}
diff --git a/frontend/src/pages/hardwareProfiles/manage/ManageHardwareProfileFooter.tsx b/frontend/src/pages/hardwareProfiles/manage/ManageHardwareProfileFooter.tsx
index 80f939b284..a9bba93af3 100644
--- a/frontend/src/pages/hardwareProfiles/manage/ManageHardwareProfileFooter.tsx
+++ b/frontend/src/pages/hardwareProfiles/manage/ManageHardwareProfileFooter.tsx
@@ -133,6 +133,7 @@ const ManageHardwareProfileFooter: React.FC<ManageHardwareProfileFooterProps> =
               isExpandable
               isInline
               variant="warning"
+              data-testid="migration-alert"
               title="Updating this profile will trigger migration"
             >
               You are editing a legacy hardware profile that was created from the{' '}


Reviews:
user: login: pnaik1
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-21T06:09:42Z
user: login: caponetto
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-21T12:16:41Z
user: login: Gkrumbach07
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-25T20:07:02Z

Review comments:
diff_hunk: @@ -0,0 +1,612 @@
+import {
+  editHardwareProfile,
+  legacyHardwareProfile,
+} from '~/__tests__/cypress/cypress/pages/hardwareProfile';
+import { mockHardwareProfile } from '~/__mocks__/mockHardwareProfile';
+import {
+  AcceleratorProfileModel,
+  HardwareProfileModel,
+  ODHDashboardConfigModel,
+} from '~/__tests__/cypress/cypress/utils/models';
+import { deleteModal } from '~/__tests__/cypress/cypress/pages/components/DeleteModal';
+import { migrationModal } from '~/__tests__/cypress/cypress/pages/components/MigrationModal';
+import { mock200Status, mockDashboardConfig, mockK8sResourceList } from '~/__mocks__';
+import { be } from '~/__tests__/cypress/cypress/utils/should';
+import { asProductAdminUser } from '~/__tests__/cypress/cypress/utils/mockUsers';
+import { testPagination } from '~/__tests__/cypress/cypress/utils/pagination';
+import { mockAcceleratorProfile } from '~/__mocks__/mockAcceleratorProfile';
+import { TolerationEffect, TolerationOperator } from '~/types';
+
+const initIntercepts = () => {
+  cy.interceptOdh(
+    'GET /api/config',
+    mockDashboardConfig({
+      modelServerSizes: [],
+      notebookSizes: [],
+    }),
+  );
+};
+
+describe('legacy profiles table', () => {
+  const testNamespace = 'opendatahub';
+
+  beforeEach(() => {
+    asProductAdminUser();
+    initIntercepts();
+  });
+  describe('Table', () => {
+    it('should not show the table when there are no items', () => {
+      legacyHardwareProfile.visit();
+      legacyHardwareProfile.findSection().should('not.exist');
+    });
+
+    it('should not show the table when there are no legacy items', () => {
+      cy.interceptK8sList(
+        { model: HardwareProfileModel, ns: testNamespace },
+        mockK8sResourceList([
+          mockHardwareProfile({
+            name: 'test-accelerator-profile',
+            displayName: 'Test Accelerator Profile',
+          }),
+        ]),
+      );
+
+      legacyHardwareProfile.visit();
+      legacyHardwareProfile.findSection().should('not.exist');
+    });
+
+    it('should initially hide the table when there are legacy items', () => {
+      cy.interceptK8sList(
+        { model: AcceleratorProfileModel, ns: testNamespace },
+        mockK8sResourceList([
+          mockAcceleratorProfile({
+            name: 'test-accelerator-profile',
+            displayName: 'Test Accelerator Profile',
+          }),
+        ]),
+      );
+
+      legacyHardwareProfile.visit();
+      legacyHardwareProfile.findSection().should('exist');
+      legacyHardwareProfile.findTable().should('not.be.visible');
+    });
+
+    it('should expand the section to visualize the table', () => {
+      cy.interceptK8sList(
+        { model: AcceleratorProfileModel, ns: testNamespace },
+        mockK8sResourceList([
+          mockAcceleratorProfile({
+            name: 'test-accelerator-profile',
+            displayName: 'Test Accelerator Profile',
+          }),
+        ]),
+      );
+
+      legacyHardwareProfile.visit();
+      legacyHardwareProfile.findTable().should('not.be.visible');
+      legacyHardwareProfile.findExpandButton().click();
+      legacyHardwareProfile.findTable().should('be.visible');
+    });
+
+    it('should show the Source column for legacy items', () => {
+      cy.interceptK8sList(
+        { model: AcceleratorProfileModel, ns: testNamespace },
+        mockK8sResourceList([
+          mockAcceleratorProfile({
+            name: 'test-accelerator-profile',
+            displayName: 'Test Accelerator Profile',
+          }),
+        ]),
+      );
+
+      legacyHardwareProfile.visit();
+      legacyHardwareProfile.findExpandButton().click();
+      legacyHardwareProfile.findTable().contains('th', 'Source').should('exist');
+    });
+
+    it('should show two entries in the table for each Accelerator Profile', () => {
+      const totalItems = 3;
+
+      cy.interceptK8sList(
+        { model: AcceleratorProfileModel, ns: testNamespace },
+        mockK8sResourceList(
+          Array.from({ length: totalItems }, (_, i) =>
+            mockAcceleratorProfile({
+              name: `test-accelerator-profile-${i}`,
+              displayName: `Test Accelerator Profile - ${i}`,
+            }),
+          ),
+        ),
+      );
+
+      legacyHardwareProfile.visit();
+      legacyHardwareProfile.findExpandButton().click();
+      legacyHardwareProfile.findRows().should('have.length', totalItems * 2);
+    });
+
+    it('should filter legacy profiles', () => {
+      const testAcceleratorProfileDisplayName = 'Test Accelerator Profile';
+      const testNotebookSizeProfileName = 'test-notebook-size-profile';
+      const testModelServerSizeProfileName = 'test-model-server-size-profile';
+
+      cy.interceptOdh(
+        'GET /api/config',
+        mockDashboardConfig({
+          modelServerSizes: [
+            {
+              name: testModelServerSizeProfileName,
+              resources: { requests: { cpu: '1', memory: '1Gi' } },
+            },
+          ],
+          notebookSizes: [
+            {
+              name: testNotebookSizeProfileName,
+              resources: { requests: { cpu: '1', memory: '1Gi' } },
+            },
+          ],
+        }),
+      );
+
+      cy.interceptK8sList(
+        { model: AcceleratorProfileModel, ns: testNamespace },
+        mockK8sResourceList([
+          mockAcceleratorProfile({
+            name: 'test-accelerator-profile',
+            displayName: testAcceleratorProfileDisplayName,
+            enabled: false,
+          }),
+        ]),
+      );
+
+      legacyHardwareProfile.visit();
+      legacyHardwareProfile.findExpandButton().click();
+      legacyHardwareProfile.findTable().should('be.visible');
+      legacyHardwareProfile.findRows().should('have.length', 4);
+
+      const tableToolbar = legacyHardwareProfile.getTableToolbar();
+
+      tableToolbar.findSearchInput().type(testAcceleratorProfileDisplayName);
+      legacyHardwareProfile.findRows().should('have.length', 2);
+      legacyHardwareProfile.getRow(testAcceleratorProfileDisplayName).find().should('exist');
+
+      tableToolbar.findFilterInput('name').clear();
+      legacyHardwareProfile.findRows().should('have.length', 4);
+
+      tableToolbar.findFilterMenuOption('filter-toolbar-dropdown', 'Enabled').click();
+      tableToolbar.selectEnableFilter('Enabled');
+      legacyHardwareProfile.findRows().should('have.length', 2);
+      legacyHardwareProfile.getRow(testNotebookSizeProfileName).find().should('exist');
+
+      tableToolbar.selectEnableFilter('Disabled');
+      legacyHardwareProfile.findRows().should('have.length', 2);
+      legacyHardwareProfile.getRow(testAcceleratorProfileDisplayName).find().should('exist');
+
+      tableToolbar.findFilterMenuOption('filter-toolbar-dropdown', 'Name').click();
+      tableToolbar.findFilterInput('name').type('No match');
+      legacyHardwareProfile.findRows().should('have.length', 0);
+
+      legacyHardwareProfile.findClearFiltersButton().click();
+      legacyHardwareProfile.findRows().should('have.length', 4);
+    });
+
+    it('should paginate legacy profiles', () => {
+      const itemCount = 25;
+      const totalItems = itemCount * 2;
+      const testModelServerProfileNamePrefix = 'test-model-server-size-profile';
+      const testNotebookSizeProfileNamePrefix = 'test-notebook-size-profile';
+
+      cy.interceptOdh(
+        'GET /api/config',
+        mockDashboardConfig({
+          modelServerSizes: Array.from({ length: itemCount }, (_, i) => ({
+            name: `${testModelServerProfileNamePrefix}-${i}`,
+            resources: { requests: { cpu: '1', memory: '1Gi' } },
+          })),
+          notebookSizes: Array.from({ length: itemCount }, (_, i) => ({
+            name: `${testNotebookSizeProfileNamePrefix}-${i}`,
+            resources: { requests: { cpu: '1', memory: '1Gi' } },
+          })),
+        }),
+      );
+
+      legacyHardwareProfile.visit();
+      legacyHardwareProfile.findExpandButton().click();
+      legacyHardwareProfile.findTable().should('be.visible');
+
+      testPagination({
+        totalItems,
+        firstElement: `${testModelServerProfileNamePrefix}-0`,
+        paginationVariant: 'top',
+      });
+
+      testPagination({
+        totalItems,
+        firstElement: `${testModelServerProfileNamePrefix}-0`,
+        paginationVariant: 'bottom',
+      });
+
+      legacyHardwareProfile.findTableHeaderButton('Name').click();
+      legacyHardwareProfile.findTableHeaderButton('Name').should(be.sortDescending);
+      legacyHardwareProfile.findTableHeaderButton('Name').click();
+      legacyHardwareProfile.findTableHeaderButton('Name').should(be.sortAscending);
+
+      legacyHardwareProfile.findTableHeaderButton('Last modified').click();
+      legacyHardwareProfile.findTableHeaderButton('Last modified').should(be.sortAscending);
+      legacyHardwareProfile.findTableHeaderButton('Last modified').click();
+      legacyHardwareProfile.findTableHeaderButton('Last modified').should(be.sortDescending);
+    });
+
+    it('should show the expandable section for items', () => {
+      const testModelServerProfileName = 'test-model-server-size-profile';
+      const testAcceleratorProfileDisplayName = 'test-accelerator-profile';
+
+      cy.interceptOdh(
+        'GET /api/config',
+        mockDashboardConfig({
+          modelServerSizes: [
+            {
+              name: testModelServerProfileName,
+              resources: { requests: { cpu: '1', memory: '1Gi' } },
+            },
+          ],
+          notebookSizes: [],
+        }),
+      );
+
+      cy.interceptK8sList(
+        { model: AcceleratorProfileModel, ns: testNamespace },
+        mockK8sResourceList([
+          mockAcceleratorProfile({
+            name: 'test-accelerator-profile',
+            displayName: testAcceleratorProfileDisplayName,
+            enabled: false,
+            tolerations: [
+              {
+                key: 'nvidia.com/gpu',
+                operator: TolerationOperator.EXISTS,
+                effect: TolerationEffect.NO_SCHEDULE,
+              },
+            ],
+          }),
+        ]),
+      );
+
+      legacyHardwareProfile.visit();
+      legacyHardwareProfile.findExpandButton().click();
+      legacyHardwareProfile.findTable().should('be.visible');
+
+      const row1 = legacyHardwareProfile.getRow(testModelServerProfileName);
+      row1.findExpandButton().click();
+      row1.findNodeResourceTable().should('exist');
+      row1.findTolerationTable().should('not.exist');
+
+      const row2 = legacyHardwareProfile.getRow(testAcceleratorProfileDisplayName);
+      row2.findExpandButton().click();
+      row2.findNodeResourceTable().should('exist');
+      row2.findTolerationTable().should('exist');
+    });
+
+    it('should not allow changes in the Enabled button', () => {
+      const testProfileDisplayNameEnabled = 'Test Accelerator Profile - Enabled';
+      const testProfileDisplayNameDisabled = 'Test Accelerator Profile - Disabled';
+
+      cy.interceptK8sList(
+        { model: AcceleratorProfileModel, ns: testNamespace },
+        mockK8sResourceList([
+          mockAcceleratorProfile({
+            name: 'test-accelerator-profile-enabled',
+            displayName: testProfileDisplayNameEnabled,
+            enabled: true,
+          }),
+          mockAcceleratorProfile({
+            name: 'test-accelerator-profile-disabled',
+            displayName: testProfileDisplayNameDisabled,
+            enabled: false,
+          }),
+        ]),
+      );
+
+      legacyHardwareProfile.visit();
+      legacyHardwareProfile.findExpandButton().click();
+      legacyHardwareProfile.findTable().should('be.visible');
+
+      const enabledRow = legacyHardwareProfile.getRow(testProfileDisplayNameEnabled);
+      enabledRow.findEnabled().should('be.checked');
+      enabledRow.findEnabled().should('be.disabled');
+
+      const disabledRow = legacyHardwareProfile.getRow(testProfileDisplayNameDisabled);
+      disabledRow.findEnabled().should('not.be.checked');
+      disabledRow.findEnabled().should('be.disabled');
+    });
+  });
+
+  describe('Accelerator profiles', () => {
+    const testProfileName = 'test-accelerator-profile';
+    const testProfileDisplayName = 'Test Accelerator Profile';
+
+    beforeEach(() => {
+      cy.interceptK8sList(
+        { model: AcceleratorProfileModel, ns: testNamespace },
+        mockK8sResourceList([
+          mockAcceleratorProfile({
+            name: testProfileName,
+            displayName: testProfileDisplayName,
+            namespace: testNamespace,
+          }),
+        ]),
+      );
+    });
+
+    it('should delete', () => {
+      cy.interceptK8s(
+        'DELETE',
+        {
+          model: AcceleratorProfileModel,
+          ns: testNamespace,
+          name: testProfileName,
+        },
+        mock200Status({}),
+      ).as('delete');
+
+      legacyHardwareProfile.visit();
+      legacyHardwareProfile.findExpandButton().click();
+      legacyHardwareProfile.getRow(testProfileDisplayName).findKebabAction('Delete').click();
+
+      deleteModal.findSubmitButton().should('be.disabled');
+      deleteModal.findInput().fill('Delete 3 resources');
+      deleteModal.findSubmitButton().should('be.enabled').click();
+
+      cy.wait('@delete');
+    });
+
+    it('should duplicate', () => {
+      legacyHardwareProfile.visit();
+      legacyHardwareProfile.findExpandButton().click();
+      legacyHardwareProfile.getRow(testProfileDisplayName).findKebabAction('Duplicate').click();
+
+      cy.url().should('include', `/hardwareProfiles/duplicate/${testProfileName}`);
+    });
+
+    it('should edit', () => {
+      legacyHardwareProfile.visit();
+      legacyHardwareProfile.findExpandButton().click();
+      legacyHardwareProfile.getRow(testProfileDisplayName).findKebabAction('Edit').click();
+
+      cy.url().should('include', `/hardwareProfiles/edit/${testProfileName}`);
+
+      editHardwareProfile.findMigrationAlert().should('be.visible');
+    });
+
+    it('should migrate', () => {
+      cy.interceptK8s(
+        'DELETE',
+        {
+          model: AcceleratorProfileModel,
+          ns: testNamespace,
+          name: testProfileName,
+        },
+        mock200Status({}),
+      ).as('deleteSource');
+
+      cy.interceptK8s(
+        'POST',
+        { model: HardwareProfileModel },
+        mockHardwareProfile({
+          name: testProfileName,
+          displayName: testProfileDisplayName,
+        }),
+      ).as('createTarget');
+
+      legacyHardwareProfile.visit();
+      legacyHardwareProfile.findExpandButton().click();
+      legacyHardwareProfile.getRow(testProfileDisplayName).findKebabAction('Migrate').click();
+
+      migrationModal.findSubmitButton().should('be.disabled');
+      migrationModal.findInput().fill(testProfileDisplayName);
+      migrationModal.findSubmitButton().should('be.enabled').click();
+
+      cy.wait(['@deleteSource', '@createTarget', '@deleteSource', '@createTarget']);
path: frontend/src/__tests__/cypress/cypress/tests/mocked/hardwareProfiles/legacyHardwareProfiles.cy.ts
user: login: pnaik1
body: @caponetto just a small addition here, Can you assert the payload of `@createTarget` as mentioned in this [doc](https://github.com/opendatahub-io/odh-dashboard/blob/main/docs/testing.md#:~:text=When%20a%20payload%20is%20sent%20as%20part%20of%20the%20network%20request%2C%20the%20test%20should%20assert%20the%20payload%20is%20the%20expected%20value.%20For%20example%20after%20filling%20out%20and%20submitting%20a%20form%2C%20assert%20the%20form%20values%20are%20present%20in%20the%20network%20request%3A)
created_at: 2025-03-21T06:07:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,612 @@
+import {
+  editHardwareProfile,
+  legacyHardwareProfile,
+} from '~/__tests__/cypress/cypress/pages/hardwareProfile';
+import { mockHardwareProfile } from '~/__mocks__/mockHardwareProfile';
+import {
+  AcceleratorProfileModel,
+  HardwareProfileModel,
+  ODHDashboardConfigModel,
+} from '~/__tests__/cypress/cypress/utils/models';
+import { deleteModal } from '~/__tests__/cypress/cypress/pages/components/DeleteModal';
+import { migrationModal } from '~/__tests__/cypress/cypress/pages/components/MigrationModal';
+import { mock200Status, mockDashboardConfig, mockK8sResourceList } from '~/__mocks__';
+import { be } from '~/__tests__/cypress/cypress/utils/should';
+import { asProductAdminUser } from '~/__tests__/cypress/cypress/utils/mockUsers';
+import { testPagination } from '~/__tests__/cypress/cypress/utils/pagination';
+import { mockAcceleratorProfile } from '~/__mocks__/mockAcceleratorProfile';
+import { TolerationEffect, TolerationOperator } from '~/types';
+
+const initIntercepts = () => {
+  cy.interceptOdh(
+    'GET /api/config',
+    mockDashboardConfig({
+      modelServerSizes: [],
+      notebookSizes: [],
+    }),
+  );
+};
+
+describe('legacy profiles table', () => {
+  const testNamespace = 'opendatahub';
+
+  beforeEach(() => {
+    asProductAdminUser();
+    initIntercepts();
+  });
+  describe('Table', () => {
+    it('should not show the table when there are no items', () => {
+      legacyHardwareProfile.visit();
+      legacyHardwareProfile.findSection().should('not.exist');
+    });
+
+    it('should not show the table when there are no legacy items', () => {
+      cy.interceptK8sList(
+        { model: HardwareProfileModel, ns: testNamespace },
+        mockK8sResourceList([
+          mockHardwareProfile({
+            name: 'test-accelerator-profile',
+            displayName: 'Test Accelerator Profile',
+          }),
+        ]),
+      );
+
+      legacyHardwareProfile.visit();
+      legacyHardwareProfile.findSection().should('not.exist');
+    });
+
+    it('should initially hide the table when there are legacy items', () => {
+      cy.interceptK8sList(
+        { model: AcceleratorProfileModel, ns: testNamespace },
+        mockK8sResourceList([
+          mockAcceleratorProfile({
+            name: 'test-accelerator-profile',
+            displayName: 'Test Accelerator Profile',
+          }),
+        ]),
+      );
+
+      legacyHardwareProfile.visit();
+      legacyHardwareProfile.findSection().should('exist');
+      legacyHardwareProfile.findTable().should('not.be.visible');
+    });
+
+    it('should expand the section to visualize the table', () => {
+      cy.interceptK8sList(
+        { model: AcceleratorProfileModel, ns: testNamespace },
+        mockK8sResourceList([
+          mockAcceleratorProfile({
+            name: 'test-accelerator-profile',
+            displayName: 'Test Accelerator Profile',
+          }),
+        ]),
+      );
+
+      legacyHardwareProfile.visit();
+      legacyHardwareProfile.findTable().should('not.be.visible');
+      legacyHardwareProfile.findExpandButton().click();
+      legacyHardwareProfile.findTable().should('be.visible');
+    });
+
+    it('should show the Source column for legacy items', () => {
+      cy.interceptK8sList(
+        { model: AcceleratorProfileModel, ns: testNamespace },
+        mockK8sResourceList([
+          mockAcceleratorProfile({
+            name: 'test-accelerator-profile',
+            displayName: 'Test Accelerator Profile',
+          }),
+        ]),
+      );
+
+      legacyHardwareProfile.visit();
+      legacyHardwareProfile.findExpandButton().click();
+      legacyHardwareProfile.findTable().contains('th', 'Source').should('exist');
+    });
+
+    it('should show two entries in the table for each Accelerator Profile', () => {
+      const totalItems = 3;
+
+      cy.interceptK8sList(
+        { model: AcceleratorProfileModel, ns: testNamespace },
+        mockK8sResourceList(
+          Array.from({ length: totalItems }, (_, i) =>
+            mockAcceleratorProfile({
+              name: `test-accelerator-profile-${i}`,
+              displayName: `Test Accelerator Profile - ${i}`,
+            }),
+          ),
+        ),
+      );
+
+      legacyHardwareProfile.visit();
+      legacyHardwareProfile.findExpandButton().click();
+      legacyHardwareProfile.findRows().should('have.length', totalItems * 2);
+    });
+
+    it('should filter legacy profiles', () => {
+      const testAcceleratorProfileDisplayName = 'Test Accelerator Profile';
+      const testNotebookSizeProfileName = 'test-notebook-size-profile';
+      const testModelServerSizeProfileName = 'test-model-server-size-profile';
+
+      cy.interceptOdh(
+        'GET /api/config',
+        mockDashboardConfig({
+          modelServerSizes: [
+            {
+              name: testModelServerSizeProfileName,
+              resources: { requests: { cpu: '1', memory: '1Gi' } },
+            },
+          ],
+          notebookSizes: [
+            {
+              name: testNotebookSizeProfileName,
+              resources: { requests: { cpu: '1', memory: '1Gi' } },
+            },
+          ],
+        }),
+      );
+
+      cy.interceptK8sList(
+        { model: AcceleratorProfileModel, ns: testNamespace },
+        mockK8sResourceList([
+          mockAcceleratorProfile({
+            name: 'test-accelerator-profile',
+            displayName: testAcceleratorProfileDisplayName,
+            enabled: false,
+          }),
+        ]),
+      );
+
+      legacyHardwareProfile.visit();
+      legacyHardwareProfile.findExpandButton().click();
+      legacyHardwareProfile.findTable().should('be.visible');
+      legacyHardwareProfile.findRows().should('have.length', 4);
+
+      const tableToolbar = legacyHardwareProfile.getTableToolbar();
+
+      tableToolbar.findSearchInput().type(testAcceleratorProfileDisplayName);
+      legacyHardwareProfile.findRows().should('have.length', 2);
+      legacyHardwareProfile.getRow(testAcceleratorProfileDisplayName).find().should('exist');
+
+      tableToolbar.findFilterInput('name').clear();
+      legacyHardwareProfile.findRows().should('have.length', 4);
+
+      tableToolbar.findFilterMenuOption('filter-toolbar-dropdown', 'Enabled').click();
+      tableToolbar.selectEnableFilter('Enabled');
+      legacyHardwareProfile.findRows().should('have.length', 2);
+      legacyHardwareProfile.getRow(testNotebookSizeProfileName).find().should('exist');
+
+      tableToolbar.selectEnableFilter('Disabled');
+      legacyHardwareProfile.findRows().should('have.length', 2);
+      legacyHardwareProfile.getRow(testAcceleratorProfileDisplayName).find().should('exist');
+
+      tableToolbar.findFilterMenuOption('filter-toolbar-dropdown', 'Name').click();
+      tableToolbar.findFilterInput('name').type('No match');
+      legacyHardwareProfile.findRows().should('have.length', 0);
+
+      legacyHardwareProfile.findClearFiltersButton().click();
+      legacyHardwareProfile.findRows().should('have.length', 4);
+    });
+
+    it('should paginate legacy profiles', () => {
+      const itemCount = 25;
+      const totalItems = itemCount * 2;
+      const testModelServerProfileNamePrefix = 'test-model-server-size-profile';
+      const testNotebookSizeProfileNamePrefix = 'test-notebook-size-profile';
+
+      cy.interceptOdh(
+        'GET /api/config',
+        mockDashboardConfig({
+          modelServerSizes: Array.from({ length: itemCount }, (_, i) => ({
+            name: `${testModelServerProfileNamePrefix}-${i}`,
+            resources: { requests: { cpu: '1', memory: '1Gi' } },
+          })),
+          notebookSizes: Array.from({ length: itemCount }, (_, i) => ({
+            name: `${testNotebookSizeProfileNamePrefix}-${i}`,
+            resources: { requests: { cpu: '1', memory: '1Gi' } },
+          })),
+        }),
+      );
+
+      legacyHardwareProfile.visit();
+      legacyHardwareProfile.findExpandButton().click();
+      legacyHardwareProfile.findTable().should('be.visible');
+
+      testPagination({
+        totalItems,
+        firstElement: `${testModelServerProfileNamePrefix}-0`,
+        paginationVariant: 'top',
+      });
+
+      testPagination({
+        totalItems,
+        firstElement: `${testModelServerProfileNamePrefix}-0`,
+        paginationVariant: 'bottom',
+      });
+
+      legacyHardwareProfile.findTableHeaderButton('Name').click();
+      legacyHardwareProfile.findTableHeaderButton('Name').should(be.sortDescending);
+      legacyHardwareProfile.findTableHeaderButton('Name').click();
+      legacyHardwareProfile.findTableHeaderButton('Name').should(be.sortAscending);
+
+      legacyHardwareProfile.findTableHeaderButton('Last modified').click();
+      legacyHardwareProfile.findTableHeaderButton('Last modified').should(be.sortAscending);
+      legacyHardwareProfile.findTableHeaderButton('Last modified').click();
+      legacyHardwareProfile.findTableHeaderButton('Last modified').should(be.sortDescending);
+    });
+
+    it('should show the expandable section for items', () => {
+      const testModelServerProfileName = 'test-model-server-size-profile';
+      const testAcceleratorProfileDisplayName = 'test-accelerator-profile';
+
+      cy.interceptOdh(
+        'GET /api/config',
+        mockDashboardConfig({
+          modelServerSizes: [
+            {
+              name: testModelServerProfileName,
+              resources: { requests: { cpu: '1', memory: '1Gi' } },
+            },
+          ],
+          notebookSizes: [],
+        }),
+      );
+
+      cy.interceptK8sList(
+        { model: AcceleratorProfileModel, ns: testNamespace },
+        mockK8sResourceList([
+          mockAcceleratorProfile({
+            name: 'test-accelerator-profile',
+            displayName: testAcceleratorProfileDisplayName,
+            enabled: false,
+            tolerations: [
+              {
+                key: 'nvidia.com/gpu',
+                operator: TolerationOperator.EXISTS,
+                effect: TolerationEffect.NO_SCHEDULE,
+              },
+            ],
+          }),
+        ]),
+      );
+
+      legacyHardwareProfile.visit();
+      legacyHardwareProfile.findExpandButton().click();
+      legacyHardwareProfile.findTable().should('be.visible');
+
+      const row1 = legacyHardwareProfile.getRow(testModelServerProfileName);
+      row1.findExpandButton().click();
+      row1.findNodeResourceTable().should('exist');
+      row1.findTolerationTable().should('not.exist');
+
+      const row2 = legacyHardwareProfile.getRow(testAcceleratorProfileDisplayName);
+      row2.findExpandButton().click();
+      row2.findNodeResourceTable().should('exist');
+      row2.findTolerationTable().should('exist');
+    });
+
+    it('should not allow changes in the Enabled button', () => {
+      const testProfileDisplayNameEnabled = 'Test Accelerator Profile - Enabled';
+      const testProfileDisplayNameDisabled = 'Test Accelerator Profile - Disabled';
+
+      cy.interceptK8sList(
+        { model: AcceleratorProfileModel, ns: testNamespace },
+        mockK8sResourceList([
+          mockAcceleratorProfile({
+            name: 'test-accelerator-profile-enabled',
+            displayName: testProfileDisplayNameEnabled,
+            enabled: true,
+          }),
+          mockAcceleratorProfile({
+            name: 'test-accelerator-profile-disabled',
+            displayName: testProfileDisplayNameDisabled,
+            enabled: false,
+          }),
+        ]),
+      );
+
+      legacyHardwareProfile.visit();
+      legacyHardwareProfile.findExpandButton().click();
+      legacyHardwareProfile.findTable().should('be.visible');
+
+      const enabledRow = legacyHardwareProfile.getRow(testProfileDisplayNameEnabled);
+      enabledRow.findEnabled().should('be.checked');
+      enabledRow.findEnabled().should('be.disabled');
+
+      const disabledRow = legacyHardwareProfile.getRow(testProfileDisplayNameDisabled);
+      disabledRow.findEnabled().should('not.be.checked');
+      disabledRow.findEnabled().should('be.disabled');
+    });
+  });
+
+  describe('Accelerator profiles', () => {
+    const testProfileName = 'test-accelerator-profile';
+    const testProfileDisplayName = 'Test Accelerator Profile';
+
+    beforeEach(() => {
+      cy.interceptK8sList(
+        { model: AcceleratorProfileModel, ns: testNamespace },
+        mockK8sResourceList([
+          mockAcceleratorProfile({
+            name: testProfileName,
+            displayName: testProfileDisplayName,
+            namespace: testNamespace,
+          }),
+        ]),
+      );
+    });
+
+    it('should delete', () => {
+      cy.interceptK8s(
+        'DELETE',
+        {
+          model: AcceleratorProfileModel,
+          ns: testNamespace,
+          name: testProfileName,
+        },
+        mock200Status({}),
+      ).as('delete');
+
+      legacyHardwareProfile.visit();
+      legacyHardwareProfile.findExpandButton().click();
+      legacyHardwareProfile.getRow(testProfileDisplayName).findKebabAction('Delete').click();
+
+      deleteModal.findSubmitButton().should('be.disabled');
+      deleteModal.findInput().fill('Delete 3 resources');
+      deleteModal.findSubmitButton().should('be.enabled').click();
+
+      cy.wait('@delete');
+    });
+
+    it('should duplicate', () => {
+      legacyHardwareProfile.visit();
+      legacyHardwareProfile.findExpandButton().click();
+      legacyHardwareProfile.getRow(testProfileDisplayName).findKebabAction('Duplicate').click();
+
+      cy.url().should('include', `/hardwareProfiles/duplicate/${testProfileName}`);
+    });
+
+    it('should edit', () => {
+      legacyHardwareProfile.visit();
+      legacyHardwareProfile.findExpandButton().click();
+      legacyHardwareProfile.getRow(testProfileDisplayName).findKebabAction('Edit').click();
+
+      cy.url().should('include', `/hardwareProfiles/edit/${testProfileName}`);
+
+      editHardwareProfile.findMigrationAlert().should('be.visible');
+    });
+
+    it('should migrate', () => {
+      cy.interceptK8s(
+        'DELETE',
+        {
+          model: AcceleratorProfileModel,
+          ns: testNamespace,
+          name: testProfileName,
+        },
+        mock200Status({}),
+      ).as('deleteSource');
+
+      cy.interceptK8s(
+        'POST',
+        { model: HardwareProfileModel },
+        mockHardwareProfile({
+          name: testProfileName,
+          displayName: testProfileDisplayName,
+        }),
+      ).as('createTarget');
+
+      legacyHardwareProfile.visit();
+      legacyHardwareProfile.findExpandButton().click();
+      legacyHardwareProfile.getRow(testProfileDisplayName).findKebabAction('Migrate').click();
+
+      migrationModal.findSubmitButton().should('be.disabled');
+      migrationModal.findInput().fill(testProfileDisplayName);
+      migrationModal.findSubmitButton().should('be.enabled').click();
+
+      cy.wait(['@deleteSource', '@createTarget', '@deleteSource', '@createTarget']);
path: frontend/src/__tests__/cypress/cypress/tests/mocked/hardwareProfiles/legacyHardwareProfiles.cy.ts
user: login: caponetto
body: Done in https://github.com/opendatahub-io/odh-dashboard/pull/3959/commits/5bf6bf3a9cd3075fc74106bcce92d00168c0b04d
created_at: 2025-03-21T12:16:40Z
author_association: CONTRIBUTOR

PR comments:
user: login: codecov[bot]
created_at: 2025-03-20T14:11:24Z
body: ## [Codecov](https://app.codecov.io/gh/opendatahub-io/odh-dashboard/pull/3959?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=opendatahub-io) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 85.08%. Comparing base [(`8274796`)](https://app.codecov.io/gh/opendatahub-io/odh-dashboard/commit/8274796f5e79bf689b54f21910d9e02ba4cd1cea?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=opendatahub-io) to head [(`d72e0eb`)](https://app.codecov.io/gh/opendatahub-io/odh-dashboard/commit/d72e0eb0cf38a3b546ef07a7f28201be279a92de?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=opendatahub-io).
> Report is 10 commits behind head on main.

<details><summary>Additional details and impacted files</summary>


[![Impacted file tree graph](https://app.codecov.io/gh/opendatahub-io/odh-dashboard/pull/3959/graphs/tree.svg?width=650&height=150&src=pr&token=UQM7NLQUXJ&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=opendatahub-io)](https://app.codecov.io/gh/opendatahub-io/odh-dashboard/pull/3959?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=opendatahub-io)

```diff
@@            Coverage Diff             @@
##             main    #3959      +/-   ##
==========================================
+ Coverage   84.85%   85.08%   +0.23%     
==========================================
  Files        1568     1568              
  Lines       37436    37436              
  Branches    10577    10577              
==========================================
+ Hits        31766    31854      +88     
+ Misses       5670     5582      -88     
```

[see 31 files with indirect coverage changes](https://app.codecov.io/gh/opendatahub-io/odh-dashboard/pull/3959/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=opendatahub-io)

------

[Continue to review full report in Codecov by Sentry](https://app.codecov.io/gh/opendatahub-io/odh-dashboard/pull/3959?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=opendatahub-io).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=opendatahub-io)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://app.codecov.io/gh/opendatahub-io/odh-dashboard/pull/3959?dropdown=coverage&src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=opendatahub-io). Last update [8274796...d72e0eb](https://app.codecov.io/gh/opendatahub-io/odh-dashboard/pull/3959?dropdown=coverage&src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=opendatahub-io). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=opendatahub-io).

</details>
<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
- :package: [JS Bundle Analysis](https://docs.codecov.com/docs/javascript-bundle-analysis): Save yourself from yourself by tracking and limiting bundle sizes in JS merges.
</details>
author_association: NONE
user: login: pnaik1
created_at: 2025-03-24T07:56:47Z
body: /lgtm
author_association: CONTRIBUTOR
user: login: openshift-ci[bot]
created_at: 2025-03-25T20:07:14Z
body: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/opendatahub-io/odh-dashboard/pull/3959#pullrequestreview-2715051463" title="Approved">Gkrumbach07</a>*

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=opendatahub-io%2Fodh-dashboard).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- ~~[frontend/OWNERS](https://github.com/opendatahub-io/odh-dashboard/blob/main/frontend/OWNERS)~~ [Gkrumbach07]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->
author_association: CONTRIBUTOR