Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions lib/json0.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,16 @@ var clone = function(o) {
return JSON.parse(JSON.stringify(o));
};

var validateListIndex = function(key) {
if (typeof key !== 'number')
throw new Error('List index must be a number');
};

var validateObjectKey = function (key) {
if (typeof key !== 'string')
throw new Error('Object key must be a number');
};

/**
* JSON OT Type
* @type {*}
Expand Down Expand Up @@ -162,6 +172,12 @@ json.apply = function(snapshot, op) {
elem = elem[key];
key = p;

if (isArray(elem) && typeof key !== 'number')
throw new Error('List index must be a number');

if (isObject(elem) && typeof key !== 'string')
throw new Error('Object key must be a string');

if (parent == null)
throw new Error('Path invalid');
}
Expand All @@ -175,6 +191,9 @@ json.apply = function(snapshot, op) {
if (typeof elem[key] != 'number')
throw new Error('Referenced element not a number');

if (typeof c.na !== 'number')
throw new Error('Number addition is not a number');

elem[key] += c.na;
}

Expand All @@ -200,6 +219,9 @@ json.apply = function(snapshot, op) {

// List move
else if (c.lm !== void 0) {
if (typeof c.lm !== 'number')
throw new Error('List move target index must be a number');

json.checkList(elem);
if (c.lm != key) {
var e = elem[key];
Expand Down
4 changes: 4 additions & 0 deletions lib/text0.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ var checkValidOp = function(op) {
text.apply = function(snapshot, op) {
var deleted;

var type = typeof snapshot;
if (type !== 'string')
throw new Error('text0 operations cannot be applied to type: ' + type);

checkValidOp(op);
for (var i = 0; i < op.length; i++) {
var component = op[i];
Expand Down
38 changes: 38 additions & 0 deletions test/json0.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ genTests = (type) ->
c_s = type.apply leftHas, right_
assert.deepEqual s_c, c_s

it 'throws when adding a string to a number', ->
assert.throws -> type.apply 1, [{p: [], na: 'a'}]

it 'throws when adding a number to a string', ->
assert.throws -> type.apply 'a', [{p: [], na: 1}]

# Strings should be handled internally by the text type. We'll just do some basic sanity checks here.
describe 'string', ->
Expand All @@ -72,6 +77,12 @@ genTests = (type) ->
assert.deepEqual 'bc', type.apply 'abc', [{p:[0], sd:'a'}]
assert.deepEqual {x:'abc'}, type.apply {x:'a'}, [{p:['x', 1], si:'bc'}]

it 'throws when the target is not a string', ->
assert.throws -> type.apply [1], [{p: [0], si: 'a'}]

it 'throws when the inserted content is not a string', ->
assert.throws -> type.apply 'a', [{p: [0], si: 1}]

describe '#transform()', ->
it 'splits deletes', ->
assert.deepEqual type.transform([{p:[0], sd:'ab'}], [{p:[1], si:'x'}], 'left'), [{p:[0], sd:'a'}, {p:[1], sd:'b'}]
Expand Down Expand Up @@ -127,6 +138,24 @@ genTests = (type) ->
assert.deepEqual ['a', 'b', 'c'], type.apply ['b', 'a', 'c'], [{p:[1], lm:0}]
assert.deepEqual ['a', 'b', 'c'], type.apply ['b', 'a', 'c'], [{p:[0], lm:1}]

it 'throws when keying a list replacement with a string', ->
assert.throws -> type.apply ['a', 'b', 'c'], [{p: ['0'], li: 'x', ld: 'a'}]

it 'throws when keying a list insertion with a string', ->
assert.throws -> type.apply ['a', 'b', 'c'], [{p: ['0'], li: 'x'}]

it 'throws when keying a list deletion with a string', ->
assert.throws -> type.apply ['a', 'b', 'c'], [{p: ['0'], ld: 'a'}]

it 'throws when keying a list move with a string', ->
assert.throws -> type.apply ['a', 'b', 'c'], [{p: ['0'], lm: 0}]

it 'throws when specifying a string as a list move target', ->
assert.throws -> type.apply ['a', 'b', 'c'], [{p: [1], lm: '0'}]

it 'throws when an array index part-way through the path is a string', ->
assert.throws -> type.apply {arr:[{x:'a'}]}, [{p:['arr', '0', 'x'], od: 'a'}]

###
'null moves compose to nops', ->
assert.deepEqual [], type.compose [], [{p:[3],lm:3}]
Expand Down Expand Up @@ -389,6 +418,15 @@ genTests = (type) ->
assert.deepEqual [], type.transform [{p:['k'], od:'x'}], [{p:['k'], od:'x'}], 'left'
assert.deepEqual [], type.transform [{p:['k'], od:'x'}], [{p:['k'], od:'x'}], 'right'

it 'throws when the insertion key is a number', ->
assert.throws -> type.apply {'1':'a'}, [{p:[2], oi: 'a'}]

it 'throws when the deletion key is a number', ->
assert.throws -> type.apply {'1':'a'}, [{p:[1], od: 'a'}]

it 'throws when an object key part-way through the path is a number', ->
assert.throws -> type.apply {'1': {x: 'a'}}, [{p:[1, 'x'], od: 'a'}]

describe 'randomizer', ->
@timeout 20000
@slow 6000
Expand Down