json-joy
Version:
Collection of libraries for building collaborative editing apps.
447 lines (446 loc) • 15.4 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const testCases = [
{
comment: 'Empty dock, empty patch',
doc: {},
patch: [],
expected: {},
},
{
comment: 'Empty patch',
doc: { foo: 1 },
patch: [],
expected: { foo: 1 },
},
{
comment: 'Rearrange object keys',
doc: { foo: 1, bar: 2 },
patch: [],
expected: { bar: 2, foo: 1 },
},
{
comment: 'Rearrange object keys in array',
doc: [{ foo: 1, bar: 2 }],
patch: [],
expected: [{ bar: 2, foo: 1 }],
},
{
comment: 'Rearrange object keys in object',
doc: { foo: { foo: 1, bar: 2 } },
patch: [],
expected: { foo: { bar: 2, foo: 1 } },
},
{
comment: 'Add replaces any existing field',
doc: { foo: null },
patch: [{ op: 'add', path: '/foo', value: 1 }],
expected: { foo: 1 },
},
{ comment: 'Top level array', doc: [], patch: [{ op: 'add', path: '/0', value: 'foo' }], expected: ['foo'] },
{ comment: 'Top level array, no change', doc: ['foo'], patch: [], expected: ['foo'] },
{
comment: 'Top level object, numeric string',
doc: {},
patch: [{ op: 'add', path: '/foo', value: '1' }],
expected: { foo: '1' },
},
{ comment: 'Top level object, integer', doc: {}, patch: [{ op: 'add', path: '/foo', value: 1 }], expected: { foo: 1 } },
{
comment: 'Top level scalar values OK?',
doc: 'foo',
patch: [{ op: 'replace', path: '', value: 'bar' }],
expected: 'bar',
disabled: true,
},
{ comment: 'Add, / target', doc: {}, patch: [{ op: 'add', path: '/', value: 1 }], expected: { '': 1 } },
{
comment: 'Add composite value at top level',
doc: { foo: 1 },
patch: [{ op: 'add', path: '/bar', value: [1, 2] }],
expected: { foo: 1, bar: [1, 2] },
},
{
comment: 'Add into composite value',
doc: { foo: 1, baz: [{ qux: 'hello' }] },
patch: [{ op: 'add', path: '/baz/0/foo', value: 'world' }],
expected: { foo: 1, baz: [{ qux: 'hello', foo: 'world' }] },
},
{
comment: 'Array location out of bounds, inside object',
doc: { bar: [1, 2] },
patch: [{ op: 'add', path: '/bar/8', value: '5' }],
error: 'INVALID_INDEX',
},
{
comment: 'Array location negative, inside object',
doc: { bar: [1, 2] },
patch: [{ op: 'add', path: '/bar/-2', value: '5' }],
error: 'INVALID_INDEX',
},
{
comment: 'Add object key equal to "true", first level object',
doc: { foo: 1 },
patch: [{ op: 'add', path: '/bar', value: true }],
expected: { foo: 1, bar: true },
},
{
comment: 'Add object key equal to "false", first level object',
doc: { foo: 1 },
patch: [{ op: 'add', path: '/bar', value: false }],
expected: { foo: 1, bar: false },
},
{
comment: 'Add object key equal to "null", first level object',
doc: { foo: 1 },
patch: [{ op: 'add', path: '/bar', value: null }],
expected: { foo: 1, bar: null },
},
{
comment: '0 can be an array index or object element name',
doc: { foo: 1 },
patch: [{ op: 'add', path: '/0', value: 'bar' }],
expected: { foo: 1, '0': 'bar' },
},
{
comment: 'First level array, insert at second position in array with one element',
doc: ['foo'],
patch: [{ op: 'add', path: '/1', value: 'bar' }],
expected: ['foo', 'bar'],
},
{
comment: 'First level array, insert at second position in the middle of two element array',
doc: ['foo', 'sil'],
patch: [{ op: 'add', path: '/1', value: 'bar' }],
expected: ['foo', 'bar', 'sil'],
},
{
comment: 'First level array, insert at first position in array with two elements',
doc: ['foo', 'sil'],
patch: [{ op: 'add', path: '/0', value: 'bar' }],
expected: ['bar', 'foo', 'sil'],
},
{
comment: 'First level array, insert at third position in array with two element',
doc: ['foo', 'sil'],
patch: [{ op: 'add', path: '/2', value: 'bar' }],
expected: ['foo', 'sil', 'bar'],
},
{
comment: 'Test against implementation-specific numeric parsing',
doc: { '1e0': 'foo' },
patch: [{ op: 'test', path: '/1e0', value: 'foo' }],
expected: { '1e0': 'foo' },
},
{
comment: 'Test with bad number should fail',
doc: ['foo', 'bar'],
patch: [{ op: 'test', path: '/1e0', value: 'bar' }],
error: 'INVALID_INDEX',
},
{
comment: 'First level array, inserting using string index',
doc: ['foo', 'sil'],
patch: [{ op: 'add', path: '/bar', value: 42 }],
error: 'INVALID_INDEX',
},
{
comment: 'First level object, remove key containing array',
doc: { foo: 1, bar: [1, 2, 3, 4] },
patch: [{ op: 'remove', path: '/bar' }],
expected: { foo: 1 },
},
{
comment: 'Value in array add not flattened',
doc: ['foo', 'sil'],
patch: [{ op: 'add', path: '/1', value: ['bar', 'baz'] }],
expected: ['foo', ['bar', 'baz'], 'sil'],
},
{
comment: 'Remove string key from object, three levels deep, in array and object',
doc: { foo: 1, baz: [{ qux: 'hello' }] },
patch: [{ op: 'remove', path: '/baz/0/qux' }],
expected: { foo: 1, baz: [{}] },
},
{
comment: 'Insert array into object at first level.',
doc: { foo: 1, baz: [{ qux: 'hello' }] },
patch: [{ op: 'replace', path: '/foo', value: [1, 2, 3, 4] }],
expected: { foo: [1, 2, 3, 4], baz: [{ qux: 'hello' }] },
},
{
comment: 'Replace object key at three levels deep',
doc: { foo: [1, 2, 3, 4], baz: [{ qux: 'hello' }] },
patch: [{ op: 'replace', path: '/baz/0/qux', value: 'world' }],
expected: { foo: [1, 2, 3, 4], baz: [{ qux: 'world' }] },
},
{
comment: 'First level array with one element, replace that element with string',
doc: ['foo'],
patch: [{ op: 'replace', path: '/0', value: 'bar' }],
expected: ['bar'],
},
{
comment: 'First level array with one element, replace that element with 0',
doc: [''],
patch: [{ op: 'replace', path: '/0', value: 0 }],
expected: [0],
},
{
comment: 'First level array with one element, replace that element with true',
doc: [''],
patch: [{ op: 'replace', path: '/0', value: true }],
expected: [true],
},
{
comment: 'First level array with one element, replace that element with false',
doc: [''],
patch: [{ op: 'replace', path: '/0', value: false }],
expected: [false],
},
{
comment: 'First level array with one element, replace that element with null',
doc: [''],
patch: [{ op: 'replace', path: '/0', value: null }],
expected: [null],
},
{
comment: 'Value in array replace not flattened',
doc: ['foo', 'sil'],
patch: [{ op: 'replace', path: '/1', value: ['bar', 'baz'] }],
expected: ['foo', ['bar', 'baz']],
},
{
comment: 'Replace whole document',
doc: { foo: 'bar' },
patch: [{ op: 'replace', path: '', value: { baz: 'qux' } }],
expected: { baz: 'qux' },
},
{
comment: 'Allow spurious patch properties',
doc: { foo: 1 },
patch: [{ op: 'test', path: '/foo', value: 1, spurious: 1 }],
expected: { foo: 1 },
},
{
comment: 'null value should be valid obj property',
doc: { foo: null },
patch: [{ op: 'test', path: '/foo', value: null }],
expected: { foo: null },
},
{
comment: 'null value should be valid obj property to be replaced with something truthy',
doc: { foo: null },
patch: [{ op: 'replace', path: '/foo', value: 'truthy' }],
expected: { foo: 'truthy' },
},
{
comment: 'null value should be valid obj property to be moved',
doc: { foo: null },
patch: [{ op: 'move', from: '/foo', path: '/bar' }],
expected: { bar: null },
},
{
comment: 'null value should be valid obj property to be copied',
doc: { foo: null },
patch: [{ op: 'copy', from: '/foo', path: '/bar' }],
expected: { foo: null, bar: null },
},
{
comment: 'null value should be valid obj property to be removed',
doc: { foo: null },
patch: [{ op: 'remove', path: '/foo' }],
expected: {},
},
{
comment: 'null value should still be valid obj property replace other value',
doc: { foo: 'bar' },
patch: [{ op: 'replace', path: '/foo', value: null }],
expected: { foo: null },
},
{
comment: 'Test should pass despite rearrangement',
doc: { foo: { foo: 1, bar: 2 } },
patch: [{ op: 'test', path: '/foo', value: { bar: 2, foo: 1 } }],
expected: { foo: { foo: 1, bar: 2 } },
},
{
comment: 'Test should pass despite (nested) rearrangement',
doc: { foo: [{ foo: 1, bar: 2 }] },
patch: [{ op: 'test', path: '/foo', value: [{ bar: 2, foo: 1 }] }],
expected: { foo: [{ foo: 1, bar: 2 }] },
},
{
comment: 'Test should pass - no error',
doc: { foo: { bar: [1, 2, 5, 4] } },
patch: [{ op: 'test', path: '/foo', value: { bar: [1, 2, 5, 4] } }],
expected: { foo: { bar: [1, 2, 5, 4] } },
},
{
comment: 'Test operation shoul not match object for array',
doc: { foo: { bar: [1, 2, 5, 4] } },
patch: [{ op: 'test', path: '/foo', value: [1, 2] }],
error: 'TEST',
},
{ comment: 'Whole document', doc: { foo: 1 }, patch: [{ op: 'test', path: '', value: { foo: 1 } }], disabled: true },
{ comment: 'Empty-string element', doc: { '': 1 }, expected: { '': 1 }, patch: [{ op: 'test', path: '/', value: 1 }] },
{
comment: 'JSON Pointer spec tests',
doc: {
foo: ['bar', 'baz'],
'': 0,
'a/b': 1,
'c%d': 2,
'e^f': 3,
'g|h': 4,
'i\\j': 5,
'k"l': 6,
' ': 7,
'm~n': 8,
},
expected: {
foo: ['bar', 'baz'],
'': 0,
'a/b': 1,
'c%d': 2,
'e^f': 3,
'g|h': 4,
'i\\j': 5,
'k"l': 6,
' ': 7,
'm~n': 8,
},
patch: [
{ op: 'test', path: '/foo', value: ['bar', 'baz'] },
{ op: 'test', path: '/foo/0', value: 'bar' },
{ op: 'test', path: '/', value: 0 },
{ op: 'test', path: '/a~1b', value: 1 },
{ op: 'test', path: '/c%d', value: 2 },
{ op: 'test', path: '/e^f', value: 3 },
{ op: 'test', path: '/g|h', value: 4 },
{ op: 'test', path: '/i\\j', value: 5 },
{ op: 'test', path: '/k"l', value: 6 },
{ op: 'test', path: '/ ', value: 7 },
{ op: 'test', path: '/m~0n', value: 8 },
],
},
{
comment: 'Move to same location has no effect',
doc: { foo: 1 },
patch: [{ op: 'move', from: '/foo', path: '/foo' }],
expected: { foo: 1 },
},
{
comment: 'Insert key into first level object',
doc: { foo: 1, baz: [{ qux: 'hello' }] },
patch: [{ op: 'move', from: '/foo', path: '/bar' }],
expected: { baz: [{ qux: 'hello' }], bar: 1 },
},
{
comment: 'Insert key into third level object',
doc: { baz: [{ qux: 'hello' }], bar: 1 },
patch: [{ op: 'move', from: '/baz/0/qux', path: '/baz/1' }],
expected: { baz: [{}, 'hello'], bar: 1 },
},
{
comment: 'Insert element into array at second level into first position',
doc: { baz: [{ qux: 'hello' }], bar: 1 },
patch: [{ op: 'copy', from: '/baz/0', path: '/boo' }],
expected: { baz: [{ qux: 'hello' }], bar: 1, boo: { qux: 'hello' } },
},
{
comment: 'Replacing the root of the document is possible with add',
doc: { foo: 'bar' },
patch: [{ op: 'add', path: '', value: { baz: 'qux' } }],
expected: { baz: 'qux' },
},
{
comment: 'Adding to "/-" adds to the end of the array',
doc: [1, 2],
patch: [{ op: 'add', path: '/-', value: { foo: ['bar', 'baz'] } }],
expected: [1, 2, { foo: ['bar', 'baz'] }],
},
{
comment: 'Adding to "/-" adds to the end of the array, even n levels down',
doc: [1, 2, [3, [4, 5]]],
patch: [{ op: 'add', path: '/2/1/-', value: { foo: ['bar', 'baz'] } }],
expected: [1, 2, [3, [4, 5, { foo: ['bar', 'baz'] }]]],
},
{
comment: 'Test remove with bad number should fail',
doc: { foo: 1, baz: [{ qux: 'hello' }] },
patch: [{ op: 'remove', path: '/baz/1e0/qux' }],
error: 'INVALID_INDEX',
},
{
comment: 'Test remove on array',
doc: [1, 2, 3, 4],
patch: [{ op: 'remove', path: '/0' }],
expected: [2, 3, 4],
},
{
comment: 'Repeated removes',
doc: [1, 2, 3, 4],
patch: [
{ op: 'remove', path: '/1' },
{ op: 'remove', path: '/2' },
],
expected: [1, 3],
skipInJsonOt: true,
},
{
comment: 'Remove with bad index should fail',
doc: [1, 2, 3, 4],
patch: [{ op: 'remove', path: '/1e0' }],
error: 'INVALID_INDEX',
},
{
comment: 'Replace with bad number should fail',
doc: [''],
patch: [{ op: 'replace', path: '/1e0', value: false }],
error: 'INVALID_INDEX',
},
{
comment: 'Test copy with bad number should fail',
doc: { baz: [1, 2, 3], bar: 1 },
patch: [{ op: 'copy', from: '/baz/1e0', path: '/boo' }],
error: 'INVALID_INDEX',
},
{
comment: 'Test move with bad number should fail',
doc: { foo: 1, baz: [1, 2, 3, 4] },
patch: [{ op: 'move', from: '/baz/1e0', path: '/foo' }],
error: 'INVALID_INDEX',
},
{
comment: 'Test add with bad number should fail',
doc: ['foo', 'sil'],
patch: [{ op: 'add', path: '/1e0', value: 'bar' }],
error: 'INVALID_INDEX',
},
{
comment: "Missing 'value' parameter to test",
doc: [null],
patch: [{ op: 'test', path: '/0' }],
error: 'OP_VALUE_MISSING',
},
{
comment: 'Missing value parameter to test - where undef is falsy',
doc: [false],
patch: [{ op: 'test', path: '/0' }],
error: 'OP_VALUE_MISSING',
},
{
comment: 'Missing from parameter to copy',
doc: [1],
patch: [{ op: 'copy', path: '/-' }],
error: 'OP_FROM_INVALID',
},
{
comment: 'Unrecognized op should fail',
doc: { foo: 1 },
patch: [{ op: 'spam', path: '/foo', value: 1 }],
error: 'OP_UNKNOWN',
},
];
exports.default = testCases;