264 lines
11 KiB
JavaScript
264 lines
11 KiB
JavaScript
// Generated, do not edit. sha256:615ef8a5d598742e85ea7f2f5b84abfb3e5a2087d59fe201265eaa5faea109f0
|
||||
|
|
import { describe, it, mock } from 'node:test';
|
|||
|
|
import assert from 'node:assert/strict';
|
|||
|
|
|
|||
|
|
import { vasteRekenvaluta } from '../helpers/rekenvaluta.mjs';
|
|||
|
|
await vasteRekenvaluta(mock, 'EUR');
|
|||
|
|
|
|||
|
|
const { fmtUnit, fmtAmount, fmtPct, pctTone, fmtBaseSigned, colouredSigns, fmtPrice, fmtMoney, currencyName, isBaseAsset, subline, toBaseByFactor } = await import('../../client/ui/format.js');
|
|||
|
|
const { setAppSetting } = await import('../../client/app-settings.js');
|
|||
|
|
import { CONFIG } from '../../shared/config.js';
|
|||
|
|
|
|||
|
|
const EUR = { symbol: 'EUR', kind: 'fiat', decimals: 2, unit: 'EUR', subunit: 'cent', displayUnit: 'unit' };
|
|||
|
|
const BTC = { kind: 'crypto', decimals: 8, unit: 'BTC', subunit: 'sat', displayUnit: 'unit' };
|
|||
|
|
const GOUD = { kind: 'metal', unit: 'ozt', subunit: 'g', displayUnit: 'unit' };
|
|||
|
|
|
|||
|
|
describe('fmtUnit bij geld', () => {
|
|||
|
|
it('houdt de decimalen van het asset vast, ook als het nullen zijn', () => {
|
|||
|
|
assert.equal(fmtUnit(130_000, EUR), '1.300,00 EUR');
|
|||
|
|
assert.equal(fmtUnit(100_530, EUR), '1.005,30 EUR');
|
|||
|
|
assert.equal(fmtUnit(0, EUR), '0,00 EUR');
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
it('laat een negatief bedrag heel (een boeking mag omlaag)', () => {
|
|||
|
|
assert.equal(fmtUnit(-2_882_490, EUR), '-28.824,90 EUR');
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
it('volgt het asset en niet de aanname dat geld twee decimalen heeft', () => {
|
|||
|
|
const zonderCenten = { kind: 'fiat', decimals: 0, unit: 'JPY', subunit: null, displayUnit: 'unit' };
|
|||
|
|
assert.equal(fmtUnit(1_300, zonderCenten), '1.300 JPY');
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
it('telt in de ondereenheid gewoon hele centen', () => {
|
|||
|
|
assert.equal(fmtUnit(100_530, { ...EUR, displayUnit: 'subunit' }), '100.530 cent');
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
describe('fmtUnit bij crypto en metaal blijft ongewijzigd', () => {
|
|||
|
|
it('laat overtollige nullen bij crypto juist wél weg', () => {
|
|||
|
|
assert.equal(fmtUnit(50_000_000, BTC), '0,5 BTC');
|
|||
|
|
assert.equal(fmtUnit(100_000_000, BTC), '1 BTC');
|
|||
|
|
assert.equal(fmtUnit(1, BTC), '0,00000001 BTC');
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
it('laat het metaal-pad ongemoeid (dat rekent met factoren, niet met 10^decimals)', () => {
|
|||
|
|
assert.equal(fmtUnit(toBaseByFactor(1, 'ozt'), GOUD), '1 ozt');
|
|||
|
|
assert.equal(fmtUnit(toBaseByFactor(2.5, 'ozt'), GOUD), '2,5 ozt');
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
describe('fmtAmount volgt dezelfde regel', () => {
|
|||
|
|
it('geeft bij geld een hoofdbedrag met vaste decimalen en de centen ernaast', () => {
|
|||
|
|
assert.deepEqual(fmtAmount(130_000, EUR), { primary: '1.300,00 EUR', secondary: '130.000 cent' });
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
it('en bij crypto niet', () => {
|
|||
|
|
assert.deepEqual(fmtAmount(50_000_000, BTC), { primary: '0,5 BTC', secondary: '50.000.000 sat' });
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
describe('isBaseAsset: is dit de eenheid waarin de app rekent', () => {
|
|||
|
|
it('herkent het reken-asset op zijn symbool', () => {
|
|||
|
|
assert.equal(isBaseAsset(EUR), true);
|
|||
|
|
assert.equal(isBaseAsset(BTC), false);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
it('kijkt NIET naar de asset-soort', () => {
|
|||
|
|
const USD = { symbol: 'USD', kind: 'fiat', decimals: 2, unit: 'USD', subunit: 'cent', displayUnit: 'unit' };
|
|||
|
|
assert.equal(isBaseAsset(USD), false);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
it('is onwaar zonder asset', () => {
|
|||
|
|
assert.equal(isBaseAsset(undefined), false);
|
|||
|
|
assert.equal(isBaseAsset(null), false);
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
describe('subline: de tweede regel onder een waarde', () => {
|
|||
|
|
it('rendert niets als er niets te tonen is', () => {
|
|||
|
|
assert.equal(subline(''), '');
|
|||
|
|
assert.equal(subline(null), '');
|
|||
|
|
assert.equal(subline(undefined), '');
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
it('rendert de regel wél als er inhoud is', () => {
|
|||
|
|
assert.equal(subline('<span class="val">€ 1,00</span>'),
|
|||
|
|
'<div class="t-muted-cell t-subline"><span class="val">€ 1,00</span></div>');
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
describe('fmtPrice: de decimalen volgen de grootte', () => {
|
|||
|
|
it('houdt een grote prijs op twee decimalen', () => {
|
|||
|
|
assert.equal(fmtPrice(74884.62115428, 'eur'), '€ 74.884,62');
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
it('houdt twee decimalen vast als er niets achter de komma staat', () => {
|
|||
|
|
assert.equal(fmtPrice(40000, 'eur'), '€ 40.000,00');
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
it('geeft een kleine prijs zijn cijfers', () => {
|
|||
|
|
assert.equal(fmtPrice(0.00012345, 'eur'), '€ 0,00012345');
|
|||
|
|
assert.equal(fmtPrice(1.23456789, 'eur'), '€ 1,2346');
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
it('weigert een onbekende munt in plaats van iets te verzinnen', () => {
|
|||
|
|
assert.throws(() => fmtPrice(1, 'xyz'), /Unknown currency/);
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
describe('fmtMoney: symbool en decimalen per munt', () => {
|
|||
|
|
it('laat euro en dollar ongewijzigd', () => {
|
|||
|
|
assert.equal(fmtMoney(1234.5, 'eur'), '€ 1.234,50');
|
|||
|
|
assert.equal(fmtMoney(1234.5, 'USD'), '$ 1.234,50');
|
|||
|
|
assert.equal(fmtMoney(0.001, 'eur'), '€ 0,001', 'onder een halve cent komen er cijfers bij');
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
it('volgt de decimalen van de munt: een yen heeft er geen', () => {
|
|||
|
|
assert.equal(fmtMoney(1234.5, 'JPY'), '¥ 1.235');
|
|||
|
|
assert.equal(fmtPrice(1234.5, 'JPY'), '¥ 1.234,5');
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
it('kent elke munt uit de lijst, en weigert een code daarbuiten', () => {
|
|||
|
|
for (const code of CONFIG.PORTFOLIO.BASE_CURRENCIES) assert.doesNotThrow(() => fmtMoney(1, code), code);
|
|||
|
|
assert.throws(() => fmtMoney(1, 'XYZ'), /Unknown currency/);
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
describe('currencyName', () => {
|
|||
|
|
it('geeft elke munt uit de lijst een Nederlandse naam met hoofdletter', () => {
|
|||
|
|
assert.equal(currencyName('eur'), 'Euro');
|
|||
|
|
for (const code of CONFIG.PORTFOLIO.BASE_CURRENCIES) {
|
|||
|
|
const naam = currencyName(code);
|
|||
|
|
assert.match(naam, /^[A-Z]/, `${code}: '${naam}'`);
|
|||
|
|
assert.notEqual(naam, code, `${code} heeft geen naam van Intl gekregen`);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
it('weigert een code buiten de lijst, net als fmtMoney', () => {
|
|||
|
|
assert.throws(() => currencyName('XYZ'), /Unknown currency/);
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
describe('fmtPct', () => {
|
|||
|
|
it('schrijft Nederlands: een komma en één decimaal', () => {
|
|||
|
|
assert.equal(fmtPct(0.306), '+30,6%');
|
|||
|
|
assert.equal(fmtPct(0.1), '+10,0%');
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
it('gebruikt het echte minteken en niet een koppelteken', () => {
|
|||
|
|
assert.equal(fmtPct(-0.008), '−0,8%');
|
|||
|
|
assert.ok(!fmtPct(-0.008).includes('-'), 'een koppelteken hoort er niet in te staan');
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
it('geeft een waarde die op nul afrondt GEEN teken, ook als hij echt negatief is', () => {
|
|||
|
|
assert.equal(fmtPct(0), '0,0%');
|
|||
|
|
assert.equal(fmtPct(-0.000167), '0,0%');
|
|||
|
|
assert.equal(fmtPct(0.0004), '0,0%');
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
it('fine: twee decimalen alleen als één decimaal de beweging verbergt', () => {
|
|||
|
|
assert.equal(fmtPct(0.0004, { fine: true }), '+0,04%');
|
|||
|
|
assert.equal(fmtPct(-0.000167, { fine: true }), '−0,02%');
|
|||
|
|
assert.equal(fmtPct(0.014, { fine: true }), '+1,4%', 'een gewone dagbeweging houdt één decimaal');
|
|||
|
|
assert.equal(fmtPct(0.0006, { fine: true }), '+0,1%', 'wat op één decimaal te zien is, blijft zo');
|
|||
|
|
assert.equal(fmtPct(0.00004, { fine: true }), '0,0%', 'ook op twee decimalen nul: dan blijft het 0,0%');
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
it('pctTone: rood bij −0,1%, geen kleur bij wat als 0,0% staat', () => {
|
|||
|
|
assert.equal(pctTone(-0.001), 't-neg');
|
|||
|
|
assert.equal(pctTone(0.001), 't-pos');
|
|||
|
|
assert.equal(pctTone(-0.0004), '', 'staat als 0,0%');
|
|||
|
|
assert.equal(pctTone(-0.0004, { fine: true }), 't-neg', 'met twee decimalen staat er −0,04%');
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
it('signs: geen, alleen negatief of allebei; de kleur blijft in elke stand', () => {
|
|||
|
|
assert.equal(fmtPct(0.306), '+30,6%', 'zonder kleur beide tekens');
|
|||
|
|
assert.equal(fmtPct(0.306, { signs: 'negative' }), '30,6%');
|
|||
|
|
assert.equal(fmtPct(-0.008, { signs: 'negative' }), '−0,8%');
|
|||
|
|
assert.equal(fmtPct(-0.008, { signs: 'none' }), '0,8%');
|
|||
|
|
assert.equal(fmtPct(0.306, { signs: 'both' }), '+30,6%');
|
|||
|
|
assert.equal(fmtPct(0.0004, { fine: true, signs: 'negative' }), '0,04%');
|
|||
|
|
assert.equal(pctTone(-0.001, { signs: 'none' }), 't-neg');
|
|||
|
|
assert.equal(pctTone(0.001, { signs: 'none' }), 't-pos');
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
it('fmtBaseSigned: euroteken, spatie, teken, bedrag; onder een cent geen teken', () => {
|
|||
|
|
assert.equal(fmtBaseSigned(-4.77, 'negative'), '€ −4,77');
|
|||
|
|
assert.equal(fmtBaseSigned(31.51, 'negative'), '€ 31,51');
|
|||
|
|
assert.equal(fmtBaseSigned(31.51, 'both'), '€ +31,51');
|
|||
|
|
assert.equal(fmtBaseSigned(-4.77, 'none'), '€ 4,77');
|
|||
|
|
assert.equal(fmtBaseSigned(-1234.5, 'both'), '€ −1.234,50');
|
|||
|
|
assert.equal(fmtBaseSigned(-0.004, 'both'), '€ 0,00');
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
it('colouredSigns leest de instelling', async () => {
|
|||
|
|
await setAppSetting('result_signs', 'none');
|
|||
|
|
assert.equal(colouredSigns(), 'none');
|
|||
|
|
await setAppSetting('result_signs', 'both');
|
|||
|
|
assert.equal(colouredSigns(), 'both');
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
it('houdt het teken zodra er wél iets te zien is', () => {
|
|||
|
|
assert.equal(fmtPct(0.0006), '+0,1%');
|
|||
|
|
assert.equal(fmtPct(-0.0006), '−0,1%');
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
it('rondt af op één decimaal en niet meer', () => {
|
|||
|
|
assert.equal(fmtPct(0.12345), '+12,3%');
|
|||
|
|
assert.equal(fmtPct(0.12678), '+12,7%');
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
it('geeft een lege tekst als er geen getal is, want dat is iets anders dan nul', () => {
|
|||
|
|
assert.equal(fmtPct(null), '');
|
|||
|
|
assert.equal(fmtPct(undefined), '');
|
|||
|
|
assert.equal(fmtPct(NaN), '');
|
|||
|
|
assert.equal(fmtPct(Infinity), '');
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
it('valt niet over een groot percentage met een duizendtalscheiding', () => {
|
|||
|
|
assert.equal(fmtPct(10), '+1.000,0%');
|
|||
|
|
assert.equal(fmtPct(20000), '+2.000.000,0%');
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const { parseLocaleNumber } = await import('../../client/ui/format.js');
|
|||
|
|
const { setLocale } = await import('../../client/i18n/index.js');
|
|||
|
|
|
|||
|
|
describe('opmaak en invoer volgen de taal', () => {
|
|||
|
|
const inTaal = (code, fn) => { setLocale(code); try { fn(); } finally { setLocale('nl'); } };
|
|||
|
|
|
|||
|
|
it('leest de eigen opmaak terug, in het Nederlands en in het Engels', () => {
|
|||
|
|
assert.equal(parseLocaleNumber('1.000,50'), 1000.5);
|
|||
|
|
assert.equal(parseLocaleNumber('100.000.000'), 1e8);
|
|||
|
|
inTaal('en', () => {
|
|||
|
|
assert.equal(parseLocaleNumber('1,000.50'), 1000.5);
|
|||
|
|
assert.equal(parseLocaleNumber('100,000,000'), 1e8);
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
it('neemt één scheidingsteken zonder drie cijfers erachter als decimaalteken, in elke taal', () => {
|
|||
|
|
assert.equal(parseLocaleNumber('3.5'), 3.5);
|
|||
|
|
assert.equal(parseLocaleNumber('0.500'), 0.5, 'na een kale nul is het geen duizendtal');
|
|||
|
|
assert.equal(parseLocaleNumber('3,5'), 3.5);
|
|||
|
|
inTaal('en', () => {
|
|||
|
|
assert.equal(parseLocaleNumber('3,5'), 3.5);
|
|||
|
|
assert.equal(parseLocaleNumber('1,000'), 1000);
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
it('geeft NaN zonder cijfers, zodat de aanroeper zelf kiest', () => {
|
|||
|
|
assert.ok(Number.isNaN(parseLocaleNumber('')));
|
|||
|
|
assert.ok(Number.isNaN(parseLocaleNumber('€ -')));
|
|||
|
|
assert.equal(parseLocaleNumber('-1.234,5'), -1234.5);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
it('formatteert in de actieve taal, ook na een wissel terug', () => {
|
|||
|
|
inTaal('en', () => {
|
|||
|
|
assert.equal(fmtMoney(1234.5, 'EUR'), '€ 1,234.50');
|
|||
|
|
assert.equal(fmtPct(10), '+1,000.0%');
|
|||
|
|
assert.equal(currencyName('USD'), 'US Dollar');
|
|||
|
|
});
|
|||
|
|
assert.equal(fmtMoney(1234.5, 'EUR'), '€ 1.234,50');
|
|||
|
|
assert.equal(currencyName('USD'), 'Amerikaanse dollar');
|
|||
|
|
});
|
|||
|
|
});
|