test-es5.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  1. 'use strict';
  2. /* Simple test script that doesn't need mocha or similar - it just parses stuff and checks the returned AST */
  3. var acorn = require('acorn');
  4. var colors = require('colors');
  5. require('../')(acorn);
  6. function parse(code, pluginOptions, scriptType) {
  7. if (Array.isArray(code)) {
  8. code = code.join('\n');
  9. }
  10. return acorn.parse(code, {
  11. sourceType: scriptType,
  12. ecmaVersion: 8,
  13. locations: true,
  14. ranges: true,
  15. plugins: {
  16. asyncawait: pluginOptions || {}
  17. }
  18. });
  19. }
  20. function isIdentThenFnDecl(ast) {
  21. return ast.body[0].type === 'ExpressionStatement' && ast.body[0].expression.type === 'Identifier' && ast.body[0].expression.name === 'async' && !ast.body[1].async === true && ast.body[1].type == "FunctionDeclaration";
  22. }
  23. function isAsyncFnDecl(ast) {
  24. return ast.body[0].async === true && ast.body[0].type === "FunctionDeclaration";
  25. }
  26. function isAsyncFnExpr(ast) {
  27. return ast.body[0].expression.async === true && ast.body[0].expression.type === "ArrowFunctionExpression";
  28. }
  29. function isExprType(type) {
  30. return function (ast, sourceType) {
  31. return ast.body[0].type === 'ExpressionStatement' && ast.body[0].expression.type === type;
  32. };
  33. }
  34. var tests = [
  35. /* Standard behaviours */
  36. {
  37. desc: "Simple async function",
  38. code: "async function x() { return undefined; }",
  39. pass: function (ast) {
  40. return ast.body[0].async === true;
  41. }
  42. },{
  43. desc: "Simple async function expression",
  44. code: "(async function (){ })",
  45. pass: function (ast) {
  46. return ast.body[0].expression.async === true;
  47. }
  48. },{
  49. desc: "Async function expression call (1)",
  50. code: "(async function (){ }())",
  51. pass: function (ast) {
  52. return ast.body[0].expression.callee.async === true;
  53. }
  54. },{
  55. desc: "Async function expression call (2)",
  56. code: "(async function (){ })()",
  57. pass: function (ast) {
  58. return ast.body[0].expression.callee.async === true;
  59. }
  60. },{
  61. desc: "Await in async is AwaitExpression",
  62. code: "async function x() { await(undefined); await undefined ; }",
  63. pass: function (ast) {
  64. return ast.body[0].body.body[0].expression.type === 'AwaitExpression' && ast.body[0].body.body[1].expression.type === 'AwaitExpression';
  65. }
  66. },{
  67. desc: "Await in function is identifier in 'script', illegal in 'module'",
  68. code: "function x() { await(undefined); }",
  69. pass: function (ast,scriptType) {
  70. return scriptType === 'script'?ast.body[0].body.body[0].expression.callee.name === 'await':ast.indexOf("(1:15)")>=0;
  71. }
  72. },{
  73. desc: "Async method {code}",
  74. code: "var a = {async x(){}}",
  75. pass: function (ast) {
  76. return ast.body[0].declarations[0].init.properties[0].value.async;
  77. }
  78. },{
  79. desc: "Async arrow",
  80. code: "var a = async()=>0",
  81. pass: function (ast) {
  82. return ast.body[0].declarations[0].init.async;
  83. }
  84. },{
  85. desc: "Abbreviated async arrow",
  86. code: "var a = async b=>-b",
  87. pass: function (ast) {
  88. return ast.body[0].declarations[0].init.async;
  89. }
  90. },{
  91. desc: "Parenthesized async arrow is a call",
  92. code: "var a = async(b=>0)",
  93. pass: function (ast) {
  94. return ast.body[0].declarations[0].init.type==='CallExpression';
  95. }
  96. },{
  97. desc: "Await declaration fails in async function",
  98. code: "async function x() { var await; }",
  99. pass: function (ex, scriptType) {
  100. return ex.indexOf("(1:25)")>=0
  101. }
  102. },{
  103. desc: "Await function declaration fails in async function",
  104. code: "async function x() { function await() {} }",
  105. pass: function (ex, scriptType) {
  106. return ex.indexOf("(1:30)")>=0
  107. }
  108. },{
  109. desc: "Await reference fails in async function",
  110. code: "async function x() { return 1+await; }",
  111. pass: function (ex) {
  112. return !!ex.match(/\(1:3[05]\)/);
  113. }
  114. },{
  115. desc: "{code} is an async FunctionExpression",
  116. code: "async ()=>0",
  117. pass: isAsyncFnExpr
  118. },{
  119. desc: "{code} is a CallExpression",
  120. code: "async(()=>0)",
  121. pass: isExprType('CallExpression')
  122. },{
  123. desc: "{code} is an async FunctionDeclaration",
  124. code: "async /* a */ function x(){}",
  125. pass: isAsyncFnDecl
  126. },{
  127. desc: "{code} is a reference to 'async' and a sync FunctionDeclaration",
  128. code: "async /*\n*/function x(){}",
  129. pass: isIdentThenFnDecl
  130. },{
  131. desc: "{code} is a reference to 'async' and a sync FunctionDeclaration",
  132. code: "async /* a */\nfunction x(){}",
  133. pass: isIdentThenFnDecl
  134. },{
  135. desc: "{code} is a reference to 'async' and a sync FunctionDeclaration",
  136. code: "async\nfunction x(){}",
  137. pass: isIdentThenFnDecl
  138. },{
  139. desc: "{code} is a reference to 'async' and a sync FunctionDeclaration",
  140. code: "async //\nfunction x(){}",
  141. pass: isIdentThenFnDecl
  142. },{
  143. desc: "{code} is a reference to 'async' and a sync FunctionDeclaration",
  144. code: "async /*\n*/\nfunction x(){}",
  145. pass: isIdentThenFnDecl
  146. },{
  147. desc: "{code} is a SyntaxError (when inAsyncFunction and awaitAnywhere option are defaults)",
  148. code: "await x",
  149. pass: function (ex, sourceType) {
  150. return sourceType==='module' ? !!ex.match(/\(1:0\)/) : ex === "Unexpected token (1:6)";
  151. }
  152. },{
  153. desc: "{code} is a CallExpression in scripts, and a SyntaxError in modules",
  154. code: "await(x)",
  155. pass: function(ast,sourceType) {
  156. return sourceType==='module'?!!ast.match(/\(1:0\)/) :isExprType('CallExpression')(ast)
  157. }
  158. },{
  159. desc: "Async method 'constructor' is valid",
  160. code: "var a = {async constructor(){}}",
  161. pass: function (ast) {
  162. var props = ast.body[0].declarations[0].init.properties ;
  163. return (props[0].kind === 'init' && props[0].key.name==='constructor' && props[0].value.async)
  164. }
  165. },{
  166. desc: "Async class constructor fails",
  167. code: "class a {async constructor(){}}",
  168. pass: function (ex) {
  169. return !!ex.match(/class constructor\(\) cannot be be async \(1:(15|9)\)/) || ex === "Constructor can't be an async method (1:15)";
  170. }
  171. },{
  172. desc: "Async setter fails",
  173. code: "var a = {async set x(y){}}",
  174. pass: function (ex) {
  175. return ex === "'set <member>(value)' cannot be be async (1:15)" || ex === "Unexpected token (1:19)";
  176. }
  177. },{
  178. desc: "Deprecated async setter fails (use 'async set x')",
  179. code: "var a = {set async x(y){}}",
  180. pass: function (ex) {
  181. return ex === "'set <member>(value)' cannot be be async (1:13)" || ex === "Unexpected token (1:19)";
  182. }
  183. },{
  184. desc: "{code} getters/setters are not async",
  185. code: "var a = {get x(){},set y(z){}}",
  186. pass: function (ast) {
  187. var props = ast.body[0].declarations[0].init.properties ;
  188. return (props[0].kind === 'get' && props[0].key.name==='x' && !props[0].value.async)
  189. && (props[1].kind === 'set' && props[1].key.name==='y' && !props[1].value.async);
  190. }
  191. },{
  192. desc: "{code} are methods, not getters/setters",
  193. code: "var a = {async get(){},async set(){}}",
  194. pass: function (ast) {
  195. var props = ast.body[0].declarations[0].init.properties ;
  196. return (props[0].kind === 'init' && props[0].key.name==='get' && props[0].value.async)
  197. && (props[1].kind === 'init' && props[1].key.name==='set' && props[1].value.async);
  198. }
  199. },{
  200. desc: "In {code}, x is an sync getter",
  201. code: "class a {get x(){}}",
  202. pass: function (ast) {
  203. return ast.body[0].body.body[0].kind==="get" && !ast.body[0].body.body[0].value.async && !ast.body[0].body.body[0].static ;
  204. }
  205. },{
  206. desc: "In {code}, x is an static sync getter",
  207. code: "class a {static get x(){}}",
  208. pass: function (ast) {
  209. return ast.body[0].body.body[0].kind==="get" && !ast.body[0].body.body[0].value.async && ast.body[0].body.body[0].static ;
  210. }
  211. },{
  212. desc: "In {code}, x is an static sync method",
  213. code: "class a {static async x(){}}",
  214. pass: function (ast) {
  215. return ast.body[0].body.body[0].kind==="method" && ast.body[0].body.body[0].value.async && ast.body[0].body.body[0].static ;
  216. }
  217. },{
  218. desc: "{code} are a getters/setters, not methods",
  219. code: "var a = {get async(){},set async(x){}}",
  220. pass: function (ast) {
  221. var props = ast.body[0].declarations[0].init.properties ;
  222. return (props[0].kind === 'get' && props[0].key.name==='async' && !props[0].value.async)
  223. && (props[1].kind === 'set' && props[1].key.name==='async' && !props[1].value.async);
  224. }
  225. },
  226. /* Extended syntax behaviour for Nodent */
  227. {
  228. desc: "Nodent:".grey+" In {code}, get is a static method",
  229. code: "class Foo { static get(v) {} }",
  230. pass: function (ast) {
  231. return ast.body[0].body.body[0].type==='MethodDefinition'
  232. && ast.body[0].body.body[0].key.name === 'get'
  233. && ast.body[0].body.body[0].kind === "method"
  234. && ast.body[0].body.body[0].static;
  235. }
  236. },{
  237. desc: "Nodent:".grey+" In {code}, get is a non-static method",
  238. code: "class Foo { get(v) {} }",
  239. pass: function (ast) {
  240. return ast.body[0].body.body[0].type==='MethodDefinition'
  241. && ast.body[0].body.body[0].key.name === 'get'
  242. && ast.body[0].body.body[0].kind === "method"
  243. && !ast.body[0].body.body[0].static;
  244. }
  245. },{
  246. desc: "Nodent:".grey+" In {code}, get is a non-static getter",
  247. code: "class Foo { get get() {} }",
  248. pass: function (ast) {
  249. return ast.body[0].body.body[0].type==='MethodDefinition'
  250. && ast.body[0].body.body[0].key.name === 'get'
  251. && ast.body[0].body.body[0].kind === "get"
  252. && !ast.body[0].body.body[0].static;
  253. }
  254. },{
  255. desc: "Nodent:".grey+" In {code}, x is an async getter",
  256. code: "var a = {async get x(){ await(0) }}",
  257. pass: function (ast) {
  258. return ast.body[0].declarations[0].init.properties[0].value.async
  259. && ast.body[0].declarations[0].init.properties[0].value.body.body[0].expression.type==='AwaitExpression';
  260. }
  261. },{
  262. desc: "Nodent:".grey+" In {code} (deprecated), x is an async getter",
  263. code: "var a = {get async x(){ await 0 }}",
  264. pass: function (ast) {
  265. return ast.body[0].declarations[0].init.properties[0].value.async
  266. && ast.body[0].declarations[0].init.properties[0].value.body.body[0].expression.type==='AwaitExpression';
  267. }
  268. },{
  269. desc: "Nodent:".grey+" In {code} (deprecated), x is an async getter",
  270. code: "var a = {get async x(){ await(0) }}",
  271. pass: function (ast) {
  272. return ast.body[0].declarations[0].init.properties[0].value.async
  273. && ast.body[0].declarations[0].init.properties[0].value.body.body[0].expression.type==='AwaitExpression';
  274. }
  275. },{
  276. desc: "Nodent:".grey+" In {code}, x is an async getter",
  277. code: "class a {async get x(){ await 0 }}",
  278. pass: function (ast) {
  279. return ast.body[0].body.body[0].value.async
  280. && ast.body[0].body.body[0].value.body.body[0].expression.type==='AwaitExpression';
  281. }
  282. },{
  283. desc: "Nodent:".grey+" In {code}, x is an async getter",
  284. code: "class a {async get x(){ await(0) }}",
  285. pass: function (ast) {
  286. return ast.body[0].body.body[0].value.async
  287. && ast.body[0].body.body[0].value.body.body[0].expression.type==='AwaitExpression';
  288. }
  289. },{
  290. desc: "Nodent:".grey+" In {code} (deprecated), x is an async getter",
  291. code: "class a {get async x(){ await 0 }}",
  292. pass: function (ast) {
  293. return ast.body[0].body.body[0].value.async
  294. && ast.body[0].body.body[0].value.body.body[0].expression.type==='AwaitExpression';
  295. }
  296. },{
  297. desc: "Nodent:".grey+" In {code} (deprecated), x is an async getter",
  298. code: "class a {get async x(){ await(0) }}",
  299. pass: function (ast) {
  300. return ast.body[0].body.body[0].value.async
  301. && ast.body[0].body.body[0].value.body.body[0].expression.type==='AwaitExpression';
  302. }
  303. },{
  304. desc: "Nodent:".grey+" In {code}, x is an static async getter",
  305. code: "class a {static async get x(){}}",
  306. pass: function (ast) {
  307. return ast.body[0].body.body[0].kind==="get" && ast.body[0].body.body[0].value.async && ast.body[0].body.body[0].static ;
  308. }
  309. },{
  310. desc: "Nodent:".grey+" In {code} (deprecated), x is an static async getter",
  311. code: "class a {static get async x(){}}",
  312. pass: function (ast) {
  313. return ast.body[0].body.body[0].kind==="get" && ast.body[0].body.body[0].value.async && ast.body[0].body.body[0].static ;
  314. }
  315. },{
  316. desc: "Nodent:".grey+" {code} is an AwaitExpression when inAsyncFunction option is true",
  317. code: "await(x)",
  318. options: {
  319. inAsyncFunction: true
  320. },
  321. pass: isExprType('AwaitExpression')
  322. },{
  323. desc: "Nodent:".grey+" {code} is an AwaitExpression when inAsyncFunction option is true",
  324. code: "await x",
  325. options: {
  326. inAsyncFunction: true
  327. },
  328. pass: isExprType('AwaitExpression')
  329. },{
  330. desc: "Nodent:".grey+" {code} is a CallExpression when awaitAnywhere option is true",
  331. code: "await(x)",
  332. options: {
  333. awaitAnywhere: true
  334. },
  335. pass: isExprType('CallExpression')
  336. },{
  337. desc: "Nodent:".grey+" {code} is an AwaitExpression when awaitAnywhere option is true",
  338. code: "await x",
  339. options: {
  340. awaitAnywhere: true
  341. },
  342. pass: isExprType('AwaitExpression')
  343. }];
  344. // TODO: Add tests for asyncExits, noAsyncGetters
  345. var out = {
  346. true: "pass".green,
  347. false: "fail".red
  348. };
  349. var testNumber = +process.argv[2] || 0;
  350. if (testNumber) {
  351. tests = [tests[testNumber - 1]];
  352. } else {
  353. testNumber += 1;
  354. }
  355. var results = {
  356. true: 0,
  357. false: 0
  358. };
  359. tests.forEach(function (test, idx) {
  360. ['script','module'].forEach(function(scriptType){
  361. var code = test.code.replace(/\n/g, ' <linefeed> ');
  362. var desc = test.desc.replace('{code}', code.yellow);
  363. var pass = function () {
  364. var p = test.pass.apply(this, arguments);
  365. results[p] += 1;
  366. return p;
  367. };
  368. var prefix = idx + testNumber + " (" + scriptType + ", acorn v" + acorn.version+")\t" ;
  369. try {
  370. console.log(prefix, desc, out[pass(parse(test.code, test.options, scriptType),scriptType)]);
  371. } catch (ex) {
  372. try {
  373. console.log(prefix, desc, ex.message.cyan, out[pass(ex.message,scriptType)]);
  374. } catch (ex) {
  375. console.log(prefix, desc, ex.message.magenta, out[false]);
  376. results.false += 1;
  377. }
  378. }
  379. });
  380. }) ;
  381. console.log('');
  382. if (results.true)
  383. console.log((results.true + " of " + tests.length*2 + " tests passed").green);
  384. if (results.false) {
  385. console.log((results.false + " of " + tests.length*2 + " tests failed").red);
  386. var exit = new Error("Test failed") ;
  387. exit.stack = "" ;
  388. throw exit ;
  389. }