module-syntax.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. // ES6 Module syntax tests -- verifies syntax of import and export statements
  6. WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js");
  7. function testModuleScript(source, message, shouldFail = false) {
  8. let testfunc = () => testRunner.LoadModule(source, 'samethread', shouldFail);
  9. if (shouldFail) {
  10. let caught = false;
  11. // We can't use assert.throws here because the SyntaxError used to construct the thrown error
  12. // is from a different context so it won't be strictly equal to our SyntaxError.
  13. try {
  14. testfunc();
  15. } catch(e) {
  16. caught = true;
  17. // Compare toString output of SyntaxError and other context SyntaxError constructor.
  18. assert.areEqual(e.constructor.toString(), SyntaxError.toString(), message);
  19. }
  20. assert.isTrue(caught, `Expected error not thrown: ${message}`);
  21. } else {
  22. assert.doesNotThrow(testfunc, message);
  23. }
  24. }
  25. var tests = [
  26. {
  27. name: "All valid (non-default) export statements",
  28. body: function () {
  29. assert.doesNotThrow(function () { WScript.LoadModuleFile('ValidExportStatements.js', 'samethread'); }, "Valid export statements");
  30. }
  31. },
  32. {
  33. name: "Syntax error return in module top level",
  34. body: function () {
  35. testModuleScript('return;', 'Syntax error if in module\'s top-level', true);
  36. testModuleScript('if (false) return;', 'Syntax error if in module\'s top-level', true);
  37. testModuleScript('if (false) {} else return;', 'Syntax error if in module\'s top-level', true);
  38. testModuleScript('while (false) return;', 'Syntax error if in module\'s top-level', true);
  39. testModuleScript('do return while(false);', 'Syntax error if in module\'s top-level', true);
  40. }
  41. },
  42. {
  43. name: "Valid default export statements",
  44. body: function () {
  45. testModuleScript('export default function () { };', 'Unnamed function expression default export');
  46. testModuleScript('export default function _fn2 () { }', 'Named function expression default export');
  47. testModuleScript('export default function* () { };', 'Unnamed generator function expression default export');
  48. testModuleScript('export default function* _gn2 () { }', 'Named generator function expression default export');
  49. testModuleScript('export default class { };', 'Unnamed class expression default export');
  50. testModuleScript('export default class _cl2 { }', 'Named class default expression export');
  51. testModuleScript('export default 1;', 'Primitive type default export');
  52. testModuleScript('var a; export default a = 10;', 'Variable in assignment expression default export');
  53. testModuleScript('export default () => 3', 'Simple default lambda expression export statement');
  54. testModuleScript('function _default() { }; export default _default', 'Named function statement default export');
  55. testModuleScript('function* g() { }; export default g', 'Named generator function statement default export');
  56. testModuleScript('class c { }; export default c', 'Named class statement default export');
  57. testModuleScript("var _ = { method: function() { return 'method_result'; }, method2: function() { return 'method2_result'; } }; export default _", 'Export object with methods - framework model');
  58. }
  59. },
  60. {
  61. name: "Valid export statements without semicolon",
  62. body: function () {
  63. testModuleScript('export var v0 if (true) { }', 'var declaration export');
  64. testModuleScript('export let l0 if (true) { }', 'let declaration export');
  65. testModuleScript('export const const0 = 0 if (true) { }', 'const declaration export');
  66. testModuleScript('export function f() { } if (true) { }', 'function declaration export');
  67. testModuleScript('export function *g() { } if (true) { }', 'function generator declaration export');
  68. testModuleScript('export var c0 = class { } if (true) { }', 'var with unnamed class expression export');
  69. testModuleScript('export class c1 { } if (true) { }', 'Named class expression export');
  70. testModuleScript('export default function () { } if (true) { }', 'Unnamed function expression default export');
  71. testModuleScript('export default function _fn2 () { } if (true) { }', 'Named function expression default export');
  72. testModuleScript('export default function* () { } if (true) { }', 'Unnamed generator function expression default export');
  73. testModuleScript('export default function* _gn2 () { } if (true) { }', 'Named generator function expression default export');
  74. testModuleScript('export default class { } if (true) { }', 'Unnamed class expression default export');
  75. testModuleScript('export default class _cl2 { } if (true) { }', 'Named class default expression export');
  76. testModuleScript('export default 1 if (true) { }', 'Primitive type default export');
  77. testModuleScript('var a; export default a = 10 if (true) { }', 'Variable in assignment expression default export');
  78. testModuleScript('function _default() { }; export default _default if (true) { }', 'Named function statement default export');
  79. testModuleScript('function* g() { }; export default g if (true) { }', 'Named generator function statement default export');
  80. testModuleScript('class c { }; export default c if (true) { }', 'Named class statement default export');
  81. testModuleScript("var _ = { method: function() { return 'method_result'; }, method2: function() { return 'method2_result'; } }; export default _ if (true) { }", 'Export object with methods - framework model');
  82. }
  83. },
  84. {
  85. name: "Syntax error export statements",
  86. body: function () {
  87. testModuleScript('export const const1;', 'Syntax error if const decl is missing initializer', true);
  88. testModuleScript('function foo() { }; export foo;', "Syntax error if we're trying to export an identifier without default or curly braces", true);
  89. testModuleScript('export function () { }', 'Syntax error if function declaration is missing binding identifier', true);
  90. testModuleScript('export function* () { }', 'Syntax error if generator declaration is missing binding identifier', true);
  91. testModuleScript('export class { }', 'Syntax error if class declaration is missing binding identifier', true);
  92. testModuleScript('function foo() { }; export [ foo ];', 'Syntax error if we use brackets instead of curly braces in export statement', true);
  93. testModuleScript('function foo() { export default function() { } }', 'Syntax error if export statement is in a nested function', true);
  94. testModuleScript('function foo() { }; export { , foo };', 'Syntax error if named export list contains an empty element', true);
  95. testModuleScript('function foo() { }; () => { export { foo }; }', 'Syntax error if export statement is in arrow function', true);
  96. testModuleScript('function foo() { }; try { export { foo }; } catch(e) { }', 'Syntax error if export statement is in try catch statement', true);
  97. testModuleScript('function foo() { }; { export { foo }; }', 'Syntax error if export statement is in any block', true);
  98. testModuleScript('export default 1, 2, 3;', "Export default takes an assignment expression which doesn't allow comma expressions", true);
  99. testModuleScript('export 12;', 'Syntax error if export is followed by non-identifier', true);
  100. testModuleScript("export 'string_constant';", 'Syntax error if export is followed by string constant', true);
  101. testModuleScript('export ', 'Syntax error if export is followed by EOF', true);
  102. testModuleScript('function foo() { }; export { foo as 100 };', 'Syntax error in named export clause if trying to export as numeric constant', true);
  103. testModuleScript('if (false) export default null;', 'Syntax error export in if', true);
  104. testModuleScript('if (false) {} else export default null;', 'Syntax error export after else', true);
  105. testModuleScript('for(var i=0; i<1; i++) export default null;', 'Syntax error export in for', true);
  106. testModuleScript('while(false) export default null;', 'Syntax error export in while', true);
  107. testModuleScript(`do export default null
  108. while (false);`, 'Syntax error export in while', true);
  109. testModuleScript('function () { export default null; }', 'Syntax error export in function', true);
  110. }
  111. },
  112. {
  113. name: "Syntax error import statements",
  114. body: function () {
  115. testModuleScript('function foo() { import foo from "ValidExportStatements.js"; }', 'Syntax error if import statement is in nested function', true);
  116. testModuleScript('import foo, bar from "ValidExportStatements.js";', 'Syntax error if import statement has multiple default bindings', true);
  117. testModuleScript('import { foo, foo } from "ValidExportStatements.js";', 'Redeclaration error if multiple imports have the same local name', true);
  118. testModuleScript('import { foo, bar as foo } from "ValidExportStatements.js";', 'Redeclaration error if multiple imports have the same local name', true);
  119. testModuleScript('const foo = 12; import { foo } from "ValidExportStatements.js";', 'Syntax error if module body has a const declaration bound to the same name as a module import', true);
  120. testModuleScript('function foo() { }; import { foo } from "ValidExportStatements.js";', 'Syntax error if module body has a function declaration bound to the same name as a module import', true);
  121. testModuleScript('import foo;', 'Syntax error if import statement is missing from clause', true);
  122. testModuleScript('import * as foo, from "ValidExportStatements.js";', 'Syntax error if import statement has comma after namespace import', true);
  123. testModuleScript('import * as foo, bar from "ValidExportStatements.js";', 'Syntax error if import statement has default binding after namespace import', true);
  124. testModuleScript('import * as foo, { bar } from "ValidExportStatements.js";', 'Syntax error if import statement has named import list after namespace import', true);
  125. testModuleScript('import { foo }, from "ValidExportStatements.js";', 'Syntax error if import statement has comma after named import list', true);
  126. testModuleScript('import { foo }, bar from "ValidExportStatements.js";', 'Syntax error if import statement has default binding after named import list', true);
  127. testModuleScript('import { foo }, * as ns1 from "ValidExportStatements.js";', 'Syntax error if import statement has namespace import after named import list', true);
  128. testModuleScript('import { foo }', 'Syntax error if import statement is missing from clause', true);
  129. testModuleScript('import [ foo ] from "ValidExportStatements.js";', 'Syntax error if named import clause uses brackets', true);
  130. testModuleScript('import * foo from "ValidExportStatements.js";', 'Syntax error if namespace import is missing "as" keyword', true);
  131. testModuleScript('import * as "foo" from "ValidExportStatements.js";', 'Syntax error if namespace imported binding name is not identifier', true);
  132. testModuleScript('import { , foo } from "ValidExportStatements.js";', 'Syntax error if named import list contains an empty element', true);
  133. testModuleScript('import foo from "ValidExportStatements.js"; import foo from "ValidExportStatements.js";', 'Default import cannot be bound to the same symbol', true);
  134. testModuleScript('import { foo } from "ValidExportStatements.js"; import { foo } from "ValidExportStatements.js";', 'Multiple named imports cannot be bound to the same symbol', true);
  135. testModuleScript('import * as foo from "ValidExportStatements.js"; import * as foo from "ValidExportStatements.js";', 'Multiple namespace imports cannot be bound to the same symbol', true);
  136. testModuleScript('import { foo as bar, bar } from "ValidExportStatements.js";', 'Named import clause may not contain multiple binding identifiers with the same name', true);
  137. testModuleScript('import foo from "ValidExportStatements.js"; import * as foo from "ValidExportStatements.js";', 'Imported bindings cannot be overwritten by later imports', true);
  138. testModuleScript('() => { import arrow from ""; }', 'Syntax error if import statement is in arrow function', true);
  139. testModuleScript('try { import _try from ""; } catch(e) { }', 'Syntax error if import statement is in try catch statement', true);
  140. testModuleScript('{ import in_block from ""; }', 'Syntax error if import statement is in any block', true);
  141. testModuleScript('import {', 'Named import clause which has EOF after left curly', true);
  142. testModuleScript('import { foo', 'Named import clause which has EOF after identifier', true);
  143. testModuleScript('import { foo as ', 'Named import clause which has EOF after identifier as', true);
  144. testModuleScript('import { foo as bar ', 'Named import clause which has EOF after identifier as identifier', true);
  145. testModuleScript('import { foo as bar, ', 'Named import clause which has EOF after identifier as identifier comma', true);
  146. testModuleScript('import { switch } from "module";', 'Named import clause which has non-identifier token as the first token', true);
  147. testModuleScript('import { foo bar } from "module";', 'Named import clause missing "as" token', true);
  148. testModuleScript('import { foo as switch } from "module";', 'Named import clause with non-identifier token after "as"', true);
  149. testModuleScript('import { foo, , } from "module";', 'Named import clause with too many trailing commas', true);
  150. testModuleScript('if (false) import { default } from "module";', 'Syntax error export in if', true);
  151. testModuleScript('if (false) {} import { default } from "module";', 'Syntax error export after else', true);
  152. testModuleScript('for(var i=0; i<1; i++) import { default } from "module";', 'Syntax error export in for', true);
  153. testModuleScript('while(false) import { default } from "module";', 'Syntax error export in while', true);
  154. testModuleScript(`do import { default } from "module"
  155. while (false);`, 'Syntax error export in while', true);
  156. testModuleScript('function () { import { default } from "module"; }', 'Syntax error export in function', true);
  157. }
  158. },
  159. {
  160. name: "Runtime error import statements",
  161. body: function () {
  162. testModuleScript('import foo from "ValidExportStatements.js"; try { (() => { foo = 12; })() } catch(e) { assert.areEqual("Assignment to const", e.message); }', 'Imported default bindings are constant bindings', false);
  163. testModuleScript('import { foo } from "ValidExportStatements.js"; try { (() => { foo = 12; })() } catch(e) { assert.areEqual("Assignment to const", e.message); }', 'Imported named bindings are constant bindings', false);
  164. testModuleScript('import * as foo from "ValidExportStatements.js"; try { (() => { foo = 12; })() } catch(e) { assert.areEqual("Assignment to const", e.message); }', 'Namespace import bindings are constant bindings', false);
  165. testModuleScript('import { foo as foo22 } from "ValidExportStatements.js"; try { (() => { foo22 = 12; })() } catch(e) { assert.areEqual("Assignment to const", e.message); }', 'Renamed import bindings are constant bindings', false);
  166. }
  167. },
  168. {
  169. name: "All valid re-export statements",
  170. body: function () {
  171. assert.doesNotThrow(function () { WScript.LoadModuleFile('ValidReExportStatements.js', 'samethread'); }, "Valid re-export statements");
  172. }
  173. },
  174. {
  175. name: "HTML comments do not parse in module code",
  176. body: function () {
  177. testModuleScript("<!--\n", "HTML open comment does not parse in module code", true);
  178. testModuleScript("\n-->", "HTML close comment does not parse in module code", true);
  179. testModuleScript("<!-- -->", "HTML comment does not parse in module code", true);
  180. testModuleScript("/* */ -->", "HTML comment after delimited comment does not parse in module code", true);
  181. testModuleScript("/* */\n-->", "HTML comment after delimited comment does not parse in module code", true);
  182. }
  183. }
  184. ];
  185. testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });