1<!doctype html>
2<html lang="en">
3<head>
4 <meta charset="utf-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1">
6 <title>Finance Dashboard Self-Test</title>
7 <link rel="stylesheet" href="style.css">
8 <style>.pass{color:var(--good)}.fail{color:var(--bad)}pre{white-space:pre-wrap;background:var(--panel);border:1px solid var(--line);padding:1rem;border-radius:14px}</style>
9</head>
10<body>
11 <header class="app-header"><div><p class="eyebrow">Diagnostics</p><h1>Self-test</h1></div><a class="button ghost" href="index.html">Back to app</a></header>
12 <main class="layout"><section class="panel table-panel"><h2 id="status">Running…</h2><pre id="output"></pre></section></main>
13 <script src="data.js"></script>
14 <script src="charts.js"></script>
15 <script>
16 const out = document.getElementById('output');
17 const status = document.getElementById('status');
18 function assert(name, condition) { if (!condition) throw new Error(name); out.textContent += '✓ ' + name + '\n'; }
19 try {
20 const D = FinanceData, C = FinanceCharts;
21 let txs = [];
22 txs = D.addTransaction(txs, { date: '2026-07-01', description: 'Pay', category: 'Income', amount: 1000 });
23 txs = D.addTransaction(txs, { date: '2026-07-02', description: 'Food', category: 'Food', amount: -25.55 });
24 assert('addTransaction adds and sorts', txs.length === 2 && txs[0].date === '2026-07-02');
25 txs = D.updateTransaction(txs, txs[0].id, { amount: -30, date: txs[0].date, description: txs[0].description, category: txs[0].category });
26 assert('updateTransaction updates amount', txs.find(t => t.category === 'Food').amount === -30);
27 assert('summarize computes net', D.summarize(txs).net === 970);
28 assert('filterTransactions filters category', D.filterTransactions(txs, { category: 'Income' }).length === 1);
29 assert('monthlyBudgetStatus computes percent', D.monthlyBudgetStatus(txs, { Food: 60 }, '2026-07')[0].percent === 50);
30 const csv = D.toCSV(txs), parsed = D.parseCSV(csv);
31 assert('CSV round trip preserves rows', parsed.length === txs.length && parsed[0].description);
32 assert('cumulativeSeries ends at net', C.cumulativeSeries(txs).at(-1).value === 970);
33 assert('scalePoints returns coordinates', Number.isFinite(C.scalePoints(C.cumulativeSeries(txs), 400, 200)[0].x));
34 txs = D.deleteTransaction(txs, txs[0].id);
35 assert('deleteTransaction removes one', txs.length === 1);
36 status.textContent = 'All tests passed'; status.className = 'pass';
37 } catch (error) { status.textContent = 'Test failed'; status.className = 'fail'; out.textContent += '\n✗ ' + error.stack; }
38 </script>
39</body>
40</html>
41
Discussion
No comments yet. Start the discussion. Recorded by @patrick-toulme.