test: added extensive help panel tests#307
test: added extensive help panel tests#307jjaquish wants to merge 1 commit intoRedHatInsights:masterfrom
Conversation
|
Warning Rate limit exceeded
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 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 configurationConfiguration used: Organization UI Review profile: CHILL Plan: Enterprise Run ID: 📒 Files selected for processing (12)
WalkthroughThis 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 ChangesHelp Panel E2E Test Suite
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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.
Built for teams:
One agent for your entire SDLC. Right inside Slack. 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. Comment |
apinkert
left a comment
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Actionable comments posted: 10
🧹 Nitpick comments (1)
playwright/global-setup-with-proxy.ts (1)
7-17: ⚡ Quick winReplace
anywith proper Playwright types.
disableCookiePromptandloginuseanyfor all Playwright-specific parameters, losing type safety in test infrastructure.Page,Route, andRequestare 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
📒 Files selected for processing (16)
playwright.config.tsplaywright.debug-global.config.tsplaywright/E2E_SETUP_NOTES.mdplaywright/HELP_PANEL_E2E_TESTS.mdplaywright/README.mdplaywright/all-learning-resources.spec.tsplaywright/debug-global-setup.tsplaywright/debug-login.spec.tsplaywright/global-setup-with-proxy.tsplaywright/help-panel-api-tab.spec.tsplaywright/help-panel-feedback-tab.spec.tsplaywright/help-panel-learn-tab.spec.tsplaywright/help-panel-search-tab.spec.tsplaywright/help-panel-support-tab.spec.tsplaywright/help-panel.spec.tsplaywright/playwright.debug.config.ts
| console.log('Proxy config:', JSON.stringify(config.projects[0].use.proxy, null, 2)); | ||
| console.log('StorageState:', config.projects[0].use.storageState); |
There was a problem hiding this comment.
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.
| 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.
| 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); | ||
| }); |
There was a problem hiding this comment.
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).
| 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(); | ||
| } |
There was a problem hiding this comment.
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.
| 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 }); | ||
| } | ||
| }); |
There was a problem hiding this comment.
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.
| 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 }); | ||
| } | ||
| }); |
There was a problem hiding this comment.
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.
| 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 | ||
| } | ||
| }); |
There was a problem hiding this comment.
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.
| // Wait for support panel to load (either empty state or table) | ||
| await page.waitForTimeout(2000); | ||
| }); |
There was a problem hiding this comment.
🧩 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:
- 1: https://github.com/microsoft/playwright/blob/main/docs/src/api/params.md
- 2: https://www.checklyhq.com/blog/never-use-page-waitfortimeout/
- 3: https://runebook.dev/en/docs/playwright/api/class-frame/frame-wait-for-timeout
- 4: https://checklyhq.com/docs/learn/playwright/waits-and-timeouts
- 5: https://circleci.com/blog/mastering-waits-and-timeouts-in-playwright/
- 6: https://playwright.dev/docs/best-practices
- 7: https://playwright.dev/docs/next/writing-tests
- 8: https://playwright.dev/docs/actionability
🏁 Script executed:
# First, find the spec file
find . -name "help-panel-support-tab.spec.ts" -type fRepository: 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"
fiRepository: 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 jsxRepository: 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 tsxRepository: 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 -40Repository: 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 tsRepository: 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.
| // 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.
| 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(); | ||
| } | ||
| }); |
There was a problem hiding this comment.
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.
| 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.
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>
d0c580b to
799bfa2
Compare
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 --uito 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
Anything reviewers should know?
Checklist
AI disclosure