import { expect, test } from "bun:test"; import { allowedFetchHosts, findSource } from "../src/core/sources/registry"; import { SourceError } from "../src/core/sources/source"; import { webtoons } from "../src/core/sources/webtoons"; import { FakeTransport, text } from "./helpers/fake-transport"; import { fixtureText } from "./helpers/fixtures"; const TOG = "https://www.webtoons.com/-/-/-/list?title_no=95"; test("resolve fetches the /-/-/-/ form and reads the canonical page", async () => { const fake = new FakeTransport().on(TOG, async () => text(await fixtureText("webtoons/tower-of-god.html"))); const series = await webtoons.resolve(new URL("https://m.webtoons.com/en/made-up-slug/list?title_no=95"), { transport: fake }); expect(fake.urls()).toEqual([TOG]); expect(series).toEqual({ source: "webtoons", externalId: "95", title: "Tower of God", language: "en", canonicalUrl: "https://www.webtoons.com/en/fantasy/tower-of-god/list?title_no=95", archiveUrls: [ "www.webtoons.com/en/fantasy/tower-of-god/list?title_no=95", "m.webtoons.com/en/fantasy/tower-of-god/list?title_no=95", ], mature: false, flags: { canvas: false }, }); }); test("currentCovers reuses the page fetched by resolve and marks covers current", async () => { const fake = new FakeTransport().on(TOG, async () => text(await fixtureText("webtoons/tower-of-god.html"))); const series = await webtoons.resolve(new URL(TOG), { transport: fake }); const covers = await webtoons.currentCovers(series, { transport: fake }); expect(fake.calls.length).toBe(1); expect(covers.map((c) => c.role)).toEqual(["poster", "square", "banner", "foreground"]); expect(covers.every((c) => c.current)).toBe(true); }); test("canvas and mature flags come through", async () => { const fake = new FakeTransport() .on("https://www.webtoons.com/-/canvas/-/list?title_no=693372", async () => text(await fixtureText("webtoons/crow-time-canvas.html"))) .on("https://www.webtoons.com/-/-/-/list?title_no=6065", async () => text(await fixtureText("webtoons/zombie-revelation-mature.html"))); const canvas = await webtoons.resolve(new URL("https://www.webtoons.com/en/canvas/crow-time/list?title_no=693372"), { transport: fake }); expect(canvas.flags.canvas).toBe(true); const mature = await webtoons.resolve(new URL("https://www.webtoons.com/-/-/-/list?title_no=6065"), { transport: fake }); expect(mature.mature).toBe(true); }); test("a canvas series linked in the /-/-/-/ form: the official form 404s, the canvas form is tried", async () => { const fake = new FakeTransport() .on("https://www.webtoons.com/-/-/-/list?title_no=693372", () => text("not found", 404)) .on("https://www.webtoons.com/-/canvas/-/list?title_no=693372", async () => text(await fixtureText("webtoons/crow-time-canvas.html"))); const series = await webtoons.resolve(new URL("https://www.webtoons.com/-/-/-/list?title_no=693372"), { transport: fake }); expect(fake.urls()).toEqual([ "https://www.webtoons.com/-/-/-/list?title_no=693372", "https://www.webtoons.com/-/canvas/-/list?title_no=693372", ]); expect(series.flags.canvas).toBe(true); expect(series.canonicalUrl).toContain("/en/canvas/crow-time/"); }); test("a Webtoons error is a SourceError with the status", async () => { const fake = new FakeTransport().on(() => true, () => text("gone", 404)); await expect(webtoons.resolve(new URL(TOG), { transport: fake })).rejects.toThrow(SourceError); await expect(webtoons.resolve(new URL(TOG), { transport: fake })).rejects.toThrow(/404/); }); test("registry finds the source and builds the proxy allowlist", () => { expect(findSource(new URL("https://www.webtoons.com/en/x/list?title_no=1"))).toBe(webtoons); expect(findSource(new URL("https://tapas.io/series/x"))).toBeUndefined(); expect([...allowedFetchHosts()].sort()).toEqual( ["m.webtoons.com", "swebtoon-phinf.pstatic.net", "web.archive.org", "www.webtoons.com"]); }); test("isSameSeries checks title_no", () => { const series = { externalId: "95" } as Parameters[1]; expect(webtoons.isSameSeries("https://www.webtoons.com/-/-/-/list?title_no=95", series)).toBe(true); expect(webtoons.isSameSeries("https://www.webtoons.com/-/-/-/list?title_no=318", series)).toBe(false); });