editor: canvas - enter on an empty last list item creates paragraph after list
[fnpeditor.git] / src / wlxml / wlxml.test.js
1 define([
2     'libs/chai',
3     './wlxml.js'
4 ], function(chai, wlxml) {
5     
6 'use strict';
7
8 /* jshint expr:true */
9 /* global it, describe, beforeEach */
10
11 var expect = chai.expect;
12
13 var nodeFromXML = function(xml) {
14     return wlxml.WLXMLElementNodeFromXML(xml);
15 };
16
17 var getDocumentFromXML = function(xml, options) {
18     return wlxml.WLXMLDocumentFromXML(xml, options || {});
19 };
20
21
22 describe('WLXMLDocument', function() {
23     
24     describe('Basic wlxml element node properties', function() {
25         it('returns its class', function() {
26             var node = nodeFromXML('<header class="class.subclass"></header>');
27             expect(node.getClass()).to.equal('class.subclass');
28         });
29
30         it('returns its class hierarchy', function() {
31             var node = nodeFromXML('<div class="a.b.c"></div>');
32             expect(node.getClassHierarchy()).to.eql(['', 'a', 'a.b', 'a.b.c']);
33         });
34     });
35
36     describe('White space handling', function() {
37         /* globals Node */
38
39         it('ignores white space surrounding block elements', function() {
40             var node = nodeFromXML('<section> <div></div> </section>'),
41                 contents = node.contents();
42             expect(contents).to.have.length(1);
43             expect(contents[0].nodeType).to.equal(Node.ELEMENT_NODE);
44         });
45         it('ignores white space between block elements', function() {
46             var node = nodeFromXML('<section><div></div> <div></div></section>'),
47             contents = node.contents();
48             expect(contents).to.have.length(2);
49             [0,1].forEach(function(idx) {
50                 expect(contents[idx].nodeType).to.equal(Node.ELEMENT_NODE);
51             });
52         });
53         it('trims white space from the beginning and the end of the block elements', function() {
54             var node = nodeFromXML('<section> Alice <span>has</span> a cat </section>');
55             expect(node.contents()[0].getText()).to.equal('Alice ');
56             expect(node.contents()[2].getText()).to.equal(' a cat');
57         });
58         it('normalizes string of white characters to one space at the inline element boundries', function() {
59             var node = nodeFromXML('<span>   Alice has a cat   </span>');
60             expect(node.contents()[0].getText()).to.equal(' Alice has a cat ');
61         });
62         it('normalizes string of white characters to one space before inline element', function() {
63             var node = nodeFromXML('<div>Alice has  <span>a cat</span></div>');
64             expect(node.contents()[0].getText()).to.equal('Alice has ');
65         });
66         it('normalizes string of white characters to one space after inline element', function() {
67             var node = nodeFromXML('<div>Alice has <span>a</span>  cat</div>');
68             expect(node.contents()[2].getText()).to.equal(' cat');
69         });
70     });
71
72     describe('formatting output xml', function() {
73
74         /*jshint multistr: true */
75
76         it('keeps white space between XML nodes', function() {
77             var xmlIn = '<section>\n\n\n<div></div>\n\n\n<div></div>\n\n\n</section>',
78             doc = getDocumentFromXML(xmlIn),
79             xmlOut = doc.toXML();
80
81             var partsIn = xmlIn.split('\n\n\n'),
82                 partsOut = xmlOut.split('\n\n\n');
83
84             expect(partsIn).to.deep.equal(partsOut);
85         });
86
87         it('keeps white space between XML nodes - inline case', function() {
88             var xmlIn = '<section>\n\n\n<span></span>\n\n\n<span></span>\n\n\n</section>',
89                 doc = getDocumentFromXML(xmlIn),
90                 xmlOut = doc.toXML();
91
92             var partsIn = xmlIn.split('\n\n\n'),
93                 partsOut = xmlOut.split('\n\n\n');
94             expect(partsIn).to.deep.equal(partsOut);
95         });
96
97         it('keeps white space at the beginning of text', function() {
98             var xmlIn = '<section>    abc<div>some div</div>    abc</section>',
99                 doc = getDocumentFromXML(xmlIn),
100                 xmlOut = doc.toXML();
101
102             expect(xmlOut).to.equal(xmlIn);
103         });
104
105         // it('nests new children block elements', function() {
106         //     var doc = getDocumentFromXML('<section></section>');
107     
108         //     doc.root.append({tag: 'header'});
109
110         //     var xmlOut = doc.toXML();
111         //     expect(xmlOut.split('\n  ')[0]).to.equal('<section>', 'nesting start ok');
112         //     expect(xmlOut.split('\n').slice(-1)[0]).to.equal('</section>', 'nesting end ok');
113
114         // });
115
116         // it('doesn\'t nest new children inline elements', function() {
117         //     var doc = getDocumentFromXML('<section></section>');
118     
119         //     doc.root.append({tag: 'span'});
120
121         //     var xmlOut = doc.toXML();
122         //     expect(xmlOut).to.equal('<section><span></span></section>');
123         // });
124
125         it('keeps original white space at the end of text', function() {
126             
127             var xmlIn = '<header>    Some text ended with white space \
128             \
129             <span class="uri">Some text</span> some text\
130         \
131         </header>',
132                 doc = getDocumentFromXML(xmlIn),
133                 xmlOut = doc.toXML();
134         
135             expect(xmlOut).to.equal(xmlIn);
136         });
137
138         it('keeps white space around text node', function() {
139             var xmlIn = '<section>\
140             <header>header1</header>\
141             Some text surrounded by white space\
142             <header>header2</header>\
143         </section>',
144                 doc = getDocumentFromXML(xmlIn),
145                 xmlOut = doc.toXML();
146             expect(xmlOut).to.equal(xmlIn);
147         });
148
149         it('keeps white space around text node - last node case', function() {
150             var xmlIn = '<section>\
151             <header>header</header>\
152                 \
153             Some text surrounded by white space\
154                 \
155         </section>',
156                 doc = getDocumentFromXML(xmlIn),
157                 xmlOut = doc.toXML();
158
159             expect(xmlOut).to.equal(xmlIn);
160         });
161
162         it('keeps white space after detaching text element', function() {
163             var xmlIn = '<section><header>header</header>\n\
164                 \n\
165             text1\n\
166                 \n\
167         </section>',
168                 expectedXmlOut = '<section><header>header</header>\n\
169                 \n\
170             \n\
171                 \n\
172         </section>',
173                 doc = getDocumentFromXML(xmlIn),
174                 contents = doc.root.contents(),
175                 text = contents[contents.length-1];
176             
177             expect(text.getText()).to.equal('text1');
178
179             text.detach();
180
181             var xmlOut = doc.toXML();
182             expect(xmlOut).to.equal(expectedXmlOut);
183         });
184
185     });
186
187     describe('Extension', function() {
188         var doc, extension, testClassNode;
189
190         beforeEach(function() {
191             doc = getDocumentFromXML('<section>Alice<div class="test_class"></div><div class="test_class.a"></div></section>');
192         });
193
194         it('allows adding method to an ElementNode of specific class', function() {
195             extension = {wlxmlClass: {test_class: {methods: {
196                 testMethod: function() { return this; }
197             }}}};
198             doc.registerExtension(extension);
199             testClassNode = doc.root.contents()[1];
200             expect(testClassNode.object.testMethod().sameNode(testClassNode)).to.equal(true, '1');
201         });
202
203         it('allows adding non-function properties to an ElementNode of specific class', function() {
204             extension = {wlxmlClass: {test_class: {methods: {
205                 testProp: 123
206             }}}};
207             doc.registerExtension(extension);
208             testClassNode = doc.root.contents()[1];
209             expect(testClassNode.object.testProp).to.equal(123);
210         });
211
212         it('allows adding transformation to an ElementNode of specific class', function() {
213             extension = {wlxmlClass: {test_class: {transformations: {
214                 testTransformation: function() { return this; },
215                 testTransformation2: {impl: function() { return this; }}
216             }}}};
217             doc.registerExtension(extension);
218             testClassNode = doc.root.contents()[1];
219             expect(testClassNode.object.testTransformation().sameNode(testClassNode)).to.equal(true, '1');
220             expect(testClassNode.object.testTransformation2().sameNode(testClassNode)).to.equal(true, '1');
221         });
222
223         it('added methods are inherited by nodes with subclasses', function() {
224             extension = {wlxmlClass: {test_class: {methods: {
225                 testMethod: function() { return this; }
226             }}}};
227             doc.registerExtension(extension);
228             testClassNode = doc.root.contents()[2];
229             expect(testClassNode.object.testMethod().sameNode(testClassNode)).to.equal(true);
230         });
231         it('added transformations are inherited by nodes with subclasses', function() {
232             extension = {wlxmlClass: {test_class: {transformations: {
233                 testTransformation: function() { return this; },
234                 testTransformation2: {impl: function() { return this; }}
235             }}}};
236             doc.registerExtension(extension);
237             testClassNode = doc.root.contents()[2];
238             expect(testClassNode.object.testTransformation().sameNode(testClassNode)).to.equal(true, '1');
239             expect(testClassNode.object.testTransformation2().sameNode(testClassNode)).to.equal(true, '2');
240         });
241     });
242
243     describe('Context roots', function() {
244         var doc = getDocumentFromXML('<section><div class="a"><div class="b"><div class="c"></div></div></div></section>');
245         doc.registerExtension({wlxmlClass: {a: {methods: {
246             isContextRoot: function(node) {
247                 return node.getClass() === 'b';
248             }
249         }}}});
250
251         var divA = doc.root.contents()[0],
252             divB = divA.contents()[0],
253             divC = divB.contents()[0];
254
255         it('allows extensions declaring a node as a context root', function() {
256             expect(divC.isContextRoot()).to.equal(false, 'c is not a context root');
257             expect(divB.isContextRoot()).to.equal(true, 'b is a context root');
258             expect(divA.isContextRoot()).to.equal(false, 'a is not a context root');
259         });
260
261         it('closes context for parent context quering methods', function() {
262             expect(divC.isInside('b')).to.equal(true, 'c inside b');
263             expect(divC.isInside('a')).to.equal(false, 'c not inside a');
264             expect(divC.isInside({tagName: 'section'})).to.equal(false, 'c not inside section');
265
266             expect(divB.isInside('a')).to.equal(true, 'b inside a');
267             expect(divB.isInside({tagName: 'section'})).to.equal(true, 'b inside section');
268
269             expect(divA.isInside({tagName: 'section'})).to.equal(true, 'a inside section');
270         });
271     });
272 });
273
274 });