Skip to content

Commit

Permalink
Merge branch 'main' of github.com:riganti/dotvvm into feature/azure-p…
Browse files Browse the repository at this point in the history
…ipelines
  • Loading branch information
cafour committed May 14, 2021
2 parents 4e04f9a + 305bc98 commit 0b448d5
Show file tree
Hide file tree
Showing 17 changed files with 281 additions and 136 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
using System.Linq.Expressions;
using Microsoft.Extensions.DependencyInjection;
using DotVVM.Framework.Binding.Properties;
using DotVVM.Framework.Utils;

namespace DotVVM.Framework.Tests.Binding
{
Expand Down Expand Up @@ -568,18 +569,19 @@ public void JsTranslator_EnumerableMin_WithSelector(string binding, string prope
}

[TestMethod]
[DataRow("Enumerable.OrderBy(ObjectArray, (TestComparisonType item) => item.Int)", "Int", DisplayName = "Regular call of Enumerable.OrderBy")]
[DataRow("Enumerable.OrderBy(ObjectArray, (TestComparisonType item) => item.Bool)", "Bool", DisplayName = "Regular call of Enumerable.OrderBy")]
[DataRow("Enumerable.OrderBy(ObjectArray, (TestComparisonType item) => item.String)", "String", DisplayName = "Regular call of Enumerable.OrderBy")]
[DataRow("Enumerable.OrderBy(ObjectArray, (TestComparisonType item) => item.Enum)", "Enum", DisplayName = "Regular call of Enumerable.OrderBy")]
[DataRow("ObjectArray.OrderBy((TestComparisonType item) => item.Int)", "Int", DisplayName = "Syntax sugar - extension method")]
[DataRow("ObjectArray.OrderBy((TestComparisonType item) => item.Bool)", "Bool", DisplayName = "Syntax sugar - extension method")]
[DataRow("ObjectArray.OrderBy((TestComparisonType item) => item.String)", "String", DisplayName = "Syntax sugar - extension method")]
[DataRow("ObjectArray.OrderBy((TestComparisonType item) => item.Enum)", "Enum", DisplayName = "Syntax sugar - extension method")]
public void JsTranslator_EnumerableOrderBy(string binding, string key)
[DataRow("Enumerable.OrderBy(ObjectArray, (TestComparisonType item) => item.Int)", "Int", typeof(int), DisplayName = "Regular call of Enumerable.OrderBy")]
[DataRow("Enumerable.OrderBy(ObjectArray, (TestComparisonType item) => item.Bool)", "Bool", typeof(bool), DisplayName = "Regular call of Enumerable.OrderBy")]
[DataRow("Enumerable.OrderBy(ObjectArray, (TestComparisonType item) => item.String)", "String", typeof(string), DisplayName = "Regular call of Enumerable.OrderBy")]
[DataRow("Enumerable.OrderBy(ObjectArray, (TestComparisonType item) => item.Enum)", "Enum", typeof(TestComparisonType.TestEnum), DisplayName = "Regular call of Enumerable.OrderBy")]
[DataRow("ObjectArray.OrderBy((TestComparisonType item) => item.Int)", "Int", typeof(int), DisplayName = "Syntax sugar - extension method")]
[DataRow("ObjectArray.OrderBy((TestComparisonType item) => item.Bool)", "Bool", typeof(bool), DisplayName = "Syntax sugar - extension method")]
[DataRow("ObjectArray.OrderBy((TestComparisonType item) => item.String)", "String", typeof(string), DisplayName = "Syntax sugar - extension method")]
[DataRow("ObjectArray.OrderBy((TestComparisonType item) => item.Enum)", "Enum", typeof(TestComparisonType.TestEnum), DisplayName = "Syntax sugar - extension method")]
public void JsTranslator_EnumerableOrderBy(string binding, string key, Type comparedType)
{
var result = CompileBinding(binding, new[] { new NamespaceImport("System.Linq") }, new[] { typeof(TestArraysViewModel) });
Assert.AreEqual($"dotvvm.arrayHelper.orderBy(ObjectArray(),function(item){{return ko.unwrap(item).{key}();}})", result);
var typeHash = (comparedType.IsEnum) ? $"\"{comparedType.GetTypeHash()}\"" : "null";
Assert.AreEqual($"dotvvm.arrayHelper.orderBy(ObjectArray(),function(item){{return ko.unwrap(item).{key}();}},{typeHash})", result);
}

[TestMethod]
Expand All @@ -592,18 +594,19 @@ public void JsTranslator_EnumerableOrderBy_NonPrimitiveTypesThrows(string bindin
}

[TestMethod]
[DataRow("Enumerable.OrderByDescending(ObjectArray, (TestComparisonType item) => item.Int)", "Int", DisplayName = "Regular call of Enumerable.OrderByDescending")]
[DataRow("Enumerable.OrderByDescending(ObjectArray, (TestComparisonType item) => item.Bool)", "Bool", DisplayName = "Regular call of Enumerable.OrderByDescending")]
[DataRow("Enumerable.OrderByDescending(ObjectArray, (TestComparisonType item) => item.String)", "String", DisplayName = "Regular call of Enumerable.OrderByDescending")]
[DataRow("Enumerable.OrderByDescending(ObjectArray, (TestComparisonType item) => item.Enum)", "Enum", DisplayName = "Regular call of Enumerable.OrderByDescending")]
[DataRow("ObjectArray.OrderByDescending((TestComparisonType item) => item.Int)", "Int", DisplayName = "Syntax sugar - extension method")]
[DataRow("ObjectArray.OrderByDescending((TestComparisonType item) => item.Bool)", "Bool", DisplayName = "Syntax sugar - extension method")]
[DataRow("ObjectArray.OrderByDescending((TestComparisonType item) => item.String)", "String", DisplayName = "Syntax sugar - extension method")]
[DataRow("ObjectArray.OrderByDescending((TestComparisonType item) => item.Enum)", "Enum", DisplayName = "Syntax sugar - extension method")]
public void JsTranslator_EnumerableOrderByDescending(string binding, string key)
[DataRow("Enumerable.OrderByDescending(ObjectArray, (TestComparisonType item) => item.Int)", "Int", typeof(int), DisplayName = "Regular call of Enumerable.OrderByDescending")]
[DataRow("Enumerable.OrderByDescending(ObjectArray, (TestComparisonType item) => item.Bool)", "Bool", typeof(bool), DisplayName = "Regular call of Enumerable.OrderByDescending")]
[DataRow("Enumerable.OrderByDescending(ObjectArray, (TestComparisonType item) => item.String)", "String", typeof(string), DisplayName = "Regular call of Enumerable.OrderByDescending")]
[DataRow("Enumerable.OrderByDescending(ObjectArray, (TestComparisonType item) => item.Enum)", "Enum", typeof(TestComparisonType.TestEnum), DisplayName = "Regular call of Enumerable.OrderByDescending")]
[DataRow("ObjectArray.OrderByDescending((TestComparisonType item) => item.Int)", "Int", typeof(int), DisplayName = "Syntax sugar - extension method")]
[DataRow("ObjectArray.OrderByDescending((TestComparisonType item) => item.Bool)", "Bool", typeof(bool), DisplayName = "Syntax sugar - extension method")]
[DataRow("ObjectArray.OrderByDescending((TestComparisonType item) => item.String)", "String", typeof(string), DisplayName = "Syntax sugar - extension method")]
[DataRow("ObjectArray.OrderByDescending((TestComparisonType item) => item.Enum)", "Enum", typeof(TestComparisonType.TestEnum), DisplayName = "Syntax sugar - extension method")]
public void JsTranslator_EnumerableOrderByDescending(string binding, string key, Type comparedType)
{
var result = CompileBinding(binding, new[] { new NamespaceImport("System.Linq") }, new[] { typeof(TestArraysViewModel) });
Assert.AreEqual($"dotvvm.arrayHelper.orderByDesc(ObjectArray(),function(item){{return ko.unwrap(item).{key}();}})", result);
var typeHash = (comparedType.IsEnum) ? $"\"{comparedType.GetTypeHash()}\"" : "null";
Assert.AreEqual($"dotvvm.arrayHelper.orderByDesc(ObjectArray(),function(item){{return ko.unwrap(item).{key}();}},{typeHash})", result);
}

[TestMethod]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using DotVVM.Framework.Binding;
using DotVVM.Framework.Binding.Expressions;
using DotVVM.Framework.Compilation.ControlTree;
using DotVVM.Framework.Configuration;
using DotVVM.Framework.Controls;
using DotVVM.Framework.Hosting;
using Microsoft.Extensions.DependencyInjection;
Expand Down Expand Up @@ -60,6 +61,34 @@ Repeater createRepeater(RenderMode renderMode)
Assert.IsTrue(!serverHtml.Contains("<div"));
}

[TestMethod]
public void RouteLink_SpaNavigation()
{
DotvvmConfiguration configuration;

RouteLink createRouteLink()
{
var routeLink = new RouteLink() {
RouteName = "TestRoute"
};
routeLink.SetValue(Internal.IsSpaPageProperty, true);

configuration = DotvvmTestHelper.CreateConfiguration();
configuration.RouteTable.Add("TestRoute", "TestRoute");
configuration.Freeze();
return routeLink;
}

var routeLinkWithoutTarget = createRouteLink();
var clientHtml1 = InvokeLifecycleAndRender(routeLinkWithoutTarget, DotvvmTestHelper.CreateContext(configuration));
Assert.IsTrue(clientHtml1.Contains("dotvvm.handleSpaNavigation(this)"));

var routeLinkWithTarget = createRouteLink();
routeLinkWithTarget.Attributes.Add("target", "_blank");
var clientHtml2 = InvokeLifecycleAndRender(routeLinkWithTarget, DotvvmTestHelper.CreateContext(configuration));
Assert.IsFalse(clientHtml2.Contains("dotvvm.handleSpaNavigation(this)"));
}

[TestMethod]
public void BindingGroup_EnumProperty()
{
Expand Down
34 changes: 34 additions & 0 deletions src/DotVVM.Framework.Tests.Common/ViewModel/SerializerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using DotVVM.Framework.ViewModel;
using DotVVM.Framework.Compilation.Parser;
using DotVVM.Framework.Configuration;
using System.Text;

namespace DotVVM.Framework.Tests.Common.ViewModel
{
Expand Down Expand Up @@ -148,6 +149,34 @@ public void Support_NestedMixedProtectedData()
Assert.AreEqual(serialized, Serialize(deserialized, out var _, false));
}

[TestMethod]
[DataRow(null)]
[DataRow(new byte[] { })]
[DataRow(new byte[] { 1 })]
[DataRow(new byte[] { 1, 2, 3 })]
public void CustomJsonConverters_ByteArray(byte[] array)
{
using var stream = new MemoryStream();
// Serialize array
using (var writer = new JsonTextWriter(new StreamWriter(stream, Encoding.UTF8, leaveOpen: true)))
{
new DotvvmByteArrayConverter().WriteJson(writer, array, new JsonSerializer());
writer.Flush();
}

// Deserialize array
stream.Position = 0;
byte[] deserialized;
using (var reader = new JsonTextReader(new StreamReader(stream, Encoding.UTF8)))
{
while (reader.TokenType == JsonToken.None)
reader.Read();

deserialized = (byte[])new DotvvmByteArrayConverter().ReadJson(reader, typeof(byte[]), null, new JsonSerializer());
}

CollectionAssert.AreEqual(array, deserialized);
}

[TestMethod]
public void SupportTuples()
Expand Down Expand Up @@ -199,6 +228,11 @@ public class TestViewModelWithNestedProtectedData
public DataNode Root { get; set; }
}

public class TestViewModelWithByteArray
{
public byte[] Bytes { get; set; }
}

public class TestViewModelWithCollectionOfNestedProtectedData
{
[Protect(ProtectMode.SignData)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@ public GenericMethodCompiler(Func<JsExpression[], JsExpression> builder, Func<Me
: builder(new [] { t.JsExpression() }.Concat(arg.Select(a => a.JsExpression())).ToArray());
}

public GenericMethodCompiler(Func<JsExpression[], Expression[], JsExpression> builder, Func<MethodInfo, Expression, Expression[], bool> check = null)
{
TryTranslateDelegate =
(t, arg, m) => check?.Invoke(m, t.OriginalExpression, arg.Select(a => a.OriginalExpression).ToArray()) == false
? null
: builder(new[] { t.JsExpression() }.Concat(arg.Select(a => a.JsExpression())).ToArray(), arg.Select(a => a.OriginalExpression).ToArray());
}

public GenericMethodCompiler(Func<JsExpression[], MethodInfo, JsExpression> builder, Func<MethodInfo, Expression, Expression[], bool> check = null)
{
TryTranslateDelegate =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ JsExpression indexer(JsExpression[] args, MethodInfo method) =>
AddMethodTranslator(typeof(IList), "get_Item", new GenericMethodCompiler(indexer));
AddMethodTranslator(typeof(IList<>), "get_Item", new GenericMethodCompiler(indexer));
AddMethodTranslator(typeof(List<>), "get_Item", new GenericMethodCompiler(indexer));
AddPropertyGetterTranslator(typeof(Nullable<>), "Value", new GenericMethodCompiler((args, method) => args[0]));
AddPropertyGetterTranslator(typeof(Nullable<>), "Value", new GenericMethodCompiler((JsExpression[] args, MethodInfo method) => args[0]));
AddPropertyGetterTranslator(typeof(Nullable<>), "HasValue",
new GenericMethodCompiler(args => new JsBinaryExpression(args[0], BinaryOperatorType.NotEqual, new JsLiteral(null))));
//AddMethodTranslator(typeof(Enumerable), nameof(Enumerable.Count), lengthMethod, new[] { typeof(IEnumerable) });
Expand Down Expand Up @@ -345,6 +345,12 @@ bool EnsureIsComparableInJavascript(MethodInfo method, Type type)
return true;
}

bool IsDelegateReturnTypeEnum(Type type)
=> type.GetGenericArguments().Last().IsEnum;

string GetDelegateReturnTypeHash(Type type)
=> type.GetGenericArguments().Last().GetTypeHash();

AddMethodTranslator(typeof(Enumerable), nameof(Enumerable.All), parameterCount: 2, translator: new GenericMethodCompiler(args =>
new JsIdentifierExpression("dotvvm").Member("arrayHelper").Member("all").Invoke(args[1], args[2])));
AddMethodTranslator(typeof(Enumerable), nameof(Enumerable.Any), parameterCount: 1, translator: new GenericMethodCompiler(args =>
Expand Down Expand Up @@ -389,10 +395,12 @@ bool EnsureIsComparableInJavascript(MethodInfo method, Type type)
}

AddMethodTranslator(typeof(Enumerable), nameof(Enumerable.OrderBy), parameterCount: 2,
translator: new GenericMethodCompiler(args => new JsIdentifierExpression("dotvvm").Member("arrayHelper").Member("orderBy").Invoke(args[1], args[2]),
translator: new GenericMethodCompiler((jArgs, dArgs) => new JsIdentifierExpression("dotvvm").Member("arrayHelper").Member("orderBy")
.Invoke(jArgs[1], jArgs[2], new JsLiteral((IsDelegateReturnTypeEnum(dArgs.Last().Type)) ? GetDelegateReturnTypeHash(dArgs.Last().Type) : null)),
check: (method, _, arguments) => EnsureIsComparableInJavascript(method, arguments.Last().Type.GetGenericArguments().Last())));
AddMethodTranslator(typeof(Enumerable), nameof(Enumerable.OrderByDescending), parameterCount: 2,
translator: new GenericMethodCompiler(args => new JsIdentifierExpression("dotvvm").Member("arrayHelper").Member("orderByDesc").Invoke(args[1], args[2]),
translator: new GenericMethodCompiler((jArgs, dArgs) => new JsIdentifierExpression("dotvvm").Member("arrayHelper").Member("orderByDesc")
.Invoke(jArgs[1], jArgs[2], new JsLiteral((IsDelegateReturnTypeEnum(dArgs.Last().Type)) ? GetDelegateReturnTypeHash(dArgs.Last().Type) : null)),
check: (method, _, arguments) => EnsureIsComparableInJavascript(method, arguments.Last().Type.GetGenericArguments().Last())));

var selectMethod = typeof(Enumerable).GetMethods(BindingFlags.Public | BindingFlags.Static)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ private JsonSerializerSettings CreateSettings()
{
new DotvvmDateTimeConverter(),
new StringEnumConverter(),
new DotvvmDictionaryConverter()
new DotvvmDictionaryConverter(),
new DotvvmByteArrayConverter()
}
};
}
Expand Down
2 changes: 1 addition & 1 deletion src/DotVVM.Framework/Controls/RouteLink.cs
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ protected virtual void WriteOnClickAttribute(IHtmlWriter writer, IDotvvmRequestC
{
// a hack that makes the RouteLink work even in container with Events.Click. This does not solve the problem in general, but better than nothing.
var onclickAttribute = "event.stopPropagation();";
if ((bool)GetValue(Internal.IsSpaPageProperty)! && (bool)GetValue(Internal.UseHistoryApiSpaNavigationProperty)!)
if ((bool)GetValue(Internal.IsSpaPageProperty)! && (bool)GetValue(Internal.UseHistoryApiSpaNavigationProperty)! && !Attributes.ContainsKey("target"))
{
onclickAttribute += "!this.hasAttribute('disabled') && dotvvm.handleSpaNavigation(this); return false;";
}
Expand Down
Loading

0 comments on commit 0b448d5

Please sign in to comment.