Skip to content

feat(recommendations): group per-plan-type SP rows in table (closes #135)#807

Merged
cristim merged 3 commits into
feat/multicloud-web-frontendfrom
fix/135-wave4
Jun 3, 2026
Merged

feat(recommendations): group per-plan-type SP rows in table (closes #135)#807
cristim merged 3 commits into
feat/multicloud-web-frontendfrom
fix/135-wave4

Conversation

@cristim
Copy link
Copy Markdown
Member

@cristim cristim commented May 28, 2026

Summary

  • Adds an SP plan-type group parent row in the Recommendations table whenever two or more savings-plans-* cell rows share the same AWS account and region
  • The parent row shows a combined Savings Plans (Compute + SageMaker + ...) label via the existing savingsPlansBucketLabel helper, a +N plan types count badge, an aggregated savings figure, and an expand/collapse chevron
  • Clicking the chevron expands the parent to reveal the individual per-plan-type child rows (which may themselves be multi-variant cells with their own chevrons); single-plan-type scopes fall through to flat rendering unchanged

Implementation shape

Parent + children: one rec-sp-group-row parent row per multi-plan-type scope; children rendered as rec-sp-group-child rows inside the expanded panel. Pure helpers spGroupKey and groupSpCellKeys exported for unit testing.

Test plan

  • npx tsc --noEmit clean (0 errors)
  • npx jest --testPathPattern=recommendations.test 337 pass, 0 fail (319 pre-existing + 18 new)
  • 10 pure-helper tests: spGroupKey, groupSpCellKeys (scoping, sort order, non-SP exclusion, singleton passthrough)
  • 8 DOM integration tests: parent row rendered, combined label, +N badge, collapsed by default, chevron expand reveals children, single plan type renders flat, non-SP rows unaffected, aggregated savings text

Summary by CodeRabbit

  • New Features

    • Savings Plans plan-type grouping in the Recommendations table: collapsed parent rows per scope (chevron), plan-type count badges, aggregated savings displayed, and expandable child rows (collapsed by default).
  • Tests

    • Added unit and DOM tests for grouping, expand/collapse behavior, aggregated savings, single-plan-type handling, non-SP isolation, and permission gating that hides checkboxes for read-only sessions and shows them for admins.

@cristim cristim added triaged Item has been triaged priority/p3 Polish / idea / may never ship severity/low Minor harm urgency/this-quarter Within the quarter impact/many Affects most users effort/s Hours type/feat New capability labels May 28, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 28, 2026

Review Change Stack

Warning

Review limit reached

@cristim, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 5 minutes and 58 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, 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 include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 21ae7ac8-205d-4f06-8f57-9841c1ad3676

📥 Commits

Reviewing files that changed from the base of the PR and between 10dcd7d and 638109b.

📒 Files selected for processing (3)
  • frontend/src/__tests__/recommendations-permissions.test.ts
  • frontend/src/__tests__/recommendations.test.ts
  • frontend/src/recommendations.ts
📝 Walkthrough

Walkthrough

The PR groups Savings Plans rows by provider/account/region, tracking group expansion with expandedSpGroups, rendering one collapsible SP parent row with aggregated savings and child plan-type rows when expanded, and adding helpers plus tests. Chevron clicks toggle group expansion and rerender the list.

Changes

Savings Plans Plan-Type Grouping

Layer / File(s) Summary
State and grouping helper functions
frontend/src/recommendations.ts
Added expandedSpGroups Set tracking SP group expansion state; updated resetExpandedCells() to clear SP-group state; exported spGroupKey() and groupSpCellKeys() to compute canonical SP parent keys and ordered child cell keys for groups with 2+ distinct plan-type cells.
SP group parent and child row rendering
frontend/src/recommendations.ts
buildListMarkup() now detects SP plan-type groups, emits a deduped .rec-sp-group-row parent with combined plan-type badge and aggregated savings, and conditionally renders child summary and variant rows when the group is expanded.
SP group chevron event wiring
frontend/src/recommendations.ts
Added .rec-sp-group-chevron click handler to toggle group expansion in expandedSpGroups and rerender the recommendations list.
SP grouping unit and integration tests
frontend/src/__tests__/recommendations.test.ts, frontend/src/__tests__/recommendations-permissions.test.ts
Added Issue #135 test suite: unit tests for spGroupKey() and groupSpCellKeys() (grouping rules, sort-order preservation, exclusions), DOM tests for parent-row rendering (collapsed default, combined badges, chevron expand/collapse, aggregated savings), and permission tests ensuring readonly sessions do not show per-child checkboxes while admin sessions do.

Sequence Diagram

sequenceDiagram
  participant User
  participant Chevron as .rec-sp-group-chevron Handler
  participant SpGroups as expandedSpGroups State
  participant Renderer as buildListMarkup()
  participant DOM

  User->>Chevron: click chevron
  Chevron->>SpGroups: toggle spGroupKey in Set
  Chevron->>Renderer: trigger rerender(loadedRecs)
  Renderer->>Renderer: compute spGroupKey / groupSpCellKeys
  alt group expanded
    Renderer->>DOM: render parent + child rows (aggregated savings)
  else group collapsed
    Renderer->>DOM: render parent row only (aggregated savings)
  end
  DOM-->>User: update table display
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • LeanerCloud/CUDly#253: Extends the prior grouped/expandable recommendations table with Savings Plans plan-type grouping and chevron controls.

Suggested labels

priority/p2, severity/medium, urgency/this-sprint, effort/m

Poem

🐰 I hopped through lists of SPs today,
Parent rows to lead the way,
Click a chevron, watch them bloom—
Aggregated sums chase gloom,
A tiny rabbit cheers the new display!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 71.43% 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding grouping of per-plan-type Savings Plans rows in the recommendations table when multiple SP cell rows share the same AWS account and region.
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
  • Commit unit tests in branch fix/135-wave4

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

@cristim
Copy link
Copy Markdown
Member Author

cristim commented May 28, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 28, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@cristim
Copy link
Copy Markdown
Member Author

cristim commented May 30, 2026

@coderabbitai full review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 30, 2026

✅ Actions performed

Full review triggered.

@cristim
Copy link
Copy Markdown
Member Author

cristim commented Jun 3, 2026

@coderabbitai review

Re-pinging - the previous full review trigger on 2026-05-30 was acknowledged but no substantive review followed. Latest commit 33b905d is ready for a fresh pass. CI is green and the branch is CLEAN against base.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 3, 2026

@cristim Apologies for the missed follow-through on the previous trigger! Kicking off a fresh full review of commit 33b905d now.

(ɔ◔‿◔)ɔ ♥

✅ Actions performed

Full review triggered.

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

🧹 Nitpick comments (2)
frontend/src/__tests__/recommendations.test.ts (2)

6623-6638: ⚡ Quick win

Assert full in-group order, not just the first element.

Line 6637 only validates index 0, so a regression that reorders the remaining keys would still pass. Assert the whole ordered sequence.

Diff suggestion
-    // order must match sortedKeys order (insertion order here)
-    expect(cellKeys).toHaveLength(3);
-    expect(cellKeys[0]).toBe(sortedKeys[0]);
+    // order must match sortedKeys order (insertion order here)
+    expect(cellKeys).toHaveLength(3);
+    expect(cellKeys).toEqual(sortedKeys);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/__tests__/recommendations.test.ts` around lines 6623 - 6638, The
test currently only asserts the first element of the in-group ordering
(expect(cellKeys[0]).toBe(sortedKeys[0])) which misses regressions that reorder
later elements; update the assertion to verify the entire sequence order by
comparing cellKeys to sortedKeys (or asserting each index equals the
corresponding sortedKeys entry) after calling groupRecsByCell and
groupSpCellKeys so the test enforces full in-group order for the variables
sortedKeys and cellKeys produced by groupRecsByCell and groupSpCellKeys.

6778-6799: ⚡ Quick win

Add collapse-after-expand assertion to lock toggle symmetry.

This test validates expand only. Add a second click assertion so collapse regressions are caught too.

Diff suggestion
     // aria-expanded should flip
     const updatedChevron = document.querySelector<HTMLButtonElement>('.rec-sp-group-chevron');
     expect(updatedChevron!.getAttribute('aria-expanded')).toBe('true');
+
+    // Collapse back: child rows should hide again and aria-expanded should reset.
+    updatedChevron!.click();
+    expect(document.querySelectorAll('.recommendation-row').length).toBe(0);
+    const collapsedChevron = document.querySelector<HTMLButtonElement>('.rec-sp-group-chevron');
+    expect(collapsedChevron!.getAttribute('aria-expanded')).toBe('false');
   });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/__tests__/recommendations.test.ts` around lines 6778 - 6799, Add
a second click and assertions to verify collapse symmetry: in the test "clicking
SP group chevron expands and shows child rows" (after calling
loadRecommendations and the first click/asserts), call chevron.click() again on
the same '.rec-sp-group-chevron' element and assert that
document.querySelectorAll('.recommendation-row') now has length 0 (no child
rows) and that the chevron's aria-expanded attribute is 'false'; this ensures
loadRecommendations test and toggle behavior are validated both for expand and
collapse.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@frontend/src/recommendations.ts`:
- Line 2647: The SP child-row calls to buildVariantRowMarkup are missing the
showCheckboxes argument, causing readonly/viewer sessions to still render
checkboxes; update the two calls that pass (childVariants[0]!, selectedRecs,
true, visibleCols) (and the similar one at the other occurrence) to include the
showCheckboxes variable instead of the hardcoded true so the signature
buildVariantRowMarkup(..., showCheckboxes, visibleCols) receives the correct
boolean from the surrounding scope.
- Around line 4022-4042: Remove the duplicate unconditional row-click listener:
the block that calls
container.querySelectorAll('tr.recommendation-row').forEach(...) and adds a
'click' handler which finds cb via
tr.querySelector('input[type="checkbox"][data-rec-id]') is redundant with the
handler already added inside the if (showCheckboxes) branch; delete this
unconditional block so row clicks are only wired when showCheckboxes is true
(or, alternatively, gate this listener with the existing showCheckboxes
condition and ensure you bail if cb is null before dispatching).

---

Nitpick comments:
In `@frontend/src/__tests__/recommendations.test.ts`:
- Around line 6623-6638: The test currently only asserts the first element of
the in-group ordering (expect(cellKeys[0]).toBe(sortedKeys[0])) which misses
regressions that reorder later elements; update the assertion to verify the
entire sequence order by comparing cellKeys to sortedKeys (or asserting each
index equals the corresponding sortedKeys entry) after calling groupRecsByCell
and groupSpCellKeys so the test enforces full in-group order for the variables
sortedKeys and cellKeys produced by groupRecsByCell and groupSpCellKeys.
- Around line 6778-6799: Add a second click and assertions to verify collapse
symmetry: in the test "clicking SP group chevron expands and shows child rows"
(after calling loadRecommendations and the first click/asserts), call
chevron.click() again on the same '.rec-sp-group-chevron' element and assert
that document.querySelectorAll('.recommendation-row') now has length 0 (no child
rows) and that the chevron's aria-expanded attribute is 'false'; this ensures
loadRecommendations test and toggle behavior are validated both for expand and
collapse.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4b5404f3-8f2c-4909-bc84-7e295f4ff363

📥 Commits

Reviewing files that changed from the base of the PR and between 80c20d2 and 33b905d.

📒 Files selected for processing (2)
  • frontend/src/__tests__/recommendations.test.ts
  • frontend/src/recommendations.ts

Comment thread frontend/src/recommendations.ts Outdated
Comment thread frontend/src/recommendations.ts Outdated
@cristim
Copy link
Copy Markdown
Member Author

cristim commented Jun 3, 2026

Rebased on feat/multicloud-web-frontend. Resolved additive conflicts in frontend/src/__tests__/recommendations.test.ts (import line + adjacent describe blocks for issue #239 sparkline and issue #135 SP grouping) and frontend/src/recommendations.ts.

Addressed CR pass-1 findings:

  • Duplicate row-click handler (double event dispatch) in recommendations.ts -> fixed in d54483f (the duplicate block was a rebase artifact: base moved the row-click handler inside the if (showCheckboxes) block per issue Viewer role: Opportunities table checkboxes + row clicks should be inert (QA Viewer 2.3+2.4) #869, so the unconditional copy added by this PR was redundant and is now removed).
  • recommendations.test.ts in-group order assertion only checked index 0 -> fixed in d54483f: now asserts the full sequence with expect(cellKeys).toEqual(sortedKeys).
  • SP group chevron toggle test only validated expand -> fixed in d54483f: added a second click asserting child rows hide and aria-expanded resets to false.

Verified locally: npx jest src/__tests__/recommendations.test.ts (350 pass / 0 fail) and npm run build succeed.

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 3, 2026

@cristim Thanks for the detailed update! The three CR pass-1 findings all look well-addressed:

  1. Duplicate row-click handler — removing the unconditional copy that was a rebase artifact from the base-branch #869 refactor is the right call.
  2. In-group order assertion — upgrading from a single index-0 check to expect(cellKeys).toEqual(sortedKeys) gives full sequence coverage.
  3. Chevron toggle collapse path — the added second-click assertion for hiding child rows and resetting aria-expanded="false" closes the gap in the toggle test.

Kicking off a fresh review of the rebased branch now.


🧠 Learnings used
Learnt from: cristim
Repo: LeanerCloud/CUDly PR: 0
File: :0-0
Timestamp: 2026-05-05T15:44:54.463Z
Learning: In the LeanerCloud/CUDly repository, when a CodeRabbit review flags something as "outside-diff" that was already implemented in an earlier commit of the same PR, it is likely a stale-snapshot artifact where the review diff was generated against a pre-fix view. The inline comment convention `(CR pass on PR #<number> Finding N)` is used by the maintainer (cristim) to mark lines that were changed in direct response to a prior CodeRabbit review pass.
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@cristim
Copy link
Copy Markdown
Member Author

cristim commented Jun 3, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 3, 2026

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@cristim
Copy link
Copy Markdown
Member Author

cristim commented Jun 3, 2026

Addressed the one open CR finding:

  • Missing showCheckboxes parameter breaks readonly sessions (frontend/src/recommendations.ts, the two SP-group child-row buildVariantRowMarkup calls) -> fixed in 7df4cb19c. Verified the finding was still live at HEAD: the calls at lines 2930 and 2982 omitted showCheckboxes, so it defaulted to true, causing SP plan-type group child rows to render checkbox cells in viewer/readonly sessions while the header and non-SP rows did not. Both calls now forward showCheckboxes from the enclosing buildListMarkup scope, matching the sibling calls at 2992/3066.

Added a regression test (recommendations-permissions.test.ts) that expands an SP plan-type group and asserts the child variant rows honor the role's checkbox visibility, for both readonly (no checkbox-col) and admin (one checkbox-col). Stash-verified: the readonly assertion fails before the fix and passes after.

Verification: recommendations-permissions.test.ts (16/16) and recommendations.test.ts (350/350) pass; npm run build succeeds.

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 3, 2026

@cristim The fix looks well-scoped and the verification methodology is solid:

  • Forwarding showCheckboxes from the enclosing buildListMarkup scope to both child-row buildVariantRowMarkup calls (lines 2930/2982) brings them in line with the sibling calls at 2992/3066 — consistent parameter passing throughout.
  • The stash-verify approach (assert fails before fix, passes after) on recommendations-permissions.test.ts gives strong confidence the regression test is actually exercising the corrected code path, not just a coverage placeholder.

Kicking off a fresh review now.


🧠 Learnings used
Learnt from: cristim
Repo: LeanerCloud/CUDly PR: 0
File: :0-0
Timestamp: 2026-05-05T15:44:54.463Z
Learning: In the LeanerCloud/CUDly repository, when a CodeRabbit review flags something as "outside-diff" that was already implemented in an earlier commit of the same PR, it is likely a stale-snapshot artifact where the review diff was generated against a pre-fix view. The inline comment convention `(CR pass on PR #<number> Finding N)` is used by the maintainer (cristim) to mark lines that were changed in direct response to a prior CodeRabbit review pass.
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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

🧹 Nitpick comments (3)
frontend/src/__tests__/recommendations.test.ts (2)

6834-6852: ⚡ Quick win

Pin the dedicated SP child-row contract, not just generic row counts.

These assertions only count .recommendation-row, so a regression that stops rendering .rec-sp-group-child rows correctly—or breaks the nested child-chevron path for multi-variant plan types—could still pass. Please add one case that asserts the child-row class/structure after expanding the parent, and one grouped child that is itself multi-variant.

Also applies to: 6905-6932

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

In `@frontend/src/__tests__/recommendations.test.ts` around lines 6834 - 6852, The
test "two SP plan types render one parent group row and no flat child rows
(collapsed)" only asserts generic .recommendation-row counts; update it to pin
the dedicated SP child-row contract by (1) simulating expansion of the parent
group (e.g. trigger the same click/expand action used elsewhere after
loadRecommendations()) and then asserting that .rec-sp-group-child elements are
rendered (use document.querySelectorAll('.rec-sp-group-child') and expect a
count of 2), and (2) include a grouped child that is multi-variant (create one
recommendation via mkSpRec that represents a multi-variant plan) and assert that
its nested multi-variant structure or chevron toggles render the expected child
elements (check for the specific class used for multi-variant children). Keep
references to mkSpRec, loadRecommendations, .rec-sp-group-row,
.rec-sp-group-child and the parent expand action so the test fails if SP
child-row rendering or multi-variant nesting breaks.

6750-6765: ⚡ Quick win

Make the order test independent of groups.keys() insertion order.

This currently derives sortedKeys from Array.from(groups.keys()), so it would still pass if groupSpCellKeys() ignored the sortedKeys argument and iterated the map directly. Build a deliberately permuted sortedKeys input here and assert the output follows that order.

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

In `@frontend/src/__tests__/recommendations.test.ts` around lines 6750 - 6765, The
test currently derives sortedKeys from Array.from(groups.keys()) which ties it
to the map insertion order; instead construct a deliberately permuted sortedKeys
array (e.g., reorder the known cell key strings created via mkSpRec) rather than
using groups.keys(), call groupSpCellKeys(sortedKeys, groups), and assert the
returned cellKeys array matches that permuted sortedKeys order; update the test
'preserves sort order within each group' so sortedKeys is an explicit hard-coded
permutation of the three keys and the expectations check equality to that
permutation.
frontend/src/__tests__/recommendations-permissions.test.ts (1)

358-359: 💤 Low value

Optional: assert the exact child-row count to tighten the guard.

This scope has two plan types, so the expanded group should produce a deterministic number of child rows. toBeGreaterThan(0) would still pass if grouping silently collapsed or duplicated rows. Asserting the exact count (e.g., toBe(2)) makes the test fail loudly on regressions in the grouping logic.

Also applies to: 374-375

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

In `@frontend/src/__tests__/recommendations-permissions.test.ts` around lines 358
- 359, The assertion using childRows.length > 0 is too loose; update the test in
recommendations-permissions.test.ts to assert the exact expected number of
expanded child rows (use childRows.length === expectedCount or Jest's
toBe/toHaveLength) for the selector 'tr.rec-variant-row' (variable childRows
from table.querySelectorAll) — replace
expect(childRows.length).toBeGreaterThan(0) with an exact count (e.g., toBe(2))
and make the same replacement for the similar assertion around lines 374-375.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@frontend/src/__tests__/recommendations-permissions.test.ts`:
- Around line 352-380: The test should target the per-variant child rows (not
the SP-group summary row) so update the test selectors to assert against the
nested variant markup produced by buildVariantRowMarkup (i.e. use
"tr.rec-variant-row") and validate checkbox presence via "td.checkbox-col" and
"input[data-rec-id]"; ensure both readonly and admin tests call expandSpGroup()
then query for "tr.rec-variant-row" and assert checkbox absence when
showCheckboxes is false and presence when true (refer to buildVariantRowMarkup,
showCheckboxes, and expandSpGroup to locate the rendering path).

---

Nitpick comments:
In `@frontend/src/__tests__/recommendations-permissions.test.ts`:
- Around line 358-359: The assertion using childRows.length > 0 is too loose;
update the test in recommendations-permissions.test.ts to assert the exact
expected number of expanded child rows (use childRows.length === expectedCount
or Jest's toBe/toHaveLength) for the selector 'tr.rec-variant-row' (variable
childRows from table.querySelectorAll) — replace
expect(childRows.length).toBeGreaterThan(0) with an exact count (e.g., toBe(2))
and make the same replacement for the similar assertion around lines 374-375.

In `@frontend/src/__tests__/recommendations.test.ts`:
- Around line 6834-6852: The test "two SP plan types render one parent group row
and no flat child rows (collapsed)" only asserts generic .recommendation-row
counts; update it to pin the dedicated SP child-row contract by (1) simulating
expansion of the parent group (e.g. trigger the same click/expand action used
elsewhere after loadRecommendations()) and then asserting that
.rec-sp-group-child elements are rendered (use
document.querySelectorAll('.rec-sp-group-child') and expect a count of 2), and
(2) include a grouped child that is multi-variant (create one recommendation via
mkSpRec that represents a multi-variant plan) and assert that its nested
multi-variant structure or chevron toggles render the expected child elements
(check for the specific class used for multi-variant children). Keep references
to mkSpRec, loadRecommendations, .rec-sp-group-row, .rec-sp-group-child and the
parent expand action so the test fails if SP child-row rendering or
multi-variant nesting breaks.
- Around line 6750-6765: The test currently derives sortedKeys from
Array.from(groups.keys()) which ties it to the map insertion order; instead
construct a deliberately permuted sortedKeys array (e.g., reorder the known cell
key strings created via mkSpRec) rather than using groups.keys(), call
groupSpCellKeys(sortedKeys, groups), and assert the returned cellKeys array
matches that permuted sortedKeys order; update the test 'preserves sort order
within each group' so sortedKeys is an explicit hard-coded permutation of the
three keys and the expectations check equality to that permutation.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5f29dfc1-0202-42a3-917f-28021fc09301

📥 Commits

Reviewing files that changed from the base of the PR and between 33b905d and 7df4cb1.

📒 Files selected for processing (3)
  • frontend/src/__tests__/recommendations-permissions.test.ts
  • frontend/src/__tests__/recommendations.test.ts
  • frontend/src/recommendations.ts

Comment thread frontend/src/__tests__/recommendations-permissions.test.ts
@cristim
Copy link
Copy Markdown
Member Author

cristim commented Jun 3, 2026

Rebased on feat/multicloud-web-frontend (pure base advance after #887, #819, #789 and #911 merged).

Resolved additive conflicts in:

frontend/src/recommendations.ts auto-merged cleanly (base's saveColumnFilters/loadColumnFilters persistence additions + this PR's SP-grouping code coexist).

Verification post-rebase:

  • This PR's own suites pass: recommendations.test.ts + recommendations-permissions.test.ts -> 381 tests, 0 failures.
  • npx tsc --noEmit -> no errors.

Note on CI: frontend-build-sentinel may report failures in recommendations-lookback.test.ts (issue #909). These are pre-existing on the base branch itself (base CI is already red on the same suite) and are tracked separately by issue #935. They are unrelated to this PR's changes.

No CR findings outstanding from prior passes. Requesting an incremental review pass on the rebased diff.

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 3, 2026

@cristim Thanks for the thorough rebase summary! The conflict resolutions look clean — union-of-imports plus append-only describe blocks are the safest possible merge outcomes. Good to know the recommendations-lookback.test.ts failures are tracked separately in #935 and pre-exist on the base branch.

Kicking off an incremental review of the rebased diff now.


🧠 Learnings used
Learnt from: cristim
Repo: LeanerCloud/CUDly PR: 0
File: :0-0
Timestamp: 2026-05-05T15:44:54.463Z
Learning: In the LeanerCloud/CUDly repository, when a CodeRabbit review flags something as "outside-diff" that was already implemented in an earlier commit of the same PR, it is likely a stale-snapshot artifact where the review diff was generated against a pre-fix view. The inline comment convention `(CR pass on PR #<number> Finding N)` is used by the maintainer (cristim) to mark lines that were changed in direct response to a prior CodeRabbit review pass.
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

cristim added 3 commits June 3, 2026 22:42
)

Add an SP plan-type group row in the Recommendations table whenever
two or more savings-plans-* cell rows share the same provider /
account / region scope.

- New module-level expandedSpGroups Set; cleared via resetExpandedCells
- spGroupKey(rec) canonical scope key (provider|account|region)
- groupSpCellKeys(sortedKeys, groups) returns Map<sgk, cellKey[]> for
  scopes with 2+ plan types; singletons fall through to flat rendering
- buildListMarkup renders a rec-sp-group-row parent with a
  rec-sp-group-chevron, combined savingsPlansBucketLabel badge,
  +N plan types count badge, and aggregated savings text
- Clicking the chevron toggles expandedSpGroups and re-renders
- 18 new tests (10 pure-helper, 8 DOM integration); 337 pass total
Address CodeRabbit nitpicks on the issue #135 SP-grouping tests:

- groupSpCellKeys in-group order: assert the entire ordered sequence
  (expect(cellKeys).toEqual(sortedKeys)) instead of only index 0, so a
  regression that reorders later keys is caught.
- SP group chevron toggle: add a second click + assertions so the test
  exercises collapse as well as expand (child rows hide, aria-expanded
  resets to false), locking toggle symmetry.
The two buildVariantRowMarkup calls that render expanded Savings Plans
plan-type group child rows omitted the showCheckboxes argument, so it
defaulted to true. In readonly/viewer sessions (showCheckboxes === false)
the SP-group child rows rendered a checkbox cell while the column header and
non-SP rows did not, leaving the table misaligned and exposing an inert
checkbox to a role with no purchase/plan actions.

Forward showCheckboxes from the enclosing buildListMarkup scope into both
nested calls so SP-group child rows match the non-SP path and the header.

Add a regression test (readonly + admin) that expands an SP plan-type group
and asserts the child variant rows honor the role's checkbox visibility.
The readonly assertion fails before the fix and passes after.
@cristim
Copy link
Copy Markdown
Member Author

cristim commented Jun 3, 2026

Re-rebased on feat/multicloud-web-frontend again after #845 (per-row "Plan" button, closes #120) merged into the base mid-iteration.

Resolved additive conflicts in:

Verification post-rebase:

CI note: any recommendations-lookback.test.ts (#909) failures are pre-existing on the base branch and tracked separately by issue #935; unrelated to this PR.

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 3, 2026

@cristim The rebase summary looks clean — purely additive conflict resolutions on both files, with the most interesting integration point being the showCheckboxes gate now wrapping ${planBtnHtml}: that means the plan button inherits the same readonly-visibility guard as the checkbox input, which is the correct behavior. The 384-test / 0-failure post-rebase run (including base's 3 new #120 plan-button tests) confirms both features coexist correctly.

Kicking off an incremental review of the latest rebased diff now.


🧠 Learnings used
Learnt from: cristim
Repo: LeanerCloud/CUDly PR: 0
File: :0-0
Timestamp: 2026-05-05T15:44:54.463Z
Learning: In the LeanerCloud/CUDly repository, when a CodeRabbit review flags something as "outside-diff" that was already implemented in an earlier commit of the same PR, it is likely a stale-snapshot artifact where the review diff was generated against a pre-fix view. The inline comment convention `(CR pass on PR #<number> Finding N)` is used by the maintainer (cristim) to mark lines that were changed in direct response to a prior CodeRabbit review pass.

Learnt from: cristim
Repo: LeanerCloud/CUDly PR: 277
File: frontend/src/__tests__/recommendations.test.ts:2472-2476
Timestamp: 2026-05-05T07:46:01.902Z
Learning: In the CUDly frontend (`frontend/src/recommendations.ts`), `effectiveSavingsPct` intentionally has NO plausibility guard (no null return or warning for reconstructed percentages above per-term ceilings). The maintainer explicitly rejected that approach in PR `#277`. The fix for inflated percentages (issue `#274`) is purely a data-path fix: plumbing the provider's canonical `on_demand_cost` through to the frontend so the denominator is not reconstructed from potentially misleading `monthly_cost + savings + amortized`. Tests that pin the reconstructed >80% behavior are intentional documentation of the pre-fix path, not bugs.
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@cristim cristim merged commit b5f6b78 into feat/multicloud-web-frontend Jun 3, 2026
5 checks passed
@cristim cristim deleted the fix/135-wave4 branch June 3, 2026 21:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

effort/s Hours impact/many Affects most users priority/p3 Polish / idea / may never ship severity/low Minor harm triaged Item has been triaged type/feat New capability urgency/this-quarter Within the quarter

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant