substance
Version:
Substance is a JavaScript library for web-based content editing. It provides building blocks for realizing custom text editors and web-based publishing systems.
1,524 lines (1,445 loc) • 71.4 kB
JavaScript
/* eslint-disable no-use-before-define */
import { module } from 'substance-test'
import { Document, EditingInterface, forEach } from 'substance'
import setupEditor from './fixture/setupEditor'
import headersAndParagraphs from './fixture/headersAndParagraphs'
import {
_h1,
_p1, P1_TEXT,
_p2, P2_TEXT,
_s1, _empty, _il1,
_block1, _block2,
_in1, IN1_TITLE,
_l1, _l11, _l12, _l13, _l1_empty, _li1plus, LI1_TEXT, LI2_TEXT, LI3_TEXT,
_l2, _l21, _l22,
_t1, _t1_sparse, T_CONTENT
} from './fixture/samples'
const test = module('Editing')
// TODO: consolidate specification and 'category labels' of tests
// TODO: we should enable t.sandbox for all tests so that this implementation is
// tested on all platforms
test("IT1: Inserting text with cursor in the middle of a TextProperty", (t) => {
let { editorSession, doc } = setupEditor(t, _p1)
editorSession.setSelection({
type: 'property',
path: ['p1', 'content'],
startOffset: 3,
containerId: 'body'
})
editorSession.transaction((tx) => {
tx.insertText('xxx')
}, { action: 'type' })
let sel = editorSession.getSelection()
let p1 = doc.get('p1')
t.equal(p1.getText(), P1_TEXT.slice(0,3)+'xxx'+P1_TEXT.slice(3), 'Text should have been inserted correctly.')
t.equal(sel.start.offset, 6, 'Cursor should be after inserted text')
t.end()
})
test("IT2: Inserting text with cursor within TextProperty inside annotation", (t) => {
let { editorSession, doc } = setupEditor(t, _p1, _s1)
editorSession.setSelection({
type: 'property',
path: ['p1', 'content'],
startOffset: 4,
containerId: 'body'
})
editorSession.transaction((tx) => {
tx.insertText('xxx')
}, { action: 'type' })
let sel = editorSession.getSelection()
let p1 = doc.get('p1')
let s1 = doc.get('s1')
t.equal(p1.getText(), P1_TEXT.slice(0,4)+'xxx'+P1_TEXT.slice(4), 'Text should have been inserted correctly.')
t.equal(s1.end.offset, 8, 'Annotation should have been expanded.')
t.equal(sel.start.offset, 7, 'Cursor should be after inserted text')
t.end()
})
test("IT3: Inserting text with cursor within TextProperty at the start of an annotation", (t) => {
let { editorSession, doc } = setupEditor(t, _p1, _s1)
editorSession.setSelection({
type: 'property',
path: ['p1', 'content'],
startOffset: 3,
containerId: 'body'
})
editorSession.transaction((tx) => {
tx.insertText('xxx')
}, { action: 'type' })
let sel = editorSession.getSelection()
let p1 = doc.get('p1')
let s1 = doc.get('s1')
t.equal(p1.getText(), P1_TEXT.slice(0,3)+'xxx'+P1_TEXT.slice(3), 'Text should have been inserted correctly.')
t.equal(s1.start.offset, 6, 'Annotation should have been moved.')
t.equal(s1.end.offset, 8, 'Annotation should have been moved.')
t.equal(sel.start.offset, 6, 'Cursor should be after inserted text')
t.end()
})
test("IT4: Inserting text with cursor within TextProperty at the end of an annotation", (t) => {
let { editorSession, doc } = setupEditor(t, _p1, _s1)
editorSession.setSelection({
type: 'property',
path: ['p1', 'content'],
startOffset: 5,
containerId: 'body'
})
editorSession.transaction((tx) => {
tx.insertText('xxx')
}, { action: 'type' })
let sel = editorSession.getSelection()
let p1 = doc.get('p1')
let s1 = doc.get('s1')
t.equal(p1.getText(), P1_TEXT.slice(0,5)+'xxx'+P1_TEXT.slice(5), 'Text should have been inserted correctly.')
t.equal(s1.start.offset, 3, 'Annotation should have been moved.')
t.equal(s1.end.offset, 8, 'Annotation should have been moved.')
t.equal(sel.start.offset, 8, 'Cursor should be after inserted text')
t.end()
})
test("IT5: Inserting text with range within TextProperty", (t) => {
let { editorSession, doc } = setupEditor(t, _p1)
editorSession.setSelection({
type: 'property',
path: ['p1', 'content'],
startOffset: 2,
endOffset: 5,
containerId: 'body'
})
editorSession.transaction((tx) => {
tx.insertText('xxx')
}, { action: 'type' })
let sel = editorSession.getSelection()
let p1 = doc.get('p1')
t.equal(p1.getText(), P1_TEXT.slice(0, 2)+'xxx'+P1_TEXT.slice(5), 'Text should have been inserted correctly.')
t.equal(sel.start.offset, 5, 'Cursor should be after inserted text')
t.ok(sel.isCollapsed(), '... collapsed')
t.end()
})
test("IT6: Inserting text with range within TextProperty overlapping an annotion aligned at the left side", (t) => {
let { editorSession, doc } = setupEditor(t, _p1, _s1)
doc.set(['s1', 'end', 'offset'], 6)
editorSession.setSelection({
type: 'property',
path: ['p1', 'content'],
startOffset: 3,
endOffset: 5,
containerId: 'body'
})
editorSession.transaction((tx) => {
tx.insertText('xxx')
}, { action: 'type' })
let sel = editorSession.getSelection()
let p1 = doc.get('p1')
let s1 = doc.get('s1')
t.equal(p1.getText(), P1_TEXT.slice(0, 3)+'xxx'+P1_TEXT.slice(5), 'Text should have been inserted correctly.')
t.equal(s1.start.offset, 3, 'Annotation should have been moved.')
t.equal(s1.end.offset, 7, 'Annotation should have been moved.')
t.equal(sel.start.offset, 6, 'Cursor should be after inserted text')
t.ok(sel.isCollapsed(), '... collapsed')
t.end()
})
test("IT7: Inserting text with range within TextProperty inside an annotion", (t) => {
let { editorSession, doc } = setupEditor(t, _p1, _s1)
editorSession.setSelection({
type: 'property',
path: ['p1', 'content'],
startOffset: 3,
endOffset: 5,
containerId: 'body'
})
editorSession.transaction((tx) => {
tx.insertText('xxx')
}, { action: 'type' })
let sel = editorSession.getSelection()
let p1 = doc.get('p1')
let s1 = doc.get('s1')
t.equal(p1.getText(), P1_TEXT.slice(0, 3)+'xxx'+P1_TEXT.slice(5), 'Text should have been inserted correctly.')
t.equal(s1.start.offset, 3, 'Annotation should have been moved.')
t.equal(s1.end.offset, 6, 'Annotation should have been moved.')
t.equal(sel.start.offset, 6, 'Cursor should be after inserted text')
t.ok(sel.isCollapsed(), '... collapsed')
t.end()
})
test("IT8: Inserting text with range within TextProperty starting inside an annotion", (t) => {
let { editorSession, doc } = setupEditor(t, _p1, _s1)
doc.set(['s1', 'end', 'offset'], 6)
editorSession.setSelection({
type: 'property',
path: ['p1', 'content'],
startOffset: 4,
endOffset: 6,
containerId: 'body'
})
editorSession.transaction((tx) => {
tx.insertText('xxx')
}, { action: 'type' })
let sel = editorSession.getSelection()
let p1 = doc.get('p1')
let s1 = doc.get('s1')
t.equal(p1.getText(), P1_TEXT.slice(0, 4)+'xxx'+P1_TEXT.slice(6), 'Text should have been inserted correctly.')
t.equal(s1.start.offset, 3, 'Annotation should have been moved.')
t.equal(s1.end.offset, 7, 'Annotation should have been moved.')
t.equal(sel.start.offset, 7, 'Cursor should be after inserted text')
t.ok(sel.isCollapsed(), '... collapsed')
t.end()
})
test("IT9: Inserting text after an InlineNode node", (t) => {
let { editorSession, doc } = setupEditor(t, _p1, _il1)
editorSession.setSelection({
type: 'property',
path: ['p1', 'content'],
startOffset: 4,
containerId: 'body'
})
editorSession.transaction((tx) => {
tx.insertText('Y')
}, { action: 'type' })
let sel = editorSession.getSelection()
let p1 = doc.get('p1')
let il1 = doc.get('il1')
t.equal(p1.getText(), P1_TEXT.slice(0,3)+'\uFEFF'+'Y'+P1_TEXT.slice(3), 'Text should have been inserted correctly.') // eslint-disable-line no-useless-concat
t.equal(sel.start.offset, 5, 'Cursor should be after inserted character')
t.deepEqual([il1.start.offset, il1.end.offset], [3,4], 'InlineNode should have correct dimensions')
t.end()
})
test("IT10: Typing over an InlineNode node", (t) => {
let { editorSession, doc } = setupEditor(t, _p1, _il1)
editorSession.setSelection({
type: 'property',
path: ['p1', 'content'],
startOffset: 3,
endOffset: 4,
containerId: 'body'
})
editorSession.transaction((tx) => {
tx.insertText('Y')
}, { action: 'type' })
let p1 = doc.get('p1')
let il1 = doc.get('il1')
t.nil(il1, 'InlineNode should have been deleted.')
t.equal(p1.getText(), P1_TEXT.slice(0,3)+'Y'+P1_TEXT.slice(3), 'Text should have been inserted correctly.') // eslint-disable-line no-useless-concat
t.end()
})
test("IT11: Typing over a selected IsolatedNode", (t) => {
let { editorSession, doc } = setupEditor(t, _in1)
editorSession.setSelection({
type: 'node',
nodeId: 'in1',
containerId: 'body'
})
editorSession.transaction((tx) => {
tx.insertText('Y')
}, { action: 'type' })
let body = doc.get('body')
t.isNil(doc.get('in1'), 'IsolatedNode should have been deleted.')
t.equal(body.getLength(), 1, 'There should be one node.')
t.equal(body.getChildAt(0).getText(), 'Y', '.. containing the typed text.')
t.end()
})
test("IT12: Typing before an IsolatedNode", (t) => {
let { editorSession, doc } = setupEditor(t, _in1)
editorSession.setSelection({
type: 'node',
nodeId: 'in1',
mode: 'before',
containerId: 'body'
})
editorSession.transaction((tx) => {
tx.insertText('Y')
}, { action: 'type' })
let body = doc.get('body')
t.equal(body.getLength(), 2, 'There should be two nodes.')
t.equal(body.getChildAt(0).getText(), 'Y', '.. first one containing the typed text.')
t.end()
})
test("IT13: Typing after an IsolatedNode", (t) => {
let { editorSession, doc } = setupEditor(t, _in1)
editorSession.setSelection({
type: 'node',
nodeId: 'in1',
mode: 'after',
containerId: 'body'
})
editorSession.transaction((tx) => {
tx.insertText('Y')
}, { action: 'type' })
let body = doc.get('body')
t.equal(body.getLength(), 2, 'There should be two nodes.')
t.equal(body.getChildAt(1).getText(), 'Y', '.. second one containing the typed text.')
t.end()
})
test("IT14: Typing over a ContainerSelection", (t) => {
let { editorSession, doc } = setupEditor(t, _p1, _p2)
let p1 = doc.get('p1')
let p2 = doc.get('p2')
editorSession.setSelection({
type: 'container',
startPath: p1.getPath(),
startOffset: 3,
endPath: p2.getPath(),
endOffset: 4,
containerId: 'body'
})
editorSession.transaction((tx) => {
tx.insertText('Y')
}, { action: 'type' })
let body = doc.get('body')
t.deepEqual(body.nodes, ['p1'], 'Only the first paragraph should be left.')
t.equal(p1.getText(), P1_TEXT.slice(0,3)+'Y'+P2_TEXT.slice(4), 'The content should have been merged correctly')
t.end()
})
test("IT15: Inserting text into a ListItem", (t) => {
let { editorSession, doc } = setupEditor(t, _l1, _l11, _l12)
editorSession.setSelection({
type: 'property',
path: ['l1-1', 'content'],
startOffset: 3,
containerId: 'body'
})
editorSession.transaction((tx) => {
tx.insertText('xxx')
}, { action: 'type' })
let sel = editorSession.getSelection()
let li = doc.get('l1-1')
t.equal(li.getText(), LI1_TEXT.slice(0,3)+'xxx'+LI1_TEXT.slice(3), 'Text should have been inserted correctly.')
t.equal(sel.start.offset, 6, 'Cursor should be after inserted text')
t.end()
})
test("IT16: Inserting text in an IsolatedNode", (t) => {
let { doc, editorSession } = setupEditor(t, _p1, _in1, _p2)
editorSession.setSelection({
type: 'property',
path: ['in1', 'title'],
startOffset: 3
})
editorSession.transaction((tx) => {
tx.insertText('xxx')
})
let in1 = doc.get('in1')
t.equal(in1.title, IN1_TITLE.slice(0,3)+'xxx'+IN1_TITLE.slice(3), 'Text should be inserted into field')
t.end()
})
test("II1: Inserting InlineNode node into a TextProperty", (t) => {
let { editorSession, doc } = setupEditor(t, _p1)
editorSession.setSelection({
type: 'property',
path: ['p1', 'content'],
startOffset: 3,
containerId: 'body'
})
editorSession.transaction((tx) => {
tx.insertInlineNode({
type: 'test-inline-node',
id: 'il1',
content: 'X'
})
})
let sel = editorSession.getSelection()
let p1 = doc.get('p1')
let il1 = doc.get('il1')
t.equal(p1.getText(), P1_TEXT.slice(0,3)+'\uFEFF'+P1_TEXT.slice(3), 'Text should have been inserted correctly.')
t.equal(il1.start.offset, 3, 'Annotation should have been moved.')
t.equal(il1.end.offset, 4, 'Annotation should have been moved.')
t.equal(sel.start.offset, 4, 'Cursor should be after inserted inline node')
t.end()
})
test("IB2: Inserting BlockNode using cursor at start of a TextNode", (t) => {
let { editorSession, doc } = setupEditor(t, _p1)
editorSession.setSelection({
type: 'property',
path: ['p1', 'content'],
startOffset: 0,
containerId: 'body'
})
editorSession.transaction((tx) => {
tx.insertBlockNode({
type: 'test-block',
id: 'ib1'
})
})
let sel = editorSession.getSelection()
let body = doc.get('body')
let p1 = doc.get('p1')
t.equal(body.nodes[0], 'ib1', 'First node should be inserted block node.')
t.equal(body.nodes[1], 'p1', 'Second node should be inserted block node.')
t.ok(sel.isCollapsed(), 'Selection should be collapsed')
t.deepEqual(sel.start.path, p1.getTextPath(), '... on paragraph')
t.equal(sel.start.offset, 0, '... first position')
t.end()
})
test("IB3: Inserting an existing BlockNode", (t) => {
let { editorSession, doc } = setupEditor(t, _p1)
editorSession.setSelection({
type: 'property',
path: ['p1', 'content'],
startOffset: 0,
containerId: 'body'
})
editorSession.transaction((tx) => {
tx.create({
type: 'test-block',
id: 'ib1'
})
})
const ib1 = doc.get('ib1')
editorSession.transaction((tx) => {
tx.insertBlockNode(ib1)
})
let body = doc.get('body')
t.ok(body.getChildAt(0) === ib1, 'First node should be the very block node instance created before.')
t.end()
})
test("IB4: Inserting a BlockNode when an IsolatedNode is selected", (t) => {
let { editorSession, doc } = setupEditor(t, _p1, _in1, _p2)
editorSession.setSelection({
type: 'node',
nodeId: 'in1',
containerId: 'body'
})
editorSession.transaction((tx) => {
tx.insertBlockNode({
type: 'test-block',
id: 'ib1'
})
})
let body = doc.get('body')
t.equals(body.length, 3, 'There should be three nodes.')
t.equals(body.nodes[1], 'ib1', 'The second one should be the inserted block node.')
t.isNil(doc.get('in1'), 'The IsolatedNode should have been deleted.')
t.end()
})
test("IB5: Inserting a BlockNode before an IsolatedNode", (t) => {
let { editorSession, doc } = setupEditor(t, _p1, _in1, _p2)
editorSession.setSelection({
type: 'node',
nodeId: 'in1',
mode: 'before',
containerId: 'body'
})
editorSession.transaction((tx) => {
tx.insertBlockNode({
type: 'test-block',
id: 'ib1'
})
})
let body = doc.get('body')
t.deepEqual(body.nodes, ['p1', 'ib1', 'in1', 'p2'], 'The body nodes should be in proper order.')
t.end()
})
test("IB6: Inserting a BlockNode after an IsolatedNode", (t) => {
let { editorSession, doc } = setupEditor(t, _p1, _in1, _p2)
editorSession.setSelection({
type: 'node',
nodeId: 'in1',
mode: 'after',
containerId: 'body'
})
editorSession.transaction((tx) => {
tx.insertBlockNode({
type: 'test-block',
id: 'ib1'
})
})
let body = doc.get('body')
t.deepEqual(body.nodes, ['p1', 'in1', 'ib1', 'p2'], 'The body nodes should be in proper order.')
t.end()
})
test("IB7: Inserting a BlockNode with an expanded PropertySelection", (t) => {
let { editorSession, doc } = setupEditor(t, _p1)
let p1 = doc.get('p1')
editorSession.setSelection({
type: 'property',
path: p1.getPath(),
startOffset: 3,
endOffset: 5,
containerId: 'body'
})
editorSession.transaction((tx) => {
tx.insertBlockNode({
type: 'test-block',
id: 'ib1'
})
})
let body = doc.get('body')
t.equal(body.getLength(), 3, 'There should be 3 nodes.')
t.equal(body.nodes[1], 'ib1', 'The second should be the inserted block node.')
t.equal(p1.getText(), P1_TEXT.slice(0, 3), 'The paragraph should have been truncated.')
t.equal(body.getChildAt(2).getText(), P1_TEXT.slice(5), '.. and the tail stored in a new paragraph.')
t.end()
})
test("IB8: Inserting a BlockNode into an empty paragraph", (t) => {
let { editorSession, doc } = setupEditor(t, _empty)
let empty = doc.get('empty')
editorSession.setSelection({
type: 'property',
path: empty.getPath(),
startOffset: 0,
containerId: 'body'
})
editorSession.transaction((tx) => {
tx.insertBlockNode({
type: 'test-block',
id: 'ib1'
})
})
let body = doc.get('body')
t.deepEqual(body.nodes, ['ib1'], 'There should only be the inserted block node.')
t.nil(doc.get('empty'), 'The empty paragraph should have been deleted.')
t.end()
})
test("IB9: Inserting a BlockNode after a text node", (t) => {
let { editorSession, doc } = setupEditor(t, _p1)
let p1 = doc.get('p1')
editorSession.setSelection({
type: 'property',
path: p1.getPath(),
startOffset: p1.getLength(),
containerId: 'body'
})
editorSession.transaction((tx) => {
tx.insertBlockNode({
type: 'test-block',
id: 'ib1'
})
})
let body = doc.get('body')
t.deepEqual(body.nodes, ['p1', 'ib1'], 'The block node should have been inserted after the paragraph.')
t.end()
})
test("IB10: Inserting a BlockNode with a collapsed ContainerSelection", (t) => {
let { editorSession, doc } = setupEditor(t, _p1)
let p1 = doc.get('p1')
editorSession.setSelection({
type: 'container',
startPath: p1.getPath(),
startOffset: 3,
endPath: p1.getPath(),
endOffset: 3,
containerId: 'body'
})
editorSession.transaction((tx) => {
tx.insertBlockNode({
type: 'test-block',
id: 'ib1'
})
})
let body = doc.get('body')
t.equal(body.getLength(), 3, 'There should be three nodes.')
t.equal(body.nodes[1], 'ib1', '.. the second one being the inserted block node.')
t.end()
})
test("IB11: Inserting a BlockNode with a ContainerSelection", (t) => {
let { editorSession, doc } = setupEditor(t, _p1, _p2)
let p1 = doc.get('p1')
let p2 = doc.get('p2')
editorSession.setSelection({
type: 'container',
startPath: p1.getPath(),
startOffset: 3,
endPath: p2.getPath(),
endOffset: 4,
containerId: 'body'
})
editorSession.transaction((tx) => {
tx.insertBlockNode({
type: 'test-block',
id: 'ib1'
})
})
let body = doc.get('body')
t.deepEqual(body.nodes, ['p1', 'ib1', 'p2'], 'There should be three nodes.')
t.equal(p1.getText(), P1_TEXT.slice(0, 3), 'The first paragraph should have been truncated correctly')
t.equal(p2.getText(), P2_TEXT.slice(4), 'The second paragraph should have been truncated correctly')
t.end()
})
test("DEL0: Deleting without selection", (t) => {
let { editorSession } = setupEditor(t, _p1, _p2)
editorSession.setSelection(null)
t.doesNotThrow(() => {
editorSession.transaction((tx) => {
tx.deleteCharacter('right')
})
}, 'Should not throw')
t.end()
})
test("DEL1: Deleting a property selection", (t) => {
let { editorSession, doc } = setupEditor(t, _p1)
let p1 = doc.get('p1')
editorSession.setSelection({
type: 'property',
path: p1.getPath(),
startOffset: 0,
endOffset: 3
})
editorSession.transaction((tx) => {
tx.deleteSelection()
})
t.equal(p1.getText(), P1_TEXT.slice(3), 'Parargaph should be truncated')
t.end()
})
test("DEL2: Deleting using DELETE with cursor at the end of a TextNode at the end of a Container", (t) => {
let { editorSession, doc } = setupEditor(t, _p1, _p2)
editorSession.setSelection({
type: 'property',
path: ['p2', 'content'],
startOffset: P2_TEXT.length,
containerId: 'body'
})
editorSession.transaction((tx) => {
tx.deleteCharacter('right')
})
// nothing should have happened
let sel = editorSession.getSelection()
let body = doc.get('body')
t.equal(body.nodes.length, 2, 'There should still be 2 nodes in body')
let p2 = doc.get('p2')
t.equal(p2.getText(), P2_TEXT, 'p2 should still have same content.')
t.equal(sel.start.offset, P2_TEXT.length, 'Cursor should still be at the same position')
t.end()
})
test("DEL3: Deleting using DELETE with cursor in the middle of a TextProperty", (t) => {
let { editorSession, doc } = setupEditor(t, _p1)
editorSession.transaction((tx) => {
tx.setSelection({
type: 'property',
path: ['p1', 'content'],
startOffset: 3
})
tx.deleteCharacter('right')
})
let sel = editorSession.getSelection()
let p1 = doc.get('p1')
t.equal(p1.getText(), P1_TEXT.slice(0,3)+P1_TEXT.slice(4), 'One character should have been deleted')
t.equal(sel.start.offset, 3, 'Cursor should be at the same position')
t.end()
})
test("DEL4: Deleting using DELETE with cursor inside an empty TextNode and TextNode as successor", (t) => {
let { editorSession, doc } = setupEditor(t, _p1, _empty, _p2)
editorSession.setSelection({
type: 'property',
path: ['empty', 'content'],
startOffset: 0,
containerId: 'body'
})
editorSession.transaction((tx) => {
tx.deleteCharacter('right')
})
let sel = editorSession.getSelection()
let body = doc.get('body')
let p2 = doc.get('p2')
t.equal(body.nodes.length, 2, 'There should be only 2 nodes left.')
t.equal(p2.getText(), P2_TEXT, 'p2 should not be affected')
t.ok(sel.isCollapsed(), 'Selection should be collapsed')
t.deepEqual(sel.start.path, p2.getTextPath(), '... on p2')
t.equal(sel.start.offset, 0, '... at first position')
t.end()
})
test("DEL5: Deleting using DELETE with cursor inside an empty TextNode and IsolatedNode as successor", (t) => {
let { editorSession, doc } = setupEditor(t, _p1, _empty, _block1)
editorSession.setSelection({
type: 'property',
path: ['empty', 'content'],
startOffset: 0,
containerId: 'body'
})
editorSession.transaction((tx) => {
tx.deleteCharacter('right')
})
let sel = editorSession.getSelection()
let body = doc.get('body')
t.equal(body.nodes.length, 2, 'There should be only 2 nodes left.')
t.ok(sel.isNodeSelection(), 'Selection should be a NodeSelection')
t.equal(sel.getNodeId(), 'block1', '... block1')
t.end()
})
test("DEL6: Deleting using DELETE with cursor inside an empty TextNode and List as successor", (t) => {
let { editorSession, doc } = setupEditor(t, _p1, _empty, _l1, _l11, _l12)
editorSession.setSelection({
type: 'property',
path: ['empty', 'content'],
startOffset: 0,
containerId: 'body'
})
editorSession.transaction((tx) => {
tx.deleteCharacter('right')
})
let sel = editorSession.getSelection()
let body = doc.get('body')
t.equal(body.nodes.length, 2, 'There should be only 2 nodes left.')
t.ok(sel.isCollapsed(), 'Selection should be collapsed')
t.deepEqual(sel.start.path, ['l1-1', 'content'], '... on first list item')
t.equal(sel.start.offset, 0,'... at first position')
t.end()
})
test("DEL7: Deleting using DELETE with cursor at the end of a non-empty TextNode and TextNode as successor", (t) => {
let { editorSession, doc } = setupEditor(t, _p1, _p2)
editorSession.setSelection({
type: 'property',
path: ['p1', 'content'],
startOffset: P1_TEXT.length,
containerId: 'body'
})
editorSession.transaction((tx) => {
tx.deleteCharacter('right')
})
let sel = editorSession.getSelection()
let body = doc.get('body')
let p1 = doc.get('p1')
t.equal(body.nodes.length, 1, 'There should be only one node left.')
t.ok(sel.isCollapsed(), 'Selection should be a collapsed')
t.deepEqual(sel.start.path, p1.getTextPath(), '... on p1')
t.equal(sel.start.offset, P1_TEXT.length, '... at the same position as before')
t.end()
})
test("DEL8: Deleting using DELETE with cursor at the end of a non-empty TextNode and IsolatedNode as successor", (t) => {
let { editorSession, doc } = setupEditor(t, _p1, _block1)
editorSession.setSelection({
type: 'property',
path: ['p1', 'content'],
startOffset: P1_TEXT.length,
containerId: 'body'
})
editorSession.transaction((tx) => {
tx.deleteCharacter('right')
})
// NOTE: if there is no merge possible, nothing should happen
// only the selection should be updated
let sel = editorSession.getSelection()
let body = doc.get('body')
t.equal(body.nodes.length, 2, 'There should still be 2 nodes left.')
t.ok(sel.isNodeSelection(), 'Selection should be a NodeSelection')
t.equal(sel.getNodeId(), 'block1', '... in block1')
t.ok(sel.isFull(), '... selecting the whole node')
t.end()
})
test("DEL9: Deleting using DELETE with cursor after an IsolatedNode and a TextNode as successor", (t) => {
let { editorSession, doc } = setupEditor(t, _block1, _p1)
editorSession.setSelection({
type: 'node',
nodeId: 'block1',
mode: 'after',
containerId: 'body'
})
editorSession.transaction((tx) => {
tx.deleteCharacter('right')
})
let p1 = doc.get('p1')
t.equal(p1.getText(), P1_TEXT.slice(1), 'First character of paragraph should have been deleted.')
_checkSelection(t, editorSession.getSelection(), {
type: 'property',
path: p1.getPath(),
startOffset: 0
})
t.end()
})
test("DEL10: Deleting using DELETE with cursor after an IsolatedNode and an IsolatedNode as successor", (t) => {
let { editorSession } = setupEditor(t, _block1, _block2)
editorSession.setSelection({
type: 'node',
nodeId: 'block1',
mode: 'after',
containerId: 'body'
})
editorSession.transaction((tx) => {
tx.deleteCharacter('right')
})
_checkSelection(t, editorSession.getSelection(), {
type: 'node',
nodeId: 'block2',
mode: 'full'
})
t.end()
})
test("DEL11: Deleting using BACKSPACE with cursor in the middle of a TextProperty", (t) => {
let { editorSession, doc } = setupEditor(t, _p1)
editorSession.setSelection({
type: 'property',
path: ['p1', 'content'],
startOffset: 4,
containerId: 'body'
})
editorSession.transaction((tx) => {
tx.deleteCharacter('left')
})
let sel = editorSession.getSelection()
let p1 = doc.get('p1')
t.equal(p1.getText(), P1_TEXT.slice(0,3)+P1_TEXT.slice(4), 'one character should have been deleted')
t.equal(sel.start.offset, 3, 'Cursor should have shifted by one character')
t.end()
})
test("DEL12: Deleting using BACKSPACE with cursor inside an empty TextNode and TextNode as predecessor", (t) => {
let { editorSession, doc } = setupEditor(t, _p1, _empty, _p2)
editorSession.setSelection({
type: 'property',
path: ['empty', 'content'],
startOffset: 0,
containerId: 'body'
})
editorSession.transaction((tx) => {
tx.deleteCharacter('left')
})
let sel = editorSession.getSelection()
let p1 = doc.get('p1')
t.isNil(doc.get('empty'), 'empty node should have been deleted')
t.equal(p1.getText(), P1_TEXT, 'p1 should be untouched')
t.deepEqual(sel.start.path, p1.getTextPath(), 'Cursor should be in p1')
t.equal(sel.start.offset, P1_TEXT.length, '... at last position')
t.end()
})
test("DEL13: Deleting using BACKSPACE with cursor inside an empty TextNode and IsolatedNode as predecessor", (t) => {
let { editorSession, doc } = setupEditor(t, _block1, _empty, _p2)
editorSession.setSelection({
type: 'property',
path: ['empty', 'content'],
startOffset: 0,
containerId: 'body'
})
editorSession.transaction((tx) => {
tx.deleteCharacter('left')
})
let sel = editorSession.getSelection()
t.isNil(doc.get('empty'), 'empty node should have been deleted')
t.ok(sel.isNodeSelection(), 'Selection should be a node selection')
t.equal(sel.getNodeId(), 'block1', '... on block1')
t.end()
})
test("DEL14: Deleting using BACKSPACE with cursor at the start of a non-empty TextNode and TextNode as predecessor", (t) => {
let { editorSession, doc } = setupEditor(t, _p1, _p2)
editorSession.setSelection({
type: 'property',
path: ['p2', 'content'],
startOffset: 0,
containerId: 'body'
})
editorSession.transaction((tx) => {
tx.deleteCharacter('left')
})
let sel = editorSession.getSelection()
let p1 = doc.get('p1')
t.isNil(doc.get('p2'), 'p2 should have been deleted')
t.equal(p1.getText(), P1_TEXT+P2_TEXT, 'Text should have been merged')
t.ok(sel.isCollapsed(), 'Selection should be collapsed')
t.deepEqual(sel.start.path, p1.getTextPath(), '... on p1')
t.ok(sel.start.offset, P1_TEXT.length, '... cursor should after the original text of p1')
t.end()
})
test("DEL15: Deleting using BACKSPACE with cursor at the start of a non-empty TextNode and IsolatedNode as predecessor", (t) => {
let { editorSession, doc } = setupEditor(t, _block1, _p2)
editorSession.setSelection({
type: 'property',
path: ['p2', 'content'],
startOffset: 0,
containerId: 'body'
})
editorSession.transaction((tx) => {
tx.deleteCharacter('left')
})
let sel = editorSession.getSelection()
let body = doc.get('body')
t.equal(body.getLength(), 2, 'There should still be two nodes')
t.ok(sel.isNodeSelection(), 'Selection should be a NodeSelection')
t.equal(sel.getNodeId(), 'block1', '... on block1')
t.ok(sel.isFull(), '... selecting the full node')
t.end()
})
test("DEL16: Deleting using BACKSPACE with cursor after IsolatedNode", (t) => {
let { editorSession, doc } = setupEditor(t, _block1, _p2)
editorSession.setSelection({
type: 'node',
mode: 'after',
nodeId: 'block1',
containerId: 'body'
})
editorSession.transaction((tx) => {
tx.deleteCharacter('left')
})
let sel = editorSession.getSelection()
let body = doc.get('body')
t.isNil(doc.get('block1'), 'IsolatedNode should have been deleted')
t.equal(body.getLength(), 2, 'There should still be two nodes')
let pnew = body.getChildAt(0)
t.ok(sel.isCollapsed(), 'Selection should be collapsed')
t.deepEqual(sel.start.path, pnew.getTextPath(), '... on new paragraph')
t.equal(sel.start.offset, 0, '... at first position')
t.end()
})
test("DEL17: Deleting using BACKSPACE with cursor before IsolatedNode with TextNode as predecessor", (t) => {
let { editorSession, doc } = setupEditor(t, _p1, _block2)
editorSession.setSelection({
type: 'node',
mode: 'before',
nodeId: 'block2',
containerId: 'body'
})
editorSession.transaction((tx) => {
tx.deleteCharacter('left')
})
let sel = editorSession.getSelection()
let body = doc.get('body')
let p1 = doc.get('p1')
t.equal(body.getLength(), 2, 'There should still be two nodes')
t.equal(p1.getText(), P1_TEXT.slice(0, -1), 'Last character of p1 should have been deleted')
t.ok(sel.isCollapsed(), 'Selection should be collapsed')
t.deepEqual(sel.start.path, p1.getTextPath(), '... on p1')
t.equal(sel.start.offset, P1_TEXT.length-1, '... at last position')
t.end()
})
test("DEL18: Deleting using BACKSPACE with cursor before IsolatedNode and IsolatedNode as predecessor", (t) => {
let { editorSession, doc } = setupEditor(t, _block1, _block2)
editorSession.setSelection({
type: 'node',
mode: 'before',
nodeId: 'block2',
containerId: 'body'
})
editorSession.transaction((tx) => {
tx.deleteCharacter('left')
})
let sel = editorSession.getSelection()
let body = doc.get('body')
t.equal(body.getLength(), 2, 'There should still be two nodes')
t.ok(sel.isNodeSelection(), 'Selection should be a NodeSelection')
t.equal(sel.getNodeId(), 'block1', '... on block1')
t.ok(sel.isFull(), '... selection the entire node')
t.end()
})
test("DEL19: Deleting an entirely selected IsolatedNode", (t) => {
let { editorSession, doc } = setupEditor(t, _p1, _block1, _p2)
editorSession.setSelection({
type: 'node',
nodeId: 'block1',
containerId: 'body'
})
editorSession.transaction((tx) => {
tx.deleteSelection()
})
let sel = editorSession.getSelection()
let body = doc.get('body')
t.isNil(doc.get('block1'), 'IsolatedNode should have been deleted')
let pnew = body.getChildAt(1)
t.equal(body.getLength(), 3, 'There should be 3 nodes')
t.ok(sel.isCollapsed(), 'Selection should be collapsed')
t.deepEqual(sel.start.path, pnew.getTextPath(), '... on new paragraph')
t.equal(sel.start.offset, 0, '... at first position')
t.end()
})
test("DEL20: Deleting a range starting before a TextNode and ending after a TextNode", (t) => {
let { editorSession, doc } = setupEditor(t, _p1, _block1, _block2, _p2)
let p1 = doc.get('p1')
let p2 = doc.get('p2')
editorSession.setSelection({
type: 'container',
startPath: p1.getTextPath(),
startOffset: 0,
endPath: p2.getTextPath(),
endOffset: p2.getLength(),
containerId: 'body'
})
editorSession.transaction((tx) => {
tx.deleteSelection()
})
let sel = editorSession.getSelection()
let body = doc.get('body')
t.equal(body.nodes.length, 1, 'There should be only one node left')
let first = body.getChildAt(0)
t.ok(first.isText(), '... which is a TextNode')
t.ok(first.isEmpty(), '... which is empty')
t.ok(sel.isCollapsed(), 'Selection should be collapsed')
t.deepEqual(sel.start.path, first.getTextPath(), '... on that TextNode')
t.equal(sel.start.offset, 0, '... at first position')
t.end()
})
test("DEL21: Deleting a range starting in the middle of a TextNode and ending after a TextNode", (t) => {
let { editorSession, doc } = setupEditor(t, _p1, _block1, _block2, _p2)
let p1 = doc.get('p1')
let p2 = doc.get('p2')
editorSession.setSelection({
type: 'container',
startPath: p1.getTextPath(),
startOffset: 3,
endPath: p2.getTextPath(),
endOffset: p2.getLength(),
containerId: 'body'
})
editorSession.transaction((tx) => {
tx.deleteSelection()
})
let sel = editorSession.getSelection()
let body = doc.get('body')
t.equal(body.nodes.length, 1, 'There should be only one node left')
let first = body.getChildAt(0)
t.ok(first.isText(), '... which is a TextNode')
t.equal(first.getText(), P1_TEXT.slice(0, 3), '... with truncated content')
t.ok(sel.isCollapsed(), 'Selection should be collapsed')
t.deepEqual(sel.start.path, first.getTextPath(), '... on that TextNode')
t.equal(sel.start.offset, 3, '... at last position')
t.end()
})
test("DEL22: Deleting a range starting before a TextNode and ending in the middle of a TextNode", (t) => {
let { editorSession, doc } = setupEditor(t, _p1, _block1, _block2, _p2)
let p1 = doc.get('p1')
let p2 = doc.get('p2')
editorSession.setSelection({
type: 'container',
startPath: p1.getTextPath(),
startOffset: 0,
endPath: p2.getTextPath(),
endOffset: 3,
containerId: 'body'
})
editorSession.transaction((tx) => {
tx.deleteSelection()
})
let sel = editorSession.getSelection()
let body = doc.get('body')
t.equal(body.nodes.length, 1, 'There should be only one node left')
let first = body.getChildAt(0)
t.ok(first.isText(), '... which is a TextNode')
t.equal(first.getText(), P2_TEXT.slice(3), '... with sliced content')
t.ok(sel.isCollapsed(), 'Selection should be collapsed')
t.deepEqual(sel.start.path, first.getTextPath(), '... on that TextNode')
t.equal(sel.start.offset, 0, '... at first position')
t.end()
})
test("DEL23: Deleting a range starting in the middle of a TextNode and ending in the middle of a TextNode", (t) => {
let { editorSession, doc } = setupEditor(t, _p1, _block1, _block2, _p2)
let p1 = doc.get('p1')
let p2 = doc.get('p2')
editorSession.setSelection({
type: 'container',
startPath: p1.getTextPath(),
startOffset: 3,
endPath: p2.getTextPath(),
endOffset: 3,
containerId: 'body'
})
editorSession.transaction((tx) => {
tx.deleteSelection()
})
let sel = editorSession.getSelection()
let body = doc.get('body')
t.equal(body.nodes.length, 1, 'There should be only one node left')
let first = body.getChildAt(0)
t.ok(first.isText(), '... which is a TextNode')
t.equal(first.getText(), P1_TEXT.slice(0,3)+P2_TEXT.slice(3), '... with merged content')
t.ok(sel.isCollapsed(), 'Selection should be collapsed')
t.deepEqual(sel.start.path, first.getTextPath(), '... on that TextNode')
t.equal(sel.start.offset, 3, '... at correct position')
t.end()
})
test("DEL24: Deleting a range starting in the middle of a TextNode and ending in the middle of a ListItem", (t) => {
let { editorSession, doc } = setupEditor(t, _p1, _block1, _block2, _l1, _l11, _l12)
let p1 = doc.get('p1')
let l1 = doc.get('l1')
editorSession.setSelection({
type: 'container',
startPath: p1.getTextPath(),
startOffset: 3,
endPath: ['l1-1', 'content'],
endOffset: 3,
containerId: 'body'
})
editorSession.transaction((tx) => {
tx.deleteSelection()
})
let sel = editorSession.getSelection()
let body = doc.get('body')
t.equal(body.nodes.length, 2, 'There should be 2 nodes left')
t.equal(p1.getText(), P1_TEXT.slice(0,3)+LI1_TEXT.slice(3), '... with merged content')
t.equal(l1.items.length, 1, 'The list should have only 1 item left')
t.ok(sel.isCollapsed(), 'Selection should be collapsed')
t.deepEqual(sel.start.path, p1.getTextPath(), '... on p1')
t.equal(sel.start.offset, 3, '... at correct position')
t.end()
})
test("DEL25: Deleting a range starting in the middle of a ListItem and ending in the middle of a TextNode", (t) => {
let { editorSession, doc } = setupEditor(t, _l1, _l11, _l12, _block1, _block2, _p1)
let p1 = doc.get('p1')
let l1 = doc.get('l1')
editorSession.setSelection({
type: 'container',
startPath: ['l1-2', 'content'],
startOffset: 3,
endPath: p1.getTextPath(),
endOffset: 3,
containerId: 'body'
})
editorSession.transaction((tx) => {
tx.deleteSelection()
})
let sel = editorSession.getSelection()
let body = doc.get('body')
t.equal(body.nodes.length, 1, 'There should be 1 node left')
t.equal(l1.items.length, 2, 'The list should have 2 items')
let li2 = l1.getItemAt(1)
t.equal(li2.getText(), LI2_TEXT.slice(0,3)+P1_TEXT.slice(3), 'The second item should container merged content')
t.ok(sel.isCollapsed(), 'Selection should be collapsed')
t.deepEqual(sel.start.path, li2.getTextPath(), '... on second list item')
t.equal(sel.start.offset, 3, '... at correct position')
t.end()
})
test("DEL26: Deleting a range within a ListItem", (t) => {
let { editorSession, doc } = setupEditor(t, _l1, _l11, _l12)
let l1 = doc.get('l1')
editorSession.setSelection({
type: 'container',
startPath: ['l1-2', 'content'],
startOffset: 3,
endPath: ['l1-2', 'content'],
endOffset: 6,
containerId: 'body'
})
editorSession.transaction((tx) => {
tx.deleteSelection()
})
let sel = editorSession.getSelection()
t.equal(l1.items.length, 2, 'The list should have 2 items')
let li2 = l1.getItemAt(1)
t.equal(li2.getText(), LI2_TEXT.slice(0,3)+LI2_TEXT.slice(6), 'The second item should be changed')
t.ok(sel.isCollapsed(), 'Selection should be collapsed')
t.deepEqual(sel.start.path, li2.getTextPath(), '... on second list item')
t.equal(sel.start.offset, 3, '... at correct position')
t.end()
})
test("DEL27: Deleting a range across two ListItems within the same List", (t) => {
let { editorSession, doc } = setupEditor(t, _l1, _l11, _l12)
let l1 = doc.get('l1')
editorSession.setSelection({
type: 'container',
startPath: ['l1-1', 'content'],
startOffset: 3,
endPath: ['l1-2', 'content'],
endOffset: 3,
containerId: 'body'
})
editorSession.transaction((tx) => {
tx.deleteSelection()
})
let sel = editorSession.getSelection()
t.equal(l1.items.length, 1, 'The list should have 1 item')
let li1 = l1.getItemAt(0)
t.equal(li1.getText(), LI1_TEXT.slice(0,3)+LI2_TEXT.slice(3), 'The items should be merged')
t.ok(sel.isCollapsed(), 'Selection should be collapsed')
t.deepEqual(sel.start.path, li1.getTextPath(), '... on the list item')
t.equal(sel.start.offset, 3, '... at correct position')
t.end()
})
test("DEL28: Deleting a ContainerSelection within a single TextNode", (t) => {
let { editorSession, doc } = setupEditor(t, _p1)
let p1 = doc.get('p1')
editorSession.setSelection({
type: 'container',
startPath: p1.getPath(),
startOffset: 3,
endPath: p1.getPath(),
endOffset: 5,
containerId: 'body'
})
editorSession.transaction((tx) => {
tx.deleteSelection()
})
t.equal(p1.getText(), P1_TEXT.slice(0,3)+P1_TEXT.slice(5), 'The text should have been deleted')
_checkSelection(t, editorSession.getSelection(), {
type: 'property',
path: p1.getPath(),
startOffset: 3
})
t.end()
})
test("DEL29: Deleting a character inside an IsolatedNode", (t) => {
let { doc, editorSession } = setupEditor(t, _p1, _in1, _p2)
editorSession.setSelection({
type: 'property',
path: ['in1', 'title'],
startOffset: 3
})
editorSession.transaction((tx) => {
tx.deleteCharacter('right')
})
let in1 = doc.get('in1')
t.equal(in1.title, IN1_TITLE.slice(0,3)+IN1_TITLE.slice(4), 'Text should be inserted into field')
t.end()
})
test("DEL30: Merging two ListItems using DELETE", (t) => {
let { editorSession, doc } = setupEditor(t, _l1, _l11, _l12)
editorSession.setSelection({
type: 'property',
path: ['l1-1', 'content'],
startOffset: LI1_TEXT.length,
containerId: 'body'
})
editorSession.transaction((tx) => {
tx.deleteCharacter('right')
})
let sel = editorSession.getSelection()
let l = doc.get('l1')
t.equal(l.items.length, 1, 'Only one list item should be left')
let li = l.getItemAt(0)
t.equal(li.getText(), LI1_TEXT+LI2_TEXT, 'The list item should have the merged text')
t.ok(sel.isCollapsed(), 'The selection should be collapsed')
t.deepEqual(sel.start.path, li.getTextPath(), '... on the list item')
t.equal(sel.start.offset, LI1_TEXT.length, '... at the end of the original content')
t.end()
})
test("DEL31: Merging a List into previous List using DELETE", (t) => {
let { editorSession, doc } = setupEditor(t, _l1, _l11, _l12, _l2, _l21, _l22)
editorSession.setSelection({
type: 'property',
path: ['l1-2', 'content'],
startOffset: LI2_TEXT.length,
containerId: 'body'
})
editorSession.transaction((tx) => {
tx.deleteCharacter('right')
})
let sel = editorSession.getSelection()
let l1 = doc.get('l1')
t.equal(l1.items.length, 4, 'First list should have 4 items')
t.ok(sel.isCollapsed(), 'The selection should be collapsed')
t.deepEqual(sel.start.path, ['l1-2', 'content'], '... on the same item')
t.equal(sel.start.offset, LI2_TEXT.length, '... at the same position')
t.end()
})
test("DEL32: Merging an empty List into previous TextNode using DELETE", (t) => {
let { editorSession, doc } = setupEditor(t, _p1, _l1)
let p1 = doc.get('p1')
editorSession.setSelection({
type: 'property',
path: p1.getPath(),
startOffset: p1.getLength(),
containerId: 'body'
})
editorSession.transaction((tx) => {
tx.deleteCharacter('right')
})
t.isNil(doc.get('l2'), 'Second list should have been removed.')
t.end()
})
test("DEL33: Deleting using BACKSPACE at the start of a text property editor", (t) => {
let { editorSession, doc } = setupEditor(t)
let p = doc.create({
id: 'p',
type: 'paragraph',
content: 'foo'
})
editorSession.setSelection({
type: 'property',
path: p.getTextPath(),
startOffset: 0,
endOffset: 0
})
t.doesNotThrow(() => {
editorSession.transaction((tx) => {
tx.deleteCharacter('left')
})
}, 'Should not throw an exception')
t.equal(p.getText(), 'foo', '.. and the text should be untouched')
t.end()
})
test("DEL44: Deleting using DEL at the end of a text property editor", (t) => {
let { editorSession, doc } = setupEditor(t)
let p = doc.create({
id: 'p',
type: 'paragraph',
content: 'foo'
})
editorSession.setSelection({
type: 'property',
path: p.getTextPath(),
startOffset: 3,
endOffset: 3
})
t.doesNotThrow(() => {
editorSession.transaction((tx) => {
tx.deleteCharacter('right')
})
}, 'Should not throw an exception')
t.equal(p.getText(), 'foo', '.. and the text should be untouched')
t.end()
})
test("BR1: Breaking without selection", (t) => {
let { editorSession } = setupEditor(t, _p1)
editorSession.setSelection(null)
t.doesNotThrow(() => {
editorSession.transaction((tx) => {
tx.break()
})
})
t.end()
})
test("BR2: Breaking a TextNode", (t) => {
let { editorSession, doc } = setupEditor(t, _p1)
editorSession.setSelection({
type: 'property',
path: ['p1', 'content'],
startOffset: 3,
containerId: 'body'
})
editorSession.transaction((tx) => {
tx.break()
}, { action: 'break' })
let body = doc.get('body')
t.equals(body.length, 2, 'There should be 2 nodes')
let first = body.getChildAt(0)
let second = body.getChildAt(1)
t.equals(second.type, 'paragraph', 'Second should be a paragraph')
t.equals(first.getText(), P1_TEXT.slice(0,3), 'First should be truncated')
t.equals(second.getText(), P1_TEXT.slice(3), '.. and second should contain the tail')
t.end()
})
test("BR3: Breaking annotated text with cursor before the annotation", (t) => {
let { editorSession, doc } = setupEditor(t, _p1, _s1)
editorSession.setSelection({
type: 'property',
path: ['p1', 'content'],
startOffset: 1,
containerId: 'body'
})
editorSession.transaction((tx) => {
tx.break()
}, { action: 'break' })
let body = doc.get('body')
let second = body.getChildAt(1)
let anno = doc.get('s1')
t.deepEqual(anno.start.path, second.getPath(), 'Annotation should now be on the second paragraph')
t.equal(anno.start.offset, 2, '.. starting at character 2')
t.equal(anno.end.offset, 4, '.. ending at character 4')
t.end()
})
test("BR4: Breaking annotated text with cursor inside the annotation", (t) => {
let { editorSession, doc } = setupEditor(t, _p1, _s1)
editorSession.setSelection({
type: 'property',
path: ['p1', 'content'],
startOffset: 4,
containerId: 'body'
})
editorSession.transaction((tx) => {
tx.break()
}, { action: 'break' })
let body = doc.get('body')
let secondPara = body.getChildAt(1)
let anno = doc.get('s1')
t.equal(anno.start.offset, 3, '.. starting at character 3')
t.equal(anno.end.offset, 4, '.. ending at character 4')
let secondAnno = doc.getIndex('annotations').get([secondPara.id, 'content'])[0]
t.equal(secondAnno.start.offset, 0, '.. starting at character 0')
t.equal(secondAnno.end.offset, 1, '.. ending at character 1')
t.end()
})
test("BR5: Breaking a TextNode at the first position", (t) => {
let { editorSession, doc } = setupEditor(t, _h1)
let h1 = doc.get('h1')
editorSession.setSelection({
type: 'property',
path: h1.getPath(),
startOffset: 0,
containerId: 'body'
})
editorSession.transaction((tx) => {
tx.break()
}, { action: 'break' })
let body = doc.get('body')
t.equals(body.length, 2, 'There should be 2 nodes')
let first = body.getChildAt(0)
let second = body.getChildAt(1)
t.equals(second, h1, 'Second should be the original node')
t.equals(first.type, h1.type, 'First should be of the same type')
t.equals(first.getText(), '', '.. but empty')
_checkSelection(t, editorSession.getSelection(), {
type: 'property',
path: h1.getPath(),
startOffset: 0
})
t.end()
})
test("BR5: Breaking a TextNode at the last position", (t) => {
let { editorSession, doc } = setupEditor(t, _h1)
let h1 = doc.get('h1')
editorSession.setSelection({
type: 'property',
path: h1.getPath(),
startOffset: h1.getLength(),
containerId: 'body'
})
editorSession.transaction((tx) => {
tx.break()
}, { action: 'break' })
let body = doc.get('body')
t.equals(body.length, 2, 'There should be 2 nodes')
let first = body.getChildAt(0)
let second = body.getChildAt(1)
t.equals(first, h1, 'First should be the original node')
t.equals(second.type, 'paragraph', 'Second should be a paragraph.')
t.equals(second.getText(), '', '.. without content')
_checkSelection(t, editorSession.getSelection(), {
type: 'property',
path: second.getPath(),
startOffset: 0
})
t.end()
})
test("BR6: Breaking a ContainerSelection", (t) => {
let { editorSession, doc } = setupEditor(t, _p1, _p2)
editorSession.setSelection({
type: 'container',
startPath: ['p1', 'content'],
startOffset: 3,
endPath: ['p2', 'content'],
endOffset: 4,
containerId: 'body'
})
editorSession.transaction((tx)=>{
tx.break()
})
let p2 = doc.get('p2')
t.equal(p2.getText(), P2_TEXT.slice(4))
_checkSelection(t, editorSession.getSelection(), {
type: 'property',
path: ['p2', 'content'],
startOffset: 0,
endOffset: 0
})
t.end()
})
test("BR7: Breaking a NodeSelection", (t) => {
let { editorSession, doc } = setupEditor(t, _p1, _in1, _p2)
editorSession.setSelection({
type: 'node',
nodeId: 'in1',
containerId: 'body'
})
editorSession.transaction((tx)=>{
tx.break()
})
let body = doc.get('body')
t.equal(body.getLength(), 4, 'There should be one more node')
let newP = body.getChildAt(2)
_checkSelection(t, editorSession.getSelection(), {
type: 'property',
path: newP.getPath(),
startOffset: 0,
endOffset: 0
})
t.end()
})
test("BR8: Breaking a NodeSelection (before)", (t) => {
let { editorSession, doc } = setupEditor(t, _p1, _in1, _p2)
editorSession.setSelection({
type: 'node',
nodeId: 'in1',
mode: 'before',
containerId: 'body'
})
editorSession.transaction((tx)=>{
tx.break()
})
let body = doc.get('body')
t.equal(body.getLength(), 4, 'There should be one more node')
_checkSelection(t, editorSession.getSelection(), {
type: 'node',
nodeId: 'in1',
mode: 'before',
containerId: 'body'
})
t.end()
})
test("BR9: Breaking with an ex