diff --git a/src/DotVVM.CommandLine/DotVVM.CommandLine.csproj b/src/DotVVM.CommandLine/DotVVM.CommandLine.csproj index d1f8eb6fec..40ce29c0dd 100644 --- a/src/DotVVM.CommandLine/DotVVM.CommandLine.csproj +++ b/src/DotVVM.CommandLine/DotVVM.CommandLine.csproj @@ -3,7 +3,7 @@ DotVVM.CommandLine dotnet-dotvvm - 3.0.0 + 3.0.2 netcoreapp2.1 dotvvmwizard.snk true @@ -11,7 +11,7 @@ dotvvm true DotVVM.CommandLine - 3.0.0 + 3.0.2 RIGANTI Command-line tools for DotVVM. false diff --git a/src/DotVVM.CommandLine/Properties/AssemblyInfo.cs b/src/DotVVM.CommandLine/Properties/AssemblyInfo.cs index 447c702639..cb127f217f 100644 --- a/src/DotVVM.CommandLine/Properties/AssemblyInfo.cs +++ b/src/DotVVM.CommandLine/Properties/AssemblyInfo.cs @@ -27,6 +27,6 @@ // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: -// [assembly: AssemblyVersion("3.0.0")] -[assembly: AssemblyVersion("3.0.0")] -[assembly: AssemblyFileVersion("3.0.0")] +// [assembly: AssemblyVersion("3.0.2")] +[assembly: AssemblyVersion("3.0.2")] +[assembly: AssemblyFileVersion("3.0.2")] diff --git a/src/DotVVM.Compiler.Light/DotVVM.Compiler.Light.csproj b/src/DotVVM.Compiler.Light/DotVVM.Compiler.Light.csproj index 6bd021c338..b98d2c45d8 100644 --- a/src/DotVVM.Compiler.Light/DotVVM.Compiler.Light.csproj +++ b/src/DotVVM.Compiler.Light/DotVVM.Compiler.Light.csproj @@ -1,11 +1,11 @@  - 3.0.0 + 3.0.2 netcoreapp2.0 dotnet-dotvvm-compiler DotVVM.Compiler.Light - 3.0.0 + 3.0.2 RIGANTI Command-line compiler for DotVVM. false diff --git a/src/DotVVM.Compiler.Light/Properties/AssemblyInfo.cs b/src/DotVVM.Compiler.Light/Properties/AssemblyInfo.cs index 51b2c852f3..7066af5b49 100644 --- a/src/DotVVM.Compiler.Light/Properties/AssemblyInfo.cs +++ b/src/DotVVM.Compiler.Light/Properties/AssemblyInfo.cs @@ -27,5 +27,5 @@ // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: -[assembly: AssemblyVersion("3.0.0")] -[assembly: AssemblyFileVersion("3.0.0")] +[assembly: AssemblyVersion("3.0.2")] +[assembly: AssemblyFileVersion("3.0.2")] diff --git a/src/DotVVM.Core/DotVVM.Core.csproj b/src/DotVVM.Core/DotVVM.Core.csproj index 99a4a7da58..430789db03 100644 --- a/src/DotVVM.Core/DotVVM.Core.csproj +++ b/src/DotVVM.Core/DotVVM.Core.csproj @@ -1,6 +1,6 @@  - 3.0.0 + 3.0.2 netstandard1.6;net451 $(NoWarn);CS1591 true @@ -17,7 +17,7 @@ false false DotVVM.Core - 3.0.0 + 3.0.2 RIGANTI This package contains base classes and interfaces of DotVVM that might be useful in a business layer. DotVVM is an open source ASP.NET-based framework which allows to build modern web apps without writing any JavaScript code. diff --git a/src/DotVVM.Core/Properties/AssemblyInfo.cs b/src/DotVVM.Core/Properties/AssemblyInfo.cs index b5c97b8793..202f9c830f 100644 --- a/src/DotVVM.Core/Properties/AssemblyInfo.cs +++ b/src/DotVVM.Core/Properties/AssemblyInfo.cs @@ -28,6 +28,6 @@ // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: -// [assembly: AssemblyVersion("3.0.0")] -[assembly: AssemblyVersion("3.0.0")] -[assembly: AssemblyFileVersion("3.0.0")] +// [assembly: AssemblyVersion("3.0.2")] +[assembly: AssemblyVersion("3.0.2")] +[assembly: AssemblyFileVersion("3.0.2")] diff --git a/src/DotVVM.Framework.Api.Swashbuckle.AspNetCore/DotVVM.Framework.Api.Swashbuckle.AspNetCore.csproj b/src/DotVVM.Framework.Api.Swashbuckle.AspNetCore/DotVVM.Framework.Api.Swashbuckle.AspNetCore.csproj index 457c1f7de2..e5e84b2eb3 100644 --- a/src/DotVVM.Framework.Api.Swashbuckle.AspNetCore/DotVVM.Framework.Api.Swashbuckle.AspNetCore.csproj +++ b/src/DotVVM.Framework.Api.Swashbuckle.AspNetCore/DotVVM.Framework.Api.Swashbuckle.AspNetCore.csproj @@ -3,7 +3,7 @@ netstandard2.0 DotVVM.Api.Swashbuckle.AspNetCore - 3.0.0 + 3.0.2 RIGANTI Swashbuckle.AspNetCore extensions for DotVVM. false diff --git a/src/DotVVM.Framework.Api.Swashbuckle.Owin/DotVVM.Framework.Api.Swashbuckle.Owin.csproj b/src/DotVVM.Framework.Api.Swashbuckle.Owin/DotVVM.Framework.Api.Swashbuckle.Owin.csproj index 05aa7d0092..ea7f2a6729 100644 --- a/src/DotVVM.Framework.Api.Swashbuckle.Owin/DotVVM.Framework.Api.Swashbuckle.Owin.csproj +++ b/src/DotVVM.Framework.Api.Swashbuckle.Owin/DotVVM.Framework.Api.Swashbuckle.Owin.csproj @@ -3,7 +3,7 @@ net451 DotVVM.Api.Swashbuckle.Owin - 3.0.0 + 3.0.2 RIGANTI Swashbuckle.Owin extensions for DotVVM. false diff --git a/src/DotVVM.Framework.Hosting.AspNetCore/DotVVM.Framework.Hosting.AspNetCore.csproj b/src/DotVVM.Framework.Hosting.AspNetCore/DotVVM.Framework.Hosting.AspNetCore.csproj index ef3b7b805a..8b9442bb42 100644 --- a/src/DotVVM.Framework.Hosting.AspNetCore/DotVVM.Framework.Hosting.AspNetCore.csproj +++ b/src/DotVVM.Framework.Hosting.AspNetCore/DotVVM.Framework.Hosting.AspNetCore.csproj @@ -1,6 +1,6 @@  - 3.0.0 + 3.0.2 netstandard2.0 $(NoWarn);CS1591 true @@ -17,7 +17,7 @@ false false DotVVM.AspNetCore - 3.0.0 + 3.0.2 RIGANTI This package contains ASP.NET Core middleware and hosting infrastructure for DotVVM apps. It supports both .NET Framework and .NET Core. diff --git a/src/DotVVM.Framework.Hosting.AspNetCore/Properties/AssemblyInfo.cs b/src/DotVVM.Framework.Hosting.AspNetCore/Properties/AssemblyInfo.cs index 83889035d8..02386ad6c1 100644 --- a/src/DotVVM.Framework.Hosting.AspNetCore/Properties/AssemblyInfo.cs +++ b/src/DotVVM.Framework.Hosting.AspNetCore/Properties/AssemblyInfo.cs @@ -18,8 +18,8 @@ // to COM components. If you need to access a type in this assembly from // COM, set the ComVisible attribute to true on that type. [assembly: ComVisible(false)] -[assembly: AssemblyVersion("3.0.0")] -[assembly: AssemblyFileVersion("3.0.0")] +[assembly: AssemblyVersion("3.0.2")] +[assembly: AssemblyFileVersion("3.0.2")] // The following GUID is for the ID of the typelib if this project is exposed to COM [assembly: Guid("c5af6a0d-5416-44d4-b14d-8ffdd1b0e99b")] diff --git a/src/DotVVM.Framework.Hosting.Owin/DotVVM.Framework.Hosting.Owin.csproj b/src/DotVVM.Framework.Hosting.Owin/DotVVM.Framework.Hosting.Owin.csproj index b1d9b271cf..dc403da410 100644 --- a/src/DotVVM.Framework.Hosting.Owin/DotVVM.Framework.Hosting.Owin.csproj +++ b/src/DotVVM.Framework.Hosting.Owin/DotVVM.Framework.Hosting.Owin.csproj @@ -1,6 +1,6 @@  - 3.0.0 + 3.0.2 net451 win7-x64;win7-x86 true @@ -17,7 +17,7 @@ false false DotVVM.Owin - 3.0.0 + 3.0.2 RIGANTI This package contains OWIN middleware and hosting infrastructure for DotVVM apps. diff --git a/src/DotVVM.Framework.Hosting.Owin/Properties/AssemblyInfo.cs b/src/DotVVM.Framework.Hosting.Owin/Properties/AssemblyInfo.cs index 06a1c867ec..aced3ba8cf 100644 --- a/src/DotVVM.Framework.Hosting.Owin/Properties/AssemblyInfo.cs +++ b/src/DotVVM.Framework.Hosting.Owin/Properties/AssemblyInfo.cs @@ -18,8 +18,8 @@ // to COM components. If you need to access a type in this assembly from // COM, set the ComVisible attribute to true on that type. [assembly: ComVisible(false)] -[assembly: AssemblyVersion("3.0.0")] -[assembly: AssemblyFileVersion("3.0.0")] +[assembly: AssemblyVersion("3.0.2")] +[assembly: AssemblyFileVersion("3.0.2")] // The following GUID is for the ID of the typelib if this project is exposed to COM [assembly: Guid("6DACF57C-91E9-4B2B-AF54-F09989BD8ED6")] diff --git a/src/DotVVM.Framework.Tests.Common/Binding/BindingCompilationTests.cs b/src/DotVVM.Framework.Tests.Common/Binding/BindingCompilationTests.cs index 2123635531..216c0c37e4 100755 --- a/src/DotVVM.Framework.Tests.Common/Binding/BindingCompilationTests.cs +++ b/src/DotVVM.Framework.Tests.Common/Binding/BindingCompilationTests.cs @@ -135,6 +135,88 @@ public void BindingCompiler_Valid_StringLiteralInSingleQuotes() Assert.AreEqual(ExecuteBinding("StringProp + 'def'", viewModel), "abcdef"); } + [TestMethod] + [DataRow(@"$'Non-Interpolated'", "Non-Interpolated")] + [DataRow(@"$'Non-Interpolated {{'", "Non-Interpolated {")] + [DataRow(@"$'Non-Interpolated {{ no-expression }}'", "Non-Interpolated { no-expression }")] + public void BindingCompiler_Valid_InterpolatedString_NoExpressions(string expression, string evaluated) + { + var binding = ExecuteBinding(expression); + Assert.AreEqual(evaluated, binding); + } + + [TestMethod] + [DataRow(@"$'} Malformed'", "Unexpected token '$' ---->}<----")] + [DataRow(@"$'{ Malformed'", "Could not find matching closing character '}' for an interpolated expression")] + [DataRow(@"$'Malformed {expr'", "Could not find matching closing character '}' for an interpolated expression")] + [DataRow(@"$'Malformed expr}'", "Unexpected token '$'Malformed expr ---->}<---- ")] + [DataRow(@"$'Malformed {'", "Could not find matching closing character '}' for an interpolated expression")] + [DataRow(@"$'Malformed }'", "Unexpected token '$'Malformed ---->}<----")] + [DataRow(@"$'Malformed {}'", "Expected expression, but instead found empty")] + [DataRow(@"$'Malformed {StringProp; IntProp}'", "Expected end of interpolated expression, but instead found Semicolon")] + [DataRow(@"$'Malformed {(string arg) => arg.Length}'", "Expected end of interpolated expression, but instead found Identifier")] + [DataRow(@"$'Malformed {(StringProp == null) ? 'StringPropWasNull' : 'StringPropWasNotNull'}'", "Conditional expression needs to be enclosed in parentheses")] + public void BindingCompiler_Invalid_InterpolatedString_MalformedExpression(string expression, string errorMessage) + { + try + { + ExecuteBinding(expression); + } + catch (Exception e) + { + // Get inner-most exception + var current = e; + while (current.InnerException != null) + current = current.InnerException; + + Assert.AreEqual(typeof(BindingCompilationException), current.GetType()); + StringAssert.Contains(current.Message, errorMessage); + } + } + + [TestMethod] + [DataRow(@"$""Interpolated {StringProp} {StringProp}""", "Interpolated abc abc")] + [DataRow(@"$'Interpolated {StringProp} {StringProp}'", "Interpolated abc abc")] + [DataRow(@"$'Interpolated {StringProp.Length}'", "Interpolated 3")] + public void BindingCompiler_Valid_InterpolatedString_WithSimpleExpressions(string expression, string evaluated) + { + var viewModel = new TestViewModel() { StringProp = "abc" }; + var binding = ExecuteBinding(expression, viewModel); + Assert.AreEqual(evaluated, binding); + } + + [TestMethod] + [DataRow(@"$'{string.Join(', ', IntArray)}'", "1, 2, 3")] + [DataRow(@"$'{string.Join(', ', 'abc', 'def', $'{string.Join(', ', IntArray)}')}'", "abc, def, 1, 2, 3")] + public void BindingCompiler_Valid_InterpolatedString_NestedExpressions(string expression, string evaluated) + { + var viewModel = new TestViewModel { IntArray = new[] { 1, 2, 3 } }; + var binding = ExecuteBinding(expression, viewModel); + Assert.AreEqual(evaluated, binding); + } + + [TestMethod] + [DataRow(@"$'Interpolated {IntProp < LongProperty}'", "Interpolated True")] + [DataRow(@"$'Interpolated {StringProp ?? 'StringPropWasNull'}'", "Interpolated StringPropWasNull")] + [DataRow(@"$'Interpolated {((StringProp == null) ? 'StringPropWasNull' : 'StringPropWasNotNull')}'", "Interpolated StringPropWasNull")] + public void BindingCompiler_Valid_InterpolatedString_WithComplexExpressions(string expression, string evaluated) + { + var viewModel = new TestViewModel() { IntProp = 1, LongProperty = 2 }; + var binding = ExecuteBinding(expression, viewModel); + Assert.AreEqual(evaluated, binding); + } + + [TestMethod] + [DataRow(@"$'Interpolated {DateFrom:R}'", "Interpolated Fri, 11 Nov 2011 12:11:11 GMT")] + [DataRow(@"$'Interpolated {$'{DateFrom:R}'}'", "Interpolated Fri, 11 Nov 2011 12:11:11 GMT")] + [DataRow(@"$'Interpolated {$'{IntProp:0000}'}'", "Interpolated 0006")] + public void BindingCompiler_Valid_InterpolatedString_WithFormattingComponent(string expression, string evaluated) + { + var viewModel = new TestViewModel() { DateFrom = DateTime.Parse("2011-11-11T11:11:11+00:00"), IntProp = 6 }; + var binding = ExecuteBinding(expression, viewModel); + Assert.AreEqual(evaluated, binding); + } + [TestMethod] public void BindingCompiler_Valid_PropertyProperty() { @@ -647,6 +729,7 @@ class TestViewModel { public string StringProp { get; set; } public int IntProp { get; set; } + public double DoubleProp { get; set; } public TestViewModel2 TestViewModel2 { get; set; } public TestViewModel2 TestViewModel2B { get; set; } public TestEnum EnumProperty { get; set; } @@ -659,6 +742,7 @@ class TestViewModel public long LongProperty { get; set; } public long[] LongArray => new long[] { 1, 2, long.MaxValue }; + public string[] StringArray => new string[] { "Hello ", "DotVVM" }; public TestViewModel2[] VmArray => new TestViewModel2[] { new TestViewModel2() }; public int[] IntArray { get; set; } diff --git a/src/DotVVM.Framework.Tests.Common/Binding/ExpressionHelperTests.cs b/src/DotVVM.Framework.Tests.Common/Binding/ExpressionHelperTests.cs index 489a67a447..cea114ae22 100644 --- a/src/DotVVM.Framework.Tests.Common/Binding/ExpressionHelperTests.cs +++ b/src/DotVVM.Framework.Tests.Common/Binding/ExpressionHelperTests.cs @@ -139,6 +139,22 @@ public void Call_FindOverload_Generic_Enumerable_Array(Type resultIdentifierType Call_FindOverload_Generic(typeof(MethodsGenericArgumentsResolvingSampleObject6), MethodsGenericArgumentsResolvingSampleObject6.MethodName, argTypes, resultIdentifierType, expectedGenericArgs); } [TestMethod] + [DataRow(typeof(GenericTestResult1), new Type[] { typeof(List), typeof(GenericInterfaceIntImplementation) }, new Type[] { typeof(int) })] + [DataRow(typeof(GenericTestResult1), new Type[] { typeof(List), typeof(GenericInterfaceFloatImplementation) }, new Type[] { typeof(float) })] + [DataRow(typeof(GenericTestResult2), new Type[] { typeof(List), typeof(DerivedFromGenericClassInt) }, new Type[] { typeof(int) })] + [DataRow(typeof(GenericTestResult3), new Type[] { typeof(List), typeof(MultiGenericInterfaceImplementation) }, new Type[] { typeof(int), typeof(string) })] + [DataRow(typeof(GenericTestResult3), new Type[] { typeof(List), typeof(GenericInterfaceStringImplementation) }, new Type[] { typeof(string) })] + [DataRow(typeof(GenericTestResult4), new Type[] { typeof(List), typeof(DerivedFromGenericClassString) }, new Type[] { typeof(string) })] + [DataRow(typeof(GenericTestResult5), new Type[] { typeof(List), typeof(GenericInterfaceIntImplementation), typeof(GenericInterfaceStringImplementation) }, new Type[] { typeof(int), typeof(string) })] + [DataRow(typeof(GenericTestResult6), new Type[] { typeof(List), typeof(GenericInterfaceIntImplementation), typeof(GenericInterfaceFloatImplementation) }, new Type[] { typeof(int) })] + [DataRow(typeof(GenericTestResult7), new Type[] { typeof(List) }, new Type[] { typeof(int) })] + [DataRow(typeof(GenericTestResult7), new Type[] { typeof(HashSet) }, new Type[] { typeof(int) })] + [DataRow(typeof(GenericTestResult8), new Type[] { typeof(List) }, new Type[] { typeof(string) })] + public void Call_FindOverload_Generic_ImplicitConversions(Type resultIdentifierType, Type[] argTypes, Type[] expectedGenericArgs) + { + Call_FindOverload_Generic(typeof(ImplicitConversionsTest), nameof(ImplicitConversionsTest.Method), argTypes, resultIdentifierType, expectedGenericArgs); + } + [TestMethod] [DataRow(typeof(GenericTestResult1), new Type[] { typeof(GenericModelSampleObject) }, new Type[] { typeof(int) })] [DataRow(typeof(GenericTestResult2), new Type[] { typeof(List[]) }, new Type[] { typeof(int) })] public void Call_FindOverload_Generic_Array_Recursive(Type resultIdentifierType, Type[] argTypes, Type[] expectedGenericArgs) @@ -322,6 +338,9 @@ public class GenericTestResult2 { } public class GenericTestResult3 { } public class GenericTestResult4 { } public class GenericTestResult5 { } + public class GenericTestResult6 { } + public class GenericTestResult7 { } + public class GenericTestResult8 { } public class ParamsPrioritizationTest { @@ -331,4 +350,28 @@ public class ParamsPrioritizationTest public static (string, int[]) Method(string arg1, params int[] arg2) => default; public static (string, object) Method(string arg1, object arg2) => default; } + + public interface GenericInterface { } + public class GenericClass { } + public class GenericInterfaceIntImplementation : GenericInterface { } + public class GenericInterfaceFloatImplementation : GenericInterface { } + public class GenericInterfaceStringImplementation : GenericInterface { } + public class MultiGenericInterfaceImplementation : GenericInterface, GenericInterface { } + public class DerivedFromGenericClassInt : GenericClass { } + public class DerivedFromGenericClassString : GenericClass { } + + public class ImplicitConversionsTest + { + public static GenericTestResult1 Method(List arg1, GenericInterface arg2) => default; + public static GenericTestResult2 Method(List arg1, GenericClass arg2) => default; + + public static GenericTestResult3 Method(List arg1, GenericInterface arg2) => default; + public static GenericTestResult4 Method(List arg1, GenericClass arg2) => default; + + public static GenericTestResult5 Method(List arg1, GenericInterface arg2, GenericInterface arg3) => default; + public static GenericTestResult6 Method(List arg1, GenericInterface arg2, GenericInterface arg3) => default; + + public static GenericTestResult7 Method(IEnumerable> arg1) => default; + public static GenericTestResult8 Method(IEnumerable> arg1) => default; + } } diff --git a/src/DotVVM.Framework.Tests.Common/Binding/JavascriptCompilationTests.cs b/src/DotVVM.Framework.Tests.Common/Binding/JavascriptCompilationTests.cs index 7b689c119e..834dbb44f8 100644 --- a/src/DotVVM.Framework.Tests.Common/Binding/JavascriptCompilationTests.cs +++ b/src/DotVVM.Framework.Tests.Common/Binding/JavascriptCompilationTests.cs @@ -126,6 +126,22 @@ public void JavascriptCompilation_ToString_Invalid() }); } + [TestMethod] + [DataRow(@"$""Interpolated {StringProp} {StringProp}""")] + [DataRow(@"$'Interpolated {StringProp} {StringProp}'")] + public void JavascriptCompilation_InterpolatedString(string expression) + { + var js = CompileBinding(expression, new[] { typeof(TestViewModel) }, typeof(string)); + Assert.AreEqual("dotvvm.globalize.format(\"Interpolated {0} {1}\",[StringProp(),StringProp()])", js); + } + + [TestMethod] + public void JavascriptCompilation_InterpolatedString_NoExpressions() + { + var js = CompileBinding("$'Non-Interpolated {{ no-expr }}'", new[] { typeof(TestViewModel) }); + Assert.AreEqual("\"Non-Interpolated { no-expr }\"", js); + } + [TestMethod] public void JavascriptCompilation_UnwrappedObservables() { @@ -232,7 +248,7 @@ public void JavascriptCompilation_Api_GetDate() public void JavascriptCompilation_Api_DateParameter() { var result = CompileBinding("_testApi.PostDateToString(DateFrom.Value)", typeof(TestViewModel)); - Assert.IsTrue(result.StartsWith("dotvvm.api.invoke(dotvvm.api._testApi,\"postDateToString\",function(){return [dotvvm.globalize.parseDotvvmDate(DateFrom())];},function(args){return [];},function(args){return [\"DotVVM.Framework.Tests.Binding.TestApiClient/\"];},$element,function(args){return \"")); + Assert.IsTrue(result.StartsWith("dotvvm.api.invoke(dotvvm.api._testApi,\"postDateToString\",function(){return [dotvvm.globalize.parseDate(DateFrom())];},function(args){return [];},function(args){return [\"DotVVM.Framework.Tests.Binding.TestApiClient/\"];},$element,function(args){return \"")); Assert.IsTrue(result.EndsWith("\";})")); } @@ -394,10 +410,287 @@ public void JsTranslator_EnumerableSelect(string binding) } [TestMethod] - public void JsTranslator_ValidMethod_UnsupportedTranslation() + [DataRow("Enumerable.Concat(LongArray, LongArray)", DisplayName = "Regular call of Enumerable.Concat")] + [DataRow("LongArray.Concat(LongArray)", DisplayName = "Syntax sugar - extension method")] + public void JsTranslator_EnumerableConcat(string binding) + { + var result = CompileBinding(binding, new[] { new NamespaceImport("System.Linq") }, new[] { typeof(TestViewModel) }); + Assert.AreEqual("LongArray().concat(LongArray())", result); + } + + [TestMethod] + [DataRow("Enumerable.Take(LongArray, 2)", DisplayName = "Regular call of Enumerable.Take")] + [DataRow("LongArray.Take(2)", DisplayName = "Syntax sugar - extension method")] + public void JsTranslator_EnumerableTake(string binding) + { + var result = CompileBinding(binding, new[] { new NamespaceImport("System.Linq") }, new[] { typeof(TestViewModel) }); + Assert.AreEqual("LongArray().slice(0,2)", result); + } + + [TestMethod] + [DataRow("Enumerable.Skip(LongArray, 2)", DisplayName = "Regular call of Enumerable.Skip")] + [DataRow("LongArray.Skip(2)", DisplayName = "Syntax sugar - extension method")] + public void JsTranslator_EnumerableSkip(string binding) + { + var result = CompileBinding(binding, new[] { new NamespaceImport("System.Linq") }, new[] { typeof(TestViewModel) }); + Assert.AreEqual("LongArray().slice(2)", result); + } + + [TestMethod] + [DataRow("Enumerable.All(LongArray, (long item) => item > 0)", DisplayName = "Regular call of Enumerable.All")] + [DataRow("LongArray.All((long item) => item > 0)", DisplayName = "Syntax sugar - extension method")] + public void JsTranslator_EnumerableAll(string binding) + { + var result = CompileBinding(binding, new[] { new NamespaceImport("System.Linq") }, new[] { typeof(TestViewModel) }); + Assert.AreEqual("dotvvm.arrayHelper.all(LongArray(),function(item){return ko.unwrap(item)>0;})", result); + } + + [TestMethod] + [DataRow("Enumerable.Any(LongArray, (long item) => item > 0)", DisplayName = "Regular call of Enumerable.Any")] + [DataRow("LongArray.Any((long item) => item > 0)", DisplayName = "Syntax sugar - extension method")] + public void JsTranslator_EnumerableAny(string binding) + { + var result = CompileBinding(binding, new[] { new NamespaceImport("System.Linq") }, new[] { typeof(TestViewModel) }); + Assert.AreEqual("dotvvm.arrayHelper.any(LongArray(),function(item){return ko.unwrap(item)>0;})", result); + } + + [TestMethod] + [DataRow("Enumerable.FirstOrDefault(LongArray)", DisplayName = "Regular call of Enumerable.FirstOrDefault")] + [DataRow("LongArray.FirstOrDefault()", DisplayName = "Syntax sugar - extension method")] + public void JsTranslator_EnumerableFirstOrDefault(string binding) + { + var result = CompileBinding(binding, new[] { new NamespaceImport("System.Linq") }, new[] { typeof(TestViewModel) }); + Assert.AreEqual("dotvvm.arrayHelper.firstOrDefault(LongArray(),function(arg){return true;})", result); + } + + [TestMethod] + [DataRow("Enumerable.FirstOrDefault(LongArray, (long item) => item > 0)", DisplayName = "Regular call of Enumerable.FirstOrDefault")] + [DataRow("LongArray.FirstOrDefault((long item) => item > 0)", DisplayName = "Syntax sugar - extension method")] + public void JsTranslator_EnumerableFirstOrDefaultParametrized(string binding) + { + var result = CompileBinding(binding, new[] { new NamespaceImport("System.Linq") }, new[] { typeof(TestViewModel) }); + Assert.AreEqual("dotvvm.arrayHelper.firstOrDefault(LongArray(),function(item){return ko.unwrap(item)>0;})", result); + } + + [TestMethod] + [DataRow("Enumerable.LastOrDefault(LongArray)", DisplayName = "Regular call of Enumerable.LastOrDefault")] + [DataRow("LongArray.LastOrDefault()", DisplayName = "Syntax sugar - extension method")] + public void JsTranslator_EnumerableLastOrDefault(string binding) + { + var result = CompileBinding(binding, new[] { new NamespaceImport("System.Linq") }, new[] { typeof(TestViewModel) }); + Assert.AreEqual("dotvvm.arrayHelper.lastOrDefault(LongArray(),function(arg){return true;})", result); + } + + [TestMethod] + [DataRow("Enumerable.LastOrDefault(LongArray, (long item) => item > 0)", DisplayName = "Regular call of Enumerable.LastOrDefault")] + [DataRow("LongArray.LastOrDefault((long item) => item > 0)", DisplayName = "Syntax sugar - extension method")] + public void JsTranslator_EnumerableLastOrDefaultParametrized(string binding) + { + var result = CompileBinding(binding, new[] { new NamespaceImport("System.Linq") }, new[] { typeof(TestViewModel) }); + Assert.AreEqual("dotvvm.arrayHelper.lastOrDefault(LongArray(),function(item){return ko.unwrap(item)>0;})", result); + } + + [TestMethod] + [DataRow("Enumerable.Distinct(VmArray)", DisplayName = "Regular call of Enumerable.Distinct")] + [DataRow("VmArray.Distinct()", DisplayName = "Syntax sugar - extension method")] + [ExpectedException(typeof(DotvvmCompilationException))] + public void JsTranslator_EnumerableDistinct_NonPrimitiveTypesThrows(string binding) + { + CompileBinding(binding, new[] { new NamespaceImport("System.Linq") }, new[] { typeof(TestViewModel) }); + } + + [TestMethod] + [DataRow("Enumerable.Max(Int32Array)", "Int32Array", DisplayName = "Regular call of Enumerable.Max")] + [DataRow("Enumerable.Max(Int64Array)", "Int64Array", DisplayName = "Regular call of Enumerable.Max")] + [DataRow("Enumerable.Max(SingleArray)", "SingleArray", DisplayName = "Regular call of Enumerable.Max")] + [DataRow("Enumerable.Max(DoubleArray)", "DoubleArray", DisplayName = "Regular call of Enumerable.Max")] + [DataRow("Enumerable.Max(DecimalArray)", "DecimalArray", DisplayName = "Regular call of Enumerable.Max")] + [DataRow("Int32Array.Max()", "Int32Array", DisplayName = "Syntax sugar - extension method")] + [DataRow("Int64Array.Max()", "Int64Array", DisplayName = "Syntax sugar - extension method")] + [DataRow("SingleArray.Max()", "SingleArray", DisplayName = "Syntax sugar - extension method")] + [DataRow("DoubleArray.Max()", "DoubleArray", DisplayName = "Syntax sugar - extension method")] + [DataRow("DecimalArray.Max()", "DecimalArray", DisplayName = "Syntax sugar - extension method")] + public void JsTranslator_EnumerableMax(string binding, string property) + { + var result = CompileBinding(binding, new[] { new NamespaceImport("System.Linq") }, new[] { typeof(TestArraysViewModel) }); + Assert.AreEqual($"dotvvm.arrayHelper.max({property}(),function(arg){{return ko.unwrap(arg);}})", result); + } + + [TestMethod] + [DataRow("Enumerable.Max(Int32Array, (int item) => -item)", "Int32Array", DisplayName = "Regular call of Enumerable.Max")] + [DataRow("Enumerable.Max(Int64Array, (long item) => -item)", "Int64Array", DisplayName = "Regular call of Enumerable.Max")] + [DataRow("Enumerable.Max(SingleArray, (float item) => -item)", "SingleArray", DisplayName = "Regular call of Enumerable.Max")] + [DataRow("Enumerable.Max(DoubleArray, (double item) => -item)", "DoubleArray", DisplayName = "Regular call of Enumerable.Max")] + [DataRow("Enumerable.Max(DecimalArray, (decimal item) => -item)", "DecimalArray", DisplayName = "Regular call of Enumerable.Max")] + [DataRow("Int32Array.Max((int item) => -item)", "Int32Array", DisplayName = "Syntax sugar - extension method")] + [DataRow("Int64Array.Max((long item) => -item)", "Int64Array", DisplayName = "Syntax sugar - extension method")] + [DataRow("SingleArray.Max((float item) => -item)", "SingleArray", DisplayName = "Syntax sugar - extension method")] + [DataRow("DoubleArray.Max((double item) => -item)", "DoubleArray", DisplayName = "Syntax sugar - extension method")] + [DataRow("DecimalArray.Max((decimal item) => -item)", "DecimalArray", DisplayName = "Syntax sugar - extension method")] + public void JsTranslator_EnumerableMax_WithSelector(string binding, string property) { - Assert.ThrowsException(() => - CompileBinding("Enumerable.Skip(LongArray, 2)", new[] { new NamespaceImport("System.Linq") }, new[] { typeof(TestViewModel) })); + var result = CompileBinding(binding, new[] { new NamespaceImport("System.Linq") }, new[] { typeof(TestArraysViewModel) }); + Assert.AreEqual($"dotvvm.arrayHelper.max({property}(),function(item){{return -ko.unwrap(item);}})", result); + } + + [TestMethod] + [DataRow("Enumerable.Min(Int32Array)", "Int32Array", DisplayName = "Regular call of Enumerable.Min")] + [DataRow("Enumerable.Min(Int64Array)", "Int64Array", DisplayName = "Regular call of Enumerable.Min")] + [DataRow("Enumerable.Min(SingleArray)", "SingleArray", DisplayName = "Regular call of Enumerable.Min")] + [DataRow("Enumerable.Min(DoubleArray)", "DoubleArray", DisplayName = "Regular call of Enumerable.Min")] + [DataRow("Enumerable.Min(DecimalArray)", "DecimalArray", DisplayName = "Regular call of Enumerable.Min")] + [DataRow("Int32Array.Min()", "Int32Array", DisplayName = "Syntax sugar - extension method")] + [DataRow("Int64Array.Min()", "Int64Array", DisplayName = "Syntax sugar - extension method")] + [DataRow("SingleArray.Min()", "SingleArray", DisplayName = "Syntax sugar - extension method")] + [DataRow("DoubleArray.Min()", "DoubleArray", DisplayName = "Syntax sugar - extension method")] + [DataRow("DecimalArray.Min()", "DecimalArray", DisplayName = "Syntax sugar - extension method")] + public void JsTranslator_EnumerableMin(string binding, string property) + { + var result = CompileBinding(binding, new[] { new NamespaceImport("System.Linq") }, new[] { typeof(TestArraysViewModel) }); + Assert.AreEqual($"dotvvm.arrayHelper.min({property}(),function(arg){{return ko.unwrap(arg);}})", result); + } + + [TestMethod] + [DataRow("Enumerable.Min(Int32Array, (int item) => -item)", "Int32Array", DisplayName = "Regular call of Enumerable.Min")] + [DataRow("Enumerable.Min(Int64Array, (long item) => -item)", "Int64Array", DisplayName = "Regular call of Enumerable.Min")] + [DataRow("Enumerable.Min(SingleArray, (float item) => -item)", "SingleArray", DisplayName = "Regular call of Enumerable.Min")] + [DataRow("Enumerable.Min(DoubleArray, (double item) => -item)", "DoubleArray", DisplayName = "Regular call of Enumerable.Min")] + [DataRow("Enumerable.Min(DecimalArray, (decimal item) => -item)", "DecimalArray", DisplayName = "Regular call of Enumerable.Min")] + [DataRow("Int32Array.Min((int item) => -item)", "Int32Array", DisplayName = "Syntax sugar - extension method")] + [DataRow("Int64Array.Min((long item) => -item)", "Int64Array", DisplayName = "Syntax sugar - extension method")] + [DataRow("SingleArray.Min((float item) => -item)", "SingleArray", DisplayName = "Syntax sugar - extension method")] + [DataRow("DoubleArray.Min((double item) => -item)", "DoubleArray", DisplayName = "Syntax sugar - extension method")] + [DataRow("DecimalArray.Min((decimal item) => -item)", "DecimalArray", DisplayName = "Syntax sugar - extension method")] + public void JsTranslator_EnumerableMin_WithSelector(string binding, string property) + { + var result = CompileBinding(binding, new[] { new NamespaceImport("System.Linq") }, new[] { typeof(TestArraysViewModel) }); + Assert.AreEqual($"dotvvm.arrayHelper.min({property}(),function(item){{return -ko.unwrap(item);}})", result); + } + + [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) + { + 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); + } + + [TestMethod] + [DataRow("Enumerable.OrderBy(ObjectArray, (TestComparisonType item) => item.Obj)")] + [DataRow("ObjectArray.OrderBy((TestComparisonType item) => item.Obj)")] + [ExpectedException(typeof(DotvvmCompilationException))] + public void JsTranslator_EnumerableOrderBy_NonPrimitiveTypesThrows(string binding) + { + CompileBinding(binding, new[] { new NamespaceImport("System.Linq") }, new[] { typeof(TestArraysViewModel) }); + } + + [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) + { + 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); + } + + [TestMethod] + [DataRow("Enumerable.OrderByDescending(ObjectArray, (TestComparisonType item) => item.Obj)")] + [DataRow("ObjectArray.OrderByDescending((TestComparisonType item) => item.Obj)")] + [ExpectedException(typeof(DotvvmCompilationException))] + public void JsTranslator_EnumerableOrderByDescending_NonPrimitiveTypesThrows(string binding) + { + CompileBinding(binding, new[] { new NamespaceImport("System.Linq") }, new[] { typeof(TestArraysViewModel) }); + } + + [TestMethod] + [DataRow("Math.Abs(IntProp)", "Math.abs(IntProp())")] + [DataRow("Math.Abs(DoubleProp)", "Math.abs(DoubleProp())")] + [DataRow("Math.Acos(DoubleProp)", "Math.acos(DoubleProp())")] + [DataRow("Math.Asin(DoubleProp)", "Math.asin(DoubleProp())")] + [DataRow("Math.Atan(DoubleProp)", "Math.atan(DoubleProp())")] + [DataRow("Math.Atan2(DoubleProp, 15)", "Math.atan2(DoubleProp(),15)")] + [DataRow("Math.Ceiling(DoubleProp)", "Math.ceil(DoubleProp())")] + [DataRow("Math.Cos(DoubleProp)", "Math.cos(DoubleProp())")] + [DataRow("Math.Cosh(DoubleProp)", "Math.cosh(DoubleProp())")] + [DataRow("Math.Exp(DoubleProp)", "Math.exp(DoubleProp())")] + [DataRow("Math.Floor(DoubleProp)", "Math.floor(DoubleProp())")] + [DataRow("Math.Log(DoubleProp)", "Math.log(DoubleProp())")] + [DataRow("Math.Log10(DoubleProp)", "Math.log10(DoubleProp())")] + [DataRow("Math.Max(IntProp, DoubleProp)", "Math.max(IntProp(),DoubleProp())")] + [DataRow("Math.Min(IntProp, DoubleProp)", "Math.min(IntProp(),DoubleProp())")] + [DataRow("Math.Pow(IntProp, 3)", "Math.pow(IntProp(),3)")] + [DataRow("Math.Round(DoubleProp)", "Math.round(DoubleProp())")] + [DataRow("Math.Round(DoubleProp, 2)", "DoubleProp().toFixed(2)")] + [DataRow("Math.Sign(IntProp)", "Math.sign(IntProp())")] + [DataRow("Math.Sign(DoubleProp)", "Math.sign(DoubleProp())")] + [DataRow("Math.Sqrt(DoubleProp)", "Math.sqrt(DoubleProp())")] + [DataRow("Math.Tan(DoubleProp)", "Math.tan(DoubleProp())")] + [DataRow("Math.Tanh(DoubleProp)", "Math.tanh(DoubleProp())")] + [DataRow("Math.Truncate(DoubleProp)", "Math.trunc(DoubleProp())")] + public void JsTranslator_MathMethods(string binding, string expected) + { + var result = CompileBinding(binding, new[] { typeof(TestViewModel) }); + Assert.AreEqual(expected, result); + } + + [TestMethod] + [DataRow("StringProp.Split('c')", "c", "None")] + [DataRow("StringProp.Split(\"str\")", "str", "None")] + [DataRow("StringProp.Split('c', StringSplitOptions.None)", "c", "None")] + [DataRow("StringProp.Split('c', StringSplitOptions.RemoveEmptyEntries)", "c", "RemoveEmptyEntries")] + public void JsTranslator_StringSplit_WithOptions(string binding, string delimiter, string options) + { + var result = CompileBinding(binding, new[] { typeof(TestViewModel) }); + Assert.AreEqual($"dotvvm.stringHelper.split(StringProp(),\"{delimiter}\",\"{options}\")", result); + } + + [TestMethod] + [DataRow("StringProp.Split('c', 'b')", "[\"c\",\"b\"]")] + [DataRow("StringProp.Split('c', 'b', 'a')", "[\"c\",\"b\",\"a\"]")] + public void JsTranslator_StringSplit_ArrayDelimiters_NoOptions(string binding, string delimiters) + { + var result = CompileBinding(binding, new[] { typeof(TestViewModel) }); + Assert.AreEqual($"StringProp().split({delimiters})", result); + } + + [TestMethod] + [DataRow("string.Join('c', StringArray)", "c")] + [DataRow("string.Join(\"str\", StringArray)", "str")] + public void JsTranslator_StringArrayJoin(string binding, string delimiter) + { + var result = CompileBinding(binding, new[] { typeof(TestViewModel) }); + Assert.AreEqual($"StringArray().join(\"{delimiter}\")", result); + } + + [TestMethod] + [DataRow("string.Join('c', StringArray.Where((string item) => item.Length > 2))", "c")] + [DataRow("string.Join(\"str\", StringArray.Where((string item) => item.Length > 2))", "str")] + public void JsTranslator_StringEnumerableJoin(string binding, string delimiter) + { + var result = CompileBinding(binding, new[] { new NamespaceImport("System.Linq") }, new[] { typeof(TestViewModel) }); + Assert.AreEqual($"StringArray().filter(function(item){{return ko.unwrap(item).length>2;}}).join(\"{delimiter}\")", result); + } + + [TestMethod] + [DataRow("StringProp.Replace('c', 'a')", "c", "a")] + [DataRow("StringProp.Replace(\"str\", \"rts\")", "str", "rts")] + public void JsTranslator_StringReplace(string binding, string original, string replacement) + { + var result = CompileBinding(binding, new[] { typeof(TestViewModel) }); + Assert.AreEqual($"StringProp().split(\"{original}\").join(\"{replacement}\")", result); } [TestMethod] @@ -490,6 +783,23 @@ public void JavascriptCompilation_ApiRefreshOn() var result = CompileBinding("_api.RefreshOnChange('here would be the API invocation', StringProp + StringProp2)", typeof(TestViewModel)); Assert.AreEqual("dotvvm.api.refreshOn(\"here would be the API invocation\",ko.pureComputed(function(){return StringProp()+StringProp2();}))", result); } + + [DataTestMethod] + [DataRow("StringProp.ToUpper()", "StringProp().toUpperCase()")] + [DataRow("StringProp.ToLower()", "StringProp().toLowerCase()")] + [DataRow("StringProp.IndexOf('test')", "StringProp().indexOf(\"test\")")] + [DataRow("StringProp.IndexOf('test',1)", "StringProp().indexOf(\"test\",1)")] + [DataRow("StringProp.LastIndexOf('test')", "StringProp().lastIndexOf(\"test\")")] + [DataRow("StringProp.LastIndexOf('test',2)", "StringProp().lastIndexOf(\"test\",2)")] + [DataRow("StringProp.Contains('test')", "StringProp().includes(\"test\")")] + [DataRow("StringProp.StartsWith('test')", "StringProp().startsWith(\"test\")")] + [DataRow("StringProp.EndsWith('test')", "StringProp().endsWith(\"test\")")] + [DataRow("string.IsNullOrEmpty(StringProp)", "StringProp()==null||StringProp()===\"\"")] + public void JavascriptCompilation_StringFunctions(string input, string expected) + { + var result = CompileBinding(input, typeof(TestViewModel)); + Assert.AreEqual(expected, result); + } } public class TestApiClient @@ -498,4 +808,29 @@ public class TestApiClient public string PostDateToString(DateTime date) => date.ToShortDateString(); public DateTime GetCurrentTime(string name) => DateTime.UtcNow; } + + public class TestArraysViewModel + { + public int[] Int32Array { get; set; } = new[] { 1, 2, 3 }; + public long[] Int64Array { get; set; } = new[] { 1L, 2L, 3L }; + public decimal[] DecimalArray { get; set; } = new[] { 1m, 2m, 3m }; + public float[] SingleArray { get; set; } = new[] { 1f, 2f, 3f }; + public double[] DoubleArray { get; set; } = new[] { 1d, 2d, 3d }; + public TestComparisonType[] ObjectArray { get; set; } = new[] { new TestComparisonType() }; + } + + public class TestComparisonType + { + public enum TestEnum + { + Value1, + Value2 + } + + public int Int { get; set; } + public object Obj { get; set; } + public bool Bool { get; set; } + public TestEnum Enum { get; set; } + public string String { get; set; } + } } diff --git a/src/DotVVM.Framework.Tests.Common/Parser/Binding/BindingParserTests.cs b/src/DotVVM.Framework.Tests.Common/Parser/Binding/BindingParserTests.cs index 4864c81057..e3dbe93f1d 100644 --- a/src/DotVVM.Framework.Tests.Common/Parser/Binding/BindingParserTests.cs +++ b/src/DotVVM.Framework.Tests.Common/Parser/Binding/BindingParserTests.cs @@ -177,6 +177,29 @@ public void BindingParser_StringLiteral_Valid() Assert.AreEqual("help\"help", ((LiteralExpressionBindingParserNode)result).Value); } + [TestMethod] + public void BindingParser_InterpolatedString_Valid() + { + var result = bindingParserNodeFactory.Parse("$\"Hello {Argument1} with {Argument2}!\"") as InterpolatedStringBindingParserNode; + Assert.AreEqual("Hello {0} with {1}!", result.Format); + Assert.IsFalse(result.HasNodeErrors); + Assert.AreEqual(2, result.Arguments.Count); + Assert.AreEqual("Argument1", ((SimpleNameBindingParserNode)result.Arguments[0]).Name); + Assert.AreEqual("Argument2", ((SimpleNameBindingParserNode)result.Arguments[1]).Name); + } + + [TestMethod] + [DataRow("$'{DateProperty:dd/MM/yyyy}'", "{0:dd/MM/yyyy}")] + [DataRow("$'{IntProperty:####}'", "{0:####}")] + public void BindingParser_InterpolatedString_WithFormattingComponenet_Valid(string expression, string formatOptions) + { + var result = bindingParserNodeFactory.Parse(expression) as InterpolatedStringBindingParserNode; + Assert.IsFalse(result.HasNodeErrors); + Assert.AreEqual(1, result.Arguments.Count); + Assert.AreEqual(typeof(FormattedBindingParserNode), result.Arguments.First().GetType()); + Assert.AreEqual(formatOptions, ((FormattedBindingParserNode)result.Arguments.First()).Format); + } + [TestMethod] public void BindingParser_StringLiteral_SingleQuotes_Valid() { diff --git a/src/DotVVM.Framework.Tests.Common/Parser/Binding/BindingTokenizerTests.cs b/src/DotVVM.Framework.Tests.Common/Parser/Binding/BindingTokenizerTests.cs index c146a7dabf..4f2f564a69 100644 --- a/src/DotVVM.Framework.Tests.Common/Parser/Binding/BindingTokenizerTests.cs +++ b/src/DotVVM.Framework.Tests.Common/Parser/Binding/BindingTokenizerTests.cs @@ -239,6 +239,26 @@ public void BindingTokenizer_MultiblockExpression_BunchingOperators_Valid() Assert.AreEqual(index, tokens.Count); } + [TestMethod] + [DataRow("$\"String {Arg}\"")] + [DataRow("$'String {Arg}'")] + public void BindingTokenizer_InterpolatedString_Valid(string expression) + { + var tokens = Tokenize(expression); + Assert.AreEqual(1, tokens.Count); + Assert.AreEqual(BindingTokenType.InterpolatedStringToken, tokens[0].Type); + } + + [TestMethod] + [DataRow("$'String {'InnerString'}'")] + [DataRow("$'String {$'Inner {'InnerInnerString'}'}'")] + public void BindingTokenizer_InterpolatedString_InnerString(string expression) + { + var tokens = Tokenize(expression); + Assert.AreEqual(1, tokens.Count); + Assert.AreEqual(BindingTokenType.InterpolatedStringToken, tokens[0].Type); + } + [TestMethod] public void BindingTokenizer_UnaryOperator_BunchingOperators_Valid() { diff --git a/src/DotVVM.Framework/Binding/HelperNamespace/NetFrameworkExtensions.cs b/src/DotVVM.Framework/Binding/HelperNamespace/NetFrameworkExtensions.cs new file mode 100644 index 0000000000..60c67c8b3f --- /dev/null +++ b/src/DotVVM.Framework/Binding/HelperNamespace/NetFrameworkExtensions.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DotVVM.Framework.Binding.HelperNamespace +{ + internal static class NetFrameworkExtensions + { + /// + /// This is an extension method that allows using unavailable string.Split(..) overload in .NET Framework + /// + public static string[] Split(this string text, string delimiter, StringSplitOptions options = StringSplitOptions.None) + { + return text.Split(new[] { delimiter }, options); + } + + /// + /// This is an extension method that allows using unavailable string.Split(..) overload in .NET Framework + /// + public static string[] Split(this string text, char delimiter, StringSplitOptions options = StringSplitOptions.None) + { + return text.Split(new[] { delimiter }, options); + } + } +} diff --git a/src/DotVVM.Framework/Compilation/Binding/ExpressionBuildingVisitor.cs b/src/DotVVM.Framework/Compilation/Binding/ExpressionBuildingVisitor.cs index 4b18d7fb3b..6aec851a6b 100644 --- a/src/DotVVM.Framework/Compilation/Binding/ExpressionBuildingVisitor.cs +++ b/src/DotVVM.Framework/Compilation/Binding/ExpressionBuildingVisitor.cs @@ -105,6 +105,26 @@ protected override Expression VisitLiteralExpression(LiteralExpressionBindingPar return Expression.Constant(node.Value); } + protected override Expression VisitInterpolatedStringExpression(InterpolatedStringBindingParserNode node) + { + var target = new MethodGroupExpression() { + MethodName = nameof(String.Format), + Target = new StaticClassIdentifierExpression(typeof(string)) + }; + + if (node.Arguments.Any()) + { + // Translate to a String.Format(...) call + var arguments = node.Arguments.Select((arg, index) => HandleErrors(node.Arguments[index], Visit)).ToArray(); + return memberExpressionFactory.Call(target, new[] { Expression.Constant(node.Format) }.Concat(arguments).ToArray()); + } + else + { + // There are no interpolation expressions - we can just return string + return Expression.Constant(node.Format); + } + } + protected override Expression VisitParenthesizedExpression(ParenthesizedExpressionBindingParserNode node) { // just visit content @@ -364,6 +384,17 @@ protected override Expression VisitBlock(BlockBindingParserNode node) else return Expression.Block(variables, left, right); } + protected override Expression VisitFormattedExpression(FormattedBindingParserNode node) + { + var target = new MethodGroupExpression() { + MethodName = nameof(String.Format), + Target = new StaticClassIdentifierExpression(typeof(string)) + }; + + var nodeObj = HandleErrors(node.Node, Visit); + return memberExpressionFactory.Call(target, new[] { Expression.Constant(node.Format), nodeObj }); + } + protected override Expression VisitVoid(VoidBindingParserNode node) => Expression.Default(typeof(void)); private Expression? GetMemberOrTypeExpression(IdentifierNameBindingParserNode node, Type[]? typeParameters) diff --git a/src/DotVVM.Framework/Compilation/Binding/MemberExpressionFactory.cs b/src/DotVVM.Framework/Compilation/Binding/MemberExpressionFactory.cs index 84ac669065..9550355430 100644 --- a/src/DotVVM.Framework/Compilation/Binding/MemberExpressionFactory.cs +++ b/src/DotVVM.Framework/Compilation/Binding/MemberExpressionFactory.cs @@ -418,17 +418,53 @@ private Type GetGenericParameterType(Type genericArg, Type[] searchedGenericType } else if (sgt.IsGenericType) { - Type[] genericArguments; + Type[] genericArguments = null; var expression = expressionTypes[i]; - // Arrays need to be handled in a special way to obtain instantiation if (expression.IsArray) + { + // Arrays need to be handled in a special way to obtain instantiation genericArguments = new[] { expression.GetElementType() }; + } else - genericArguments = expression.GetGenericArguments(); + { + if (expression.IsGenericType && sgt.GetGenericTypeDefinition() == expression.GetGenericTypeDefinition()) + { + // We have exactly the same type => return generic arguments + genericArguments = expression.GetGenericArguments(); + } + else if (sgt.IsInterface) + { + // We must find the instantiation within an implemented generic interface + var implementation = expression.GetInterfaces().Where(ifc => ifc.IsGenericType && ifc.GetGenericTypeDefinition() == sgt.GetGenericTypeDefinition()).Take(2).ToList(); + if (implementation.Count == 1) + { + genericArguments = implementation.Single().GetGenericArguments(); + } + } + else + { + // Otherwise we must find the instantiation within a generic base type + genericArguments = null; + var current = expression.BaseType; + while (current != null) + { + if (current.IsGenericType && current.GetGenericTypeDefinition() == sgt.GetGenericTypeDefinition()) + { + genericArguments = current.GetGenericArguments(); + break; + } + + current = current.BaseType; + } + } + } - var value = GetGenericParameterType(genericArg, sgt.GetGenericArguments(), genericArguments); - if (value is Type) return value; + if (genericArguments != null) + { + var value = GetGenericParameterType(genericArg, sgt.GetGenericArguments(), genericArguments); + if (value is Type) return value; + } } } return null; diff --git a/src/DotVVM.Framework/Compilation/ExtensionMethodsCache.cs b/src/DotVVM.Framework/Compilation/ExtensionMethodsCache.cs index e5a62ff45a..4bc61ed550 100644 --- a/src/DotVVM.Framework/Compilation/ExtensionMethodsCache.cs +++ b/src/DotVVM.Framework/Compilation/ExtensionMethodsCache.cs @@ -7,6 +7,7 @@ using System.Threading.Tasks; using System.Reflection; using System.Runtime.CompilerServices; +using DotVVM.Framework.Utils; namespace DotVVM.Framework.Compilation { @@ -31,7 +32,7 @@ private ImmutableArray CreateExtensionsForNamespace(string @namespac var extensions = new List(); foreach (var assembly in assemblyCache.GetAllAssemblies()) - foreach (var type in assembly.GetTypes().Where(t => t.Namespace == @namespace && t.IsClass && t.IsAbstract && t.IsSealed)) + foreach (var type in assembly.GetLoadableTypes().Where(t => t.Namespace == @namespace && t.IsClass && t.IsAbstract && t.IsSealed)) foreach (var method in type.GetMethods(BindingFlags.Public | BindingFlags.Static).Where(m => m.GetCustomAttribute(typeof(ExtensionAttribute)) != null)) extensions.Add(method); diff --git a/src/DotVVM.Framework/Compilation/Javascript/JavascriptTranslatableMethodCollection.cs b/src/DotVVM.Framework/Compilation/Javascript/JavascriptTranslatableMethodCollection.cs old mode 100755 new mode 100644 index 6e7068c9b1..b87fce7ea6 --- a/src/DotVVM.Framework/Compilation/Javascript/JavascriptTranslatableMethodCollection.cs +++ b/src/DotVVM.Framework/Compilation/Javascript/JavascriptTranslatableMethodCollection.cs @@ -114,11 +114,6 @@ public void AddDefaultMethodTranslators() AddPropertyGetterTranslator(typeof(ICollection), nameof(ICollection.Count), lengthMethod); AddPropertyGetterTranslator(typeof(ICollection<>), nameof(ICollection.Count), lengthMethod); AddPropertyGetterTranslator(typeof(string), nameof(string.Length), lengthMethod); - AddMethodTranslator(typeof(Enumerable), "Count", parameterCount: 1, translator: new GenericMethodCompiler(a => a[1].Member("length"))); - AddMethodTranslator(typeof(object), "ToString", new GenericMethodCompiler( - a => new JsIdentifierExpression("String").Invoke(a[0]), (m, c, a) => ToStringCheck(c)), 0); - AddMethodTranslator(typeof(Convert), "ToString", new GenericMethodCompiler( - a => new JsIdentifierExpression("String").Invoke(a[1]), (m, c, a) => ToStringCheck(a[0])), 1, true); AddMethodTranslator(typeof(Enums), "GetNames", new EnumGetNamesMethodTranslator(), 0); JsExpression indexer(JsExpression[] args, MethodInfo method) => @@ -126,8 +121,6 @@ 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)); - AddMethodTranslator(typeof(Enumerable).GetMethod("ElementAt", BindingFlags.Static | BindingFlags.Public), new GenericMethodCompiler((args, method) => - BuildIndexer(args[1], args[2], method))); AddPropertyGetterTranslator(typeof(Nullable<>), "Value", new GenericMethodCompiler((args, method) => args[0])); AddPropertyGetterTranslator(typeof(Nullable<>), "HasValue", new GenericMethodCompiler(args => new JsBinaryExpression(args[0], BinaryOperatorType.NotEqual, new JsLiteral(null)))); @@ -138,17 +131,28 @@ JsExpression indexer(JsExpression[] args, MethodInfo method) => BindingPageInfo.RegisterJavascriptTranslations(this); BindingCollectionInfo.RegisterJavascriptTranslations(this); - // string formatting - var stringFormatTranslator = new GenericMethodCompiler( - args => new JsIdentifierExpression("dotvvm").Member("globalize").Member("format").Invoke(args[1], new JsArrayExpression(args.Skip(2))) - ); - // TODO: string.Format could be two-way - AddMethodTranslator(typeof(string).GetMethod("Format", new[] { typeof(string), typeof(object) }), stringFormatTranslator); - AddMethodTranslator(typeof(string).GetMethod("Format", new[] { typeof(string), typeof(object), typeof(object) }), stringFormatTranslator); - AddMethodTranslator(typeof(string).GetMethod("Format", new[] { typeof(string), typeof(object), typeof(object), typeof(object) }), stringFormatTranslator); - AddMethodTranslator(typeof(string).GetMethod("Format", new[] { typeof(string), typeof(object[]) }), new GenericMethodCompiler( - args => new JsIdentifierExpression("dotvvm").Member("globalize").Member("format").Invoke(args[1], args[2]) + AddPropertyGetterTranslator(typeof(Task<>), "Result", new GenericMethodCompiler(args => FunctionalExtensions.ApplyAction(args[0], a => a.RemoveAnnotations(typeof(ViewModelInfoAnnotation))))); + + AddMethodTranslator(typeof(DotvvmBindableObject).GetMethods(BindingFlags.Instance | BindingFlags.Public).Single(m => m.Name == "GetValue" && !m.ContainsGenericParameters), new GenericMethodCompiler( + args => { + var dotvvmproperty = ((DotvvmProperty)((JsLiteral)args[1]).Value); + return JavascriptTranslationVisitor.TranslateViewModelProperty(args[0], (MemberInfo)dotvvmproperty.PropertyInfo ?? dotvvmproperty.PropertyType.GetTypeInfo(), name: dotvvmproperty.Name); + } )); + + AddDefaultToStringTranslations(); + AddDefaultStringTranslations(); + AddDefaultEnumerableTranslations(); + AddDefaultMathTranslations(); + } + + private void AddDefaultToStringTranslations() + { + AddMethodTranslator(typeof(object), "ToString", new GenericMethodCompiler( + a => new JsIdentifierExpression("String").Invoke(a[0]), (m, c, a) => ToStringCheck(c)), 0); + AddMethodTranslator(typeof(Convert), "ToString", new GenericMethodCompiler( + a => new JsIdentifierExpression("String").Invoke(a[1]), (m, c, a) => ToStringCheck(a[0])), 1, true); + AddMethodTranslator(typeof(DateTime).GetMethod("ToString", Type.EmptyTypes), new GenericMethodCompiler( args => new JsIdentifierExpression("dotvvm").Member("globalize").Member("bindingDateToString") .WithAnnotation(new GlobalizeResourceBindingProperty()) @@ -187,23 +191,224 @@ JsExpression indexer(JsExpression[] args, MethodInfo method) => .WithAnnotation(ResultIsObservableAnnotation.Instance) )); } + } - AddPropertyGetterTranslator(typeof(Task<>), "Result", new GenericMethodCompiler(args => FunctionalExtensions.ApplyAction(args[0], a => a.RemoveAnnotations(typeof(ViewModelInfoAnnotation))))); - - AddMethodTranslator(typeof(DotvvmBindableObject).GetMethods(BindingFlags.Instance | BindingFlags.Public).Single(m => m.Name == "GetValue" && !m.ContainsGenericParameters), new GenericMethodCompiler( - args => { - var dotvvmproperty = ((DotvvmProperty)((JsLiteral)args[1]).Value); - return JavascriptTranslationVisitor.TranslateViewModelProperty(args[0], (MemberInfo)dotvvmproperty.PropertyInfo ?? dotvvmproperty.PropertyType.GetTypeInfo(), name: dotvvmproperty.Name); - } + private void AddDefaultStringTranslations() + { + var stringFormatTranslator = new GenericMethodCompiler( + args => new JsIdentifierExpression("dotvvm").Member("globalize").Member("format").Invoke(args[1], new JsArrayExpression(args.Skip(2))) + ); + // TODO: string.Format could be two-way + AddMethodTranslator(typeof(string).GetMethod("Format", new[] { typeof(string), typeof(object) }), stringFormatTranslator); + AddMethodTranslator(typeof(string).GetMethod("Format", new[] { typeof(string), typeof(object), typeof(object) }), stringFormatTranslator); + AddMethodTranslator(typeof(string).GetMethod("Format", new[] { typeof(string), typeof(object), typeof(object), typeof(object) }), stringFormatTranslator); + AddMethodTranslator(typeof(string).GetMethod("Format", new[] { typeof(string), typeof(object[]) }), new GenericMethodCompiler( + args => new JsIdentifierExpression("dotvvm").Member("globalize").Member("format").Invoke(args[1], args[2]) )); - var whereMethod = typeof(Enumerable).GetMethods(BindingFlags.Public | BindingFlags.Static) - .Where(m => m.Name == "Where" && m.GetParameters().Length == 2 && m.GetParameters().Last().ParameterType.GetGenericTypeDefinition() == typeof(Func<,>)).Single(); - AddMethodTranslator(whereMethod, translator: new GenericMethodCompiler(args => args[1].Member("filter").Invoke(args[2]))); + AddMethodTranslator(typeof(string), nameof(string.IndexOf), parameters: new[] { typeof(string) }, translator: new GenericMethodCompiler( + a => a[0].Member("indexOf").Invoke(a[1]))); + AddMethodTranslator(typeof(string), nameof(string.IndexOf), parameters: new[] { typeof(string), typeof(int) }, translator: new GenericMethodCompiler( + a => a[0].Member("indexOf").Invoke(a[1], a[2]))); + AddMethodTranslator(typeof(string), nameof(string.LastIndexOf), parameters: new[] { typeof(string) }, translator: new GenericMethodCompiler( + a => a[0].Member("lastIndexOf").Invoke(a[1]))); + AddMethodTranslator(typeof(string), nameof(string.LastIndexOf), parameters: new[] { typeof(string), typeof(int) }, translator: new GenericMethodCompiler( + a => a[0].Member("lastIndexOf").Invoke(a[1], a[2]))); + AddMethodTranslator(typeof(string), nameof(string.ToUpper), parameterCount: 0, translator: new GenericMethodCompiler( + a => a[0].Member("toUpperCase").Invoke())); + AddMethodTranslator(typeof(string), nameof(string.ToLower), parameterCount: 0, translator: new GenericMethodCompiler( + a => a[0].Member("toLowerCase").Invoke())); + AddMethodTranslator(typeof(string), nameof(string.Contains), parameters: new[] { typeof(string) }, translator: new GenericMethodCompiler( + a => a[0].Member("includes").Invoke(a[1]))); + AddMethodTranslator(typeof(string), nameof(string.StartsWith), parameters: new[] { typeof(string) }, translator: new GenericMethodCompiler( + a => a[0].Member("startsWith").Invoke(a[1]))); + AddMethodTranslator(typeof(string), nameof(string.EndsWith), parameters: new[] { typeof(string) }, translator: new GenericMethodCompiler( + a => a[0].Member("endsWith").Invoke(a[1]))); + AddMethodTranslator(typeof(string), nameof(string.IsNullOrEmpty), parameters: new[] { typeof(string) }, translator: new GenericMethodCompiler( + a => new JsBinaryExpression( + new JsBinaryExpression(a[1], BinaryOperatorType.Equal, new JsLiteral(null)), + BinaryOperatorType.ConditionalOr, + new JsBinaryExpression(a[1].Clone(), BinaryOperatorType.StrictlyEqual, new JsLiteral(""))))); + + var joinStringArrayMethod = typeof(string).GetMethods(BindingFlags.Public | BindingFlags.Static) + .Where(m => m.Name == nameof(string.Join) && m.GetParameters().Length == 2 && m.GetParameters().Last().ParameterType == typeof(string[]) && m.GetParameters().First().ParameterType == typeof(string)).Single(); + AddMethodTranslator(joinStringArrayMethod, translator: new GenericMethodCompiler(args => args[2].Member("join").Invoke(args[1]))); + var joinStringEnumerableMethod = typeof(string).GetMethods(BindingFlags.Public | BindingFlags.Static) + .Where(m => m.Name == nameof(string.Join) && m.GetParameters().Length == 2 && m.GetParameters().Last().ParameterType == typeof(IEnumerable) && m.GetParameters().First().ParameterType == typeof(string)).Single(); + AddMethodTranslator(joinStringEnumerableMethod, translator: new GenericMethodCompiler(args => args[2].Member("join").Invoke(args[1]))); + + AddMethodTranslator(typeof(string), nameof(string.Replace), parameters: new[] { typeof(string), typeof(string) }, translator: new GenericMethodCompiler( + args => args[0].Member("split").Invoke(args[1]).Member("join").Invoke(args[2]))); + + var splitMethod = typeof(string).GetMethods(BindingFlags.Public | BindingFlags.Instance).SingleOrDefault(m => m.Name == nameof(string.Split) + && m.GetParameters().Length == 2 && m.GetParameters()[0].ParameterType == typeof(string) && m.GetParameters()[1].ParameterType == typeof(StringSplitOptions)); + var genericSplitCompiler = new GenericMethodCompiler(args => new JsIdentifierExpression("dotvvm").Member("stringHelper").Member("split").Invoke(args[0], args[1], args[2])); + var genericExtensionSplitCompiler = new GenericMethodCompiler(args => new JsIdentifierExpression("dotvvm").Member("stringHelper").Member("split").Invoke(args[1], args[2], args[3])); + if (splitMethod == null) + { + // Some overloads are not available in .NET Framework, therefore we substitute some with custom extensions + AddMethodTranslator(typeof(NetFrameworkExtensions), nameof(NetFrameworkExtensions.Split), parameters: new[] { typeof(string), typeof(char), typeof(StringSplitOptions) }, + translator: genericExtensionSplitCompiler); + AddMethodTranslator(typeof(NetFrameworkExtensions), nameof(NetFrameworkExtensions.Split), parameters: new[] { typeof(string), typeof(string), typeof(StringSplitOptions) }, + translator: genericExtensionSplitCompiler); + } + else + { + AddMethodTranslator(typeof(string), nameof(string.Split), parameters: new[] { typeof(char), typeof(StringSplitOptions) }, translator: genericSplitCompiler); + AddMethodTranslator(typeof(string), nameof(string.Split), parameters: new[] { typeof(string), typeof(StringSplitOptions) }, translator: genericSplitCompiler); + } + + AddMethodTranslator(typeof(string), nameof(NetFrameworkExtensions.Split), parameters: new[] { typeof(char[]) }, + translator: new GenericMethodCompiler(args => args[0].Member("split").Invoke(args[1]))); + } + + private void AddDefaultMathTranslations() + { + AddMethodTranslator(typeof(Math), nameof(Math.Abs), parameters: new[] { typeof(int) }, translator: new GenericMethodCompiler( + args => new JsIdentifierExpression("Math").Member("abs").Invoke(args[1]))); + AddMethodTranslator(typeof(Math), nameof(Math.Abs), parameters: new[] { typeof(double) }, translator: new GenericMethodCompiler( + args => new JsIdentifierExpression("Math").Member("abs").Invoke(args[1]))); + AddMethodTranslator(typeof(Math), nameof(Math.Acos), parameters: new[] { typeof(double) }, translator: new GenericMethodCompiler( + args => new JsIdentifierExpression("Math").Member("acos").Invoke(args[1]))); + AddMethodTranslator(typeof(Math), nameof(Math.Asin), parameters: new[] { typeof(double) }, translator: new GenericMethodCompiler( + args => new JsIdentifierExpression("Math").Member("asin").Invoke(args[1]))); + AddMethodTranslator(typeof(Math), nameof(Math.Atan), parameters: new[] { typeof(double) }, translator: new GenericMethodCompiler( + args => new JsIdentifierExpression("Math").Member("atan").Invoke(args[1]))); + AddMethodTranslator(typeof(Math), nameof(Math.Atan2), parameters: new[] { typeof(double), typeof(double) }, translator: new GenericMethodCompiler( + args => new JsIdentifierExpression("Math").Member("atan2").Invoke(args[1], args[2]))); + + AddMethodTranslator(typeof(Math), nameof(Math.Ceiling), parameters: new[] { typeof(double) }, translator: new GenericMethodCompiler( + args => new JsIdentifierExpression("Math").Member("ceil").Invoke(args[1]))); + AddMethodTranslator(typeof(Math), nameof(Math.Cos), parameters: new[] { typeof(double) }, translator: new GenericMethodCompiler( + args => new JsIdentifierExpression("Math").Member("cos").Invoke(args[1]))); + AddMethodTranslator(typeof(Math), nameof(Math.Cosh), parameters: new[] { typeof(double) }, translator: new GenericMethodCompiler( + args => new JsIdentifierExpression("Math").Member("cosh").Invoke(args[1]))); + + AddMethodTranslator(typeof(Math), nameof(Math.Exp), parameters: new[] { typeof(double) }, translator: new GenericMethodCompiler( + args => new JsIdentifierExpression("Math").Member("exp").Invoke(args[1]))); + + AddMethodTranslator(typeof(Math), nameof(Math.Floor), parameters: new[] { typeof(double) }, translator: new GenericMethodCompiler( + args => new JsIdentifierExpression("Math").Member("floor").Invoke(args[1]))); + + AddMethodTranslator(typeof(Math), nameof(Math.Log), parameters: new[] { typeof(double) }, translator: new GenericMethodCompiler( + args => new JsIdentifierExpression("Math").Member("log").Invoke(args[1]))); + AddMethodTranslator(typeof(Math), nameof(Math.Log10), parameters: new[] { typeof(double) }, translator: new GenericMethodCompiler( + args => new JsIdentifierExpression("Math").Member("log10").Invoke(args[1]))); + + AddMethodTranslator(typeof(Math), nameof(Math.Max), parameters: new[] { typeof(int), typeof(int) }, translator: new GenericMethodCompiler( + args => new JsIdentifierExpression("Math").Member("max").Invoke(args[1], args[2]))); + AddMethodTranslator(typeof(Math), nameof(Math.Max), parameters: new[] { typeof(double), typeof(double) }, translator: new GenericMethodCompiler( + args => new JsIdentifierExpression("Math").Member("max").Invoke(args[1], args[2]))); + AddMethodTranslator(typeof(Math), nameof(Math.Min), parameters: new[] { typeof(int), typeof(int) }, translator: new GenericMethodCompiler( + args => new JsIdentifierExpression("Math").Member("min").Invoke(args[1], args[2]))); + AddMethodTranslator(typeof(Math), nameof(Math.Min), parameters: new[] { typeof(double), typeof(double) }, translator: new GenericMethodCompiler( + args => new JsIdentifierExpression("Math").Member("min").Invoke(args[1], args[2]))); + + AddMethodTranslator(typeof(Math), nameof(Math.Pow), parameters: new[] { typeof(double), typeof(double) }, translator: new GenericMethodCompiler( + args => new JsIdentifierExpression("Math").Member("pow").Invoke(args[1], args[2]))); + + AddMethodTranslator(typeof(Math), nameof(Math.Round), parameters: new[] { typeof(double) }, translator: new GenericMethodCompiler( + args => new JsIdentifierExpression("Math").Member("round").Invoke(args[1]))); + AddMethodTranslator(typeof(Math), nameof(Math.Round), parameters: new[] { typeof(double), typeof(int) }, translator: new GenericMethodCompiler( + args => args[1].Member("toFixed").Invoke(args[2]))); + + AddMethodTranslator(typeof(Math), nameof(Math.Sign), parameters: new[] { typeof(int) }, translator: new GenericMethodCompiler( + args => new JsIdentifierExpression("Math").Member("sign").Invoke(args[1]))); + AddMethodTranslator(typeof(Math), nameof(Math.Sign), parameters: new[] { typeof(double) }, translator: new GenericMethodCompiler( + args => new JsIdentifierExpression("Math").Member("sign").Invoke(args[1]))); + AddMethodTranslator(typeof(Math), nameof(Math.Sin), parameters: new[] { typeof(double) }, translator: new GenericMethodCompiler( + args => new JsIdentifierExpression("Math").Member("sin").Invoke(args[1]))); + AddMethodTranslator(typeof(Math), nameof(Math.Sinh), parameters: new[] { typeof(double) }, translator: new GenericMethodCompiler( + args => new JsIdentifierExpression("Math").Member("sinh").Invoke(args[1]))); + AddMethodTranslator(typeof(Math), nameof(Math.Sqrt), parameters: new[] { typeof(double) }, translator: new GenericMethodCompiler( + args => new JsIdentifierExpression("Math").Member("sqrt").Invoke(args[1]))); + + AddMethodTranslator(typeof(Math), nameof(Math.Tan), parameters: new[] { typeof(double) }, translator: new GenericMethodCompiler( + args => new JsIdentifierExpression("Math").Member("tan").Invoke(args[1]))); + AddMethodTranslator(typeof(Math), nameof(Math.Tanh), parameters: new[] { typeof(double) }, translator: new GenericMethodCompiler( + args => new JsIdentifierExpression("Math").Member("tanh").Invoke(args[1]))); + AddMethodTranslator(typeof(Math), nameof(Math.Truncate), parameters: new[] { typeof(double) }, translator: new GenericMethodCompiler( + args => new JsIdentifierExpression("Math").Member("trunc").Invoke(args[1]))); + } + + private void AddDefaultEnumerableTranslations() + { + var returnTrueFunc = new JsFunctionExpression(new[] { new JsIdentifier("arg") }, new JsBlockStatement(new JsReturnStatement(new JsLiteral(true)))); + var selectIdentityFunc = new JsFunctionExpression(new[] { new JsIdentifier("arg") }, + new JsBlockStatement(new JsReturnStatement(new JsIdentifierExpression("ko").Member("unwrap").Invoke(new JsIdentifierExpression("arg"))))); + + bool EnsureIsComparableInJavascript(MethodInfo method, Type type) + { + if (!ReflectionUtils.IsPrimitiveType(type)) + throw new DotvvmCompilationException($"Can not translate invocation of method \"{method.Name}\" to JavaScript. Comparison of non-primitive types is not supported."); + + return true; + } + + 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 => + new JsIdentifierExpression("dotvvm").Member("arrayHelper").Member("any").Invoke(args[1], returnTrueFunc.Clone()))); + AddMethodTranslator(typeof(Enumerable), nameof(Enumerable.Any), parameterCount: 2, translator: new GenericMethodCompiler(args => + new JsIdentifierExpression("dotvvm").Member("arrayHelper").Member("any").Invoke(args[1], args[2]))); + + AddMethodTranslator(typeof(Enumerable), nameof(Enumerable.Concat), parameterCount: 2, translator: new GenericMethodCompiler(args => + args[1].Member("concat").Invoke(args[2]))); + AddMethodTranslator(typeof(Enumerable), nameof(Enumerable.Count), parameterCount: 1, translator: new GenericMethodCompiler(a => a[1].Member("length"))); + + AddMethodTranslator(typeof(Enumerable), nameof(Enumerable.Distinct), parameterCount: 1, + translator: new GenericMethodCompiler(args => new JsIdentifierExpression("dotvvm").Member("arrayHelper").Member("distinct").Invoke(args[1]), + check: (method, target, arguments) => EnsureIsComparableInJavascript(method, target?.Type ?? ReflectionUtils.GetEnumerableType(arguments.First().Type)))); + + AddMethodTranslator(typeof(Enumerable).GetMethod("ElementAt", BindingFlags.Static | BindingFlags.Public), new GenericMethodCompiler((args, method) => + BuildIndexer(args[1], args[2], method))); + + AddMethodTranslator(typeof(Enumerable), nameof(Enumerable.FirstOrDefault), parameterCount: 1, translator: new GenericMethodCompiler(args => + new JsIdentifierExpression("dotvvm").Member("arrayHelper").Member("firstOrDefault").Invoke(args[1], returnTrueFunc.Clone()).WithAnnotation(ResultIsObservableAnnotation.Instance))); + AddMethodTranslator(typeof(Enumerable), nameof(Enumerable.FirstOrDefault), parameterCount: 2, translator: new GenericMethodCompiler(args => + new JsIdentifierExpression("dotvvm").Member("arrayHelper").Member("firstOrDefault").Invoke(args[1], args[2]).WithAnnotation(ResultIsObservableAnnotation.Instance))); + + AddMethodTranslator(typeof(Enumerable), nameof(Enumerable.LastOrDefault), parameterCount: 1, translator: new GenericMethodCompiler(args => + new JsIdentifierExpression("dotvvm").Member("arrayHelper").Member("lastOrDefault").Invoke(args[1], returnTrueFunc.Clone()).WithAnnotation(ResultIsObservableAnnotation.Instance))); + AddMethodTranslator(typeof(Enumerable), nameof(Enumerable.LastOrDefault), parameterCount: 2, translator: new GenericMethodCompiler(args => + new JsIdentifierExpression("dotvvm").Member("arrayHelper").Member("lastOrDefault").Invoke(args[1], args[2]).WithAnnotation(ResultIsObservableAnnotation.Instance))); + + foreach (var type in new[] { typeof(int), typeof(long), typeof(float), typeof(double), typeof(decimal), typeof(int?), typeof(long?), typeof(float?), typeof(double?), typeof(decimal?) }) + { + AddMethodTranslator(typeof(Enumerable), nameof(Enumerable.Max), parameters: new[] { typeof(IEnumerable<>).MakeGenericType(type) }, translator: new GenericMethodCompiler(args => + new JsIdentifierExpression("dotvvm").Member("arrayHelper").Member("max").Invoke(args[1], selectIdentityFunc.Clone()))); + var maxSelect = typeof(Enumerable).GetMethods(BindingFlags.Public | BindingFlags.Static) + .Where(m => m.Name == nameof(Enumerable.Max) && m.GetParameters().Length == 2 && m.GetParameters().Last().ParameterType.GetGenericArguments().Last() == type).Single(); + AddMethodTranslator(maxSelect, translator: new GenericMethodCompiler(args => new JsIdentifierExpression("dotvvm").Member("arrayHelper").Member("max").Invoke(args[1], args[2]))); + + AddMethodTranslator(typeof(Enumerable), nameof(Enumerable.Min), parameters: new[] { typeof(IEnumerable<>).MakeGenericType(type) }, translator: new GenericMethodCompiler(args => + new JsIdentifierExpression("dotvvm").Member("arrayHelper").Member("min").Invoke(args[1], selectIdentityFunc.Clone()))); + var minSelect = typeof(Enumerable).GetMethods(BindingFlags.Public | BindingFlags.Static) + .Where(m => m.Name == nameof(Enumerable.Min) && m.GetParameters().Length == 2 && m.GetParameters().Last().ParameterType.GetGenericArguments().Last() == type).Single(); + AddMethodTranslator(minSelect, translator: new GenericMethodCompiler(args => new JsIdentifierExpression("dotvvm").Member("arrayHelper").Member("min").Invoke(args[1], args[2]))); + } + + AddMethodTranslator(typeof(Enumerable), nameof(Enumerable.OrderBy), parameterCount: 2, + translator: new GenericMethodCompiler(args => new JsIdentifierExpression("dotvvm").Member("arrayHelper").Member("orderBy").Invoke(args[1], args[2]), + 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]), + check: (method, _, arguments) => EnsureIsComparableInJavascript(method, arguments.Last().Type.GetGenericArguments().Last()))); var selectMethod = typeof(Enumerable).GetMethods(BindingFlags.Public | BindingFlags.Static) - .Where(m => m.Name == "Select" && m.GetParameters().Length == 2 && m.GetParameters().Last().ParameterType.GetGenericTypeDefinition() == typeof(Func<,>)).Single(); + .Where(m => m.Name == nameof(Enumerable.Select) && m.GetParameters().Length == 2 && m.GetParameters().Last().ParameterType.GetGenericTypeDefinition() == typeof(Func<,>)).Single(); AddMethodTranslator(selectMethod, translator: new GenericMethodCompiler(args => args[1].Member("map").Invoke(args[2]))); + AddMethodTranslator(typeof(Enumerable), nameof(Enumerable.Skip), parameterCount: 2, translator: new GenericMethodCompiler(args => + args[1].Member("slice").Invoke(args[2]))); + + AddMethodTranslator(typeof(Enumerable), nameof(Enumerable.Take), parameterCount: 2, translator: new GenericMethodCompiler(args => + args[1].Member("slice").Invoke(new JsLiteral(0), args[2]))); + AddMethodTranslator(typeof(Enumerable), nameof(Enumerable.ToArray), parameterCount: 1, translator: new GenericMethodCompiler(args => args[1])); + AddMethodTranslator(typeof(Enumerable), nameof(Enumerable.ToList), parameterCount: 1, translator: new GenericMethodCompiler(args => args[1])); + + var whereMethod = typeof(Enumerable).GetMethods(BindingFlags.Public | BindingFlags.Static) + .Where(m => m.Name == nameof(Enumerable.Where) && m.GetParameters().Length == 2 && m.GetParameters().Last().ParameterType.GetGenericTypeDefinition() == typeof(Func<,>)).Single(); + AddMethodTranslator(whereMethod, translator: new GenericMethodCompiler(args => args[1].Member("filter").Invoke(args[2]))); } public JsExpression TryTranslateCall(LazyTranslatedExpression context, LazyTranslatedExpression[] args, MethodInfo method) diff --git a/src/DotVVM.Framework/Compilation/Parser/Binding/Parser/BindingParser.cs b/src/DotVVM.Framework/Compilation/Parser/Binding/Parser/BindingParser.cs index 52719f6862..a106022a6c 100644 --- a/src/DotVVM.Framework/Compilation/Parser/Binding/Parser/BindingParser.cs +++ b/src/DotVVM.Framework/Compilation/Parser/Binding/Parser/BindingParser.cs @@ -621,6 +621,21 @@ private BindingParserNode ReadAtomicExpression() } return node; } + else if (token != null && token.Type == BindingTokenType.InterpolatedStringToken) + { + // interpolated string + + Read(); + + var (format, arguments) = ParseInterpolatedString(token.Text, out var error); + var node = CreateNode(new InterpolatedStringBindingParserNode(format, arguments), startIndex); + if (error != null) + { + node.NodeErrors.Add(error); + } + + return node; + } else { // identifier @@ -738,6 +753,74 @@ private IdentifierNameBindingParserNode ReadGenericArguments(int startIndex, Bin return CreateNode(new SimpleNameBindingParserNode(identifier), startIndex); } + private BindingParserNode ReadFormattedExpression() + { + var startIndex = CurrentIndex; + BindingParserNode? node; + + SkipWhiteSpace(); + + // 1) Parse expression + if (Peek() is BindingToken operatorToken && operatorToken.Type == BindingTokenType.OpenParenthesis) + { + // Conditional expressions must be enclosed in parentheses + Read(); + SkipWhiteSpace(); + node = ReadConditionalExpression(); + SkipWhiteSpace(); + if (IsCurrentTokenIncorrect(BindingTokenType.CloseParenthesis)) + { + node.NodeErrors.Add("Expected ')' after this expression."); + } + else + { + Read(); + } + } + else + { + // If expression is not enclosed in parentheses, read null coalescing expression + node = ReadNullCoalescingExpression(); + } + + SkipWhiteSpace(); + + // 2) Parse formatting component (optional) + if (Peek() is BindingToken delimitingToken && delimitingToken.Type == BindingTokenType.ColonOperator) + { + Read(); + if (IsCurrentTokenIncorrect(BindingTokenType.Identifier)) + { + node.NodeErrors.Add("Expected an identifier after ':'. The identifier should specify formatting for the previous expression!"); + } + + // Scan all remaining tokens + BindingToken? currentToken; + var formatTokens = new List(); + while ((currentToken = Read()) != null) + formatTokens.Add(currentToken); + + var format = $"{{0:{string.Concat(formatTokens.Select(token => token.Text))}}}"; + return CreateNode(new FormattedBindingParserNode(node, format), startIndex); + } + + SkipWhiteSpace(); + if (Peek() != null) + { + if (Peek()!.Type == BindingTokenType.QuestionMarkOperator) + { + // If it seems that user tried to use conditional expression, provide more concrete error message + node.NodeErrors.Add("Conditional expression needs to be enclosed in parentheses."); + } + else + { + node.NodeErrors.Add($"Expected end of interpolated expression, but instead found {Peek()!.Type}"); + } + } + + return node; + } + private static object? ParseNumberLiteral(string text, out string? error) { text = text.ToLower(); @@ -868,43 +951,183 @@ private static string ParseStringLiteral(string text, out string? error) { error = null; var sb = new StringBuilder(); - for (var i = 1; i < text.Length - 1; i++) + + var index = 1; + while (index < text.Length - 1) { - if (text[i] == '\\') + if (TryParseCharacter(text, ref index, out var character, out var innerError)) { - // handle escaped characters - i++; - if (i == text.Length - 1) - { - error = "The escape character cannot be at the end of the string literal!"; - } - else if (text[i] == '\'' || text[i] == '"' || text[i] == '\\') - { - sb.Append(text[i]); - } - else if (text[i] == 'n') + sb.Append(character); + } + else + { + error = innerError; + } + } + + return sb.ToString(); + } + + private static bool TryParseCharacter(string text, ref int index, out char character, out string? error) + { + var result = TryPeekCharacter(text, index, out var count, out character, out error); + index += count; + return result; + } + + private static bool TryPeekCharacter(string text, int index, out int length, out char character, out string? error) + { + if (text[index] == '\\') + { + // handle escaped characters + length = 2; + index++; + if (index == text.Length - 1) + { + error = "The escape character cannot be at the end of the string literal!"; + character = default; + return false; + } + else if (text[index] == '\'' || text[index] == '"' || text[index] == '\\') + { + character = text[index]; + } + else if (text[index] == 'n') + { + character = '\n'; + } + else if (text[index] == 'r') + { + character = '\r'; + } + else if (text[index] == 't') + { + character = '\t'; + } + else + { + error = "The escape sequence is either not valid or not supported in dotVVM bindings!"; + character = default; + return false; + } + + error = default; + return true; + } + else + { + character = text[index]; + error = default; + length = 1; + return true; + } + } + + private static (string, List) ParseInterpolatedString(string text, out string? error) + { + error = null; + var sb = new StringBuilder(); + var arguments = new List(); + + var index = 2; + while (index < text.Length - 1) + { + if (TryParseCharacter(text, ref index, out var current, out var innerError)) + { + var hasNext = TryPeekCharacter(text, index, out var length, out var next, out _); + if (hasNext && current == next && (current == '{' || current == '}')) { - sb.Append('\n'); + // If encountered double '{' or '}' do not treat is as an control character + sb.Append(current); + index += length; } - else if (text[i] == 'r') + else if (current == '{') { - sb.Append('\r'); + if (!TryParseInterpolationExpression(text, index, out var end, out var argument, out innerError)) + { + arguments.Clear(); + error = string.Concat(error, " Interpolation expression is malformed. ", innerError).TrimStart(); + return (string.Empty, arguments); + } + arguments.Add(argument!); + sb.Append("{" + (arguments.Count - 1).ToString() + "}"); + index = end + 1; } - else if (text[i] == 't') + else if (current == '}') { - sb.Append('\t'); + innerError = "Could not find matching opening character '{' for an interpolated expression."; + error = string.Concat(error, " Interpolation expression is malformed. ", innerError).TrimStart(); + return (string.Empty, arguments); } else { - error = "The escape sequence is either not valid or not supported in dotVVM bindings!"; + sb.Append(current); } } else { - sb.Append(text[i]); + error = innerError; + index++; } } - return sb.ToString(); + + return (sb.ToString(), arguments); + } + + private static bool TryParseInterpolationExpression(string text, int start, out int end, out BindingParserNode? expression, out string? error) + { + var index = start; + var foundEnd = false; + + var exprDepth = 0; + while (index < text.Length) + { + var current = text[index++]; + if (current == '{') + { + exprDepth++; + } + if (current == '}') + { + if (exprDepth == 0) + { + foundEnd = true; + break; + } + exprDepth--; + } + } + + if (!foundEnd) + { + end = -1; + expression = null; + error = "Could not find matching closing character '}' for an interpolated expression."; + return false; + } + + end = index - 1; + if (start == end) + { + // Provided expression is empty + expression = null; + error = "Expected expression, but instead found empty \"{}\"."; + return false; + } + + error = null; + var rawExpression = text.Substring(start, end - start); + var tokenizer = new BindingTokenizer(); + tokenizer.Tokenize(rawExpression); + var parser = new BindingParser() { Tokens = tokenizer.Tokens }; + expression = parser.ReadFormattedExpression(); + if (expression.HasNodeErrors) + { + error = string.Join(" ", new[] { $"Error while parsing expression \"{rawExpression}\"." }.Concat(expression.NodeErrors)); + return false; + } + + return expression != null; } private T CreateNode(T node, int startIndex, string? error = null) where T : BindingParserNode diff --git a/src/DotVVM.Framework/Compilation/Parser/Binding/Parser/BindingParserNodeVisitor.cs b/src/DotVVM.Framework/Compilation/Parser/Binding/Parser/BindingParserNodeVisitor.cs index 864b9fe6aa..f017ce0b3b 100644 --- a/src/DotVVM.Framework/Compilation/Parser/Binding/Parser/BindingParserNodeVisitor.cs +++ b/src/DotVVM.Framework/Compilation/Parser/Binding/Parser/BindingParserNodeVisitor.cs @@ -36,6 +36,10 @@ public virtual T Visit(BindingParserNode node) { return VisitLiteralExpression((LiteralExpressionBindingParserNode)node); } + else if (node is InterpolatedStringBindingParserNode) + { + return VisitInterpolatedStringExpression((InterpolatedStringBindingParserNode)node); + } else if (node is MemberAccessBindingParserNode) { return VisitMemberAccess((MemberAccessBindingParserNode)node); @@ -64,6 +68,10 @@ public virtual T Visit(BindingParserNode node) { return VisitAssemblyQualifiedName((AssemblyQualifiedNameBindingParserNode)node); } + else if (node is FormattedBindingParserNode) + { + return VisitFormattedExpression((FormattedBindingParserNode)node); + } else if (node is BlockBindingParserNode blockNode) { return VisitBlock(blockNode); @@ -127,6 +135,11 @@ protected virtual T VisitLiteralExpression(LiteralExpressionBindingParserNode no return DefaultVisit(node); } + protected virtual T VisitInterpolatedStringExpression(InterpolatedStringBindingParserNode node) + { + return DefaultVisit(node); + } + protected virtual T VisitMemberAccess(MemberAccessBindingParserNode node) { return DefaultVisit(node); @@ -152,6 +165,11 @@ protected virtual T VisitAssemblyQualifiedName(AssemblyQualifiedNameBindingParse return DefaultVisit(node); } + protected virtual T VisitFormattedExpression(FormattedBindingParserNode node) + { + return DefaultVisit(node); + } + protected virtual T VisitBlock(BlockBindingParserNode node) { return DefaultVisit(node); diff --git a/src/DotVVM.Framework/Compilation/Parser/Binding/Parser/FormattedBindingParserNode.cs b/src/DotVVM.Framework/Compilation/Parser/Binding/Parser/FormattedBindingParserNode.cs new file mode 100644 index 0000000000..a75d9b0e86 --- /dev/null +++ b/src/DotVVM.Framework/Compilation/Parser/Binding/Parser/FormattedBindingParserNode.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DotVVM.Framework.Compilation.Parser.Binding.Parser +{ + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class FormattedBindingParserNode : BindingParserNode + { + public BindingParserNode Node { get; private set; } + public string Format { get; private set; } + + public FormattedBindingParserNode(BindingParserNode node, string format) + { + this.Node = node; + this.Format = format; + } + + public override IEnumerable EnumerateChildNodes() + => base.EnumerateNodes().Concat(new[] { Node }); + + public override string ToDisplayString() + => $"{Node.ToDisplayString()}:{Format}"; + } +} diff --git a/src/DotVVM.Framework/Compilation/Parser/Binding/Parser/InterpolatedStringBindingParserNode.cs b/src/DotVVM.Framework/Compilation/Parser/Binding/Parser/InterpolatedStringBindingParserNode.cs new file mode 100644 index 0000000000..b5e7d0b363 --- /dev/null +++ b/src/DotVVM.Framework/Compilation/Parser/Binding/Parser/InterpolatedStringBindingParserNode.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using DotVVM.Framework.Utils; + +namespace DotVVM.Framework.Compilation.Parser.Binding.Parser +{ + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class InterpolatedStringBindingParserNode : BindingParserNode + { + public string Format { get; set; } + public List Arguments { get; set; } + + public InterpolatedStringBindingParserNode(string format, List arguments) + { + this.Format = format; + this.Arguments = arguments; + } + + public override IEnumerable EnumerateChildNodes() + => base.EnumerateNodes().Concat(Arguments); + + public override string ToDisplayString() + => $"String.Format(\"{Format}\", {Arguments.Select(arg => arg.ToDisplayString()).StringJoin(", ")})"; + } +} diff --git a/src/DotVVM.Framework/Compilation/Parser/Binding/Tokenizer/BindingTokenType.cs b/src/DotVVM.Framework/Compilation/Parser/Binding/Tokenizer/BindingTokenType.cs index 99e966f28e..ed47387cf9 100644 --- a/src/DotVVM.Framework/Compilation/Parser/Binding/Tokenizer/BindingTokenType.cs +++ b/src/DotVVM.Framework/Compilation/Parser/Binding/Tokenizer/BindingTokenType.cs @@ -30,6 +30,7 @@ public enum BindingTokenType NotOperator, StringLiteralToken, + InterpolatedStringToken, NullCoalescingOperator, QuestionMarkOperator, diff --git a/src/DotVVM.Framework/Compilation/Parser/Binding/Tokenizer/BindingTokenizer.cs b/src/DotVVM.Framework/Compilation/Parser/Binding/Tokenizer/BindingTokenizer.cs index 5cfeb3ecf5..d6a7aba6b0 100644 --- a/src/DotVVM.Framework/Compilation/Parser/Binding/Tokenizer/BindingTokenizer.cs +++ b/src/DotVVM.Framework/Compilation/Parser/Binding/Tokenizer/BindingTokenizer.cs @@ -211,11 +211,25 @@ private void TokenizeBindingValue() } break; + case '$': case '\'': case '"': + var bindingTokenType = default(BindingTokenType); + var errorMessage = default(string); FinishIncompleteIdentifier(); - ReadStringLiteral(out var errorMessage); - CreateToken(BindingTokenType.StringLiteralToken, errorProvider: t => CreateTokenError(t, errorMessage ?? "unknown error")); + + if (ch == '$') + { + bindingTokenType = BindingTokenType.InterpolatedStringToken; + ReadInterpolatedString(out errorMessage); + } + else + { + bindingTokenType = BindingTokenType.StringLiteralToken; + ReadStringLiteral(out errorMessage); + } + + CreateToken(bindingTokenType, errorProvider: t => CreateTokenError(t, errorMessage ?? "unknown error")); break; case '?': @@ -291,6 +305,11 @@ internal void ReadStringLiteral(out string? errorMessage) ReadStringLiteral(Peek, Read, out errorMessage); } + internal void ReadInterpolatedString(out string? errorMessage) + { + ReadInterpolatedString(Peek, Read, out errorMessage); + } + /// /// Reads the string literal. /// @@ -324,5 +343,43 @@ internal static void ReadStringLiteral(Func peekFunction, Func readF errorMessage = null; } + + internal static void ReadInterpolatedString(Func peekFunction, Func readFunction, out string? errorMessage) + { + readFunction(); + var quoteChar = readFunction(); + var exprDepth = 0; + + while (peekFunction() != quoteChar || exprDepth != 0) + { + if (peekFunction() == NullChar) + { + errorMessage = "Interpolated string was not closed!"; + return; + } + + if (peekFunction() == '\\' && exprDepth == 0) + { + readFunction(); + } + else if (peekFunction() == '{') + { + exprDepth++; + } + else if (peekFunction() == '}') + { + if (--exprDepth <= -1) + { + errorMessage = "Could not find matching '{' character!"; + return; + } + } + + readFunction(); + } + + readFunction(); + errorMessage = null; + } } } diff --git a/src/DotVVM.Framework/Configuration/RestApiRegistrationHelpers.cs b/src/DotVVM.Framework/Configuration/RestApiRegistrationHelpers.cs index 4a82803607..8b02b0b3e0 100644 --- a/src/DotVVM.Framework/Configuration/RestApiRegistrationHelpers.cs +++ b/src/DotVVM.Framework/Configuration/RestApiRegistrationHelpers.cs @@ -76,7 +76,7 @@ private static JsExpression Serialize(JsExpression expr) => new JsIdentifierExpression("dotvvm").Member("serialization").Member("serialize").Invoke(expr.WithAnnotation(ShouldBeObservableAnnotation.Instance)); private static JsExpression SerializeDate(JsExpression expr) => - new JsIdentifierExpression("dotvvm").Member("globalize").Member("parseDotvvmDate").Invoke(expr); + new JsIdentifierExpression("dotvvm").Member("globalize").Member("parseDate").Invoke(expr); private static JsExpression[] ReplaceDefaultWithUndefined(IEnumerable arguments, ParameterInfo[] parameters) { diff --git a/src/DotVVM.Framework/Controls/FileUpload.cs b/src/DotVVM.Framework/Controls/FileUpload.cs index 982fb28bad..55e05af6b6 100644 --- a/src/DotVVM.Framework/Controls/FileUpload.cs +++ b/src/DotVVM.Framework/Controls/FileUpload.cs @@ -162,9 +162,8 @@ protected override void AddAttributesToRender(IHtmlWriter writer, IDotvvmRequest protected override void RenderContents(IHtmlWriter writer, IDotvvmRequestContext context) { - RenderIframe(writer); - RenderUploadButton(writer); + RenderInputControl(writer, context); RenderUploadedFilesTitle(writer); RenderProgressWrapper(writer); RenderResultTitle(writer); @@ -220,13 +219,23 @@ private void RenderUploadButton(IHtmlWriter writer) writer.RenderEndTag(); } - private void RenderIframe(IHtmlWriter writer) + private void RenderInputControl(IHtmlWriter writer, IDotvvmRequestContext context) { - // render iframe - writer.AddAttribute("class", "dotvvm-upload-iframe"); - writer.AddAttribute("src", GetFileUploadHandlerUrl()); - writer.RenderBeginTag("iframe"); - writer.RenderEndTag(); + writer.AddStyleAttribute("display", "none"); + writer.AddAttribute("type", "file"); + + if (AllowMultipleFiles) + { + writer.AddAttribute("multiple", "multiple"); + } + + if (!string.IsNullOrWhiteSpace(AllowedFileTypes)) + { + writer.AddAttribute("accept", AllowedFileTypes); + } + + writer.AddKnockoutDataBind("dotvvm-FileUpload", JsonConvert.SerializeObject(new { url = context.TranslateVirtualPath(GetFileUploadHandlerUrl()) })); + writer.RenderSelfClosingTag("input"); } private string GetFileUploadHandlerUrl() diff --git a/src/DotVVM.Framework/Controls/FileUploadPageTemplate.cs b/src/DotVVM.Framework/Controls/FileUploadPageTemplate.cs deleted file mode 100644 index 38f4a754d8..0000000000 --- a/src/DotVVM.Framework/Controls/FileUploadPageTemplate.cs +++ /dev/null @@ -1,413 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version: 16.0.0.0 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -// ------------------------------------------------------------------------------ -namespace DotVVM.Framework.Controls -{ - using System.Linq; - using System.Text; - using System.Collections.Generic; - using System.Net; - using System; - - /// - /// Class to produce the template output - /// - - #line 1 "D:\Work\Dotvvm\src\DotVVM.Framework\Controls\FileUploadPageTemplate.tt" - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "16.0.0.0")] - public partial class FileUploadPageTemplate : FileUploadPageTemplateBase - { -#line hidden - /// - /// Create the template output - /// - public virtual string TransformText() - { - this.Write("\r\n\r\n\r\n\r\n \r\n\t" + - "\r\n\r\n\r\n\r\n - -\r\n\r\n\r\n\r\n\r\n"); - return this.GenerationEnvironment.ToString(); - } - - #line 64 "D:\Work\Dotvvm\src\DotVVM.Framework\Controls\FileUploadPageTemplate.tt" - -public string StartupScript { get; set; } -public string FormPostUrl { get; set; } -public bool AllowMultipleFiles { get; set; } -public string AllowedFileTypes { get; set; } - - - #line default - #line hidden - } - - #line default - #line hidden - #region Base class - /// - /// Base class for this transformation - /// - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "16.0.0.0")] - public class FileUploadPageTemplateBase - { - #region Fields - private global::System.Text.StringBuilder generationEnvironmentField; - private global::System.CodeDom.Compiler.CompilerErrorCollection errorsField; - private global::System.Collections.Generic.List indentLengthsField; - private string currentIndentField = ""; - private bool endsWithNewline; - private global::System.Collections.Generic.IDictionary sessionField; - #endregion - #region Properties - /// - /// The string builder that generation-time code is using to assemble generated output - /// - protected System.Text.StringBuilder GenerationEnvironment - { - get - { - if ((this.generationEnvironmentField == null)) - { - this.generationEnvironmentField = new global::System.Text.StringBuilder(); - } - return this.generationEnvironmentField; - } - set - { - this.generationEnvironmentField = value; - } - } - /// - /// The error collection for the generation process - /// - public System.CodeDom.Compiler.CompilerErrorCollection Errors - { - get - { - if ((this.errorsField == null)) - { - this.errorsField = new global::System.CodeDom.Compiler.CompilerErrorCollection(); - } - return this.errorsField; - } - } - /// - /// A list of the lengths of each indent that was added with PushIndent - /// - private System.Collections.Generic.List indentLengths - { - get - { - if ((this.indentLengthsField == null)) - { - this.indentLengthsField = new global::System.Collections.Generic.List(); - } - return this.indentLengthsField; - } - } - /// - /// Gets the current indent we use when adding lines to the output - /// - public string CurrentIndent - { - get - { - return this.currentIndentField; - } - } - /// - /// Current transformation session - /// - public virtual global::System.Collections.Generic.IDictionary Session - { - get - { - return this.sessionField; - } - set - { - this.sessionField = value; - } - } - #endregion - #region Transform-time helpers - /// - /// Write text directly into the generated output - /// - public void Write(string textToAppend) - { - if (string.IsNullOrEmpty(textToAppend)) - { - return; - } - // If we're starting off, or if the previous text ended with a newline, - // we have to append the current indent first. - if (((this.GenerationEnvironment.Length == 0) - || this.endsWithNewline)) - { - this.GenerationEnvironment.Append(this.currentIndentField); - this.endsWithNewline = false; - } - // Check if the current text ends with a newline - if (textToAppend.EndsWith(global::System.Environment.NewLine, global::System.StringComparison.CurrentCulture)) - { - this.endsWithNewline = true; - } - // This is an optimization. If the current indent is "", then we don't have to do any - // of the more complex stuff further down. - if ((this.currentIndentField.Length == 0)) - { - this.GenerationEnvironment.Append(textToAppend); - return; - } - // Everywhere there is a newline in the text, add an indent after it - textToAppend = textToAppend.Replace(global::System.Environment.NewLine, (global::System.Environment.NewLine + this.currentIndentField)); - // If the text ends with a newline, then we should strip off the indent added at the very end - // because the appropriate indent will be added when the next time Write() is called - if (this.endsWithNewline) - { - this.GenerationEnvironment.Append(textToAppend, 0, (textToAppend.Length - this.currentIndentField.Length)); - } - else - { - this.GenerationEnvironment.Append(textToAppend); - } - } - /// - /// Write text directly into the generated output - /// - public void WriteLine(string textToAppend) - { - this.Write(textToAppend); - this.GenerationEnvironment.AppendLine(); - this.endsWithNewline = true; - } - /// - /// Write formatted text directly into the generated output - /// - public void Write(string format, params object[] args) - { - this.Write(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args)); - } - /// - /// Write formatted text directly into the generated output - /// - public void WriteLine(string format, params object[] args) - { - this.WriteLine(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args)); - } - /// - /// Raise an error - /// - public void Error(string message) - { - System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError(); - error.ErrorText = message; - this.Errors.Add(error); - } - /// - /// Raise a warning - /// - public void Warning(string message) - { - System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError(); - error.ErrorText = message; - error.IsWarning = true; - this.Errors.Add(error); - } - /// - /// Increase the indent - /// - public void PushIndent(string indent) - { - if ((indent == null)) - { - throw new global::System.ArgumentNullException("indent"); - } - this.currentIndentField = (this.currentIndentField + indent); - this.indentLengths.Add(indent.Length); - } - /// - /// Remove the last indent that was added with PushIndent - /// - public string PopIndent() - { - string returnValue = ""; - if ((this.indentLengths.Count > 0)) - { - int indentLength = this.indentLengths[(this.indentLengths.Count - 1)]; - this.indentLengths.RemoveAt((this.indentLengths.Count - 1)); - if ((indentLength > 0)) - { - returnValue = this.currentIndentField.Substring((this.currentIndentField.Length - indentLength)); - this.currentIndentField = this.currentIndentField.Remove((this.currentIndentField.Length - indentLength)); - } - } - return returnValue; - } - /// - /// Remove any indentation - /// - public void ClearIndent() - { - this.indentLengths.Clear(); - this.currentIndentField = ""; - } - #endregion - #region ToString Helpers - /// - /// Utility class to produce culture-oriented representation of an object as a string. - /// - public class ToStringInstanceHelper - { - private System.IFormatProvider formatProviderField = global::System.Globalization.CultureInfo.InvariantCulture; - /// - /// Gets or sets format provider to be used by ToStringWithCulture method. - /// - public System.IFormatProvider FormatProvider - { - get - { - return this.formatProviderField ; - } - set - { - if ((value != null)) - { - this.formatProviderField = value; - } - } - } - /// - /// This is called from the compile/run appdomain to convert objects within an expression block to a string - /// - public string ToStringWithCulture(object objectToConvert) - { - if ((objectToConvert == null)) - { - throw new global::System.ArgumentNullException("objectToConvert"); - } - System.Type t = objectToConvert.GetType(); - System.Reflection.MethodInfo method = t.GetMethod("ToString", new System.Type[] { - typeof(System.IFormatProvider)}); - if ((method == null)) - { - return objectToConvert.ToString(); - } - else - { - return ((string)(method.Invoke(objectToConvert, new object[] { - this.formatProviderField }))); - } - } - } - private ToStringInstanceHelper toStringHelperField = new ToStringInstanceHelper(); - /// - /// Helper to produce culture-oriented representation of an object as a string - /// - public ToStringInstanceHelper ToStringHelper - { - get - { - return this.toStringHelperField; - } - } - #endregion - } - #endregion -} diff --git a/src/DotVVM.Framework/Controls/FileUploadPageTemplate.tt b/src/DotVVM.Framework/Controls/FileUploadPageTemplate.tt deleted file mode 100644 index 49596c6c6f..0000000000 --- a/src/DotVVM.Framework/Controls/FileUploadPageTemplate.tt +++ /dev/null @@ -1,69 +0,0 @@ -<#@ template language="C#" #> -<#@ assembly name="System.Core" #> -<#@ import namespace="System.Linq" #> -<#@ import namespace="System.Text" #> -<#@ import namespace="System.Collections.Generic" #> -<#@ import namespace="System.Net" #> - - - - - - - - - -
- multiple="multiple" <# } #> <# if (!string.IsNullOrWhiteSpace(AllowedFileTypes)) { #> accept="<#= WebUtility.HtmlEncode(AllowedFileTypes) #>" <# } #> onchange="dotvvmSubmit();"/> -
- - - - - - -<#+ -public string StartupScript { get; set; } -public string FormPostUrl { get; set; } -public bool AllowMultipleFiles { get; set; } -public string AllowedFileTypes { get; set; } -#> diff --git a/src/DotVVM.Framework/DotVVM.Framework.csproj b/src/DotVVM.Framework/DotVVM.Framework.csproj index 66f07c1efd..461294a89d 100644 --- a/src/DotVVM.Framework/DotVVM.Framework.csproj +++ b/src/DotVVM.Framework/DotVVM.Framework.csproj @@ -1,7 +1,7 @@  DotVVM - 3.0.0 + 3.0.2 net451;netstandard2.0;netstandard2.1 $(NoWarn);CS1591;CS1573 true @@ -9,7 +9,7 @@ true true DotVVM - 3.0.0 + 3.0.2 RIGANTI DotVVM is an open source ASP.NET-based framework which allows to build modern web apps without writing any JavaScript code. false @@ -132,10 +132,6 @@ DelegateTemplate.cs - - TextTemplatingFilePreprocessor - FileUploadPageTemplate.cs - TextTemplatingFilePreprocessor ErrorPageTemplate.cs @@ -149,11 +145,6 @@ - - True - True - FileUploadPageTemplate.tt - True True diff --git a/src/DotVVM.Framework/Hosting/DotvvmPresenter.cs b/src/DotVVM.Framework/Hosting/DotvvmPresenter.cs index b55e8ac446..d15d61b0d6 100644 --- a/src/DotVVM.Framework/Hosting/DotvvmPresenter.cs +++ b/src/DotVVM.Framework/Hosting/DotvvmPresenter.cs @@ -425,17 +425,20 @@ await OutputRenderer.WriteStaticCommandResponse( } context.CommandException = ex; } - - // run OnCommandExecuted on action filters - foreach (var filter in methodFilters.Reverse()) + finally { - await filter.OnCommandExecutedAsync(context, action, context.CommandException); - } + // run OnCommandExecuted on action filters + foreach (var filter in methodFilters.Reverse()) + { + await filter.OnCommandExecutedAsync(context, action, context.CommandException); + } - if (context.CommandException != null && !context.IsCommandExceptionHandled) - { - throw new Exception("Unhandled exception occurred in the command!", context.CommandException); + if (context.CommandException != null && !context.IsCommandExceptionHandled) + { + throw new Exception("Unhandled exception occurred in the command!", context.CommandException); + } } + return null; } diff --git a/src/DotVVM.Framework/Hosting/Middlewares/DotvvmFileUploadMiddleware.cs b/src/DotVVM.Framework/Hosting/Middlewares/DotvvmFileUploadMiddleware.cs index 8091f18be8..d2e797ecef 100644 --- a/src/DotVVM.Framework/Hosting/Middlewares/DotvvmFileUploadMiddleware.cs +++ b/src/DotVVM.Framework/Hosting/Middlewares/DotvvmFileUploadMiddleware.cs @@ -97,30 +97,17 @@ private async Task ProcessMultipartRequest(IDotvvmRequestContext request) await RenderResponse(request, isPost, errorMessage, uploadedFiles); } - private bool ShouldReturnJsonResponse(IHttpContext context) => - context.Request.Headers[HostingConstants.DotvvmFileUploadAsyncHeaderName] == "true" || - context.Request.Query["returnJson"] == "true"; - private async Task RenderResponse(IDotvvmRequestContext request, bool isPost, string errorMessage, List uploadedFiles) { var context = request.HttpContext; - var settings = DefaultSerializerSettingsProvider.Instance.Settings; - if (isPost && ShouldReturnJsonResponse(context)) + if (isPost) { // modern browser - return JSON if (string.IsNullOrEmpty(errorMessage)) { var json = viewModelSerializer.BuildStaticCommandResponse(request, uploadedFiles); - if (context.Request.Query["iframe"] == "true") - { - // IE will otherwise try to download the response as JSON file - await outputRenderer.RenderPlainTextResponse(context, json); - } - else - { - await outputRenderer.RenderPlainJsonResponse(context, json); - } + await outputRenderer.RenderPlainJsonResponse(context, json); } else { @@ -128,31 +115,7 @@ private async Task RenderResponse(IDotvvmRequestContext request, bool isPost, st context.Response.StatusCode = (int)HttpStatusCode.InternalServerError; } } - else - { - // old browser - return HTML - var template = new FileUploadPageTemplate { - FormPostUrl = context.Request.Url.ToString(), - AllowMultipleFiles = context.Request.Query["multiple"] == "true", - AllowedFileTypes = context.Request.Query["fileTypes"] - }; - - if (isPost) - { - if (string.IsNullOrEmpty(errorMessage)) - { - template.StartupScript = string.Format("reportProgress(false, 100, {0})", - viewModelSerializer.BuildStaticCommandResponse(request, uploadedFiles)); - } - else - { - template.StartupScript = string.Format("reportProgress(false, 100, {0})", - JsonConvert.SerializeObject(errorMessage, settings)); - } - } - - await outputRenderer.RenderHtmlResponse(context, template.TransformText()); - } + } private async Task SaveFiles(IHttpContext context, Group boundary, List uploadedFiles) diff --git a/src/DotVVM.Framework/Properties/AssemblyInfo.cs b/src/DotVVM.Framework/Properties/AssemblyInfo.cs index 36085e86c9..1b036d3fdb 100644 --- a/src/DotVVM.Framework/Properties/AssemblyInfo.cs +++ b/src/DotVVM.Framework/Properties/AssemblyInfo.cs @@ -31,9 +31,9 @@ // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: -// [assembly: AssemblyVersion("3.0.0")] -[assembly: AssemblyVersion("3.0.0")] -[assembly: AssemblyFileVersion("3.0.0")] +// [assembly: AssemblyVersion("3.0.2")] +[assembly: AssemblyVersion("3.0.2")] +[assembly: AssemblyFileVersion("3.0.2")] [assembly: InternalsVisibleTo("DotVVM.Framework.Tests.Common, PublicKey=002400000480000094000000060200000024000052534131000400000100010007c544d7dbb49a258f8f7509b74f488534c1872c417b2fba85a66b1fba2465caafbaec56663cab0f072cd801f9e22753a85dc55247d4ace012a5eceac50d84c3b9044b9a37ac8baa5eb24dec644ad9fafcc869ad93b6603ffd5321124362cf0ab3684b89db1ed2aca4f175f6fbfc770bdc076396b36017c6dce5a4385c7b67b7")] [assembly: InternalsVisibleTo("DotVVM.Framework.Tests.Owin, PublicKey=002400000480000094000000060200000024000052534131000400000100010007c544d7dbb49a258f8f7509b74f488534c1872c417b2fba85a66b1fba2465caafbaec56663cab0f072cd801f9e22753a85dc55247d4ace012a5eceac50d84c3b9044b9a37ac8baa5eb24dec644ad9fafcc869ad93b6603ffd5321124362cf0ab3684b89db1ed2aca4f175f6fbfc770bdc076396b36017c6dce5a4385c7b67b7")] diff --git a/src/DotVVM.Framework/ResourceManagement/ScriptModuleResource.cs b/src/DotVVM.Framework/ResourceManagement/ScriptModuleResource.cs index 55071bbcda..668158c5db 100644 --- a/src/DotVVM.Framework/ResourceManagement/ScriptModuleResource.cs +++ b/src/DotVVM.Framework/ResourceManagement/ScriptModuleResource.cs @@ -38,7 +38,7 @@ public override void RenderLink(IResourceLocation location, IHtmlWriter writer, if (NomoduleLocation is object) { writer.AddAttribute("nomodule", null); - writer.AddAttribute("src", location.GetUrl(context, resourceName) + "?type=nomodule"); + writer.AddAttribute("src", NomoduleLocation.GetUrl(context, resourceName) + "?type=nomodule"); if (Defer) writer.AddAttribute("defer", null); writer.RenderBeginTag("script"); diff --git a/src/DotVVM.Framework/Resources/Scripts/DotVVM.Globalize.ts b/src/DotVVM.Framework/Resources/Scripts/DotVVM.Globalize.ts index 98ba61bd37..471ee67745 100644 --- a/src/DotVVM.Framework/Resources/Scripts/DotVVM.Globalize.ts +++ b/src/DotVVM.Framework/Resources/Scripts/DotVVM.Globalize.ts @@ -1,4 +1,4 @@ -import { parseDate as parseDotvvmDate, serializeDate as serializeDotvvmDate } from './serialization/date' +import { parseDate as serializationParseDate, serializeDate } from './serialization/date' import { getCulture } from './dotvvm-base'; function getGlobalize(): GlobalizeStatic { @@ -30,7 +30,7 @@ export function formatString(format: string | null | undefined, value: Globalize if (typeof value === "string") { // JSON date in string - value = parseDotvvmDate(value); + value = serializationParseDate(value); if (value == null) { throw new Error(`Could not parse ${value} as a date`); } @@ -51,6 +51,8 @@ export function parseDate(value: string, format: string, previousValue?: Date) { return getGlobalize().parseDate(value, format, getCulture(), previousValue); } +export const parseDotvvmDate = serializationParseDate; + export function bindingDateToString(value: KnockoutObservable | string | Date, format: string = "G") { if (!value) { return ""; @@ -58,7 +60,7 @@ export function bindingDateToString(value: KnockoutObservable | s const unwrapDate = () => { const unwrappedVal = ko.unwrap(value); - return typeof unwrappedVal == "string" ? parseDotvvmDate(unwrappedVal) : unwrappedVal; + return typeof unwrappedVal == "string" ? serializationParseDate(unwrappedVal) : unwrappedVal; }; const formatDate = () => formatString(format, value); @@ -66,11 +68,11 @@ export function bindingDateToString(value: KnockoutObservable | s if (ko.isWriteableObservable(value)) { const unwrappedVal = unwrapDate(); const setter = typeof unwrappedVal == "string" ? (v: Date | null) => { - return value(v && serializeDotvvmDate(v, false)); + return value(v && serializeDate(v, false)); } : value; return ko.pureComputed({ read: formatDate, - write: val => setter(parseDate(val, format)) + write: val => setter(parseDate(val, format) || parseDate(val, "")) }); } else { diff --git a/src/DotVVM.Framework/Resources/Scripts/Polyfills/utils.js b/src/DotVVM.Framework/Resources/Scripts/Polyfills/utils.js index cd794679af..c657543872 100644 --- a/src/DotVVM.Framework/Resources/Scripts/Polyfills/utils.js +++ b/src/DotVVM.Framework/Resources/Scripts/Polyfills/utils.js @@ -2,4 +2,29 @@ if (!Math.trunc) { Math.trunc = function (v) { return v < 0 ? Math.ceil(v) : Math.floor(v); }; +} +if (!String.prototype.includes) { + String.prototype.includes = function (search, start) { + 'use strict'; + + if (search instanceof RegExp) { + throw TypeError('first argument must not be a RegExp'); + } + if (start === undefined) { start = 0; } + return this.indexOf(search, start) !== -1; + }; +} +if (!String.prototype.endsWith) { + String.prototype.endsWith = function (search, this_len) { + if (this_len === undefined || this_len > this.length) { + this_len = this.length; + } + return this.substring(this_len - search.length, this_len) === search; + }; +} +if (!String.prototype.startsWith) { + String.prototype.startsWith = function(search, rawPos) { + var pos = rawPos > 0 ? rawPos | 0 : 0; + return this.substring(pos, pos + search.length) === search; + } } \ No newline at end of file diff --git a/src/DotVVM.Framework/Resources/Scripts/binding-handlers/all-handlers.ts b/src/DotVVM.Framework/Resources/Scripts/binding-handlers/all-handlers.ts index 00db7a583c..e03de7e5b1 100644 --- a/src/DotVVM.Framework/Resources/Scripts/binding-handlers/all-handlers.ts +++ b/src/DotVVM.Framework/Resources/Scripts/binding-handlers/all-handlers.ts @@ -9,6 +9,7 @@ import updateProgress from './update-progress' import gridviewdataset from './gridviewdataset' import withViewModules from './with-view-modules' import namedCommand from './named-command' +import fileUpload from './file-upload' type KnockoutHandlerDictionary = { [name: string]: KnockoutBindingHandler @@ -24,7 +25,8 @@ const allHandlers: KnockoutHandlerDictionary = { ...updateProgress, ...gridviewdataset, ...withViewModules, - ...namedCommand + ...namedCommand, + ...fileUpload } export default allHandlers diff --git a/src/DotVVM.Framework/Resources/Scripts/binding-handlers/file-upload.ts b/src/DotVVM.Framework/Resources/Scripts/binding-handlers/file-upload.ts new file mode 100644 index 0000000000..6a1b4a277b --- /dev/null +++ b/src/DotVVM.Framework/Resources/Scripts/binding-handlers/file-upload.ts @@ -0,0 +1,44 @@ +export default { + "dotvvm-FileUpload": { + init: function (element: HTMLInputElement, valueAccessor: () => any, allBindings?: any, viewModel?: any, bindingContext?: KnockoutBindingContext) { + + var args = ko.unwrap(valueAccessor()); + + function reportProgress(isBusy: boolean, percent: number, resultOrError: string | DotvvmStaticCommandResponse) { + dotvvm.fileUpload.reportProgress( element, isBusy, percent, resultOrError); + } + + element.addEventListener("change", function() { + if (!element.files || !element.files.length) return; + + var xhr = XMLHttpRequest ? new XMLHttpRequest() : new ((window as any)["ActiveXObject"])("Microsoft.XMLHTTP"); + xhr.open("POST", args.url, true); + xhr.setRequestHeader("X-DotVVM-AsyncUpload", "true"); + xhr.upload.onprogress = function (e: ProgressEvent) { + if (e.lengthComputable) { + reportProgress(true, Math.round(e.loaded * 100 / e.total), ''); + } + }; + xhr.onload = function () { + if (xhr.status == 200) { + reportProgress(false, 100, JSON.parse(xhr.responseText)); + element.value = ""; + } else { + reportProgress(false, 0, "Upload failed."); + } + }; + + var formData = new FormData(); + if (element.files.length > 1) { + for (var i = 0; i < element.files.length; i++) { + formData.append("upload[]", element.files[i]); + } + } else if (element.files.length > 0) { + formData.append("upload", element.files[0]); + } + xhr.send(formData); + }); + + } + } +} diff --git a/src/DotVVM.Framework/Resources/Scripts/binding-handlers/textbox-text.ts b/src/DotVVM.Framework/Resources/Scripts/binding-handlers/textbox-text.ts index 8e6dce9ea8..317f05131a 100644 --- a/src/DotVVM.Framework/Resources/Scripts/binding-handlers/textbox-text.ts +++ b/src/DotVVM.Framework/Resources/Scripts/binding-handlers/textbox-text.ts @@ -1,4 +1,4 @@ -import { parseDate as parseDotvvmDate, serializeDate } from '../serialization/date' +import { parseDate, serializeDate } from '../serialization/date' import * as globalize from '../DotVVM.Globalize' import { DotvvmValidationElementMetadata, DotvvmValidationObservableMetadata, getValidationMetadata } from '../validation/common'; import { lastSetErrorSymbol } from '../state-manager'; @@ -55,9 +55,9 @@ export default { // parse date let currentValue = obs(); if (currentValue != null) { - currentValue = parseDotvvmDate(currentValue); + currentValue = parseDate(currentValue); } - result = globalize.parseDate(element.value, elmMetadata.format, currentValue); + result = globalize.parseDate(element.value, elmMetadata.format, currentValue) || globalize.parseDate(element.value, "", currentValue); isEmpty = result == null; newValue = isEmpty ? null : serializeDate(result, false); } else if (elmMetadata.dataType === "number") { diff --git a/src/DotVVM.Framework/Resources/Scripts/collections/arrayHelper.ts b/src/DotVVM.Framework/Resources/Scripts/collections/arrayHelper.ts new file mode 100644 index 0000000000..eab015877f --- /dev/null +++ b/src/DotVVM.Framework/Resources/Scripts/collections/arrayHelper.ts @@ -0,0 +1,121 @@ +import { orderBy, orderByDesc } from './sortingHelper' + +export { + all, + any, + clear, + distinct, + firstOrDefault, + forEach, + lastOrDefault, + max, + min, + orderBy, + orderByDesc, + remove, + removeFirst +} + +function any(source: T[], predicate: (s: T) => boolean): boolean { + return firstOrDefault(source, predicate) != null; +} + +function all(source: T[], predicate: (s: T) => boolean) { + for (let i = 0; i < source.length; i++) { + if (!predicate(source[i])) { + return false; + } + } + return true; +} + +function clear(source: T[]): void { + source.splice(0, source.length); +} + +function distinct(source: T[]): T[] { + let r = []; + for (let i = 0; i < source.length; i++) { + let found = false; + for (var j = 0; j < r.length; j++) { + if (r[j] == source[i]) { + found = true; + break; + } + } + if (found) + continue; + r.push(source[i]); + } + return r; +} + +function firstOrDefault(source: T[], predicate: (s: T) => boolean): T | null { + for (let i = 0; i < source.length; i++) { + if (predicate(source[i])) { + return source[i]; + } + } + return null; +} + +function forEach(items: T[], action: (s: T, i: number) => void): void { + for (let i = 0; i < items.length; i++) { + action(items[i], i); + } +} + +function lastOrDefault(source: T[], predicate: (s: T) => boolean): T | null { + for (let i = source.length - 1; i >= 0; i--) { + if (predicate(source[i])) { + return source[i]; + } + } + return null; +} + +function max(source: T[], selector: (item: T) => number): number { + if (source.length === 0) + throw new Error("Source is empty! Max operation cannot be performed."); + if (source.length == 1) + return selector(source[0]); + let max = selector(source[0]); + for (let i = 1; i < source.length; i++) { + let v = selector(source[i]); + if (v > max) + max = v; + } + return max; +} + +function min(source: T[], selector: (item: T) => number): number { + if (source.length === 0) + throw new Error("Source is empty! Min operation cannot be performed."); + if (source.length == 1) + return selector(source[0]); + let min = selector(source[0]); + for (let i = 1; i < source.length; i++) { + let v = selector(source[i]); + if (v < min) + min = v; + } + return min; +} + +function remove(source: T[], predicate: (s: T) => boolean) { + for (let i = 0; i < source.length; i++) { + if (predicate(source[i])) { + source.splice(i, 1); + i--; + } + } +} + +function removeFirst(source: T[], predicate: (s: T) => boolean) { + for (let i = 0; i < source.length; i++) { + if (predicate(source[i])) { + source.splice(i, 1); + return; + } + } +} diff --git a/src/DotVVM.Framework/Resources/Scripts/collections/sortingHelper.ts b/src/DotVVM.Framework/Resources/Scripts/collections/sortingHelper.ts new file mode 100644 index 0000000000..974e701661 --- /dev/null +++ b/src/DotVVM.Framework/Resources/Scripts/collections/sortingHelper.ts @@ -0,0 +1,139 @@ +import { getTypeInfo } from "../metadata/typeMap"; +import { primitiveTypes } from "../metadata/primitiveTypes"; +import { keys } from "../utils/objects"; +type ElementType = string | number | boolean; + +export const orderBy = (array: T[], selector: (item: T) => ElementType) => + orderByImpl(array, selector, getComparer(array[0], selector, true)) + +export const orderByDesc = (array: T[], selector: (item: T) => ElementType) => + orderByImpl(array, selector, getComparer(array[0], selector, false)) + +function orderByImpl(array: T[], selector: (item: T) => ElementType, compare: (first: ElementType, second: ElementType) => number): T[] { + + if ((!array || array.length < 2)) + return array; + + return array + .map((item, index) => ({ item, index })) + .sort((first, second) => compare(ko.unwrap(selector(first.item)), ko.unwrap(selector(second.item))) || first.index - second.index) + .map(({ item }) => item); +} + +function getComparer(element: T, selector: (item: T) => ElementType, ascending: boolean): (first: ElementType, second: ElementType) => number { + let metadataInfo = getMetadataInfo(element, selector(element)); + + if (metadataInfo !== null && metadataInfo.type === "object") { + throw new Error("Can not compare objects!"); + } + else if (metadataInfo !== null && metadataInfo.type === "enum") { + // Enums should be compared based on their underlying primitive values + // This is the same behaviour as used by .NET + let enumMetadataInfo = metadataInfo as EnumTypeMetadata; + return function (first: ElementType, second: ElementType) { + let firstNumeric = enumMetadataInfo.values[first as string]; + let secondNumeric = enumMetadataInfo.values[second as string]; + return defaultPrimitivesComparer(firstNumeric, secondNumeric, ascending); + } + } + else { + // We are comparing primitive types + return function (first: ElementType, second: ElementType) { + return defaultPrimitivesComparer(first, second, ascending); + } + } +} + +function getMetadataInfo(original: any, selected: any): TypeMetadata | null { + var path = getPath(original, selected); + if (path === null) { + return null; + } + else { + let typeId = ko.unwrap((ko.unwrap(original).$type)) as string; + let type = typeId as TypeDefinition; + let pathSegmentIndex = 0; + + while (pathSegmentIndex < path.length) { + if (Array.isArray(type)) { + const index = parseInt(path[pathSegmentIndex++]); + type = type[index]; + } + else if (typeof type === "object") { + if (type.type == "nullable") { + type = type.inner; + } + else if (type.type === "dynamic") { + // No metadata available + return null; + } + } + else if (typeof type === "string") { + if (type in primitiveTypes) { + // No metadata available + return null; + } else { + let metadata = getTypeInfo(type); + if (metadata && metadata.type === "object") { + let pathSegment = path[pathSegmentIndex++]; + type = metadata.properties[pathSegment].type; + } + else if (metadata && metadata.type === "enum") { + return metadata; + } + } + } + } + + return resolveMetadata(type); + } +} + +function resolveMetadata(type: TypeDefinition): TypeMetadata | null { + if (!Array.isArray(type) && typeof type === "object" && type.type === "nullable") { + // Unwrap nullables + type = type.inner; + } + + if (Array.isArray(type) || (typeof type === "object" && type.type === "dynamic")) { + // We can not retrieve metadata for arrays and dynamics + throw new Error("Could not resolve metadata!"); + } + + if (type as string in primitiveTypes) { + // No metadata available + return null; + } + else { + return getTypeInfo(type as string); + } +} + +function getPath(from: any, target: any): string[] | null { + from = ko.unwrap(from); + target = ko.unwrap(target); + + if (from == target) + return []; + + for (let key of keys(from)) { + let item = ko.unwrap(from[key]); + if (item && typeof item === "object") { + let subPath = getPath(item, target); + if (subPath) { + subPath.unshift(key); + return subPath; + } + } + else if (item === target) { + return [key]; + } + } + + return null; +} + +const defaultPrimitivesComparer = (first: ElementType, second: ElementType, ascending: boolean) => { + let comparision = (first < second) ? -1 : (first == second) ? 0 : 1; + return (ascending) ? comparision : comparision * -1; +} diff --git a/src/DotVVM.Framework/Resources/Scripts/controls/fileUpload.ts b/src/DotVVM.Framework/Resources/Scripts/controls/fileUpload.ts index 972928ee68..e4925197d7 100644 --- a/src/DotVVM.Framework/Resources/Scripts/controls/fileUpload.ts +++ b/src/DotVVM.Framework/Resources/Scripts/controls/fileUpload.ts @@ -4,22 +4,13 @@ import { updateTypeInfo } from '../metadata/typeMap'; export function showUploadDialog(sender: HTMLElement) { // trigger the file upload dialog - const iframe = getIframe(sender); - createUploadId(sender, iframe); - openUploadDialog(iframe); + let fileUpload = sender.parentElement!.parentElement!.querySelector("input[type=file]"); + fileUpload!.click(); } -export function createUploadId(sender: HTMLElement, iframe: HTMLElement): void { - iframe = iframe || getIframe(sender); - const uploadId = "DotVVM_upl" + new Date().getTime().toString(); - sender.parentElement!.parentElement!.setAttribute("data-dotvvm-upload-id", uploadId); - - iframe.setAttribute("data-dotvvm-upload-id", uploadId); -} - -export function reportProgress(targetControlId: any, isBusy: boolean, progress: number, result: DotvvmStaticCommandResponse | string): void { +export function reportProgress(inputControl: HTMLInputElement, isBusy: boolean, progress: number, result: DotvvmStaticCommandResponse | string): void { // find target control viewmodel - const targetControl = document.querySelector("div[data-dotvvm-upload-id='" + targetControlId.value + "']"); + const targetControl = inputControl.parentElement!; const viewModel = ko.dataFor(targetControl.firstChild); // determine the status @@ -43,33 +34,3 @@ export function reportProgress(targetControlId: any, isBusy: boolean, progress: viewModel.IsBusy(isBusy); } -function getIframe(sender: HTMLElement): HTMLIFrameElement { - return sender.parentElement!.previousSibling; -} - -function openUploadDialog(iframe: HTMLIFrameElement): void { - const window = iframe.contentWindow; - if (window) { - const fileUpload = window.document.getElementById('upload'); - fileUpload.click(); - } -} - -type DotvvmFileUploadCollection = { - Files: KnockoutObservableArray>; - Progress: KnockoutObservable; - Error: KnockoutObservable; - IsBusy: KnockoutObservable; -} -type DotvvmFileUploadData = { - FileId: KnockoutObservable; - FileName: KnockoutObservable; - FileSize: KnockoutObservable; - IsFileTypeAllowed: KnockoutObservable; - IsMaxSizeExceeded: KnockoutObservable; - IsAllowed: KnockoutObservable; -} -type DotvvmFileSize = { - Bytes: KnockoutObservable; - FormattedText: KnockoutObservable; -} diff --git a/src/DotVVM.Framework/Resources/Scripts/dotvvm-root.ts b/src/DotVVM.Framework/Resources/Scripts/dotvvm-root.ts index 03ff0a3fc1..15681ea30a 100644 --- a/src/DotVVM.Framework/Resources/Scripts/dotvvm-root.ts +++ b/src/DotVVM.Framework/Resources/Scripts/dotvvm-root.ts @@ -24,6 +24,9 @@ import * as eventHub from './api/eventHub' import * as viewModuleManager from './viewModules/viewModuleManager' import { notifyModuleLoaded } from './postback/resourceLoader' import { logError, logWarning, logInfo, logInfoVerbose, level, logPostBackScriptError } from "./utils/logging" +import { orderBy, orderByDesc } from './collections/sortingHelper' +import * as arrayHelper from './collections/arrayHelper' +import * as stringHelper from './utils/stringHelper' if (compileConstants.nomodules) { addPolyfills() @@ -54,8 +57,7 @@ const dotvvmExports = { }, fileUpload: { reportProgress: fileUpload.reportProgress, - showUploadDialog: fileUpload.showUploadDialog, - createUploadId: fileUpload.createUploadId + showUploadDialog: fileUpload.showUploadDialog }, api: { invoke: api.invoke, @@ -113,7 +115,9 @@ const dotvvmExports = { logInfoVerbose, logPostBackScriptError, level - } + }, + arrayHelper, + stringHelper } if (compileConstants.isSpa) { diff --git a/src/DotVVM.Framework/Resources/Scripts/global-declarations.ts b/src/DotVVM.Framework/Resources/Scripts/global-declarations.ts index c5297ffe74..8da997c623 100644 --- a/src/DotVVM.Framework/Resources/Scripts/global-declarations.ts +++ b/src/DotVVM.Framework/Resources/Scripts/global-declarations.ts @@ -273,3 +273,22 @@ type CoerceErrorType = { } type CoerceResult = CoerceErrorType | { value: any, wasCoerced?: boolean, isError?: false }; + +type DotvvmFileUploadCollection = { + Files: KnockoutObservableArray>; + Progress: KnockoutObservable; + Error: KnockoutObservable; + IsBusy: KnockoutObservable; +} +type DotvvmFileUploadData = { + FileId: KnockoutObservable; + FileName: KnockoutObservable; + FileSize: KnockoutObservable; + IsFileTypeAllowed: KnockoutObservable; + IsMaxSizeExceeded: KnockoutObservable; + IsAllowed: KnockoutObservable; +} +type DotvvmFileSize = { + Bytes: KnockoutObservable; + FormattedText: KnockoutObservable; +} diff --git a/src/DotVVM.Framework/Resources/Scripts/metadata/coercer.ts b/src/DotVVM.Framework/Resources/Scripts/metadata/coercer.ts index 90f82a7b02..2f67e5bb8c 100644 --- a/src/DotVVM.Framework/Resources/Scripts/metadata/coercer.ts +++ b/src/DotVVM.Framework/Resources/Scripts/metadata/coercer.ts @@ -72,12 +72,26 @@ function tryCoerceNullable(value: any, innerType: TypeDefinition, originalValue: } function tryCoerceEnum(value: any, type: EnumTypeMetadata): CoerceResult { - if (typeof value === "string" && value in type.values) { - return { value }; - } else if (typeof value === "number") { + let wasCoerced = false; + if (typeof value === "string") { + if (value in type.values) { + return { value }; + } else if (value !== "") { + // number value as string + const numberValue = Number(value); + if (!isNaN(numberValue)) { + value = numberValue; + wasCoerced = true; + } + } + } + if (typeof value === "number") { const matched = keys(type.values).filter(k => type.values[k] === value); if (matched.length) { return { value: matched[0], wasCoerced: true } + } else { + // number value not in enum + return { value: value | 0, wasCoerced } } } return new CoerceError(`Cannot cast '${value}' to type 'Enum(${keys(type.values).join(",")})'.`); diff --git a/src/DotVVM.Framework/Resources/Scripts/metadata/primitiveTypes.ts b/src/DotVVM.Framework/Resources/Scripts/metadata/primitiveTypes.ts index a8b1925ce8..0e7c7a8d62 100644 --- a/src/DotVVM.Framework/Resources/Scripts/metadata/primitiveTypes.ts +++ b/src/DotVVM.Framework/Resources/Scripts/metadata/primitiveTypes.ts @@ -1,5 +1,5 @@ import { formatString, parseDate as globalizeParseDate } from "../DotVVM.Globalize"; -import { parseDate as serializationParseDate, serializeDate, serializeTime } from "../serialization/date"; +import { parseDate as serializationParseDate, parseTimeSpan as serializationParseTimeSpan, parseDateTimeOffset as serializationParseDateTimeOffset, serializeDate, serializeTimeSpan } from "../serialization/date"; import { CoerceError } from "../shared-classes"; type PrimitiveTypes = { @@ -69,9 +69,11 @@ export const primitiveTypes: PrimitiveTypes = { tryCoerce: validateDateTime }, TimeSpan: { - tryCoerce: validateDateTime + tryCoerce: validateTimeSpan + }, + DateTimeOffset: { + tryCoerce: validateDateTimeOffset } - }; function validateInt(value: any, min: number, max: number) { @@ -178,3 +180,31 @@ function validateDateTime(value: any) { return { value: serializeDate(value, false), wasCoerced: true }; } } + +function validateTimeSpan(value: any) { + if (typeof value === "string") { + // strict DotVVM format parse + const parsedValue = serializationParseTimeSpan(value); + if (parsedValue !== null) { + return { value: serializeTimeSpan(parsedValue) }; + } + } + + if (typeof value === "number") { + return { value: serializeTimeSpan(value), wasCoerced: true }; + } +} + +function validateDateTimeOffset(value: any) { + if (typeof value === "string") { + // strict DotVVM format parse + if (serializationParseDateTimeOffset(value)) { + return { value }; + } + } + + // TODO: support conversion to date + // if (value instanceof Date) { + // return { value: serializeDate(value, false), wasCoerced: true }; + // } +} \ No newline at end of file diff --git a/src/DotVVM.Framework/Resources/Scripts/serialization/date.ts b/src/DotVVM.Framework/Resources/Scripts/serialization/date.ts index 3816f4258c..2c2a047480 100644 --- a/src/DotVVM.Framework/Resources/Scripts/serialization/date.ts +++ b/src/DotVVM.Framework/Resources/Scripts/serialization/date.ts @@ -1,7 +1,7 @@ import { logWarning } from "../utils/logging"; export function parseDate(value: string): Date | null { - const match = value.match("^([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})(\\.[0-9]{3,7})$"); + const match = value.match("^([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})(\\.[0-9]{1,7})$"); if (match) { return new Date(parseInt(match[1], 10), parseInt(match[2], 10) - 1, parseInt(match[3], 10), parseInt(match[4], 10), parseInt(match[5], 10), parseInt(match[6], 10), match.length > 7 ? parseInt(match[7].substring(1, 4), 10) : 0); @@ -9,6 +9,30 @@ export function parseDate(value: string): Date | null { return null; } +export function parseTimeSpan(value: string): number | null { + const match = value.match("^(-?)([0-9]+\\.)?([0-9]+):([0-9]{2}):([0-9]{2})(\\.[0-9]{3,7})?$"); + if (match) { + const sign = match[1] ? -1 : 1; + const days = match[2] ? parseInt(match[2], 10) : 0; + const ticks = (days * 24 + parseInt(match[3], 10)) * 3600 * 1000 + + parseInt(match[4], 10) * 60 * 1000 + + parseInt(match[5], 10) * 1000 + + (match[6] ? parseInt(match[6].substring(1, 4), 10) : 0); + return sign * ticks; + } + return null; +} + +export function parseDateTimeOffset(value: string): Date | null { + const match = value.match("^([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})(\\.[0-9]{1,7})?(Z|[+-]([0-9]{1,2}):([0-9]{2}))$"); + if (match) { + const offset = match[8] === "Z" ? 0 : ((match[8] === "-" ? -1 : 1) * (parseInt(match[9], 10) * 60 + parseInt(match[10], 10))); + return new Date(parseInt(match[1], 10), parseInt(match[2], 10) - 1, parseInt(match[3], 10), + parseInt(match[4], 10), parseInt(match[5], 10) + offset, parseInt(match[6], 10), match[7] ? parseInt(match[7].substring(1, 4), 10) : 0); + } + return null; +} + function padNumber(value: string | number, digits: number): string { value = value + "" while (value.length < digits) { @@ -44,26 +68,40 @@ export function serializeDate(date: string | Date | null, convertToUtc: boolean return `${y}-${m}-${d}T${h}:${mi}:${s}.${ms}0000`; } -export function serializeTime(date: string | Date | null, convertToUtc: boolean = true): string | null { - if (date == null) { +export function serializeTimeSpan(time: string | number | null): string | null { + let ticks: number; + + if (time === null) { return null; - } else if (typeof date == "string") { + } else if (typeof time == "string") { // just print in the console if it's invalid - if (parseDate(date) == null) { - logWarning("coercer", `Date ${date} is invalid.`); + const parsedTime = parseTimeSpan(time); + if (parsedTime === null) { + logWarning("coercer", `TimeSpan ${time} is invalid.`); + return null; } - return date; - } - let date2 = new Date(date.getTime()); - if (convertToUtc) { - date2.setMinutes(date.getMinutes() + date.getTimezoneOffset()); + + ticks = parsedTime; } else { - date2 = date; + ticks = time; } + + const sign = ticks >= 0 ? "" : "-"; + ticks = Math.abs(ticks); + const hours = (ticks / 1000 / 3600) | 0; + const minutes = (ticks / 1000 / 60) | 0; + const seconds = (ticks / 1000) | 0; + const milliseconds = (ticks % 1000) | 0; - const h = (date2.getTime() - new Date(1, 0, 1).getTime() / 1000 / 3600) | 0; - const mi = padNumber(date2.getMinutes(), 2); - const s = padNumber(date2.getSeconds(), 2); - const ms = padNumber(date2.getMilliseconds(), 3); - return `${h}:${mi}:${s}.${ms}0000`; + const h = padNumber(hours % 24, 2); + const mi = padNumber(minutes % 60, 2); + const s = padNumber(seconds % 60, 2); + const ms = milliseconds !== 0 ? ("." + padNumber(milliseconds, 3) + "0000") : ""; + + if (hours < 24) { + return `${sign}${h}:${mi}:${s}${ms}`; + } else { + const d = (hours / 24) | 0; + return `${sign}${d}.${h}:${mi}:${s}${ms}`; + } } diff --git a/src/DotVVM.Framework/Resources/Scripts/tests/coercer.test.ts b/src/DotVVM.Framework/Resources/Scripts/tests/coercer.test.ts index f960d5458a..5f55f296c9 100644 --- a/src/DotVVM.Framework/Resources/Scripts/tests/coercer.test.ts +++ b/src/DotVVM.Framework/Resources/Scripts/tests/coercer.test.ts @@ -176,6 +176,51 @@ test("Date - invalid, undefined", () => { expect(result.isError).toBeTruthy(); }) +test("TimeSpan - valid, zero", () => { + const result = tryCoerce("00:00:00", "TimeSpan"); + expect(result.wasCoerced).toBeFalsy(); + expect(result.value).toEqual("00:00:00"); +}) + +test("TimeSpan - valid, from number", () => { + const result = tryCoerce(-3600000, "TimeSpan"); + expect(result.wasCoerced).toBeTruthy(); + expect(result.value).toEqual("-01:00:00"); +}) + +test("TimeSpan - valid, string representation", () => { + const result = tryCoerce("1.02:03:45.678", "TimeSpan"); + expect(result.wasCoerced).toBeFalsy(); + expect(result.value).toEqual("1.02:03:45.6780000"); +}) + +test("TimeSpan - valid, string representation, reformatting", () => { + const result = tryCoerce("2:03:45.678", "TimeSpan"); + expect(result.wasCoerced).toBeFalsy(); + expect(result.value).toEqual("02:03:45.6780000"); +}) + +test("TimeSpan - valid, string representation, 24hours reformatting", () => { + const result = tryCoerce("-26:03:45.678", "TimeSpan"); + expect(result.wasCoerced).toBeFalsy(); + expect(result.value).toEqual("-1.02:03:45.6780000"); +}) + +test("TimeSpan - invalid, string representation", () => { + const result = tryCoerce("", "TimeSpan"); + expect(result.isError).toBeTruthy(); +}) + +test("TimeSpan - invalid, null", () => { + const result = tryCoerce(null, "TimeSpan"); + expect(result.isError).toBeTruthy(); +}) + +test("TimeSpan - invalid, undefined", () => { + const result = tryCoerce(void 0, "TimeSpan"); + expect(result.isError).toBeTruthy(); +}) + test("Guid - valid", () => { const result = tryCoerce("00000000-0000-0000-0000-000000000000", "Guid"); @@ -276,6 +321,41 @@ test("string - invalid, array", () => { }) +test("enum - valid, string", () => { + const result = tryCoerce("One", "e1"); + expect(result.wasCoerced).toBeFalsy(); + expect(result.value).toEqual("One"); +}) + +test("enum - valid, number", () => { + const result = tryCoerce(1, "e1"); + expect(result.wasCoerced).toBeTruthy(); + expect(result.value).toEqual("One"); +}) + +test("enum - valid, number in string", () => { + const result = tryCoerce("1", "e1"); + expect(result.wasCoerced).toBeTruthy(); + expect(result.value).toEqual("One"); +}) + +test("enum - valid, number without string representation", () => { + const result = tryCoerce(3, "e1"); + expect(result.wasCoerced).toBeFalsy(); + expect(result.value).toEqual(3); +}) + +test("enum - invalid, undefined", () => { + const result = tryCoerce(void 0, "e1"); + expect(result.isError).toBeTruthy(); +}) + +test("enum - invalid, null", () => { + const result = tryCoerce(null, "e1"); + expect(result.isError).toBeTruthy(); +}) + + test("boolean - valid, true", () => { const result = tryCoerce(true, "Boolean"); expect(result.wasCoerced).toBeFalsy(); diff --git a/src/DotVVM.Framework/Resources/Scripts/utils/objects.ts b/src/DotVVM.Framework/Resources/Scripts/utils/objects.ts index b79f00b0bb..6f881350d5 100644 --- a/src/DotVVM.Framework/Resources/Scripts/utils/objects.ts +++ b/src/DotVVM.Framework/Resources/Scripts/utils/objects.ts @@ -17,5 +17,5 @@ export const symbolOrDollar : (name: string) => symbol = export const keys = compileConstants.nomodules ? - ((o: any) => isPrimitive(o) ? [] : Object.keys(o)) : + ((o: any) => isPrimitive(o) ? [] : Object.keys(o).filter(s => !s.startsWith("@"))) : Object.keys diff --git a/src/DotVVM.Framework/Resources/Scripts/utils/stringHelper.ts b/src/DotVVM.Framework/Resources/Scripts/utils/stringHelper.ts new file mode 100644 index 0000000000..f8f0bf787e --- /dev/null +++ b/src/DotVVM.Framework/Resources/Scripts/utils/stringHelper.ts @@ -0,0 +1,8 @@ +export function split(text: string, delimiter: string, options: string): string[] { + let tokens = text.split(delimiter); + if (options === "RemoveEmptyEntries") { + tokens = tokens.filter(t => t); + } + + return tokens; +} diff --git a/src/DotVVM.Framework/ViewModel/Serialization/ViewModelTypeMetadataSerializer.cs b/src/DotVVM.Framework/ViewModel/Serialization/ViewModelTypeMetadataSerializer.cs index 897e39e91f..897b76aa7a 100644 --- a/src/DotVVM.Framework/ViewModel/Serialization/ViewModelTypeMetadataSerializer.cs +++ b/src/DotVVM.Framework/ViewModel/Serialization/ViewModelTypeMetadataSerializer.cs @@ -32,7 +32,8 @@ public class ViewModelTypeMetadataSerializer : IViewModelTypeMetadataSerializer typeof(Char), typeof(Guid), typeof(DateTime), - typeof(TimeSpan) + typeof(TimeSpan), + typeof(DateTimeOffset) }; private static readonly ConcurrentDictionary cachedObjectMetadata = new ConcurrentDictionary(); diff --git a/src/DotVVM.Samples.Common/DotVVM.Samples.Common.csproj b/src/DotVVM.Samples.Common/DotVVM.Samples.Common.csproj index bca34a5368..3729bee60b 100644 --- a/src/DotVVM.Samples.Common/DotVVM.Samples.Common.csproj +++ b/src/DotVVM.Samples.Common/DotVVM.Samples.Common.csproj @@ -12,12 +12,6 @@ - - - - - - @@ -60,10 +54,15 @@ + + + + + diff --git a/src/DotVVM.Samples.Common/ViewModels/FeatureSamples/CommandActionFilter/CommandActionFilterViewModel.cs b/src/DotVVM.Samples.Common/ViewModels/FeatureSamples/CommandActionFilter/CommandActionFilterViewModel.cs new file mode 100644 index 0000000000..6cee36a895 --- /dev/null +++ b/src/DotVVM.Samples.Common/ViewModels/FeatureSamples/CommandActionFilter/CommandActionFilterViewModel.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using DotVVM.Framework.Hosting; +using DotVVM.Framework.Runtime.Filters; +using DotVVM.Framework.ViewModel; + +namespace DotVVM.Samples.Common.ViewModels.FeatureSamples.CommandActionFilter +{ + public class CommandActionFilterViewModel : DotvvmViewModelBase + { + public bool OnCommandExecuted { get; set; } + public bool OnCommandExecuting { get; set; } + + [TestActionFilter] + public void Method() + { + + } + } + + public class TestActionFilter : ActionFilterAttribute + { + protected override Task OnCommandExecutedAsync(IDotvvmRequestContext context, ActionInfo actionInfo, Exception exception) + { + ((CommandActionFilterViewModel)context.ViewModel).OnCommandExecuted = true; + return base.OnCommandExecutedAsync(context, actionInfo, exception); + } + + protected override Task OnCommandExecutingAsync(IDotvvmRequestContext context, ActionInfo actionInfo) + { + ((CommandActionFilterViewModel)context.ViewModel).OnCommandExecuting = true; + return base.OnCommandExecutingAsync(context, actionInfo); + } + } +} + diff --git a/src/DotVVM.Samples.Common/ViewModels/FeatureSamples/JavascriptTranslation/MathMethodTranslationViewModel.cs b/src/DotVVM.Samples.Common/ViewModels/FeatureSamples/JavascriptTranslation/MathMethodTranslationViewModel.cs new file mode 100644 index 0000000000..ecaa4c6883 --- /dev/null +++ b/src/DotVVM.Samples.Common/ViewModels/FeatureSamples/JavascriptTranslation/MathMethodTranslationViewModel.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using DotVVM.Framework.ViewModel; + +namespace DotVVM.Samples.Common.ViewModels.FeatureSamples.JavascriptTranslation +{ + public class MathMethodTranslationViewModel : DotvvmViewModelBase + { + public double DArg1 { get; set; } = 0.0; + public double DArg2 { get; set; } = 0.0; + + public int IArg1 { get; set; } = 0; + public int IArg2 { get; set; } = 0; + + public double Result { get; set; } = 0.0; + } +} + diff --git a/src/DotVVM.Samples.Common/ViewModels/FeatureSamples/LambdaExpressions/ClientSideFilteringViewModel.cs b/src/DotVVM.Samples.Common/ViewModels/FeatureSamples/LambdaExpressions/ClientSideFilteringViewModel.cs new file mode 100644 index 0000000000..4cad0ec728 --- /dev/null +++ b/src/DotVVM.Samples.Common/ViewModels/FeatureSamples/LambdaExpressions/ClientSideFilteringViewModel.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using DotVVM.Framework.ViewModel; + +namespace DotVVM.Samples.Common.ViewModels.FeatureSamples.LambdaExpressions +{ + public class ClientSideFilteringViewModel : DotvvmViewModelBase + { + public string Filter { get; set; } + + public List AllCategories { get; set; } = new List() { "Red", "Green", "Blue" }; + + public List SelectedCategories { get; set; } = new List(); + + public List Customers { get; } = new List() + { + new CustomerData() {Id = 1, Name = "Alice", Category = "Red"}, + new CustomerData() {Id = 2, Name = "Dean", Category = "Green"}, + new CustomerData() {Id = 3, Name = "Everett", Category = "Blue"}, + new CustomerData() {Id = 4, Name = "Jenny", Category = "Blue"}, + new CustomerData() {Id = 5, Name = "Carl", Category = "Green"}, + new CustomerData() {Id = 6, Name = "Karen", Category = "Red"}, + new CustomerData() {Id = 7, Name = "John", Category = "Red"}, + new CustomerData() {Id = 8, Name = "Johnny", Category = "Red"}, + new CustomerData() {Id = 9, Name = "Robert", Category = "Green"}, + new CustomerData() {Id = 10, Name = "Roger", Category = "Blue"} + }; + + + } + + public class CustomerData + { + public int Id { get; set; } + public string Name { get; set; } + public string Category { get; set; } + } +} + diff --git a/src/DotVVM.Samples.Common/ViewModels/FeatureSamples/LambdaExpressions/StaticCommandsViewModel.cs b/src/DotVVM.Samples.Common/ViewModels/FeatureSamples/LambdaExpressions/StaticCommandsViewModel.cs new file mode 100644 index 0000000000..7f790f1ef9 --- /dev/null +++ b/src/DotVVM.Samples.Common/ViewModels/FeatureSamples/LambdaExpressions/StaticCommandsViewModel.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using DotVVM.Framework.ViewModel; + +namespace DotVVM.Samples.Common.ViewModels.FeatureSamples.LambdaExpressions +{ + public class StaticCommandsViewModel : DotvvmViewModelBase + { + public List SourceCustomers { get; set; } = new List() + { + new CustomerData() {Id = 1, Name = "Alice", Category = Category.Red, RegisteredAt = DateTime.Parse("2018-06-02T03:20:45"), IsActive = true, FinishedTransactions = 11 }, + new CustomerData() {Id = 2, Name = "Dean", Category = Category.Green, RegisteredAt = DateTime.Parse("2018-06-02T20:45:29"), IsActive = false, FinishedTransactions = 3 }, + new CustomerData() {Id = 3, Name = "Everett", Category = Category.Blue, RegisteredAt = DateTime.Parse("2018-01-18T00:09:20"), IsActive = false, FinishedTransactions = 5 }, + new CustomerData() {Id = 4, Name = "Jenny", Category = Category.Blue, RegisteredAt = DateTime.Parse("2018-10-20T13:16:35"), IsActive = true, FinishedTransactions = 93 }, + new CustomerData() {Id = 5, Name = "Carl", Category = Category.Blue, RegisteredAt = DateTime.Parse("2019-05-29T16:47:25"), IsActive = true, FinishedTransactions = 3 }, + new CustomerData() {Id = 6, Name = "Karen", Category = Category.Red, RegisteredAt = DateTime.Parse("2019-02-15T11:37:15"), IsActive = false, FinishedTransactions = 121 }, + new CustomerData() {Id = 7, Name = "John", Category = Category.Red, RegisteredAt = DateTime.Parse("2020-05-28T20:57:41"), IsActive = true, FinishedTransactions = 12 }, + new CustomerData() {Id = 8, Name = "Johnny", Category = Category.Red, RegisteredAt = DateTime.Parse("2018-01-21T07:03:41"), IsActive = false, FinishedTransactions = 15 }, + new CustomerData() {Id = 9, Name = "Robert", Category = Category.Green, RegisteredAt = DateTime.Parse("2019-05-22T18:58:33"), IsActive = true, FinishedTransactions = 19 }, + new CustomerData() {Id = 10, Name = "Roger", Category = Category.Blue, RegisteredAt = DateTime.Parse("2020-12-01T06:57:57"), IsActive = false, FinishedTransactions = 27 } + }; + + public List FilteredCustomers { get; set; } + + public CustomerData SingleCustomer { get; set; } + + public string OperationResult { get; set; } + + public class CustomerData + { + public int Id { get; set; } + public string Name { get; set; } + public Category Category { get; set; } + public bool IsActive { get; set; } + public DateTime RegisteredAt { get; set; } + public int FinishedTransactions { get; set; } + } + + public enum Category + { + Red, + Green, + Blue + } + } +} + diff --git a/src/DotVVM.Samples.Common/ViewModels/FeatureSamples/Serialization/EnumSerializationCoercionViewModel.cs b/src/DotVVM.Samples.Common/ViewModels/FeatureSamples/Serialization/EnumSerializationCoercionViewModel.cs new file mode 100644 index 0000000000..d5076fe326 --- /dev/null +++ b/src/DotVVM.Samples.Common/ViewModels/FeatureSamples/Serialization/EnumSerializationCoercionViewModel.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using DotVVM.Framework.ViewModel; + +namespace DotVVM.Samples.Common.ViewModels.FeatureSamples.Serialization +{ + public class EnumSerializationCoercionViewModel : DotvvmViewModelBase + { + public MyEnum EnumWithString { get; set; } = MyEnum.One; + + public MyEnum EnumWithoutString { get; set; } + + public void ChangeValues() + { + EnumWithString = (MyEnum)(-1); + EnumWithoutString = MyEnum.Two; + } + } + + public enum MyEnum + { + One = 1, + Two = 2 + } +} + diff --git a/src/DotVVM.Samples.Common/ViewModels/FeatureSamples/Serialization/SerializationDateTimeOffsetViewModel.cs b/src/DotVVM.Samples.Common/ViewModels/FeatureSamples/Serialization/SerializationDateTimeOffsetViewModel.cs new file mode 100644 index 0000000000..732ab84d02 --- /dev/null +++ b/src/DotVVM.Samples.Common/ViewModels/FeatureSamples/Serialization/SerializationDateTimeOffsetViewModel.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using DotVVM.Framework.ViewModel; + +namespace DotVVM.Samples.Common.ViewModels.FeatureSamples.Serialization +{ + public class SerializationDateTimeOffsetViewModel : DotvvmViewModelBase + { + + public DateTimeOffset Offset { get; set; } = new DateTimeOffset(2000, 1, 2, 3, 4, 5, 0, TimeSpan.Zero); + + public DateTimeOffset? NullableOffset { get; set; } + + + public void AddHour() + { + Offset = Offset.AddHours(1); + NullableOffset = NullableOffset?.AddHours(1); + } + + public void SetNullable() + { + NullableOffset = new DateTimeOffset(2000, 1, 2, 3, 4, 5, 0, TimeSpan.FromHours(2)); + } + + } +} + diff --git a/src/DotVVM.Samples.Common/ViewModels/FeatureSamples/Serialization/TimeSpanViewModel.cs b/src/DotVVM.Samples.Common/ViewModels/FeatureSamples/Serialization/TimeSpanViewModel.cs new file mode 100644 index 0000000000..e908454304 --- /dev/null +++ b/src/DotVVM.Samples.Common/ViewModels/FeatureSamples/Serialization/TimeSpanViewModel.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using DotVVM.Framework.ViewModel; + +namespace DotVVM.Samples.Common.ViewModels.FeatureSamples.Serialization +{ + public class TimeSpanViewModel : DotvvmViewModelBase + { + + public TimeSpan Time { get; set; } + + public TimeSpan? NullableTime { get; set; } + + public void AddHour() + { + Time = Time.Add(TimeSpan.FromHours(1)); + NullableTime = NullableTime?.Add(TimeSpan.FromHours(1)); + } + + + public override Task PreRender() + { + Context.ResourceManager.AddCurrentCultureGlobalizationResource(); + + return base.PreRender(); + } + } +} + diff --git a/src/DotVVM.Samples.Common/Views/FeatureSamples/CommandActionFilter/CommandActionFilter.dothtml b/src/DotVVM.Samples.Common/Views/FeatureSamples/CommandActionFilter/CommandActionFilter.dothtml new file mode 100644 index 0000000000..6c2505444d --- /dev/null +++ b/src/DotVVM.Samples.Common/Views/FeatureSamples/CommandActionFilter/CommandActionFilter.dothtml @@ -0,0 +1,16 @@ +@viewModel DotVVM.Samples.Common.ViewModels.FeatureSamples.CommandActionFilter.CommandActionFilterViewModel + + + + + + + + + + +
+ Callbacks received: {{value: (OnCommandExecuting && OnCommandExecuted) ? "SUCCESS" : "--"}}
+ + + diff --git a/src/DotVVM.Samples.Common/Views/FeatureSamples/JavascriptTranslation/MathMethodTranslation.dothtml b/src/DotVVM.Samples.Common/Views/FeatureSamples/JavascriptTranslation/MathMethodTranslation.dothtml new file mode 100644 index 0000000000..0db094e770 --- /dev/null +++ b/src/DotVVM.Samples.Common/Views/FeatureSamples/JavascriptTranslation/MathMethodTranslation.dothtml @@ -0,0 +1,65 @@ +@viewModel DotVVM.Samples.Common.ViewModels.FeatureSamples.JavascriptTranslation.MathMethodTranslationViewModel, DotVVM.Samples.Common + + + + + + + + + + +
+

Input

+ Arg1(int): + Arg2(int): +
+ Arg1(double): + Arg2(double): +
+ +
+

Operations

+

Rounding

+ + + + + +
+ +

Goniometric operations

+ + + + + + + + +
+ +

Basic functions

+ + + + + + + + + + + + + +
+ +
+

Result

+ +
+ + + + diff --git a/src/DotVVM.Samples.Common/Views/FeatureSamples/LambdaExpressions/ClientSideFiltering.dothtml b/src/DotVVM.Samples.Common/Views/FeatureSamples/LambdaExpressions/ClientSideFiltering.dothtml new file mode 100644 index 0000000000..7a3f6db142 --- /dev/null +++ b/src/DotVVM.Samples.Common/Views/FeatureSamples/LambdaExpressions/ClientSideFiltering.dothtml @@ -0,0 +1,38 @@ +@viewModel DotVVM.Samples.Common.ViewModels.FeatureSamples.LambdaExpressions.ClientSideFilteringViewModel, DotVVM.Samples.Common + + + + + + + + + + +

Client-side filtering

+ +

+ Search: + +

+

+ Categories: + + +     + +

+ + + + + + + + + + + diff --git a/src/DotVVM.Samples.Common/Views/FeatureSamples/LambdaExpressions/StaticCommands.dothtml b/src/DotVVM.Samples.Common/Views/FeatureSamples/LambdaExpressions/StaticCommands.dothtml new file mode 100644 index 0000000000..9fa4ff99aa --- /dev/null +++ b/src/DotVVM.Samples.Common/Views/FeatureSamples/LambdaExpressions/StaticCommands.dothtml @@ -0,0 +1,96 @@ +@viewModel DotVVM.Samples.Common.ViewModels.FeatureSamples.LambdaExpressions.StaticCommandsViewModel, DotVVM.Samples.Common +@import DotVVM.Samples.Common.ViewModels.FeatureSamples.LambdaExpressions + + + + + + + Hello from DotVVM! + + + +

Client side filtering using static commands

+ +
+

Source collection

+ + + + + + + + +
+ +
+

Operations

+ + + + + + + + + + + + +
+ + + + +
+ + + +
+ + + + +
+ + + + + + +
+ +
+

Filtered collection

+ + + + + + + + +
+ +
+

Other operations result

+ +
+ + + + + diff --git a/src/DotVVM.Samples.Common/Views/FeatureSamples/Serialization/EnumSerializationCoercion.dothtml b/src/DotVVM.Samples.Common/Views/FeatureSamples/Serialization/EnumSerializationCoercion.dothtml new file mode 100644 index 0000000000..b608f2b70c --- /dev/null +++ b/src/DotVVM.Samples.Common/Views/FeatureSamples/Serialization/EnumSerializationCoercion.dothtml @@ -0,0 +1,23 @@ +@viewModel DotVVM.Samples.Common.ViewModels.FeatureSamples.Serialization.EnumSerializationCoercionViewModel, DotVVM.Samples.Common + + + + + + + + + + +

Enum serialization test

+ +

Enum value with string equivalent: {{value: EnumWithString}}

+ +

Enum value without string equivalent: {{value: EnumWithoutString}}

+ + + + + + + diff --git a/src/DotVVM.Samples.Common/Views/FeatureSamples/Serialization/SerializationDateTimeOffset.dothtml b/src/DotVVM.Samples.Common/Views/FeatureSamples/Serialization/SerializationDateTimeOffset.dothtml new file mode 100644 index 0000000000..fd62c89cbb --- /dev/null +++ b/src/DotVVM.Samples.Common/Views/FeatureSamples/Serialization/SerializationDateTimeOffset.dothtml @@ -0,0 +1,22 @@ +@viewModel DotVVM.Samples.Common.ViewModels.FeatureSamples.Serialization.SerializationDateTimeOffsetViewModel, DotVVM.Samples.Common + + + + + + + + + + +

DateTimeOffset:

+ +

Nullable DateTimeOffset:

+ + + + + + + + diff --git a/src/DotVVM.Samples.Common/Views/FeatureSamples/Serialization/TimeSpan.dothtml b/src/DotVVM.Samples.Common/Views/FeatureSamples/Serialization/TimeSpan.dothtml new file mode 100644 index 0000000000..3b46a3d71f --- /dev/null +++ b/src/DotVVM.Samples.Common/Views/FeatureSamples/Serialization/TimeSpan.dothtml @@ -0,0 +1,28 @@ +@viewModel DotVVM.Samples.Common.ViewModels.FeatureSamples.Serialization.TimeSpanViewModel, DotVVM.Samples.Common + + + + + + + + + + +

TimeSpan serialization

+ +

+ TimeSpan: + {{value: Time}} +

+

+ Nullable TimeSpan: + {{value: NullableTime}} +

+ + + + + + + diff --git a/src/DotVVM.Samples.Tests/Complex/AuthTests.cs b/src/DotVVM.Samples.Tests/Complex/AuthTests.cs index 6790ceee83..90eb3464a2 100644 --- a/src/DotVVM.Samples.Tests/Complex/AuthTests.cs +++ b/src/DotVVM.Samples.Tests/Complex/AuthTests.cs @@ -21,14 +21,12 @@ public void Complex_Auth_Login() browser.NavigateToUrl(SamplesRouteUrls.ComplexSamples_Auth_Login); browser.SendKeys("input[type=text]", "user"); - browser.First("input[type=button]").Click().Wait(500); + browser.First("input[type=button]").Click(); browser.Refresh(); - browser.Wait(2000); browser.Last("a").Click(); - browser.Wait(2000); browser.SendKeys("input[type=text]", "message"); - browser.First("input[type=button]").Click().Wait(500); + browser.First("input[type=button]").Click(); AssertUI.InnerText(browser.ElementAt("h1", 1), s => @@ -41,11 +39,11 @@ public void Complex_Auth_Login() browser.ClearElementsContent("input[type=text]"); browser.SendKeys("input[type=text]", "ADMIN"); browser.First("input[type=checkbox]").Click(); - browser.First("input[type=button]").Click().Wait(500); + browser.First("input[type=button]").Click(); browser.Last("a").Click(); browser.SendKeys("input[type=text]", "message"); - browser.First("input[type=button]").Click().Wait(500); + browser.First("input[type=button]").Click(); AssertUI.InnerText(browser.First("span"), s => s.Contains("ADMIN: message"), "User can't send message"); }); diff --git a/src/DotVVM.Samples.Tests/Complex/ButtonInMarkupControlTests.cs b/src/DotVVM.Samples.Tests/Complex/ButtonInMarkupControlTests.cs index 197214dac5..11fff88a47 100644 --- a/src/DotVVM.Samples.Tests/Complex/ButtonInMarkupControlTests.cs +++ b/src/DotVVM.Samples.Tests/Complex/ButtonInMarkupControlTests.cs @@ -19,9 +19,9 @@ public void Complex_ButtonInMarkupControl_Enabled() var enabled = browser.Single("enabled", SelectByDataUi); AssertUI.TextEquals(enabled, "false"); browser.Single("btn-off", SelectByDataUi).Click(); - browser.WaitFor(() => AssertUI.TextEquals(enabled, "true"), 1000); + AssertUI.TextEquals(enabled, "true"); browser.Single("btn-on", SelectByDataUi).Click(); - browser.WaitFor(() => AssertUI.TextEquals(enabled, "false"), 1000); + AssertUI.TextEquals(enabled, "false"); }); } diff --git a/src/DotVVM.Samples.Tests/Complex/CascadeSelectorsTests.cs b/src/DotVVM.Samples.Tests/Complex/CascadeSelectorsTests.cs index cc237fabfa..0243bad26b 100644 --- a/src/DotVVM.Samples.Tests/Complex/CascadeSelectorsTests.cs +++ b/src/DotVVM.Samples.Tests/Complex/CascadeSelectorsTests.cs @@ -1,6 +1,7 @@ using DotVVM.Samples.Tests.Base; using DotVVM.Testing.Abstractions; using Riganti.Selenium.Core; +using Riganti.Selenium.DotVVM; using Xunit; namespace DotVVM.Samples.Tests.Complex @@ -29,22 +30,26 @@ public void Complex_CascadeSelectors_TripleComboBoxes() RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.ComplexSamples_CascadeSelectors_TripleComboBoxes); - browser.ElementAt("input[type=button]", 0).Click().Wait(); + browser.ElementAt("input[type=button]", 0).Click(); + browser.WaitForPostback(); AssertUI.InnerTextEquals(browser.ElementAt(".active", 0), "North America: 1"); AssertUI.InnerTextEquals(browser.ElementAt(".active", 1), "USA: 11"); AssertUI.InnerTextEquals(browser.ElementAt(".active", 2), "New York: 111"); - browser.ElementAt("input[type=button]", 2).Click().Wait(); + browser.ElementAt("input[type=button]", 2).Click(); + browser.WaitForPostback(); AssertUI.InnerTextEquals(browser.ElementAt(".active", 0), "North America: 1"); AssertUI.InnerTextEquals(browser.ElementAt(".active", 1), "Canada: 12"); AssertUI.InnerTextEquals(browser.ElementAt(".active", 2), "Toronto: 121"); - browser.ElementAt("input[type=button]", 5).Click().Wait(); + browser.ElementAt("input[type=button]", 5).Click(); + browser.WaitForPostback(); AssertUI.InnerTextEquals(browser.ElementAt(".active", 0), "Europe: 2"); AssertUI.InnerTextEquals(browser.ElementAt(".active", 1), "Germany: 21"); AssertUI.InnerTextEquals(browser.ElementAt(".active", 2), "Munich: 212"); - browser.ElementAt("input[type=button]", 8).Click().Wait(); + browser.ElementAt("input[type=button]", 8).Click(); + browser.WaitForPostback(); AssertUI.InnerTextEquals(browser.ElementAt(".active", 0), "Asia: 3"); AssertUI.InnerTextEquals(browser.ElementAt(".active", 1), "China: 31"); AssertUI.InnerTextEquals(browser.ElementAt(".active", 2), "Beijing: 311"); @@ -59,21 +64,25 @@ private void Complex_CascadeSelectorsBase(string url) // select city browser.First("select").Select(1); - browser.First("input[type=button]").Click().Wait(); + browser.First("input[type=button]").Click(); + browser.WaitForPostback(); // select hotel browser.Last("select").Select(1); - browser.Last("input[type=button]").Click().Wait(); + browser.Last("input[type=button]").Click(); + browser.WaitForPostback(); AssertUI.InnerTextEquals(browser.First("h2"), "Hotel Seattle #2"); // select city browser.First("select").Select(0); - browser.First("input[type=button]").Click().Wait(); + browser.First("input[type=button]").Click(); + browser.WaitForPostback(); // select hotel browser.Last("select").Select(0); - browser.Last("input[type=button]").Click().Wait(); + browser.Last("input[type=button]").Click(); + browser.WaitForPostback(); AssertUI.InnerTextEquals(browser.First("h2"), "Hotel Prague #1"); }); diff --git a/src/DotVVM.Samples.Tests/Complex/ChangedEventTests.cs b/src/DotVVM.Samples.Tests/Complex/ChangedEventTests.cs index d1f2167a66..e039651ea2 100644 --- a/src/DotVVM.Samples.Tests/Complex/ChangedEventTests.cs +++ b/src/DotVVM.Samples.Tests/Complex/ChangedEventTests.cs @@ -30,14 +30,10 @@ public void Complex_ChangedEvent_ChangedEvent() AssertUI.InnerTextEquals(totalChanges, "0"); var firstTextbox = browser.First("*[data-id='first-textbox']"); - browser.WaitFor(() => { - AssertUI.InnerText(firstTextbox, s => s.Contains("Valuetes")); - }, 4000, 100); + AssertUI.InnerText(firstTextbox, s => s.Contains("Valuetes")); new Actions(browser.Driver).SendKeys(Keys.Enter).SendKeys(Keys.Tab).Perform(); - browser.WaitFor(() => { - AssertUI.InnerTextEquals(firstTextbox, "Valuetest"); - }, 4000, 100); + AssertUI.InnerTextEquals(firstTextbox, "Valuetest"); AssertUI.InnerTextEquals(totalChanges, "1"); // second textbox @@ -46,16 +42,12 @@ public void Complex_ChangedEvent_ChangedEvent() textBox2.SetFocus(); new Actions(browser.Driver).SendKeys("test").Perform(); - browser.WaitFor(() => { - AssertUI.InnerTextEquals(totalChanges, "1"); - }, 4000, 100); + AssertUI.InnerTextEquals(totalChanges, "1"); var secondTextbox = browser.First("*[data-id='second-textbox']"); AssertUI.InnerTextEquals(secondTextbox, "Value"); new Actions(browser.Driver).SendKeys(Keys.Enter).SendKeys(Keys.Tab).Perform(); - browser.WaitFor(() => { - AssertUI.InnerTextEquals(secondTextbox, "Valuetest"); - }, 4000, 100); + AssertUI.InnerTextEquals(secondTextbox, "Valuetest"); AssertUI.InnerTextEquals(totalChanges, "2"); @@ -70,72 +62,47 @@ public void Complex_ChangedEvent_ChangedEvent() new Actions(browser.Driver).SendKeys("c").Perform(); browser.Wait(100); new Actions(browser.Driver).SendKeys(Keys.Backspace).Perform(); - browser.Wait(100); - browser.WaitFor(() => { - AssertUI.InnerTextEquals(totalChanges, "6"); - }, 4000, 100); + AssertUI.InnerTextEquals(totalChanges, "6"); var thirdTextBox = browser.First("*[data-id='third-textbox']"); AssertUI.InnerTextEquals(thirdTextBox, "ab"); new Actions(browser.Driver).SendKeys(Keys.Enter).SendKeys(Keys.Tab).Perform(); - browser.WaitFor(() => { - AssertUI.InnerTextEquals(thirdTextBox, "ab"); - }, 4000, 100); + AssertUI.InnerTextEquals(thirdTextBox, "ab"); AssertUI.InnerTextEquals(totalChanges, "6"); // click on checkbox browser.Click("input[type=checkbox]"); - browser.WaitFor(() => { - AssertUI.InnerTextEquals(totalChanges, "7"); - }, 4000, 100); + AssertUI.InnerTextEquals(totalChanges, "7"); browser.Click("input[type=checkbox]"); - browser.WaitFor(() => { - AssertUI.InnerTextEquals(totalChanges, "8"); - }, 4000, 100); + AssertUI.InnerTextEquals(totalChanges, "8"); // click on radio button browser.ElementAt("input[type=radio]", 0).Click(); - browser.WaitFor(() => { - AssertUI.InnerTextEquals(totalChanges, "9"); - }, 4000, 100); + AssertUI.InnerTextEquals(totalChanges, "9"); browser.ElementAt("input[type=radio]", 1).Click(); - browser.WaitFor(() => { - AssertUI.InnerTextEquals(totalChanges, "10"); - }, 4000, 100); + AssertUI.InnerTextEquals(totalChanges, "10"); browser.ElementAt("input[type=radio]", 2).Click(); - browser.WaitFor(() => { - AssertUI.InnerTextEquals(totalChanges, "11"); - }, 4000, 100); + AssertUI.InnerTextEquals(totalChanges, "11"); browser.ElementAt("input[type=radio]", 3).Click(); - browser.WaitFor(() => { - AssertUI.InnerTextEquals(totalChanges, "12"); - }, 4000, 100); + AssertUI.InnerTextEquals(totalChanges, "12"); browser.ElementAt("input[type=radio]", 4).Click(); - browser.WaitFor(() => { - AssertUI.InnerTextEquals(totalChanges, "13"); - }, 4000, 100); + AssertUI.InnerTextEquals(totalChanges, "13"); // combo box browser.First("select").Select(1); - browser.WaitFor(() => { - AssertUI.InnerTextEquals(totalChanges, "14"); - }, 4000, 100); + AssertUI.InnerTextEquals(totalChanges, "14"); browser.First("select").Select(2); - browser.WaitFor(() => { - AssertUI.InnerTextEquals(totalChanges, "15"); - }, 4000, 100); + AssertUI.InnerTextEquals(totalChanges, "15"); browser.First("select").Select(0); - browser.WaitFor(() => { - AssertUI.InnerTextEquals(totalChanges, "16"); - }, 4000, 100); + AssertUI.InnerTextEquals(totalChanges, "16"); }); } diff --git a/src/DotVVM.Samples.Tests/Complex/ClassBindingsTests.cs b/src/DotVVM.Samples.Tests/Complex/ClassBindingsTests.cs index 7ea1b5b709..36eeb2b043 100644 --- a/src/DotVVM.Samples.Tests/Complex/ClassBindingsTests.cs +++ b/src/DotVVM.Samples.Tests/Complex/ClassBindingsTests.cs @@ -26,15 +26,13 @@ public void Complex_ClassBindings_AttributeAndPropertyGroup() var textBox = browser.Single("classes", SelectByDataUi); textBox.SendKeys("orange"); textBox.SendKeys(Keys.Tab); - browser.WaitFor(() => AssertUI.HasClass(target, "orange"), 2000); + AssertUI.HasClass(target, "orange"); browser.Single("inverted", SelectByDataUi).Click(); browser.Single("border", SelectByDataUi).Click(); - browser.WaitFor(() => { - AssertUI.HasClass(target, "orange"); - AssertUI.HasClass(target, "inverted"); - AssertUI.HasClass(target, "border"); - }, 2000); + AssertUI.HasClass(target, "orange"); + AssertUI.HasClass(target, "inverted"); + AssertUI.HasClass(target, "border"); }); } diff --git a/src/DotVVM.Samples.Tests/Complex/DataTemplateTests.cs b/src/DotVVM.Samples.Tests/Complex/DataTemplateTests.cs index ee1784eafc..650c5c77ed 100644 --- a/src/DotVVM.Samples.Tests/Complex/DataTemplateTests.cs +++ b/src/DotVVM.Samples.Tests/Complex/DataTemplateTests.cs @@ -13,7 +13,6 @@ public void Complex_EmptyDataTemplate_RepeaterGridView() { RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.ComplexSamples_EmptyDataTemplate_RepeaterGridView); - browser.Wait(); void isDisplayed(string id) => AssertUI.IsDisplayed(browser, "#" + id); void isHidden(string id) => AssertUI.IsNotDisplayed(browser, "#" + id); void isNotPresent(string id) => browser.FindElements("#" + id + " > *").ThrowIfDifferentCountThan(0); diff --git a/src/DotVVM.Samples.Tests/Complex/InvoiceCalculatorTests.cs b/src/DotVVM.Samples.Tests/Complex/InvoiceCalculatorTests.cs index 0749d81ed5..4c6b210553 100644 --- a/src/DotVVM.Samples.Tests/Complex/InvoiceCalculatorTests.cs +++ b/src/DotVVM.Samples.Tests/Complex/InvoiceCalculatorTests.cs @@ -20,16 +20,13 @@ public void Complex_InvoiceCalculator_InvoiceCalculator() // add lines addButton.Click(); - browser.Wait(); addButton.Click(); - browser.Wait(); addButton.Click(); - browser.Wait(); // fill second line var cells = table.ElementAt("tr", 2).FindElements("td"); cells.ElementAt(0).First("input").Clear().SendKeys("111"); - cells.ElementAt(1).First("select").Select(1).Wait(); + cells.ElementAt(1).First("select").Select(1); cells.ElementAt(2).First("input").Clear().SendKeys("Bread"); cells.ElementAt(3).First("input").Clear().SendKeys("12"); cells.ElementAt(4).First("input").Clear().SendKeys("10"); @@ -37,7 +34,7 @@ public void Complex_InvoiceCalculator_InvoiceCalculator() // fill third line cells = table.ElementAt("tr", 3).FindElements("td"); cells.ElementAt(0).First("input").Clear().SendKeys("222"); - cells.ElementAt(1).First("select").Select(2).Wait(); + cells.ElementAt(1).First("select").Select(2); cells.ElementAt(2).First("input").Clear().SendKeys("Ham"); cells.ElementAt(3).First("input").Clear().SendKeys("1"); cells.ElementAt(4).First("input").Clear().SendKeys("5"); @@ -45,7 +42,7 @@ public void Complex_InvoiceCalculator_InvoiceCalculator() // fill fourth line cells = table.ElementAt("tr", 4).FindElements("td"); cells.ElementAt(0).First("input").Clear().SendKeys("333"); - cells.ElementAt(1).First("select").Select(3).Wait(); + cells.ElementAt(1).First("select").Select(3); cells.ElementAt(2).First("input").Clear().SendKeys("Cheese"); cells.ElementAt(3).First("input").Clear().SendKeys("10"); cells.ElementAt(4).First("input").Clear().SendKeys("15"); @@ -58,13 +55,13 @@ public void Complex_InvoiceCalculator_InvoiceCalculator() AssertUI.InnerTextEquals(table.ElementAt("tr", 4).ElementAt("td", 5), "180"); // recalculate - recalculateButton.Click().Wait(); + recalculateButton.Click(); // verify total price AssertUI.InnerTextEquals(table.Last("tr").ElementAt("th", 1), "407.5"); // remove second line - table.ElementAt("tr", 2).Last("td").First("a").Click().Wait(); + table.ElementAt("tr", 2).Last("td").First("a").Click(); // verify total price AssertUI.InnerTextEquals(table.Last("tr").ElementAt("th", 1), "281.5"); diff --git a/src/DotVVM.Samples.Tests/Complex/NestedComboBoxTests.cs b/src/DotVVM.Samples.Tests/Complex/NestedComboBoxTests.cs index ed3a8bb2ef..2e9c693bd4 100644 --- a/src/DotVVM.Samples.Tests/Complex/NestedComboBoxTests.cs +++ b/src/DotVVM.Samples.Tests/Complex/NestedComboBoxTests.cs @@ -30,10 +30,10 @@ public void Complex_NestedComboBox_HeavilyNested() var combobox = browser.Single("combobox", SelectByDataUi); combobox.Select(1); - browser.WaitFor(() => AssertUI.TextEquals(selectedValue, "2"), 1000); + AssertUI.TextEquals(selectedValue, "2"); combobox.Select(0); - browser.WaitFor(() => AssertUI.TextEquals(selectedValue, ""), 1000); + AssertUI.TextEquals(selectedValue, ""); }); } } diff --git a/src/DotVVM.Samples.Tests/Complex/RedirectAndUrlTests.cs b/src/DotVVM.Samples.Tests/Complex/RedirectAndUrlTests.cs index bad931678a..3c829030bc 100644 --- a/src/DotVVM.Samples.Tests/Complex/RedirectAndUrlTests.cs +++ b/src/DotVVM.Samples.Tests/Complex/RedirectAndUrlTests.cs @@ -3,6 +3,7 @@ using Riganti.Selenium.Core; using Riganti.Selenium.Core.Abstractions; using Riganti.Selenium.Core.Abstractions.Exceptions; +using Riganti.Selenium.DotVVM; using Xunit; using Xunit.Abstractions; @@ -21,14 +22,12 @@ public void Complex_RedirectAndUrl_PostbackInteruption() //Postback with no redirect sets message browser.NavigateToUrl(SamplesRouteUrls.ComplexSamples_RedirectAndUrl_ScrollingPage); browser.First("a[data-ui=test-link]").Click(); - browser.Wait(200); AssertUI.InnerTextEquals(browser.First("span[data-ui='message1']"), "TestMessage"); //used RedirectToUrl to redirect to page with Id, however the redirect made page reload and discarted the viewmodel //therefore message1 should be blank //view should scroll to #paragraph2 browser.First("a[data-ui='go-to-2-url-link']").Click(); - browser.Wait(1200); // message 2 should be scrolled to message 1 should not, both should be blank var message2element = browser.First("span[data-ui='message2']"); message2element.IsDisplayed(); @@ -53,12 +52,10 @@ public void Complex_RedirectAndUrl_ScrollingPage() //Postback with no redirect sets message to 'TestMessage' browser.NavigateToUrl(SamplesRouteUrls.ComplexSamples_RedirectAndUrl_ScrollingPage); browser.First("a[data-ui=test-link]").Click(); - browser.Wait(200); AssertUI.InnerText(browser.First("span[data-ui='message1']"), s => s.Equals("TestMessage")); //Postback should run and view should scroll, page should not reload therefore messeges remain. browser.First("a[data-ui='go-to-2-link']").Click(); - browser.Wait(200); var message2element = browser.First("span[data-ui='message2']"); var message1element = browser.First("span[data-ui='message1']"); @@ -75,7 +72,6 @@ public void Complex_RedirectAndUrl_ScrollingPage() //basically the same just clicking on link to do postback and scroll back to paragraph1 after browser.First("a[data-ui='go-to-1-link']").Click(); - browser.Wait(200); // message 2 should be scrolled to message 1 should not, both should be blank message2element.IsDisplayed(); @@ -95,6 +91,7 @@ public void Complex_RedirectAndUrl_ScrollingPage() message2element.CheckIfIsElementInView(); goTo1Link.Click(); + browser.WaitForPostback(); message1element.CheckIfIsElementInView(); message2element.CheckIfIsElementNotInView(); }); diff --git a/src/DotVVM.Samples.Tests/Complex/SPAErrorReportingTests.cs b/src/DotVVM.Samples.Tests/Complex/SPAErrorReportingTests.cs index d61bf2e0f2..ddb59c9308 100644 --- a/src/DotVVM.Samples.Tests/Complex/SPAErrorReportingTests.cs +++ b/src/DotVVM.Samples.Tests/Complex/SPAErrorReportingTests.cs @@ -6,6 +6,7 @@ using OpenQA.Selenium.Chrome; using Riganti.Selenium.Core; using Riganti.Selenium.Core.Abstractions.Attributes; +using Riganti.Selenium.DotVVM; using Xunit; using Xunit.Abstractions; @@ -45,6 +46,7 @@ void SetOfflineMode(bool offline) // go to Test page and verify the success browser.ElementAt("a", 1).Click(); + browser.WaitForPostback(); AssertUI.TextEquals(browser.Single("h2"), "Test"); SetOfflineMode(true); diff --git a/src/DotVVM.Samples.Tests/Complex/SPARedirectTests.cs b/src/DotVVM.Samples.Tests/Complex/SPARedirectTests.cs index 2a5c8827dc..c597360bea 100644 --- a/src/DotVVM.Samples.Tests/Complex/SPARedirectTests.cs +++ b/src/DotVVM.Samples.Tests/Complex/SPARedirectTests.cs @@ -16,7 +16,6 @@ public void Complex_SPARedirect_RedirectToLoginPage() { RunInAllBrowsers(browser => { browser.NavigateToUrl("ComplexSamples/SPARedirect"); - browser.Wait(1000); //check url AssertUI.Url(browser, s => s.Contains("/ComplexSamples/SPARedirect/login?ReturnUrl=%2FComplexSamples%2FSPARedirect")); @@ -24,28 +23,28 @@ public void Complex_SPARedirect_RedirectToLoginPage() // login to the app IElementWrapper getLoginElement() => browser.First("input[type=button]"); AssertUI.Attribute(getLoginElement(), "value", "Login"); - getLoginElement().Click().Wait(1000); + getLoginElement().Click(); //check url AssertUI.Url(browser, s => s.Contains("ComplexSamples/SPARedirect")); // sign out AssertUI.Attribute(getLoginElement(), "value", "Sign Out"); - getLoginElement().Click().Wait(1000); + getLoginElement().Click(); //check url AssertUI.Url(browser, s => s.Contains("/ComplexSamples/SPARedirect/login?ReturnUrl=%2FComplexSamples%2FSPARedirect")); // login to the app AssertUI.Attribute(getLoginElement(), "value", "Login"); - getLoginElement().Click().Wait(1000); + getLoginElement().Click(); //check url AssertUI.Url(browser, s => s.Contains("ComplexSamples/SPARedirect")); // sign out AssertUI.Attribute(getLoginElement(), "value", "Sign Out"); - getLoginElement().Click().Wait(1000); + getLoginElement().Click(); }); } diff --git a/src/DotVVM.Samples.Tests/Complex/SPATests.cs b/src/DotVVM.Samples.Tests/Complex/SPATests.cs index c2145c05fe..917404e4dc 100644 --- a/src/DotVVM.Samples.Tests/Complex/SPATests.cs +++ b/src/DotVVM.Samples.Tests/Complex/SPATests.cs @@ -1,6 +1,7 @@ using DotVVM.Samples.Tests.Base; using DotVVM.Testing.Abstractions; using Riganti.Selenium.Core; +using Riganti.Selenium.DotVVM; using Xunit; using Xunit.Abstractions; @@ -15,14 +16,12 @@ public void Complex_SPA_NavigationAndBackButtons() { RunInAllBrowsers(browser => { browser.NavigateToUrl("/"); - browser.Wait(1000); browser.NavigateToUrl(SamplesRouteUrls.ComplexSamples_SPA_default); - browser.Wait(1000); // go to test page AssertUI.TextEquals(browser.Single("h2"), "Default"); - browser.ElementAt("a", 1).Click().Wait(); + browser.ElementAt("a", 1).Click(); // check url and page contents AssertUI.TextEquals(browser.Single("h2"), "Test"); @@ -30,7 +29,6 @@ public void Complex_SPA_NavigationAndBackButtons() // use the back button browser.NavigateBack(); - browser.Wait(1000); // check url and page contents AssertUI.TextEquals(browser.Single("h2"), "Default"); @@ -38,11 +36,9 @@ public void Complex_SPA_NavigationAndBackButtons() // exit SPA using the back button browser.NavigateBack(); - browser.Wait(1000); // reenter SPA browser.NavigateForward(); - browser.Wait(1000); // check url and page contents AssertUI.TextEquals(browser.Single("h2"), "Default"); @@ -50,14 +46,13 @@ public void Complex_SPA_NavigationAndBackButtons() // go forward to the test page browser.NavigateForward(); - browser.Wait(1000); // check url and page contents AssertUI.TextEquals(browser.Single("h2"), "Test"); AssertUI.Url(browser, s => s.Contains("ComplexSamples/SPA/test")); // open the default page - browser.ElementAt("a", 0).Click().Wait(); + browser.ElementAt("a", 0).Click(); // check url and page contents AssertUI.TextEquals(browser.Single("h2"), "Default"); @@ -65,7 +60,6 @@ public void Complex_SPA_NavigationAndBackButtons() // go back to the test page browser.NavigateBack(); - browser.Wait(1000); // check url and page contents AssertUI.TextEquals(browser.Single("h2"), "Test"); @@ -73,7 +67,6 @@ public void Complex_SPA_NavigationAndBackButtons() // go back to the default page browser.NavigateBack(); - browser.Wait(1000); // check url and page contents AssertUI.TextEquals(browser.Single("h2"), "Default"); @@ -88,25 +81,21 @@ public void Complex_SPA_ValidationAndNavigation() { RunInAllBrowsers(browser => { browser.NavigateToUrl("/"); - browser.Wait(1000); browser.NavigateToUrl(SamplesRouteUrls.ComplexSamples_SPA_test); - browser.Wait(1000); // click to generate validation error browser.Single("input[type=button]").Click(); // check if validation error is displayed - browser.Wait(500); AssertUI.InnerTextEquals(browser.Single("span[data-ui='sample-text']"), string.Empty); // go to default page - browser.ElementAt("a", 0).Click().Wait(); - browser.Wait(1000); + browser.ElementAt("a", 0).Click(); // click to check if validation error disapeared browser.Single("input[type=button]").Click(); - browser.Wait(500); + browser.WaitForPostback(); AssertUI.InnerTextEquals(browser.Single("span[data-ui='sample-text']"), "Sample Text"); }); } diff --git a/src/DotVVM.Samples.Tests/Complex/SPAViewModelReapplicationTests.cs b/src/DotVVM.Samples.Tests/Complex/SPAViewModelReapplicationTests.cs index 0d42c9a3c2..2814112a61 100644 --- a/src/DotVVM.Samples.Tests/Complex/SPAViewModelReapplicationTests.cs +++ b/src/DotVVM.Samples.Tests/Complex/SPAViewModelReapplicationTests.cs @@ -15,7 +15,6 @@ public void Complex_SPAViewModelReapplication() { RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.ComplexSamples_SPAViewModelReapplication_pageA); - browser.Wait(1000); // verify items count browser.FindElements("ul#first li").ThrowIfDifferentCountThan(3); @@ -32,12 +31,10 @@ public void Complex_SPAViewModelReapplication() // try the postback browser.First("input[type=button]").Click(); - browser.Wait(); AssertUI.InnerTextEquals(browser.First("#testResult"), "Hello1"); // go to the second page browser.Single("#pageB").Click(); - browser.Wait(); // verify items count and browser.FindElements("ul#first li").ThrowIfDifferentCountThan(3); @@ -50,12 +47,10 @@ public void Complex_SPAViewModelReapplication() // try the postback browser.First("input[type=button]").Click(); - browser.Wait(); AssertUI.InnerTextEquals(browser.First("#testResult"), "World2"); // go to first page browser.Single("#pageA").Click(); - browser.Wait(); // verify items count browser.FindElements("ul#first li").ThrowIfDifferentCountThan(3); diff --git a/src/DotVVM.Samples.Tests/Complex/ServerRenderingTests.cs b/src/DotVVM.Samples.Tests/Complex/ServerRenderingTests.cs index 519a28f4ac..346fbe38d9 100644 --- a/src/DotVVM.Samples.Tests/Complex/ServerRenderingTests.cs +++ b/src/DotVVM.Samples.Tests/Complex/ServerRenderingTests.cs @@ -3,6 +3,7 @@ using Riganti.Selenium.Core; using Riganti.Selenium.Core.Abstractions; using Riganti.Selenium.Core.Abstractions.Exceptions; +using Riganti.Selenium.DotVVM; using Xunit; using Xunit.Abstractions; @@ -18,7 +19,7 @@ public void Complex_ServerRendering_ControlUsageSample() RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.ComplexSamples_ServerRendering_ControlUsageSample); browser.First("a[data-ui=show-link]").Click(); - browser.Wait(500); + browser.WaitForPostback(); AssertUI.Attribute(browser.First("input[data-ui=textbox]"), "value", v => v.Contains("a")); }); } @@ -30,10 +31,10 @@ public void Complex_ServerRendering_ControlUsageSampleRewriting() RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.ComplexSamples_ServerRendering_ControlUsageSampleRewriting); browser.First("a[data-ui=show-link]").Click(); - browser.Wait(500); + browser.WaitForPostback(); AssertUI.Attribute(browser.First("div[data-ui='context-1']").First("input[data-ui=textbox]"), "value", v => v.Contains("a")); browser.First("a[data-ui=rewrite-link]").Click(); - browser.Wait(500); + browser.WaitForPostback(); AssertUI.Attribute(browser.First("div[data-ui='context-2']").First("input[data-ui=textbox]"), "value", v => v.Contains("b")); }); } @@ -51,7 +52,6 @@ public void Complex_ServerRendering_AddingIntoEmptyRepeater() //Add element and see browser.First("a[data-ui='add-link']").Click(); - browser.Wait(500); //Has nonempty repeater been updated? var neArticlesPostAdd = browser.Single("div[data-ui='nonempty-repeater']").FindElements("article[data-ui='test-article']"); @@ -81,7 +81,6 @@ public void Complex_ServerRendering_MarkupControlInRepeaterEditing() // Click 'Edit' articleDetail.Single("a").Click(); - browser.Wait(500); // Check if the textbox contains the same message repeater = browser.Single("div[data-ui='repeater']"); diff --git a/src/DotVVM.Samples.Tests/Complex/TaskListTests.cs b/src/DotVVM.Samples.Tests/Complex/TaskListTests.cs index 453e94c1d7..09fd44c01d 100644 --- a/src/DotVVM.Samples.Tests/Complex/TaskListTests.cs +++ b/src/DotVVM.Samples.Tests/Complex/TaskListTests.cs @@ -1,6 +1,7 @@ using DotVVM.Samples.Tests.Base; using DotVVM.Testing.Abstractions; using Riganti.Selenium.Core; +using Riganti.Selenium.DotVVM; using Xunit; namespace DotVVM.Samples.Tests.Complex @@ -23,17 +24,18 @@ public void Complex_TaskList_TaskListAsyncCommands() //add task browser.SendKeys("input[type=text]", "DotVVM"); browser.ElementAt("input[type=button]",0).Click(); - browser.Wait(500); + browser.WaitForPostback(); browser.FindElements(".table tr").ThrowIfDifferentCountThan(4); //mark last task as completed browser.Last("a").Click(); - browser.Wait(500); + browser.WaitForPostback(); AssertUI.ClassAttribute(browser.Last(".table tr"), a => a.Contains("completed"), "Last task is not marked as completed."); - browser.ElementAt("input[type=button]", 1).Click().Wait(1000); + browser.ElementAt("input[type=button]", 1).Click(); + browser.WaitForPostback(); browser.FindElements(".table tr").ThrowIfDifferentCountThan(5); }); } @@ -50,13 +52,11 @@ public void Complex_TaskList_ServerRenderedTaskList() //add task browser.SendKeys("input[type=text]", "DotVVM"); browser.Click("input[type=button]"); - browser.Wait(500); browser.FindElements(".table tr").ThrowIfDifferentCountThan(4); //mark last task as completed browser.Last("a").Click(); - browser.Wait(500); AssertUI.ClassAttribute(browser.Last(".table tr"), a => a.Contains("completed"), "Last task is not marked as completed."); diff --git a/src/DotVVM.Samples.Tests/Control/AuthenticatedViewTests.cs b/src/DotVVM.Samples.Tests/Control/AuthenticatedViewTests.cs index efa49992fc..d8c1a7e05e 100644 --- a/src/DotVVM.Samples.Tests/Control/AuthenticatedViewTests.cs +++ b/src/DotVVM.Samples.Tests/Control/AuthenticatedViewTests.cs @@ -8,6 +8,7 @@ using DotVVM.Testing.Abstractions; using Xunit; using Xunit.Abstractions; +using Riganti.Selenium.DotVVM; namespace DotVVM.Samples.Tests.Control { @@ -21,12 +22,15 @@ public void Control_AuthenticatedView_AuthenticatedViewTest() browser.NavigateToUrl(SamplesRouteUrls.ControlSamples_AuthenticatedView_AuthenticatedViewTest); // make sure we are signed out - browser.First("input[value='Sign Out']").Click().Wait(); + browser.First("input[value='Sign Out']").Click(); + browser.WaitForPostback(); AssertUI.InnerTextEquals(browser.First(".result"), "I am not authenticated!"); - browser.First("input[value='Sign In']").Click().Wait(); + browser.First("input[value='Sign In']").Click(); + browser.WaitForPostback(); AssertUI.InnerTextEquals(browser.First(".result"), "I am authenticated!"); - browser.First("input[value='Sign Out']").Click().Wait(); + browser.First("input[value='Sign Out']").Click(); + browser.WaitForPostback(); AssertUI.InnerTextEquals(browser.First(".result"), "I am not authenticated!"); }); } diff --git a/src/DotVVM.Samples.Tests/Control/CheckBoxTests.cs b/src/DotVVM.Samples.Tests/Control/CheckBoxTests.cs index c50050b2f0..860cc88ece 100644 --- a/src/DotVVM.Samples.Tests/Control/CheckBoxTests.cs +++ b/src/DotVVM.Samples.Tests/Control/CheckBoxTests.cs @@ -20,7 +20,6 @@ public void Control_CheckBox_CheckBox() // single check box boxes.ElementAt(0).First("input[type=checkbox]").Click(); boxes.ElementAt(0).First("input[type=button]").Click(); - browser.Wait(); AssertUI.InnerTextEquals(boxes.ElementAt(0).First("span.result") , "True"); @@ -29,7 +28,6 @@ public void Control_CheckBox_CheckBox() boxes.ElementAt(1).ElementAt("input[type=checkbox]", 1).Click(); boxes.ElementAt(1).ElementAt("input[type=checkbox]", 2).Click(); boxes.ElementAt(1).First("input[type=button]").Click(); - browser.Wait(); AssertUI.InnerTextEquals(boxes.ElementAt(1).First("span.result") , "g, b"); @@ -37,21 +35,18 @@ public void Control_CheckBox_CheckBox() boxes.ElementAt(1).ElementAt("input[type=checkbox]", 2).Click(); boxes.ElementAt(1).ElementAt("input[type=checkbox]", 0).Click(); boxes.ElementAt(1).First("input[type=button]").Click(); - browser.Wait(); AssertUI.InnerTextEquals(boxes.ElementAt(1).First("span.result") , "g, r"); // checked changed boxes.ElementAt(2).ElementAt("input[type=checkbox]", 0).Click(); - browser.Wait(); AssertUI.InnerTextEquals(boxes.ElementAt(2).Last("span.result") , "1"); AssertUI.IsChecked(boxes.ElementAt(2).First("input[type=checkbox]")); boxes.ElementAt(2).ElementAt("input[type=checkbox]", 0).Click(); - browser.Wait(); AssertUI.InnerTextEquals(boxes.ElementAt(2).Last("span.result") , "2"); diff --git a/src/DotVVM.Samples.Tests/Control/ClaimViewTests.cs b/src/DotVVM.Samples.Tests/Control/ClaimViewTests.cs index 6e4e30fc0b..a0abcea194 100644 --- a/src/DotVVM.Samples.Tests/Control/ClaimViewTests.cs +++ b/src/DotVVM.Samples.Tests/Control/ClaimViewTests.cs @@ -2,6 +2,7 @@ using DotVVM.Samples.Tests.Base; using DotVVM.Testing.Abstractions; using Riganti.Selenium.Core; +using Riganti.Selenium.DotVVM; using Xunit; namespace DotVVM.Samples.Tests.Control @@ -20,15 +21,14 @@ public void Control_ClaimView_ClaimViewTest() void AssertInnerTextEquals(string selector, string text) { - browser.WaitFor(() => { - AssertUI.InnerTextEquals( - browser.FindElements(selector).ThrowIfDifferentCountThan(1).First(), - text); - }, 5000); + AssertUI.InnerTextEquals( + browser.FindElements(selector).ThrowIfDifferentCountThan(1).First(), + text); } // make sure we are signed out (first should show IfNotMember, second should be hidden) browser.First("input[value='Sign Out']").Click(); + browser.WaitForPostback(); AssertInnerTextEquals(".result1", "I am not a member!"); AssertUI.IsNotDisplayed(browser, ".result2"); @@ -36,7 +36,9 @@ void AssertInnerTextEquals(string selector, string text) // sign in as admin (both should show IsMember content) browser.First("input[type=checkbox][value=admin]").Click(); + browser.WaitForPostback(); browser.First("input[value='Sign In']").Click(); + browser.WaitForPostback(); AssertInnerTextEquals(".result1", "I am a member!"); AssertInnerTextEquals(".result2", "I am a member!"); @@ -44,8 +46,11 @@ void AssertInnerTextEquals(string selector, string text) // sign in as moderator and headhunter (both should show IsMember content) browser.First("input[type=checkbox][value=moderator]").Click(); + browser.WaitForPostback(); browser.First("input[type=checkbox][value=headhunter]").Click(); + browser.WaitForPostback(); browser.First("input[value='Sign In']").Click(); + browser.WaitForPostback(); AssertInnerTextEquals(".result1", "I am a member!"); AssertInnerTextEquals(".result2", "I am a member!"); @@ -53,7 +58,9 @@ void AssertInnerTextEquals(string selector, string text) // sign in as headhunter only (both should be visible but show that user is not a member) browser.First("input[type=checkbox][value=headhunter]").Click(); + browser.WaitForPostback(); browser.First("input[value='Sign In']").Click(); + browser.WaitForPostback(); AssertInnerTextEquals(".result1", "I am not a member!"); AssertInnerTextEquals(".result2", "I am not a member!"); @@ -61,7 +68,9 @@ void AssertInnerTextEquals(string selector, string text) // sign in as tester only (both should show IsMember content) browser.First("input[type=checkbox][value=tester]").Click(); + browser.WaitForPostback(); browser.First("input[value='Sign In']").Click(); + browser.WaitForPostback(); AssertInnerTextEquals(".result1", "I am a member!"); AssertInnerTextEquals(".result2", "I am a member!"); @@ -69,6 +78,7 @@ void AssertInnerTextEquals(string selector, string text) // sign out (first should show IfNotMember, second should be hidden) browser.First("input[value='Sign Out']").Click(); + browser.WaitForPostback(); AssertInnerTextEquals(".result1", "I am not a member!"); AssertUI.IsNotDisplayed(browser, ".result2"); diff --git a/src/DotVVM.Samples.Tests/Control/ComboBoxTests.cs b/src/DotVVM.Samples.Tests/Control/ComboBoxTests.cs index 6d47e1783a..81bc4affdd 100644 --- a/src/DotVVM.Samples.Tests/Control/ComboBoxTests.cs +++ b/src/DotVVM.Samples.Tests/Control/ComboBoxTests.cs @@ -23,19 +23,19 @@ public void Control_ComboBox_Default() var selectedValue = browser.First("selected-value", SelectByDataUi); AssertUI.IsDisplayed(comboBox.Select(0)); - browser.WaitFor(() => AssertUI.InnerTextEquals(selectedValue, "1"), 2000, 30); + AssertUI.InnerTextEquals(selectedValue, "1"); // select second option from combobox comboBox.Select(1); - browser.WaitFor(() => AssertUI.InnerTextEquals(selectedValue, "2"), 1000, 30); + AssertUI.InnerTextEquals(selectedValue, "2"); // select third option from combobox comboBox.Select(2); - browser.WaitFor(() => AssertUI.InnerTextEquals(selectedValue, "3"), 1000, 30); + AssertUI.InnerTextEquals(selectedValue, "3"); // select fourth option from combobox comboBox.Select(3); - browser.WaitFor(() => AssertUI.InnerTextEquals(selectedValue, "4"), 1000, 30); + AssertUI.InnerTextEquals(selectedValue, "4"); }); } @@ -50,19 +50,19 @@ public void Control_ComboBox_ComboBoxBinded() var selectedText = browser.First("selected-text", SelectByDataUi); AssertUI.IsDisplayed(comboBox.Select(0)); - browser.WaitFor(() => AssertUI.InnerTextEquals(selectedText, "A"), 2000, 30); + AssertUI.InnerTextEquals(selectedText, "A"); // select second option from combobox comboBox.Select(1); - browser.WaitFor(() => AssertUI.InnerTextEquals(selectedText, "AA"), 1000, 30); + AssertUI.InnerTextEquals(selectedText, "AA"); // select third option from combobox comboBox.Select(2); - browser.WaitFor(() => AssertUI.InnerTextEquals(selectedText, "AAA"), 1000, 30); + AssertUI.InnerTextEquals(selectedText, "AAA"); // select fourth option from combobox comboBox.Select(3); - browser.WaitFor(() => AssertUI.InnerTextEquals(selectedText, "AAAA"), 1000, 30); + AssertUI.InnerTextEquals(selectedText, "AAAA"); }); } @@ -79,10 +79,8 @@ public void Control_ComboBox_DelaySync() // change the DataSource collection on the server and verify that the second item is selected in both ComboBoxes browser.First("input").Click(); - browser.WaitFor(() => { - AssertUI.IsSelected(browser.ElementAt("select", 0).ElementAt("option", 1)); - AssertUI.IsSelected(browser.ElementAt("select", 1).ElementAt("option", 1)); - }, 800, 30); + AssertUI.IsSelected(browser.ElementAt("select", 0).ElementAt("option", 1)); + AssertUI.IsSelected(browser.ElementAt("select", 1).ElementAt("option", 1)); }); } @@ -92,18 +90,15 @@ public void Control_ComboBox_DelaySync2() RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.ControlSamples_ComboBox_DelaySync2); browser.First("input[type=button]").Click(); + browser.WaitForPostback(); - browser.WaitFor(() => { - - - // check the comboboxes - AssertUI.IsSelected(browser.ElementAt("select", 0).ElementAt("option", 0)); - AssertUI.IsSelected(browser.ElementAt("select", 1).ElementAt("option", 1)); + // check the comboboxes + AssertUI.IsSelected(browser.ElementAt("select", 0).ElementAt("option", 0)); + AssertUI.IsSelected(browser.ElementAt("select", 1).ElementAt("option", 1)); - // check the labels - AssertUI.InnerTextEquals(browser.ElementAt(".result", 0), "1"); - AssertUI.InnerTextEquals(browser.ElementAt(".result", 1), "2"); - }, 1000, 30); + // check the labels + AssertUI.InnerTextEquals(browser.ElementAt(".result", 0), "1"); + AssertUI.InnerTextEquals(browser.ElementAt(".result", 1), "2"); }); } @@ -135,7 +130,7 @@ public void Control_ComboBox_Nullable() // check combobox works var combobox = browser.Single("combobox", SelectByDataUi); combobox.Select(0); - browser.WaitFor(() => AssertUI.InnerTextEquals(span, "First"), 1000); + AssertUI.InnerTextEquals(span, "First"); // test buttons browser.ElementAt("input[type=button]", 0).Click(); @@ -164,11 +159,9 @@ public void Control_ComboBox_ItemBinding_ItemValueBinding_Complex_Error() RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.ControlSamples_ComboBox_ItemBinding_ItemValueBinding_Complex_Error); - browser.WaitFor(()=> { - AssertUI.InnerText(browser.First(".exceptionMessage"), s => s.Contains("Return type") && s.Contains("ItemValueBinding") && s.Contains("primitive type")); - AssertUI.InnerText(browser.First("p.summary"), s => s.Contains("DotVVM.Framework.Compilation.DotvvmCompilationException")); - AssertUI.InnerText(browser.First(".errorUnderline"), s => s.Contains("ItemValueBinding=") && s.Contains("{value:")); - },1000); + AssertUI.InnerText(browser.First(".exceptionMessage"), s => s.Contains("Return type") && s.Contains("ItemValueBinding") && s.Contains("primitive type")); + AssertUI.InnerText(browser.First("p.summary"), s => s.Contains("DotVVM.Framework.Compilation.DotvvmCompilationException")); + AssertUI.InnerText(browser.First(".errorUnderline"), s => s.Contains("ItemValueBinding=") && s.Contains("{value:")); }); } @@ -189,9 +182,7 @@ public void Control_ComboBox_ItemBinding_ItemValueBinding_Enum() dropDown.Click(); dropDownButtons.ElementAt(i).Click(); - browser.WaitFor(()=> { - AssertUI.InnerTextEquals(value,"EValue"+((i%3)+1).ToString()); - },2000); + AssertUI.InnerTextEquals(value, "EValue" + ((i % 3) + 1).ToString()); } }); } @@ -213,9 +204,7 @@ public void Control_ComboBox_ItemBinding_ItemValueBinding_Number() dropDown.Click(); dropDownButtons.ElementAt(i).Click(); - browser.WaitFor(() => { - AssertUI.InnerTextEquals(value, (i+1).ToString()); - }, 2000); + AssertUI.InnerTextEquals(value, (i + 1).ToString()); } }); } @@ -226,11 +215,9 @@ public void Control_ComboBox_ItemBinding_ItemValueBinding_SelectedValue_Complex_ RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.ControlSamples_ComboBox_ItemBinding_ItemValueBinding_SelectedValue_ComplexToInt_Error); - browser.WaitFor(() => { - AssertUI.InnerText(browser.First(".exceptionMessage"), s => s.Contains("DotVVM.Samples.Common.ViewModels.ControlSamples.ComboBox.ComboxItemBindingViewModel+ComplexType") && s.Contains("not assignable") && s.Contains("System.Int32")); - AssertUI.InnerText(browser.First("p.summary"), s => s.Contains("DotVVM.Framework.Compilation.DotvvmCompilationException")); - AssertUI.InnerText(browser.First(".errorUnderline"), s => s.Contains("{value: SelectedInt}")); - }, 1000); + AssertUI.InnerText(browser.First(".exceptionMessage"), s => s.Contains("DotVVM.Samples.Common.ViewModels.ControlSamples.ComboBox.ComboxItemBindingViewModel+ComplexType") && s.Contains("not assignable") && s.Contains("System.Int32")); + AssertUI.InnerText(browser.First("p.summary"), s => s.Contains("DotVVM.Framework.Compilation.DotvvmCompilationException")); + AssertUI.InnerText(browser.First(".errorUnderline"), s => s.Contains("{value: SelectedInt}")); }); } @@ -240,11 +227,9 @@ public void Control_ComboBox_ItemBinding_ItemValueBinding_SelectedValue_StringTo RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.ControlSamples_ComboBox_ItemBinding_ItemValueBinding_SelectedValue_StringToInt_Error); - browser.WaitFor(() => { - AssertUI.InnerText(browser.First(".exceptionMessage"), s => s.Contains("System.String") && s.Contains("not assignable") && s.Contains("System.Int32")); - AssertUI.InnerText(browser.First("p.summary"), s => s.Contains("DotVVM.Framework.Compilation.DotvvmCompilationException")); - AssertUI.InnerText(browser.First(".errorUnderline"), s => s.Contains("{value: SelectedInt}")); - }, 1000); + AssertUI.InnerText(browser.First(".exceptionMessage"), s => s.Contains("System.String") && s.Contains("not assignable") && s.Contains("System.Int32")); + AssertUI.InnerText(browser.First("p.summary"), s => s.Contains("DotVVM.Framework.Compilation.DotvvmCompilationException")); + AssertUI.InnerText(browser.First(".errorUnderline"), s => s.Contains("{value: SelectedInt}")); }); } @@ -265,9 +250,7 @@ public void Control_ComboBox_ItemBinding_ItemValueBinding_String() dropDown.Click(); dropDownButtons.ElementAt(i).Click(); - browser.WaitFor(() => { - AssertUI.InnerTextEquals(value, dropDownButtons.ElementAt(i).GetInnerText()); - }, 2000); + AssertUI.InnerTextEquals(value, dropDownButtons.ElementAt(i).GetInnerText()); } }); } @@ -301,13 +284,11 @@ public void Control_ComboBox_BindingCTValidation_StringToEnum() { dropDownButtons.ElementAt(i).Click(); - browser.WaitFor(()=> { - AssertUI.TextEquals(enum1, dropDownButtons.ElementAt(i).GetInnerText()); - AssertUI.TextNotEquals(enum2, dropDownButtons.ElementAt(i).GetInnerText()); - },2000); - + AssertUI.TextEquals(enum1, dropDownButtons.ElementAt(i).GetInnerText()); + AssertUI.TextNotEquals(enum2, dropDownButtons.ElementAt(i).GetInnerText()); + setSecondaryFieldButton.Click(); - browser.WaitFor(()=> {AssertUI.TextEquals(enum2, dropDownButtons.ElementAt(i).GetInnerText()); },2000); + AssertUI.TextEquals(enum2, dropDownButtons.ElementAt(i).GetInnerText()); } }); } diff --git a/src/DotVVM.Samples.Tests/Control/DataPagerTests.cs b/src/DotVVM.Samples.Tests/Control/DataPagerTests.cs index cdd709e3c0..a03ebe029e 100644 --- a/src/DotVVM.Samples.Tests/Control/DataPagerTests.cs +++ b/src/DotVVM.Samples.Tests/Control/DataPagerTests.cs @@ -23,7 +23,6 @@ public void Control_DataPager_DataPager_ShowHideControl() { RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.ControlSamples_DataPager_DataPager); - browser.Wait(); // verify the second pager is hidden AssertUI.IsDisplayed(browser.First(".pagination")); @@ -53,7 +52,6 @@ public void Control_DataPager_DataPager_ActiveCssClass() { RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.ControlSamples_DataPager_DataPager); - browser.Wait(); // the first li should be visible because it contains text, the second with the link should be hidden var pageIndex1 = browser.First("#pager1").ElementAt("li", 2); @@ -79,7 +77,6 @@ public void Control_DataPager_DataPager_DisabledAttribute() { RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.ControlSamples_DataPager_DataPager); - browser.Wait(); // the first ul should not be disabled AssertUI.HasNotAttribute(browser.Single("#pager1").ElementAt("li a", 0), "disabled"); @@ -119,7 +116,6 @@ public void Control_DataPager_DataPager_DisabledControlClick() { RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.ControlSamples_DataPager_DataPager); - browser.Wait(); // populate with data browser.Single("populate-button", this.SelectByDataUi).Click(); @@ -168,14 +164,13 @@ public void Control_DataPager_DataPager_DisabledByBindingControlClick() { RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.ControlSamples_DataPager_DataPager); - browser.Wait(); // populate with data browser.Single("populate-button", this.SelectByDataUi).Click(); // pager 4 should be disabled by value // try to switch to next page - browser.Single("#pager4").ElementAt("li a", browser.Single("#pager4").FindElements("li a").Count - 2).ScrollTo().Click().Wait(); + browser.Single("#pager4").ElementAt("li a", browser.Single("#pager4").FindElements("li a").Count - 2).ScrollTo().Click(); AssertUI.InnerTextEquals(browser.First("ul").First("li"), "Item 0"); // try to switch to last page @@ -210,7 +205,6 @@ public void Control_DataPager_ShowHideControl() { RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.ControlSamples_DataPager_DataPager); - browser.Wait(); // verify the second pager is hidden AssertUI.IsDisplayed(browser.First(".pagination")); @@ -239,7 +233,6 @@ public void Control_DataPager_NearPageIndexes() { RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.ControlSamples_DataPager_DataPager); - browser.Wait(); void CheckNearPageIndexes(IEnumerable indexes) { @@ -248,7 +241,7 @@ void CheckNearPageIndexes(IEnumerable indexes) var nearPageIndexesCount = indexes.Count(); // Including first page, previous page, next page, last page links - elements.ThrowIfDifferentCountThan(nearPageIndexesCount + 4); + browser.WaitFor(() => elements.ThrowIfDifferentCountThan(nearPageIndexesCount + 4), 1000); foreach (var value in indexes.Zip(elements.Skip(2), (i, e) => new { Index = i, Element = e })) { diff --git a/src/DotVVM.Samples.Tests/Control/EnabledPropertyTests.cs b/src/DotVVM.Samples.Tests/Control/EnabledPropertyTests.cs index 8e7128f2ef..88ad9772c4 100644 --- a/src/DotVVM.Samples.Tests/Control/EnabledPropertyTests.cs +++ b/src/DotVVM.Samples.Tests/Control/EnabledPropertyTests.cs @@ -24,7 +24,7 @@ public void Control_EnabledProperty_EnabledProperty() AssertUI.IsEnabled(browser.ElementAt("select", 1)); AssertUI.IsEnabled(browser.First("[data-ui=button]")); - browser.First("[data-ui=switch-button]").Click().Wait(); + browser.First("[data-ui=switch-button]").Click(); AssertUI.IsNotEnabled(browser.ElementAt("select", 0)); AssertUI.IsNotEnabled(browser.ElementAt("input", 0)); diff --git a/src/DotVVM.Samples.Tests/Control/FileUploadInRepeaterTests.cs b/src/DotVVM.Samples.Tests/Control/FileUploadInRepeaterTests.cs index b9ec06d3c8..b898116481 100644 --- a/src/DotVVM.Samples.Tests/Control/FileUploadInRepeaterTests.cs +++ b/src/DotVVM.Samples.Tests/Control/FileUploadInRepeaterTests.cs @@ -1,4 +1,5 @@ -using System.IO; +using System; +using System.IO; using System.Linq; using DotVVM.Samples.Tests.Base; using DotVVM.Testing.Abstractions; @@ -18,32 +19,28 @@ public FileUploadInRepeaterTests(ITestOutputHelper output) : base(output) [Fact] public void Complex_FileUploadInRepeater_FileUploadInRepeater() { - RunInAllBrowsers(browser => - { + RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.ComplexSamples_FileUploadInRepeater_FileUploadInRepeater); - browser.Wait(1000); - + browser.WaitUntilDotvvmInited(); var tempPath = Path.GetTempFileName(); File.WriteAllBytes(tempPath, Enumerable.Range(0, 255).Select(i => (byte)i).ToArray()); AssertUI.InnerTextEquals(browser.ElementAt(".files-count", 0), "0"); - DotVVMAssert.UploadFile((ElementWrapper)browser.ElementAt(".dotvvm-upload-button a", 0), tempPath); + DotVVMAssertModified.UploadFile((ElementWrapper)browser.ElementAt(".dotvvm-upload-button a", 0), tempPath); - browser.WaitFor(() => browser.ElementAt(".files-count", 0).GetInnerText() == "1", 10000, "FileCount is not updated to '1'."); + AssertUI.InnerTextEquals(browser.ElementAt(".files-count", 0), "1", failureMessage: "FileCount is not updated to '1'."); AssertUI.InnerTextEquals(browser.ElementAt(".files-count", 1), "0"); AssertUI.InnerTextEquals(browser.ElementAt(".files-count", 2), "0"); DotVVMAssert.UploadFile((ElementWrapper)browser.ElementAt(".dotvvm-upload-button a", 2), tempPath); - browser.Wait(6000); AssertUI.InnerTextEquals(browser.ElementAt(".files-count", 0), "1"); AssertUI.InnerTextEquals(browser.ElementAt(".files-count", 1), "0"); AssertUI.InnerTextEquals(browser.ElementAt(".files-count", 2), "1"); DotVVMAssert.UploadFile((ElementWrapper)browser.ElementAt(".dotvvm-upload-button a", 0), tempPath); - browser.Wait(6000); AssertUI.InnerTextEquals(browser.ElementAt(".files-count", 0), "2"); AssertUI.InnerTextEquals(browser.ElementAt(".files-count", 1), "0"); @@ -53,9 +50,9 @@ public void Complex_FileUploadInRepeater_FileUploadInRepeater() { File.Delete(tempPath); } - catch + catch (Exception ex) { - //TODO log + TestOutput.WriteLine(ex.ToString()); } }); } diff --git a/src/DotVVM.Samples.Tests/Control/FileUploadTests.cs b/src/DotVVM.Samples.Tests/Control/FileUploadTests.cs index 17e2f92609..2aeead546a 100644 --- a/src/DotVVM.Samples.Tests/Control/FileUploadTests.cs +++ b/src/DotVVM.Samples.Tests/Control/FileUploadTests.cs @@ -21,11 +21,9 @@ public void Control_FileUpload_FileUpload() RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.ControlSamples_FileUpload_FileUpload); - browser.Wait(1000); // get existing files var existingFiles = browser.FindElements("li").Select(e => e.GetText()).ToList(); - browser.Wait(1000); // generate a sample file to upload var tempFile = Path.GetTempFileName(); @@ -33,12 +31,10 @@ public void Control_FileUpload_FileUpload() // write the full path to the dialog - DotVVMAssert.UploadFile((ElementWrapper)browser.First(".dotvvm-upload-button a"), tempFile); + DotVVMAssertModified.UploadFile((ElementWrapper)browser.First(".dotvvm-upload-button a"), tempFile); // wait for the file to be uploaded - - browser.WaitFor(() => browser.First(".dotvvm-upload-files").GetText() == "1 files", 60000, - "File was not uploaded in 1 min interval."); + AssertUI.TextEquals(browser.First(".dotvvm-upload-files"), "1 files", failureMessage: "File was not uploaded in 1 min interval."); //TODO: TestContext.WriteLine("The file was uploaded."); @@ -69,16 +65,15 @@ public void Control_FileUpload_IsAllowedOrNot_IsFileAllowed() RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.ControlSamples_FileUpload_IsAllowedOrNot); - browser.Wait(1000); + var isFileTypeAllowed = browser.Single("span.isFileTypeAllowed"); var isMaxSizeExceeded = browser.Single("span.isMaxSizeExceeded"); var textFile = CreateTempFile("txt", 1); - DotVVMAssert.UploadFile((ElementWrapper)browser.First(".dotvvm-upload-button a"), textFile); + DotVVMAssertModified.UploadFile((ElementWrapper)browser.First(".dotvvm-upload-button a"), textFile); - browser.WaitFor(() => browser.First(".dotvvm-upload-files").GetText() == "1 files", 60000, - "File was not uploaded in 1 min interval."); + AssertUI.TextEquals(browser.First(".dotvvm-upload-files"),"1 files",failureMessage: "File was not uploaded in 1 min interval."); AssertUI.TextEquals(isFileTypeAllowed, "true"); AssertUI.TextEquals(isMaxSizeExceeded, "false"); @@ -94,16 +89,14 @@ public void Control_FileUpload_IsFileNotAllowed() RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.ControlSamples_FileUpload_IsAllowedOrNot); - browser.Wait(1000); var isFileTypeAllowed = browser.Single("span.isFileTypeAllowed"); var isMaxSizeExceeded = browser.Single("span.isMaxSizeExceeded"); var mdFile = CreateTempFile("md", 1); - DotVVMAssert.UploadFile((ElementWrapper)browser.First(".dotvvm-upload-button a"), mdFile); + DotVVMAssertModified.UploadFile((ElementWrapper)browser.First(".dotvvm-upload-button a"), mdFile); - browser.WaitFor(() => browser.First(".dotvvm-upload-files").GetText() == "1 files", 60000, - "File was not uploaded in 1 min interval."); + AssertUI.TextEquals(browser.First(".dotvvm-upload-files"), "1 files", failureMessage: "File was not uploaded in 1 min interval."); AssertUI.TextEquals(isFileTypeAllowed, "false"); AssertUI.TextEquals(isMaxSizeExceeded, "false"); @@ -120,16 +113,14 @@ public void Control_FileUpload_IsAllowedOrNot_FileTooLarge() RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.ControlSamples_FileUpload_IsAllowedOrNot); - browser.Wait(1000); var isFileTypeAllowed = browser.Single("span.isFileTypeAllowed"); var isMaxSizeExceeded = browser.Single("span.isMaxSizeExceeded"); var largeFile = CreateTempFile("txt", 2); - DotVVMAssert.UploadFile((ElementWrapper)browser.First(".dotvvm-upload-button a"), largeFile); + DotVVMAssertModified.UploadFile((ElementWrapper)browser.First(".dotvvm-upload-button a"), largeFile); - browser.WaitFor(() => browser.First(".dotvvm-upload-files").GetText() == "1 files", 60000, - "File was not uploaded in 1 min interval."); + AssertUI.TextEquals(browser.First(".dotvvm-upload-files"), "1 files", failureMessage: "File was not uploaded in 1 min interval."); AssertUI.TextEquals(isFileTypeAllowed, "true"); AssertUI.TextEquals(isMaxSizeExceeded, "true"); @@ -145,15 +136,13 @@ public void Control_FileUpload_FileSize() RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.ControlSamples_FileUpload_FileSize); - browser.Wait(1000); var fileSize = browser.Single("span.fileSize"); var file = CreateTempFile("txt", 2); - DotVVMAssert.UploadFile((ElementWrapper)browser.First(".dotvvm-upload-button a"), file); + DotVVMAssertModified.UploadFile((ElementWrapper)browser.First(".dotvvm-upload-button a"), file); - browser.WaitFor(() => browser.First(".dotvvm-upload-files").GetText() == "1 files", 60000, - "File was not uploaded in 1 min interval."); + AssertUI.TextEquals(browser.First(".dotvvm-upload-files"), "1 files", failureMessage: "File was not uploaded in 1 min interval."); AssertUI.TextEquals(fileSize, "2 MB"); @@ -179,4 +168,4 @@ private string CreateTempFile(string extension, long size) // TODO: RenderSettings.Mode="Server" } -} \ No newline at end of file +} diff --git a/src/DotVVM.Samples.Tests/Control/GridViewTests.cs b/src/DotVVM.Samples.Tests/Control/GridViewTests.cs index b26d914628..6570afdbd3 100644 --- a/src/DotVVM.Samples.Tests/Control/GridViewTests.cs +++ b/src/DotVVM.Samples.Tests/Control/GridViewTests.cs @@ -28,6 +28,7 @@ public void Control_GridView_GridViewInlineEditingValidation() //Edit firstRow.ElementAt("td", 5).First("button").Click(); + browser.WaitForPostback(); rows = browser.First("table tbody"); firstRow = rows.ElementAt("tr", 0); @@ -49,7 +50,7 @@ public void Control_GridView_GridViewInlineEditingValidation() //getting rid iof "postback interupted message" browser.FindElements("div#debugNotification").First().Click(); - browser.Wait(1000); + browser.WaitForPostback(); var validationResult = browser.ElementAt(".validation", 0); @@ -63,6 +64,7 @@ public void Control_GridView_GridViewInlineEditingValidation() //update firstRow.ElementAt("td", 5).First("button").Click(); + browser.WaitForPostback(); //check validation AssertUI.InnerTextEquals(validationResult, "The Email field is not a valid e-mail address."); @@ -74,7 +76,6 @@ public void Control_GridView_GridViewStaticCommand() { RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.ControlSamples_GridView_GridViewStaticCommand); - browser.Wait(); //check rows browser.FindElements("table tbody tr").ThrowIfDifferentCountThan(5); @@ -82,6 +83,7 @@ public void Control_GridView_GridViewStaticCommand() AssertUI.InnerTextEquals(browser.First("table tbody tr td span"), "1"); //cal static command for delete row browser.First("table tbody tr input[type=button]").Click(); + browser.WaitForPostback(); //check rows again browser.FindElements("table tbody tr").ThrowIfDifferentCountThan(4); //check first row Id @@ -105,7 +107,7 @@ public void Control_GridView_GridViewInlineEditingValidation_GridViewInlineEditi //Edit firstRow.ElementAt("td", 5).First("button").Click(); - browser.Wait(500); + browser.WaitForPostback(); //init again rows = browser.First("table tbody"); @@ -130,7 +132,7 @@ public void Control_GridView_GridViewInlineEditingPrimaryKeyGuid() AssertUI.InnerTextEquals(firstRow.ElementAt("td", 0).First("span"), "9536d712-2e91-43d2-8ebb-93fbec31cf34"); //Edit firstRow.ElementAt("td", 4).First("button").Click(); - browser.Wait(500); + browser.WaitForPostback(); //init again rows = browser.First("table tbody"); @@ -150,7 +152,7 @@ public void Control_GridView_GridViewInlineEditingPrimaryKeyGuid() //update firstRow.ElementAt("td", 4).First("button").Click(); - browser.Wait(500); + browser.WaitForPostback(); //init again rows = browser.First("table tbody"); @@ -174,7 +176,7 @@ public void Control_GridView_GridViewInlineEditingPrimaryKeyString() AssertUI.InnerTextEquals(firstRow.ElementAt("td", 0).First("span"), "A"); //Edit firstRow.ElementAt("td", 4).First("button").Click(); - browser.Wait(500); + browser.WaitForPostback(); //init again rows = browser.First("table tbody"); @@ -194,7 +196,7 @@ public void Control_GridView_GridViewInlineEditingPrimaryKeyString() //update firstRow.ElementAt("td", 4).First("button").Click(); - browser.Wait(500); + browser.WaitForPostback(); //init again rows = browser.First("table tbody"); @@ -233,7 +235,7 @@ public void Control_GridView_GridViewInlineEditing(string path, int tableID) // click on Cancel button firstRow.ElementAt("td", 3).ElementAt("button", 1).ScrollTo().Click(); - browser.Wait(500); + browser.WaitForPostback(); // click the Edit button on another row table = browser.ElementAt("table", tableID); @@ -279,7 +281,7 @@ public void Control_GridView_GridViewInlineEditing_PagingWhenEditing(string path //page to second page var navigation = browser.ElementAt(".pagination", 0); navigation.FindElements("li a").Single(s => s.GetText() == "2").Click(); - browser.Wait(500); + browser.WaitForPostback(); table = browser.ElementAt("table", tableID); firstRow = table.First("tbody tr"); @@ -288,7 +290,7 @@ public void Control_GridView_GridViewInlineEditing_PagingWhenEditing(string path //page to back navigation = browser.ElementAt(".pagination", 0); navigation.FindElements("li a").Single(s => s.GetText() == "1").Click(); - browser.Wait(500); + browser.WaitForPostback(); //after page back check edit row table = browser.ElementAt("table", tableID); @@ -318,78 +320,67 @@ public void Control_GridView_GridViewPagingSortingBase(string path) // go to second page AssertUI.InnerTextEquals(browser.ElementAt("table", 0).ElementAt("tr", 1).ElementAt("td", 0), "1"); browser.ElementAt("ul", 0).FindElements("li a").Single(s => s.GetText() == "2").Click(); + browser.WaitForPostback(); // go to previous page - browser.WaitFor(() => { - AssertUI.InnerTextEquals(browser.ElementAt("table", 0).ElementAt("tr", 1).ElementAt("td", 0), "11"); - }, 5000); + AssertUI.InnerTextEquals(browser.ElementAt("table", 0).ElementAt("tr", 1).ElementAt("td", 0), "11"); browser.ElementAt("ul", 0).FindElements("li a").Single(s => s.GetText() == "««").Click(); + browser.WaitForPostback(); // go to next page - browser.WaitFor(() => { - AssertUI.InnerTextEquals(browser.ElementAt("table", 0).ElementAt("tr", 1).ElementAt("td", 0), "1"); - }, 5000); + AssertUI.InnerTextEquals(browser.ElementAt("table", 0).ElementAt("tr", 1).ElementAt("td", 0), "1"); browser.ElementAt("ul", 0).FindElements("li a").Single(s => s.GetText() == "»»").Click(); + browser.WaitForPostback(); // try the disabled link - nothing should happen - browser.WaitFor(() => { - AssertUI.InnerTextEquals(browser.ElementAt("table", 0).ElementAt("tr", 1).ElementAt("td", 0), "11"); - }, 5000); + AssertUI.InnerTextEquals(browser.ElementAt("table", 0).ElementAt("tr", 1).ElementAt("td", 0), "11"); browser.ElementAt("ul", 0).FindElements("li a").Single(s => s.GetText() == "»»").Click(); + browser.WaitForPostback(); - browser.WaitFor(() => { - AssertUI.InnerTextEquals(browser.ElementAt("table", 0).ElementAt("tr", 1).ElementAt("td", 0), "11"); - }, 5000); + AssertUI.InnerTextEquals(browser.ElementAt("table", 0).ElementAt("tr", 1).ElementAt("td", 0), "11"); // try sorting in the first grid browser.ElementAt("table", 0).ElementAt("tr", 0).ElementAt("th", 2).ElementAt("button", 0).Click(); - browser.WaitFor(() => { - AssertUI.InnerTextEquals(browser.ElementAt("table", 0).ElementAt("tr", 1).ElementAt("td", 0), "4"); - }, 5000); + browser.WaitForPostback(); + AssertUI.InnerTextEquals(browser.ElementAt("table", 0).ElementAt("tr", 1).ElementAt("td", 0), "4"); browser.ElementAt("table", 0).ElementAt("tr", 0).ElementAt("th", 1).ElementAt("a", 0).Click(); - browser.WaitFor(() => { - AssertUI.ClassAttribute(browser.ElementAt("table", 0).ElementAt("tr", 0).ElementAt("th", 1), "sort-asc"); - }, 5000); + browser.WaitForPostback(); + AssertUI.ClassAttribute(browser.ElementAt("table", 0).ElementAt("tr", 0).ElementAt("th", 1), "sort-asc"); browser.ElementAt("table", 0).ElementAt("tr", 0).ElementAt("th", 0).ElementAt("a", 0).Click(); - browser.WaitFor(() => { - AssertUI.InnerTextEquals(browser.ElementAt("table", 0).ElementAt("tr", 1).ElementAt("td", 0), "1"); - }, 5000); + browser.WaitForPostback(); + AssertUI.InnerTextEquals(browser.ElementAt("table", 0).ElementAt("tr", 1).ElementAt("td", 0), "1"); // sort descending in the first grid browser.ElementAt("table", 0).ElementAt("tr", 0).ElementAt("th", 1).ElementAt("a", 0).Click(); - browser.WaitFor(() => { - AssertUI.InnerTextEquals(browser.ElementAt("table", 0).ElementAt("tr", 1).ElementAt("td", 0), "7"); - }, 5000); + browser.WaitForPostback(); + AssertUI.InnerTextEquals(browser.ElementAt("table", 0).ElementAt("tr", 1).ElementAt("td", 0), "7"); browser.ElementAt("table", 0).ElementAt("tr", 0).ElementAt("th", 1).ElementAt("a", 0).Click(); - browser.WaitFor(() => { - AssertUI.ClassAttribute(browser.ElementAt("table", 0).ElementAt("tr", 0).ElementAt("th", 1), "sort-desc"); - AssertUI.InnerTextEquals(browser.ElementAt("table", 0).ElementAt("tr", 1).ElementAt("td", 0), "16"); - }, 5000); + browser.WaitForPostback(); + AssertUI.ClassAttribute(browser.ElementAt("table", 0).ElementAt("tr", 0).ElementAt("th", 1), "sort-desc"); + AssertUI.InnerTextEquals(browser.ElementAt("table", 0).ElementAt("tr", 1).ElementAt("td", 0), "16"); // sort by different column in the first grid browser.ElementAt("table", 0).ElementAt("tr", 0).ElementAt("th", 0).ElementAt("a", 0).Click(); - browser.WaitFor(() => { - AssertUI.InnerTextEquals(browser.ElementAt("table", 0).ElementAt("tr", 1).ElementAt("td", 0), "1"); - }, 5000); + browser.WaitForPostback(); + AssertUI.InnerTextEquals(browser.ElementAt("table", 0).ElementAt("tr", 1).ElementAt("td", 0), "1"); }; Control_GridViewShowHeaderWhenNoData(browser); - browser.Wait(); performTest(); - browser.Wait(); + browser.NavigateToUrl(); - browser.Wait(); + browser.NavigateBack(); - browser.Wait(); + performTest(); }); } private void Control_GridViewShowHeaderWhenNoData(IBrowserWrapper browser) { - browser.FindElements("[data-ui='ShowHeaderWhenNoDataGrid']").FindElements("th").First().IsDisplayed(); + browser.Single("ShowHeaderWhenNoDataGrid", SelectByDataUi).FindElements("th").First().IsDisplayed(); } [Fact] @@ -403,14 +394,12 @@ public void Control_GridView_GridViewRowDecorators() // check that clicking selects the row which gets the 'selected' class // we dont want to check if element is clickable, it is not a button just fire click event browser.ElementAt("tr", 3).ElementAt("td", 0).Click(); - browser.Wait(); for (int i = 0; i < 6; i++) { AssertUI.ClassAttribute(browser.ElementAt("table", 0).ElementAt("tr", i), v => v.Contains("selected") == (i == 3)); } // we dont want to check if element is clickable, it is not a button just fire click event browser.ElementAt("tr", 2).ElementAt("td", 0).Click(); - browser.Wait(); for (int i = 0; i < 6; i++) { AssertUI.ClassAttribute(browser.ElementAt("table", 0).ElementAt("tr", i), v => v.Contains("selected") == (i == 2)); @@ -441,16 +430,14 @@ public void Control_GridView_GridViewRowDecorators_ClickPropagation() { RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.ControlSamples_GridView_GridViewRowDecorators); - browser.Wait(); - browser.ElementAt("table", 0).ElementAt("tr", 4).First("input[type=button]").Click().Wait(); + browser.ElementAt("table", 0).ElementAt("tr", 4).First("input[type=button]").Click(); AssertUI.HasNotClass(browser.ElementAt("table", 0).ElementAt("tr", 4), "selected"); AssertUI.InnerText(browser.ElementAt("table", 0).ElementAt("tr", 4).ElementAt("td", 1), t => t == "xxx"); browser.NavigateToUrl(SamplesRouteUrls.ControlSamples_GridView_GridViewRowDecorators); - browser.Wait(); - browser.ElementAt("table", 0).ElementAt("tr", 4).First("a").Click().Wait(); + browser.ElementAt("table", 0).ElementAt("tr", 4).First("a").Click(); AssertUI.HasNotClass(browser.ElementAt("table", 0).ElementAt("tr", 4), "selected"); AssertUI.InnerText(browser.ElementAt("table", 0).ElementAt("tr", 4).ElementAt("td", 1), t => t == "xxx"); }); @@ -462,11 +449,10 @@ public void Control_GridView_GridViewRowDecorators_RouteLinkClickPropagation() { RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.ControlSamples_GridView_GridViewRowDecorators); - browser.Wait(); var routeLinks = browser.FindElements("table [data-ui=route-link]"); - routeLinks.First().Click().Wait(); + routeLinks.First().Click(); AssertUI.UrlEquals(browser, browser.BaseUrl); }); } @@ -476,7 +462,6 @@ public void Control_GridView_ColumnVisible() { RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.ControlSamples_GridView_ColumnVisible); - browser.Wait(); // check that columns are visible for (int i = 0; i < 6; i++) @@ -546,10 +531,10 @@ public void Control_GridView_RenamedPrimaryKey() AssertUI.NotContainsElement(gridview, "input"); browser.First("edit-button", SelectByDataUi).Click(); - browser.WaitFor(() => AssertUI.ContainsElement(gridview, "input"), 1000); + AssertUI.ContainsElement(gridview, "input"); browser.First("save-button", SelectByDataUi).Click(); - browser.WaitFor(() => AssertUI.NotContainsElement(gridview, "input"), 1000); + AssertUI.NotContainsElement(gridview, "input"); }); } @@ -569,7 +554,7 @@ public void Control_GridView_InvalidCssClass_TextBox_Attached() textbox.Clear(); gridview.First("save-button", SelectByDataUi).Click(); - browser.WaitFor(() => AssertUI.HasClass(textbox, "invalid"), 1000); + AssertUI.HasClass(textbox, "invalid"); }); } @@ -592,8 +577,8 @@ public void Control_GridView_InvalidCssClass_TextBox_Both() textbox.Clear(); gridview.First("save-button", SelectByDataUi).Click(); - browser.WaitFor(() => AssertUI.HasClass(textbox, "invalid"), 1000); - browser.WaitFor(() => AssertUI.HasClass(validator, "invalid"), 1000); + AssertUI.HasClass(textbox, "invalid"); + AssertUI.HasClass(validator, "invalid"); }); } @@ -608,10 +593,12 @@ public void Control_GridView_InvalidCssClass_CheckBox() AssertUI.HasNotClass(gridview.First(".is-standalone > span"), "invalid"); gridview.First("edit-button", SelectByDataUi).Click(); + browser.WaitForPostback(); var checkBox = browser.First(".is-standalone > input"); checkBox.Click(); + browser.WaitForPostback(); gridview.First("save-button", SelectByDataUi).Click(); - browser.WaitFor(() => AssertUI.HasClass(gridview.First(".is-standalone > span"), "invalid"), 1000); + AssertUI.HasClass(gridview.First(".is-standalone > span"), "invalid"); }); } @@ -628,59 +615,43 @@ public void Control_GridView_GridViewSortChanged() // click the Name column in the first table tables[0].ElementAt("th", 1).Single("a").Click(); - browser.WaitFor(() => { - AssertUI.TextEquals(sortExpression, "Name"); - AssertUI.TextEquals(sortDescending, "false"); - }, 5000); + AssertUI.TextEquals(sortExpression, "Name"); + AssertUI.TextEquals(sortDescending, "false"); // click the Name column in the first table again to change sort direction tables[0].ElementAt("th", 1).Single("a").Click(); - browser.WaitFor(() => { - AssertUI.TextEquals(sortExpression, "Name"); - AssertUI.TextEquals(sortDescending, "true"); - }, 5000); + AssertUI.TextEquals(sortExpression, "Name"); + AssertUI.TextEquals(sortDescending, "true"); // click the Message received column in the first table tables[0].ElementAt("th", 3).Single("a").Click(); - browser.WaitFor(() => { - AssertUI.TextEquals(sortExpression, "MessageReceived"); - AssertUI.TextEquals(sortDescending, "false"); - }, 5000); + AssertUI.TextEquals(sortExpression, "MessageReceived"); + AssertUI.TextEquals(sortDescending, "false"); // click the Message received column in the first table again to change sort direction tables[0].ElementAt("th", 3).Single("a").Click(); - browser.WaitFor(() => { - AssertUI.TextEquals(sortExpression, "MessageReceived"); - AssertUI.TextEquals(sortDescending, "true"); - }, 5000); + AssertUI.TextEquals(sortExpression, "MessageReceived"); + AssertUI.TextEquals(sortDescending, "true"); // click the Name column in the second table tables[1].ElementAt("th", 1).Single("a").Click(); - browser.WaitFor(() => { - AssertUI.TextEquals(sortExpression, "Name"); - AssertUI.TextEquals(sortDescending, "false"); - }, 5000); + AssertUI.TextEquals(sortExpression, "Name"); + AssertUI.TextEquals(sortDescending, "false"); // click the Name column in the second table again - sort direction should remain unchanged tables[1].ElementAt("th", 1).Single("a").Click(); - browser.WaitFor(() => { - AssertUI.TextEquals(sortExpression, "Name"); - AssertUI.TextEquals(sortDescending, "false"); - }, 5000); + AssertUI.TextEquals(sortExpression, "Name"); + AssertUI.TextEquals(sortDescending, "false"); // click the Message received column in the first table tables[1].ElementAt("th", 3).Single("a").Click(); - browser.WaitFor(() => { - AssertUI.TextEquals(sortExpression, "MessageReceived"); - AssertUI.TextEquals(sortDescending, "false"); - }, 5000); + AssertUI.TextEquals(sortExpression, "MessageReceived"); + AssertUI.TextEquals(sortDescending, "false"); // click the Message received column in the second table again - sort direction should remain unchanged tables[1].ElementAt("th", 3).Single("a").Click(); - browser.WaitFor(() => { - AssertUI.TextEquals(sortExpression, "MessageReceived"); - AssertUI.TextEquals(sortDescending, "false"); - }, 5000); + AssertUI.TextEquals(sortExpression, "MessageReceived"); + AssertUI.TextEquals(sortDescending, "false"); }); } @@ -692,18 +663,22 @@ public void Control_GridView_NestedGridViewsWithInlineEditing() browser.WaitUntilDotvvmInited(); // Edit customer - browser.FindElements("input[type=button]").First(b => b.GetText() == "Edit Customer").Click().Wait(); + browser.FindElements("input[type=button]").First(b => b.GetText() == "Edit Customer").Click(); + browser.WaitForPostback(); // Edit customer name browser.First("input[type=text]").ClearInputByKeyboard().SendKeys("NewName"); // Save customer - browser.FindElements("input[type=button]").First(b => b.GetText() == "Save Customer").Click().Wait(); + browser.FindElements("input[type=button]").First(b => b.GetText() == "Save Customer").Click(); + browser.WaitForPostback(); // Edit shopping cart-item - browser.FindElements("input[type=button]").First(b => b.GetText() == "Edit Cart-item").Click().Wait(); + browser.FindElements("input[type=button]").First(b => b.GetText() == "Edit Cart-item").Click(); + browser.WaitForPostback(); // Edit quantity browser.First("input[type=text]").ClearInputByKeyboard().SendKeys("1111"); // Save shooping cart-item - browser.FindElements("input[type=button]").First(b => b.GetText() == "Save Cart-item").Click().Wait(); + browser.FindElements("input[type=button]").First(b => b.GetText() == "Save Cart-item").Click(); + browser.WaitForPostback(); }); } } diff --git a/src/DotVVM.Samples.Tests/Control/IncludeInPagePropertyTests.cs b/src/DotVVM.Samples.Tests/Control/IncludeInPagePropertyTests.cs index be98c2172e..b5f1ff74d9 100644 --- a/src/DotVVM.Samples.Tests/Control/IncludeInPagePropertyTests.cs +++ b/src/DotVVM.Samples.Tests/Control/IncludeInPagePropertyTests.cs @@ -93,7 +93,6 @@ private void CheckIncludeInPage(Action beforeSwitch, Action { browser.NavigateToUrl(SamplesRouteUrls.ControlSamples_IncludeInPageProperty_IncludeInPage); - browser.Wait(); beforeSwitch(browser); browser.Single("switch-includeInPage", this.SelectByDataUi).Click(); browser.WaitFor(() => { @@ -122,9 +121,9 @@ private void CheckTextBox(string dataUi, string text, bool checkVisible = false) if (checkVisible) { var switchVisible = browser.Single("switch-visible", this.SelectByDataUi); - switchVisible.Click().Wait(); + switchVisible.Click(); AssertUI.IsNotDisplayed(textBox); - switchVisible.Click().Wait(); + switchVisible.Click(); AssertUI.IsDisplayed(textBox); } }, browser => { diff --git a/src/DotVVM.Samples.Tests/Control/LinkButtonTests.cs b/src/DotVVM.Samples.Tests/Control/LinkButtonTests.cs index b36d585d97..5ac8e63761 100644 --- a/src/DotVVM.Samples.Tests/Control/LinkButtonTests.cs +++ b/src/DotVVM.Samples.Tests/Control/LinkButtonTests.cs @@ -20,19 +20,15 @@ public void Control_LinkButton_LinkButton() // try to click on a disabled button browser.Click("#EnabledLinkButton"); - browser.Wait(); AssertUI.InnerTextEquals(browser.Last("span"), "0"); // enable it browser.Click("input[type=checkbox]"); - browser.Wait(); browser.Click("#EnabledLinkButton"); - browser.Wait(); AssertUI.InnerTextEquals(browser.Last("span"), "1"); // try to click on a disabled button again browser.Click("#EnabledLinkButton"); - browser.Wait(); AssertUI.InnerTextEquals(browser.Last("span"), "1"); }); } diff --git a/src/DotVVM.Samples.Tests/Control/MultiSelectTests.cs b/src/DotVVM.Samples.Tests/Control/MultiSelectTests.cs index 6eaf4283ae..25795227c1 100644 --- a/src/DotVVM.Samples.Tests/Control/MultiSelectTests.cs +++ b/src/DotVVM.Samples.Tests/Control/MultiSelectTests.cs @@ -22,20 +22,20 @@ public void Control_MultiSelect_Binded() var selectedValues = browser.First("selected-values", SelectByDataUi); AssertUI.IsDisplayed(multiselect.Select(0)); - browser.WaitFor(() => AssertUI.InnerTextEquals(selectedValues, "Praha"), 2000, 30); + AssertUI.InnerTextEquals(selectedValues, "Praha"); // select second option from combobox multiselect.Select(1); - browser.WaitFor(() => AssertUI.InnerTextEquals(selectedValues, "Praha Brno"), 1000, 30); + AssertUI.InnerTextEquals(selectedValues, "Praha Brno"); // select third option from combobox multiselect.Select(2); - browser.WaitFor(() => AssertUI.InnerTextEquals(selectedValues, "Praha Brno Napajedla"), 1000, 30); + AssertUI.InnerTextEquals(selectedValues, "Praha Brno Napajedla"); // select third option from combobox multiselect.Children[0].Click(); multiselect.Children[1].Click(); - browser.WaitFor(() => AssertUI.InnerTextEquals(selectedValues, "Napajedla"), 1000, 30); + AssertUI.InnerTextEquals(selectedValues, "Napajedla"); }); } @@ -49,20 +49,20 @@ public void Control_MultiSelect_Hardcoded() var selectedValues = browser.First("selected-values", SelectByDataUi); AssertUI.IsDisplayed(multiselect.Select(0)); - browser.WaitFor(() => AssertUI.InnerTextEquals(selectedValues, "1"), 2000, 30); + AssertUI.InnerTextEquals(selectedValues, "1"); // select second option from combobox multiselect.Select(1); - browser.WaitFor(() => AssertUI.InnerTextEquals(selectedValues, "1 2"), 1000, 30); + AssertUI.InnerTextEquals(selectedValues, "1 2"); // select third option from combobox multiselect.Select(2); - browser.WaitFor(() => AssertUI.InnerTextEquals(selectedValues, "1 2 3"), 1000, 30); + AssertUI.InnerTextEquals(selectedValues, "1 2 3"); // select third option from combobox multiselect.Children[0].Click(); multiselect.Children[1].Click(); - browser.WaitFor(() => AssertUI.InnerTextEquals(selectedValues, "3"), 1000, 30); + AssertUI.InnerTextEquals(selectedValues, "3"); }); } } diff --git a/src/DotVVM.Samples.Tests/Control/NestedRepeaterTests.cs b/src/DotVVM.Samples.Tests/Control/NestedRepeaterTests.cs index 99af4de7eb..b72f1c2963 100644 --- a/src/DotVVM.Samples.Tests/Control/NestedRepeaterTests.cs +++ b/src/DotVVM.Samples.Tests/Control/NestedRepeaterTests.cs @@ -17,69 +17,44 @@ public void Control_Repeater_NestedRepeater() { RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.ControlSamples_Repeater_NestedRepeater); - browser.Wait(); var result = browser.First("#result"); browser.ElementAt("a", 0).Click(); - browser.WaitFor(() => { - AssertUI.InnerTextEquals(result, "Child 1 Subchild 1"); - }, 5000); + AssertUI.InnerTextEquals(result, "Child 1 Subchild 1"); browser.ElementAt("a", 1).Click(); - browser.WaitFor(() => { - AssertUI.InnerTextEquals(result, "Child 1 Subchild 2"); - }, 5000); + AssertUI.InnerTextEquals(result, "Child 1 Subchild 2"); browser.ElementAt("a", 2).Click(); - browser.WaitFor(() => { - AssertUI.InnerTextEquals(result, "Child 1 Subchild 3"); - }, 5000); + AssertUI.InnerTextEquals(result, "Child 1 Subchild 3"); browser.ElementAt("a", 3).Click(); - browser.WaitFor(() => { - AssertUI.InnerTextEquals(result, "Child 2 Subchild 1"); - }, 5000); + AssertUI.InnerTextEquals(result, "Child 2 Subchild 1"); browser.ElementAt("a", 4).Click(); - browser.WaitFor(() => { - AssertUI.InnerTextEquals(result, "Child 2 Subchild 2"); - }, 5000); + AssertUI.InnerTextEquals(result, "Child 2 Subchild 2"); browser.ElementAt("a", 5).Click(); - browser.WaitFor(() => { - AssertUI.InnerTextEquals(result, "Child 3 Subchild 1"); - }, 5000); + AssertUI.InnerTextEquals(result, "Child 3 Subchild 1"); browser.ElementAt("a", 6).Click(); - browser.WaitFor(() => { - AssertUI.InnerTextEquals(result, "Child 1 Subchild 1"); - }, 5000); + AssertUI.InnerTextEquals(result, "Child 1 Subchild 1"); browser.ElementAt("a", 7).Click(); - browser.WaitFor(() => { - AssertUI.InnerTextEquals(result, "Child 1 Subchild 2"); - }, 5000); + AssertUI.InnerTextEquals(result, "Child 1 Subchild 2"); browser.ElementAt("a", 8).Click(); - browser.WaitFor(() => { - AssertUI.InnerTextEquals(result, "Child 1 Subchild 3"); - }, 5000); + AssertUI.InnerTextEquals(result, "Child 1 Subchild 3"); browser.ElementAt("a", 9).Click(); - browser.WaitFor(() => { - AssertUI.InnerTextEquals(result, "Child 2 Subchild 1"); - }, 5000); + AssertUI.InnerTextEquals(result, "Child 2 Subchild 1"); browser.ElementAt("a", 10).Click(); - browser.WaitFor(() => { - AssertUI.InnerTextEquals(result, "Child 2 Subchild 2"); - }, 5000); + AssertUI.InnerTextEquals(result, "Child 2 Subchild 2"); browser.ElementAt("a", 11).Click(); - browser.WaitFor(() => { - AssertUI.InnerTextEquals(result, "Child 3 Subchild 1"); - }, 5000); + AssertUI.InnerTextEquals(result, "Child 3 Subchild 1"); }); } @@ -89,7 +64,6 @@ public void Control_Repeater_NestedRepeaterWithControl() RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.ControlSamples_Repeater_NestedRepeaterWithControl); browser.WaitUntilDotvvmInited(); - browser.Wait(500); var result = browser.First("#result"); var buttons = browser.FindElements("input[type=button]"); @@ -97,7 +71,7 @@ public void Control_Repeater_NestedRepeaterWithControl() int count = 1; foreach (var button in buttons) { - browser.WaitFor(() => AssertUI.InnerTextEquals(result, count.ToString()), 5000); + AssertUI.InnerTextEquals(result, count.ToString()); button?.Click(); count++; } diff --git a/src/DotVVM.Samples.Tests/Control/RadioButtonTests.cs b/src/DotVVM.Samples.Tests/Control/RadioButtonTests.cs index 8c601cf787..b9828d3b58 100644 --- a/src/DotVVM.Samples.Tests/Control/RadioButtonTests.cs +++ b/src/DotVVM.Samples.Tests/Control/RadioButtonTests.cs @@ -19,18 +19,15 @@ public void Control_RadioButton_RadioButton() { RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.ControlSamples_RadioButton_RadioButton); - browser.Wait(); browser.ElementAt("input[type=radio]", 2).Click(); browser.ElementAt("input[type=radio]", 3).Click(); browser.First("input[type=button]").Click(); - browser.Wait(); AssertUI.InnerTextEquals(browser.Last("span"), "4"); browser.ElementAt("input[type=radio]", 1).Click(); browser.First("input[type=button]").Click(); - browser.Wait(); AssertUI.InnerTextEquals(browser.Last("span"), "2"); }); @@ -51,26 +48,26 @@ public void Control_RadioButton_Nullable() AssertUI.InnerTextEquals(span, ""); radio1.Click(); - browser.WaitFor(() => AssertUI.InnerTextEquals(span, "First"), 5000); + AssertUI.InnerTextEquals(span, "First"); radio2.Click(); - browser.WaitFor(() => AssertUI.InnerTextEquals(span, "Second"), 5000); + AssertUI.InnerTextEquals(span, "Second"); browser.ElementAt("input[type=button]", 0).Click(); - browser.WaitFor(() => AssertUI.InnerTextEquals(span, "Second"), 5000); + AssertUI.InnerTextEquals(span, "Second"); AssertUI.IsChecked(radio2); browser.ElementAt("input[type=button]", 1).Click(); - browser.WaitFor(() => AssertUI.InnerTextEquals(span, ""), 5000); + AssertUI.InnerTextEquals(span, ""); AssertUI.IsNotChecked(radio1); AssertUI.IsNotChecked(radio2); browser.ElementAt("input[type=button]", 2).Click(); - browser.WaitFor(() => AssertUI.InnerTextEquals(span, "First"), 5000); + AssertUI.InnerTextEquals(span, "First"); AssertUI.IsChecked(radio1); browser.ElementAt("input[type=button]", 3).Click(); - browser.WaitFor(() => AssertUI.InnerTextEquals(span, "Second"), 5000); + AssertUI.InnerTextEquals(span, "Second"); AssertUI.IsChecked(radio2); }); } @@ -102,7 +99,7 @@ public void Control_RadioButton_RadioButtonObjects() AssertUI.TextEquals(ul.ElementAt("li", 0), "3: Blue"); // click button - browser.Single("input[type=button]").Click().Wait(500); + browser.Single("input[type=button]").Click(); AssertUI.IsNotChecked(radios[0]); AssertUI.IsChecked(radios[1]); AssertUI.IsNotChecked(radios[2]); diff --git a/src/DotVVM.Samples.Tests/Control/RepeaterTests.cs b/src/DotVVM.Samples.Tests/Control/RepeaterTests.cs index 24aa8762fc..2e4f56f8d6 100644 --- a/src/DotVVM.Samples.Tests/Control/RepeaterTests.cs +++ b/src/DotVVM.Samples.Tests/Control/RepeaterTests.cs @@ -4,6 +4,7 @@ using DotVVM.Testing.Abstractions; using Riganti.Selenium.Core; using Riganti.Selenium.Core.Abstractions; +using Riganti.Selenium.DotVVM; using Xunit; using Xunit.Abstractions; @@ -20,7 +21,6 @@ public void Control_Repeater_DataSourceNull() { RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.ControlSamples_Repeater_DataSourceNull); - browser.Wait(); var clientRepeater = browser.Single("client-repeater", this.SelectByDataUi); var serverRepeater = browser.Single("server-repeater", this.SelectByDataUi); @@ -29,7 +29,7 @@ public void Control_Repeater_DataSourceNull() Assert.Equal(0, serverRepeater.Children.Count); var button = browser.Single("set-collection-button", this.SelectByDataUi); - button.Click().Wait(); + button.Click(); clientRepeater = browser.Single("client-repeater", this.SelectByDataUi); serverRepeater = browser.Single("server-repeater", this.SelectByDataUi); @@ -44,7 +44,6 @@ public void Control_Repeater_RepeaterAsSeparator() { RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.ControlSamples_Repeater_RepeaterAsSeparator); - browser.Wait(); var repeater = browser.Single("root-repeater", this.SelectByDataUi); @@ -64,6 +63,7 @@ public void Control_Repeater_RepeaterAsSeparator() } browser.Single("add-item-button", SelectByDataUi).Click(); + browser.WaitForPostback(); } }); } @@ -74,7 +74,6 @@ public void Control_Repeater_RepeaterAsSeparator_CorrectBindingContext() { RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.ControlSamples_Repeater_RepeaterAsSeparator); - browser.Wait(); var repeater = browser.Single("root-repeater", this.SelectByDataUi); @@ -87,7 +86,7 @@ public void Control_Repeater_RepeaterAsSeparator_CorrectBindingContext() foreach (var button in incrementButtons) { button.Click(); - browser.WaitFor(() => AssertUI.InnerTextEquals(counterValue, counter.ToString()), 5000, "Counter value invalid!"); + AssertUI.InnerTextEquals(counterValue, counter.ToString(), failureMessage: "Counter value invalid!"); counter++; } @@ -97,7 +96,7 @@ public void Control_Repeater_RepeaterAsSeparator_CorrectBindingContext() foreach (var button in incrementButtons) { button.Click(); - browser.WaitFor(() => AssertUI.InnerTextEquals(counterValue, counter.ToString()), 5000, "Counter value invalid!"); + AssertUI.InnerTextEquals(counterValue, counter.ToString(),failureMessage:"Counter value invalid!"); counter++; } }); @@ -257,7 +256,6 @@ public void Control_Repeater_RequiredResource() { RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.ControlSamples_Repeater_RequiredResource); - browser.Wait(); var clientRepeater = browser.Single("client-repeater", this.SelectByDataUi); var serverRepeater = browser.Single("server-repeater", this.SelectByDataUi); @@ -272,7 +270,6 @@ public void Control_Repeater_CollectionIndex() { RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.ControlSamples_Repeater_CollectionIndex); - browser.Wait(); var clientRenderedItems = browser.FindElements("client-rendered-item", this.SelectByDataUi); var serverRenderedItems = browser.FindElements("server-rendered-item", this.SelectByDataUi); @@ -284,9 +281,7 @@ public void Control_Repeater_CollectionIndex() var counter = 0; void CheckCounter() { - browser.WaitFor(() => { - AssertUI.InnerTextEquals(counterElement, counter.ToString()); - }, 5000); + AssertUI.InnerTextEquals(counterElement, counter.ToString()); } foreach (var item in allItems) diff --git a/src/DotVVM.Samples.Tests/Control/RoleViewTests.cs b/src/DotVVM.Samples.Tests/Control/RoleViewTests.cs index ac41e6c7a2..c6bde5f7cb 100644 --- a/src/DotVVM.Samples.Tests/Control/RoleViewTests.cs +++ b/src/DotVVM.Samples.Tests/Control/RoleViewTests.cs @@ -3,6 +3,7 @@ using DotVVM.Testing.Abstractions; using Xunit; using Riganti.Selenium.Core; +using Riganti.Selenium.DotVVM; namespace DotVVM.Samples.Tests.Control { @@ -20,50 +21,58 @@ public void Control_RoleView_RoleViewTest() void AssertInnerTextEquals(string selector, string text) { - browser.WaitFor(() => { - AssertUI.InnerTextEquals( + AssertUI.InnerTextEquals( browser.FindElements(selector).ThrowIfDifferentCountThan(1).First(), text); - }, 5000); } // make sure we are signed out (first should show IfNotMember, second should be hidden) browser.First("input[value='Sign Out']").Click(); - + browser.WaitForPostback(); AssertInnerTextEquals(".result1", "I am not a member!"); AssertUI.IsNotDisplayed(browser, ".result2"); // sign in as admin (both should show IsMember content) browser.First("input[type=checkbox][value=admin]").Click(); + browser.WaitForPostback(); browser.First("input[value='Sign In']").Click(); + browser.WaitForPostback(); AssertInnerTextEquals(".result1", "I am a member!"); AssertInnerTextEquals(".result2", "I am a member!"); // sign in as moderator and headhunter (both should show IsMember content) browser.First("input[type=checkbox][value=moderator]").Click(); + browser.WaitForPostback(); browser.First("input[type=checkbox][value=headhunter]").Click(); + browser.WaitForPostback(); browser.First("input[value='Sign In']").Click(); + browser.WaitForPostback(); AssertInnerTextEquals(".result1", "I am a member!"); AssertInnerTextEquals(".result2", "I am a member!"); // sign in as headhunter only (both should be visible but show that user is not a member) browser.First("input[type=checkbox][value=headhunter]").Click(); + browser.WaitForPostback(); browser.First("input[value='Sign In']").Click(); + browser.WaitForPostback(); AssertInnerTextEquals(".result1", "I am not a member!"); AssertInnerTextEquals(".result2", "I am not a member!"); // sign in as tester only (both should show IsMember content) browser.First("input[type=checkbox][value=tester]").Click(); + browser.WaitForPostback(); browser.First("input[value='Sign In']").Click(); + browser.WaitForPostback(); AssertInnerTextEquals(".result1", "I am a member!"); AssertInnerTextEquals(".result2", "I am a member!"); // sign out (first should show IfNotMember, second should be hidden) browser.First("input[value='Sign Out']").Click(); + browser.WaitForPostback(); AssertInnerTextEquals(".result1", "I am not a member!"); AssertUI.IsNotDisplayed(browser, ".result2"); diff --git a/src/DotVVM.Samples.Tests/Control/SpaContentPlaceHolderTests.cs b/src/DotVVM.Samples.Tests/Control/SpaContentPlaceHolderTests.cs index efc9f1f756..403ff284d4 100644 --- a/src/DotVVM.Samples.Tests/Control/SpaContentPlaceHolderTests.cs +++ b/src/DotVVM.Samples.Tests/Control/SpaContentPlaceHolderTests.cs @@ -22,16 +22,13 @@ public void Control_SpaContentPlaceHolder_SpaContentPlaceHolder_HistoryApi() { RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.ControlSamples_SpaContentPlaceHolder_HistoryApi_Default); - browser.Wait(2000); // verify the URL after redirect to the DefaultRoute AssertUI.AlertTextEquals(browser, "javascript 2 resource loaded!"); browser.ConfirmAlert(); - browser.Wait(2000); // navigate to SPA using link browser.ElementAt("a", 1).Click(); - browser.Wait(2000); AssertUI.AlertTextEquals(browser, "javascript resource loaded!"); browser.ConfirmAlert(); @@ -39,63 +36,49 @@ public void Control_SpaContentPlaceHolder_SpaContentPlaceHolder_HistoryApi() // go to first page browser.ElementAt("a", 0).Click(); - browser.Wait(); AssertUI.UrlEquals(browser, browser.BaseUrl + SamplesRouteUrls.ControlSamples_SpaContentPlaceHolder_HistoryApi_PageA + "/16"); // test first page AssertUI.TextEquals(browser.ElementAt("span", 0), "0"); browser.Click("input[type=button]"); - browser.Wait(); browser.Click("input[type=button]"); - browser.Wait(); browser.Click("input[type=button]"); - browser.Wait(); AssertUI.TextEquals(browser.ElementAt("span", 0), "3"); // go to second page browser.FindElements("a").Single(l => l.GetText() == "Go to Task List").Click(); - browser.Wait(); AssertUI.UrlEquals(browser, browser.BaseUrl + SamplesRouteUrls.ControlSamples_SpaContentPlaceHolder_HistoryApi_PageB); // try the task list - browser.FindElements(".table tr").ThrowIfDifferentCountThan(3); + browser.WaitFor(()=> browser.FindElements(".table tr").ThrowIfDifferentCountThan(3),1000); browser.SendKeys("input[type=text]", "DotVVM rocks!"); browser.Click("input[type=button]"); - browser.Wait(); - browser.FindElements(".table tr").ThrowIfDifferentCountThan(4); + browser.WaitFor(() => browser.FindElements(".table tr").ThrowIfDifferentCountThan(4), 1000); browser.Last("tr a").Click(); - browser.Wait(); AssertUI.HasClass(browser.Last(".table tr"), "completed"); // test the browse back button browser.NavigateBack(); - browser.Wait(); AssertUI.UrlEquals(browser, browser.BaseUrl + SamplesRouteUrls.ControlSamples_SpaContentPlaceHolder_HistoryApi_PageA + "/16"); // test first page AssertUI.TextEquals(browser.ElementAt("span", 0), "0"); browser.Click("input[type=button]"); - browser.Wait(); browser.Click("input[type=button]"); - browser.Wait(); browser.Click("input[type=button]"); - browser.Wait(); AssertUI.TextEquals(browser.ElementAt("span", 0), "3"); // test the forward button browser.NavigateForward(); - browser.Wait(); browser.FindElements(".table tr").ThrowIfDifferentCountThan(3); // test the redirect inside SPA browser.First(".navigation input[type=button]").Click(); - browser.Wait(2000); AssertUI.UrlEquals(browser, browser.BaseUrl + SamplesRouteUrls.ControlSamples_SpaContentPlaceHolder_HistoryApi_PageA + "/15"); // test the redirect outside SPA AssertUI.TextEquals(browser.ElementAt("span", 0), "0"); browser.FindElements("a").Single(l => l.GetText().Contains("Exit SPA")).Click(); - browser.Wait(); AssertUI.UrlEquals(browser, browser.BaseUrl + SamplesRouteUrls.ComplexSamples_TaskList_ServerRenderedTaskList); }); } @@ -145,63 +128,49 @@ public void Control_SpaContentPlaceHolder_SpaContentPlaceHolder_HistoryApi_Enter // go to first page browser.First("a").Click(); - browser.Wait(); AssertUI.UrlEquals(browser, browser.BaseUrl + SamplesRouteUrls.ControlSamples_SpaContentPlaceHolder_HistoryApi_PageA + "/16"); // test first page AssertUI.TextEquals(browser.ElementAt("span", 0), "0"); browser.Click("input[type=button]"); - browser.Wait(); browser.Click("input[type=button]"); - browser.Wait(); browser.Click("input[type=button]"); - browser.Wait(); AssertUI.TextEquals(browser.ElementAt("span", 0), "3"); // go to second page browser.FindElements("a").Single(l => l.GetText() == "Go to Task List").Click(); - browser.Wait(); AssertUI.UrlEquals(browser, browser.BaseUrl + SamplesRouteUrls.ControlSamples_SpaContentPlaceHolder_HistoryApi_PageB); // try the task list - browser.FindElements(".table tr").ThrowIfDifferentCountThan(3); + browser.WaitFor(() => browser.FindElements(".table tr").ThrowIfDifferentCountThan(3),1000); browser.SendKeys("input[type=text]", "DotVVM rocks!"); browser.Click("input[type=button]"); - browser.Wait(); - browser.FindElements(".table tr").ThrowIfDifferentCountThan(4); + browser.WaitFor(()=>browser.FindElements(".table tr").ThrowIfDifferentCountThan(4),1000); browser.Last("tr a").Click(); - browser.Wait(); AssertUI.HasClass(browser.Last(".table tr"), "completed"); // test the browse back button browser.NavigateBack(); - browser.Wait(); AssertUI.UrlEquals(browser, browser.BaseUrl + SamplesRouteUrls.ControlSamples_SpaContentPlaceHolder_HistoryApi_PageA + "/16"); // test first page AssertUI.TextEquals(browser.ElementAt("span", 0), "0"); browser.Click("input[type=button]"); - browser.Wait(); browser.Click("input[type=button]"); - browser.Wait(); browser.Click("input[type=button]"); - browser.Wait(); AssertUI.TextEquals(browser.ElementAt("span", 0), "3"); // test the forward button browser.NavigateForward(); - browser.Wait(); browser.FindElements(".table tr").ThrowIfDifferentCountThan(3); // test the redirect inside SPA browser.First(".navigation input[type=button]").Click(); - browser.Wait(2000); AssertUI.UrlEquals(browser, browser.BaseUrl + SamplesRouteUrls.ControlSamples_SpaContentPlaceHolder_HistoryApi_PageA + "/15"); // test the redirect outside SPA AssertUI.TextEquals(browser.ElementAt("span", 0), "0"); browser.FindElements("a").Single(l => l.GetText().Contains("Exit SPA")).Click(); - browser.Wait(); AssertUI.UrlEquals(browser, browser.BaseUrl + SamplesRouteUrls.ComplexSamples_TaskList_ServerRenderedTaskList); }); } @@ -214,7 +183,6 @@ public void Control_SpaContentPlaceHolder_SpaContentPlaceHolder_HistoryApi_Enter { RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.ControlSamples_SpaContentPlaceHolder_HistoryApi_Default + "#!/" + SamplesRouteUrls.ControlSamples_SpaContentPlaceHolder_HistoryApi_PageB); - browser.Wait(); // verify the URL after redirect to the desired page browser.WaitFor(() => { @@ -239,33 +207,32 @@ void CheckOnlyOneSpaRouteLink(IBrowserWrapper browser) RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.ControlSamples_SpaContentPlaceHolder_HistoryApi_MultiSpaDefault); - browser.Wait(); var defaultUrl = browser.CurrentUrl; var baseUrl = browser.CurrentUrl.Substring(0, browser.CurrentUrlPath.LastIndexOf('/')); var routeLinks = browser.FindElements("a"); - routeLinks.First().Click().Wait(); + routeLinks.First().Click(); AssertUI.UrlEquals(browser, $"{baseUrl}/Spa1PageA"); CheckOnlyOneSpaRouteLink(browser); browser.FindElements("a").Single(elem => elem.GetText() == "SPA1: Go to Second Page"); - routeLinks.Skip(1).First().Click().Wait(); + routeLinks.Skip(1).First().Click(); AssertUI.UrlEquals(browser, $"{baseUrl}/Spa1PageB"); CheckOnlyOneSpaRouteLink(browser); browser.FindElements("a").Single(elem => elem.GetText() == "SPA1: Go to First Page"); - routeLinks.Skip(2).First().Click().Wait(); + routeLinks.Skip(2).First().Click(); AssertUI.UrlEquals(browser, $"{baseUrl}/Spa2PageA"); CheckOnlyOneSpaRouteLink(browser); browser.FindElements("a").Single(elem => elem.GetText() == "SPA2: Go to Second Page"); - routeLinks.Skip(3).First().Click().Wait(); + routeLinks.Skip(3).First().Click(); AssertUI.UrlEquals(browser, $"{baseUrl}/Spa2PageB"); CheckOnlyOneSpaRouteLink(browser); browser.FindElements("a").Single(elem => elem.GetText().Contains("SPA2: Go to First Page")); - routeLinks.Skip(4).First().Click().Wait(); + routeLinks.Skip(4).First().Click(); AssertUI.UrlEquals(browser, $"{baseUrl}/Spa1Spa2Page"); browser.FindElements("span").Single(elem => elem.GetText() == "SPA1: Hello World!"); browser.FindElements("span").Single(elem => elem.GetText() == "SPA2: Hello World!"); diff --git a/src/DotVVM.Samples.Tests/Control/TextBoxTests.cs b/src/DotVVM.Samples.Tests/Control/TextBoxTests.cs index cb484c2d57..d43ffe51a8 100644 --- a/src/DotVVM.Samples.Tests/Control/TextBoxTests.cs +++ b/src/DotVVM.Samples.Tests/Control/TextBoxTests.cs @@ -22,7 +22,6 @@ public void Control_TextBox_TextBox_FormatDoubleProperty() AssertUI.TextEquals(browser.Single("[data-ui='textBox']"), "0.00"); browser.Single("[data-ui='button']").Click(); - browser.Wait(500); AssertUI.TextEquals(browser.Single("[data-ui='textBox']"), "10.50"); }); @@ -37,7 +36,6 @@ public void Control_TextBox_IntBoundTextBox() browser.ElementAt("input", 0).Clear(); browser.ElementAt("input", 0).SendKeys("hello"); browser.ElementAt("input[type=button]", 0).Click(); - browser.Wait(); AssertUI.Value(browser.ElementAt("input", 0), "hello"); AssertUI.InnerTextEquals(browser.ElementAt("span", 0), "0"); @@ -170,7 +168,7 @@ public void Control_TextBox_StringFormat(string cultureName, string url, string //write new valid values dateTextBox.Clear().SendKeys(dateResult2); numberTextbox.Clear().SendKeys(2000.ToString("n0", culture)); - dateTextBox.Click().Wait(); + dateTextBox.Click(); //check new values AssertUI.InnerTextEquals(dateText, new DateTime(2018, 12, 27).ToString("G", culture)); @@ -194,7 +192,7 @@ public void Control_TextBox_StringFormat(string cultureName, string url, string //write new valid values dateTextBox.Clear().SendKeys(new DateTime(2018, 1, 1).ToString("d", culture)); numberTextbox.Clear().SendKeys(1000.550277.ToString(culture)); - dateTextBox.Click().Wait(); + dateTextBox.Click(); //check new values AssertUI.InnerTextEquals(dateText, new DateTime(2018, 1, 1).ToString("G", culture)); @@ -202,6 +200,15 @@ public void Control_TextBox_StringFormat(string cultureName, string url, string AssertUI.Attribute(numberTextbox, "value", 1000.550277.ToString("n4", culture)); AssertUI.Attribute(dateTextBox, "value", dateResult3); + + // try to supply different date formats + dateTextBox.Clear().SendKeys(new DateTime(2020, 2, 16).ToString("G", culture)).SendKeys(Keys.Tab); + AssertUI.Attribute(dateTextBox, "value", new DateTime(2020, 2, 16).ToString("d", culture)); + AssertUI.InnerTextEquals(dateText, new DateTime(2020, 2, 16).ToString("G", culture)); + + nullableDateTextBox.Clear().SendKeys(new DateTime(2020, 4, 2).ToString("d", culture)).SendKeys(Keys.Tab); + AssertUI.Attribute(nullableDateTextBox, "value", new DateTime(2020, 4, 2).ToString("G", culture)); + AssertUI.InnerTextEquals(nullableDateText, new DateTime(2020, 4, 2).ToString("G", culture)); }); } @@ -229,39 +236,31 @@ void ClearInput(IElementWrapper element) IElementWrapper numberTextbox = null; IElementWrapper numberValueText = null; - browser.WaitFor(() => { - numberTextbox = browser.First("#bindingNumberFormatTextbox"); - AssertUI.Attribute(numberTextbox, "value", 0.ToString("N", culture)); + numberTextbox = browser.First("#bindingNumberFormatTextbox"); + AssertUI.Attribute(numberTextbox, "value", 0.ToString("N", culture)); - numberValueText = browser.First("#resultNumberValueText"); - AssertUI.InnerTextEquals(numberValueText, 0.ToString(culture)); - }, 2000); + numberValueText = browser.First("#resultNumberValueText"); + AssertUI.InnerTextEquals(numberValueText, 0.ToString(culture)); // send new values ClearInput(numberTextbox); numberTextbox.SendKeys("42") - .SendEnterKey() - .Wait(); + .SendEnterKey(); LoseFocus(); // check new values - browser.WaitFor(() => { - AssertUI.InnerTextEquals(numberValueText, 42.ToString(culture)); - AssertUI.Attribute(numberTextbox, "value", 42.ToString("N", culture)); - }, 5000); + AssertUI.InnerTextEquals(numberValueText, 42.ToString(culture)); + AssertUI.Attribute(numberTextbox, "value", 42.ToString("N", culture)); // send new values ClearInput(numberTextbox); numberTextbox.SendKeys(123.456789.ToString(culture)) - .SendEnterKey() - .Wait(); + .SendEnterKey(); LoseFocus(); // check new values - browser.WaitFor(() => { - AssertUI.InnerTextEquals(numberValueText, 123.456789.ToString(culture)); - AssertUI.Attribute(numberTextbox, "value", 123.456789.ToString("N", culture)); - }, 5000); + AssertUI.InnerTextEquals(numberValueText, 123.456789.ToString(culture)); + AssertUI.Attribute(numberTextbox, "value", 123.456789.ToString("N", culture)); }); } diff --git a/src/DotVVM.Samples.Tests/Control/UpdateProgressTests.cs b/src/DotVVM.Samples.Tests/Control/UpdateProgressTests.cs index 60062bf9ab..2b332c3215 100644 --- a/src/DotVVM.Samples.Tests/Control/UpdateProgressTests.cs +++ b/src/DotVVM.Samples.Tests/Control/UpdateProgressTests.cs @@ -26,7 +26,6 @@ public void Control_UpdateProgress_UpdateProgress() { RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.ControlSamples_UpdateProgress_UpdateProgress); - browser.Wait(); // click the button and verify that the progress appears and disappears again AssertUI.IsNotDisplayed(browser.First(".update-progress")); @@ -38,7 +37,6 @@ public void Control_UpdateProgress_UpdateProgress() // click the second button and verify that the progress appears and disappears again AssertUI.IsNotDisplayed(browser.First(".update-progress")); browser.ElementAt("input[type=button]", 1).Click(); - browser.Wait(1000); AssertUI.IsNotDisplayed(browser.First(".update-progress")); }); } @@ -49,7 +47,6 @@ public void Control_UpdateProgress_UpdateProgressDelayLongTest() { RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.ControlSamples_UpdateProgress_UpdateProgressDelay); - browser.Wait(); // click the button with long test and verify that the progress appears and disappears again AssertUI.IsNotDisplayed(browser.First(".update-progress")); @@ -73,13 +70,12 @@ public void Control_UpdateProgress_UpdateProgressDelayShortTest() { RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.ControlSamples_UpdateProgress_UpdateProgressDelay); - browser.Wait(); // click the second button with short test and verify that the progress does not appear AssertUI.IsNotDisplayed(browser.First(".update-progress")); browser.First(".short-test").Click(); - browser.WaitFor(() => AssertUI.IsNotDisplayed(browser.First(".update-progress")), 3000); + AssertUI.IsNotDisplayed(browser.First(".update-progress")); }); } @@ -90,7 +86,7 @@ public void Control_UpdateProgress_UpdateProgressDelayInterruptTest() { RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.ControlSamples_UpdateProgress_UpdateProgressDelay); - browser.Wait(); + browser.Wait(); var updateProgressControl = browser.First(".update-progress"); // click the second button with short test and verify that the progress does not appear @@ -128,7 +124,6 @@ public void Control_UpdateProgress_UpdateProgressQueues() { RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.ControlSamples_UpdateProgress_UpdateProgressQueues); - browser.Wait(); var button1 = browser.ElementAt("input[type=button]", 0); var button2 = browser.ElementAt("input[type=button]", 1); @@ -201,7 +196,6 @@ public void Control_UpdateProgress_SPA_Redirect(string route) { RunInAllBrowsers(browser => { browser.NavigateToUrl(route); - browser.Wait(); var spaTextElement = browser.Single("text", SelectByDataUi); var goToSpa2Btn = browser.Single("btn-2", SelectByDataUi); @@ -247,7 +241,6 @@ public void Control_UpdateProgress_SPA_LongAction() { RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.ControlSamples_UpdateProgress_UpdateProgressRedirectSPA1); - browser.Wait(); var progress = browser.Single("progress", By.Id); AssertUI.IsNotDisplayed(progress); diff --git a/src/DotVVM.Samples.Tests/Control/ValidationSummaryTests.cs b/src/DotVVM.Samples.Tests/Control/ValidationSummaryTests.cs index 0c1ad1e131..e13fa57ea0 100644 --- a/src/DotVVM.Samples.Tests/Control/ValidationSummaryTests.cs +++ b/src/DotVVM.Samples.Tests/Control/ValidationSummaryTests.cs @@ -19,12 +19,12 @@ public void Control_ValidationSummary_RecursiveValidationSummary() RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.ControlSamples_ValidationSummary_RecursiveValidationSummary); - browser.ElementAt("input[type=button]", 0).Click().Wait(); + browser.ElementAt("input[type=button]", 0).Click(); browser.ElementAt("ul", 0).FindElements("li").ThrowIfDifferentCountThan(2); AssertUI.InnerTextEquals(browser.First("#result"), "false"); - browser.ElementAt("input[type=button]", 1).Click().Wait(); + browser.ElementAt("input[type=button]", 1).Click(); browser.ElementAt("ul", 1).FindElements("li").ThrowIfDifferentCountThan(1); AssertUI.InnerTextEquals(browser.First("#result"), "false"); }); @@ -39,12 +39,12 @@ public void Control_ValidationSummary_HideWhenValid() AssertUI.IsNotDisplayed(browser.ElementAt("ul", 0)); // Generate error - browser.ElementAt("input[type=button]", 0).Click().Wait(); + browser.ElementAt("input[type=button]", 0).Click(); AssertUI.IsDisplayed(browser.ElementAt("ul", 0)); // Fix the error browser.SendKeys("input[type=text]", "message"); - browser.ElementAt("input[type=button]", 0).Click().Wait(); + browser.ElementAt("input[type=button]", 0).Click(); AssertUI.IsNotDisplayed(browser.ElementAt("ul", 0)); }); } diff --git a/src/DotVVM.Samples.Tests/DotVVM.Samples.Tests.csproj b/src/DotVVM.Samples.Tests/DotVVM.Samples.Tests.csproj index b7ed8e6ecc..2005b8b3d7 100644 --- a/src/DotVVM.Samples.Tests/DotVVM.Samples.Tests.csproj +++ b/src/DotVVM.Samples.Tests/DotVVM.Samples.Tests.csproj @@ -12,14 +12,13 @@ - - - - - - - - + + + + + + +
diff --git a/src/DotVVM.Samples.Tests/DotVVMAssertModified.cs b/src/DotVVM.Samples.Tests/DotVVMAssertModified.cs new file mode 100644 index 0000000000..401303368c --- /dev/null +++ b/src/DotVVM.Samples.Tests/DotVVMAssertModified.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Riganti.Selenium.Core; +using Riganti.Selenium.Core.Abstractions; +using Riganti.Selenium.DotVVM; + +namespace DotVVM.Samples.Tests +{ + public static class DotVVMAssertModified + { + public static void UploadFile(IElementWrapper element, string fullFileName) + { + if (element.BrowserWrapper.IsDotvvmPage()) + { + element.BrowserWrapper.LogVerbose("Selenium.DotVVM : Uploading file"); + var name = element.GetTagName(); + if (name == "a" && element.HasAttribute("onclick") && (element.GetAttribute("onclick")?.Contains("showUploadDialog") ?? false)) + { + element = element.ParentElement.ParentElement; + } + + if (element.GetTagName() == "div") + { + var fileInput = element.Single("input[type=file]"); + fileInput.SendKeys(fullFileName); + + element.Wait(element.ActionWaitTime); + return; + } + + element.BrowserWrapper.LogVerbose("Selenium.DotVVM : Cannot identify DotVVM scenario. Uploading over standard procedure."); + } + + element.BrowserWrapper.OpenInputFileDialog(element, fullFileName); + } + + } +} diff --git a/src/DotVVM.Samples.Tests/Feature/ActionFilterErrorHandlingTest.cs b/src/DotVVM.Samples.Tests/Feature/ActionFilterErrorHandlingTest.cs index eef8d92e99..8bccc4c15d 100644 --- a/src/DotVVM.Samples.Tests/Feature/ActionFilterErrorHandlingTest.cs +++ b/src/DotVVM.Samples.Tests/Feature/ActionFilterErrorHandlingTest.cs @@ -35,7 +35,6 @@ public void Feature_ActionFilterErrorHandling_ActionFilterErrorHandling_PageExce { RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.FeatureSamples_ActionFilterErrorHandling_ActionFilterPageErrorHandling); - browser.Wait(1000); AssertUI.Url(browser, u => u.Contains("error500")); }); } @@ -46,18 +45,14 @@ public void Feature_ActionFilterErrorHandling_ActionFilterRedirect() RunInAllBrowsers(browser => { // try the first button browser.NavigateToUrl(SamplesRouteUrls.FeatureSamples_ActionFilterErrorHandling_ActionFilterRedirect); - browser.Wait(1000); AssertUI.Url(browser, u => !u.Contains("?redirected=true")); browser.ElementAt("input", 0).Click(); - browser.Wait(1000); AssertUI.Url(browser, u => u.Contains("?redirected=true")); // try the second button browser.NavigateToUrl(SamplesRouteUrls.FeatureSamples_ActionFilterErrorHandling_ActionFilterRedirect); - browser.Wait(1000); AssertUI.Url(browser, u => !u.Contains("?redirected=true")); browser.ElementAt("input", 1).Click(); - browser.Wait(1000); AssertUI.Url(browser, u => u.Contains("?redirected=true")); }); } diff --git a/src/DotVVM.Samples.Tests/Feature/BindingContextsTests.cs b/src/DotVVM.Samples.Tests/Feature/BindingContextsTests.cs index 0a74977cda..c6f12c91af 100644 --- a/src/DotVVM.Samples.Tests/Feature/BindingContextsTests.cs +++ b/src/DotVVM.Samples.Tests/Feature/BindingContextsTests.cs @@ -14,16 +14,13 @@ public void Feature_BindingContexts_BindingContext() { RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.FeatureSamples_BindingContexts_BindingContext); - browser.Wait(1000); var linkCount = browser.FindElements("a").Count; for (var i = 0; i < linkCount; i++) { var link = browser.ElementAt("a", i); link.Click(); - browser.WaitFor(() => { - AssertUI.InnerTextEquals(browser.Single(".result"), link.GetInnerText()); - }, 3000, 50); + AssertUI.InnerTextEquals(browser.Single(".result"), link.GetInnerText()); } }); } @@ -35,13 +32,17 @@ public void Feature_BindingContexts_CollectionContext() foreach (var a in new[] { "Client", "Server" }) { browser.NavigateToUrl(SamplesRouteUrls.FeatureSamples_BindingContexts_CollectionContext + $"?renderMode={a}"); - browser.Wait(1000); var elements = browser.FindElements(By.ClassName("collection-index")); elements.ThrowIfSequenceEmpty(); - elements.ForEach(e => AssertUI.InnerTextEquals(e, elements.IndexOf(e).ToString())); + int i = 0; + foreach(var e in elements){ + AssertUI.InnerTextEquals(e, (i++).ToString()); + } + } }); + } public BindingContextsTests(ITestOutputHelper output) : base(output) diff --git a/src/DotVVM.Samples.Tests/Feature/CommandActionFilterTests.cs b/src/DotVVM.Samples.Tests/Feature/CommandActionFilterTests.cs new file mode 100644 index 0000000000..5aadb6802d --- /dev/null +++ b/src/DotVVM.Samples.Tests/Feature/CommandActionFilterTests.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Text; +using DotVVM.Samples.Tests.Base; +using DotVVM.Testing.Abstractions; +using Riganti.Selenium.Core; +using Riganti.Selenium.DotVVM; +using Xunit; +using Xunit.Abstractions; + +namespace DotVVM.Samples.Tests.Feature +{ + public class CommandActionFilterTests : AppSeleniumTest + { + [Fact] + public void Feature_CommandActionFilter_CallbacksReceivedTest() + { + RunInAllBrowsers(browser => { + browser.NavigateToUrl(SamplesRouteUrls.FeatureSamples_CommandActionFilter_CommandActionFilter); + browser.WaitUntilDotvvmInited(); + + browser.Click("input[type=button]"); + AssertUI.InnerText(browser.First("span"), s => s.Contains("SUCCESS"), "OnCommandExecutingAsync or OnCommandExecuted was not called!"); + }); + } + + public CommandActionFilterTests(ITestOutputHelper output) : base(output) + { + } + } +} diff --git a/src/DotVVM.Samples.Tests/Feature/CommandArgumentsTests.cs b/src/DotVVM.Samples.Tests/Feature/CommandArgumentsTests.cs index 07f296dc31..99d8b7c4c2 100644 --- a/src/DotVVM.Samples.Tests/Feature/CommandArgumentsTests.cs +++ b/src/DotVVM.Samples.Tests/Feature/CommandArgumentsTests.cs @@ -31,9 +31,7 @@ public void Feature_CommandArguments_CommandArguments() alert.SendKeys(Value); alert.Accept(); - browser.WaitFor(() => { - AssertUI.InnerTextEquals(text, Value); - }, 2000); + AssertUI.InnerTextEquals(text, Value); }); } @@ -50,9 +48,7 @@ public void Feature_CommandArguments_CommandArgumentTypes() browser.Single("[data-ui='button2'] input[type=text]").Clear().SendKeys(Value); browser.Single("[data-ui='button2'] button").Click(); - browser.WaitFor(() => { - AssertUI.InnerTextEquals(text, Value + "(from second button)"); - }, 2000); + AssertUI.InnerTextEquals(text, Value + "(from second button)"); }); } diff --git a/src/DotVVM.Samples.Tests/Feature/DateTimeSerializationTests.cs b/src/DotVVM.Samples.Tests/Feature/DateTimeSerializationTests.cs index 126bd538d2..e55f92f289 100644 --- a/src/DotVVM.Samples.Tests/Feature/DateTimeSerializationTests.cs +++ b/src/DotVVM.Samples.Tests/Feature/DateTimeSerializationTests.cs @@ -29,56 +29,43 @@ public void Feature_DateTimeSerialization_DateTimeSerialization() browser.ElementAt("input[type=text]", 0).Clear().SendKeys("18.2.1988"); browser.ElementAt("input[type=button]", 1).Click(); - browser.WaitFor(() => { - AssertUI.InnerText(browser.ElementAt("span", 0), s => DateTime.Parse(s).Equals(new DateTime(1988, 2, 18))); - }, 5000); + AssertUI.InnerText(browser.ElementAt("span", 0), s => DateTime.Parse(s).Equals(new DateTime(1988, 2, 18))); browser.ElementAt("input[type=text]", 0).Clear(); browser.ElementAt("input[type=button]", 1).Click(); - browser.WaitFor(() => { - // the value is invalid so the viewmodel stays as is - AssertUI.InnerText(browser.ElementAt("span", 0), s => DateTime.Parse(s).Equals(new DateTime(1988, 2, 18))); - }, 5000); + AssertUI.InnerText(browser.ElementAt("span", 0), s => DateTime.Parse(s).Equals(new DateTime(1988, 2, 18))); // make the viewmodel valid again browser.ElementAt("input[type=text]", 0).Clear().SendKeys("18.2.1988"); browser.ElementAt("input[type=button]", 1).Click(); - browser.WaitFor(() => { - AssertUI.InnerText(browser.ElementAt("span", 0), s => DateTime.Parse(s).Equals(new DateTime(1988, 2, 18))); - }, 5000); + AssertUI.InnerText(browser.ElementAt("span", 0), s => DateTime.Parse(s).Equals(new DateTime(1988, 2, 18))); // verify the second date browser.ElementAt("input[type=text]", 1).Clear().SendKeys("2011-03-19 16:48:17"); browser.ElementAt("input[type=button]", 3).Click(); - browser.WaitFor(() => { - AssertUI.InnerText(browser.ElementAt("span", 1), + AssertUI.InnerText(browser.ElementAt("span", 1), s => DateTime.Parse(s).Equals(new DateTime(2011, 3, 19, 16, 48, 0))); - }, 5000); browser.ElementAt("input[type=text]", 1).Clear(); browser.ElementAt("input[type=button]", 3).Click(); - browser.WaitFor(() => { - AssertUI.InnerTextEquals(browser.ElementAt("span", 1), "null"); - }, 5000); + AssertUI.InnerTextEquals(browser.ElementAt("span", 1), "null"); // try to set dates from server browser.ElementAt("input[type=button]", 0).Click(); browser.WaitForPostback(); browser.ElementAt("input[type=button]", 2).Click(); - browser.WaitFor(() => { - // there is no time in the field - AssertUI.Attribute(browser.ElementAt("input[type=text]", 0), "value", - s => (DateTime.Now - DateTime.Parse(s, culture)).TotalHours < 24); + // there is no time in the field + AssertUI.Attribute(browser.ElementAt("input[type=text]", 0), "value", + s => (DateTime.Now - DateTime.Parse(s, culture)).TotalHours < 24); - // the minutes can differ slightly - AssertUI.Attribute(browser.ElementAt("input[type=text]", 1), "value", - s => (DateTime.Now - DateTime.Parse(s, culture)).TotalMinutes < 1); - }, 5000); + // the minutes can differ slightly + AssertUI.Attribute(browser.ElementAt("input[type=text]", 1), "value", + s => (DateTime.Now - DateTime.Parse(s, culture)).TotalMinutes < 1); }); } diff --git a/src/DotVVM.Samples.Tests/Feature/DependencyInjectionTests.cs b/src/DotVVM.Samples.Tests/Feature/DependencyInjectionTests.cs index 3c44b677f1..378eb1cad0 100644 --- a/src/DotVVM.Samples.Tests/Feature/DependencyInjectionTests.cs +++ b/src/DotVVM.Samples.Tests/Feature/DependencyInjectionTests.cs @@ -19,7 +19,7 @@ public void Feature_DependencyInjection_ViewModelScopedService() var value = browser.First(".result").GetInnerText(); AssertUI.InnerTextEquals(browser.First(".result2"), value); - browser.First("input[type=button]").Click().Wait(); + browser.First("input[type=button]").Click(); var value2 = browser.First(".result").GetInnerText(); AssertUI.InnerTextEquals(browser.First(".result2"), value2); diff --git a/src/DotVVM.Samples.Tests/Feature/FormControlsEnabledTests.cs b/src/DotVVM.Samples.Tests/Feature/FormControlsEnabledTests.cs index b6c296ec4d..bc32c8d90d 100644 --- a/src/DotVVM.Samples.Tests/Feature/FormControlsEnabledTests.cs +++ b/src/DotVVM.Samples.Tests/Feature/FormControlsEnabledTests.cs @@ -17,7 +17,6 @@ public void Feature_FormControlsEnabled_FormControlsEnabled() RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.FeatureSamples_FormControlsEnabled_FormControlsEnabled); - browser.Wait(2000); // LinkButton tests. Selenium does not recognize them as disabled as that is handled by DotVVM. int linkButtonPresses = 0; @@ -76,7 +75,7 @@ public void Feature_FormControlsEnabled_FormControlsEnabled() TestLinkButton(browser, "repeater_0_linkb-default", false, ref linkButtonPresses); TestLinkButton(browser, "repeater_1_linkb-default", true, ref linkButtonPresses); - browser.First("#toggle").Click().Wait(); + browser.First("#toggle").Click(); enabled = !enabled; } }); @@ -84,16 +83,14 @@ public void Feature_FormControlsEnabled_FormControlsEnabled() private void TestLinkButton(IBrowserWrapper browser, string id, bool shouldBeEnabled, ref int currentPresses) { - browser.First($"#{id}").Click().Wait(); + browser.First($"#{id}").Click(); if (shouldBeEnabled) { currentPresses++; } var c = currentPresses; - browser.WaitFor(() => { - AssertUI.InnerTextEquals(browser.First("#linkbuttons-pressed"), c.ToString()); - }, 2000); + AssertUI.InnerTextEquals(browser.First("#linkbuttons-pressed"), c.ToString()); } public FormControlsEnabledTests(ITestOutputHelper output) : base(output) diff --git a/src/DotVVM.Samples.Tests/Feature/FormattingTests.cs b/src/DotVVM.Samples.Tests/Feature/FormattingTests.cs index 30471ae19c..5bc3b4c50e 100644 --- a/src/DotVVM.Samples.Tests/Feature/FormattingTests.cs +++ b/src/DotVVM.Samples.Tests/Feature/FormattingTests.cs @@ -34,7 +34,6 @@ public void Feature_Formatting_Formatting() // do the postback browser.Click("input[type=button]"); - browser.Wait(); // verify items rendered on client and on the server are the same items1 = browser.FindElements(".list1 li"); diff --git a/src/DotVVM.Samples.Tests/Feature/IdGenerationTests.cs b/src/DotVVM.Samples.Tests/Feature/IdGenerationTests.cs index e3c0fd22fc..df85a94c26 100644 --- a/src/DotVVM.Samples.Tests/Feature/IdGenerationTests.cs +++ b/src/DotVVM.Samples.Tests/Feature/IdGenerationTests.cs @@ -13,7 +13,6 @@ public void Feature_IdGeneration_IdGeneration() { RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.FeatureSamples_IdGeneration_IdGeneration); - browser.Wait(); AssertUI.Attribute(browser.Single("*[data-id=test1_marker]"), "id", s => s.Equals("test1"), "Wrong ID"); diff --git a/src/DotVVM.Samples.Tests/Feature/JavascriptEventsTests.cs b/src/DotVVM.Samples.Tests/Feature/JavascriptEventsTests.cs index 4a46627c55..92a1ee5547 100644 --- a/src/DotVVM.Samples.Tests/Feature/JavascriptEventsTests.cs +++ b/src/DotVVM.Samples.Tests/Feature/JavascriptEventsTests.cs @@ -15,7 +15,6 @@ public void Feature_JavascriptEvents_JavascriptEvents() browser.NavigateToUrl(SamplesRouteUrls.FeatureSamples_JavascriptEvents_JavascriptEvents); // init alert - browser.Wait(); AssertUI.AlertTextEquals(browser, "init"); browser.ConfirmAlert(); @@ -24,7 +23,6 @@ public void Feature_JavascriptEvents_JavascriptEvents() AssertUI.AlertTextEquals(browser, "beforePostback"); browser.ConfirmAlert(); - browser.Wait(); AssertUI.AlertTextEquals(browser, "afterPostback"); browser.ConfirmAlert(); @@ -34,11 +32,9 @@ public void Feature_JavascriptEvents_JavascriptEvents() AssertUI.AlertTextEquals(browser, "beforePostback"); browser.ConfirmAlert(); - browser.Wait(); AssertUI.AlertTextEquals(browser, "afterPostback"); browser.ConfirmAlert(); - browser.Wait(); AssertUI.AlertTextEquals(browser, "custom error handler"); browser.ConfirmAlert(); diff --git a/src/DotVVM.Samples.Tests/Feature/LocalizationTests.cs b/src/DotVVM.Samples.Tests/Feature/LocalizationTests.cs index 8b3d3a799a..80ebe38639 100644 --- a/src/DotVVM.Samples.Tests/Feature/LocalizationTests.cs +++ b/src/DotVVM.Samples.Tests/Feature/LocalizationTests.cs @@ -106,16 +106,16 @@ void CheckForm(IBrowserWrapper browser) { browser.SelectMethod = SelectByDataUi; browser.Single("button-hello").Click(); - browser.WaitFor(() => AssertUI.TextEquals(browser.Single("span-hello"), "Hello"), 2000); + AssertUI.TextEquals(browser.Single("span-hello"), "Hello"); browser.Single("textbox-parse").SendKeys("42"); browser.Single("button-parse").Click(); - browser.WaitFor(() => AssertUI.TextEquals(browser.Single("span-parse"), "42"), 2000); + AssertUI.TextEquals(browser.Single("span-parse"), "42"); browser.Single("textbox-multiplyA").Clear().SendKeys("6"); browser.Single("textbox-multiplyB").Clear().SendKeys("-7"); browser.Single("button-multiply").Click(); - browser.WaitFor(() => AssertUI.TextEquals(browser.Single("span-multiply"), "-42"), 2000); + AssertUI.TextEquals(browser.Single("span-multiply"), "-42"); AssertUI.TextEquals(browser.Single("postback-counter"), "3"); diff --git a/src/DotVVM.Samples.Tests/Feature/MarkupControlTests.cs b/src/DotVVM.Samples.Tests/Feature/MarkupControlTests.cs index fe5523a9a6..b9bb90ff43 100644 --- a/src/DotVVM.Samples.Tests/Feature/MarkupControlTests.cs +++ b/src/DotVVM.Samples.Tests/Feature/MarkupControlTests.cs @@ -100,34 +100,22 @@ public void Feature_MarkupControl_CommandBindingInRepeater() AssertUI.InnerTextEquals(browser.First("span[data-uitest=result]"), "Hello from DotVVM!"); browser.ElementAt("input[type=button]", 0).Click(); - browser.WaitFor(() => { - AssertUI.InnerTextEquals(browser.First("span[data-uitest=result]"), "Action1 - Item 1"); - }, 1000, 30); + AssertUI.InnerTextEquals(browser.First("span[data-uitest=result]"), "Action1 - Item 1"); ; browser.ElementAt("input[type=button]", 1).Click(); - browser.WaitFor(() => { - AssertUI.InnerTextEquals(browser.First("span[data-uitest=result]"), "Action2 - Item 1"); - }, 1000, 30); + AssertUI.InnerTextEquals(browser.First("span[data-uitest=result]"), "Action2 - Item 1"); browser.ElementAt("input[type=button]", 2).Click(); - browser.WaitFor(() => { - AssertUI.InnerTextEquals(browser.First("span[data-uitest=result]"), "Action1 - Item 2"); - }, 1000, 30); + AssertUI.InnerTextEquals(browser.First("span[data-uitest=result]"), "Action1 - Item 2"); browser.ElementAt("input[type=button]", 3).Click(); - browser.WaitFor(() => { - AssertUI.InnerTextEquals(browser.First("span[data-uitest=result]"), "Action2 - Item 2"); - }, 1000, 30); + AssertUI.InnerTextEquals(browser.First("span[data-uitest=result]"), "Action2 - Item 2"); browser.ElementAt("input[type=button]", 4).Click(); - browser.WaitFor(() => { - AssertUI.InnerTextEquals(browser.First("span[data-uitest=result]"), "Action1 - Item 3"); - }, 1000, 30); + AssertUI.InnerTextEquals(browser.First("span[data-uitest=result]"), "Action1 - Item 3"); browser.ElementAt("input[type=button]", 5).Click(); - browser.WaitFor(() => { - AssertUI.InnerTextEquals(browser.First("span[data-uitest=result]"), "Action2 - Item 3"); - }, 1000, 30); + AssertUI.InnerTextEquals(browser.First("span[data-uitest=result]"), "Action2 - Item 3"); }); } @@ -140,11 +128,11 @@ public void Feature_MarkupControl_CommandBindingInDataContextWithControlProperty AssertUI.InnerTextEquals(browser.First("span[data-uitest=result1]"), "Init"); AssertUI.InnerTextEquals(browser.First("span[data-uitest=result2]"), "Init"); - browser.ElementAt("input[type=button]", 0).Click().Wait(); + browser.ElementAt("input[type=button]", 0).Click(); AssertUI.InnerTextEquals(browser.First("span[data-uitest=result1]"), "changed"); AssertUI.InnerTextEquals(browser.First("span[data-uitest=result2]"), "Init"); - browser.ElementAt("input[type=button]", 1).Click().Wait(); + browser.ElementAt("input[type=button]", 1).Click(); AssertUI.InnerTextEquals(browser.First("span[data-uitest=result1]"), "changed"); AssertUI.InnerTextEquals(browser.First("span[data-uitest=result2]"), "changed"); }); @@ -166,7 +154,7 @@ public void Feature_MarkupControl_ControlPropertyUpdatedByServer() AssertUI.Value(browser.ElementAt("input[data-uitest=editor]", 1), ""); AssertUI.Value(browser.First("input[data-uitest=childProperty]"), ""); - browser.First("input[data-uitest=childPropertyButton]").Click().Wait(); + browser.First("input[data-uitest=childPropertyButton]").Click(); AssertUI.Value(browser.ElementAt("input[data-uitest=editor]", 1), "TEST"); AssertUI.Value(browser.First("input[data-uitest=childProperty]"), "TEST"); }); @@ -180,7 +168,7 @@ public void Feature_MarkupControl_ControlPropertyUpdating() AssertUI.Value(browser.ElementAt("input[type=text]", 0), "TEST 123"); AssertUI.InnerTextEquals(browser.First("span[data-uitest=result]"), "TEST 123 HUHA"); - browser.First("input[type=button]").Click().Wait(); + browser.First("input[type=button]").Click(); AssertUI.Value(browser.ElementAt("input[type=text]", 0), "ABC FFF"); AssertUI.InnerTextEquals(browser.First("span[data-uitest=result]"), "ABC FFF HUHA"); }); @@ -192,26 +180,26 @@ public void Feature_MarkupControl_ControlPropertyValidationPage() RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.FeatureSamples_MarkupControl_ControlPropertyValidationPage); - browser.Single("input[type=button]").Click().Wait(); + browser.Single("input[type=button]").Click(); browser.FindElements("li").ThrowIfDifferentCountThan(1); AssertUI.InnerTextEquals(browser.First("li"), "The Text field is required."); AssertUI.InnerTextEquals(browser.Single("span"), "VALIDATION ERROR"); browser.ElementAt("input[type=text]", 0).SendKeys("test"); - browser.Single("input[type=button]").Click().Wait(); + browser.Single("input[type=button]").Click(); AssertUI.Value(browser.ElementAt("input[type=text]", 1), "test"); browser.FindElements("li").ThrowIfDifferentCountThan(1); AssertUI.InnerTextEquals(browser.First("li"), "The Text field is not a valid e-mail address."); AssertUI.InnerTextEquals(browser.Single("span"), "VALIDATION ERROR"); browser.ElementAt("input[type=text]", 0).SendKeys("@mail.com"); - browser.Single("input[type=button]").Click().Wait(); + browser.Single("input[type=button]").Click(); AssertUI.Value(browser.ElementAt("input[type=text]", 1), "test@mail.com"); browser.FindElements("li").ThrowIfDifferentCountThan(0); AssertUI.InnerTextEquals(browser.Single("span"), ""); browser.ElementAt("input[type=text]", 0).Clear(); - browser.Single("input[type=button]").Click().Wait(); + browser.Single("input[type=button]").Click(); browser.FindElements("li").ThrowIfDifferentCountThan(1); AssertUI.InnerTextEquals(browser.First("li"), "The Text field is required."); AssertUI.InnerTextEquals(browser.Single("span"), "VALIDATION ERROR"); @@ -224,42 +212,28 @@ public void Feature_MarkupControl_MarkupControlRegistration() RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.FeatureSamples_MarkupControl_MarkupControlRegistration); - browser.WaitFor(() => { - AssertUI.InnerTextEquals(browser.ElementAt("h2", 0), "First Control"); - AssertUI.InnerTextEquals(browser.ElementAt("h2", 1), "Second control name was set from the binding"); - }, 5000); + AssertUI.InnerTextEquals(browser.ElementAt("h2", 0), "First Control"); + AssertUI.InnerTextEquals(browser.ElementAt("h2", 1), "Second control name was set from the binding"); AssertUI.Value(browser.ElementAt("input[type=text]", 0), "15"); browser.ElementAt("input[type=button]", 0).Click(); - browser.WaitFor(() => { - AssertUI.Value(browser.ElementAt("input[type=text]", 0), "16"); - }, 5000); + AssertUI.Value(browser.ElementAt("input[type=text]", 0), "16"); browser.ElementAt("input[type=button]", 0).Click(); - browser.WaitFor(() => { - AssertUI.Value(browser.ElementAt("input[type=text]", 0), "17"); - }, 5000); + AssertUI.Value(browser.ElementAt("input[type=text]", 0), "17"); browser.ElementAt("input[type=button]", 1).Click(); - browser.WaitFor(() => { - AssertUI.Value(browser.ElementAt("input[type=text]", 0), "16"); - }, 5000); + AssertUI.Value(browser.ElementAt("input[type=text]", 0), "16"); AssertUI.Value(browser.ElementAt("input[type=text]", 1), "25"); browser.ElementAt("input[type=button]", 2).Click(); - browser.WaitFor(() => { - AssertUI.Value(browser.ElementAt("input[type=text]", 1), "26"); - }, 5000); + AssertUI.Value(browser.ElementAt("input[type=text]", 1), "26"); browser.ElementAt("input[type=button]", 2).Click(); - browser.WaitFor(() => { - AssertUI.Value(browser.ElementAt("input[type=text]", 1), "27"); - }, 5000); + AssertUI.Value(browser.ElementAt("input[type=text]", 1), "27"); browser.ElementAt("input[type=button]", 3).Click(); - browser.WaitFor(() => { - AssertUI.Value(browser.ElementAt("input[type=text]", 1), "26"); - }, 5000); + AssertUI.Value(browser.ElementAt("input[type=text]", 1), "26"); }); } @@ -299,10 +273,8 @@ public void Feature_MarkupControl_ComboBoxDataSourceBoundToStaticCollection() RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.FeatureSamples_MarkupControl_ComboBoxDataSourceBoundToStaticCollection); - browser.WaitFor(() => { - var innerControlLiteral = browser.First("[data-ui=inner-control-literal]"); - AssertUI.InnerTextEquals(innerControlLiteral, "Default item"); - }, 5000); + var innerControlLiteral = browser.First("[data-ui=inner-control-literal]"); + AssertUI.InnerTextEquals(innerControlLiteral, "Default item"); var comboboxSelectedValue = browser.First("[data-ui=combobox-selected-value]"); var combobox = browser.First("[data-ui=combobox]"); @@ -335,9 +307,7 @@ public void Feature_MarkupControl_CommandPropertiesInMarkupControl() AssertUI.NotContainsElement(body, "[data-ui=cancel]"); ok.Click(); - browser.WaitFor(() => { - AssertUI.InnerTextEquals(span, "Command result."); - }, 1000); + AssertUI.InnerTextEquals(span, "Command result."); }); } @@ -350,9 +320,7 @@ public void Feature_MarkupControl_StaticCommandInMarkupControl() browser.WaitUntilDotvvmInited(); browser.First("[data-ui=reset]").Click(); - browser.WaitFor(() => { - AssertUI.TextEquals(browser.First("[data-ui='test-state']"), "OK"); - }, 8000, "Test could not clear state."); + AssertUI.TextEquals(browser.First("[data-ui='test-state']"), "OK", failureMessage: "Test could not clear state."); // start the test over browser.NavigateToUrl(SamplesRouteUrls.FeatureSamples_MarkupControl_StaticCommandInMarkupControl); @@ -370,35 +338,26 @@ public void Feature_MarkupControl_StaticCommandInMarkupControl() input().Clear().SendKeys("test1"); save().Click(); - browser.WaitFor(() => { - AssertUI.TextEquals(browser.Last("article>span"), "test1"); - }, 4000); + browser.WaitFor(()=>AssertUI.TextEquals(browser.Last("article>span"), "test1"),2000); editButton().Click(); input().Clear().SendKeys("changed"); save().Click(); - browser.WaitFor(() => { - AssertUI.Any(browser.FindElements("article>span")).TextEquals("changed"); - }, 4000); + AssertUI.Any(browser.FindElements("article>span")).TextEquals("changed"); editButton().Click(); input().Clear().SendKeys("changed2"); save().Click(); - browser.WaitFor(() => { - AssertUI.Any(browser.FindElements("article>span")).TextEquals("changed2"); - AssertUI.All(browser.FindElements("article>span")).TextNotEquals("changed"); - }, 4000); + browser.WaitFor(() => AssertUI.Any(browser.FindElements("article>span")).TextEquals("changed2"), 2000); + browser.WaitFor(() => AssertUI.All(browser.FindElements("article>span")).TextNotEquals("changed"), 2000); removeButton().Click(); - browser.WaitFor(() => { - AssertUI.All(browser.FindElements("article>span")).TextNotEquals("changed2"); - AssertUI.All(browser.FindElements("article>span")).TextNotEquals("changed"); - }, 4000); - + browser.WaitFor(() => AssertUI.All(browser.FindElements("article>span")).TextNotEquals("changed2"), 2000); + browser.WaitFor(() => AssertUI.All(browser.FindElements("article>span")).TextNotEquals("changed"), 2000); }); } - } + } } diff --git a/src/DotVVM.Samples.Tests/Feature/ParameterBindingTests.cs b/src/DotVVM.Samples.Tests/Feature/ParameterBindingTests.cs index c291681b3c..ad58570c45 100644 --- a/src/DotVVM.Samples.Tests/Feature/ParameterBindingTests.cs +++ b/src/DotVVM.Samples.Tests/Feature/ParameterBindingTests.cs @@ -19,7 +19,6 @@ public void Feature_ParameterBinding_ParameterBinding() { RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.FeatureSamples_ParameterBinding_ParameterBinding + "/123?B=abc"); - browser.Wait(); AssertUI.InnerTextEquals(browser.Single(".root-a"), "123"); AssertUI.InnerTextEquals(browser.Single(".root-b"), "abc"); diff --git a/src/DotVVM.Samples.Tests/Feature/PostBackTests.cs b/src/DotVVM.Samples.Tests/Feature/PostBackTests.cs index a1c1d4e1bb..e030c00d57 100644 --- a/src/DotVVM.Samples.Tests/Feature/PostBackTests.cs +++ b/src/DotVVM.Samples.Tests/Feature/PostBackTests.cs @@ -20,7 +20,6 @@ public void Feature_PostBack_PostbackUpdate() browser.ClearElementsContent("input[type=text]"); browser.SendKeys("input[type=text]", "15"); browser.Click("input[type=button]"); - browser.Wait(); browser.FindElements("br").ThrowIfDifferentCountThan(14); @@ -28,7 +27,6 @@ public void Feature_PostBack_PostbackUpdate() browser.ClearElementsContent("input[type=text]"); browser.SendKeys("input[type=text]", "5"); browser.Click("input[type=button]"); - browser.Wait(); browser.FindElements("br").ThrowIfDifferentCountThan(4); }); @@ -102,53 +100,44 @@ private void ValidatePostbackHandlersComplexSection(string sectionSelector, IBro section.ElementAt("input[type=button]", 0).Click(); AssertUI.AlertTextEquals(browser, "Confirmation 1"); browser.ConfirmAlert(); - browser.Wait(); AssertUI.InnerTextEquals(index, "1"); // cancel second section.ElementAt("input[type=button]", 1).Click(); AssertUI.AlertTextEquals(browser, "Confirmation 1"); browser.ConfirmAlert(); - browser.Wait(); AssertUI.AlertTextEquals(browser, "Confirmation 2"); browser.DismissAlert(); - browser.Wait(); AssertUI.InnerTextEquals(index, "1"); // confirm second section.ElementAt("input[type=button]", 1).Click(); AssertUI.AlertTextEquals(browser, "Confirmation 1"); browser.ConfirmAlert(); - browser.Wait(); AssertUI.AlertTextEquals(browser, "Confirmation 2"); browser.ConfirmAlert(); - browser.Wait(); AssertUI.InnerTextEquals(index, "2"); // confirm third section.ElementAt("input[type=button]", 2).Click(); Assert.False(browser.HasAlert()); - browser.Wait(); AssertUI.InnerTextEquals(index, "3"); // confirm fourth section.ElementAt("input[type=button]", 3).Click(); AssertUI.AlertTextEquals(browser, "Generated 1"); browser.ConfirmAlert(); - browser.Wait(); AssertUI.InnerTextEquals(index, "4"); // confirm fifth section.ElementAt("input[type=button]", 4).Click(); AssertUI.AlertTextEquals(browser, "Generated 2"); browser.ConfirmAlert(); - browser.Wait(); AssertUI.InnerTextEquals(index, "5"); // confirm conditional section.ElementAt("input[type=button]", 5).Click(); Assert.False(browser.HasAlert()); - browser.Wait(); AssertUI.InnerTextEquals(index, "6"); browser.First("input[type=checkbox]").Click(); @@ -156,14 +145,12 @@ private void ValidatePostbackHandlersComplexSection(string sectionSelector, IBro section.ElementAt("input[type=button]", 5).Click(); AssertUI.AlertTextEquals(browser, "Conditional 1"); browser.ConfirmAlert(); - browser.Wait(); AssertUI.InnerTextEquals(index, "6"); browser.First("input[type=checkbox]").Click(); section.ElementAt("input[type=button]", 5).Click(); Assert.False(browser.HasAlert()); - browser.Wait(); AssertUI.InnerTextEquals(index, "6"); browser.First("input[type=checkbox]").Click(); @@ -171,7 +158,6 @@ private void ValidatePostbackHandlersComplexSection(string sectionSelector, IBro section.ElementAt("input[type=button]", 5).Click(); AssertUI.AlertTextEquals(browser, "Conditional 1"); browser.ConfirmAlert(); - browser.Wait(); AssertUI.InnerTextEquals(index, "6"); //localization - resource binding in confirm postback handler message @@ -179,7 +165,6 @@ private void ValidatePostbackHandlersComplexSection(string sectionSelector, IBro section.ElementAt("input[type=button]", 6).Click(); AssertUI.AlertTextEquals(browser, "EnglishValue"); browser.ConfirmAlert(); - browser.Wait(); AssertUI.InnerTextEquals(index, "7"); browser.First("#ChangeLanguageCZ").Click(); @@ -195,13 +180,11 @@ private void ValidatePostbackHandlersComplexSection(string sectionSelector, IBro section.ElementAt("input[type=button]", 6).Click(); AssertUI.AlertTextEquals(browser, "CzechValue"); browser.DismissAlert(); - browser.Wait(); AssertUI.InnerTextEquals(index, "0"); section.ElementAt("input[type=button]", 6).Click(); AssertUI.AlertTextEquals(browser, "CzechValue"); browser.ConfirmAlert(); - browser.Wait(); AssertUI.InnerTextEquals(index, "7"); } diff --git a/src/DotVVM.Samples.Tests/Feature/PostbackConcurrencyTests.cs b/src/DotVVM.Samples.Tests/Feature/PostbackConcurrencyTests.cs index 58d2392c04..76677f99f4 100644 --- a/src/DotVVM.Samples.Tests/Feature/PostbackConcurrencyTests.cs +++ b/src/DotVVM.Samples.Tests/Feature/PostbackConcurrencyTests.cs @@ -56,10 +56,8 @@ public void Feature_PostbackConcurrency_DefaultMode(string longActionSelector, s AssertUI.InnerTextEquals(lastActionSpan, "short"); browser.Wait(6000); // the result of the long action should be canceled, the counter shouldn't increase - browser.WaitFor(()=> { - AssertUI.InnerTextEquals(postbackIndexSpan, "1"); - AssertUI.InnerTextEquals(lastActionSpan, "short"); - },3000); + AssertUI.InnerTextEquals(postbackIndexSpan, "1"); + AssertUI.InnerTextEquals(lastActionSpan, "short"); }); } diff --git a/src/DotVVM.Samples.Tests/Feature/PostbackSpaNavigationTests.cs b/src/DotVVM.Samples.Tests/Feature/PostbackSpaNavigationTests.cs index 2133f70129..f915836b89 100644 --- a/src/DotVVM.Samples.Tests/Feature/PostbackSpaNavigationTests.cs +++ b/src/DotVVM.Samples.Tests/Feature/PostbackSpaNavigationTests.cs @@ -26,17 +26,17 @@ public void PostbackSpaNavigationTest_SuccessfulNavigation() // click the button and make sure the postback works AssertUI.TextEquals(result, "0"); - buttons[0].Click().Wait(); + buttons[0].Click(); AssertUI.TextEquals(result, "1"); - buttons[1].Click().Wait(); + buttons[1].Click(); AssertUI.TextEquals(result, "2"); // click the first link to trigger the navigation links[0].Click(); - buttons[0].Click().Wait(); + buttons[0].Click(); AssertUI.TextEquals(result, "2"); - buttons[1].Click().Wait(); + buttons[1].Click(); AssertUI.TextEquals(result, "2"); browser.Wait(3000); @@ -58,18 +58,18 @@ public void PostbackSpaNavigationTest_FailedNavigation() // click the button and make sure the postback works AssertUI.TextEquals(result, "0"); - buttons[0].Click().Wait(); + buttons[0].Click(); AssertUI.TextEquals(result, "1"); - buttons[1].Click().Wait(); + buttons[1].Click(); AssertUI.TextEquals(result, "2"); // click the second link to trigger the navigation links[1].Click(); // now the buttons shouldn't do anything - buttons[0].Click().Wait(); + buttons[0].Click(); AssertUI.TextEquals(result, "2"); - buttons[1].Click().Wait(); + buttons[1].Click(); AssertUI.TextEquals(result, "2"); browser.Wait(3000); @@ -79,9 +79,9 @@ public void PostbackSpaNavigationTest_FailedNavigation() browser.Single("#debugWindow button").Click(); // now the buttons should work - buttons[0].Click().Wait(); + buttons[0].Click(); AssertUI.TextEquals(result, "3"); - buttons[1].Click().Wait(); + buttons[1].Click(); AssertUI.TextEquals(result, "4"); }); } @@ -100,7 +100,7 @@ public void PostbackSpaNavigationTest_SuccessfulNavigation_SurvivingCommand() // click the button to start a long postback AssertUI.TextEquals(result, "0"); - buttons[2].Click().Wait(); + buttons[2].Click(); // click the first link to trigger the navigation links[0].Click(); @@ -129,7 +129,7 @@ public void PostbackSpaNavigationTest_SuccessfulNavigation_SurvivingStaticComman // click the button to start a long postback AssertUI.TextEquals(result, "0"); - buttons[3].Click().Wait(); + buttons[3].Click(); // click the first link to trigger the navigation links[0].Click(); diff --git a/src/DotVVM.Samples.Tests/Feature/RedirectTests.cs b/src/DotVVM.Samples.Tests/Feature/RedirectTests.cs index 73fdd5a598..70af6f5cc2 100644 --- a/src/DotVVM.Samples.Tests/Feature/RedirectTests.cs +++ b/src/DotVVM.Samples.Tests/Feature/RedirectTests.cs @@ -2,6 +2,7 @@ using System.Linq; using DotVVM.Samples.Tests.Base; using DotVVM.Testing.Abstractions; +using Riganti.Selenium.DotVVM; using Xunit; using Xunit.Abstractions; @@ -23,11 +24,13 @@ public void Feature_Redirect_Redirect() Assert.Matches(@"time=\d+", currentUrl.Query); browser.First("[data-ui=object-redirect-button]").Click(); + browser.WaitForPostback(); currentUrl = new Uri(browser.CurrentUrl); Assert.Matches(@"^\?(param=temp1&time=\d+|time=\d+¶m=temp1)$", currentUrl.Query); Assert.Equal("#test1", currentUrl.Fragment); browser.First("[data-ui=dictionary-redirect-button]").Click(); + browser.WaitForPostback(); currentUrl = new Uri(browser.CurrentUrl); Assert.Matches(@"^\?(time=\d+¶m=temp2|param=temp2&time=\d+)$", currentUrl.Query); Assert.Equal("#test2", currentUrl.Fragment); diff --git a/src/DotVVM.Samples.Tests/Feature/RegexValidationTests.cs b/src/DotVVM.Samples.Tests/Feature/RegexValidationTests.cs index 2b7ca2b435..cdb747430a 100644 --- a/src/DotVVM.Samples.Tests/Feature/RegexValidationTests.cs +++ b/src/DotVVM.Samples.Tests/Feature/RegexValidationTests.cs @@ -15,15 +15,12 @@ public void Feature_Validation_RegexValidation() browser.NavigateToUrl(SamplesRouteUrls.FeatureSamples_Validation_RegexValidation); browser.ElementAt("input", 0).SendKeys("25"); - browser.Wait(); browser.ElementAt("input[type=button]", 0).Click(); AssertUI.IsNotDisplayed(browser.ElementAt("span", 0)); - browser.Wait(); AssertUI.InnerTextEquals(browser.ElementAt("span", 1), "25"); browser.ElementAt("input", 0).SendKeys("a"); - browser.Wait(); browser.ElementAt("input[type=button]", 0).Click(); AssertUI.IsDisplayed(browser.ElementAt("span", 0)); diff --git a/src/DotVVM.Samples.Tests/Feature/ResourcesTests.cs b/src/DotVVM.Samples.Tests/Feature/ResourcesTests.cs index 7b0a0393ef..301a3caf60 100644 --- a/src/DotVVM.Samples.Tests/Feature/ResourcesTests.cs +++ b/src/DotVVM.Samples.Tests/Feature/ResourcesTests.cs @@ -62,10 +62,10 @@ public void Feature_Resource_RequiredOnPostback() AssertUI.TextEquals(welcome, "Welcome"); browser.Single("button", SelectByDataUi).Click(); - browser.WaitFor(() => AssertUI.AlertTextEquals(browser, "javascript resource loaded!"), 5000); + AssertUI.AlertTextEquals(browser, "javascript resource loaded!"); browser.ConfirmAlert(); - browser.WaitFor(() => AssertUI.TextEquals(welcome, "Welcome"), 1000); + AssertUI.TextEquals(welcome, "Welcome"); }); } diff --git a/src/DotVVM.Samples.Tests/Feature/ReturnedFileTests.cs b/src/DotVVM.Samples.Tests/Feature/ReturnedFileTests.cs index 22a5ff3344..b76c2e09a0 100644 --- a/src/DotVVM.Samples.Tests/Feature/ReturnedFileTests.cs +++ b/src/DotVVM.Samples.Tests/Feature/ReturnedFileTests.cs @@ -1,9 +1,11 @@ -using System.Net; +using System; +using System.Net; using DotVVM.Samples.Tests.Base; using DotVVM.Testing.Abstractions; using OpenQA.Selenium; using Riganti.Selenium.Core; using Riganti.Selenium.Core.Abstractions; +using Riganti.Selenium.DotVVM; using Xunit; using Xunit.Abstractions; @@ -36,24 +38,29 @@ public void Feature_ReturnedFile_ReturnedFileSample_Inline() { RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.FeatureSamples_ReturnedFile_ReturnedFileSample); + browser.WaitUntilDotvvmInited(); browser.First("textarea").SendKeys("hello world"); - browser.Last("input[type=button]").Click().Wait(); + browser.Last("input[type=button]").Click(); - AssertUI.TextEquals(browser.First("pre"), "hello world"); + browser.WaitFor(() => { + AssertUI.TextEquals(browser.First("pre"), "hello world"); + },5000); }); } private void ReturnedFileDownload(IBrowserWrapper browser, string fileContent) { browser.NavigateToUrl(SamplesRouteUrls.FeatureSamples_ReturnedFile_ReturnedFileSample); + browser.WaitUntilDotvvmInited(); + var jsexec = browser.GetJavaScriptExecutor(); jsexec.ExecuteScript("window.downloadURL = \"\";"); jsexec.ExecuteScript("dotvvm.events.redirect.subscribe(function (args) { window.downloadURL = args.url; });"); browser.First("textarea").SendKeys(fileContent); browser.First("input").SendKeys(Keys.Enter); - browser.Wait(5000); + browser.WaitForPostback(); var downloadURL = (string)jsexec.ExecuteScript("return window.downloadURL;"); Assert.False(string.IsNullOrEmpty(downloadURL)); diff --git a/src/DotVVM.Samples.Tests/Feature/SerializationTests.cs b/src/DotVVM.Samples.Tests/Feature/SerializationTests.cs index d12608bcbb..ec15acf7ab 100644 --- a/src/DotVVM.Samples.Tests/Feature/SerializationTests.cs +++ b/src/DotVVM.Samples.Tests/Feature/SerializationTests.cs @@ -1,5 +1,6 @@ using DotVVM.Samples.Tests.Base; using DotVVM.Testing.Abstractions; +using OpenQA.Selenium; using Riganti.Selenium.Core; using Riganti.Selenium.DotVVM; using Xunit; @@ -21,11 +22,9 @@ public void Feature_Serialization_Serialization() browser.Click("input[type=button]"); // verify the results - browser.WaitFor(() => { - AssertUI.Attribute(browser.ElementAt("input[type=text]", 0), "value", s => s.Equals("")); - AssertUI.Attribute(browser.ElementAt("input[type=text]", 1), "value", s => s.Equals("2")); - AssertUI.InnerTextEquals(browser.Last("span"), ",2"); - }, 2000); + AssertUI.Attribute(browser.ElementAt("input[type=text]", 0), "value", s => s.Equals("")); + AssertUI.Attribute(browser.ElementAt("input[type=text]", 1), "value", s => s.Equals("2")); + AssertUI.InnerTextEquals(browser.Last("span"), ",2"); }); } @@ -34,7 +33,6 @@ public void Feature_Serialization_ObservableCollectionShouldContainObservables() { RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.FeatureSamples_Serialization_ObservableCollectionShouldContainObservables); - browser.Wait(); // verify that the values are selected browser.ElementAt("select", 0).Select(0); @@ -45,13 +43,10 @@ public void Feature_Serialization_ObservableCollectionShouldContainObservables() browser.Click("input[type=button]"); // verify that the values are correct - browser.WaitFor(() => { - AssertUI.InnerTextEquals(browser.First("p.result"), "1,2,3"); - AssertUI.Attribute(browser.ElementAt("select", 0), "value", "1"); - AssertUI.Attribute(browser.ElementAt("select", 1), "value", "2"); - AssertUI.Attribute(browser.ElementAt("select", 2), "value", "3"); - browser.Wait(); - }, 2000); + AssertUI.InnerTextEquals(browser.First("p.result"), "1,2,3"); + AssertUI.Attribute(browser.ElementAt("select", 0), "value", "1"); + AssertUI.Attribute(browser.ElementAt("select", 1), "value", "2"); + AssertUI.Attribute(browser.ElementAt("select", 2), "value", "3"); // change the values browser.ElementAt("select", 0).Select(1); @@ -62,12 +57,10 @@ public void Feature_Serialization_ObservableCollectionShouldContainObservables() browser.Click("input[type=button]"); // verify that the values are correct - browser.WaitFor(() => { - AssertUI.InnerTextEquals(browser.First("p.result"), "2,3,2"); - AssertUI.Attribute(browser.ElementAt("select", 0), "value", "2"); - AssertUI.Attribute(browser.ElementAt("select", 1), "value", "3"); - AssertUI.Attribute(browser.ElementAt("select", 2), "value", "2"); - }, 2000); + AssertUI.InnerTextEquals(browser.First("p.result"), "2,3,2"); + AssertUI.Attribute(browser.ElementAt("select", 0), "value", "2"); + AssertUI.Attribute(browser.ElementAt("select", 1), "value", "3"); + AssertUI.Attribute(browser.ElementAt("select", 2), "value", "2"); }); } @@ -76,10 +69,9 @@ public void Feature_Serialization_EnumSerializationWithJsonConverter() { RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.FeatureSamples_Serialization_EnumSerializationWithJsonConverter); - browser.Wait(); // click on the button - browser.Single("input[type=button]").Click().Wait(); + browser.Single("input[type=button]").Click(); // make sure that deserialization worked correctly AssertUI.InnerTextEquals(browser.First("p.result"), "Success!"); @@ -91,7 +83,6 @@ public void Feature_Serialization_DeserializationVirtualElements() { RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.FeatureSamples_Serialization_DeserializationVirtualElements); - browser.Wait(); // check that there are three rows browser.FindElements("thead tr").ThrowIfDifferentCountThan(2); @@ -99,14 +90,16 @@ public void Feature_Serialization_DeserializationVirtualElements() // add item browser.Single("input[type=text]").SendKeys("Four"); - browser.First("input[type=button]").Click().Wait(); + browser.First("input[type=button]").Click(); + browser.WaitForPostback(); // check that there are four rows browser.FindElements("tbody tr").ThrowIfDifferentCountThan(4); AssertUI.InnerTextEquals(browser.ElementAt("tbody tr", 3).First("td"), "Four"); // delete second row - browser.ElementAt("tbody tr", 1).Single("input[type=button]").Click().Wait(); + browser.ElementAt("tbody tr", 1).Single("input[type=button]").Click(); + browser.WaitForPostback(); // check that there are three rows browser.FindElements("tbody tr").ThrowIfDifferentCountThan(3); @@ -131,6 +124,117 @@ public void Feature_Serialization_Dictionary() }); } + [Fact] + public void Feature_Serialization_TimeSpan() + { + RunInAllBrowsers(browser => { + browser.NavigateToUrl(SamplesRouteUrls.FeatureSamples_Serialization_TimeSpan); + + var timeTextBox = browser.ElementAt("input[type=text]", 0); + var nullableTimeTextBox = browser.ElementAt("input[type=text]", 1); + + var time = browser.Single(".result-time"); + var nullableTime = browser.Single(".result-nullable-time"); + + var button = browser.Single("input[type=button]"); + + // initial state + browser.WaitFor(() => AssertUI.TextEquals(time, "00:00:00"), 5000); + browser.WaitFor(() => AssertUI.Value(timeTextBox, "00:00:00"), 5000); + button.Click(); + browser.WaitFor(() => AssertUI.TextEquals(time, "01:00:00"), 5000); + browser.WaitFor(() => AssertUI.Value(timeTextBox, "01:00:00"), 5000); + + // over 24 hours + timeTextBox.Clear().SendKeys("23:45:17").SendKeys(Keys.Tab); + browser.WaitFor(() => AssertUI.TextEquals(time, "23:45:17"), 5000); + button.Click(); + browser.WaitFor(() => AssertUI.TextEquals(time, "1.00:45:17"), 5000); + browser.WaitFor(() => AssertUI.Value(timeTextBox, "1.00:45:17"), 5000); + + // more than 24 hours without the day specifier + timeTextBox.Clear().SendKeys("126:45:17").SendKeys(Keys.Tab); + browser.WaitFor(() => AssertUI.TextEquals(time, "5.06:45:17"), 5000); + button.Click(); + browser.WaitFor(() => AssertUI.TextEquals(time, "5.07:45:17"), 5000); + browser.WaitFor(() => AssertUI.Value(timeTextBox, "5.07:45:17"), 5000); + + // negative + timeTextBox.Clear().SendKeys("-1.0:20:34.145").SendKeys(Keys.Tab); + browser.WaitFor(() => AssertUI.TextEquals(time, "-1.00:20:34.1450000"), 5000); + button.Click(); + browser.WaitFor(() => AssertUI.TextEquals(time, "-23:20:34.1450000"), 5000); + browser.WaitFor(() => AssertUI.Value(timeTextBox, "-23:20:34.1450000"), 5000); + + // nullable - set value + browser.WaitFor(() => AssertUI.TextEquals(nullableTime, ""), 5000); + browser.WaitFor(() => AssertUI.Value(nullableTimeTextBox, ""), 5000); + nullableTimeTextBox.Clear().SendKeys("4:56:01").SendKeys(Keys.Tab); + button.Click(); + browser.WaitFor(() => AssertUI.TextEquals(nullableTime, "05:56:01"), 5000); + browser.WaitFor(() => AssertUI.Value(nullableTimeTextBox, "05:56:01"), 5000); + + // nullable - clear + nullableTimeTextBox.Clear(); + browser.WaitFor(() => AssertUI.Value(nullableTimeTextBox, ""), 5000); + button.Click(); + browser.WaitFor(() => AssertUI.TextEquals(nullableTime, ""), 5000); + browser.WaitFor(() => AssertUI.Value(nullableTimeTextBox, ""), 5000); + }); + } + + [Fact] + public void Feature_Serialization_SerializationDateTimeOffset() + { + RunInAllBrowsers(browser => { + browser.NavigateToUrl(SamplesRouteUrls.FeatureSamples_Serialization_SerializationDateTimeOffset); + browser.WaitUntilDotvvmInited(); + + var offset = browser.ElementAt("input[type=text]",0); + var nullableOffset = browser.ElementAt("input[type=text]", 1); + var button1 = browser.ElementAt("input[type=button]", 0); + var button2 = browser.ElementAt("input[type=button]", 1); + + browser.WaitFor(() => { + AssertUI.Value(offset, "2000-01-02T03:04:05+00:00"); + AssertUI.Value(nullableOffset, ""); + }, 5000); + + button1.Click(); + + browser.WaitFor(() => { + AssertUI.Value(offset, "2000-01-02T03:04:05+00:00"); + AssertUI.Value(nullableOffset, "2000-01-02T03:04:05+02:00"); + }, 5000); + + // TODO: changing values is not supported - DateTimeOffset serialization in DotVVM changes the time zone info + }); + } + + [Fact] + public void Feature_Serialization_EnumSerializationCoercion() + { + RunInAllBrowsers(browser => { + browser.NavigateToUrl(SamplesRouteUrls.FeatureSamples_Serialization_EnumSerializationCoercion); + browser.WaitUntilDotvvmInited(); + + var result = browser.Single(".result-with-string"); + var result2 = browser.Single(".result-without-string"); + + browser.WaitFor(() => { + AssertUI.TextEquals(result, "One"); + AssertUI.TextEquals(result2, "0"); + }, 5000); + + var changeButton = browser.First("input[type=button]"); + changeButton.Click(); + + browser.WaitFor(() => { + AssertUI.TextEquals(result, "-1"); + AssertUI.TextEquals(result2, "Two"); + }, 5000); + }); + } public SerializationTests(ITestOutputHelper output) : base(output) { diff --git a/src/DotVVM.Samples.Tests/Feature/StaticCommandTests.cs b/src/DotVVM.Samples.Tests/Feature/StaticCommandTests.cs index b24d539368..15ac377f81 100644 --- a/src/DotVVM.Samples.Tests/Feature/StaticCommandTests.cs +++ b/src/DotVVM.Samples.Tests/Feature/StaticCommandTests.cs @@ -172,35 +172,35 @@ private static void Feature_StaticCommand_ComboBoxSelectionChangedViewModel_Core browser.IsDotvvmPage(); // select second value in the first combo box, the second one should select the second value too - browser.ElementAt("select", 0).Select(1).Wait(); - browser.ElementAt("select", 1).Select(1).Wait(); + browser.ElementAt("select", 0).Select(1); + browser.ElementAt("select", 1).Select(1); AssertUI.IsSelected(browser.ElementAt("select", 0).ElementAt("option", 1)); AssertUI.IsSelected(browser.ElementAt("select", 1).ElementAt("option", 1)); // select third value in the first combo box, the second one should select the third value too - browser.ElementAt("select", 0).Select(2).Wait(); - browser.ElementAt("select", 1).Select(2).Wait(); + browser.ElementAt("select", 0).Select(2); + browser.ElementAt("select", 1).Select(2); AssertUI.IsSelected(browser.ElementAt("select", 0).ElementAt("option", 2)); AssertUI.IsSelected(browser.ElementAt("select", 1).ElementAt("option", 2)); // select first value in the first combo box, the second one should select the first value too - browser.ElementAt("select", 0).Select(0).Wait(); - browser.ElementAt("select", 1).Select(0).Wait(); + browser.ElementAt("select", 0).Select(0); + browser.ElementAt("select", 1).Select(0); AssertUI.IsSelected(browser.ElementAt("select", 0).ElementAt("option", 0)); AssertUI.IsSelected(browser.ElementAt("select", 1).ElementAt("option", 0)); // click the first button - the second value should be selected in the first select, the second select should not change - browser.ElementAt("input", 0).Click().Wait(); + browser.ElementAt("input", 0).Click(); AssertUI.IsSelected(browser.ElementAt("select", 0).ElementAt("option", 1)); AssertUI.IsSelected(browser.ElementAt("select", 1).ElementAt("option", 0)); // click the second button - the third value should be selected in the second select, the first select should not change - browser.ElementAt("input", 1).Click().Wait(); + browser.ElementAt("input", 1).Click(); AssertUI.IsSelected(browser.ElementAt("select", 0).ElementAt("option", 1)); AssertUI.IsSelected(browser.ElementAt("select", 1).ElementAt("option", 2)); // click the third button - the first value should be selected in the second select, the first select should not change - browser.ElementAt("input", 2).Click().Wait(); + browser.ElementAt("input", 2).Click(); AssertUI.IsSelected(browser.ElementAt("select", 0).ElementAt("option", 1)); AssertUI.IsSelected(browser.ElementAt("select", 1).ElementAt("option", 0)); } @@ -223,24 +223,18 @@ void resetTextbox() } commandButton.Click(); - browser.WaitFor(() => { - AssertUI.Value(textBox, "55"); - }, 500); + AssertUI.Value(textBox, "55"); resetTextbox(); staticCommandButton.Click(); - browser.WaitFor(() => { - AssertUI.Value(textBox, "55"); - }, 500); + AssertUI.Value(textBox, "55"); resetTextbox(); internalSequenceStaticCommandButton.Click(); - browser.WaitFor(() => { - AssertUI.Value(textBox, "2"); - }, 500); + AssertUI.Value(textBox, "2"); }); } @@ -254,7 +248,7 @@ public void Feature_StaticCommand_StaticCommand_ArrayAssigment() AssertUI.InnerTextEquals(browser.ElementAt(".item", 1), "Martin"); var button = browser.Single("input[type=button]"); - button.Click().Wait(); + button.Click(); AssertUI.InnerTextEquals(browser.ElementAt(".item", 0), "Bob"); AssertUI.InnerTextEquals(browser.ElementAt(".item", 1), "Oliver"); @@ -272,7 +266,7 @@ public void Feature_StaticCommand_StaticCommand_LoadComplexDataFromService() textBox.SendKeys("Vindaloo"); var button = browser.Single("input[type=button]"); - button.Click().Wait(); + button.Click(); AssertUI.InnerTextEquals(browser.ElementAt(".name", 0), "Martin"); AssertUI.InnerTextEquals(browser.ElementAt(".name", 1), "Roman"); @@ -296,10 +290,8 @@ public void Feature_StaticCommand_CustomAwaiter() clearButton.Click(); testButton.Click(); - browser.WaitFor(() => { - var testElement = browser.First("[data-ui=result]"); - Assert.Equal("Test ok", testElement.GetInnerText()); - }, 1000); + var testElement = browser.First("[data-ui=result]"); + Assert.Equal("Test ok", testElement.GetInnerText()); }); } } diff --git a/src/DotVVM.Samples.Tests/Feature/ValidationSummaryTests.cs b/src/DotVVM.Samples.Tests/Feature/ValidationSummaryTests.cs index 40bbf1a9d1..f3b6ac69db 100644 --- a/src/DotVVM.Samples.Tests/Feature/ValidationSummaryTests.cs +++ b/src/DotVVM.Samples.Tests/Feature/ValidationSummaryTests.cs @@ -17,12 +17,12 @@ public void Control_ValidationSummary_RecursiveValidationSummary() RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.ControlSamples_ValidationSummary_RecursiveValidationSummary); - browser.ElementAt("input[type=button]", 0).Click().Wait(); + browser.ElementAt("input[type=button]", 0).Click(); browser.ElementAt("ul", 0).FindElements("li").ThrowIfDifferentCountThan(2); AssertUI.InnerTextEquals(browser.First("#result"), "false"); - browser.ElementAt("input[type=button]", 1).Click().Wait(); + browser.ElementAt("input[type=button]", 1).Click(); browser.ElementAt("ul", 1).FindElements("li").ThrowIfDifferentCountThan(1); AssertUI.InnerTextEquals(browser.First("#result"), "false"); }); @@ -39,23 +39,23 @@ public void Control_ValidationSummary_IncludeErrorsFromTarget(string url) browser.NavigateToUrl(url); var summary = browser.First("[data-ui=validationSummary]"); - Assert.Equal(0, summary.Children.Count); + browser.WaitFor(() => Assert.Equal(0, summary.Children.Count), 1000); var loginButton = browser.First("[data-ui=login-button]"); loginButton.Click(); - Assert.Equal(2, summary.Children.Count); + browser.WaitFor(() => Assert.Equal(2, summary.Children.Count), 1000); browser.First("[data-ui=nick-textbox]").SendKeys("Mike"); loginButton.Click(); - Assert.Equal(1, summary.Children.Count); + browser.WaitFor(() => Assert.Equal(1, summary.Children.Count), 1000); browser.First("[data-ui=password-textbox]").SendKeys("123"); loginButton.Click(); - Assert.Equal(1, summary.Children.Count); + browser.WaitFor(() => Assert.Equal(1, summary.Children.Count), 1000); browser.First("[data-ui=password-textbox]").SendKeys("4"); - loginButton.Click().Wait(); - Assert.Equal(0, summary.Children.Count); + loginButton.Click(); + browser.WaitFor(() => Assert.Equal(0, summary.Children.Count), 1000); }); } } diff --git a/src/DotVVM.Samples.Tests/Feature/ValidationTests.cs b/src/DotVVM.Samples.Tests/Feature/ValidationTests.cs index d3eaaa470a..b40553a89b 100644 --- a/src/DotVVM.Samples.Tests/Feature/ValidationTests.cs +++ b/src/DotVVM.Samples.Tests/Feature/ValidationTests.cs @@ -98,7 +98,7 @@ void testValue(string value) { textBox.Clear().SendKeys(value); } - button.Click().Wait(); + button.Click(); } void assertValidators(params bool[] states) { @@ -194,7 +194,6 @@ public void Feature_Validation_DynamicValidation() // load the customer browser.Click("input[type=button]"); - browser.Wait(); // try to validate browser.Last("input[type=button]").Click(); @@ -320,6 +319,7 @@ public void Feature_Validation_ModelStateErrors() //click first button - viewmodel error browser.ElementAt("input[type=button]", 0).Click(); + browser.WaitForPostback(); browser.FindElements(".vmErrors li").ThrowIfDifferentCountThan(1); AssertUI.IsNotDisplayed(browser.ElementAt(".vm1Error", 0)); AssertUI.IsNotDisplayed(browser.ElementAt(".vm2Error", 0)); @@ -336,6 +336,7 @@ public void Feature_Validation_ModelStateErrors() //click third button - nested viewmodel2 two errors browser.ElementAt("input[type=button]", 2).Click(); + browser.WaitForPostback(); browser.FindElements(".vmErrors li").ThrowIfDifferentCountThan(2); AssertUI.IsNotDisplayed(browser.ElementAt(".vm1Error", 0)); AssertUI.IsDisplayed(browser.ElementAt(".vm2Error", 0)); @@ -385,7 +386,6 @@ public void Feature_Validation_NestedValidation() // fill invalid value in the task title browser.SendKeys("input[type=text]", "test"); - browser.Wait(); browser.Click("input[type=button]"); // ensure validators @@ -496,14 +496,12 @@ public void Feature_Validation_RegexValidation() browser.NavigateToUrl(SamplesRouteUrls.FeatureSamples_Validation_RegexValidation); browser.ElementAt("input", 0).SendKeys("25"); - browser.Wait(); browser.ElementAt("input[type=button]", 0).Click(); AssertUI.IsNotDisplayed(browser.ElementAt("span", 0)); AssertUI.InnerTextEquals(browser.ElementAt("span", 1), "25"); browser.ElementAt("input", 0).SendKeys("a"); - browser.Wait(); browser.ElementAt("input[type=button]", 0).Click(); AssertUI.IsDisplayed(browser.ElementAt("span", 0)); @@ -546,7 +544,6 @@ public void Feature_Validation_SimpleValidation() // fill invalid value in the task title browser.SendKeys("input[type=text]", "test"); - browser.Wait(); browser.Click("input[type=button]"); // ensure validators visible @@ -560,7 +557,6 @@ public void Feature_Validation_SimpleValidation() // fill valid value in the task title browser.ClearElementsContent("input[type=text]"); browser.SendKeys("input[type=text]", "test@mail.com"); - browser.Wait(); browser.Click("input[type=button]"); // ensure validators not visible @@ -587,7 +583,6 @@ public void Feature_Validation_ValidationRulesLoadOnPostback() // click the validate button browser.Last("input[type=button]").Click(); - browser.Wait(); // ensure validators are hidden AssertUI.InnerTextEquals(browser.Last("span"), "true"); @@ -642,6 +637,7 @@ public void Feature_Validation_ValidationScopes2() browser.ElementAt("input[type=text]", 0).SendKeys("aaa"); browser.ElementAt("input[type=text]", 1).SendKeys("aaa"); AssertUI.TextEquals(browser.Single(".result"), "0"); + AssertUI.IsClickable(browser.ElementAt("input[type=button]", 0)); browser.ElementAt("input[type=button]", 0).Click(); AssertUI.TextEquals(browser.Single(".result"), "0"); AssertUI.HasNotClass(browser.ElementAt("input[type=text]", 0), "has-error"); @@ -695,24 +691,18 @@ public void Feature_Validation_Localization() browser.NavigateToUrl(SamplesRouteUrls.FeatureSamples_Validation_Localization); browser.ElementAt("button[type=submit]", 0).Click(); - browser.WaitFor(() => { - AssertUI.TextEquals(browser.Single(".result-code"), "This comes from resource file!"); - AssertUI.TextEquals(browser.Single(".result-markup"), "This comes from resource file!"); - }, 2000); + AssertUI.TextEquals(browser.Single(".result-code"), "This comes from resource file!"); + AssertUI.TextEquals(browser.Single(".result-markup"), "This comes from resource file!"); - browser.ElementAt("a", 1).Click().Wait(); + browser.ElementAt("a", 1).Click(); browser.ElementAt("button[type=submit]", 0).Click(); - browser.WaitFor(() => { - AssertUI.TextEquals(browser.Single(".result-code"), "Tohle pochází z resource souboru!"); - AssertUI.TextEquals(browser.Single(".result-markup"), "Tohle pochází z resource souboru!"); - }, 2000); + AssertUI.TextEquals(browser.Single(".result-code"), "Tohle pochází z resource souboru!"); + AssertUI.TextEquals(browser.Single(".result-markup"), "Tohle pochází z resource souboru!"); - browser.ElementAt("a", 0).Click().Wait(); + browser.ElementAt("a", 0).Click(); browser.ElementAt("button[type=submit]", 0).Click(); - browser.WaitFor(() => { - AssertUI.TextEquals(browser.Single(".result-code"), "This comes from resource file!"); - AssertUI.TextEquals(browser.Single(".result-markup"), "This comes from resource file!"); - }, 2000); + AssertUI.TextEquals(browser.Single(".result-code"), "This comes from resource file!"); + AssertUI.TextEquals(browser.Single(".result-markup"), "This comes from resource file!"); }); } @@ -727,34 +717,26 @@ public void Feature_Validation_CustomValidation() var textbox = browser.First("[data-ui=name-textbox]"); submitButton.Click(); - browser.WaitFor(() => { - Assert.Equal(0, validationSummary.Children.Count); - AssertUI.HasNotClass(textbox, "has-error"); - }, 4000); + Assert.Equal(0, validationSummary.Children.Count); + AssertUI.HasNotClass(textbox, "has-error"); textbox.SendKeys("123"); submitButton.Click(); - browser.WaitFor(() => { - AssertUI.HasClass(textbox, "has-error"); - Assert.Equal(1, validationSummary.Children.Count); - }, 4000); + AssertUI.HasClass(textbox, "has-error"); + Assert.Equal(1, validationSummary.Children.Count); textbox.Clear(); textbox.SendKeys("Ted"); submitButton.Click(); - browser.WaitFor(() => { - Assert.Equal(0, validationSummary.Children.Count); - AssertUI.HasNotClass(textbox, "has-error"); - }, 4000); + Assert.Equal(0, validationSummary.Children.Count); + AssertUI.HasNotClass(textbox, "has-error"); textbox.SendKeys("123"); browser.First("[data-ui=notation-checkbox]").Click(); submitButton.Click(); - browser.WaitFor(() => { - AssertUI.HasClass(textbox, "has-error"); - Assert.Equal(1, validationSummary.Children.Count); - }, 4000); + AssertUI.HasClass(textbox, "has-error"); + Assert.Equal(1, validationSummary.Children.Count); }); } diff --git a/src/DotVVM.Samples.Tests/Feature/ViewModelCacheTests.cs b/src/DotVVM.Samples.Tests/Feature/ViewModelCacheTests.cs index c0825943d8..5edbd99032 100644 --- a/src/DotVVM.Samples.Tests/Feature/ViewModelCacheTests.cs +++ b/src/DotVVM.Samples.Tests/Feature/ViewModelCacheTests.cs @@ -20,7 +20,6 @@ public void Feature_ViewModelCache_ViewModelCacheMiss() { RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.FeatureSamples_ViewModelCache_ViewModelCacheMiss); - browser.Wait(); var cacheEnabled = browser.Single(".cacheEnabled").GetText() == "True"; @@ -31,10 +30,8 @@ public void Feature_ViewModelCache_ViewModelCacheMiss() // normal postback browser.ElementAt("input[type=button]", 0).Click(); - browser.WaitFor(() => { - AssertUI.TextEquals(result, "1"); - AssertUI.TextEquals(requestCount, "1"); - }, 5000); + AssertUI.TextEquals(result, "1"); + AssertUI.TextEquals(requestCount, "1"); // tamper with viewmodel cache id - it should do two requests but it should still work browser.ElementAt("input[type=button]", 1).Click(); diff --git a/src/DotVVM.Samples.Tests/Feature/ViewModelProtectionTests.cs b/src/DotVVM.Samples.Tests/Feature/ViewModelProtectionTests.cs index eef7e31904..7cbca92aa3 100644 --- a/src/DotVVM.Samples.Tests/Feature/ViewModelProtectionTests.cs +++ b/src/DotVVM.Samples.Tests/Feature/ViewModelProtectionTests.cs @@ -57,25 +57,19 @@ public void Feature_ViewModelProtection_NestedSignatures() // check that postback works browser.ElementAt("input[type=button]", 0).Click(); - browser.WaitFor(() => { - AssertUI.Text(pre, t => !t.Contains("encryptedThing", StringComparison.CurrentCultureIgnoreCase)); - }, 2000); + AssertUI.Text(pre, t => !t.Contains("encryptedThing", StringComparison.CurrentCultureIgnoreCase)); // change the viewmodel on client side and check that it works - browser.ElementAt("input[type=button]", 1).Click().Wait(); + browser.ElementAt("input[type=button]", 1).Click(); AssertUI.Text(pre, t => !t.Contains("\"Next\": {", StringComparison.CurrentCultureIgnoreCase)); browser.ElementAt("input[type=button]", 0).Click(); - browser.WaitFor(() => { - AssertUI.Text(pre, t => t.Contains("\"Next\": {", StringComparison.CurrentCultureIgnoreCase)); - }, 2000); + AssertUI.Text(pre, t => t.Contains("\"Next\": {", StringComparison.CurrentCultureIgnoreCase)); AssertUI.Text(pre, t => !t.Contains("encryptedThing", StringComparison.CurrentCultureIgnoreCase)); // tamper with encrypted values browser.GetJavaScriptExecutor().ExecuteScript("dotvvm.viewModels.root.viewModel.$encryptedValues(dotvvm.viewModels.root.viewModel.$encryptedValues()[1] + dotvvm.viewModels.root.viewModel.$encryptedValues()[0] + dotvvm.viewModels.root.viewModel.$encryptedValues().substring(2));"); browser.ElementAt("input[type=button]", 0).Click(); - browser.WaitFor(() => { - AssertUI.IsDisplayed(browser.Single("#debugWindow")); - }, 2000); + AssertUI.IsDisplayed(browser.Single("#debugWindow")); }); } @@ -92,18 +86,14 @@ public void Feature_ViewModelProtection_ViewModelProtection() browser.Last("a").Click(); // make sure it happened - browser.WaitFor(() => { - AssertUI.InnerTextEquals(browser.First("strong span"), "hello"); - }, 2000); + AssertUI.InnerTextEquals(browser.First("strong span"), "hello"); // try to do postback browser.SendKeys("input[type=text]", "DotVVM rocks!"); browser.Click("input[type=button]"); // verify that the original value was restored - browser.WaitFor(() => { - AssertUI.InnerTextEquals(browser.First("strong span"), originalValue); - }, 2000); + AssertUI.InnerTextEquals(browser.First("strong span"), originalValue); }); } @@ -118,7 +108,7 @@ public void Feature_ViewModelProtection_ComplexViewModelProtection(string messag RunComplexViewModelProtectionTest(browser => { var message = browser.Single(messageDataUi, this.SelectByDataUi); AssertUI.TextEquals(message, originalText); - browser.Single($"change-{messageDataUi}", this.SelectByDataUi).Click().Wait(); + browser.Single($"change-{messageDataUi}", this.SelectByDataUi).Click(); message = browser.Single(messageDataUi, this.SelectByDataUi); AssertUI.TextEquals(message, changedText); diff --git a/src/DotVVM.Samples.Tests/Feature/ViewModuleTests.cs b/src/DotVVM.Samples.Tests/Feature/ViewModuleTests.cs index 54ad25aae5..de78d85fba 100644 --- a/src/DotVVM.Samples.Tests/Feature/ViewModuleTests.cs +++ b/src/DotVVM.Samples.Tests/Feature/ViewModuleTests.cs @@ -22,10 +22,8 @@ public void Feature_ViewModules_ModuleInMarkupControl() { RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.FeatureSamples_ViewModules_ModuleInMarkupControl); - browser.Wait(); - var log = browser.Single("#log"); - browser.WaitFor(() => AssertLastLogEntry(log, "testViewModule: init"), 8000); + AssertLastLogEntry(log, "testViewModule: init"); var moduleButtons = browser.FindElements("input[type=button]"); var incrementValue = browser.First(".increment-value"); @@ -39,10 +37,9 @@ public void Feature_ViewModules_ModuleInMarkupControlTwice() { RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.FeatureSamples_ViewModules_ModuleInMarkupControlTwice); - browser.Wait(); var log = browser.Single("#log"); - browser.WaitFor(() => AssertLastLogEntry(log, "testViewModule: init"), 8000); + AssertLastLogEntry(log, "testViewModule: init"); var toggleButton = browser.Single(".toggle input[type=button]"); @@ -54,7 +51,7 @@ public void Feature_ViewModules_ModuleInMarkupControlTwice() // show second instance toggleButton.Click(); - browser.WaitFor(() => AssertLastLogEntry(log, "testViewModule: init"), 8000); + AssertLastLogEntry(log, "testViewModule: init"); // test second instance moduleButtons = browser.FindElements(".control2 input[type=button]"); @@ -64,11 +61,11 @@ public void Feature_ViewModules_ModuleInMarkupControlTwice() // hide second instance toggleButton.Click(); - browser.WaitFor(() => AssertLastLogEntry(log, "testViewModule: dispose"), 8000); + AssertLastLogEntry(log, "testViewModule: dispose"); // show second instance toggleButton.Click(); - browser.WaitFor(() => AssertLastLogEntry(log, "testViewModule: init"), 8000); + AssertLastLogEntry(log, "testViewModule: init"); }); } @@ -77,10 +74,9 @@ public void Feature_ViewModules_ModuleInPage() { RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.FeatureSamples_ViewModules_ModuleInPage); - browser.Wait(); var log = browser.Single("#log"); - browser.WaitFor(() => AssertLastLogEntry(log, "testViewModule: init"), 8000); + AssertLastLogEntry(log, "testViewModule: init"); var moduleButtons = browser.FindElements("input[type=button]"); var incrementValue = browser.First(".increment-value"); @@ -94,14 +90,12 @@ public void Feature_ViewModules_ModuleInPageCommandAmbiguous() { RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.FeatureSamples_ViewModules_ModuleInPageCommandAmbiguous); - browser.Wait(); var log = browser.Single("#log"); - browser.WaitFor(() => AssertLogEntry(log, "testViewModule: init"), 8000); - browser.WaitFor(() => AssertLogEntry(log, "testViewModule2: init"), 8000); + AssertLogEntry(log, "testViewModule: init"); + AssertLogEntry(log, "testViewModule2: init"); browser.First("input[type=button]").Click(); - browser.Wait(8000); AssertUI.InnerText(log, t => !t.Contains("testViewModule: commands.noArgs()")); AssertUI.InnerText(log, t => !t.Contains("testViewModule2: commands.noArgs()")); }); @@ -112,11 +106,10 @@ public void Feature_ViewModules_ModuleInPageMasterPage() { RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.FeatureSamples_ViewModules_ModuleInPageMasterPage); - browser.Wait(); var log = browser.Single("#log"); - browser.WaitFor(() => AssertLogEntry(log, "testViewModule: init"), 8000); - browser.WaitFor(() => AssertLogEntry(log, "testViewModule2: init"), 8000); + AssertLogEntry(log, "testViewModule: init"); + AssertLogEntry(log, "testViewModule2: init"); var moduleButtons = browser.FindElements(".master input[type=button]"); var incrementValue = browser.First(".master .increment-value"); @@ -135,17 +128,16 @@ public void Feature_ViewModules_ModuleInPageSpaMasterPage() { RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.FeatureSamples_ViewModules_ModuleInPageSpaMasterPage2); - browser.Wait(); + var links = browser.FindElements("a"); var log = browser.Single("#log"); - browser.WaitFor(() => AssertLastLogEntry(log, "testViewModule: init"), 8000); - browser.Wait(8000); + AssertLastLogEntry(log, "testViewModule: init"); AssertUI.InnerText(log, t => !t.Contains("testViewModule2: init")); links[0].Click(); - browser.WaitFor(() => AssertLastLogEntry(log, "testViewModule2: init"), 8000); + AssertLastLogEntry(log, "testViewModule2: init"); var moduleButtons = browser.FindElements(".master input[type=button]"); var incrementValue = browser.First(".master .increment-value"); @@ -158,36 +150,36 @@ public void Feature_ViewModules_ModuleInPageSpaMasterPage() TestModule(browser, log, moduleButtons, incrementValue, result, "testViewModule2"); links[1].Click(); - browser.WaitFor(() => AssertLastLogEntry(log, "testViewModule2: dispose"), 8000); + AssertLastLogEntry(log, "testViewModule2: dispose"); links[0].Click(); - browser.WaitFor(() => AssertLastLogEntry(log, "testViewModule2: init"), 8000); + AssertLastLogEntry(log, "testViewModule2: init"); }); } private void TestModule(IBrowserWrapper browser, IElementWrapper log, IElementWrapperCollection moduleButtons, IElementWrapper incrementValue, IElementWrapper result, string prefix) { moduleButtons[0].Click(); - browser.WaitFor(() => AssertLastLogEntry(log, prefix + ": commands.noArgs()"), 8000); + AssertLastLogEntry(log, prefix + ": commands.noArgs()"); moduleButtons[1].Click(); - browser.WaitFor(() => AssertLastLogEntry(log, prefix + ": commands.oneArg(10)"), 8000); + AssertLastLogEntry(log, prefix + ": commands.oneArg(10)"); moduleButtons[2].Click(); - browser.WaitFor(() => AssertLastLogEntry(log, prefix + @": commands.twoArgs(10, {""Test"":""Hello"",""$type"":""4wHojaMvtyXNR6aMRsZ4cWanOvA=""})"), 8000); + AssertLastLogEntry(log, prefix + @": commands.twoArgs(10, {""Test"":""Hello"",""$type"":""4wHojaMvtyXNR6aMRsZ4cWanOvA=""})"); AssertUI.InnerTextEquals(incrementValue, "0"); moduleButtons[3].Click(); - browser.WaitFor(() => AssertLastLogEntry(log, prefix + ": commands.syncIncrement(0)"), 8000); + AssertLastLogEntry(log, prefix + ": commands.syncIncrement(0)"); AssertUI.InnerTextEquals(incrementValue, "1"); moduleButtons[4].Click(); browser.WaitFor(() => AssertLastLogEntry(log, prefix + ": commands.asyncIncrement(1) begin"), 8000); browser.WaitFor(() => AssertLastLogEntry(log, prefix + ": commands.asyncIncrement(1) end"), 8000); AssertUI.InnerTextEquals(incrementValue, "2"); moduleButtons[5].Click(); - browser.WaitFor(() => AssertLastLogEntry(log, prefix + ": commands.callIncrementCommand(2)"), 8000); + AssertLastLogEntry(log, prefix + ": commands.callIncrementCommand(2)"); AssertUI.InnerTextEquals(incrementValue, "3"); moduleButtons[6].Click(); - browser.WaitFor(() => AssertLastLogEntry(log, prefix + ": commands.callSetResultCommand()"), 8000); + AssertLastLogEntry(log, prefix + ": commands.callSetResultCommand()"); AssertUI.InnerTextEquals(result, "1_test_abc"); } @@ -208,15 +200,13 @@ public void Feature_ViewModules_IncrementerInRepeater() { RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.FeatureSamples_ViewModules_IncrementerInRepeater); - browser.Wait(); + var buttons = browser.FindElements("input[type=button]"); void EnsureId(IElementWrapper inc, string id) { - browser.WaitFor(() => { - AssertUI.TextEquals(inc.Single(".id"), id); - }, 500); + AssertUI.TextEquals(inc.Single(".id"), id); } void EnsureValue(IElementWrapper inc, string value) { diff --git a/src/DotVVM.Samples.Tests/Profiles/seleniumconfig.aspnetcore.chrome.json b/src/DotVVM.Samples.Tests/Profiles/seleniumconfig.aspnetcore.chrome.json index 9e4ff3f94b..2f125bcda2 100644 --- a/src/DotVVM.Samples.Tests/Profiles/seleniumconfig.aspnetcore.chrome.json +++ b/src/DotVVM.Samples.Tests/Profiles/seleniumconfig.aspnetcore.chrome.json @@ -1,7 +1,7 @@ { "factories": { "chrome:fast": { - "capabilities": [ "--no-sandbox" ] + "capabilities": [ "--headless", "--no-sandbox" ] } }, "baseUrls": [ diff --git a/src/DotVVM.Samples.Tests/Profiles/seleniumconfig.aspnetcore.ie.json b/src/DotVVM.Samples.Tests/Profiles/seleniumconfig.aspnetcore.ie.json index 7871cbd033..9e3387bb79 100644 --- a/src/DotVVM.Samples.Tests/Profiles/seleniumconfig.aspnetcore.ie.json +++ b/src/DotVVM.Samples.Tests/Profiles/seleniumconfig.aspnetcore.ie.json @@ -1,6 +1,6 @@ { "factories": { - "ie:fast": {} + "ie:dev": {} }, "baseUrls": [ "http://localhost:16018/" diff --git a/src/DotVVM.Samples.Tests/Profiles/seleniumconfig.aspnetcorelatest.ie.json b/src/DotVVM.Samples.Tests/Profiles/seleniumconfig.aspnetcorelatest.ie.json index fb5e4b7eba..bc51f0db38 100644 --- a/src/DotVVM.Samples.Tests/Profiles/seleniumconfig.aspnetcorelatest.ie.json +++ b/src/DotVVM.Samples.Tests/Profiles/seleniumconfig.aspnetcorelatest.ie.json @@ -1,6 +1,6 @@ { "factories": { - "ie:fast": {} + "ie:dev": {} }, "baseUrls": [ "http://localhost:16019/" diff --git a/src/DotVVM.Samples.Tests/Profiles/seleniumconfig.owin.chrome.json b/src/DotVVM.Samples.Tests/Profiles/seleniumconfig.owin.chrome.json index 38459bf1f1..56ce5e0f4f 100644 --- a/src/DotVVM.Samples.Tests/Profiles/seleniumconfig.owin.chrome.json +++ b/src/DotVVM.Samples.Tests/Profiles/seleniumconfig.owin.chrome.json @@ -1,7 +1,7 @@ { "factories": { "chrome:fast": { - "capabilities": [ "--no-sandbox" ] + "capabilities": [ "--headless" ] } }, "baseUrls": [ diff --git a/src/DotVVM.Samples.Tests/Profiles/seleniumconfig.owin.ie.json b/src/DotVVM.Samples.Tests/Profiles/seleniumconfig.owin.ie.json index f71028d1b8..2fe771fa21 100644 --- a/src/DotVVM.Samples.Tests/Profiles/seleniumconfig.owin.ie.json +++ b/src/DotVVM.Samples.Tests/Profiles/seleniumconfig.owin.ie.json @@ -1,6 +1,6 @@ { "factories": { - "ie:fast": {} + "ie:dev": {} }, "baseUrls": [ "http://localhost:5407/" diff --git a/src/DotVVM.Samples.Tests/SpaNavigationToEmptyUrlTests.cs b/src/DotVVM.Samples.Tests/SpaNavigationToEmptyUrlTests.cs index c5803414d3..da7695614b 100644 --- a/src/DotVVM.Samples.Tests/SpaNavigationToEmptyUrlTests.cs +++ b/src/DotVVM.Samples.Tests/SpaNavigationToEmptyUrlTests.cs @@ -18,7 +18,6 @@ public void SpaNavigationToEmptyUrlTest() { RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.SpaNavigationToEmptyUrl); - browser.Wait(); browser.Single("a").Click().Wait(); @@ -33,7 +32,6 @@ public void SpaNavigationToEmptyUrlTest_Redirect() { RunInAllBrowsers(browser => { browser.NavigateToUrl(SamplesRouteUrls.SpaNavigationToEmptyUrl); - browser.Wait(); browser.Single("input[type=button]").Click().Wait(); diff --git a/src/DotVVM.Testing.Abstractions/SamplesRouteUrls.designer.cs b/src/DotVVM.Testing.Abstractions/SamplesRouteUrls.designer.cs index 36d20baa80..e872422f9c 100644 --- a/src/DotVVM.Testing.Abstractions/SamplesRouteUrls.designer.cs +++ b/src/DotVVM.Testing.Abstractions/SamplesRouteUrls.designer.cs @@ -200,6 +200,7 @@ public partial class SamplesRouteUrls public const string FeatureSamples_Caching_CachedValues = "FeatureSamples/Caching/CachedValues"; public const string FeatureSamples_ChildViewModelInvokeMethods_ChildViewModelInvokeMethods = "FeatureSamples/ChildViewModelInvokeMethods/ChildViewModelInvokeMethods"; public const string FeatureSamples_ClientExtenders_PasswordStrength = "FeatureSamples/ClientExtenders/PasswordStrength"; + public const string FeatureSamples_CommandActionFilter_CommandActionFilter = "FeatureSamples/CommandActionFilter/CommandActionFilter"; public const string FeatureSamples_CommandArguments_CommandArguments = "FeatureSamples/CommandArguments/CommandArguments"; public const string FeatureSamples_CommandArguments_CommandArgumentTypes = "FeatureSamples/CommandArguments/CommandArgumentTypes"; public const string FeatureSamples_CommandArguments_ReturnValue = "FeatureSamples/CommandArguments/ReturnValue"; @@ -220,7 +221,10 @@ public partial class SamplesRouteUrls public const string FeatureSamples_IdGeneration_IdGeneration = "FeatureSamples/IdGeneration/IdGeneration"; public const string FeatureSamples_JavascriptEvents_JavascriptEvents = "FeatureSamples/JavascriptEvents/JavascriptEvents"; public const string FeatureSamples_JavascriptTranslation_GenericMethodTranslation = "FeatureSamples/JavascriptTranslation/GenericMethodTranslation"; + public const string FeatureSamples_JavascriptTranslation_MathMethodTranslation = "FeatureSamples/JavascriptTranslation/MathMethodTranslation"; + public const string FeatureSamples_LambdaExpressions_ClientSideFiltering = "FeatureSamples/LambdaExpressions/ClientSideFiltering"; public const string FeatureSamples_LambdaExpressions_LambdaExpressions = "FeatureSamples/LambdaExpressions/LambdaExpressions"; + public const string FeatureSamples_LambdaExpressions_StaticCommands = "FeatureSamples/LambdaExpressions/StaticCommands"; public const string FeatureSamples_LiteralBinding_LiteralBinding_Zero = "FeatureSamples/LiteralBinding/LiteralBinding_Zero"; public const string FeatureSamples_Localization_Globalize = "FeatureSamples/Localization/Globalize"; public const string FeatureSamples_Localization_Localization = "FeatureSamples/Localization/Localization"; @@ -273,9 +277,12 @@ public partial class SamplesRouteUrls public const string FeatureSamples_ReturnedFile_ReturnedFileSample = "FeatureSamples/ReturnedFile/ReturnedFileSample"; public const string FeatureSamples_Serialization_DeserializationVirtualElements = "FeatureSamples/Serialization/DeserializationVirtualElements"; public const string FeatureSamples_Serialization_Dictionary = "FeatureSamples/Serialization/Dictionary"; + public const string FeatureSamples_Serialization_EnumSerializationCoercion = "FeatureSamples/Serialization/EnumSerializationCoercion"; public const string FeatureSamples_Serialization_EnumSerializationWithJsonConverter = "FeatureSamples/Serialization/EnumSerializationWithJsonConverter"; public const string FeatureSamples_Serialization_ObservableCollectionShouldContainObservables = "FeatureSamples/Serialization/ObservableCollectionShouldContainObservables"; public const string FeatureSamples_Serialization_Serialization = "FeatureSamples/Serialization/Serialization"; + public const string FeatureSamples_Serialization_TimeSpan = "FeatureSamples/Serialization/TimeSpan"; + public const string FeatureSamples_Serialization_SerializationDateTimeOffset = "FeatureSamples/Serialization/SerializationDateTimeOffset"; public const string FeatureSamples_ServerComments_ServerComments = "FeatureSamples/ServerComments/ServerComments"; public const string FeatureSamples_ServerSideStyles_ServerSideStyles = "FeatureSamples/ServerSideStyles/ServerSideStyles"; public const string FeatureSamples_ServerSideStyles_ServerSideStyles_ControlProperties = "FeatureSamples/ServerSideStyles/ServerSideStyles_ControlProperties"; diff --git a/src/DotVVM.Tools.StartupPerfTester/DotVVM.Tools.StartupPerfTester.csproj b/src/DotVVM.Tools.StartupPerfTester/DotVVM.Tools.StartupPerfTester.csproj index d6ed39472b..71d637e16a 100644 --- a/src/DotVVM.Tools.StartupPerfTester/DotVVM.Tools.StartupPerfTester.csproj +++ b/src/DotVVM.Tools.StartupPerfTester/DotVVM.Tools.StartupPerfTester.csproj @@ -8,7 +8,7 @@ dotvvm-startup-perf true DotVVM.Tools.StartupPerf - 3.0.0 + 3.0.2 RIGANTI Command-line tool for measuring startup performance of DotVVM apps. false diff --git a/src/DotVVM.sln b/src/DotVVM.sln index 1013d786fc..edffc92011 100644 --- a/src/DotVVM.sln +++ b/src/DotVVM.sln @@ -1,4 +1,5 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 + +Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.29102.190 MinimumVisualStudioVersion = 10.0.40219.1 @@ -82,7 +83,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotVVM.Samples.BasicSamples EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotVVM.Samples.BasicSamples.Api.AspNetCoreLatest", "DotVVM.Samples.BasicSamples.Api.AspNetCoreLatest\DotVVM.Samples.BasicSamples.Api.AspNetCoreLatest.csproj", "{DFB2A49E-F186-46F8-A079-1A799F9B5461}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotVVM.Tools.StartupPerfTester", "DotVVM.Tools.StartupPerfTester\DotVVM.Tools.StartupPerfTester.csproj", "{797EC052-6E80-4591-B4F8-4F7E6651DA01}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotVVM.Tools.StartupPerfTester", "DotVVM.Tools.StartupPerfTester\DotVVM.Tools.StartupPerfTester.csproj", "{797EC052-6E80-4591-B4F8-4F7E6651DA01}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/src/Templates/DotVVM.Templates.nuspec b/src/Templates/DotVVM.Templates.nuspec index e2f6dd4854..8ad4271952 100644 --- a/src/Templates/DotVVM.Templates.nuspec +++ b/src/Templates/DotVVM.Templates.nuspec @@ -2,7 +2,7 @@ DotVVM.Templates - 3.0.0 + 3.0.2 RIGANTI DotVVM Project Templates en-US diff --git a/src/Tools/CopyBetweenNugetFeeds.ps1 b/src/Tools/CopyBetweenNugetFeeds.ps1 index 4f034d0fb5..2f54346bb0 100644 --- a/src/Tools/CopyBetweenNugetFeeds.ps1 +++ b/src/Tools/CopyBetweenNugetFeeds.ps1 @@ -29,53 +29,55 @@ foreach ($package in $packages) { # standard package if ($package.Type -eq "standard") { - & .\tools\nuget.exe install $packageId -OutputDirectory .\tools\packages -version $version -DirectDownload -NoCache -DependencyVersion Ignore -source $internalServer + & .\tools\nuget.exe install $packageId -OutputDirectory .\tools\packages -version $version -DirectDownload -NoCache -DependencyVersion Ignore -source $internalServer | Out-Host $nupkgFile = dir -s ./tools/packages/$packageId.$version.nupkg | Select -First 1 Write-Host "Downloaded package located on '$nupkgFile'" } # standard package if ($package.Type -eq "tool") { ## dotnet tools - dotnet tool install $packageId --tool-path ./tools/packages --version $version + dotnet tool install $packageId --tool-path ./tools/packages --version $version --add-source $internalServer | Out-Host $nupkgFile = dir -s ./tools/packages/*/$packageId.$version.nupkg | Select -First 1 Write-Host "Downloaded tool located on '$nupkgFile'" } # dotnet templates if ($package.Type -eq "template") { - dotnet new --install "$packageId::$version" --force --nuget-source $internalServer - $nupkgFile = dir $env:USERPROFILE\.templateengine\dotnetcli\ -s | where { $_.Name -eq "$packageId.$version.nupkg" } | select { $_.FullName } | Select -First 1 + dotnet new --install "$packageId::$version" --force --nuget-source $internalServer | Out-Host + $nupkgFile = dir -s $env:USERPROFILE/.templateengine/dotnetcli -filter $packageId.$version.nupkg | Select -First 1 Write-Host "Downloaded template located on '$nupkgFile'" } if ($nupkgFile) { # upload Write-Host "Uploading package..." - & .\tools\nuget.exe push $nupkgFile -source $server -apiKey $apiKey + & .\tools\nuget.exe push $nupkgFile -source $server -apiKey $apiKey | Out-Host Write-Host "Package uploaded to $server." } - if ( Test-Path -Path ./tools/packages ) { + if (Test-Path -Path ./tools/packages) { Remove-Item -Recurse -Force ./tools/packages } # snupkg management - $snupkgUrl = "file://$internalSnupkgServer/snupkg/" + $snupkgUrl = "file://$internalSnupkgServer/snupkg/"+$packageId + "." + $version + ".snupkg" $snupkgFile = Join-Path $PSScriptRoot ($packageId + "." + $version + ".snupkg") try { $webClient.DownloadFile($snupkgUrl, $snupkgFile) $snupkgDownloaded = $true; - }catch { + } catch { Write-Host "No snupkg package found!" $snupkgDownloaded = $false; - } + } if ($snupkgDownloaded -eq $true){ Write-Host "Uploading snupkg package..." - & .\Tools\nuget.exe push $snupkgFile -source $server -apiKey $apiKey - Remove-Item $nupkgFile - try {Remove-Item $snupkgFile}catch { + & .\Tools\nuget.exe push $snupkgFile -source $server -apiKey $apiKey | Out-Host + Write-Host "Uploaded snupkg package." + try { + Remove-Item $snupkgFile + } catch { Write-Host "Unable to cleanup snupkg..." } } -} +} \ No newline at end of file diff --git a/src/Tools/npm/dotvvm-types/package.json b/src/Tools/npm/dotvvm-types/package.json index e77f849713..df68dd9995 100644 --- a/src/Tools/npm/dotvvm-types/package.json +++ b/src/Tools/npm/dotvvm-types/package.json @@ -1,6 +1,6 @@ { "name": "dotvvm-types", - "version": "3.0.0-preview01", + "version": "3.0.2", "description": "MVVM framework for ASP.NET", "types": "index.d.ts", "repository": {