Something went wrong. Try again.
I will pick a better name later.
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122import { describe, expect, it } from "vitest";import { normalizeFeedUrl, parseFeedXml, parseOpmlXml } from "~/lib/rss";
describe("RSS parsing", () => { it("normalizes feed URLs", () => { expect(normalizeFeedUrl("HTTPS://Example.com/feed.xml?utm_source=x#top")).toMatchObject({ url: "https://example.com/feed.xml", normalizedUrl: "https://example.com/feed.xml", }); expect(() => normalizeFeedUrl("not a url")).toThrow("Enter a valid RSS or Atom feed URL."); });
it("parses RSS feeds into normalized items", () => { const feed = parseFeedXml( `<?xml version="1.0"?> <rss version="2.0"> <channel> <title>Example Feed</title> <link>https://example.com/</link> <description>Stories</description> <item> <title>First story</title> <link>/stories/first?utm_campaign=test</link> <guid>story-1</guid> <pubDate>Wed, 01 Jan 2026 10:00:00 GMT</pubDate> <description><![CDATA[<p>Hello<script>alert(1)</script></p>]]></description> </item> </channel> </rss>`, "https://example.com/feed.xml", );
expect(feed.title).toBe("Example Feed"); expect(feed.siteUrl).toBe("https://example.com/"); expect(feed.items).toHaveLength(1); expect(feed.items[0]).toMatchObject({ externalId: "story-1", itemKey: "id:story-1", url: "https://example.com/stories/first", normalizedUrl: "https://example.com/stories/first", title: "First story", }); expect(feed.items[0].summaryHtml).toBe("<p>Hello</p>"); });
it("decodes HTML entities in feed text", () => { const feed = parseFeedXml( `<?xml version="1.0"?> <rss version="2.0"> <channel> <title>Медиа</title> <link>https://example.com/</link> <description>Мы выбираем новости</description> <item> <title>Новость</title> <link>https://example.com/story</link> <description><p>Привет</p></description> </item> </channel> </rss>`, "https://example.com/feed.xml", );
expect(feed.title).toBe("Медиа"); expect(feed.description).toBe("Мы выбираем новости"); expect(feed.items[0].title).toBe("Новость"); expect(feed.items[0].summaryHtml).toBe("<p>Привет</p>"); });
it("parses OPML subscription files", () => { const feeds = parseOpmlXml( `<?xml version="1.0"?> <opml version="2.0"> <body> <outline text="Tech"> <outline text="Example" title="Example Feed" type="rss" xmlUrl="https://example.com/feed.xml?utm_source=opml" htmlUrl="https://example.com/" /> <outline text="Invalid" xmlUrl="not-a-url" /> </outline> <outline text="Atom" xmlUrl="https://example.org/atom.xml" /> </body> </opml>`, );
expect(feeds).toEqual([ { title: "Example Feed", url: "https://example.com/feed.xml", normalizedUrl: "https://example.com/feed.xml", siteUrl: "https://example.com/" }, { title: "Atom", url: "https://example.org/atom.xml", normalizedUrl: "https://example.org/atom.xml", siteUrl: null }, ]); });
it("rejects OPML without feed URLs", () => { expect(() => parseOpmlXml("<opml><body><outline text=\"Folder\" /></body></opml>")).toThrow("OPML file does not contain"); });
it("parses Atom feeds", () => { const feed = parseFeedXml( `<?xml version="1.0"?> <feed xmlns="http://www.w3.org/2005/Atom"> <title>Atom Feed</title> <link href="https://example.org/" /> <subtitle>Updates</subtitle> <entry> <title>Atom story</title> <id>tag:example.org,2026:1</id> <link href="https://example.org/story" /> <updated>2026-01-02T12:00:00Z</updated> <summary><![CDATA[<strong>Summary</strong>]]></summary> </entry> </feed>`, "https://example.org/atom.xml", );
expect(feed.title).toBe("Atom Feed"); expect(feed.description).toBe("Updates"); expect(feed.items[0]).toMatchObject({ externalId: "tag:example.org,2026:1", itemKey: "id:tag:example.org,2026:1", url: "https://example.org/story", title: "Atom story", }); });});