Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
80621 views
1
/*
2
Copyright (C) 2012 Ariya Hidayat <[email protected]>
3
Copyright (C) 2012 Joost-Wim Boekesteijn <[email protected]>
4
Copyright (C) 2012 Yusuke Suzuki <[email protected]>
5
Copyright (C) 2012 Arpad Borsos <[email protected]>
6
Copyright (C) 2011 Ariya Hidayat <[email protected]>
7
Copyright (C) 2011 Yusuke Suzuki <[email protected]>
8
Copyright (C) 2011 Arpad Borsos <[email protected]>
9
10
Redistribution and use in source and binary forms, with or without
11
modification, are permitted provided that the following conditions are met:
12
13
* Redistributions of source code must retain the above copyright
14
notice, this list of conditions and the following disclaimer.
15
* Redistributions in binary form must reproduce the above copyright
16
notice, this list of conditions and the following disclaimer in the
17
documentation and/or other materials provided with the distribution.
18
19
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22
ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
23
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
*/
30
31
/*jslint browser:true node:true */
32
/*global esprima:true, testFixture:true */
33
34
var runTests;
35
36
// Special handling for regular expression literal since we need to
37
// convert it to a string literal, otherwise it will be decoded
38
// as object "{}" and the regular expression would be lost.
39
function adjustRegexLiteral(key, value) {
40
'use strict';
41
if (key === 'value' && value instanceof RegExp) {
42
value = value.toString();
43
}
44
return value;
45
}
46
47
function NotMatchingError(expected, actual) {
48
'use strict';
49
Error.call(this, 'Expected ');
50
this.expected = expected;
51
this.actual = actual;
52
}
53
NotMatchingError.prototype = new Error();
54
55
function errorToObject(e) {
56
'use strict';
57
var msg = e.toString();
58
59
// Opera 9.64 produces an non-standard string in toString().
60
if (msg.substr(0, 6) !== 'Error:') {
61
if (typeof e.message === 'string') {
62
msg = 'Error: ' + e.message;
63
}
64
}
65
66
return {
67
index: e.index,
68
lineNumber: e.lineNumber,
69
column: e.column,
70
message: msg
71
};
72
}
73
74
function sortedObject(o) {
75
if (o === null) {
76
return o;
77
}
78
if (o instanceof Array) {
79
return o.map(sortedObject);
80
}
81
if (typeof o !== 'object') {
82
return o;
83
}
84
if (o instanceof RegExp) {
85
return o;
86
}
87
var keys = Object.keys(o);
88
var result = {
89
range: undefined,
90
loc: undefined
91
};
92
keys.forEach(function (key) {
93
if (o.hasOwnProperty(key)){
94
result[key] = sortedObject(o[key]);
95
}
96
});
97
return result;
98
}
99
100
function hasAttachedComment(syntax) {
101
var key;
102
for (key in syntax) {
103
if (key === 'leadingComments' || key === 'trailingComments') {
104
return true;
105
}
106
if (typeof syntax[key] === 'object' && syntax[key] !== null) {
107
if (hasAttachedComment(syntax[key])) {
108
return true;
109
}
110
}
111
}
112
return false;
113
}
114
115
function testParse(esprima, code, syntax) {
116
'use strict';
117
var expected, tree, actual, options, StringObject, i, len, err;
118
119
// alias, so that JSLint does not complain.
120
StringObject = String;
121
122
options = {
123
comment: (typeof syntax.comments !== 'undefined'),
124
range: true,
125
loc: true,
126
tokens: (typeof syntax.tokens !== 'undefined'),
127
raw: true,
128
tolerant: (typeof syntax.errors !== 'undefined'),
129
source: null
130
};
131
132
if (options.comment) {
133
options.attachComment = hasAttachedComment(syntax);
134
}
135
136
if (typeof syntax.tokens !== 'undefined') {
137
if (syntax.tokens.length > 0) {
138
options.range = (typeof syntax.tokens[0].range !== 'undefined');
139
options.loc = (typeof syntax.tokens[0].loc !== 'undefined');
140
}
141
}
142
143
if (typeof syntax.comments !== 'undefined') {
144
if (syntax.comments.length > 0) {
145
options.range = (typeof syntax.comments[0].range !== 'undefined');
146
options.loc = (typeof syntax.comments[0].loc !== 'undefined');
147
}
148
}
149
150
if (options.loc) {
151
options.source = syntax.loc.source;
152
}
153
154
syntax = sortedObject(syntax);
155
expected = JSON.stringify(syntax, null, 4);
156
try {
157
// Some variations of the options.
158
tree = esprima.parse(code, { tolerant: options.tolerant });
159
tree = esprima.parse(code, { tolerant: options.tolerant, range: true });
160
tree = esprima.parse(code, { tolerant: options.tolerant, loc: true });
161
162
tree = esprima.parse(code, options);
163
tree = (options.comment || options.tokens || options.tolerant) ? tree : tree.body[0];
164
165
if (options.tolerant) {
166
for (i = 0, len = tree.errors.length; i < len; i += 1) {
167
tree.errors[i] = errorToObject(tree.errors[i]);
168
}
169
}
170
tree = sortedObject(tree);
171
actual = JSON.stringify(tree, adjustRegexLiteral, 4);
172
173
// Only to ensure that there is no error when using string object.
174
esprima.parse(new StringObject(code), options);
175
176
} catch (e) {
177
throw new NotMatchingError(expected, e.toString());
178
}
179
if (expected !== actual) {
180
throw new NotMatchingError(expected, actual);
181
}
182
183
function filter(key, value) {
184
if (key === 'value' && value instanceof RegExp) {
185
value = value.toString();
186
}
187
return (key === 'loc' || key === 'range') ? undefined : value;
188
}
189
190
if (options.tolerant) {
191
return;
192
}
193
194
195
// Check again without any location info.
196
options.range = false;
197
options.loc = false;
198
syntax = sortedObject(syntax);
199
expected = JSON.stringify(syntax, filter, 4);
200
try {
201
tree = esprima.parse(code, options);
202
tree = (options.comment || options.tokens) ? tree : tree.body[0];
203
204
if (options.tolerant) {
205
for (i = 0, len = tree.errors.length; i < len; i += 1) {
206
tree.errors[i] = errorToObject(tree.errors[i]);
207
}
208
}
209
tree = sortedObject(tree);
210
actual = JSON.stringify(tree, filter, 4);
211
} catch (e) {
212
throw new NotMatchingError(expected, e.toString());
213
}
214
if (expected !== actual) {
215
throw new NotMatchingError(expected, actual);
216
}
217
}
218
219
function testTokenize(esprima, code, tokens) {
220
'use strict';
221
var options, expected, actual, tree;
222
223
options = {
224
comment: true,
225
tolerant: true,
226
loc: true,
227
range: true
228
};
229
230
expected = JSON.stringify(tokens, null, 4);
231
232
try {
233
tree = esprima.tokenize(code, options);
234
actual = JSON.stringify(tree, null, 4);
235
} catch (e) {
236
throw new NotMatchingError(expected, e.toString());
237
}
238
if (expected !== actual) {
239
throw new NotMatchingError(expected, actual);
240
}
241
}
242
243
function testError(esprima, code, exception) {
244
'use strict';
245
var i, options, expected, actual, err, handleInvalidRegexFlag, tokenize;
246
247
// Different parsing options should give the same error.
248
options = [
249
{},
250
{ comment: true },
251
{ raw: true },
252
{ raw: true, comment: true }
253
];
254
255
// If handleInvalidRegexFlag is true, an invalid flag in a regular expression
256
// will throw an exception. In some old version V8, this is not the case
257
// and hence handleInvalidRegexFlag is false.
258
handleInvalidRegexFlag = false;
259
try {
260
'test'.match(new RegExp('[a-z]', 'x'));
261
} catch (e) {
262
handleInvalidRegexFlag = true;
263
}
264
265
exception.description = exception.message.replace(/Error: Line [0-9]+: /, '');
266
267
if (exception.tokenize) {
268
tokenize = true;
269
exception.tokenize = undefined;
270
}
271
expected = JSON.stringify(exception);
272
273
for (i = 0; i < options.length; i += 1) {
274
275
try {
276
if (tokenize) {
277
esprima.tokenize(code, options[i])
278
} else {
279
esprima.parse(code, options[i]);
280
}
281
} catch (e) {
282
err = errorToObject(e);
283
err.description = e.description;
284
actual = JSON.stringify(err);
285
}
286
287
if (expected !== actual) {
288
289
// Compensate for old V8 which does not handle invalid flag.
290
if (exception.message.indexOf('Invalid regular expression') > 0) {
291
if (typeof actual === 'undefined' && !handleInvalidRegexFlag) {
292
return;
293
}
294
}
295
296
throw new NotMatchingError(expected, actual);
297
}
298
299
}
300
}
301
302
function testAPI(esprima, code, result) {
303
'use strict';
304
var expected, res, actual;
305
306
expected = JSON.stringify(result.result, null, 4);
307
try {
308
if (typeof result.property !== 'undefined') {
309
res = esprima[result.property];
310
} else {
311
res = esprima[result.call].apply(esprima, result.args);
312
}
313
actual = JSON.stringify(res, adjustRegexLiteral, 4);
314
} catch (e) {
315
throw new NotMatchingError(expected, e.toString());
316
}
317
if (expected !== actual) {
318
throw new NotMatchingError(expected, actual);
319
}
320
}
321
322
function runTest(esprima, code, result) {
323
'use strict';
324
if (result.hasOwnProperty('lineNumber')) {
325
testError(esprima, code, result);
326
} else if (result.hasOwnProperty('result')) {
327
testAPI(esprima, code, result);
328
} else if (result instanceof Array) {
329
testTokenize(esprima, code, result);
330
} else {
331
testParse(esprima, code, result);
332
}
333
}
334
335
if (typeof window !== 'undefined') {
336
// Run all tests in a browser environment.
337
runTests = function () {
338
'use strict';
339
var total = 0,
340
failures = 0,
341
category,
342
fixture,
343
source,
344
tick,
345
expected,
346
index,
347
len;
348
349
function setText(el, str) {
350
if (typeof el.innerText === 'string') {
351
el.innerText = str;
352
} else {
353
el.textContent = str;
354
}
355
}
356
357
function startCategory(category) {
358
var report, e;
359
report = document.getElementById('report');
360
e = document.createElement('h4');
361
setText(e, category);
362
report.appendChild(e);
363
}
364
365
function reportSuccess(code) {
366
var report, e;
367
report = document.getElementById('report');
368
e = document.createElement('pre');
369
e.setAttribute('class', 'code');
370
setText(e, code);
371
report.appendChild(e);
372
}
373
374
function reportFailure(code, expected, actual) {
375
var report, e;
376
377
report = document.getElementById('report');
378
379
e = document.createElement('p');
380
setText(e, 'Code:');
381
report.appendChild(e);
382
383
e = document.createElement('pre');
384
e.setAttribute('class', 'code');
385
setText(e, code);
386
report.appendChild(e);
387
388
e = document.createElement('p');
389
setText(e, 'Expected');
390
report.appendChild(e);
391
392
e = document.createElement('pre');
393
e.setAttribute('class', 'expected');
394
setText(e, expected);
395
report.appendChild(e);
396
397
e = document.createElement('p');
398
setText(e, 'Actual');
399
report.appendChild(e);
400
401
e = document.createElement('pre');
402
e.setAttribute('class', 'actual');
403
setText(e, actual);
404
report.appendChild(e);
405
}
406
407
setText(document.getElementById('version'), esprima.version);
408
409
tick = new Date();
410
for (category in testFixture) {
411
if (testFixture.hasOwnProperty(category)) {
412
startCategory(category);
413
fixture = testFixture[category];
414
for (source in fixture) {
415
if (fixture.hasOwnProperty(source)) {
416
expected = fixture[source];
417
total += 1;
418
try {
419
runTest(esprima, source, expected);
420
reportSuccess(source, JSON.stringify(expected, null, 4));
421
} catch (e) {
422
failures += 1;
423
reportFailure(source, e.expected, e.actual);
424
}
425
}
426
}
427
}
428
}
429
tick = (new Date()) - tick;
430
431
if (failures > 0) {
432
document.getElementById('status').className = 'alert-box alert';
433
setText(document.getElementById('status'), total + ' tests. ' +
434
'Failures: ' + failures + '. ' + tick + ' ms.');
435
} else {
436
document.getElementById('status').className = 'alert-box success';
437
setText(document.getElementById('status'), total + ' tests. ' +
438
'No failure. ' + tick + ' ms.');
439
}
440
};
441
} else {
442
(function () {
443
'use strict';
444
445
var esprima = require('../esprima'),
446
vm = require('vm'),
447
fs = require('fs'),
448
diff = require('json-diff').diffString,
449
total = 0,
450
failures = [],
451
tick = new Date(),
452
expected,
453
header;
454
455
vm.runInThisContext(fs.readFileSync(__dirname + '/test.js', 'utf-8'));
456
457
Object.keys(testFixture).forEach(function (category) {
458
Object.keys(testFixture[category]).forEach(function (source) {
459
total += 1;
460
expected = testFixture[category][source];
461
try {
462
runTest(esprima, source, expected);
463
} catch (e) {
464
e.source = source;
465
failures.push(e);
466
}
467
});
468
});
469
tick = (new Date()) - tick;
470
471
header = total + ' tests. ' + failures.length + ' failures. ' +
472
tick + ' ms';
473
if (failures.length) {
474
console.error(header);
475
failures.forEach(function (failure) {
476
try {
477
var expectedObject = JSON.parse(failure.expected);
478
var actualObject = JSON.parse(failure.actual);
479
480
console.error(failure.source + ': Expected\n ' +
481
failure.expected.split('\n').join('\n ') +
482
'\nto match\n ' + failure.actual + '\nDiff:\n' +
483
diff(expectedObject, actualObject));
484
} catch (ex) {
485
console.error(failure.source + ': Expected\n ' +
486
failure.expected.split('\n').join('\n ') +
487
'\nto match\n ' + failure.actual);
488
}
489
});
490
} else {
491
console.log(header);
492
}
493
process.exit(failures.length === 0 ? 0 : 1);
494
}());
495
}
496
497