smartxml: throw exception on transformation returning invalid change root
[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 attributes other than class and meta-* as other attributes', function() {
31             var node = nodeFromXML('<span class="uri" meta-attr="val" attr1="val1" attr2="val2"></span>');
32             expect(node.getOtherAttributes()).to.eql({attr1: 'val1', attr2: 'val2'});
33         });
34     });
35
36     describe('WLXML node meta attributes', function() {
37
38         it('inherits keys from super classes', function() {
39             var testClasses = {
40                     '': {
41                         attrs: {'common': {type: 'string'}}
42                     },
43                     'a': {
44                         attrs: {'a_attr': {type: 'string'}}
45                     },
46                     'a.b': {
47                         attrs: {'a_b_attr': {type: 'string'}}
48                     },
49                     'a.b.c': {
50                         attrs: {'a_b_c_attr': {type: 'string'}}
51                     }
52                 },
53                 doc = getDocumentFromXML('<section></section>', {wlxmlClasses: testClasses}),
54                 section = doc.root;
55
56             expect(section.getMetaAttributes().keys()).to.eql(['common']);
57
58             section.setClass('a');
59             expect(section.getMetaAttributes().keys().sort()).to.eql(['common', 'a_attr'].sort());
60
61             section.setClass('a.b');
62             expect(section.getMetaAttributes().keys().sort()).to.eql(['common', 'a_attr', 'a_b_attr'].sort());
63
64             section.setClass('a.b.c');
65             expect(section.getMetaAttributes().keys().sort()).to.eql(['common', 'a_attr', 'a_b_attr', 'a_b_c_attr'].sort());
66         });
67
68         describe('api', function() {
69             it('returns meta attributes as a dict', function() {
70                 var testClasses = {
71                         'test': {
72                             attrs: {
73                                 attr1: {type: 'string'},
74                                 attr2: {type: 'date'}
75                             }
76                         }
77                     },
78                     node = getDocumentFromXML(
79                         '<span class="test" meta-attr1="val1" meta-attr2="2014-01-01"></span>',
80                         {wlxmlClasses: testClasses}
81                     ).root,
82                     attrs = node.getMetaAttributes();
83
84                 expect(attrs.keys().sort()).to.eql(['attr1', 'attr2'].sort());
85                 expect(attrs.attr1.value).to.equal('val1');
86                 expect(attrs.attr1.type).to.equal('string');
87                 expect(attrs.attr2.value).to.equal('2014-01-01');
88                 expect(attrs.attr2.type).to.equal('date');
89             });
90             it('returns undefined value if attribute is missing', function() {
91                 var testClasses = {
92                         'test': {
93                             attrs: {
94                                 attr1: {type: 'string'},
95                             }
96                         }
97                     },
98                     node = getDocumentFromXML('<span class="test"></span>', {wlxmlClasses: testClasses}).root,
99                     attrs = node.getMetaAttributes();
100                     expect(attrs.attr1.value).to.be.undefined;
101             });
102         });
103     });
104
105     describe('White space handling', function() {
106         /* globals Node */
107
108         it('ignores white space surrounding block elements', function() {
109             var node = nodeFromXML('<section> <div></div> </section>'),
110                 contents = node.contents();
111             expect(contents).to.have.length(1);
112             expect(contents[0].nodeType).to.equal(Node.ELEMENT_NODE);
113         });
114         it('ignores white space between block elements', function() {
115             var node = nodeFromXML('<section><div></div> <div></div></section>'),
116             contents = node.contents();
117             expect(contents).to.have.length(2);
118             [0,1].forEach(function(idx) {
119                 expect(contents[idx].nodeType).to.equal(Node.ELEMENT_NODE);
120             });
121         });
122         it('trims white space from the beginning and the end of the block elements', function() {
123             var node = nodeFromXML('<section> Alice <span>has</span> a cat </section>');
124             expect(node.contents()[0].getText()).to.equal('Alice ');
125             expect(node.contents()[2].getText()).to.equal(' a cat');
126         });
127         it('normalizes string of white characters to one space at the inline element boundries', function() {
128             var node = nodeFromXML('<span>   Alice has a cat   </span>');
129             expect(node.contents()[0].getText()).to.equal(' Alice has a cat ');
130         });
131         it('normalizes string of white characters to one space before inline element', function() {
132             var node = nodeFromXML('<div>Alice has  <span>a cat</span></div>');
133             expect(node.contents()[0].getText()).to.equal('Alice has ');
134         });
135         it('normalizes string of white characters to one space after inline element', function() {
136             var node = nodeFromXML('<div>Alice has <span>a</span>  cat</div>');
137             expect(node.contents()[2].getText()).to.equal(' cat');
138         });
139     });
140
141     describe('formatting output xml', function() {
142
143         /*jshint multistr: true */
144
145         it('keeps white space between XML nodes', function() {
146             var xmlIn = '<section>\n\n\n<div></div>\n\n\n<div></div>\n\n\n</section>',
147             doc = getDocumentFromXML(xmlIn),
148             xmlOut = doc.toXML();
149
150             var partsIn = xmlIn.split('\n\n\n'),
151                 partsOut = xmlOut.split('\n\n\n');
152
153             expect(partsIn).to.deep.equal(partsOut);
154         });
155
156         it('keeps white space between XML nodes - inline case', function() {
157             var xmlIn = '<section>\n\n\n<span></span>\n\n\n<span></span>\n\n\n</section>',
158                 doc = getDocumentFromXML(xmlIn),
159                 xmlOut = doc.toXML();
160
161             var partsIn = xmlIn.split('\n\n\n'),
162                 partsOut = xmlOut.split('\n\n\n');
163             expect(partsIn).to.deep.equal(partsOut);
164         });
165
166         it('keeps white space at the beginning of text', function() {
167             var xmlIn = '<section>    abc<div>some div</div>    abc</section>',
168                 doc = getDocumentFromXML(xmlIn),
169                 xmlOut = doc.toXML();
170
171             expect(xmlOut).to.equal(xmlIn);
172         });
173
174         // it('nests new children block elements', function() {
175         //     var doc = getDocumentFromXML('<section></section>');
176     
177         //     doc.root.append({tag: 'header'});
178
179         //     var xmlOut = doc.toXML();
180         //     expect(xmlOut.split('\n  ')[0]).to.equal('<section>', 'nesting start ok');
181         //     expect(xmlOut.split('\n').slice(-1)[0]).to.equal('</section>', 'nesting end ok');
182
183         // });
184
185         // it('doesn\'t nest new children inline elements', function() {
186         //     var doc = getDocumentFromXML('<section></section>');
187     
188         //     doc.root.append({tag: 'span'});
189
190         //     var xmlOut = doc.toXML();
191         //     expect(xmlOut).to.equal('<section><span></span></section>');
192         // });
193
194         it('keeps original white space at the end of text', function() {
195             
196             var xmlIn = '<header>    Some text ended with white space \
197             \
198             <span class="uri">Some text</span> some text\
199         \
200         </header>',
201                 doc = getDocumentFromXML(xmlIn),
202                 xmlOut = doc.toXML();
203         
204             expect(xmlOut).to.equal(xmlIn);
205         });
206
207         it('keeps white space around text node', function() {
208             var xmlIn = '<section>\
209             <header>header1</header>\
210             Some text surrounded by white space\
211             <header>header2</header>\
212         </section>',
213                 doc = getDocumentFromXML(xmlIn),
214                 xmlOut = doc.toXML();
215             expect(xmlOut).to.equal(xmlIn);
216         });
217
218         it('keeps white space around text node - last node case', function() {
219             var xmlIn = '<section>\
220             <header>header</header>\
221                 \
222             Some text surrounded by white space\
223                 \
224         </section>',
225                 doc = getDocumentFromXML(xmlIn),
226                 xmlOut = doc.toXML();
227
228             expect(xmlOut).to.equal(xmlIn);
229         });
230
231         it('keeps white space after detaching text element', function() {
232             var xmlIn = '<section><header>header</header>\n\
233                 \n\
234             text1\n\
235                 \n\
236         </section>',
237                 expectedXmlOut = '<section><header>header</header>\n\
238                 \n\
239             \n\
240                 \n\
241         </section>',
242                 doc = getDocumentFromXML(xmlIn),
243                 contents = doc.root.contents(),
244                 text = contents[contents.length-1];
245             
246             expect(text.getText()).to.equal('text1');
247
248             text.detach();
249
250             var xmlOut = doc.toXML();
251             expect(xmlOut).to.equal(expectedXmlOut);
252         });
253
254     });
255
256     describe('Extension', function() {
257         var doc, extension, elementNode, textNode, testClassNode;
258
259         beforeEach(function() {
260             doc = getDocumentFromXML('<section>Alice<div class="test_class"></div></section>');
261             elementNode = doc.root;
262             textNode = doc.root.contents()[0];
263             testClassNode = doc.root.contents('.test_class');
264             extension = {};
265             
266             expect(testClassNode.object).to.be.undefined;
267
268         });
269
270         it('allows adding method to an ElementNode of specific class', function() {
271             extension = {wlxmlClass: {test_class: {methods: {
272                 testMethod: function() { return this; }
273             }}}};
274             doc.registerExtension(extension);
275             testClassNode = doc.root.contents()[1];
276             expect(testClassNode.object.testMethod().sameNode(testClassNode)).to.equal(true, '1');
277         });
278
279         it('allows adding transformation to an ElementNode of specific class', function() {
280             extension = {wlxmlClass: {test_class: {transformations: {
281                 testTransformation: function() { return this; },
282                 testTransformation2: {impl: function() { return this; }}
283             }}}};
284             doc.registerExtension(extension);
285             testClassNode = doc.root.contents()[1];
286             expect(testClassNode.object.testTransformation().sameNode(testClassNode)).to.equal(true, '1');
287             expect(testClassNode.object.testTransformation2().sameNode(testClassNode)).to.equal(true, '1');
288         });
289
290
291     });
292
293 });
294
295 });