import { parseJsonLd, parseRumbleViews, parseSearchHtml, resetRumbleNegativeCache, parseCardsHtml, scrapeRumbleList, scrapeRumbleVideo, armRumbleFetchCooldown, resetRumbleFetchCooldown, rumbleFailureForStatus } from '../providers/rumble.mjs'; import { parseRelativeDate } from '../providers/youtube-innertube.mjs'; import { itemDurationSec } from '../search-filters.mjs'; let pass = 0; const eq = (a, b, m) => { const x = JSON.stringify(a), y = JSON.stringify(b); if (x !== y) throw new Error(`${m}\n expected ${y}\n got ${x}`); pass++; console.log(` V ${m}`); }; /** * Comparaison d'horodatage calculé sur l'horloge VIVANTE : le test et le code * appellent `Date.now()` à des instants distincts, donc une égalité à la * milliseconde est une loterie (elle échouait 1 fois sur ~2, selon le tick). * On vérifie l'ordre de grandeur : c'est ce que la fonction promet. */ const eqNear = (iso, expectedMs, toleranceMs, m) => { const got = Date.parse(iso); if (!Number.isFinite(got)) throw new Error(`${m}\n expected ~${new Date(expectedMs).toISOString()}\n got ${iso}`); const delta = Math.abs(got - expectedMs); if (delta > toleranceMs) throw new Error(`${m}\n expected ~${new Date(expectedMs).toISOString()} (±${toleranceMs}ms)\n got ${iso} (écart ${delta}ms)`); pass++; console.log(` V ${m}`); }; // --- parseRumbleViews (bug historique : "1,2K" -> 12) --- eq(parseRumbleViews('1,2K views'), 1200, 'FR compact "1,2K" -> 1200 (was 12)'); eq(parseRumbleViews('1.2M views'), 1200000, 'EN compact "1.2M" -> 1 200 000'); eq(parseRumbleViews('3.4B views'), 3400000000, 'EN compact billions'); eq(parseRumbleViews('12,345 views'), 12345, '"12,345" -> thousands, not 12.345'); eq(parseRumbleViews('3 456'), 3456, 'space-separated thousands'); eq(parseRumbleViews('1 234 567'), 1234567, 'multi-group thousands'); eq(parseRumbleViews('1.5k'), 1500, 'lowercase k'); eq(parseRumbleViews('999'), 999, 'plain number'); eq(parseRumbleViews('vues indisponibles'), undefined, 'non-numeric -> undefined (never 0)'); eq(parseRumbleViews(''), undefined, 'empty -> undefined'); eq(parseRumbleViews(null), undefined, 'null -> undefined'); eq(parseRumbleViews('0 views'), undefined, 'zero -> undefined (phase 2.2: no zero counters)'); // --- DOM parsing (Phase 1.4/1.5/1.6) --- const domHtml = ``; const dom = parseSearchHtml(domHtml, { limit: 10 }); eq(dom.length, 2, 'DOM: both cards parsed'); eq(dom[0].publishedAt, '2026-02-01T15:00:00.000Z', 'DOM: publishedAt from