Add ValidationMessageTagHelper.
- Added tests to validate ValidationMessageTagHelper functionality. #1250
This commit is contained in:
parent
57d1c542db
commit
2729cdc9b7
|
|
@ -0,0 +1,55 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNet.Mvc.Rendering;
|
||||
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
|
||||
using Microsoft.AspNet.Razor.TagHelpers;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="ITagHelper"/> implementation targeting <span> elements with <c>validation-for</c> attributes.
|
||||
/// </summary>
|
||||
[TagName("span")]
|
||||
[ContentBehavior(ContentBehavior.Modify)]
|
||||
public class ValidationMessageTagHelper : TagHelper
|
||||
{
|
||||
[Activate]
|
||||
private ViewContext ViewContext { get; set; }
|
||||
|
||||
[Activate]
|
||||
private IHtmlGenerator Generator { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Name to be validated on the current model.
|
||||
/// </summary>
|
||||
[HtmlAttributeName("validation-for")]
|
||||
public ModelExpression For { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Process(TagHelperContext context, TagHelperOutput output)
|
||||
{
|
||||
if (For != null)
|
||||
{
|
||||
var tagBuilder = Generator.GenerateValidationMessage(ViewContext,
|
||||
For.Name,
|
||||
message: null,
|
||||
tag: null,
|
||||
htmlAttributes: null);
|
||||
|
||||
if (tagBuilder != null)
|
||||
{
|
||||
output.MergeAttributes(tagBuilder);
|
||||
|
||||
// We check for whitespace to detect scenarios such as:
|
||||
// <span validation-for="Name">
|
||||
// </span>
|
||||
if (string.IsNullOrWhiteSpace(output.Content))
|
||||
{
|
||||
output.Content = tagBuilder.InnerHtml;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,211 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||
using Microsoft.AspNet.Mvc.Razor;
|
||||
using Microsoft.AspNet.Mvc.Rendering;
|
||||
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
|
||||
using Microsoft.AspNet.Routing;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||
{
|
||||
public class ValidationMessageTagHelperTest
|
||||
{
|
||||
[Fact]
|
||||
public async Task ProcessAsync_GeneratesExpectedOutput()
|
||||
{
|
||||
// Arrange
|
||||
var metadataProvider = new DataAnnotationsModelMetadataProvider();
|
||||
var modelExpression = CreateModelExpression("Name");
|
||||
var validationMessageTagHelper = new ValidationMessageTagHelper
|
||||
{
|
||||
For = modelExpression
|
||||
};
|
||||
|
||||
var tagHelperContext = new TagHelperContext(
|
||||
allAttributes: new Dictionary<string, object>
|
||||
{
|
||||
{ "id", "myvalidationmessage" },
|
||||
{ "for", modelExpression },
|
||||
});
|
||||
var output = new TagHelperOutput(
|
||||
"original tag name",
|
||||
attributes: new Dictionary<string, string>
|
||||
{
|
||||
{ "id", "myvalidationmessage" }
|
||||
},
|
||||
content: "Something");
|
||||
var htmlGenerator = new TestableHtmlGenerator(metadataProvider);
|
||||
var viewContext = TestableHtmlGenerator.GetViewContext(model: null,
|
||||
htmlGenerator: htmlGenerator,
|
||||
metadataProvider: metadataProvider);
|
||||
|
||||
var activator = new DefaultTagHelperActivator();
|
||||
activator.Activate(validationMessageTagHelper, viewContext);
|
||||
|
||||
// Act
|
||||
await validationMessageTagHelper.ProcessAsync(tagHelperContext, output);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(4, output.Attributes.Count);
|
||||
var attribute = Assert.Single(output.Attributes, kvp => kvp.Key.Equals("id"));
|
||||
Assert.Equal("myvalidationmessage", attribute.Value);
|
||||
attribute = Assert.Single(output.Attributes, kvp => kvp.Key.Equals("class"));
|
||||
Assert.Equal("field-validation-valid", attribute.Value);
|
||||
attribute = Assert.Single(output.Attributes, kvp => kvp.Key.Equals("data-valmsg-for"));
|
||||
Assert.Equal("Name", attribute.Value);
|
||||
attribute = Assert.Single(output.Attributes, kvp => kvp.Key.Equals("data-valmsg-replace"));
|
||||
Assert.Equal("true", attribute.Value);
|
||||
Assert.Equal("Something", output.Content);
|
||||
Assert.Equal("original tag name", output.TagName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ProcessAsync_CallsIntoGenerateValidationMessageWithExpectedParameters()
|
||||
{
|
||||
// Arrange
|
||||
var validationMessageTagHelper = new ValidationMessageTagHelper
|
||||
{
|
||||
For = CreateModelExpression("Hello")
|
||||
};
|
||||
var output = new TagHelperOutput(
|
||||
"span",
|
||||
attributes: new Dictionary<string, string>(),
|
||||
content: "Content of validation message");
|
||||
var expectedViewContext = CreateViewContext();
|
||||
var generator = new Mock<IHtmlGenerator>();
|
||||
generator
|
||||
.Setup(mock =>
|
||||
mock.GenerateValidationMessage(expectedViewContext, "Hello", null, null, null))
|
||||
.Returns(new TagBuilder("span"))
|
||||
.Verifiable();
|
||||
|
||||
SetViewContextAndGenerator(validationMessageTagHelper, expectedViewContext, generator.Object);
|
||||
|
||||
// Act & Assert
|
||||
await validationMessageTagHelper.ProcessAsync(context: null, output: output);
|
||||
|
||||
generator.Verify();
|
||||
Assert.Equal("span", output.TagName);
|
||||
Assert.Empty(output.Attributes);
|
||||
Assert.Equal("Content of validation message", output.Content);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("Content of validation message", "Content of validation message")]
|
||||
[InlineData("\r\n \r\n", "New HTML")]
|
||||
public async Task ProcessAsync_MergesTagBuilderFromGenerateValidationMessage(
|
||||
string outputContent, string expectedOutputContent)
|
||||
{
|
||||
// Arrange
|
||||
var validationMessageTagHelper = new ValidationMessageTagHelper
|
||||
{
|
||||
For = CreateModelExpression("Hello")
|
||||
};
|
||||
var output = new TagHelperOutput(
|
||||
"span",
|
||||
attributes: new Dictionary<string, string>(),
|
||||
content: outputContent);
|
||||
var tagBuilder = new TagBuilder("span2")
|
||||
{
|
||||
InnerHtml = "New HTML"
|
||||
};
|
||||
tagBuilder.Attributes.Add("data-foo", "bar");
|
||||
tagBuilder.Attributes.Add("data-hello", "world");
|
||||
|
||||
var expectedViewContext = CreateViewContext();
|
||||
var generator = new Mock<IHtmlGenerator>(MockBehavior.Strict);
|
||||
var setup = generator
|
||||
.Setup(mock => mock.GenerateValidationMessage(
|
||||
It.IsAny<ViewContext>(),
|
||||
It.IsAny<string>(),
|
||||
It.IsAny<string>(),
|
||||
It.IsAny<string>(),
|
||||
It.IsAny<object>()))
|
||||
.Returns(tagBuilder);
|
||||
|
||||
SetViewContextAndGenerator(validationMessageTagHelper, expectedViewContext, generator.Object);
|
||||
|
||||
// Act
|
||||
await validationMessageTagHelper.ProcessAsync(context: null, output: output);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(output.TagName, "span");
|
||||
Assert.Equal(2, output.Attributes.Count);
|
||||
var attribute = Assert.Single(output.Attributes, kvp => kvp.Key.Equals("data-foo"));
|
||||
Assert.Equal("bar", attribute.Value);
|
||||
attribute = Assert.Single(output.Attributes, kvp => kvp.Key.Equals("data-hello"));
|
||||
Assert.Equal("world", attribute.Value);
|
||||
Assert.Equal(expectedOutputContent, output.Content);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ProcessAsync_DoesNothingIfNullFor()
|
||||
{
|
||||
// Arrange
|
||||
var validationMessageTagHelper = new ValidationMessageTagHelper();
|
||||
var output = new TagHelperOutput(
|
||||
"span",
|
||||
attributes: new Dictionary<string, string>(),
|
||||
content: "Content of validation message");
|
||||
var expectedViewContext = CreateViewContext();
|
||||
var generator = new Mock<IHtmlGenerator>(MockBehavior.Strict);
|
||||
|
||||
SetViewContextAndGenerator(validationMessageTagHelper, expectedViewContext, generator.Object);
|
||||
|
||||
// Act
|
||||
await validationMessageTagHelper.ProcessAsync(context: null, output: output);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("span", output.TagName);
|
||||
Assert.Empty(output.Attributes);
|
||||
Assert.Equal("Content of validation message", output.Content);
|
||||
}
|
||||
|
||||
private static ModelExpression CreateModelExpression(string name)
|
||||
{
|
||||
return new ModelExpression(
|
||||
name,
|
||||
new ModelMetadata(
|
||||
new Mock<IModelMetadataProvider>().Object,
|
||||
containerType: null,
|
||||
modelAccessor: null,
|
||||
modelType: typeof(object),
|
||||
propertyName: string.Empty));
|
||||
}
|
||||
|
||||
private static ViewContext CreateViewContext()
|
||||
{
|
||||
var actionContext = new ActionContext(
|
||||
new Mock<HttpContext>().Object,
|
||||
new RouteData(),
|
||||
new ActionDescriptor());
|
||||
|
||||
return new ViewContext(
|
||||
actionContext,
|
||||
Mock.Of<IView>(),
|
||||
new ViewDataDictionary(
|
||||
new DataAnnotationsModelMetadataProvider()),
|
||||
new StringWriter());
|
||||
}
|
||||
|
||||
private static void SetViewContextAndGenerator(ITagHelper tagHelper,
|
||||
ViewContext viewContext,
|
||||
IHtmlGenerator generator)
|
||||
{
|
||||
var tagHelperType = tagHelper.GetType();
|
||||
|
||||
tagHelperType.GetProperty("ViewContext", BindingFlags.NonPublic | BindingFlags.Instance)
|
||||
.SetValue(tagHelper, viewContext);
|
||||
tagHelperType.GetProperty("Generator", BindingFlags.NonPublic | BindingFlags.Instance)
|
||||
.SetValue(tagHelper, generator);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue