Files
2026-10-29 11:57:29 +01:00

228 lines
9.0 KiB
JavaScript

// Generated, do not edit. sha256:05fac95c720d81fea977fcda4544cbb3cf0d29e4d449a955de2c4a1154d3e9ab
import { describe, it, mock } from 'node:test';
import assert from 'node:assert/strict';
mock.module('../../client/app-settings.js', {
namedExports: { appSetting: () => 1, baseCurrency: () => 'EUR' },
});
let ASSETS = [];
let BEREIK = null;
mock.module('../../client/data/assets.js', { namedExports: { listAssets: async () => ASSETS } });
mock.module('../../client/data/preferences.js', {
namedExports: { getPref: async (_s, _k, d) => BEREIK ?? d, setPref: async () => {} },
});
let RIJEN = [];
let SPOT = null;
mock.module('../../client/data/price-history.js', { namedExports: { listPriceHistory: async () => RIJEN } });
const KOERS = { USD: 0.8 };
mock.module('../../client/price/providers.js', {
namedExports: {
liveSpotFor: async () => (SPOT == null ? null : { price: SPOT }),
basePriceOf: s => (s && Number.isFinite(s.price) ? s.price : null),
currentRate: async code => KOERS[code] ?? null,
lastSpotSource: () => null,
},
});
mock.module('../../client/data/providers.js', {
namedExports: { listProviders: async () => [{ id: 7, name: 'CoinGecko' }, { id: 9, name: 'Atreides Services' }] },
});
let GETEKEND = null;
let HINT = null;
let STAART = null;
let FIJN = null;
mock.module('../../client/ui/sparkline.js', {
namedExports: {
mountSparkline: (_host, punten, opts = {}) => { GETEKEND = punten; HINT = opts.formatValue || null; },
withSpot: (punten, prijs) => (prijs == null ? punten : [...punten, [Date.now(), prijs]]),
sliceTone: () => 'up',
changePctHtml: (_punten, staart = '', opts = {}) => { STAART = staart; FIJN = !!opts.fine; return ''; },
},
});
globalThis.document = { addEventListener() {} };
const { renderSpotWidget } = await import('../../client/widgets/spot.js');
const { fmtMoney } = await import('../../client/ui/format.js');
const { CONFIG } = await import('../../shared/config.js');
const { recordSpot, clearMomentum } = await import('../../client/price/momentum.js');
const OZT = CONFIG.METALS.UNITS.ozt;
function nepHost() {
const perSelector = new Map();
return {
innerHTML: '',
isConnected: true,
querySelector(sel) {
if (!perSelector.has(sel)) perSelector.set(sel, { classList: { remove() {}, toggle() {} } });
return perSelector.get(sel);
},
el(sel) { return perSelector.get(sel); },
};
}
async function teken(asset, { spot = null, rijen = [], params = {} } = {}) {
ASSETS = [asset];
SPOT = spot;
RIJEN = rijen;
GETEKEND = null;
const host = nepHost();
await renderSpotWidget(host, { params: { assetId: asset.id, ...params } });
await new Promise(r => setImmediate(r));
return host;
}
const vandaag = new Date().toISOString().slice(0, 10);
const gisteren = new Date(Date.now() - 86400000).toISOString().slice(0, 10);
const geschiedenis = (a, b) => [
{ observedAt: `${gisteren}T00:00:00Z`, fiatPrice: a },
{ observedAt: `${vandaag}T00:00:00Z`, fiatPrice: b },
];
const GOUD = { id: 7, symbol: 'XAU', name: 'Goud', kind: 'metal', unit: 'ozt', subunit: 'g', decimals: 7, displayUnit: 'unit' };
const BTC = { id: 1, symbol: 'BTC', name: 'Bitcoin', kind: 'crypto', unit: 'BTC', subunit: 'sat', decimals: 8, displayUnit: 'unit' };
describe('spotwidget: de koers in de weergave-eenheid van het asset', () => {
it('goud in ozt: de spot per troy ounce, zonder eenheid erachter', async () => {
const host = await teken(GOUD, { spot: 100, rijen: geschiedenis(90, 95) });
assert.equal(host.el('#spot-price').textContent, fmtMoney(100 * OZT, 'EUR', { decimals: 0 }));
assert.doesNotMatch(host.innerHTML, /\/ozt/);
});
it('goud in ozt: de sparklijn in dezelfde eenheid als het getal', async () => {
await teken(GOUD, { spot: 100, rijen: geschiedenis(90, 95) });
const waarden = GETEKEND.map(p => p[1]);
assert.ok(Math.abs(waarden[0] - 90 * OZT) < 1e-6, `eerste punt ${waarden[0]}`);
assert.ok(Math.abs(waarden[waarden.length - 1] - 100 * OZT) < 1e-6, 'het laatste punt is de spot');
});
it('zonder spot: de laatste slotkoers, ook in ozt', async () => {
const host = await teken(GOUD, { spot: null, rijen: geschiedenis(90, 95) });
assert.equal(host.el('#spot-price').textContent, fmtMoney(95 * OZT, 'EUR', { decimals: 0 }));
});
it('goud in gram als weergave-eenheid: geen omrekening', async () => {
const host = await teken({ ...GOUD, displayUnit: 'subunit' }, { spot: 100 });
assert.equal(host.el('#spot-price').textContent, fmtMoney(100, 'EUR', { decimals: 2 }));
});
it('tweede valuta: de getoonde koers (per ozt) gedeeld door de wisselkoers', async () => {
const host = await teken(GOUD, { spot: 100, params: { second: 'USD' } });
assert.equal(host.el('#spot-second').textContent, fmtMoney(100 * OZT / 0.8, 'USD', { decimals: 0 }));
});
it('tweede valuta: zonder keuze blijft hij leeg', async () => {
const host = await teken(GOUD, { spot: 100 });
assert.equal(host.el('#spot-second')?.textContent, undefined);
});
it('crypto: per munt, zonder omrekening', async () => {
const host = await teken(BTC, { spot: 60000 });
assert.equal(host.el('#spot-price').textContent, fmtMoney(60000, 'EUR', { decimals: 0 }));
});
});
describe('spotwidget: het tijdvak dag', () => {
const dagen = n => new Date(Date.now() - n * 86400000).toISOString().slice(0, 10);
const maandTotGisteren = () => Array.from({ length: 30 }, (_, i) => ({
observedAt: `${dagen(30 - i)}T00:00:00Z`, fiatPrice: 100 + i,
}));
it('tekent precies twee punten: de slotkoers van gisteren en de spot', async () => {
BEREIK = 'day';
try {
await teken(BTC, { spot: 200, rijen: maandTotGisteren() });
assert.equal(GETEKEND.length, 2);
assert.deepEqual(GETEKEND.map(p => p[1]), [129, 200]);
assert.equal(FIJN, true);
} finally { BEREIK = null; }
});
it('een week blijft een week, zonder fijne decimaal', async () => {
BEREIK = 'week';
try {
await teken(BTC, { spot: 200, rijen: maandTotGisteren() });
assert.ok(GETEKEND.length >= 7, `${GETEKEND.length} punten`);
assert.equal(FIJN, false);
} finally { BEREIK = null; }
});
});
describe('spotwidget: de bron in de hint van een punt', () => {
const dag = n => new Date(Date.now() - n * 86400000).toISOString().slice(0, 10);
const ms = n => Date.parse(`${dag(n)}T00:00:00Z`);
it('noemt de provider van een eigen dag, en bij een doorgetrokken dag de dag van de koers', async () => {
BEREIK = 'week';
try {
await teken(BTC, { spot: null, rijen: [
{ observedAt: `${dag(5)}T00:00:00Z`, fiatPrice: 100, providerId: 7 },
{ observedAt: `${dag(2)}T00:00:00Z`, fiatPrice: 110, providerId: 9 },
] });
assert.match(HINT(100, ms(5)), /\nCoinGecko$/);
assert.match(HINT(110, ms(2)), /\nAtreides Services$/);
assert.match(HINT(100, ms(4)), /\nCoinGecko, koers van /);
} finally { BEREIK = null; }
});
it('een rij van een onbekende provider: geen bron in plaats van een verkeerde', async () => {
BEREIK = 'week';
try {
await teken(BTC, { spot: null, rijen: [
{ observedAt: `${dag(3)}T00:00:00Z`, fiatPrice: 100, providerId: 42 },
{ observedAt: `${dag(2)}T00:00:00Z`, fiatPrice: 110, providerId: 7 },
] });
assert.doesNotMatch(HINT(100, ms(3)), /CoinGecko|Atreides/);
} finally { BEREIK = null; }
});
});
describe('spotwidget: de stip van de korte trend', () => {
async function dalend(assetId) {
clearMomentum();
const nu = Date.now();
for (let i = 0; i <= 3; i++) await recordSpot(assetId, 100 - i, nu - (3 - i) * 60000);
}
it('rood in de pil na dalingen', async () => {
await dalend(BTC.id);
await teken(BTC, { spot: 60000, rijen: geschiedenis(59000, 59500) });
assert.match(STAART, /class="momentum-dot is-down"/);
});
it('de hint noemt het aandeel van de kant van de kleur', async () => {
await dalend(BTC.id);
await teken(BTC, { spot: 60000, rijen: geschiedenis(59000, 59500) });
assert.match(STAART, /title="100% van de laatste 3 koersbewegingen omlaag"/);
});
it('twee op en één neer: 67% omhoog', async () => {
clearMomentum();
const nu = Date.now();
for (const [i, p] of [100, 101, 102, 101].entries()) await recordSpot(BTC.id, p, nu - (3 - i) * 60000);
await teken(BTC, { spot: 60000, rijen: geschiedenis(59000, 59500) });
assert.match(STAART, /class="momentum-dot is-up" title="67% van de laatste 3 koersbewegingen omhoog"/);
});
it('niet als de schakelaar uit staat', async () => {
await dalend(BTC.id);
await teken(BTC, { spot: 60000, rijen: geschiedenis(59000, 59500), params: { momentum: false } });
assert.equal(STAART, '');
});
it('niet zonder live spot: de lijn eindigt dan op een slotkoers', async () => {
await dalend(BTC.id);
await teken(BTC, { spot: null, rijen: geschiedenis(59000, 59500) });
assert.equal(STAART, '');
});
it('niet bij een ander asset', async () => {
await dalend(GOUD.id);
await teken(BTC, { spot: 60000, rijen: geschiedenis(59000, 59500) });
assert.equal(STAART, '');
});
});