wip: refactoring method/transformation registration out of wlxml.js to smartxml ...
[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 */
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         it('ignores white space surrounding block elements', function() {
107             var node = nodeFromXML('<section> <div></div> </section>'),
108                 contents = node.contents();
109             expect(contents).to.have.length(1);
110             expect(contents[0].nodeType).to.equal(Node.ELEMENT_NODE);
111         });
112         it('ignores white space between block elements', function() {
113             var node = nodeFromXML('<section><div></div> <div></div></section>'),
114             contents = node.contents();
115             expect(contents).to.have.length(2);
116             [0,1].forEach(function(idx) {
117                 expect(contents[idx].nodeType).to.equal(Node.ELEMENT_NODE);
118             });
119         });
120         it('trims white space from the beginning and the end of the block elements', function() {
121             var node = nodeFromXML('<section> Alice <span>has</span> a cat </section>');
122             expect(node.contents()[0].getText()).to.equal('Alice ');
123             expect(node.contents()[2].getText()).to.equal(' a cat');
124         });
125         it('normalizes string of white characters to one space at the inline element boundries', function() {
126             var node = nodeFromXML('<span>   Alice has a cat   </span>');
127             expect(node.contents()[0].getText()).to.equal(' Alice has a cat ');
128         });
129         it('normalizes string of white characters to one space before inline element', function() {
130             var node = nodeFromXML('<div>Alice has  <span>a cat</span></div>');
131             expect(node.contents()[0].getText()).to.equal('Alice has ');
132         });
133         it('normalizes string of white characters to one space after inline element', function() {
134             var node = nodeFromXML('<div>Alice has <span>a</span>  cat</div>');
135             expect(node.contents()[2].getText()).to.equal(' cat');
136         });
137     });
138
139     describe('formatting output xml', function() {
140
141         /*jshint multistr: true */
142
143         it('keeps white space between XML nodes', function() {
144             var xmlIn = '<section>\n\n\n<div></div>\n\n\n<div></div>\n\n\n</section>',
145             doc = getDocumentFromXML(xmlIn),
146             xmlOut = doc.toXML();
147
148             var partsIn = xmlIn.split('\n\n\n'),
149                 partsOut = xmlOut.split('\n\n\n');
150
151             expect(partsIn).to.deep.equal(partsOut);
152         });
153
154         it('keeps white space between XML nodes - inline case', function() {
155             var xmlIn = '<section>\n\n\n<span></span>\n\n\n<span></span>\n\n\n</section>',
156                 doc = getDocumentFromXML(xmlIn),
157                 xmlOut = doc.toXML();
158
159             var partsIn = xmlIn.split('\n\n\n'),
160                 partsOut = xmlOut.split('\n\n\n');
161             expect(partsIn).to.deep.equal(partsOut);
162         });
163
164         it('keeps white space at the beginning of text', function() {
165             var xmlIn = '<section>    abc<div>some div</div>    abc</section>',
166                 doc = getDocumentFromXML(xmlIn),
167                 xmlOut = doc.toXML();
168
169             expect(xmlOut).to.equal(xmlIn);
170         });
171
172         // it('nests new children block elements', function() {
173         //     var doc = getDocumentFromXML('<section></section>');
174     
175         //     doc.root.append({tag: 'header'});
176
177         //     var xmlOut = doc.toXML();
178         //     expect(xmlOut.split('\n  ')[0]).to.equal('<section>', 'nesting start ok');
179         //     expect(xmlOut.split('\n').slice(-1)[0]).to.equal('</section>', 'nesting end ok');
180
181         // });
182
183         // it('doesn\'t nest new children inline elements', function() {
184         //     var doc = getDocumentFromXML('<section></section>');
185     
186         //     doc.root.append({tag: 'span'});
187
188         //     var xmlOut = doc.toXML();
189         //     expect(xmlOut).to.equal('<section><span></span></section>');
190         // });
191
192         it('keeps original white space at the end of text', function() {
193             
194             var xmlIn = '<header>    Some text ended with white space \
195             \
196             <span class="uri">Some text</span> some text\
197         \
198         </header>',
199                 doc = getDocumentFromXML(xmlIn),
200                 xmlOut = doc.toXML();
201         
202             expect(xmlOut).to.equal(xmlIn);
203         });
204
205         it('keeps white space around text node', function() {
206             var xmlIn = '<section>\
207             <header>header1</header>\
208             Some text surrounded by white space\
209             <header>header2</header>\
210         </section>',
211                 doc = getDocumentFromXML(xmlIn),
212                 xmlOut = doc.toXML();
213             expect(xmlOut).to.equal(xmlIn);
214         });
215
216         it('keeps white space around text node - last node case', function() {
217             var xmlIn = '<section>\
218             <header>header</header>\
219                 \
220             Some text surrounded by white space\
221                 \
222         </section>',
223                 doc = getDocumentFromXML(xmlIn),
224                 xmlOut = doc.toXML();
225
226             expect(xmlOut).to.equal(xmlIn);
227         });
228
229         it('keeps white space after detaching text element', function() {
230             var xmlIn = '<section><header>header</header>\n\
231                 \n\
232             text1\n\
233                 \n\
234         </section>',
235                 expectedXmlOut = '<section><header>header</header>\n\
236                 \n\
237             \n\
238                 \n\
239         </section>',
240                 doc = getDocumentFromXML(xmlIn),
241                 contents = doc.root.contents(),
242                 text = contents[contents.length-1];
243             
244             expect(text.getText()).to.equal('text1');
245
246             text.detach();
247
248             var xmlOut = doc.toXML();
249             expect(xmlOut).to.equal(expectedXmlOut);
250         });
251
252     });
253
254     describe('Extension', function() {
255         var doc, extension, elementNode, textNode, testClassNode;
256
257         beforeEach(function() {
258             doc = getDocumentFromXML('<section>Alice<div class="test_class"></div></section>');
259             elementNode = doc.root;
260             textNode = doc.root.contents()[0];
261             extension = {};
262             
263             console.log('A');
264             expect(function() {
265                 elementNode.transform('testTransformation');
266             }).to.throw(Error);
267             console.log('B');
268             expect(function() {
269                 textNode.transform('testTransformation');
270             }).to.throw(Error);
271             console.log('C');
272             expect(function() {
273                 doc.transform('testTransformation');
274             }).to.throw(Error);
275             expect(doc.testMethod).to.be.undefined;
276             expect(elementNode.testMethod).to.be.undefined;
277             expect(textNode.testMethod).to.be.undefined;
278         });
279
280         it('allows adding method to a document', function() {
281             extension = {document: {methods: {
282                 testMethod: function() { return this; }
283             }}};
284
285             doc.registerExtension(extension);
286             expect(doc.testMethod()).to.equal(doc, 'context is set to a document instance');
287         });
288
289         it('allows adding transformation to a document', function() {
290             extension = {document: {transformations: {
291                 testTransformation: function() { return this; },
292                 testTransformation2: {impl: function() { return this;}}
293             }}};
294
295             doc.registerExtension(extension);
296             expect(doc.transform('testTransformation')).to.equal(doc, 'context is set to a document instance');
297             expect(doc.transform('testTransformation2')).to.equal(doc, 'context is set to a document instance');
298         });
299
300         it('allows adding method to a DocumentNode instance', function() {
301             extension = {documentNode: {methods: {
302                 testMethod: function() { return this; }    
303             }}};
304
305             doc.registerExtension(extension);
306
307             /* refresh */
308             elementNode = doc.root;
309             textNode = doc.root.contents()[0];
310
311             expect(elementNode.testMethod().sameNode(elementNode)).to.equal(true, 'context is set to a node instance');
312             expect(textNode.testMethod().sameNode(textNode)).to.equal(true, 'context is set to a node instance');
313         });
314
315         it('allows adding transformation to a DocumentNode', function() {
316             extension = {documentNode: {transformations: {
317                 testTransformation: function() { return this; },
318                 testTransformation2: {impl: function() { return this;}}
319             }}};
320             
321             doc.registerExtension(extension);
322
323             expect(elementNode.transform('testTransformation').sameNode(elementNode)).to.equal(true, '1');
324             expect(elementNode.transform('testTransformation2').sameNode(elementNode)).to.equal(true, '2');
325             expect(textNode.transform('testTransformation').sameNode(textNode)).to.equal(true, '3');
326             expect(textNode.transform('testTransformation2').sameNode(textNode)).to.equal(true, '4');
327         });
328
329         it('allows adding method to an ElementNode of specific class', function() {
330             extension = {wlxmlClass: {test_class: {methods: {
331                 testMethod: function() { return this; }
332             }}}};
333             doc.registerExtension(extension);
334             testClassNode = doc.root.contents()[1];
335             expect(testClassNode.object.testMethod().sameNode(testClassNode)).to.equal(true, '1');
336         });
337
338         it('allows adding transformation to an ElementNode of specific class', function() {
339             extension = {wlxmlClass: {test_class: {transformations: {
340                 testTransformation: function() { return this; },
341                 testTransformation2: {impl: function() { return this; }}
342             }}}};
343             doc.registerExtension(extension);
344             testClassNode = doc.root.contents()[1];
345             expect(testClassNode.object.transform('testTransformation').sameNode(testClassNode)).to.equal(true, '1');
346             expect(testClassNode.object.transform('testTransformation2').sameNode(testClassNode)).to.equal(true, '1');
347         });
348
349
350     });
351
352 });
353
354 });