import { expect, test } from "bun:test"; import { parseSeriesPage } from "../src/core/sources/webtoons/parse"; import { fixtureText } from "./helpers/fixtures"; // Expected URLs are what the fixtures captured on 2026-10-14. If Webtoons changed since and you // re-captured, the fixture is the truth: update these strings to what it now contains. const P = "https://swebtoon-phinf.pstatic.net"; test("official series: poster, square, banner, foreground; title, canonical, flags", async () => { const page = parseSeriesPage(await fixtureText("webtoons/tower-of-god.html"), new URL("https://www.webtoons.com/en/fantasy/tower-of-god/list?title_no=95")); expect(page.title).toBe("Tower of God"); expect(page.canonicalUrl).toBe("https://www.webtoons.com/en/fantasy/tower-of-god/list?title_no=95"); expect(page.canvas).toBe(false); expect(page.mature).toBe(false); expect(page.covers.map((c) => [c.role, c.url])).toEqual([ ["poster", `${P}/20250204_46/17386458444547o1b2_JPEG/95.jpg`], ["square", `${P}/20230324_171/1679612128056zKhRj_JPEG/1TowerOfGod_thumbnail_mobile.jpg`], ["banner", `${P}/20230408_147/1680918244853P9prU_JPEG/0TowerOfGod_landingpage_desktop_bg.jpg`], ["foreground", `${P}/20230324_64/1679612177711Smf0u_PNG/6TowerOfGod_landingpage_desktop_fg.png`], ]); expect(page.covers[0]!.originalUrl).toContain("?type=crop540_540"); expect(page.covers.every((c) => c.current === false)).toBe(true); }); test("canvas series: squares only, hero image deduplicated by URL", async () => { const page = parseSeriesPage(await fixtureText("webtoons/crow-time-canvas.html"), new URL("https://www.webtoons.com/en/canvas/crow-time/list?title_no=693372")); expect(page.canvas).toBe(true); expect(page.covers.map((c) => c.role)).toEqual(["square", "square"]); expect(page.covers[0]!.url).toBe(`${P}/20240408_279/1712548929813q5cnb_JPEG/thumbnail.jpg`); }); test("mature series is flagged", async () => { const page = parseSeriesPage(await fixtureText("webtoons/zombie-revelation-mature.html"), new URL("https://www.webtoons.com/en/action/zombie-revelation-82-08/list?title_no=6065")); expect(page.mature).toBe(true); expect(page.covers.length).toBeGreaterThan(0); }); test("2015 Wayback snapshot: unquoted url(), old naver.net hosts", async () => { const page = parseSeriesPage(await fixtureText("wayback/tower-of-god-2015.html"), new URL("http://www.webtoons.com:80/en/fantasy/tower-of-god/list?title_no=95")); const byRole = Object.fromEntries(page.covers.map((c) => [c.role, c])); expect(byRole.poster!.url).toBe(`${P}/20140710_28/1404979632969uKBA8_JPEG/01_EC8BA0EC9D98ED8391.jpg`); expect(byRole.poster!.originalUrl).toBe("http://swebtoon.phinf.naver.net/20140710_28/1404979632969uKBA8_JPEG/01_EC8BA0EC9D98ED8391.jpg"); expect(byRole.banner!.url).toBe(`${P}/20140915_195/1410759727783HvB4R_JPEG/11_EC8BA0EC9D98ED8391_bg.jpg`); expect(byRole.foreground!.url).toBe(`${P}/20140915_275/1410759723052oa5KP_PNG/11_EC8BA0EC9D98ED8391.png`); }); test("pages without the markup give no covers, not an error", () => { const page = parseSeriesPage("