/// <summary> /// Parses the specified expression and verifies that both the computed path and expected results are achieved /// </summary> void TestExpression <TRoot, TResult>(TRoot instance, string expression, TResult expectedValue, string expectedPath) { // Get the corresponding model type var type = typeof(TRoot).GetModelType(); // Use the type to parse the model expression var exp = type.GetExpression <TResult>(expression); // Ensure the computed path is correct Assert.AreEqual(expectedPath, exp.Path.Path); // Ensure the expression yields the correct value if (exp.Expression.Parameters.Count == 0) { Assert.AreEqual(expectedValue, exp.Expression.Compile().DynamicInvoke()); } else { Assert.AreEqual(expectedValue, exp.Expression.Compile().DynamicInvoke(instance)); } // Convert the expression to javascript var translation = translator.Translate(exp.Expression); if (translation.Exceptions.Count > 0) { throw translation.Exceptions.First(); } // Invoke the javascript expression var js = "function (x) { " + translation.Exports.Aggregate("", (exports, export) => exports += export.Key + "=" + export.Value + ";") + " return (" + translation.Function + ").call(x);}(x)"; var f = Accessors.CreateScriptFunction("x", js); var result = f(type.GetModelInstance(instance)); if (typeof(TResult) == typeof(string)) { result = String.Concat(result); } else if (typeof(TResult) == typeof(DateTime)) { result = ((Jurassic.Library.DateInstance)f(type.GetModelInstance(instance))).Value; } else { result = Convert.ChangeType(f(type.GetModelInstance(instance)), typeof(TResult)); } if (typeof(TResult) == typeof(DateTime)) { // Jurassic automatically converts to UTC but in reality we always convert to LocalTime on the client DateTime resultDate = (DateTime)Convert.ChangeType(result, typeof(DateTime)); var expectedDate = (DateTime)(object)expectedValue; Assert.AreEqual(new DateTime(expectedDate.Ticks, DateTimeKind.Local), resultDate.ToLocalTime()); } else { // Verify that the javascript expression evaluated to the correct result Assert.AreEqual(expectedValue, result); } }
public void TestCompiledExpressions() { // basic test to make sure functions compile var plus = Accessors.CreateScriptFunction("a", "b", "a + b"); Assert.AreEqual(1.0 + 2.0, plus(1.0, 2.0)); // create a second function with same arg names to test function isolation var minus = Accessors.CreateScriptFunction("a", "b", "a - b"); Assert.AreEqual(1.0 - 2.0, minus(1.0, 2.0)); Assert.AreEqual(1.0 + 2.0, plus(1.0, 2.0)); // make sure arguments are declared locally (in above functions) try { Accessors.CreateScriptFunction("a", "b"); Assert.Fail("Should not be able to reference undefined arguments"); } catch { } // make sure arguments are local (again) var localSideEffects = Accessors.CreateScriptFunction("a", "++a"); Assert.AreEqual(2.0, localSideEffects(1.0)); Assert.AreEqual(2.0, localSideEffects(1.0)); // test full function body Assert.AreEqual("xyz", Accessors.CreateScriptFunction("a", "var b=a; return b;", false)("xyz")); }
public void TestEntityArrays() { var data = new Category() { Name = "parent " }; data.ChildCategories.Add(new Category() { Name = "child1" }); data.ChildCategories.Add(new Category() { Name = "child2" }); var echo = Accessors.CreateScriptFunction("list", "list"); AssertLists.AreSame(data.ChildCategories, (IEnumerable)echo(data.ChildCategories), "lists should be the same"); // test wrap/unwrap Assert.AreEqual((uint)data.ChildCategories.Count, Accessors.CreateScriptFunction("list", "list.length")(data.ChildCategories)); // make sure the list is an array Assert.AreEqual((uint)data.ChildCategories.Count, Accessors.CreateScriptFunction("list", "list.length")(data.ChildCategories)); // test optimization (wrap/unwrap)+ var unwrapped1 = echo(data.ChildCategories); var unwrapped2 = echo(unwrapped1); Assert.AreEqual(Accessors.GetUnwrappedArray(unwrapped1), Accessors.GetUnwrappedArray(unwrapped2)); }
public void TestCompiledExpressionsNullAndUndefinedMarshalling() { Assert.AreEqual(true, Accessors.CreateScriptFunction("x", "x === null")(null), "marshal null === null"); Assert.AreEqual(true, Accessors.CreateScriptFunction("x", "x !== undefined")(null), "marshal null !== undefined"); Assert.AreEqual(null, Accessors.CreateScriptFunction("x", "null")(null), "marshal return null"); Assert.AreEqual(null, Accessors.CreateScriptFunction("x", "undefined")(null), "marshal return undefined"); }
public void TestExpressionsReturningFunctions() { var functionGenerator = Accessors.CreateScriptFunction("a", "function(x) {return x;}"); var f = (FunctionInstance)functionGenerator("1"); Assert.AreEqual("abc", f.Call(null, "abc")); }
public void TestCompiledExpressionsFunctionMarshaling() { int value = 7; var add = Accessors.CreateScriptFunction("arg", "arg(3)"); Assert.AreEqual( value + 3, add((Func <int, int>)(i => value + i)), "marshal ModelInstance"); }
public void TestCompiledExpressionsSyntaxError() { try { Accessors.CreateScriptFunction("a", "a ==== a"); Assert.Fail("Syntax error should be detected"); } catch (Exception e) { Assert.IsTrue(e.GetType().Name.EndsWith("ScriptFunctionSyntaxException"), "a special syntax exception should be thrown"); } }
private static void CreateDateParseLocale() { var createParseLocale = Accessors.CreateScriptFunction("o", @"(function() { Date.parseLocale = function (s) { var slashParts = s.split('/'); if (slashParts.length === 3) { return new Date(parseInt(slashParts[2], 10), parseInt(slashParts[0], 10) - 1, parseInt(slashParts[1], 10)); } throw new Error(""Date '"" + s + ""' could not be parsed.""); }; }())" ); createParseLocale(true); }
private static void CreateNumberLocaleFormat() { var createLocaleFormat = Accessors.CreateScriptFunction("o", @"(function() { Number.prototype.localeFormat = function (format) { if (format === 'g') { return this.toString(); } if (format === 'C') { return '$' + this.toFixed(2).toString(); } throw new Error(""Number format '"" + format + ""' is not supported.""); }; }())" ); createLocaleFormat(true); }
public void TestCompiledExpressionsObjectMarshaling() { var data = new Request { User = new User() }; var path = Accessors.CreateScriptFunction("arg", "arg.get_User()"); Assert.AreEqual(((IModelInstance)data.User).Instance, path(((IModelInstance)data).Instance), "marshal ModelInstance"); var f = Accessors.CreateScriptFunction("arg", "arg"); Assert.AreEqual(true, f(true)); Assert.AreEqual("abc", f("abc")); Assert.AreEqual(1.0, f(1.0)); Assert.AreEqual(((IModelInstance)data).Instance, f(((IModelInstance)data).Instance)); }
private static void CreateDateLocaleFormat() { var createLocaleFormat = Accessors.CreateScriptFunction("o", @"(function() { Date.prototype.localeFormat = function (format) { var date = this; if (format === 'd') { return (date.getMonth() + 1) + '/' + date.getDate() + '/' + (1900 + date.getYear()).toString(); } if (format.indexOf('/') > 0) { var parts = format.split('/'); if (parts.every(function(p) { return p === 'M' || p === 'MM' || p === 'd' || p === 'dd' || p === 'yyyy'; })) { return parts.map(function(p) { if (p === 'M') { return (date.getMonth() + 1).toString(); } else if (p === 'MM') { return (date.getMonth() < 9 ? '0' : '') + (date.getMonth() + 1).toString(); } else if (p === 'd') { return date.getDate().toString(); } else if (p === 'dd') { return (date.getDate() < 10 ? '0' : '') + date.getDate().toString(); } else if (p === 'yyyy') { return (1900 + date.getYear()).toString(); } }).join('/'); } } throw new Error(""Date format '"" + format + ""' is not supported.""); }; }())" ); createLocaleFormat(true); }