test(`ctrl-k clears selected text`, (assert) => { let initialText = 'something'; const mobiledoc = Helpers.mobiledoc.build( ({post, markupSection, marker}) => post([ markupSection('p', [marker(initialText)]) ])); editor = new Editor({mobiledoc}); editor.render(editorElement); let textElement = editor.post.sections.head.markers.head.renderNode.element; Helpers.dom.moveCursorTo(editor, textElement, 4, textElement, 8); Helpers.dom.triggerKeyCommand(editor, 'K', MODIFIERS.CTRL); let changedMobiledoc = editor.serialize(); let expectedMobiledoc = Helpers.mobiledoc.build( ({post, markupSection, marker}) => { return post([ markupSection('p', [ marker('someg') ]) ]); }); assert.deepEqual(changedMobiledoc, expectedMobiledoc, 'mobiledoc updated appropriately'); });
test('right arrow at start of card moves the cursor across the card', assert => { let mobiledoc = Helpers.mobiledoc.build(({post, cardSection}) => { return post([ cardSection('my-card') ]); }); editor = new Editor({mobiledoc, cards}); editor.render(editorElement); let cardHead = editor.post.sections.head.headPosition(); let cardTail = editor.post.sections.head.tailPosition(); // Before zwnj Helpers.dom.moveCursorTo(editor, editorElement.firstChild.firstChild, 0); Helpers.dom.triggerRightArrowKey(editor, MODIFIERS.SHIFT); let { range } = editor; assert.positionIsEqual(range.head, cardHead); assert.positionIsEqual(range.tail, cardTail); // After zwnj Helpers.dom.moveCursorTo(editor, editorElement.firstChild.firstChild, 1); Helpers.dom.triggerRightArrowKey(editor, MODIFIERS.SHIFT); range = editor.range; assert.positionIsEqual(range.head, cardHead); assert.positionIsEqual(range.tail, cardTail); });
test('left arrow when at the start of a card moves the cursor to the previous section', assert => { let mobiledoc = Helpers.mobiledoc.build(({post, markupSection, cardSection}) => { return post([ markupSection('p'), cardSection('my-card') ]); }); editor = new Editor({mobiledoc, cards}); editor.render(editorElement); let sectionTail = editor.post.sections.head.tailPosition(); // Before zwnj let sectionElement = editor.post.sections.tail.renderNode.element; Helpers.dom.moveCursorTo(editor, sectionElement.firstChild, 0); Helpers.dom.triggerLeftArrowKey(editor); let { range } = editor; assert.positionIsEqual(range.head, sectionTail); assert.positionIsEqual(range.tail, sectionTail); // After zwnj Helpers.dom.moveCursorTo(editor, sectionElement.firstChild, 1); Helpers.dom.triggerLeftArrowKey(editor); range = editor.range; assert.positionIsEqual(range.head, sectionTail); assert.positionIsEqual(range.tail, sectionTail); });
test('pasting when replacing a list item works', (assert) => { let mobiledoc = Helpers.mobiledoc.build( ({post, listSection, listItem, markupSection, marker}) => { return post([ markupSection('p', [marker('X')]), listSection('ul', [ listItem([marker('Y')]) ]) ]); }); editor = new Editor({mobiledoc, cards}); editor.render(editorElement); assert.hasElement('#editor li:contains(Y)', 'precond: has li with Y'); Helpers.dom.selectText(editor, 'X', editorElement); Helpers.dom.triggerCopyEvent(editor); Helpers.dom.selectText(editor, 'Y', editorElement); Helpers.dom.triggerPasteEvent(editor); assert.hasElement('#editor li:contains(X)', 'replaces Y with X in li'); assert.hasNoElement('#editor li:contains(Y)', 'li with Y is gone'); });
test('left arrow when at the end of a card moves the cursor across the card', assert => { let mobiledoc = Helpers.mobiledoc.build(({post, cardSection}) => { return post([ cardSection('my-card') ]); }); editor = new Editor({mobiledoc, cards}); editor.render(editorElement); let cardHead = editor.post.sections.head.headPosition(); // Before zwnj Helpers.dom.moveCursorTo(editor, editorElement.firstChild.lastChild, 0); Helpers.dom.triggerLeftArrowKey(editor); let { range } = editor; assert.positionIsEqual(range.head, cardHead); assert.positionIsEqual(range.tail, cardHead); // After zwnj Helpers.dom.moveCursorTo(editor, editorElement.firstChild.lastChild, 1); Helpers.dom.triggerLeftArrowKey(editor); range = editor.range; assert.positionIsEqual(range.head, cardHead); assert.positionIsEqual(range.tail, cardHead); // On wrapper Helpers.dom.moveCursorTo(editor, editorElement.firstChild, 2); Helpers.dom.triggerLeftArrowKey(editor); range = editor.range; assert.positionIsEqual(range.head, cardHead); assert.positionIsEqual(range.tail, cardHead); });
test('typing tab enters a tab character', (assert) => { let done = assert.async(); assert.expect(3); let mobiledoc = Helpers.mobiledoc.build(({post}) => post()); editor = new Editor({mobiledoc}); editor.render(editorElement); assert.hasElement('#editor'); assert.hasNoElement('#editor p'); Helpers.dom.moveCursorTo(editor, $('#editor')[0]); Helpers.dom.insertText(editor, TAB); Helpers.dom.insertText(editor, 'Y'); window.setTimeout(() => { let expectedPost = Helpers.postAbstract.build(({post, markupSection, marker}) => { return post([ markupSection('p', [ marker(`${TAB}Y`) ]) ]); }); assert.postIsSimilar(editor.post, expectedPost); done(); }); });
test('copy-paste can copy list sections', (assert) => { const mobiledoc = Helpers.mobiledoc.build( ({post, markupSection, marker, listSection, listItem}) => { return post([ markupSection('p', [marker('abc')]), listSection('ul', [ listItem([marker('list')]) ]), markupSection('p', [marker('123')]) ]); }); editor = new Editor({mobiledoc}); editor.render(editorElement); Helpers.dom.selectText(editor, 'c', editor.element, '1', editor.element); Helpers.dom.triggerCopyEvent(editor); let textNode = $('#editor p')[1].childNodes[0]; assert.equal(textNode.textContent, '123', 'precond - correct textNode'); Helpers.dom.moveCursorTo(editor, textNode, 3); // end of node Helpers.dom.triggerPasteEvent(editor); assert.equal($('#editor ul').length, 2, 'pastes the list'); assert.hasElement($('#editor ul:eq(0) li:contains(list)')); });
test('when editing is disabled, the selection detection code is disabled', (assert) => { let done = assert.async(); $('#qunit-fixture').append('<p>outside section 1</p>'); $('#qunit-fixture').append('<p>outside section 2</p>'); editor = new Editor({mobiledoc: mobileDocWithSection}); editor.render(editorElement); editor.disableEditing(); const outside1 = $('p:contains(outside section 1)')[0]; const outside2 = $('p:contains(outside section 2)')[0]; Helpers.wait(() => { Helpers.dom.selectText(editor ,'outside', outside1, 'section 2', outside2); Helpers.wait(() => { assert.equal(editor.activeSections.length, 0, 'no selection inside the editor'); const selectedText = Helpers.dom.getSelectedText(); assert.ok(selectedText.indexOf('outside section 1') !== -1 && selectedText.indexOf('outside section 2') !== -1, 'selects the text'); done(); }); }); });
test('selecting text across markers and deleting joins markers', (assert) => { editor = new Editor({mobiledoc: mobileDocWith2Sections}); editor.render(editorElement); Helpers.dom.selectText('rst sect', editorElement); editor.run(postEditor => postEditor.toggleMarkup('strong')); let firstTextNode = editorElement .childNodes[0] // p .childNodes[1] // b .childNodes[0]; // textNode containing "rst sect" let secondTextNode = editorElement .childNodes[0] // p .childNodes[2]; // textNode containing "ion" assert.equal(firstTextNode.textContent, 'rst sect', 'correct first text node'); assert.equal(secondTextNode.textContent, 'ion', 'correct second text node'); Helpers.dom.selectText('t sect', firstTextNode, 'ion', secondTextNode); Helpers.dom.triggerDelete(editor); assert.hasElement('p:contains(firs)', 'deletes across markers'); assert.hasElement('strong:contains(rs)', 'maintains bold text'); firstTextNode = editorElement .childNodes[0] // p .childNodes[1] // b .childNodes[0]; // textNode now containing "rs" assert.deepEqual(Helpers.dom.getCursorPosition(), {node: firstTextNode, offset: 2}); });
test('returning false from key command still runs built-in functionality', (assert) => { const mobiledoc = Helpers.mobiledoc.build( ({post, markupSection, marker}) => post([ markupSection('p', [marker('something')]) ])); editor = new Editor({mobiledoc}); let passedEditor; editor.registerKeyCommand({ str: 'enter', run(editor) { passedEditor = editor; return false; } }); editor.render(editorElement); assert.equal($('#editor p').length, 1, 'has 1 paragraph to start'); Helpers.dom.moveCursorTo(editor, editorElement.childNodes[0].childNodes[0], 5); Helpers.dom.triggerEnter(editor); assert.ok(!!passedEditor && passedEditor === editor, 'run method is called'); assert.equal($('#editor p').length, 2, 'has added a new paragraph'); });
test('selecting all text across sections and hitting enter deletes and moves cursor to empty section', (assert) => { editor = new Editor({mobiledoc: mobileDocWith2Sections}); editor.render(editorElement); let firstSection = $('#editor p:eq(0)')[0], secondSection = $('#editor p:eq(1)')[0]; Helpers.dom.selectText(editor ,'first section', firstSection, 'second section', secondSection); Helpers.dom.triggerEnter(editor); assert.equal($('#editor p').length, 1, 'single section'); assert.equal($('#editor p:eq(0)').text(), '', 'blank text'); // Firefox reports that the cursor is on the "<br>", but Safari and Chrome do not. // Grab the selection here, then set it to the expected value, and compare again // the window's selection let selection = window.getSelection(); let cursorElement = $('#editor p br')[0]; assert.ok(cursorElement, 'has cursor element'); Helpers.dom.selectRange(cursorElement, 0, cursorElement, 0); let newSelection = window.getSelection(); assert.equal(selection.anchorNode, newSelection.anchorNode, 'correct anchorNode'); assert.equal(selection.focusNode, newSelection.focusNode, 'correct focusNode'); assert.equal(selection.anchorOffset, newSelection.anchorOffset, 'correct anchorOffset'); assert.equal(selection.focusOffset, newSelection.focusOffset, 'correct focusOffset'); });
test('returning false from key command causes next match to run', (assert) => { const mobiledoc = Helpers.mobiledoc.build( ({post, markupSection, marker}) => post([ markupSection('p', [marker('something')]) ])); let firstCommandRan, secondCommandRan; editor = new Editor({mobiledoc}); editor.registerKeyCommand({ str: 'ctrl+x', run() { firstCommandRan = true; } }); editor.registerKeyCommand({ str: 'ctrl+x', run() { secondCommandRan = true; return false; } }); editor.render(editorElement); Helpers.dom.triggerKeyCommand(editor, 'X', MODIFIERS.CTRL); assert.ok(!!secondCommandRan, 'last registered method is called'); assert.ok(!!firstCommandRan, 'first registered method is called'); });
test('new key commands can be registered without modifiers', (assert) => { const mobiledoc = Helpers.mobiledoc.build( ({post, markupSection, marker}) => post([ markupSection('p', [marker('something')]) ])); let passedEditor; editor = new Editor({mobiledoc}); editor.registerKeyCommand({ str: 'X', run(editor) { passedEditor = editor; } }); editor.render(editorElement); Helpers.dom.triggerKeyCommand(editor, 'Y', MODIFIERS.CTRL); assert.ok(!passedEditor, 'incorrect key combo does not trigger key command'); Helpers.dom.triggerKeyCommand(editor, 'X', MODIFIERS.CTRL); assert.ok(!passedEditor, 'key with modifier combo does not trigger key command'); Helpers.dom.triggerKeyCommand(editor, 'X'); assert.ok(!!passedEditor && passedEditor === editor, 'run method is called'); });
test(`${command} applies markup ${markupName} to highlighted text`, (assert) => { assert.expect(2); let done = assert.async(); let modifier = MODIFIERS[modifierName]; let modifierKeyCode = Keycodes[modifierName]; let initialText = 'something'; const mobiledoc = Helpers.mobiledoc.build( ({post, markupSection, marker}) => post([ markupSection('p', [marker(initialText)]) ])); editor = new Editor({mobiledoc}); editor.render(editorElement); assert.hasNoElement(`#editor ${markupName}`, `precond - no ${markupName} text`); Helpers.dom.selectText(editor ,initialText, editorElement); Helpers.dom.triggerKeyCommand(editor, key, modifier); Helpers.dom.triggerKeyEvent(editor, 'keyup', {charCode: 0, keyCode: modifierKeyCode}); setTimeout(() => { assert.hasElement(`#editor ${markupName}:contains(${initialText})`, `text wrapped in ${markupName}`); done(); }); });
test('deleting when after deletion there is a trailing space positions cursor at end of selection', (assert) => { const done = assert.async(); editor = new Editor({mobiledoc: mobileDocWith2Sections}); editor.render(editorElement); let firstSectionTextNode = editor.element.childNodes[0].firstChild; Helpers.dom.moveCursorTo(editor, firstSectionTextNode, 'first section'.length); let count = 'ection'.length; while (count--) { Helpers.dom.triggerDelete(editor); } assert.equal($('#editor p:eq(0)').text(), 'first s', 'precond - correct section text after initial deletions'); Helpers.dom.triggerDelete(editor); assert.equal($('#editor p:eq(0)').text(), `first${NO_BREAK_SPACE}`, 'precond - correct text after deleting last char before space'); let text = 'e'; Helpers.dom.insertText(editor, text); Helpers.wait(() => { assert.equal(editor.post.sections.head.text, `first ${text}`, 'character is placed after space'); done(); }); });
test('selecting text across markers deletes intermediary markers', (assert) => { let mobiledoc = Helpers.mobiledoc.build( ({post, markupSection, marker, markup}) => { return post([ markupSection('p', [ marker('abc'), marker('123', [markup('strong')]), marker('def') ]) ]); }); editor = new Editor({mobiledoc}); editor.render(editorElement); const textNode1 = editorElement.childNodes[0].childNodes[0], textNode2 = editorElement.childNodes[0].childNodes[2]; assert.equal(textNode1.textContent, 'abc', 'precond - text node 1'); assert.equal(textNode2.textContent, 'def', 'precond - text node 2'); Helpers.dom.selectText('b', textNode1, 'e', textNode2); Helpers.dom.triggerDelete(editor); assert.hasElement('p:contains(af)', 'has remaining first section'); Helpers.dom.insertText(editor, 'X'); assert.hasElement('p:contains(aXf)', 'inserts text at correct place'); });
test('left arrow when at the start of a card moves to previous list item', assert => { let mobiledoc = Helpers.mobiledoc.build( ({post, listSection, listItem, marker, cardSection}) => { return post([ listSection('ul', [listItem([marker('abc')])]), cardSection('my-card') ]); }); editor = new Editor({mobiledoc, cards}); editor.render(editorElement); let itemTail = editor.post.sections.head.items.head.tailPosition(); // Before zwnj let sectionElement = editor.post.sections.tail.renderNode.element; Helpers.dom.moveCursorTo(sectionElement.firstChild, 0); Helpers.dom.triggerLeftArrowKey(editor); let { range } = editor; assert.positionIsEqual(range.head, itemTail); assert.positionIsEqual(range.tail, itemTail); // After zwnj sectionElement = editor.post.sections.tail.renderNode.element; Helpers.dom.moveCursorTo(sectionElement.firstChild, 1); Helpers.dom.triggerLeftArrowKey(editor); range = editor.range; assert.positionIsEqual(range.head, itemTail); assert.positionIsEqual(range.tail, itemTail); });
test('deleting text across markers preserves node after', (assert) => { let mobiledoc = Helpers.mobiledoc.build( ({post, markupSection, marker, markup}) => { return post([ markupSection('p', [ marker('abc'), marker('123', [markup('strong')]), marker('def') ]) ]); }); editor = new Editor({mobiledoc}); editor.render(editorElement); const textNode1 = editorElement.childNodes[0].childNodes[0], textNode2 = editorElement.childNodes[0].childNodes[1]; assert.equal(textNode1.textContent, 'abc', 'precond -text node 1'); assert.equal(textNode2.textContent, '123', 'precond -text node 2'); Helpers.dom.selectText('b', editorElement, '2', editorElement); Helpers.dom.triggerDelete(editor); assert.equal( editorElement.childNodes[0].textContent, 'a3def', 'has remaining first section' ); Helpers.dom.insertText(editor, 'X'); assert.equal( editorElement.childNodes[0].textContent, 'aX3def', 'inserts text at correct spot'); });
test('typing enter splits lines, sets cursor', (assert) => { let done = assert.async(); let mobiledoc = Helpers.mobiledoc.build(({post, markupSection, marker}) => { return post([ markupSection('p', [ marker('hihey') ]) ]); }); editor = new Editor({mobiledoc}); editor.render(editorElement); assert.hasElement('#editor p'); Helpers.dom.moveCursorTo(editor, $('#editor p')[0].firstChild, 2); Helpers.dom.insertText(editor, ENTER); window.setTimeout(() => { let expectedPost = Helpers.postAbstract.build(({post, markupSection, marker}) => { return post([ markupSection('p', [ marker(`hi`) ]), markupSection('p', [ marker(`hey`) ]) ]); }); assert.postIsSimilar(editor.post, expectedPost, 'correctly encoded'); let expectedRange = new Range(new Position(editor.post.sections.tail, 0)); assert.ok(expectedRange.isEqual(editor.range), 'range is at start of new section'); done(); }, 0); });
test('keystroke of printable character while text is selected deletes the text', (assert) => { editor = new Editor({mobiledoc: mobileDocWith2Sections}); editor.render(editorElement); Helpers.dom.selectText('first section', editorElement); editor.run(postEditor => { editor.activeSections.forEach(section => { postEditor.changeSectionTagName(section, 'h2'); }); }); assert.ok($('#editor h2:contains(first section)').length, 'first section is a heading'); const firstSectionTextNode = editorElement.childNodes[0].childNodes[0]; const secondSectionTextNode = editorElement.childNodes[1].childNodes[0]; Helpers.dom.selectText('section', firstSectionTextNode, 'secon', secondSectionTextNode); Helpers.dom.insertText(editor, 'X'); assert.ok($(`#editor h2:contains(first Xd section)`).length, 'updates the section'); });
test('copy sets html & text for pasting externally', (assert) => { const mobiledoc = Helpers.mobiledoc.build( ({post, markupSection, marker}) => { return post([ markupSection('h1', [marker('h1 heading')]), markupSection('h2', [marker('h2 subheader')]), markupSection('p', [marker('The text')]) ]); }); editor = new Editor({mobiledoc}); editor.render(editorElement); Helpers.dom.selectText(editor, 'heading', editor.element, 'The text', editor.element); Helpers.dom.triggerCopyEvent(editor); let html = Helpers.dom.getCopyData(MIME_TEXT_HTML); let text = Helpers.dom.getCopyData(MIME_TEXT_PLAIN); assert.equal(text, ["heading", "h2 subheader", "The text" ].join('\n'), 'gets plain text'); assert.ok(html.indexOf("<h1>heading") !== -1, 'html has h1'); assert.ok(html.indexOf("<h2>h2 subheader") !== -1, 'html has h2'); assert.ok(html.indexOf("<p>The text") !== -1, 'html has p'); });
test('selecting text across markup and list sections', (assert) => { const build = Helpers.mobiledoc.build; const mobiledoc = build(({post, markupSection, listSection, listItem, marker}) => post([ markupSection('p', [marker('abc')]), listSection('ul', [ listItem([marker('123')]), listItem([marker('456')]) ]) ]) ); editor = new Editor({mobiledoc}); editor.render(editorElement); Helpers.dom.selectText('bc', editorElement, '12', editorElement); Helpers.dom.triggerDelete(editor); assert.hasElement('#editor p:contains(a3)', 'combines partially-selected list item onto markup section'); assert.hasNoElement('#editor p:contains(bc)', 'deletes selected text "bc"'); assert.hasNoElement('#editor p:contains(12)', 'deletes selected text "12"'); assert.hasElement('#editor li:contains(6)', 'leaves remaining text in list item'); });
test('right arrow at end of card moves cursor to next section', assert => { let mobiledoc = Helpers.mobiledoc.build(({post, markupSection, cardSection}) => { return post([ cardSection('my-card'), markupSection('p') ]); }); editor = new Editor({mobiledoc, cards}); editor.render(editorElement); let sectionHead = editor.post.sections.tail.headPosition(); // Before zwnj let sectionElement = editor.post.sections.head.renderNode.element; Helpers.dom.moveCursorTo(editor, sectionElement.lastChild, 0); Helpers.dom.triggerRightArrowKey(editor); let { range } = editor; assert.positionIsEqual(range.head, sectionHead); assert.positionIsEqual(range.tail, sectionHead); // After zwnj Helpers.dom.moveCursorTo(editor, sectionElement.lastChild, 1); Helpers.dom.triggerRightArrowKey(editor); range = editor.range; // On wrapper Helpers.dom.moveCursorTo(editor, editorElement.firstChild, 2); Helpers.dom.triggerRightArrowKey(editor); range = editor.range; assert.positionIsEqual(range.head, sectionHead); assert.positionIsEqual(range.tail, sectionHead); });
test('selecting text that starts in a list item and ends in a markup section', (assert) => { const build = Helpers.mobiledoc.build; const mobiledoc = build(({post, markupSection, listSection, listItem, marker}) => post([ listSection('ul', [ listItem([marker('123')]), listItem([marker('456')]) ]), markupSection('p', [marker('def')]) ]) ); editor = new Editor({mobiledoc}); editor.render(editorElement); Helpers.dom.selectText('23', editorElement, 'de', editorElement); Helpers.dom.triggerEvent(document, 'mouseup'); Helpers.dom.triggerDelete(editor); assert.hasElement('#editor li:contains(1f)', 'combines sides of selection'); assert.hasNoElement('#editor li:contains(123)', 'deletes li 1'); assert.hasNoElement('#editor li:contains(456)', 'deletes li 2'); assert.hasNoElement('#editor p:contains(def)', 'deletes p content'); assert.hasNoElement('#editor p', 'removes p entirely'); });
test('left arrow at start of card moves selection to prev section', assert => { let mobiledoc = Helpers.mobiledoc.build( ({post, markupSection, marker, cardSection}) => { return post([ markupSection('p', [marker('abc')]), cardSection('my-card') ]); }); editor = new Editor({mobiledoc, cards}); editor.render(editorElement); let cardHead = editor.post.sections.tail.headPosition(); let sectionTail = editor.post.sections.head.tailPosition(); // Before zwnj Helpers.dom.moveCursorTo(editor, editorElement.lastChild.firstChild, 0); Helpers.dom.triggerLeftArrowKey(editor, MODIFIERS.SHIFT); let { range } = editor; assert.positionIsEqual(range.head, sectionTail); assert.positionIsEqual(range.tail, cardHead); // After zwnj Helpers.dom.moveCursorTo(editor, editorElement.lastChild.firstChild, 1); Helpers.dom.triggerLeftArrowKey(editor, MODIFIERS.SHIFT); range = editor.range; assert.positionIsEqual(range.head, sectionTail); assert.positionIsEqual(range.tail, cardHead); });
test('selecting text that includes a card section and deleting deletes card section', (assert) => { const build = Helpers.mobiledoc.build; const mobiledoc = build(({post, markupSection, cardSection, marker}) => post([ markupSection('p', [marker('abc')]), cardSection('simple-card'), markupSection('p', [marker('def')]) ]) ); const cards = [{ name: 'simple-card', type: 'dom', render() { return $('<span id="card-el"></span>')[0]; } }]; editor = new Editor({mobiledoc, cards}); editor.render(editorElement); assert.hasElement('#card-el', 'precond - card el is rendered'); Helpers.dom.selectText('bc', editorElement, 'de', editorElement); Helpers.dom.triggerEvent(document, 'mouseup'); Helpers.dom.triggerDelete(editor); assert.hasElement('#editor p:contains(af)', 'combines sides of selection'); assert.hasNoElement('#editor span#card-el', 'card el is removed'); assert.hasNoElement('#editor p:contains(abc)', 'previous section 1 is removed'); assert.hasNoElement('#editor p:contains(def)', 'previous section 2 is removed'); });
test('right arrow at end of card moves to next list item', (assert) => { let mobiledoc = Helpers.mobiledoc.build( ({post, listSection, listItem, marker, cardSection}) => { return post([ cardSection('my-card'), listSection('ul', [listItem([marker('abc')])]) ]); }); editor = new Editor({mobiledoc, cards}); editor.render(editorElement); let cardTail = editor.post.sections.head.tailPosition(); let itemHead = editor.post.sections.tail.items.head.headPosition(); // Before zwnj Helpers.dom.moveCursorTo(editor, editorElement.firstChild.lastChild, 0); Helpers.dom.triggerRightArrowKey(editor, MODIFIERS.SHIFT); let { range } = editor; assert.positionIsEqual(range.head, cardTail); assert.positionIsEqual(range.tail, itemHead); // After zwnj Helpers.dom.moveCursorTo(editor, editorElement.firstChild.lastChild, 1); Helpers.dom.triggerRightArrowKey(editor, MODIFIERS.SHIFT); range = editor.range; assert.positionIsEqual(range.head, cardTail); assert.positionIsEqual(range.tail, itemHead); });
test('keystroke of delete at start of section joins with previous section', (assert) => { editor = new Editor({mobiledoc: mobileDocWith2Sections}); editor.render(editorElement); let secondSectionTextNode = editor.element.childNodes[1].firstChild; assert.equal(secondSectionTextNode.textContent, 'second section', 'precond - section section text node'); Helpers.dom.moveCursorTo(editor, secondSectionTextNode, 0); //editor.range = null; Helpers.dom.triggerDelete(editor); assert.equal(editor.element.childNodes.length, 1, 'only 1 section remaining'); let secondSectionNode = editor.element.firstChild; secondSectionTextNode = secondSectionNode.firstChild; assert.equal(secondSectionNode.textContent, 'first sectionsecond section', 'joins two sections'); Helpers.dom.insertText(editor, 'X'); assert.hasElement('#editor p:contains(first sectionXsecond section)', 'inserts text at correct spot'); });
test('marking atom with markup adds markup', (assert) => { assert.expect(1); let done = assert.async(); editor = new Editor({mobiledoc: mobiledocWithAtom, atoms: [simpleAtom]}); editor.render(editorElement); let pNode = $('#editor p')[0]; Helpers.dom.selectRange(pNode.firstChild, 16, pNode.lastChild, 0); Helpers.wait(() => { editor.run(postEditor => { let markup = editor.builder.createMarkup('strong'); postEditor.addMarkupToRange(editor.range, markup); }); assert.postIsSimilar(editor.post, Helpers.postAbstract.build( ({post, markupSection, atom, marker, markup}) => { return post([ markupSection('p', [ marker('text before atom'), atom('simple-atom', 'Bob', {}, [markup('strong')]), marker('text after atom') ]) ]); })); done(); }); });
test('removing last card from mobiledoc allows additional editing', (assert) => { const done = assert.async(); let button; const cards = [{ name: 'simple-card', type: 'dom', render({env}) { button = $('<button>Click me</button>'); button.on('click', env.remove); return button[0]; } }]; editor = new Editor({mobiledoc, cards}); editor.render(editorElement); assert.hasElement('#editor button:contains(Click me)', 'precond - button'); button.click(); setTimeout(() => { assert.hasNoElement('#editor button:contains(Click me)', 'button is removed'); assert.hasNoElement('#editor p'); Helpers.dom.moveCursorTo($('#editor')[0]); Helpers.dom.insertText(editor, 'X'); assert.hasElement('#editor p:contains(X)'); done(); }); });