diff --git a/src/Spring/Spring.Core/Validation/ErrorMessage.cs b/src/Spring/Spring.Core/Validation/ErrorMessage.cs index ee25e528..a44a0d1d 100644 --- a/src/Spring/Spring.Core/Validation/ErrorMessage.cs +++ b/src/Spring/Spring.Core/Validation/ErrorMessage.cs @@ -167,6 +167,11 @@ namespace Spring.Validation /// Resolved error message. public string GetMessage(IMessageSource messageSource) { + if (messageSource == null) + { + return Id; + } + if (Parameters == null) { return messageSource.GetMessage(Id); diff --git a/src/Spring/Spring.Core/Validation/ValidationErrors.cs b/src/Spring/Spring.Core/Validation/ValidationErrors.cs index be4449f3..de96ecd6 100644 --- a/src/Spring/Spring.Core/Validation/ValidationErrors.cs +++ b/src/Spring/Spring.Core/Validation/ValidationErrors.cs @@ -262,6 +262,8 @@ namespace Spring.Validation /// public IList GetResolvedErrors(string provider, IMessageSource messageSource) { + AssertUtils.ArgumentNotNull(provider, "provider"); + IList messages = new ArrayList(); IList errors = (IList) errorMap[provider]; diff --git a/src/Spring/Spring.Web/Web/UI/Controls/AbstractValidationControl.cs b/src/Spring/Spring.Web/Web/UI/Controls/AbstractValidationControl.cs index 7a3b7b2b..7ee5acf0 100644 --- a/src/Spring/Spring.Web/Web/UI/Controls/AbstractValidationControl.cs +++ b/src/Spring/Spring.Web/Web/UI/Controls/AbstractValidationControl.cs @@ -20,9 +20,12 @@ #region Imports +using System; using System.Collections; using System.Web.UI; using Spring.Context; +using Spring.Util; +using Spring.Validation; using Spring.Web.UI.Validation; #endregion @@ -42,17 +45,25 @@ namespace Spring.Web.UI.Controls /// Gets or sets the provider. /// /// The provider. - public string Provider + public virtual string Provider { get { if (this.provider == null) { - return this.ID; + this.provider = this.ID; + if (this.provider == null) + { + this.provider = string.Empty; + } } return this.provider; } - set { this.provider = value; } + set + { + AssertUtils.ArgumentNotNull(value, "Provider"); + this.provider = value; + } } /// @@ -69,10 +80,15 @@ namespace Spring.Web.UI.Controls if (this.renderer == null) { this.renderer = CreateValidationErrorsRenderer(); + AssertUtils.ArgumentNotNull(this.renderer, "Renderer", "CreateValidationErrorsRenderer must not return null"); } return this.renderer; } - set { this.renderer = value; } + set + { + AssertUtils.ArgumentNotNull(value, "Renderer"); + this.renderer = value; + } } /// @@ -83,7 +99,7 @@ namespace Spring.Web.UI.Controls /// protected virtual IMessageSource MessageSource { - get { return ValidationContainer.MessageSource; } + get { return ValidationContainer==null ? null : ValidationContainer.MessageSource; } } /// @@ -108,6 +124,7 @@ namespace Spring.Web.UI.Controls return container; } } +// throw new NotSupportedException(string.Format("Controls of Type {0} must be placed on a container control implementing IValidationContainer", this.GetType().Name)); return null; } } @@ -116,11 +133,31 @@ namespace Spring.Web.UI.Controls /// Resolves the 's list of validation errors to a list /// of elements containing the error messages to be rendered. /// - /// a list containing elements + /// a list containing elements. May return null protected virtual IList ResolveErrorMessages() { IList errorMessages; - errorMessages = this.ValidationContainer.ValidationErrors.GetResolvedErrors(this.Provider, this.MessageSource); + + // good catch - idea & patch from Roberto Paterlini + if (DesignMode) + { + errorMessages = new string[] { GetType().Name + ":" + ID }; + return errorMessages; + } + + IValidationContainer container = this.ValidationContainer; + if (container == null) + { + return null; + } + + IValidationErrors validationErrors = container.ValidationErrors; + if (validationErrors == null) + { + return null; + } + + errorMessages = validationErrors.GetResolvedErrors(this.Provider, this.MessageSource); return errorMessages; } @@ -130,8 +167,11 @@ namespace Spring.Web.UI.Controls /// protected override void Render(HtmlTextWriter writer) { - IList errorMessages = ResolveErrorMessages(); - Renderer.RenderErrors(Page as Spring.Web.UI.Page, writer, errorMessages); + IList errorMessages; + + errorMessages = ResolveErrorMessages(); + + Renderer.RenderErrors(Page as Spring.Web.UI.Page, writer, errorMessages); } } } \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Validation/ValidationErrorsTests.cs b/test/Spring/Spring.Core.Tests/Validation/ValidationErrorsTests.cs index ea62791a..fd10cb36 100644 --- a/test/Spring/Spring.Core.Tests/Validation/ValidationErrorsTests.cs +++ b/test/Spring/Spring.Core.Tests/Validation/ValidationErrorsTests.cs @@ -44,6 +44,14 @@ namespace Spring.Validation private ErrorMessage ErrorMessageTwo = new ErrorMessage("This Is Eva Green", null); private ErrorMessage ErrorMessageOne = new ErrorMessage("Kissing Leads To Brain Disease", null); + [Test] + public void ResolveErrorsWithoutMessageSource() + { + ValidationErrors errors = new ValidationErrors(); + errors.AddError(string.Empty, ErrorMessageOne); + IList resolvedErrors = errors.GetResolvedErrors(string.Empty, null); + Assert.AreEqual(ErrorMessageOne.Id, (string)resolvedErrors[0]); + } [Test] public void ContainsNoErrorsDirectlyAfterInstantiation() { diff --git a/test/Spring/Spring.Web.Tests/Web/UI/Controls/AbstractValidationControlTests.cs b/test/Spring/Spring.Web.Tests/Web/UI/Controls/AbstractValidationControlTests.cs index b22eee81..6d152b69 100644 --- a/test/Spring/Spring.Web.Tests/Web/UI/Controls/AbstractValidationControlTests.cs +++ b/test/Spring/Spring.Web.Tests/Web/UI/Controls/AbstractValidationControlTests.cs @@ -21,10 +21,16 @@ #region Imports using System; +using System.Collections; +using System.Globalization; using System.Web.UI; using NUnit.Framework; using Rhino.Mocks; using Spring.Context; +using Spring.Context.Support; +using Spring.Util; +using Spring.Validation; +using Spring.Web.UI.Validation; #endregion @@ -37,42 +43,89 @@ namespace Spring.Web.UI.Controls [TestFixture] public class AbstractValidationControlTests { - public class TestValidationControl : AbstractValidationControl + [Test] + public void DefaultsToControlIDOrStringEmptyAsProviderName() { - private IValidationContainer _vc; + TestValidationControl vc; - public TestValidationControl() - { - } + vc = new TestValidationControl(null); + Assert.AreEqual(string.Empty, vc.Provider); - public TestValidationControl(IValidationContainer testVc) - { - _vc = testVc; - } + vc = new TestValidationControl(null); + vc.ID = "TestControl"; + Assert.AreEqual(vc.ID, vc.Provider); - protected override Spring.Web.UI.Validation.IValidationErrorsRenderer CreateValidationErrorsRenderer() - { - throw new Exception("The method or operation is not implemented."); - } + vc = new TestValidationControl(null); + vc.ID = "TestControl"; + vc.Provider = "TestProvider"; - protected override IValidationContainer ValidationContainer - { - get - { - if (_vc != null) return _vc; - return base.ValidationContainer; - } - } + Assert.AreEqual("TestProvider", vc.Provider); + } - public IMessageSource TheMessageSource - { - get { return base.MessageSource; } - } + [Test] + public void ResolvesAndRendersValidationErrors() + { + TestValidationControl vc = new TestValidationControl(); + vc.ID = "TestControl"; - public IValidationContainer TheValidationContainer + Page page = new Page(); + page.Controls.Add(vc); + page.ValidationErrors.AddError(vc.Provider, new ErrorMessage("msgId")); + StaticMessageSource msgSrc = new StaticMessageSource(); + msgSrc.AddMessage("msgId", CultureInfo.CurrentUICulture, "Resolved Message Text"); + page.MessageSource = msgSrc; + + vc.TestRender(null); + Assert.AreEqual("Resolved Message Text", vc.LastErrorsRendered[0]); + } + + [Test] + public void ThrowsIfCreateValidationErrorsRendererReturnsNull() + { + TestValidationControl vc = new TestValidationControl(null); + vc.ID = "TestControl"; + + Page page = new Page(); + page.Controls.Add(vc); + page.ValidationErrors.AddError(vc.Provider, new ErrorMessage("msgId")); + + try { - get { return base.ValidationContainer; } + vc.TestRender(null); + Assert.Fail(); } + catch (ArgumentNullException ane) + { + Assert.AreEqual("Renderer", ane.ParamName); + } + } + + [Test] + public void NoExceptionWhenNoValidationContainer() + { + TestValidationControl vc = new TestValidationControl(); + Assert.IsFalse(vc.TheDesignMode); + Assert.IsNull(vc.TheMessageSource); + Assert.IsNull(vc.TheValidationContainer); + + vc.TestRender(null); + } + + [Test] + public void NoExceptionWhenNoErrors() + { + Page page = new Page(); + page.MessageSource = new StaticMessageSource(); + TestValidationControl vc = new TestValidationControl(); + page.Controls.Add(vc); + + // assert assumptions + Assert.IsFalse(vc.TheDesignMode); + Assert.AreSame(page, vc.TheValidationContainer); + Assert.AreSame( page.MessageSource, vc.TheMessageSource ); + Assert.AreEqual(0, page.ValidationErrors.GetResolvedErrors(vc.Provider, vc.TheMessageSource).Count); + + vc.TestRender(null); } [Test] @@ -116,13 +169,82 @@ namespace Spring.Web.UI.Controls { MockRepository mocks = new MockRepository(); IMessageSource messageSource = (IMessageSource)mocks.DynamicMock(typeof(IMessageSource)); - IValidationContainer container = (IValidationContainer)mocks.DynamicMock(typeof(IValidationContainer)); - TestValidationControl ctl = new TestValidationControl(container); + // IValidationContainer container = (IValidationContainer)mocks.DynamicMock(typeof(IValidationContainer)); - Expect.Call(container.MessageSource).Return(messageSource); - mocks.ReplayAll(); + Page page = new Page(); + page.MessageSource = messageSource; + TestValidationControl ctl = new TestValidationControl(); + page.Controls.Add(ctl); Assert.AreEqual(messageSource, ctl.TheMessageSource); } + + #region TestValidationControl + + private class TestValidationControl : AbstractValidationControl + { + public class CapturingRenderer : IValidationErrorsRenderer + { + public IList LastErrorsRendered; + private readonly IValidationErrorsRenderer _inner; + + public CapturingRenderer(IValidationErrorsRenderer inner) + { + _inner = inner; + } + + public void RenderErrors(Page page, HtmlTextWriter writer, IList errors) + { + LastErrorsRendered = errors; + if (_inner != null) + { + _inner.RenderErrors(page, writer, errors); + } + } + } + + private readonly IValidationErrorsRenderer _ver; + + public TestValidationControl() + : this(new CapturingRenderer(null)) + { } + + public TestValidationControl(IValidationErrorsRenderer renderer) + { + _ver =renderer; + } + + public void TestRender(HtmlTextWriter writer) + { + base.Render(writer); + } + + protected override IValidationErrorsRenderer CreateValidationErrorsRenderer() + { + return _ver; + } + + public IList LastErrorsRendered + { + get { return ((CapturingRenderer)_ver).LastErrorsRendered; } + } + + public IMessageSource TheMessageSource + { + get { return base.MessageSource; } + } + + public IValidationContainer TheValidationContainer + { + get { return base.ValidationContainer; } + } + + public bool TheDesignMode + { + get { return base.DesignMode; } + } + } + + #endregion } } \ No newline at end of file