From 167b7b5f301a813dd42bffcf0d8316622e1b779b Mon Sep 17 00:00:00 2001 From: Thiago Dallacqua Date: Wed, 16 Oct 2024 17:32:31 -0300 Subject: [PATCH 01/10] feat: Add ExperimentMoveModal component and update ExperimentActionDropdown with move modal integration. --- .../components/ExperimentActionDropdown.ts | 4 ++ .../models/components/ExperimentMoveModal.ts | 16 +++++ .../src/e2e/tests/experimentList.spec.ts | 71 ++++++++++++++++++- 3 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 webui/react/src/e2e/models/components/ExperimentMoveModal.ts diff --git a/webui/react/src/e2e/models/components/ExperimentActionDropdown.ts b/webui/react/src/e2e/models/components/ExperimentActionDropdown.ts index f95d12a3450..4c0cb3d2bd0 100644 --- a/webui/react/src/e2e/models/components/ExperimentActionDropdown.ts +++ b/webui/react/src/e2e/models/components/ExperimentActionDropdown.ts @@ -1,6 +1,7 @@ import { DropdownMenu } from 'e2e/models/common/hew/Dropdown'; import ExperimentEditModal from './ExperimentEditModal'; +import ExperimentMoveModal from './ExperimentMoveModal'; /** * Represents the ExperimentActionDropdown component in src/components/ExperimentActionDropdown.tsx @@ -14,4 +15,7 @@ export class ExperimentActionDropdown extends DropdownMenu { readonly editModal = new ExperimentEditModal({ root: this.root, }); + readonly moveModal = new ExperimentMoveModal({ + root: this.root, + }); } diff --git a/webui/react/src/e2e/models/components/ExperimentMoveModal.ts b/webui/react/src/e2e/models/components/ExperimentMoveModal.ts new file mode 100644 index 00000000000..b6c71be3ea5 --- /dev/null +++ b/webui/react/src/e2e/models/components/ExperimentMoveModal.ts @@ -0,0 +1,16 @@ +import { Modal } from 'e2e/models/common/hew/Modal'; +import { Select } from 'e2e/models/common/hew/Select'; + +/** + * Represents the ExperimentMoveModal component in src/components/ExperimentMoveModal.tsx + */ +export default class ExperimentMoveModal extends Modal { + readonly destinationWorkspace = new Select({ + parent: this, + selector: 'input[id="workspace"]', + }); + readonly destinationProject = new Select({ + parent: this, + selector: 'input[id="projectId"]', + }); +} diff --git a/webui/react/src/e2e/tests/experimentList.spec.ts b/webui/react/src/e2e/tests/experimentList.spec.ts index e2bcc21a50b..8e8e18f025b 100644 --- a/webui/react/src/e2e/tests/experimentList.spec.ts +++ b/webui/react/src/e2e/tests/experimentList.spec.ts @@ -4,12 +4,13 @@ import { ProjectDetails } from 'e2e/models/pages/ProjectDetails'; import { detExecSync, fullPath } from 'e2e/utils/detCLI'; import { safeName } from 'e2e/utils/naming'; import { repeatWithFallback } from 'e2e/utils/polling'; +import { V1Project, V1Workspace } from 'services/api-ts-sdk'; import { ExperimentBase } from 'types'; test.describe('Experiment List', () => { let projectDetailsPage: ProjectDetails; // trial click to wait for the element to be stable won't work here - const waitTableStable = async () => await projectDetailsPage._page.waitForTimeout(2_000); + const waitTableStable = async (timeout?:number) => await projectDetailsPage._page.waitForTimeout(timeout ?? 2_000); const getCount = async () => { const count = await projectDetailsPage.f_experimentList.tableActionBar.count.pwLocator.textContent(); @@ -559,4 +560,72 @@ test.describe('Experiment List', () => { }); }); }); + + test.describe('Row Actions', () => { + let destinationProject: V1Project; + let destinationWorkspace: V1Workspace; + let experimentId: number; + + // create a new project, workspace and experiment + test.beforeAll(async ({ backgroundApiProject, backgroundApiWorkspace }) => { + destinationWorkspace = (await backgroundApiWorkspace.createWorkspace( + backgroundApiWorkspace.new(), + )).workspace; + destinationProject = (await backgroundApiProject.createProject( + destinationWorkspace.id, + backgroundApiProject.new(), + )).project; + + const expId = Number(detExecSync( + `experiment create ${fullPath('examples/tutorials/mnist_pytorch/adaptive.yaml')} --paused --project_id ${destinationProject.id}`, + ).split(' ')[2]); // returns in the format "Created experiment " + + if (!Number.isNaN(expId)) experimentId = expId; + }); + + test.beforeEach(async ({ authedPage, newProject }) => { + test.slow(); + projectDetailsPage = new ProjectDetails(authedPage); + + await projectDetailsPage.gotoProject(newProject.response.project.id); + + const grid = projectDetailsPage.f_experimentList.dataGrid; + + await grid.setColumnHeight(); + await grid.headRow.setColumnDefs(); + }); + + // cleanup + test.afterAll(async ({ backgroundApiProject, backgroundApiWorkspace }) => { + if (experimentId !== undefined) { + detExecSync(`experiment kill ${experimentId}`); + detExecSync(`experiment delete ${experimentId} --y`); + } + + await backgroundApiProject.deleteProject(destinationProject.id); + await backgroundApiWorkspace.deleteWorkspace(destinationProject.id); + + }); + + test('move experiment', async () => { + if (experimentId === undefined) return; + + const newExperimentRow = await projectDetailsPage.f_experimentList.dataGrid.getRowByColumnValue('ID', experimentId.toString()); + + const menuMove = await newExperimentRow.experimentActionDropdown.open(); + + await menuMove.menuItem('Move').pwLocator.click(); + await menuMove.moveModal.destinationWorkspace.pwLocator.fill(destinationProject.workspaceName ?? ''); + await menuMove.moveModal.destinationProject.pwLocator.waitFor({ state: 'visible' }); + await menuMove.moveModal.destinationProject.pwLocator.fill(destinationProject.name); + await menuMove.moveModal.footer.submit.pwLocator.click(); + await menuMove.moveModal.pwLocator.waitFor({ state: 'hidden' }); + + await newExperimentRow.pwLocator.waitFor({ state: 'hidden' }); + + await projectDetailsPage.gotoProject(destinationProject.id); + await waitTableStable(); + await expect((await projectDetailsPage.f_experimentList.dataGrid.getRowByColumnValue('ID', experimentId.toString())).pwLocator).toBeVisible(); + }); + }); }); From 89c5f2d15b6e0ad5d50c8fd9c71274c797adbcbc Mon Sep 17 00:00:00 2001 From: Thiago Dallacqua Date: Wed, 16 Oct 2024 17:49:10 -0300 Subject: [PATCH 02/10] refactor(e2e): Remove unused V1Workspace import and clean up test code. --- .../src/e2e/tests/experimentList.spec.ts | 31 ++++--------------- 1 file changed, 6 insertions(+), 25 deletions(-) diff --git a/webui/react/src/e2e/tests/experimentList.spec.ts b/webui/react/src/e2e/tests/experimentList.spec.ts index 8e8e18f025b..86502fa91ce 100644 --- a/webui/react/src/e2e/tests/experimentList.spec.ts +++ b/webui/react/src/e2e/tests/experimentList.spec.ts @@ -4,7 +4,7 @@ import { ProjectDetails } from 'e2e/models/pages/ProjectDetails'; import { detExecSync, fullPath } from 'e2e/utils/detCLI'; import { safeName } from 'e2e/utils/naming'; import { repeatWithFallback } from 'e2e/utils/polling'; -import { V1Project, V1Workspace } from 'services/api-ts-sdk'; +import { V1Project } from 'services/api-ts-sdk'; import { ExperimentBase } from 'types'; test.describe('Experiment List', () => { @@ -563,48 +563,30 @@ test.describe('Experiment List', () => { test.describe('Row Actions', () => { let destinationProject: V1Project; - let destinationWorkspace: V1Workspace; let experimentId: number; // create a new project, workspace and experiment - test.beforeAll(async ({ backgroundApiProject, backgroundApiWorkspace }) => { - destinationWorkspace = (await backgroundApiWorkspace.createWorkspace( - backgroundApiWorkspace.new(), - )).workspace; + test.beforeAll(async ({ backgroundApiProject, newProject: { response: { project } } }) => { destinationProject = (await backgroundApiProject.createProject( - destinationWorkspace.id, - backgroundApiProject.new(), + project.workspaceId, + backgroundApiProject.new({ projectProps: { workspaceId: project.workspaceId } }), )).project; const expId = Number(detExecSync( - `experiment create ${fullPath('examples/tutorials/mnist_pytorch/adaptive.yaml')} --paused --project_id ${destinationProject.id}`, + `experiment create ${fullPath('examples/tutorials/mnist_pytorch/adaptive.yaml')} --paused --project_id ${project.id}`, ).split(' ')[2]); // returns in the format "Created experiment " if (!Number.isNaN(expId)) experimentId = expId; }); - test.beforeEach(async ({ authedPage, newProject }) => { - test.slow(); - projectDetailsPage = new ProjectDetails(authedPage); - - await projectDetailsPage.gotoProject(newProject.response.project.id); - - const grid = projectDetailsPage.f_experimentList.dataGrid; - - await grid.setColumnHeight(); - await grid.headRow.setColumnDefs(); - }); - // cleanup - test.afterAll(async ({ backgroundApiProject, backgroundApiWorkspace }) => { + test.afterAll(async ({ backgroundApiProject }) => { if (experimentId !== undefined) { detExecSync(`experiment kill ${experimentId}`); detExecSync(`experiment delete ${experimentId} --y`); } await backgroundApiProject.deleteProject(destinationProject.id); - await backgroundApiWorkspace.deleteWorkspace(destinationProject.id); - }); test('move experiment', async () => { @@ -615,7 +597,6 @@ test.describe('Experiment List', () => { const menuMove = await newExperimentRow.experimentActionDropdown.open(); await menuMove.menuItem('Move').pwLocator.click(); - await menuMove.moveModal.destinationWorkspace.pwLocator.fill(destinationProject.workspaceName ?? ''); await menuMove.moveModal.destinationProject.pwLocator.waitFor({ state: 'visible' }); await menuMove.moveModal.destinationProject.pwLocator.fill(destinationProject.name); await menuMove.moveModal.footer.submit.pwLocator.click(); From c8985d71d5b4a44b4deb4f8a3f29e128b1706aab Mon Sep 17 00:00:00 2001 From: Thiago Dallacqua Date: Wed, 16 Oct 2024 17:59:41 -0300 Subject: [PATCH 03/10] refactor: Add destinationWorkspace to move experiment function. --- webui/react/src/e2e/tests/experimentList.spec.ts | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/webui/react/src/e2e/tests/experimentList.spec.ts b/webui/react/src/e2e/tests/experimentList.spec.ts index 86502fa91ce..d8c41977802 100644 --- a/webui/react/src/e2e/tests/experimentList.spec.ts +++ b/webui/react/src/e2e/tests/experimentList.spec.ts @@ -4,7 +4,7 @@ import { ProjectDetails } from 'e2e/models/pages/ProjectDetails'; import { detExecSync, fullPath } from 'e2e/utils/detCLI'; import { safeName } from 'e2e/utils/naming'; import { repeatWithFallback } from 'e2e/utils/polling'; -import { V1Project } from 'services/api-ts-sdk'; +import { V1Project, V1Workspace } from 'services/api-ts-sdk'; import { ExperimentBase } from 'types'; test.describe('Experiment List', () => { @@ -563,10 +563,12 @@ test.describe('Experiment List', () => { test.describe('Row Actions', () => { let destinationProject: V1Project; + let destinationWorkspace: V1Workspace; let experimentId: number; // create a new project, workspace and experiment - test.beforeAll(async ({ backgroundApiProject, newProject: { response: { project } } }) => { + test.beforeAll(async ({ backgroundApiProject, backgroundApiWorkspace, newProject: { response: { project } } }) => { + destinationWorkspace = (await backgroundApiWorkspace.createWorkspace(backgroundApiWorkspace.new())).workspace; destinationProject = (await backgroundApiProject.createProject( project.workspaceId, backgroundApiProject.new({ projectProps: { workspaceId: project.workspaceId } }), @@ -580,13 +582,14 @@ test.describe('Experiment List', () => { }); // cleanup - test.afterAll(async ({ backgroundApiProject }) => { + test.afterAll(async ({ backgroundApiProject, backgroundApiWorkspace }) => { if (experimentId !== undefined) { detExecSync(`experiment kill ${experimentId}`); detExecSync(`experiment delete ${experimentId} --y`); } await backgroundApiProject.deleteProject(destinationProject.id); + await backgroundApiWorkspace.deleteWorkspace(destinationWorkspace.id); }); test('move experiment', async () => { @@ -597,9 +600,15 @@ test.describe('Experiment List', () => { const menuMove = await newExperimentRow.experimentActionDropdown.open(); await menuMove.menuItem('Move').pwLocator.click(); + await menuMove.moveModal.destinationWorkspace.pwLocator.fill(destinationProject.workspaceName ?? ''); await menuMove.moveModal.destinationProject.pwLocator.waitFor({ state: 'visible' }); await menuMove.moveModal.destinationProject.pwLocator.fill(destinationProject.name); await menuMove.moveModal.footer.submit.pwLocator.click(); + // TODO: check why it's failing on submit (somehow the check bellow isn' passing) + /** + * const handleSubmit = async () => { + if (workspaceId === sourceWorkspaceId && projectId === sourceProjectId) { + */ await menuMove.moveModal.pwLocator.waitFor({ state: 'hidden' }); await newExperimentRow.pwLocator.waitFor({ state: 'hidden' }); From 4816890690e0a3166717e202b3eed8f26ef14fff Mon Sep 17 00:00:00 2001 From: Thiago Dallacqua Date: Fri, 18 Oct 2024 17:48:15 -0300 Subject: [PATCH 04/10] lint --- webui/react/src/e2e/tests/experimentList.spec.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/webui/react/src/e2e/tests/experimentList.spec.ts b/webui/react/src/e2e/tests/experimentList.spec.ts index d8c41977802..9cb07719cc8 100644 --- a/webui/react/src/e2e/tests/experimentList.spec.ts +++ b/webui/react/src/e2e/tests/experimentList.spec.ts @@ -10,7 +10,8 @@ import { ExperimentBase } from 'types'; test.describe('Experiment List', () => { let projectDetailsPage: ProjectDetails; // trial click to wait for the element to be stable won't work here - const waitTableStable = async (timeout?:number) => await projectDetailsPage._page.waitForTimeout(timeout ?? 2_000); + const waitTableStable = async (timeout?: number) => + await projectDetailsPage._page.waitForTimeout(timeout ?? 2_000); const getCount = async () => { const count = await projectDetailsPage.f_experimentList.tableActionBar.count.pwLocator.textContent(); From 5700567d62d78e9ed330d26a2490dca6aad4f24b Mon Sep 17 00:00:00 2001 From: Thiago Dallacqua Date: Fri, 18 Oct 2024 17:48:43 -0300 Subject: [PATCH 05/10] lint --- .../src/e2e/tests/experimentList.spec.ts | 40 +++++++++++++------ 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/webui/react/src/e2e/tests/experimentList.spec.ts b/webui/react/src/e2e/tests/experimentList.spec.ts index 9cb07719cc8..052805880ee 100644 --- a/webui/react/src/e2e/tests/experimentList.spec.ts +++ b/webui/react/src/e2e/tests/experimentList.spec.ts @@ -568,19 +568,33 @@ test.describe('Experiment List', () => { let experimentId: number; // create a new project, workspace and experiment - test.beforeAll(async ({ backgroundApiProject, backgroundApiWorkspace, newProject: { response: { project } } }) => { - destinationWorkspace = (await backgroundApiWorkspace.createWorkspace(backgroundApiWorkspace.new())).workspace; - destinationProject = (await backgroundApiProject.createProject( - project.workspaceId, - backgroundApiProject.new({ projectProps: { workspaceId: project.workspaceId } }), - )).project; - - const expId = Number(detExecSync( - `experiment create ${fullPath('examples/tutorials/mnist_pytorch/adaptive.yaml')} --paused --project_id ${project.id}`, - ).split(' ')[2]); // returns in the format "Created experiment " - - if (!Number.isNaN(expId)) experimentId = expId; - }); + test.beforeAll( + async ({ + backgroundApiProject, + backgroundApiWorkspace, + newProject: { + response: { project }, + }, + }) => { + destinationWorkspace = ( + await backgroundApiWorkspace.createWorkspace(backgroundApiWorkspace.new()) + ).workspace; + destinationProject = ( + await backgroundApiProject.createProject( + project.workspaceId, + backgroundApiProject.new({ projectProps: { workspaceId: project.workspaceId } }), + ) + ).project; + + const expId = Number( + detExecSync( + `experiment create ${fullPath('examples/tutorials/mnist_pytorch/adaptive.yaml')} --paused --project_id ${project.id}`, + ).split(' ')[2], + ); // returns in the format "Created experiment " + + if (!Number.isNaN(expId)) experimentId = expId; + }, + ); // cleanup test.afterAll(async ({ backgroundApiProject, backgroundApiWorkspace }) => { From 135b6a713863b5b0c83b4559d679f35fd7b53d26 Mon Sep 17 00:00:00 2001 From: Thiago Dallacqua Date: Fri, 18 Oct 2024 18:57:48 -0300 Subject: [PATCH 06/10] feat: Update move experiment test with correct workspace and project names. --- webui/react/src/e2e/tests/experimentList.spec.ts | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/webui/react/src/e2e/tests/experimentList.spec.ts b/webui/react/src/e2e/tests/experimentList.spec.ts index 052805880ee..a4cd76f7c25 100644 --- a/webui/react/src/e2e/tests/experimentList.spec.ts +++ b/webui/react/src/e2e/tests/experimentList.spec.ts @@ -607,23 +607,21 @@ test.describe('Experiment List', () => { await backgroundApiWorkspace.deleteWorkspace(destinationWorkspace.id); }); - test('move experiment', async () => { + test('move experiment', async ({ newWorkspace: { response: { workspace } } }) => { if (experimentId === undefined) return; const newExperimentRow = await projectDetailsPage.f_experimentList.dataGrid.getRowByColumnValue('ID', experimentId.toString()); const menuMove = await newExperimentRow.experimentActionDropdown.open(); + console.log(destinationProject.workspaceName) await menuMove.menuItem('Move').pwLocator.click(); - await menuMove.moveModal.destinationWorkspace.pwLocator.fill(destinationProject.workspaceName ?? ''); + await menuMove.moveModal.destinationWorkspace.pwLocator.fill(workspace.name); + await menuMove.moveModal.destinationWorkspace.selectMenuOption(workspace.name); await menuMove.moveModal.destinationProject.pwLocator.waitFor({ state: 'visible' }); await menuMove.moveModal.destinationProject.pwLocator.fill(destinationProject.name); + await menuMove.moveModal.destinationWorkspace.selectMenuOption(destinationProject.name); await menuMove.moveModal.footer.submit.pwLocator.click(); - // TODO: check why it's failing on submit (somehow the check bellow isn' passing) - /** - * const handleSubmit = async () => { - if (workspaceId === sourceWorkspaceId && projectId === sourceProjectId) { - */ await menuMove.moveModal.pwLocator.waitFor({ state: 'hidden' }); await newExperimentRow.pwLocator.waitFor({ state: 'hidden' }); From 1a946534f95e9232a6e58ead95cc3e4a0e15d0ae Mon Sep 17 00:00:00 2001 From: Thiago Dallacqua Date: Mon, 21 Oct 2024 19:25:26 -0300 Subject: [PATCH 07/10] refactor: Improve code readability by formatting lines in experimentList.spec.ts. --- .../src/e2e/tests/experimentList.spec.ts | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/webui/react/src/e2e/tests/experimentList.spec.ts b/webui/react/src/e2e/tests/experimentList.spec.ts index a4cd76f7c25..271c1f2b7bc 100644 --- a/webui/react/src/e2e/tests/experimentList.spec.ts +++ b/webui/react/src/e2e/tests/experimentList.spec.ts @@ -607,13 +607,20 @@ test.describe('Experiment List', () => { await backgroundApiWorkspace.deleteWorkspace(destinationWorkspace.id); }); - test('move experiment', async ({ newWorkspace: { response: { workspace } } }) => { + test('move experiment', async ({ + newWorkspace: { + response: { workspace }, + }, + }) => { if (experimentId === undefined) return; - const newExperimentRow = await projectDetailsPage.f_experimentList.dataGrid.getRowByColumnValue('ID', experimentId.toString()); + const newExperimentRow = + await projectDetailsPage.f_experimentList.dataGrid.getRowByColumnValue( + 'ID', + experimentId.toString(), + ); const menuMove = await newExperimentRow.experimentActionDropdown.open(); - console.log(destinationProject.workspaceName) await menuMove.menuItem('Move').pwLocator.click(); await menuMove.moveModal.destinationWorkspace.pwLocator.fill(workspace.name); @@ -628,7 +635,14 @@ test.describe('Experiment List', () => { await projectDetailsPage.gotoProject(destinationProject.id); await waitTableStable(); - await expect((await projectDetailsPage.f_experimentList.dataGrid.getRowByColumnValue('ID', experimentId.toString())).pwLocator).toBeVisible(); + await expect( + ( + await projectDetailsPage.f_experimentList.dataGrid.getRowByColumnValue( + 'ID', + experimentId.toString(), + ) + ).pwLocator, + ).toBeVisible(); }); }); }); From 9fcdfeedfec6eabcc5ab3752a01740c22048f58c Mon Sep 17 00:00:00 2001 From: Thiago Dallacqua Date: Tue, 22 Oct 2024 14:14:45 -0300 Subject: [PATCH 08/10] refactor: Update data-test attributes and improve selectors. --- .../src/components/ExperimentMoveModal.tsx | 2 ++ .../models/components/ExperimentMoveModal.ts | 4 ++-- .../src/e2e/tests/experimentList.spec.ts | 20 ++++++++----------- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/webui/react/src/components/ExperimentMoveModal.tsx b/webui/react/src/components/ExperimentMoveModal.tsx index 07e380ea5c8..f46f1b64357 100644 --- a/webui/react/src/components/ExperimentMoveModal.tsx +++ b/webui/react/src/components/ExperimentMoveModal.tsx @@ -169,6 +169,7 @@ const ExperimentMoveModalComponent: React.FC = ({ name="workspaceId" rules={[{ message: 'Workspace is required', required: true }]}> (option?.title?.toString() ?? '').toLowerCase().includes(input.toLowerCase()) } diff --git a/webui/react/src/e2e/models/components/ExperimentMoveModal.ts b/webui/react/src/e2e/models/components/ExperimentMoveModal.ts index b6c71be3ea5..cd90cd80334 100644 --- a/webui/react/src/e2e/models/components/ExperimentMoveModal.ts +++ b/webui/react/src/e2e/models/components/ExperimentMoveModal.ts @@ -7,10 +7,10 @@ import { Select } from 'e2e/models/common/hew/Select'; export default class ExperimentMoveModal extends Modal { readonly destinationWorkspace = new Select({ parent: this, - selector: 'input[id="workspace"]', + selector: '[data-test="workspace"]', }); readonly destinationProject = new Select({ parent: this, - selector: 'input[id="projectId"]', + selector: '[data-test="project"]', }); } diff --git a/webui/react/src/e2e/tests/experimentList.spec.ts b/webui/react/src/e2e/tests/experimentList.spec.ts index 271c1f2b7bc..a45b572d2ea 100644 --- a/webui/react/src/e2e/tests/experimentList.spec.ts +++ b/webui/react/src/e2e/tests/experimentList.spec.ts @@ -623,26 +623,22 @@ test.describe('Experiment List', () => { const menuMove = await newExperimentRow.experimentActionDropdown.open(); await menuMove.menuItem('Move').pwLocator.click(); - await menuMove.moveModal.destinationWorkspace.pwLocator.fill(workspace.name); await menuMove.moveModal.destinationWorkspace.selectMenuOption(workspace.name); await menuMove.moveModal.destinationProject.pwLocator.waitFor({ state: 'visible' }); - await menuMove.moveModal.destinationProject.pwLocator.fill(destinationProject.name); - await menuMove.moveModal.destinationWorkspace.selectMenuOption(destinationProject.name); + await menuMove.moveModal.destinationProject.selectMenuOption(destinationProject.name); await menuMove.moveModal.footer.submit.pwLocator.click(); await menuMove.moveModal.pwLocator.waitFor({ state: 'hidden' }); await newExperimentRow.pwLocator.waitFor({ state: 'hidden' }); await projectDetailsPage.gotoProject(destinationProject.id); - await waitTableStable(); - await expect( - ( - await projectDetailsPage.f_experimentList.dataGrid.getRowByColumnValue( - 'ID', - experimentId.toString(), - ) - ).pwLocator, - ).toBeVisible(); + const grid = projectDetailsPage.f_experimentList.dataGrid; + await grid.setColumnHeight(); + await grid.headRow.setColumnDefs(); + const newProjectRows = await projectDetailsPage.f_experimentList.dataGrid.filterRows(() => + Promise.resolve(true), + ); + await expect(newProjectRows.length).toBe(1); }); }); }); From 58dc60180bb9271d591f738ebc64153bcb2d5ede Mon Sep 17 00:00:00 2001 From: Thiago Dallacqua Date: Tue, 22 Oct 2024 15:16:51 -0300 Subject: [PATCH 09/10] refactor: Simplify experiment move actions in e2e test. --- .../src/e2e/tests/experimentList.spec.ts | 39 ++++++++++--------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/webui/react/src/e2e/tests/experimentList.spec.ts b/webui/react/src/e2e/tests/experimentList.spec.ts index a45b572d2ea..50910b1c907 100644 --- a/webui/react/src/e2e/tests/experimentList.spec.ts +++ b/webui/react/src/e2e/tests/experimentList.spec.ts @@ -4,14 +4,13 @@ import { ProjectDetails } from 'e2e/models/pages/ProjectDetails'; import { detExecSync, fullPath } from 'e2e/utils/detCLI'; import { safeName } from 'e2e/utils/naming'; import { repeatWithFallback } from 'e2e/utils/polling'; -import { V1Project, V1Workspace } from 'services/api-ts-sdk'; +import { V1Project } from 'services/api-ts-sdk'; import { ExperimentBase } from 'types'; test.describe('Experiment List', () => { let projectDetailsPage: ProjectDetails; // trial click to wait for the element to be stable won't work here - const waitTableStable = async (timeout?: number) => - await projectDetailsPage._page.waitForTimeout(timeout ?? 2_000); + const waitTableStable = async () => await projectDetailsPage._page.waitForTimeout(2_000); const getCount = async () => { const count = await projectDetailsPage.f_experimentList.tableActionBar.count.pwLocator.textContent(); @@ -564,21 +563,16 @@ test.describe('Experiment List', () => { test.describe('Row Actions', () => { let destinationProject: V1Project; - let destinationWorkspace: V1Workspace; let experimentId: number; // create a new project, workspace and experiment test.beforeAll( async ({ backgroundApiProject, - backgroundApiWorkspace, newProject: { response: { project }, }, }) => { - destinationWorkspace = ( - await backgroundApiWorkspace.createWorkspace(backgroundApiWorkspace.new()) - ).workspace; destinationProject = ( await backgroundApiProject.createProject( project.workspaceId, @@ -592,19 +586,20 @@ test.describe('Experiment List', () => { ).split(' ')[2], ); // returns in the format "Created experiment " - if (!Number.isNaN(expId)) experimentId = expId; + if (!Number.isNaN(expId)) throw new Error('No experiment ID was found'); + + experimentId = expId; }, ); // cleanup - test.afterAll(async ({ backgroundApiProject, backgroundApiWorkspace }) => { + test.afterAll(async ({ backgroundApiProject }) => { if (experimentId !== undefined) { detExecSync(`experiment kill ${experimentId}`); detExecSync(`experiment delete ${experimentId} --y`); } await backgroundApiProject.deleteProject(destinationProject.id); - await backgroundApiWorkspace.deleteWorkspace(destinationWorkspace.id); }); test('move experiment', async ({ @@ -612,7 +607,7 @@ test.describe('Experiment List', () => { response: { workspace }, }, }) => { - if (experimentId === undefined) return; + if (experimentId === undefined) throw new Error('No experiment ID was found'); const newExperimentRow = await projectDetailsPage.f_experimentList.dataGrid.getRowByColumnValue( @@ -620,14 +615,20 @@ test.describe('Experiment List', () => { experimentId.toString(), ); - const menuMove = await newExperimentRow.experimentActionDropdown.open(); + const experimentActionDropdown = await newExperimentRow.experimentActionDropdown.open(); - await menuMove.menuItem('Move').pwLocator.click(); - await menuMove.moveModal.destinationWorkspace.selectMenuOption(workspace.name); - await menuMove.moveModal.destinationProject.pwLocator.waitFor({ state: 'visible' }); - await menuMove.moveModal.destinationProject.selectMenuOption(destinationProject.name); - await menuMove.moveModal.footer.submit.pwLocator.click(); - await menuMove.moveModal.pwLocator.waitFor({ state: 'hidden' }); + await experimentActionDropdown.menuItem('Move').pwLocator.click(); + await experimentActionDropdown.moveModal.destinationWorkspace.selectMenuOption( + workspace.name, + ); + await experimentActionDropdown.moveModal.destinationProject.pwLocator.waitFor({ + state: 'visible', + }); + await experimentActionDropdown.moveModal.destinationProject.selectMenuOption( + destinationProject.name, + ); + await experimentActionDropdown.moveModal.footer.submit.pwLocator.click(); + await experimentActionDropdown.moveModal.pwLocator.waitFor({ state: 'hidden' }); await newExperimentRow.pwLocator.waitFor({ state: 'hidden' }); From c8fafb3dda1736ab761a9ac50ddb3731dccbe654 Mon Sep 17 00:00:00 2001 From: Thiago Dallacqua Date: Tue, 22 Oct 2024 16:33:28 -0300 Subject: [PATCH 10/10] chore: fix assertion --- webui/react/src/e2e/tests/experimentList.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webui/react/src/e2e/tests/experimentList.spec.ts b/webui/react/src/e2e/tests/experimentList.spec.ts index 50910b1c907..ece0eaf536f 100644 --- a/webui/react/src/e2e/tests/experimentList.spec.ts +++ b/webui/react/src/e2e/tests/experimentList.spec.ts @@ -586,7 +586,7 @@ test.describe('Experiment List', () => { ).split(' ')[2], ); // returns in the format "Created experiment " - if (!Number.isNaN(expId)) throw new Error('No experiment ID was found'); + if (Number.isNaN(expId)) throw new Error('No experiment ID was found'); experimentId = expId; },