Skip to content

test: added extensive help panel tests#307

Open
jjaquish wants to merge 1 commit intoRedHatInsights:masterfrom
jjaquish:jjaquish/RHCLOUD-45255
Open

test: added extensive help panel tests#307
jjaquish wants to merge 1 commit intoRedHatInsights:masterfrom
jjaquish:jjaquish/RHCLOUD-45255

Conversation

@jjaquish
Copy link
Copy Markdown
Contributor

@jjaquish jjaquish commented May 4, 2026

Description

RHCLOUD-45255
This is under the learn tab testing Jira, but covers all help panel testing tickets currently assigned to me.

This is to add extensive E2E testing in playwright for all features (where feasible) of the help panel and its various tabs.

As a note for review, I would suggest cloning this PR locally and using npx playwright test --ui to view them in the Playwright UI. The names are descriptive so coverage intention can be seen there, and the tests can be run if desired. This does not cover fixing any currently standing Playwright tests, only adding the new help panel tests.

All running tests are currently passing, and some were added and skipped with notes to be addressed later.

Screenshots

image image image

Anything reviewers should know?


Checklist

  • Accessibility: color contrast, keyboard nav, screen reader tested (or N/A)
  • All PR checks pass locally (build, lint, test)
  • No unrelated changes included
  • (Optional) QE: OUIA changed, test impact, no coverage
  • (Optional) UX: end-user UX modified, designs need sign-off

AI disclosure

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 4, 2026

Warning

Rate limit exceeded

@jjaquish has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 3 minutes and 32 seconds before requesting another review.

To continue reviewing without waiting, purchase usage credits in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Enterprise

Run ID: 79aa9948-2671-4e28-892e-217dbbf98e0f

📥 Commits

Reviewing files that changed from the base of the PR and between d0c580b and 799bfa2.

📒 Files selected for processing (12)
  • playwright.config.ts
  • playwright/E2E_SETUP_NOTES.md
  • playwright/HELP_PANEL_E2E_TESTS.md
  • playwright/README.md
  • playwright/all-learning-resources.spec.ts
  • playwright/global-setup-with-proxy.ts
  • playwright/help-panel-api-tab.spec.ts
  • playwright/help-panel-feedback-tab.spec.ts
  • playwright/help-panel-learn-tab.spec.ts
  • playwright/help-panel-search-tab.spec.ts
  • playwright/help-panel-support-tab.spec.ts
  • playwright/help-panel.spec.ts

Walkthrough

This PR introduces a comprehensive Playwright E2E test suite for the Help Panel with proxy-based authentication support. It adds configuration files, global setup scripts, five new test spec files covering different Help Panel tabs, documentation, and debugging utilities. The configuration now uses a custom global-setup-with-proxy.ts script and updated stage base URL while maintaining single-worker, sequential test execution.

Changes

Help Panel E2E Test Suite

Layer / File(s) Summary
Configuration
playwright.config.ts, playwright.debug-global.config.ts, playwright/playwright.debug.config.ts
Playwright config updated to use local ./playwright/global-setup-with-proxy.ts instead of shared auth package; default baseURL changed to https://console.stage.redhat.com. Debug configs added with 10s timeout, single worker, optional proxy support via E2E_PROXY env var.
Authentication & Setup
playwright/global-setup-with-proxy.ts, playwright/debug-global-setup.ts
global-setup-with-proxy.ts implements E2E login flow with proxy config passthrough, cookie consent blocking, lockdown detection, and storageState save. debug-global-setup.ts logs config values, performs browser diagnostics, and detects proxy/auth issues for troubleshooting.
Test Specifications
playwright/help-panel-*.spec.ts, playwright/help-panel.spec.ts, playwright/all-learning-resources.spec.ts, playwright/debug-login.spec.ts
Five new tab-specific test suites added: API tab (loads docs, catalog link), Feedback tab (forms, research checkbox, validation), Learn tab (resource loading, filtering, scoping, bookmarking), Search tab (recommended content, search/filter/history/scope, bookmarking), Support tab (empty state, case popup). Existing tests updated with tab navigation verification; debug-login.spec.ts added for diagnostic logging.
Documentation & Debugging
playwright/README.md, playwright/E2E_SETUP_NOTES.md, playwright/HELP_PANEL_E2E_TESTS.md
Comprehensive README covering prerequisites, env vars, run commands, coverage matrix, debugging (trace/UI/reports), common issues, CI/CD integration notes, and test data considerations. E2E_SETUP_NOTES.md documents setup, known issues (proxy support, base URL correction, SSO redirect), debugging tips, and Tekton CI guidance. HELP_PANEL_E2E_TESTS.md details per-tab specs, coverage scope, testing patterns (API waits, external links, empty-state branching), and provides a coverage summary.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'test: added extensive help panel tests' directly summarizes the main change—adding comprehensive Playwright E2E tests for the help panel.
Description check ✅ Passed The PR description includes a JIRA reference, a 2-3 sentence summary of the proposed changes, helpful context for reviewers, and a screenshots section with three relevant images. All major template sections are completed.
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.

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

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

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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.

@jjaquish jjaquish changed the title test: dded extensive help panel tests test: added extensive help panel tests May 4, 2026
Copy link
Copy Markdown
Collaborator

@apinkert apinkert left a comment

Choose a reason for hiding this comment

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

Hey Jack, this is looking pretty good! Looks like there are some failures in the CI related to the search panel e2e test. Also, we can remove all the knowledgebase tests as we're going to be removing it from the help panel.

Copy link
Copy Markdown

@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: 10

🧹 Nitpick comments (1)
playwright/global-setup-with-proxy.ts (1)

7-17: ⚡ Quick win

Replace any with proper Playwright types.

disableCookiePrompt and login use any for all Playwright-specific parameters, losing type safety in test infrastructure. Page, Route, and Request are all named exports from 'playwright'.

♻️ Proposed fix
-import { chromium, type FullConfig } from 'playwright';
+import { chromium, type FullConfig, type Page, type Route, type Request } from 'playwright';

-async function disableCookiePrompt(page: any) {
-  await page.route('**/*', async (route: any, request: any) => {
+async function disableCookiePrompt(page: Page) {
+  await page.route('**/*', async (route: Route, request: Request) => {
     if (request.url().includes('consent.trustarc.com') && request.resourceType() !== 'document') {
       await route.abort();
     } else {
       await route.continue();
     }
   });
 }

-async function login(page: any, user: string, password: string) {
+async function login(page: Page, user: string, password: string) {
🤖 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 `@playwright/global-setup-with-proxy.ts` around lines 7 - 17, The functions
disableCookiePrompt and login currently use any for Playwright types; update
their signatures to use Playwright's types by importing and using Page for the
page parameter and typing the route handler parameters as (route: Route,
request: Request) so the call to page.route has properly typed callback args;
ensure you add the named imports (Page, Route, Request) from 'playwright' and
replace all occurrences of any in disableCookiePrompt and login with these
concrete types to restore type safety.
🤖 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 `@playwright/debug-global-setup.ts`:
- Around line 6-7: The current console.log calls print raw sensitive values from
config.projects[0].use.proxy and config.projects[0].use.storageState; replace
these with presence-only or redacted summaries: for proxy, log only whether a
proxy is configured and which keys are present (e.g., list keys like "server",
"username" but not their values) or mask values (e.g., replace characters with
asterisks), and for storageState log only that a storage state file/string
exists and its filename or size (not contents). Update the debug output in
debug-global-setup.ts to use a small helper (e.g., logProxySummary or redact) to
produce these summaries when referencing config and config.projects[0].use.* so
no secrets or full paths are emitted to CI logs.

In `@playwright/debug-login.spec.ts`:
- Around line 3-32: The test "debug login page" currently contains only logging
calls and no assertions, so it will always pass; either mark it as skipped
(test.skip/test.fixme) or add explicit Playwright assertions to validate the
critical login state (e.g., import expect from '@playwright/test' and assert
things like page.title() includes expected text, page.getByLabel('Red Hat
login').count() is > 0, or that specific "Lockdown" or error counts are 0);
update the test declaration test('debug login page', ...) accordingly and
replace the console checks with expect(...) assertions (or change to
test.skip/test.fixme if you intend to keep it diagnostic).

In `@playwright/E2E_SETUP_NOTES.md`:
- Around line 7-9: Replace the hardcoded credentials and proxy entries
(E2E_USER, E2E_PASSWORD, E2E_PROXY) with non-sensitive placeholder values and
update every occurrence including inline command examples and references in
playwright/README.md; change examples to point to an internal secrets store
(e.g., Vault or team wiki) and document how to fetch them, remove the plaintext
password 'PlatformExperience!1' everywhere, and ensure the exposed stage account
credentials are rotated immediately after committing this change.

In `@playwright/help-panel-feedback-tab.spec.ts`:
- Around line 227-236: The breadcrumb-click step is non-deterministic because it
only clicks when isVisible is truthy; instead assert the breadcrumb is visible
then perform the click to fail fast if missing. Replace the conditional around
breadcrumb/isVisible with an explicit assertion using
expect(breadcrumb).toBeVisible(), then call breadcrumb.click(),
waitForTimeout(500) and assert the heading via expect(page.getByRole('heading',
{ name: /tell us about your experience/i })).toBeVisible(); keep the same
locators (breadcrumb = page.locator(...)) and waits.

In `@playwright/help-panel-learn-tab.spec.ts`:
- Around line 69-102: The test "toggles between All and bundle scope" currently
silently passes when the scope toggle is missing; change the early conditional
so that when scopeToggle (locator
'[data-ouia-component-id="help-panel-scope-toggle"]') is not visible you call
test.skip('scope toggle not present - skipping bundle-specific assertions')
instead of simply returning, otherwise proceed with the existing flow (use
allToggle and bundleToggle locators and the toolbar check as before); ensure you
await scopeToggle.isVisible() then skip via test.skip to make non-applicability
explicit.

In `@playwright/help-panel-search-tab.spec.ts`:
- Around line 203-229: The test "toggles recommended content scope" currently
returns a vacuous pass when the recommendedToggle locator isn't visible; after
awaiting recommendedToggle.isVisible() call test.skip(true, 'recommended scope
toggle not present on this page') (or test.skip(!isVisible, ...)) and return so
the test is reported as skipped instead of passing; update the block around the
recommendedToggle locator
(data-ouia-component-id="help-panel-recommended-scope-toggle") to perform the
visibility await, call test.skip if false, and only proceed with the existing
clicks/assertions (allToggle, bundleToggle, recommendedItems) when visible.
- Around line 231-276: The two Playwright tests "bookmarks a learning resource
from search results" and "favorites a service from search results" contain no
assertions and therefore should be skipped until proper assertions are added;
update the test declarations for those cases (the test(...) calls with those
exact titles) to test.skip(...) so they are not executed and give false
confidence, and add a TODO comment referencing the need to assert the post-click
state (e.g., icon/state change or API response) before re-enabling them.

In `@playwright/help-panel-support-tab.spec.ts`:
- Around line 41-78: Both tests ("displays empty state when no support cases
exist" and "opens Customer Portal when clicking \"Open a support case\"")
currently short-circuit with if (isEmptyVisible) { ... } causing vacuous passes;
replace the conditional checks on the emptyState locator with explicit
assertions (e.g., await expect(emptyState).toBeVisible() or
expect(isEmptyVisible).toBeTruthy()) so the test fails when the empty state is
not present, and then proceed to interact with openCaseButton and popupPromise
(or convert the check into a clear precondition using test.skip/test.fail with a
message) to ensure those branches are validated rather than silently skipped.
- Around line 24-26: Replace the fixed sleep (await page.waitForTimeout(2000))
with a state-based wait that checks the support panel UI using the same
selectors and pattern used in the following test: remove the page.waitForTimeout
call and wrap a check for the panel elements (the exact locators used in the
next test) in an expect(async () => { /* await
page.locator(...).isVisible()/count()/textContent() */ }).toPass() so the test
waits for either the empty-state or table to appear instead of relying on a
timeout.

In `@playwright/README.md`:
- Around line 18-21: Replace the hardcoded credentials (E2E_USER, E2E_PASSWORD)
and the cross-repo .env pointer in the PLAYWRIGHT README with neutral
placeholders and a reference to the internal team credentials document;
specifically, update the lines exporting E2E_USER and E2E_PASSWORD to use
placeholder values (e.g., <E2E_USER_PLACEHOLDER>, <E2E_PASSWORD_PLACEHOLDER>),
keep or parameterize E2E_PROXY if needed, remove the reference to
insights-rbac-ui/e2e/auth/.env.v1-orgadmin, and add a short note directing
readers to the internal credentials store or team doc for obtaining real test
account credentials.

---

Nitpick comments:
In `@playwright/global-setup-with-proxy.ts`:
- Around line 7-17: The functions disableCookiePrompt and login currently use
any for Playwright types; update their signatures to use Playwright's types by
importing and using Page for the page parameter and typing the route handler
parameters as (route: Route, request: Request) so the call to page.route has
properly typed callback args; ensure you add the named imports (Page, Route,
Request) from 'playwright' and replace all occurrences of any in
disableCookiePrompt and login with these concrete types to restore type safety.
🪄 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: Organization UI

Review profile: CHILL

Plan: Enterprise

Run ID: be23765d-eb96-4512-87fc-641b920b77b6

📥 Commits

Reviewing files that changed from the base of the PR and between 4100b96 and d0c580b.

📒 Files selected for processing (16)
  • playwright.config.ts
  • playwright.debug-global.config.ts
  • playwright/E2E_SETUP_NOTES.md
  • playwright/HELP_PANEL_E2E_TESTS.md
  • playwright/README.md
  • playwright/all-learning-resources.spec.ts
  • playwright/debug-global-setup.ts
  • playwright/debug-login.spec.ts
  • playwright/global-setup-with-proxy.ts
  • playwright/help-panel-api-tab.spec.ts
  • playwright/help-panel-feedback-tab.spec.ts
  • playwright/help-panel-learn-tab.spec.ts
  • playwright/help-panel-search-tab.spec.ts
  • playwright/help-panel-support-tab.spec.ts
  • playwright/help-panel.spec.ts
  • playwright/playwright.debug.config.ts

Comment thread playwright/debug-global-setup.ts Outdated
Comment on lines +6 to +7
console.log('Proxy config:', JSON.stringify(config.projects[0].use.proxy, null, 2));
console.log('StorageState:', config.projects[0].use.storageState);
Copy link
Copy Markdown

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

Do not log raw proxy/storage configuration values.

At Line 6–7, logging full proxy/storage values can leak credentials or sensitive paths in CI logs. Redact or log presence-only metadata.

Suggested change
-  console.log('Proxy config:', JSON.stringify(config.projects[0].use.proxy, null, 2));
-  console.log('StorageState:', config.projects[0].use.storageState);
+  const hasProxy = Boolean(config.projects[0].use.proxy);
+  const hasStorageState = Boolean(config.projects[0].use.storageState);
+  console.log('Proxy configured:', hasProxy);
+  console.log('StorageState configured:', hasStorageState);
📝 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
console.log('Proxy config:', JSON.stringify(config.projects[0].use.proxy, null, 2));
console.log('StorageState:', config.projects[0].use.storageState);
const hasProxy = Boolean(config.projects[0].use.proxy);
const hasStorageState = Boolean(config.projects[0].use.storageState);
console.log('Proxy configured:', hasProxy);
console.log('StorageState configured:', hasStorageState);
🤖 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 `@playwright/debug-global-setup.ts` around lines 6 - 7, The current console.log
calls print raw sensitive values from config.projects[0].use.proxy and
config.projects[0].use.storageState; replace these with presence-only or
redacted summaries: for proxy, log only whether a proxy is configured and which
keys are present (e.g., list keys like "server", "username" but not their
values) or mask values (e.g., replace characters with asterisks), and for
storageState log only that a storage state file/string exists and its filename
or size (not contents). Update the debug output in debug-global-setup.ts to use
a small helper (e.g., logProxySummary or redact) to produce these summaries when
referencing config and config.projects[0].use.* so no secrets or full paths are
emitted to CI logs.

Comment thread playwright/debug-login.spec.ts Outdated
Comment on lines +3 to +32
test('debug login page', async ({ page }) => {
// Navigate to the base URL
await page.goto('/', { waitUntil: 'load', timeout: 60000 });

// Take a screenshot
await page.screenshot({ path: 'playwright/debug-login-page.png', fullPage: true });

// Get page title
const title = await page.title();
console.log('Page title:', title);

// Get page URL
console.log('Page URL:', page.url());

// Check for "Lockdown" text
const lockdownCount = await page.locator('text=Lockdown').count();
console.log('Lockdown text count:', lockdownCount);

// Check for login form
const loginFieldCount = await page.getByLabel('Red Hat login').count();
console.log('Login field count:', loginFieldCount);

// Get visible text on page
const bodyText = await page.locator('body').textContent();
console.log('First 500 chars of body:', bodyText?.substring(0, 500));

// Try to find any error messages
const errorText = await page.locator('text=/error|invalid|failed|denied/i').count();
console.log('Error-like text count:', errorText);
});
Copy link
Copy Markdown

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

This diagnostic spec always passes and can hide failures.

At Line 3, the test contains no assertions, so it cannot fail on broken login states. Mark it test.skip/test.fixme or add explicit pass/fail criteria.

🤖 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 `@playwright/debug-login.spec.ts` around lines 3 - 32, The test "debug login
page" currently contains only logging calls and no assertions, so it will always
pass; either mark it as skipped (test.skip/test.fixme) or add explicit
Playwright assertions to validate the critical login state (e.g., import expect
from '@playwright/test' and assert things like page.title() includes expected
text, page.getByLabel('Red Hat login').count() is > 0, or that specific
"Lockdown" or error counts are 0); update the test declaration test('debug login
page', ...) accordingly and replace the console checks with expect(...)
assertions (or change to test.skip/test.fixme if you intend to keep it
diagnostic).

Comment thread playwright/E2E_SETUP_NOTES.md Outdated
Comment on lines +227 to +236
const breadcrumb = page.locator('.pf-v6-c-breadcrumb__item button, .pf-v6-c-breadcrumb__item a');
const isVisible = await breadcrumb.isVisible();

if (isVisible) {
await breadcrumb.click();
await page.waitForTimeout(500);

// Verify we're back at home
await expect(page.getByRole('heading', { name: /tell us about your experience/i })).toBeVisible();
}
Copy link
Copy Markdown

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

Make breadcrumb-back test deterministic.

At Line 230, wrapping the breadcrumb click in if (isVisible) lets the test pass even if breadcrumb navigation is broken. Assert visibility first, then 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 `@playwright/help-panel-feedback-tab.spec.ts` around lines 227 - 236, The
breadcrumb-click step is non-deterministic because it only clicks when isVisible
is truthy; instead assert the breadcrumb is visible then perform the click to
fail fast if missing. Replace the conditional around breadcrumb/isVisible with
an explicit assertion using expect(breadcrumb).toBeVisible(), then call
breadcrumb.click(), waitForTimeout(500) and assert the heading via
expect(page.getByRole('heading', { name: /tell us about your experience/i
})).toBeVisible(); keep the same locators (breadcrumb = page.locator(...)) and
waits.

Comment on lines +69 to +102
test('toggles between All and bundle scope', async ({ page }) => {
// Wait for resources to load
await expect(page.locator('[data-ouia-component-id="help-panel-learning-resources-list"]')).toBeVisible({ timeout: 15000 });

// Check if we're on a bundle page (toggle should be visible)
const scopeToggle = page.locator('[data-ouia-component-id="help-panel-scope-toggle"]');
const isVisible = await scopeToggle.isVisible();

if (isVisible) {
// Get count when "All" is selected
const allToggle = page.locator('[data-ouia-component-id="help-panel-scope-toggle-all"]');
await allToggle.click();
await page.waitForTimeout(1000); // Wait for filter to apply

const toolbar = page.locator('[data-ouia-component-id="help-panel-learning-results-toolbar"]');
const allText = await toolbar.textContent();
const allMatch = allText?.match(/Learning resources \((\d+)\)/);
const allCount = allMatch ? parseInt(allMatch[1], 10) : 0;

// Switch to bundle scope
const bundleToggle = page.locator('[data-ouia-component-id="help-panel-scope-toggle-bundle"]');
await bundleToggle.click();
await page.waitForTimeout(1000); // Wait for filter to apply

// Verify count changed
await expect(async () => {
const bundleText = await toolbar.textContent();
const bundleMatch = bundleText?.match(/Learning resources \((\d+)\)/);
const bundleCount = bundleMatch ? parseInt(bundleMatch[1], 10) : 0;
// Bundle count should be less than or equal to all count
expect(bundleCount).toBeLessThanOrEqual(allCount);
}).toPass({ timeout: 5000 });
}
});
Copy link
Copy Markdown

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

toggles between All and bundle scope passes vacuously when the toggle is absent.

If scopeToggle is not visible, the entire test body is skipped but the test still reports as passing. This silently masks selector regressions — if the toggle's data-ouia-component-id is ever changed, the test will keep passing with zero assertions.

Use test.skip() to make the non-applicability explicit:

🛡️ Proposed fix
   const isVisible = await scopeToggle.isVisible();
 
-  if (isVisible) {
-    // ... all assertions
-  }
+  if (!isVisible) {
+    test.skip(true, 'Scope toggle not present on this page — skipping bundle scope test');
+    return;
+  }
+
+  // Get count when "All" is selected
+  const allToggle = page.locator('[data-ouia-component-id="help-panel-scope-toggle-all"]');
+  await allToggle.click();
+  // ... rest of assertions unconditionally
🤖 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 `@playwright/help-panel-learn-tab.spec.ts` around lines 69 - 102, The test
"toggles between All and bundle scope" currently silently passes when the scope
toggle is missing; change the early conditional so that when scopeToggle
(locator '[data-ouia-component-id="help-panel-scope-toggle"]') is not visible
you call test.skip('scope toggle not present - skipping bundle-specific
assertions') instead of simply returning, otherwise proceed with the existing
flow (use allToggle and bundleToggle locators and the toolbar check as before);
ensure you await scopeToggle.isVisible() then skip via test.skip to make
non-applicability explicit.

Comment on lines +203 to +229
test('toggles recommended content scope', async ({ page }) => {
// Check if we're on a bundle page (toggle should be visible)
const recommendedToggle = page.locator('[data-ouia-component-id="help-panel-recommended-scope-toggle"]');
const isVisible = await recommendedToggle.isVisible();

if (isVisible) {
// Wait for recommended content to load
await page.waitForTimeout(2000);

// Click "All" toggle
const allToggle = page.locator('[data-ouia-component-id="help-panel-recommended-scope-toggle-all"]');
await allToggle.click();
await page.waitForTimeout(1000);

// Verify recommended content updates (at least one item should be visible)
const recommendedItems = page.locator('[aria-label="Recommended content"] .pf-v6-c-data-list__item');
await expect(recommendedItems.first()).toBeVisible({ timeout: 10000 });

// Switch to bundle scope
const bundleToggle = page.locator('[data-ouia-component-id="help-panel-recommended-scope-toggle-bundle"]');
await bundleToggle.click();
await page.waitForTimeout(1000);

// Recommended content should still be visible (may be different items)
await expect(recommendedItems.first()).toBeVisible({ timeout: 10000 });
}
});
Copy link
Copy Markdown

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

toggles recommended content scope has the same vacuous-pass pattern as the Learn tab toggle test.

When recommendedToggle is not visible the test passes with zero assertions, silently masking selector regressions. Apply the same test.skip(true, ...) guard used in the suggested fix for the Learn tab.

🤖 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 `@playwright/help-panel-search-tab.spec.ts` around lines 203 - 229, The test
"toggles recommended content scope" currently returns a vacuous pass when the
recommendedToggle locator isn't visible; after awaiting
recommendedToggle.isVisible() call test.skip(true, 'recommended scope toggle not
present on this page') (or test.skip(!isVisible, ...)) and return so the test is
reported as skipped instead of passing; update the block around the
recommendedToggle locator
(data-ouia-component-id="help-panel-recommended-scope-toggle") to perform the
visibility await, call test.skip if false, and only proceed with the existing
clicks/assertions (allToggle, bundleToggle, recommendedItems) when visible.

Comment on lines +231 to +276
test('bookmarks a learning resource from search results', async ({ page }) => {
// Search for quickstarts
const searchInput = page.getByPlaceholder(/Search for topics, products, use cases/i);
await searchInput.fill('Getting started');
await page.waitForTimeout(SEARCH_DEBOUNCE_MS + 500);

// Wait for results
await expect(page.getByRole('list', { name: /search results/i })).toBeVisible({ timeout: 15000 });

// Find first bookmark button
const bookmarkButton = page.locator('[aria-label*="bookmark"]').first();
const isVisible = await bookmarkButton.isVisible();

if (isVisible) {
await bookmarkButton.click();
// Wait for API call to complete
await page.waitForTimeout(2000);

// Bookmark should toggle (icon changes)
// This is a basic test - more thorough validation would check icon state
}
});

test('favorites a service from search results', async ({ page }) => {
// Search for services
const searchInput = page.getByPlaceholder(/Search for topics, products, use cases/i);
await searchInput.fill('Advisor');
await page.waitForTimeout(SEARCH_DEBOUNCE_MS + 500);

// Wait for results
const searchResults = page.getByRole('list', { name: /search results/i });
await expect(searchResults).toBeVisible({ timeout: 15000 });

// Look for a star/favorite button (service results have these)
const favoriteButton = page.locator('button[aria-label*="favorite"], button[aria-label*="star"]').first();
const isVisible = await favoriteButton.isVisible();

if (isVisible) {
await favoriteButton.click();
// Wait for API call to complete
await page.waitForTimeout(2000);

// Favorite should toggle (icon changes)
// This is a basic test - more thorough validation would check icon state
}
});
Copy link
Copy Markdown

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

bookmarks a learning resource and favorites a service have zero assertions — mark them test.skip until assertions are added.

Both tests locate a button, click it if visible, then waitForTimeout with no assertion about the resulting state. The comment in each test acknowledges this ("more thorough validation would check icon state"). As written, both tests always pass regardless of whether bookmarking/favoriting actually works. Until proper assertions are in place, leaving them active creates false confidence.

🛡️ Proposed fix — explicitly skip until assertions are implemented
-  test('bookmarks a learning resource from search results', async ({ page }) => {
+  test.skip('bookmarks a learning resource from search results', async ({ page }) => {
+    // TODO: Add assertion that bookmark icon toggles state after click
-  test('favorites a service from search results', async ({ page }) => {
+  test.skip('favorites a service from search results', async ({ page }) => {
+    // TODO: Add assertion that favorite icon toggles state after 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 `@playwright/help-panel-search-tab.spec.ts` around lines 231 - 276, The two
Playwright tests "bookmarks a learning resource from search results" and
"favorites a service from search results" contain no assertions and therefore
should be skipped until proper assertions are added; update the test
declarations for those cases (the test(...) calls with those exact titles) to
test.skip(...) so they are not executed and give false confidence, and add a
TODO comment referencing the need to assert the post-click state (e.g.,
icon/state change or API response) before re-enabling them.

Comment on lines +24 to +26
// Wait for support panel to load (either empty state or table)
await page.waitForTimeout(2000);
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

In Playwright Test best practices, is page.waitForTimeout() discouraged for production E2E tests, and what are recommended alternatives for waiting on UI readiness?

💡 Result:

Yes, page.waitForTimeout is discouraged for production E2E tests in Playwright Test best practices. Official API documentation notes it should not be used for testing, recommending reliance on web assertions instead [1]. Multiple sources emphasize avoiding hard waits like waitForTimeout because they lead to brittle, slow, and flaky tests [2][3][4][5]. Playwright's design promotes auto-waiting and retrying, making fixed timeouts unnecessary [6][7][8].

Citations:


🏁 Script executed:

# First, find the spec file
find . -name "help-panel-support-tab.spec.ts" -type f

Repository: RedHatInsights/learning-resources

Length of output: 121


🏁 Script executed:

# If file exists, check its content around lines 24-26
if [ -f "playwright/help-panel-support-tab.spec.ts" ]; then
  sed -n '20,30p' "playwright/help-panel-support-tab.spec.ts"
fi

Repository: RedHatInsights/learning-resources

Length of output: 510


🏁 Script executed:

# Search for the data-ouia-component-id selectors mentioned in the suggested fix
rg "data-ouia-component-id.*help-panel-support" --type ts --type tsx --type js --type jsx

Repository: RedHatInsights/learning-resources

Length of output: 105


🏁 Script executed:

# Search for waitForTimeout usage in the test file to understand context
rg "waitForTimeout" playwright/ --type ts --type tsx

Repository: RedHatInsights/learning-resources

Length of output: 105


🏁 Script executed:

# Read the full context of the spec file around the issue
cat -n "playwright/help-panel-support-tab.spec.ts" | head -40

Repository: RedHatInsights/learning-resources

Length of output: 1968


🏁 Script executed:

# Search for the specific data-ouia-component-id values mentioned in the fix
rg "help-panel-support-empty-state|help-panel-support-cases-table"

Repository: RedHatInsights/learning-resources

Length of output: 2889


🏁 Script executed:

# Also search for any component files that might contain these OUIA IDs
rg "help-panel-support" --type ts

Repository: RedHatInsights/learning-resources

Length of output: 3387


Replace fixed sleep with state-based wait to reduce flakiness.

The waitForTimeout(2000) at line 25 makes this suite timing-sensitive across environments. Replace with a state-based wait for the support panel UI elements. The subsequent test in this same file (lines 28-38) already demonstrates the correct pattern using expect(async () => { ... }).toPass() with the same selectors.

Suggested change
-    // Wait for support panel to load (either empty state or table)
-    await page.waitForTimeout(2000);
+    // Wait for support panel to load (either empty state or table)
+    await expect(async () => {
+      const emptyState = page.locator('[data-ouia-component-id="help-panel-support-empty-state"]');
+      const casesTable = page.locator('[data-ouia-component-id="help-panel-support-cases-table"]');
+      expect((await emptyState.isVisible()) || (await casesTable.isVisible())).toBe(true);
+    }).toPass({ timeout: 15000 });
📝 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
// Wait for support panel to load (either empty state or table)
await page.waitForTimeout(2000);
});
// Wait for support panel to load (either empty state or table)
await expect(async () => {
const emptyState = page.locator('[data-ouia-component-id="help-panel-support-empty-state"]');
const casesTable = page.locator('[data-ouia-component-id="help-panel-support-cases-table"]');
expect((await emptyState.isVisible()) || (await casesTable.isVisible())).toBe(true);
}).toPass({ timeout: 15000 });
});
🤖 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 `@playwright/help-panel-support-tab.spec.ts` around lines 24 - 26, Replace the
fixed sleep (await page.waitForTimeout(2000)) with a state-based wait that
checks the support panel UI using the same selectors and pattern used in the
following test: remove the page.waitForTimeout call and wrap a check for the
panel elements (the exact locators used in the next test) in an expect(async ()
=> { /* await page.locator(...).isVisible()/count()/textContent() */ }).toPass()
so the test waits for either the empty-state or table to appear instead of
relying on a timeout.

Comment on lines +41 to +78
test('displays empty state when no support cases exist', async ({ page }) => {
// Check if empty state is displayed
const emptyState = page.locator('[data-ouia-component-id="help-panel-support-empty-state"]');
const isEmptyVisible = await emptyState.isVisible();

if (isEmptyVisible) {
// Verify empty state title
await expect(page.getByRole('heading', { name: /no open support cases/i })).toBeVisible();

// Verify "Open a support case" button exists
const openCaseButton = page.getByRole('button', { name: /open a support case/i });
await expect(openCaseButton).toBeVisible();
}
});

test('opens Customer Portal when clicking "Open a support case"', async ({ page }) => {
// Check if empty state is displayed
const emptyState = page.locator('[data-ouia-component-id="help-panel-support-empty-state"]');
const isEmptyVisible = await emptyState.isVisible();

if (isEmptyVisible) {
// Set up listener for new page/tab
const popupPromise = page.waitForEvent('popup', { timeout: 10000 });

// Click "Open a support case" button
const openCaseButton = page.getByRole('button', { name: /open a support case/i });
await openCaseButton.click();

// Verify new tab opened to Customer Portal
const popup = await popupPromise;
await expect(popup).toBeTruthy();

const url = popup.url();
expect(url).toMatch(/access\.redhat\.com\/support\/cases/);

await popup.close();
}
});
Copy link
Copy Markdown

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

Avoid vacuous passes in conditional support-tab tests.

At Line 46 and Line 61, these tests can succeed without validating their intent when the expected state is not present (if (...) { ... } with no else assertion). This produces false positives.

Suggested pattern
-    if (isEmptyVisible) {
-      // assertions...
-    }
+    expect(isEmptyVisible, 'Expected Support empty state for this scenario').toBe(true);
+    // assertions...
📝 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
test('displays empty state when no support cases exist', async ({ page }) => {
// Check if empty state is displayed
const emptyState = page.locator('[data-ouia-component-id="help-panel-support-empty-state"]');
const isEmptyVisible = await emptyState.isVisible();
if (isEmptyVisible) {
// Verify empty state title
await expect(page.getByRole('heading', { name: /no open support cases/i })).toBeVisible();
// Verify "Open a support case" button exists
const openCaseButton = page.getByRole('button', { name: /open a support case/i });
await expect(openCaseButton).toBeVisible();
}
});
test('opens Customer Portal when clicking "Open a support case"', async ({ page }) => {
// Check if empty state is displayed
const emptyState = page.locator('[data-ouia-component-id="help-panel-support-empty-state"]');
const isEmptyVisible = await emptyState.isVisible();
if (isEmptyVisible) {
// Set up listener for new page/tab
const popupPromise = page.waitForEvent('popup', { timeout: 10000 });
// Click "Open a support case" button
const openCaseButton = page.getByRole('button', { name: /open a support case/i });
await openCaseButton.click();
// Verify new tab opened to Customer Portal
const popup = await popupPromise;
await expect(popup).toBeTruthy();
const url = popup.url();
expect(url).toMatch(/access\.redhat\.com\/support\/cases/);
await popup.close();
}
});
test('displays empty state when no support cases exist', async ({ page }) => {
// Check if empty state is displayed
const emptyState = page.locator('[data-ouia-component-id="help-panel-support-empty-state"]');
const isEmptyVisible = await emptyState.isVisible();
expect(isEmptyVisible, 'Expected Support empty state for this scenario').toBe(true);
// Verify empty state title
await expect(page.getByRole('heading', { name: /no open support cases/i })).toBeVisible();
// Verify "Open a support case" button exists
const openCaseButton = page.getByRole('button', { name: /open a support case/i });
await expect(openCaseButton).toBeVisible();
});
test('opens Customer Portal when clicking "Open a support case"', async ({ page }) => {
// Check if empty state is displayed
const emptyState = page.locator('[data-ouia-component-id="help-panel-support-empty-state"]');
const isEmptyVisible = await emptyState.isVisible();
expect(isEmptyVisible, 'Expected Support empty state for this scenario').toBe(true);
// Set up listener for new page/tab
const popupPromise = page.waitForEvent('popup', { timeout: 10000 });
// Click "Open a support case" button
const openCaseButton = page.getByRole('button', { name: /open a support case/i });
await openCaseButton.click();
// Verify new tab opened to Customer Portal
const popup = await popupPromise;
await expect(popup).toBeTruthy();
const url = popup.url();
expect(url).toMatch(/access\.redhat\.com\/support\/cases/);
await popup.close();
});
🤖 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 `@playwright/help-panel-support-tab.spec.ts` around lines 41 - 78, Both tests
("displays empty state when no support cases exist" and "opens Customer Portal
when clicking \"Open a support case\"") currently short-circuit with if
(isEmptyVisible) { ... } causing vacuous passes; replace the conditional checks
on the emptyState locator with explicit assertions (e.g., await
expect(emptyState).toBeVisible() or expect(isEmptyVisible).toBeTruthy()) so the
test fails when the empty state is not present, and then proceed to interact
with openCaseButton and popupPromise (or convert the check into a clear
precondition using test.skip/test.fail with a message) to ensure those branches
are validated rather than silently skipped.

Comment thread playwright/README.md Outdated
Add Playwright E2E tests for all Help Panel tabs:
- Learn tab: API integration, filtering, bookmarking (7 tests)
- API tab: Bundle filtering, external links (5 tests)
- Search tab: Search API, filters, recent queries (11 tests)
- Support tab: Support cases API, empty states (8 tests)
- Feedback tab: Forms, validation, bug reporting (12 tests)

Total: 43 E2E tests with real stage API integration

Test infrastructure:
- Custom global setup with proxy support for local testing
- Comprehensive documentation in playwright/README.md
- Troubleshooting guide in playwright/E2E_SETUP_NOTES.md

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@jjaquish jjaquish force-pushed the jjaquish/RHCLOUD-45255 branch from d0c580b to 799bfa2 Compare May 7, 2026 16:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants