changeset.test.ts

  1import { expect, test } from "vitest";
  2import * as Y from "yjs";
  3import { Changeset } from "./changeset";
  4import {
  5    getByTypeAndAttr,
  6    MAIN_DOCUMENT,
  7    queryByType,
  8    queryByTypeAndAttr,
  9} from "./filter.ts";
 10
 11const makeDoc = (...elems: Array<Y.XmlText | Y.XmlElement>): Y.Doc => {
 12    const doc = new Y.Doc();
 13    const array = doc.getArray(MAIN_DOCUMENT);
 14    array.insert(0, elems);
 15    return doc;
 16};
 17
 18const paragraph = (...elems: Array<Y.XmlElement | Y.XmlText>): Y.XmlElement => {
 19    const p = new Y.XmlElement("paragraph");
 20    p.insert(0, elems);
 21    return p;
 22};
 23
 24const text = (s: string): Y.XmlText => {
 25    const e = new Y.XmlText();
 26    e.insert(0, s);
 27    return e;
 28};
 29
 30const waypoint = (attrs: Record<string, string> = {}): Y.XmlElement => {
 31    const e = new Y.XmlElement("waypoint");
 32    for (const [key, value] of Object.entries(attrs)) {
 33        e.setAttribute(key, value);
 34    }
 35    return e;
 36};
 37
 38test("it can apply a changeset", () => {
 39    const cs = new Changeset();
 40    const doc = makeDoc(
 41        paragraph(text("test")),
 42        paragraph(waypoint({ id: "1" })),
 43    );
 44    const elem = getByTypeAndAttr("waypoint", ["id", "1"], doc).unwrapOr(null);
 45    cs.updateAttributes(elem, { test: "yes" });
 46    const result = cs.apply(doc);
 47    expect(result.isOk()).toBe(true);
 48
 49    const updatedElem = getByTypeAndAttr("waypoint", ["id", "1"], doc).unwrapOr(
 50        null,
 51    );
 52    expect(updatedElem.getAttribute("test")).toBe("yes");
 53});
 54
 55test("it can insert before and update text", () => {
 56    const cs = new Changeset();
 57    const doc = makeDoc(
 58        paragraph(text("test")),
 59        paragraph(waypoint({ id: "1" })),
 60    );
 61    const elem = getByTypeAndAttr("waypoint", ["id", "1"], doc).unwrapOr(null);
 62    cs.insertBefore(elem, text("inserted text"));
 63    const result = cs.apply(doc);
 64    expect(result.isOk()).toBe(true);
 65
 66    const updatedPs = queryByType("paragraph", doc);
 67    expect(updatedPs.length).toBe(2);
 68    expect(updatedPs[1].toString()).toBe(
 69        '<paragraph>inserted text<waypoint id="1"></waypoint></paragraph>',
 70    );
 71
 72    const cs2 = new Changeset();
 73    cs2.appendText(" again", elem.prevSibling);
 74    cs2.apply(doc);
 75    expect(updatedPs[1].toString()).toBe(
 76        '<paragraph>inserted text again<waypoint id="1"></waypoint></paragraph>',
 77    );
 78
 79    const cs3 = new Changeset();
 80    cs3.prependText("again ", elem.prevSibling);
 81    cs3.apply(doc);
 82    expect(updatedPs[1].toString()).toBe(
 83        '<paragraph>again inserted text again<waypoint id="1"></waypoint></paragraph>',
 84    );
 85});
 86
 87test("it can insert after", () => {
 88    const cs = new Changeset();
 89    const doc = makeDoc(
 90        paragraph(text("test")),
 91        paragraph(waypoint({ id: "1" })),
 92    );
 93    const elem = getByTypeAndAttr("waypoint", ["id", "1"], doc).unwrapOr(null);
 94    cs.insertAfter(elem, text("inserted text"));
 95    const result = cs.apply(doc);
 96    expect(result.isOk()).toBe(true);
 97
 98    const updatedPs = queryByType("paragraph", doc);
 99    expect(updatedPs.length).toBe(2);
100    expect(updatedPs[1].toString()).toBe(
101        '<paragraph><waypoint id="1"></waypoint>inserted text</paragraph>',
102    );
103});
104
105test("it can insert paragraphs after", () => {
106    const cs = new Changeset();
107    const doc = makeDoc(paragraph(text("1")), paragraph(text("2")));
108    const paras = queryByType("paragraph", doc);
109    cs.insertAfter(paras[0], paragraph(text("1.5")));
110    const result = cs.apply(doc);
111    expect(result.isOk()).toBe(true);
112
113    const updatedPs = queryByType("paragraph", doc);
114    expect(updatedPs.length).toBe(3);
115    expect(updatedPs[1].toString()).toBe("<paragraph>1.5</paragraph>");
116});
117
118test("it can insert paragraphs before", () => {
119    const cs = new Changeset();
120    const doc = makeDoc(paragraph(text("1")), paragraph(text("2")));
121    const paras = queryByType("paragraph", doc);
122    cs.insertBefore(paras[0], paragraph(text("0")));
123    const result = cs.apply(doc);
124    expect(result.isOk()).toBe(true);
125
126    const updatedPs = queryByType("paragraph", doc);
127    expect(updatedPs.length).toBe(3);
128    expect(updatedPs[0].toString()).toBe("<paragraph>0</paragraph>");
129});