Skip to content
This repository has been archived by the owner on Dec 14, 2018. It is now read-only.

Commit

Permalink
Add ValidationMessageTagHelper.
Browse files Browse the repository at this point in the history
- Added tests to validate ValidationMessageTagHelper functionality.

#1250
  • Loading branch information
NTaylorMullen authored and dougbu committed Oct 17, 2014
1 parent 57d1c54 commit 2729cdc
Show file tree
Hide file tree
Showing 2 changed files with 266 additions and 0 deletions.
55 changes: 55 additions & 0 deletions src/Microsoft.AspNet.Mvc.TagHelpers/ValidationMessageTagHelper.cs
Original file line number Diff line number Diff line change
@@ -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 &lt;span&gt; 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;
}
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
}

0 comments on commit 2729cdc

Please sign in to comment.