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

Commit 1cf6935

Browse files
author
NTaylorMullen
committed
Add ValidationMessageTagHelper.
- Added tests to validate ValidationMessageTagHelper functionality. #1250
1 parent e494f83 commit 1cf6935

File tree

2 files changed

+266
-0
lines changed

2 files changed

+266
-0
lines changed
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using Microsoft.AspNet.Mvc.Rendering;
5+
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
6+
using Microsoft.AspNet.Razor.TagHelpers;
7+
8+
namespace Microsoft.AspNet.Mvc.TagHelpers
9+
{
10+
/// <summary>
11+
/// <see cref="ITagHelper"/> implementation targeting &lt;span&gt; elements with <c>validation-for</c> attributes.
12+
/// </summary>
13+
[TagName("span")]
14+
[ContentBehavior(ContentBehavior.Modify)]
15+
public class ValidationMessageTagHelper : TagHelper
16+
{
17+
[Activate]
18+
private ViewContext ViewContext { get; set; }
19+
20+
[Activate]
21+
private IHtmlGenerator Generator { get; set; }
22+
23+
/// <summary>
24+
/// Name to be validated on the current model.
25+
/// </summary>
26+
[HtmlAttributeName("validation-for")]
27+
public ModelExpression For { get; set; }
28+
29+
/// <inheritdoc />
30+
public override void Process(TagHelperContext context, TagHelperOutput output)
31+
{
32+
if (For != null)
33+
{
34+
var tagBuilder = Generator.GenerateValidationMessage(ViewContext,
35+
For.Name,
36+
message: null,
37+
tag: null,
38+
htmlAttributes: null);
39+
40+
if (tagBuilder != null)
41+
{
42+
output.MergeAttributes(tagBuilder);
43+
44+
// We check for whitespace to detect scenarios such as:
45+
// <span validation-for="Name">
46+
// </span>
47+
if (string.IsNullOrWhiteSpace(output.Content))
48+
{
49+
output.Content = tagBuilder.InnerHtml;
50+
}
51+
}
52+
}
53+
}
54+
}
55+
}
Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System.Collections.Generic;
5+
using System.IO;
6+
using System.Reflection;
7+
using System.Threading.Tasks;
8+
using Microsoft.AspNet.Http;
9+
using Microsoft.AspNet.Mvc.ModelBinding;
10+
using Microsoft.AspNet.Mvc.Razor;
11+
using Microsoft.AspNet.Mvc.Rendering;
12+
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
13+
using Microsoft.AspNet.Routing;
14+
using Moq;
15+
using Xunit;
16+
17+
namespace Microsoft.AspNet.Mvc.TagHelpers
18+
{
19+
public class ValidationMessageTagHelperTest
20+
{
21+
[Fact]
22+
public async Task ProcessAsync_GeneratesExpectedOutput()
23+
{
24+
// Arrange
25+
var metadataProvider = new DataAnnotationsModelMetadataProvider();
26+
var modelExpression = CreateModelExpression("Name");
27+
var validationMessageTagHelper = new ValidationMessageTagHelper
28+
{
29+
For = modelExpression
30+
};
31+
32+
var tagHelperContext = new TagHelperContext(
33+
allAttributes: new Dictionary<string, object>
34+
{
35+
{ "id", "myvalidationmessage" },
36+
{ "for", modelExpression },
37+
});
38+
var output = new TagHelperOutput(
39+
"original tag name",
40+
attributes: new Dictionary<string, string>
41+
{
42+
{ "id", "myvalidationmessage" }
43+
},
44+
content: "Something");
45+
var htmlGenerator = new TestableHtmlGenerator(metadataProvider);
46+
var viewContext = TestableHtmlGenerator.GetViewContext(model: null,
47+
htmlGenerator: htmlGenerator,
48+
metadataProvider: metadataProvider);
49+
50+
var activator = new DefaultTagHelperActivator();
51+
activator.Activate(validationMessageTagHelper, viewContext);
52+
53+
// Act
54+
await validationMessageTagHelper.ProcessAsync(tagHelperContext, output);
55+
56+
// Assert
57+
Assert.Equal(4, output.Attributes.Count);
58+
var attribute = Assert.Single(output.Attributes, kvp => kvp.Key.Equals("id"));
59+
Assert.Equal("myvalidationmessage", attribute.Value);
60+
attribute = Assert.Single(output.Attributes, kvp => kvp.Key.Equals("class"));
61+
Assert.Equal("field-validation-valid", attribute.Value);
62+
attribute = Assert.Single(output.Attributes, kvp => kvp.Key.Equals("data-valmsg-for"));
63+
Assert.Equal("Name", attribute.Value);
64+
attribute = Assert.Single(output.Attributes, kvp => kvp.Key.Equals("data-valmsg-replace"));
65+
Assert.Equal("true", attribute.Value);
66+
Assert.Equal("Something", output.Content);
67+
Assert.Equal("original tag name", output.TagName);
68+
}
69+
70+
[Fact]
71+
public async Task ProcessAsync_CallsIntoGenerateValidationMessageWithExpectedParameters()
72+
{
73+
// Arrange
74+
var validationMessageTagHelper = new ValidationMessageTagHelper
75+
{
76+
For = CreateModelExpression("Hello")
77+
};
78+
var output = new TagHelperOutput(
79+
"span",
80+
attributes: new Dictionary<string, string>(),
81+
content: "Content of validation message");
82+
var expectedViewContext = CreateViewContext();
83+
var generator = new Mock<IHtmlGenerator>();
84+
generator
85+
.Setup(mock =>
86+
mock.GenerateValidationMessage(expectedViewContext, "Hello", null, null, null))
87+
.Returns(new TagBuilder("span"))
88+
.Verifiable();
89+
90+
SetViewContextAndGenerator(validationMessageTagHelper, expectedViewContext, generator.Object);
91+
92+
// Act & Assert
93+
await validationMessageTagHelper.ProcessAsync(context: null, output: output);
94+
95+
generator.Verify();
96+
Assert.Equal("span", output.TagName);
97+
Assert.Empty(output.Attributes);
98+
Assert.Equal("Content of validation message", output.Content);
99+
}
100+
101+
[Theory]
102+
[InlineData("Content of validation message", "Content of validation message")]
103+
[InlineData("\r\n \r\n", "New HTML")]
104+
public async Task ProcessAsync_MergesTagBuilderFromGenerateValidationMessage(
105+
string outputContent, string expectedOutputContent)
106+
{
107+
// Arrange
108+
var validationMessageTagHelper = new ValidationMessageTagHelper
109+
{
110+
For = CreateModelExpression("Hello")
111+
};
112+
var output = new TagHelperOutput(
113+
"span",
114+
attributes: new Dictionary<string, string>(),
115+
content: outputContent);
116+
var tagBuilder = new TagBuilder("span2")
117+
{
118+
InnerHtml = "New HTML"
119+
};
120+
tagBuilder.Attributes.Add("data-foo", "bar");
121+
tagBuilder.Attributes.Add("data-hello", "world");
122+
123+
var expectedViewContext = CreateViewContext();
124+
var generator = new Mock<IHtmlGenerator>(MockBehavior.Strict);
125+
var setup = generator
126+
.Setup(mock => mock.GenerateValidationMessage(
127+
It.IsAny<ViewContext>(),
128+
It.IsAny<string>(),
129+
It.IsAny<string>(),
130+
It.IsAny<string>(),
131+
It.IsAny<object>()))
132+
.Returns(tagBuilder);
133+
134+
SetViewContextAndGenerator(validationMessageTagHelper, expectedViewContext, generator.Object);
135+
136+
// Act
137+
await validationMessageTagHelper.ProcessAsync(context: null, output: output);
138+
139+
// Assert
140+
Assert.Equal(output.TagName, "span");
141+
Assert.Equal(2, output.Attributes.Count);
142+
var attribute = Assert.Single(output.Attributes, kvp => kvp.Key.Equals("data-foo"));
143+
Assert.Equal("bar", attribute.Value);
144+
attribute = Assert.Single(output.Attributes, kvp => kvp.Key.Equals("data-hello"));
145+
Assert.Equal("world", attribute.Value);
146+
Assert.Equal(expectedOutputContent, output.Content);
147+
}
148+
149+
[Fact]
150+
public async Task ProcessAsync_DoesNothingIfNullFor()
151+
{
152+
// Arrange
153+
var validationMessageTagHelper = new ValidationMessageTagHelper();
154+
var output = new TagHelperOutput(
155+
"span",
156+
attributes: new Dictionary<string, string>(),
157+
content: "Content of validation message");
158+
var expectedViewContext = CreateViewContext();
159+
var generator = new Mock<IHtmlGenerator>(MockBehavior.Strict);
160+
161+
SetViewContextAndGenerator(validationMessageTagHelper, expectedViewContext, generator.Object);
162+
163+
// Act
164+
await validationMessageTagHelper.ProcessAsync(context: null, output: output);
165+
166+
// Assert
167+
Assert.Equal("span", output.TagName);
168+
Assert.Empty(output.Attributes);
169+
Assert.Equal("Content of validation message", output.Content);
170+
}
171+
172+
private static ModelExpression CreateModelExpression(string name)
173+
{
174+
return new ModelExpression(
175+
name,
176+
new ModelMetadata(
177+
new Mock<IModelMetadataProvider>().Object,
178+
containerType: null,
179+
modelAccessor: null,
180+
modelType: typeof(object),
181+
propertyName: string.Empty));
182+
}
183+
184+
private static ViewContext CreateViewContext()
185+
{
186+
var actionContext = new ActionContext(
187+
new Mock<HttpContext>().Object,
188+
new RouteData(),
189+
new ActionDescriptor());
190+
191+
return new ViewContext(
192+
actionContext,
193+
Mock.Of<IView>(),
194+
new ViewDataDictionary(
195+
new DataAnnotationsModelMetadataProvider()),
196+
new StringWriter());
197+
}
198+
199+
private static void SetViewContextAndGenerator(ITagHelper tagHelper,
200+
ViewContext viewContext,
201+
IHtmlGenerator generator)
202+
{
203+
var tagHelperType = tagHelper.GetType();
204+
205+
tagHelperType.GetProperty("ViewContext", BindingFlags.NonPublic | BindingFlags.Instance)
206+
.SetValue(tagHelper, viewContext);
207+
tagHelperType.GetProperty("Generator", BindingFlags.NonPublic | BindingFlags.Instance)
208+
.SetValue(tagHelper, generator);
209+
}
210+
}
211+
}

0 commit comments

Comments
 (0)