Skip to content

fix(frontend/test): add missing escapeHtmlAttr stub to utils mock - #1117

Merged
cristim merged 1 commit into
feat/multicloud-web-frontendfrom
fix/frontend-test-escape-html-attr-mocks
Jun 8, 2026
Merged

fix(frontend/test): add missing escapeHtmlAttr stub to utils mock#1117
cristim merged 1 commit into
feat/multicloud-web-frontendfrom
fix/frontend-test-escape-html-attr-mocks

Conversation

@cristim

@cristim cristim commented Jun 8, 2026

Copy link
Copy Markdown
Member

Problem

plans-range-validation.test.ts uses a closed mock factory for ../utils that lists every util function explicitly. When escapeHtmlAttr was added to utils.ts it was not added to this factory, so any test that exercises renderPlannedPurchaseRow or renderPlanCard threw TypeError: escapeHtmlAttr is not a function, failing the entire suite in CI.

The two other XSS test files (xss-provider-class.test.ts, xss-purchase-status.test.ts) use jest.requireActual with ...actual spread, so they inherit the real implementation automatically and were not affected.

Fix

Add escapeHtmlAttr to the closed mock factory in plans-range-validation.test.ts with a faithful HTML-attribute escape implementation (same character set the real utils.ts function escapes).

Verification

All 42 tests in plans-range-validation.test.ts pass locally after this change.

Labels (mirrored from #1115)

priority/p2 | severity/medium | urgency/this-sprint | impact/internal | effort/s | type/bug | triaged

Closes #1115


Generated by Claude Code

plans-range-validation.test.ts used a closed mock factory for ../utils
that listed every util function explicitly but omitted escapeHtmlAttr,
which was added to utils.ts after the test was written.  The missing
stub caused a "not a function" TypeError whenever plans.ts called
escapeHtmlAttr during renderPlannedPurchaseRow / renderPlanCard,
making the entire plans-range-validation suite fail in CI.

Added the stub with a faithful HTML-attribute escape implementation
(mirrors the real utils.ts implementation) so the closed factory
remains exhaustive.

Closes #1115
@coderabbitai

coderabbitai Bot commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 51392074-5c93-4be0-87b1-9bcb58f0daa9

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/frontend-test-escape-html-attr-mocks

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

@cristim cristim added triaged Item has been triaged priority/p2 Backlog-worthy severity/medium Moderate harm urgency/this-sprint Within the current sprint impact/internal Team-internal only effort/s Hours type/bug Defect labels Jun 8, 2026 — with Claude

cristim commented Jun 8, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review


Generated by Claude Code

@coderabbitai

coderabbitai Bot commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Get the PR diff
gh pr view 1117 --repo LeanerCloud/CUDly --json title,body,files,commits,additions,deletions,state

# Get the diff
gh pr diff 1117 --repo LeanerCloud/CUDly

Length of output: 3455


🏁 Script executed:

#!/bin/bash
# Find and read the real escapeHtmlAttr implementation
rg -n 'escapeHtmlAttr' --type ts -A 10 -B 2

# Also check if there are any other closed mock factories that might be missing escapeHtmlAttr
rg -n 'escapeHtml' --type ts frontend/src/__tests__/

Length of output: 34390



Review: fix(frontend/test): add missing escapeHtmlAttr stub to utils mock

Verdict: ✅ Approved — no issues found.

What the change does

plans-range-validation.test.ts uses a closed mock factory for ../utils that must list every exported function explicitly. escapeHtmlAttr was added to utils.ts after this test file was written, so the factory silently omitted it, causing a TypeError: escapeHtmlAttr is not a function whenever renderPlannedPurchaseRow / renderPlanCard ran.

Correctness

Check Result
Real utils.ts escapeHtmlAttr delegates to escapeHtml, which encodes & < > " ✅ Stub encodes all five: & < > " ' (superset — the '&#39; encoding is correct for attribute context)
Null / undefined handling str == null guard returns '' — matches the real function's behaviour and is actually more precise than the !str guard used in the other test files (won't falsely return '' for the string '0')
Consistent with other closed-mock factories ✅ Pattern matches history.test.ts, plans.test.ts, allowed-accounts.test.ts, history-approve-button.test.ts, etc.
Any other closed-mock factories missing escapeHtmlAttr? ✅ None — dashboard.ts and recommendations.ts do not import escapeHtmlAttr, so their test mocks don't need it

The fix is minimal, targeted, and consistent with the established pattern across the test suite.

[approve]


🧠 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: 0
File: :0-0
Timestamp: 2026-06-06T06:43:53.792Z
Learning: In LeanerCloud/CUDly PR `#995` (issue `#950` ownership gate), the Dashboard "Upcoming purchases" widget (`dashboard.ts`) has two Cancel entry points (card-level and modal) that both call `api.deletePlannedPurchase`. The `UpcomingPurchase` response DTO must include `CreatedByUserID *string` (omitempty, mirroring `PlannedPurchase`) so the frontend can gate these buttons via a `canCancelUpcomingPurchase` helper that mirrors `canManageScheduledPurchase` on the Plans page. Without this, the backend correctly 403s non-owners but the UI has no gate and the UX is broken. Regressions: `frontend/src/__tests__/dashboard-ownership-950.test.ts` (6 tests) and `TestHandler_getUpcomingPurchases_PropagatesCreatedByUserID`. Fixed in commit 94326f6b9.

Learnt from: cristim
Repo: LeanerCloud/CUDly PR: 0
File: :0-0
Timestamp: 2026-06-08T04:21:35.802Z
Learning: In LeanerCloud/CUDly `frontend/src/recommendations.ts` (PR `#838`, commit d7c5764), `perRecPayments` in `FanOutBucket` must be seeded **sparsely** (override-only: only recs whose resolved payment differs from the bucket-level default). The execute path in `frontend/src/app.ts` resolves each rec's payment as `perRecPayments.get(rec.id) ?? b.payment`; if the map is fully populated the fallback is dead and changing the bucket-level dropdown has no effect on unedited rows. The per-rec change handler deletes the map entry when the user reverts to the bucket default (resuming tracking via the fallback); the bucket-level change handler re-syncs visible per-rec selects that have no override, skipping rows whose service does not support the new payment (mixed-SP buckets). Tests (i) and (j) cover the override-only contract and bucket-level propagation respectively; 322 tests pass, `tsc --noEmit` clean.

@cristim
cristim merged commit 608d78a into feat/multicloud-web-frontend Jun 8, 2026
5 checks passed
@cristim
cristim deleted the fix/frontend-test-escape-html-attr-mocks branch July 27, 2026 11:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

effort/s Hours impact/internal Team-internal only priority/p2 Backlog-worthy severity/medium Moderate harm triaged Item has been triaged type/bug Defect urgency/this-sprint Within the current sprint

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants