Versie 0.2.0
This commit is contained in:
commit
e77d8fd373
495 files changed
+81026
No files matched your search
@@ -0,0 +1,227 @@
|
||||
// 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, '');
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user