Files
mangabaka-seeder/test/webtoons-source.test.ts
T
Lucas WintherandClaude Opus 5.5 187e85fcaa feat(core): Tapas source (comics and novels), registered
Series by slug, id or episode link; portrait original and banner; age-checked
mature series resolve with a warning and no current covers; Wayback history
keeps only this series' captures.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 11:56:03 +02:00

80 lines
4.3 KiB
TypeScript

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://www.lezhin.com/en/comic/x"))).toBeUndefined();
expect([...allowedFetchHosts()].sort()).toEqual(
["d30womf5coomej.cloudfront.net", "m.tapas.io", "m.webtoons.com", "swebtoon-phinf.pstatic.net", "tapas.io", "us-a.tapas.io",
"web.archive.org", "www.tapas.io", "www.webtoons.com"]);
});
test("isSameSeries checks title_no", () => {
const series = { externalId: "95" } as Parameters<typeof webtoons.isSameSeries>[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);
});