[CSM Portal] add e2e coverage for cases, operations, engagements, content, security-center - #1241
Conversation
…tent, security-center Adds Playwright e2e tests (14 page objects, ~48 tests) across cases (list/create/detail lifecycle), operations (service requests, problems, incident & change-request detail), engagements, announcements, updates, recent-nav, security-center, and a time-card reject flow — extending the existing timecards/operations suite. - Runs against the real backend; specs self-provision and [E2E]-tag any records they create and never mutate pre-existing records. Data-dependent specs self-skip (rather than fail) when the tenant lacks the needed data. - Provisioning targets a configurable test project via E2E_PROJECT (default "Customer Portal Project") so the create cascade has usable deployment/product/catalog data. - Selector robustness: option pickers are scoped to the open listbox (so the rich-text editor's native <select> options can't be matched); required MUI fields are matched by role with a required-marker-tolerant name; detail pages retry-until-rendered to tolerate backend read latency. - The auth fixture also replays IdP-domain cookies so the SDK's silent token refresh can carry a long run. - Upgrades @playwright/test 1.49.0 -> 1.62.0 so the suite runs on Node 24 without a type-stripping workaround.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (8)
🚧 Files skipped from review as they are similar to previous changes (7)
📝 WalkthroughWalkthroughThe PR expands CSM Portal Playwright coverage with shared session and selector utilities, reusable page objects, and E2E scenarios for cases, operations, content pages, security workflows, recent navigation, and time cards. It also upgrades Playwright and strengthens several MUI locator and navigation assertions. ChangesCSM portal E2E coverage
Estimated code review effort: 5 (Critical) | ~120 minutes Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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 |
|
@coderabbitai resume |
✅ Action performedReviews resumed. |
|
@coderabbitai resume |
✅ Action performedReviews resumed. |
There was a problem hiding this comment.
Actionable comments posted: 10
🧹 Nitpick comments (5)
apps/csm-portal/webapp/tests/e2e/specs/timecards/reject.spec.ts (1)
42-117: 📐 Maintainability & Code Quality | 🔵 TrivialSignificant duplication with
approvals.spec.ts's card-provisioning setup.Lines 42-117 (session skip check, cross-context card creation, case-number capture) closely mirror
approvals.spec.ts's equivalent block. Consider extracting a shared helper (similar in spirit tologTimeOnFirstCaseinmy-sheets.spec.ts) that both specs can call, reducing drift risk between the two nearly-identical flows.🤖 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 `@apps/csm-portal/webapp/tests/e2e/specs/timecards/reject.spec.ts` around lines 42 - 117, Extract the repeated engineer-session setup and cross-context time-card creation flow from the reject test and approvals.spec.ts into a shared helper, modeled after logTimeOnFirstCase. The helper should handle the session/case availability skips, create the card with the approver query, return the created case number, and close the engineer context; update both specs to call it while preserving their existing test-specific assertions.apps/csm-portal/webapp/tests/e2e/specs/security-center/security-center.spec.ts (1)
119-143: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDuplicates the "discover-first-option" logic already extracted in
service-request.spec.ts.This inline open/wait/read/close/reselect sequence for Deployment and Deployed product duplicates the
pickFirstOptionpattern already factored out inapps/csm-portal/webapp/tests/e2e/specs/operations/service-request.spec.ts. Consider extracting a shared test utility both specs can import.♻️ Sketch of a shared helper
// e.g. tests/e2e/utils/pickers.ts export async function pickFirstOptionLabel(page: Page, label: string): Promise<string | null> { const escaped = label.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); await page.getByRole("combobox", { name: new RegExp(`^${escaped}\\s*\\*?$`) }).click(); const option = page.getByRole("listbox").getByRole("option").first(); const appeared = await option.waitFor({ state: "visible", timeout: 8_000 }).then(() => true).catch(() => false); if (!appeared) { await page.keyboard.press("Escape"); return null; } const text = (await option.textContent())?.trim() || null; await option.click(); return text; }🤖 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 `@apps/csm-portal/webapp/tests/e2e/specs/security-center/security-center.spec.ts` around lines 119 - 143, Extract the repeated first-option discovery flow into a shared picker utility, such as pickFirstOptionLabel, and update both security-center.spec.ts and service-request.spec.ts to use it. The helper should open the named combobox, wait for the first option, return its trimmed label or null when unavailable, and close or select the option consistently; preserve each test’s existing skip behavior and subsequent Deployment/Deployed product selection requirements.apps/csm-portal/webapp/tests/e2e/specs/cases/list.spec.ts (1)
161-172: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winWrap saved-view cleanup in try/finally.
If the visibility assertion (line 168) throws,
deleteSavedView(line 170) never runs, leaving the saved view behind for future runs.detail-lifecycle.spec.ts's attachment test usesfinallyfor the analogous cleanup — worth mirroring here.♻️ Proposed fix
const viewName = `e2e-list-spec-${Date.now()}`; - await cases.saveCurrentView(viewName); - - await cases.openSavedViewsMenu(); - await expect(page.getByRole("menuitem", { name: viewName, exact: false })).toBeVisible(); - - await cases.deleteSavedView(viewName); - await expect(page.getByRole("menuitem", { name: viewName, exact: false })).toHaveCount(0); + await cases.saveCurrentView(viewName); + + try { + await cases.openSavedViewsMenu(); + await expect(page.getByRole("menuitem", { name: viewName, exact: false })).toBeVisible(); + } finally { + await cases.deleteSavedView(viewName); + } + await expect(page.getByRole("menuitem", { name: viewName, exact: false })).toHaveCount(0);🤖 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 `@apps/csm-portal/webapp/tests/e2e/specs/cases/list.spec.ts` around lines 161 - 172, Wrap the saved-view assertion and deletion flow in the affected test with a try/finally, ensuring deleteSavedView(viewName) runs even when the visibility assertion fails. Keep the existing openSavedViewsMenu and final absence assertion behavior while anchoring the cleanup to CasesListPage.deleteSavedView.apps/csm-portal/webapp/tests/e2e/pages/ProblemCreatePage.ts (1)
57-77: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDuplicate async-pick logic — consolidate like
IncidentDetailPage.pickAsyncField.
pickOriginCaseandpickPrimaryIncidentare identical apart from the field-name regex.IncidentDetailPage.tsin this same PR already generalizes this exact pattern (pickAsyncField(label, query)); reuse the same approach here.♻️ Proposed consolidation
- async pickOriginCase(query: string): Promise<void> { - const input = this.page.getByRole("combobox", { name: /^Origin case\s*\*?$/ }); - await input.click(); - await input.fill(query); - const option = this.page.getByRole("listbox").getByRole("option").first(); - await option.waitFor({ state: "visible", timeout: 10_000 }); - await option.click(); - } - - /** Types into the "Primary incident" async search-and-pick field and picks - * the first result, scoped to the open listbox — see `selectOption` above. */ - async pickPrimaryIncident(query: string): Promise<void> { - const input = this.page.getByRole("combobox", { name: /^Primary incident\s*\*?$/ }); - await input.click(); - await input.fill(query); - const option = this.page.getByRole("listbox").getByRole("option").first(); - await option.waitFor({ state: "visible", timeout: 10_000 }); - await option.click(); - } + /** Async search-and-pick field ("Origin case" / "Primary incident") — + * types `query` and picks the first result, scoped to the open listbox + * — see `selectOption` above. */ + private async pickAsyncField(label: string, query: string): Promise<void> { + const escaped = label.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); + const input = this.page.getByRole("combobox", { name: new RegExp(`^${escaped}\\s*\\*?$`) }); + await input.click(); + await input.fill(query); + const option = this.page.getByRole("listbox").getByRole("option").first(); + await option.waitFor({ state: "visible", timeout: 10_000 }); + await option.click(); + } + + async pickOriginCase(query: string): Promise<void> { + await this.pickAsyncField("Origin case", query); + } + + async pickPrimaryIncident(query: string): Promise<void> { + await this.pickAsyncField("Primary incident", query); + }🤖 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 `@apps/csm-portal/webapp/tests/e2e/pages/ProblemCreatePage.ts` around lines 57 - 77, Consolidate the duplicated logic in ProblemCreatePage’s pickOriginCase and pickPrimaryIncident by adding a shared pickAsyncField(label, query) helper, following IncidentDetailPage.pickAsyncField. Have both field-specific methods delegate to the helper with their respective labels, preserving the existing listbox scoping, visibility wait, and first-option selection.apps/csm-portal/webapp/tests/e2e/specs/cases/create.spec.ts (1)
39-144: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDuplicated deployment/product-selection boilerplate across both tests.
The "page structure" and "happy path" tests repeat the same deployment-field/product-field discovery logic almost verbatim (Lines 58-77 vs 109-126). Extracting a shared helper (e.g.
pickDeploymentAndProduct(page)returning success/failure) would remove the duplication and mean theisVisible/waitForfix above only needs to live in one place.🤖 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 `@apps/csm-portal/webapp/tests/e2e/specs/cases/create.spec.ts` around lines 39 - 144, Extract the duplicated deployment and deployed-product selection flow from both tests into a shared helper, such as pickDeploymentAndProduct(page), that handles visibility, option availability, waits, selections, and reports whether setup succeeded. Replace the inline blocks in the page-structure and happy-path tests with this helper while preserving each test’s existing skip messages and cloud-project behavior.
🤖 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 `@apps/csm-portal/webapp/tests/e2e/fixtures/test.ts`:
- Around line 58-62: Wrap the context.addCookies call in applySession’s cookie
replay path with try/catch so malformed bundle.cookies do not abort session
setup or withRole’s beforeEach. Preserve the existing best-effort behavior and
follow the storage replay’s handling pattern, including appropriate error
reporting if available.
In `@apps/csm-portal/webapp/tests/e2e/pages/CaseCreatePage.ts`:
- Around line 109-139: Update the final URL assertion in
CaseCreatePage.fillRequiredFieldsAndSubmit to exclude the literal “new” segment
with a negative lookahead, matching ChangeRequestCreatePage.fillSubjectAndSubmit
and IncidentCreatePage.fillRequiredFieldsAndSubmit. Keep the existing timeout
and require navigation to a concrete case URL after submission.
In `@apps/csm-portal/webapp/tests/e2e/pages/CasesListPage.ts`:
- Around line 190-195: Update applySavedView to use an exact accessible-name
match when locating the menuitem, aligning the selector with the method’s
documented exact-name behavior and preventing substring matches against other
menu entries.
In `@apps/csm-portal/webapp/tests/e2e/pages/RecentNav.ts`:
- Around line 107-109: Update the quickNavResult method to locate labels within
the open QuickNav palette’s result container before applying .first(). Use the
existing palette/result-container locator symbol or selector so duplicate
sidebar or page text cannot be selected, while preserving support for page-name
results.
- Around line 135-137: Escape the dynamic title before constructing regex
locators in RecentNav.recentViewRow and the pin-toggle locator, then reuse that
escaped value in both patterns. Update
apps/csm-portal/webapp/tests/e2e/pages/RecentNav.ts lines 135-137 and 146-149;
preserve the existing matching behavior while treating regex metacharacters in
titles literally.
In `@apps/csm-portal/webapp/tests/e2e/specs/cases/create.spec.ts`:
- Around line 61-76: Replace all four option-availability checks in the create
tests, including the deployment and product checks in the shown flow and both
checks in the happy-path test, with polling visibility waits such as
Locator.waitFor({ state: "visible" }) or the established expect.poll pattern
from provisionCase in detail-lifecycle.spec.ts. Preserve the existing test.skip
behavior and messages, but ensure asynchronous option loading is given time to
complete before determining availability.
In `@apps/csm-portal/webapp/tests/e2e/specs/cases/detail-lifecycle.spec.ts`:
- Around line 112-120: Replace the single-shot createButton().isEnabled() check
in provisionCase with the file’s existing expect.poll pattern, waiting until the
Create button becomes enabled before clicking it. Do not return undefined on a
transient disabled read; preserve failures as test failures so dependent
lifecycle tests are not silently skipped.
In `@apps/csm-portal/webapp/tests/e2e/specs/content/updates.spec.ts`:
- Around line 108-120: Move the search-and-wait JSDoc block from above
catalogFailedToLoad to directly above runFirstAvailableSearch. Keep the
catalogFailedToLoad documentation immediately above its declaration so each
function is documented by the correct comment.
In `@apps/csm-portal/webapp/tests/e2e/specs/shell/recent.spec.ts`:
- Around line 96-106: Update the recent-entry lookup around
readStoredRecentViews and caseEntry so it selects the case whose href matches
the current detail-page URL, rather than the first entry with kind "case".
Preserve the existing shortLabel extraction and null handling after selecting
the matching entry, and ensure the URL comparison uses the current page location
consistently.
In `@apps/csm-portal/webapp/tests/e2e/specs/timecards/reject.spec.ts`:
- Around line 107-117: The reject test currently calls filterWorkItem before
confirming the newly created card is present in the reloaded Approvals data.
Update the initial Approvals load around TimeCardsPage.goto, openApprovals, and
cardRow to use the existing reload-retry pattern and verify the card identity
first; only call filterWorkItem after the row is confirmed available, while
preserving the skip behavior when propagation fails.
---
Nitpick comments:
In `@apps/csm-portal/webapp/tests/e2e/pages/ProblemCreatePage.ts`:
- Around line 57-77: Consolidate the duplicated logic in ProblemCreatePage’s
pickOriginCase and pickPrimaryIncident by adding a shared pickAsyncField(label,
query) helper, following IncidentDetailPage.pickAsyncField. Have both
field-specific methods delegate to the helper with their respective labels,
preserving the existing listbox scoping, visibility wait, and first-option
selection.
In `@apps/csm-portal/webapp/tests/e2e/specs/cases/create.spec.ts`:
- Around line 39-144: Extract the duplicated deployment and deployed-product
selection flow from both tests into a shared helper, such as
pickDeploymentAndProduct(page), that handles visibility, option availability,
waits, selections, and reports whether setup succeeded. Replace the inline
blocks in the page-structure and happy-path tests with this helper while
preserving each test’s existing skip messages and cloud-project behavior.
In `@apps/csm-portal/webapp/tests/e2e/specs/cases/list.spec.ts`:
- Around line 161-172: Wrap the saved-view assertion and deletion flow in the
affected test with a try/finally, ensuring deleteSavedView(viewName) runs even
when the visibility assertion fails. Keep the existing openSavedViewsMenu and
final absence assertion behavior while anchoring the cleanup to
CasesListPage.deleteSavedView.
In
`@apps/csm-portal/webapp/tests/e2e/specs/security-center/security-center.spec.ts`:
- Around line 119-143: Extract the repeated first-option discovery flow into a
shared picker utility, such as pickFirstOptionLabel, and update both
security-center.spec.ts and service-request.spec.ts to use it. The helper should
open the named combobox, wait for the first option, return its trimmed label or
null when unavailable, and close or select the option consistently; preserve
each test’s existing skip behavior and subsequent Deployment/Deployed product
selection requirements.
In `@apps/csm-portal/webapp/tests/e2e/specs/timecards/reject.spec.ts`:
- Around line 42-117: Extract the repeated engineer-session setup and
cross-context time-card creation flow from the reject test and approvals.spec.ts
into a shared helper, modeled after logTimeOnFirstCase. The helper should handle
the session/case availability skips, create the card with the approver query,
return the created case number, and close the engineer context; update both
specs to call it while preserving their existing test-specific assertions.
🪄 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: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 8d9b94c3-9b1b-431e-a3b5-6c5261525b94
⛔ Files ignored due to path filters (1)
apps/csm-portal/webapp/pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (36)
apps/csm-portal/webapp/package.jsonapps/csm-portal/webapp/tests/e2e/fixtures/test.tsapps/csm-portal/webapp/tests/e2e/pages/AnnouncementsPage.tsapps/csm-portal/webapp/tests/e2e/pages/CaseCreatePage.tsapps/csm-portal/webapp/tests/e2e/pages/CaseDetailPage.tsapps/csm-portal/webapp/tests/e2e/pages/CasesListPage.tsapps/csm-portal/webapp/tests/e2e/pages/ChangeRequestCreatePage.tsapps/csm-portal/webapp/tests/e2e/pages/ChangeRequestDetailPage.tsapps/csm-portal/webapp/tests/e2e/pages/CreateSecurityReportPage.tsapps/csm-portal/webapp/tests/e2e/pages/IncidentCreatePage.tsapps/csm-portal/webapp/tests/e2e/pages/IncidentDetailPage.tsapps/csm-portal/webapp/tests/e2e/pages/LogTimeDialog.tsapps/csm-portal/webapp/tests/e2e/pages/ProblemCreatePage.tsapps/csm-portal/webapp/tests/e2e/pages/ProblemDetailPage.tsapps/csm-portal/webapp/tests/e2e/pages/ProblemsListPage.tsapps/csm-portal/webapp/tests/e2e/pages/RecentNav.tsapps/csm-portal/webapp/tests/e2e/pages/SecurityCenterPage.tsapps/csm-portal/webapp/tests/e2e/pages/ServiceRequestCreatePage.tsapps/csm-portal/webapp/tests/e2e/pages/TimeCardsPage.tsapps/csm-portal/webapp/tests/e2e/pages/UpdatesPage.tsapps/csm-portal/webapp/tests/e2e/specs/cases/create.spec.tsapps/csm-portal/webapp/tests/e2e/specs/cases/detail-lifecycle.spec.tsapps/csm-portal/webapp/tests/e2e/specs/cases/list.spec.tsapps/csm-portal/webapp/tests/e2e/specs/content/announcements.spec.tsapps/csm-portal/webapp/tests/e2e/specs/content/updates.spec.tsapps/csm-portal/webapp/tests/e2e/specs/engagements/engagements.spec.tsapps/csm-portal/webapp/tests/e2e/specs/operations/change-request-detail.spec.tsapps/csm-portal/webapp/tests/e2e/specs/operations/incident-detail.spec.tsapps/csm-portal/webapp/tests/e2e/specs/operations/problem.spec.tsapps/csm-portal/webapp/tests/e2e/specs/operations/service-request.spec.tsapps/csm-portal/webapp/tests/e2e/specs/security-center/security-center.spec.tsapps/csm-portal/webapp/tests/e2e/specs/shell/recent.spec.tsapps/csm-portal/webapp/tests/e2e/specs/timecards/approvals.spec.tsapps/csm-portal/webapp/tests/e2e/specs/timecards/my-sheets.spec.tsapps/csm-portal/webapp/tests/e2e/specs/timecards/reject.spec.tsapps/csm-portal/webapp/tests/e2e/utils/selectors.ts
- fixtures: make the IdP-cookie replay genuinely best-effort (try/catch) so a malformed bundle can't abort a role's beforeEach. - CaseCreatePage: post-submit URL assertion excludes the literal /cases/new so it can't pass on the pre-submit route. - CasesListPage: apply saved view by exact name (avoid strict-mode collision). - RecentNav: scope QuickNav result lookup to the open palette; regex-escape the recent-view title. - cases/create + detail-lifecycle: poll for option visibility / Create-enabled instead of single-shot isVisible/isEnabled. - content/updates: attach the runFirstAvailableSearch JSDoc to the right fn. - shell/recent: match the recorded recent-view entry by href to the case just opened, not the first stored case.
|
@coderabbitai resume |
✅ Action performedReviews resumed. |
|
@coderabbitai resume |
✅ Action performedReviews resumed. |
|
@coderabbitai resume |
✅ Action performedReviews resumed. |
|
@coderabbitai resume |
✅ Action performedReviews resumed. |
Purpose
The CSM portal webapp had end-to-end (Playwright) coverage only for time cards and two operations create-flows. This adds e2e coverage across the core CS-engineer workflows so regressions in the day-to-day flows are caught automatically.
Goals
Approach
tests/e2e/pages; specs grouped by feature undertests/e2e/specs/<feature>.[E2E]-tags any record it creates and never mutates a pre-existing record; a record's own create/read is used to verify.E2E_PROJECT(defaultCustomer Portal Project) so the create cascade has usable deployment/product/catalog data.<select>options can't be matched); required fields are matched by role with a required-marker-tolerant name; detail pages retry-until-rendered to tolerate backend read latency.@playwright/test1.49.0 → 1.62.0 so the suite runs on Node 24 without a type-stripping workaround.User stories
Exercises the CS-engineer workflows: create/resolve a case, self-assign and transition case state, comment (internal/public per state gating), manage watchers, set fix ETA, attachments; create service requests / change requests / incidents / problems and handle their detail views; browse engagements, announcements and product updates; file a security report; and log/approve/reject time.
Release note
N/A — test-only change; no user-facing behavior.
Documentation
N/A — test-only change; no product documentation impact.
Training
N/A — no training impact.
Certification
N/A — no certification impact.
Marketing
N/A.
Automation tests
Security checks
tscandeslintrun clean.tests/e2e/storageState/*.json) are gitignored; no keys/tokens or personal data are in the diff.Samples
N/A.
Related PRs
N/A. Some flows self-skip pending a corresponding backing-service change, tracked separately.
Migrations (if applicable)
N/A.
Test environment
Node 24, macOS, Chromium (Chrome for Testing 151 via Playwright 1.62), run against the local dev stack with the DEV backend.
Learning
Playwright Page Object Model; replaying a captured SPA session (localStorage + sessionStorage + IdP-domain cookies) so a token-based SPA can be driven without automating the login flow.
Summary by CodeRabbit