1 define(function(require) {
4 /* globals describe, it */
6 var _ = require('libs/underscore'),
7 chai = require('libs/chai'),
8 sinon = require('libs/sinon'),
9 wlxml = require('wlxml/wlxml'),
10 canvas = require('modules/documentCanvas/canvas/canvas'),
11 keyboard = require('modules/documentCanvas/canvas/keyboard'),
12 keyEvent = require('modules/documentCanvas/canvas/keyEvent'),
13 corePlugin = require('./core.js'),
16 var K = keyboard.KEYS;
18 var getDocumentFromXML = function(xml, options) {
19 var doc = wlxml.WLXMLDocumentFromXML(xml, options || {});
20 doc.registerExtension(corePlugin.documentExtension);
25 var getCanvasFromXML = function(xml, elements) {
26 return canvas.fromXMLDocument(getDocumentFromXML(xml), elements);
29 var getTextNodes = function(text, doc) {
32 var search = function(node) {
33 node.contents().forEach(function(node) {
34 if(node.nodeType === Node.TEXT_NODE) {
35 if(node.getText() === text) {
47 var getTextNode = function(text, doc) {
48 var nodes = getTextNodes(text, doc),
50 if(nodes.length === 0) {
51 error = 'Text not found';
52 } else if(nodes.length > 1) {
53 error = 'Text not unique';
54 } else if(nodes[0].getText() !== text) {
55 error = 'I was trying to cheat your test :(';
58 throw new Error(error);
63 var getTextElement = function(text, c) {
64 var node = getTextNode(text, c.wlxmlDocument),
65 element = node && node.getData('canvasElement');
66 if(!(element && element.getText() === text)) {
73 describe('Document extensions', function() {
74 describe('break content', function() {
75 it('break text into two nodes', function() {
76 var doc = getDocumentFromXML('<section><div>Alice</div></section>'),
77 textNode = doc.root.contents()[0].contents()[0];
79 var result = textNode.breakContent({offset:3});
81 var section = doc.root;
82 expect(section.contents().length).to.equal(2);
83 expect(section.contents()[0].contents()[0].getText()).to.equal('Ali');
84 expect(section.contents()[1].contents()[0].getText()).to.equal('ce');
86 expect(result.first.sameNode(section.contents()[0])).to.equal(true);
87 expect(result.second.sameNode(section.contents()[1])).to.equal(true);
88 expect(result.emptyText).to.equal(undefined, 'no new text node created');
90 it('puts empty text node when breaking at the very beginning', function() {
91 var doc = getDocumentFromXML('<section><div>Alice</div></section>'),
92 textNode = doc.root.contents()[0].contents()[0];
94 var result = textNode.breakContent({offset:0}),
95 firstNode = doc.root.contents()[0];
97 expect(result.emptyText.sameNode(firstNode.contents()[0]));
98 expect(result.emptyText.getText()).to.equal('');
100 it('puts empty text node when breaking at the very end', function() {
101 var doc = getDocumentFromXML('<section><div>Alice</div></section>'),
102 textNode = doc.root.contents()[0].contents()[0];
104 var result = textNode.breakContent({offset:5}),
105 secondNode = doc.root.contents()[1];
107 expect(result.emptyText.sameNode(secondNode.contents()[0]));
108 expect(result.emptyText.getText()).to.equal('');
112 describe('mergin text with preceding content', function() {
113 it('does nothing if text node parent has no preceding element', function() {
114 var doc = getDocumentFromXML('<section><div><div>some text</div></div></section>'),
115 text = getTextNode('some text', doc),
118 doc.on('change', spy);
119 text.mergeContentUp();
120 expect(spy.callCount).to.equal(0);
122 it('does nothing if text node parent is precedeed by text node', function() {
123 var doc = getDocumentFromXML('<section><div></div>another text<div>some text</div></section>'),
124 text = getTextNode('some text', doc),
127 doc.on('change', spy);
128 text.mergeContentUp();
129 expect(spy.callCount).to.equal(0);
131 it('does nothing if text node is not first child of its parent', function() {
132 var doc = getDocumentFromXML('<section><div></div><div><a></a>some text</div></section>'),
133 text = getTextNode('some text', doc),
136 doc.on('change', spy);
137 text.mergeContentUp();
138 expect(spy.callCount).to.equal(0);
140 it('moves text node and its siblings to the block element preceding text node parent', function() {
141 var doc = getDocumentFromXML('<section><div></div><div>some text<span>is</span> here!</div></section>'),
142 text = getTextNode('some text', doc);
144 text.mergeContentUp();
146 var contents = doc.root.contents();
147 expect(contents.length).to.equal(1);
148 expect(contents[0].contents().length).to.equal(3);
149 expect(contents[0].contents()[0].getText()).to.equal('some text');
150 expect(contents[0].contents()[1].getTagName()).to.equal('span');
151 expect(contents[0].contents()[2].getText()).to.equal(' here!');
156 describe.only('Keyboard interactions', function() {
158 var Keyboard = function(canvas) {
159 this.canvas = canvas;
162 _.extend(Keyboard.prototype, {
163 press: function(key) {
164 this.canvas.triggerKeyEvent(keyEvent.fromParams({key:key}), this.selection);
165 this.selection = this.canvas.getSelection();
168 withCaret: function(where) {
169 var offset = where.indexOf('|'),
170 text = where.split('|').join(''),
171 el = getTextElement(text, this.canvas),
172 selection = this.canvas.createSelection({type: 'caret', element: el, offset: offset});
174 throw new Error('Invalid caret');
176 this.selection = selection;
179 withSelection: function(start, end) {
180 var startOffset = start.indexOf('|'),
181 endOffset = end.indexOf('|'),
182 startText= start.split('|').join(''),
183 endText = end.split('|').join(''),
184 startElement = getTextElement(startText, this.canvas),
185 endElement = getTextElement(endText, this.canvas),
186 selection = this.canvas.createSelection({
187 type: 'textSelection',
188 anchorElement: startElement,
189 anchorOffset: startOffset,
190 focusElement: endElement,
191 focusOffset: endOffset
193 if(startOffset === -1 || endOffset === -1) {
194 throw new Error('Invalid text selection');
196 this.selection = selection;
201 describe('deleting text with selection', function() {
202 [K.BACKSPACE, K.DELETE].forEach(function(key) {
203 it('deletes text withing a single text element ' + key, function() {
204 var c = getCanvasFromXML('<section><div>Alice</div></section>'),
207 k.withSelection('A|lice', 'Alic|e').press(key);
208 expect(c.wlxmlDocument.root.contents()[0].contents()[0].getText()).to.equal('Ae');
210 it('deletes text across two paragraphs ' + key, function() {
211 var c = getCanvasFromXML('<section><div class="p">Alice</div><div class="p">cat</div></section>'),
214 k.withSelection('A|lice', 'c|at').press(key);
215 var rootContents = c.wlxmlDocument.root.contents();
217 expect(rootContents.length).to.equal(2);
218 expect(rootContents[0].contents()[0].getText()).to.equal('A');
219 expect(rootContents[1].contents()[0].getText()).to.equal('at');
222 it('keeps an empty paragraph after deleting its whole text ' + key, function() {
223 var c = getCanvasFromXML('<section><div class="p">Alice</div></section>'),
226 k.withSelection('|Alice', 'Alice|').press(key);
227 var rootContents = c.wlxmlDocument.root.contents();
229 expect(rootContents.length).to.equal(1);
230 expect(rootContents[0].contents()[0].getText()).to.equal('');
236 // describe('deleting with a caret', function() {
237 // it('keeps an empty paragraph after deleteing last letter with backspace', function() {
238 // var c = getCanvasFromXML('<section><div class="p">A</div></section>'),
239 // k = new Keyboard(c);
241 // k.withCaret('A|').press(K.BACKSPACE);
242 // var rootContents = c.wlxmlDocument.root.contents();
244 // expect(rootContents.length).to.equal(1);
245 // expect(rootContents[0].contents()[0].getText()).to.equal('');
247 // // it('removes a paragraph on yet another delete' + key, function() {
253 // + empty when bck/ins + l===1
255 describe('backspace at the beginning', function() {
256 it('merges two adjacent paragraphs', function() {
257 var c = getCanvasFromXML('<section><div class="p">A</div><div class="p">B</div></section>'),
260 k.withCaret('|B').press(K.BACKSPACE);
262 var rootContents = c.wlxmlDocument.root.contents();
263 expect(rootContents.length).to.equal(1);
264 expect(rootContents[0].getClass()).to.equal('p');
265 expect(rootContents[0].contents()[0].getText()).to.equal('AB');
267 it('merges a paragraph with a header', function() {
268 var c = getCanvasFromXML('<section><header>A</header><div class="p">B</div></section>'),
271 k.withCaret('|B').press(K.BACKSPACE);
273 var rootContents = c.wlxmlDocument.root.contents();
274 expect(rootContents.length).to.equal(1);
275 expect(rootContents[0].getTagName()).to.equal('header');
276 expect(rootContents[0].contents()[0].getText()).to.equal('AB');
278 it('merges two adjacent headers', function() {
279 var c = getCanvasFromXML('<section><header>A</header><header>B</header></section>'),
282 k.withCaret('|B').press(K.BACKSPACE);
283 var rootContents = c.wlxmlDocument.root.contents();
284 expect(rootContents.length).to.equal(1);
285 expect(rootContents[0].getTagName()).to.equal('header');
286 expect(rootContents[0].contents()[0].getText()).to.equal('AB');
288 it('merges a header with a paragraph', function() {
289 var c = getCanvasFromXML('<section><div class="p">A</div><header>B</header></section>'),
292 k.withCaret('|B').press(K.BACKSPACE);
294 var rootContents = c.wlxmlDocument.root.contents();
295 expect(rootContents.length).to.equal(1);
296 expect(rootContents[0].is('p')).to.equal(true);
297 expect(rootContents[0].contents()[0].getText()).to.equal('AB');
299 it('merges a paragraph into a last list item', function() {
300 var c = getCanvasFromXML('<section><div class="list"><div class="item">item</div></div><div class="p">paragraph</div></section>'),
301 list = c.wlxmlDocument.root.contents()[0],
304 k.withCaret('|paragraph').press(K.BACKSPACE);
306 var rootContents = c.wlxmlDocument.root.contents();
307 expect(rootContents.length).to.equal(1);
308 expect(rootContents[0].sameNode(list)).to.equal(true);
310 var items = list.contents();
311 expect(items.length).to.equal(1);
312 expect(items[0].contents()[0].getText()).to.equal('itemparagraph');
314 it('merges a list item with a list item', function() {
315 var c = getCanvasFromXML('<section><div class="list"><div class="item">item1</div><div class="item">item2</div></div></section>'),
316 list = c.wlxmlDocument.root.contents()[0],
319 k.withCaret('|item2').press(K.BACKSPACE);
321 var rootContents = c.wlxmlDocument.root.contents();
322 expect(rootContents.length).to.equal(1);
324 expect(rootContents[0].sameNode(list)).to.equal(true);
326 var items = list.contents();
328 expect(items.length).to.equal(1);
329 expect(items[0].contents()[0].getText()).to.equal('item1item2');
331 it('creates a new paragraph preceding the list from a first list item', function() {
332 var c = getCanvasFromXML('<section><div class="list"><div class="item">item1</div><div class="item">item2</div></div></section>'),
333 list = c.wlxmlDocument.root.contents()[0],
336 k.withCaret('|item1').press(K.BACKSPACE);
338 var rootContents = c.wlxmlDocument.root.contents();
339 expect(rootContents.length).to.equal(2);
341 expect(rootContents[0].getClass()).to.equal('p');
342 expect(rootContents[0].contents()[0].getText()).to.equal('item1');
344 expect(rootContents[1].sameNode(list)).to.equal(true);
346 it('removes list after moving up its only item', function() {
347 var c = getCanvasFromXML('<section><div class="list"><div class="item">item</div></div></section>'),
350 k.withCaret('|item').press(K.BACKSPACE);
351 var rootContents = c.wlxmlDocument.root.contents();
352 expect(rootContents.length).to.equal(1);
354 expect(rootContents[0].getClass()).to.equal('p');
355 expect(rootContents[0].contents()[0].getText()).to.equal('item');