editor: delete/insert at the edges of a span
[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 from the end of the preceding text element', function() {
413             var c = getCanvasFromXML('<section>Alice<span>has a cat</span></section>'),
414                 k = new Keyboard(c);
415
416             k.withCaret('|has a cat').press(K.BACKSPACE);
417
418             var rootContents = c.wlxmlDocument.root.contents();
419             expect(rootContents.length).to.equal(2);
420             expect(rootContents[0].getText()).to.equal('Alic');
421
422             var selection = c.getSelection();
423             expect(selection.type).to.equal('caret');
424             expect(selection.element.sameNode(getTextElement('has a cat', c))).to.equal(true);
425             expect(selection.offset).to.equal(0);
426         });
427
428         it('deletes from the end of the preceding text element - multiple spans', function() {
429             var c = getCanvasFromXML('<section>Alice<span><span>has a cat</span></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 span element content', function() {
445             var c = getCanvasFromXML('<section><span>Alice</span><span>has a cat</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].is({tagName: 'span'})).to.equal(true);
453             expect(rootContents[0].contents()[0].getText()).to.equal('Alic');
454
455             expect(rootContents[1].contents()[0].getText()).to.equal('has a cat');
456
457             var selection = c.getSelection();
458             expect(selection.type).to.equal('caret');
459             expect(selection.element.sameNode(getTextElement('has a cat', c))).to.equal(true);
460             expect(selection.offset).to.equal(0);
461         });
462
463         it('deletes from the end of the preceding span element content - multiple spans', function() {
464             var c = getCanvasFromXML('<section><span>Alice</span><span><span>has a cat</span></span></section>'),
465                 k = new Keyboard(c);
466
467             k.withCaret('|has a cat').press(K.BACKSPACE);
468
469             var rootContents = c.wlxmlDocument.root.contents();
470             expect(rootContents.length).to.equal(2);
471             expect(rootContents[0].is({tagName: 'span'})).to.equal(true);
472             expect(rootContents[0].contents()[0].getText()).to.equal('Alic');
473
474             var outerSpan = rootContents[1];
475             expect(outerSpan.is({tagName: 'span'})).to.equal(true);
476
477             var innerSpan = outerSpan.contents()[0];
478             expect(innerSpan.contents()[0].getText()).to.equal('has a cat');
479
480             var selection = c.getSelection();
481             expect(selection.type).to.equal('caret');
482             expect(selection.element.sameNode(getTextElement('has a cat', c))).to.equal(true);
483             expect(selection.offset).to.equal(0);
484         });
485
486         it('merges two paragrahps if span is a first content of the second paragraph', function() {
487             var c = getCanvasFromXML('<section><div class="p">para</div><div class="p"><span>Alice</span> has a cat</div></section>'),
488                 k = new Keyboard(c);
489             
490             k.withCaret('|Alice').press(K.BACKSPACE);
491
492             var rootContents = c.wlxmlDocument.root.contents();
493
494             expect(rootContents.length).to.equal(1, 'single paragraph left');
495
496             var p = rootContents[0],
497                 pContents = p.contents();
498
499             expect(p.is('p')).to.equal(true);
500
501             expect(pContents.length).to.equal(3);
502             expect(pContents[0].getText()).to.equal('para');
503             expect(pContents[1].contents().length).to.equal(1);
504             expect(pContents[1].contents()[0].getText()).to.equal('Alice');
505
506             expect(pContents[2].getText()).to.equal(' has a cat');
507
508             var selection = c.getSelection();
509             expect(selection.type).to.equal('caret');
510             expect(selection.element.sameNode(getTextElement('Alice', c))).to.equal(true);
511             expect(selection.offset).to.equal(0);
512         });
513     });
514
515     describe('backspace before a span', function() {
516         it('deletes from the end of a span', function() {
517             var c = getCanvasFromXML('<section><span>Alice</span>has a cat</section>'),
518                 k = new Keyboard(c);
519
520             k.withCaret('|has a cat').press(K.BACKSPACE);
521
522             var rootContents = c.wlxmlDocument.root.contents();
523             expect(rootContents.length).to.equal(2);
524             expect(rootContents[0].is({tagName: 'span'})).to.equal(true);
525             expect(rootContents[0].contents()[0].getText()).to.equal('Alic');
526             expect(rootContents[1].getText()).to.equal('has a cat');
527
528             var selection = c.getSelection();
529             expect(selection.type).to.equal('caret');
530             expect(selection.element.sameNode(getTextElement('has a cat', c))).to.equal(true);
531             expect(selection.offset).to.equal(0);
532         });
533         it('deletes span if it contains only one character', function() {
534             var c = getCanvasFromXML('<section>Alice <span>h</span> a cat</section>'),
535                 k = new Keyboard(c);
536
537             k.withCaret('| a cat').press(K.BACKSPACE);
538
539             var rootContents = c.wlxmlDocument.root.contents();
540             expect(rootContents.length).to.equal(1);
541             expect(rootContents[0].getText()).to.equal('Alice  a cat');
542
543             var selection = c.getSelection();
544             expect(selection.type).to.equal('caret');
545             expect(selection.element.sameNode(getTextElement('Alice  a cat', c))).to.equal(true);
546             expect(selection.offset).to.equal(6);
547         });
548     });
549
550     describe('splitting with enter', function() {
551         afterEach(removeCanvas);
552
553         it('splits paragraph into two in the middle', function() {
554             var c = getCanvasFromXML('<section><div class="p">paragraph</div></section>'),
555                 k = new Keyboard(c);
556
557             k.withCaret('para|graph').press(K.ENTER);
558
559             var rootContents = c.wlxmlDocument.root.contents();
560             expect(rootContents.length).to.equal(2);
561             expect(rootContents[0].is({tagName: 'div', klass: 'p'})).to.equal(true);
562             expect(rootContents[0].contents()[0].getText()).to.equal('para');
563             expect(rootContents[1].is({tagName: 'div', klass: 'p'})).to.equal(true);
564             expect(rootContents[1].contents()[0].getText()).to.equal('graph');
565
566             var selection = c.getSelection();
567             expect(selection.type).to.equal('caret');
568             expect(selection.element.sameNode(getTextElement('graph', c))).to.equal(true);
569             expect(selection.offset).to.equal(0);
570         });
571         it('splits paragraph into two at the beginning', function() {
572             var c = getCanvasFromXML('<section><div class="p">paragraph</div></section>'),
573                 k = new Keyboard(c);
574
575             k.withCaret('|paragraph').press(K.ENTER);
576
577             var rootContents = c.wlxmlDocument.root.contents();
578             expect(rootContents.length).to.equal(2);
579             expect(rootContents[0].is({tagName: 'div', klass: 'p'})).to.equal(true);
580             expect(rootContents[0].contents()[0].getText()).to.equal('');
581             expect(rootContents[1].is({tagName: 'div', klass: 'p'})).to.equal(true);
582             expect(rootContents[1].contents()[0].getText()).to.equal('paragraph');
583
584             var selection = c.getSelection();
585             expect(selection.type).to.equal('caret');
586             expect(selection.element.sameNode(getTextElement('', c))).to.equal(true);
587             expect(selection.offset).to.equal(0);
588         });
589         it('splits paragraph into two at the end', function() {
590             var c = getCanvasFromXML('<section><div class="p">paragraph</div></section>'),
591                 k = new Keyboard(c);
592
593             k.withCaret('paragraph|').press(K.ENTER);
594
595             var rootContents = c.wlxmlDocument.root.contents();
596             expect(rootContents.length).to.equal(2);
597             expect(rootContents[0].is({tagName: 'div', klass: 'p'})).to.equal(true);
598             expect(rootContents[0].contents()[0].getText()).to.equal('paragraph');
599             expect(rootContents[1].is({tagName: 'div', klass: 'p'})).to.equal(true);
600             expect(rootContents[1].contents()[0].getText()).to.equal('');
601
602             var selection = c.getSelection();
603             expect(selection.type).to.equal('caret');
604             expect(selection.element.sameNode(getTextElement('', c))).to.equal(true);
605             expect(selection.offset).to.equal(0);
606         });
607
608         it('splits its parent box if inside a span', function() {
609             var c = getCanvasFromXML('<section><div class="p">this <span>is</span> a paragraph</div></section>'),
610                 k = new Keyboard(c);
611
612             k.withCaret('i|s').press(K.ENTER);
613
614             var rootContents = c.wlxmlDocument.root.contents();
615
616             expect(rootContents.length).to.equal(2);
617
618             var p1 = rootContents[0],
619                 p2 = rootContents[1];
620
621             expect(p1.is({tagName: 'div', klass: 'p'})).to.equal(true);
622             expect(p2.is({tagName: 'div', klass: 'p'})).to.equal(true);
623
624             var p1Contents = p1.contents(),
625                 p2Contents = p2.contents();
626
627             expect(p1Contents[0].getText()).to.equal('this ');
628             expect(p1Contents[1].is({tagName: 'span'})).to.equal(true);
629             expect(p1Contents[1].contents()[0].getText()).to.equal('i');
630
631             
632             expect(p2Contents[0].is({tagName: 'span'})).to.equal(true);
633             expect(p2Contents[0].contents()[0].getText()).to.equal('s');
634             expect(p2Contents[1].getText()).to.equal(' a paragraph');
635
636             var selection = c.getSelection();
637             expect(selection.element.sameNode(getTextElement('s', c))).to.equal(true);
638             expect(selection.offset).to.equal(0);
639         });
640
641         it('splits its parent box if inside a double span', function() {
642             var c = getCanvasFromXML('<section><div class="p">this <span test="outer"><span test="inner">is</span></span> a paragraph</div></section>'),
643                 k = new Keyboard(c);
644
645             k.withCaret('i|s').press(K.ENTER);
646
647             var rootContents = c.wlxmlDocument.root.contents();
648
649             expect(rootContents.length).to.equal(2);
650
651             var p1 = rootContents[0],
652                 p2 = rootContents[1];
653
654             expect(p1.is({tagName: 'div', klass: 'p'})).to.equal(true);
655             expect(p2.is({tagName: 'div', klass: 'p'})).to.equal(true);
656
657             var p1Contents = p1.contents(),
658                 p2Contents = p2.contents();
659
660             /* first paragraph */
661             expect(p1Contents[0].getText()).to.equal('this ');
662             
663             var outer1 = p1Contents[1];
664             expect(outer1.getAttr('test')).to.equal('outer');
665             expect(outer1.contents().length).to.equal(1);
666             var inner1 = outer1.contents()[0];
667             expect(inner1.getAttr('test')).to.equal('inner');
668             expect(inner1.contents()[0].getText()).to.equal('i');
669
670             /* second paragraph */
671             var outer2 = p2Contents[0];
672             expect(outer2.getAttr('test')).to.equal('outer');
673             expect(outer2.contents().length).to.equal(1);
674             var inner2 = outer2.contents()[0];
675             expect(inner2.getAttr('test')).to.equal('inner');
676             expect(inner2.contents()[0].getText()).to.equal('s');
677
678             expect(p2Contents[1].getText()).to.equal(' a paragraph');
679
680             /* caret */
681             var selection = c.getSelection();
682             expect(selection.element.sameNode(getTextElement('s', c))).to.equal(true);
683             expect(selection.offset).to.equal(0);
684         });
685     });
686
687
688 });
689
690
691 });