2023-11-02 06:20:34 +00:00
|
|
|
|
import { describe, expect, it } from "vitest";
|
2023-12-21 03:40:39 +00:00
|
|
|
|
import { Delta, Loro, TextDiff } from "../src";
|
2023-11-05 08:13:40 +00:00
|
|
|
|
import { setDebug } from "loro-wasm";
|
2023-11-02 06:20:34 +00:00
|
|
|
|
|
|
|
|
|
describe("richtext", () => {
|
|
|
|
|
it("mark", () => {
|
|
|
|
|
const doc = new Loro();
|
2024-01-22 04:03:35 +00:00
|
|
|
|
doc.configTextStyle({
|
|
|
|
|
bold: { expand: "after" },
|
|
|
|
|
link: { expand: "before" }
|
|
|
|
|
});
|
2023-11-02 06:20:34 +00:00
|
|
|
|
const text = doc.getText("text");
|
|
|
|
|
text.insert(0, "Hello World!");
|
|
|
|
|
text.mark({ start: 0, end: 5 }, "bold", true);
|
|
|
|
|
expect(text.toDelta()).toStrictEqual([
|
|
|
|
|
{
|
|
|
|
|
insert: "Hello",
|
|
|
|
|
attributes: {
|
|
|
|
|
bold: true,
|
2023-12-03 06:54:45 +00:00
|
|
|
|
},
|
2023-11-02 06:20:34 +00:00
|
|
|
|
},
|
|
|
|
|
{
|
2023-12-03 06:54:45 +00:00
|
|
|
|
insert: " World!",
|
|
|
|
|
},
|
|
|
|
|
] as Delta<string>[]);
|
|
|
|
|
});
|
2023-11-02 06:20:34 +00:00
|
|
|
|
|
2023-11-04 04:24:05 +00:00
|
|
|
|
it("insert after emoji", () => {
|
|
|
|
|
const doc = new Loro();
|
|
|
|
|
const text = doc.getText("text");
|
|
|
|
|
text.insert(0, "👨👩👦");
|
|
|
|
|
text.insert(8, "a");
|
2023-12-03 06:54:45 +00:00
|
|
|
|
expect(text.toString()).toBe("👨👩👦a");
|
|
|
|
|
});
|
2023-11-04 04:24:05 +00:00
|
|
|
|
|
2023-11-02 06:20:34 +00:00
|
|
|
|
it("emit event correctly", () => {
|
|
|
|
|
const doc = new Loro();
|
|
|
|
|
const text = doc.getText("text");
|
|
|
|
|
text.subscribe(doc, (event) => {
|
|
|
|
|
if (event.diff.type == "text") {
|
2023-12-03 06:54:45 +00:00
|
|
|
|
expect(event.diff.diff).toStrictEqual([
|
|
|
|
|
{
|
|
|
|
|
insert: "Hello",
|
|
|
|
|
attributes: {
|
|
|
|
|
bold: true,
|
2023-11-02 06:20:34 +00:00
|
|
|
|
},
|
2023-12-03 06:54:45 +00:00
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
insert: " World!",
|
|
|
|
|
},
|
|
|
|
|
] as Delta<string>[]);
|
2023-11-02 06:20:34 +00:00
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
text.insert(0, "Hello World!");
|
|
|
|
|
text.mark({ start: 0, end: 5 }, "bold", true);
|
2023-12-03 06:54:45 +00:00
|
|
|
|
});
|
2023-11-02 06:20:34 +00:00
|
|
|
|
|
2023-12-03 06:54:45 +00:00
|
|
|
|
it("emit event from merging doc correctly", async () => {
|
2023-11-02 06:20:34 +00:00
|
|
|
|
const doc = new Loro();
|
|
|
|
|
const text = doc.getText("text");
|
|
|
|
|
let called = false;
|
|
|
|
|
text.subscribe(doc, (event) => {
|
|
|
|
|
if (event.diff.type == "text") {
|
|
|
|
|
called = true;
|
2023-12-03 06:54:45 +00:00
|
|
|
|
expect(event.diff.diff).toStrictEqual([
|
|
|
|
|
{
|
|
|
|
|
insert: "Hello",
|
|
|
|
|
attributes: {
|
|
|
|
|
bold: true,
|
2023-11-02 06:20:34 +00:00
|
|
|
|
},
|
2023-12-03 06:54:45 +00:00
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
insert: " World!",
|
|
|
|
|
},
|
|
|
|
|
] as Delta<string>[]);
|
2023-11-02 06:20:34 +00:00
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const docB = new Loro();
|
|
|
|
|
const textB = docB.getText("text");
|
|
|
|
|
textB.insert(0, "Hello World!");
|
|
|
|
|
textB.mark({ start: 0, end: 5 }, "bold", true);
|
|
|
|
|
doc.import(docB.exportFrom());
|
2023-12-03 06:54:45 +00:00
|
|
|
|
await new Promise((r) => setTimeout(r, 1));
|
2023-11-02 06:20:34 +00:00
|
|
|
|
expect(called).toBeTruthy();
|
2023-12-03 06:54:45 +00:00
|
|
|
|
});
|
2023-11-05 08:13:40 +00:00
|
|
|
|
|
|
|
|
|
it("Delete emoji", async () => {
|
|
|
|
|
const doc = new Loro();
|
|
|
|
|
const text = doc.getText("text");
|
|
|
|
|
text.insert(0, "012345👨👩👦6789");
|
|
|
|
|
doc.commit();
|
|
|
|
|
text.mark({ start: 0, end: 18 }, "bold", true);
|
|
|
|
|
doc.commit();
|
2023-12-03 06:54:45 +00:00
|
|
|
|
expect(text.toDelta()).toStrictEqual([
|
|
|
|
|
{
|
|
|
|
|
insert: "012345👨👩👦6789",
|
|
|
|
|
attributes: { bold: true },
|
|
|
|
|
},
|
|
|
|
|
]);
|
2023-11-05 08:13:40 +00:00
|
|
|
|
text.delete(6, 8);
|
|
|
|
|
doc.commit();
|
2023-12-03 06:54:45 +00:00
|
|
|
|
expect(text.toDelta()).toStrictEqual([
|
|
|
|
|
{
|
|
|
|
|
insert: "0123456789",
|
|
|
|
|
attributes: { bold: true },
|
|
|
|
|
},
|
|
|
|
|
]);
|
2023-11-05 08:13:40 +00:00
|
|
|
|
});
|
2023-12-21 03:40:39 +00:00
|
|
|
|
|
|
|
|
|
it("apply delta", async () => {
|
2024-01-22 08:00:32 +00:00
|
|
|
|
const doc1 = new Loro();
|
|
|
|
|
doc1.configTextStyle({
|
|
|
|
|
link: { expand: "none" },
|
|
|
|
|
bold: { expand: "after" },
|
|
|
|
|
})
|
|
|
|
|
const text1 = doc1.getText("text");
|
2023-12-21 03:40:39 +00:00
|
|
|
|
const doc2 = new Loro();
|
2024-01-22 08:00:32 +00:00
|
|
|
|
doc2.configTextStyle({
|
|
|
|
|
link: { expand: "none" },
|
|
|
|
|
bold: { expand: "after" },
|
|
|
|
|
})
|
2023-12-21 03:40:39 +00:00
|
|
|
|
const text2 = doc2.getText("text");
|
2024-01-22 08:00:32 +00:00
|
|
|
|
text1.subscribe(doc1, (event) => {
|
2023-12-21 03:40:39 +00:00
|
|
|
|
const e = event.diff as TextDiff;
|
|
|
|
|
text2.applyDelta(e.diff);
|
|
|
|
|
});
|
2024-01-22 08:00:32 +00:00
|
|
|
|
text1.insert(0, "foo");
|
|
|
|
|
text1.mark({ start: 0, end: 3 }, "link", true);
|
|
|
|
|
doc1.commit();
|
|
|
|
|
await new Promise((r) => setTimeout(r, 1));
|
|
|
|
|
expect(text2.toDelta()).toStrictEqual(text1.toDelta());
|
|
|
|
|
text1.insert(3, "baz");
|
|
|
|
|
doc1.commit();
|
2023-12-21 03:40:39 +00:00
|
|
|
|
await new Promise((r) => setTimeout(r, 1));
|
|
|
|
|
expect(text2.toDelta()).toStrictEqual([{ insert: 'foo', attributes: { link: true } }, { insert: 'baz' }]);
|
2024-01-22 08:00:32 +00:00
|
|
|
|
expect(text2.toDelta()).toStrictEqual(text1.toDelta());
|
|
|
|
|
text1.mark({ start: 2, end: 5 }, "bold", true);
|
|
|
|
|
doc1.commit();
|
|
|
|
|
await new Promise((r) => setTimeout(r, 1));
|
|
|
|
|
expect(text2.toDelta()).toStrictEqual(text1.toDelta());
|
2023-12-21 03:40:39 +00:00
|
|
|
|
})
|
2024-01-17 14:55:46 +00:00
|
|
|
|
|
|
|
|
|
it("custom richtext type", async () => {
|
|
|
|
|
const doc = new Loro();
|
|
|
|
|
doc.configTextStyle({
|
|
|
|
|
myStyle: {
|
|
|
|
|
expand: "none",
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
const text = doc.getText("text");
|
|
|
|
|
text.insert(0, "foo");
|
|
|
|
|
text.mark({ start: 0, end: 3 }, "myStyle", 123);
|
|
|
|
|
expect(text.toDelta()).toStrictEqual([{ insert: 'foo', attributes: { myStyle: 123 } }]);
|
|
|
|
|
|
|
|
|
|
expect(() => {
|
|
|
|
|
text.mark({ start: 0, end: 3 }, "unknownStyle", 2);
|
|
|
|
|
}).toThrowError()
|
|
|
|
|
|
|
|
|
|
expect(() => {
|
|
|
|
|
// default style config should be overwritten
|
|
|
|
|
text.mark({ start: 0, end: 3 }, "bold", 2);
|
|
|
|
|
}).toThrowError()
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it("allow overlapped styles", () => {
|
|
|
|
|
const doc = new Loro();
|
|
|
|
|
doc.configTextStyle({
|
|
|
|
|
comment: { expand: "none", }
|
|
|
|
|
})
|
|
|
|
|
const text = doc.getText("text");
|
|
|
|
|
text.insert(0, "The fox jumped.");
|
|
|
|
|
text.mark({ start: 0, end: 7 }, "comment:alice", "Hi");
|
|
|
|
|
text.mark({ start: 4, end: 14 }, "comment:bob", "Jump");
|
|
|
|
|
expect(text.toDelta()).toStrictEqual([
|
|
|
|
|
{
|
|
|
|
|
insert: "The ", attributes: { "comment:alice": "Hi" },
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
insert: "fox", attributes: { "comment:alice": "Hi", "comment:bob": "Jump" },
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
insert: " jumped", attributes: { "comment:bob": "Jump" },
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
insert: ".",
|
|
|
|
|
}
|
|
|
|
|
])
|
|
|
|
|
})
|
2023-12-03 06:54:45 +00:00
|
|
|
|
});
|