Created
May 14, 2020 17:48
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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