From 729b3a01db24366368130e0d73886c09bb8e5957 Mon Sep 17 00:00:00 2001 From: Cristian Magherusan-Stanciu Date: Mon, 8 Jun 2026 07:16:18 -0700 Subject: [PATCH] fix(frontend/test): stub escapeHtmlAttr in utils mocks to unbreak Build frontend Seven jest suites (plans, history-retry-button, history-approve-button, history-approval-queue, history-cancel-button, history-cancel-permissions, allowed-accounts) each had a jest.mock('../utils', ...) factory that omitted escapeHtmlAttr. When plans.ts and history.ts call it at render time the mock throws "TypeError: (0 , utils_1.escapeHtmlAttr) is not a function", causing cascading test failures. Added the same escapeHtmlAttr stub already present in history.test.ts (encodes & < > " ') to each affected factory. No logic changes. Closes #1115 --- frontend/src/__tests__/allowed-accounts.test.ts | 4 ++++ frontend/src/__tests__/history-approval-queue.test.ts | 4 ++++ frontend/src/__tests__/history-approve-button.test.ts | 4 ++++ frontend/src/__tests__/history-cancel-button.test.ts | 4 ++++ frontend/src/__tests__/history-cancel-permissions.test.ts | 4 ++++ frontend/src/__tests__/history-retry-button.test.ts | 4 ++++ frontend/src/__tests__/plans.test.ts | 4 ++++ 7 files changed, 28 insertions(+) diff --git a/frontend/src/__tests__/allowed-accounts.test.ts b/frontend/src/__tests__/allowed-accounts.test.ts index 43ca2fe5a..8592f77cc 100644 --- a/frontend/src/__tests__/allowed-accounts.test.ts +++ b/frontend/src/__tests__/allowed-accounts.test.ts @@ -55,6 +55,10 @@ jest.mock('../utils', () => ({ formatDate: jest.fn((val: string) => (val ? new Date(val).toLocaleDateString() : '')), formatTerm: jest.fn((years: number) => (years == null ? '' : `${years} Year${years === 1 ? '' : 's'}`)), escapeHtml: jest.fn((str: string) => str || ''), + escapeHtmlAttr: jest.fn((str: string | null | undefined) => { + if (!str) return ''; + return str.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"').replace(/'/g, '''); + }), populateAccountFilter: jest.fn(() => Promise.resolve()), })); diff --git a/frontend/src/__tests__/history-approval-queue.test.ts b/frontend/src/__tests__/history-approval-queue.test.ts index 7a928a36c..a929062bf 100644 --- a/frontend/src/__tests__/history-approval-queue.test.ts +++ b/frontend/src/__tests__/history-approval-queue.test.ts @@ -37,6 +37,10 @@ jest.mock('../utils', () => ({ formatDate: jest.fn((val) => (val ? new Date(val).toLocaleDateString() : '')), formatTerm: jest.fn((years) => (years == null ? '' : `${years} Year${years === 1 ? '' : 's'}`)), escapeHtml: jest.fn((str) => str || ''), + escapeHtmlAttr: jest.fn((str: string | null | undefined) => { + if (!str) return ''; + return str.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"').replace(/'/g, '''); + }), populateAccountFilter: jest.fn(() => Promise.resolve()), })); diff --git a/frontend/src/__tests__/history-approve-button.test.ts b/frontend/src/__tests__/history-approve-button.test.ts index a6f5c4922..224becfeb 100644 --- a/frontend/src/__tests__/history-approve-button.test.ts +++ b/frontend/src/__tests__/history-approve-button.test.ts @@ -35,6 +35,10 @@ jest.mock('../utils', () => ({ formatDate: jest.fn((val) => (val ? new Date(val).toLocaleDateString() : '')), formatTerm: jest.fn((years) => (years == null ? '' : `${years} Year${years === 1 ? '' : 's'}`)), escapeHtml: jest.fn((str) => str || ''), + escapeHtmlAttr: jest.fn((str: string | null | undefined) => { + if (!str) return ''; + return str.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"').replace(/'/g, '''); + }), populateAccountFilter: jest.fn(() => Promise.resolve()), })); diff --git a/frontend/src/__tests__/history-cancel-button.test.ts b/frontend/src/__tests__/history-cancel-button.test.ts index bf3649a60..0ff3bbefb 100644 --- a/frontend/src/__tests__/history-cancel-button.test.ts +++ b/frontend/src/__tests__/history-cancel-button.test.ts @@ -32,6 +32,10 @@ jest.mock('../utils', () => ({ formatDate: jest.fn((val) => (val ? new Date(val).toLocaleDateString() : '')), formatTerm: jest.fn((years) => (years == null ? '' : `${years} Year${years === 1 ? '' : 's'}`)), escapeHtml: jest.fn((str) => str || ''), + escapeHtmlAttr: jest.fn((str: string | null | undefined) => { + if (!str) return ''; + return str.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"').replace(/'/g, '''); + }), populateAccountFilter: jest.fn(() => Promise.resolve()), })); diff --git a/frontend/src/__tests__/history-cancel-permissions.test.ts b/frontend/src/__tests__/history-cancel-permissions.test.ts index 306462780..b8dee68da 100644 --- a/frontend/src/__tests__/history-cancel-permissions.test.ts +++ b/frontend/src/__tests__/history-cancel-permissions.test.ts @@ -34,6 +34,10 @@ jest.mock('../utils', () => ({ formatDate: jest.fn((val) => (val ? new Date(val).toLocaleDateString() : '')), formatTerm: jest.fn((years) => (years == null ? '' : `${years} Year${years === 1 ? '' : 's'}`)), escapeHtml: jest.fn((str) => str || ''), + escapeHtmlAttr: jest.fn((str: string | null | undefined) => { + if (!str) return ''; + return str.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"').replace(/'/g, '''); + }), populateAccountFilter: jest.fn(() => Promise.resolve()), })); diff --git a/frontend/src/__tests__/history-retry-button.test.ts b/frontend/src/__tests__/history-retry-button.test.ts index ba4f0cc15..d44506f77 100644 --- a/frontend/src/__tests__/history-retry-button.test.ts +++ b/frontend/src/__tests__/history-retry-button.test.ts @@ -41,6 +41,10 @@ jest.mock('../utils', () => ({ formatDate: jest.fn((val) => (val ? new Date(val).toLocaleDateString() : '')), formatTerm: jest.fn((years) => (years == null ? '' : `${years} Year${years === 1 ? '' : 's'}`)), escapeHtml: jest.fn((str) => str || ''), + escapeHtmlAttr: jest.fn((str: string | null | undefined) => { + if (!str) return ''; + return str.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"').replace(/'/g, '''); + }), populateAccountFilter: jest.fn(() => Promise.resolve()), })); diff --git a/frontend/src/__tests__/plans.test.ts b/frontend/src/__tests__/plans.test.ts index d98f96bb8..c5953af0e 100644 --- a/frontend/src/__tests__/plans.test.ts +++ b/frontend/src/__tests__/plans.test.ts @@ -107,6 +107,10 @@ jest.mock('../utils', () => ({ formatRampSchedule: jest.fn((val) => val || 'Unknown'), getStatusBadge: jest.fn(() => ({ class: 'active', label: 'Active' })), escapeHtml: jest.fn((str) => str || ''), + escapeHtmlAttr: jest.fn((str: string | null | undefined) => { + if (!str) return ''; + return str.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"').replace(/'/g, '''); + }), formatCurrency: jest.fn((val) => `$${val || 0}`), populateAccountFilter: jest.fn(() => Promise.resolve()), // providerBadgeHtml added for H1 fix: returns a deterministic span so XSS