Skip to content

Instantly share code, notes, and snippets.

@meredian
Created May 14, 2020 17:48
Show Gist options
  • Save meredian/6763ac1ffbc67dd4ca4a84369e849cd5 to your computer and use it in GitHub Desktop.
Save meredian/6763ac1ffbc67dd4ca4a84369e849cd5 to your computer and use it in GitHub Desktop.
ProxyWrapper to catch data changes - almost full spec, good for TDD approach :) Missing details is "Minimal subtree in diffs" on update requirement, but it's already complex enough.
const assert = require('assert');
const ProxyWrapper = require('./proxyWrapper');
describe('ProxyWrapper', function() {
describe('#wrap', function() {
beforeEach(function() {
this.data = { a: 1, b: { c: { d: 2 } }, e: { f: [3, 4] } };
this.wrappedData = ProxyWrapper.wrap(this.data);
});
it('wraps data for for reading', function() {
assert.strictEqual(this.wrappedData.a, 1);
assert.deepStrictEqual(this.wrappedData.b, { c: { d: 2 } });
});
context('on root property modification', function() {
beforeEach(function() {
this.wrappedData.a = 4;
})
it('prevents original data from being modified', function() {
assert.strictEqual(this.data.a, 1);
});
it('persists modification of the wrapped data', function() {
assert.strictEqual(this.wrappedData.a, 4);
});
});
context('on root property addition', function() {
beforeEach(function() {
this.wrappedData.xxx = 4;
})
it('prevents original data from being modified', function() {
assert.strictEqual(this.data.xxx, undefined);
});
it('persists modification of the wrapped data', function() {
assert.strictEqual(this.wrappedData.xxx, 4);
});
});
context('on deep modification', function() {
it('prevents original data from being modified', function() {
this.wrappedData.b.c.d = 42;
assert.strictEqual(this.data.b.c.d, 2);
});
it('persists modification of the wrapped data', function() {
this.wrappedData.b.c.d = 42;
assert.strictEqual(this.wrappedData.b.c.d, 42);
});
});
context('on array in-place modification', function() {
it('prevents original data from being modified', function() {
this.wrappedData.e.f.push(42);
assert.deepStrictEqual(this.data.e.f, [3, 4]);
});
it('persists modification of the wrapped data', function() {
this.wrappedData.e.f.push(42);
assert.deepStrictEqual(this.wrappedData.e.f, [3, 4, 42]);
});
});
context('on root property deletion', function() {
it('prevents original data from being modified', function() {
delete this.wrappedData.e;
assert(this.data.e);
});
it('persists modification of the wrapped data', function() {
delete this.wrappedData.e;
assert.deepStrictEqual(this.wrappedData.e, undefined);
});
})
context('when projection is given', function() {
it('throws on accessing field not in projection', function() {
const data = { a: 1, b: 2 };
const wrappedData = ProxyWrapper.wrap(data, ['a']);
assert.throws(function() {
wrappedData.b = 4;
}, ProxyWrapper.ProjectedDataAccessError);
});
})
});
describe('#unwrap', function() {
it('revokes proxy so that it can\'t be reused after unwrapping', function() {
const wrappedData = ProxyWrapper.wrap({ a: 1 });
assert.doesNotThrow(function() {
wrappedData.a;
});
const unwrapped = ProxyWrapper.unwrap(wrappedData);
assert.throws(function() {
wrappedData.a;
}, TypeError);
});
context('when called for wrapped data', function() {
context('when no change performed', function() {
it('returns empty diff', function() {
const { diff } = ProxyWrapper.unwrap(ProxyWrapper.wrap({ a: 1 }));
assert.deepStrictEqual(diff, {});
});
});
it('returns old and new value, and set of diffs', function() {
const data = { a: 1, b: 2 };
const wrappedData = ProxyWrapper.wrap(data);
wrappedData.a = 4;
assert.deepStrictEqual(ProxyWrapper.unwrap(wrappedData), {
old: { a: 1, b: 2 },
new: { a: 4, b: 2 },
diff: {
a: { from: 1, to: 4 }
}
});
});
});
context('when called for unwrapped data', function() {
it('throws TypeError', function() {
assert.throws(function() {
ProxyWrapper.unwrap({ a: 4, b: 2 });
}, TypeError);
});
});
});
describe('#tryUnwrap', function() {
context('when called for wrapped data', function() {
it('behaves as #unwrap', function() {
const data = { a: 1, b: 2 };
const wrappedData = ProxyWrapper.wrap(data);
wrappedData.a = 4;
assert.deepStrictEqual(ProxyWrapper.tryUnwrap(wrappedData), {
old: { a: 1, b: 2 },
new: { a: 4, b: 2 },
diff: {
a: { from: 1, to: 4 }
}
});
});
});
context('when called for unwrapped data', function() {
it('interprets it as having empty wrapped object', function() {
assert.deepStrictEqual(ProxyWrapper.tryUnwrap({ a: 4, b: 2 }), {
old: { },
new: { a: 4, b: 2 },
diff: {
a: { from: undefined, to: 4 },
b: { from: undefined, to: 2 }
}
});
});
});
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment