From 585f627135b65392c11e0ef604b09a8ad4671feb Mon Sep 17 00:00:00 2001 From: Fede Alonso Date: Fri, 13 Dec 2024 12:24:51 +0100 Subject: [PATCH 01/14] test: Cypress Pipelines e2e ODS2206 --- .../iris_pipeline_pip_index_url_compiled.yaml | 296 ++++++++++++++++++ .../pages/pipelines/pipelineRunsGlobal.ts | 9 + .../e2e/dataSciencePipelines/pipelines.cy.ts | 79 +++-- .../cypress/cypress/utils/fileImportUtils.ts | 8 + 4 files changed, 368 insertions(+), 24 deletions(-) create mode 100644 frontend/src/__tests__/cypress/cypress/fixtures/resources/pipelines/iris_pipeline_pip_index_url_compiled.yaml diff --git a/frontend/src/__tests__/cypress/cypress/fixtures/resources/pipelines/iris_pipeline_pip_index_url_compiled.yaml b/frontend/src/__tests__/cypress/cypress/fixtures/resources/pipelines/iris_pipeline_pip_index_url_compiled.yaml new file mode 100644 index 0000000000..1bd4ee8013 --- /dev/null +++ b/frontend/src/__tests__/cypress/cypress/fixtures/resources/pipelines/iris_pipeline_pip_index_url_compiled.yaml @@ -0,0 +1,296 @@ +# PIPELINE DEFINITION +# Name: iris-training-pipeline +# Inputs: +# neighbors: int [Default: 3.0] +# standard_scaler: bool [Default: True] +# Outputs: +# train-model-metrics: system.ClassificationMetrics +components: + comp-create-dataset: + executorLabel: exec-create-dataset + outputDefinitions: + artifacts: + iris_dataset: + artifactType: + schemaTitle: system.Dataset + schemaVersion: 0.0.1 + comp-normalize-dataset: + executorLabel: exec-normalize-dataset + inputDefinitions: + artifacts: + input_iris_dataset: + artifactType: + schemaTitle: system.Dataset + schemaVersion: 0.0.1 + parameters: + standard_scaler: + parameterType: BOOLEAN + outputDefinitions: + artifacts: + normalized_iris_dataset: + artifactType: + schemaTitle: system.Dataset + schemaVersion: 0.0.1 + comp-train-model: + executorLabel: exec-train-model + inputDefinitions: + artifacts: + normalized_iris_dataset: + artifactType: + schemaTitle: system.Dataset + schemaVersion: 0.0.1 + parameters: + n_neighbors: + parameterType: NUMBER_INTEGER + outputDefinitions: + artifacts: + metrics: + artifactType: + schemaTitle: system.ClassificationMetrics + schemaVersion: 0.0.1 + model: + artifactType: + schemaTitle: system.Model + schemaVersion: 0.0.1 +deploymentSpec: + executors: + exec-create-dataset: + container: + args: + - --executor_input + - '{{$}}' + - --function_to_execute + - create_dataset + command: + - sh + - -c + - "\nif ! [ -x \"$(command -v pip)\" ]; then\n python3 -m ensurepip ||\ + \ python3 -m ensurepip --user || apt-get install python3-pip\nfi\n\nPIP_DISABLE_PIP_VERSION_CHECK=1\ + \ python3 -m pip install --quiet --no-warn-script-location --index-url $PIP_INDEX_URL\ + \ --trusted-host $PIP_TRUSTED_HOST 'kfp==2.9.0' '--no-deps' 'typing-extensions>=3.7.4,<5;\ + \ python_version<\"3.9\"' && python3 -m pip install --quiet --no-warn-script-location\ + \ --index-url $PIP_INDEX_URL --trusted-host $PIP_TRUSTED_HOST 'pandas==2.2.0'\ + \ && \"$0\" \"$@\"\n" + - sh + - -ec + - 'program_path=$(mktemp -d) + + + printf "%s" "$0" > "$program_path/ephemeral_component.py" + + _KFP_RUNTIME=true python3 -m kfp.dsl.executor_main --component_module_path "$program_path/ephemeral_component.py" "$@" + + ' + - "\nimport kfp\nfrom kfp import dsl\nfrom kfp.dsl import *\nfrom typing import\ + \ *\n\ndef create_dataset(iris_dataset: Output[Dataset]):\n from io import\ + \ StringIO # noqa: PLC0415\n\n import pandas as pd # noqa: PLC0415\n\ + \n data = \"\"\"\n 5.1,3.5,1.4,0.2,Iris-setosa\n 4.9,3.0,1.4,0.2,Iris-setosa\n\ + \ 4.7,3.2,1.3,0.2,Iris-setosa\n 4.6,3.1,1.5,0.2,Iris-setosa\n 5.0,3.6,1.4,0.2,Iris-setosa\n\ + \ 5.7,3.8,1.7,0.3,Iris-setosa\n 5.1,3.8,1.5,0.3,Iris-setosa\n 5.4,3.4,1.7,0.2,Iris-setosa\n\ + \ 5.1,3.7,1.5,0.4,Iris-setosa\n 5.1,3.4,1.5,0.2,Iris-setosa\n 5.0,3.5,1.3,0.3,Iris-setosa\n\ + \ 4.5,2.3,1.3,0.3,Iris-setosa\n 4.4,3.2,1.3,0.2,Iris-setosa\n 5.0,3.5,1.6,0.6,Iris-setosa\n\ + \ 5.1,3.8,1.9,0.4,Iris-setosa\n 4.8,3.0,1.4,0.3,Iris-setosa\n 5.1,3.8,1.6,0.2,Iris-setosa\n\ + \ 4.6,3.2,1.4,0.2,Iris-setosa\n 5.3,3.7,1.5,0.2,Iris-setosa\n 5.0,3.3,1.4,0.2,Iris-setosa\n\ + \ 7.0,3.2,4.7,1.4,Iris-versicolor\n 6.4,3.2,4.5,1.5,Iris-versicolor\n\ + \ 6.9,3.1,4.9,1.5,Iris-versicolor\n 5.5,2.3,4.0,1.3,Iris-versicolor\n\ + \ 6.5,2.8,4.6,1.5,Iris-versicolor\n 6.2,2.2,4.5,1.5,Iris-versicolor\n\ + \ 5.6,2.5,3.9,1.1,Iris-versicolor\n 5.9,3.2,4.8,1.8,Iris-versicolor\n\ + \ 6.1,2.8,4.0,1.3,Iris-versicolor\n 6.3,2.5,4.9,1.5,Iris-versicolor\n\ + \ 6.1,2.8,4.7,1.2,Iris-versicolor\n 6.4,2.9,4.3,1.3,Iris-versicolor\n\ + \ 6.6,3.0,4.4,1.4,Iris-versicolor\n 5.6,2.7,4.2,1.3,Iris-versicolor\n\ + \ 5.7,3.0,4.2,1.2,Iris-versicolor\n 5.7,2.9,4.2,1.3,Iris-versicolor\n\ + \ 6.2,2.9,4.3,1.3,Iris-versicolor\n 5.1,2.5,3.0,1.1,Iris-versicolor\n\ + \ 5.7,2.8,4.1,1.3,Iris-versicolor\n 6.3,3.3,6.0,2.5,Iris-virginica\n\ + \ 5.8,2.7,5.1,1.9,Iris-virginica\n 7.1,3.0,5.9,2.1,Iris-virginica\n\ + \ 6.3,2.9,5.6,1.8,Iris-virginica\n 6.5,3.0,5.8,2.2,Iris-virginica\n\ + \ 6.9,3.1,5.1,2.3,Iris-virginica\n 5.8,2.7,5.1,1.9,Iris-virginica\n\ + \ 6.8,3.2,5.9,2.3,Iris-virginica\n 6.7,3.3,5.7,2.5,Iris-virginica\n\ + \ 6.7,3.0,5.2,2.3,Iris-virginica\n 6.3,2.5,5.0,1.9,Iris-virginica\n\ + \ 6.5,3.0,5.2,2.0,Iris-virginica\n 6.2,3.4,5.4,2.3,Iris-virginica\n\ + \ 5.9,3.0,5.1,1.8,Iris-virginica\n \"\"\"\n col_names = [\"Sepal_Length\"\ + , \"Sepal_Width\", \"Petal_Length\", \"Petal_Width\", \"Labels\"]\n df\ + \ = pd.read_csv(StringIO(data), names=col_names)\n\n with open(iris_dataset.path,\ + \ \"w\") as f:\n df.to_csv(f)\n\n" + image: registry.redhat.io/ubi8/python-39@sha256:3523b184212e1f2243e76d8094ab52b01ea3015471471290d011625e1763af61 + exec-normalize-dataset: + container: + args: + - --executor_input + - '{{$}}' + - --function_to_execute + - normalize_dataset + command: + - sh + - -c + - "\nif ! [ -x \"$(command -v pip)\" ]; then\n python3 -m ensurepip ||\ + \ python3 -m ensurepip --user || apt-get install python3-pip\nfi\n\nPIP_DISABLE_PIP_VERSION_CHECK=1\ + \ python3 -m pip install --quiet --no-warn-script-location --index-url $PIP_INDEX_URL\ + \ --trusted-host $PIP_TRUSTED_HOST 'kfp==2.9.0' '--no-deps' 'typing-extensions>=3.7.4,<5;\ + \ python_version<\"3.9\"' && python3 -m pip install --quiet --no-warn-script-location\ + \ --index-url $PIP_INDEX_URL --trusted-host $PIP_TRUSTED_HOST 'pandas==2.2.0'\ + \ 'scikit-learn==1.4.0' && \"$0\" \"$@\"\n" + - sh + - -ec + - 'program_path=$(mktemp -d) + + + printf "%s" "$0" > "$program_path/ephemeral_component.py" + + _KFP_RUNTIME=true python3 -m kfp.dsl.executor_main --component_module_path "$program_path/ephemeral_component.py" "$@" + + ' + - "\nimport kfp\nfrom kfp import dsl\nfrom kfp.dsl import *\nfrom typing import\ + \ *\n\ndef normalize_dataset(\n input_iris_dataset: Input[Dataset],\n\ + \ normalized_iris_dataset: Output[Dataset],\n standard_scaler: bool,\n\ + ):\n import pandas as pd # noqa: PLC0415\n from sklearn.preprocessing\ + \ import MinMaxScaler, StandardScaler # noqa: PLC0415\n\n with open(input_iris_dataset.path)\ + \ as f:\n df = pd.read_csv(f)\n labels = df.pop(\"Labels\")\n\n\ + \ scaler = StandardScaler() if standard_scaler else MinMaxScaler()\n\n\ + \ df = pd.DataFrame(scaler.fit_transform(df))\n df[\"Labels\"] = labels\n\ + \ normalized_iris_dataset.metadata[\"state\"] = \"Normalized\"\n with\ + \ open(normalized_iris_dataset.path, \"w\") as f:\n df.to_csv(f)\n\ + \n" + image: registry.redhat.io/ubi8/python-39@sha256:3523b184212e1f2243e76d8094ab52b01ea3015471471290d011625e1763af61 + exec-train-model: + container: + args: + - --executor_input + - '{{$}}' + - --function_to_execute + - train_model + command: + - sh + - -c + - "\nif ! [ -x \"$(command -v pip)\" ]; then\n python3 -m ensurepip ||\ + \ python3 -m ensurepip --user || apt-get install python3-pip\nfi\n\nPIP_DISABLE_PIP_VERSION_CHECK=1\ + \ python3 -m pip install --quiet --no-warn-script-location --index-url $PIP_INDEX_URL\ + \ --trusted-host $PIP_TRUSTED_HOST 'kfp==2.9.0' '--no-deps' 'typing-extensions>=3.7.4,<5;\ + \ python_version<\"3.9\"' && python3 -m pip install --quiet --no-warn-script-location\ + \ --index-url $PIP_INDEX_URL --trusted-host $PIP_TRUSTED_HOST 'pandas==2.2.0'\ + \ 'scikit-learn==1.4.0' && \"$0\" \"$@\"\n" + - sh + - -ec + - 'program_path=$(mktemp -d) + + + printf "%s" "$0" > "$program_path/ephemeral_component.py" + + _KFP_RUNTIME=true python3 -m kfp.dsl.executor_main --component_module_path "$program_path/ephemeral_component.py" "$@" + + ' + - "\nimport kfp\nfrom kfp import dsl\nfrom kfp.dsl import *\nfrom typing import\ + \ *\n\ndef train_model(\n normalized_iris_dataset: Input[Dataset],\n\ + \ model: Output[Model],\n metrics: Output[ClassificationMetrics],\n\ + \ n_neighbors: int,\n):\n import pickle # noqa: PLC0415\n\n import\ + \ pandas as pd # noqa: PLC0415\n from sklearn.metrics import confusion_matrix\ + \ # noqa: PLC0415\n from sklearn.model_selection import cross_val_predict,\ + \ train_test_split # noqa: PLC0415\n from sklearn.neighbors import KNeighborsClassifier\ + \ # noqa: PLC0415\n\n with open(normalized_iris_dataset.path) as f:\n\ + \ df = pd.read_csv(f)\n\n y = df.pop(\"Labels\")\n X = df\n\ + \n X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)\ + \ # noqa: F841\n\n clf = KNeighborsClassifier(n_neighbors=n_neighbors)\n\ + \ clf.fit(X_train, y_train)\n\n predictions = cross_val_predict(clf,\ + \ X_train, y_train, cv=3)\n metrics.log_confusion_matrix(\n [\"\ + Iris-Setosa\", \"Iris-Versicolour\", \"Iris-Virginica\"],\n confusion_matrix(y_train,\ + \ predictions).tolist(), # .tolist() to convert np array to list.\n \ + \ )\n\n model.metadata[\"framework\"] = \"scikit-learn\"\n with open(model.path,\ + \ \"wb\") as f:\n pickle.dump(clf, f)\n\n" + image: registry.redhat.io/ubi8/python-39@sha256:3523b184212e1f2243e76d8094ab52b01ea3015471471290d011625e1763af61 +pipelineInfo: + name: iris-training-pipeline +root: + dag: + outputs: + artifacts: + train-model-metrics: + artifactSelectors: + - outputArtifactKey: metrics + producerSubtask: train-model + tasks: + create-dataset: + cachingOptions: {} + componentRef: + name: comp-create-dataset + taskInfo: + name: create-dataset + normalize-dataset: + cachingOptions: {} + componentRef: + name: comp-normalize-dataset + dependentTasks: + - create-dataset + inputs: + artifacts: + input_iris_dataset: + taskOutputArtifact: + outputArtifactKey: iris_dataset + producerTask: create-dataset + parameters: + standard_scaler: + componentInputParameter: standard_scaler + taskInfo: + name: normalize-dataset + train-model: + cachingOptions: {} + componentRef: + name: comp-train-model + dependentTasks: + - normalize-dataset + inputs: + artifacts: + normalized_iris_dataset: + taskOutputArtifact: + outputArtifactKey: normalized_iris_dataset + producerTask: normalize-dataset + parameters: + n_neighbors: + componentInputParameter: neighbors + taskInfo: + name: train-model + inputDefinitions: + parameters: + neighbors: + defaultValue: 3.0 + isOptional: true + parameterType: NUMBER_INTEGER + standard_scaler: + defaultValue: true + isOptional: true + parameterType: BOOLEAN + outputDefinitions: + artifacts: + train-model-metrics: + artifactType: + schemaTitle: system.ClassificationMetrics + schemaVersion: 0.0.1 +schemaVersion: 2.1.0 +sdkVersion: kfp-2.9.0 +--- +platforms: + kubernetes: + deploymentSpec: + executors: + exec-create-dataset: + configMapAsEnv: + - configMapName: ds-pipeline-custom-env-vars + keyToEnv: + - configMapKey: pip_index_url + envVar: PIP_INDEX_URL + - configMapKey: pip_trusted_host + envVar: PIP_TRUSTED_HOST + exec-normalize-dataset: + configMapAsEnv: + - configMapName: ds-pipeline-custom-env-vars + keyToEnv: + - configMapKey: pip_index_url + envVar: PIP_INDEX_URL + - configMapKey: pip_trusted_host + envVar: PIP_TRUSTED_HOST + exec-train-model: + configMapAsEnv: + - configMapName: ds-pipeline-custom-env-vars + keyToEnv: + - configMapKey: pip_index_url + envVar: PIP_INDEX_URL + - configMapKey: pip_trusted_host + envVar: PIP_TRUSTED_HOST diff --git a/frontend/src/__tests__/cypress/cypress/pages/pipelines/pipelineRunsGlobal.ts b/frontend/src/__tests__/cypress/cypress/pages/pipelines/pipelineRunsGlobal.ts index fa41cd20d3..2b9e85424c 100644 --- a/frontend/src/__tests__/cypress/cypress/pages/pipelines/pipelineRunsGlobal.ts +++ b/frontend/src/__tests__/cypress/cypress/pages/pipelines/pipelineRunsGlobal.ts @@ -1,4 +1,5 @@ import { DeleteModal } from '~/__tests__/cypress/cypress/pages/components/DeleteModal'; +import { appChrome } from '../appChrome'; class PipelineRunsGlobal { visit(projectName: string, runType?: 'active' | 'archived' | 'scheduled') { @@ -9,6 +10,14 @@ class PipelineRunsGlobal { ); this.wait(); } + findNavItem() { + return appChrome.findNavItem('Data Science Pipelines', 'Runs'); + } + + navigate() { + this.findNavItem().click(); + this.wait(); + } private wait() { cy.findByTestId('app-page-title').contains('Runs'); diff --git a/frontend/src/__tests__/cypress/cypress/tests/e2e/dataSciencePipelines/pipelines.cy.ts b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataSciencePipelines/pipelines.cy.ts index f554df571a..2e260060b7 100644 --- a/frontend/src/__tests__/cypress/cypress/tests/e2e/dataSciencePipelines/pipelines.cy.ts +++ b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataSciencePipelines/pipelines.cy.ts @@ -3,45 +3,42 @@ import { HTPASSWD_CLUSTER_ADMIN_USER } from '~/__tests__/cypress/cypress/utils/e import { projectListPage, projectDetails } from '~/__tests__/cypress/cypress/pages/projects'; import { pipelineImportModal } from '~/__tests__/cypress/cypress/pages/pipelines/pipelineImportModal'; import { createRunPage } from '~/__tests__/cypress/cypress/pages/pipelines/createRunPage'; +import { pipelineRunsGlobal } from '~/__tests__/cypress/cypress/pages/pipelines/pipelineRunsGlobal'; import { pipelineDetails, pipelineRunDetails, } from '~/__tests__/cypress/cypress/pages/pipelines/topology'; import { provisionProjectForPipelines } from '~/__tests__/cypress/cypress/utils/pipelines'; +import { getIrisPipelinePath } from '../../../utils/fileImportUtils'; const projectName = 'test-pipelines-prj'; const dspaSecretName = 'dashboard-dspa-secret'; const testPipelineName = 'test-pipelines-pipeline'; +const testPipelineIrisName = 'test-iris-pipeline'; const testRunName = 'test-pipelines-run'; describe('An admin user can import and run a pipeline', { testIsolation: false }, () => { - before(() => { - // Create a Project for pipelines - provisionProjectForPipelines(projectName, dspaSecretName); - }); + // before(() => { + // // Create a Project for pipelines + // provisionProjectForPipelines(projectName, dspaSecretName); + // }); - after(() => { - // Delete provisioned Project - deleteOpenShiftProject(projectName); - }); + // after(() => { + // // Delete provisioned Project + // deleteOpenShiftProject(projectName); + // }); - it('An admin User can Import and Run a Pipeline', () => { - // Login as an admin + it.skip('An admin User can Import and Run a Pipeline', () => { + cy.step('Navigate to DSP ${projectName}'); cy.visitWithLogin('/', HTPASSWD_CLUSTER_ADMIN_USER); - - /** - * Import Pipeline by URL from Project Details view - */ projectListPage.navigate(); - - // Open the project projectListPage.filterProjectByName(projectName); projectListPage.findProjectLink(projectName).click(); + cy.step('Import a pipeline by URL'); // Increasing the timeout to ~3mins so the DSPA can be loaded projectDetails.findImportPipelineButton(180000).click(); - - // Fill tue Import Pipeline modal + // Fill the Import Pipeline modal pipelineImportModal.findPipelineNameInput().type(testPipelineName); pipelineImportModal.findPipelineDescriptionInput().type('Pipeline Description'); pipelineImportModal.findImportPipelineRadio().click(); @@ -56,20 +53,54 @@ describe('An admin user can import and run a pipeline', { testIsolation: false } // It can take a little longer to load pipelineDetails.findPageTitle(60000).should('have.text', testPipelineName); - /** - * Run the Pipeline using the Actions button in the pipeline detail view - */ - + cy.step('Run the pipeline from the Actions button in the pipeline detail view'); pipelineDetails.selectActionDropdownItem('Create run'); - - //Fill the Create run fields createRunPage.experimentSelect.findToggleButton().click(); createRunPage.selectExperimentByName('Default'); createRunPage.fillName(testRunName); createRunPage.fillDescription('Run Description'); createRunPage.findSubmitButton().click(); + cy.step('Expect the run to Succeed'); //Redirected to the Graph view of the created run pipelineRunDetails.expectStatusLabelToBe('Succeeded', 180000); }); + + it('Verify User Can Create, Run and Delete A DS Pipeline From DS Project Details Page Using Custom Pip Mirror', () => { + cy.step('Navigate to DSP ${projectName}'); + cy.visitWithLogin('/', HTPASSWD_CLUSTER_ADMIN_USER); + projectListPage.navigate(); + projectListPage.filterProjectByName(projectName); + projectListPage.findProjectLink(projectName).click(); + + cy.step('Import a pipeline from a yaml local file'); + // Increasing the timeout to ~3mins so the DSPA can be loaded + projectDetails.findImportPipelineButton(180000).click(); + // Fill the Import Pipeline modal + pipelineImportModal.findPipelineNameInput().type(testPipelineIrisName); + pipelineImportModal.findUploadPipelineRadio().click(); + pipelineImportModal.uploadPipelineYaml(getIrisPipelinePath()); + pipelineImportModal.submit(); + + // Verify that we are at the details page of the pipeline by checking the title + // It can take a little longer to load + pipelineDetails.findPageTitle(60000).should('have.text', testPipelineIrisName); + + cy.step('Create a ${testPipelineIrisName} pipeline run from the Runs view'); + pipelineRunsGlobal.navigate(); + pipelineRunsGlobal.findCreateRunButton().click(); + + // cy.step('Run the pipeline from the Actions button in the pipeline detail view'); + // pipelineDetails.selectActionDropdownItem('Create run'); + // //Fill the Create run fields + // createRunPage.experimentSelect.findToggleButton().click(); + // createRunPage.selectExperimentByName('Defsault'); + // createRunPage.fillName(testRunName); + // createRunPage.fillDescription('Run Description'); + // createRunPage.findSubmitButton().click(); + + // cy.step('Expect the run to Succeed'); + // //Redirected to the Graph view of the created run + // pipelineRunDetails.expectStatusLabelToBe('Succeeded', 180000); + }); }); diff --git a/frontend/src/__tests__/cypress/cypress/utils/fileImportUtils.ts b/frontend/src/__tests__/cypress/cypress/utils/fileImportUtils.ts index e9efd8d864..7af9152886 100644 --- a/frontend/src/__tests__/cypress/cypress/utils/fileImportUtils.ts +++ b/frontend/src/__tests__/cypress/cypress/utils/fileImportUtils.ts @@ -11,6 +11,10 @@ export const MODEL_SERVING_PATHS = { }, }; +export const PIPELINES_PATHS = { + IRIS_INDEX_URL: 'resources/pipelines/iris_pipeline_pip_index_url_compiled.yaml', +}; + // Utility function to get fixture path export function getFixturePath(relativePath: string): string { return path.join('cypress/fixtures', relativePath); @@ -24,3 +28,7 @@ export function getMultiModelPath(): string { export function getSingleModelPath(): string { return getFixturePath(MODEL_SERVING_PATHS.SINGLE_MODEL.SINGLE_SERVING_KSERVE_RUNTIME); } + +export function getIrisPipelinePath(): string { + return getFixturePath(PIPELINES_PATHS.IRIS_INDEX_URL); +} From fd43246819a840dd8d1f10e8274c2cb09fde55c0 Mon Sep 17 00:00:00 2001 From: Fede Alonso Date: Mon, 16 Dec 2024 17:52:15 +0100 Subject: [PATCH 02/14] Save point --- .../subComponents/SearchSelector.ts | 12 +++ .../cypress/cypress/pages/components/table.ts | 2 +- .../pages/pipelines/pipelineRunsGlobal.ts | 14 +-- .../pages/pipelines/pipelinesGlobal.ts | 2 +- .../cypress/pages/pipelines/pipelinesTable.ts | 7 +- .../cypress/support/commands/application.ts | 2 +- .../e2e/dataSciencePipelines/pipelines.cy.ts | 95 +++++++++++++++---- .../src/__tests__/cypress/cypress/types.ts | 7 ++ .../cypress/utils/oc_commands/configmap.ts | 37 ++++++++ .../cypress/cypress/utils/testConfig.ts | 6 ++ .../cypress/test-variables.yml.example | 5 +- 11 files changed, 155 insertions(+), 34 deletions(-) create mode 100644 frontend/src/__tests__/cypress/cypress/utils/oc_commands/configmap.ts diff --git a/frontend/src/__tests__/cypress/cypress/pages/components/subComponents/SearchSelector.ts b/frontend/src/__tests__/cypress/cypress/pages/components/subComponents/SearchSelector.ts index 38a13d49ef..473cda970a 100644 --- a/frontend/src/__tests__/cypress/cypress/pages/components/subComponents/SearchSelector.ts +++ b/frontend/src/__tests__/cypress/cypress/pages/components/subComponents/SearchSelector.ts @@ -36,4 +36,16 @@ export class SearchSelector extends SubComponentBase { findMenuList(): Cypress.Chainable> { return this.findContextualItem('menuList'); } + + // Search for an item by typing into the search input + searchItem(name: string): void { + this.findSearchInput().clear().type(name); + } + + // Perform the entire process: open, search, and select + openAndSelectItem(name: string): void { + this.findToggleButton().click(); // Open the dropdown + this.searchItem(name); // Type the name in the search input + this.selectItem(name); // Select the item + } } diff --git a/frontend/src/__tests__/cypress/cypress/pages/components/table.ts b/frontend/src/__tests__/cypress/cypress/pages/components/table.ts index 7915e8d252..14fb30c2ae 100644 --- a/frontend/src/__tests__/cypress/cypress/pages/components/table.ts +++ b/frontend/src/__tests__/cypress/cypress/pages/components/table.ts @@ -17,7 +17,7 @@ export class TableRow extends Contextual { } findKebabAction(name: string): Cypress.Chainable> { - return this.find().findKebabAction(name); + return this.find().findKebabAction(name).should('exist').and('be.visible'); } findKebab(): Cypress.Chainable> { diff --git a/frontend/src/__tests__/cypress/cypress/pages/pipelines/pipelineRunsGlobal.ts b/frontend/src/__tests__/cypress/cypress/pages/pipelines/pipelineRunsGlobal.ts index 2b9e85424c..6abf0496bc 100644 --- a/frontend/src/__tests__/cypress/cypress/pages/pipelines/pipelineRunsGlobal.ts +++ b/frontend/src/__tests__/cypress/cypress/pages/pipelines/pipelineRunsGlobal.ts @@ -10,12 +10,9 @@ class PipelineRunsGlobal { ); this.wait(); } - findNavItem() { - return appChrome.findNavItem('Data Science Pipelines', 'Runs'); - } navigate() { - this.findNavItem().click(); + appChrome.findNavItem('Runs', 'Data Science Pipelines').click(); this.wait(); } @@ -41,7 +38,7 @@ class PipelineRunsGlobal { } findProjectSelect() { - return cy.findByTestId('project-selector-dropdown'); + return cy.findByTestId('project-selector-toggle'); } findCreateRunButton() { @@ -69,7 +66,12 @@ class PipelineRunsGlobal { } selectProjectByName(name: string) { - this.findProjectSelect().findDropdownItem(name).click(); + this.findProjectSelect().click(); + cy.findByTestId('project-selector-search').fill(name); + cy.findByTestId('project-selector-menuList') + .contains('button', name) + .should('be.visible') + .click(); } } diff --git a/frontend/src/__tests__/cypress/cypress/pages/pipelines/pipelinesGlobal.ts b/frontend/src/__tests__/cypress/cypress/pages/pipelines/pipelinesGlobal.ts index 11ef14e657..fbe8e45c2e 100644 --- a/frontend/src/__tests__/cypress/cypress/pages/pipelines/pipelinesGlobal.ts +++ b/frontend/src/__tests__/cypress/cypress/pages/pipelines/pipelinesGlobal.ts @@ -12,7 +12,7 @@ class PipelinesGlobal { } navigate() { - appChrome.findNavItem('Data Science Pipelines').click(); + appChrome.findNavItem('Pipelines', 'Data Science Pipelines').click(); this.wait(); } diff --git a/frontend/src/__tests__/cypress/cypress/pages/pipelines/pipelinesTable.ts b/frontend/src/__tests__/cypress/cypress/pages/pipelines/pipelinesTable.ts index 447de8f5be..16d45f676b 100644 --- a/frontend/src/__tests__/cypress/cypress/pages/pipelines/pipelinesTable.ts +++ b/frontend/src/__tests__/cypress/cypress/pages/pipelines/pipelinesTable.ts @@ -168,9 +168,10 @@ class PipelinesTable { getRowById(id: string) { return new PipelinesTableRow( () => - this.find().findByTestId(['pipeline-row', id]) as unknown as Cypress.Chainable< - JQuery - >, + this.find() + .findByTestId(['pipeline-row', id]) + .should('exist') + .and('be.visible') as unknown as Cypress.Chainable>, ); } diff --git a/frontend/src/__tests__/cypress/cypress/support/commands/application.ts b/frontend/src/__tests__/cypress/cypress/support/commands/application.ts index 247550ec2e..653d635ac6 100644 --- a/frontend/src/__tests__/cypress/cypress/support/commands/application.ts +++ b/frontend/src/__tests__/cypress/cypress/support/commands/application.ts @@ -232,7 +232,7 @@ Cypress.Commands.add('findMenuItem', { prevSubject: 'element' }, (subject, name) if ($el.attr('aria-expanded') === 'false') { cy.wrap($el).click(); } - return cy.get('[data-ouia-component-type="PF6/Menu"]').findByRole('menuitem', { name }); + return cy.get('[data-ouia-component-type="PF6/Menu"]').find('td').contains(name); }); }); diff --git a/frontend/src/__tests__/cypress/cypress/tests/e2e/dataSciencePipelines/pipelines.cy.ts b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataSciencePipelines/pipelines.cy.ts index 2e260060b7..0a903a78cf 100644 --- a/frontend/src/__tests__/cypress/cypress/tests/e2e/dataSciencePipelines/pipelines.cy.ts +++ b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataSciencePipelines/pipelines.cy.ts @@ -4,12 +4,18 @@ import { projectListPage, projectDetails } from '~/__tests__/cypress/cypress/pag import { pipelineImportModal } from '~/__tests__/cypress/cypress/pages/pipelines/pipelineImportModal'; import { createRunPage } from '~/__tests__/cypress/cypress/pages/pipelines/createRunPage'; import { pipelineRunsGlobal } from '~/__tests__/cypress/cypress/pages/pipelines/pipelineRunsGlobal'; +import { + pipelinesGlobal, + pipelineDeleteModal, +} from '~/__tests__/cypress/cypress/pages/pipelines/pipelinesGlobal'; +import { pipelinesTable } from '~/__tests__/cypress/cypress/pages/pipelines/pipelinesTable'; import { pipelineDetails, pipelineRunDetails, } from '~/__tests__/cypress/cypress/pages/pipelines/topology'; import { provisionProjectForPipelines } from '~/__tests__/cypress/cypress/utils/pipelines'; import { getIrisPipelinePath } from '../../../utils/fileImportUtils'; +import { createOpenShiftConfigMap } from '../../../utils/oc_commands/configmap'; const projectName = 'test-pipelines-prj'; const dspaSecretName = 'dashboard-dspa-secret'; @@ -18,15 +24,15 @@ const testPipelineIrisName = 'test-iris-pipeline'; const testRunName = 'test-pipelines-run'; describe('An admin user can import and run a pipeline', { testIsolation: false }, () => { - // before(() => { - // // Create a Project for pipelines - // provisionProjectForPipelines(projectName, dspaSecretName); - // }); + before(() => { + // Create a Project for pipelines + provisionProjectForPipelines(projectName, dspaSecretName); + }); - // after(() => { - // // Delete provisioned Project - // deleteOpenShiftProject(projectName); - // }); + after(() => { + // Delete provisioned Project + deleteOpenShiftProject(projectName); + }); it.skip('An admin User can Import and Run a Pipeline', () => { cy.step('Navigate to DSP ${projectName}'); @@ -67,7 +73,17 @@ describe('An admin user can import and run a pipeline', { testIsolation: false } }); it('Verify User Can Create, Run and Delete A DS Pipeline From DS Project Details Page Using Custom Pip Mirror', () => { - cy.step('Navigate to DSP ${projectName}'); + let pipeline_id: string = ''; + let version_id: string = ''; + + cy.step('Create Pipelines ConfigMap With Custom Pip Index Url And Trusted Host '); + const pipConfig = Cypress.env('PIP_CONFIG'); + createOpenShiftConfigMap('ds-pipeline-custom-env-vars', projectName, { + pip_index_url: pipConfig.PIP_INDEX_URL, + pip_trusted_host: pipConfig.PIP_TRUSTED_HOST, + }); + + cy.step(`Navigate to DSP ${projectName}`); cy.visitWithLogin('/', HTPASSWD_CLUSTER_ADMIN_USER); projectListPage.navigate(); projectListPage.filterProjectByName(projectName); @@ -86,21 +102,58 @@ describe('An admin user can import and run a pipeline', { testIsolation: false } // It can take a little longer to load pipelineDetails.findPageTitle(60000).should('have.text', testPipelineIrisName); - cy.step('Create a ${testPipelineIrisName} pipeline run from the Runs view'); + cy.url().then((currentUrl) => { + const regex = /\/pipelines\/[^/]+\/([^/]+)\/([^/]+)\/view/; + const match = currentUrl.match(regex); + + if (match) { + pipeline_id = match[1]; + version_id = match[2]; + cy.log(`Pipeline ID: ${pipeline_id}`); + cy.log(`Version ID: ${version_id}`); + } else { + throw new Error('Pipeline ID and Version ID could not be extracted from the URL.'); + } + }); + + cy.step(`Create a ${testPipelineIrisName} pipeline run from the Runs view`); pipelineRunsGlobal.navigate(); + pipelineRunsGlobal.selectProjectByName(projectName); pipelineRunsGlobal.findCreateRunButton().click(); - // cy.step('Run the pipeline from the Actions button in the pipeline detail view'); - // pipelineDetails.selectActionDropdownItem('Create run'); + cy.step('Run the pipeline from the Runs view'); // //Fill the Create run fields - // createRunPage.experimentSelect.findToggleButton().click(); - // createRunPage.selectExperimentByName('Defsault'); - // createRunPage.fillName(testRunName); - // createRunPage.fillDescription('Run Description'); - // createRunPage.findSubmitButton().click(); - - // cy.step('Expect the run to Succeed'); - // //Redirected to the Graph view of the created run - // pipelineRunDetails.expectStatusLabelToBe('Succeeded', 180000); + createRunPage.experimentSelect.findToggleButton().click(); + createRunPage.selectExperimentByName('Default'); + createRunPage.fillName(testRunName); + createRunPage.fillDescription('Run Description'); + createRunPage.pipelineSelect.openAndSelectItem(testPipelineIrisName); + createRunPage.pipelineVersionSelect.selectItem(testPipelineIrisName); + createRunPage.findSubmitButton().click(); + + cy.step('Expect the run to Succeed'); + pipelineRunDetails.expectStatusLabelToBe('Succeeded', 180000); + + cy.step('Delete the pipeline version'); + pipelinesGlobal.navigate(); + // pipelineRunsGlobal.selectProjectByName(projectName); + const pipelineRowWithVersion = pipelinesTable.getRowById(pipeline_id); + pipelineRowWithVersion.findExpandButton().click(); + pipelineRowWithVersion + .getPipelineVersionRowById(version_id) + .findKebabAction('Delete pipeline version') + .click(); + pipelineDeleteModal.findInput().fill(testPipelineIrisName); + pipelineDeleteModal.findSubmitButton().click(); + + cy.step('Delete the pipeline'); + const pipelineRow = pipelinesTable.getRowById(pipeline_id); + pipelineRow.findKebabAction('Delete pipeline').click(); + pipelineDeleteModal.findInput().fill(testPipelineIrisName); + pipelineDeleteModal.findSubmitButton().click(); }); }); + +// logPipeline ID: 9a4523be-a396-46fd-b3b7-bd78398d06fa +// 52 +// logVersion ID: e3842a7f-b89b-4df4-a808-aaf684b8631d diff --git a/frontend/src/__tests__/cypress/cypress/types.ts b/frontend/src/__tests__/cypress/cypress/types.ts index e494a45e81..c17d6d3f8a 100644 --- a/frontend/src/__tests__/cypress/cypress/types.ts +++ b/frontend/src/__tests__/cypress/cypress/types.ts @@ -92,6 +92,7 @@ export type TestConfig = { OCP_ADMIN_USER: UserAuthConfig; S3: AWSS3Buckets; APPLICATIONS_NAMESPACE: NamespaceConfig; + PIP_CONFIG: PipConfig; }; export type DataScienceProjectData = { @@ -158,6 +159,12 @@ export type ResourcesData = { CustomTutorial: ResourceData[]; }; }; + export type NamespaceConfig = { APPLICATIONS_NAMESPACE: string; }; + +export type PipConfig = { + PIP_INDEX_URL: string; + PIP_TRUSTED_HOST: string; +}; diff --git a/frontend/src/__tests__/cypress/cypress/utils/oc_commands/configmap.ts b/frontend/src/__tests__/cypress/cypress/utils/oc_commands/configmap.ts new file mode 100644 index 0000000000..69a0f0bea6 --- /dev/null +++ b/frontend/src/__tests__/cypress/cypress/utils/oc_commands/configmap.ts @@ -0,0 +1,37 @@ +import type { CommandLineResult, DashboardConfig } from '~/__tests__/cypress/cypress/types'; + +/** + * Create an OpenShift ConfigMap + * + * This function creates a ConfigMap in the specified OpenShift namespace with the provided key-value pairs. + * + * @param configMapName Name of the ConfigMap to be created + * @param namespace Namespace in which the ConfigMap will be created + * @param keyValues An object containing key-value pairs to include in the ConfigMap + * Each key will be used as the ConfigMap variable, and the corresponding value as its value. + * @returns Cypress.Chainable - Result object of the `oc` command execution + * @throws Error - If the `oc create configmap` command fails + */ +export const createOpenShiftConfigMap = ( + configMapName: string, + namespace: string, + keyValues: Record, // Object of key-value pairs +): Cypress.Chainable => { + // Construct the `--from-literal` arguments dynamically + const literals = Object.entries(keyValues) + .map(([key, value]) => `--from-literal=${key}=${value}`) + .join(' '); + + // Construct the `oc create configmap` command + const ocCommand = `oc create configmap ${configMapName} -n ${namespace} ${literals}`; + + return cy.exec(ocCommand, { failOnNonZeroExit: false }).then((result) => { + if (result.code !== 0) { + cy.log(`ERROR creating ConfigMap ${configMapName} in namespace ${namespace} + stdout: ${result.stdout} + stderr: ${result.stderr}`); + throw new Error(`Command failed with code ${result.code}`); + } + return result; + }); +}; diff --git a/frontend/src/__tests__/cypress/cypress/utils/testConfig.ts b/frontend/src/__tests__/cypress/cypress/utils/testConfig.ts index c58b97cc73..e1912a6112 100644 --- a/frontend/src/__tests__/cypress/cypress/utils/testConfig.ts +++ b/frontend/src/__tests__/cypress/cypress/utils/testConfig.ts @@ -8,6 +8,7 @@ import type { TestConfig, AWSS3BucketDetails, AWSS3Buckets, + PipConfig, } from '~/__tests__/cypress/cypress/types'; [ @@ -52,6 +53,10 @@ const AWS_PIPELINES: AWSS3Buckets = { BUCKET_2: AWS_PIPELINES_BUCKET_DETAILS, }; const TEST_NAMESPACE = testConfig?.APPLICATIONS_NAMESPACE; +const PIP_CONFIG: PipConfig = { + PIP_INDEX_URL: testConfig?.PIP_CONFIG.PIP_INDEX_URL || '', + PIP_TRUSTED_HOST: testConfig?.PIP_CONFIG.PIP_TRUSTED_HOST || '', +}; // spread the cypressEnv variables into the cypress config export const cypressEnv = { @@ -59,6 +64,7 @@ export const cypressEnv = { HTPASSWD_CLUSTER_ADMIN_USER, AWS_PIPELINES, TEST_NAMESPACE, + PIP_CONFIG, }; // re-export the updated process env diff --git a/frontend/src/__tests__/cypress/test-variables.yml.example b/frontend/src/__tests__/cypress/test-variables.yml.example index ea157e30e7..ce0a3827a2 100644 --- a/frontend/src/__tests__/cypress/test-variables.yml.example +++ b/frontend/src/__tests__/cypress/test-variables.yml.example @@ -17,4 +17,7 @@ S3: BUCKET_2: NAME: pipeline-bucket-name REGION: pipeline-bucket-region - ENDPOINT: https://pipeline-bucket-endpoint.com/ \ No newline at end of file + ENDPOINT: https://pipeline-bucket-endpoint.com/ +PIP_CONFIG: + PIP_INDEX_URL: https://pypi.org/simple + PIP_TRUSTED_HOST: pypi.org \ No newline at end of file From 59e0d3c47519a898e651e819fe1d17a35a8d6041 Mon Sep 17 00:00:00 2001 From: Fede Alonso Date: Tue, 17 Dec 2024 15:28:11 +0100 Subject: [PATCH 03/14] checkpoint --- .../e2e/dataSciencePipelines/pipelines.cy.ts | 87 ++++++++++--------- 1 file changed, 46 insertions(+), 41 deletions(-) diff --git a/frontend/src/__tests__/cypress/cypress/tests/e2e/dataSciencePipelines/pipelines.cy.ts b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataSciencePipelines/pipelines.cy.ts index 0a903a78cf..ba1d4286f6 100644 --- a/frontend/src/__tests__/cypress/cypress/tests/e2e/dataSciencePipelines/pipelines.cy.ts +++ b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataSciencePipelines/pipelines.cy.ts @@ -99,9 +99,10 @@ describe('An admin user can import and run a pipeline', { testIsolation: false } pipelineImportModal.submit(); // Verify that we are at the details page of the pipeline by checking the title - // It can take a little longer to load + // It can take a little longer than expected to load pipelineDetails.findPageTitle(60000).should('have.text', testPipelineIrisName); + // Get the pipeline ID and version ID from the URL cy.url().then((currentUrl) => { const regex = /\/pipelines\/[^/]+\/([^/]+)\/([^/]+)\/view/; const match = currentUrl.match(regex); @@ -114,46 +115,50 @@ describe('An admin user can import and run a pipeline', { testIsolation: false } } else { throw new Error('Pipeline ID and Version ID could not be extracted from the URL.'); } - }); - - cy.step(`Create a ${testPipelineIrisName} pipeline run from the Runs view`); - pipelineRunsGlobal.navigate(); - pipelineRunsGlobal.selectProjectByName(projectName); - pipelineRunsGlobal.findCreateRunButton().click(); - - cy.step('Run the pipeline from the Runs view'); - // //Fill the Create run fields - createRunPage.experimentSelect.findToggleButton().click(); - createRunPage.selectExperimentByName('Default'); - createRunPage.fillName(testRunName); - createRunPage.fillDescription('Run Description'); - createRunPage.pipelineSelect.openAndSelectItem(testPipelineIrisName); - createRunPage.pipelineVersionSelect.selectItem(testPipelineIrisName); - createRunPage.findSubmitButton().click(); - cy.step('Expect the run to Succeed'); - pipelineRunDetails.expectStatusLabelToBe('Succeeded', 180000); - - cy.step('Delete the pipeline version'); - pipelinesGlobal.navigate(); - // pipelineRunsGlobal.selectProjectByName(projectName); - const pipelineRowWithVersion = pipelinesTable.getRowById(pipeline_id); - pipelineRowWithVersion.findExpandButton().click(); - pipelineRowWithVersion - .getPipelineVersionRowById(version_id) - .findKebabAction('Delete pipeline version') - .click(); - pipelineDeleteModal.findInput().fill(testPipelineIrisName); - pipelineDeleteModal.findSubmitButton().click(); - - cy.step('Delete the pipeline'); - const pipelineRow = pipelinesTable.getRowById(pipeline_id); - pipelineRow.findKebabAction('Delete pipeline').click(); - pipelineDeleteModal.findInput().fill(testPipelineIrisName); - pipelineDeleteModal.findSubmitButton().click(); + cy.step(`Create a ${testPipelineIrisName} pipeline run from the Runs view`); + pipelineRunsGlobal.navigate(); + pipelineRunsGlobal.selectProjectByName(projectName); + pipelineRunsGlobal.findCreateRunButton().click(); + + cy.step('Run the pipeline from the Runs view'); + createRunPage.experimentSelect.findToggleButton().click(); + createRunPage.selectExperimentByName('Default'); + createRunPage.fillName(testRunName); + createRunPage.fillDescription('Run Description'); + createRunPage.pipelineSelect.openAndSelectItem(testPipelineIrisName); + createRunPage.pipelineVersionSelect.selectItem(testPipelineIrisName); + createRunPage.findSubmitButton().click(); + + cy.step('Expect the run to Succeed'); + pipelineRunDetails.expectStatusLabelToBe('Succeeded', 180000); + + cy.step('Delete the pipeline version'); + pipelinesGlobal.navigate(); + // pipelineRunsGlobal.selectProjectByName(projectName); + const pipelineRowWithVersion = pipelinesTable.getRowById(pipeline_id); + pipelineRowWithVersion.findExpandButton().click(); + pipelineRowWithVersion + .getPipelineVersionRowById(version_id) + .findKebabAction('Delete pipeline version') + .click(); + pipelineDeleteModal.findInput().fill(testPipelineIrisName); + pipelineDeleteModal.findSubmitButton().click(); + + cy.step('Verify that the pipeline version no longer exist'); + // cy.wait(1000); // There's a reload spinner which sometimes take a little bit longer + const pipelineRowWithVersionDeleted = pipelinesTable.getRowById(pipeline_id); + pipelineRowWithVersionDeleted.findExpandButton().click(); + pipelineRowWithVersionDeleted.shouldNotHavePipelineVersion(); + + cy.step('Delete the pipeline'); + const pipelineRow = pipelinesTable.getRowById(pipeline_id); + pipelineRow.findKebabAction('Delete pipeline').click(); + pipelineDeleteModal.findInput().fill(testPipelineIrisName); + pipelineDeleteModal.findSubmitButton().click(); + + cy.step('Verify that the pipeline no longer exist'); + pipelinesTable.shouldBeEmpty(); + }); }); }); - -// logPipeline ID: 9a4523be-a396-46fd-b3b7-bd78398d06fa -// 52 -// logVersion ID: e3842a7f-b89b-4df4-a808-aaf684b8631d From a50b6da9895b7946a04fffd3d6b348261226e431 Mon Sep 17 00:00:00 2001 From: Fede Alonso Date: Tue, 17 Dec 2024 16:56:22 +0100 Subject: [PATCH 04/14] try to wait to the modal to be closed --- .../cypress/pages/pipelines/pipelineRunsGlobal.ts | 2 +- .../tests/e2e/dataSciencePipelines/pipelines.cy.ts | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/frontend/src/__tests__/cypress/cypress/pages/pipelines/pipelineRunsGlobal.ts b/frontend/src/__tests__/cypress/cypress/pages/pipelines/pipelineRunsGlobal.ts index 6abf0496bc..cec6f12013 100644 --- a/frontend/src/__tests__/cypress/cypress/pages/pipelines/pipelineRunsGlobal.ts +++ b/frontend/src/__tests__/cypress/cypress/pages/pipelines/pipelineRunsGlobal.ts @@ -1,5 +1,5 @@ import { DeleteModal } from '~/__tests__/cypress/cypress/pages/components/DeleteModal'; -import { appChrome } from '../appChrome'; +import { appChrome } from '~/__tests__/cypress/cypress/pages/appChrome'; class PipelineRunsGlobal { visit(projectName: string, runType?: 'active' | 'archived' | 'scheduled') { diff --git a/frontend/src/__tests__/cypress/cypress/tests/e2e/dataSciencePipelines/pipelines.cy.ts b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataSciencePipelines/pipelines.cy.ts index ba1d4286f6..849708d033 100644 --- a/frontend/src/__tests__/cypress/cypress/tests/e2e/dataSciencePipelines/pipelines.cy.ts +++ b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataSciencePipelines/pipelines.cy.ts @@ -14,8 +14,8 @@ import { pipelineRunDetails, } from '~/__tests__/cypress/cypress/pages/pipelines/topology'; import { provisionProjectForPipelines } from '~/__tests__/cypress/cypress/utils/pipelines'; -import { getIrisPipelinePath } from '../../../utils/fileImportUtils'; -import { createOpenShiftConfigMap } from '../../../utils/oc_commands/configmap'; +import { getIrisPipelinePath } from '~/__tests__/cypress/cypress/utils/fileImportUtils'; +import { createOpenShiftConfigMap } from '~/__tests__/cypress/cypress/utils/oc_commands/configmap'; const projectName = 'test-pipelines-prj'; const dspaSecretName = 'dashboard-dspa-secret'; @@ -73,8 +73,8 @@ describe('An admin user can import and run a pipeline', { testIsolation: false } }); it('Verify User Can Create, Run and Delete A DS Pipeline From DS Project Details Page Using Custom Pip Mirror', () => { - let pipeline_id: string = ''; - let version_id: string = ''; + let pipeline_id = ''; + let version_id = ''; cy.step('Create Pipelines ConfigMap With Custom Pip Index Url And Trusted Host '); const pipConfig = Cypress.env('PIP_CONFIG'); @@ -144,6 +144,7 @@ describe('An admin user can import and run a pipeline', { testIsolation: false } .click(); pipelineDeleteModal.findInput().fill(testPipelineIrisName); pipelineDeleteModal.findSubmitButton().click(); + pipelineDeleteModal.shouldBeOpen(false); cy.step('Verify that the pipeline version no longer exist'); // cy.wait(1000); // There's a reload spinner which sometimes take a little bit longer From 5ac66398c6809913ca0e69d39600203497192f07 Mon Sep 17 00:00:00 2001 From: Fede Alonso Date: Tue, 17 Dec 2024 18:12:48 +0100 Subject: [PATCH 05/14] lint fixes --- .../e2e/dataSciencePipelines/pipelines.cy.ts | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/frontend/src/__tests__/cypress/cypress/tests/e2e/dataSciencePipelines/pipelines.cy.ts b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataSciencePipelines/pipelines.cy.ts index 849708d033..bbbcb26c53 100644 --- a/frontend/src/__tests__/cypress/cypress/tests/e2e/dataSciencePipelines/pipelines.cy.ts +++ b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataSciencePipelines/pipelines.cy.ts @@ -73,9 +73,6 @@ describe('An admin user can import and run a pipeline', { testIsolation: false } }); it('Verify User Can Create, Run and Delete A DS Pipeline From DS Project Details Page Using Custom Pip Mirror', () => { - let pipeline_id = ''; - let version_id = ''; - cy.step('Create Pipelines ConfigMap With Custom Pip Index Url And Trusted Host '); const pipConfig = Cypress.env('PIP_CONFIG'); createOpenShiftConfigMap('ds-pipeline-custom-env-vars', projectName, { @@ -108,10 +105,9 @@ describe('An admin user can import and run a pipeline', { testIsolation: false } const match = currentUrl.match(regex); if (match) { - pipeline_id = match[1]; - version_id = match[2]; - cy.log(`Pipeline ID: ${pipeline_id}`); - cy.log(`Version ID: ${version_id}`); + const [, pipelineId, versionId] = match; + cy.log(`Pipeline ID: ${pipelineId}`); + cy.log(`Version ID: ${versionId}`); } else { throw new Error('Pipeline ID and Version ID could not be extracted from the URL.'); } @@ -136,24 +132,25 @@ describe('An admin user can import and run a pipeline', { testIsolation: false } cy.step('Delete the pipeline version'); pipelinesGlobal.navigate(); // pipelineRunsGlobal.selectProjectByName(projectName); - const pipelineRowWithVersion = pipelinesTable.getRowById(pipeline_id); + const pipelineRowWithVersion = pipelinesTable.getRowById(pipelineId); pipelineRowWithVersion.findExpandButton().click(); pipelineRowWithVersion - .getPipelineVersionRowById(version_id) + .getPipelineVersionRowById(versionId) .findKebabAction('Delete pipeline version') .click(); pipelineDeleteModal.findInput().fill(testPipelineIrisName); pipelineDeleteModal.findSubmitButton().click(); - pipelineDeleteModal.shouldBeOpen(false); + // The line below it's not working due to a bug + // pipelineDeleteModal.shouldBeOpen(false) + cy.get('[role=dialog]').should('not.exist'); cy.step('Verify that the pipeline version no longer exist'); - // cy.wait(1000); // There's a reload spinner which sometimes take a little bit longer - const pipelineRowWithVersionDeleted = pipelinesTable.getRowById(pipeline_id); + const pipelineRowWithVersionDeleted = pipelinesTable.getRowById(pipelineId); pipelineRowWithVersionDeleted.findExpandButton().click(); pipelineRowWithVersionDeleted.shouldNotHavePipelineVersion(); cy.step('Delete the pipeline'); - const pipelineRow = pipelinesTable.getRowById(pipeline_id); + const pipelineRow = pipelinesTable.getRowById(pipelineId); pipelineRow.findKebabAction('Delete pipeline').click(); pipelineDeleteModal.findInput().fill(testPipelineIrisName); pipelineDeleteModal.findSubmitButton().click(); From 90d54844f3aa64f388fcad6986e081581a988db1 Mon Sep 17 00:00:00 2001 From: Fede Alonso Date: Tue, 17 Dec 2024 18:40:00 +0100 Subject: [PATCH 06/14] Unskip first test --- .../cypress/tests/e2e/dataSciencePipelines/pipelines.cy.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/__tests__/cypress/cypress/tests/e2e/dataSciencePipelines/pipelines.cy.ts b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataSciencePipelines/pipelines.cy.ts index bbbcb26c53..c39308c73c 100644 --- a/frontend/src/__tests__/cypress/cypress/tests/e2e/dataSciencePipelines/pipelines.cy.ts +++ b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataSciencePipelines/pipelines.cy.ts @@ -34,7 +34,7 @@ describe('An admin user can import and run a pipeline', { testIsolation: false } deleteOpenShiftProject(projectName); }); - it.skip('An admin User can Import and Run a Pipeline', () => { + it('An admin User can Import and Run a Pipeline', () => { cy.step('Navigate to DSP ${projectName}'); cy.visitWithLogin('/', HTPASSWD_CLUSTER_ADMIN_USER); projectListPage.navigate(); From c9774fc1aed31afb57a1e3d607524aeaf86b2cd1 Mon Sep 17 00:00:00 2001 From: Fede Alonso Date: Wed, 18 Dec 2024 07:09:11 +0100 Subject: [PATCH 07/14] Split into two different files --- ...eateRunDeletePipelineCustomPipMirror.cy.ts | 123 ++++++++++++++++++ .../e2e/dataSciencePipelines/pipelines.cy.ts | 97 -------------- 2 files changed, 123 insertions(+), 97 deletions(-) create mode 100644 frontend/src/__tests__/cypress/cypress/tests/e2e/dataSciencePipelines/CreateRunDeletePipelineCustomPipMirror.cy.ts diff --git a/frontend/src/__tests__/cypress/cypress/tests/e2e/dataSciencePipelines/CreateRunDeletePipelineCustomPipMirror.cy.ts b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataSciencePipelines/CreateRunDeletePipelineCustomPipMirror.cy.ts new file mode 100644 index 0000000000..12cc2d62bd --- /dev/null +++ b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataSciencePipelines/CreateRunDeletePipelineCustomPipMirror.cy.ts @@ -0,0 +1,123 @@ +import { deleteOpenShiftProject } from '~/__tests__/cypress/cypress/utils/oc_commands/project'; +import { HTPASSWD_CLUSTER_ADMIN_USER } from '~/__tests__/cypress/cypress/utils/e2eUsers'; +import { projectListPage, projectDetails } from '~/__tests__/cypress/cypress/pages/projects'; +import { pipelineImportModal } from '~/__tests__/cypress/cypress/pages/pipelines/pipelineImportModal'; +import { createRunPage } from '~/__tests__/cypress/cypress/pages/pipelines/createRunPage'; +import { pipelineRunsGlobal } from '~/__tests__/cypress/cypress/pages/pipelines/pipelineRunsGlobal'; +import { + pipelinesGlobal, + pipelineDeleteModal, +} from '~/__tests__/cypress/cypress/pages/pipelines/pipelinesGlobal'; +import { pipelinesTable } from '~/__tests__/cypress/cypress/pages/pipelines/pipelinesTable'; +import { + pipelineDetails, + pipelineRunDetails, +} from '~/__tests__/cypress/cypress/pages/pipelines/topology'; +import { provisionProjectForPipelines } from '~/__tests__/cypress/cypress/utils/pipelines'; +import { getIrisPipelinePath } from '~/__tests__/cypress/cypress/utils/fileImportUtils'; +import { createOpenShiftConfigMap } from '~/__tests__/cypress/cypress/utils/oc_commands/configmap'; + +const projectName = 'test-dsp-custom-pip-prj'; +const dspaSecretName = 'test-custom-pip-dspa-secret'; +const testPipelineIrisName = 'test-iris-pipeline'; +const testRunName = 'test-pipelines-run'; + +describe('An admin user can import and run a pipeline', { testIsolation: false }, () => { + before(() => { + // Create a Project for pipelines + provisionProjectForPipelines(projectName, dspaSecretName); + }); + + after(() => { + // Delete provisioned Project + deleteOpenShiftProject(projectName); + }); + + it('Verify User Can Create, Run and Delete A DS Pipeline From DS Project Details Page Using Custom Pip Mirror', () => { + cy.step('Create Pipelines ConfigMap With Custom Pip Index Url And Trusted Host '); + const pipConfig = Cypress.env('PIP_CONFIG'); + createOpenShiftConfigMap('ds-pipeline-custom-env-vars', projectName, { + pip_index_url: pipConfig.PIP_INDEX_URL, + pip_trusted_host: pipConfig.PIP_TRUSTED_HOST, + }); + + cy.step(`Navigate to DSP ${projectName}`); + cy.visitWithLogin('/', HTPASSWD_CLUSTER_ADMIN_USER); + projectListPage.navigate(); + projectListPage.filterProjectByName(projectName); + projectListPage.findProjectLink(projectName).click(); + + cy.step('Import a pipeline from a yaml local file'); + // Increasing the timeout to ~3mins so the DSPA can be loaded + projectDetails.findImportPipelineButton(180000).click(); + // Fill the Import Pipeline modal + pipelineImportModal.findPipelineNameInput().type(testPipelineIrisName); + pipelineImportModal.findUploadPipelineRadio().click(); + pipelineImportModal.uploadPipelineYaml(getIrisPipelinePath()); + pipelineImportModal.submit(); + + // Verify that we are at the details page of the pipeline by checking the title + // It can take a little longer than expected to load + pipelineDetails.findPageTitle(60000).should('have.text', testPipelineIrisName); + + // Get the pipeline ID and version ID from the URL + cy.url().then((currentUrl) => { + const regex = /\/pipelines\/[^/]+\/([^/]+)\/([^/]+)\/view/; + const match = currentUrl.match(regex); + + if (match) { + const [, pipelineId, versionId] = match; + cy.log(`Pipeline ID: ${pipelineId}`); + cy.log(`Version ID: ${versionId}`); + } else { + throw new Error('Pipeline ID and Version ID could not be extracted from the URL.'); + } + + cy.step(`Create a ${testPipelineIrisName} pipeline run from the Runs view`); + pipelineRunsGlobal.navigate(); + pipelineRunsGlobal.selectProjectByName(projectName); + pipelineRunsGlobal.findCreateRunButton().click(); + + cy.step('Run the pipeline from the Runs view'); + createRunPage.experimentSelect.findToggleButton().click(); + createRunPage.selectExperimentByName('Default'); + createRunPage.fillName(testRunName); + createRunPage.fillDescription('Run Description'); + createRunPage.pipelineSelect.openAndSelectItem(testPipelineIrisName); + createRunPage.pipelineVersionSelect.selectItem(testPipelineIrisName); + createRunPage.findSubmitButton().click(); + + cy.step('Expect the run to Succeed'); + pipelineRunDetails.expectStatusLabelToBe('Succeeded', 180000); + + cy.step('Delete the pipeline version'); + pipelinesGlobal.navigate(); + // pipelineRunsGlobal.selectProjectByName(projectName); + const pipelineRowWithVersion = pipelinesTable.getRowById(pipelineId); + pipelineRowWithVersion.findExpandButton().click(); + pipelineRowWithVersion + .getPipelineVersionRowById(versionId) + .findKebabAction('Delete pipeline version') + .click(); + pipelineDeleteModal.findInput().fill(testPipelineIrisName); + pipelineDeleteModal.findSubmitButton().click(); + // The line below it's not working due to a bug + // pipelineDeleteModal.shouldBeOpen(false) + cy.get('[role=dialog]').should('not.exist'); + + cy.step('Verify that the pipeline version no longer exist'); + const pipelineRowWithVersionDeleted = pipelinesTable.getRowById(pipelineId); + pipelineRowWithVersionDeleted.findExpandButton().click(); + pipelineRowWithVersionDeleted.shouldNotHavePipelineVersion(); + + cy.step('Delete the pipeline'); + const pipelineRow = pipelinesTable.getRowById(pipelineId); + pipelineRow.findKebabAction('Delete pipeline').click(); + pipelineDeleteModal.findInput().fill(testPipelineIrisName); + pipelineDeleteModal.findSubmitButton().click(); + + cy.step('Verify that the pipeline no longer exist'); + pipelinesTable.shouldBeEmpty(); + }); + }); +}); diff --git a/frontend/src/__tests__/cypress/cypress/tests/e2e/dataSciencePipelines/pipelines.cy.ts b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataSciencePipelines/pipelines.cy.ts index c39308c73c..5cb69634eb 100644 --- a/frontend/src/__tests__/cypress/cypress/tests/e2e/dataSciencePipelines/pipelines.cy.ts +++ b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataSciencePipelines/pipelines.cy.ts @@ -3,24 +3,15 @@ import { HTPASSWD_CLUSTER_ADMIN_USER } from '~/__tests__/cypress/cypress/utils/e import { projectListPage, projectDetails } from '~/__tests__/cypress/cypress/pages/projects'; import { pipelineImportModal } from '~/__tests__/cypress/cypress/pages/pipelines/pipelineImportModal'; import { createRunPage } from '~/__tests__/cypress/cypress/pages/pipelines/createRunPage'; -import { pipelineRunsGlobal } from '~/__tests__/cypress/cypress/pages/pipelines/pipelineRunsGlobal'; -import { - pipelinesGlobal, - pipelineDeleteModal, -} from '~/__tests__/cypress/cypress/pages/pipelines/pipelinesGlobal'; -import { pipelinesTable } from '~/__tests__/cypress/cypress/pages/pipelines/pipelinesTable'; import { pipelineDetails, pipelineRunDetails, } from '~/__tests__/cypress/cypress/pages/pipelines/topology'; import { provisionProjectForPipelines } from '~/__tests__/cypress/cypress/utils/pipelines'; -import { getIrisPipelinePath } from '~/__tests__/cypress/cypress/utils/fileImportUtils'; -import { createOpenShiftConfigMap } from '~/__tests__/cypress/cypress/utils/oc_commands/configmap'; const projectName = 'test-pipelines-prj'; const dspaSecretName = 'dashboard-dspa-secret'; const testPipelineName = 'test-pipelines-pipeline'; -const testPipelineIrisName = 'test-iris-pipeline'; const testRunName = 'test-pipelines-run'; describe('An admin user can import and run a pipeline', { testIsolation: false }, () => { @@ -71,92 +62,4 @@ describe('An admin user can import and run a pipeline', { testIsolation: false } //Redirected to the Graph view of the created run pipelineRunDetails.expectStatusLabelToBe('Succeeded', 180000); }); - - it('Verify User Can Create, Run and Delete A DS Pipeline From DS Project Details Page Using Custom Pip Mirror', () => { - cy.step('Create Pipelines ConfigMap With Custom Pip Index Url And Trusted Host '); - const pipConfig = Cypress.env('PIP_CONFIG'); - createOpenShiftConfigMap('ds-pipeline-custom-env-vars', projectName, { - pip_index_url: pipConfig.PIP_INDEX_URL, - pip_trusted_host: pipConfig.PIP_TRUSTED_HOST, - }); - - cy.step(`Navigate to DSP ${projectName}`); - cy.visitWithLogin('/', HTPASSWD_CLUSTER_ADMIN_USER); - projectListPage.navigate(); - projectListPage.filterProjectByName(projectName); - projectListPage.findProjectLink(projectName).click(); - - cy.step('Import a pipeline from a yaml local file'); - // Increasing the timeout to ~3mins so the DSPA can be loaded - projectDetails.findImportPipelineButton(180000).click(); - // Fill the Import Pipeline modal - pipelineImportModal.findPipelineNameInput().type(testPipelineIrisName); - pipelineImportModal.findUploadPipelineRadio().click(); - pipelineImportModal.uploadPipelineYaml(getIrisPipelinePath()); - pipelineImportModal.submit(); - - // Verify that we are at the details page of the pipeline by checking the title - // It can take a little longer than expected to load - pipelineDetails.findPageTitle(60000).should('have.text', testPipelineIrisName); - - // Get the pipeline ID and version ID from the URL - cy.url().then((currentUrl) => { - const regex = /\/pipelines\/[^/]+\/([^/]+)\/([^/]+)\/view/; - const match = currentUrl.match(regex); - - if (match) { - const [, pipelineId, versionId] = match; - cy.log(`Pipeline ID: ${pipelineId}`); - cy.log(`Version ID: ${versionId}`); - } else { - throw new Error('Pipeline ID and Version ID could not be extracted from the URL.'); - } - - cy.step(`Create a ${testPipelineIrisName} pipeline run from the Runs view`); - pipelineRunsGlobal.navigate(); - pipelineRunsGlobal.selectProjectByName(projectName); - pipelineRunsGlobal.findCreateRunButton().click(); - - cy.step('Run the pipeline from the Runs view'); - createRunPage.experimentSelect.findToggleButton().click(); - createRunPage.selectExperimentByName('Default'); - createRunPage.fillName(testRunName); - createRunPage.fillDescription('Run Description'); - createRunPage.pipelineSelect.openAndSelectItem(testPipelineIrisName); - createRunPage.pipelineVersionSelect.selectItem(testPipelineIrisName); - createRunPage.findSubmitButton().click(); - - cy.step('Expect the run to Succeed'); - pipelineRunDetails.expectStatusLabelToBe('Succeeded', 180000); - - cy.step('Delete the pipeline version'); - pipelinesGlobal.navigate(); - // pipelineRunsGlobal.selectProjectByName(projectName); - const pipelineRowWithVersion = pipelinesTable.getRowById(pipelineId); - pipelineRowWithVersion.findExpandButton().click(); - pipelineRowWithVersion - .getPipelineVersionRowById(versionId) - .findKebabAction('Delete pipeline version') - .click(); - pipelineDeleteModal.findInput().fill(testPipelineIrisName); - pipelineDeleteModal.findSubmitButton().click(); - // The line below it's not working due to a bug - // pipelineDeleteModal.shouldBeOpen(false) - cy.get('[role=dialog]').should('not.exist'); - - cy.step('Verify that the pipeline version no longer exist'); - const pipelineRowWithVersionDeleted = pipelinesTable.getRowById(pipelineId); - pipelineRowWithVersionDeleted.findExpandButton().click(); - pipelineRowWithVersionDeleted.shouldNotHavePipelineVersion(); - - cy.step('Delete the pipeline'); - const pipelineRow = pipelinesTable.getRowById(pipelineId); - pipelineRow.findKebabAction('Delete pipeline').click(); - pipelineDeleteModal.findInput().fill(testPipelineIrisName); - pipelineDeleteModal.findSubmitButton().click(); - - cy.step('Verify that the pipeline no longer exist'); - pipelinesTable.shouldBeEmpty(); - }); - }); }); From 171adae64e413f6f6e126e61100adf8db439e627 Mon Sep 17 00:00:00 2001 From: Fede Alonso Date: Wed, 18 Dec 2024 07:11:04 +0100 Subject: [PATCH 08/14] rename file --- ...pMirror.cy.ts => createRunDeletePipelineCustomPipMirror.cy.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename frontend/src/__tests__/cypress/cypress/tests/e2e/dataSciencePipelines/{CreateRunDeletePipelineCustomPipMirror.cy.ts => createRunDeletePipelineCustomPipMirror.cy.ts} (100%) diff --git a/frontend/src/__tests__/cypress/cypress/tests/e2e/dataSciencePipelines/CreateRunDeletePipelineCustomPipMirror.cy.ts b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataSciencePipelines/createRunDeletePipelineCustomPipMirror.cy.ts similarity index 100% rename from frontend/src/__tests__/cypress/cypress/tests/e2e/dataSciencePipelines/CreateRunDeletePipelineCustomPipMirror.cy.ts rename to frontend/src/__tests__/cypress/cypress/tests/e2e/dataSciencePipelines/createRunDeletePipelineCustomPipMirror.cy.ts From 7f2b84d895e2c70540ea4bea39c19ab62daa8e2f Mon Sep 17 00:00:00 2001 From: Fede Alonso Date: Wed, 18 Dec 2024 11:37:38 +0100 Subject: [PATCH 09/14] Lint fixes --- .../subComponents/SearchSelector.ts | 6 +++--- ...eateRunDeletePipelineCustomPipMirror.cy.ts | 20 +++++++++---------- .../cypress/utils/oc_commands/configmap.ts | 6 ++---- 3 files changed, 15 insertions(+), 17 deletions(-) diff --git a/frontend/src/__tests__/cypress/cypress/pages/components/subComponents/SearchSelector.ts b/frontend/src/__tests__/cypress/cypress/pages/components/subComponents/SearchSelector.ts index 473cda970a..3f7da48627 100644 --- a/frontend/src/__tests__/cypress/cypress/pages/components/subComponents/SearchSelector.ts +++ b/frontend/src/__tests__/cypress/cypress/pages/components/subComponents/SearchSelector.ts @@ -44,8 +44,8 @@ export class SearchSelector extends SubComponentBase { // Perform the entire process: open, search, and select openAndSelectItem(name: string): void { - this.findToggleButton().click(); // Open the dropdown - this.searchItem(name); // Type the name in the search input - this.selectItem(name); // Select the item + this.findToggleButton().click(); + this.searchItem(name); + this.selectItem(name); } } diff --git a/frontend/src/__tests__/cypress/cypress/tests/e2e/dataSciencePipelines/createRunDeletePipelineCustomPipMirror.cy.ts b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataSciencePipelines/createRunDeletePipelineCustomPipMirror.cy.ts index 12cc2d62bd..05742362e2 100644 --- a/frontend/src/__tests__/cypress/cypress/tests/e2e/dataSciencePipelines/createRunDeletePipelineCustomPipMirror.cy.ts +++ b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataSciencePipelines/createRunDeletePipelineCustomPipMirror.cy.ts @@ -24,23 +24,23 @@ const testRunName = 'test-pipelines-run'; describe('An admin user can import and run a pipeline', { testIsolation: false }, () => { before(() => { - // Create a Project for pipelines provisionProjectForPipelines(projectName, dspaSecretName); + const pipConfig = Cypress.env('PIP_CONFIG'); + //Create Pipelines ConfigMap With Custom Pip Index Url And Trusted Host + createOpenShiftConfigMap('ds-pipeline-custom-env-vars', projectName, { + // The following lines should be snake case + /* eslint-disable-next-line camelcase */ + pip_index_url: pipConfig.PIP_INDEX_URL, + /* eslint-disable-next-line camelcase */ + pip_trusted_host: pipConfig.PIP_TRUSTED_HOST, + }); }); after(() => { - // Delete provisioned Project deleteOpenShiftProject(projectName); }); it('Verify User Can Create, Run and Delete A DS Pipeline From DS Project Details Page Using Custom Pip Mirror', () => { - cy.step('Create Pipelines ConfigMap With Custom Pip Index Url And Trusted Host '); - const pipConfig = Cypress.env('PIP_CONFIG'); - createOpenShiftConfigMap('ds-pipeline-custom-env-vars', projectName, { - pip_index_url: pipConfig.PIP_INDEX_URL, - pip_trusted_host: pipConfig.PIP_TRUSTED_HOST, - }); - cy.step(`Navigate to DSP ${projectName}`); cy.visitWithLogin('/', HTPASSWD_CLUSTER_ADMIN_USER); projectListPage.navigate(); @@ -102,7 +102,7 @@ describe('An admin user can import and run a pipeline', { testIsolation: false } pipelineDeleteModal.findInput().fill(testPipelineIrisName); pipelineDeleteModal.findSubmitButton().click(); // The line below it's not working due to a bug - // pipelineDeleteModal.shouldBeOpen(false) + // pipelineDeleteModal.shouldBeOpen(false); cy.get('[role=dialog]').should('not.exist'); cy.step('Verify that the pipeline version no longer exist'); diff --git a/frontend/src/__tests__/cypress/cypress/utils/oc_commands/configmap.ts b/frontend/src/__tests__/cypress/cypress/utils/oc_commands/configmap.ts index 69a0f0bea6..d976501f31 100644 --- a/frontend/src/__tests__/cypress/cypress/utils/oc_commands/configmap.ts +++ b/frontend/src/__tests__/cypress/cypress/utils/oc_commands/configmap.ts @@ -1,4 +1,4 @@ -import type { CommandLineResult, DashboardConfig } from '~/__tests__/cypress/cypress/types'; +import type { CommandLineResult } from '~/__tests__/cypress/cypress/types'; /** * Create an OpenShift ConfigMap @@ -17,12 +17,10 @@ export const createOpenShiftConfigMap = ( namespace: string, keyValues: Record, // Object of key-value pairs ): Cypress.Chainable => { - // Construct the `--from-literal` arguments dynamically + // Build the `--from-literal` arguments dynamically const literals = Object.entries(keyValues) .map(([key, value]) => `--from-literal=${key}=${value}`) .join(' '); - - // Construct the `oc create configmap` command const ocCommand = `oc create configmap ${configMapName} -n ${namespace} ${literals}`; return cy.exec(ocCommand, { failOnNonZeroExit: false }).then((result) => { From 8615f3ae5cb355d199ef805be1cfe261efcfe325 Mon Sep 17 00:00:00 2001 From: Fede Alonso Date: Thu, 19 Dec 2024 12:55:59 +0100 Subject: [PATCH 10/14] fix application issue --- .../cypress/support/commands/application.ts | 30 +++++++++++++------ 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/frontend/src/__tests__/cypress/cypress/support/commands/application.ts b/frontend/src/__tests__/cypress/cypress/support/commands/application.ts index 653d635ac6..af6efcb7fb 100644 --- a/frontend/src/__tests__/cypress/cypress/support/commands/application.ts +++ b/frontend/src/__tests__/cypress/cypress/support/commands/application.ts @@ -226,15 +226,27 @@ Cypress.Commands.add('findDropdownItem', { prevSubject: 'element' }, (subject, n }); }); -Cypress.Commands.add('findMenuItem', { prevSubject: 'element' }, (subject, name) => { - Cypress.log({ displayName: 'findMenuItem', message: name }); - return cy.wrap(subject).then(($el) => { - if ($el.attr('aria-expanded') === 'false') { - cy.wrap($el).click(); - } - return cy.get('[data-ouia-component-type="PF6/Menu"]').find('td').contains(name); - }); -}); +Cypress.Commands.add( + 'findMenuItem', + { prevSubject: 'element' }, + (subject: JQuery, name: string | RegExp): Cypress.Chainable> => { + Cypress.log({ displayName: 'findMenuItem', message: name.toString() }); + + return cy.wrap(subject).then(($el) => { + // Check if the dropdown is collapsed and expand it + if ($el.attr('aria-expanded') === 'false') { + cy.wrap($el).click(); + } + + // Find the menu and locate the desired item by name or RegExp + return cy + .get('[data-ouia-component-type="PF6/Menu"]') + .find('td') // Target table cells + .contains(name) + .then((cell) => cy.wrap(cell as unknown as JQuery)); // Cast to HTMLElement + }); + }, +); Cypress.Commands.add('findDropdownItemByTestId', { prevSubject: 'element' }, (subject, testId) => { Cypress.log({ displayName: 'findDropdownItemByTestId', message: testId }); From 5489de0751bef342263741df31de2f8efbf120f8 Mon Sep 17 00:00:00 2001 From: Fede Alonso Date: Fri, 20 Dec 2024 08:08:49 +0100 Subject: [PATCH 11/14] Apply Purva's solution --- ...eateRunDeletePipelineCustomPipMirror.cy.ts | 84 +++++++++---------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/frontend/src/__tests__/cypress/cypress/tests/e2e/dataSciencePipelines/createRunDeletePipelineCustomPipMirror.cy.ts b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataSciencePipelines/createRunDeletePipelineCustomPipMirror.cy.ts index 05742362e2..82a8de38d2 100644 --- a/frontend/src/__tests__/cypress/cypress/tests/e2e/dataSciencePipelines/createRunDeletePipelineCustomPipMirror.cy.ts +++ b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataSciencePipelines/createRunDeletePipelineCustomPipMirror.cy.ts @@ -69,55 +69,55 @@ describe('An admin user can import and run a pipeline', { testIsolation: false } const [, pipelineId, versionId] = match; cy.log(`Pipeline ID: ${pipelineId}`); cy.log(`Version ID: ${versionId}`); - } else { - throw new Error('Pipeline ID and Version ID could not be extracted from the URL.'); - } - cy.step(`Create a ${testPipelineIrisName} pipeline run from the Runs view`); - pipelineRunsGlobal.navigate(); - pipelineRunsGlobal.selectProjectByName(projectName); - pipelineRunsGlobal.findCreateRunButton().click(); + cy.step(`Create a ${testPipelineIrisName} pipeline run from the Runs view`); + pipelineRunsGlobal.navigate(); + pipelineRunsGlobal.selectProjectByName(projectName); + pipelineRunsGlobal.findCreateRunButton().click(); - cy.step('Run the pipeline from the Runs view'); - createRunPage.experimentSelect.findToggleButton().click(); - createRunPage.selectExperimentByName('Default'); - createRunPage.fillName(testRunName); - createRunPage.fillDescription('Run Description'); - createRunPage.pipelineSelect.openAndSelectItem(testPipelineIrisName); - createRunPage.pipelineVersionSelect.selectItem(testPipelineIrisName); - createRunPage.findSubmitButton().click(); + cy.step('Run the pipeline from the Runs view'); + createRunPage.experimentSelect.findToggleButton().click(); + createRunPage.selectExperimentByName('Default'); + createRunPage.fillName(testRunName); + createRunPage.fillDescription('Run Description'); + createRunPage.pipelineSelect.openAndSelectItem(testPipelineIrisName); + createRunPage.pipelineVersionSelect.selectItem(testPipelineIrisName); + createRunPage.findSubmitButton().click(); - cy.step('Expect the run to Succeed'); - pipelineRunDetails.expectStatusLabelToBe('Succeeded', 180000); + cy.step('Expect the run to Succeed'); + pipelineRunDetails.expectStatusLabelToBe('Succeeded', 180000); - cy.step('Delete the pipeline version'); - pipelinesGlobal.navigate(); - // pipelineRunsGlobal.selectProjectByName(projectName); - const pipelineRowWithVersion = pipelinesTable.getRowById(pipelineId); - pipelineRowWithVersion.findExpandButton().click(); - pipelineRowWithVersion - .getPipelineVersionRowById(versionId) - .findKebabAction('Delete pipeline version') - .click(); - pipelineDeleteModal.findInput().fill(testPipelineIrisName); - pipelineDeleteModal.findSubmitButton().click(); - // The line below it's not working due to a bug - // pipelineDeleteModal.shouldBeOpen(false); - cy.get('[role=dialog]').should('not.exist'); + cy.step('Delete the pipeline version'); + pipelinesGlobal.navigate(); + // pipelineRunsGlobal.selectProjectByName(projectName); + const pipelineRowWithVersion = pipelinesTable.getRowById(pipelineId); + pipelineRowWithVersion.findExpandButton().click(); + pipelineRowWithVersion + .getPipelineVersionRowById(versionId) + .findKebabAction('Delete pipeline version') + .click(); + pipelineDeleteModal.findInput().fill(testPipelineIrisName); + pipelineDeleteModal.findSubmitButton().click(); + // The line below it's not working due to a bug + // pipelineDeleteModal.shouldBeOpen(false); + cy.get('[role=dialog]').should('not.exist'); - cy.step('Verify that the pipeline version no longer exist'); - const pipelineRowWithVersionDeleted = pipelinesTable.getRowById(pipelineId); - pipelineRowWithVersionDeleted.findExpandButton().click(); - pipelineRowWithVersionDeleted.shouldNotHavePipelineVersion(); + cy.step('Verify that the pipeline version no longer exist'); + const pipelineRowWithVersionDeleted = pipelinesTable.getRowById(pipelineId); + pipelineRowWithVersionDeleted.findExpandButton().click(); + pipelineRowWithVersionDeleted.shouldNotHavePipelineVersion(); - cy.step('Delete the pipeline'); - const pipelineRow = pipelinesTable.getRowById(pipelineId); - pipelineRow.findKebabAction('Delete pipeline').click(); - pipelineDeleteModal.findInput().fill(testPipelineIrisName); - pipelineDeleteModal.findSubmitButton().click(); + cy.step('Delete the pipeline'); + const pipelineRow = pipelinesTable.getRowById(pipelineId); + pipelineRow.findKebabAction('Delete pipeline').click(); + pipelineDeleteModal.findInput().fill(testPipelineIrisName); + pipelineDeleteModal.findSubmitButton().click(); - cy.step('Verify that the pipeline no longer exist'); - pipelinesTable.shouldBeEmpty(); + cy.step('Verify that the pipeline no longer exist'); + pipelinesTable.shouldBeEmpty(); + } else { + throw new Error('Pipeline ID and Version ID could not be extracted from the URL.'); + } }); }); }); From a1dcb3526efe23e2611def05d8858ddc601fa4ee Mon Sep 17 00:00:00 2001 From: Fede Alonso Date: Fri, 20 Dec 2024 11:00:06 +0100 Subject: [PATCH 12/14] workbenches --- .../workbenches/workbenches.cy.ts | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/workbenches/workbenches.cy.ts b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/workbenches/workbenches.cy.ts index 2ba00f9d7f..4a7928e1f5 100644 --- a/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/workbenches/workbenches.cy.ts +++ b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/workbenches/workbenches.cy.ts @@ -13,7 +13,6 @@ import { deleteOpenShiftProject } from '~/__tests__/cypress/cypress/utils/oc_com import { createPersistentVolumeClaim } from '~/__tests__/cypress/cypress/utils/oc_commands/presistentVolumeClaim'; describe('Workbench and PVSs tests', () => { - // let testData: PVCReplacements; let projectName: string; let PVCName: string; let PVCDisplayName: string; @@ -48,14 +47,6 @@ describe('Workbench and PVSs tests', () => { }); }); - after(() => { - // Delete provisioned Project - if (projectName) { - cy.log(`Deleting Project ${projectName} after the test has finished.`); - deleteOpenShiftProject(projectName); - } - }); - it('Verify users can create a workbench and connect an existent PersistentVolume', () => { const workbenchName = projectName.replace('dsp-', ''); @@ -72,7 +63,7 @@ describe('Workbench and PVSs tests', () => { cy.step(`Create Workbench ${projectName} using storage ${PVCDisplayName}`); workbenchPage.findCreateButton().click(); createSpawnerPage.getNameInput().fill(workbenchName); - createSpawnerPage.findNotebookImage('jupyter-minimal-notebook').click(); + createSpawnerPage.findNotebookImage('code-server-notebook').click(); createSpawnerPage.findAttachExistingStorageButton().click(); attachExistingStorageModal.selectExistingPersistentStorage(PVCDisplayName); attachExistingStorageModal.findStandardPathInput().fill(workbenchName); @@ -82,7 +73,7 @@ describe('Workbench and PVSs tests', () => { cy.step(`Wait for Workbench ${workbenchName} to display a "Running" status`); const notebookRow = workbenchPage.getNotebookRow(workbenchName); notebookRow.expectStatusLabelToBe('Running', 120000); - notebookRow.shouldHaveNotebookImageName('Minimal Python'); + notebookRow.shouldHaveNotebookImageName('code-server'); notebookRow.shouldHaveContainerSize('Small'); cy.step(`Check the cluster storage ${PVCDisplayName} is now connected to ${workbenchName}`); From 3fe6df7593c1dbb08309fae4a4b53dfc1213d6b8 Mon Sep 17 00:00:00 2001 From: Fede Alonso Date: Fri, 20 Dec 2024 13:51:27 +0100 Subject: [PATCH 13/14] fix pip config retrieving --- .../createRunDeletePipelineCustomPipMirror.cy.ts | 5 ++--- .../dataScienceProjects/workbenches/workbenches.cy.ts | 8 ++++++++ frontend/src/__tests__/cypress/cypress/types.ts | 8 ++------ .../src/__tests__/cypress/cypress/utils/testConfig.ts | 10 ++++------ .../src/__tests__/cypress/test-variables.yml.example | 5 ++--- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/frontend/src/__tests__/cypress/cypress/tests/e2e/dataSciencePipelines/createRunDeletePipelineCustomPipMirror.cy.ts b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataSciencePipelines/createRunDeletePipelineCustomPipMirror.cy.ts index 82a8de38d2..a9a2d20628 100644 --- a/frontend/src/__tests__/cypress/cypress/tests/e2e/dataSciencePipelines/createRunDeletePipelineCustomPipMirror.cy.ts +++ b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataSciencePipelines/createRunDeletePipelineCustomPipMirror.cy.ts @@ -25,14 +25,13 @@ const testRunName = 'test-pipelines-run'; describe('An admin user can import and run a pipeline', { testIsolation: false }, () => { before(() => { provisionProjectForPipelines(projectName, dspaSecretName); - const pipConfig = Cypress.env('PIP_CONFIG'); //Create Pipelines ConfigMap With Custom Pip Index Url And Trusted Host createOpenShiftConfigMap('ds-pipeline-custom-env-vars', projectName, { // The following lines should be snake case /* eslint-disable-next-line camelcase */ - pip_index_url: pipConfig.PIP_INDEX_URL, + pip_index_url: Cypress.env('PIP_INDEX_URL'), /* eslint-disable-next-line camelcase */ - pip_trusted_host: pipConfig.PIP_TRUSTED_HOST, + pip_trusted_host: Cypress.env('PIP_TRUSTED_HOST'), }); }); diff --git a/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/workbenches/workbenches.cy.ts b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/workbenches/workbenches.cy.ts index 18f495c285..aa7d9b5f7a 100644 --- a/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/workbenches/workbenches.cy.ts +++ b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/workbenches/workbenches.cy.ts @@ -47,6 +47,14 @@ describe('Workbench and PVSs tests', () => { }); }); + after(() => { + // Delete provisioned Project + if (projectName) { + cy.log(`Deleting Project ${projectName} after the test has finished.`); + deleteOpenShiftProject(projectName); + } + }); + it('Verify users can create a workbench and connect an existent PersistentVolume', () => { const workbenchName = projectName.replace('dsp-', ''); diff --git a/frontend/src/__tests__/cypress/cypress/types.ts b/frontend/src/__tests__/cypress/cypress/types.ts index 7effff1ebf..56bd954f74 100644 --- a/frontend/src/__tests__/cypress/cypress/types.ts +++ b/frontend/src/__tests__/cypress/cypress/types.ts @@ -92,7 +92,8 @@ export type TestConfig = { OCP_ADMIN_USER: UserAuthConfig; S3: AWSS3Buckets; APPLICATIONS_NAMESPACE: NamespaceConfig; - PIP_CONFIG: PipConfig; + PIP_INDEX_URL: string; + PIP_TRUSTED_HOST: string; }; export type DataScienceProjectData = { @@ -175,8 +176,3 @@ export type ResourcesData = { export type NamespaceConfig = { APPLICATIONS_NAMESPACE: string; }; - -export type PipConfig = { - PIP_INDEX_URL: string; - PIP_TRUSTED_HOST: string; -}; diff --git a/frontend/src/__tests__/cypress/cypress/utils/testConfig.ts b/frontend/src/__tests__/cypress/cypress/utils/testConfig.ts index e1912a6112..1659a9321a 100644 --- a/frontend/src/__tests__/cypress/cypress/utils/testConfig.ts +++ b/frontend/src/__tests__/cypress/cypress/utils/testConfig.ts @@ -8,7 +8,6 @@ import type { TestConfig, AWSS3BucketDetails, AWSS3Buckets, - PipConfig, } from '~/__tests__/cypress/cypress/types'; [ @@ -53,10 +52,8 @@ const AWS_PIPELINES: AWSS3Buckets = { BUCKET_2: AWS_PIPELINES_BUCKET_DETAILS, }; const TEST_NAMESPACE = testConfig?.APPLICATIONS_NAMESPACE; -const PIP_CONFIG: PipConfig = { - PIP_INDEX_URL: testConfig?.PIP_CONFIG.PIP_INDEX_URL || '', - PIP_TRUSTED_HOST: testConfig?.PIP_CONFIG.PIP_TRUSTED_HOST || '', -}; +const PIP_INDEX_URL = testConfig?.PIP_INDEX_URL; +const PIP_TRUSTED_HOST = testConfig?.PIP_TRUSTED_HOST; // spread the cypressEnv variables into the cypress config export const cypressEnv = { @@ -64,7 +61,8 @@ export const cypressEnv = { HTPASSWD_CLUSTER_ADMIN_USER, AWS_PIPELINES, TEST_NAMESPACE, - PIP_CONFIG, + PIP_INDEX_URL, + PIP_TRUSTED_HOST, }; // re-export the updated process env diff --git a/frontend/src/__tests__/cypress/test-variables.yml.example b/frontend/src/__tests__/cypress/test-variables.yml.example index ce0a3827a2..6f9f81a10e 100644 --- a/frontend/src/__tests__/cypress/test-variables.yml.example +++ b/frontend/src/__tests__/cypress/test-variables.yml.example @@ -18,6 +18,5 @@ S3: NAME: pipeline-bucket-name REGION: pipeline-bucket-region ENDPOINT: https://pipeline-bucket-endpoint.com/ -PIP_CONFIG: - PIP_INDEX_URL: https://pypi.org/simple - PIP_TRUSTED_HOST: pypi.org \ No newline at end of file +PIP_INDEX_URL: https://pypi.org/simple +PIP_TRUSTED_HOST: pypi.org \ No newline at end of file From d84d0793576f7c70e47f51f07e5fb5ef1fd67c99 Mon Sep 17 00:00:00 2001 From: Fede Alonso Date: Mon, 23 Dec 2024 13:12:50 +0100 Subject: [PATCH 14/14] Hope It does fix the mocked tests --- .../subComponents/SearchSelector.ts | 6 +++- .../cypress/support/commands/application.ts | 30 ++++++------------- ...eateRunDeletePipelineCustomPipMirror.cy.ts | 2 +- 3 files changed, 15 insertions(+), 23 deletions(-) diff --git a/frontend/src/__tests__/cypress/cypress/pages/components/subComponents/SearchSelector.ts b/frontend/src/__tests__/cypress/cypress/pages/components/subComponents/SearchSelector.ts index 3f7da48627..65af2fa4b3 100644 --- a/frontend/src/__tests__/cypress/cypress/pages/components/subComponents/SearchSelector.ts +++ b/frontend/src/__tests__/cypress/cypress/pages/components/subComponents/SearchSelector.ts @@ -10,7 +10,7 @@ export class SearchSelector extends SubComponentBase { } findItem(name: string): Cypress.Chainable> { - return this.findToggleButton().findMenuItem(name); + return this.findResultTableList().contains(name).should('exist'); } selectItem(name: string): void { @@ -25,6 +25,10 @@ export class SearchSelector extends SubComponentBase { return this.findContextualItem('toggle'); } + findResultTableList(): Cypress.Chainable> { + return this.findContextualItem('table-list'); + } + findSearchHelpText(): Cypress.Chainable> { return this.findContextualItem('searchHelpText'); } diff --git a/frontend/src/__tests__/cypress/cypress/support/commands/application.ts b/frontend/src/__tests__/cypress/cypress/support/commands/application.ts index e47be3f3c9..d9f2ee1137 100644 --- a/frontend/src/__tests__/cypress/cypress/support/commands/application.ts +++ b/frontend/src/__tests__/cypress/cypress/support/commands/application.ts @@ -241,27 +241,15 @@ Cypress.Commands.add('findDropdownItem', { prevSubject: 'element' }, (subject, n }); }); -Cypress.Commands.add( - 'findMenuItem', - { prevSubject: 'element' }, - (subject: JQuery, name: string | RegExp): Cypress.Chainable> => { - Cypress.log({ displayName: 'findMenuItem', message: name.toString() }); - - return cy.wrap(subject).then(($el) => { - // Check if the dropdown is collapsed and expand it - if ($el.attr('aria-expanded') === 'false') { - cy.wrap($el).click(); - } - - // Find the menu and locate the desired item by name or RegExp - return cy - .get('[data-ouia-component-type="PF6/Menu"]') - .find('td') // Target table cells - .contains(name) - .then((cell) => cy.wrap(cell as unknown as JQuery)); // Cast to HTMLElement - }); - }, -); +Cypress.Commands.add('findMenuItem', { prevSubject: 'element' }, (subject, name) => { + Cypress.log({ displayName: 'findMenuItem', message: name }); + return cy.wrap(subject).then(($el) => { + if ($el.attr('aria-expanded') === 'false') { + cy.wrap($el).click(); + } + return cy.get('[data-ouia-component-type="PF6/Menu"]').findByRole('menuitem', { name }); + }); +}); Cypress.Commands.add('findDropdownItemByTestId', { prevSubject: 'element' }, (subject, testId) => { Cypress.log({ displayName: 'findDropdownItemByTestId', message: testId }); diff --git a/frontend/src/__tests__/cypress/cypress/tests/e2e/dataSciencePipelines/createRunDeletePipelineCustomPipMirror.cy.ts b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataSciencePipelines/createRunDeletePipelineCustomPipMirror.cy.ts index a9a2d20628..93965c286c 100644 --- a/frontend/src/__tests__/cypress/cypress/tests/e2e/dataSciencePipelines/createRunDeletePipelineCustomPipMirror.cy.ts +++ b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataSciencePipelines/createRunDeletePipelineCustomPipMirror.cy.ts @@ -80,7 +80,7 @@ describe('An admin user can import and run a pipeline', { testIsolation: false } createRunPage.fillName(testRunName); createRunPage.fillDescription('Run Description'); createRunPage.pipelineSelect.openAndSelectItem(testPipelineIrisName); - createRunPage.pipelineVersionSelect.selectItem(testPipelineIrisName); + createRunPage.pipelineVersionSelect.openAndSelectItem(testPipelineIrisName); createRunPage.findSubmitButton().click(); cy.step('Expect the run to Succeed');