-
-
Notifications
You must be signed in to change notification settings - Fork 568
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix JSON.stringify for lists (#1006)
* split InteropTests a bit * cleanup JsonSerializer a bit
- Loading branch information
Showing
5 changed files
with
405 additions
and
305 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,185 @@ | ||
using System.Collections.Generic; | ||
using System.Dynamic; | ||
using System.Linq; | ||
using Xunit; | ||
|
||
namespace Jint.Tests.Runtime | ||
{ | ||
public partial class InteropTests | ||
{ | ||
[Fact] | ||
public void CanAccessExpandoObject() | ||
{ | ||
var engine = new Engine(); | ||
dynamic expando = new ExpandoObject(); | ||
expando.Name = "test"; | ||
engine.SetValue("expando", expando); | ||
Assert.Equal("test", engine.Evaluate("expando.Name").ToString()); | ||
} | ||
|
||
[Fact] | ||
public void CanAccessMemberNamedItemThroughExpando() | ||
{ | ||
var parent = (IDictionary<string, object>) new ExpandoObject(); | ||
var child = (IDictionary<string, object>) new ExpandoObject(); | ||
var values = (IDictionary<string, object>) new ExpandoObject(); | ||
|
||
parent["child"] = child; | ||
child["item"] = values; | ||
values["title"] = "abc"; | ||
|
||
_engine.SetValue("parent", parent); | ||
Assert.Equal("abc", _engine.Evaluate("parent.child.item.title")); | ||
} | ||
|
||
|
||
[Fact] | ||
public void EngineShouldStringifyAnExpandoObjectCorrectly() | ||
{ | ||
var engine = new Engine(); | ||
|
||
dynamic expando = new ExpandoObject(); | ||
expando.foo = 5; | ||
expando.bar = "A string"; | ||
engine.SetValue(nameof(expando), expando); | ||
|
||
var result = engine.Evaluate($"JSON.stringify({nameof(expando)})").AsString(); | ||
Assert.Equal("{\"foo\":5,\"bar\":\"A string\"}", result); | ||
} | ||
|
||
[Fact] | ||
public void EngineShouldStringifyAnExpandoObjectWithValuesCorrectly() | ||
{ | ||
// https://github.com/sebastienros/jint/issues/995 | ||
var engine = new Engine(); | ||
|
||
dynamic expando = new ExpandoObject(); | ||
expando.Values = 1; | ||
engine.SetValue("expando", expando); | ||
|
||
Assert.Equal("{\"Values\":1}", engine.Evaluate($"JSON.stringify(expando)").AsString()); | ||
} | ||
|
||
[Fact] | ||
public void ShouldForOfOnExpandoObject() | ||
{ | ||
dynamic o = new ExpandoObject(); | ||
o.a = 1; | ||
o.b = 2; | ||
|
||
_engine.SetValue("dynamic", o); | ||
|
||
var result = _engine.Evaluate("var l = ''; for (var x of dynamic) l += x; return l;").AsString(); | ||
|
||
Assert.Equal("a,1b,2", result); | ||
} | ||
|
||
[Fact] | ||
public void ShouldConvertObjectInstanceToExpando() | ||
{ | ||
_engine.Evaluate("var o = {a: 1, b: 'foo'}"); | ||
var result = _engine.GetValue("o"); | ||
|
||
dynamic value = result.ToObject(); | ||
|
||
Assert.Equal(1, value.a); | ||
Assert.Equal("foo", value.b); | ||
|
||
var dic = (IDictionary<string, object>) result.ToObject(); | ||
|
||
Assert.Equal(1d, dic["a"]); | ||
Assert.Equal("foo", dic["b"]); | ||
} | ||
|
||
[Fact] | ||
public void EngineShouldStringifyAnJObjectArrayWithValuesCorrectly() | ||
{ | ||
//https://github.com/OrchardCMS/OrchardCore/issues/10648 | ||
var engine = new Engine(); | ||
var queryResults = new List<dynamic>(); | ||
queryResults.Add(new { Text = "Text1", Value = 1 }); | ||
queryResults.Add(new { Text = "Text2", Value = 2 }); | ||
|
||
engine.SetValue("testSubject", queryResults.ToArray()); | ||
var fromEngine2 = engine.Evaluate("return JSON.stringify(testSubject);"); | ||
var result2 = fromEngine2.ToString(); | ||
Assert.Equal("[{\"Text\":\"Text1\",\"Value\":1},{\"Text\":\"Text2\",\"Value\":2}]", result2); | ||
} | ||
|
||
[Fact] | ||
public void EngineShouldStringifyDynamicObjectListWithValuesCorrectly() | ||
{ | ||
var engine = new Engine(); | ||
var source = new dynamic[] { new { Text = "Text1", Value = 1 }, new { Text = "Text2", Value = 2 } }; | ||
|
||
var objects = source.ToList(); | ||
engine.SetValue("testSubject", objects); | ||
var fromEngine = engine.Evaluate("return JSON.stringify(testSubject);"); | ||
var result = fromEngine.ToString(); | ||
Assert.Equal("[{\"Text\":\"Text1\",\"Value\":1},{\"Text\":\"Text2\",\"Value\":2}]", result); | ||
} | ||
|
||
[Fact] | ||
public void EngineShouldStringifyDynamicObjectArrayWithValuesCorrectly() | ||
{ | ||
var engine = new Engine(); | ||
var source = new dynamic[] { new { Text = "Text1", Value = 1 }, new { Text = "Text2", Value = 2 } }; | ||
|
||
engine.SetValue("testSubject", source.AsEnumerable()); | ||
var fromEngine = engine.Evaluate("return JSON.stringify(testSubject);"); | ||
var result = fromEngine.ToString(); | ||
Assert.Equal("[{\"Text\":\"Text1\",\"Value\":1},{\"Text\":\"Text2\",\"Value\":2}]", result); | ||
} | ||
|
||
[Fact] | ||
public void CanAccessDynamicObject() | ||
{ | ||
var test = new DynamicClass(); | ||
var engine = new Engine(); | ||
|
||
engine.SetValue("test", test); | ||
|
||
Assert.Equal("a", engine.Evaluate("test.a").AsString()); | ||
Assert.Equal("b", engine.Evaluate("test.b").AsString()); | ||
|
||
engine.Evaluate("test.a = 5; test.b = 10; test.Name = 'Jint'"); | ||
|
||
Assert.Equal(5, engine.Evaluate("test.a").AsNumber()); | ||
Assert.Equal(10, engine.Evaluate("test.b").AsNumber()); | ||
|
||
Assert.Equal("Jint", engine.Evaluate("test.Name").AsString()); | ||
Assert.True(engine.Evaluate("test.ContainsKey('a')").AsBoolean()); | ||
Assert.True(engine.Evaluate("test.ContainsKey('b')").AsBoolean()); | ||
Assert.False(engine.Evaluate("test.ContainsKey('c')").AsBoolean()); | ||
} | ||
|
||
private class DynamicClass : DynamicObject | ||
{ | ||
private readonly Dictionary<string, object> _properties = new Dictionary<string, object>(); | ||
|
||
public override bool TryGetMember(GetMemberBinder binder, out object result) | ||
{ | ||
result = binder.Name; | ||
if (_properties.TryGetValue(binder.Name, out var value)) | ||
{ | ||
result = value; | ||
} | ||
|
||
return true; | ||
} | ||
|
||
public override bool TrySetMember(SetMemberBinder binder, object value) | ||
{ | ||
_properties[binder.Name] = value; | ||
return true; | ||
} | ||
|
||
public string Name { get; set; } | ||
|
||
public bool ContainsKey(string key) | ||
{ | ||
return _properties.ContainsKey(key); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using Jint.Runtime; | ||
using Jint.Runtime.Interop; | ||
using Newtonsoft.Json.Linq; | ||
using Xunit; | ||
|
||
namespace Jint.Tests.Runtime | ||
{ | ||
public partial class InteropTests | ||
{ | ||
[Fact] | ||
public void AccessingJObjectShouldWork() | ||
{ | ||
var o = new JObject | ||
{ | ||
new JProperty("name", "test-name") | ||
}; | ||
_engine.SetValue("o", o); | ||
Assert.True(_engine.Evaluate("return o.name == 'test-name'").AsBoolean()); | ||
} | ||
|
||
[Fact] | ||
public void AccessingJArrayViaIntegerIndexShouldWork() | ||
{ | ||
var o = new JArray("item1", "item2"); | ||
_engine.SetValue("o", o); | ||
Assert.True(_engine.Evaluate("return o[0] == 'item1'").AsBoolean()); | ||
Assert.True(_engine.Evaluate("return o[1] == 'item2'").AsBoolean()); | ||
} | ||
|
||
[Fact] | ||
public void DictionaryLikeShouldCheckIndexerAndFallBackToProperty() | ||
{ | ||
const string json = @"{ ""Type"": ""Cat"" }"; | ||
var jObjectWithTypeProperty = JObject.Parse(json); | ||
|
||
_engine.SetValue("o", jObjectWithTypeProperty); | ||
|
||
var typeResult = _engine.Evaluate("o.Type"); | ||
|
||
// JToken requires conversion | ||
Assert.Equal("Cat", TypeConverter.ToString(typeResult)); | ||
|
||
// weak equality does conversions from native types | ||
Assert.True(_engine.Evaluate("o.Type == 'Cat'").AsBoolean()); | ||
} | ||
|
||
[Fact] | ||
public void ShouldBeAbleToIndexJObjectWithStrings() | ||
{ | ||
var engine = new Engine(); | ||
|
||
const string json = @" | ||
{ | ||
'Properties': { | ||
'expirationDate': { | ||
'Value': '2021-10-09T00:00:00Z' | ||
} | ||
} | ||
}"; | ||
|
||
var obj = JObject.Parse(json); | ||
engine.SetValue("o", obj); | ||
var value = engine.Evaluate("o.Properties.expirationDate.Value"); | ||
var wrapper = Assert.IsAssignableFrom<ObjectWrapper>(value); | ||
var token = wrapper.Target as JToken; | ||
var localDateTimeString = DateTime.Parse("2021-10-09T00:00:00Z").ToUniversalTime(); | ||
Assert.Equal(localDateTimeString.ToString(), token.ToString()); | ||
} | ||
|
||
// https://github.com/OrchardCMS/OrchardCore/issues/10648 | ||
[Fact] | ||
public void EngineShouldStringifyAnJObjectListWithValuesCorrectly() | ||
{ | ||
var engine = new Engine(); | ||
var queryResults = new List<dynamic> | ||
{ | ||
new { Text = "Text1", Value = 1 }, | ||
new { Text = "Text2", Value = 2 } | ||
}; | ||
|
||
engine.SetValue("testSubject", queryResults.Select(x => JObject.FromObject(x))); | ||
var fromEngine = engine.Evaluate("return JSON.stringify(testSubject);"); | ||
var result = fromEngine.ToString(); | ||
|
||
// currently we do not materialize LINQ enumerables | ||
// Assert.Equal("[{\"Text\":\"Text1\",\"Value\":1},{\"Text\":\"Text2\",\"Value\":2}]", result); | ||
|
||
Assert.Equal("{\"Current\":null}", result); | ||
} | ||
} | ||
} |
Oops, something went wrong.