ModuleTests.cs 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. #if(NET6_0_OR_GREATER)
  2. using System.IO;
  3. using System.Reflection;
  4. #endif
  5. using System;
  6. using Jint.Native;
  7. using Jint.Runtime;
  8. using Xunit;
  9. namespace Jint.Tests.Runtime;
  10. public class ModuleTests
  11. {
  12. private readonly Engine _engine;
  13. public ModuleTests()
  14. {
  15. _engine = new Engine();
  16. }
  17. [Fact]
  18. public void ShouldExportNamed()
  19. {
  20. _engine.AddModule("my-module", @"export const value = 'exported value';");
  21. var ns = _engine.ImportModule("my-module");
  22. Assert.Equal("exported value", ns.Get("value").AsString());
  23. }
  24. [Fact]
  25. public void ShouldExportNamedListRenamed()
  26. {
  27. _engine.AddModule("my-module", @"const value1 = 1; const value2 = 2; export { value1 as renamed1, value2 as renamed2 }");
  28. var ns = _engine.ImportModule("my-module");
  29. Assert.Equal(1, ns.Get("renamed1").AsInteger());
  30. Assert.Equal(2, ns.Get("renamed2").AsInteger());
  31. }
  32. [Fact]
  33. public void ShouldExportDefault()
  34. {
  35. _engine.AddModule("my-module", @"export default 'exported value';");
  36. var ns = _engine.ImportModule("my-module");
  37. Assert.Equal("exported value", ns.Get("default").AsString());
  38. }
  39. [Fact]
  40. public void ShouldExportAll()
  41. {
  42. _engine.AddModule("module1", @"export const value = 'exported value';");
  43. _engine.AddModule("module2", @"export * from 'module1';");
  44. var ns = _engine.ImportModule("module2");
  45. Assert.Equal("exported value", ns.Get("value").AsString());
  46. }
  47. [Fact]
  48. public void ShouldImportNamed()
  49. {
  50. _engine.AddModule("imported-module", @"export const value = 'exported value';");
  51. _engine.AddModule("my-module", @"import { value } from 'imported-module'; export const exported = value;");
  52. var ns = _engine.ImportModule("my-module");
  53. Assert.Equal("exported value", ns.Get("exported").AsString());
  54. }
  55. [Fact]
  56. public void ShouldImportRenamed()
  57. {
  58. _engine.AddModule("imported-module", @"export const value = 'exported value';");
  59. _engine.AddModule("my-module", @"import { value as renamed } from 'imported-module'; export const exported = renamed;");
  60. var ns = _engine.ImportModule("my-module");
  61. Assert.Equal("exported value", ns.Get("exported").AsString());
  62. }
  63. [Fact]
  64. public void ShouldImportDefault()
  65. {
  66. _engine.AddModule("imported-module", @"export default 'exported value';");
  67. _engine.AddModule("my-module", @"import imported from 'imported-module'; export const exported = imported;");
  68. var ns = _engine.ImportModule("my-module");
  69. Assert.Equal("exported value", ns.Get("exported").AsString());
  70. }
  71. [Fact]
  72. public void ShouldImportAll()
  73. {
  74. _engine.AddModule("imported-module", @"export const value = 'exported value';");
  75. _engine.AddModule("my-module", @"import * as imported from 'imported-module'; export const exported = imported.value;");
  76. var ns = _engine.ImportModule("my-module");
  77. Assert.Equal("exported value", ns.Get("exported").AsString());
  78. }
  79. [Fact]
  80. public void ShouldPropagateThrowStatementOnCSharpImport()
  81. {
  82. _engine.AddModule("my-module", @"throw new Error('imported successfully');");
  83. var exc = Assert.Throws<JavaScriptException>(() => _engine.ImportModule("my-module"));
  84. Assert.Equal("imported successfully", exc.Message);
  85. Assert.Equal("my-module", exc.Location.Source);
  86. }
  87. [Fact]
  88. public void ShouldPropagateThrowStatementThroughJavaScriptImport()
  89. {
  90. _engine.AddModule("imported-module", @"throw new Error('imported successfully');");
  91. _engine.AddModule("my-module", @"import 'imported-module';");
  92. var exc = Assert.Throws<JavaScriptException>(() => _engine.ImportModule("my-module"));
  93. Assert.Equal("imported successfully", exc.Message);
  94. Assert.Equal("imported-module", exc.Location.Source);
  95. }
  96. [Fact]
  97. public void ShouldAddModuleFromJsValue()
  98. {
  99. _engine.AddModule("my-module", builder => builder.ExportValue("value", JsString.Create("hello world")));
  100. var ns = _engine.ImportModule("my-module");
  101. Assert.Equal("hello world", ns.Get("value").AsString());
  102. }
  103. [Fact]
  104. public void ShouldAddModuleFromClrInstance()
  105. {
  106. _engine.AddModule("imported-module", builder => builder.ExportObject("value", new ImportedClass { Value = "instance value" }));
  107. _engine.AddModule("my-module", @"import { value } from 'imported-module'; export const exported = value.value;");
  108. var ns = _engine.ImportModule("my-module");
  109. Assert.Equal("instance value", ns.Get("exported").AsString());
  110. }
  111. [Fact]
  112. public void ShouldAllowInvokeUserDefinedClass()
  113. {
  114. _engine.AddModule("user", "export class UserDefined { constructor(v) { this._v = v; } hello(c) { return `hello ${this._v}${c}`; } }");
  115. var ctor = _engine.ImportModule("user").Get("UserDefined");
  116. var instance = _engine.Construct(ctor, JsString.Create("world"));
  117. var result = instance.GetMethod("hello").Call(instance, JsString.Create("!"));
  118. Assert.Equal("hello world!", result);
  119. }
  120. [Fact]
  121. public void ShouldAddModuleFromClrType()
  122. {
  123. _engine.AddModule("imported-module", builder => builder.ExportType<ImportedClass>());
  124. _engine.AddModule("my-module", @"import { ImportedClass } from 'imported-module'; export const exported = new ImportedClass().value;");
  125. var ns = _engine.ImportModule("my-module");
  126. Assert.Equal("hello world", ns.Get("exported").AsString());
  127. }
  128. private class ImportedClass
  129. {
  130. public string Value { get; set; } = "hello world";
  131. }
  132. [Fact]
  133. public void ShouldAllowExportMultipleImports()
  134. {
  135. _engine.AddModule("@mine/import1", builder => builder.ExportValue("value1", JsNumber.Create(1)));
  136. _engine.AddModule("@mine/import2", builder => builder.ExportValue("value2", JsNumber.Create(2)));
  137. _engine.AddModule("@mine", "export * from '@mine/import1'; export * from '@mine/import2'");
  138. _engine.AddModule("app", @"import { value1, value2 } from '@mine'; export const result = `${value1} ${value2}`");
  139. var ns = _engine.ImportModule("app");
  140. Assert.Equal("1 2", ns.Get("result").AsString());
  141. }
  142. /* ECMAScript 2020 "export * as ns from"
  143. [Fact]
  144. public void ShouldAllowNamedStarExport()
  145. {
  146. _engine.AddModule("imported-module", builder => builder.ExportValue("value1", 5));
  147. _engine.AddModule("my-module", "export * as ns from 'imported-module';");
  148. var ns = _engine.ImportModule("my-module");
  149. Assert.Equal(5, ns.Get("ns").Get("value1").AsNumber());
  150. }
  151. */
  152. [Fact]
  153. public void ShouldAllowChaining()
  154. {
  155. _engine.AddModule("dependent-module", "export const dependency = 1;");
  156. _engine.AddModule("my-module", builder => builder
  157. .AddSource("import { dependency } from 'dependent-module';")
  158. .AddSource("export const output = dependency + 1;")
  159. .ExportValue("num", JsNumber.Create(-1))
  160. );
  161. var ns = _engine.ImportModule("my-module");
  162. Assert.Equal(2, ns.Get("output").AsInteger());
  163. Assert.Equal(-1, ns.Get("num").AsInteger());
  164. }
  165. [Fact]
  166. public void ShouldAllowLoadingMoreThanOnce()
  167. {
  168. var called = 0;
  169. _engine.AddModule("imported-module", builder => builder.ExportFunction("count", args => called++));
  170. _engine.AddModule("my-module", @"import { count } from 'imported-module'; count();");
  171. _engine.ImportModule("my-module");
  172. _engine.ImportModule("my-module");
  173. Assert.Equal(called, 1);
  174. }
  175. #if(NET6_0_OR_GREATER)
  176. [Fact]
  177. public void CanLoadModuleImportsFromFiles()
  178. {
  179. var engine = new Engine(options => options.EnableModules(GetBasePath()));
  180. engine.AddModule("my-module", "import { User } from './modules/user.js'; export const user = new User('John', 'Doe');");
  181. var ns = engine.ImportModule("my-module");
  182. Assert.Equal("John Doe", ns["user"].Get("name").AsString());
  183. }
  184. [Fact]
  185. public void CanImportFromFile()
  186. {
  187. var engine = new Engine(options => options.EnableModules(GetBasePath()));
  188. var ns = engine.ImportModule("./modules/format-name.js");
  189. var result = engine.Invoke(ns.Get("formatName"), "John", "Doe").AsString();
  190. Assert.Equal("John Doe", result);
  191. }
  192. private static string GetBasePath()
  193. {
  194. var assemblyPath = new Uri(typeof(ModuleTests).GetTypeInfo().Assembly.Location).LocalPath;
  195. var assemblyDirectory = new FileInfo(assemblyPath).Directory;
  196. return Path.Combine(
  197. assemblyDirectory?.Parent?.Parent?.Parent?.FullName ?? throw new NullReferenceException("Could not find tests base path"),
  198. "Runtime",
  199. "Scripts");
  200. }
  201. #endif
  202. }