import { describe, expect, it } from "vitest";
import { readArticleInput } from "./article-input";
import { publicationIssues, safePublicationUrl } from "./publication-preflight";
describe("publication preflight", () => {
it("allows local images and web links but rejects unsafe schemes and credentials", () => {
expect(safePublicationUrl("/images/news.png", true)).toBe(true);
expect(safePublicationUrl("https://example.com/image.png", true)).toBe(
true,
);
for (const url of [
"javascript:alert(1)",
"//external.test/image",
"https://user:password@example.com/a",
"data:text/html,test",
"https:\\example.com",
]) {
expect(safePublicationUrl(url, true)).toBe(false);
}
});
it("checks encoded link schemes without fetching destinations", () => {
const issues = publicationIssues({
kind: "article",
image: "/cover.png",
body: 'bad',
});
expect(issues).toContainEqual({
code: "linksInvalid",
severity: "error",
field: "fullStory",
});
expect(
publicationIssues({
kind: "article",
image: "/cover.png",
body: 'HelpMail',
}),
).toEqual([]);
});
it("rejects reversed event dates and distinguishes a past-date warning", () => {
expect(
publicationIssues({
kind: "event",
startsAt: "2030-01-02",
endsAt: "2030-01-01",
}),
).toEqual(
expect.arrayContaining([
expect.objectContaining({ code: "scheduleInvalid", severity: "error" }),
]),
);
expect(
publicationIssues(
{ kind: "event", startsAt: "2020-01-01" },
Date.parse("2021-01-01"),
),
).toEqual(
expect.arrayContaining([
expect.objectContaining({ code: "schedulePast", severity: "warning" }),
]),
);
});
it("warns for missing image and normalized slug without blocking drafts", () => {
const issues = publicationIssues({
kind: "article",
slug: "Hello World",
normalizedSlug: "hello-world",
});
expect(issues.map((i) => i.code)).toEqual([
"imageMissing",
"slugNormalized",
]);
expect(issues.every((i) => i.severity === "warning")).toBe(true);
});
it("requires a valid scheduled publication date", () => {
expect(
publicationIssues({
kind: "article",
status: "scheduled",
publishAt: "",
}),
).toEqual(
expect.arrayContaining([
expect.objectContaining({ code: "scheduleInvalid" }),
]),
);
});
it("enforces image and link checks in server article input but preserves draft saving", () => {
const form = new FormData();
form.set("title", "News");
form.set("image", "javascript:alert(1)");
expect(() => readArticleInput(form)).toThrow("imageInvalid");
form.set("status", "draft");
expect(readArticleInput(form).image).toBe("javascript:alert(1)");
form.set("status", "published");
form.set("image", "/cover.png");
form.set("fullStory", 'bad');
expect(() => readArticleInput(form)).toThrow("linksInvalid");
});
});