From 1555a02b9c6be79d8b9f1ee2dd9a15e07cf12e2f Mon Sep 17 00:00:00 2001 From: jfebres Date: Tue, 26 Mar 2013 09:48:23 -0500 Subject: [PATCH 1/2] Added support to validate list items. --- .../Ajax/IntegratedAjaxEndpoint.cs | 22 ++++++- .../Ajax/IntegratedAjaxTester.cs | 47 ++++++++++++++- .../FubuMVC.Validation.Tests.csproj | 1 + .../FubuValidationServiceRegistryTester.cs | 6 ++ .../ListFieldValidationSourceTester.cs | 59 +++++++++++++++++++ .../FubuMVC.Validation.csproj | 16 +++-- .../FubuValidationServiceRegistry.cs | 1 + .../ListFieldValidationSource.cs | 23 ++++++++ .../Fields/ListValidationRule.cs | 37 ++++++++++++ src/FubuValidation/FubuValidation.csproj | 1 + 10 files changed, 205 insertions(+), 8 deletions(-) create mode 100644 src/FubuMVC.Validation.Tests/ListFieldValidationSourceTester.cs create mode 100644 src/FubuMVC.Validation/ListFieldValidationSource.cs create mode 100644 src/FubuValidation/Fields/ListValidationRule.cs diff --git a/src/FubuMVC.Validation.IntegrationTesting/Ajax/IntegratedAjaxEndpoint.cs b/src/FubuMVC.Validation.IntegrationTesting/Ajax/IntegratedAjaxEndpoint.cs index ddfefda..507eb7c 100644 --- a/src/FubuMVC.Validation.IntegrationTesting/Ajax/IntegratedAjaxEndpoint.cs +++ b/src/FubuMVC.Validation.IntegrationTesting/Ajax/IntegratedAjaxEndpoint.cs @@ -1,4 +1,5 @@ -using FubuMVC.Core.Ajax; +using System.Collections.Generic; +using FubuMVC.Core.Ajax; using FubuValidation; namespace FubuMVC.Validation.IntegrationTesting.Ajax @@ -9,6 +10,12 @@ public AjaxContinuation post_ajax(AjaxRequest request) { return AjaxContinuation.Successful(); } + + public AjaxContinuation post_ajax_collection(AjaxCollectionRequest request) + { + return AjaxContinuation.Successful(); + } + } public class AjaxRequest @@ -16,4 +23,17 @@ public class AjaxRequest [Required] public string Name { get; set; } } + + public class AjaxCollectionRequest + { + public List Collection { get; set; } + } + + public class CollectionItem + { + [Required] + public string Name { get; set; } + } + + } \ No newline at end of file diff --git a/src/FubuMVC.Validation.IntegrationTesting/Ajax/IntegratedAjaxTester.cs b/src/FubuMVC.Validation.IntegrationTesting/Ajax/IntegratedAjaxTester.cs index 3521f3b..a52084c 100644 --- a/src/FubuMVC.Validation.IntegrationTesting/Ajax/IntegratedAjaxTester.cs +++ b/src/FubuMVC.Validation.IntegrationTesting/Ajax/IntegratedAjaxTester.cs @@ -1,5 +1,7 @@ -using System.Diagnostics; +using System.Collections.Generic; +using System.Diagnostics; using System.Linq; +using FubuCore; using FubuCore.Reflection; using FubuMVC.Core.Ajax; using FubuTestingSupport; @@ -11,11 +13,16 @@ namespace FubuMVC.Validation.IntegrationTesting.Ajax public class IntegratedAjaxTester : ValidationHarness { private AjaxRequest theRequest; + private AjaxCollectionRequest theCollectionRequest; [SetUp] public void SetUp() { theRequest = new AjaxRequest(); + theCollectionRequest = new AjaxCollectionRequest + { + Collection = new List {new CollectionItem(), new CollectionItem()} + }; } protected override void configure(Core.FubuRegistry registry) @@ -39,7 +46,27 @@ private JsonResponse theContinuation Debug.WriteLine(response.ReadAsText()); throw; } - + + } + } + + + private JsonResponse theCollectionContinuation + { + get + { + var response = endpoints.PostJson(theCollectionRequest); + + try + { + return response.ReadAsJson(); + } + catch + { + Debug.WriteLine(response.ReadAsText()); + throw; + } + } } @@ -49,6 +76,12 @@ public void validation_passes() theRequest.Name = "Josh"; theContinuation.success.ShouldBeTrue(); } + [Test] + public void collection_validation_passes() + { + theCollectionRequest.Collection.Each((x, i) => x.Name = "Item{0}".ToFormat(i + 1)); + theCollectionContinuation.success.ShouldBeTrue(); + } [Test] public void validation_error_for_name() @@ -58,6 +91,16 @@ public void validation_error_for_name() errors.ShouldHaveCount(1); errors.Any(x => x.field == ReflectionHelper.GetAccessor(r => r.Name).Name).ShouldBeTrue(); + } + [Test] + public void validation_errors_for_collection_item_names() + { + theCollectionRequest.Collection.Each(x => x.Name = null); + var errors = theCollectionContinuation.errors; + + errors.ShouldHaveCount(2); + errors.Any(x => x.field == ReflectionHelper.GetAccessor(r => r.Collection[0].Name).Name).ShouldBeTrue(); + errors.Any(x => x.field == ReflectionHelper.GetAccessor(r => r.Collection[1].Name).Name).ShouldBeTrue(); } } diff --git a/src/FubuMVC.Validation.Tests/FubuMVC.Validation.Tests.csproj b/src/FubuMVC.Validation.Tests/FubuMVC.Validation.Tests.csproj index bda99bf..44aa52a 100644 --- a/src/FubuMVC.Validation.Tests/FubuMVC.Validation.Tests.csproj +++ b/src/FubuMVC.Validation.Tests/FubuMVC.Validation.Tests.csproj @@ -105,6 +105,7 @@ + diff --git a/src/FubuMVC.Validation.Tests/FubuValidationServiceRegistryTester.cs b/src/FubuMVC.Validation.Tests/FubuValidationServiceRegistryTester.cs index 7f6852b..c1d5d93 100644 --- a/src/FubuMVC.Validation.Tests/FubuValidationServiceRegistryTester.cs +++ b/src/FubuMVC.Validation.Tests/FubuValidationServiceRegistryTester.cs @@ -52,6 +52,12 @@ public void registers_the_default_field_rule_registry_as_singleton() verifyDefaultType().IsSingleton.ShouldBeTrue(); } + [Test] + public void registers_the_list_field_validation_source() + { + theGraph.ServicesFor().ShouldContain(x => x.Type == typeof(ListFieldValidationSource)); + } + [Test] public void adds_the_accessor_rules_field_source() { diff --git a/src/FubuMVC.Validation.Tests/ListFieldValidationSourceTester.cs b/src/FubuMVC.Validation.Tests/ListFieldValidationSourceTester.cs new file mode 100644 index 0000000..f0e59e3 --- /dev/null +++ b/src/FubuMVC.Validation.Tests/ListFieldValidationSourceTester.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using FubuCore.Reflection; +using FubuTestingSupport; +using FubuValidation.Fields; +using NUnit.Framework; + +namespace FubuMVC.Validation.Tests +{ + [TestFixture] + public class ListFieldValidationSourceTester : InteractionContext + { + [Test] + public void returns_an_empty_list_for_properties_that_are_not_of_ilist_generic_type() + { + ClassUnderTest.RulesFor(getProperty(x => x.GenericEnumerableProperty)).ShouldHaveCount(0); + ClassUnderTest.RulesFor(getProperty(x => x.RegularEnumerableProperty)).ShouldHaveCount(0); + ClassUnderTest.RulesFor(getProperty(x => x.RegularListProperty)).ShouldHaveCount(0); + ClassUnderTest.RulesFor(getProperty(x => x.CollectionProperty)).ShouldHaveCount(0); + ClassUnderTest.RulesFor(getProperty(x => x.StringProperty)).ShouldHaveCount(0); + ClassUnderTest.RulesFor(getProperty(x => x.ObjectProperty)).ShouldHaveCount(0); + ClassUnderTest.RulesFor(getProperty(x => x.RegularListProperty)).ShouldHaveCount(0); + } + + [Test] + public void returns_a_ListValidationRule_for_properties_that_are_of_ilist_generic_type() + { + ClassUnderTest.RulesFor(getProperty(x => x.GenericListProperty)).ToArray() + .ShouldHaveCount(1).First() + .ShouldBeOfType(); + + ClassUnderTest.RulesFor(getProperty(x => x.ArrayProperty)).ToArray() + .ShouldHaveCount(1).First() + .ShouldBeOfType(); + } + + private PropertyInfo getProperty(Expression> expression) + { + return ReflectionHelper.GetProperty(expression); + } + public class ListFieldValidationSourceTesterModel + { + public IList GenericListProperty { get; set; } + public Model[] ArrayProperty { get; set; } + public IEnumerable GenericEnumerableProperty { get; set; } + public IEnumerable RegularEnumerableProperty { get; set; } + public IList RegularListProperty { get; set; } + public ICollection CollectionProperty { get; set; } + public string StringProperty { get; set; } + public object ObjectProperty { get; set; } + public class Model { } + + } + } +} \ No newline at end of file diff --git a/src/FubuMVC.Validation/FubuMVC.Validation.csproj b/src/FubuMVC.Validation/FubuMVC.Validation.csproj index ca10d32..9294bc5 100644 --- a/src/FubuMVC.Validation/FubuMVC.Validation.csproj +++ b/src/FubuMVC.Validation/FubuMVC.Validation.csproj @@ -4,7 +4,8 @@ Debug AnyCPU - + + 2.0 {1D14C442-9F90-4B28-B4F9-79CD6E1B6E40} {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} @@ -14,8 +15,10 @@ FubuMVC.Validation v4.0 false - - + + + + 4.0 @@ -88,6 +91,7 @@ + @@ -193,10 +197,12 @@ True 18912 / - + + False False - + + False diff --git a/src/FubuMVC.Validation/FubuValidationServiceRegistry.cs b/src/FubuMVC.Validation/FubuValidationServiceRegistry.cs index 9078163..3b5c376 100644 --- a/src/FubuMVC.Validation/FubuValidationServiceRegistry.cs +++ b/src/FubuMVC.Validation/FubuValidationServiceRegistry.cs @@ -14,6 +14,7 @@ public FubuValidationServiceRegistry() SetServiceIfNone(); AddService(); + AddService(); setSingleton(); setSingleton(); diff --git a/src/FubuMVC.Validation/ListFieldValidationSource.cs b/src/FubuMVC.Validation/ListFieldValidationSource.cs new file mode 100644 index 0000000..8215343 --- /dev/null +++ b/src/FubuMVC.Validation/ListFieldValidationSource.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using System.Reflection; +using FubuCore; +using FubuValidation.Fields; + +namespace FubuMVC.Validation +{ + public class ListFieldValidationSource : IFieldValidationSource + { + public IEnumerable RulesFor(PropertyInfo property) + { + if (property.PropertyType.Closes(typeof(IList<>))) + { + yield return new ListValidationRule(); + } + } + + public void AssertIsValid() + { + + } + } +} \ No newline at end of file diff --git a/src/FubuValidation/Fields/ListValidationRule.cs b/src/FubuValidation/Fields/ListValidationRule.cs new file mode 100644 index 0000000..7a504c5 --- /dev/null +++ b/src/FubuValidation/Fields/ListValidationRule.cs @@ -0,0 +1,37 @@ +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using FubuCore; +using FubuCore.Reflection; +using FubuLocalization; + +namespace FubuValidation.Fields +{ + public class ListValidationRule : IFieldValidationRule + { + private static readonly MethodInfo _method; + static ListValidationRule() + { + _method = ReflectionHelper.GetMethod(x => x[0]); + } + + public void Validate(Accessor accessor, ValidationContext context) + { + accessor.GetValue(context.Target).As().Cast().Each((item, i) => + { + var notification = context.Provider.Validate(item); + if (!notification.IsValid()) + { + var indexer = new MethodValueGetter(_method, new object[] { i }); + var values = new List(); + values.AddRange(accessor.Getters()); + values.Add(indexer); + var childAccessor = new PropertyChain(values.ToArray()); + context.Notification.AddChild(childAccessor, notification); + } + }); + } + StringToken IFieldValidationRule.Token { get; set; } + } +} \ No newline at end of file diff --git a/src/FubuValidation/FubuValidation.csproj b/src/FubuValidation/FubuValidation.csproj index 85db153..2c35992 100644 --- a/src/FubuValidation/FubuValidation.csproj +++ b/src/FubuValidation/FubuValidation.csproj @@ -73,6 +73,7 @@ + From c6a34c5a71d086a4b9c2ff2fc1fdf75da66bc7f6 Mon Sep 17 00:00:00 2001 From: jfebres Date: Fri, 5 Apr 2013 19:37:50 -0500 Subject: [PATCH 2/2] Added test for LoFi collection scenario. --- ...buMVC.Validation.IntegrationTesting.csproj | 2 + .../LoFi/IntegratedCollectionLoFiTester.cs | 62 +++++++++++++++++++ .../LoFi/IntegratedLoFiCollectionEdpoint.cs | 33 ++++++++++ 3 files changed, 97 insertions(+) create mode 100644 src/FubuMVC.Validation.IntegrationTesting/LoFi/IntegratedCollectionLoFiTester.cs create mode 100644 src/FubuMVC.Validation.IntegrationTesting/LoFi/IntegratedLoFiCollectionEdpoint.cs diff --git a/src/FubuMVC.Validation.IntegrationTesting/FubuMVC.Validation.IntegrationTesting.csproj b/src/FubuMVC.Validation.IntegrationTesting/FubuMVC.Validation.IntegrationTesting.csproj index f0d6db0..1c76c10 100644 --- a/src/FubuMVC.Validation.IntegrationTesting/FubuMVC.Validation.IntegrationTesting.csproj +++ b/src/FubuMVC.Validation.IntegrationTesting/FubuMVC.Validation.IntegrationTesting.csproj @@ -127,6 +127,8 @@ + + diff --git a/src/FubuMVC.Validation.IntegrationTesting/LoFi/IntegratedCollectionLoFiTester.cs b/src/FubuMVC.Validation.IntegrationTesting/LoFi/IntegratedCollectionLoFiTester.cs new file mode 100644 index 0000000..cc94b8d --- /dev/null +++ b/src/FubuMVC.Validation.IntegrationTesting/LoFi/IntegratedCollectionLoFiTester.cs @@ -0,0 +1,62 @@ +using System.Collections.Generic; +using FubuCore; +using FubuMVC.Core.Endpoints; +using FubuTestingSupport; +using NUnit.Framework; + +namespace FubuMVC.Validation.IntegrationTesting.LoFi +{ + [TestFixture] + public class IntegratedCollectionLoFiTester : ValidationHarness + { + private LoFiCollectionInput theCollectionInput; + + [SetUp] + public void SetUp() + { + theCollectionInput = new LoFiCollectionInput + { + Collection = new List + { + new LoFiCollectionElement(), + new LoFiCollectionElement(), + } + }; + } + + protected override void configure(Core.FubuRegistry registry) + { + registry.Actions.IncludeType(); + registry.Import(); + registry.Policies.Add(x => + { + x.Where.InputTypeIs(); + x.Conneg.ApplyConneg(); + }); + } + + private HttpResponse theCollectionResponse + { + get + { + // NOTE: PostAsForm does not serialize the Collection member correctly, it is just serializing it by calling to ToString + // posting the request as Xml does the trick + return endpoints.PostXml(theCollectionInput); + } + } + + [Test] + public void output_from_collection_endpoint_if_validation_succeeds() + { + theCollectionInput.Collection.Each((x, i) => x.Name = "Item{0}".ToFormat(i + 1)); + theCollectionResponse.ReadAsText().ShouldEqual("\"" + IntegratedLoFiCollectionEdpoint.SUCCESS + "\""); + } + + [Test] + public void redirects_to_collection_get_if_validation_fails() + { + theCollectionInput.Collection.Each(x => x.Name = null); + theCollectionResponse.ReadAsText().ShouldEqual("\"" + IntegratedLoFiCollectionEdpoint.GET + "\""); + } + } +} \ No newline at end of file diff --git a/src/FubuMVC.Validation.IntegrationTesting/LoFi/IntegratedLoFiCollectionEdpoint.cs b/src/FubuMVC.Validation.IntegrationTesting/LoFi/IntegratedLoFiCollectionEdpoint.cs new file mode 100644 index 0000000..90eb69a --- /dev/null +++ b/src/FubuMVC.Validation.IntegrationTesting/LoFi/IntegratedLoFiCollectionEdpoint.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; +using FubuValidation; + +namespace FubuMVC.Validation.IntegrationTesting.LoFi +{ + public class IntegratedLoFiCollectionEdpoint + { + public const string GET = "Collection input data"; + public const string SUCCESS = "Success collection"; + + public string get_lofi_collection(LoFiCollectionInput input) + { + return GET; + } + + public string post_lofi_collection(LoFiCollectionInput input) + { + return SUCCESS; + } + } + + public class LoFiCollectionInput + { + public List Collection { get; set; } + } + + public class LoFiCollectionElement + { + [Required] + public string Name { get; set; } + } + +} \ No newline at end of file