import { expect, test } from "bun:test"; import { createHeadlessEditor } from "@lexical/headless"; import { $createParagraphNode, $createTextNode, $getRoot, $getSelection, $isRangeSelection, } from "lexical"; import { $createCodeBlockNode, $exportPlan, registry } from "@chopin/dialect"; import { $pasteMarkdown, looksLikeMarkdown, pastedMarkdown, prefersMarkdown, } from "./markdown-paste"; import type { LexicalNode } from "lexical"; const REGISTRY = registry(); function paste(text: string, build: () => LexicalNode = () => $createParagraphNode()) { let editor = createHeadlessEditor({ nodes: REGISTRY.nodes, onError(error) { throw error; }, }); let handled = false; editor.update(() => { let block = build(); $getRoot().append(block); block.selectEnd(); let selection = $getSelection(); if ($isRangeSelection(selection)) handled = $pasteMarkdown(selection, text); }, { discrete: true }); let source = editor.getEditorState().read(() => $exportPlan({ registry: REGISTRY })); return { handled, source }; } test("recognises clear block and inline syntax", () => { for ( let text of [ "## Heading", "- one\n- two", "1. one\n2. two", "- [ ] task", "> quoted", "```ts\nlet a = 1;\n```", "| a | b |\n| - | - |\n| 1 | 2 |", "Some **bold** text", "A [link](https://example.com)", "Run `bun test`", "Not ~~this~~", ] ) expect(looksLikeMarkdown(text), text).toBe(true); }); test("leaves ordinary prose alone", () => { for ( let text of [ "Just a sentence.", "#1 priority is shipping", "5 * 3 * 2 = 30", "a ** b ** c", "- a lone dash line", "snake_case_name and __init", "It costs $5 and $10", "```\nunclosed fence", "Two\n\nparagraphs of prose", ] ) expect(looksLikeMarkdown(text), text).toBe(false); }); test("reads plain text and structureless HTML as Markdown, but not rich HTML", () => { expect(prefersMarkdown(["text/plain"], "")).toBe(true); expect(prefersMarkdown(["text/plain", "text/html"], "
a b
")).toBe(false); expect(prefersMarkdown(["text/plain", "application/x-lexical-editor"], "")).toBe(false); expect(prefersMarkdown(["text/plain", "Files"], "")).toBe(false); expect(prefersMarkdown(["text/html"], "")).toBe(false); }); test("keeps components and expressions literal", () => { let tree = pastedMarkdown( '## Notes\n\n