Skip to content

CONSOLE-5299: migrate dev-console Cypress tests to Playwright (batch 1)#16475

Draft
Cragsmann wants to merge 1 commit into
openshift:mainfrom
Cragsmann:CONSOLE-5299
Draft

CONSOLE-5299: migrate dev-console Cypress tests to Playwright (batch 1)#16475
Cragsmann wants to merge 1 commit into
openshift:mainfrom
Cragsmann:CONSOLE-5299

Conversation

@Cragsmann
Copy link
Copy Markdown
Contributor

@Cragsmann Cragsmann commented May 20, 2026

Analysis / Root cause:
Migrating dev-console Cypress E2E tests to Playwright as part of the broader Cypress→Playwright migration effort (CONSOLE-5299). The existing Gherkin .feature files use a 4-layer indirection (feature → step-def → page-action → page-object) that is collapsed into idiomatic Playwright 2-file structure (spec + page object).

Solution description:
Migrate 18 Gherkin feature files from packages/dev-console/integration-tests/ to Playwright under e2e/tests/dev-console/, covering:

  • Project lifecycle: creation, access
  • Dev perspective navigation: search, routes, config maps, pods
  • Vulnerability scanning: project vulnerability page
  • Samples: sample card creation with builder images and devfiles
  • Health checks: adding probes via topology sidebar
  • User preferences: perspective, topology view, resource method, resource type
  • Catalog customization: enabling/disabling sub-catalogs and add page items via form view
  • Perspective configuration: enabling/disabling dev perspective
  • Pinned resources: configuring pinned resources in console customization

Files added:

  • 10 page objects under e2e/pages/dev-console/
  • 18 spec files under e2e/tests/dev-console/

Key patterns:

  • Manual/broken tests preserved as test.skip(true, 'reason') with annotations
  • Automatable tests use self-contained create→assert→cleanup pattern
  • Page objects extend BasePage with robustClick(), waitForLoadingComplete(), goTo()
  • KubernetesClient replaces cy.exec('oc ...') for resource management

Screenshots / screen recording:
N/A — test infrastructure only, no UI changes

Test setup:

  • Requires a running OpenShift cluster with dev-console enabled
  • Configure e2e/.env with cluster credentials
  • Run: npx playwright test --project=dev-console

Test cases:

  • Skip-only tests pass (framework + cluster connection verified)
  • Automatable tests require Developer perspective enabled on cluster
  • TypeScript compilation passes (npx tsc --noEmit -p e2e/tsconfig.json)
  • ESLint passes (yarn eslint e2e/tests/dev-console/ e2e/pages/dev-console/)

Browser conformance:

  • Chrome
  • Firefox
  • Safari (or Epiphany on Linux)

Additional info:
This is batch 1 of the dev-console migration. ~42 feature files remain for subsequent batches.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Tests
    • Added comprehensive end-to-end test coverage for Developer Console features including: ConfigMap creation and editing, health checks configuration, perspective customization, pod list management, route creation, sample applications, resource search, user preferences, project creation, and vulnerability scanning.

Migrate 18 Gherkin feature files from dev-console Cypress integration
tests to Playwright, covering: project lifecycle, search, routes,
config maps, pods, vulnerabilities, samples, health checks, user
preferences, catalog customization, perspective configuration, and
pinned resources.

- 10 page objects under e2e/pages/dev-console/
- 18 spec files under e2e/tests/dev-console/
- Manual/broken tests preserved as test.skip() with reason annotations
- Automatable tests use self-contained create→assert→cleanup pattern

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 20, 2026
@openshift-ci-robot
Copy link
Copy Markdown
Contributor

openshift-ci-robot commented May 20, 2026

@Cragsmann: This pull request references CONSOLE-5299 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 sub-task to target the "5.0.0" version, but no target version was set.

Details

In response to this:

Analysis / Root cause:
Migrating dev-console Cypress E2E tests to Playwright as part of the broader Cypress→Playwright migration effort (CONSOLE-5299). The existing Gherkin .feature files use a 4-layer indirection (feature → step-def → page-action → page-object) that is collapsed into idiomatic Playwright 2-file structure (spec + page object).

Solution description:
Migrate 18 Gherkin feature files from packages/dev-console/integration-tests/ to Playwright under e2e/tests/dev-console/, covering:

  • Project lifecycle: creation, access
  • Dev perspective navigation: search, routes, config maps, pods
  • Vulnerability scanning: project vulnerability page
  • Samples: sample card creation with builder images and devfiles
  • Health checks: adding probes via topology sidebar
  • User preferences: perspective, topology view, resource method, resource type
  • Catalog customization: enabling/disabling sub-catalogs and add page items via form view
  • Perspective configuration: enabling/disabling dev perspective
  • Pinned resources: configuring pinned resources in console customization

Files added:

  • 10 page objects under e2e/pages/dev-console/
  • 18 spec files under e2e/tests/dev-console/

Key patterns:

  • Manual/broken tests preserved as test.skip(true, 'reason') with annotations
  • Automatable tests use self-contained create→assert→cleanup pattern
  • Page objects extend BasePage with robustClick(), waitForLoadingComplete(), goTo()
  • KubernetesClient replaces cy.exec('oc ...') for resource management

Screenshots / screen recording:
N/A — test infrastructure only, no UI changes

Test setup:

  • Requires a running OpenShift cluster with dev-console enabled
  • Configure e2e/.env with cluster credentials
  • Run: npx playwright test --project=dev-console

Test cases:

  • Skip-only tests pass (framework + cluster connection verified)
  • Automatable tests require Developer perspective enabled on cluster
  • TypeScript compilation passes (npx tsc --noEmit -p e2e/tsconfig.json)
  • ESLint passes (yarn eslint e2e/tests/dev-console/ e2e/pages/dev-console/)

Browser conformance:

  • Chrome
  • Firefox
  • Safari (or Epiphany on Linux)

Additional info:
This is batch 1 of the dev-console migration. ~42 feature files remain for subsequent batches.

🤖 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.

@openshift-ci openshift-ci Bot requested review from cajieh and sg00dwin May 20, 2026 18:41
@openshift-ci
Copy link
Copy Markdown
Contributor

openshift-ci Bot commented May 20, 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 logonoff 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

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 20, 2026

📝 Walkthrough

<review_stack_artifact>

Walkthrough

This PR introduces a comprehensive Playwright E2E test framework for the OpenShift Console developer perspective. It adds 10 reusable page objects that abstract common UI interactions (perspective switching, project selection, CRUD workflows across ConfigMaps, Routes, Health Checks, Samples, Search, Vulnerabilities, and Pod lists) and 16 test suites that exercise these page objects across smoke tests, feature workflows, customization scenarios, and complex multi-step interactions. The infrastructure establishes a maintainable pattern for test authoring by decoupling locators and helpers from test logic, enabling developers to write concise, readable test flows.

Suggested reviewers

  • cajieh
  • TheRealJon
  • vikram-raj
🚥 Pre-merge checks | ✅ 10 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Microshift Test Compatibility ⚠️ Warning Tests reference unavailable MicroShift APIs (DeploymentConfig, BuildConfig, AlertingRule, console.openshift.io) without [apigroup] tags, [Skipped:MicroShift] labels, or skip guards. Add [apigroup] tags to test names (e.g., [apigroup:apps.openshift.io] for DeploymentConfig tests) or skip via isMicroShiftCluster check in test bodies.
Single Node Openshift (Sno) Test Compatibility ❓ Inconclusive The custom check is written for Ginkgo E2E tests (Go backend), but this PR adds Playwright E2E tests (TypeScript frontend). The check instructions do not apply to frontend test frameworks. If SNO compatibility should be checked for Playwright tests, clarify scope and adapt the check for frontend test patterns (Playwright/Cypress). If SNO checks are only needed for backend Ginkgo tests, this PR may be excluded.
✅ Passed checks (10 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly references the Jira issue (CONSOLE-5299) and accurately summarizes the main change: migrating dev-console Cypress tests to Playwright in batch 1.
Description check ✅ Passed The PR description covers all required template sections: analysis/root cause, solution description, test setup, test cases, browser conformance, and additional context about the migration scope and patterns.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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 Test names are stable. Two parameterized tests use static values (Httpd/Go samples, HTTP/TCP/command probes). Date.now() used only for namespace setup, not test names.
Test Structure And Quality ✅ Passed Custom check targets Ginkgo tests, but PR contains Playwright tests. Framework mismatch makes check inapplicable.
Topology-Aware Scheduling Compatibility ✅ Passed PR adds only E2E test code (Playwright page objects & specs), no deployment manifests, operators, or scheduling constraints. No topology-aware scheduling issues detected.
Ote Binary Stdout Contract ✅ Passed OTE Binary Stdout Contract applies to Go/Ginkgo server tests. PR adds only Playwright E2E tests (TypeScript), which run via Node.js, not OTE binaries.
Ipv6 And Disconnected Network Test Compatibility ✅ Passed Check is designed for Ginkgo Go tests, not Playwright TypeScript tests. No IPv4-specific code patterns found in test implementation.

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

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

Warning

Review ran into problems

🔥 Problems

Git: Failed to clone repository. Please run the @coderabbitai full review command to re-trigger a full review. If the issue persists, set path_filters to include or exclude specific files.


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.

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: 11

🧹 Nitpick comments (1)
frontend/e2e/tests/dev-console/customization-of-pinned-resource.spec.ts (1)

22-24: ⚡ Quick win

Migrate to the cleanup fixture for robust resource teardown

The current afterAll pattern is missing a null-guard on k8s. If beforeAll throws before initialization, the subsequent cleanup will fail with an undefined error, masking the original failure.

However, the better approach is to use the provided cleanup fixture (per E2E guidelines). It provides built-in robustness with null-checks, error handling, and proper logging. See project-creation.spec.ts for the pattern: accept { cleanup } in the test function and call cleanup.trackNamespace(namespace).

🤖 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/dev-console/customization-of-pinned-resource.spec.ts`
around lines 22 - 24, Replace the fragile test.afterAll cleanup that calls
k8s.deleteNamespace(namespace) with the E2E cleanup fixture: accept the fixture
in the test declaration (use the same pattern as project-creation.spec.ts),
remove the test.afterAll block, and call cleanup.trackNamespace(namespace) when
the namespace is created so teardown is robust and avoids null-guard issues with
k8s; update references to test.afterAll/k8s.deleteNamespace to use
cleanup.trackNamespace(namespace).
🤖 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/pages/dev-console/perspective-page.ts`:
- Around line 43-52: The current implementation in switchToAdministrator calls
switchToPerspective('Administrator') unconditionally which can fail when the UI
uses the new "Core platform" label; first read
perspectiveSwitcherToggle.textContent() and if it already contains
"Administrator" return, otherwise if it contains "Core platform" call
switchToPerspective('Core platform'); only if neither label is present attempt
switchToPerspective('Administrator') and if that doesn't result in the expected
label then fallback to switchToPerspective('Core platform') so the function
handles both legacy and new labels reliably.

In `@frontend/e2e/pages/dev-console/route-page.ts`:
- Around line 41-42: The dropdown selector breaks because port is derived via a
fixed 4-character substring; update the parsing in route-page.ts where
targetPort is handled so the port is extracted as the actual numeric port string
(e.g., strip protocol/suffix like "/tcp" and non-digit chars, or split on '/'
and take the first token, or parseInt and convert back to string) before calling
this.page.locator and this.robustClick (referenced symbols: targetPort, const
port, page.locator, robustClick). Ensure the resulting port string exactly
matches the dropdown data-test value (e.g., "80" or "8080") rather than a
fixed-width slice.

In `@frontend/e2e/pages/dev-console/samples-page.ts`:
- Around line 72-74: The test currently only checks visibility; update
expectGitUrlReadonly to assert the field is actually read-only by verifying it's
not editable and/or has the readonly attribute. For example, replace the
visibility-only assertion with checks against the Locator this.gitUrlInput such
as awaiting expect(this.gitUrlInput).not.toBeEditable() and/or awaiting
expect(this.gitUrlInput).toHaveAttribute('readonly', expect.any(String)) so the
method verifies read-only behavior rather than just visibility.

In `@frontend/e2e/tests/dev-console/add-health-checks-topology-sidebar.spec.ts`:
- Around line 20-45: The test creates multiple deployments using deployments and
k8s.appsV1Api.createNamespacedDeployment but proceeds immediately to topology
actions; add an await loop after creation that polls the Deployment status (via
k8s.appsV1Api.readNamespacedDeployment or readNamespacedDeploymentStatus) for
each name in deployments in the given namespace until status.availableReplicas
>= 1 (or desired replicas) or a timeout is reached, and only continue once all
created deployments report ready to avoid race flakes.

In `@frontend/e2e/tests/dev-console/configure-perspectives.spec.ts`:
- Around line 43-46: Replace the silent .catch on the click with an explicit
visibility check: locate the clear button via
page.locator('button[aria-label="Clear input value"]') into a variable (e.g.,
clearButton), call await clearButton.isVisible() and only call await
clearButton.click() if that returns true so failures to click aren't swallowed;
use the same locator and methods (page.locator, isVisible, click) to implement
this conditional flow.
- Around line 31-42: The test falls back to selecting the first
.co-resource-item when the expected consoleItem (variable consoleItem) isn't
found, masking failures; update the UI component that renders resource items to
include a deterministic data-test attribute (e.g.,
data-test={`resource-item-${groupName}`} on the resource item element) and
change the test (using the existing combobox, filterInput and consoleItem
locators) to select by that data-test selector and assert its count is > 0,
failing the test if the specific resource-item-{groupName} is not present
instead of using .first().

In
`@frontend/e2e/tests/dev-console/customization-of-catalog-add-page-form-view.spec.ts`:
- Around line 26-188: Add an afterEach cleanup that restores cluster
customization to a known baseline after each test (or mark the suite .serial);
implement it by instantiating CustomizationPage (and PerspectivePage if needed),
navigating to consoles/cluster/customization (use methods navigateToConsoles,
clickCluster, openCustomization), switching to the Developer tab
(clickDeveloperTab), and calling the appropriate reset method (e.g.,
enableAll/enableOnlySoftwareCatalogItem, disableAllAddPageItems reversal or a
new restoreDefaultCustomization helper) then saving and waiting for
expectSaveMessage to succeed so each test leaves the cluster in a consistent
state.
- Around line 22-24: The afterAll teardown unconditionally calls
k8s.deleteNamespace(namespace) which can throw if namespace or k8s were never
initialized; guard the call by checking that both the KubernetesClient instance
(k8s) and the namespace variable are defined before calling deleteNamespace.
Locate the test.afterAll block and wrap or conditionally execute
k8s.deleteNamespace(namespace) only when namespace and k8s are truthy (mirror
the pattern used in oauth.spec.ts that checks if (!originalOAuthConfig ||
!client)). Ensure the check covers failures from new KubernetesClient() and
createNamespace() in beforeAll.

In `@frontend/e2e/tests/dev-console/pod-list-page.spec.ts`:
- Around line 44-56: The current setup swallows failures from
k8s.waitForCustomResourceCondition (called with 'apps','v1', namespace,
'deployments','nodejs-ex-git') which masks real cluster issues; remove the
.catch suppression so the test fails fast and surfaces the original error, or
replace the suppressed branch with an explicit assertion that pods exist (e.g.,
call a pod-listing/wait helper for the deployment and assert length>0) so
failures are reported with context before proceeding to UI steps.

In `@frontend/e2e/tests/dev-console/project-creation.spec.ts`:
- Around line 16-19: Move the cleanup registration to happen before triggering
project creation so the namespace is always tracked even if creation throws;
specifically, call cleanup.trackNamespace(projectName) before awaiting
perspectivePage.selectOrCreateProject(projectName) in the test step that opens
the Create Project modal (references: perspectivePage.selectOrCreateProject and
cleanup.trackNamespace). Ensure the order is changed in that block so
cleanup.trackNamespace runs first but keep the same projectName variable and
existing assertions intact.

In `@frontend/e2e/tests/dev-console/user-preferences-dev-perspective.spec.ts`:
- Around line 80-221: Tests mutate durable user preferences (Topology,
Create/Edit resource method, Resource Type) without restoring them; capture the
current preference before calling UserPreferencesPage.changePreferenceDropdown
and restore it in a finally block (or implement a shared afterEach cleanup) so
mutations don't leak across tests: for each test that calls
UserPreferencesPage.changePreferenceDropdown (e.g., tests using PerspectivePage
and UserPreferencesPage, methods
openUserPreferences/changePreferenceDropdown/reloadConsole/expectTopologyGraphView/expectYamlViewSelected/clickTab),
read and store the prior value (via an existing getter on UserPreferencesPage or
by reading the dropdown state), then in finally call changePreferenceDropdown to
revert to the saved value (or call a new UserPreferencesPage.resetPreference
helper), ensuring cleanup occurs even on failures.

---

Nitpick comments:
In `@frontend/e2e/tests/dev-console/customization-of-pinned-resource.spec.ts`:
- Around line 22-24: Replace the fragile test.afterAll cleanup that calls
k8s.deleteNamespace(namespace) with the E2E cleanup fixture: accept the fixture
in the test declaration (use the same pattern as project-creation.spec.ts),
remove the test.afterAll block, and call cleanup.trackNamespace(namespace) when
the namespace is created so teardown is robust and avoids null-guard issues with
k8s; update references to test.afterAll/k8s.deleteNamespace to use
cleanup.trackNamespace(namespace).
🪄 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 YAML (base), Central YAML (inherited)

Review profile: CHILL

Plan: Enterprise

Run ID: 280e00db-b189-416a-9ee5-ff23daa97f09

📥 Commits

Reviewing files that changed from the base of the PR and between 226ddf9 and 15dfb40.

📒 Files selected for processing (28)
  • frontend/e2e/pages/dev-console/config-map-page.ts
  • frontend/e2e/pages/dev-console/customization-page.ts
  • frontend/e2e/pages/dev-console/health-checks-page.ts
  • frontend/e2e/pages/dev-console/perspective-page.ts
  • frontend/e2e/pages/dev-console/pod-list-page.ts
  • frontend/e2e/pages/dev-console/route-page.ts
  • frontend/e2e/pages/dev-console/samples-page.ts
  • frontend/e2e/pages/dev-console/search-page.ts
  • frontend/e2e/pages/dev-console/user-preferences-page.ts
  • frontend/e2e/pages/dev-console/vulnerability-page.ts
  • frontend/e2e/tests/dev-console/add-health-checks-git-form.spec.ts
  • frontend/e2e/tests/dev-console/add-health-checks-topology-sidebar.spec.ts
  • frontend/e2e/tests/dev-console/config-maps-dev-perspective.spec.ts
  • frontend/e2e/tests/dev-console/configure-perspectives.spec.ts
  • frontend/e2e/tests/dev-console/configure-pinned-resources.spec.ts
  • frontend/e2e/tests/dev-console/customization-of-catalog-add-page-form-view.spec.ts
  • frontend/e2e/tests/dev-console/customization-of-catalog.spec.ts
  • frontend/e2e/tests/dev-console/customization-of-pinned-resource.spec.ts
  • frontend/e2e/tests/dev-console/getting-started-to-dev-perspective.spec.ts
  • frontend/e2e/tests/dev-console/getting-started-tour-dev-perspective.spec.ts
  • frontend/e2e/tests/dev-console/pod-list-page.spec.ts
  • frontend/e2e/tests/dev-console/project-access.spec.ts
  • frontend/e2e/tests/dev-console/project-creation.spec.ts
  • frontend/e2e/tests/dev-console/project-vulnerability.spec.ts
  • frontend/e2e/tests/dev-console/route-dev-perspective.spec.ts
  • frontend/e2e/tests/dev-console/sample-card-add-page.spec.ts
  • frontend/e2e/tests/dev-console/search-devperspective.spec.ts
  • frontend/e2e/tests/dev-console/user-preferences-dev-perspective.spec.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (14)
frontend/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

frontend/**/*.{ts,tsx,js,jsx}: Never import from package index files (e.g., @console/shared) in new code, as they can create circular dependencies and slow builds. Import from specific file paths instead.
Do not use backticks in t() calls for i18n strings, as the i18n parser cannot extract keys from template literals. Use single or double quotes instead.

Files:

  • frontend/e2e/pages/dev-console/pod-list-page.ts
  • frontend/e2e/tests/dev-console/customization-of-pinned-resource.spec.ts
  • frontend/e2e/tests/dev-console/add-health-checks-git-form.spec.ts
  • frontend/e2e/tests/dev-console/getting-started-tour-dev-perspective.spec.ts
  • frontend/e2e/tests/dev-console/project-access.spec.ts
  • frontend/e2e/tests/dev-console/customization-of-catalog.spec.ts
  • frontend/e2e/pages/dev-console/config-map-page.ts
  • frontend/e2e/pages/dev-console/search-page.ts
  • frontend/e2e/tests/dev-console/configure-pinned-resources.spec.ts
  • frontend/e2e/tests/dev-console/getting-started-to-dev-perspective.spec.ts
  • frontend/e2e/tests/dev-console/configure-perspectives.spec.ts
  • frontend/e2e/tests/dev-console/project-creation.spec.ts
  • frontend/e2e/tests/dev-console/search-devperspective.spec.ts
  • frontend/e2e/tests/dev-console/project-vulnerability.spec.ts
  • frontend/e2e/tests/dev-console/route-dev-perspective.spec.ts
  • frontend/e2e/tests/dev-console/sample-card-add-page.spec.ts
  • frontend/e2e/pages/dev-console/health-checks-page.ts
  • frontend/e2e/pages/dev-console/user-preferences-page.ts
  • frontend/e2e/pages/dev-console/samples-page.ts
  • frontend/e2e/pages/dev-console/customization-page.ts
  • frontend/e2e/tests/dev-console/pod-list-page.spec.ts
  • frontend/e2e/tests/dev-console/add-health-checks-topology-sidebar.spec.ts
  • frontend/e2e/pages/dev-console/route-page.ts
  • frontend/e2e/tests/dev-console/customization-of-catalog-add-page-form-view.spec.ts
  • frontend/e2e/pages/dev-console/perspective-page.ts
  • frontend/e2e/pages/dev-console/vulnerability-page.ts
  • frontend/e2e/tests/dev-console/config-maps-dev-perspective.spec.ts
  • frontend/e2e/tests/dev-console/user-preferences-dev-perspective.spec.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Never import from deprecated packages or use code with the @deprecated TSdoc tag in new code.

**/*.{ts,tsx}: Use React functional components with hooks instead of class components
State Management: Use React hooks and Context API (migrating away from legacy Redux/Immutable.js)
Hooks: Use existing hooks from console-shared when possible (useK8sWatchResource, useUserSettings, etc.)
API calls: Use k8s resource hooks for data fetching, consoleFetchJSON for HTTP requests
Extensions: Use console extension points for plugin integration
Types: Check existing types in console-shared before creating new ones
Dynamic Plugins: Use console extension points for plugin integration
Styling: Use SCSS modules co-located with components, PatternFly design system components, avoid any SCSS/CSS if possible
Accessibility: Follow WCAG 2.1 AA standards, use semantic HTML, ARIA labels where needed, ensure keyboard navigation, test with screen readers
i18n: Use useTranslation('namespace') hook with key format for translation keys
Error Handling: Use ErrorBoundary components and graceful degradation patterns
Optimize re-renders: Use useCallback for memoized callbacks to avoid function recreation every render
Optimize re-renders: Use useMemo for expensive computations to avoid recalculating on every render
Lazy loading: Use React.lazy() to lazy load heavy components
TypeScript type safety: Avoid using any type; suggest proper type definitions and verify null/undefined are handled properly
Type component props properly: Reuse existing component prop types instead of duplicating type definitions
Use proper hooks: Use specialized hooks like usePluginInfo for plugin data instead of generic data fetching patterns
Avoid deprecated components: Check for JSDoc @deprecated tags, import paths containing /deprecated, and DEPRECATED_ file name prefix before using components
Importing from barrel files and circular dependencies: Import directly from specific files instead...

Files:

  • frontend/e2e/pages/dev-console/pod-list-page.ts
  • frontend/e2e/tests/dev-console/customization-of-pinned-resource.spec.ts
  • frontend/e2e/tests/dev-console/add-health-checks-git-form.spec.ts
  • frontend/e2e/tests/dev-console/getting-started-tour-dev-perspective.spec.ts
  • frontend/e2e/tests/dev-console/project-access.spec.ts
  • frontend/e2e/tests/dev-console/customization-of-catalog.spec.ts
  • frontend/e2e/pages/dev-console/config-map-page.ts
  • frontend/e2e/pages/dev-console/search-page.ts
  • frontend/e2e/tests/dev-console/configure-pinned-resources.spec.ts
  • frontend/e2e/tests/dev-console/getting-started-to-dev-perspective.spec.ts
  • frontend/e2e/tests/dev-console/configure-perspectives.spec.ts
  • frontend/e2e/tests/dev-console/project-creation.spec.ts
  • frontend/e2e/tests/dev-console/search-devperspective.spec.ts
  • frontend/e2e/tests/dev-console/project-vulnerability.spec.ts
  • frontend/e2e/tests/dev-console/route-dev-perspective.spec.ts
  • frontend/e2e/tests/dev-console/sample-card-add-page.spec.ts
  • frontend/e2e/pages/dev-console/health-checks-page.ts
  • frontend/e2e/pages/dev-console/user-preferences-page.ts
  • frontend/e2e/pages/dev-console/samples-page.ts
  • frontend/e2e/pages/dev-console/customization-page.ts
  • frontend/e2e/tests/dev-console/pod-list-page.spec.ts
  • frontend/e2e/tests/dev-console/add-health-checks-topology-sidebar.spec.ts
  • frontend/e2e/pages/dev-console/route-page.ts
  • frontend/e2e/tests/dev-console/customization-of-catalog-add-page-form-view.spec.ts
  • frontend/e2e/pages/dev-console/perspective-page.ts
  • frontend/e2e/pages/dev-console/vulnerability-page.ts
  • frontend/e2e/tests/dev-console/config-maps-dev-perspective.spec.ts
  • frontend/e2e/tests/dev-console/user-preferences-dev-perspective.spec.ts
frontend/**/*.{ts,tsx,js,jsx,json}

📄 CodeRabbit inference engine (AGENTS.md)

Never use absolute URLs or paths in the console code. The console runs behind a proxy under an arbitrary path.

Files:

  • frontend/e2e/pages/dev-console/pod-list-page.ts
  • frontend/e2e/tests/dev-console/customization-of-pinned-resource.spec.ts
  • frontend/e2e/tests/dev-console/add-health-checks-git-form.spec.ts
  • frontend/e2e/tests/dev-console/getting-started-tour-dev-perspective.spec.ts
  • frontend/e2e/tests/dev-console/project-access.spec.ts
  • frontend/e2e/tests/dev-console/customization-of-catalog.spec.ts
  • frontend/e2e/pages/dev-console/config-map-page.ts
  • frontend/e2e/pages/dev-console/search-page.ts
  • frontend/e2e/tests/dev-console/configure-pinned-resources.spec.ts
  • frontend/e2e/tests/dev-console/getting-started-to-dev-perspective.spec.ts
  • frontend/e2e/tests/dev-console/configure-perspectives.spec.ts
  • frontend/e2e/tests/dev-console/project-creation.spec.ts
  • frontend/e2e/tests/dev-console/search-devperspective.spec.ts
  • frontend/e2e/tests/dev-console/project-vulnerability.spec.ts
  • frontend/e2e/tests/dev-console/route-dev-perspective.spec.ts
  • frontend/e2e/tests/dev-console/sample-card-add-page.spec.ts
  • frontend/e2e/pages/dev-console/health-checks-page.ts
  • frontend/e2e/pages/dev-console/user-preferences-page.ts
  • frontend/e2e/pages/dev-console/samples-page.ts
  • frontend/e2e/pages/dev-console/customization-page.ts
  • frontend/e2e/tests/dev-console/pod-list-page.spec.ts
  • frontend/e2e/tests/dev-console/add-health-checks-topology-sidebar.spec.ts
  • frontend/e2e/pages/dev-console/route-page.ts
  • frontend/e2e/tests/dev-console/customization-of-catalog-add-page-form-view.spec.ts
  • frontend/e2e/pages/dev-console/perspective-page.ts
  • frontend/e2e/pages/dev-console/vulnerability-page.ts
  • frontend/e2e/tests/dev-console/config-maps-dev-perspective.spec.ts
  • frontend/e2e/tests/dev-console/user-preferences-dev-perspective.spec.ts
frontend/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

When writing code for static plugins, ensure that all $codeRef reference the corresponding extension type from the @console/dynamic-plugin-sdk package.

Files:

  • frontend/e2e/pages/dev-console/pod-list-page.ts
  • frontend/e2e/tests/dev-console/customization-of-pinned-resource.spec.ts
  • frontend/e2e/tests/dev-console/add-health-checks-git-form.spec.ts
  • frontend/e2e/tests/dev-console/getting-started-tour-dev-perspective.spec.ts
  • frontend/e2e/tests/dev-console/project-access.spec.ts
  • frontend/e2e/tests/dev-console/customization-of-catalog.spec.ts
  • frontend/e2e/pages/dev-console/config-map-page.ts
  • frontend/e2e/pages/dev-console/search-page.ts
  • frontend/e2e/tests/dev-console/configure-pinned-resources.spec.ts
  • frontend/e2e/tests/dev-console/getting-started-to-dev-perspective.spec.ts
  • frontend/e2e/tests/dev-console/configure-perspectives.spec.ts
  • frontend/e2e/tests/dev-console/project-creation.spec.ts
  • frontend/e2e/tests/dev-console/search-devperspective.spec.ts
  • frontend/e2e/tests/dev-console/project-vulnerability.spec.ts
  • frontend/e2e/tests/dev-console/route-dev-perspective.spec.ts
  • frontend/e2e/tests/dev-console/sample-card-add-page.spec.ts
  • frontend/e2e/pages/dev-console/health-checks-page.ts
  • frontend/e2e/pages/dev-console/user-preferences-page.ts
  • frontend/e2e/pages/dev-console/samples-page.ts
  • frontend/e2e/pages/dev-console/customization-page.ts
  • frontend/e2e/tests/dev-console/pod-list-page.spec.ts
  • frontend/e2e/tests/dev-console/add-health-checks-topology-sidebar.spec.ts
  • frontend/e2e/pages/dev-console/route-page.ts
  • frontend/e2e/tests/dev-console/customization-of-catalog-add-page-form-view.spec.ts
  • frontend/e2e/pages/dev-console/perspective-page.ts
  • frontend/e2e/pages/dev-console/vulnerability-page.ts
  • frontend/e2e/tests/dev-console/config-maps-dev-perspective.spec.ts
  • frontend/e2e/tests/dev-console/user-preferences-dev-perspective.spec.ts
**/*.{tsx,ts}

📄 CodeRabbit inference engine (TESTING.md)

**/*.{tsx,ts}: Always use page.getByTestId('x') for Playwright selectors which queries [data-test="x"]. If a React element only has a legacy test attribute, add data-test to the element. Never remove legacy attributes
Prefer data-test attributes in Cypress selectors (e.g., cy.get('[data-test="create-deployment"]')) over brittle CSS/ARIA selectors

File Naming: PascalCase for components, kebab-case for utilities, *.spec.ts(x) for tests

Files:

  • frontend/e2e/pages/dev-console/pod-list-page.ts
  • frontend/e2e/tests/dev-console/customization-of-pinned-resource.spec.ts
  • frontend/e2e/tests/dev-console/add-health-checks-git-form.spec.ts
  • frontend/e2e/tests/dev-console/getting-started-tour-dev-perspective.spec.ts
  • frontend/e2e/tests/dev-console/project-access.spec.ts
  • frontend/e2e/tests/dev-console/customization-of-catalog.spec.ts
  • frontend/e2e/pages/dev-console/config-map-page.ts
  • frontend/e2e/pages/dev-console/search-page.ts
  • frontend/e2e/tests/dev-console/configure-pinned-resources.spec.ts
  • frontend/e2e/tests/dev-console/getting-started-to-dev-perspective.spec.ts
  • frontend/e2e/tests/dev-console/configure-perspectives.spec.ts
  • frontend/e2e/tests/dev-console/project-creation.spec.ts
  • frontend/e2e/tests/dev-console/search-devperspective.spec.ts
  • frontend/e2e/tests/dev-console/project-vulnerability.spec.ts
  • frontend/e2e/tests/dev-console/route-dev-perspective.spec.ts
  • frontend/e2e/tests/dev-console/sample-card-add-page.spec.ts
  • frontend/e2e/pages/dev-console/health-checks-page.ts
  • frontend/e2e/pages/dev-console/user-preferences-page.ts
  • frontend/e2e/pages/dev-console/samples-page.ts
  • frontend/e2e/pages/dev-console/customization-page.ts
  • frontend/e2e/tests/dev-console/pod-list-page.spec.ts
  • frontend/e2e/tests/dev-console/add-health-checks-topology-sidebar.spec.ts
  • frontend/e2e/pages/dev-console/route-page.ts
  • frontend/e2e/tests/dev-console/customization-of-catalog-add-page-form-view.spec.ts
  • frontend/e2e/pages/dev-console/perspective-page.ts
  • frontend/e2e/pages/dev-console/vulnerability-page.ts
  • frontend/e2e/tests/dev-console/config-maps-dev-perspective.spec.ts
  • frontend/e2e/tests/dev-console/user-preferences-dev-perspective.spec.ts
frontend/e2e/pages/**/*.ts

📄 CodeRabbit inference engine (TESTING.md)

Extend BasePage in Playwright page objects which provides robustClick(), waitForLoadingComplete(), and goTo(). Locators are private readonly properties; actions are async methods

Files:

  • frontend/e2e/pages/dev-console/pod-list-page.ts
  • frontend/e2e/pages/dev-console/config-map-page.ts
  • frontend/e2e/pages/dev-console/search-page.ts
  • frontend/e2e/pages/dev-console/health-checks-page.ts
  • frontend/e2e/pages/dev-console/user-preferences-page.ts
  • frontend/e2e/pages/dev-console/samples-page.ts
  • frontend/e2e/pages/dev-console/customization-page.ts
  • frontend/e2e/pages/dev-console/route-page.ts
  • frontend/e2e/pages/dev-console/perspective-page.ts
  • frontend/e2e/pages/dev-console/vulnerability-page.ts
**/*.{go,ts,tsx,js,jsx}

📄 CodeRabbit inference engine (STYLEGUIDE.md)

Use lowercase dash-separated names for all files to avoid git issues with case-insensitive file systems

Files:

  • frontend/e2e/pages/dev-console/pod-list-page.ts
  • frontend/e2e/tests/dev-console/customization-of-pinned-resource.spec.ts
  • frontend/e2e/tests/dev-console/add-health-checks-git-form.spec.ts
  • frontend/e2e/tests/dev-console/getting-started-tour-dev-perspective.spec.ts
  • frontend/e2e/tests/dev-console/project-access.spec.ts
  • frontend/e2e/tests/dev-console/customization-of-catalog.spec.ts
  • frontend/e2e/pages/dev-console/config-map-page.ts
  • frontend/e2e/pages/dev-console/search-page.ts
  • frontend/e2e/tests/dev-console/configure-pinned-resources.spec.ts
  • frontend/e2e/tests/dev-console/getting-started-to-dev-perspective.spec.ts
  • frontend/e2e/tests/dev-console/configure-perspectives.spec.ts
  • frontend/e2e/tests/dev-console/project-creation.spec.ts
  • frontend/e2e/tests/dev-console/search-devperspective.spec.ts
  • frontend/e2e/tests/dev-console/project-vulnerability.spec.ts
  • frontend/e2e/tests/dev-console/route-dev-perspective.spec.ts
  • frontend/e2e/tests/dev-console/sample-card-add-page.spec.ts
  • frontend/e2e/pages/dev-console/health-checks-page.ts
  • frontend/e2e/pages/dev-console/user-preferences-page.ts
  • frontend/e2e/pages/dev-console/samples-page.ts
  • frontend/e2e/pages/dev-console/customization-page.ts
  • frontend/e2e/tests/dev-console/pod-list-page.spec.ts
  • frontend/e2e/tests/dev-console/add-health-checks-topology-sidebar.spec.ts
  • frontend/e2e/pages/dev-console/route-page.ts
  • frontend/e2e/tests/dev-console/customization-of-catalog-add-page-form-view.spec.ts
  • frontend/e2e/pages/dev-console/perspective-page.ts
  • frontend/e2e/pages/dev-console/vulnerability-page.ts
  • frontend/e2e/tests/dev-console/config-maps-dev-perspective.spec.ts
  • frontend/e2e/tests/dev-console/user-preferences-dev-perspective.spec.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (STYLEGUIDE.md)

**/*.{ts,tsx,js,jsx}: New code MUST be written in TypeScript, not JavaScript
Prefer functional programming patterns and immutable data structures
Run the linter and follow all rules defined in .eslintrc
Never use absolute paths in code - the app should be able to run behind a proxy under an arbitrary path

Files:

  • frontend/e2e/pages/dev-console/pod-list-page.ts
  • frontend/e2e/tests/dev-console/customization-of-pinned-resource.spec.ts
  • frontend/e2e/tests/dev-console/add-health-checks-git-form.spec.ts
  • frontend/e2e/tests/dev-console/getting-started-tour-dev-perspective.spec.ts
  • frontend/e2e/tests/dev-console/project-access.spec.ts
  • frontend/e2e/tests/dev-console/customization-of-catalog.spec.ts
  • frontend/e2e/pages/dev-console/config-map-page.ts
  • frontend/e2e/pages/dev-console/search-page.ts
  • frontend/e2e/tests/dev-console/configure-pinned-resources.spec.ts
  • frontend/e2e/tests/dev-console/getting-started-to-dev-perspective.spec.ts
  • frontend/e2e/tests/dev-console/configure-perspectives.spec.ts
  • frontend/e2e/tests/dev-console/project-creation.spec.ts
  • frontend/e2e/tests/dev-console/search-devperspective.spec.ts
  • frontend/e2e/tests/dev-console/project-vulnerability.spec.ts
  • frontend/e2e/tests/dev-console/route-dev-perspective.spec.ts
  • frontend/e2e/tests/dev-console/sample-card-add-page.spec.ts
  • frontend/e2e/pages/dev-console/health-checks-page.ts
  • frontend/e2e/pages/dev-console/user-preferences-page.ts
  • frontend/e2e/pages/dev-console/samples-page.ts
  • frontend/e2e/pages/dev-console/customization-page.ts
  • frontend/e2e/tests/dev-console/pod-list-page.spec.ts
  • frontend/e2e/tests/dev-console/add-health-checks-topology-sidebar.spec.ts
  • frontend/e2e/pages/dev-console/route-page.ts
  • frontend/e2e/tests/dev-console/customization-of-catalog-add-page-form-view.spec.ts
  • frontend/e2e/pages/dev-console/perspective-page.ts
  • frontend/e2e/pages/dev-console/vulnerability-page.ts
  • frontend/e2e/tests/dev-console/config-maps-dev-perspective.spec.ts
  • frontend/e2e/tests/dev-console/user-preferences-dev-perspective.spec.ts
**/*.ts

📄 CodeRabbit inference engine (STYLEGUIDE.md)

Plugin SDK Changes: Any updates to console-dynamic-plugin-sdk should aim to maintain backward compatibility as it's a public API - use the plugin-api-review skill to vet changes for public API impact and ensure proper documentation updates

Files:

  • frontend/e2e/pages/dev-console/pod-list-page.ts
  • frontend/e2e/tests/dev-console/customization-of-pinned-resource.spec.ts
  • frontend/e2e/tests/dev-console/add-health-checks-git-form.spec.ts
  • frontend/e2e/tests/dev-console/getting-started-tour-dev-perspective.spec.ts
  • frontend/e2e/tests/dev-console/project-access.spec.ts
  • frontend/e2e/tests/dev-console/customization-of-catalog.spec.ts
  • frontend/e2e/pages/dev-console/config-map-page.ts
  • frontend/e2e/pages/dev-console/search-page.ts
  • frontend/e2e/tests/dev-console/configure-pinned-resources.spec.ts
  • frontend/e2e/tests/dev-console/getting-started-to-dev-perspective.spec.ts
  • frontend/e2e/tests/dev-console/configure-perspectives.spec.ts
  • frontend/e2e/tests/dev-console/project-creation.spec.ts
  • frontend/e2e/tests/dev-console/search-devperspective.spec.ts
  • frontend/e2e/tests/dev-console/project-vulnerability.spec.ts
  • frontend/e2e/tests/dev-console/route-dev-perspective.spec.ts
  • frontend/e2e/tests/dev-console/sample-card-add-page.spec.ts
  • frontend/e2e/pages/dev-console/health-checks-page.ts
  • frontend/e2e/pages/dev-console/user-preferences-page.ts
  • frontend/e2e/pages/dev-console/samples-page.ts
  • frontend/e2e/pages/dev-console/customization-page.ts
  • frontend/e2e/tests/dev-console/pod-list-page.spec.ts
  • frontend/e2e/tests/dev-console/add-health-checks-topology-sidebar.spec.ts
  • frontend/e2e/pages/dev-console/route-page.ts
  • frontend/e2e/tests/dev-console/customization-of-catalog-add-page-form-view.spec.ts
  • frontend/e2e/pages/dev-console/perspective-page.ts
  • frontend/e2e/pages/dev-console/vulnerability-page.ts
  • frontend/e2e/tests/dev-console/config-maps-dev-perspective.spec.ts
  • frontend/e2e/tests/dev-console/user-preferences-dev-perspective.spec.ts
frontend/**/*.{js,ts,tsx}

📄 CodeRabbit inference engine (README.md)

frontend/**/*.{js,ts,tsx}: Support only the latest versions of Edge, Chrome, Safari, and Firefox browsers; IE 11 and earlier are not supported
CSP violations should be automatically reported to telemetry by parsing dynamic plugin names from securitypolicyviolation events, with throttling to prevent duplicate reports within a day

Files:

  • frontend/e2e/pages/dev-console/pod-list-page.ts
  • frontend/e2e/tests/dev-console/customization-of-pinned-resource.spec.ts
  • frontend/e2e/tests/dev-console/add-health-checks-git-form.spec.ts
  • frontend/e2e/tests/dev-console/getting-started-tour-dev-perspective.spec.ts
  • frontend/e2e/tests/dev-console/project-access.spec.ts
  • frontend/e2e/tests/dev-console/customization-of-catalog.spec.ts
  • frontend/e2e/pages/dev-console/config-map-page.ts
  • frontend/e2e/pages/dev-console/search-page.ts
  • frontend/e2e/tests/dev-console/configure-pinned-resources.spec.ts
  • frontend/e2e/tests/dev-console/getting-started-to-dev-perspective.spec.ts
  • frontend/e2e/tests/dev-console/configure-perspectives.spec.ts
  • frontend/e2e/tests/dev-console/project-creation.spec.ts
  • frontend/e2e/tests/dev-console/search-devperspective.spec.ts
  • frontend/e2e/tests/dev-console/project-vulnerability.spec.ts
  • frontend/e2e/tests/dev-console/route-dev-perspective.spec.ts
  • frontend/e2e/tests/dev-console/sample-card-add-page.spec.ts
  • frontend/e2e/pages/dev-console/health-checks-page.ts
  • frontend/e2e/pages/dev-console/user-preferences-page.ts
  • frontend/e2e/pages/dev-console/samples-page.ts
  • frontend/e2e/pages/dev-console/customization-page.ts
  • frontend/e2e/tests/dev-console/pod-list-page.spec.ts
  • frontend/e2e/tests/dev-console/add-health-checks-topology-sidebar.spec.ts
  • frontend/e2e/pages/dev-console/route-page.ts
  • frontend/e2e/tests/dev-console/customization-of-catalog-add-page-form-view.spec.ts
  • frontend/e2e/pages/dev-console/perspective-page.ts
  • frontend/e2e/pages/dev-console/vulnerability-page.ts
  • frontend/e2e/tests/dev-console/config-maps-dev-perspective.spec.ts
  • frontend/e2e/tests/dev-console/user-preferences-dev-perspective.spec.ts
**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (INTERNATIONALIZATION.md)

For dynamic translation keys that cannot be parsed by i18next-parser (t(key), t('key' + id), t(key${id})), specify possible static values in comments for the parser to extract

Files:

  • frontend/e2e/pages/dev-console/pod-list-page.ts
  • frontend/e2e/tests/dev-console/customization-of-pinned-resource.spec.ts
  • frontend/e2e/tests/dev-console/add-health-checks-git-form.spec.ts
  • frontend/e2e/tests/dev-console/getting-started-tour-dev-perspective.spec.ts
  • frontend/e2e/tests/dev-console/project-access.spec.ts
  • frontend/e2e/tests/dev-console/customization-of-catalog.spec.ts
  • frontend/e2e/pages/dev-console/config-map-page.ts
  • frontend/e2e/pages/dev-console/search-page.ts
  • frontend/e2e/tests/dev-console/configure-pinned-resources.spec.ts
  • frontend/e2e/tests/dev-console/getting-started-to-dev-perspective.spec.ts
  • frontend/e2e/tests/dev-console/configure-perspectives.spec.ts
  • frontend/e2e/tests/dev-console/project-creation.spec.ts
  • frontend/e2e/tests/dev-console/search-devperspective.spec.ts
  • frontend/e2e/tests/dev-console/project-vulnerability.spec.ts
  • frontend/e2e/tests/dev-console/route-dev-perspective.spec.ts
  • frontend/e2e/tests/dev-console/sample-card-add-page.spec.ts
  • frontend/e2e/pages/dev-console/health-checks-page.ts
  • frontend/e2e/pages/dev-console/user-preferences-page.ts
  • frontend/e2e/pages/dev-console/samples-page.ts
  • frontend/e2e/pages/dev-console/customization-page.ts
  • frontend/e2e/tests/dev-console/pod-list-page.spec.ts
  • frontend/e2e/tests/dev-console/add-health-checks-topology-sidebar.spec.ts
  • frontend/e2e/pages/dev-console/route-page.ts
  • frontend/e2e/tests/dev-console/customization-of-catalog-add-page-form-view.spec.ts
  • frontend/e2e/pages/dev-console/perspective-page.ts
  • frontend/e2e/pages/dev-console/vulnerability-page.ts
  • frontend/e2e/tests/dev-console/config-maps-dev-perspective.spec.ts
  • frontend/e2e/tests/dev-console/user-preferences-dev-perspective.spec.ts
frontend/e2e/tests/**/*.spec.ts

📄 CodeRabbit inference engine (TESTING.md)

frontend/e2e/tests/**/*.spec.ts: E2E tests must validate full user workflows against a real OpenShift cluster using Playwright, located in frontend/e2e/tests/<package>/
Each Playwright E2E test block must create its own resources, assert independently, and clean up via the cleanup fixture
Use KubernetesClient for cluster interactions in Playwright tests - never use shell commands in tests
Import test and expect from e2e/fixtures, not from @playwright/test. Custom fixtures provide cleanup, testConfig, and k8sClient

Files:

  • frontend/e2e/tests/dev-console/customization-of-pinned-resource.spec.ts
  • frontend/e2e/tests/dev-console/add-health-checks-git-form.spec.ts
  • frontend/e2e/tests/dev-console/getting-started-tour-dev-perspective.spec.ts
  • frontend/e2e/tests/dev-console/project-access.spec.ts
  • frontend/e2e/tests/dev-console/customization-of-catalog.spec.ts
  • frontend/e2e/tests/dev-console/configure-pinned-resources.spec.ts
  • frontend/e2e/tests/dev-console/getting-started-to-dev-perspective.spec.ts
  • frontend/e2e/tests/dev-console/configure-perspectives.spec.ts
  • frontend/e2e/tests/dev-console/project-creation.spec.ts
  • frontend/e2e/tests/dev-console/search-devperspective.spec.ts
  • frontend/e2e/tests/dev-console/project-vulnerability.spec.ts
  • frontend/e2e/tests/dev-console/route-dev-perspective.spec.ts
  • frontend/e2e/tests/dev-console/sample-card-add-page.spec.ts
  • frontend/e2e/tests/dev-console/pod-list-page.spec.ts
  • frontend/e2e/tests/dev-console/add-health-checks-topology-sidebar.spec.ts
  • frontend/e2e/tests/dev-console/customization-of-catalog-add-page-form-view.spec.ts
  • frontend/e2e/tests/dev-console/config-maps-dev-perspective.spec.ts
  • frontend/e2e/tests/dev-console/user-preferences-dev-perspective.spec.ts
**/*.spec.{ts,tsx}

📄 CodeRabbit inference engine (STYLEGUIDE.md)

Tests should follow a similar 'test tables' convention as used in Go where applicable

Files:

  • frontend/e2e/tests/dev-console/customization-of-pinned-resource.spec.ts
  • frontend/e2e/tests/dev-console/add-health-checks-git-form.spec.ts
  • frontend/e2e/tests/dev-console/getting-started-tour-dev-perspective.spec.ts
  • frontend/e2e/tests/dev-console/project-access.spec.ts
  • frontend/e2e/tests/dev-console/customization-of-catalog.spec.ts
  • frontend/e2e/tests/dev-console/configure-pinned-resources.spec.ts
  • frontend/e2e/tests/dev-console/getting-started-to-dev-perspective.spec.ts
  • frontend/e2e/tests/dev-console/configure-perspectives.spec.ts
  • frontend/e2e/tests/dev-console/project-creation.spec.ts
  • frontend/e2e/tests/dev-console/search-devperspective.spec.ts
  • frontend/e2e/tests/dev-console/project-vulnerability.spec.ts
  • frontend/e2e/tests/dev-console/route-dev-perspective.spec.ts
  • frontend/e2e/tests/dev-console/sample-card-add-page.spec.ts
  • frontend/e2e/tests/dev-console/pod-list-page.spec.ts
  • frontend/e2e/tests/dev-console/add-health-checks-topology-sidebar.spec.ts
  • frontend/e2e/tests/dev-console/customization-of-catalog-add-page-form-view.spec.ts
  • frontend/e2e/tests/dev-console/config-maps-dev-perspective.spec.ts
  • frontend/e2e/tests/dev-console/user-preferences-dev-perspective.spec.ts
**/*route*.{ts,tsx}

📄 CodeRabbit inference engine (STYLEGUIDE.md)

Routing: Plugin routes go in plugin-specific route files

Files:

  • frontend/e2e/tests/dev-console/route-dev-perspective.spec.ts
  • frontend/e2e/pages/dev-console/route-page.ts
🧠 Learnings (4)
📚 Learning: 2026-05-19T17:15:42.558Z
Learnt from: fsgreco
Repo: openshift/console PR: 16461
File: frontend/e2e/pages/web-terminal-config-page.ts:18-18
Timestamp: 2026-05-19T17:15:42.558Z
Learning: In the openshift/console repo, the guideline “Never use absolute URLs or paths in the console code” should be treated as an application-source-only rule. Do not flag absolute-root paths/URLs (e.g., strings starting with `/` like `/k8s/cluster/...`) in Playwright E2E code under `frontend/e2e/`, including page objects (e.g., `page.goto()` / `goTo()` calls). These absolute paths are considered correct and intentional because Playwright resolves them against the `baseURL` configured in `frontend/e2e/playwright.config.ts`. Only enforce the no-absolute-paths rule in application code under `frontend/public` and `frontend/packages`.

Applied to files:

  • frontend/e2e/pages/dev-console/pod-list-page.ts
  • frontend/e2e/tests/dev-console/customization-of-pinned-resource.spec.ts
  • frontend/e2e/tests/dev-console/add-health-checks-git-form.spec.ts
  • frontend/e2e/tests/dev-console/getting-started-tour-dev-perspective.spec.ts
  • frontend/e2e/tests/dev-console/project-access.spec.ts
  • frontend/e2e/tests/dev-console/customization-of-catalog.spec.ts
  • frontend/e2e/pages/dev-console/config-map-page.ts
  • frontend/e2e/pages/dev-console/search-page.ts
  • frontend/e2e/tests/dev-console/configure-pinned-resources.spec.ts
  • frontend/e2e/tests/dev-console/getting-started-to-dev-perspective.spec.ts
  • frontend/e2e/tests/dev-console/configure-perspectives.spec.ts
  • frontend/e2e/tests/dev-console/project-creation.spec.ts
  • frontend/e2e/tests/dev-console/search-devperspective.spec.ts
  • frontend/e2e/tests/dev-console/project-vulnerability.spec.ts
  • frontend/e2e/tests/dev-console/route-dev-perspective.spec.ts
  • frontend/e2e/tests/dev-console/sample-card-add-page.spec.ts
  • frontend/e2e/pages/dev-console/health-checks-page.ts
  • frontend/e2e/pages/dev-console/user-preferences-page.ts
  • frontend/e2e/pages/dev-console/samples-page.ts
  • frontend/e2e/pages/dev-console/customization-page.ts
  • frontend/e2e/tests/dev-console/pod-list-page.spec.ts
  • frontend/e2e/tests/dev-console/add-health-checks-topology-sidebar.spec.ts
  • frontend/e2e/pages/dev-console/route-page.ts
  • frontend/e2e/tests/dev-console/customization-of-catalog-add-page-form-view.spec.ts
  • frontend/e2e/pages/dev-console/perspective-page.ts
  • frontend/e2e/pages/dev-console/vulnerability-page.ts
  • frontend/e2e/tests/dev-console/config-maps-dev-perspective.spec.ts
  • frontend/e2e/tests/dev-console/user-preferences-dev-perspective.spec.ts
📚 Learning: 2026-05-20T13:09:57.325Z
Learnt from: fsgreco
Repo: openshift/console PR: 16461
File: frontend/e2e/pages/web-terminal-page.ts:59-59
Timestamp: 2026-05-20T13:09:57.325Z
Learning: In page object files under `frontend/e2e/pages/`, do not treat `.catch(() => {})` (or other empty/ignored catches) chained to `waitFor(...)` as a bug when the wait is intended to be best-effort. These catches indicate the element may or may not appear, so the page object should not fail the test; real expectations should be asserted in the spec files (e.g., `expect(...).toBeVisible()`). This is consistent with the `waitForLoadingComplete` behavior in `BasePage`—reviewers should allow the empty catches when they match this pattern.

Applied to files:

  • frontend/e2e/pages/dev-console/pod-list-page.ts
  • frontend/e2e/pages/dev-console/config-map-page.ts
  • frontend/e2e/pages/dev-console/search-page.ts
  • frontend/e2e/pages/dev-console/health-checks-page.ts
  • frontend/e2e/pages/dev-console/user-preferences-page.ts
  • frontend/e2e/pages/dev-console/samples-page.ts
  • frontend/e2e/pages/dev-console/customization-page.ts
  • frontend/e2e/pages/dev-console/route-page.ts
  • frontend/e2e/pages/dev-console/perspective-page.ts
  • frontend/e2e/pages/dev-console/vulnerability-page.ts
📚 Learning: 2026-05-20T13:21:50.712Z
Learnt from: fsgreco
Repo: openshift/console PR: 16461
File: frontend/e2e/pages/web-terminal-page.ts:11-26
Timestamp: 2026-05-20T13:21:50.712Z
Learning: In Playwright page objects under `frontend/e2e/pages/`, treat locators that intentionally use dual CSS selectors like `.locator('[data-test="X"], [data-test-id="X"]')` as a backwards-compatibility shim. The legacy `data-test-id` is required because newly added `data-test` attributes may not exist on yet-to-be-upgraded deployed clusters. Do not flag these dual-selector locators as a code smell and do not suggest replacing them with a single `getByTestId('X')` until the new `data-test` attributes have propagated to all target clusters.

Applied to files:

  • frontend/e2e/pages/dev-console/pod-list-page.ts
  • frontend/e2e/pages/dev-console/config-map-page.ts
  • frontend/e2e/pages/dev-console/search-page.ts
  • frontend/e2e/pages/dev-console/health-checks-page.ts
  • frontend/e2e/pages/dev-console/user-preferences-page.ts
  • frontend/e2e/pages/dev-console/samples-page.ts
  • frontend/e2e/pages/dev-console/customization-page.ts
  • frontend/e2e/pages/dev-console/route-page.ts
  • frontend/e2e/pages/dev-console/perspective-page.ts
  • frontend/e2e/pages/dev-console/vulnerability-page.ts
📚 Learning: 2026-05-20T18:30:38.087Z
Learnt from: stefanonardo
Repo: openshift/console PR: 16431
File: frontend/e2e/tests/console/app/filtering-and-searching.spec.ts:20-62
Timestamp: 2026-05-20T18:30:38.087Z
Learning: In the OpenShift console Playwright E2E tests (frontend/e2e/tests/), follow the documented teardown strategy:
- Use the per-test Playwright `cleanup` fixture when each test creates its own resources and needs isolated create/cleanup.
- If multiple tests in the same suite share an expensive resource (e.g., a Deployment that takes ~30s to become Ready), prefer manual suite setup/teardown using `beforeAll`/`afterAll` with a `KubernetesClient` instance instead of the `cleanup` fixture, because the `cleanup` fixture is not meant for resources shared across multiple tests.
- During review, do not flag `beforeAll`/`afterAll` teardown that uses `KubernetesClient` as an “isolation violation” when it is clearly sharing a common resource for performance and matches this Strategy B pattern.

Applied to files:

  • frontend/e2e/tests/dev-console/customization-of-pinned-resource.spec.ts
  • frontend/e2e/tests/dev-console/add-health-checks-git-form.spec.ts
  • frontend/e2e/tests/dev-console/getting-started-tour-dev-perspective.spec.ts
  • frontend/e2e/tests/dev-console/project-access.spec.ts
  • frontend/e2e/tests/dev-console/customization-of-catalog.spec.ts
  • frontend/e2e/tests/dev-console/configure-pinned-resources.spec.ts
  • frontend/e2e/tests/dev-console/getting-started-to-dev-perspective.spec.ts
  • frontend/e2e/tests/dev-console/configure-perspectives.spec.ts
  • frontend/e2e/tests/dev-console/project-creation.spec.ts
  • frontend/e2e/tests/dev-console/search-devperspective.spec.ts
  • frontend/e2e/tests/dev-console/project-vulnerability.spec.ts
  • frontend/e2e/tests/dev-console/route-dev-perspective.spec.ts
  • frontend/e2e/tests/dev-console/sample-card-add-page.spec.ts
  • frontend/e2e/tests/dev-console/pod-list-page.spec.ts
  • frontend/e2e/tests/dev-console/add-health-checks-topology-sidebar.spec.ts
  • frontend/e2e/tests/dev-console/customization-of-catalog-add-page-form-view.spec.ts
  • frontend/e2e/tests/dev-console/config-maps-dev-perspective.spec.ts
  • frontend/e2e/tests/dev-console/user-preferences-dev-perspective.spec.ts
🔇 Additional comments (25)
frontend/e2e/pages/dev-console/config-map-page.ts (1)

5-62: LGTM!

frontend/e2e/pages/dev-console/health-checks-page.ts (1)

6-69: LGTM!

frontend/e2e/pages/dev-console/pod-list-page.ts (1)

6-29: LGTM!

frontend/e2e/pages/dev-console/route-page.ts (1)

6-40: LGTM!

Also applies to: 45-69

frontend/e2e/pages/dev-console/samples-page.ts (1)

6-71: LGTM!

Also applies to: 76-109

frontend/e2e/pages/dev-console/search-page.ts (1)

6-48: LGTM!

frontend/e2e/pages/dev-console/vulnerability-page.ts (1)

6-129: LGTM!

frontend/e2e/tests/dev-console/config-maps-dev-perspective.spec.ts (1)

1-90: LGTM!

frontend/e2e/tests/dev-console/route-dev-perspective.spec.ts (1)

1-134: LGTM!

frontend/e2e/tests/dev-console/sample-card-add-page.spec.ts (1)

1-158: LGTM!

frontend/e2e/tests/dev-console/project-vulnerability.spec.ts (1)

1-153: LGTM!

frontend/e2e/tests/dev-console/search-devperspective.spec.ts (1)

1-112: LGTM!

frontend/e2e/pages/dev-console/perspective-page.ts (1)

1-42: LGTM!

Also applies to: 55-103

frontend/e2e/pages/dev-console/user-preferences-page.ts (1)

1-89: LGTM!

frontend/e2e/pages/dev-console/customization-page.ts (1)

1-113: LGTM!

frontend/e2e/tests/dev-console/add-health-checks-git-form.spec.ts (1)

1-7: LGTM!

frontend/e2e/tests/dev-console/getting-started-to-dev-perspective.spec.ts (1)

1-7: LGTM!

frontend/e2e/tests/dev-console/getting-started-tour-dev-perspective.spec.ts (1)

1-19: LGTM!

frontend/e2e/tests/dev-console/configure-pinned-resources.spec.ts (1)

1-35: LGTM!

frontend/e2e/tests/dev-console/project-creation.spec.ts (1)

1-15: LGTM!

Also applies to: 21-28

frontend/e2e/tests/dev-console/configure-perspectives.spec.ts (2)

7-17: LGTM!


19-77: 🏗️ Heavy lift

Restore cluster configuration after enabling Developer perspective

This test mutates cluster-wide perspective configuration but the provided snippet does not show any cleanup or state restoration. Per the E2E testing guidelines, tests must clean up resources via the cleanup fixture. Either add teardown logic to restore the previous perspective state, or use the provided cleanup fixture if the Page Object already supports recording changes for cleanup.

frontend/e2e/tests/dev-console/customization-of-catalog.spec.ts (1)

1-23: LGTM!

frontend/e2e/tests/dev-console/customization-of-pinned-resource.spec.ts (1)

26-60: LGTM!

frontend/e2e/tests/dev-console/project-access.spec.ts (1)

1-11: LGTM!

Comment on lines +43 to +52
async switchToAdministrator(): Promise<void> {
await this.switchToPerspective('Administrator');
// Also handle "Core platform" label (OpenShift 5.0+)
const currentText = await this.perspectiveSwitcherToggle.textContent();
if (
!currentText?.includes('Administrator') &&
!currentText?.includes('Core platform')
) {
await this.switchToPerspective('Core platform');
}
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

Fix fallback order for Administrator/Core platform switching.

On Line 44, switchToPerspective('Administrator') can fail before the Core platform fallback runs (Lines 46–52) when only the new label is present.

Proposed fix
   async switchToAdministrator(): Promise<void> {
-    await this.switchToPerspective('Administrator');
-    // Also handle "Core platform" label (OpenShift 5.0+)
-    const currentText = await this.perspectiveSwitcherToggle.textContent();
-    if (
-      !currentText?.includes('Administrator') &&
-      !currentText?.includes('Core platform')
-    ) {
-      await this.switchToPerspective('Core platform');
-    }
+    const currentText = await this.perspectiveSwitcherToggle.textContent();
+    if (currentText?.includes('Administrator') || currentText?.includes('Core platform')) {
+      return;
+    }
+
+    await this.robustClick(this.perspectiveSwitcherToggle);
+    const options = this.perspectiveSwitcherMenu.locator(
+      '[data-test-id="perspective-switcher-menu-option"]',
+    );
+    const administratorOption = options.filter({ hasText: 'Administrator' });
+    const corePlatformOption = options.filter({ hasText: 'Core platform' });
+
+    if ((await administratorOption.count()) > 0) {
+      await this.robustClick(administratorOption);
+    } else {
+      await this.robustClick(corePlatformOption);
+    }
+    await this.waitForLoadingComplete();
   }
🤖 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/pages/dev-console/perspective-page.ts` around lines 43 - 52, The
current implementation in switchToAdministrator calls
switchToPerspective('Administrator') unconditionally which can fail when the UI
uses the new "Core platform" label; first read
perspectiveSwitcherToggle.textContent() and if it already contains
"Administrator" return, otherwise if it contains "Core platform" call
switchToPerspective('Core platform'); only if neither label is present attempt
switchToPerspective('Administrator') and if that doesn't result in the expected
label then fallback to switchToPerspective('Core platform') so the function
handles both legacy and new labels reliably.

Comment on lines +41 to +42
const port = targetPort.substring(0, 4);
await this.robustClick(this.page.locator(`[data-test-dropdown-menu="${port}-tcp"]`));
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

Fix target-port parsing to avoid invalid dropdown selectors.

The current 4-char slice can build wrong selectors for non-4-digit ports, causing route creation to fail.

Proposed fix
-    const port = targetPort.substring(0, 4);
-    await this.robustClick(this.page.locator(`[data-test-dropdown-menu="${port}-tcp"]`));
+    const port = targetPort.split('-')[0];
+    await this.robustClick(this.page.locator(`[data-test-dropdown-menu="${port}-tcp"]`));
📝 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
const port = targetPort.substring(0, 4);
await this.robustClick(this.page.locator(`[data-test-dropdown-menu="${port}-tcp"]`));
const port = targetPort.split('-')[0];
await this.robustClick(this.page.locator(`[data-test-dropdown-menu="${port}-tcp"]`));
🤖 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/pages/dev-console/route-page.ts` around lines 41 - 42, The
dropdown selector breaks because port is derived via a fixed 4-character
substring; update the parsing in route-page.ts where targetPort is handled so
the port is extracted as the actual numeric port string (e.g., strip
protocol/suffix like "/tcp" and non-digit chars, or split on '/' and take the
first token, or parseInt and convert back to string) before calling
this.page.locator and this.robustClick (referenced symbols: targetPort, const
port, page.locator, robustClick). Ensure the resulting port string exactly
matches the dropdown data-test value (e.g., "80" or "8080") rather than a
fixed-width slice.

Comment on lines +72 to +74
async expectGitUrlReadonly(): Promise<void> {
await expect(this.gitUrlInput).toBeVisible();
}
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

Assert read-only state, not just visibility.

expectGitUrlReadonly() does not currently validate read-only behavior, so the check can pass even when the field is editable.

Proposed fix
   async expectGitUrlReadonly(): Promise<void> {
-    await expect(this.gitUrlInput).toBeVisible();
+    await expect(this.gitUrlInput).toBeVisible();
+    await expect(this.gitUrlInput).toBeDisabled();
   }
📝 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
async expectGitUrlReadonly(): Promise<void> {
await expect(this.gitUrlInput).toBeVisible();
}
async expectGitUrlReadonly(): Promise<void> {
await expect(this.gitUrlInput).toBeVisible();
await expect(this.gitUrlInput).toBeDisabled();
}
🤖 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/pages/dev-console/samples-page.ts` around lines 72 - 74, The
test currently only checks visibility; update expectGitUrlReadonly to assert the
field is actually read-only by verifying it's not editable and/or has the
readonly attribute. For example, replace the visibility-only assertion with
checks against the Locator this.gitUrlInput such as awaiting
expect(this.gitUrlInput).not.toBeEditable() and/or awaiting
expect(this.gitUrlInput).toHaveAttribute('readonly', expect.any(String)) so the
method verifies read-only behavior rather than just visibility.

Comment on lines +20 to +45
for (const name of deployments) {
await k8s.appsV1Api.createNamespacedDeployment({
namespace,
body: {
apiVersion: 'apps/v1',
kind: 'Deployment',
metadata: { name, namespace },
spec: {
replicas: 1,
selector: { matchLabels: { app: name } },
template: {
metadata: { labels: { app: name } },
spec: {
containers: [
{
name,
image: 'registry.access.redhat.com/ubi8/nodejs-18:latest',
ports: [{ containerPort: 8080, protocol: 'TCP' }],
},
],
},
},
},
},
});
}
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

Wait for seeded deployments to become available before topology actions.

The suite creates deployments and immediately runs topology interactions that assume nodes exist. Add readiness waits in setup to avoid race-driven flakes.

Suggested fix
     for (const name of deployments) {
       await k8s.appsV1Api.createNamespacedDeployment({
         namespace,
         body: {
@@
         },
       });
+      await k8s.waitForCustomResourceCondition(
+        'apps',
+        'v1',
+        namespace,
+        'deployments',
+        name,
+        (resource: any) => resource?.status?.availableReplicas > 0,
+        120_000,
+      );
     }
🤖 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/dev-console/add-health-checks-topology-sidebar.spec.ts`
around lines 20 - 45, The test creates multiple deployments using deployments
and k8s.appsV1Api.createNamespacedDeployment but proceeds immediately to
topology actions; add an await loop after creation that polls the Deployment
status (via k8s.appsV1Api.readNamespacedDeployment or
readNamespacedDeploymentStatus) for each name in deployments in the given
namespace until status.availableReplicas >= 1 (or desired replicas) or a timeout
is reached, and only continue once all created deployments report ready to avoid
race flakes.

Comment on lines +31 to +42
const combobox = page.locator('[role="combobox"]');
await combobox.click();
const filterInput = page.locator('[aria-label="Type to filter"]');
await filterInput.fill('console');
const consoleItem = page.locator('[class="co-resource-item"]').filter({
hasText: 'operator.openshift.io',
});
if ((await consoleItem.count()) > 0) {
await consoleItem.click();
} else {
await page.locator('[class="co-resource-item"]').first().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 | 🟠 Major | ⚡ Quick win

❓ Verification inconclusive

Script executed:

#!/bin/bash
# First, check if the file exists and read the relevant lines
if [ -f "frontend/e2e/tests/dev-console/configure-perspectives.spec.ts" ]; then
  echo "=== File found. Reading lines around 31-42 ==="
  wc -l frontend/e2e/tests/dev-console/configure-perspectives.spec.ts
  echo ""
  sed -n '25,50p' frontend/e2e/tests/dev-console/configure-perspectives.spec.ts
else
  echo "File not found"
  find . -name "*configure-perspectives*" -type f
fi

Repository: openshift/console


Repository: openshift/console
Exit code: 0

stdout:

=== File found. Reading lines around 31-42 ===
79 frontend/e2e/tests/dev-console/configure-perspectives.spec.ts

      await page.goto('/');
      await perspectivePage.switchToAdministrator();
    });

    await test.step('Search for console resource', async () => {
      await navPage.clickNavLink(['Home', 'Search']);
      const combobox = page.locator('[role="combobox"]');
      await combobox.click();
      const filterInput = page.locator('[aria-label="Type to filter"]');
      await filterInput.fill('console');
      const consoleItem = page.locator('[class="co-resource-item"]').filter({
        hasText: 'operator.openshift.io',
      });
      if ((await consoleItem.count()) > 0) {
        await consoleItem.click();
      } else {
        await page.locator('[class="co-resource-item"]').first().click();
      }
      await page
        .locator('button[aria-label="Clear input value"]')
        .click()
        .catch(() => {});
      await page.locator('body').click();
    });

    await test.step('Click on cluster', async () => {


Script executed:

#!/bin/bash
# Find E2E test files and understand the test structure
find frontend/e2e/tests -name "*.spec.ts" | head -5
echo ""
echo "=== Listing dev-console test directory ==="
ls -la frontend/e2e/tests/dev-console/ 2>/dev/null | head -20

Repository: openshift/console


Repository: openshift/console
Exit code: 0

stdout:

frontend/e2e/tests/console/cluster-settings/channel-modal.spec.ts
frontend/e2e/tests/console/cluster-settings/cluster-settings.spec.ts
frontend/e2e/tests/console/cluster-settings/managed-control-plane.spec.ts
frontend/e2e/tests/console/cluster-settings/oauth.spec.ts
frontend/e2e/tests/console/cluster-settings/update-in-progress.spec.ts

=== Listing dev-console test directory ===
total 100
drwxr-xr-x 2 jailuser jailuser  400 May 20 18:48 .
drwxr-xr-x 5 jailuser jailuser  100 May 20 18:48 ..
-rw-r--r-- 1 jailuser jailuser  284 May 20 18:48 add-health-checks-git-form.spec.ts
-rw-r--r-- 1 jailuser jailuser 6920 May 20 18:48 add-health-checks-topology-sidebar.spec.ts
-rw-r--r-- 1 jailuser jailuser 3321 May 20 18:48 config-maps-dev-perspective.spec.ts
-rw-r--r-- 1 jailuser jailuser 3153 May 20 18:48 configure-perspectives.spec.ts
-rw-r--r-- 1 jailuser jailuser 1440 May 20 18:48 configure-pinned-resources.spec.ts
-rw-r--r-- 1 jailuser jailuser 7810 May 20 18:48 customization-of-catalog-add-page-form-view.spec.ts
-rw-r--r-- 1 jailuser jailuser  816 May 20 18:48 customization-of-catalog.spec.ts
-rw-r--r-- 1 jailuser jailuser 2363 May 20 18:48 customization-of-pinned-resource.spec.ts
-rw-r--r-- 1 jailuser jailuser  331 May 20 18:48 getting-started-to-dev-perspective.spec.ts
-rw-r--r-- 1 jailuser jailuser  649 May 20 18:48 getting-started-tour-dev-perspective.spec.ts
-rw-r--r-- 1 jailuser jailuser 3513 May 20 18:48 pod-list-page.spec.ts
-rw-r--r-- 1 jailuser jailuser  467 May 20 18:48 project-access.spec.ts
-rw-r--r-- 1 jailuser jailuser 1096 May 20 18:48 project-creation.spec.ts
-rw-r--r-- 1 jailuser jailuser 5638 May 20 18:48 project-vulnerability.spec.ts
-rw-r--r-- 1 jailuser jailuser 4665 May 20 18:48 route-dev-perspective.spec.ts
-rw-r--r-- 1 jailuser jailuser 6051 May 20 18:48 sample-card-add-page.spec.ts
-rw-r--r-- 1 jailuser jailuser 3896 May 20 18:48 search-devperspective.spec.ts


Script executed:

#!/bin/bash
# Search for the resource item component and search components to check for data-test attributes
echo "=== Searching for co-resource-item in components ==="
rg "co-resource-item" --type ts --type tsx -l | head -10

echo ""
echo "=== Searching for data-test attributes on resource items ==="
rg "co-resource-item" --type ts --type tsx -B2 -A2 | head -40

Repository: openshift/console


Repository: openshift/console
Exit code: 0

stdout:

=== Searching for co-resource-item in components ===

=== Searching for data-test attributes on resource items ===

stderr:

unrecognized file type: tsx
unrecognized file type: tsx


Script executed:

#!/bin/bash
# Look at the SearchPage helper to understand the test pattern
find frontend/e2e -name "*search*" -o -name "*page*" | grep -E "\.(ts|tsx)$" | head -15
echo ""
echo "=== Check fixtures directory ==="
ls frontend/e2e/fixtures/ 2>/dev/null | head -20

Repository: openshift/console


Repository: openshift/console
Exit code: 0

stdout:

frontend/e2e/pages/base-page.ts
frontend/e2e/pages/cluster-settings-page.ts
frontend/e2e/pages/details-page.ts
frontend/e2e/pages/dev-console/config-map-page.ts
frontend/e2e/pages/dev-console/customization-page.ts
frontend/e2e/pages/dev-console/health-checks-page.ts
frontend/e2e/pages/dev-console/perspective-page.ts
frontend/e2e/pages/dev-console/pod-list-page.ts
frontend/e2e/pages/dev-console/route-page.ts
frontend/e2e/pages/dev-console/samples-page.ts
frontend/e2e/pages/dev-console/search-page.ts
frontend/e2e/pages/dev-console/user-preferences-page.ts
frontend/e2e/pages/dev-console/vulnerability-page.ts
frontend/e2e/pages/oauth-page.ts
frontend/e2e/pages/overview-page.ts

=== Check fixtures directory ===
cleanup-fixture.ts
index.ts


Script executed:

#!/bin/bash
# Search for data-test attributes on combobox and filter inputs in the codebase
echo "=== Searching for combobox with data-test ==="
rg 'data-test.*combobox|role="combobox"' --type ts --type tsx -B1 -A1 | head -50

echo ""
echo "=== Searching for SearchResourceItem or similar components ==="
rg "SearchResourceItem|resource.*item|co-resource-item" --type ts --type tsx -l | head -10

Repository: openshift/console


Repository: openshift/console
Exit code: 0

stdout:

=== Searching for combobox with data-test ===

=== Searching for SearchResourceItem or similar components ===

stderr:

unrecognized file type: tsx
unrecognized file type: tsx


Script executed:

#!/bin/bash
# Check the actual component that renders the resource items
find frontend -path "*/node_modules" -prune -o -name "*.tsx" -o -name "*.ts" | xargs grep -l "co-resource-item" | head -10

Repository: openshift/console


Repository: openshift/console
Exit code: 0

stdout:

frontend/e2e/tests/dev-console/configure-perspectives.spec.ts
frontend/packages/console-app/src/components/volume-snapshot/volume-snapshot-class.tsx
frontend/packages/console-shared/src/components/dropdown/ResourceDropdown.tsx
frontend/packages/dev-console/integration-tests/support/step-definitions/customization/configure-perspectives.ts
frontend/packages/dev-console/src/components/catalog/PinnedResourcesConfiguration.tsx
frontend/packages/helm-plugin/src/components/list-page/HelmReleaseListRow.tsx
frontend/packages/helm-plugin/src/topology/sidebar/release-panel/resource-link.tsx
frontend/packages/integration-tests/tests/app/demo-dynamic-plugin.cy.ts
frontend/packages/integration-tests/views/environment.ts
frontend/packages/integration-tests/views/rolebindings.ts

stderr:

xargs: grep: terminated by signal 13


Script executed:

#!/bin/bash
# Find where co-resource-item class is used and what components render it
rg "className.*co-resource-item|co-resource-item" --type tsx --type ts -B3 -A3 | head -60

Repository: openshift/console


Repository: openshift/console
Exit code: 0

stderr:

unrecognized file type: tsx


Script executed:

#!/bin/bash
# Read the search page helper
cat frontend/e2e/pages/dev-console/search-page.ts

Repository: openshift/console


Repository: openshift/console
Exit code: 0

stdout:

import type { Page } from '`@playwright/test`';
import { expect } from '`@playwright/test`';

import BasePage from '../base-page';

export class SearchPage extends BasePage {
  private readonly resourceFilterInput = this.page.locator('input[placeholder="Resources"]');
  private readonly resourceOptionsMenu = this.page.locator('[aria-label="Options menu"]');
  private readonly recentlyUsedSection = this.page.locator('[aria-labelledby="Recently-used"]');
  private readonly clearHistoryButton = this.page.locator('[data-test-id="close-icon"]');

  constructor(page: Page) {
    super(page);
  }

  async searchAndSelectResource(resourceName: string): Promise<void> {
    await this.robustClick(this.page.locator('[aria-label="Type to filter"]'));
    await this.resourceFilterInput.clear();
    await this.resourceFilterInput.fill(resourceName);
    await this.robustClick(this.page.locator(`label[id$="${resourceName}"]`));
  }

  async openResourceFilter(): Promise<void> {
    await expect(this.resourceOptionsMenu).toBeVisible();
    await this.robustClick(this.resourceOptionsMenu);
  }

  async expectRecentlyUsedToContain(resourceName: string): Promise<void> {
    await expect(
      this.recentlyUsedSection.locator(`[data-filter-text="AR${resourceName}"]`),
    ).toBeVisible();
  }

  async expectRecentlyUsedToContainAll(resources: string[]): Promise<void> {
    for (const resource of resources) {
      await expect(this.recentlyUsedSection.locator('label')).toContainText(resource);
    }
  }

  async clearHistory(): Promise<void> {
    await expect(this.clearHistoryButton).toBeVisible();
    await this.robustClick(this.clearHistoryButton, { force: true });
  }

  async expectRecentlyUsedNotVisible(): Promise<void> {
    await expect(this.recentlyUsedSection).not.toBeAttached();
  }
}


Script executed:

#!/bin/bash
# Check the ResourceDropdown component
head -100 frontend/packages/console-shared/src/components/dropdown/ResourceDropdown.tsx

Repository: openshift/console


Repository: openshift/console
Exit code: 0

stdout:

import type { FC, ReactNode } from 'react';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import * as fuzzy from 'fuzzysearch';
import * as _ from 'lodash';
import { useTranslation } from 'react-i18next';
import type { WatchK8sResultsObject } from '`@console/dynamic-plugin-sdk`';
import type { ConsoleSelectProps } from '`@console/internal/components/utils/console-select`';
import { ConsoleSelect } from '`@console/internal/components/utils/console-select`';
import { ResourceIcon } from '`@console/internal/components/utils/resource-icon`';
import { LoadingInline } from '`@console/internal/components/utils/status-box`';
import type { K8sResourceKind, K8sKind } from '`@console/internal/module/k8s`';
import { referenceForModel, modelFor, referenceFor } from '`@console/internal/module/k8s`';

/** Extended result type that includes optional kind for badge display fallback */
type ResourceDropdownResult = WatchK8sResultsObject<K8sResourceKind | K8sResourceKind[]> & {
  kind?: string;
};

type DropdownItemProps = {
  model: K8sKind;
  name: string;
};

const DropdownItem: FC<DropdownItemProps> = ({ model, name }) => (
  <span className="co-resource-item">
    <span className="co-resource-icon--fixed-width">
      <ResourceIcon kind={referenceForModel(model)} />
    </span>
    <span className="co-resource-item__resource-name">
      <span>{name}</span>
    </span>
  </span>
);

export type ResourceDropdownItems = ConsoleSelectProps['items'];

export interface ResourceDropdownProps {
  actionItems?: ConsoleSelectProps['actionItems'];
  ariaLabel?: ConsoleSelectProps['ariaLabel'];
  autocompleteFilter?: ConsoleSelectProps['autocompleteFilter'];
  buttonClassName?: ConsoleSelectProps['buttonClassName'];
  className?: ConsoleSelectProps['className'];
  disabled?: ConsoleSelectProps['disabled'];
  id?: ConsoleSelectProps['id'];
  isFullWidth?: ConsoleSelectProps['isFullWidth'];
  menuClassName?: ConsoleSelectProps['menuClassName'];
  placeholder?: ConsoleSelectProps['autocompletePlaceholder'];
  selectedKey: ConsoleSelectProps['selectedKey'];
  title?: ConsoleSelectProps['title'];
  titlePrefix?: ConsoleSelectProps['titlePrefix'];
  userPreferencePrefix?: ConsoleSelectProps['userPreferencePrefix'];

  allSelectorItem?: {
    allSelectorKey?: string;
    allSelectorTitle?: string;
  };
  noneSelectorItem?: {
    noneSelectorKey?: string;
    noneSelectorTitle?: string;
  };
  dataSelector: string[] | number[] | symbol[];
  transformLabel?: Function;
  loaded?: boolean;
  loadError?: unknown;
  resources?: ResourceDropdownResult[];
  autoSelect?: boolean;
  resourceFilter?: (resource: K8sResourceKind) => boolean;
  onChange?: (
    key: string,
    name?: ResourceDropdownItems[keyof ResourceDropdownItems],
    selectedResource?: K8sResourceKind,
  ) => void;
  onLoad?: (items: ResourceDropdownItems) => void;
  showBadge?: boolean;
  customResourceKey?: (key: string, resource: K8sResourceKind) => string;
  appendItems?: ResourceDropdownItems;
}

const craftResourceKey = (
  resource: K8sResourceKind,
  dataSelector: ResourceDropdownProps['dataSelector'],
  resourceFilter: ResourceDropdownProps['resourceFilter'],
  customResourceKey: ResourceDropdownProps['customResourceKey'],
): { customKey: string; key: string } => {
  let key;
  if (resourceFilter && resourceFilter(resource)) {
    key = _.get(resource, dataSelector);
  } else if (!resourceFilter) {
    key = _.get(resource, dataSelector);
  }
  return {
    customKey: customResourceKey ? customResourceKey(key, resource) : key,
    key,
  };
};

export const ResourceDropdown: FC<ResourceDropdownProps> = ({
  actionItems,
  allSelectorItem,
  appendItems,


Script executed:

#!/bin/bash
# Search for data-test attributes in the dropdown/search components
grep -r "data-test" frontend/packages/console-shared/src/components/dropdown/ | head -20

Repository: openshift/console


Repository: openshift/console
Exit code: 0

stdout:

frontend/packages/console-shared/src/components/dropdown/dropdown-with-switch/DropdownWithSwitchGroups.tsx:                  data-test={`dropdown-with-switch-menu-item-${iKey}`}
frontend/packages/console-shared/src/components/dropdown/dropdown-with-switch/DropdownWithSwitchMenu.tsx:              data-test="dropdown-with-switch-switch"
frontend/packages/console-shared/src/components/dropdown/dropdown-with-switch/DropdownWithSwitchToggle.tsx:            data-test="dropdown-with-switch-toggle"


Script executed:

#!/bin/bash
# Look at the PinnedResourcesConfiguration to understand resource selection
head -150 frontend/packages/dev-console/src/components/catalog/PinnedResourcesConfiguration.tsx

Repository: openshift/console


Repository: openshift/console
Exit code: 0

stdout:

import type { FC, ReactElement } from 'react';
import { useState, useMemo, memo, useEffect } from 'react';
import { FormHelperText, FormSection, Icon, Tooltip } from '`@patternfly/react-core`';
import { DualListSelector } from '`@patternfly/react-core/deprecated`';
import * as fuzzy from 'fuzzysearch';
import type { Map as ImmutableMap } from 'immutable';
import { Set as ImmutableSet } from 'immutable';
import { useTranslation } from 'react-i18next';
import { connect } from 'react-redux';
import type {
  DiscoveryResources,
  ExtensionK8sModel,
  K8sKind,
  K8sModel,
} from '`@console/dynamic-plugin-sdk/src/lib-core`';
import { ResourceIcon } from '`@console/dynamic-plugin-sdk/src/lib-core`';
import type { K8sResourceKind } from '`@console/internal/module/k8s`';
import {
  modelFor,
  referenceForGroupVersionKind,
  referenceForModel,
} from '`@console/internal/module/k8s`';
import type { RootState } from '`@console/internal/redux`';
import { LoadError } from '`@console/shared/src/components/cluster-configuration/LoadError`';
import { patchConsoleOperatorConfig } from '`@console/shared/src/components/cluster-configuration/patchConsoleOperatorConfig`';
import type { SaveStatusProps } from '`@console/shared/src/components/cluster-configuration/SaveStatus`';
import { SaveStatus } from '`@console/shared/src/components/cluster-configuration/SaveStatus`';
import { useConsoleOperatorConfig } from '`@console/shared/src/components/cluster-configuration/useConsoleOperatorConfig`';
import { YellowExclamationTriangleIcon } from '`@console/shared/src/components/status/icons`';
import { useDebounceCallback } from '`@console/shared/src/hooks/useDebounceCallback`';
import type {
  Perspective,
  PerspectivePinnedResource,
} from '`@console/shared/src/hooks/usePerspectives`';
import {
  PerspectiveVisibilityState,
  usePerspectives,
} from '`@console/shared/src/hooks/usePerspectives`';
import { useTelemetry } from '`@console/shared/src/hooks/useTelemetry`';
import './PinnedResourcesConfiguration.scss';

// skip duplicate resources.
const skipGroups = ImmutableSet([
  // Prefer rbac.authorization.k8s.io/v1, which has the same resources.
  'authorization.openshift.io',
]);

const skipResources = ImmutableSet([
  // Prefer core/v1
  'events.k8s.io/v1beta1.Event',
]);

type PerspectivesConsoleConfig = K8sResourceKind & {
  spec: {
    customization?: {
      perspectives: Perspective[];
    };
  };
};

type DefaultPins = {
  dev?: ExtensionK8sModel[];
};

type PinnedResourcesConfigurationProps = {
  readonly: boolean;
  groupVersionMap: DiscoveryResources['groupVersionMap'];
  allK8sModels: ImmutableMap<string, K8sModel>;
};

const PinnedResourcesConfiguration: FC<PinnedResourcesConfigurationProps> = ({
  readonly,
  allK8sModels,
  groupVersionMap,
}) => {
  const { t } = useTranslation();
  const fireTelemetryEvent = useTelemetry();
  const perspectiveExtensions = usePerspectives();
  const [pinnedResources, setPinnedResources] = useState<PerspectivePinnedResource[]>();
  const [perspectiveData, setPerspectiveData] = useState<Perspective[]>();
  const [pinnedResourcesConfigured, setPinnedResourcesConfigured] = useState<
    PerspectivePinnedResource[]
  >();
  const defaultPins: DefaultPins = useMemo(
    () =>
      perspectiveExtensions.reduce(
        (acc, e) => ({
          ...acc,
          [e.properties.id]: (e.properties.defaultPins || []).map((gvk) => gvk),
        }),
        {},
      ),
    [perspectiveExtensions],
  );

  const resources = useMemo(() => {
    return allK8sModels
      ?.filter(({ apiGroup, apiVersion, kind, verbs }) => {
        if (skipGroups.has(apiGroup) || skipResources.has(`${apiGroup}/${apiVersion}.${kind}`)) {
          return false;
        }

        // Only show resources that can be listed.
        if (!verbs?.some((v) => v === 'list')) {
          return false;
        }

        // Only show preferred version for resources in the same API group.
        const preferred = (m: K8sKind) =>
          groupVersionMap?.[m.apiGroup]?.preferredVersion === m.apiVersion;

        const sameGroupKind = (m: K8sKind) =>
          m.kind === kind && m.apiGroup === apiGroup && m.apiVersion !== apiVersion;

        return !allK8sModels.find((m) => sameGroupKind(m) && preferred(m));
      })
      .toOrderedMap()
      .sortBy(({ kind, apiGroup }) => `${kind} ${apiGroup}`);
  }, [allK8sModels, groupVersionMap]);

  // Track duplicate names so we know when to show the group.
  const kinds = resources.groupBy((m) => m.kind);
  const isDup = (kind) => kinds.get(kind).size > 1;

  type ItemProps = { title?: string; model?: K8sKind };

  const Item = memo<ItemProps>(({ model }) => (
    <div style={{ display: 'flex', alignItems: 'center' }}>
      <span className="co-resource-item">
        <span className="co-resource-icon--fixed-width">
          <ResourceIcon kind={referenceForModel(model)} />
        </span>
        <span className="co-resource-item__resource-name">
          <span>
            {model.labelKey ? t(model.labelKey) : model.kind}
            {model.badge && model.badge === 'Tech Preview' && (
              <span className="co-resource-item__tech-dev-preview">{t('public~Tech Preview')}</span>
            )}
          </span>
          {isDup(model.kind) && (
            <div className="pf-v6-u-font-size-xs pf-v6-u-text-color-subtle co-truncate co-nowrap">
              {model.apiGroup || 'core'}/{model.apiVersion}
            </div>
          )}
        </span>
      </span>
    </div>
  ));

  const InvalidItem = memo<ItemProps>(({ title }) => (


Add data-test attributes to resource items and assert the expected resource exists rather than falling back

The fallback to .first() is non-deterministic—if the expected resource isn't found, the test selects a random item and masks the real issue. Per E2E test guidelines, resource items should be identifiable via data-test attributes; consider adding them to the resource item components (e.g., data-test={resource-item-${groupName}}) so the test can assert the expected item exists rather than silently accepting the first result.

Code snippet
      const consoleItem = page.locator('[class="co-resource-item"]').filter({
        hasText: 'operator.openshift.io',
      });
      if ((await consoleItem.count()) > 0) {
        await consoleItem.click();
      } else {
        await page.locator('[class="co-resource-item"]').first().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/dev-console/configure-perspectives.spec.ts` around lines
31 - 42, The test falls back to selecting the first .co-resource-item when the
expected consoleItem (variable consoleItem) isn't found, masking failures;
update the UI component that renders resource items to include a deterministic
data-test attribute (e.g., data-test={`resource-item-${groupName}`} on the
resource item element) and change the test (using the existing combobox,
filterInput and consoleItem locators) to select by that data-test selector and
assert its count is > 0, failing the test if the specific
resource-item-{groupName} is not present instead of using .first().

Comment on lines +22 to +24
test.afterAll(async () => {
await k8s.deleteNamespace(namespace);
});
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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check the file structure and the specific lines mentioned
cat -n "frontend/e2e/tests/dev-console/customization-of-catalog-add-page-form-view.spec.ts" | head -50

Repository: openshift/console

Length of output: 2381


🏁 Script executed:

#!/bin/bash
# Also check what imports are used and how k8s is obtained
head -30 "frontend/e2e/tests/dev-console/customization-of-catalog-add-page-form-view.spec.ts"

Repository: openshift/console

Length of output: 1212


🏁 Script executed:

#!/bin/bash
# Look at the fixture definitions to understand k8s initialization
find frontend/e2e -name "fixtures*" -type f | head -5

Repository: openshift/console

Length of output: 43


🏁 Script executed:

#!/bin/bash
# Find the fixtures file
find frontend/e2e -name "fixtures.ts" -o -name "fixtures.tsx" -o -name "fixtures/index.ts"

Repository: openshift/console

Length of output: 43


🏁 Script executed:

#!/bin/bash
# Search for other afterAll patterns in e2e tests
grep -r "test.afterAll" frontend/e2e/tests --include="*.spec.ts" -A 2 | head -40

Repository: openshift/console

Length of output: 2842


🏁 Script executed:

#!/bin/bash
# Check if there are any try-catch patterns in afterAll blocks
grep -r "test.afterAll" frontend/e2e/tests --include="*.spec.ts" -A 5 | grep -A 5 "try\|catch\|if"

Repository: openshift/console

Length of output: 992


🏁 Script executed:

#!/bin/bash
# Look at the oauth.spec.ts to understand the guarding pattern
grep -B 10 -A 10 "if (!originalOAuthConfig || !client)" frontend/e2e/tests/console/cluster-settings/oauth.spec.ts

Repository: openshift/console

Length of output: 617


🏁 Script executed:

#!/bin/bash
# Check the fixtures file to understand k8s initialization
find frontend/e2e -name "fixtures*" -type f

Repository: openshift/console

Length of output: 43


🏁 Script executed:

#!/bin/bash
# Search for fixtures index
ls -la frontend/e2e/

Repository: openshift/console

Length of output: 770


🏁 Script executed:

#!/bin/bash
# Check the fixtures directory
ls -la frontend/e2e/fixtures/

Repository: openshift/console

Length of output: 299


🏁 Script executed:

#!/bin/bash
# Look at the fixtures file content
cat frontend/e2e/fixtures/index.ts

Repository: openshift/console

Length of output: 2410


🏁 Script executed:

#!/bin/bash
# Look at the oauth.spec.ts full context around initialization
grep -B 20 "test.beforeAll" frontend/e2e/tests/console/cluster-settings/oauth.spec.ts | head -40

Repository: openshift/console

Length of output: 516


🏁 Script executed:

#!/bin/bash
# Check if this test pattern is documented or if there are any error handling guides
grep -r "beforeAll" frontend/e2e/tests --include="*.spec.ts" -A 10 | grep -B 3 -A 7 "try\|catch" | head -50

Repository: openshift/console

Length of output: 43


🏁 Script executed:

#!/bin/bash
# Check the oauth.spec.ts beforeAll to see how it initializes
grep -A 15 "test.beforeAll" frontend/e2e/tests/console/cluster-settings/oauth.spec.ts

Repository: openshift/console

Length of output: 547


Guard teardown against initialization failures

The afterAll unconditionally calls k8s.deleteNamespace(namespace). If new KubernetesClient() or createNamespace() fails in beforeAll, the variable remains undefined and teardown throws a secondary error, masking the original failure. The codebase already establishes this pattern (see oauth.spec.ts's if (!originalOAuthConfig || !client)).

Suggested fix
 test.afterAll(async () => {
-  await k8s.deleteNamespace(namespace);
+  if (k8s) {
+    await k8s.deleteNamespace(namespace);
+  }
 });
🤖 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/dev-console/customization-of-catalog-add-page-form-view.spec.ts`
around lines 22 - 24, The afterAll teardown unconditionally calls
k8s.deleteNamespace(namespace) which can throw if namespace or k8s were never
initialized; guard the call by checking that both the KubernetesClient instance
(k8s) and the namespace variable are defined before calling deleteNamespace.
Locate the test.afterAll block and wrap or conditionally execute
k8s.deleteNamespace(namespace) only when namespace and k8s are truthy (mirror
the pattern used in oauth.spec.ts that checks if (!originalOAuthConfig ||
!client)). Ensure the check covers failures from new KubernetesClient() and
createNamespace() in beforeAll.

Comment on lines +26 to +188
test('when all the sub-catalogs are disabled', async ({ page }) => {
const perspectivePage = new PerspectivePage(page);
const customizationPage = new CustomizationPage(page);

await test.step('Navigate to console customization', async () => {
await page.goto('/');
await perspectivePage.switchToDeveloper();
await perspectivePage.selectOrCreateProject(namespace);
await customizationPage.navigateToConsoles();
await customizationPage.clickCluster();
await customizationPage.openCustomization();
});

await test.step('Disable all software catalog items', async () => {
await customizationPage.clickDeveloperTab();
await customizationPage.disableAllSoftwareCatalogItems();
});

await test.step('Verify save message', async () => {
await customizationPage.expectSaveMessage();
});

await test.step('Verify software catalog not visible', async () => {
await perspectivePage.switchToAdministrator();
await perspectivePage.switchToDeveloper();
await perspectivePage.selectOrCreateProject(namespace);
await perspectivePage.navigateToAdd();
await page.reload();
await page.waitForLoadState('domcontentloaded');
await expect(page.getByTestId('card developer-catalog')).toBeHidden();
});
});

test('when specific sub-catalog is disabled', async ({ page }) => {
const perspectivePage = new PerspectivePage(page);
const customizationPage = new CustomizationPage(page);

await test.step('Navigate to customization', async () => {
await page.goto('/');
await perspectivePage.switchToDeveloper();
await perspectivePage.selectOrCreateProject(namespace);
await customizationPage.navigateToConsoles();
await customizationPage.clickCluster();
await customizationPage.openCustomization();
});

await test.step('Disable Helm Charts in software catalog', async () => {
await customizationPage.clickDeveloperTab();
await customizationPage.disableSoftwareCatalogItem('Helm Charts');
});

await test.step('Verify save message', async () => {
await customizationPage.expectSaveMessage();
});

await test.step('Verify Helm Charts not visible but catalog exists', async () => {
await perspectivePage.switchToAdministrator();
await perspectivePage.switchToDeveloper();
await perspectivePage.selectOrCreateProject(namespace);
await perspectivePage.navigateToAdd();
await page.reload();
await page.waitForLoadState('domcontentloaded');
await expect(page.getByTestId('card developer-catalog')).toBeVisible({ timeout: 60_000 });
await expect(page.getByTestId('item helm')).toBeHidden();
});
});

test('when specific sub-catalog is enabled', async ({ page }) => {
const perspectivePage = new PerspectivePage(page);
const customizationPage = new CustomizationPage(page);

await test.step('Navigate to customization', async () => {
await page.goto('/');
await perspectivePage.switchToDeveloper();
await perspectivePage.selectOrCreateProject(namespace);
await customizationPage.navigateToConsoles();
await customizationPage.clickCluster();
await customizationPage.openCustomization();
});

await test.step('Enable only Helm Charts', async () => {
await customizationPage.clickDeveloperTab();
await customizationPage.enableOnlySoftwareCatalogItem('Helm Charts');
});

await test.step('Verify save message', async () => {
await customizationPage.expectSaveMessage();
});

await test.step('Verify only Helm Charts visible', async () => {
await perspectivePage.switchToAdministrator();
await perspectivePage.switchToDeveloper();
await perspectivePage.selectOrCreateProject(namespace);
await perspectivePage.navigateToAdd();
await page.reload();
await page.waitForLoadState('domcontentloaded');
await expect(page.getByTestId('card developer-catalog')).toBeVisible({ timeout: 60_000 });
await expect(page.getByTestId('item helm')).toBeVisible();
await expect(page.getByTestId('item operator-backed')).toBeHidden();
});
});

test('when all the add page items are disabled', async ({ page }) => {
const perspectivePage = new PerspectivePage(page);
const customizationPage = new CustomizationPage(page);

await test.step('Navigate to customization', async () => {
await page.goto('/');
await perspectivePage.switchToDeveloper();
await perspectivePage.selectOrCreateProject(namespace);
await customizationPage.navigateToConsoles();
await customizationPage.clickCluster();
await customizationPage.openCustomization();
});

await test.step('Disable all add page items', async () => {
await customizationPage.clickDeveloperTab();
await customizationPage.disableAllAddPageItems();
});

await test.step('Verify save message', async () => {
await customizationPage.expectSaveMessage();
});

await test.step('Verify add page only shows Getting Started', async () => {
await perspectivePage.navigateToAdd();
await page.reload();
await page.waitForLoadState('domcontentloaded');
await expect(page.getByTestId('getting-started')).toBeVisible({ timeout: 60_000 });
const cards = page.getByTestId('add-page').locator('[data-test^=card]');
await expect(cards).toHaveCount(2);
});
});

test('when specific add page item is disabled', async ({ page }) => {
const perspectivePage = new PerspectivePage(page);
const customizationPage = new CustomizationPage(page);

await test.step('Navigate to customization', async () => {
await page.goto('/');
await perspectivePage.switchToDeveloper();
await perspectivePage.selectOrCreateProject(namespace);
await customizationPage.navigateToConsoles();
await customizationPage.clickCluster();
await customizationPage.openCustomization();
});

await test.step('Disable Import from Git', async () => {
await customizationPage.clickDeveloperTab();
await customizationPage.disableAddPageItem('Import from Git');
});

await test.step('Verify save message', async () => {
await customizationPage.expectSaveMessage();
});

await test.step('Verify Import from Git not visible', async () => {
await perspectivePage.navigateToAdd();
await page.reload();
await page.waitForLoadState('domcontentloaded');
await expect(page.getByTestId('card git-repository')).toBeHidden();
});
});
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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify Playwright parallelism settings and whether dev-console customization specs are serialized.
fd -a 'playwright.config.ts' frontend/e2e --exec sed -n '1,240p' {}

# Inspect relevant specs for serial mode and cluster-customization mutations.
rg -n --type=ts -C2 'test\.describe(\.serial)?|fullyParallel|workers|openCustomization|disableAllSoftwareCatalogItems|enableOnlySoftwareCatalogItem|disableAllAddPageItems|disableAddPageItem' frontend/e2e/tests/dev-console

Repository: openshift/console

Length of output: 16697


🏁 Script executed:

#!/bin/bash
# Find playwright config file
fd -type f -name 'playwright.config.*' 

# Get the exact test.describe declaration and any afterAll/beforeAll hooks from the target file
sed -n '1,30p' frontend/e2e/tests/dev-console/customization-of-catalog-add-page-form-view.spec.ts

# Look for afterAll hook and cleanup logic
tail -20 frontend/e2e/tests/dev-console/customization-of-catalog-add-page-form-view.spec.ts

Repository: openshift/console

Length of output: 2042


🏁 Script executed:

#!/bin/bash
# Find playwright config
find . -name 'playwright.config.*' -type f

# Check full file for .serial modifier and any restoration logic
grep -n -E '(\.serial|restore|revert|beforeEach|afterEach|cleanup.*custom)' frontend/e2e/tests/dev-console/customization-of-catalog-add-page-form-view.spec.ts

# Read the entire target file to see structure
wc -l frontend/e2e/tests/dev-console/customization-of-catalog-add-page-form-view.spec.ts

Repository: openshift/console

Length of output: 180


🏁 Script executed:

#!/bin/bash
# Read playwright config for parallelism settings
cat ./frontend/playwright.config.ts

# Read full target file to see complete structure
cat -n frontend/e2e/tests/dev-console/customization-of-catalog-add-page-form-view.spec.ts

Repository: openshift/console

Length of output: 13101


Add afterEach cleanup to restore cluster customization state between tests

Tests in this suite mutate cluster-wide customization (catalog/add-page visibility) without resetting state between tests, creating order dependencies that violate the E2E guideline requiring each test to create and clean up its own resources. While CI serialization (workers: 1) prevents cross-suite interference during CI runs, local development environments run tests in parallel, exposing potential flakiness. Add an afterEach hook to restore baseline customization after each test, or consider marking the suite with .serial for local environments.

🤖 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/dev-console/customization-of-catalog-add-page-form-view.spec.ts`
around lines 26 - 188, Add an afterEach cleanup that restores cluster
customization to a known baseline after each test (or mark the suite .serial);
implement it by instantiating CustomizationPage (and PerspectivePage if needed),
navigating to consoles/cluster/customization (use methods navigateToConsoles,
clickCluster, openCustomization), switching to the Developer tab
(clickDeveloperTab), and calling the appropriate reset method (e.g.,
enableAll/enableOnlySoftwareCatalogItem, disableAllAddPageItems reversal or a
new restoreDefaultCustomization helper) then saving and waiting for
expectSaveMessage to succeed so each test leaves the cluster in a consistent
state.

Comment on lines +44 to +56
await k8s
.waitForCustomResourceCondition(
'apps',
'v1',
namespace,
'deployments',
'nodejs-ex-git',
(resource: any) => resource?.status?.availableReplicas > 0,
120_000,
)
.catch(() => {
// Deployment may not be fully ready, but we need pods to exist
});
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

Fail fast on deployment readiness failures in setup.

Suppressing the readiness error here hides real cluster/setup failures and turns them into later flaky UI failures. Let this fail with context (or assert pod existence explicitly) before executing test steps.

Suggested fix
-    await k8s
-      .waitForCustomResourceCondition(
-        'apps',
-        'v1',
-        namespace,
-        'deployments',
-        'nodejs-ex-git',
-        (resource: any) => resource?.status?.availableReplicas > 0,
-        120_000,
-      )
-      .catch(() => {
-        // Deployment may not be fully ready, but we need pods to exist
-      });
+    await k8s.waitForCustomResourceCondition(
+      'apps',
+      'v1',
+      namespace,
+      'deployments',
+      'nodejs-ex-git',
+      (resource: any) => resource?.status?.availableReplicas > 0,
+      120_000,
+    );
📝 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 k8s
.waitForCustomResourceCondition(
'apps',
'v1',
namespace,
'deployments',
'nodejs-ex-git',
(resource: any) => resource?.status?.availableReplicas > 0,
120_000,
)
.catch(() => {
// Deployment may not be fully ready, but we need pods to exist
});
await k8s.waitForCustomResourceCondition(
'apps',
'v1',
namespace,
'deployments',
'nodejs-ex-git',
(resource: any) => resource?.status?.availableReplicas > 0,
120_000,
);
🤖 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/dev-console/pod-list-page.spec.ts` around lines 44 - 56,
The current setup swallows failures from k8s.waitForCustomResourceCondition
(called with 'apps','v1', namespace, 'deployments','nodejs-ex-git') which masks
real cluster issues; remove the .catch suppression so the test fails fast and
surfaces the original error, or replace the suppressed branch with an explicit
assertion that pods exist (e.g., call a pod-listing/wait helper for the
deployment and assert length>0) so failures are reported with context before
proceeding to UI steps.

Comment on lines +16 to +19
await test.step('Open Create Project modal', async () => {
await perspectivePage.selectOrCreateProject(projectName);
cleanup.trackNamespace(projectName);
});
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

Register cleanup before triggering project creation.

On Line 18, cleanup is tracked only after selectOrCreateProject returns. If creation succeeds but that helper throws later, the namespace can be orphaned.

Proposed fix
     await test.step('Open Create Project modal', async () => {
-      await perspectivePage.selectOrCreateProject(projectName);
       cleanup.trackNamespace(projectName);
+      await perspectivePage.selectOrCreateProject(projectName);
     });

As per coding guidelines: "Each Playwright E2E test block must create its own resources, assert independently, and clean up via the cleanup fixture".

📝 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 test.step('Open Create Project modal', async () => {
await perspectivePage.selectOrCreateProject(projectName);
cleanup.trackNamespace(projectName);
});
await test.step('Open Create Project modal', async () => {
cleanup.trackNamespace(projectName);
await perspectivePage.selectOrCreateProject(projectName);
});
🤖 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/dev-console/project-creation.spec.ts` around lines 16 -
19, Move the cleanup registration to happen before triggering project creation
so the namespace is always tracked even if creation throws; specifically, call
cleanup.trackNamespace(projectName) before awaiting
perspectivePage.selectOrCreateProject(projectName) in the test step that opens
the Create Project modal (references: perspectivePage.selectOrCreateProject and
cleanup.trackNamespace). Ensure the order is changed in that block so
cleanup.trackNamespace runs first but keep the same projectName variable and
existing assertions intact.

Comment on lines +80 to +221
test('setting graph preference for topology', { tag: ['@regression'] }, async ({ page }) => {
const perspectivePage = new PerspectivePage(page);
const userPreferencesPage = new UserPreferencesPage(page);

await test.step('Create deployment workload', async () => {
await k8s.appsV1Api.createNamespacedDeployment({
namespace,
body: {
apiVersion: 'apps/v1',
kind: 'Deployment',
metadata: { name: 'node1', namespace },
spec: {
replicas: 1,
selector: { matchLabels: { app: 'node1' } },
template: {
metadata: { labels: { app: 'node1' } },
spec: {
containers: [
{
name: 'node1',
image: 'registry.access.redhat.com/ubi8/nodejs-18:latest',
ports: [{ containerPort: 8080, protocol: 'TCP' }],
},
],
},
},
},
},
});
});

await test.step('Set topology preference to Graph', async () => {
await page.goto('/');
await perspectivePage.switchToAdministrator();
await userPreferencesPage.openUserPreferences();
await userPreferencesPage.changePreferenceDropdown('Topology', 'Graph');
});

await test.step('Reload and navigate to topology', async () => {
await userPreferencesPage.reloadConsole();
await perspectivePage.switchToDeveloper();
await perspectivePage.selectOrCreateProject(namespace);
await perspectivePage.navigateToTopology();
});

await test.step('Verify graph view', async () => {
await userPreferencesPage.expectTopologyGraphView();
});
});

test('setting list preference for topology', { tag: ['@regression'] }, async () => {
test.skip(true, 'Marked @broken-test in Cypress — bugzilla 2014313');
});

test(
'setting form preference for create/edit resource method',
{ tag: ['@regression'] },
async () => {
test.skip(true, 'Marked @broken-test in Cypress');
},
);

test(
'setting YAML preference for create/edit resource method',
{ tag: ['@regression'] },
async ({ page }) => {
const perspectivePage = new PerspectivePage(page);
const userPreferencesPage = new UserPreferencesPage(page);

await test.step('Set create/edit preference to YAML', async () => {
await page.goto('/');
await perspectivePage.switchToAdministrator();
await userPreferencesPage.openUserPreferences();
await userPreferencesPage.changePreferenceDropdown('Create/Edit resource method', 'YAML');
});

await test.step('Navigate to Helm charts and install', async () => {
await perspectivePage.switchToDeveloper();
await perspectivePage.selectOrCreateProject(namespace);
await perspectivePage.navigateToAdd();
const helmCard = page.getByTestId('card helm');
await helmCard.click();
const searchInput = page.locator('[data-test="search-catalog"]');
await searchInput.fill('Nodejs');
const helmChartCard = page
.locator('[data-test^="HelmChart"]')
.filter({ hasText: 'Nodejs' })
.first();
await helmChartCard.click();
const installButton = page.locator('[data-test="install-helm"]');
await installButton.click();
});

await test.step('Verify YAML view selected', async () => {
await userPreferencesPage.expectYamlViewSelected();
});
},
);

test('setting a preference for language', { tag: ['@regression'] }, async () => {
test.skip(true, 'Marked @broken-test in Cypress');
});

test('setting routing options preference for import form', { tag: ['@regression'] }, async () => {
test.skip(true, 'Marked @broken-test in Cypress — ODC-6303');
});

test('setting theme preference for console', { tag: ['@regression'] }, async () => {
test.skip(true, 'Manual test — ODC-5990');
});

test(
'setting resource type preference for console',
{ tag: ['@regression'] },
async ({ page }) => {
const perspectivePage = new PerspectivePage(page);
const userPreferencesPage = new UserPreferencesPage(page);

await test.step('Set resource type to DeploymentConfig', async () => {
await page.goto('/');
await perspectivePage.switchToAdministrator();
await userPreferencesPage.openUserPreferences();
await userPreferencesPage.clickTab('Applications');
await userPreferencesPage.changePreferenceDropdown('Resource Type', 'DeploymentConfig');
});

await test.step('Navigate to Container images', async () => {
await perspectivePage.switchToDeveloper();
await perspectivePage.selectOrCreateProject(namespace);
await perspectivePage.navigateToAdd();
const containerImageCard = page.getByTestId('card container-image');
await containerImageCard.click();
});

await test.step('Verify DeploymentConfig is selected', async () => {
const resourceDropdown = page.locator('[data-test-id="dropdown-button"]').filter({
hasText: 'DeploymentConfig',
});
await expect(resourceDropdown.first()).toBeVisible({ timeout: 30_000 });
});
},
);
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

Reset persisted user-preference mutations to avoid cross-test leakage.

These tests change durable preferences (Topology, Create/Edit resource method, Resource Type) and don’t restore them. That can contaminate later tests and produce order-dependent failures.

Use try/finally in each mutating test (or a shared afterEach) to restore previous values.

As per coding guidelines: “Each Playwright E2E test block must create its own resources, assert independently, and clean up via the cleanup fixture”.

🤖 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/dev-console/user-preferences-dev-perspective.spec.ts`
around lines 80 - 221, Tests mutate durable user preferences (Topology,
Create/Edit resource method, Resource Type) without restoring them; capture the
current preference before calling UserPreferencesPage.changePreferenceDropdown
and restore it in a finally block (or implement a shared afterEach cleanup) so
mutations don't leak across tests: for each test that calls
UserPreferencesPage.changePreferenceDropdown (e.g., tests using PerspectivePage
and UserPreferencesPage, methods
openUserPreferences/changePreferenceDropdown/reloadConsole/expectTopologyGraphView/expectYamlViewSelected/clickTab),
read and store the prior value (via an existing getter on UserPreferencesPage or
by reading the dropdown state), then in finally call changePreferenceDropdown to
revert to the saved value (or call a new UserPreferencesPage.resetPreference
helper), ensuring cleanup occurs even on failures.

@Cragsmann Cragsmann marked this pull request as draft May 20, 2026 19:05
@openshift-ci openshift-ci Bot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label May 20, 2026
@openshift-ci
Copy link
Copy Markdown
Contributor

openshift-ci Bot commented May 20, 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/frontend 15dfb40 link true /test frontend

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

do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. jira/valid-reference Indicates that this PR references a valid Jira ticket of any type.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants