import { describe, it } from '@ephox/bedrock-client'; import { LegacyUnit, TinyAssertions, TinyHooks } from '@ephox/wrap-mcagar'; import { assert } from 'chai'; import Editor from 'tinymce/core/api/Editor'; describe('browser.tinymce.core.content.InsertContentCommandTest', () => { const hook = TinyHooks.bddSetupLight({ add_unload_trigger: false, disable_nodechange: true, indent: false, entities: 'raw', convert_urls: false, valid_styles: { '*': 'color,font-size,font-family,background-color,font-weight,font-style,text-decoration,' + 'float,margin,margin-top,margin-right,margin-bottom,margin-left,padding-left,text-align,display' }, base_url: '/project/tinymce/js/tinymce' }, []); const isTextNode = (node: Node): node is Text => node.nodeType === 3; const normalizeRng = (rng: Range) => { if (isTextNode(rng.startContainer)) { if (rng.startOffset === 0) { rng.setStartBefore(rng.startContainer); } else if (rng.startOffset >= rng.startContainer.data.length - 1) { rng.setStartAfter(rng.startContainer); } } if (isTextNode(rng.endContainer)) { if (rng.endOffset === 0) { rng.setEndBefore(rng.endContainer); } else if (rng.endOffset >= rng.endContainer.data.length - 1) { rng.setEndAfter(rng.endContainer); } } return rng; }; it('mceInsertContent - p inside text of p', () => { const editor = hook.editor(); editor.setContent('

1234

'); editor.focus(); let rng = editor.dom.createRng(); rng.setStart(editor.dom.select('p')[0].firstChild as Text, 1); rng.setEnd(editor.dom.select('p')[0].firstChild as Text, 3); editor.selection.setRng(rng); editor.execCommand('mceInsertContent', false, '

abc

'); TinyAssertions.assertContent(editor, '

1

abc

4

'); rng = normalizeRng(editor.selection.getRng()); assert.isTrue(rng.collapsed); assert.equal(rng.startContainer.nodeName, 'P'); assert.equal(rng.startOffset, 1); assert.equal(rng.endContainer.nodeName, 'P'); assert.equal(rng.endOffset, 1); assert.equal((rng.startContainer as HTMLElement).innerHTML, 'abc'); }); it('mceInsertContent before HR', () => { const editor = hook.editor(); editor.setContent('
'); editor.focus(); const rng = editor.dom.createRng(); rng.setStart(editor.getBody(), 0); rng.setEnd(editor.getBody(), 0); editor.selection.setRng(rng); editor.execCommand('mceInsertContent', false, 'x'); TinyAssertions.assertContent(editor, '

x


'); }); it('mceInsertContent HR at end of H1', () => { const editor = hook.editor(); editor.setContent('

abc

'); LegacyUnit.setSelection(editor, 'h1', 3); editor.execCommand('mceInsertContent', false, '
'); LegacyUnit.equalDom(editor.selection.getNode(), editor.getBody().lastChild as HTMLHeadingElement); assert.equal(editor.selection.getNode().nodeName, 'H1'); TinyAssertions.assertContent(editor, '

abc


\u00a0

'); }); it('mceInsertContent HR at end of H1 with P sibling', () => { const editor = hook.editor(); editor.setContent('

abc

def

'); LegacyUnit.setSelection(editor, 'h1', 3); editor.execCommand('mceInsertContent', false, '
'); LegacyUnit.equalDom(editor.selection.getNode(), editor.getBody().lastChild as HTMLParagraphElement); assert.equal(editor.selection.getNode().nodeName, 'P'); TinyAssertions.assertContent(editor, '

abc


def

'); }); it('mceInsertContent HR at end of H1 with inline elements with P sibling', () => { const editor = hook.editor(); editor.setContent('

abc

def

'); LegacyUnit.setSelection(editor, 'strong', 3); editor.execCommand('mceInsertContent', false, '
'); LegacyUnit.equalDom(editor.selection.getNode(), editor.getBody().lastChild as HTMLParagraphElement); assert.equal(editor.selection.getNode().nodeName, 'P'); TinyAssertions.assertContent(editor, '

abc


def

'); }); it('mceInsertContent empty block', () => { const editor = hook.editor(); editor.setContent('

abc

'); LegacyUnit.setSelection(editor, 'h1', 1); editor.execCommand('mceInsertContent', false, '

'); LegacyUnit.equalDom(editor.selection.getNode(), editor.getBody().childNodes[1]); assert.equal(editor.selection.getNode().nodeName, 'P'); TinyAssertions.assertContent(editor, '

a

\u00a0

bc

'); }); it('mceInsertContent table at end of H1 with P sibling', () => { const editor = hook.editor(); editor.setContent('

abc

def

'); LegacyUnit.setSelection(editor, 'h1', 3); editor.execCommand('mceInsertContent', false, '
'); assert.equal(editor.selection.getNode().nodeName, 'TD'); TinyAssertions.assertContent(editor, '

abc

\u00a0

def

'); }); it('mceInsertContent - p inside whole p', () => { const editor = hook.editor(); let rng; editor.setContent('

1234

'); rng = editor.dom.createRng(); rng.setStart(editor.dom.select('p')[0].firstChild as Text, 0); rng.setEnd(editor.dom.select('p')[0].firstChild as Text, 4); editor.selection.setRng(rng); editor.execCommand('mceInsertContent', false, '

abc

'); TinyAssertions.assertContent(editor, '

abc

'); rng = normalizeRng(editor.selection.getRng()); assert.isTrue(rng.collapsed); assert.equal(rng.startContainer.nodeName, 'P'); assert.equal(rng.startOffset, 1); assert.equal(rng.endContainer.nodeName, 'P'); assert.equal(rng.endOffset, 1); assert.equal((rng.startContainer as HTMLParagraphElement).innerHTML, 'abc'); }); it('mceInsertContent - pre in text of pre', () => { const editor = hook.editor(); let rng; editor.setContent('
1234
'); rng = editor.dom.createRng(); rng.setStart(editor.dom.select('pre')[0].firstChild as Text, 1); rng.setEnd(editor.dom.select('pre')[0].firstChild as Text, 3); editor.selection.setRng(rng); editor.execCommand('mceInsertContent', false, '
abc
'); TinyAssertions.assertContent(editor, '
1
abc
4
'); rng = normalizeRng(editor.selection.getRng()); assert.isTrue(rng.collapsed); assert.equal(rng.startContainer.nodeName, 'PRE'); assert.equal(rng.startOffset, 1); assert.equal(rng.endContainer.nodeName, 'PRE'); assert.equal(rng.endOffset, 1); assert.equal((rng.startContainer as HTMLPreElement).innerHTML, 'abc'); }); it('mceInsertContent - h1 in text of h1', () => { const editor = hook.editor(); let rng; editor.setContent('

1234

'); rng = editor.dom.createRng(); rng.setStart(editor.dom.select('h1')[0].firstChild as Text, 1); rng.setEnd(editor.dom.select('h1')[0].firstChild as Text, 3); editor.selection.setRng(rng); editor.execCommand('mceInsertContent', false, '

abc

'); TinyAssertions.assertContent(editor, '

1

abc

4

'); rng = normalizeRng(editor.selection.getRng()); assert.isTrue(rng.collapsed); assert.equal(rng.startContainer.nodeName, 'H1'); assert.equal(rng.startOffset, 1); assert.equal(rng.endContainer.nodeName, 'H1'); assert.equal(rng.endOffset, 1); assert.equal((rng.startContainer as HTMLHeadingElement).innerHTML, 'abc'); }); it('mceInsertContent - li inside li', () => { const editor = hook.editor(); let rng; editor.setContent(''); rng = editor.dom.createRng(); rng.setStart(editor.dom.select('li')[0].firstChild as Text, 1); rng.setEnd(editor.dom.select('li')[0].firstChild as Text, 3); editor.selection.setRng(rng); editor.execCommand('mceInsertContent', false, '
  • abc
  • '); TinyAssertions.assertContent(editor, ''); rng = normalizeRng(editor.selection.getRng()); assert.isTrue(rng.collapsed); assert.equal(rng.startContainer.nodeName, 'LI'); assert.equal(rng.startOffset, 1); assert.equal(rng.endContainer.nodeName, 'LI'); assert.equal(rng.endOffset, 1); assert.equal((rng.startContainer as HTMLLIElement).innerHTML, 'abc'); }); it('mceInsertContent - p inside empty editor', () => { const editor = hook.editor(); editor.setContent(''); editor.execCommand('mceInsertContent', false, '

    abc

    '); TinyAssertions.assertContent(editor, '

    abc

    '); const rng = normalizeRng(editor.selection.getRng()); assert.isTrue(rng.collapsed); assert.equal(rng.startContainer.nodeName, 'P'); assert.equal(rng.startOffset, 1); assert.equal(rng.endContainer.nodeName, 'P'); assert.equal(rng.endOffset, 1); assert.equal((rng.startContainer as HTMLElement).innerHTML, 'abc'); }); it('mceInsertContent - text inside empty p', () => { const editor = hook.editor(); editor.getBody().innerHTML = '

    '; LegacyUnit.setSelection(editor, 'p', 0); editor.execCommand('mceInsertContent', false, 'abc'); assert.equal( editor.getBody().innerHTML.toLowerCase().replace(/^
    /, ''), '

    abc

    ' ); // Opera inserts a BR at the beginning of contents if the P is empty const rng = normalizeRng(editor.selection.getRng()); assert.isTrue(rng.collapsed); assert.equal(rng.startContainer.nodeName, 'P'); assert.equal(rng.startOffset, 1); assert.equal(rng.endContainer.nodeName, 'P'); assert.equal(rng.endOffset, 1); assert.equal((rng.startContainer as HTMLElement).innerHTML, 'abc'); }); it('mceInsertContent - text inside empty p with br caret node', () => { const editor = hook.editor(); let rng; editor.getBody().innerHTML = '


    '; rng = editor.dom.createRng(); rng.setStart(editor.getBody().firstChild as Text, 0); rng.setEnd(editor.getBody().firstChild as Text, 0); editor.selection.setRng(rng); editor.execCommand('mceInsertContent', false, 'abc'); assert.equal(editor.getBody().innerHTML.toLowerCase(), '

    abc

    '); rng = normalizeRng(editor.selection.getRng()); assert.isTrue(rng.collapsed); assert.equal(rng.startContainer.nodeName, 'P'); assert.equal(rng.startOffset, 1); assert.equal(rng.endContainer.nodeName, 'P'); assert.equal(rng.endOffset, 1); assert.equal((rng.startContainer as HTMLParagraphElement).innerHTML, 'abc'); }); it('mceInsertContent - image inside p', () => { const editor = hook.editor(); let rng; editor.setContent('

    1

    '); rng = editor.dom.createRng(); rng.setStart(editor.dom.select('p')[0].firstChild as Text, 0); rng.setEnd(editor.dom.select('p')[0].firstChild as Text, 1); editor.selection.setRng(rng); editor.execCommand('mceInsertContent', false, ''); TinyAssertions.assertContent(editor, '

    '); rng = normalizeRng(editor.selection.getRng()); assert.isTrue(rng.collapsed); assert.equal(rng.startContainer.nodeName, 'P'); assert.equal(rng.startOffset, 1); assert.equal(rng.endContainer.nodeName, 'P'); assert.equal(rng.endOffset, 1); }); it('mceInsertContent - legacy content', () => { const editor = hook.editor(); // Convert legacy content editor.setContent('

    1

    '); const rng = editor.dom.createRng(); rng.setStart(editor.dom.select('p')[0].firstChild as Text, 0); rng.setEnd(editor.dom.select('p')[0].firstChild as Text, 1); editor.selection.setRng(rng); editor.execCommand('mceInsertContent', false, 'strikefont'); TinyAssertions.assertContent( editor, '

    strikefont

    ' ); }); it('mceInsertContent - hr', () => { const editor = hook.editor(); let rng; editor.setContent('

    123

    '); rng = editor.dom.createRng(); rng.setStart(editor.dom.select('p')[0].firstChild as Text, 1); rng.setEnd(editor.dom.select('p')[0].firstChild as Text, 2); editor.selection.setRng(rng); editor.execCommand('mceInsertContent', false, '
    '); TinyAssertions.assertContent(editor, '

    1


    3

    '); rng = normalizeRng(editor.selection.getRng()); assert.isTrue(rng.collapsed); LegacyUnit.equalDom(rng.startContainer, editor.getBody().lastChild as HTMLParagraphElement); assert.equal(rng.startContainer.nodeName, 'P'); assert.equal(rng.startOffset, 0); assert.equal(rng.endContainer.nodeName, 'P'); assert.equal(rng.endOffset, 0); }); it('mceInsertContent - forced root block', () => { const editor = hook.editor(); // Forced root block editor.getBody().innerHTML = ''; editor.execCommand('mceInsertContent', false, 'test123'); // Opera adds an extra paragraph since it adds a BR at the end of the contents pass though this for now since it's an minority browser assert.equal(editor.getContent().replace(/

    \u00a0<\/p>/g, ''), '

    test123

    '); }); it('mceInsertContent - mixed inline content inside td', () => { const editor = hook.editor(); // Forced root block editor.getBody().innerHTML = '
    X
    '; LegacyUnit.setSelection(editor, 'td', 0, 'td', 0); editor.execCommand('mceInsertContent', false, 'test123'); TinyAssertions.assertContent(editor, '
    test123X
    '); }); it('mceInsertContent - invalid insertion with spans on page', () => { const editor = hook.editor(); const startingContent = '

    123 testing span later in document

    '; const insertedContent = ''; editor.setContent(startingContent); const rng = editor.dom.createRng(); rng.setStart(editor.dom.select('p')[0].firstChild as Text, 0); rng.setEnd(editor.dom.select('p')[0].firstChild as Text, 0); editor.selection.setRng(rng); editor.execCommand('mceInsertContent', false, insertedContent); TinyAssertions.assertContent(editor, insertedContent + startingContent); }); it('mceInsertContent - text with space before at start of block', () => { const editor = hook.editor(); editor.getBody().innerHTML = '

    a

    '; LegacyUnit.setSelection(editor, 'p', 0); editor.execCommand('mceInsertContent', false, ' b'); TinyAssertions.assertContent(editor, '

    \u00a0ba

    '); }); it('mceInsertContent - text with space after at end of block', () => { const editor = hook.editor(); editor.getBody().innerHTML = '

    a

    '; LegacyUnit.setSelection(editor, 'p', 1); editor.execCommand('mceInsertContent', false, 'b '); TinyAssertions.assertContent(editor, '

    ab\u00a0

    '); }); it('mceInsertContent - text with space before/after at middle of block', () => { const editor = hook.editor(); editor.getBody().innerHTML = '

    ac

    '; LegacyUnit.setSelection(editor, 'p', 1); editor.execCommand('mceInsertContent', false, ' b '); TinyAssertions.assertContent(editor, '

    a b c

    '); }); it('mceInsertContent - inline element with space before/after at middle of block', () => { const editor = hook.editor(); editor.getBody().innerHTML = '

    ac

    '; LegacyUnit.setSelection(editor, 'p', 1); editor.execCommand('mceInsertContent', false, ' b '); TinyAssertions.assertContent(editor, '

    a b c

    '); }); it('mceInsertContent - block element with space before/after at middle of block', () => { const editor = hook.editor(); editor.getBody().innerHTML = '

    ac

    '; LegacyUnit.setSelection(editor, 'p', 1); editor.execCommand('mceInsertContent', false, '

    b

    '); TinyAssertions.assertContent(editor, '

    a

    b

    c

    '); }); it('mceInsertContent - strong in strong', () => { const editor = hook.editor(); editor.getBody().innerHTML = 'ac'; LegacyUnit.setSelection(editor, 'strong', 1); editor.execCommand('mceInsertContent', false, { content: 'b', merge: true }); TinyAssertions.assertContent(editor, '

    abc

    '); }); it('mceInsertContent - span in span same style color', () => { const editor = hook.editor(); editor.getBody().innerHTML = 'ac'; LegacyUnit.setSelection(editor, 'span', 1); editor.execCommand('mceInsertContent', false, { content: 'b', merge: true }); TinyAssertions.assertContent(editor, '

    abc

    '); }); it('mceInsertContent - span in span different style color', () => { const editor = hook.editor(); editor.getBody().innerHTML = 'ac'; LegacyUnit.setSelection(editor, 'span', 1); editor.execCommand('mceInsertContent', false, { content: 'b', merge: true }); TinyAssertions.assertContent(editor, '

    abc

    '); }); it('mceInsertContent - select with option element', () => { const editor = hook.editor(); editor.getBody().innerHTML = '

    1

    '; LegacyUnit.setSelection(editor, 'p', 1); editor.execCommand('mceInsertContent', false, '2'); TinyAssertions.assertContent(editor, '

    12

    '); }); it('mceInsertContent - insert P in span style element #7090', () => { const editor = hook.editor(); editor.setContent('

    1

    3

    '); LegacyUnit.setSelection(editor, 'span', 1); editor.execCommand('mceInsertContent', false, '

    2

    '); TinyAssertions.assertContent(editor, '

    1

    2

    3

    '); }); it('mceInsertContent - insert char at char surrounded by spaces', () => { const editor = hook.editor(); editor.setContent('

    a b c

    '); LegacyUnit.setSelection(editor, 'p', 2, 'p', 3); editor.execCommand('mceInsertContent', false, 'X'); assert.equal(JSON.stringify(editor.getContent()), '"

    a X c

    "'); }); });