UNPKG

graph-crdt

Version:
443 lines (323 loc) 13.3 kB
'use strict'; var _slicedToArray2 = require('babel-runtime/helpers/slicedToArray'); var _slicedToArray3 = _interopRequireDefault(_slicedToArray2); var _toConsumableArray2 = require('babel-runtime/helpers/toConsumableArray'); var _toConsumableArray3 = _interopRequireDefault(_toConsumableArray2); var _stringify = require('babel-runtime/core-js/json/stringify'); var _stringify2 = _interopRequireDefault(_stringify); var _expect = require('expect'); var _expect2 = _interopRequireDefault(_expect); var _index = require('./index'); var _index2 = _interopRequireDefault(_index); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } /* eslint-env mocha */ describe('Node static method', function () { describe('"from"', function () { it('turns a POJO into a node', function () { var node = _index2.default.from({}); (0, _expect2.default)(node).toBeA(_index2.default); }); it('imports the properties from the target object', function () { var date = new Date('1 Jan 1980'); var node = _index2.default.from({ name: 'Sam', birthday: date }); (0, _expect2.default)(node.value('name')).toBe('Sam'); (0, _expect2.default)(node.value('birthday')).toBe(date); }); it('provides an initial state', function () { var node = _index2.default.from({ name: 'Alvin' }); (0, _expect2.default)(node.state('name')).toBe(1); }); }); describe('"source"', function () { it('creates a node that draws from an object', function () { var node = _index2.default.create(); node.merge({ data: 'intact' }); var string = (0, _stringify2.default)(node); var object = JSON.parse(string); var copy = _index2.default.source(object); (0, _expect2.default)(copy.value('data')).toBe('intact'); }); }); }); describe('Node', function () { var node = void 0; beforeEach(function () { node = _index2.default.create(); }); /* Generic tests */ it('does not have properties upon creation', function () { var keys = [].concat((0, _toConsumableArray3.default)(node)).map(function (_ref) { var _ref2 = (0, _slicedToArray3.default)(_ref, 1), key = _ref2[0]; return key; }); (0, _expect2.default)(keys.length).toBe(0); }); it('returns the uid when `toString` is called', function () { var result = node.toString(); var _node$meta = node.meta(), uid = _node$meta.uid; (0, _expect2.default)(result).toBe(uid); }); it('returns the node when `toJSON` is called', function () { node.merge({ 'json worked': true }); var result = (0, _stringify2.default)(node); (0, _expect2.default)(result).toContain('json worked'); }); /* Not so generic tests */ describe('uid', function () { it('exists on creation', function () { var _node$meta2 = node.meta(), uid = _node$meta2.uid; (0, _expect2.default)(uid).toNotBe(undefined); }); it('is unique', function () { var _Node$create$meta = _index2.default.create().meta(), uid1 = _Node$create$meta.uid; var _Node$create$meta2 = _index2.default.create().meta(), uid2 = _Node$create$meta2.uid; (0, _expect2.default)(uid1).toNotBe(uid2); }); it('uses the configuration provided', function () { var node = _index2.default.create({ uid: 'custom' }); var _node$meta3 = node.meta(), uid = _node$meta3.uid; (0, _expect2.default)(uid).toBe('custom'); }); }); describe('state()', function () { it('returns 0 when there is no property', function () { var state = node.state('no such key'); (0, _expect2.default)(state).toBe(0); }); }); describe('meta()', function () { it('returns null if no values exist', function () { var result = node.meta('no such key'); (0, _expect2.default)(result).toBe(null); }); it('returns the metadata if the field exists', function () { node.merge({ name: 'Steve' }); var result = node.meta('name'); (0, _expect2.default)(result).toBeAn(Object); }); }); describe('Symbol.iterator()', function () { it('skips the node metadata field', function () { node.merge({ name: 'John' }); var keys = [].concat((0, _toConsumableArray3.default)(node)).map(function (_ref3) { var _ref4 = (0, _slicedToArray3.default)(_ref3, 1), key = _ref4[0]; return key; }); // Should not contain '@object'. (0, _expect2.default)(keys).toEqual(['name']); }); it('includes key/value pairs for every field', function () { node.merge({ name: 'Stewart', tier: 'premium' }); var pairs = [].concat((0, _toConsumableArray3.default)(node)); (0, _expect2.default)(pairs).toEqual([['name', 'Stewart'], ['tier', 'premium']]); }); }); describe('value()', function () { it('returns undefined if the key cannot be found', function () { var result = node.value('no such key'); (0, _expect2.default)(result).toBe(undefined); }); it('returns undefined if called on reserved fields', function () { // Please, never do this in your code. node.meta().value = 'failure!'; var result = node.value('@object'); (0, _expect2.default)(result).toBe(undefined); }); }); describe('new()', function () { it('creates a node with the same ID', function () { var _node$meta4 = node.meta(), uid = _node$meta4.uid; var copy = node.new(); (0, _expect2.default)(copy.meta()).toContain({ uid: uid }); }); it('does not carry over any properties', function () { node.merge({ original: true }); var copy = node.new(); (0, _expect2.default)(copy.snapshot()).toEqual({}); }); }); describe('setMetadata()', function () { it('updates the field metadata', function () { var update = { value: 'jack', lastChanged: 1491531671735 }; node.setMetadata('username', update); (0, _expect2.default)(node.meta('username')).toContain(update); }); it('sets the state', function () { var update = { value: 6.28, logs: 'address' }; node.setMetadata('pressure', update); (0, _expect2.default)(node.state('pressure')).toBe(1); }); it('bumps the state if the field existed before', function () { node.merge({ 'temp': 31 }); var update = { value: 30, encoding: 'KELVIN' }; node.setMetadata('temp', update); (0, _expect2.default)(node.state('temp')).toBe(2); }); it('ignores state if specified in the metadata', function () { node.setMetadata('dangerous', { state: 6 }); (0, _expect2.default)(node.state('dangerous')).toBe(1); }); it('does not mutate the metadata object', function () { var update = { state: 5 }; node.setMetadata('field', update); (0, _expect2.default)(update.state).toBe(5); (0, _expect2.default)(node.meta('field')).toNotBe(update); }); it('returns the merge delta', function () { var result = node.setMetadata('speed', { value: 150 }); (0, _expect2.default)(result.update).toBeA(_index2.default); (0, _expect2.default)(result.history).toBeA(_index2.default); }); it('emits "update"', function () { var spy = (0, _expect.createSpy)(); node.on('update', spy); node.setMetadata('field', { value: 'update!' }); (0, _expect2.default)(spy).toHaveBeenCalled(); var _spy$calls$0$argument = (0, _slicedToArray3.default)(spy.calls[0].arguments, 1), update = _spy$calls$0$argument[0]; (0, _expect2.default)(update).toBeA(_index2.default); (0, _expect2.default)(update.meta().uid).toBe(node.meta().uid); (0, _expect2.default)([].concat((0, _toConsumableArray3.default)(update))).toEqual([['field', 'update!']]); }); it('emits "history"', function () { var spy = (0, _expect.createSpy)(); node.on('history', spy); node.merge({ field: 'history' }); node.setMetadata('field', 'update'); (0, _expect2.default)(spy).toHaveBeenCalled(); var _spy$calls$0$argument2 = (0, _slicedToArray3.default)(spy.calls[0].arguments, 1), update = _spy$calls$0$argument2[0]; (0, _expect2.default)(update).toBeA(_index2.default); (0, _expect2.default)([].concat((0, _toConsumableArray3.default)(update))).toEqual([['field', 'history']]); }); }); describe('rebase()', function () { var target = void 0; beforeEach(function () { target = new _index2.default(); }); it('returns a new node', function () { var result = node.rebase(); (0, _expect2.default)(result).toBeA(_index2.default); (0, _expect2.default)(result).toNotBe(node); }); it('returns the same node id', function () { var _node$rebase$meta = node.rebase(target).meta(), uid = _node$rebase$meta.uid; (0, _expect2.default)(uid).toBe(node.meta().uid); }); it('does not change state if the node is empty', function () { node.merge({ initial: true }); var result = node.rebase(target); (0, _expect2.default)(result.state('initial')).toBe(1); }); it('does not change state if the fields do not overlap', function () { node.merge({ initial: true }); target.merge({ different: true }); target.meta('different').state = 2001; var result = node.rebase(target); (0, _expect2.default)(result.state('initial')).toBe(1); }); it('increments the state for overlapping fields', function () { node.merge({ existing: 'should replace' }); target.merge({ existing: 'replace me', different: true }); var result = node.rebase(target); (0, _expect2.default)(result.state('existing')).toBe(2); (0, _expect2.default)(result.state('different')).toBe(1); }); it('does not mutate field metadata', function () { node.merge({ old: false }); target.merge({ old: true }); var result = node.rebase(target); (0, _expect2.default)(result.meta('old')).toNotBe(node.meta('old')).toNotBe(target.meta('old')).toNotContain({ state: 1 }); }); it('does not change fields which are already greater', function () { var state = 20000; node.merge({ old: false }); node.meta('old').state = state; target.merge({ old: true }); var result = node.rebase(target); (0, _expect2.default)(result.state('old')).toBe(state); }); }); describe('overlap()', function () { var target = void 0; beforeEach(function () { target = new _index2.default(); }); it('returns a new node', function () { var result = node.overlap(target); (0, _expect2.default)(result).toBeA(_index2.default); (0, _expect2.default)(result).toNotBe(node); (0, _expect2.default)(result).toNotBe(target); }); it('has the same id', function () { var _node$overlap$meta = node.overlap(target).meta(), uid = _node$overlap$meta.uid; (0, _expect2.default)(uid).toBe(node.meta().uid); }); it('has no fields if the target is empty', function () { node.merge({ existing: true }); var result = node.overlap(target); (0, _expect2.default)([].concat((0, _toConsumableArray3.default)(result))).toEqual([]); }); it('ignores additional properties in the target', function () { node.merge({ existing: true }); target.merge({ existing: true, different: true }); var result = node.overlap(target); (0, _expect2.default)([].concat((0, _toConsumableArray3.default)(result))).toEqual([].concat((0, _toConsumableArray3.default)(node))); }); it('ignores additional properties in the source', function () { node.merge({ shared: true, extra: true }); target.merge({ shared: true }); var result = node.overlap(target); (0, _expect2.default)([].concat((0, _toConsumableArray3.default)(result))).toEqual([].concat((0, _toConsumableArray3.default)(target))); }); it('is identical when compared against itself', function () { node.merge({ exhausted: 'field name creativity' }); var result = node.overlap(node); (0, _expect2.default)([].concat((0, _toConsumableArray3.default)(result))).toEqual([].concat((0, _toConsumableArray3.default)(node))); }); it('contains the values from the source node', function () { node.merge({ shared: 'node value' }); target.merge({ shared: 'target value' }); var result = node.overlap(target); (0, _expect2.default)(result.meta('shared')).toBe(node.meta('shared')); }); }); describe('snapshot()', function () { it('returns an object', function () { var result = node.snapshot(); (0, _expect2.default)(result).toBeAn(Object); }); it('contains every key/value pair in the node', function () { var state = { name: 'Living Room Lamp', state: 'ON' }; node.merge(state); var result = node.snapshot(); (0, _expect2.default)(result).toEqual(state); }); }); });