dynamic-module-functionality.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  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 functionality tests -- verifies functionality of import and export statements
  6. WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js");
  7. function testScript(source, message, shouldFail = false, explicitAsync = false) {
  8. message += " (script)";
  9. let testfunc = () => testRunner.LoadScript(source, undefined, shouldFail, explicitAsync);
  10. if (shouldFail) {
  11. let caught = false;
  12. assert.throws(testfunc, SyntaxErrr, message);
  13. assert.isTrue(caught, `Expected error not thrown: ${message}`);
  14. } else {
  15. assert.doesNotThrow(testfunc, message);
  16. }
  17. }
  18. function testModuleScript(source, message, shouldFail = false, explicitAsync = false) {
  19. message += " (module)";
  20. let testfunc = () => testRunner.LoadModule(source, 'samethread', shouldFail, explicitAsync);
  21. if (shouldFail) {
  22. let caught = false;
  23. // We can't use assert.throws here because the SyntaxError used to construct the thrown error
  24. // is from a different context so it won't be strictly equal to our SyntaxError.
  25. try {
  26. testfunc();
  27. } catch(e) {
  28. caught = true;
  29. // Compare toString output of SyntaxError and other context SyntaxError constructor.
  30. assert.areEqual(e.constructor.toString(), SyntaxError.toString(), message);
  31. }
  32. assert.isTrue(caught, `Expected error not thrown: ${message}`);
  33. } else {
  34. assert.doesNotThrow(testfunc, message);
  35. }
  36. }
  37. function testDynamicImport(importFunc, thenFunc, catchFunc, _asyncEnter, _asyncExit) {
  38. var promise = importFunc();
  39. assert.isTrue(promise instanceof Promise);
  40. promise.then((v)=>{
  41. _asyncEnter();
  42. thenFunc(v);
  43. _asyncExit();
  44. }).catch((err)=>{
  45. _asyncEnter();
  46. catchFunc(err);
  47. _asyncExit();
  48. });
  49. }
  50. var tests = [
  51. {
  52. name: "Runtime evaluation of module specifier",
  53. body: function () {
  54. let functionBody =
  55. `testDynamicImport(
  56. ()=>{
  57. var getName = ()=>{ return 'ModuleSimpleExport'; };
  58. return import( getName() + '.js');
  59. },
  60. (v)=>{
  61. assert.areEqual('ModuleSimpleExport', v.ModuleSimpleExport_foo(), 'Failed to import ModuleSimpleExport_foo from ModuleSimpleExport.js');
  62. },
  63. (err)=>{ assert.fail(err.message); }, _asyncEnter, _asyncExit
  64. )`;
  65. testScript(functionBody, "Test importing a simple exported function", false, true);
  66. testModuleScript(functionBody, "Test importing a simple exported function", false, true);
  67. }
  68. },
  69. {
  70. name: "Validate a simple module export",
  71. body: function () {
  72. let functionBody =
  73. `testDynamicImport(
  74. ()=>{ return import('ModuleSimpleExport.js'); },
  75. (v)=>{ assert.areEqual('ModuleSimpleExport', v.ModuleSimpleExport_foo(), 'Failed to import ModuleSimpleExport_foo from ModuleSimpleExport.js'); },
  76. (err)=>{ assert.fail(err.message); }, _asyncEnter, _asyncExit
  77. )`;
  78. testScript(functionBody, "Test importing a simple exported function", false, true);
  79. testModuleScript(functionBody, "Test importing a simple exported function", false, true);
  80. }
  81. },
  82. {
  83. name: "Validate importing from multiple modules",
  84. body: function () {
  85. let functionBody =
  86. `testDynamicImport(
  87. ()=>import('ModuleComplexExports.js'),
  88. (v)=>{
  89. assert.areEqual('foo', v.foo2(), 'Failed to import foo2 from ModuleComplexExports.js');
  90. },
  91. (err)=>{ assert.fail(err.message); }, _asyncEnter, _asyncExit
  92. )`;
  93. testScript(functionBody, "Test importing from multiple modules", false, true);
  94. testModuleScript(functionBody, "Test importing from multiple modules", false, true);
  95. }
  96. },
  97. {
  98. name: "Validate a variety of more complex exports",
  99. body: function () {
  100. let functionBody =
  101. `testDynamicImport(
  102. ()=>import('ModuleComplexExports.js'),
  103. (v)=>{
  104. assert.areEqual('foo', v.foo(), 'Failed to import foo from ModuleComplexExports.js');
  105. assert.areEqual('foo', v.foo2(), 'Failed to import foo2 from ModuleComplexExports.js');
  106. assert.areEqual('bar', v.bar(), 'Failed to import bar from ModuleComplexExports.js');
  107. assert.areEqual('bar', v.bar2(), 'Failed to import bar2 from ModuleComplexExports.js');
  108. assert.areEqual('let2', v.let2, 'Failed to import let2 from ModuleComplexExports.js');
  109. assert.areEqual('let3', v.let3, 'Failed to import let3 from ModuleComplexExports.js');
  110. assert.areEqual('let2', v.let4, 'Failed to import let4 from ModuleComplexExports.js');
  111. assert.areEqual('let3', v.let5, 'Failed to import let5 from ModuleComplexExports.js');
  112. assert.areEqual('const2', v.const2, 'Failed to import const2 from ModuleComplexExports.js');
  113. assert.areEqual('const3', v.const3, 'Failed to import const3 from ModuleComplexExports.js');
  114. assert.areEqual('const2', v.const4, 'Failed to import const4 from ModuleComplexExports.js');
  115. assert.areEqual('const3', v.const5, 'Failed to import const5 from ModuleComplexExports.js');
  116. assert.areEqual('var2', v.var2, 'Failed to import var2 from ModuleComplexExports.js');
  117. assert.areEqual('var3', v.var3, 'Failed to import var3 from ModuleComplexExports.js');
  118. assert.areEqual('var2', v.var4, 'Failed to import var4 from ModuleComplexExports.js');
  119. assert.areEqual('var3', v.var5, 'Failed to import var5 from ModuleComplexExports.js');
  120. assert.areEqual('class2', v.class2.static_member(), 'Failed to import class2 from ModuleComplexExports.js');
  121. assert.areEqual('class2', new v.class2().member(), 'Failed to create intance of class2 from ModuleComplexExports.js');
  122. assert.areEqual('class2', v.class3.static_member(), 'Failed to import class3 from ModuleComplexExports.js');
  123. assert.areEqual('class2', new v.class3().member(), 'Failed to create intance of class3 from ModuleComplexExports.js');
  124. assert.areEqual('class4', v.class4.static_member(), 'Failed to import class4 from ModuleComplexExports.js');
  125. assert.areEqual('class4', new v.class4().member(), 'Failed to create intance of class4 from ModuleComplexExports.js');
  126. assert.areEqual('class4', v.class5.static_member(), 'Failed to import class4 from ModuleComplexExports.js');
  127. assert.areEqual('class4', new v.class5().member(), 'Failed to create intance of class4 from ModuleComplexExports.js');
  128. assert.areEqual('default', v.default(), 'Failed to import default from ModuleComplexExports.js');
  129. assert.areEqual('ModuleComplexExports', v.function, 'Failed to import v.function from ModuleComplexExports.js');
  130. assert.areEqual('ModuleComplexExports', v.export, 'Failed to import v.export from ModuleComplexExports.js');
  131. },
  132. (err)=>{ assert.fail(err.message); }, _asyncEnter, _asyncExit);
  133. `;
  134. testScript(functionBody, "Test importing a variety of exports", false, true);
  135. testModuleScript(functionBody, "Test importing a variety of exports", false, true);
  136. }
  137. },
  138. {
  139. name: "Exporting module changes exported value",
  140. body: function () {
  141. let functionBody =
  142. `testDynamicImport(
  143. ()=>import('ModuleComplexExports.js'),
  144. (v)=>{
  145. v.reset();
  146. assert.areEqual('before', v.target(), 'Failed to import target from ModuleComplexExports.js');
  147. assert.areEqual('ok', v.changeTarget(), 'Failed to import changeTarget from ModuleComplexExports.js');
  148. assert.areEqual('after', v.target(), 'changeTarget failed to change export value');
  149. },
  150. (err)=>{ assert.fail(err.message); }, _asyncEnter, _asyncExit);
  151. `;
  152. testScript(functionBody, "Changing exported value", false, true);
  153. testModuleScript(functionBody, "Changing exported value", false, true);
  154. }
  155. },
  156. {
  157. name: "Simple re-export forwards import to correct slot",
  158. body: function () {
  159. let functionBody =
  160. `testDynamicImport(
  161. ()=>import('ModuleSimpleReexport.js'),
  162. (v)=>{
  163. assert.areEqual('ModuleSimpleExport', v.ModuleSimpleExport_foo(), 'Failed to import ModuleSimpleExport_foo from ModuleSimpleReexport.js');
  164. },
  165. (err)=>{ assert.fail(err.message); }, _asyncEnter, _asyncExit);
  166. `;
  167. testScript(functionBody, "Simple re-export from one module to another", false, true);
  168. testModuleScript(functionBody, "Simple re-export from one module to another", false, true);
  169. }
  170. },
  171. {
  172. name: "Renamed re-export and dynamic import",
  173. body: function () {
  174. let functionBody =
  175. `testDynamicImport(
  176. ()=>import('ModuleComplexReexports.js'),
  177. (v)=>{
  178. assert.areEqual('bar', v.ModuleComplexReexports_foo(), 'Failed to import ModuleComplexReexports_foo from ModuleComplexReexports.js');
  179. },
  180. (err)=>{ assert.fail(err.message); }, _asyncEnter, _asyncExit);
  181. `;
  182. testScript(functionBody, "Rename already renamed re-export", false, true);
  183. testModuleScript(functionBody, "Rename already renamed re-export", false, true);
  184. }
  185. },
  186. {
  187. name: "Explicit export/import to default binding",
  188. body: function () {
  189. let functionBody =
  190. `testDynamicImport(
  191. ()=>import('ModuleDefaultExport1.js'),
  192. (v)=>{
  193. assert.areEqual('ModuleDefaultExport1', v.default(), 'Failed to import default from ModuleDefaultExport1.js');
  194. },
  195. (err)=>{ assert.fail(err.message); }, _asyncEnter, _asyncExit);
  196. `;
  197. testScript(functionBody, "Explicitly export and import a local name to the default binding", false, true);
  198. testModuleScript(functionBody, "Explicitly export and import a local name to the default binding", false, true);
  199. }
  200. },
  201. {
  202. name: "Explicit import of default binding",
  203. body: function () {
  204. let functionBody =
  205. `testDynamicImport(
  206. ()=>import('ModuleDefaultExport2.js'),
  207. (v)=>{
  208. assert.areEqual('ModuleDefaultExport2', v.default(), 'Failed to import default from ModuleDefaultExport2.js');
  209. },
  210. (err)=>{ assert.fail(err.message); }, _asyncEnter, _asyncExit);
  211. `;
  212. testScript(functionBody, "Explicitly import the default export binding", false, true);
  213. testModuleScript(functionBody, "Explicitly import the default export binding", false, true);
  214. }
  215. },
  216. {
  217. name: "Exporting module changes value of default export",
  218. body: function () {
  219. let functionBody =
  220. `testDynamicImport(
  221. ()=>import('ModuleDefaultExport3.js'),
  222. (v)=>{
  223. assert.areEqual(2, v.default, 'Failed to import default from ModuleDefaultExport3.js');
  224. },
  225. (err)=>{ assert.fail(err.message); }, _asyncEnter, _asyncExit);
  226. `;
  227. testScript(functionBody, "Exported value incorrectly bound", false, true);
  228. testModuleScript(functionBody, "Exported value incorrectly bound", false, true);
  229. functionBody =
  230. `testDynamicImport(
  231. ()=>import('ModuleDefaultExport4.js'),
  232. (v)=>{
  233. assert.areEqual(1, v.default, 'Failed to import default from ModuleDefaultExport4.js');
  234. },
  235. (err)=>{ assert.fail(err.message); }, _asyncEnter, _asyncExit);
  236. `;
  237. testScript(functionBody, "Exported value incorrectly bound", false, true);
  238. testModuleScript(functionBody, "Exported value incorrectly bound", false, true);
  239. }
  240. },
  241. {
  242. name: "Import bindings used in a nested function",
  243. body: function () {
  244. let functionBody =
  245. `function test(func) {
  246. assert.areEqual('ModuleDefaultExport2', func(), 'Failed to import default from ModuleDefaultExport2.js');
  247. }
  248. testDynamicImport(
  249. ()=>import('ModuleDefaultExport2.js'),
  250. (v)=>test(v.default),
  251. (err)=>{ assert.fail(err.message); }, _asyncEnter, _asyncExit);
  252. `;
  253. testScript(functionBody, "Failed to find imported name correctly in nested function", false, true);
  254. testModuleScript(functionBody, "Failed to find imported name correctly in nested function", false, true);
  255. }
  256. },
  257. {
  258. name: "Exported name may be any keyword",
  259. body: function () {
  260. let functionBody =
  261. `testDynamicImport(
  262. ()=>import('ModuleComplexExports.js'),
  263. (v)=>{
  264. assert.areEqual('ModuleComplexExports', v.export, 'Failed to import export from ModuleDefaultExport2.js');
  265. assert.areEqual('ModuleComplexExports', v.function, 'Failed to import function from ModuleDefaultExport2.js');
  266. },
  267. (err)=>{ assert.fail(err.message); }, _asyncEnter, _asyncExit);
  268. `;
  269. testScript(functionBody, "Exported name may be a keyword (import binding must be binding identifier)", false, true);
  270. testModuleScript(functionBody, "Exported name may be a keyword (import binding must be binding identifier)", false, true);
  271. }
  272. },
  273. {
  274. name: "Odd case of 'export { as as as }; import { as as as };'",
  275. body: function () {
  276. let functionBody =
  277. `testDynamicImport(
  278. ()=>import('ModuleComplexExports.js'),
  279. (v)=>{
  280. assert.areEqual('as', v.as(), 'String "as" is not reserved word');
  281. },
  282. (err)=>{ assert.fail(err.message); }, _asyncEnter, _asyncExit);
  283. `;
  284. testScript(functionBody, "Test 'import { as as as}'", false, true);
  285. testModuleScript(functionBody, "Test 'import { as as as}'", false, true);
  286. }
  287. },
  288. {
  289. name: "Typeof a module export",
  290. body: function () {
  291. let functionBody =
  292. `testDynamicImport(
  293. ()=>import('ModuleDefaultExport2.js'),
  294. (v)=>{
  295. assert.areEqual('function', typeof v.default, 'typeof default export from ModuleDefaultExport2.js is function');
  296. },
  297. (err)=>{ assert.fail(err.message); }, _asyncEnter, _asyncExit);
  298. `;
  299. testScript(functionBody, "Typeof a module export", false, true);
  300. testModuleScript(functionBody, "Typeof a module export", false, true);
  301. }
  302. },
  303. {
  304. name: "Circular module dependency",
  305. body: function () {
  306. let functionBody =
  307. `testDynamicImport(
  308. ()=>import('ModuleCircularFoo.js'),
  309. (v)=>{
  310. v.reset();
  311. assert.areEqual(2, v.circular_foo(), 'This function calls between both modules in the circular dependency incrementing a counter in each');
  312. assert.areEqual(4, v.rexportbar(), 'Second call originates in the other module but still increments the counter twice');
  313. },
  314. (err)=>{ assert.fail(err.message); }, _asyncEnter, _asyncExit);
  315. `;
  316. testScript(functionBody, "Circular module dependency", false, true);
  317. testModuleScript(functionBody, "Circular module dependency", false, true);
  318. }
  319. },
  320. {
  321. name: "Implicitly re-exporting an import binding (import { foo } from ''; export { foo };)",
  322. body: function () {
  323. let functionBody =
  324. `testDynamicImport(
  325. ()=>import('ModuleComplexReexports.js'),
  326. (v)=>{
  327. assert.areEqual('foo', v.foo(), 'Simple implicit re-export');
  328. assert.areEqual('foo', v.baz(), 'Renamed export imported and renamed during implicit re-export');
  329. assert.areEqual('foo', v.localfoo(), 'Export renamed as import and implicitly re-exported');
  330. assert.areEqual('foo', v.bar(), 'Renamed export renamed as import and renamed again during implicit re-exported');
  331. assert.areEqual('foo', v.localfoo2(), 'Renamed export renamed as import and implicitly re-exported');
  332. assert.areEqual('foo', v.bar2(), 'Renamed export renamed as import and renamed again during implicit re-export');
  333. },
  334. (err)=>{ assert.fail(err.message); }, _asyncEnter, _asyncExit);
  335. `;
  336. testScript(functionBody, "Implicitly re-exporting an import binding (import { foo } from ''; export { foo };)", false, true);
  337. testModuleScript(functionBody, "Implicitly re-exporting an import binding (import { foo } from ''; export { foo };)", false, true);
  338. }
  339. },
  340. {
  341. name: "Validate a simple module export inside eval()",
  342. body: function () {
  343. let functionBody =
  344. `testDynamicImport(
  345. ()=>{ return eval("import('ModuleSimpleExport.js')"); },
  346. (v)=>{ assert.areEqual('ModuleSimpleExport', v.ModuleSimpleExport_foo(), 'Failed to import ModuleSimpleExport_foo from ModuleSimpleExport.js'); },
  347. (err)=>{ assert.fail(err.message); }, _asyncEnter, _asyncExit
  348. )`;
  349. testScript(functionBody, "Test importing a simple exported function", false, true);
  350. testModuleScript(functionBody, "Test importing a simple exported function", false, true);
  351. }
  352. },
  353. ];
  354. testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });