import { describe, expect, test } from "bun:test"; import { renderMarkdown } from "../extract/markdown-renderer"; import { buildHnDocument, buildHnThreadMarkdown, extractHnThreadFromHtml, parseHnItemId, type HnCommentNode, type HnItem, } from "../adapters/hn"; describe("hn adapter helpers", () => { test("parses item id from hn item url", () => { expect(parseHnItemId(new URL("https://news.ycombinator.com/item?id=47534848"))).toBe(47534848); expect(parseHnItemId(new URL("https://news.ycombinator.com/newest"))).toBeNull(); }); test("renders threaded comments with author, time, and nested indentation", () => { const story: HnItem = { id: 47534848, type: "story", by: "mmcclure", time: 1774554485, title: "Example & Title", url: "https://example.com/post", score: 257, descendants: 2, }; const comments: HnCommentNode[] = [ { item: { id: 47535377, type: "comment", by: "jackfruitpeel", time: 1774557334, text: "Root comment

With two paragraphs.", }, children: [ { item: { id: 47535469, type: "comment", by: "__MatrixMan__", time: 1774557848, text: "Nested reply with a relative link.", }, children: [], }, ], }, ]; const body = buildHnThreadMarkdown(story, comments, "https://news.ycombinator.com/item?id=47534848"); expect(body).toContain("Source: [https://example.com/post](https://example.com/post)"); expect(body).toContain("Submitted by mmcclure at 2026-03-26 19:48:05 UTC"); expect(body).toContain("- jackfruitpeel · [2026-03-26 20:35:34 UTC](https://news.ycombinator.com/item?id=47534848#47535377)"); expect(body).toContain(" Root comment"); expect(body).toContain(" With two paragraphs."); expect(body).toContain(" - __MatrixMan__ · [2026-03-26 20:44:08 UTC](https://news.ycombinator.com/item?id=47534848#47535469)"); expect(body).toContain(" Nested reply with a [relative link](https://news.ycombinator.com/item?id=1)."); }); test("extracts story metadata and nested comments from hn html", () => { const parsed = extractHnThreadFromHtml( `
Example story
257 points by mmcclure 1 hour ago 152 comments
Story

body

jackfruitpeel 36 minutes ago
Root
willio58 27 minutes ago
Child
`, "https://news.ycombinator.com/item?id=47534848", ); expect(parsed).not.toBeNull(); expect(parsed?.story.title).toBe("Example story"); expect(parsed?.story.url).toBe("https://example.com/post"); expect(parsed?.story.by).toBe("mmcclure"); expect(parsed?.story.time).toBe(1774554485); expect(parsed?.story.score).toBe(257); expect(parsed?.story.descendants).toBe(152); expect(parsed?.story.text).toContain("Story"); expect(parsed?.comments).toHaveLength(1); expect(parsed?.comments[0]?.item.by).toBe("jackfruitpeel"); expect(parsed?.comments[0]?.children).toHaveLength(1); expect(parsed?.comments[0]?.children[0]?.item.by).toBe("willio58"); }); test("builds hn document with metadata and markdown body", () => { const document = buildHnDocument( { id: 123, type: "story", by: "pg", time: 1175714200, title: "Ask HN: Example", text: "What are you working on?", score: 111, descendants: 0, }, [], "https://news.ycombinator.com/item?id=123", ); const markdown = renderMarkdown(document); expect(document.adapter).toBe("hn"); expect(document.siteName).toBe("Hacker News"); expect(document.publishedAt).toBe("2007-04-04T19:16:40.000Z"); expect(markdown).toContain('adapter: "hn"'); expect(markdown).toContain('siteName: "Hacker News"'); expect(markdown).toContain("# Ask HN: Example"); expect(markdown).toContain("## Post"); expect(markdown).toContain("What are you working on?"); expect(markdown).toContain("## Comments"); expect(markdown).toContain("No comments."); }); });