Something went wrong. Try again.
a tool for shared writing and social publishing
Something went wrong. Try again.
TypeScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143import { describe, expect, test } from "vitest";
import { documentToBlockFacts } from "./documentToBlockFacts";
const DID = "did:plc:m2sjv3wncvsasdapla35hzwj";const BASE = "https://leaflet.pub";
const wrap = (...blocks: unknown[]) => blocks.map((block) => ({ $type: "pub.leaflet.pages.linearDocument#block", block, }));
const attrs = (facts: Array<{ attribute: string }>) => facts.map((f) => f.attribute);
const valueOf = (facts: Array<{ attribute: string; data: any }>, a: string) => facts.find((f) => f.attribute === a)?.data;
describe("documentToBlockFacts", () => { test("a text block becomes a typed block with Yjs text", () => { const out = documentToBlockFacts( wrap({ $type: "pub.leaflet.blocks.text", plaintext: "hello" }), DID, BASE, ); expect(out).not.toBeNull(); expect(attrs(out![0].facts)).toEqual(["block/type", "block/text"]); expect(valueOf(out![0].facts, "block/type").value).toBe("text"); // The Yjs update is base64; decoding is covered in richtextToYjs.test. expect(typeof valueOf(out![0].facts, "block/text").value).toBe("string"); });
test("a header carries its level", () => { const out = documentToBlockFacts( wrap({ $type: "pub.leaflet.blocks.header", plaintext: "Title", level: 2, }), DID, BASE, )!; expect(valueOf(out[0].facts, "block/type").value).toBe("heading"); expect(valueOf(out[0].facts, "block/heading-level").value).toBe(2); });
test("code keeps its language and body", () => { const out = documentToBlockFacts( wrap({ $type: "pub.leaflet.blocks.code", plaintext: "let x = 1", language: "typescript", }), DID, BASE, )!; expect(valueOf(out[0].facts, "block/code").value).toBe("let x = 1"); expect(valueOf(out[0].facts, "block/code-language").value).toBe( "typescript", ); });
test("an image becomes a proxied src with its intrinsic size", () => { const out = documentToBlockFacts( wrap({ $type: "pub.leaflet.blocks.image", image: { ref: { $link: "bafkreiabc" } }, aspectRatio: { width: 800, height: 600 }, alt: "a plant", }), DID, BASE, )!; const image = valueOf(out[0].facts, "block/image"); expect(image.width).toBe(800); expect(image.height).toBe(600); // uploadImage re-fetches this src on the next publish, so it has to be a // real URL rather than a bare CID. expect(image.src).toContain("/api/atproto_images?did="); expect(image.src).toContain("bafkreiabc"); expect(valueOf(out[0].facts, "image/alt").value).toBe("a plant"); });
test("an image with no aspect ratio refuses rather than guessing a size", () => { expect( documentToBlockFacts( wrap({ $type: "pub.leaflet.blocks.image", image: { ref: { $link: "bafkreiabc" } }, }), DID, BASE, ), ).toBeNull(); });
test("horizontal rule needs only its type", () => { const out = documentToBlockFacts( wrap({ $type: "pub.leaflet.blocks.horizontalRule" }), DID, BASE, )!; expect(attrs(out[0].facts)).toEqual(["block/type"]); });
test("one unrepresentable block refuses the whole document", () => { // Adopting partially would drop the list from the author's published post // on the next save. expect( documentToBlockFacts( wrap( { $type: "pub.leaflet.blocks.text", plaintext: "intro" }, { $type: "pub.leaflet.blocks.unorderedList", children: [] }, ), DID, BASE, ), ).toBeNull(); });
test("blocks keep their order", () => { const out = documentToBlockFacts( wrap( { $type: "pub.leaflet.blocks.header", plaintext: "One", level: 1 }, { $type: "pub.leaflet.blocks.text", plaintext: "Two" }, { $type: "pub.leaflet.blocks.horizontalRule" }, ), DID, BASE, )!; expect(out.map((b) => valueOf(b.facts, "block/type").value)).toEqual([ "heading", "text", "horizontal-rule", ]); });
test("a non-array body is refused", () => { expect(documentToBlockFacts(undefined, DID, BASE)).toBeNull(); });});