2
0

ExtensionMethodsTest.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. using Jint.Native;
  2. using Jint.Tests.Runtime.Domain;
  3. namespace Jint.Tests.Runtime.ExtensionMethods;
  4. public class ExtensionMethodsTest
  5. {
  6. [Fact]
  7. public void ShouldInvokeObjectExtensionMethod()
  8. {
  9. var person = new Person();
  10. person.Name = "Mickey Mouse";
  11. person.Age = 35;
  12. var options = new Options();
  13. options.AddExtensionMethods(typeof(PersonExtensions));
  14. var engine = new Engine(options);
  15. engine.SetValue("person", person);
  16. var age = engine.Evaluate("person.MultiplyAge(2)").AsInteger();
  17. Assert.Equal(70, age);
  18. }
  19. [Fact]
  20. public void ShouldInvokeStringExtensionMethod()
  21. {
  22. var options = new Options();
  23. options.AddExtensionMethods(typeof(CustomStringExtensions));
  24. var engine = new Engine(options);
  25. var result = engine.Evaluate("\"Hello World!\".Backwards()").AsString();
  26. Assert.Equal("!dlroW olleH", result);
  27. }
  28. [Fact]
  29. public void ShouldInvokeNumberExtensionMethod()
  30. {
  31. var options = new Options();
  32. options.AddExtensionMethods(typeof(DoubleExtensions));
  33. var engine = new Engine(options);
  34. var result = engine.Evaluate("let numb = 27; numb.Add(13)").AsInteger();
  35. Assert.Equal(40, result);
  36. }
  37. [Fact]
  38. public void ShouldPrioritizingNonGenericMethod()
  39. {
  40. var options = new Options();
  41. options.AddExtensionMethods(typeof(CustomStringExtensions));
  42. var engine = new Engine(options);
  43. var result = engine.Evaluate("\"{'name':'Mickey'}\".DeserializeObject()").ToObject() as dynamic;
  44. Assert.Equal("Mickey", result.name);
  45. }
  46. [Fact]
  47. public void PrototypeFunctionsShouldNotBeOverridden()
  48. {
  49. var engine = new Engine(opts =>
  50. {
  51. opts.AddExtensionMethods(typeof(CustomStringExtensions));
  52. });
  53. //uses split function from StringPrototype
  54. var arr = engine.Evaluate("'yes,no'.split(',')").AsArray();
  55. Assert.Equal("yes", arr[0]);
  56. Assert.Equal("no", arr[1]);
  57. //uses split function from CustomStringExtensions
  58. var arr2 = engine.Evaluate("'yes,no'.split(2)").AsArray();
  59. Assert.Equal("ye", arr2[0]);
  60. Assert.Equal("s,no", arr2[1]);
  61. }
  62. [Fact]
  63. public void OverridePrototypeFunctions()
  64. {
  65. var engine = new Engine(opts =>
  66. {
  67. opts.AddExtensionMethods(typeof(OverrideStringPrototypeExtensions));
  68. });
  69. //uses the overridden split function from OverrideStringPrototypeExtensions
  70. var arr = engine.Evaluate("'yes,no'.split(',')").AsArray();
  71. Assert.Equal("YES", arr[0]);
  72. Assert.Equal("NO", arr[1]);
  73. }
  74. [Fact]
  75. public void HasOwnPropertyShouldWorkCorrectlyInPresenceOfExtensionMethods()
  76. {
  77. var person = new Person();
  78. var options = new Options();
  79. options.AddExtensionMethods(typeof(PersonExtensions));
  80. var engine = new Engine(options);
  81. engine.SetValue("person", person);
  82. var isBogusInPerson = engine.Evaluate("'bogus' in person").AsBoolean();
  83. Assert.False(isBogusInPerson);
  84. var propertyValue = engine.Evaluate("person.bogus");
  85. Assert.Equal(JsValue.Undefined, propertyValue);
  86. }
  87. private Engine GetLinqEngine()
  88. {
  89. return new Engine(opts =>
  90. {
  91. opts.AddExtensionMethods(typeof(Enumerable));
  92. });
  93. }
  94. [Fact]
  95. public void LinqExtensionMethodWithoutGenericParameter()
  96. {
  97. var engine = GetLinqEngine();
  98. var intList = new List<int>() { 0, 1, 2, 3 };
  99. engine.SetValue("intList", intList);
  100. var intSumRes = engine.Evaluate("intList.Sum()").AsNumber();
  101. Assert.Equal(6, intSumRes);
  102. }
  103. // TODO this fails due to double -> long assignment on FW
  104. #if !NETFRAMEWORK
  105. [Fact]
  106. public void LinqExtensionMethodWithSingleGenericParameter()
  107. {
  108. var engine = GetLinqEngine();
  109. var stringList = new List<string>() { "working", "linq" };
  110. engine.SetValue("stringList", stringList);
  111. var stringSumRes = engine.Evaluate("stringList.Sum(x => x.length)").AsNumber();
  112. Assert.Equal(11, stringSumRes);
  113. }
  114. #endif
  115. [Fact]
  116. public void LinqExtensionMethodWithMultipleGenericParameters()
  117. {
  118. var engine = GetLinqEngine();
  119. var stringList = new List<string>() { "working", "linq" };
  120. engine.SetValue("stringList", stringList);
  121. var stringRes = engine.Evaluate("stringList.Select((x) => x + 'a').ToArray().join()").AsString();
  122. Assert.Equal("workinga,linqa", stringRes);
  123. // The method ambiguity resolver is not so smart to choose the Select method with the correct number of parameters
  124. // Thus, the following script will not work as expected.
  125. // stringList.Select((x, i) => x + i).ToArray().join()
  126. }
  127. [Fact]
  128. public void GenericTypeExtension()
  129. {
  130. var options = new Options();
  131. options.AddExtensionMethods(typeof(ObservableExtensions));
  132. var engine = new Engine(options);
  133. engine.SetValue("log", new System.Action<object>(System.Console.WriteLine));
  134. NameObservable observable = new NameObservable();
  135. engine.SetValue("observable", observable);
  136. engine.Evaluate(@"
  137. log('before');
  138. observable.Subscribe((name) =>{
  139. log('observable: subscribe: name: ' + name);
  140. });
  141. observable.UpdateName('foobar');
  142. log('after');
  143. ");
  144. Assert.Equal("foobar", observable.Last);
  145. }
  146. [Fact]
  147. public void GenericExtensionMethodOnClosedGenericType()
  148. {
  149. var options = new Options();
  150. options.AddExtensionMethods(typeof(ObservableExtensions));
  151. var engine = new Engine(options);
  152. engine.SetValue("log", new System.Action<object>(System.Console.WriteLine));
  153. NameObservable observable = new NameObservable();
  154. engine.SetValue("observable", observable);
  155. var result = engine.Evaluate(@"
  156. log('before calling Select');
  157. var result = observable.Select('some text');
  158. log('result: ' + result);
  159. return result;
  160. ");
  161. //System.Console.WriteLine("GenericExtensionMethodOnGenericType: result: " + result + " result.ToString(): " + result.ToString());
  162. Assert.Equal("some text", result);
  163. }
  164. [Fact]
  165. public void GenericExtensionMethodOnClosedGenericType2()
  166. {
  167. var options = new Options();
  168. options.AddExtensionMethods(typeof(ObservableExtensions));
  169. var engine = new Engine(options);
  170. NameObservable observable = new NameObservable();
  171. observable.Where((text) =>
  172. {
  173. System.Console.WriteLine("GenericExtensionMethodOnClosedGenericType2: NameObservable: Where: text: " + text);
  174. return true;
  175. });
  176. engine.SetValue("observable", observable);
  177. var result = engine.Evaluate(@"
  178. var result = observable.Where(function(text){
  179. return true;
  180. });
  181. observable.UpdateName('testing yo');
  182. observable.CommitName();
  183. return result;
  184. ");
  185. var nameObservableResult = result.ToObject() as NameObservable;
  186. Assert.NotNull(nameObservableResult);
  187. Assert.Equal("testing yo", nameObservableResult.Last);
  188. }
  189. [Fact]
  190. public void GenericExtensionMethodOnOpenGenericType()
  191. {
  192. var options = new Options();
  193. options.AddExtensionMethods(typeof(ObservableExtensions));
  194. var engine = new Engine(options);
  195. BaseObservable<string> observable = new BaseObservable<string>();
  196. observable.Where((text) =>
  197. {
  198. System.Console.WriteLine("GenericExtensionMethodOnOpenGenericType: BaseObservable: Where: text: " + text);
  199. return true;
  200. });
  201. engine.SetValue("observable", observable);
  202. var result = engine.Evaluate(@"
  203. var result = observable.Where(function(text){
  204. return true;
  205. });
  206. observable.Update('testing yo');
  207. observable.BroadcastCompleted();
  208. return result;
  209. ");
  210. System.Console.WriteLine("GenericExtensionMethodOnOpenGenericType: result: " + result + " result.ToString(): " + result.ToString());
  211. var baseObservableResult = result.ToObject() as BaseObservable<string>;
  212. System.Console.WriteLine("GenericExtensionMethodOnOpenGenericType: baseObservableResult: " + baseObservableResult);
  213. Assert.NotNull(baseObservableResult);
  214. Assert.Equal("testing yo", baseObservableResult.Last);
  215. }
  216. [Fact]
  217. public void GenericExtensionMethodOnGenericTypeInstantiatedInJs()
  218. {
  219. var options = new Options();
  220. options.AddExtensionMethods(typeof(ObservableExtensions));
  221. var engine = new Engine(options);
  222. engine.SetValue("BaseObservable", typeof(BaseObservable<>));
  223. engine.SetValue("ObservableFactory", typeof(ObservableFactory));
  224. var result = engine.Evaluate(@"
  225. // you can't instantiate generic types in JS (without providing the types as arguments to the constructor) - i.e. not compatible with transpiled typescript
  226. //const observable = new BaseObservable();
  227. //const observable = BaseObservable.GetBoolBaseObservable();
  228. const observable = ObservableFactory.GetBoolBaseObservable();
  229. var result = observable.Where(function(someBool){
  230. return true;
  231. });
  232. observable.Update(false);
  233. observable.BroadcastCompleted();
  234. return result;
  235. ");
  236. var baseObservableResult = result.ToObject() as BaseObservable<bool>;
  237. System.Console.WriteLine("GenericExtensionMethodOnOpenGenericType: baseObservableResult: " + baseObservableResult);
  238. Assert.NotNull(baseObservableResult);
  239. Assert.Equal(false, baseObservableResult.Last);
  240. }
  241. [Fact]
  242. public void CanProjectAndGroupWhenExpandoObjectWrappingDisabled()
  243. {
  244. var engine = new Engine(options =>
  245. {
  246. options.AllowClr().AddExtensionMethods(typeof(Enumerable));
  247. // prevent ExpandoObject wrapping
  248. options.Interop.CreateClrObject = null;
  249. });
  250. engine.Execute("var a = [ 2, 4 ];");
  251. var selected = engine.Evaluate("JSON.stringify(a.Select(m => ({a:m,b:m})).ToArray());").AsString();
  252. Assert.Equal("""[{"a":2,"b":2},{"a":4,"b":4}]""", selected);
  253. var grouped1 = engine.Evaluate("a.GroupBy(m => ({a:m,b:m})).ToArray()").AsArray();
  254. var grouped = engine.Evaluate("JSON.stringify(a.GroupBy(m => ({a:m,b:m})).Select(x => x.Key).ToArray());").AsString();
  255. Assert.Equal("""[{"a":2,"b":2},{"a":4,"b":4}]""", grouped);
  256. }
  257. }