1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
28 |
|
29 |
|
30 |
|
31 |
|
32 |
|
33 |
|
34 | var runTests;
|
35 |
|
36 |
|
37 |
|
38 |
|
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 |
|
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 needLoc(syntax) {
|
75 | var need = true;
|
76 | if (typeof syntax.tokens !== 'undefined' && syntax.tokens.length > 0) {
|
77 | need = (typeof syntax.tokens[0].loc !== 'undefined');
|
78 | }
|
79 | if (typeof syntax.comments !== 'undefined' && syntax.comments.length > 0) {
|
80 | need = (typeof syntax.comments[0].loc !== 'undefined');
|
81 | }
|
82 | return need;
|
83 | }
|
84 |
|
85 | function needRange(syntax) {
|
86 | var need = true;
|
87 | if (typeof syntax.tokens !== 'undefined' && syntax.tokens.length > 0) {
|
88 | need = (typeof syntax.tokens[0].range !== 'undefined');
|
89 | }
|
90 | if (typeof syntax.comments !== 'undefined' && syntax.comments.length > 0) {
|
91 | need = (typeof syntax.comments[0].range !== 'undefined');
|
92 | }
|
93 | return need;
|
94 | }
|
95 |
|
96 | function testParse(esprima, code, syntax) {
|
97 | 'use strict';
|
98 | var expected, tree, actual, options, StringObject, i, len, err;
|
99 |
|
100 |
|
101 | StringObject = String;
|
102 |
|
103 | options = {
|
104 | comment: (typeof syntax.comments !== 'undefined'),
|
105 | range: needRange(syntax),
|
106 | loc: needLoc(syntax),
|
107 | tokens: (typeof syntax.tokens !== 'undefined'),
|
108 | raw: true,
|
109 | tolerant: (typeof syntax.errors !== 'undefined'),
|
110 | source: null
|
111 | };
|
112 |
|
113 | if (options.loc) {
|
114 | options.source = syntax.loc.source;
|
115 | }
|
116 |
|
117 | expected = JSON.stringify(syntax, null, 4);
|
118 | try {
|
119 | tree = esprima.parse(code, options);
|
120 | tree = (options.comment || options.tokens || options.tolerant) ? tree : tree.body[0];
|
121 |
|
122 | if (options.tolerant) {
|
123 | for (i = 0, len = tree.errors.length; i < len; i += 1) {
|
124 | tree.errors[i] = errorToObject(tree.errors[i]);
|
125 | }
|
126 | }
|
127 |
|
128 | actual = JSON.stringify(tree, adjustRegexLiteral, 4);
|
129 |
|
130 |
|
131 | esprima.parse(new StringObject(code), options);
|
132 |
|
133 | } catch (e) {
|
134 | throw new NotMatchingError(expected, e.toString());
|
135 | }
|
136 | if (expected !== actual) {
|
137 | throw new NotMatchingError(expected, actual);
|
138 | }
|
139 |
|
140 | function filter(key, value) {
|
141 | if (key === 'value' && value instanceof RegExp) {
|
142 | value = value.toString();
|
143 | }
|
144 | return (key === 'loc' || key === 'range') ? undefined : value;
|
145 | }
|
146 |
|
147 | if (options.tolerant) {
|
148 | return;
|
149 | }
|
150 |
|
151 |
|
152 |
|
153 | options.range = false;
|
154 | options.loc = false;
|
155 | expected = JSON.stringify(syntax, filter, 4);
|
156 | try {
|
157 | tree = esprima.parse(code, options);
|
158 | tree = (options.comment || options.tokens) ? tree : tree.body[0];
|
159 |
|
160 | if (options.tolerant) {
|
161 | for (i = 0, len = tree.errors.length; i < len; i += 1) {
|
162 | tree.errors[i] = errorToObject(tree.errors[i]);
|
163 | }
|
164 | }
|
165 |
|
166 | actual = JSON.stringify(tree, filter, 4);
|
167 | } catch (e) {
|
168 | throw new NotMatchingError(expected, e.toString());
|
169 | }
|
170 | if (expected !== actual) {
|
171 | throw new NotMatchingError(expected, actual);
|
172 | }
|
173 | }
|
174 |
|
175 | function mustHaveLocRange(testName, node, needLoc, needRange, stack) {
|
176 | var error;
|
177 | if (node.hasOwnProperty('type')) {
|
178 | if (needLoc && !node.loc) {
|
179 | error = "doesn't have 'loc' property";
|
180 | }
|
181 | if (needRange && !node.range) {
|
182 | error = "doesn't have 'range' property";
|
183 | }
|
184 | if (error) {
|
185 | stack = stack.length ? ' at [' + stack.join('][') + ']' : '';
|
186 | throw new Error("Test '" + testName + "'" + stack + " (type = " + node.type + ") " + error);
|
187 | }
|
188 | }
|
189 | for (i in node) {
|
190 | if (node.hasOwnProperty(i) && node[i] !== null && typeof node[i] === 'object') {
|
191 | stack.push(i);
|
192 | mustHaveLocRange(testName, node[i], needLoc, needRange, stack);
|
193 | stack.pop();
|
194 | }
|
195 | }
|
196 | }
|
197 |
|
198 | function testTokenize(esprima, code, tokens) {
|
199 | 'use strict';
|
200 | var options, expected, actual, tree;
|
201 |
|
202 | options = {
|
203 | comment: true,
|
204 | tolerant: true,
|
205 | loc: true,
|
206 | range: true
|
207 | };
|
208 |
|
209 | expected = JSON.stringify(tokens, null, 4);
|
210 |
|
211 | try {
|
212 | tree = esprima.tokenize(code, options);
|
213 | actual = JSON.stringify(tree, null, 4);
|
214 | } catch (e) {
|
215 | throw new NotMatchingError(expected, e.toString());
|
216 | }
|
217 | if (expected !== actual) {
|
218 | throw new NotMatchingError(expected, actual);
|
219 | }
|
220 | }
|
221 |
|
222 | function testError(esprima, code, exception) {
|
223 | 'use strict';
|
224 | var i, options, expected, actual, err, handleInvalidRegexFlag, tokenize;
|
225 |
|
226 |
|
227 | options = [
|
228 | {},
|
229 | { comment: true },
|
230 | { raw: true },
|
231 | { raw: true, comment: true }
|
232 | ];
|
233 |
|
234 |
|
235 |
|
236 |
|
237 | handleInvalidRegexFlag = false;
|
238 | try {
|
239 | 'test'.match(new RegExp('[a-z]', 'x'));
|
240 | } catch (e) {
|
241 | handleInvalidRegexFlag = true;
|
242 | }
|
243 |
|
244 | exception.description = exception.message.replace(/Error: Line [0-9]+: /, '');
|
245 |
|
246 | if (exception.tokenize) {
|
247 | tokenize = true;
|
248 | exception.tokenize = undefined;
|
249 | }
|
250 | expected = JSON.stringify(exception);
|
251 |
|
252 | for (i = 0; i < options.length; i += 1) {
|
253 |
|
254 | try {
|
255 | if (tokenize) {
|
256 | esprima.tokenize(code, options[i])
|
257 | } else {
|
258 | esprima.parse(code, options[i]);
|
259 | }
|
260 | } catch (e) {
|
261 | err = errorToObject(e);
|
262 | err.description = e.description;
|
263 | actual = JSON.stringify(err);
|
264 | }
|
265 |
|
266 | if (expected !== actual) {
|
267 |
|
268 |
|
269 | if (exception.message.indexOf('Invalid regular expression') > 0) {
|
270 | if (typeof actual === 'undefined' && !handleInvalidRegexFlag) {
|
271 | return;
|
272 | }
|
273 | }
|
274 |
|
275 | throw new NotMatchingError(expected, actual);
|
276 | }
|
277 |
|
278 | }
|
279 | }
|
280 |
|
281 | function testAPI(esprima, code, result) {
|
282 | 'use strict';
|
283 | var expected, res, actual;
|
284 |
|
285 | expected = JSON.stringify(result.result, null, 4);
|
286 | try {
|
287 | if (typeof result.property !== 'undefined') {
|
288 | res = esprima[result.property];
|
289 | } else {
|
290 | res = esprima[result.call].apply(esprima, result.args);
|
291 | }
|
292 | actual = JSON.stringify(res, adjustRegexLiteral, 4);
|
293 | } catch (e) {
|
294 | throw new NotMatchingError(expected, e.toString());
|
295 | }
|
296 | if (expected !== actual) {
|
297 | throw new NotMatchingError(expected, actual);
|
298 | }
|
299 | }
|
300 |
|
301 | function runTest(esprima, code, result) {
|
302 | 'use strict';
|
303 | if (result.hasOwnProperty('lineNumber')) {
|
304 | testError(esprima, code, result);
|
305 | } else if (result.hasOwnProperty('result')) {
|
306 | testAPI(esprima, code, result);
|
307 | } else if (result instanceof Array) {
|
308 | testTokenize(esprima, code, result);
|
309 | } else {
|
310 | testParse(esprima, code, result);
|
311 | }
|
312 | }
|
313 |
|
314 | if (typeof window !== 'undefined') {
|
315 |
|
316 | runTests = function () {
|
317 | 'use strict';
|
318 | var total = 0,
|
319 | failures = 0,
|
320 | category,
|
321 | fixture,
|
322 | source,
|
323 | tick,
|
324 | expected,
|
325 | index,
|
326 | len;
|
327 |
|
328 | function setText(el, str) {
|
329 | if (typeof el.innerText === 'string') {
|
330 | el.innerText = str;
|
331 | } else {
|
332 | el.textContent = str;
|
333 | }
|
334 | }
|
335 |
|
336 | function startCategory(category) {
|
337 | var report, e;
|
338 | report = document.getElementById('report');
|
339 | e = document.createElement('h4');
|
340 | setText(e, category);
|
341 | report.appendChild(e);
|
342 | }
|
343 |
|
344 | function reportSuccess(code) {
|
345 | var report, e;
|
346 | report = document.getElementById('report');
|
347 | e = document.createElement('pre');
|
348 | e.setAttribute('class', 'code');
|
349 | setText(e, code);
|
350 | report.appendChild(e);
|
351 | }
|
352 |
|
353 | function reportFailure(code, expected, actual) {
|
354 | var report, e;
|
355 |
|
356 | report = document.getElementById('report');
|
357 |
|
358 | e = document.createElement('p');
|
359 | setText(e, 'Code:');
|
360 | report.appendChild(e);
|
361 |
|
362 | e = document.createElement('pre');
|
363 | e.setAttribute('class', 'code');
|
364 | setText(e, code);
|
365 | report.appendChild(e);
|
366 |
|
367 | e = document.createElement('p');
|
368 | setText(e, 'Expected');
|
369 | report.appendChild(e);
|
370 |
|
371 | e = document.createElement('pre');
|
372 | e.setAttribute('class', 'expected');
|
373 | setText(e, expected);
|
374 | report.appendChild(e);
|
375 |
|
376 | e = document.createElement('p');
|
377 | setText(e, 'Actual');
|
378 | report.appendChild(e);
|
379 |
|
380 | e = document.createElement('pre');
|
381 | e.setAttribute('class', 'actual');
|
382 | setText(e, actual);
|
383 | report.appendChild(e);
|
384 | }
|
385 |
|
386 | setText(document.getElementById('version'), esprima.version);
|
387 |
|
388 | tick = new Date();
|
389 | for (category in testFixture) {
|
390 | if (testFixture.hasOwnProperty(category)) {
|
391 | startCategory(category);
|
392 | fixture = testFixture[category];
|
393 | for (source in fixture) {
|
394 | if (fixture.hasOwnProperty(source)) {
|
395 | expected = fixture[source];
|
396 | total += 1;
|
397 | try {
|
398 | runTest(esprima, source, expected);
|
399 | reportSuccess(source, JSON.stringify(expected, null, 4));
|
400 | } catch (e) {
|
401 | failures += 1;
|
402 | reportFailure(source, e.expected, e.actual);
|
403 | }
|
404 | }
|
405 | }
|
406 | }
|
407 | }
|
408 | tick = (new Date()) - tick;
|
409 |
|
410 | if (failures > 0) {
|
411 | document.getElementById('status').className = 'alert-box alert';
|
412 | setText(document.getElementById('status'), total + ' tests. ' +
|
413 | 'Failures: ' + failures + '. ' + tick + ' ms.');
|
414 | } else {
|
415 | document.getElementById('status').className = 'alert-box success';
|
416 | setText(document.getElementById('status'), total + ' tests. ' +
|
417 | 'No failure. ' + tick + ' ms.');
|
418 | }
|
419 | };
|
420 | } else {
|
421 | (function () {
|
422 | 'use strict';
|
423 |
|
424 | var esprima = require('../esprima'),
|
425 | vm = require('vm'),
|
426 | fs = require('fs'),
|
427 | diff = require('json-diff').diffString,
|
428 | total = 0,
|
429 | failures = [],
|
430 | tick = new Date(),
|
431 | expected,
|
432 | header;
|
433 |
|
434 | vm.runInThisContext(fs.readFileSync(__dirname + '/test.js', 'utf-8'));
|
435 | vm.runInThisContext(fs.readFileSync(__dirname + '/harmonytest.js', 'utf-8'));
|
436 | vm.runInThisContext(fs.readFileSync(__dirname + '/fbtest.js', 'utf-8'));
|
437 |
|
438 | Object.keys(testFixture).forEach(function (category) {
|
439 | Object.keys(testFixture[category]).forEach(function (source) {
|
440 | total += 1;
|
441 | expected = testFixture[category][source];
|
442 | if (!expected.hasOwnProperty('lineNumber') && !expected.hasOwnProperty('result')) {
|
443 | mustHaveLocRange(source, expected, needLoc(expected), needRange(expected), []);
|
444 | }
|
445 | try {
|
446 | runTest(esprima, source, expected);
|
447 | } catch (e) {
|
448 | e.source = source;
|
449 | failures.push(e);
|
450 | }
|
451 | });
|
452 | });
|
453 | tick = (new Date()) - tick;
|
454 |
|
455 | header = total + ' tests. ' + failures.length + ' failures. ' +
|
456 | tick + ' ms';
|
457 | if (failures.length) {
|
458 | console.error(header);
|
459 | failures.forEach(function (failure) {
|
460 | try {
|
461 | var expectedObject = JSON.parse(failure.expected);
|
462 | var actualObject = JSON.parse(failure.actual);
|
463 |
|
464 | console.error(failure.source + ': Expected\n ' +
|
465 | failure.expected.split('\n').join('\n ') +
|
466 | '\nto match\n ' + failure.actual + '\nDiff:\n' +
|
467 | diff(expectedObject, actualObject));
|
468 | } catch (ex) {
|
469 | console.error(failure.source + ': Expected\n ' +
|
470 | failure.expected.split('\n').join('\n ') +
|
471 | '\nto match\n ' + failure.actual);
|
472 | }
|
473 | });
|
474 | } else {
|
475 | console.log(header);
|
476 | }
|
477 | process.exit(failures.length === 0 ? 0 : 1);
|
478 | }());
|
479 | }
|