154 lines
6.9 KiB
TypeScript
154 lines
6.9 KiB
TypeScript
import { create } from "@bufbuild/protobuf";
|
|
import { afterEach, describe, expect, it } from "vitest";
|
|
import { AttachmentSchema, MotionMediaFamily, MotionMediaRole, MotionMediaSchema } from "@/types/proto/api/v1/attachment_service_pb";
|
|
import {
|
|
buildManagedAttachmentMarkdown,
|
|
extractManagedAttachmentUIDs,
|
|
filterInlineManagedAttachments,
|
|
findInvalidManagedAttachmentReferences,
|
|
parseManagedAttachmentImageURL,
|
|
removeManagedAttachmentReferences,
|
|
resolveManagedAttachmentImageSource,
|
|
setManagedAttachmentInstanceUrl,
|
|
} from "@/utils/managed-attachment";
|
|
|
|
const attachment = (name: string, filename = "photo.png", externalLink = "") =>
|
|
create(AttachmentSchema, { name, filename, type: "image/png", externalLink });
|
|
|
|
afterEach(() => setManagedAttachmentInstanceUrl(undefined));
|
|
|
|
describe("managed attachment Markdown", () => {
|
|
it("builds canonical URLs and escapes filename alt text", () => {
|
|
expect(buildManagedAttachmentMarkdown(attachment("attachments/image-one", "garden[west]\\view.png"))).toBe(
|
|
"![garden\\[west\\]\\\\view](/file/attachments/image-one)",
|
|
);
|
|
});
|
|
|
|
it("extracts canonical, legacy, and reference-style images", () => {
|
|
const content = [
|
|
"",
|
|
"",
|
|
"![three][photo]",
|
|
"[photo]: /file/attachments/image-three",
|
|
"``",
|
|
"```md\n\n```",
|
|
].join("\n\n");
|
|
|
|
expect(Array.from(extractManagedAttachmentUIDs(content))).toEqual(["image-one", "image-two", "image-three"]);
|
|
expect(findInvalidManagedAttachmentReferences(content)).toEqual([]);
|
|
});
|
|
|
|
it("uses the first duplicate reference definition like Goldmark", () => {
|
|
const content = ["![photo][asset]", "[asset]: /file/attachments/first-image", "[asset]: /file/attachments/second-image"].join("\n\n");
|
|
|
|
expect(Array.from(extractManagedAttachmentUIDs(content))).toEqual(["first-image"]);
|
|
});
|
|
|
|
it("does not normalize path-relative images into managed attachment URLs", () => {
|
|
const source = "file/attachments/image-one";
|
|
|
|
expect(parseManagedAttachmentImageURL(source)).toBeUndefined();
|
|
expect(extractManagedAttachmentUIDs(``)).toEqual(new Set());
|
|
expect(resolveManagedAttachmentImageSource(source, [attachment("attachments/image-one")])).toBe(source);
|
|
});
|
|
|
|
// The API matches absolute URLs against the configured instance URL, not the
|
|
// browsing origin, and rejects them outright when no instance URL is set.
|
|
it("resolves absolute images against the configured instance URL", () => {
|
|
setManagedAttachmentInstanceUrl("https://memos.example.com");
|
|
const content = [
|
|
"",
|
|
``,
|
|
"",
|
|
].join("\n\n");
|
|
|
|
expect(Array.from(extractManagedAttachmentUIDs(content))).toEqual(["image-one"]);
|
|
expect(findInvalidManagedAttachmentReferences(content)).toEqual([]);
|
|
});
|
|
|
|
// Matches the API, which errors on any absolute URL under the managed route
|
|
// when it has no instance URL to compare against — whoever hosts it.
|
|
it("rejects absolute images when no instance URL is configured", () => {
|
|
const own = `${window.location.origin}/file/attachments/image-one`;
|
|
const content = [``, ""].join("\n\n");
|
|
|
|
expect(extractManagedAttachmentUIDs(content)).toEqual(new Set());
|
|
expect(findInvalidManagedAttachmentReferences(content)).toEqual([own, "https://example.com/file/attachments/image-two"]);
|
|
});
|
|
|
|
it("reports raw HTML managed images as unsupported instead of ignoring them", () => {
|
|
const raw = '<img src="/file/attachments/html-image">';
|
|
|
|
expect(extractManagedAttachmentUIDs(raw)).toEqual(new Set());
|
|
expect(findInvalidManagedAttachmentReferences(raw)).toEqual([raw]);
|
|
// Code spans and fences are not raw HTML, so they stay out of the way.
|
|
expect(findInvalidManagedAttachmentReferences('`<img src="/file/attachments/html-image">`')).toEqual([]);
|
|
});
|
|
|
|
it("rejects encoded, protocol-relative, queried, and malformed managed paths", () => {
|
|
for (const url of [
|
|
"/file/attachments/%69mage",
|
|
"//localhost/file/attachments/image",
|
|
"/file/attachments/image?token=x",
|
|
"/file/attachments/image#fragment",
|
|
"/file/attachments/image/",
|
|
"/file/attachments/image/file/extra",
|
|
]) {
|
|
expect(parseManagedAttachmentImageURL(url)).toBeUndefined();
|
|
expect(findInvalidManagedAttachmentReferences(``)).toEqual([url]);
|
|
}
|
|
});
|
|
|
|
it("removes only matching image nodes and leaves code and other content intact", () => {
|
|
const content = [
|
|
"before",
|
|
"",
|
|
"",
|
|
"",
|
|
"keep  here",
|
|
"",
|
|
"``",
|
|
].join("\n");
|
|
|
|
expect(removeManagedAttachmentReferences(content, new Set(["remove-me"]))).toBe(
|
|
["before", "", "keep  here", "", "``"].join("\n"),
|
|
);
|
|
});
|
|
|
|
it("filters the entire motion group when its still image is inline", () => {
|
|
const motion = create(MotionMediaSchema, {
|
|
family: MotionMediaFamily.APPLE_LIVE_PHOTO,
|
|
role: MotionMediaRole.STILL,
|
|
groupId: "live-one",
|
|
});
|
|
const still = create(AttachmentSchema, { ...attachment("attachments/still"), motionMedia: motion });
|
|
const video = create(AttachmentSchema, {
|
|
name: "attachments/video",
|
|
filename: "photo.mov",
|
|
type: "video/quicktime",
|
|
motionMedia: create(MotionMediaSchema, { ...motion, role: MotionMediaRole.VIDEO }),
|
|
});
|
|
const regular = attachment("attachments/regular");
|
|
|
|
expect(filterInlineManagedAttachments("", [still, video, regular])).toEqual([regular]);
|
|
});
|
|
|
|
it("filters repeated references once and never hides an unrelated attachment", () => {
|
|
const own = attachment("attachments/own");
|
|
const content = [
|
|
"",
|
|
"",
|
|
"",
|
|
].join("\n\n");
|
|
|
|
expect(filterInlineManagedAttachments(content, [own, attachment("attachments/unrelated")])).toEqual([
|
|
attachment("attachments/unrelated"),
|
|
]);
|
|
});
|
|
|
|
it("resolves managed image sources through attachment metadata", () => {
|
|
const shared = attachment("attachments/shared", "shared.png", `${window.location.origin}/file/attachments/shared?share_token=abc`);
|
|
expect(resolveManagedAttachmentImageSource("/file/attachments/shared", [shared])).toBe(shared.externalLink);
|
|
expect(resolveManagedAttachmentImageSource("https://example.com/image.png", [shared])).toBe("https://example.com/image.png");
|
|
});
|
|
});
|