// Webmentions, the pure half (shared/mentions.ts): reading a source page as
// microformats2, checking it links to the target, finding a site's endpoint,
// and listing a note's links to other sites the way the reading view renders
// them. No network: the fixtures under tests/fixtures/mentions are read from
// disk with the target's address written in.
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import path from "node:path";
import { describe, it } from "node:test";
import { fileURLToPath } from "node:url";
import { discoverEndpoint, linksTo, normalizeUrl, outboundLinks, parseMention, sameUrl } from "../shared/mentions.ts";
const FIX = path.join(path.dirname(fileURLToPath(import.meta.url)), "fixtures", "mentions");
const TARGET = "https://blog.example/Essays/On%20Reading";
const SOURCE = "https://other.example/posts/1";
const fixture = (name: string): string => readFileSync(path.join(FIX, name), "utf8").replaceAll("{{TARGET}}", TARGET);
describe("reading a source as microformats2", () => {
it("a u-like-of the target is a like, with the h-card author and its photo", () => {
const m = parseMention(fixture("like.html"), SOURCE, TARGET);
assert.equal(m.type, "like");
assert.equal(m.author?.name, "Layla Reader");
assert.equal(m.author?.url, "https://liker.example");
assert.equal(m.author?.photo, "https://other.example/me.jpg");
assert.equal(m.url, "https://other.example/likes/1");
});
it("a u-repost-of an h-cite whose u-url is the target is a repost", () => {
const m = parseMention(fixture("repost.html"), SOURCE, TARGET);
assert.equal(m.type, "repost");
assert.equal(m.author?.name, "Omar");
assert.equal(m.author?.url, "https://omar.example");
});
it("a u-in-reply-to the target is a reply, its e-content the summary", () => {
const m = parseMention(fixture("reply.html"), SOURCE, TARGET);
assert.equal(m.type, "reply");
assert.equal(m.author?.name, "Sam Writer");
assert.equal(m.summary, "I disagree with the second paragraph, kindly. The rest is right.");
assert.equal(m.published, Date.parse("2026-09-01T10:00:00Z"));
});
it("an entry that only links is a mention; a nested entry's reply is not its own; the page's h-card is the author", () => {
const m = parseMention(fixture("mention.html"), SOURCE, TARGET);
assert.equal(m.type, "mention");
assert.equal(m.name, "Links of the week");
assert.equal(m.author?.name, "The Weekly");
});
it("a page with no h-entry is a mention named by its title", () => {
const m = parseMention(fixture("plain.html"), SOURCE, TARGET);
assert.equal(m.type, "mention");
assert.equal(m.author, null);
assert.equal(m.name, "Just a page");
});
it("a like of ANOTHER page is not a like of this one", () => {
const html = fixture("like.html").replace(`class="u-like-of" href="${TARGET}"`, 'class="u-like-of" href="https://elsewhere.example/x"');
assert.equal(parseMention(html, SOURCE, TARGET).type, "mention");
});
});
describe("does the source link to the target", () => {
it("an anchor counts, spelled with a trailing slash or a fragment or decoded", () => {
assert.equal(linksTo(fixture("like.html"), SOURCE, [TARGET]), true);
assert.equal(linksTo(`x`, SOURCE, [TARGET]), true);
assert.equal(linksTo('x', SOURCE, [TARGET]), true);
});
it("an address spelled out in words does not", () => {
assert.equal(linksTo(fixture("nolink.html"), SOURCE, [TARGET]), false);
});
it("a relative link resolves against the source", () => {
assert.equal(linksTo('x', "https://blog.example/other", [TARGET]), true);
});
});
describe("finding an endpoint", () => {
const page = "https://site.example/a/post";
it("the Link header wins, relative to the page", () => {
assert.equal(discoverEndpoint('; rel="other", ; rel="webmention"', "", page), "https://site.example/wm");
assert.equal(discoverEndpoint('; rel="webmention nofollow"', "", page), "https://site.example/a/wm?x=1");
});
it("then the first or with rel=webmention, in document order", () => {
assert.equal(discoverEndpoint(null, 'x', page), "https://site.example/a-first");
assert.equal(discoverEndpoint(null, '', page), "https://wm.example/e");
});
it("an empty href is the page itself; none is null", () => {
assert.equal(discoverEndpoint(null, '', page), page);
assert.equal(discoverEndpoint(null, "
nothing
", page), null);
assert.equal(discoverEndpoint('; rel="webmention"', "", page), null);
});
});
describe("a note's links to other sites", () => {
it("reads the renderer's three spellings and a raw anchor, once each", () => {
const md = [
"---",
"source: https://front.example/matter",
"---",
"See [the essay](https://a.example/essay) and .",
"Bare https://c.example/bare, then (https://d.example/paren).",
'raw and again [twice](https://a.example/essay).',
" is not a link.",
"[[A wikilink]] is the vault's; `https://code.example/inline` is code.",
"```",
"https://code.example/fenced",
"```",
].join("\n");
assert.deepEqual(outboundLinks(md), [
"https://a.example/essay",
"https://b.example/auto",
"https://c.example/bare",
"https://d.example/paren",
"https://e.example/raw",
]);
});
});
describe("addresses", () => {
it("normalise and compare", () => {
assert.equal(normalizeUrl("HTTPS://Blog.Example/a/#x"), "https://blog.example/a");
assert.equal(normalizeUrl("javascript:alert(1)"), null);
assert.equal(sameUrl("https://blog.example/%D9%85", "https://blog.example/م"), true);
});
});