import "jest"; import { Schema } from "prosemirror-model"; import { fragmentFactoryFactory, markFactoryFactory, nodeFactoryFactory, parseText, PosRefBox, RefsFragment, RefsNode } from "../composing"; import "./expect.toMatchNode"; const schema = new Schema({ nodes: { doc: { content: "group1+" }, node1: { attrs: { foo: { default: undefined } }, group: "group1", content: "text*" }, node2: { group: "group1", content: "node1" }, text: {} }, marks: { mark1: {}, mark2: {} } }); const ref = (name: symbol | string) => new PosRefBox(name); describe(parseText.name, () => { it("empty string", () => { const nodes = parseText(""); expect(nodes).toMatchObject([""]); }); it("single inline ref", () => { const nodes = parseText("{a}"); expect(nodes).toMatchObject(["", ref("a"), ""]); }); it("multiple inline refs", () => { const nodes = parseText("{a}{b}"); expect(nodes).toMatchObject(["", ref("a"), "", ref("b"), ""]); }); it("text", () => { const nodes = parseText("0"); expect(nodes).toMatchObject(["0"]); }); it("ref at the start of a string", () => { const nodes = parseText("{a}0"); expect(nodes).toMatchObject(["", ref("a"), "0"]); }); it("multiple refs at the start of a string", () => { const nodes = parseText("{a}{b}0"); expect(nodes).toMatchObject(["", ref("a"), "", ref("b"), "0"]); }); it("supports ref in the middle of a string", () => { const nodes = parseText("0{a}1"); expect(nodes).toMatchObject(["0", ref("a"), "1"]); }); it("supports multiple refs in the middle of a string", () => { const nodes = parseText("0{a}{b}1"); expect(nodes).toMatchObject(["0", ref("a"), "", ref("b"), "1"]); }); it("supports a ref at the end of a string", () => { const nodes = parseText("0{a}"); expect(nodes).toMatchObject(["0", ref("a"), ""]); }); it("supports skipping refs with a backslash", () => { const nodes = parseText("\\{a}"); expect(nodes).toMatchObject(["{a}"]); }); it("supports skipping backslash adjacent to refs", () => { const nodes = parseText("\\\\{a}"); expect(nodes).toMatchObject(["\\", ref("a"), ""]); }); it("supports skipping refs with a backslash (multiple)", () => { const nodes = parseText("\\\\\\{a}"); expect(nodes).toMatchObject(["\\{a}"]); }); it("supports a < ref", () => { const nodes = parseText("{<}0"); expect(nodes).toMatchObject(["", ref("<"), "0"]); }); }); describe(nodeFactoryFactory.name, () => { it("returns a function", () => { expect(nodeFactoryFactory(schema.nodes.node1, {})).toBeInstanceOf(Function); }); it("returns a factory that returns ref'd nodes", () => { const node1 = nodeFactoryFactory(schema.nodes.node1, {}); expect(node1("foo")).toBeInstanceOf(RefsNode); }); it("correctly calculates flat node ref positions", () => { const node1 = nodeFactoryFactory(schema.nodes.node1, {}); const { node, refMap } = node1("t{a}ex{b}t"); expect(node.textBetween(refMap.get("a")!, refMap.get("b")!)).toBe("ex"); }); it("correctly calculates flat node ref positions with refs tracking node", () => { const node1 = nodeFactoryFactory(schema.nodes.node1, {}); const { node, refMap } = node1("{a}", "text", "{b}"); expect(node.textBetween(refMap.get("a")!, refMap.get("b")!)).toBe("text"); }); it("correctly calculates single nested node ref positions", () => { const doc = nodeFactoryFactory(schema.nodes.doc, {}); const node1 = nodeFactoryFactory(schema.nodes.node1, {}); const { node, refMap } = doc(node1("t{a}ex{b}t")); expect(node.textBetween(refMap.get("a")!, refMap.get("b")!)).toBe("ex"); }); it("correctly calculates twice nested node ref positions", () => { const doc = nodeFactoryFactory(schema.nodes.doc, {}); const node1 = nodeFactoryFactory(schema.nodes.node1, {}); const node2 = nodeFactoryFactory(schema.nodes.node2, {}); const { node, refMap } = doc(node2(node1("t{a}ex{b}t"))); expect(node.textBetween(refMap.get("a")!, refMap.get("b")!)).toBe("ex"); }); it("checks schema constriants", () => { expect(() => { const node1 = nodeFactoryFactory(schema.nodes.node1, {}); const node2 = nodeFactoryFactory(schema.nodes.node2, {}); node1(node2("t{a}ex{b}t")); }).toThrowErrorMatchingSnapshot(); }); it("supports Node children", () => { const doc = nodeFactoryFactory(schema.nodes.doc, {}); const node1 = nodeFactoryFactory(schema.nodes.node1, {}); const { node, refMap } = doc("{a}", node1("text").node, "{b}"); expect(node.textBetween(refMap.get("a")!, refMap.get("b")!)).toBe("text"); }); it("supports Fragment children", () => { const doc = nodeFactoryFactory(schema.nodes.doc, {}); const node1 = nodeFactoryFactory(schema.nodes.node1, {}); const { node, refMap } = doc("{a}", node1(node1("text").node.content), "{b}"); expect(node.textBetween(refMap.get("a")!, refMap.get("b")!)).toBe("text"); }); }); describe(markFactoryFactory.name, () => { const node1 = nodeFactoryFactory(schema.nodes.node1); const mark1 = markFactoryFactory(schema.marks.mark1); const mark2 = markFactoryFactory(schema.marks.mark2); it("returns a function", () => { expect(markFactoryFactory(schema.marks.mark1, {})).toBeInstanceOf(Function); }); it("returns a builder that returns a refs fragment", () => { expect(mark1()).toBeInstanceOf(RefsFragment); }); it("correctly calculates refs", () => { const { node, refMap } = node1(mark1("t{a}ex{b}t")); expect(node.textBetween(refMap.get("a")!, refMap.get("b")!)).toBe("ex"); }); it("supports being composed with text() and maintaining refs", () => { const { node, refMap } = node1(mark1(mark2("t{a}ex{b}t"))); expect(node.textBetween(refMap.get("a")!, refMap.get("b")!)).toBe("ex"); }); it("supports being composed with multiple text() and maintaining refs", () => { const { node, refMap } = node1(mark2("t{a}ex{b}t", "t{c}ex{d}t")); expect(node.textBetween(refMap.get("a")!, refMap.get("b")!)).toBe("ex"); expect(node.textBetween(refMap.get("c")!, refMap.get("d")!)).toBe("ex"); }); }); describe(fragmentFactoryFactory.name, () => { const node1 = nodeFactoryFactory(schema.nodes.node1, {}); it("fragment factory examples", () => { const fragment = fragmentFactoryFactory(schema); expect(fragment("hello")).toMatchSnapshot(); expect(fragment(node1("hello"))).toMatchSnapshot(); }); }); describe(RefsNode.name, () => { const node1 = nodeFactoryFactory(schema.nodes.node1); const node1WithAttrs = nodeFactoryFactory(schema.nodes.node1, { foo: "bar" }); describe(RefsNode.prototype.append.name, () => { it("allows strings containing inline refs", () => { const { node, refMap } = node1("hello").append("{a}world{b}"); expect(node.textBetween(refMap.get("a")!, refMap.get("b")!)).toBe("world"); }); it("does not look for inline refs in text nodes", () => { const { node, refMap } = node1("hello").append("{a}", schema.text("{a}world{b}"), "{b}"); expect(node.textBetween(refMap.get("a")!, refMap.get("b")!)).toBe("{a}world{b}"); }); it("preserves attrs on the node", () => { const { node } = node1WithAttrs("hello").append("{a}", schema.text("{a}world{b}"), "{b}"); expect(node).toMatchNode(node1WithAttrs("hello\\{a}world\\{b}").node); }); it("prevents schema violations", () => { expect(() => node1("hello").append(node1("world"))).toThrowErrorMatchingSnapshot(); }); it("combines refs", () => { const { node, refMap } = node1("{a}hello").append(" world{b}"); expect(node.textBetween(refMap.get("a")!, refMap.get("b")!)).toBe("hello world"); }); it("trailing refs have presedence over leading refs", () => { const { node, refMap } = node1("{a}hello{b}").append("{a}world{b}"); expect(node.textBetween(refMap.get("a")!, refMap.get("b")!)).toBe("world"); }); }); describe(RefsNode.prototype.prepend.name, () => { it("allows strings containing inline refs", () => { const { node, refMap } = node1("world").prepend("{a}hello{b} "); expect(node.textBetween(refMap.get("a")!, refMap.get("b")!)).toBe("hello"); }); it("does not look for inline refs in text nodes", () => { const { node, refMap } = node1("world").prepend("{a}", schema.text("{a}hello {b}"), "{b}"); expect(node.textBetween(refMap.get("a")!, refMap.get("b")!)).toBe("{a}hello {b}"); }); it("preserves attrs on the node", () => { const { node } = node1WithAttrs("world").prepend("{a}", schema.text("{a}hello{b}"), "{b}"); expect(node).toMatchNode(node1WithAttrs("\\{a}hello\\{b}world").node); }); it("prevents schema violations", () => { expect(() => node1("world").prepend(node1("hello"))).toThrowErrorMatchingSnapshot(); }); it("combines refs", () => { const { node, refMap } = node1(" world{b}").prepend("{a}hello"); expect(node.textBetween(refMap.get("a")!, refMap.get("b")!)).toBe("hello world"); }); it("trailing refs have presedence over leading refs", () => { const { node, refMap } = node1("{a}world{b}").prepend("{a}hello{b}"); expect(node.textBetween(refMap.get("a")!, refMap.get("b")!)).toBe("world"); }); }); describe(RefsNode.prototype.getContent.name, () => { it("preserves correct refmap", () => { const { fragment, refMap } = node1("{a}world{b}").getContent(); expect(fragment.child(0).textBetween(refMap.get("a")!, refMap.get("b")!)).toBe("world"); }); }); describe(RefsNode.from.name, () => { it("uses an empty refmap", () => { expect(RefsNode.from(node1("foo").node)).toBeInstanceOf(RefsNode); }); }); });