UNPKG

marshall-y-slate

Version:
392 lines (382 loc) 12.2 kB
import { toSlateDoc } from '../src'; import { CustomNode } from '../src/model'; import { TestEditor, TransformFunc } from './test-editor'; import { createNode, createTestEditor, createValue, wait } from './utils'; const tests = [ [ 'Insert text into a paragraph node', [createNode('paragraph', '')], [ TestEditor.makeInsertText('Hello ', { path: [0, 0], offset: 0 }), TestEditor.makeInsertText('collaborator', { path: [0, 0], offset: 6 }), TestEditor.makeInsertText('!', { path: [0, 0], offset: 18 }) ], [createNode('paragraph', 'Hello collaborator!')] ], [ 'Delete characters from a paragraph node', [createNode('paragraph', 'Hello collaborator!')], [ TestEditor.makeRemoveCharacters(7, { path: [0, 0], offset: 11 }), TestEditor.makeRemoveCharacters(6, { path: [0, 0], offset: 5 }) ], [createNode('paragraph', 'Hello!')] ], [ 'Insert new nodes (both paragraph and text)', [createNode()], [ TestEditor.makeInsertNodes({ type: 'paragraph', children: [] }, [1]), TestEditor.makeInsertNodes({ text: 'Hello collaborator!' }, [1, 0]) ], [createNode(), createNode('paragraph', 'Hello collaborator!')] ], [ 'Insert new nodes (two paragraphs)', [ createNode('paragraph', 'alfa'), createNode('paragraph', 'bravo'), createNode('paragraph', 'charlie'), createNode('paragraph', 'delta') ], [ TestEditor.makeInsertNodes(createNode('paragraph', 'echo'), [1]), TestEditor.makeInsertNodes(createNode('paragraph', 'foxtrot'), [4]) ], [ createNode('paragraph', 'alfa'), createNode('paragraph', 'echo'), createNode('paragraph', 'bravo'), createNode('paragraph', 'charlie'), createNode('paragraph', 'foxtrot'), createNode('paragraph', 'delta') ] ], [ 'Insert new nodes (three paragraphs)', [ createNode('paragraph', 'one'), createNode('paragraph', 'two'), createNode('paragraph', 'three'), createNode('paragraph', 'four') ], [ TestEditor.makeInsertNodes(createNode('paragraph', 'five'), [1]), TestEditor.makeInsertNodes(createNode('paragraph', 'six'), [3]), TestEditor.makeInsertNodes(createNode('paragraph', 'seven'), [5]) ], [ createNode('paragraph', 'one'), createNode('paragraph', 'five'), createNode('paragraph', 'two'), createNode('paragraph', 'six'), createNode('paragraph', 'three'), createNode('paragraph', 'seven'), createNode('paragraph', 'four') ] ], [ 'Merge two paragraph nodes', [ createNode('paragraph', 'Hello '), createNode('paragraph', 'collaborator!') ], [TestEditor.makeMergeNodes([1])], [createNode('paragraph', 'Hello collaborator!')] ], [ 'Move a paragraph node to an existing position', [ createNode('paragraph', 'first'), createNode('paragraph', 'second'), createNode('paragraph', 'third') ], [TestEditor.makeMoveNodes([1], [0])], [ createNode('paragraph', 'second'), createNode('paragraph', 'first'), createNode('paragraph', 'third') ] ], [ 'Move a paragraph node to the end', [ createNode('paragraph', 'first'), createNode('paragraph', 'second'), createNode('paragraph', 'third') ], [TestEditor.makeMoveNodes([0], [3])], [ createNode('paragraph', 'second'), createNode('paragraph', 'third'), createNode('paragraph', 'first') ] ], [ 'Move a paragraph node far past the end', [ createNode('paragraph', 'first'), createNode('paragraph', 'second'), createNode('paragraph', 'third') ], [TestEditor.makeMoveNodes([0], [1000])], [ createNode('paragraph', 'second'), createNode('paragraph', 'third'), createNode('paragraph', 'first') ] ], [ 'Move a text node', [ createNode('paragraph', 'first'), createNode('paragraph', 'second'), createNode('paragraph', 'third') ], [TestEditor.makeMoveNodes([1, 0], [2, 0])], [ createNode('paragraph', 'first'), createNode('paragraph', ''), createNode('paragraph', 'secondthird') ] ], [ 'Remove a paragraph node', [ createNode('paragraph', 'first'), createNode('paragraph', 'second'), createNode('paragraph', 'third') ], [TestEditor.makeRemoveNodes([0])], [createNode('paragraph', 'second'), createNode('paragraph', 'third')] ], [ 'Remove two non-consecutive paragraph nodes', [ createNode('paragraph', 'first'), createNode('paragraph', 'second'), createNode('paragraph', 'third'), createNode('paragraph', 'fourth') ], [TestEditor.makeRemoveNodes([0]), TestEditor.makeRemoveNodes([1])], [createNode('paragraph', 'second'), createNode('paragraph', 'fourth')] ], [ 'Remove two consecutive paragraph nodes', [ createNode('paragraph', 'first'), createNode('paragraph', 'second'), createNode('paragraph', 'third'), createNode('paragraph', 'fourth') ], [TestEditor.makeRemoveNodes([1]), TestEditor.makeRemoveNodes([1])], [createNode('paragraph', 'first'), createNode('paragraph', 'fourth')] ], [ 'Remove a text node', [ createNode('paragraph', 'first'), createNode('paragraph', 'second'), createNode('paragraph', 'third') ], [TestEditor.makeRemoveNodes([1, 0])], [ createNode('paragraph', 'first'), createNode('paragraph', ''), createNode('paragraph', 'third') ] ], [ 'Set properties of a paragraph node', [ createNode('paragraph', 'first', { test: '1234' }), createNode('paragraph', 'second') ], [TestEditor.makeSetNodes([0], { test: '4567' })], [ createNode('paragraph', 'first', { test: '4567' }), createNode('paragraph', 'second') ] ], [ 'Set properties of a text node', [ createNode('paragraph', 'first', { test: '1234' }), createNode('paragraph', 'second') ], [TestEditor.makeSetNodes([1, 0], { data: '4567' })], [ createNode('paragraph', 'first', { test: '1234' }), { type: 'paragraph', children: [ { data: '4567', text: 'second' } ] } ] ], [ 'Split an existing paragraph', [createNode('paragraph', 'Hello collaborator!')], [TestEditor.makeSplitNodes({ path: [0, 0], offset: 6 })], [ createNode('paragraph', 'Hello '), createNode('paragraph', 'collaborator!') ] ], [ 'Insert and remove text in the same paragraph', [createNode('paragraph', 'abc def')], [ TestEditor.makeInsertText('ghi ', { path: [0, 0], offset: 4 }), TestEditor.makeRemoveCharacters(2, { path: [0, 0], offset: 1 }), TestEditor.makeInsertText('jkl ', { path: [0, 0], offset: 6 }), TestEditor.makeRemoveCharacters(1, { path: [0, 0], offset: 11 }), TestEditor.makeInsertText(' mno', { path: [0, 0], offset: 12 }) ], [createNode('paragraph', 'a ghi jkl df mno')] ], [ 'Remove first paragraph, insert text into second paragraph', [createNode('paragraph', 'abcd'), createNode('paragraph', 'efgh')], [ TestEditor.makeRemoveNodes([0]), TestEditor.makeInsertText(' ijkl ', { path: [0, 0], offset: 2 }) ], [createNode('paragraph', 'ef ijkl gh')] ], [ 'More complex case: insert text, both insert and remove nodes', [ createNode('paragraph', 'abcd'), createNode('paragraph', 'efgh'), createNode('paragraph', 'ijkl') ], [ TestEditor.makeInsertText(' mnop ', { path: [0, 0], offset: 2 }), TestEditor.makeRemoveNodes([1]), TestEditor.makeInsertText(' qrst ', { path: [1, 0], offset: 2 }), TestEditor.makeInsertNodes(createNode('paragraph', 'uvxw'), [1]) ], [ createNode('paragraph', 'ab mnop cd'), createNode('paragraph', 'uvxw'), createNode('paragraph', 'ij qrst kl') ] ], [ 'Insert text, then insert node that affects the path to the affected text', [createNode('paragraph', 'abcd')], [ TestEditor.makeInsertText(' efgh ', { path: [0, 0], offset: 2 }), TestEditor.makeInsertNodes(createNode('paragraph', 'ijkl'), [0]) ], [createNode('paragraph', 'ijkl'), createNode('paragraph', 'ab efgh cd')] ], [ 'Set properties, then insert node that affects path to the affected node', [createNode('paragraph', 'abcd')], [ TestEditor.makeSetNodes([0], { test: '1234' }), TestEditor.makeInsertNodes(createNode('paragraph', 'ijkl'), [0]) ], [ createNode('paragraph', 'ijkl'), createNode('paragraph', 'abcd', { test: '1234' }) ] ], [ 'Insert node, then insert second node that affects path to the first node', [ createValue([ createNode('paragraph', 'abc'), createNode('paragraph', 'def') ]) ], [ TestEditor.makeInsertNodes(createNode('paragraph', 'jkl'), [0, 1]), TestEditor.makeInsertNodes(createNode('paragraph', 'ghi'), [0]) ], [ createNode('paragraph', 'ghi'), createValue([ createNode('paragraph', 'abc'), createNode('paragraph', 'jkl'), createNode('paragraph', 'def') ]) ] ], [ 'remove_text op spans location of previous remove_text op', [createNode('paragraph', 'abc defg ijklm')], [TestEditor.makeRemoveCharacters(5, { path: [0, 0], offset: 4 })], [createNode('paragraph', 'abc ijklm')], [TestEditor.makeRemoveCharacters(6, { path: [0, 0], offset: 1 })], [createNode('paragraph', 'alm')] ], [ 'remove_text op spans locations of two previous remove_text ops', [createNode('paragraph', 'abcdefghijklmnopqrst')], [TestEditor.makeRemoveCharacters(3, { path: [0, 0], offset: 2 })], [createNode('paragraph', 'abfghijklmnopqrst')], [TestEditor.makeRemoveCharacters(8, { path: [0, 0], offset: 5 })], [createNode('paragraph', 'abfghqrst')], [TestEditor.makeRemoveCharacters(7, { path: [0, 0], offset: 1 })], [createNode('paragraph', 'at')] ], [ 'set_selection op is a null op', [createNode('paragraph', 'abcdefghijklmnopqrst')], [ TestEditor.makeSetSelection( { path: [0, 0], offset: 2 }, { path: [0, 0], offset: 4 } ) ], [createNode('paragraph', 'abcdefghijklmnopqrst')] ] ]; describe('slate operations propagate between editors', () => { tests.forEach(([testName, input, ...cases]) => { it(`${testName}`, async () => { // Create two editors. const src = createTestEditor(); const dst = createTestEditor(); // Set initial state for src editor, propagate changes to dst editor. TestEditor.applyTransform( src, TestEditor.makeInsertNodes(input as CustomNode[], [0]) ); await wait(); let updates = TestEditor.getCapturedYjsUpdates(src); TestEditor.applyYjsUpdatesToYjs(dst, updates); await wait(); // Verify initial states. expect(src.children).toEqual(input); expect(toSlateDoc(src.sharedType)).toEqual(input); expect(toSlateDoc(dst.sharedType)).toEqual(input); expect(dst.children).toEqual(input); // Allow for multiple rounds of applying transforms and verifying state. const toTest = cases.slice(); while (toTest.length > 0) { const [transforms, output] = toTest.splice(0, 2); // Apply transforms to src editor, propagate changes to dst editor. TestEditor.applyTransforms(src, transforms as TransformFunc[]); // eslint-disable-next-line no-await-in-loop await wait(); updates = TestEditor.getCapturedYjsUpdates(src); TestEditor.applyYjsUpdatesToYjs(dst, updates); // eslint-disable-next-line no-await-in-loop await wait(); // Verify final states. expect(src.children).toEqual(output); expect(toSlateDoc(src.sharedType)).toEqual(output); expect(toSlateDoc(dst.sharedType)).toEqual(output); expect(dst.children).toEqual(output); } }); }); });