basic-modules.js 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. // Because you can't easily load modules directly we load them via here and check
  2. // if they passed by checking the result
  3. function validTestModule(filename) {
  4. if (!filename.endsWith(".mjs") || !filename.startsWith("./")) {
  5. throw new ExpectationError(
  6. `Expected module name to start with './' and end with '.mjs' but got '${filename}'`
  7. );
  8. }
  9. }
  10. function expectModulePassed(filename, options = undefined) {
  11. validTestModule(filename);
  12. let moduleLoaded = false;
  13. let moduleResult = null;
  14. let thrownError = null;
  15. import(filename, options)
  16. .then(result => {
  17. moduleLoaded = true;
  18. moduleResult = result;
  19. expect(moduleResult).toHaveProperty("passed", true);
  20. })
  21. .catch(error => {
  22. thrownError = error;
  23. });
  24. runQueuedPromiseJobs();
  25. if (thrownError) {
  26. throw thrownError;
  27. }
  28. expect(moduleLoaded).toBeTrue();
  29. return moduleResult;
  30. }
  31. function expectedModuleToThrowSyntaxError(filename, message) {
  32. validTestModule(filename);
  33. let moduleLoaded = false;
  34. let thrownError = null;
  35. import(filename)
  36. .then(() => {
  37. moduleLoaded = true;
  38. })
  39. .catch(error => {
  40. thrownError = error;
  41. });
  42. runQueuedPromiseJobs();
  43. if (thrownError) {
  44. expect(() => {
  45. throw thrownError;
  46. }).toThrowWithMessage(SyntaxError, message);
  47. } else {
  48. throw new ExpectationError(
  49. `Expected module: '${filename}' to fail to load with a syntax error but did not throw.`
  50. );
  51. }
  52. }
  53. describe("testing behavior", () => {
  54. // To ensure the other tests are interpreter correctly we first test the underlying
  55. // mechanisms so these tests don't use expectModulePassed.
  56. test("can load a module", () => {
  57. let passed = false;
  58. let error = null;
  59. import("./empty.mjs")
  60. .then(() => {
  61. passed = true;
  62. })
  63. .catch(err => {
  64. error = err;
  65. });
  66. runQueuedPromiseJobs();
  67. if (error) throw error;
  68. expect(passed).toBeTrue();
  69. });
  70. test("can load a module twice", () => {
  71. let passed = false;
  72. let error = null;
  73. import("./empty.mjs")
  74. .then(() => {
  75. passed = true;
  76. })
  77. .catch(err => {
  78. error = err;
  79. });
  80. runQueuedPromiseJobs();
  81. if (error) throw error;
  82. expect(passed).toBeTrue();
  83. });
  84. test("can retrieve exported value", () => {
  85. async function getValue(filename) {
  86. const imported = await import(filename);
  87. expect(imported).toHaveProperty("passed", true);
  88. }
  89. let passed = false;
  90. let error = null;
  91. getValue("./single-const-export.mjs")
  92. .then(obj => {
  93. passed = true;
  94. })
  95. .catch(err => {
  96. error = err;
  97. });
  98. runQueuedPromiseJobs();
  99. if (error) throw error;
  100. expect(passed).toBeTrue();
  101. });
  102. test("expectModulePassed works", () => {
  103. expectModulePassed("./single-const-export.mjs");
  104. });
  105. test("can call expectModulePassed with options", () => {
  106. expectModulePassed("./single-const-export.mjs", { key: "value" });
  107. expectModulePassed("./single-const-export.mjs", { key1: "value1", key2: "value2" });
  108. });
  109. });
  110. describe("in- and exports", () => {
  111. test("variable and lexical declarations", () => {
  112. const result = expectModulePassed("./basic-export-types.mjs");
  113. expect(result).not.toHaveProperty("default", null);
  114. expect(result).toHaveProperty("constValue", 1);
  115. expect(result).toHaveProperty("letValue", 2);
  116. expect(result).toHaveProperty("varValue", 3);
  117. expect(result).toHaveProperty("namedConstValue", 1 + 3);
  118. expect(result).toHaveProperty("namedLetValue", 2 + 3);
  119. expect(result).toHaveProperty("namedVarValue", 3 + 3);
  120. });
  121. test("default exports", () => {
  122. const result = expectModulePassed("./module-with-default.mjs");
  123. expect(result).toHaveProperty("defaultValue");
  124. expect(result.default).toBe(result.defaultValue);
  125. });
  126. test("declaration exports which can be used in the module it self", () => {
  127. expectModulePassed("./declarations-tests.mjs");
  128. });
  129. test("string '*' is not a full namespace import", () => {
  130. expectModulePassed("./string-import-names.mjs");
  131. });
  132. test("can combine string and default exports", () => {
  133. expectModulePassed("./string-import-namespace.mjs");
  134. });
  135. test("can re export string names", () => {
  136. expectModulePassed("./string-import-namespace-indirect.mjs");
  137. });
  138. test("re exporting all-but-default does not export a default value", () => {
  139. expectedModuleToThrowSyntaxError(
  140. "./indirect-export-without-default.mjs",
  141. "Invalid or ambiguous export entry 'default'"
  142. );
  143. });
  144. test("can import with (useless) assertions", () => {
  145. expectModulePassed("./import-with-assertions.mjs");
  146. });
  147. test("namespace has expected ordering", () => {
  148. expectModulePassed("./namespace-order.mjs");
  149. });
  150. test("can have multiple star imports even from the same file", () => {
  151. expectModulePassed("./multiple-star-imports.mjs");
  152. });
  153. test("can export namespace via binding", () => {
  154. expectModulePassed("./re-export-namespace-via-binding.mjs");
  155. });
  156. test("import variable before import statement behaves as undefined and non mutable variable", () => {
  157. expectModulePassed("./accessing-var-import-before-decl.mjs");
  158. });
  159. test("import lexical binding before import statement behaves as initialized but non mutable binding", () => {
  160. expectModulePassed("./accessing-lex-import-before-decl.mjs");
  161. });
  162. test("exporting anonymous function", () => {
  163. expectModulePassed("./anon-func-decl-default-export.mjs");
  164. });
  165. test.xfailIf(
  166. isBytecodeInterpreterEnabled(),
  167. "can have top level using declarations which trigger at the end of running a module",
  168. () => {
  169. expectModulePassed("./top-level-dispose.mjs");
  170. }
  171. );
  172. test("can export default a RegExp", () => {
  173. const result = expectModulePassed("./default-regexp-export.mjs");
  174. expect(result.default).toBeInstanceOf(RegExp);
  175. expect(result.default.toString()).toBe(/foo/.toString());
  176. });
  177. });
  178. describe("loops", () => {
  179. test("import and export from own file", () => {
  180. expectModulePassed("./loop-self.mjs");
  181. });
  182. test("import something which imports a cycle", () => {
  183. expectModulePassed("./loop-entry.mjs");
  184. });
  185. });
  186. describe("failing modules cascade", () => {
  187. let failingModuleError = "Left-hand side of postfix";
  188. test("importing a file with a SyntaxError results in a SyntaxError", () => {
  189. expectedModuleToThrowSyntaxError("./failing.mjs", failingModuleError);
  190. });
  191. test("importing a file without a syntax error which imports a file with a syntax error fails", () => {
  192. expectedModuleToThrowSyntaxError("./importing-failing-module.mjs", failingModuleError);
  193. });
  194. test("importing a file which re exports a file with a syntax error fails", () => {
  195. expectedModuleToThrowSyntaxError("./exporting-from-failing.mjs", failingModuleError);
  196. });
  197. test("importing a file re exports nothing from a file with a syntax error fails", () => {
  198. expectedModuleToThrowSyntaxError(
  199. "./exporting-nothing-from-failing.mjs",
  200. failingModuleError
  201. );
  202. });
  203. });
  204. describe("scoping in modules", () => {
  205. test("functions within functions", () => {
  206. expectModulePassed("./function-in-function.mjs");
  207. });
  208. });