add icons to lists and solve money tracker bugs
All checks were successful
Test and deploy staging / staging (push) Successful in 2m30s
All checks were successful
Test and deploy staging / staging (push) Successful in 2m30s
This commit is contained in:
@@ -1,10 +1,12 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import {
|
||||
accountBalances,
|
||||
accountColor,
|
||||
balanceSeries,
|
||||
forecastSeries,
|
||||
utilityEstimate,
|
||||
wealthProjection
|
||||
wealthProjection,
|
||||
wealthTimeline
|
||||
} from './charts';
|
||||
|
||||
describe('money tracker calculations', () => {
|
||||
@@ -31,6 +33,18 @@ describe('money tracker calculations', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("uses an account's chosen color and falls back to the palette", () => {
|
||||
const series = balanceSeries(
|
||||
[
|
||||
{ id: 1, name: 'Revolut', openingBalanceCents: 0, color: '#000000' },
|
||||
{ id: 2, name: 'Knab', openingBalanceCents: 0, color: null }
|
||||
],
|
||||
[{ accountId: 1, date: '2026-01-01', amountCents: 100 }]
|
||||
);
|
||||
expect(series[0].color).toBe('#000000');
|
||||
expect(series[1].color).toBe(accountColor({ color: null }, 1));
|
||||
});
|
||||
|
||||
it('plots combined transactions in date order even if the input is unordered', () => {
|
||||
const combined = balanceSeries(
|
||||
[{ id: 1, name: 'Cash', openingBalanceCents: 1000 }],
|
||||
@@ -113,6 +127,108 @@ describe('money tracker calculations', () => {
|
||||
expect(series[0].points.at(-1)?.value).toBe(15000);
|
||||
});
|
||||
|
||||
it('invests excess each month without requiring an existing investment account', () => {
|
||||
const projection = wealthProjection(
|
||||
[{ id: 1, kind: 'spendable', openingBalanceCents: 40054 }],
|
||||
new Map([[1, 40054]]),
|
||||
[],
|
||||
[],
|
||||
[{ amountCents: 127137, cadence: 'monthly', accountId: 1 }],
|
||||
{
|
||||
investmentAccountId: null,
|
||||
reserveCents: 100000,
|
||||
annualReturnPercent: '5.00',
|
||||
contributionMode: 'excess',
|
||||
fixedMonthlyCents: 0,
|
||||
includeRecurring: 1,
|
||||
endDate: '2026-11-30'
|
||||
},
|
||||
'2026-09-23'
|
||||
);
|
||||
expect(projection[0].points.map((point) => point.value)).toEqual([
|
||||
40054, 40054, 167191, 294602
|
||||
]);
|
||||
expect(projection[1].points.at(-1)?.value).toBe(100000);
|
||||
expect(projection[2].points.at(-1)?.value).toBe(194602);
|
||||
});
|
||||
|
||||
it('continues actual history into the projection as one dashed line', () => {
|
||||
const accounts = [
|
||||
{ id: 1, name: 'Bank', kind: 'spendable', openingBalanceCents: 0 },
|
||||
{ id: 2, name: 'Broker', kind: 'investment', openingBalanceCents: 0 }
|
||||
];
|
||||
const actual = [
|
||||
{ accountId: 1, date: '2026-09-01', amountCents: 50000 },
|
||||
{ accountId: 2, date: '2026-09-10', amountCents: 20000 }
|
||||
];
|
||||
const projection = [
|
||||
{
|
||||
label: 'Total wealth',
|
||||
color: '',
|
||||
points: [
|
||||
{ date: '2026-09-23', value: 70000 },
|
||||
{ date: '2026-10-31', value: 90000 }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'Spendable',
|
||||
color: '',
|
||||
points: [
|
||||
{ date: '2026-09-23', value: 50000 },
|
||||
{ date: '2026-10-31', value: 60000 }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'Invested',
|
||||
color: '',
|
||||
points: [
|
||||
{ date: '2026-09-23', value: 20000 },
|
||||
{ date: '2026-10-31', value: 30000 }
|
||||
]
|
||||
}
|
||||
];
|
||||
const [total] = wealthTimeline(accounts, actual, projection, '2026-09-23', 'total');
|
||||
expect(total.dashedFrom).toBe('2026-09-23');
|
||||
expect(total.points).toEqual([
|
||||
{ date: '2026-09-01', value: 50000 },
|
||||
{ date: '2026-09-10', value: 70000 },
|
||||
{ date: '2026-09-23', value: 70000 },
|
||||
{ date: '2026-10-31', value: 90000 }
|
||||
]);
|
||||
const [, spendable, invested] = wealthTimeline(
|
||||
accounts,
|
||||
actual,
|
||||
projection,
|
||||
'2026-09-23',
|
||||
'split'
|
||||
);
|
||||
expect(spendable.points.map((point) => point.value)).toEqual([50000, 50000, 50000, 60000]);
|
||||
expect(invested.points.map((point) => point.value)).toEqual([0, 20000, 20000, 30000]);
|
||||
const byAccount = wealthTimeline(accounts, actual, projection, '2026-09-23', 'accounts');
|
||||
expect(byAccount.map((line) => line.label)).toEqual(['Bank', 'Broker', 'Total']);
|
||||
expect(byAccount[0].points.at(-1)).toEqual({ date: '2026-09-23', value: 50000 });
|
||||
});
|
||||
|
||||
it('starts the timeline at today when there is no history', () => {
|
||||
const [total] = wealthTimeline(
|
||||
[],
|
||||
[],
|
||||
[
|
||||
{
|
||||
label: 'Total wealth',
|
||||
color: '',
|
||||
points: [
|
||||
{ date: '2026-09-23', value: 0 },
|
||||
{ date: '2026-10-31', value: 100 }
|
||||
]
|
||||
}
|
||||
],
|
||||
'2026-09-23',
|
||||
'total'
|
||||
);
|
||||
expect(total.points.map((point) => point.date)).toEqual(['2026-09-23', '2026-10-31']);
|
||||
});
|
||||
|
||||
it('compares estimated usage cost with prorated advances', () => {
|
||||
const result = utilityEstimate(
|
||||
[
|
||||
|
||||
Reference in New Issue
Block a user