+
+ describe('mergin text with preceding content', function() {
+ it('does nothing if text node parent has no preceding element', function() {
+ var doc = getDocumentFromXML('<section><div><div>some text</div></div></section>'),
+ text = getTextNode('some text', doc),
+ spy = sinon.spy();
+
+ doc.on('change', spy);
+ text.mergeContentUp();
+ expect(spy.callCount).to.equal(0);
+ });
+ it('does nothing if text node parent is precedeed by text node', function() {
+ var doc = getDocumentFromXML('<section><div></div>another text<div>some text</div></section>'),
+ text = getTextNode('some text', doc),
+ spy = sinon.spy();
+
+ doc.on('change', spy);
+ text.mergeContentUp();
+ expect(spy.callCount).to.equal(0);
+ });
+ it('does nothing if text node is not first child of its parent', function() {
+ var doc = getDocumentFromXML('<section><div></div><div><a></a>some text</div></section>'),
+ text = getTextNode('some text', doc),
+ spy = sinon.spy();
+
+ doc.on('change', spy);
+ text.mergeContentUp();
+ expect(spy.callCount).to.equal(0);
+ });
+ it('moves text node and its siblings to the block element preceding text node parent', function() {
+ var doc = getDocumentFromXML('<section><div></div><div>some text<span>is</span> here!</div></section>'),
+ text = getTextNode('some text', doc);
+
+ text.mergeContentUp();
+
+ var contents = doc.root.contents();
+ expect(contents.length).to.equal(1);
+ expect(contents[0].contents().length).to.equal(3);
+ expect(contents[0].contents()[0].getText()).to.equal('some text');
+ expect(contents[0].contents()[1].getTagName()).to.equal('span');
+ expect(contents[0].contents()[2].getText()).to.equal(' here!');
+ });
+ });
+});
+
+describe('Keyboard interactions', function() {
+
+ var Keyboard = function(canvas) {
+ this.canvas = canvas;
+ };
+
+ _.extend(Keyboard.prototype, {
+ press: function(key) {
+ this.canvas.triggerKeyEvent(keyEvent.fromParams({key:key}), this.selection);
+ this.selection = this.canvas.getSelection();
+ return this;
+ },
+ withCaret: function(where) {
+ var offset = where.indexOf('|'),
+ text = where.split('|').join(''),
+ el = getTextElement(text, this.canvas),
+ selection = this.canvas.createSelection({type: 'caret', element: el, offset: offset});
+ if(offset === -1) {
+ throw new Error('Invalid caret');
+ }
+ this.selection = selection;
+ return this;
+ },
+ withSelection: function(start, end) {
+ var startOffset = start.indexOf('|'),
+ endOffset = end.indexOf('|'),
+ startText= start.split('|').join(''),
+ endText = end.split('|').join(''),
+ startElement = getTextElement(startText, this.canvas),
+ endElement = getTextElement(endText, this.canvas),
+ selection = this.canvas.createSelection({
+ type: 'textSelection',
+ anchorElement: startElement,
+ anchorOffset: startOffset,
+ focusElement: endElement,
+ focusOffset: endOffset
+ });
+ if(startOffset === -1 || endOffset === -1) {
+ throw new Error('Invalid text selection');
+ }
+ this.selection = selection;
+ return this;
+ }
+ });
+
+ describe('deleting text with selection', function() {
+ afterEach(removeCanvas);
+
+ [K.BACKSPACE, K.DELETE].forEach(function(key) {
+ it('deletes text withing a single text element ' + key, function() {
+ var c = getCanvasFromXML('<section><div>Alice</div></section>'),
+ k = new Keyboard(c);
+
+ k.withSelection('A|lice', 'Alic|e').press(key);
+ expect(c.wlxmlDocument.root.contents()[0].contents()[0].getText()).to.equal('Ae');
+
+
+ var selection = c.getSelection();
+ expect(selection.type).to.equal('caret');
+ expect(selection.element.getText()).to.equal('Ae');
+ expect(selection.offset).to.equal(1);
+ });
+ it('deletes text across two paragraphs ' + key, function() {
+ var c = getCanvasFromXML('<section><div class="p">Alice</div><div class="p">cat</div></section>'),
+ k = new Keyboard(c);
+
+ k.withSelection('A|lice', 'c|at').press(key);
+ var rootContents = c.wlxmlDocument.root.contents();
+
+ expect(rootContents.length).to.equal(2);
+ expect(rootContents[0].contents()[0].getText()).to.equal('A');
+ expect(rootContents[1].contents()[0].getText()).to.equal('at');
+
+ var selection = c.getSelection();
+ expect(selection.type).to.equal('caret');
+ expect(selection.element.wlxmlNode.getText()).to.equal(key === K.BACKSPACE ? 'A' : 'at');
+ });
+
+ it('keeps an empty paragraph after deleting its whole text ' + key, function() {
+ var c = getCanvasFromXML('<section><div class="p">Alice</div></section>'),
+ k = new Keyboard(c);
+
+ k.withSelection('|Alice', 'Alice|').press(key);
+ var rootContents = c.wlxmlDocument.root.contents();
+
+ expect(rootContents.length).to.equal(1);
+ expect(rootContents[0].contents()[0].getText()).to.equal('');
+
+ var selection = c.getSelection();
+ expect(selection.type).to.equal('caret');
+ expect(selection.element.wlxmlNode.parent().sameNode(c.wlxmlDocument.root.contents()[0]));
+ });
+ });
+
+ });
+
+
+ describe('backspace at the beginning', function() {
+ afterEach(removeCanvas);
+
+ it('merges two adjacent paragraphs', function() {
+ var c = getCanvasFromXML('<section><div class="p">A</div><div class="p">B</div></section>'),
+ k = new Keyboard(c);
+
+ k.withCaret('|B').press(K.BACKSPACE);
+
+ var rootContents = c.wlxmlDocument.root.contents();
+ expect(rootContents.length).to.equal(1);
+ expect(rootContents[0].getClass()).to.equal('p');
+ expect(rootContents[0].contents()[0].getText()).to.equal('AB');
+
+ var selection = c.getSelection();
+ expect(selection.type).to.equal('caret');
+ expect(selection.element.sameNode(getTextElement('AB', c))).to.equal(true);
+ expect(selection.offset).to.equal(1);
+ });
+ it('merges a paragraph with a header', function() {
+ var c = getCanvasFromXML('<section><header>A</header><div class="p">B</div></section>'),
+ k = new Keyboard(c);
+
+ k.withCaret('|B').press(K.BACKSPACE);
+
+ var rootContents = c.wlxmlDocument.root.contents();
+ expect(rootContents.length).to.equal(1);
+ expect(rootContents[0].getTagName()).to.equal('header');
+ expect(rootContents[0].contents()[0].getText()).to.equal('AB');
+
+ var selection = c.getSelection();
+ expect(selection.type).to.equal('caret');
+ expect(selection.element.sameNode(getTextElement('AB', c))).to.equal(true);
+ expect(selection.offset).to.equal(1);
+ });
+ it('merges two adjacent headers', function() {
+ var c = getCanvasFromXML('<section><header>A</header><header>B</header></section>'),
+ k = new Keyboard(c);
+
+ k.withCaret('|B').press(K.BACKSPACE);
+ var rootContents = c.wlxmlDocument.root.contents();
+ expect(rootContents.length).to.equal(1);
+ expect(rootContents[0].getTagName()).to.equal('header');
+ expect(rootContents[0].contents()[0].getText()).to.equal('AB');
+
+ var selection = c.getSelection();
+ expect(selection.type).to.equal('caret');
+ expect(selection.element.sameNode(getTextElement('AB', c))).to.equal(true);
+ expect(selection.offset).to.equal(1);
+ });
+ it('merges a header with a paragraph', function() {
+ var c = getCanvasFromXML('<section><div class="p">A</div><header>B</header></section>'),
+ k = new Keyboard(c);
+
+ k.withCaret('|B').press(K.BACKSPACE);
+
+ var rootContents = c.wlxmlDocument.root.contents();
+ expect(rootContents.length).to.equal(1);
+ expect(rootContents[0].is('p')).to.equal(true);
+ expect(rootContents[0].contents()[0].getText()).to.equal('AB');
+
+ var selection = c.getSelection();
+ expect(selection.type).to.equal('caret');
+ expect(selection.element.sameNode(getTextElement('AB', c))).to.equal(true);
+ expect(selection.offset).to.equal(1);
+ });
+ it('merges a paragraph into a last list item', function() {
+ var c = getCanvasFromXML('<section><div class="list"><div class="item">item</div></div><div class="p">paragraph</div></section>'),
+ list = c.wlxmlDocument.root.contents()[0],
+ k = new Keyboard(c);
+
+ k.withCaret('|paragraph').press(K.BACKSPACE);
+
+ var rootContents = c.wlxmlDocument.root.contents();
+ expect(rootContents.length).to.equal(1);
+ expect(rootContents[0].sameNode(list)).to.equal(true);
+
+ var items = list.contents();
+ expect(items.length).to.equal(1);
+ expect(items[0].contents()[0].getText()).to.equal('itemparagraph');
+
+ var selection = c.getSelection();
+ expect(selection.type).to.equal('caret');
+ expect(selection.element.sameNode(getTextElement('itemparagraph', c))).to.equal(true);
+ expect(selection.offset).to.equal(4);
+ });
+ it('merges a list item with a list item', function() {
+ var c = getCanvasFromXML('<section><div class="list"><div class="item">item1</div><div class="item">item2</div></div></section>'),
+ list = c.wlxmlDocument.root.contents()[0],
+ k = new Keyboard(c);
+
+ k.withCaret('|item2').press(K.BACKSPACE);
+
+ var rootContents = c.wlxmlDocument.root.contents();
+ expect(rootContents.length).to.equal(1);
+
+ expect(rootContents[0].sameNode(list)).to.equal(true);
+
+ var items = list.contents();
+
+ expect(items.length).to.equal(1);
+ expect(items[0].contents()[0].getText()).to.equal('item1item2');
+
+ var selection = c.getSelection();
+ expect(selection.type).to.equal('caret');
+ expect(selection.element.sameNode(getTextElement('item1item2', c))).to.equal(true);
+ expect(selection.offset).to.equal(5);
+ });
+ it('creates a new paragraph preceding the list from a first list item', function() {
+ var c = getCanvasFromXML('<section><div class="list"><div class="item">item1</div><div class="item">item2</div></div></section>'),
+ list = c.wlxmlDocument.root.contents()[0],
+ k = new Keyboard(c);
+
+ k.withCaret('|item1').press(K.BACKSPACE);
+
+ var rootContents = c.wlxmlDocument.root.contents();
+ expect(rootContents.length).to.equal(2);
+
+ expect(rootContents[0].getClass()).to.equal('p');
+ expect(rootContents[0].contents()[0].getText()).to.equal('item1');
+
+ expect(rootContents[1].sameNode(list)).to.equal(true);
+
+ var selection = c.getSelection();
+ expect(selection.type).to.equal('caret');
+ expect(selection.element.sameNode(getTextElement('item1', c))).to.equal(true);
+ expect(selection.offset).to.equal(0);
+ });
+ it('removes list after moving up its only item', function() {
+ var c = getCanvasFromXML('<section><div class="list"><div class="item">item</div></div></section>'),
+ k = new Keyboard(c);
+
+ k.withCaret('|item').press(K.BACKSPACE);
+ var rootContents = c.wlxmlDocument.root.contents();
+ expect(rootContents.length).to.equal(1);
+
+ expect(rootContents[0].getClass()).to.equal('p');
+ expect(rootContents[0].contents()[0].getText()).to.equal('item');
+
+ var selection = c.getSelection();
+ expect(selection.type).to.equal('caret');
+ expect(selection.element.sameNode(getTextElement('item', c))).to.equal(true);
+ expect(selection.offset).to.equal(0);
+ });
+ });
+
+ describe('splitting with enter', function() {
+ afterEach(removeCanvas);
+
+ it('splits paragraph into two in the middle', function() {
+ var c = getCanvasFromXML('<section><div class="p">paragraph</div></section>'),
+ k = new Keyboard(c);
+
+ k.withCaret('para|graph').press(K.ENTER);
+
+ var rootContents = c.wlxmlDocument.root.contents();
+ expect(rootContents.length).to.equal(2);
+ expect(rootContents[0].is({tagName: 'div', klass: 'p'})).to.equal(true);
+ expect(rootContents[0].contents()[0].getText()).to.equal('para');
+ expect(rootContents[1].is({tagName: 'div', klass: 'p'})).to.equal(true);
+ expect(rootContents[1].contents()[0].getText()).to.equal('graph');
+
+ var selection = c.getSelection();
+ expect(selection.type).to.equal('caret');
+ expect(selection.element.sameNode(getTextElement('graph', c))).to.equal(true);
+ expect(selection.offset).to.equal(0);
+ });
+ it('splits paragraph into two at the beginning', function() {
+ var c = getCanvasFromXML('<section><div class="p">paragraph</div></section>'),
+ k = new Keyboard(c);
+
+ k.withCaret('|paragraph').press(K.ENTER);
+
+ var rootContents = c.wlxmlDocument.root.contents();
+ expect(rootContents.length).to.equal(2);
+ expect(rootContents[0].is({tagName: 'div', klass: 'p'})).to.equal(true);
+ expect(rootContents[0].contents()[0].getText()).to.equal('');
+ expect(rootContents[1].is({tagName: 'div', klass: 'p'})).to.equal(true);
+ expect(rootContents[1].contents()[0].getText()).to.equal('paragraph');
+
+ var selection = c.getSelection();
+ expect(selection.type).to.equal('caret');
+ expect(selection.element.sameNode(getTextElement('', c))).to.equal(true);
+ expect(selection.offset).to.equal(0);
+ });
+ it('splits paragraph into two at the end', function() {
+ var c = getCanvasFromXML('<section><div class="p">paragraph</div></section>'),
+ k = new Keyboard(c);
+
+ k.withCaret('paragraph|').press(K.ENTER);
+
+ var rootContents = c.wlxmlDocument.root.contents();
+ expect(rootContents.length).to.equal(2);
+ expect(rootContents[0].is({tagName: 'div', klass: 'p'})).to.equal(true);
+ expect(rootContents[0].contents()[0].getText()).to.equal('paragraph');
+ expect(rootContents[1].is({tagName: 'div', klass: 'p'})).to.equal(true);
+ expect(rootContents[1].contents()[0].getText()).to.equal('');
+
+ var selection = c.getSelection();
+ expect(selection.type).to.equal('caret');
+ expect(selection.element.sameNode(getTextElement('', c))).to.equal(true);
+ expect(selection.offset).to.equal(0);
+ });
+ });
+
+