editor: canvas - enter on an empty last list item creates paragraph after list
[fnpeditor.git] / src / editor / plugins / core / core.test.js
1 define(function(require) {
2     
3 'use strict';
4
5 /* globals describe, it, afterEach */
6
7 var $ = require('libs/jquery'),
8     _ = require('libs/underscore'),
9     chai = require('libs/chai'),
10     sinon = require('libs/sinon'),
11     wlxml = require('wlxml/wlxml'),
12     canvas = require('modules/documentCanvas/canvas/canvas'),
13     keyboard = require('modules/documentCanvas/canvas/keyboard'),
14     keyEvent = require('modules/documentCanvas/canvas/keyEvent'),
15     corePlugin = require('./core.js'),
16     expect = chai.expect;
17
18 var K = keyboard.KEYS;
19
20 var getDocumentFromXML = function(xml, options) {
21     var doc = wlxml.WLXMLDocumentFromXML(xml, options || {});
22     doc.registerExtension(corePlugin.documentExtension);
23     return doc;
24 };
25
26
27 var getCanvasFromXML = function(xml, elements) {
28     var c = canvas.fromXMLDocument(getDocumentFromXML(xml), elements),
29         view = c.view();
30     view.attr('canvas-test', true);
31     /* globals document */
32     $(document.body).append(view);
33     return c;
34 };
35 var removeCanvas = function() {
36     $('[canvas-test]').remove();
37 };
38
39 var getTextNodes = function(text, doc) {
40     /* globals Node */
41     var toret = [];
42     var search = function(node) {
43         node.contents().forEach(function(node) {
44             if(node.nodeType === Node.TEXT_NODE) {
45                 if(node.getText() === text) {
46                     toret.push(node);
47                 }
48             } else {
49                 search(node);
50             }
51         });
52     };
53     search(doc.root);
54     return toret;
55 };
56
57 var getTextNode = function(text, doc) {
58     var nodes = getTextNodes(text, doc),
59         error;
60     if(nodes.length === 0) {
61         error = 'Text not found';
62     } else if(nodes.length > 1) {
63         error = 'Text not unique';
64     } else if(nodes[0].getText() !== text) {
65         error = 'I was trying to cheat your test :(';
66     }
67     if(error) {
68         throw new Error(error);
69     }
70     return nodes[0];
71 };
72
73 var getTextElement = function(text, c) {
74     var node = getTextNode(text, c.wlxmlDocument),
75         element =  node && node.getData('canvasElement');
76     if(!(element && element.getText() === text)) {
77         throw new Error();
78     }
79     return element;
80 };
81
82
83 describe('Document extensions', function() {
84     describe('break content', function() {
85         it('break text into two nodes', function() {
86             var doc = getDocumentFromXML('<section><div>Alice</div></section>'),
87                 textNode = doc.root.contents()[0].contents()[0];
88             
89             var result = textNode.breakContent({offset:3});
90
91             var section = doc.root;
92             expect(section.contents().length).to.equal(2);
93             expect(section.contents()[0].contents()[0].getText()).to.equal('Ali');
94             expect(section.contents()[1].contents()[0].getText()).to.equal('ce');
95
96             expect(result.first.sameNode(section.contents()[0])).to.equal(true);
97             expect(result.second.sameNode(section.contents()[1])).to.equal(true);
98             expect(result.emptyText).to.equal(undefined, 'no new text node created');
99         });
100         it('puts empty text node when breaking at the very beginning', function() {
101             var doc = getDocumentFromXML('<section><div>Alice</div></section>'),
102                 textNode = doc.root.contents()[0].contents()[0];
103             
104             var result = textNode.breakContent({offset:0}),
105                 firstNode = doc.root.contents()[0];
106
107             expect(result.emptyText.sameNode(firstNode.contents()[0]));
108             expect(result.emptyText.getText()).to.equal('');
109         });
110         it('puts empty text node when breaking at the very end', function() {
111             var doc = getDocumentFromXML('<section><div>Alice</div></section>'),
112             textNode = doc.root.contents()[0].contents()[0];
113             
114             var result = textNode.breakContent({offset:5}),
115                 secondNode = doc.root.contents()[1];
116             
117             expect(result.emptyText.sameNode(secondNode.contents()[0]));
118             expect(result.emptyText.getText()).to.equal('');
119         });
120     });
121
122     describe('mergin text with preceding content', function() {
123         it('does nothing if text node parent has no preceding element', function() {
124             var doc = getDocumentFromXML('<section><div><div>some text</div></div></section>'),
125                 text = getTextNode('some text', doc),
126                 spy = sinon.spy();
127
128             doc.on('change', spy);
129             text.mergeContentUp();
130             expect(spy.callCount).to.equal(0);
131         });
132         it('does nothing if text node parent is precedeed by text node', function() {
133             var doc = getDocumentFromXML('<section><div></div>another text<div>some text</div></section>'),
134                 text = getTextNode('some text', doc),
135                 spy = sinon.spy();
136
137             doc.on('change', spy);
138             text.mergeContentUp();
139             expect(spy.callCount).to.equal(0);
140         });
141         it('does nothing if text node is not first child of its parent', function() {
142             var doc = getDocumentFromXML('<section><div></div><div><a></a>some text</div></section>'),
143                 text = getTextNode('some text', doc),
144                 spy = sinon.spy();
145
146             doc.on('change', spy);
147             text.mergeContentUp();
148             expect(spy.callCount).to.equal(0);
149         });
150         it('moves text node and its siblings to the block element preceding text node parent', function() {
151             var doc = getDocumentFromXML('<section><div></div><div>some text<span>is</span> here!</div></section>'),
152                 text = getTextNode('some text', doc);
153             
154             text.mergeContentUp();
155             
156             var contents = doc.root.contents();
157             expect(contents.length).to.equal(1);
158             expect(contents[0].contents().length).to.equal(3);
159             expect(contents[0].contents()[0].getText()).to.equal('some text');
160             expect(contents[0].contents()[1].getTagName()).to.equal('span');
161             expect(contents[0].contents()[2].getText()).to.equal(' here!');
162         });
163     });
164 });
165
166 describe('Keyboard interactions', function() {
167
168     var Keyboard = function(canvas) {
169         this.canvas = canvas;
170     };
171
172     _.extend(Keyboard.prototype, {
173         press: function(key) {
174             this.canvas.triggerKeyEvent(keyEvent.fromParams({key:key}), this.selection);
175             this.selection = this.canvas.getSelection();
176             return this;
177         },
178         withCaret: function(where) {
179             var offset = where.indexOf('|'),
180                 text = where.split('|').join(''),
181                 el = getTextElement(text, this.canvas),
182                 selection = this.canvas.createSelection({type: 'caret', element: el, offset: offset});
183             if(offset === -1) {
184                 throw new Error('Invalid caret');
185             }
186             this.selection = selection;
187             return this;
188         },
189         withSelection: function(start, end) {
190             var startOffset = start.indexOf('|'),
191                 endOffset = end.indexOf('|'),
192                 startText= start.split('|').join(''),
193                 endText = end.split('|').join(''),
194                 startElement = getTextElement(startText, this.canvas),
195                 endElement = getTextElement(endText, this.canvas),
196                 selection = this.canvas.createSelection({
197                     type: 'textSelection', 
198                     anchorElement: startElement,
199                     anchorOffset: startOffset,
200                     focusElement: endElement,
201                     focusOffset: endOffset
202                 });
203             if(startOffset === -1 || endOffset === -1) {
204                 throw new Error('Invalid text selection');
205             }
206             this.selection = selection;
207             return this;    
208         }
209     });
210
211     describe('deleting text with selection', function() {
212         afterEach(removeCanvas);
213
214         [K.BACKSPACE, K.DELETE].forEach(function(key) {
215             it('deletes text withing a single text element ' + key, function() {
216                 var c = getCanvasFromXML('<section><div>Alice</div></section>'),
217                     k = new Keyboard(c);
218
219                 k.withSelection('A|lice', 'Alic|e').press(key);
220                 expect(c.wlxmlDocument.root.contents()[0].contents()[0].getText()).to.equal('Ae');
221
222
223                 var selection = c.getSelection();
224                 expect(selection.type).to.equal('caret');
225                 expect(selection.element.getText()).to.equal('Ae');
226                 expect(selection.offset).to.equal(1);
227             });
228             it('deletes text across two paragraphs ' + key, function() {
229                 var c = getCanvasFromXML('<section><div class="p">Alice</div><div class="p">cat</div></section>'),
230                     k = new Keyboard(c);
231
232                 k.withSelection('A|lice', 'c|at').press(key);
233                 var rootContents = c.wlxmlDocument.root.contents();
234
235                 expect(rootContents.length).to.equal(2);
236                 expect(rootContents[0].contents()[0].getText()).to.equal('A');
237                 expect(rootContents[1].contents()[0].getText()).to.equal('at');
238
239                 var selection = c.getSelection();
240                 expect(selection.type).to.equal('caret');
241                 expect(selection.element.wlxmlNode.getText()).to.equal(key === K.BACKSPACE ? 'A' : 'at');
242             });
243
244             it('keeps an empty paragraph after deleting its whole text ' + key, function() {
245                 var c = getCanvasFromXML('<section><div class="p">Alice</div></section>'),
246                     k = new Keyboard(c);
247
248                 k.withSelection('|Alice', 'Alice|').press(key);
249                 var rootContents = c.wlxmlDocument.root.contents();
250
251                 expect(rootContents.length).to.equal(1);
252                 expect(rootContents[0].contents()[0].getText()).to.equal('');
253                 
254                 var selection = c.getSelection();
255                 expect(selection.type).to.equal('caret');
256                 expect(selection.element.wlxmlNode.parent().sameNode(c.wlxmlDocument.root.contents()[0]));
257             });
258         });
259
260     });
261
262
263     describe('backspace at the beginning of a block', function() {
264         afterEach(removeCanvas);
265
266         it('merges two adjacent paragraphs', function() {
267             var c = getCanvasFromXML('<section><div class="p">A</div><div class="p">B</div></section>'),
268                 k = new Keyboard(c);
269
270             k.withCaret('|B').press(K.BACKSPACE);
271
272             var rootContents = c.wlxmlDocument.root.contents();
273             expect(rootContents.length).to.equal(1);
274             expect(rootContents[0].getClass()).to.equal('p');
275             expect(rootContents[0].contents()[0].getText()).to.equal('AB');
276
277             var selection = c.getSelection();
278             expect(selection.type).to.equal('caret');
279             expect(selection.element.sameNode(getTextElement('AB', c))).to.equal(true);
280             expect(selection.offset).to.equal(1);
281         });
282         it('merges a paragraph with a header', function() {
283             var c = getCanvasFromXML('<section><header>A</header><div class="p">B</div></section>'),
284                 k = new Keyboard(c);
285
286             k.withCaret('|B').press(K.BACKSPACE);
287
288             var rootContents = c.wlxmlDocument.root.contents();
289             expect(rootContents.length).to.equal(1);
290             expect(rootContents[0].getTagName()).to.equal('header');
291             expect(rootContents[0].contents()[0].getText()).to.equal('AB');
292
293             var selection = c.getSelection();
294             expect(selection.type).to.equal('caret');
295             expect(selection.element.sameNode(getTextElement('AB', c))).to.equal(true);
296             expect(selection.offset).to.equal(1);
297         });
298         it('merges two adjacent headers', function() {
299             var c = getCanvasFromXML('<section><header>A</header><header>B</header></section>'),
300                 k = new Keyboard(c);
301
302             k.withCaret('|B').press(K.BACKSPACE);
303             var rootContents = c.wlxmlDocument.root.contents();
304             expect(rootContents.length).to.equal(1);
305             expect(rootContents[0].getTagName()).to.equal('header');
306             expect(rootContents[0].contents()[0].getText()).to.equal('AB');
307
308             var selection = c.getSelection();
309             expect(selection.type).to.equal('caret');
310             expect(selection.element.sameNode(getTextElement('AB', c))).to.equal(true);
311             expect(selection.offset).to.equal(1);
312         });
313         it('merges a header with a paragraph', function() {
314             var c = getCanvasFromXML('<section><div class="p">A</div><header>B</header></section>'),
315                 k = new Keyboard(c);
316
317             k.withCaret('|B').press(K.BACKSPACE);
318
319             var rootContents = c.wlxmlDocument.root.contents();
320             expect(rootContents.length).to.equal(1);
321             expect(rootContents[0].is('p')).to.equal(true);
322             expect(rootContents[0].contents()[0].getText()).to.equal('AB');
323
324             var selection = c.getSelection();
325             expect(selection.type).to.equal('caret');
326             expect(selection.element.sameNode(getTextElement('AB', c))).to.equal(true);
327             expect(selection.offset).to.equal(1);
328         });
329         it('merges a paragraph into a last list item', function() {
330             var c = getCanvasFromXML('<section><div class="list"><div class="item">item</div></div><div class="p">paragraph</div></section>'),
331                 list = c.wlxmlDocument.root.contents()[0],
332                 k = new Keyboard(c);
333
334             k.withCaret('|paragraph').press(K.BACKSPACE);
335
336             var rootContents = c.wlxmlDocument.root.contents();
337             expect(rootContents.length).to.equal(1);
338             expect(rootContents[0].sameNode(list)).to.equal(true);
339
340             var items = list.contents();
341             expect(items.length).to.equal(1);
342             expect(items[0].contents()[0].getText()).to.equal('itemparagraph');
343
344             var selection = c.getSelection();
345             expect(selection.type).to.equal('caret');
346             expect(selection.element.sameNode(getTextElement('itemparagraph', c))).to.equal(true);
347             expect(selection.offset).to.equal(4);
348         });
349         it('merges a list item with a list item', function() {
350             var c = getCanvasFromXML('<section><div class="list"><div class="item">item1</div><div class="item">item2</div></div></section>'),
351                 list = c.wlxmlDocument.root.contents()[0],
352                 k = new Keyboard(c);
353
354             k.withCaret('|item2').press(K.BACKSPACE);
355
356             var rootContents = c.wlxmlDocument.root.contents();
357             expect(rootContents.length).to.equal(1);
358             
359             expect(rootContents[0].sameNode(list)).to.equal(true);
360
361             var items = list.contents();
362
363             expect(items.length).to.equal(1);
364             expect(items[0].contents()[0].getText()).to.equal('item1item2');
365
366             var selection = c.getSelection();
367             expect(selection.type).to.equal('caret');
368             expect(selection.element.sameNode(getTextElement('item1item2', c))).to.equal(true);
369             expect(selection.offset).to.equal(5);
370         });
371         it('creates a new paragraph preceding the list from a first list item', function() {
372             var c = getCanvasFromXML('<section><div class="list"><div class="item">item1</div><div class="item">item2</div></div></section>'),
373                 list = c.wlxmlDocument.root.contents()[0],
374                 k = new Keyboard(c);
375
376             k.withCaret('|item1').press(K.BACKSPACE);
377
378             var rootContents = c.wlxmlDocument.root.contents();
379             expect(rootContents.length).to.equal(2);
380             
381             expect(rootContents[0].getClass()).to.equal('p');
382             expect(rootContents[0].contents()[0].getText()).to.equal('item1');
383
384             expect(rootContents[1].sameNode(list)).to.equal(true);
385
386             var selection = c.getSelection();
387             expect(selection.type).to.equal('caret');
388             expect(selection.element.sameNode(getTextElement('item1', c))).to.equal(true);
389             expect(selection.offset).to.equal(0);
390         });
391         it('removes list after moving up its only item', function() {
392             var c = getCanvasFromXML('<section><div class="list"><div class="item">item</div></div></section>'),
393                 k = new Keyboard(c);
394
395             k.withCaret('|item').press(K.BACKSPACE);
396             var rootContents = c.wlxmlDocument.root.contents();
397             expect(rootContents.length).to.equal(1);
398             
399             expect(rootContents[0].getClass()).to.equal('p');
400             expect(rootContents[0].contents()[0].getText()).to.equal('item');
401
402             var selection = c.getSelection();
403             expect(selection.type).to.equal('caret');
404             expect(selection.element.sameNode(getTextElement('item', c))).to.equal(true);
405             expect(selection.offset).to.equal(0);
406         });
407     });
408
409     describe('backspace at the beginning of a span', function() {
410         afterEach(removeCanvas);
411
412         it('deletes span if it contains only one character', function() {
413             var c = getCanvasFromXML('<section>Alice<span class="emp">h</span>a cat</section>'),
414                 k = new Keyboard(c);
415
416             k.withCaret('h|').press(K.BACKSPACE);
417
418             var rootContents = c.wlxmlDocument.root.contents();
419             expect(rootContents.length).to.equal(1);
420             expect(rootContents[0].getText()).to.equal('Alicea cat');
421
422             var selection = c.getSelection();
423             expect(selection.type).to.equal('caret');
424             expect(selection.element.sameNode(getTextElement('Alicea cat', c))).to.equal(true);
425             expect(selection.offset).to.equal(5);
426         });
427
428         it('deletes from the end of the preceding text element', function() {
429             var c = getCanvasFromXML('<section>Alice<span>has a cat</span></section>'),
430                 k = new Keyboard(c);
431
432             k.withCaret('|has a cat').press(K.BACKSPACE);
433
434             var rootContents = c.wlxmlDocument.root.contents();
435             expect(rootContents.length).to.equal(2);
436             expect(rootContents[0].getText()).to.equal('Alic');
437
438             var selection = c.getSelection();
439             expect(selection.type).to.equal('caret');
440             expect(selection.element.sameNode(getTextElement('has a cat', c))).to.equal(true);
441             expect(selection.offset).to.equal(0);
442         });
443
444         it('deletes from the end of the preceding text element - multiple spans', function() {
445             var c = getCanvasFromXML('<section>Alice<span><span>has a cat</span></span></section>'),
446                 k = new Keyboard(c);
447
448             k.withCaret('|has a cat').press(K.BACKSPACE);
449
450             var rootContents = c.wlxmlDocument.root.contents();
451             expect(rootContents.length).to.equal(2);
452             expect(rootContents[0].getText()).to.equal('Alic');
453
454             var selection = c.getSelection();
455             expect(selection.type).to.equal('caret');
456             expect(selection.element.sameNode(getTextElement('has a cat', c))).to.equal(true);
457             expect(selection.offset).to.equal(0);
458         });
459
460         it('deletes from the end of the preceding span element content', function() {
461             var c = getCanvasFromXML('<section><span>Alice</span><span>has a cat</span></section>'),
462                 k = new Keyboard(c);
463
464             k.withCaret('|has a cat').press(K.BACKSPACE);
465
466             var rootContents = c.wlxmlDocument.root.contents();
467             expect(rootContents.length).to.equal(2);
468             expect(rootContents[0].is({tagName: 'span'})).to.equal(true);
469             expect(rootContents[0].contents()[0].getText()).to.equal('Alic');
470
471             expect(rootContents[1].contents()[0].getText()).to.equal('has a cat');
472
473             var selection = c.getSelection();
474             expect(selection.type).to.equal('caret');
475             expect(selection.element.sameNode(getTextElement('has a cat', c))).to.equal(true);
476             expect(selection.offset).to.equal(0);
477         });
478
479         it('deletes from the end of the preceding span element content - multiple spans', function() {
480             var c = getCanvasFromXML('<section><span>Alice</span><span><span>has a cat</span></span></section>'),
481                 k = new Keyboard(c);
482
483             k.withCaret('|has a cat').press(K.BACKSPACE);
484
485             var rootContents = c.wlxmlDocument.root.contents();
486             expect(rootContents.length).to.equal(2);
487             expect(rootContents[0].is({tagName: 'span'})).to.equal(true);
488             expect(rootContents[0].contents()[0].getText()).to.equal('Alic');
489
490             var outerSpan = rootContents[1];
491             expect(outerSpan.is({tagName: 'span'})).to.equal(true);
492
493             var innerSpan = outerSpan.contents()[0];
494             expect(innerSpan.contents()[0].getText()).to.equal('has a cat');
495
496             var selection = c.getSelection();
497             expect(selection.type).to.equal('caret');
498             expect(selection.element.sameNode(getTextElement('has a cat', c))).to.equal(true);
499             expect(selection.offset).to.equal(0);
500         });
501
502         it('merges two paragrahps if span is a first content of the second paragraph', function() {
503             var c = getCanvasFromXML('<section><div class="p">para</div><div class="p"><span>Alice</span> has a cat</div></section>'),
504                 k = new Keyboard(c);
505             
506             k.withCaret('|Alice').press(K.BACKSPACE);
507
508             var rootContents = c.wlxmlDocument.root.contents();
509
510             expect(rootContents.length).to.equal(1, 'single paragraph left');
511
512             var p = rootContents[0],
513                 pContents = p.contents();
514
515             expect(p.is('p')).to.equal(true);
516
517             expect(pContents.length).to.equal(3);
518             expect(pContents[0].getText()).to.equal('para');
519             expect(pContents[1].contents().length).to.equal(1);
520             expect(pContents[1].contents()[0].getText()).to.equal('Alice');
521
522             expect(pContents[2].getText()).to.equal(' has a cat');
523
524             var selection = c.getSelection();
525             expect(selection.type).to.equal('caret');
526             expect(selection.element.sameNode(getTextElement('Alice', c))).to.equal(true);
527             expect(selection.offset).to.equal(0);
528         });
529     });
530
531     describe('backspace before a span', function() {
532         it('deletes from the end of a span', function() {
533             var c = getCanvasFromXML('<section><span>Alice</span>has a cat</section>'),
534                 k = new Keyboard(c);
535
536             k.withCaret('|has a cat').press(K.BACKSPACE);
537
538             var rootContents = c.wlxmlDocument.root.contents();
539             expect(rootContents.length).to.equal(2);
540             expect(rootContents[0].is({tagName: 'span'})).to.equal(true);
541             expect(rootContents[0].contents()[0].getText()).to.equal('Alic');
542             expect(rootContents[1].getText()).to.equal('has a cat');
543
544             var selection = c.getSelection();
545             expect(selection.type).to.equal('caret');
546             expect(selection.element.sameNode(getTextElement('has a cat', c))).to.equal(true);
547             expect(selection.offset).to.equal(0);
548         });
549         it('deletes span if it contains only one character', function() {
550             var c = getCanvasFromXML('<section>Alice <span>h</span> a cat</section>'),
551                 k = new Keyboard(c);
552
553             k.withCaret('| a cat').press(K.BACKSPACE);
554
555             var rootContents = c.wlxmlDocument.root.contents();
556             expect(rootContents.length).to.equal(1);
557             expect(rootContents[0].getText()).to.equal('Alice  a cat');
558
559             var selection = c.getSelection();
560             expect(selection.type).to.equal('caret');
561             expect(selection.element.sameNode(getTextElement('Alice  a cat', c))).to.equal(true);
562             expect(selection.offset).to.equal(6);
563         });
564     });
565
566     describe('splitting with enter', function() {
567         afterEach(removeCanvas);
568
569         it('splits paragraph into two in the middle', function() {
570             var c = getCanvasFromXML('<section><div class="p">paragraph</div></section>'),
571                 k = new Keyboard(c);
572
573             k.withCaret('para|graph').press(K.ENTER);
574
575             var rootContents = c.wlxmlDocument.root.contents();
576             expect(rootContents.length).to.equal(2);
577             expect(rootContents[0].is({tagName: 'div', klass: 'p'})).to.equal(true);
578             expect(rootContents[0].contents()[0].getText()).to.equal('para');
579             expect(rootContents[1].is({tagName: 'div', klass: 'p'})).to.equal(true);
580             expect(rootContents[1].contents()[0].getText()).to.equal('graph');
581
582             var selection = c.getSelection();
583             expect(selection.type).to.equal('caret');
584             expect(selection.element.sameNode(getTextElement('graph', c))).to.equal(true);
585             expect(selection.offset).to.equal(0);
586         });
587         it('splits paragraph into two at the beginning', function() {
588             var c = getCanvasFromXML('<section><div class="p">paragraph</div></section>'),
589                 k = new Keyboard(c);
590
591             k.withCaret('|paragraph').press(K.ENTER);
592
593             var rootContents = c.wlxmlDocument.root.contents();
594             expect(rootContents.length).to.equal(2);
595             expect(rootContents[0].is({tagName: 'div', klass: 'p'})).to.equal(true);
596             expect(rootContents[0].contents()[0].getText()).to.equal('');
597             expect(rootContents[1].is({tagName: 'div', klass: 'p'})).to.equal(true);
598             expect(rootContents[1].contents()[0].getText()).to.equal('paragraph');
599
600             var selection = c.getSelection();
601             expect(selection.type).to.equal('caret');
602             expect(selection.element.sameNode(getTextElement('', c))).to.equal(true);
603             expect(selection.offset).to.equal(0);
604         });
605         it('splits paragraph into two at the end', function() {
606             var c = getCanvasFromXML('<section><div class="p">paragraph</div></section>'),
607                 k = new Keyboard(c);
608
609             k.withCaret('paragraph|').press(K.ENTER);
610
611             var rootContents = c.wlxmlDocument.root.contents();
612             expect(rootContents.length).to.equal(2);
613             expect(rootContents[0].is({tagName: 'div', klass: 'p'})).to.equal(true);
614             expect(rootContents[0].contents()[0].getText()).to.equal('paragraph');
615             expect(rootContents[1].is({tagName: 'div', klass: 'p'})).to.equal(true);
616             expect(rootContents[1].contents()[0].getText()).to.equal('');
617
618             var selection = c.getSelection();
619             expect(selection.type).to.equal('caret');
620             expect(selection.element.sameNode(getTextElement('', c))).to.equal(true);
621             expect(selection.offset).to.equal(0);
622         });
623
624         it('splits its parent box if inside a span', function() {
625             var c = getCanvasFromXML('<section><div class="p">this <span>is</span> a paragraph</div></section>'),
626                 k = new Keyboard(c);
627
628             k.withCaret('i|s').press(K.ENTER);
629
630             var rootContents = c.wlxmlDocument.root.contents();
631
632             expect(rootContents.length).to.equal(2);
633
634             var p1 = rootContents[0],
635                 p2 = rootContents[1];
636
637             expect(p1.is({tagName: 'div', klass: 'p'})).to.equal(true);
638             expect(p2.is({tagName: 'div', klass: 'p'})).to.equal(true);
639
640             var p1Contents = p1.contents(),
641                 p2Contents = p2.contents();
642
643             expect(p1Contents[0].getText()).to.equal('this ');
644             expect(p1Contents[1].is({tagName: 'span'})).to.equal(true);
645             expect(p1Contents[1].contents()[0].getText()).to.equal('i');
646
647             
648             expect(p2Contents[0].is({tagName: 'span'})).to.equal(true);
649             expect(p2Contents[0].contents()[0].getText()).to.equal('s');
650             expect(p2Contents[1].getText()).to.equal(' a paragraph');
651
652             var selection = c.getSelection();
653             expect(selection.element.sameNode(getTextElement('s', c))).to.equal(true);
654             expect(selection.offset).to.equal(0);
655         });
656
657         it('splits its parent box if inside a double span', function() {
658             var c = getCanvasFromXML('<section><div class="p">this <span test="outer"><span test="inner">is</span></span> a paragraph</div></section>'),
659                 k = new Keyboard(c);
660
661             k.withCaret('i|s').press(K.ENTER);
662
663             var rootContents = c.wlxmlDocument.root.contents();
664
665             expect(rootContents.length).to.equal(2);
666
667             var p1 = rootContents[0],
668                 p2 = rootContents[1];
669
670             expect(p1.is({tagName: 'div', klass: 'p'})).to.equal(true);
671             expect(p2.is({tagName: 'div', klass: 'p'})).to.equal(true);
672
673             var p1Contents = p1.contents(),
674                 p2Contents = p2.contents();
675
676             /* first paragraph */
677             expect(p1Contents[0].getText()).to.equal('this ');
678             
679             var outer1 = p1Contents[1];
680             expect(outer1.getAttr('test')).to.equal('outer');
681             expect(outer1.contents().length).to.equal(1);
682             var inner1 = outer1.contents()[0];
683             expect(inner1.getAttr('test')).to.equal('inner');
684             expect(inner1.contents()[0].getText()).to.equal('i');
685
686             /* second paragraph */
687             var outer2 = p2Contents[0];
688             expect(outer2.getAttr('test')).to.equal('outer');
689             expect(outer2.contents().length).to.equal(1);
690             var inner2 = outer2.contents()[0];
691             expect(inner2.getAttr('test')).to.equal('inner');
692             expect(inner2.contents()[0].getText()).to.equal('s');
693
694             expect(p2Contents[1].getText()).to.equal(' a paragraph');
695
696             /* caret */
697             var selection = c.getSelection();
698             expect(selection.element.sameNode(getTextElement('s', c))).to.equal(true);
699             expect(selection.offset).to.equal(0);
700         });
701     });
702
703     describe('Enter on a list items', function() {
704         afterEach(removeCanvas);
705
706         it('creates a paragraph after a list if hitting enter on the last and empty list item', function() {
707             var c = getCanvasFromXML('<section><div class="list"><div class="item">item</div></div></section>'),
708                 k = new Keyboard(c);
709
710             k.withCaret('item|').press(K.ENTER).press(K.ENTER);
711             
712             var rootContents = c.wlxmlDocument.root.contents();
713             expect(rootContents.length).to.equal(2);
714             expect(rootContents[0].is('list')).to.equal(true);
715             expect(rootContents[1].is('p')).to.equal(true);
716
717             var list = rootContents[0];
718             expect(list.contents().length).to.equal(1);
719             
720             var selection = c.getSelection();
721             expect(selection.element.wlxmlNode.sameNode(rootContents[1].contents()[0])).to.equal(true);
722             expect(selection.offset).to.equal(0);
723         });
724     });
725
726     describe('Deleting text from a node', function() {
727         it('deletes last character with backspace', function() {
728             var c = getCanvasFromXML('<section><div class="p">a</div><div class="p">b</div></section>'),
729                 k = new Keyboard(c);
730
731             k.withCaret('b|').press(K.BACKSPACE);
732
733             var rootContents = c.wlxmlDocument.root.contents();
734             expect(rootContents.length).to.equal(2);
735             expect(rootContents[0].is({tagName: 'div', klass: 'p'})).to.equal(true);
736             expect(rootContents[0].contents()[0].getText()).to.equal('a');
737             expect(rootContents[1].is({tagName: 'div', klass: 'p'})).to.equal(true);
738             expect(rootContents[1].contents()[0].getText()).to.equal('');
739
740             var selection = c.getSelection();
741             expect(selection.type).to.equal('caret');
742             expect(selection.element.sameNode(getTextElement('', c))).to.equal(true);
743             expect(selection.offset).to.equal(0);
744         });
745     });
746
747 });
748
749
750 });