// Generated, do not edit. sha256:81c29b0b17dff467d076992c914cba7f0090a65799888be3dc9347a850d7de68 import { describe, it } from 'node:test'; import assert from 'node:assert/strict'; import { trapPunten, magnetischeIndex, withSpot, mountSparkline, changePctHtml, smoothPath, labelLeft } from '../../client/ui/sparkline.js'; const p = (px, py) => ({ px, py }); describe('trapPunten', () => { it('laat een vlakke reeks ongemoeid', () => { const in_ = [p(0, 50), p(10, 50), p(20, 50)]; assert.deepEqual(trapPunten(in_), in_); }); it('zet bij een stap een hoekpunt op de x van de NIEUWE waarde', () => { const uit = trapPunten([p(0, 80), p(10, 40)]); assert.deepEqual(uit, [p(0, 80), p(10, 80), p(10, 40)]); }); it('werkt net zo omlaag, want een verkoop verlaagt de inleg', () => { const uit = trapPunten([p(0, 40), p(10, 80)]); assert.deepEqual(uit, [p(0, 40), p(10, 40), p(10, 80)]); }); it('doet niets bij een verschil onder de drempel', () => { const in_ = [p(0, 50), p(10, 50.005)]; assert.deepEqual(trapPunten(in_), in_); }); it('houdt de eerste en de laatste waarde op hun plek', () => { const uit = trapPunten([p(0, 90), p(5, 60), p(10, 20)]); assert.deepEqual(uit[0], p(0, 90)); assert.deepEqual(uit[uit.length - 1], p(10, 20)); assert.equal(uit.length, 5, 'drie punten met twee stappen worden vijf hoekpunten'); }); it('valt niet om op een lege of eenpuntige reeks', () => { assert.deepEqual(trapPunten([]), []); assert.deepEqual(trapPunten([p(0, 1)]), [p(0, 1)]); }); }); describe('labelLeft: het label blijft binnen de grafiek', () => { it('staat op het punt als er ruimte is', () => { assert.equal(labelLeft(115, 100, 230), 115); }); it('schuift naar binnen aan beide randen, met 4 px afstand', () => { assert.equal(labelLeft(5, 200, 230), 104); assert.equal(labelLeft(225, 200, 230), 126); }); it('staat in het midden als het label breder is dan de grafiek', () => { assert.equal(labelLeft(10, 260, 230), 115); }); }); describe('magnetischeIndex', () => { const reeks = Array.from({ length: 11 }, (_, i) => ({ fx: i / 10 })); it('geeft het dichtstbijzijnde punt als er geen markering in de buurt is', () => { assert.equal(magnetischeIndex(reeks, [8], 0.22, 0.03), 2); }); it('klikt op de markering zodra die binnen de tolerantie ligt', () => { assert.equal(magnetischeIndex(reeks, [5], 0.52, 0.03), 5); assert.equal(magnetischeIndex(reeks, [5], 0.48, 0.03), 5); }); it('laat de markering NIET winnen buiten de tolerantie', () => { assert.equal(magnetischeIndex(reeks, [5], 0.30, 0.03), 3); }); it('kiest de dichtstbijzijnde markering als er twee in de buurt liggen', () => { assert.equal(magnetischeIndex(reeks, [4, 5], 0.49, 0.05), 5); }); it('wint binnen de tolerantie ook van een punt dat NÓG dichterbij ligt', () => { assert.equal(magnetischeIndex(reeks, [5], 0.401, 0.11), 5); }); it('valt terug op het dichtstbijzijnde punt zonder markeringen', () => { assert.equal(magnetischeIndex(reeks, [], 0.71, 0.03), 7); }); it('negeert een markering die buiten de reeks wijst', () => { assert.equal(magnetischeIndex(reeks, [99], 0.31, 0.5), 3); }); }); describe('withSpot', () => { const DAG = 86400000; const dag = iso => Date.parse(`${iso}T00:00:00Z`); const nu = dag('2026-09-04') + 13 * 3600000; const week = [ [dag('2026-09-01'), 100], [dag('2026-09-02'), 110], [dag('2026-09-03'), 120], [dag('2026-09-04'), 130], ]; it('vervangt de waarde van vandaag en maakt de reeks niet langer', () => { const uit = withSpot(week, 140, nu); assert.equal(uit.length, week.length); assert.deepEqual(uit[uit.length - 1], [dag('2026-09-04'), 140]); assert.deepEqual(uit.slice(0, -1), week.slice(0, -1)); }); it('houdt het punt op middernacht van die dag en zet er geen tijdstip in', () => { assert.equal(withSpot(week, 140, nu).at(-1)[0] % DAG, 0); }); it('voegt vandaag toe als de reeks bij gisteren ophoudt', () => { const totGisteren = week.slice(0, -1); const uit = withSpot(totGisteren, 140, nu); assert.equal(uit.length, totGisteren.length + 1); assert.deepEqual(uit.at(-1), [dag('2026-09-04'), 140]); }); it('laat een reeks die voorbij vandaag loopt ongemoeid', () => { const toekomst = [...week, [dag('2026-09-05'), 150]]; assert.equal(withSpot(toekomst, 140, nu), toekomst); }); it('geeft de reeks onveranderd terug zonder bruikbare prijs', () => { for (const prijs of [null, undefined, NaN, 0, -1, '140']) { assert.equal(withSpot(week, prijs, nu), week); } }); it('geeft een lege of ontbrekende reeks onveranderd terug', () => { assert.deepEqual(withSpot([], 140, nu), []); assert.equal(withSpot(null, 140, nu), null); }); }); describe('mountSparkline', () => { const DAG = 86400000; const punten = [[0, 100], [DAG, 110], [2 * DAG, 105]]; function bouw(opties = {}) { const leeg = () => ({ className: '', innerHTML: '', appendChild() {}, querySelector: () => ({}), addEventListener() {} }); globalThis.document = { createElement: leeg }; const host = { innerHTML: '', appendChild(kind) { this.kind = kind; } }; mountSparkline(host, punten, opties); return host.kind.innerHTML; } it('zet de markeringen, de hover-lijn en het puntje ín de kliplaag', () => { const html = bouw({ markers: [{ x: 0, variant: 'buy' }] }); const clip = html.indexOf('
'); const label = html.indexOf('
= 0 && label > clip, 'de kliplaag hoort te bestaan en vóór het label te staan'); const binnen = html.slice(clip, label); for (const deel of ['spark-mini-mark', 'spark-mini-vline', 'spark-mini-dot']) { assert.ok(binnen.includes(deel), `${deel} hoort in de kliplaag te zitten`); } assert.equal((binnen.match(/
/g) || []).length, 'de kliplaag hoort gesloten te zijn vóór het prijslabel'); }); it('houdt het prijslabel BUITEN de kliplaag', () => { const html = bouw(); const label = html.indexOf('
0); assert.equal(html.slice(label).includes('spark-mini-dot'), false); assert.equal(html.slice(label).includes('spark-mini-clip'), false); }); it('geeft een markering op het eerste punt zijn echte x en schuift hem niet naar binnen', () => { const html = bouw({ markers: [{ x: 0, variant: 'buy' }] }); assert.match(html, /spark-mini-mark is-buy[^>]*--spark-x:0\.00%/); }); it('draagt de sleutel van een markering mee als `data-mark`', () => { const html = bouw({ markers: [{ x: DAG, variant: 'sell', key: 't42' }] }); assert.match(html, /data-mark="t42"/); }); it('maakt het vak leeg bij te weinig punten', () => { const leeg = () => ({ className: '', innerHTML: '', appendChild() {}, querySelector: () => ({}), addEventListener() {} }); globalThis.document = { createElement: leeg }; const host = { innerHTML: 'oud', appendChild() { throw new Error('er hoort niets gemount te worden'); } }; mountSparkline(host, [[0, 100]]); assert.equal(host.innerHTML, ''); }); }); describe('changePctHtml: de verandering als pil', () => { it('geeft een stijging een groene pil, met het teken uit de instelling', () => { assert.equal(changePctHtml([[0, 100], [1, 110]]), '10,0%'); assert.match(changePctHtml([[0, 100], [1, 110]], '', { signs: 'both' }), />\+10,0%10,0% { assert.match(changePctHtml([[0, 100], [1, 90]]), /class="spark-change delta t-neg">−10,0% { assert.equal(changePctHtml([[0, 100]]), ''); assert.equal(changePctHtml([[0, 0], [1, 5]]), ''); }); it('zet een staart ín de pil, achter het getal', () => { assert.equal(changePctHtml([[0, 100], [1, 110]], ''), '10,0%'); }); it('fine: een tweede decimaal als één decimaal de beweging verbergt', () => { assert.match(changePctHtml([[0, 10000], [1, 10003]], '', { fine: true }), />0,03%0,0% { const p = ys => ys.map((py, i) => ({ px: i * 10, py })); const segmenten = d => [...d.matchAll(/C[\d.-]+,([\d.-]+) [\d.-]+,([\d.-]+) [\d.-]+,([\d.-]+)/g)] .map(m => m.slice(1).map(Number)); it('loopt door elk punt', () => { const ys = [50, 20, 80, 10, 60]; const d = smoothPath(p(ys)); assert.match(d, /^M0\.00,50\.00 /); assert.deepEqual(segmenten(d).map(s => s[2]), ys.slice(1)); }); it('schiet nergens door: de controlepunten blijven binnen hun segment', () => { const ys = [50, 20, 80, 10, 60, 61, 95, 94, 3]; segmenten(smoothPath(p(ys))).forEach(([c1, c2], i) => { const lo = Math.min(ys[i], ys[i + 1]) - 1e-9, hi = Math.max(ys[i], ys[i + 1]) + 1e-9; assert.ok(c1 >= lo && c1 <= hi && c2 >= lo && c2 <= hi, `segment ${i}: ${c1}, ${c2} buiten [${lo}, ${hi}]`); }); }); it('een top of dal krijgt een vlakke raaklijn', () => { const [s0, s1] = segmenten(smoothPath(p([50, 20, 80]))); assert.equal(s0[1], 20); assert.equal(s1[0], 20); }); it('twee punten zijn een rechte lijn, en geen punten geen pad', () => { assert.equal(smoothPath(p([10, 20])), 'M0.00,10.00 L10.00,20.00'); assert.equal(smoothPath([]), ''); }); it('mountSparkline tekent standaard rond (overal, gebruiker 08-10-2026), en recht met `smooth: false`', () => { const leeg = () => ({ className: '', innerHTML: '', appendChild() {}, querySelector: () => ({}), addEventListener() {} }); globalThis.document = { createElement: leeg }; const teken = opties => { const host = { innerHTML: '', appendChild(kind) { this.kind = kind; } }; mountSparkline(host, [[0, 100], [1, 110], [2, 105]], opties); return host.kind.innerHTML; }; assert.match(teken({}), /