Skip to content

Commit

Permalink
Merge branch 'main' into set-nested-object
Browse files Browse the repository at this point in the history
  • Loading branch information
hackerwins committed Nov 23, 2023
2 parents dcfd796 + 5b1425d commit 5a8b835
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/document/crdt/primitive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ export class Primitive extends CRDTElement {
public deepcopy(): Primitive {
const primitive = Primitive.of(this.value, this.getCreatedAt());
primitive.setMovedAt(this.getMovedAt());
primitive.setRemovedAt(this.getRemovedAt());
return primitive;
}

Expand Down
76 changes: 76 additions & 0 deletions test/integration/object_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -712,6 +712,82 @@ describe('Object', function () {
assert.equal(doc1Checkpoint.getServerSeq().toInt(), 5);
});

it(`Should not propagate changes when there is no applied undo operation`, async function ({
task,
}) {
interface TestDoc {
shape?: { color: string };
}
const docKey = toDocKey(`${task.name}-${new Date().getTime()}`);
const doc1 = new Document<TestDoc>(docKey);
const doc2 = new Document<TestDoc>(docKey);

const client1 = new Client(testRPCAddr);
const client2 = new Client(testRPCAddr);
await client1.activate();
await client2.activate();

await client1.attach(doc1, { isRealtimeSync: false });
let doc1ChangeID = doc1.getChangeID();
let doc1Checkpoint = doc1.getCheckpoint();
assert.equal(doc1ChangeID.getClientSeq(), 1);
assert.equal(doc1Checkpoint.getClientSeq(), 1);
assert.equal(doc1Checkpoint.getServerSeq().toInt(), 1);

doc1.update((root) => {
root.shape = { color: 'black' };
}, 'init doc');
await client1.sync();
assert.equal(doc1.toSortedJSON(), '{"shape":{"color":"black"}}');
doc1ChangeID = doc1.getChangeID();
doc1Checkpoint = doc1.getCheckpoint();
assert.equal(doc1ChangeID.getClientSeq(), 2);
assert.equal(doc1Checkpoint.getClientSeq(), 2);
assert.equal(doc1Checkpoint.getServerSeq().toInt(), 2);

await client2.attach(doc2, { isRealtimeSync: false });
assert.equal(doc2.toSortedJSON(), '{"shape":{"color":"black"}}');

doc2.update((root) => {
delete root.shape;
}, 'delete shape');
await client2.sync();
await client1.sync();
assert.equal(doc1.toSortedJSON(), '{}');
assert.equal(doc2.toSortedJSON(), '{}');
doc1ChangeID = doc1.getChangeID();
doc1Checkpoint = doc1.getCheckpoint();
assert.equal(doc1ChangeID.getClientSeq(), 2);
assert.equal(doc1Checkpoint.getClientSeq(), 2);
assert.equal(doc1Checkpoint.getServerSeq().toInt(), 4);

// c2 deleted the shape, so the reverse operation cannot be applied
doc1.history.undo();
assert.equal(doc1.toSortedJSON(), '{}');
assert.equal(doc1.getRedoStackForTest().length, 0);
assert.equal(doc1.history.canRedo(), false);
await client1.sync();
await client2.sync();
await client1.sync();
// Since there are no applied operations, there should be no change in the sequence.
doc1ChangeID = doc1.getChangeID();
doc1Checkpoint = doc1.getCheckpoint();
assert.equal(doc1ChangeID.getClientSeq(), 2);
assert.equal(doc1Checkpoint.getClientSeq(), 2);
assert.equal(doc1Checkpoint.getServerSeq().toInt(), 4);

doc1.update((root) => {
root.shape = { color: 'red' };
});
await client1.sync();
assert.equal(doc1.toSortedJSON(), '{"shape":{"color":"red"}}');
doc1ChangeID = doc1.getChangeID();
doc1Checkpoint = doc1.getCheckpoint();
assert.equal(doc1ChangeID.getClientSeq(), 3);
assert.equal(doc1Checkpoint.getClientSeq(), 3);
assert.equal(doc1Checkpoint.getServerSeq().toInt(), 5);
});

it('Can handle concurrent undo/redo: local undo & global redo', async function ({
task,
}) {
Expand Down
21 changes: 21 additions & 0 deletions test/unit/document/document_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,27 @@ describe.sequential('Document', function () {
assert.equal(doc.toSortedJSON(), '{"list":[{"id":1}]}');
});

it('splice array with nested object', function () {
const doc = new Document<{
list: Array<{ point: { x?: number; y?: number } }>;
}>('test-doc');

doc.update((root) => {
root.list = [{ point: { x: 0, y: 0 } }, { point: { x: 1, y: 1 } }];
delete root.list[1].point.y;
});
assert.equal(
doc.toSortedJSON(),
'{"list":[{"point":{"x":0,"y":0}},{"point":{"x":1}}]}',
);

doc.update((root) => {
const res = root.list.splice(1, 1);
assert.equal(res.toString(), '{"point":{"x":1}}');
});
assert.equal(doc.toSortedJSON(), '{"list":[{"point":{"x":0,"y":0}}]}');
});

describe('should support standard array read-only operations', () => {
type TestDoc = {
empty: [];
Expand Down

0 comments on commit 5a8b835

Please sign in to comment.