9d58afb85daf7bde043a80e953b44fd4fb187242
[platform/framework/web/crosswalk-tizen.git] /
1 /*global describe:false, it:false, beforeEach:false */
2 "use strict";
3
4 var expect = require('expect.js');
5 var rocambole = require('../');
6
7
8 describe('parse', function () {
9
10     it('should parse string and return AST', function () {
11         var ast = rocambole.parse('(function(){ return 123 })');
12         expect( ast.type ).to.equal( 'Program' );
13         expect( ast.body[0].type ).to.equal( 'ExpressionStatement' );
14     });
15
16
17     it('should include tokens before and after "program" end', function () {
18         var ast = rocambole.parse('//foo\n(function(){ return 123 })\n//bar\n');
19         expect( ast.startToken.value ).to.equal( 'foo' );
20         expect( ast.endToken.value ).to.equal( '\n' );
21         ast = rocambole.parse('\n//foo\n(function(){ return 123 })\n//dolor');
22         expect( ast.startToken.value ).to.equal( '\n' );
23         expect( ast.endToken.value ).to.equal( 'dolor' );
24     });
25
26
27     it('should work with any kind of line breaks & spaces', function () {
28         var ast = rocambole.parse('\nvar n\r\n=\n10;\r\r  \t\t  \n', {loc : true});
29
30         var br_1 = ast.startToken;
31         expect( br_1.type ).to.be( 'LineBreak' );
32         expect( br_1.value ).to.be( '\n' );
33         expect( br_1.range ).to.eql( [0, 1] );
34         expect( br_1.loc ).to.eql({
35             start : {
36                 line : 1,
37                 column : 0
38             },
39             end : {
40                 line : 1,
41                 column : 1
42             }
43         });
44
45         var ws_1 = ast.startToken.next.next;
46         expect( ws_1.type ).to.be( 'WhiteSpace' );
47         expect( ws_1.value ).to.be( ' ' );
48
49         var br_2 = br_1.next.next.next.next;
50         expect( br_2.type ).to.be( 'LineBreak' );
51         expect( br_2.value ).to.be( '\r\n' );
52         expect( br_2.range ).to.eql( [6, 8] );
53         expect( br_2.loc ).to.eql({
54             start : {
55                 line : 2,
56                 column : 5
57             },
58             end : {
59                 line : 2,
60                 column : 7
61             }
62         });
63
64         // it's important to notice that esprima doesn't parse "\r" as line
65         // break, so if it is not at EOF it will give conflicting "loc" info.
66         var br_6 = ast.endToken;
67         expect( br_6.type ).to.be( 'LineBreak' );
68         expect( br_6.value ).to.be( '\n' );
69         expect( br_6.range ).to.eql( [21, 22] );
70         expect( br_6.loc ).to.eql({
71             start : {
72                 line : 6,
73                 column : 6
74             },
75             end : {
76                 line : 6,
77                 column : 7
78             }
79         });
80
81         var ws_2 = ast.endToken.prev;
82         expect( ws_2.type ).to.be( 'WhiteSpace' );
83         expect( ws_2.value ).to.be( '  \t\t  ' );
84         expect( ws_2.range ).to.eql( [15, 21] );
85         expect( ws_2.loc ).to.eql({
86             start : {
87                 line : 6,
88                 column : 0
89             },
90             end : {
91                 line : 6,
92                 column : 6
93             }
94         });
95
96         var br_5 = ws_2.prev;
97         expect( br_5.type ).to.be( 'LineBreak' );
98         expect( br_5.value ).to.be( '\r' );
99
100         var br_4 = br_5.prev;
101         expect( br_4.type ).to.be( 'LineBreak' );
102         expect( br_4.value ).to.be( '\r' );
103     });
104
105
106     it('should not include any char that isn\'t a white space on a WhiteSpace token [issue #3]', function () {
107         var ast = rocambole.parse("\n/* foo */\n/* bar */\nfunction foo(){\n  var bar = 'baz';\n\n  //foo\n  //bar\n\n  var lorem = 'ipsum';\n  return bar + lorem;\n}");
108         var tk = ast.startToken;
109         var nComments = 0;
110         while (tk) {
111             if (tk.type === 'WhiteSpace') {
112                 expect( tk.value ).to.match( /^[\s\t]+$/ );
113             } else if (tk.type === 'LineBreak') {
114                 expect( tk.value ).to.equal( '\n' );
115             } else if (tk.type === 'LineComment') {
116                 expect( tk.raw ).to.match( /^\/\/\w{3}$/ );
117                 nComments++;
118             } else if (tk.type === 'BlockComment') {
119                 expect( tk.raw ).to.match( /^\/\* \w{3} \*\/$/ );
120                 nComments++;
121             }
122             tk = tk.next;
123         }
124         expect( nComments ).to.be( 4 );
125     });
126
127
128     it('should instrument object expression "value" node', function () {
129         // this was a bug introduced while trying to improve performance
130         var ast = rocambole.parse('amet(123, a, {flag : true});');
131         var exp = ast.body[0].expression;
132         expect( exp.startToken ).not.to.be(undefined);
133         expect( exp.callee.startToken ).not.to.be(undefined);
134         expect( exp['arguments'][0].startToken ).not.to.be(undefined);
135         expect( exp['arguments'][1].startToken ).not.to.be(undefined);
136         expect( exp['arguments'][2].startToken ).not.to.be(undefined);
137         expect( exp['arguments'][2].properties[0].startToken ).not.to.be(undefined);
138         expect( exp['arguments'][2].properties[0].key.startToken ).not.to.be(undefined);
139         expect( exp['arguments'][2].properties[0].value.startToken ).not.to.be(undefined);
140     });
141
142
143
144     describe('Node', function () {
145
146         var ast, program, expressionStatement,
147             fnExpression, block, returnStatement;
148
149         beforeEach(function(){
150             ast                 = rocambole.parse('/* block */\n(function(){\n return 123; // line\n})');
151             program             = ast;
152             expressionStatement = ast.body[0];
153             fnExpression        = expressionStatement.expression;
154             block               = fnExpression.body;
155             returnStatement     = block.body[0];
156         });
157
158         describe('node.parent', function () {
159             it('should add reference to parent node', function () {
160                 expect( program.parent ).to.equal( undefined );
161                 expect( expressionStatement.parent ).to.equal( program );
162                 expect( fnExpression.parent ).to.equal( expressionStatement );
163                 expect( block.parent ).to.equal( fnExpression );
164             });
165         });
166
167
168         describe('node.toString()', function(){
169             it('should return the node source', function () {
170                 expect( returnStatement.type ).to.equal( 'ReturnStatement' );
171                 expect( returnStatement.toString() ).to.equal( 'return 123;' );
172             });
173             it('should use raw value of comments', function () {
174                 expect( block.toString() ).to.equal( '{\n return 123; // line\n}' );
175             });
176             it('should use raw value of comments', function () {
177                 expect( ast.toString() ).to.equal( '/* block */\n(function(){\n return 123; // line\n})' );
178             });
179         });
180
181
182         describe('depth', function () {
183             it('should add depth property to nodes', function () {
184                 expect( program.depth ).to.equal( 0 );
185                 expect( expressionStatement.depth ).to.equal( 1 );
186                 expect( fnExpression.depth ).to.equal( 2 );
187                 expect( block.depth ).to.equal( 3 );
188                 expect( returnStatement.depth ).to.equal( 4 );
189             });
190         });
191
192
193         describe('node.endToken', function () {
194             it('should return last token inside node', function () {
195                 expect( program.endToken.value ).to.equal( ')' );
196                 expect( expressionStatement.endToken.value ).to.equal( ')' );
197                 expect( fnExpression.endToken.value ).to.equal( '}' );
198                 expect( block.endToken.value ).to.equal( '}' );
199                 expect( returnStatement.endToken.value ).to.equal( ';' );
200             });
201
202             it('should capture end token properly', function () {
203                 var ast = rocambole.parse('[1,2,[3,4,[5,6,[7,8,9]]]];');
204                 var exp = ast.body[0].expression;
205                 expect( exp.endToken.value ).to.equal( ']' );
206                 expect( exp.elements[0].value ).to.equal( 1 );
207                 expect( exp.elements[0].startToken.value ).to.equal( '1' );
208                 expect( exp.elements[0].endToken.value ).to.equal( '1' );
209             });
210         });
211
212
213         describe('node.startToken', function () {
214             it('should return first token inside node', function () {
215                 expect( program.startToken.value ).to.equal( ' block ' );
216                 expect( expressionStatement.startToken.value ).to.equal( '(' );
217                 expect( fnExpression.startToken.value ).to.equal( 'function' );
218                 expect( block.startToken.value ).to.equal( '{' );
219                 expect( returnStatement.startToken.value ).to.equal( 'return' );
220             });
221         });
222
223
224         describe('Node.next & Node.prev', function () {
225             it('should return reference to previous and next nodes', function () {
226                 var ast = rocambole.parse("\n/* foo */\n/* bar */\nfunction foo(){\n  var bar = 'baz';\n  var lorem = 'ipsum';\n  return bar + lorem;\n}");
227                 var block = ast.body[0].body.body;
228                 var firstNode = block[0];
229                 var secondNode = block[1];
230                 var lastNode = block[2];
231                 expect( firstNode.prev ).to.equal( undefined );
232                 expect( firstNode.next ).to.equal( secondNode );
233                 expect( secondNode.prev ).to.equal( firstNode );
234                 expect( secondNode.next ).to.equal( lastNode );
235                 expect( lastNode.prev ).to.equal( secondNode );
236                 expect( lastNode.next ).to.equal( undefined );
237             });
238         });
239
240     });
241
242
243     describe('Token', function () {
244
245         it('should instrument tokens', function () {
246             var ast = rocambole.parse('function foo(){ return "bar"; }');
247             var tokens = ast.tokens;
248
249             expect( tokens[0].prev ).to.be(undefined);
250             expect( tokens[0].next ).to.be( tokens[1] );
251             expect( tokens[1].prev ).to.be( tokens[0] );
252             expect( tokens[1].next ).to.be( tokens[2] );
253         });
254
255         it('should add range and loc info to comment tokens', function () {
256             var ast = rocambole.parse('\n/* foo\n  bar\n*/\nfunction foo(){ return "bar"; }\n// end', {loc:true});
257             var blockComment = ast.startToken.next;
258             expect( blockComment.range ).to.eql( [1, 16] );
259             expect( blockComment.loc ).to.eql({
260                 start : {
261                     line : 2,
262                     column : 0
263                 },
264                 end : {
265                     line : 4,
266                     column : 2
267                 }
268             });
269             var lineComment = ast.endToken;
270             expect( lineComment.range ).to.eql( [49, 55] );
271             expect( lineComment.loc ).to.eql({
272                 start : {
273                     line : 6,
274                     column : 0
275                 },
276                 end : {
277                     line : 6,
278                     column : 6
279                 }
280             });
281         });
282
283         it('should add originalIndent info to block comments', function () {
284             var ast = rocambole.parse('  /* foo */\n\t\t// bar');
285             expect( ast.startToken.next.originalIndent ).to.be('  ');
286         });
287
288         it('should not add originalIndent info to line comments', function () {
289             var ast = rocambole.parse('  /* foo */\n\t\t// bar');
290             expect( ast.endToken.originalIndent ).to.be(undefined);
291         });
292
293         it('should not add as originalIndent if prev token is not white space', function () {
294             var ast = rocambole.parse('lorem;/* foo */\n\t\t// bar');
295             expect( ast.startToken.next.next.originalIndent ).to.be(undefined);
296         });
297
298         it('should not add as originalIndent if prev token is not on a new line', function () {
299             var ast = rocambole.parse('lorem;  /* foo */\n\t\t// bar');
300             expect( ast.startToken.next.next.next.originalIndent ).to.be(undefined);
301         });
302
303         it('should add as originalIndent if on a new line', function () {
304             var ast = rocambole.parse('lorem;\n  /* foo */\n\t\t// bar');
305             expect( ast.startToken.next.next.next.next.originalIndent ).to.be('  ');
306         });
307     });
308
309
310     describe('export BYPASS_RECURSION', function () {
311         it('should export BYPASS_RECURSION', function () {
312             expect( rocambole.BYPASS_RECURSION.root ).to.be(true);
313         });
314     });
315
316
317     describe('empty program', function () {
318         it('should not throw if program is empty', function () {
319             expect(function(){
320                 rocambole.parse('');
321             }).not.throwError();
322         });
323         it('should return augmented AST', function () {
324             var ast = rocambole.parse('');
325             expect(ast).to.eql({
326                 type: 'Program',
327                 body: [],
328                 range: [0,0],
329                 comments: [],
330                 tokens: [],
331                 // we check toString behavior later
332                 toString: ast.toString,
333                 startToken: null,
334                 endToken: null,
335                 depth: 0
336             });
337         });
338         it('toString should return proper value', function() {
339             var ast = rocambole.parse('');
340             expect(ast.toString()).to.be('');
341         });
342     });
343
344
345     describe('support anything that implements `toString` as input', function () {
346         it('should support arrays', function () {
347             var ast = rocambole.parse([1,2,3]);
348             expect(ast.body[0].toString()).to.eql('1,2,3');
349         });
350         it('should support functions', function () {
351             var ast = rocambole.parse(function doStuff(){
352                 doStuff(1, 2);
353             });
354             expect(ast.body[0].type).to.be('FunctionDeclaration');
355         });
356     });
357
358
359     describe('sparse array', function() {
360       // yes, people shold not be writting code like this, but we should not
361       // bail when that happens
362       it('should not fail on sparse arrays', function() {
363         var ast = rocambole.parse('[,3,[,4]]');
364         expect(ast.toString()).to.eql('[,3,[,4]]');
365         var elements = ast.body[0].expression.elements;
366         expect(elements[0]).to.be(null);
367         expect(elements[1].type).to.be('Literal');
368         expect(elements[1].value).to.be(3);
369       });
370     });
371
372 });
373