diff --git a/doc/technical_choices.md b/doc/technical_choices.md
new file mode 100644
index 000000000..a017f8032
--- /dev/null
+++ b/doc/technical_choices.md
@@ -0,0 +1,55 @@
+
+# The diff strategy
+
+## The simple strategy vs diff strategy
+It would be simple to make the change in our API, then change the related nodes
+in the DOM. But there is some problems that we need to address.
+
+So rather than 2 steps (direct strategy):
+1) apply in VDOC
+2) apply in DOM
+
+We do 4 steps (diff strategy):
+1) apply in DOM
+2) apply in VDOC
+3) diff DOM-VDOC
+4) apply in DOM
+
+### Problem 1: Dictionnary and spell checkers
+When preventing an event, if the dictionnary was saving data, it does not clear
+it's internal memory. So when another event happen, the dictionnary has wrong
+data and might create strange correction.
+
+[provide examples]
+
+### Problem 2: Mouvement between words
+When you have a word and you hit `ctrl + arrow`, you want to move from one word
+to the other. In some language like Lao, the words might not be separated by
+spaces. In order to know where is the delimitation of the word, we allow the
+browser to move the cursor and we reflect that navigation change in our VDOC.
+
+### Problem of the diff strategy
+Because we need the event to happen in the DOM it is possible that our VDOC will
+have a different rendering then what the browser registred. In such case, glitch
+can occurs.
+
+[Provide gif images to show a glitch]
+
+### Exceptions
+It is possible in some case to be sure that there is no dictionnary/spell cheker
+running or that we can predict the movement of the words. In such case, we
+can use the browser `event.preventDefault()` and therfore avoid any possible
+glitch.
+
+### Alternatives
+Ckeditor: deactivate the spellcheck alltogether and use third party plugins.
+
+Quilljs: ?
+
+# One char per char
+## Benefits
+1) When doing enter, backspace, [list more case], it is easier to create an
+algorithm that does not need to look at all the modifier.
+2) It is easier to handle the range. When we move characters, we do not need
+to calculate the range.
+
diff --git a/doc/to_organize.md b/doc/to_organize.md
new file mode 100644
index 000000000..3fd8b9bf8
--- /dev/null
+++ b/doc/to_organize.md
@@ -0,0 +1,5 @@
+## Quilljs bugs
+bug with selection, backspace, ctrl+z: https://drive.google.com/file/d/1JjBrquTsa69DZsUzyUud3BPmqnVsRwKU/view
+
+## Difficult case to handle
+`
text
`
diff --git a/packages/core/src/Memory/test/memory.test.ts b/packages/core/src/Memory/test/memory.test.ts
index c98051c69..8ba6acbd9 100644
--- a/packages/core/src/Memory/test/memory.test.ts
+++ b/packages/core/src/Memory/test/memory.test.ts
@@ -8,2911 +8,2893 @@ import { makeVersionable, markNotVersionable, proxifyVersionable } from '../Vers
import { memoryProxyPramsKey } from '../const';
describe('core', () => {
- describe('state', () => {
- describe('memory', () => {
- describe('throw', () => {
- const memory = new Memory();
- memory.create('1');
- memory.switchTo('1');
-
- it('if try create a slice twice', () => {
- expect((): Memory => memory.create('1')).to.throw('already exists');
- });
- it('if try remove a slice before switch on an other', () => {
- memory.create('rem-2');
- memory.switchTo('rem-2');
- memory.create('rem-3');
- memory.switchTo('rem-3');
- expect((): Memory => memory.remove('rem-2')).to.throw('switch');
- });
- it('if try remove the original slice', () => {
- expect((): Memory => memory.remove('')).to.throw('original');
- });
- it('if try switch on a undefined slice', () => {
- expect((): Memory => memory.switchTo('2')).to.throw('must create');
- });
- it('if try to link a non versionable object', () => {
- expect((): void => {
- memory.attach({
- test: 1,
- });
- }).to.throw('VersionableObject', 'object');
- expect((): void => {
- memory.attach([1]);
- }).to.throw('VersionableObject', 'array');
- expect((): void => {
- memory.attach(new Set([1]));
- }).to.throw('VersionableObject', 'set');
- });
- it('if try to link a non versionable object from proxy Object.assing', () => {
- const versionable = makeVersionable({ test: 1 });
- const obj = Object.assign({}, versionable);
- expect((): void => {
- memory.attach(obj);
- }).to.throw('VersionableObject');
- });
- it('if try to add as attribute a non versionable object from proxy Object.assing', () => {
- const versionable = makeVersionable({ test: 1 });
- const obj = Object.assign({}, versionable);
- const ref = makeVersionable({ toto: undefined });
- expect((): void => {
- ref.toto = obj;
- }).to.throw('VersionableObject');
- });
- it('if try to link an object and not the proxy of versionable', () => {
- const obj = { test: 1 };
- makeVersionable(obj);
- expect((): void => {
- memory.attach(obj);
- }).to.throw('already');
- });
- it('if try to add as attribute an object and not the proxy of versionable', () => {
- const obj = { test: 1 };
- const ref = makeVersionable({ toto: undefined });
- makeVersionable(obj);
- expect((): void => {
- ref.toto = obj;
- }).to.throw('already');
- });
- it('if try to link a versionable object to 2 memories', () => {
- const memoryTest = new Memory();
- memoryTest.create('1');
- memoryTest.switchTo('1');
- const obj = new VersionableObject();
- memoryTest.attach(obj);
- const array = new VersionableArray();
- memoryTest.attach(array);
- const set = new VersionableSet();
- memoryTest.attach(set);
-
- expect((): void => {
- memory.attach(obj);
- }).to.throw('other memory', 'object');
- expect((): void => {
- memory.attach(array);
- }).to.throw('other memory', 'array');
- expect((): void => {
- memory.attach(set);
- }).to.throw('other memory', 'set');
- });
- it('if try to link a versionable from an other memory in versionable', () => {
- const memoryTest = new Memory();
- memoryTest.create('1');
- memoryTest.switchTo('1');
- const root = new VersionableObject();
- memory.attach(root);
- const obj = new VersionableObject();
- memoryTest.attach(obj);
- const array = new VersionableArray();
- memoryTest.attach(array);
- const set = new VersionableSet();
- memoryTest.attach(set);
-
- expect((): void => {
- root['x+y'] = obj;
- }).to.throw('other memory', 'object');
- expect((): void => {
- root['x+y'] = array;
- }).to.throw('other memory', 'array');
- expect((): void => {
- root['x+y'] = set;
- }).to.throw('other memory', 'set');
- });
- it('if try to makeVersionable on a object already versionable with the old ref', () => {
- const obj = {};
+ describe('memory', () => {
+ describe('throw', () => {
+ const memory = new Memory();
+ memory.create('1');
+ memory.switchTo('1');
+
+ it('if try create a slice twice', () => {
+ expect((): Memory => memory.create('1')).to.throw('already exists');
+ });
+ it('if try remove a slice before switch on an other', () => {
+ memory.create('rem-2');
+ memory.switchTo('rem-2');
+ memory.create('rem-3');
+ memory.switchTo('rem-3');
+ expect((): Memory => memory.remove('rem-2')).to.throw('switch');
+ });
+ it('if try remove the original slice', () => {
+ expect((): Memory => memory.remove('')).to.throw('original');
+ });
+ it('if try switch on a undefined slice', () => {
+ expect((): Memory => memory.switchTo('2')).to.throw('must create');
+ });
+ it('if try to link a non versionable object', () => {
+ expect((): void => {
+ memory.attach({
+ test: 1,
+ });
+ }).to.throw('VersionableObject', 'object');
+ expect((): void => {
+ memory.attach([1]);
+ }).to.throw('VersionableObject', 'array');
+ expect((): void => {
+ memory.attach(new Set([1]));
+ }).to.throw('VersionableObject', 'set');
+ });
+ it('if try to link a non versionable object from proxy Object.assing', () => {
+ const versionable = makeVersionable({ test: 1 });
+ const obj = Object.assign({}, versionable);
+ expect((): void => {
+ memory.attach(obj);
+ }).to.throw('VersionableObject');
+ });
+ it('if try to add as attribute a non versionable object from proxy Object.assing', () => {
+ const versionable = makeVersionable({ test: 1 });
+ const obj = Object.assign({}, versionable);
+ const ref = makeVersionable({ toto: undefined });
+ expect((): void => {
+ ref.toto = obj;
+ }).to.throw('VersionableObject');
+ });
+ it('if try to link an object and not the proxy of versionable', () => {
+ const obj = { test: 1 };
+ makeVersionable(obj);
+ expect((): void => {
+ memory.attach(obj);
+ }).to.throw('already');
+ });
+ it('if try to add as attribute an object and not the proxy of versionable', () => {
+ const obj = { test: 1 };
+ const ref = makeVersionable({ toto: undefined });
+ makeVersionable(obj);
+ expect((): void => {
+ ref.toto = obj;
+ }).to.throw('already');
+ });
+ it('if try to link a versionable object to 2 memories', () => {
+ const memoryTest = new Memory();
+ memoryTest.create('1');
+ memoryTest.switchTo('1');
+ const obj = new VersionableObject();
+ memoryTest.attach(obj);
+ const array = new VersionableArray();
+ memoryTest.attach(array);
+ const set = new VersionableSet();
+ memoryTest.attach(set);
+
+ expect((): void => {
+ memory.attach(obj);
+ }).to.throw('other memory', 'object');
+ expect((): void => {
+ memory.attach(array);
+ }).to.throw('other memory', 'array');
+ expect((): void => {
+ memory.attach(set);
+ }).to.throw('other memory', 'set');
+ });
+ it('if try to link a versionable from an other memory in versionable', () => {
+ const memoryTest = new Memory();
+ memoryTest.create('1');
+ memoryTest.switchTo('1');
+ const root = new VersionableObject();
+ memory.attach(root);
+ const obj = new VersionableObject();
+ memoryTest.attach(obj);
+ const array = new VersionableArray();
+ memoryTest.attach(array);
+ const set = new VersionableSet();
+ memoryTest.attach(set);
+
+ expect((): void => {
+ root['x+y'] = obj;
+ }).to.throw('other memory', 'object');
+ expect((): void => {
+ root['x+y'] = array;
+ }).to.throw('other memory', 'array');
+ expect((): void => {
+ root['x+y'] = set;
+ }).to.throw('other memory', 'set');
+ });
+ it('if try to makeVersionable on a object already versionable with the old ref', () => {
+ const obj = {};
+ makeVersionable(obj);
+ expect((): void => {
makeVersionable(obj);
- expect((): void => {
- makeVersionable(obj);
- }).to.throw('proxy', 'object');
- const array = [];
+ }).to.throw('proxy', 'object');
+ const array = [];
+ makeVersionable(array);
+ expect((): void => {
makeVersionable(array);
- expect((): void => {
- makeVersionable(array);
- }).to.throw('proxy', 'array');
- const set = new Set();
+ }).to.throw('proxy', 'array');
+ const set = new Set();
+ makeVersionable(set);
+ expect((): void => {
makeVersionable(set);
- expect((): void => {
- makeVersionable(set);
- }).to.throw('proxy', 'set');
- });
- it('if try to link an object in versionable', () => {
- const obj = new VersionableArray();
- expect((): void => {
- obj['t+h'] = {};
- }).to.throw('VersionableObject', 'object');
-
- const array = new VersionableArray();
- expect((): void => {
- array.push({});
- }).to.throw('VersionableObject', 'array');
-
- const set = new VersionableSet();
- expect((): void => {
- set.add({});
- }).to.throw('VersionableObject', 'set');
- });
- it('if try to link an object in custom class constructor', () => {
- class SuperObject extends VersionableObject {
- obj: object;
- constructor() {
- super();
- this.obj = {};
- }
+ }).to.throw('proxy', 'set');
+ });
+ it('if try to link an object in versionable', () => {
+ const obj = new VersionableArray();
+ expect((): void => {
+ obj['t+h'] = {};
+ }).to.throw('VersionableObject', 'object');
+
+ const array = new VersionableArray();
+ expect((): void => {
+ array.push({});
+ }).to.throw('VersionableObject', 'array');
+
+ const set = new VersionableSet();
+ expect((): void => {
+ set.add({});
+ }).to.throw('VersionableObject', 'set');
+ });
+ it('if try to link an object in custom class constructor', () => {
+ class SuperObject extends VersionableObject {
+ obj: object;
+ constructor() {
+ super();
+ this.obj = {};
}
- expect((): SuperObject => new SuperObject()).to.throw('VersionableObject');
- });
- it('if try to link an object in custom class', () => {
- class SuperObject extends VersionableObject {
- obj: object;
- myMethod(): void {
- this.obj = {};
- }
+ }
+ expect((): SuperObject => new SuperObject()).to.throw('VersionableObject');
+ });
+ it('if try to link an object in custom class', () => {
+ class SuperObject extends VersionableObject {
+ obj: object;
+ myMethod(): void {
+ this.obj = {};
}
- const instance = new SuperObject();
- expect((): void => {
- instance.myMethod();
- }).to.throw('VersionableObject');
- });
- it('if try to edit attribute in a frozen slice', () => {
- const obj = new VersionableObject();
- memory.attach(obj);
- memory.switchTo('1');
- memory.create('1-1');
- memory.switchTo('1');
- expect((): number => (obj['H' + 1] = 4)).to.throw('can not update', 'object');
- memory.remove('1-1');
- });
- it('if try to edit array in a frozen slice', () => {
- const memory = new Memory();
- const array = new VersionableArray();
- memory.attach(array);
- memory.create('1');
- memory.switchTo('1');
- memory.create('2');
- expect((): number => array.push(4)).to.throw('can not update', 'array');
- expect((): number => (array[1] = 3)).to.throw('can not update', 'array');
- memory.switchTo('1');
- memory.remove('2');
- });
- it('if try to edit set in a frozen slice', () => {
- const memory = new Memory();
- const set = new VersionableSet();
- memory.attach(set);
- memory.create('1');
- memory.switchTo('1');
- memory.create('1-1');
- memory.switchTo('1');
- expect((): VersionableSet => set.add(4)).to.throw('can not update', 'add');
- expect((): boolean => set.delete(4)).to.throw('can not update', 'delete');
- expect((): VersionableSet => set.clear()).to.throw('can not update', 'clear');
- memory.remove('1-1');
- });
- it('if try to compress/snapshot with wrong slice', () => {
- const memory = new Memory();
- memory.create('test');
- memory.switchTo('test');
- memory.create('test-0');
- memory.switchTo('test-0');
- memory.create('test-1');
- memory.switchTo('test-1');
- memory.create('test-2');
- memory.switchTo('test-2');
- expect((): void => memory.snapshot('test-2', 'test', 'snap')).to.throw('merge');
- });
+ }
+ const instance = new SuperObject();
+ expect((): void => {
+ instance.myMethod();
+ }).to.throw('VersionableObject');
});
-
- describe('create versionable', () => {
+ it('if try to edit attribute in a frozen slice', () => {
+ const obj = new VersionableObject();
+ memory.attach(obj);
+ memory.switchTo('1');
+ memory.create('1-1');
+ memory.switchTo('1');
+ expect((): number => (obj['H' + 1] = 4)).to.throw('can not update', 'object');
+ memory.remove('1-1');
+ });
+ it('if try to edit array in a frozen slice', () => {
const memory = new Memory();
+ const array = new VersionableArray();
+ memory.attach(array);
memory.create('1');
memory.switchTo('1');
+ memory.create('2');
+ expect((): number => array.push(4)).to.throw('can not update', 'array');
+ expect((): number => (array[1] = 3)).to.throw('can not update', 'array');
+ memory.switchTo('1');
+ memory.remove('2');
+ });
+ it('if try to edit set in a frozen slice', () => {
+ const memory = new Memory();
+ const set = new VersionableSet();
+ memory.attach(set);
+ memory.create('1');
+ memory.switchTo('1');
+ memory.create('1-1');
+ memory.switchTo('1');
+ expect((): VersionableSet => set.add(4)).to.throw('can not update', 'add');
+ expect((): boolean => set.delete(4)).to.throw('can not update', 'delete');
+ expect((): VersionableSet => set.clear()).to.throw('can not update', 'clear');
+ memory.remove('1-1');
+ });
+ it('if try to compress/snapshot with wrong slice', () => {
+ const memory = new Memory();
+ memory.create('test');
+ memory.switchTo('test');
+ memory.create('test-0');
+ memory.switchTo('test-0');
+ memory.create('test-1');
+ memory.switchTo('test-1');
+ memory.create('test-2');
+ memory.switchTo('test-2');
+ expect((): void => memory.snapshot('test-2', 'test', 'snap')).to.throw('merge');
+ });
+ });
- it('link a object marked as not versionable', () => {
- const obj = new VersionableObject();
- const context = {};
- markNotVersionable(context);
- memory.attach(obj);
- // do not throw
- obj['g+k'] = context;
- });
- it('makeVersionable an object after Object.assing by a versionable', () => {
- const versionable = makeVersionable({ test: 1 });
- // do not throw
- const obj = makeVersionable(Object.assign({}, versionable));
- expect(obj.test).to.equal(1);
- obj.test = 3;
- expect(obj.test).to.equal(3);
- expect(versionable.test).to.equal(1, 'Original object value');
- });
- it('custom class/object', () => {
- class SuperObject extends VersionableObject {
- my: number;
- toto: number;
- myMethod(num: number): void {
- this.my = num;
- }
- }
- const obj = new SuperObject();
- obj.toto = 99;
- expect(obj.my).to.equal(
- undefined,
- 'Should keep undefined value on versionable',
- );
- expect(obj.toto).to.equal(99, 'Should set a value on unlinked versionable');
- memory.attach(obj);
- expect(obj.my).to.equal(
- undefined,
- 'Should keep undefined value when link to memory',
- );
- expect(obj.toto).to.equal(99, 'Should keep value when link to memory');
- obj.myMethod(42);
- expect(obj.my).to.equal(42, 'Should use custom method on versionable');
- expect(obj.toto).to.equal(99, 'Should keep same value');
- });
- it('custom Array', () => {
- class SuperArray extends VersionableArray {
- myMethod(num: number): void {
- this.push(num);
- }
+ describe('create versionable', () => {
+ const memory = new Memory();
+ memory.create('1');
+ memory.switchTo('1');
+
+ it('link a object marked as not versionable', () => {
+ const obj = new VersionableObject();
+ const context = {};
+ markNotVersionable(context);
+ memory.attach(obj);
+ // do not throw
+ obj['g+k'] = context;
+ });
+ it('makeVersionable an object after Object.assing by a versionable', () => {
+ const versionable = makeVersionable({ test: 1 });
+ // do not throw
+ const obj = makeVersionable(Object.assign({}, versionable));
+ expect(obj.test).to.equal(1);
+ obj.test = 3;
+ expect(obj.test).to.equal(3);
+ expect(versionable.test).to.equal(1, 'Original object value');
+ });
+ it('custom class/object', () => {
+ class SuperObject extends VersionableObject {
+ my: number;
+ toto: number;
+ myMethod(num: number): void {
+ this.my = num;
}
- const obj = new SuperArray();
- obj.push(1);
- obj.myMethod(99);
- expect(obj.join(',')).to.equal('1,99');
- expect(obj.length).to.equal(2);
- memory.attach(obj);
- expect(obj.join(',')).to.equal('1,99');
- expect(obj.length).to.equal(2);
- obj.myMethod(42);
- expect(obj.join(',')).to.equal('1,99,42');
- expect(obj.length).to.equal(3);
- });
- it('use proxy of versionable array', () => {
- const obj = makeVersionable({ test: [] });
- const array = new VersionableArray();
- const proxy = new Proxy(array, {});
- memory.attach(obj);
- obj.test = proxy;
- expect(obj.test).to.equal(proxy);
- obj.test.push(9);
- expect(array).to.deep.equal([9]);
- });
- it('custom Set who extend versionableSet', () => {
- class SuperSet extends VersionableSet {
- myMethod(num: number): void {
- this.add(num);
- }
+ }
+ const obj = new SuperObject();
+ obj.toto = 99;
+ expect(obj.my).to.equal(undefined, 'Should keep undefined value on versionable');
+ expect(obj.toto).to.equal(99, 'Should set a value on unlinked versionable');
+ memory.attach(obj);
+ expect(obj.my).to.equal(
+ undefined,
+ 'Should keep undefined value when link to memory',
+ );
+ expect(obj.toto).to.equal(99, 'Should keep value when link to memory');
+ obj.myMethod(42);
+ expect(obj.my).to.equal(42, 'Should use custom method on versionable');
+ expect(obj.toto).to.equal(99, 'Should keep same value');
+ });
+ it('custom Array', () => {
+ class SuperArray extends VersionableArray {
+ myMethod(num: number): void {
+ this.push(num);
}
- const obj = new SuperSet();
- obj.add(1);
- obj.myMethod(99);
- function join(obj: SuperSet): string {
- const array = [];
- obj.forEach((value: number): number => array.push(value));
- return array.join(',');
+ }
+ const obj = new SuperArray();
+ obj.push(1);
+ obj.myMethod(99);
+ expect(obj.join(',')).to.equal('1,99');
+ expect(obj.length).to.equal(2);
+ memory.attach(obj);
+ expect(obj.join(',')).to.equal('1,99');
+ expect(obj.length).to.equal(2);
+ obj.myMethod(42);
+ expect(obj.join(',')).to.equal('1,99,42');
+ expect(obj.length).to.equal(3);
+ });
+ it('use proxy of versionable array', () => {
+ const obj = makeVersionable({ test: [] });
+ const array = new VersionableArray();
+ const proxy = new Proxy(array, {});
+ memory.attach(obj);
+ obj.test = proxy;
+ expect(obj.test).to.equal(proxy);
+ obj.test.push(9);
+ expect(array).to.deep.equal([9]);
+ });
+ it('custom Set who extend versionableSet', () => {
+ class SuperSet extends VersionableSet {
+ myMethod(num: number): void {
+ this.add(num);
}
- expect(join(obj)).to.equal('1,99');
- expect(obj.size).to.equal(2);
- memory.attach(obj);
- expect(join(obj)).to.equal('1,99');
- expect(obj.size).to.equal(2);
- obj.myMethod(42);
- expect(join(obj)).to.equal('1,99,42');
- expect(obj.size).to.equal(3);
- });
- it('Set with default value', () => {
- const obj = makeVersionable({
- a: 1,
- b: new Set([{}]),
- c: 3,
- });
- const b = obj.b;
- const memory = new Memory();
- memory.create('1');
- memory.switchTo('1');
- memory.attach(obj);
- memory.create('test');
- memory.switchTo('test');
- obj.b = new VersionableSet([1]);
- expect([...obj.b]).to.deep.equal([1]);
- memory.switchTo('1');
- expect(obj.b).to.equal(b, 'switch object');
- expect(obj.b.size).to.equal(1, 'one object');
- expect([...obj.b]).to.deep.equal(
- [{}],
- 'should have the set who contains an object',
- );
- memory.switchTo('test');
- expect(obj.b.size).to.equal(1, 'one number');
- expect([...obj.b]).to.deep.equal(
- [1],
- 'should have the set who contains a number',
- );
- });
- it('create versionableSet with array value to construct it', () => {
- const set = new VersionableSet([1, 2, 3]);
- const memory = new Memory();
- memory.create('1');
- memory.switchTo('1');
- memory.attach(set);
- expect(set.size).to.equal(3);
+ }
+ const obj = new SuperSet();
+ obj.add(1);
+ obj.myMethod(99);
+ function join(obj: SuperSet): string {
+ const array = [];
+ obj.forEach((value: number): number => array.push(value));
+ return array.join(',');
+ }
+ expect(join(obj)).to.equal('1,99');
+ expect(obj.size).to.equal(2);
+ memory.attach(obj);
+ expect(join(obj)).to.equal('1,99');
+ expect(obj.size).to.equal(2);
+ obj.myMethod(42);
+ expect(join(obj)).to.equal('1,99,42');
+ expect(obj.size).to.equal(3);
+ });
+ it('Set with default value', () => {
+ const obj = makeVersionable({
+ a: 1,
+ b: new Set([{}]),
+ c: 3,
});
- it('create versionableSet with customSet value to construct it', () => {
- class TestSet extends Set {
- method(): number {
- this.add(5);
- return 3;
- }
+ const b = obj.b;
+ const memory = new Memory();
+ memory.create('1');
+ memory.switchTo('1');
+ memory.attach(obj);
+ memory.create('test');
+ memory.switchTo('test');
+ obj.b = new VersionableSet([1]);
+ expect([...obj.b]).to.deep.equal([1]);
+ memory.switchTo('1');
+ expect(obj.b).to.equal(b, 'switch object');
+ expect(obj.b.size).to.equal(1, 'one object');
+ expect([...obj.b]).to.deep.equal(
+ [{}],
+ 'should have the set who contains an object',
+ );
+ memory.switchTo('test');
+ expect(obj.b.size).to.equal(1, 'one number');
+ expect([...obj.b]).to.deep.equal([1], 'should have the set who contains a number');
+ });
+ it('create versionableSet with array value to construct it', () => {
+ const set = new VersionableSet([1, 2, 3]);
+ const memory = new Memory();
+ memory.create('1');
+ memory.switchTo('1');
+ memory.attach(set);
+ expect(set.size).to.equal(3);
+ });
+ it('create versionableSet with customSet value to construct it', () => {
+ class TestSet extends Set {
+ method(): number {
+ this.add(5);
+ return 3;
}
- const set = new TestSet();
- set.add(3);
- const proxy = (new VersionableSet(set) as unknown) as TestSet;
- const memory = new Memory();
- memory.create('1');
- memory.switchTo('1');
- memory.attach(proxy);
- expect(proxy.size).to.equal(1);
- expect(proxy.method()).to.equal(3);
- });
- it('create versionableSet with versionableSet', () => {
- const set = new VersionableSet();
- set.add(3);
- expect(set.size).to.equal(1);
- const set2 = new VersionableSet(set);
- set2.add(4);
- expect(set2.size).to.equal(2);
- expect([...set2.values()]).to.deep.equal([3, 4]);
- set.add(5);
- expect(set2.size).to.equal(2);
- });
- it('custom class who contains proxy of Versionable', () => {
- class SuperObject extends VersionableObject {
- obj: object;
- ref = 0;
- constructor() {
- super();
- this.obj = proxifyVersionable(new VersionableObject(), {
- set: (obj: object, prop: string, value: number): boolean => {
- obj[prop] = value;
- if (value === 42) {
- this.ref++;
- }
- return true;
- },
- });
- }
+ }
+ const set = new TestSet();
+ set.add(3);
+ const proxy = (new VersionableSet(set) as unknown) as TestSet;
+ const memory = new Memory();
+ memory.create('1');
+ memory.switchTo('1');
+ memory.attach(proxy);
+ expect(proxy.size).to.equal(1);
+ expect(proxy.method()).to.equal(3);
+ });
+ it('create versionableSet with versionableSet', () => {
+ const set = new VersionableSet();
+ set.add(3);
+ expect(set.size).to.equal(1);
+ const set2 = new VersionableSet(set);
+ set2.add(4);
+ expect(set2.size).to.equal(2);
+ expect([...set2.values()]).to.deep.equal([3, 4]);
+ set.add(5);
+ expect(set2.size).to.equal(2);
+ });
+ it('custom class who contains proxy of Versionable', () => {
+ class SuperObject extends VersionableObject {
+ obj: object;
+ ref = 0;
+ constructor() {
+ super();
+ this.obj = proxifyVersionable(new VersionableObject(), {
+ set: (obj: object, prop: string, value: number): boolean => {
+ obj[prop] = value;
+ if (value === 42) {
+ this.ref++;
+ }
+ return true;
+ },
+ });
+ }
+ }
+ const instance = new SuperObject();
+ expect(instance.ref).to.equal(0);
+ instance.obj['A'.toString()] = 99;
+ expect(instance.ref).to.equal(0);
+ instance.obj['A'.toString()] = 42;
+ expect(instance.ref).to.equal(1);
+
+ memory.attach(instance);
+
+ expect(instance.obj).to.be.an.instanceof(VersionableObject);
+ expect(instance.obj['A'.toString()]).to.equal(42);
+ expect(instance.ref).to.equal(1);
+ instance.obj['B'.toString()] = 99;
+ expect(instance.ref).to.equal(1);
+ instance.obj['B'.toString()] = 42;
+ expect(instance.ref).to.equal(2);
+ });
+ it('custom class who contains get overwrite of Versionable', () => {
+ class SuperObject extends VersionableObject {
+ obj: Record;
+ constructor() {
+ super();
+ this.obj = new VersionableObject() as Record;
+ Object.defineProperty(this.obj, 'truc', {
+ get: (): number => {
+ return 42;
+ },
+ set: (): void => {
+ return;
+ },
+ });
}
- const instance = new SuperObject();
- expect(instance.ref).to.equal(0);
- instance.obj['A'.toString()] = 99;
- expect(instance.ref).to.equal(0);
- instance.obj['A'.toString()] = 42;
- expect(instance.ref).to.equal(1);
-
- memory.attach(instance);
-
- expect(instance.obj).to.be.an.instanceof(VersionableObject);
- expect(instance.obj['A'.toString()]).to.equal(42);
- expect(instance.ref).to.equal(1);
- instance.obj['B'.toString()] = 99;
- expect(instance.ref).to.equal(1);
- instance.obj['B'.toString()] = 42;
- expect(instance.ref).to.equal(2);
- });
- it('custom class who contains get overwrite of Versionable', () => {
- class SuperObject extends VersionableObject {
- obj: Record;
- constructor() {
- super();
- this.obj = new VersionableObject() as Record;
- Object.defineProperty(this.obj, 'truc', {
- get: (): number => {
- return 42;
- },
- set: (): void => {
- return;
- },
- });
- }
- get getter(): Record {
- return this.obj;
- }
+ get getter(): Record {
+ return this.obj;
}
- const instance = new SuperObject();
- instance.obj.A = 99;
- expect(instance.obj.A).to.equal(99);
- expect(instance.obj.truc).to.equal(42);
- instance.obj.truc = 99;
- expect(instance.obj.truc).to.equal(42);
- expect(instance.obj).to.equal(instance.getter);
-
- memory.attach(instance);
-
- expect(instance.obj).to.be.an.instanceof(VersionableObject);
- instance.obj.B = 99;
- expect(instance.obj.B).to.equal(99);
- expect(instance.obj.truc).to.equal(42);
- instance.obj.truc = 99;
- expect(instance.obj.truc).to.equal(42);
- expect(instance.obj).to.equal(instance.getter);
- });
+ }
+ const instance = new SuperObject();
+ instance.obj.A = 99;
+ expect(instance.obj.A).to.equal(99);
+ expect(instance.obj.truc).to.equal(42);
+ instance.obj.truc = 99;
+ expect(instance.obj.truc).to.equal(42);
+ expect(instance.obj).to.equal(instance.getter);
+
+ memory.attach(instance);
+
+ expect(instance.obj).to.be.an.instanceof(VersionableObject);
+ instance.obj.B = 99;
+ expect(instance.obj.B).to.equal(99);
+ expect(instance.obj.truc).to.equal(42);
+ instance.obj.truc = 99;
+ expect(instance.obj.truc).to.equal(42);
+ expect(instance.obj).to.equal(instance.getter);
});
+ });
- describe('make versionable', () => {
- const memory = new Memory();
- memory.create('1');
- memory.switchTo('1');
+ describe('make versionable', () => {
+ const memory = new Memory();
+ memory.create('1');
+ memory.switchTo('1');
- it('object', () => {
- const obj: Record = {
- a: 1,
- b: 2,
- c: 3,
- };
- Object.defineProperty(obj, 'z', {
- get() {
- return 42;
- },
- enumerable: false,
- configurable: false,
- });
- const proxy = makeVersionable(obj);
- memory.attach(proxy);
- expect(proxy.a).to.equal(1);
- expect(proxy.b).to.equal(2);
- expect(proxy.c).to.equal(3);
- expect(proxy.z).to.equal(42);
- });
- it('object who contains Object', () => {
- const obj = makeVersionable({
- a: 1,
- b: {
- x: 1,
+ it('object', () => {
+ const obj: Record = {
+ a: 1,
+ b: 2,
+ c: 3,
+ };
+ Object.defineProperty(obj, 'z', {
+ get() {
+ return 42;
+ },
+ enumerable: false,
+ configurable: false,
+ });
+ const proxy = makeVersionable(obj);
+ memory.attach(proxy);
+ expect(proxy.a).to.equal(1);
+ expect(proxy.b).to.equal(2);
+ expect(proxy.c).to.equal(3);
+ expect(proxy.z).to.equal(42);
+ });
+ it('object who contains Object', () => {
+ const obj = makeVersionable({
+ a: 1,
+ b: {
+ x: 1,
+ y: 2,
+ },
+ c: 3,
+ });
+ memory.attach(obj);
+ expect(obj.a).to.equal(1);
+ expect(obj.b).to.be.a('object');
+ expect(Object.keys(obj.b).join()).to.equal('x,y');
+ expect(obj.b.x).to.equal(1);
+ expect(obj.b.y).to.equal(2);
+ expect(obj.c).to.equal(3);
+ });
+ it('object who contains Object who contains Object', () => {
+ const obj = makeVersionable({
+ a: 1,
+ b: {
+ x: 1,
+ y: {
+ e: 1,
y: 2,
},
- c: 3,
- });
- memory.attach(obj);
- expect(obj.a).to.equal(1);
- expect(obj.b).to.be.a('object');
- expect(Object.keys(obj.b).join()).to.equal('x,y');
- expect(obj.b.x).to.equal(1);
- expect(obj.b.y).to.equal(2);
- expect(obj.c).to.equal(3);
- });
- it('object who contains Object who contains Object', () => {
- const obj = makeVersionable({
- a: 1,
- b: {
- x: 1,
- y: {
- e: 1,
- y: 2,
- },
- },
- c: 3,
- });
- memory.attach(obj);
- expect(obj.b).to.be.a('object');
- expect(Object.keys(obj.b).join()).to.equal('x,y');
- expect(obj.b.x).to.equal(1);
- expect(obj.b.y).to.be.a('object');
- expect(Object.keys(obj.b.y).join()).to.equal('e,y');
- expect(obj.b.y.e).to.equal(1);
- expect(obj.b.y.y).to.equal(2);
- });
- it('object who contains Set', () => {
- const obj = makeVersionable({
- a: 1,
- b: new Set(['x', 'y', 'z']),
- c: 3,
- });
- memory.attach(obj);
- expect(obj.a).to.equal(1);
- expect(obj.b).to.be.instanceOf(Set);
- expect(obj.b.has('x')).to.equal(true);
- expect(obj.b.has('y')).to.equal(true);
- expect(obj.b.has('z')).to.equal(true);
- expect(obj.b.has('h')).to.equal(false);
- expect(obj.c).to.equal(3);
- });
- it('object who contains Array', () => {
- const obj = makeVersionable({
+ },
+ c: 3,
+ });
+ memory.attach(obj);
+ expect(obj.b).to.be.a('object');
+ expect(Object.keys(obj.b).join()).to.equal('x,y');
+ expect(obj.b.x).to.equal(1);
+ expect(obj.b.y).to.be.a('object');
+ expect(Object.keys(obj.b.y).join()).to.equal('e,y');
+ expect(obj.b.y.e).to.equal(1);
+ expect(obj.b.y.y).to.equal(2);
+ });
+ it('object who contains Set', () => {
+ const obj = makeVersionable({
+ a: 1,
+ b: new Set(['x', 'y', 'z']),
+ c: 3,
+ });
+ memory.attach(obj);
+ expect(obj.a).to.equal(1);
+ expect(obj.b).to.be.instanceOf(Set);
+ expect(obj.b.has('x')).to.equal(true);
+ expect(obj.b.has('y')).to.equal(true);
+ expect(obj.b.has('z')).to.equal(true);
+ expect(obj.b.has('h')).to.equal(false);
+ expect(obj.c).to.equal(3);
+ });
+ it('object who contains Array', () => {
+ const obj = makeVersionable({
+ a: 1,
+ b: ['x', 'y', 'z'],
+ c: 3,
+ });
+ memory.attach(obj);
+ expect(obj.a).to.equal(1);
+ expect(obj.b).to.be.instanceOf(Array);
+ expect(obj.b.indexOf('x')).to.equal(0, "should find 'x' at 0");
+ expect(obj.b.indexOf('y')).to.equal(1, "should find 'y' at 0");
+ expect(obj.b.indexOf('z')).to.equal(2, "should find 'z' at 0");
+ expect(obj.b.indexOf('h')).to.equal(-1, "should not find 'h'");
+ let str = '';
+ obj.b.forEach(v => {
+ str += v;
+ });
+ expect(str).to.equal('xyz', 'should use forEach');
+ expect(obj.c).to.equal(3);
+
+ memory.switchTo('');
+ expect(obj).to.deep.equal({}, 'should not find object in other memory slice');
+ memory.switchTo('1');
+ expect(obj).to.deep.equal(
+ {
a: 1,
b: ['x', 'y', 'z'],
c: 3,
- });
- memory.attach(obj);
- expect(obj.a).to.equal(1);
- expect(obj.b).to.be.instanceOf(Array);
- expect(obj.b.indexOf('x')).to.equal(0, "should find 'x' at 0");
- expect(obj.b.indexOf('y')).to.equal(1, "should find 'y' at 0");
- expect(obj.b.indexOf('z')).to.equal(2, "should find 'z' at 0");
- expect(obj.b.indexOf('h')).to.equal(-1, "should not find 'h'");
- let str = '';
- obj.b.forEach(v => {
- str += v;
- });
- expect(str).to.equal('xyz', 'should use forEach');
- expect(obj.c).to.equal(3);
-
- memory.switchTo('');
- expect(obj).to.deep.equal({}, 'should not find object in other memory slice');
- memory.switchTo('1');
- expect(obj).to.deep.equal(
- {
- a: 1,
- b: ['x', 'y', 'z'],
- c: 3,
+ },
+ 'Should have all values',
+ );
+ });
+ it('complexe', () => {
+ const full = makeVersionable({
+ a: 1,
+ b: ['x', 'y', 'z'],
+ c: new Set(['x', 'y', 'z']),
+ d: {
+ x: 1,
+ y: {
+ e: 1,
+ y: 2,
},
- 'Should have all values',
- );
+ },
+ e: [],
+ f: {},
+ g: new Set(),
+ h: 'a',
+ i: [{ 'a': 1 }, 55],
+ j: new Set([{ 'b': 1 }]),
+ k: [['a'], ['b', 'c', 'd'], ['x', 'y', 'z']],
});
- it('complexe', () => {
- const full = makeVersionable({
- a: 1,
- b: ['x', 'y', 'z'],
- c: new Set(['x', 'y', 'z']),
- d: {
- x: 1,
- y: {
- e: 1,
- y: 2,
- },
- },
- e: [],
- f: {},
- g: new Set(),
- h: 'a',
- i: [{ 'a': 1 }, 55],
- j: new Set([{ 'b': 1 }]),
- k: [['a'], ['b', 'c', 'd'], ['x', 'y', 'z']],
- });
-
- expect(!!full.i[0][memoryProxyPramsKey].ID).to.equal(
- true,
- 'proxify object in array',
- );
- const list = [];
- full.j.forEach((item: object) => {
- list.push(item);
- });
- expect(!!list[0][memoryProxyPramsKey].ID).to.equal(
- true,
- 'proxify object in set',
- );
- expect(!!full.k[0][memoryProxyPramsKey].ID).to.equal(
- true,
- 'proxify array in array',
- );
-
- memory.attach(full);
- memory.create('1-a');
- memory.switchTo('1-a');
- full.k[1][3] = 'e';
- expect(full.k[1]).to.deep.equal(
- ['b', 'c', 'd', 'e'],
- 'should link to memory and change the array in array',
- );
- memory.switchTo('1');
- expect(full.k[1]).to.deep.equal(
- ['b', 'c', 'd'],
- 'should have the previous value in the array in array',
- );
+ expect(!!full.i[0][memoryProxyPramsKey].ID).to.equal(
+ true,
+ 'proxify object in array',
+ );
+ const list = [];
+ full.j.forEach((item: object) => {
+ list.push(item);
});
- it('make versionable a class who extends Set class', () => {
- class TestSet extends Set {
- method(): number {
- this.add(5);
- return 3;
- }
- }
- let set = new TestSet();
- expect(set.size).to.equal(0, 'before link, set is empty');
- expect(set.method()).to.equal(3, 'before link, call method');
- expect(set.size).to.equal(1, 'before link, set contains the value');
-
- set = makeVersionable(new TestSet());
- expect(set.size).to.equal(0, 'make versionable, set is empty');
- expect(set.method()).to.equal(3, 'make versionable, call method');
- expect(set.size).to.equal(1, 'make versionable, set contains the value');
-
- set = makeVersionable(new TestSet());
- const memory = new Memory();
- memory.create('1');
- memory.switchTo('1');
- memory.attach(set);
- expect(set.size).to.equal(0, 'linked, set is empty');
- expect(set.method()).to.equal(3, 'linked, call method');
- expect(set.size).to.equal(1, 'linked, set contains the value');
- });
- });
+ expect(!!list[0][memoryProxyPramsKey].ID).to.equal(true, 'proxify object in set');
+ expect(!!full.k[0][memoryProxyPramsKey].ID).to.equal(
+ true,
+ 'proxify array in array',
+ );
- describe('specific getter', () => {
- it('object getter should keep proxy for "this"', () => {
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- let obj: any = null;
- let test = 0;
- obj = makeVersionable({
- get test() {
- if (obj) {
- test++;
- expect(this).to.equal(obj);
- }
- return test;
- },
- });
- expect(obj.test).to.equal(1);
- });
- it('custom class getter should keep proxy for "this"', () => {
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- let obj: any = null;
- let test = 0;
- class Custom extends VersionableObject {
- get test(): number {
- if (obj) {
- test++;
- expect(this).to.equal(obj);
- }
- return test;
- }
- }
- obj = new Custom();
- expect(obj.test).to.equal(1);
- });
- it('extended custom class getter should keep proxy for "this"', () => {
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- let obj: any = null;
- let test = 0;
- class Custom extends VersionableObject {
- get test(): number {
- if (obj) {
- test++;
- expect(this).to.equal(obj);
- }
- return test;
- }
- }
- class CustomCustom extends Custom {}
- obj = new CustomCustom();
- expect(obj.test).to.equal(1);
- });
- it('overwrite toString method', () => {
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- let obj: any = null;
- class Custom extends VersionableObject {
- toString(): string {
- return '5';
- }
- }
- class CustomCustom extends Custom {}
- obj = new CustomCustom();
- expect(typeof obj.toString).to.equal('function');
- expect(obj.toString()).to.equal('5');
- });
- it('get constructor', () => {
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- let obj: any = null;
- class Custom extends VersionableObject {
- getConstructor(): string {
- return this.constructor.name;
- }
- }
- class CustomCustom extends Custom {}
- obj = new CustomCustom();
- expect(obj.getConstructor()).to.equal('CustomCustom');
- });
- it.skip('array getter should keep proxy for "this" (use defineProperty)', () => {
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- const obj = new VersionableArray() as any;
- let test = 0;
- Object.defineProperty(obj, 'test', {
- get() {
- if (obj) {
- test++;
- expect(this).to.equal(obj);
- }
- return test;
- },
- });
- expect(obj.test).to.equal(1);
- });
- it('array slice', () => {
- const memory = new Memory();
- const array = makeVersionable([1, 2, 3, 4, 5]);
- memory.attach(array);
- const newArray = array.slice();
- expect(newArray).to.deep.equal([1, 2, 3, 4, 5]);
- expect(newArray[memoryProxyPramsKey]).to.equal(undefined);
- });
- it('array slice on VersionableArray', () => {
- const memory = new Memory();
- const array = new VersionableArray(1, 2, 3, 4, 5);
- memory.attach(array);
- const newArray = array.slice();
- expect(newArray).to.deep.equal([1, 2, 3, 4, 5]);
- expect(newArray[memoryProxyPramsKey].ID).to.not.equal(
- array[memoryProxyPramsKey].ID,
- );
- });
- it('array splice', () => {
- const memory = new Memory();
- const array = makeVersionable([1, 2, 3, 4, 5]);
- memory.attach(array);
- const newArray = array.splice(1, 3);
- expect(array).to.deep.equal([1, 5]);
- expect(newArray).to.deep.equal([2, 3, 4]);
- expect(newArray[memoryProxyPramsKey]).to.equal(undefined);
- });
- it('array splice on VersionableArray', () => {
- const memory = new Memory();
- const array = new VersionableArray(1, 2, 3, 4, 5);
- memory.attach(array);
- const newArray = array.splice(1, 3);
- expect(array).to.deep.equal([1, 5]);
- expect(newArray).to.deep.equal([2, 3, 4]);
- expect(newArray[memoryProxyPramsKey].ID).to.not.equal(
- array[memoryProxyPramsKey].ID,
- );
- });
- it('array forEach', () => {
- const memory = new Memory();
- const array = new VersionableArray(1, 2, 3, 4, 5);
- memory.attach(array);
- const values = [];
- const indexes = [];
- const arrays = [];
- const res = array.forEach((value, index, array) => {
- values.push(value);
- indexes.push(index);
- arrays.push(array);
- return 9;
- });
- expect(res).to.equal(undefined);
- expect(values).to.deep.equal([1, 2, 3, 4, 5]);
- expect(indexes).to.deep.equal([0, 1, 2, 3, 4]);
- expect(arrays).to.deep.equal([array, array, array, array, array]);
- });
- it('array map', () => {
- const memory = new Memory();
- const array = new VersionableArray(1, 2, 3, 4, 5);
- memory.attach(array);
- const values = [];
- const indexes = [];
- const arrays = [];
- const res = array.map((value: number, index, array) => {
- values.push(value);
- indexes.push(index);
- arrays.push(array);
- return 8 + value;
- });
- expect(res[memoryProxyPramsKey]).to.equal(undefined);
- expect(res).to.deep.equal([9, 10, 11, 12, 13]);
- expect(values).to.deep.equal([1, 2, 3, 4, 5]);
- expect(indexes).to.deep.equal([0, 1, 2, 3, 4]);
- expect(arrays).to.deep.equal([array, array, array, array, array]);
- });
- it('array filter', () => {
- const memory = new Memory();
- const array = new VersionableArray(1, 2, 3, 4, 5);
- memory.attach(array);
- const values = [];
- const indexes = [];
- const arrays = [];
- const res = array.filter((value: number, index, array) => {
- values.push(value);
- indexes.push(index);
- arrays.push(array);
- return !!(value % 2);
- });
- expect(res[memoryProxyPramsKey]).to.equal(undefined);
- expect(res).to.deep.equal([1, 3, 5]);
- expect(values).to.deep.equal([1, 2, 3, 4, 5]);
- expect(indexes).to.deep.equal([0, 1, 2, 3, 4]);
- expect(arrays).to.deep.equal([array, array, array, array, array]);
- });
- it('array indexOf', () => {
- const memory = new Memory();
- memory.create('test');
- memory.switchTo('test');
-
- const result = [];
- const array = new VersionableArray();
- for (let k = 0; k < 20; k++) {
- array.push(k);
- result.push(k);
- }
- memory.attach(array);
- for (let k = 20; k < 40; k++) {
- array.push(k);
- result.push(k);
- }
+ memory.attach(full);
- for (let k = 0, len = array.length; k < len; k++) {
- expect(array.indexOf(k)).to.equal(k);
+ memory.create('1-a');
+ memory.switchTo('1-a');
+ full.k[1][3] = 'e';
+ expect(full.k[1]).to.deep.equal(
+ ['b', 'c', 'd', 'e'],
+ 'should link to memory and change the array in array',
+ );
+ memory.switchTo('1');
+ expect(full.k[1]).to.deep.equal(
+ ['b', 'c', 'd'],
+ 'should have the previous value in the array in array',
+ );
+ });
+ it('make versionable a class who extends Set class', () => {
+ class TestSet extends Set {
+ method(): number {
+ this.add(5);
+ return 3;
}
- expect(array.indexOf(-99)).to.equal(-1);
-
- array[3] = -3;
-
- expect(array.indexOf(3)).to.equal(-1);
- expect(array.indexOf(-3)).to.equal(3);
- });
- it('array indexOf value several times', () => {
- const array = new VersionableArray(0, 1, 2, 3, 1, 4);
- const memory = new Memory();
- memory.create('test');
- memory.switchTo('test');
- memory.attach(array);
-
- expect(array.indexOf(1)).to.equal(1, 'should return the first index');
- array[1] = 42;
- expect(array.indexOf(1)).to.equal(4, 'should return the other index');
- expect(array.indexOf(4)).to.equal(5, 'should the index');
- array[1] = 4;
- expect(array.indexOf(4)).to.equal(1, 'should the newest index');
- });
+ }
+ let set = new TestSet();
+ expect(set.size).to.equal(0, 'before link, set is empty');
+ expect(set.method()).to.equal(3, 'before link, call method');
+ expect(set.size).to.equal(1, 'before link, set contains the value');
+
+ set = makeVersionable(new TestSet());
+ expect(set.size).to.equal(0, 'make versionable, set is empty');
+ expect(set.method()).to.equal(3, 'make versionable, call method');
+ expect(set.size).to.equal(1, 'make versionable, set contains the value');
+
+ set = makeVersionable(new TestSet());
+ const memory = new Memory();
+ memory.create('1');
+ memory.switchTo('1');
+ memory.attach(set);
+ expect(set.size).to.equal(0, 'linked, set is empty');
+ expect(set.method()).to.equal(3, 'linked, call method');
+ expect(set.size).to.equal(1, 'linked, set contains the value');
});
+ });
- describe('specific setter', () => {
- it('object setter should keep proxy for "this"', () => {
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- let obj: any = null;
- let test = 0;
- obj = makeVersionable({
- get test() {
- return 0;
- },
- set test(x: number) {
+ describe('specific getter', () => {
+ it('object getter should keep proxy for "this"', () => {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ let obj: any = null;
+ let test = 0;
+ obj = makeVersionable({
+ get test() {
+ if (obj) {
test++;
expect(this).to.equal(obj);
- },
- });
- obj.test = 1;
- expect(test).to.equal(1);
- });
- it('custom class setter should keep proxy for "this"', () => {
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- let obj: any = null;
- let test = 0;
- class Custom extends VersionableObject {
- get test(): number {
- return 0;
}
- set test(x: number) {
+ return test;
+ },
+ });
+ expect(obj.test).to.equal(1);
+ });
+ it('custom class getter should keep proxy for "this"', () => {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ let obj: any = null;
+ let test = 0;
+ class Custom extends VersionableObject {
+ get test(): number {
+ if (obj) {
test++;
expect(this).to.equal(obj);
}
+ return test;
}
- obj = new Custom();
- obj.test = 1;
- expect(test).to.equal(1);
- });
- it('extended custom class setter should keep proxy for "this"', () => {
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- let obj: any = null;
- let test = 0;
- class Custom extends VersionableObject {
- get test(): number {
- return 0;
- }
- set test(x: number) {
+ }
+ obj = new Custom();
+ expect(obj.test).to.equal(1);
+ });
+ it('extended custom class getter should keep proxy for "this"', () => {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ let obj: any = null;
+ let test = 0;
+ class Custom extends VersionableObject {
+ get test(): number {
+ if (obj) {
test++;
expect(this).to.equal(obj);
}
+ return test;
}
- class CustomCustom extends Custom {}
- obj = new CustomCustom();
- obj.test = 1;
- expect(test).to.equal(1);
- });
- it('array setter should keep proxy for "this"', () => {
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- const obj = new VersionableArray() as any;
- let test = 0;
- Object.defineProperty(obj, 'test', {
- get() {
- return 0;
- },
- set() {
+ }
+ class CustomCustom extends Custom {}
+ obj = new CustomCustom();
+ expect(obj.test).to.equal(1);
+ });
+ it('overwrite toString method', () => {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ let obj: any = null;
+ class Custom extends VersionableObject {
+ toString(): string {
+ return '5';
+ }
+ }
+ class CustomCustom extends Custom {}
+ obj = new CustomCustom();
+ expect(typeof obj.toString).to.equal('function');
+ expect(obj.toString()).to.equal('5');
+ });
+ it('get constructor', () => {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ let obj: any = null;
+ class Custom extends VersionableObject {
+ getConstructor(): string {
+ return this.constructor.name;
+ }
+ }
+ class CustomCustom extends Custom {}
+ obj = new CustomCustom();
+ expect(obj.getConstructor()).to.equal('CustomCustom');
+ });
+ it.skip('array getter should keep proxy for "this" (use defineProperty)', () => {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ const obj = new VersionableArray() as any;
+ let test = 0;
+ Object.defineProperty(obj, 'test', {
+ get() {
+ if (obj) {
test++;
expect(this).to.equal(obj);
- },
- });
- obj.test = 1;
- expect(test).to.equal(1);
- });
- it('use Object.assing on versionable object', () => {
- const versionable = makeVersionable({ test: 1 });
- const obj = makeVersionable({ test: 5 });
- Object.assign(obj, versionable);
- expect(obj[memoryProxyPramsKey]).to.not.equal(versionable[memoryProxyPramsKey]);
- expect(obj.test).to.equal(1);
- obj.test = 3;
- expect(obj.test).to.equal(3);
- expect(versionable.test).to.equal(1, 'Original object value');
- });
- it('use Object.assing on versionable array', () => {
- const versionable = makeVersionable([1, 2, 3]);
- const array = makeVersionable([]);
- Object.assign(array, versionable);
- expect(array[memoryProxyPramsKey]).to.not.equal(
- versionable[memoryProxyPramsKey],
- );
- expect(array).to.deep.equal([1, 2, 3]);
- array[1] = 42;
- expect(array[1]).to.equal(42);
- expect(versionable[1]).to.equal(2, 'Original object value');
+ }
+ return test;
+ },
});
- it('array changes with splice', () => {
- const memory = new Memory();
- const array = makeVersionable(['x', 'y', 'z']);
- memory.attach(array);
- memory.create('1');
- memory.create('test');
- memory.switchTo('test');
- array.splice(1, 0, 'B'); // ['x', 'B', 'y', 'z']
- array.push('z'); // ['x', 'B', 'y', 'z', 'z']
- array.splice(3, 0, 'A'); // ['x', 'B', 'y', 'A', 'z', 'z']
-
- expect(array.slice()).to.deep.equal(['x', 'B', 'y', 'A', 'z', 'z']);
-
- array.splice(1, 2); // ['x', 'A', 'z', 'z']
- array[2] = 'y'; // ['x', 'A', 'y', 'z']
-
- expect(array.slice()).to.deep.equal(['x', 'A', 'y', 'z'], 'before switch');
-
- memory.switchTo('1');
- memory.switchTo('test');
+ expect(obj.test).to.equal(1);
+ });
+ it('array slice', () => {
+ const memory = new Memory();
+ const array = makeVersionable([1, 2, 3, 4, 5]);
+ memory.attach(array);
+ const newArray = array.slice();
+ expect(newArray).to.deep.equal([1, 2, 3, 4, 5]);
+ expect(newArray[memoryProxyPramsKey]).to.equal(undefined);
+ });
+ it('array slice on VersionableArray', () => {
+ const memory = new Memory();
+ const array = new VersionableArray(1, 2, 3, 4, 5);
+ memory.attach(array);
+ const newArray = array.slice();
+ expect(newArray).to.deep.equal([1, 2, 3, 4, 5]);
+ expect(newArray[memoryProxyPramsKey].ID).to.not.equal(
+ array[memoryProxyPramsKey].ID,
+ );
+ });
+ it('array splice', () => {
+ const memory = new Memory();
+ const array = makeVersionable([1, 2, 3, 4, 5]);
+ memory.attach(array);
+ const newArray = array.splice(1, 3);
+ expect(array).to.deep.equal([1, 5]);
+ expect(newArray).to.deep.equal([2, 3, 4]);
+ expect(newArray[memoryProxyPramsKey]).to.equal(undefined);
+ });
+ it('array splice on VersionableArray', () => {
+ const memory = new Memory();
+ const array = new VersionableArray(1, 2, 3, 4, 5);
+ memory.attach(array);
+ const newArray = array.splice(1, 3);
+ expect(array).to.deep.equal([1, 5]);
+ expect(newArray).to.deep.equal([2, 3, 4]);
+ expect(newArray[memoryProxyPramsKey].ID).to.not.equal(
+ array[memoryProxyPramsKey].ID,
+ );
+ });
+ it('array forEach', () => {
+ const memory = new Memory();
+ const array = new VersionableArray(1, 2, 3, 4, 5);
+ memory.attach(array);
+ const values = [];
+ const indexes = [];
+ const arrays = [];
+ const res = array.forEach((value, index, array) => {
+ values.push(value);
+ indexes.push(index);
+ arrays.push(array);
+ return 9;
+ });
+ expect(res).to.equal(undefined);
+ expect(values).to.deep.equal([1, 2, 3, 4, 5]);
+ expect(indexes).to.deep.equal([0, 1, 2, 3, 4]);
+ expect(arrays).to.deep.equal([array, array, array, array, array]);
+ });
+ it('array map', () => {
+ const memory = new Memory();
+ const array = new VersionableArray(1, 2, 3, 4, 5);
+ memory.attach(array);
+ const values = [];
+ const indexes = [];
+ const arrays = [];
+ const res = array.map((value: number, index, array) => {
+ values.push(value);
+ indexes.push(index);
+ arrays.push(array);
+ return 8 + value;
+ });
+ expect(res[memoryProxyPramsKey]).to.equal(undefined);
+ expect(res).to.deep.equal([9, 10, 11, 12, 13]);
+ expect(values).to.deep.equal([1, 2, 3, 4, 5]);
+ expect(indexes).to.deep.equal([0, 1, 2, 3, 4]);
+ expect(arrays).to.deep.equal([array, array, array, array, array]);
+ });
+ it('array filter', () => {
+ const memory = new Memory();
+ const array = new VersionableArray(1, 2, 3, 4, 5);
+ memory.attach(array);
+ const values = [];
+ const indexes = [];
+ const arrays = [];
+ const res = array.filter((value: number, index, array) => {
+ values.push(value);
+ indexes.push(index);
+ arrays.push(array);
+ return !!(value % 2);
+ });
+ expect(res[memoryProxyPramsKey]).to.equal(undefined);
+ expect(res).to.deep.equal([1, 3, 5]);
+ expect(values).to.deep.equal([1, 2, 3, 4, 5]);
+ expect(indexes).to.deep.equal([0, 1, 2, 3, 4]);
+ expect(arrays).to.deep.equal([array, array, array, array, array]);
+ });
+ it('array indexOf', () => {
+ const memory = new Memory();
+ memory.create('test');
+ memory.switchTo('test');
+
+ const result = [];
+ const array = new VersionableArray();
+ for (let k = 0; k < 20; k++) {
+ array.push(k);
+ result.push(k);
+ }
+ memory.attach(array);
+ for (let k = 20; k < 40; k++) {
+ array.push(k);
+ result.push(k);
+ }
+
+ for (let k = 0, len = array.length; k < len; k++) {
+ expect(array.indexOf(k)).to.equal(k);
+ }
+ expect(array.indexOf(-99)).to.equal(-1);
+
+ array[3] = -3;
+
+ expect(array.indexOf(3)).to.equal(-1);
+ expect(array.indexOf(-3)).to.equal(3);
+ });
+ it('array indexOf value several times', () => {
+ const array = new VersionableArray(0, 1, 2, 3, 1, 4);
+ const memory = new Memory();
+ memory.create('test');
+ memory.switchTo('test');
+ memory.attach(array);
+
+ expect(array.indexOf(1)).to.equal(1, 'should return the first index');
+ array[1] = 42;
+ expect(array.indexOf(1)).to.equal(4, 'should return the other index');
+ expect(array.indexOf(4)).to.equal(5, 'should the index');
+ array[1] = 4;
+ expect(array.indexOf(4)).to.equal(1, 'should the newest index');
+ });
+ });
- expect(array.slice()).to.deep.equal(['x', 'A', 'y', 'z']);
- });
- it('array changes with splice without second value', () => {
- const memory = new Memory();
- const array = makeVersionable(['x', 'y', 'z']);
- memory.attach(array);
- memory.create('1');
- memory.create('test');
- memory.switchTo('test');
- array.splice(1);
- memory.switchTo('1');
- memory.switchTo('test');
- expect(array.slice()).to.deep.equal(['x']);
+ describe('specific setter', () => {
+ it('object setter should keep proxy for "this"', () => {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ let obj: any = null;
+ let test = 0;
+ obj = makeVersionable({
+ get test() {
+ return 0;
+ },
+ set test(x: number) {
+ test++;
+ expect(this).to.equal(obj);
+ },
});
- it('remove item with splice at index -2', () => {
- const memory = new Memory();
- const array = makeVersionable(['x', 'y', 'z']);
- memory.attach(array);
-
- memory.create('test');
- memory.switchTo('test');
-
- array.splice(-2, 1);
-
- expect(array.slice()).to.deep.equal(['x', 'z']);
+ obj.test = 1;
+ expect(test).to.equal(1);
+ });
+ it('custom class setter should keep proxy for "this"', () => {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ let obj: any = null;
+ let test = 0;
+ class Custom extends VersionableObject {
+ get test(): number {
+ return 0;
+ }
+ set test(x: number) {
+ test++;
+ expect(this).to.equal(obj);
+ }
+ }
+ obj = new Custom();
+ obj.test = 1;
+ expect(test).to.equal(1);
+ });
+ it('extended custom class setter should keep proxy for "this"', () => {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ let obj: any = null;
+ let test = 0;
+ class Custom extends VersionableObject {
+ get test(): number {
+ return 0;
+ }
+ set test(x: number) {
+ test++;
+ expect(this).to.equal(obj);
+ }
+ }
+ class CustomCustom extends Custom {}
+ obj = new CustomCustom();
+ obj.test = 1;
+ expect(test).to.equal(1);
+ });
+ it('array setter should keep proxy for "this"', () => {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ const obj = new VersionableArray() as any;
+ let test = 0;
+ Object.defineProperty(obj, 'test', {
+ get() {
+ return 0;
+ },
+ set() {
+ test++;
+ expect(this).to.equal(obj);
+ },
});
- it('add items with splice at index -1 and switch memory', () => {
- const memory = new Memory();
- const array = makeVersionable(['x', 'y', 'z']);
- memory.attach(array);
+ obj.test = 1;
+ expect(test).to.equal(1);
+ });
+ it('use Object.assing on versionable object', () => {
+ const versionable = makeVersionable({ test: 1 });
+ const obj = makeVersionable({ test: 5 });
+ Object.assign(obj, versionable);
+ expect(obj[memoryProxyPramsKey]).to.not.equal(versionable[memoryProxyPramsKey]);
+ expect(obj.test).to.equal(1);
+ obj.test = 3;
+ expect(obj.test).to.equal(3);
+ expect(versionable.test).to.equal(1, 'Original object value');
+ });
+ it('use Object.assing on versionable array', () => {
+ const versionable = makeVersionable([1, 2, 3]);
+ const array = makeVersionable([]);
+ Object.assign(array, versionable);
+ expect(array[memoryProxyPramsKey]).to.not.equal(versionable[memoryProxyPramsKey]);
+ expect(array).to.deep.equal([1, 2, 3]);
+ array[1] = 42;
+ expect(array[1]).to.equal(42);
+ expect(versionable[1]).to.equal(2, 'Original object value');
+ });
+ it('array changes with splice', () => {
+ const memory = new Memory();
+ const array = makeVersionable(['x', 'y', 'z']);
+ memory.attach(array);
+ memory.create('1');
+ memory.create('test');
+ memory.switchTo('test');
+ array.splice(1, 0, 'B'); // ['x', 'B', 'y', 'z']
+ array.push('z'); // ['x', 'B', 'y', 'z', 'z']
+ array.splice(3, 0, 'A'); // ['x', 'B', 'y', 'A', 'z', 'z']
- memory.create('test');
- memory.switchTo('test');
+ expect(array.slice()).to.deep.equal(['x', 'B', 'y', 'A', 'z', 'z']);
- memory.create('1-1');
- memory.switchTo('1-1');
- array.splice(-1, 0, 'A', 'B');
+ array.splice(1, 2); // ['x', 'A', 'z', 'z']
+ array[2] = 'y'; // ['x', 'A', 'y', 'z']
- expect(array.slice()).to.deep.equal(['x', 'y', 'A', 'B', 'z']);
+ expect(array.slice()).to.deep.equal(['x', 'A', 'y', 'z'], 'before switch');
- memory.switchTo('test');
- memory.create('1-2');
- memory.switchTo('1-2');
- array.splice(-1, 0, 'C', 'D');
+ memory.switchTo('1');
+ memory.switchTo('test');
- expect(array.slice()).to.deep.equal(['x', 'y', 'C', 'D', 'z']);
- });
- it('ramove and add items with splice at index -2', () => {
- const memory = new Memory();
- const array = makeVersionable(['x', 'y', 'z']);
- memory.attach(array);
+ expect(array.slice()).to.deep.equal(['x', 'A', 'y', 'z']);
+ });
+ it('array changes with splice without second value', () => {
+ const memory = new Memory();
+ const array = makeVersionable(['x', 'y', 'z']);
+ memory.attach(array);
+ memory.create('1');
+ memory.create('test');
+ memory.switchTo('test');
+ array.splice(1);
+ memory.switchTo('1');
+ memory.switchTo('test');
+ expect(array.slice()).to.deep.equal(['x']);
+ });
+ it('remove item with splice at index -2', () => {
+ const memory = new Memory();
+ const array = makeVersionable(['x', 'y', 'z']);
+ memory.attach(array);
- memory.create('test');
- memory.switchTo('test');
+ memory.create('test');
+ memory.switchTo('test');
- array.splice(-2, 1, 'A', 'B');
+ array.splice(-2, 1);
- expect(array.slice()).to.deep.equal(['x', 'A', 'B', 'z']);
- });
+ expect(array.slice()).to.deep.equal(['x', 'z']);
});
-
- describe('specific updates', () => {
+ it('add items with splice at index -1 and switch memory', () => {
const memory = new Memory();
- const full = makeVersionable({
- a: 1,
- b: ['x', 'y', 'z'],
- c: new Set(['x', 'y', 'z']),
- d: {
- x: 1,
- y: {
- e: 1,
- y: 2,
- },
- },
- e: [],
- f: {},
- g: new Set(),
- h: 'a',
- i: [{ 'a': 1 }, 55],
- j: new Set([{ 'b': 1 }]) as Set