import assert from 'node:assert/strict'; import { readFileSync } from 'node:fs'; import { dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; import { describe, it } from 'node:test'; import { hasIgnoredLabel, parseBoard, parsePageCount, parseThreadId } from '../src/lib/forum.js'; const here = dirname(fileURLToPath(import.meta.url)); const BOARD = readFileSync(join(here, 'fixtures', 'board.html'), 'utf8'); describe('Board-Übersicht lesen', () => { const threads = parseBoard(BOARD); it('liest die Threads der Liste', () => { assert.equal(threads.length, 5); assert.deepEqual( threads.map((t) => t.threadId), [7001, 7002, 7004, 7005, 7003], ); }); it('nimmt den Ersteller aus der Übersicht, nicht den letzten Antwortenden', () => { const first = threads.find((t) => t.threadId === 7001); assert.equal(first?.authorName, 'Antragsteller'); assert.equal(first?.authorForumId, 501, 'die Forum-ID überlebt Namensänderungen'); }); it('löst Umlaut- und Anführungszeichen-Entities auf', () => { const second = threads.find((t) => t.threadId === 7002); assert.equal(second?.title, 'Custom Character Redesign | Alt zu Neu'); }); it('erkennt einen zurückgelassenen Verweis als solchen', () => { const stub = threads.find((t) => t.threadId === 7003); assert.equal(stub?.isMovedStub, true); }); it('ignoriert Unterforen und deren letzte Beiträge', () => { // 9999 steht im wbbBoardNode-Kasten, gehoert aber nicht in dieses Board. assert.equal(threads.some((t) => t.threadId === 9999), false); }); it('liest die Gesamtzahl der Seiten, nicht nur die verlinkten', () => { // Die Blätterleiste verlinkt hier nur Seite 2 und 3, das Board hat aber 25. // Wer die Links auswertet, liest den grössten Teil des Archivs nie. assert.equal(parsePageCount(BOARD), 25); }); it('fällt ohne das Element auf die Links zurück', () => { assert.equal(parsePageCount('7'), 7); assert.equal(parsePageCount('ohne Blaetterleiste'), 1); }); it('kommt mit einer leeren Seite klar', () => { assert.deepEqual(parseBoard('nichts'), []); }); }); describe('Ankündigungen aussortieren', () => { const threads = parseBoard(BOARD); it('liest die Labels eines Threads', () => { const regelwerk = threads.find((t) => t.threadId === 7004); assert.deepEqual(regelwerk?.labels, ['Wichtig', 'Global']); }); it('erkennt Wichtig und Information als Ausschluss', () => { assert.equal(hasIgnoredLabel(threads.find((t) => t.threadId === 7004)!), true); assert.equal(hasIgnoredLabel(threads.find((t) => t.threadId === 7005)!), true); }); it('lässt echte Anträge durch, auch mit anderen Labels', () => { assert.equal(hasIgnoredLabel(threads.find((t) => t.threadId === 7001)!), false); }); it('wertet den Titel nicht aus', () => { // "The Duke Rang Update" war ein echter Antrag, "Regelwerk" keiner. Am Titel // ist das nicht zu erkennen - deshalb entscheidet allein das Label. const ohneStichwort = parseBoard(BOARD).find((t) => t.threadId === 7005); assert.ok(ohneStichwort, 'auch ein Thread ohne CC im Titel wird gelesen'); }); }); describe('Thread-Verweis lesen', () => { it('nimmt den vollen Link', () => { assert.equal( parseThreadId('https://senseigaming.de/forum/index.php?thread/8041-cc-antrag-feldagent/'), 8041, ); }); it('nimmt auch nur den Pfad oder die blanke Nummer', () => { assert.equal(parseThreadId('index.php?thread/7001-irgendwas/'), 7001); assert.equal(parseThreadId('7001'), 7001); }); it('weist Unbrauchbares ab', () => { assert.equal(parseThreadId(''), null); assert.equal(parseThreadId('kein link'), null); }); });