From bced6c0eef1edf9c89dec64b9c18460feb35e262 Mon Sep 17 00:00:00 2001 From: CodeAlien Date: Sat, 29 Aug 2026 09:51:57 +0000 Subject: [PATCH] test: add unknown-status fallback test to static dashboard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit STATUS_COLORS in dashboard/app.js maps known InvoiceStatus values to badge CSS classes. If the backend ever returns a status not present in this map (e.g. a new contract status added without updating the dashboard), the previous code silently fell back to 'badge-pending', which is misleading — a Pending style on a non-Pending status. Changes: - Add DEFAULT_BADGE_CLASS = 'badge-unknown' constant in app.js. - Update getStatusBadge() to use DEFAULT_BADGE_CLASS instead of the hard-coded 'badge-pending' string, making the fallback intent explicit and easy to grep/update. - Add .badge-unknown CSS class in style.css (neutral grey, distinct from all status-specific colours). Tests added in app.test.js: - badge-unknown applied for an unrecognised status string - badge-unknown applied for an empty string status - badge-unknown NOT applied for any of the 6 known statuses - invoice row with unknown status renders with .badge-unknown badge - table with a mix of known and unknown statuses renders without error Closes #[issue_id] --- dashboard/app.js | 6 +++- dashboard/app.test.js | 71 +++++++++++++++++++++++++++++++++++++++++-- dashboard/style.css | 6 ++++ 3 files changed, 80 insertions(+), 3 deletions(-) diff --git a/dashboard/app.js b/dashboard/app.js index 0a0d5c6..974b635 100644 --- a/dashboard/app.js +++ b/dashboard/app.js @@ -9,6 +9,10 @@ const STATUS_COLORS = { Released: 'badge-released', }; +// Fallback badge class applied when a status is not present in STATUS_COLORS +// (e.g. a new status added to the contract without updating this static dashboard). +const DEFAULT_BADGE_CLASS = 'badge-unknown'; + let invoices = []; let sortField = 'id'; let sortAsc = true; @@ -43,7 +47,7 @@ function formatTimestamp(ts) { } function getStatusBadge(status) { - const cls = STATUS_COLORS[status] || 'badge-pending'; + const cls = STATUS_COLORS[status] || DEFAULT_BADGE_CLASS; return `${status === 'RefundRequested' ? 'Refund Requested' : status}`; } diff --git a/dashboard/app.test.js b/dashboard/app.test.js index 554ac10..fb9b3e7 100644 --- a/dashboard/app.test.js +++ b/dashboard/app.test.js @@ -135,9 +135,9 @@ describe('getStatusBadge()', () => { loadAppScript(); }); - it('returns a span with badge-pending for unknown statuses', () => { + it('returns a span with badge-unknown (the safe fallback) for unknown statuses', () => { const badge = getStatusBadge('Unknown'); - expect(badge).toContain('badge-pending'); + expect(badge).toContain('badge-unknown'); expect(badge).toContain(' { + const badge = getStatusBadge(''); + expect(badge).toContain('badge-unknown'); + }); + + it('does NOT apply badge-unknown for known statuses', () => { + const known = ['Pending', 'Paid', 'Expired', 'Cancelled', 'RefundRequested', 'Released']; + for (const s of known) { + const badge = getStatusBadge(s); + expect(badge).not.toContain('badge-unknown'); + } + }); + + it('renders an invoice with unknown status and applies the fallback badge class', () => { + setupDOM(); + const invoice = { + id: 99, + merchant: 'GBR...X1', + customer: 'GBR...Y2', + amount: '100', + token: 'USDC', + status: 'ArchivedByNewContract', + created_at: 1719000000, + expires_at: 1719600000, + }; + renderTable([invoice]); + const row = document.querySelector('#invoiceBody tr'); + expect(row).not.toBeNull(); + const badgeCell = row.querySelectorAll('td')[4]; + expect(badgeCell.querySelector('.badge-unknown')).not.toBeNull(); + expect(badgeCell.textContent).toContain('ArchivedByNewContract'); + }); + + it('unknown status renders without crashing the whole table', () => { + setupDOM(); + const invoices = getDemoInvoices(); + const withUnknown = [...invoices, { + id: 100, + merchant: 'GBR...Z0', + customer: 'GBR...Z1', + amount: '999', + token: 'USDC', + status: 'BrandNewStatus', + created_at: 1719000000, + expires_at: 1719600000, + }]; + renderTable(withUnknown); + const rows = document.querySelectorAll('#invoiceBody tr'); + expect(rows.length).toBe(withUnknown.length); + }); +}); diff --git a/dashboard/style.css b/dashboard/style.css index 08561df..8d9722a 100644 --- a/dashboard/style.css +++ b/dashboard/style.css @@ -185,6 +185,12 @@ tbody td { color: #065f46; } +/* Fallback badge for unrecognized/future contract statuses */ +.badge-unknown { + background: #f3f4f6; + color: #6b7280; +} + .legend { margin-top: 24px; padding: 16px;