InteropTests.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734
  1. using System;
  2. using System.Collections.Generic;
  3. using Jint.Native;
  4. using Jint.Native.Object;
  5. using Jint.Tests.Runtime.Converters;
  6. using Jint.Tests.Runtime.Domain;
  7. using Shapes;
  8. using Xunit;
  9. namespace Jint.Tests.Runtime
  10. {
  11. public class InteropTests : IDisposable
  12. {
  13. private readonly Engine _engine;
  14. public InteropTests()
  15. {
  16. _engine = new Engine(cfg => cfg.AllowClr(typeof(Shape).Assembly))
  17. .SetValue("log", new Action<object>(Console.WriteLine))
  18. .SetValue("assert", new Action<bool>(Assert.True))
  19. ;
  20. }
  21. void IDisposable.Dispose()
  22. {
  23. }
  24. private void RunTest(string source)
  25. {
  26. _engine.Execute(source);
  27. }
  28. [Fact]
  29. public void PrimitiveTypesCanBeSet()
  30. {
  31. _engine.SetValue("x", 10);
  32. _engine.SetValue("y", true);
  33. _engine.SetValue("z", "foo");
  34. RunTest(@"
  35. assert(x === 10);
  36. assert(y === true);
  37. assert(z === 'foo');
  38. ");
  39. }
  40. [Fact]
  41. public void DelegatesCanBeSet()
  42. {
  43. _engine.SetValue("square", new Func<double, double>(x => x * x));
  44. RunTest(@"
  45. assert(square(10) === 100);
  46. ");
  47. }
  48. [Fact]
  49. public void CanGetObjectProperties()
  50. {
  51. var p = new Person
  52. {
  53. Name = "Mickey Mouse"
  54. };
  55. _engine.SetValue("p", p);
  56. RunTest(@"
  57. assert(p.Name === 'Mickey Mouse');
  58. ");
  59. }
  60. [Fact]
  61. public void CanInvokeObjectMethods()
  62. {
  63. var p = new Person
  64. {
  65. Name = "Mickey Mouse"
  66. };
  67. _engine.SetValue("p", p);
  68. RunTest(@"
  69. assert(p.ToString() === 'Mickey Mouse');
  70. ");
  71. }
  72. [Fact]
  73. public void CanSetObjectProperties()
  74. {
  75. var p = new Person
  76. {
  77. Name = "Mickey Mouse"
  78. };
  79. _engine.SetValue("p", p);
  80. RunTest(@"
  81. p.Name = 'Donald Duck';
  82. assert(p.Name === 'Donald Duck');
  83. ");
  84. Assert.Equal("Donald Duck", p.Name);
  85. }
  86. [Fact]
  87. public void CanAccessAnonymousObject()
  88. {
  89. var p = new
  90. {
  91. Name = "Mickey Mouse",
  92. };
  93. _engine.SetValue("p", p);
  94. RunTest(@"
  95. assert(p.Name === 'Mickey Mouse');
  96. ");
  97. }
  98. [Fact]
  99. public void CanAccessAnonymousObjectProperties()
  100. {
  101. var p = new
  102. {
  103. Address = new
  104. {
  105. City = "Mouseton"
  106. }
  107. };
  108. _engine.SetValue("p", p);
  109. RunTest(@"
  110. assert(p.Address.City === 'Mouseton');
  111. ");
  112. }
  113. [Fact]
  114. public void PocosCanReturnJsValueDirectly()
  115. {
  116. var o = new
  117. {
  118. x = new JsValue(1),
  119. y = new JsValue("string"),
  120. };
  121. _engine.SetValue("o", o);
  122. RunTest(@"
  123. assert(o.x === 1);
  124. assert(o.y === 'string');
  125. ");
  126. }
  127. [Fact]
  128. public void PocosCanReturnObjectInstanceDirectly()
  129. {
  130. var x = new ObjectInstance(_engine) { Extensible = true};
  131. x.Put("foo", new JsValue("bar"), false);
  132. var o = new
  133. {
  134. x
  135. };
  136. _engine.SetValue("o", o);
  137. RunTest(@"
  138. assert(o.x.foo === 'bar');
  139. ");
  140. }
  141. [Fact]
  142. public void DateTimeIsConvertedToDate()
  143. {
  144. var o = new
  145. {
  146. z = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)
  147. };
  148. _engine.SetValue("o", o);
  149. RunTest(@"
  150. assert(o.z.valueOf() === 0);
  151. ");
  152. }
  153. [Fact]
  154. public void DateTimeOffsetIsConvertedToDate()
  155. {
  156. var o = new
  157. {
  158. z = new DateTimeOffset(1970, 1, 1, 0, 0, 0, new TimeSpan())
  159. };
  160. _engine.SetValue("o", o);
  161. RunTest(@"
  162. assert(o.z.valueOf() === 0);
  163. ");
  164. }
  165. [Fact]
  166. public void EcmaValuesAreAutomaticallyConvertedWhenSetInPoco()
  167. {
  168. var p = new Person
  169. {
  170. Name = "foo",
  171. };
  172. _engine.SetValue("p", p);
  173. RunTest(@"
  174. assert(p.Name === 'foo');
  175. assert(p.Age === 0);
  176. p.Name = 'bar';
  177. p.Age = 10;
  178. ");
  179. Assert.Equal("bar", p.Name);
  180. Assert.Equal(10, p.Age);
  181. }
  182. [Fact]
  183. public void EcmaValuesAreAutomaticallyConvertedToBestMatchWhenSetInPoco()
  184. {
  185. var p = new Person
  186. {
  187. Name = "foo",
  188. };
  189. _engine.SetValue("p", p);
  190. RunTest(@"
  191. p.Name = 10;
  192. p.Age = '20';
  193. ");
  194. Assert.Equal("10", p.Name);
  195. Assert.Equal(20, p.Age);
  196. }
  197. [Fact]
  198. public void ShouldCallInstanceMethodWithoutArgument()
  199. {
  200. _engine.SetValue("a", new A());
  201. RunTest(@"
  202. assert(a.Call1() === 0);
  203. ");
  204. }
  205. [Fact]
  206. public void ShouldCallInstanceMethodOverloadArgument()
  207. {
  208. _engine.SetValue("a", new A());
  209. RunTest(@"
  210. assert(a.Call1(1) === 1);
  211. ");
  212. }
  213. [Fact]
  214. public void ShouldCallInstanceMethodWithString()
  215. {
  216. var p = new Person();
  217. _engine.SetValue("a", new A());
  218. _engine.SetValue("p", p);
  219. RunTest(@"
  220. p.Name = a.Call2('foo');
  221. assert(p.Name === 'foo');
  222. ");
  223. Assert.Equal("foo", p.Name);
  224. }
  225. [Fact]
  226. public void CanUseTrim()
  227. {
  228. var p = new Person { Name = "Mickey Mouse "};
  229. _engine.SetValue("p", p);
  230. RunTest(@"
  231. assert(p.Name === 'Mickey Mouse ');
  232. p.Name = p.Name.trim();
  233. assert(p.Name === 'Mickey Mouse');
  234. ");
  235. Assert.Equal("Mickey Mouse", p.Name);
  236. }
  237. [Fact]
  238. public void CanUseMathFloor()
  239. {
  240. var p = new Person();
  241. _engine.SetValue("p", p);
  242. RunTest(@"
  243. p.Age = Math.floor(1.6);p
  244. assert(p.Age === 1);
  245. ");
  246. Assert.Equal(1, p.Age);
  247. }
  248. [Fact]
  249. public void CanUseDelegateAsFunction()
  250. {
  251. var even = new Func<int, bool>(x => x % 2 == 0);
  252. _engine.SetValue("even", even);
  253. RunTest(@"
  254. assert(even(2) === true);
  255. ");
  256. }
  257. [Fact]
  258. public void ShouldConvertArrayToArrayInstance()
  259. {
  260. var result = _engine
  261. .SetValue("values", new[] { 1, 2, 3, 4, 5, 6 })
  262. .Execute("values.filter(function(x){ return x % 2 == 0; })");
  263. var parts = result.GetCompletionValue().ToObject();
  264. Assert.True(parts.GetType().IsArray);
  265. Assert.Equal(3, ((object[])parts).Length);
  266. Assert.Equal(2d, ((object[])parts)[0]);
  267. Assert.Equal(4d, ((object[])parts)[1]);
  268. Assert.Equal(6d, ((object[])parts)[2]);
  269. }
  270. [Fact]
  271. public void ShouldConvertListsToArrayInstance()
  272. {
  273. var result = _engine
  274. .SetValue("values", new List<object> { 1, 2, 3, 4, 5, 6 })
  275. .Execute("new Array(values).filter(function(x){ return x % 2 == 0; })");
  276. var parts = result.GetCompletionValue().ToObject();
  277. Assert.True(parts.GetType().IsArray);
  278. Assert.Equal(3, ((object[])parts).Length);
  279. Assert.Equal(2d, ((object[])parts)[0]);
  280. Assert.Equal(4d, ((object[])parts)[1]);
  281. Assert.Equal(6d, ((object[])parts)[2]);
  282. }
  283. [Fact]
  284. public void ShouldConvertArrayInstanceToArray()
  285. {
  286. var result = _engine.Execute("'[email protected]'.split('@');");
  287. var parts = result.GetCompletionValue().ToObject();
  288. Assert.True(parts.GetType().IsArray);
  289. Assert.Equal(2, ((object[])parts).Length);
  290. Assert.Equal("foo", ((object[])parts)[0]);
  291. Assert.Equal("bar.com", ((object[])parts)[1]);
  292. }
  293. [Fact]
  294. public void ShouldConvertBooleanInstanceToBool()
  295. {
  296. var result = _engine.Execute("new Boolean(true)");
  297. var value = result.GetCompletionValue().ToObject();
  298. Assert.Equal(typeof(bool), value.GetType());
  299. Assert.Equal(true, value);
  300. }
  301. [Fact]
  302. public void ShouldConvertDateInstanceToDateTime()
  303. {
  304. var result = _engine.Execute("new Date(0)");
  305. var value = result.GetCompletionValue().ToObject();
  306. Assert.Equal(typeof(DateTime), value.GetType());
  307. Assert.Equal(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc), value);
  308. }
  309. [Fact]
  310. public void ShouldConvertNumberInstanceToDouble()
  311. {
  312. var result = _engine.Execute("new Number(10)");
  313. var value = result.GetCompletionValue().ToObject();
  314. Assert.Equal(typeof(double), value.GetType());
  315. Assert.Equal(10d, value);
  316. }
  317. [Fact]
  318. public void ShouldConvertStringInstanceToString()
  319. {
  320. var result = _engine.Execute("new String('foo')");
  321. var value = result.GetCompletionValue().ToObject();
  322. Assert.Equal(typeof(string), value.GetType());
  323. Assert.Equal("foo", value);
  324. }
  325. [Fact]
  326. public void ShouldConvertObjectInstanceToExpando()
  327. {
  328. _engine.Execute("var o = {a: 1, b: 'foo'}");
  329. var result = _engine.GetValue("o");
  330. dynamic value = result.ToObject();
  331. Assert.Equal(1, value.a);
  332. Assert.Equal("foo", value.b);
  333. var dic = (IDictionary<string, object>)result.ToObject();
  334. Assert.Equal(1d, dic["a"]);
  335. Assert.Equal("foo", dic["b"]);
  336. }
  337. [Fact]
  338. public void ShouldNotTryToConvertCompatibleTypes()
  339. {
  340. _engine.SetValue("a", new A());
  341. RunTest(@"
  342. assert(a.Call3('foo') === 'foo');
  343. assert(a.Call3(1) === '1');
  344. ");
  345. }
  346. [Fact]
  347. public void ShouldNotTryToConvertDerivedTypes()
  348. {
  349. _engine.SetValue("a", new A());
  350. _engine.SetValue("p", new Person { Name = "Mickey" });
  351. RunTest(@"
  352. assert(a.Call4(p) === 'Mickey');
  353. ");
  354. }
  355. [Fact]
  356. public void ShouldExecuteFunctionCallBackAsDelegate()
  357. {
  358. _engine.SetValue("a", new A());
  359. RunTest(@"
  360. assert(a.Call5(function(a,b){ return a+b }) === '1foo');
  361. ");
  362. }
  363. [Fact]
  364. public void ShouldExecuteFunctionCallBackAsFuncAndThisCanBeAssigned()
  365. {
  366. _engine.SetValue("a", new A());
  367. RunTest(@"
  368. assert(a.Call6(function(a,b){ return this+a+b }) === 'bar1foo');
  369. ");
  370. }
  371. [Fact]
  372. public void ShouldExecuteFunctionCallBackAsPredicate()
  373. {
  374. _engine.SetValue("a", new A());
  375. // Func<>
  376. RunTest(@"
  377. assert(a.Call8(function(){ return 'foo'; }) === 'foo');
  378. ");
  379. }
  380. [Fact]
  381. public void ShouldExecuteFunctionWithParameterCallBackAsPredicate()
  382. {
  383. _engine.SetValue("a", new A());
  384. // Func<,>
  385. RunTest(@"
  386. assert(a.Call7('foo', function(a){ return a === 'foo'; }) === true);
  387. ");
  388. }
  389. [Fact]
  390. public void ShouldExecuteActionCallBackAsPredicate()
  391. {
  392. _engine.SetValue("a", new A());
  393. // Action
  394. RunTest(@"
  395. var value;
  396. a.Call9(function(){ value = 'foo'; });
  397. assert(value === 'foo');
  398. ");
  399. }
  400. [Fact]
  401. public void ShouldExecuteActionWithParameterCallBackAsPredicate()
  402. {
  403. _engine.SetValue("a", new A());
  404. // Action<>
  405. RunTest(@"
  406. var value;
  407. a.Call10('foo', function(b){ value = b; });
  408. assert(value === 'foo');
  409. ");
  410. }
  411. [Fact]
  412. public void ShouldExecuteActionWithMultipleParametersCallBackAsPredicate()
  413. {
  414. _engine.SetValue("a", new A());
  415. // Action<,>
  416. RunTest(@"
  417. var value;
  418. a.Call11('foo', 'bar', function(a,b){ value = a + b; });
  419. assert(value === 'foobar');
  420. ");
  421. }
  422. [Fact]
  423. public void ShouldExecuteActionCallbackOnEventChanged()
  424. {
  425. var collection = new System.Collections.ObjectModel.ObservableCollection<string>();
  426. Assert.True(collection.Count == 0);
  427. _engine.SetValue("collection", collection);
  428. RunTest(@"
  429. var eventAction;
  430. collection.add_CollectionChanged(function(sender, eventArgs) { eventAction = eventArgs.Action; } );
  431. collection.Add('test');
  432. ");
  433. var eventAction = _engine.GetValue("eventAction").AsNumber();
  434. Assert.True(eventAction == 0);
  435. Assert.True(collection.Count == 1);
  436. }
  437. [Fact]
  438. public void ShouldUseSystemIO()
  439. {
  440. RunTest(@"
  441. var filename = System.IO.Path.GetTempFileName();
  442. var sw = System.IO.File.CreateText(filename);
  443. sw.Write('Hello World');
  444. sw.Dispose();
  445. var content = System.IO.File.ReadAllText(filename);
  446. System.Console.WriteLine(content);
  447. assert(content === 'Hello World');
  448. ");
  449. }
  450. [Fact]
  451. public void ShouldImportNamespace()
  452. {
  453. RunTest(@"
  454. var Shapes = importNamespace('Shapes');
  455. var circle = new Shapes.Circle();
  456. assert(circle.Radius === 0);
  457. assert(circle.Perimeter() === 0);
  458. ");
  459. }
  460. [Fact]
  461. public void ShouldConstructWithParameters()
  462. {
  463. RunTest(@"
  464. var Shapes = importNamespace('Shapes');
  465. var circle = new Shapes.Circle(1);
  466. assert(circle.Radius === 1);
  467. assert(circle.Perimeter() === Math.PI);
  468. ");
  469. }
  470. [Fact]
  471. public void ShouldInvokeAFunctionByName()
  472. {
  473. RunTest(@"
  474. function add(x, y) { return x + y; }
  475. ");
  476. Assert.Equal(3, _engine.Invoke("add", 1, 2));
  477. }
  478. [Fact]
  479. public void ShouldNotInvokeNonFunctionValue()
  480. {
  481. RunTest(@"
  482. var x= 10;
  483. ");
  484. Assert.Throws<ArgumentException>(() => _engine.Invoke("x", 1, 2));
  485. }
  486. [Fact]
  487. public void CanGetField()
  488. {
  489. var o = new ClassWithField
  490. {
  491. Field = "Mickey Mouse"
  492. };
  493. _engine.SetValue("o", o);
  494. RunTest(@"
  495. assert(o.Field === 'Mickey Mouse');
  496. ");
  497. }
  498. [Fact]
  499. public void CanSetField()
  500. {
  501. var o = new ClassWithField();
  502. _engine.SetValue("o", o);
  503. RunTest(@"
  504. o.Field = 'Mickey Mouse';
  505. assert(o.Field === 'Mickey Mouse');
  506. ");
  507. Assert.Equal("Mickey Mouse", o.Field);
  508. }
  509. [Fact]
  510. public void CanSetCustomConverters()
  511. {
  512. var engine1 = new Engine();
  513. engine1.SetValue("p", new { Test = true });
  514. engine1.Execute("var result = p.Test;");
  515. Assert.True((bool)engine1.GetValue("result").ToObject());
  516. var engine2 = new Engine(o => o.AddObjectConverter(new NegateBoolConverter()));
  517. engine2.SetValue("p", new { Test = true });
  518. engine2.Execute("var result = p.Test;");
  519. Assert.False((bool)engine2.GetValue("result").ToObject());
  520. }
  521. [Fact]
  522. public void CanUserIncrementOperator()
  523. {
  524. var p = new Person
  525. {
  526. Age = 1,
  527. };
  528. _engine.SetValue("p", p);
  529. RunTest(@"
  530. assert(++p.Age === 2);
  531. ");
  532. Assert.Equal(2, p.Age);
  533. }
  534. [Fact]
  535. public void CanOverwriteValues()
  536. {
  537. _engine.SetValue("x", 3);
  538. _engine.SetValue("x", 4);
  539. RunTest(@"
  540. assert(x === 4);
  541. ");
  542. }
  543. [Fact]
  544. public void ShouldCreateGenericType()
  545. {
  546. RunTest(@"
  547. var ListOfString = System.Collections.Generic.List(System.String);
  548. var list = new ListOfString();
  549. list.Add('foo');
  550. list.Add(1);
  551. assert(2 === list.Count);
  552. ");
  553. }
  554. [Fact]
  555. public void EnumIsConvertedToNumber()
  556. {
  557. var o = new
  558. {
  559. r = Colors.Red,
  560. b = Colors.Blue,
  561. g = Colors.Green
  562. };
  563. _engine.SetValue("o", o);
  564. RunTest(@"
  565. assert(o.r === 0);
  566. assert(o.g === 1);
  567. assert(o.b === 10);
  568. ");
  569. }
  570. [Fact]
  571. public void ShouldConvertToEnum()
  572. {
  573. var s = new Circle
  574. {
  575. Color = Colors.Red,
  576. };
  577. _engine.SetValue("s", s);
  578. RunTest(@"
  579. assert(s.Color === 0);
  580. s.Color = 10;
  581. assert(s.Color === 10);
  582. ");
  583. _engine.SetValue("s", s);
  584. RunTest(@"
  585. s.Color = 11;
  586. assert(s.Color === 11);
  587. ");
  588. Assert.Equal(Colors.Blue | Colors.Green, s.Color);
  589. }
  590. }
  591. }