mirror of
https://github.com/loro-dev/loro.git
synced 2025-01-23 13:39:12 +00:00
219 lines
6.3 KiB
TypeScript
219 lines
6.3 KiB
TypeScript
import { describe, expect, expectTypeOf, it } from "vitest";
|
|
import {
|
|
Delta,
|
|
ListDiff,
|
|
Loro,
|
|
LoroList,
|
|
LoroMap,
|
|
LoroMovableList,
|
|
LoroText,
|
|
TextDiff,
|
|
} from "../src";
|
|
|
|
describe("movable list", () => {
|
|
it("should work like list", () => {
|
|
const doc = new Loro();
|
|
const list = doc.getMovableList("list");
|
|
expect(list.length).toBe(0);
|
|
list.push("a");
|
|
expect(list.length).toBe(1);
|
|
expect(list.get(0)).toBe("a");
|
|
let v = list.pop();
|
|
expect(list.length).toBe(0);
|
|
expect(v).toBe("a");
|
|
});
|
|
|
|
it("can be synced", () => {
|
|
const doc = new Loro();
|
|
const list = doc.getMovableList("list");
|
|
list.push("a");
|
|
list.push("b");
|
|
list.push("c");
|
|
expect(list.toArray()).toEqual(["a", "b", "c"]);
|
|
list.set(2, "d");
|
|
list.move(0, 1);
|
|
const doc2 = new Loro();
|
|
const list2 = doc2.getMovableList("list");
|
|
expect(list2.length).toBe(0);
|
|
doc2.import(doc.exportFrom());
|
|
expect(list2.length).toBe(3);
|
|
expect(list2.get(0)).toBe("b");
|
|
expect(list2.get(1)).toBe("a");
|
|
expect(list2.get(2)).toBe("d");
|
|
});
|
|
|
|
it("should support move", () => {
|
|
const doc = new Loro();
|
|
const list = doc.getMovableList("list");
|
|
list.push("a");
|
|
list.push("b");
|
|
list.push("c");
|
|
expect(list.toArray()).toEqual(["a", "b", "c"]);
|
|
list.move(0, 1);
|
|
expect(list.toArray()).toEqual(["b", "a", "c"]);
|
|
});
|
|
|
|
it("should support set", () => {
|
|
const doc = new Loro();
|
|
const list = doc.getMovableList("list");
|
|
list.push("a");
|
|
list.push("b");
|
|
list.push("c");
|
|
expect(list.toArray()).toEqual(["a", "b", "c"]);
|
|
list.set(1, "d");
|
|
expect(list.toArray()).toEqual(["a", "d", "c"]);
|
|
});
|
|
|
|
it.todo("should support get cursor", () => {
|
|
const doc = new Loro();
|
|
doc.setPeerId(1);
|
|
const list = doc.getMovableList("list");
|
|
list.push("a");
|
|
list.push("b");
|
|
list.push("c");
|
|
expect(list.toArray()).toEqual(["a", "b", "c"]);
|
|
const cursor = list.getCursor(1)!;
|
|
const ans = doc.getCursorPos(cursor);
|
|
expect(ans.offset).toBe(1);
|
|
expect(ans.update).toBeFalsy();
|
|
|
|
// cursor position should not be affected by set and move
|
|
list.set(1, "d");
|
|
list.move(1, 2);
|
|
const ans2 = doc.getCursorPos(cursor);
|
|
expect(ans2.offset).toBe(1);
|
|
expect(ans2.update).toBeTruthy();
|
|
const pos = ans2.update?.pos();
|
|
expect(pos).toStrictEqual({ peer: "1", counter: 4 });
|
|
});
|
|
|
|
it("inserts sub-container", () => {
|
|
const doc = new Loro();
|
|
const list = doc.getMovableList("list");
|
|
list.push("a");
|
|
list.push("b");
|
|
list.push("c");
|
|
const subList = list.insertContainer(1, new LoroList());
|
|
subList.push("d");
|
|
subList.push("e");
|
|
subList.push("f");
|
|
expect(list.toJSON()).toEqual(["a", ["d", "e", "f"], "b", "c"]);
|
|
list.move(1, 0);
|
|
expect(list.toJSON()).toEqual([["d", "e", "f"], "a", "b", "c"]);
|
|
list.move(0, 3);
|
|
expect(list.toJSON()).toEqual(["a", "b", "c", ["d", "e", "f"]]);
|
|
});
|
|
|
|
it("can be inserted into a list as an attached container", () => {
|
|
const doc = new Loro();
|
|
const list = doc.getMovableList("list");
|
|
list.push("a");
|
|
list.push("b");
|
|
list.push("c");
|
|
const blist = doc.getList("blist");
|
|
const newList: LoroMovableList = blist.insertContainer(0, list);
|
|
expect(blist.toJSON()).toEqual([["a", "b", "c"]]);
|
|
newList.move(0, 1);
|
|
expect(blist.toJSON()).toEqual([["b", "a", "c"]]);
|
|
list.move(0, 2);
|
|
// change on list should not affect blist
|
|
expect(blist.toJSON()).toEqual([["b", "a", "c"]]);
|
|
});
|
|
|
|
it("length should be correct when there are concurrent move", () => {
|
|
const docA = new Loro();
|
|
const list = docA.getMovableList("list");
|
|
list.push("a");
|
|
list.push("b");
|
|
list.push("c");
|
|
const docB = new Loro();
|
|
const listB = docB.getMovableList("list");
|
|
docB.import(docA.exportFrom());
|
|
listB.move(0, 1);
|
|
list.move(0, 1);
|
|
docB.import(docA.exportFrom());
|
|
expect(listB.toJSON()).toEqual(["b", "a", "c"]);
|
|
expect(listB.length).toBe(3);
|
|
});
|
|
|
|
it("concurrent set the one with larger peer id win", () => {
|
|
const docA = new Loro();
|
|
docA.setPeerId(0);
|
|
const listA = docA.getMovableList("list");
|
|
listA.push("a");
|
|
listA.push("b");
|
|
listA.push("c");
|
|
const docB = new Loro();
|
|
docB.setPeerId(1);
|
|
const listB = docB.getMovableList("list");
|
|
docB.import(docA.exportFrom());
|
|
listA.set(1, "fromA");
|
|
listB.set(1, "fromB");
|
|
docB.import(docA.exportFrom());
|
|
docA.import(docB.exportFrom());
|
|
expect(listA.toJSON()).toEqual(["a", "fromB", "c"]);
|
|
expect(listA.length).toBe(3);
|
|
expect(listB.toJSON()).toEqual(["a", "fromB", "c"]);
|
|
expect(listB.length).toBe(3);
|
|
});
|
|
|
|
it("can be subscribe", async () => {
|
|
const doc = new Loro();
|
|
const list = doc.getMovableList("list");
|
|
list.push("a");
|
|
list.push("b");
|
|
list.push("c");
|
|
let called = false;
|
|
let calledTimes = 0;
|
|
const id = list.subscribe((event) => {
|
|
expect(event.by).toBe("local");
|
|
for (const e of event.events) {
|
|
expect(e.target).toBe(list.id);
|
|
if (e.diff.type === "list") {
|
|
expect(e.diff).toStrictEqual(
|
|
{
|
|
"type": "list",
|
|
"diff": [{ insert: ["a", "b", "c"] }],
|
|
} as ListDiff,
|
|
);
|
|
} else {
|
|
throw new Error("unknown diff type");
|
|
}
|
|
}
|
|
|
|
called = true;
|
|
calledTimes += 1;
|
|
});
|
|
await new Promise((r) => setTimeout(r, 1));
|
|
expect(called).toBeFalsy();
|
|
doc.commit();
|
|
await new Promise((r) => setTimeout(r, 1));
|
|
expect(called).toBeTruthy();
|
|
expect(calledTimes).toBe(1);
|
|
list.unsubscribe(id);
|
|
list.push("d");
|
|
doc.commit();
|
|
await new Promise((r) => setTimeout(r, 1));
|
|
expect(calledTimes).toBe(1);
|
|
});
|
|
|
|
it("has the right type", () => {
|
|
const doc = new Loro<
|
|
{ list: LoroMovableList<LoroMap<{ name: string }>> }
|
|
>();
|
|
const list = doc.getMovableList("list");
|
|
const map = list.insertContainer(0, new LoroMap());
|
|
expectTypeOf(map).toMatchTypeOf<LoroMap<{ name: string }>>();
|
|
map.set("name", "Alice");
|
|
expect(list.toJSON()).toStrictEqual([{ name: "Alice" }]);
|
|
});
|
|
|
|
it("set container", () => {
|
|
const doc = new Loro();
|
|
const list = doc.getMovableList("list");
|
|
list.insert(0, 100);
|
|
const text = list.setContainer(0, new LoroText());
|
|
text.insert(0, "Hello");
|
|
expect(list.toJSON()).toStrictEqual(["Hello"]);
|
|
});
|
|
});
|