Skip to content

CONSOLE-5282: Migrate storage e2e tests from Cypress to Playwright#16519

Open
Cragsmann wants to merge 1 commit into
openshift:mainfrom
Cragsmann:CONSOLE-5282
Open

CONSOLE-5282: Migrate storage e2e tests from Cypress to Playwright#16519
Cragsmann wants to merge 1 commit into
openshift:mainfrom
Cragsmann:CONSOLE-5282

Conversation

@Cragsmann
Copy link
Copy Markdown
Contributor

@Cragsmann Cragsmann commented May 29, 2026

Analysis / Root cause:
Migrate 4 Cypress storage test files (14 tests) to Playwright as part of the console e2e migration effort (CONSOLE-5282).

Solution description:

  • Migrated create-storage-class.cy.ts → 9 provisioner-parameterized tests (all passing)
  • Migrated clone.cy.ts → 2 PVC clone tests (skip on non-AWS)
  • Migrated snapshot.cy.ts → 2 VolumeSnapshot tests (skip on non-AWS)
  • Migrated volume-attributes-class.cy.ts → 1 VAC lifecycle test (skip on non-AWS)
  • Added ListPage and ModalPage page objects
  • Extended KubernetesClient with cluster-scoped custom resource, PVC, and Deployment methods
  • Fixed ListPage filter selector: PF6 TextInput does not forward data-test to the DOM input, so use data-test-id="item-filter" instead
  • Deleted original Cypress test files, views, and mocks

Screenshots / screen recording:
N/A (e2e test migration, no UI changes)

Test setup:
Requires BRIDGE_AWS=true in e2e/.env and an AWS cluster with EBS CSI driver for clone, snapshot, and VAC tests. The create-storage-class tests run on any platform.

Test cases:

  • create-storage-class: 9 tests validated passing on GCP cluster
  • clone, snapshot, volume-attributes-class: correctly skip on non-AWS clusters via BRIDGE_AWS env var

Browser conformance:
N/A (e2e test migration, no UI changes)

Additional info:
🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added end-to-end testing for PVC cloning functionality across storage classes.
    • Added storage class creation support with provisioner-specific parameter configuration.
    • Added volume snapshot creation, listing, and deletion capabilities.
    • Added snapshot restoration to create new PVCs.
    • Added volume attributes class management and modification features.
  • Tests

    • Migrated storage tests from legacy framework to new test infrastructure with enhanced page object patterns.

Migrate 4 Cypress storage test files to Playwright under
e2e/tests/console/storage/ and delete the original Cypress files.

- create-storage-class: 9 provisioner tests (all passing)
- clone: 2 PVC clone tests (skip on non-AWS clusters)
- snapshot: 2 VolumeSnapshot tests (skip on non-AWS clusters)
- volume-attributes-class: 1 VAC lifecycle test (skip on non-AWS)

Add KubernetesClient methods for cluster-scoped custom resources,
PVCs, and Deployments. Add ListPage and ModalPage page objects.
Fix ListPage filter selector to use data-test-id="item-filter"
(PF6 TextInput does not forward data-test to the DOM input).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@openshift-ci-robot openshift-ci-robot added the jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. label May 29, 2026
@openshift-ci-robot
Copy link
Copy Markdown
Contributor

openshift-ci-robot commented May 29, 2026

@Cragsmann: This pull request references CONSOLE-5282 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "5.0.0" version, but no target version was set.

Details

In response to this:

Analysis / Root cause:
Migrate 4 Cypress storage test files (14 tests) to Playwright as part of the console e2e migration effort (CONSOLE-5282).

Solution description:

  • Migrated create-storage-class.cy.ts → 9 provisioner-parameterized tests (all passing)
  • Migrated clone.cy.ts → 2 PVC clone tests (skip on non-AWS)
  • Migrated snapshot.cy.ts → 2 VolumeSnapshot tests (skip on non-AWS)
  • Migrated volume-attributes-class.cy.ts → 1 VAC lifecycle test (skip on non-AWS)
  • Added ListPage and ModalPage page objects
  • Extended KubernetesClient with cluster-scoped custom resource, PVC, and Deployment methods
  • Fixed ListPage filter selector: PF6 TextInput does not forward data-test to the DOM input, so use data-test-id="item-filter" instead
  • Deleted original Cypress test files, views, and mocks

Screenshots / screen recording:
N/A (e2e test migration, no UI changes)

Test setup:
Requires BRIDGE_AWS=true in e2e/.env and an AWS cluster with EBS CSI driver for clone, snapshot, and VAC tests. The create-storage-class tests run on any platform.

Test cases:

  • create-storage-class: 9 tests validated passing on GCP cluster
  • clone, snapshot, volume-attributes-class: correctly skip on non-AWS clusters via BRIDGE_AWS env var

Browser conformance:
N/A (e2e test migration, no UI changes)

Additional info:
🤖 Generated with Claude Code

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 29, 2026

Walkthrough

This PR migrates CSI storage tests from Cypress integration tests to Playwright e2e, adding Kubernetes API client helpers, page-object infrastructure, storage fixture data, and four comprehensive e2e test suites covering PVC cloning, storage class creation, volume snapshots, and volume attributes class workflows.

Changes

Storage E2E Test Migration

Layer / File(s) Summary
Kubernetes client API extensions
frontend/e2e/clients/kubernetes-client.ts
KubernetesClient gains cluster-scoped custom resource methods (create/delete/get/patch via CustomObjectsApi) and convenience wrappers for PVC and Deployment lifecycle operations, with 404-safe deletion and appropriate patch strategies.
E2E page object infrastructure
frontend/e2e/pages/modal-page.ts, frontend/e2e/pages/list-page.ts, frontend/e2e/pages/base-page.ts
New Playwright page objects: ModalPage with lifecycle checks and interactions; ListPage with filtering, row presence/absence checks, kebab action execution with retry handling, and name-based navigation; BasePage enhancement to dismiss optional tour UI.
Storage test fixtures and data
frontend/e2e/mocks/storage.ts
Consolidated storage test data: Deployment/PVC (gp2/gp3) fixtures, VolumeSnapshotClass, volume patch for snapshot restore, Parameter type and provisionersMap for multi-provisioner coverage, plus getVACFixtures and getDeployment factories.
PVC cloning e2e tests
frontend/e2e/tests/console/storage/clone.spec.ts
Two test scenarios for CSI PVC cloning: first clones with default storage class, second clones with explicit gp3-csi class; both verify clone details via UI assertions and K8s API, then delete and confirm absence.
Storage class creation e2e tests
frontend/e2e/tests/console/storage/create-storage-class.spec.ts
Parameterized e2e test iterating over provisionersMap, creating StorageClass resources for each provisioner; handles provisioner-specific parameters (dropdowns, checkboxes, text inputs) with recursive nested parameter support, validates details page, and deletes via UI.
Volume snapshot e2e tests
frontend/e2e/tests/console/storage/snapshot.spec.ts
Two snapshot workflows: first creates snapshot, verifies readiness and metadata via UI and K8s API, confirms list visibility, then deletes; second creates snapshot and restores as new PVC, patches deployment, verifies restored PVC becomes Bound.
Volume attributes class e2e tests
frontend/e2e/tests/console/storage/volume-attributes-class.spec.ts
Complete VAC lifecycle test: creates PVC with low-IOPS VAC, verifies Bound status and VAC fields, modifies to high-IOPS VAC via modal, attempts invalid modification with error assertion, and cleans up cluster resources with finalizer patching.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested labels

component/dev-console, component/shared, kind/cypress, verified

Suggested reviewers

  • rhamilto
  • spadgett
  • logonoff
🚥 Pre-merge checks | ✅ 13 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Test Structure And Quality ⚠️ Warning Cluster resource cleanup in test.step not guaranteed; snapshot.spec lines 104-111 and volume-attributes-class lines 137-153 need afterAll hooks; create-storage-class line 49 missing exact: true. Move cleanup to test.afterAll hooks; add exact: true to line 49; add assertion failure messages.
✅ Passed checks (13 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly and concisely summarizes the main change: migrating storage e2e tests from Cypress to Playwright, with the JIRA issue prefix CONSOLE-5282.
Description check ✅ Passed The PR description covers all required template sections: analysis/root cause, solution description, test setup, test cases, and additional info. Minor omissions (screenshots, browser conformance, reviewers) are non-critical.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Stable And Deterministic Test Names ✅ Passed The custom check addresses Ginkgo test names, but this PR contains only Playwright TypeScript tests. No Ginkgo tests are present in this Cypress-to-Playwright migration PR.
Microshift Test Compatibility ✅ Passed The custom check applies only to Ginkgo e2e tests; this PR adds Playwright frontend tests (TypeScript), which are a different testing framework and scope entirely.
Single Node Openshift (Sno) Test Compatibility ✅ Passed This PR adds Playwright e2e tests in the OpenShift Console (TypeScript) repository, not Ginkgo e2e tests. The custom check applies only to Ginkgo tests in openshift/origin.
Topology-Aware Scheduling Compatibility ✅ Passed PR contains only E2E test code and fixtures in frontend/e2e/ and frontend/packages/integration-tests/, not deployment manifests, operator code, or controllers. Check does not apply.
Ote Binary Stdout Contract ✅ Passed PR contains only frontend TypeScript/JavaScript E2E tests (Playwright/Cypress); no Go test binaries or process-level code subject to OTE stdout contract.
Ipv6 And Disconnected Network Test Compatibility ✅ Passed Check is inapplicable: these are Playwright (TypeScript) tests, not Ginkgo (Go) tests. No IPv4 assumptions, hardcoded IPs, or external connectivity found.
No-Weak-Crypto ✅ Passed No weak cryptography or insecure crypto patterns found. PR is E2E tests and test infrastructure with no cryptographic code.
Container-Privileges ✅ Passed No privileged container configurations found in PR changes. All Kubernetes manifests in the new test mocks and fixtures use standard non-privileged container settings.
No-Sensitive-Data-In-Logs ✅ Passed No console.log statements, hardcoded credentials, secret encoding, or response body logging found in new code. Error messages only contain safe context info.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@openshift-ci openshift-ci Bot requested review from TheRealJon and cajieh May 29, 2026 11:59
@openshift-ci
Copy link
Copy Markdown
Contributor

openshift-ci Bot commented May 29, 2026

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: Cragsmann
Once this PR has been reviewed and has the lgtm label, please assign vojtechszocs for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@openshift-ci openshift-ci Bot added the kind/cypress Related to Cypress e2e integration testing label May 29, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (3)
frontend/e2e/tests/console/storage/clone.spec.ts (1)

12-141: ⚡ Quick win

Consider extracting the shared clone flow to reduce duplication.

The two tests are nearly identical: setup, wait-for-Bound, clone, verify-details, and delete steps are copied verbatim, differing only in the storage-class selection. A small parameterized helper would keep these in sync as the flow evolves.

♻️ Sketch of a shared helper
const runCloneTest = async (
  { page, k8sClient, cleanup }: { page: Page; k8sClient: KubernetesClient; cleanup: Cleanup },
  nsPrefix: string,
  storageClass?: string,
) => {
  const ns = `${nsPrefix}-${Date.now()}`;
  const pvcName = PVC.metadata.name;
  const cloneName = `${pvcName}-clone`;
  const listPage = new ListPage(page);
  const modal = new ModalPage(page);

  await test.step('Set up namespace and resources', async () => {
    await k8sClient.createNamespace(ns);
    cleanup.trackNamespace(ns);
    await k8sClient.createPVC(ns, { ...PVC, metadata: { ...PVC.metadata, namespace: ns } } as any);
    await k8sClient.createPVC(ns, { ...PVCGP3, metadata: { ...PVCGP3.metadata, namespace: ns } } as any);
    await k8sClient.createDeployment(ns, { ...testerDeployment, metadata: { ...testerDeployment.metadata, namespace: ns } } as any);
  });

  // ...wait-for-Bound, clone (optionally selecting `storageClass`), verify, delete...
};

test('creates and deletes a PVC clone', (fixtures) => runCloneTest(fixtures, 'test-clone'));
test('creates PVC clone with different storage class', (fixtures) => runCloneTest(fixtures, 'test-clone-sc', 'gp3-csi'));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/e2e/tests/console/storage/clone.spec.ts` around lines 12 - 141, Both
tests duplicate the same clone flow; extract a parameterized helper (e.g.,
runCloneTest) that accepts the fixtures ({ page, k8sClient, cleanup }), an
nsPrefix, and optional storageClass, and move the shared steps
(namespace/resource setup using PVC, PVCGP3, testerDeployment; wait-for-Bound
using ListPage; clone flow using ModalPage and cloneSize; verify via
k8sClient.getPVC and page checks; delete) into it; then replace the two tests
with calls to runCloneTest(fixtures, 'test-clone') and runCloneTest(fixtures,
'test-clone-sc', 'gp3-csi') to select the alternate storage class when provided.
frontend/e2e/clients/kubernetes-client.ts (1)

491-498: 💤 Low value

Consider adding explicit types instead of as any cast.

The as any cast on line 498 bypasses TypeScript type checking. If the Kubernetes client library types are too strict, consider defining an explicit interface for the patch options to maintain type safety while satisfying the compiler.

🔍 Similar pattern at line 542

The same as any cast pattern appears in patchDeployment at line 542. If the types are updated here, consider updating both locations for consistency.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/e2e/clients/kubernetes-client.ts` around lines 491 - 498, Replace
the unsafe `as any` cast on the argument passed to
`this.coApi.patchClusterCustomObject` by defining and using an explicit options
interface (e.g., PatchClusterCustomObjectOptions) that matches the shape
required by the k8s client (properties: body, group, name, plural, version,
contentType) and use that type for the object literal; then update the call
signature to pass the strongly-typed object instead of `as any`. Also apply the
same change to the similar `patchDeployment` call to keep both
`patchClusterCustomObject` and `patchDeployment` consistent and type-safe.
frontend/e2e/tests/console/storage/snapshot.spec.ts (1)

19-56: ⚡ Quick win

Consider extracting the shared setup to reduce duplication.

The namespace/PVC/deployment/VolumeSnapshotClass creation (Lines 19-37) and the wait-for-Bound + create-snapshot steps (Lines 39-56) are duplicated almost verbatim in the second test (Lines 122-164). A small helper (e.g., setupSnapshotEnv(k8sClient, cleanup, ns)) would keep the two scenarios in sync and ease future maintenance.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/e2e/tests/console/storage/snapshot.spec.ts` around lines 19 - 56,
Extract the duplicated setup into a single helper like
setupSnapshotEnv(k8sClient, cleanup, ns) that performs the namespace creation
(k8sClient.createNamespace), PVC creation (k8sClient.createPVC), deployment
creation (k8sClient.createDeployment), VolumeSnapshotClass creation
(k8sClient.createClusterCustomResource for 'snapshot.storage.k8s.io' v1
volumesnapshotclasses), waits for the PVC to become Bound using
listPage.navigateTo + listPage.filterByName + expect on the status locator, and
performs the snapshot creation steps (navigate to VolumeSnapshot list, open
dropdowns and submit modal). Replace the two duplicated blocks in both tests
with calls to this helper, passing any needed identifiers like pvcName or ns so
both tests share the same implementation.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@frontend/e2e/tests/console/storage/create-storage-class.spec.ts`:
- Around line 47-49: The option lookup for the provisioner is using
page.getByRole('option', { name: provisionerName }) which does
substring/case-insensitive matching; update the selector to use exact: true so
the test picks the exact provisioner string. Locate the block that clicks
'storage-class-provisioner-dropdown' and replace the getByRole call that uses
provisionerName with one that passes { name: provisionerName, exact: true } to
ensure the correct option is selected.

In `@frontend/e2e/tests/console/storage/snapshot.spec.ts`:
- Around line 104-111: Move the cluster-scoped VolumeSnapshotClass deletion out
of the final test.step and into a failure-safe teardown (e.g., an afterEach hook
or a try-finally surrounding each test) so it always runs even if assertions
fail; call k8sClient.deleteClusterCustomResource for
'snapshot.storage.k8s.io'/'v1'/'volumesnapshotclasses' with
SnapshotClass.metadata.name inside that teardown and handle "already deleted"
cases by catching the error and ignoring 404/NotFound responses. Ensure you
remove the existing deletion from the test.step to avoid double-deletes and keep
the teardown idempotent.

In `@frontend/e2e/tests/console/storage/volume-attributes-class.spec.ts`:
- Around line 31-59: The cluster-scoped resources created via
k8sClient.createClusterCustomResource (STORAGE_CLASS, VAC_LOW_IOPS,
VAC_HIGH_IOPS, VAC_INVALID) are only cleaned up in the test body and will leak
if the test fails early; move deletion of those cluster-scoped resources into a
guaranteed teardown (e.g., an afterAll hook) or wrap the setup/test in a
try/finally so that you call the corresponding
k8sClient.deleteClusterCustomResource for each resource on teardown; keep
cleanup.trackNamespace(ns) for the namespace but ensure cluster-scoped deletes
run in afterAll (or finally) so they always execute even on assertion failures.

---

Nitpick comments:
In `@frontend/e2e/clients/kubernetes-client.ts`:
- Around line 491-498: Replace the unsafe `as any` cast on the argument passed
to `this.coApi.patchClusterCustomObject` by defining and using an explicit
options interface (e.g., PatchClusterCustomObjectOptions) that matches the shape
required by the k8s client (properties: body, group, name, plural, version,
contentType) and use that type for the object literal; then update the call
signature to pass the strongly-typed object instead of `as any`. Also apply the
same change to the similar `patchDeployment` call to keep both
`patchClusterCustomObject` and `patchDeployment` consistent and type-safe.

In `@frontend/e2e/tests/console/storage/clone.spec.ts`:
- Around line 12-141: Both tests duplicate the same clone flow; extract a
parameterized helper (e.g., runCloneTest) that accepts the fixtures ({ page,
k8sClient, cleanup }), an nsPrefix, and optional storageClass, and move the
shared steps (namespace/resource setup using PVC, PVCGP3, testerDeployment;
wait-for-Bound using ListPage; clone flow using ModalPage and cloneSize; verify
via k8sClient.getPVC and page checks; delete) into it; then replace the two
tests with calls to runCloneTest(fixtures, 'test-clone') and
runCloneTest(fixtures, 'test-clone-sc', 'gp3-csi') to select the alternate
storage class when provided.

In `@frontend/e2e/tests/console/storage/snapshot.spec.ts`:
- Around line 19-56: Extract the duplicated setup into a single helper like
setupSnapshotEnv(k8sClient, cleanup, ns) that performs the namespace creation
(k8sClient.createNamespace), PVC creation (k8sClient.createPVC), deployment
creation (k8sClient.createDeployment), VolumeSnapshotClass creation
(k8sClient.createClusterCustomResource for 'snapshot.storage.k8s.io' v1
volumesnapshotclasses), waits for the PVC to become Bound using
listPage.navigateTo + listPage.filterByName + expect on the status locator, and
performs the snapshot creation steps (navigate to VolumeSnapshot list, open
dropdowns and submit modal). Replace the two duplicated blocks in both tests
with calls to this helper, passing any needed identifiers like pvcName or ns so
both tests share the same implementation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: openshift/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: b7421ca8-28e4-4cc9-9244-c0b333a06ba2

📥 Commits

Reviewing files that changed from the base of the PR and between e55c685 and 6b2c7ce.

📒 Files selected for processing (18)
  • frontend/e2e/clients/kubernetes-client.ts
  • frontend/e2e/mocks/storage.ts
  • frontend/e2e/pages/base-page.ts
  • frontend/e2e/pages/list-page.ts
  • frontend/e2e/pages/modal-page.ts
  • frontend/e2e/tests/console/storage/clone.spec.ts
  • frontend/e2e/tests/console/storage/create-storage-class.spec.ts
  • frontend/e2e/tests/console/storage/snapshot.spec.ts
  • frontend/e2e/tests/console/storage/volume-attributes-class.spec.ts
  • frontend/packages/integration-tests/mocks/snapshot.ts
  • frontend/packages/integration-tests/mocks/storage-class.ts
  • frontend/packages/integration-tests/mocks/volume-attributes-class.ts
  • frontend/packages/integration-tests/tests/storage/clone.cy.ts
  • frontend/packages/integration-tests/tests/storage/create-storage-class.cy.ts
  • frontend/packages/integration-tests/tests/storage/snapshot.cy.ts
  • frontend/packages/integration-tests/tests/storage/volume-attributes-class.cy.ts
  • frontend/packages/integration-tests/views/storage/create-storage-class.ts
  • frontend/packages/integration-tests/views/storage/snapshot.ts
💤 Files with no reviewable changes (9)
  • frontend/packages/integration-tests/views/storage/create-storage-class.ts
  • frontend/packages/integration-tests/mocks/snapshot.ts
  • frontend/packages/integration-tests/tests/storage/volume-attributes-class.cy.ts
  • frontend/packages/integration-tests/mocks/volume-attributes-class.ts
  • frontend/packages/integration-tests/tests/storage/create-storage-class.cy.ts
  • frontend/packages/integration-tests/mocks/storage-class.ts
  • frontend/packages/integration-tests/tests/storage/clone.cy.ts
  • frontend/packages/integration-tests/views/storage/snapshot.ts
  • frontend/packages/integration-tests/tests/storage/snapshot.cy.ts

Comment on lines +47 to +49
await page.getByTestId('storage-class-provisioner-dropdown').click();
await page.getByTestId('console-select-search-input').locator('input').fill(provisionerName);
await page.getByRole('option', { name: provisionerName }).click();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use exact: true when selecting the provisioner option.

getByRole('option', { name: provisionerName }) does case-insensitive substring matching by default. If one provisioner name is a substring of another that survives the search filter, this can resolve to multiple options (strict-mode failure) or the wrong one. The dropdown selection at Line 88 already uses exact: true; mirror that here for consistency and safety.

🔧 Proposed fix
-          await page.getByRole('option', { name: provisionerName }).click();
+          await page.getByRole('option', { name: provisionerName, exact: true }).click();
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
await page.getByTestId('storage-class-provisioner-dropdown').click();
await page.getByTestId('console-select-search-input').locator('input').fill(provisionerName);
await page.getByRole('option', { name: provisionerName }).click();
await page.getByTestId('storage-class-provisioner-dropdown').click();
await page.getByTestId('console-select-search-input').locator('input').fill(provisionerName);
await page.getByRole('option', { name: provisionerName, exact: true }).click();
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/e2e/tests/console/storage/create-storage-class.spec.ts` around lines
47 - 49, The option lookup for the provisioner is using page.getByRole('option',
{ name: provisionerName }) which does substring/case-insensitive matching;
update the selector to use exact: true so the test picks the exact provisioner
string. Locate the block that clicks 'storage-class-provisioner-dropdown' and
replace the getByRole call that uses provisionerName with one that passes {
name: provisionerName, exact: true } to ensure the correct option is selected.

Comment on lines +104 to +111
await test.step('Clean up cluster-scoped resources', async () => {
await k8sClient.deleteClusterCustomResource(
'snapshot.storage.k8s.io',
'v1',
'volumesnapshotclasses',
SnapshotClass.metadata.name,
);
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check Playwright parallelism config and any per-file overrides
fd -t f 'playwright.config.*' frontend/e2e --exec cat {}
rg -nP '(fullyParallel|test\.describe\.(parallel|serial)|workers)' frontend/e2e

Repository: openshift/console

Length of output: 43


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Playwright config files under frontend/e2e =="
fd -t f 'playwright.config.*' frontend/e2e | sort | sed 's/^/FILE: /'

echo "== Contents of any playwright.config.* files (limited) =="
for f in $(fd -t f 'playwright.config.*' frontend/e2e | sort); do
  echo "--- $f (first 220 lines) ---"
  sed -n '1,220p' "$f"
done

echo "== Search for parallel/serial controls under frontend/e2e (config/tests) =="
rg -nP '(fullyParallel|test\.describe\.(parallel|serial)|workers)' frontend/e2e || true

echo "== Inspect snapshot.spec.ts cleanup/teardown context =="
# Print a manageable slice around the referenced line numbers
# (Using sed with guessed line windows; adjust if file shorter)
file="frontend/e2e/tests/console/storage/snapshot.spec.ts"
if [ -f "$file" ]; then
  echo "--- $file (lines 1-220) ---"
  sed -n '1,220p' "$file"
  echo "--- $file (lines 220-520) ---"
  sed -n '220,520p' "$file"
else
  echo "Missing file: $file" >&2
fi

Repository: openshift/console

Length of output: 8126


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Find Playwright config files repo-wide =="
fd -t f 'playwright.config.*' . | sort | sed 's/^/FILE: /'

echo "== Search for parallelism controls repo-wide =="
rg -n --hidden --no-ignore -S '(fullyParallel|test\.describe\.configure\(|test\.describe\.parallel|test\.describe\.serial|workers:|maxWorkers|projects:|shard|mode:\s*["'\'']parallel["'\''])' . \
  || true

echo "== Inspect Playwright fixtures used by snapshot.spec.ts =="
# The spec imports ../../../fixtures from frontend/e2e/tests/console/storage
# => frontend/e2e/fixtures.* (but verify existence)
fd -t f 'fixtures.*' frontend/e2e | sort | head -n 50

# Print the fixtures file(s) most likely to contain cleanup teardown
for f in $(fd -t f 'fixtures.*' frontend/e2e | sort); do
  echo "--- $f (first 220 lines) ---"
  sed -n '1,220p' "$f"
done

Repository: openshift/console

Length of output: 50374


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== frontend/playwright.config.ts (key parallelism-related sections) =="
sed -n '1,200p' frontend/playwright.config.ts
echo "----"
sed -n '200,320p' frontend/playwright.config.ts
echo "----"
rg -n --hidden -S '(fullyParallel|test\.describe\.configure|test\.describe\.(parallel|serial)|mode:\s*["'"'"'](parallel|serial)["'"'"']|workers:|maxWorkers|projects:\s*\[)' frontend/playwright.config.ts

echo "== snapshot.spec.ts parallelism keywords =="
rg -n '(fullyParallel|test\.describe\.configure|test\.describe\.(parallel|serial)|mode:\s*["'"'"'](parallel|serial)["'"'"'])' frontend/e2e/tests/console/storage/snapshot.spec.ts || true

echo "== Locate fixtures entrypoint imported as ../../../fixtures =="
# From frontend/e2e/tests/console/storage/snapshot.spec.ts -> frontend/e2e/tests/fixtures? Actually ../../../fixtures
# We'll resolve by searching for 'export const cleanup' or 'cleanup' fixture in frontend/e2e/fixtures
fd -t f 'fixtures.*' frontend/e2e | sort | head -n 50
fd -t f 'index.*' frontend/e2e/fixtures frontend/e2e/tests frontend/e2e | sort | head -n 50

echo "== Search for cleanup fixture implementation (limit to frontend/e2e) =="
rg -n --hidden -S 'fixture\(\s*['"'"'"]cleanup['"'"'"]|export const cleanup|cleanup:\s*async|cleanup\s*\)' frontend/e2e --glob '*.{ts,js,mjs,cjs}' || true

Repository: openshift/console

Length of output: 4485


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="frontend/e2e/fixtures/index.ts"
echo "== $file (cleanup + trackNamespace sections) =="
# show around cleanup fixture
sed -n '1,140p' "$file"
echo "----"
rg -n --hidden -S 'trackNamespace|cleanup|deleteClusterCustomResource|deleteCluster|cluster-scoped|VolumeSnapshotClass|volumesnapshotclasses' "$file" || true

Repository: openshift/console

Length of output: 2682


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="frontend/e2e/fixtures/cleanup-fixture.ts"
echo "== $file =="
wc -l "$file"
sed -n '1,220p' "$file"
echo "----"
sed -n '220,520p' "$file" || true
echo "----"
rg -n --hidden -S 'trackNamespace|count|shouldSkipCleanup|executeCleanup|deleteClusterCustomResource|volumesnapshotclasses|SnapshotClass|VolumeSnapshotClass|cluster-scoped' frontend/e2e/fixtures/cleanup-fixture.ts

Repository: openshift/console

Length of output: 5338


🏁 Script executed:

#!/bin/bash
set -euo pipefail
rg -n --hidden -S '(fullyParallel|test\.describe\.configure\(|mode:\s*["'"'"']parallel["'"'"'])' frontend/e2e/tests --glob '*.{ts,js,mjs,cjs}' || true

Repository: openshift/console

Length of output: 43


Move VolumeSnapshotClass deletion into failure-safe teardown.

Right now the cluster-scoped volumesnapshotclasses (SnapshotClass.metadata.name) is deleted only in the last test.step, so any earlier assertion/wait failure prevents the delete and leaves the VSC behind (the cleanup fixture only tracks namespaces and won’t delete an untracked cluster-scoped resource).

test.describe parallel mode (fullyParallel / mode: 'parallel') isn’t used here, and CI runs with workers: 1, so the shared-VSC teardown race between the two tests is unlikely in CI; the primary fix is still making teardown run via afterEach/try-finally and handling “already deleted” cases (e.g., ignore 404).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/e2e/tests/console/storage/snapshot.spec.ts` around lines 104 - 111,
Move the cluster-scoped VolumeSnapshotClass deletion out of the final test.step
and into a failure-safe teardown (e.g., an afterEach hook or a try-finally
surrounding each test) so it always runs even if assertions fail; call
k8sClient.deleteClusterCustomResource for
'snapshot.storage.k8s.io'/'v1'/'volumesnapshotclasses' with
SnapshotClass.metadata.name inside that teardown and handle "already deleted"
cases by catching the error and ignoring 404/NotFound responses. Ensure you
remove the existing deletion from the test.step to avoid double-deletes and keep
the teardown idempotent.

Comment on lines +31 to +59
await test.step('Set up namespace and cluster-scoped resources', async () => {
await k8sClient.createNamespace(ns);
cleanup.trackNamespace(ns);
await k8sClient.createClusterCustomResource(
'storage.k8s.io',
'v1',
'storageclasses',
STORAGE_CLASS as any,
);
await k8sClient.createClusterCustomResource(
'storage.k8s.io',
'v1',
'volumeattributesclasses',
VAC_LOW_IOPS as any,
);
await k8sClient.createClusterCustomResource(
'storage.k8s.io',
'v1',
'volumeattributesclasses',
VAC_HIGH_IOPS as any,
);
await k8sClient.createClusterCustomResource(
'storage.k8s.io',
'v1',
'volumeattributesclasses',
VAC_INVALID as any,
);
await k8sClient.createDeployment(ns, getDeployment(ns, TEST_PVC) as any);
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Cluster-scoped resources will leak if test fails before cleanup step.

The test creates cluster-scoped resources (StorageClass and VolumeAttributesClasses) in the setup step but only cleans them up in a test step at the end. If any assertion fails before line 137, these cluster-scoped resources will remain in the cluster because:

  • They are not namespace-scoped (won't be deleted with the namespace).
  • The cleanup fixture only tracks the namespace (line 33), not the cluster-scoped resources.
  • The manual cleanup step (lines 137-153) is part of the test body, not a guaranteed teardown hook.

Move cluster-scoped resource cleanup to an afterAll hook or use a try/finally pattern to ensure cleanup runs even on test failure. Based on learnings, manual suite setup/teardown using afterAll with a KubernetesClient instance is the documented pattern for resources that need guaranteed cleanup.

♻️ Recommended fix: Move cleanup to afterAll
 test.describe('VolumeAttributesClass E2E tests', { tag: ['`@admin`', '`@storage`'] }, () => {
   test.skip(!isAws, 'Requires AWS platform with EBS CSI driver');

+  let clusterResources: {
+    vacNames: string[];
+    storageClassName: string;
+  } | null = null;
+
+  test.afterAll(async ({ k8sClient }) => {
+    if (!clusterResources) return;
+    
+    const { vacNames, storageClassName } = clusterResources;
+    
+    for (const vacName of vacNames) {
+      await k8sClient
+        .patchClusterCustomResource('storage.k8s.io', 'v1', 'volumeattributesclasses', vacName, {
+          metadata: { finalizers: [] },
+        })
+        .catch(() => {});
+    }
+    for (const vacName of vacNames) {
+      await k8sClient
+        .deleteClusterCustomResource('storage.k8s.io', 'v1', 'volumeattributesclasses', vacName)
+        .catch(() => {});
+    }
+    await k8sClient
+      .deleteClusterCustomResource('storage.k8s.io', 'v1', 'storageclasses', storageClassName)
+      .catch(() => {});
+  });
+
   test('creates PVC with VAC, modifies VAC, and handles invalid VAC', async ({
     page,
     k8sClient,
     cleanup,
   }) => {
     const ns = `test-vac-${Date.now()}`;
     const fixtures = getVACFixtures(ns);
     const {
       VAC_LOW_IOPS,
       VAC_HIGH_IOPS,
       VAC_INVALID,
       STORAGE_CLASS,
       TEST_VAC_LOW_IOPS,
       TEST_VAC_HIGH_IOPS,
       TEST_VAC_INVALID,
       TEST_PVC,
       TEST_STORAGECLASS,
       getDeployment,
     } = fixtures;
     const modal = new ModalPage(page);

+    clusterResources = {
+      vacNames: [TEST_VAC_LOW_IOPS, TEST_VAC_HIGH_IOPS, TEST_VAC_INVALID],
+      storageClassName: TEST_STORAGECLASS,
+    };
+
     await test.step('Set up namespace and cluster-scoped resources', async () => {
       // ... existing setup code ...
     });

     // ... rest of test steps ...

-    await test.step('Clean up cluster-scoped resources', async () => {
-      for (const vacName of [TEST_VAC_LOW_IOPS, TEST_VAC_HIGH_IOPS, TEST_VAC_INVALID]) {
-        await k8sClient
-          .patchClusterCustomResource('storage.k8s.io', 'v1', 'volumeattributesclasses', vacName, {
-            metadata: { finalizers: [] },
-          })
-          .catch(() => {});
-      }
-      for (const vacName of [TEST_VAC_LOW_IOPS, TEST_VAC_HIGH_IOPS, TEST_VAC_INVALID]) {
-        await k8sClient
-          .deleteClusterCustomResource('storage.k8s.io', 'v1', 'volumeattributesclasses', vacName)
-          .catch(() => {});
-      }
-      await k8sClient
-        .deleteClusterCustomResource('storage.k8s.io', 'v1', 'storageclasses', TEST_STORAGECLASS)
-        .catch(() => {});
-    });
   });
 });

Also applies to: 137-153

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/e2e/tests/console/storage/volume-attributes-class.spec.ts` around
lines 31 - 59, The cluster-scoped resources created via
k8sClient.createClusterCustomResource (STORAGE_CLASS, VAC_LOW_IOPS,
VAC_HIGH_IOPS, VAC_INVALID) are only cleaned up in the test body and will leak
if the test fails early; move deletion of those cluster-scoped resources into a
guaranteed teardown (e.g., an afterAll hook) or wrap the setup/test in a
try/finally so that you call the corresponding
k8sClient.deleteClusterCustomResource for each resource on teardown; keep
cleanup.trackNamespace(ns) for the namespace but ensure cluster-scoped deletes
run in afterAll (or finally) so they always execute even on assertion failures.

@openshift-ci
Copy link
Copy Markdown
Contributor

openshift-ci Bot commented May 29, 2026

@Cragsmann: The following test failed, say /retest to rerun all failed tests or /retest-required to rerun all mandatory failed tests:

Test name Commit Details Required Rerun command
ci/prow/e2e-gcp-console 6b2c7ce link true /test e2e-gcp-console

Full PR test history. Your PR dashboard.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. kind/cypress Related to Cypress e2e integration testing

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants