Add tests of `TextAreaTagHelper`
- new Microsoft.AspNet.Mvc.TagHelpers.Test project - bit of test infrastructure -- `TestableHtmlGenerator` - short-curcuits validation attribute and AntiForgery additions - should help when testing all MVC tag helpers
This commit is contained in:
parent
a9c2c4c5eb
commit
ba8b3d14a4
15
Mvc.sln
15
Mvc.sln
|
|
@ -1,7 +1,7 @@
|
||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
# Visual Studio 14
|
# Visual Studio 14
|
||||||
VisualStudioVersion = 14.0.22115.0
|
VisualStudioVersion = 14.0.22209.0
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{DAAE4C74-D06F-4874-A166-33305D2643CE}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{DAAE4C74-D06F-4874-A166-33305D2643CE}"
|
||||||
EndProject
|
EndProject
|
||||||
|
|
@ -102,6 +102,8 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "TagHelperSample.Web", "samp
|
||||||
EndProject
|
EndProject
|
||||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Mvc.TagHelpers", "src\Microsoft.AspNet.Mvc.TagHelpers\Microsoft.AspNet.Mvc.TagHelpers.kproj", "{B2347320-308E-4D2B-AEC8-005DFA68B0C9}"
|
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Mvc.TagHelpers", "src\Microsoft.AspNet.Mvc.TagHelpers\Microsoft.AspNet.Mvc.TagHelpers.kproj", "{B2347320-308E-4D2B-AEC8-005DFA68B0C9}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Mvc.TagHelpers.Test", "test\Microsoft.AspNet.Mvc.TagHelpers.Test\Microsoft.AspNet.Mvc.TagHelpers.Test.kproj", "{860119ED-3DB1-424D-8D0A-30132A8A7D96}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
|
@ -542,6 +544,16 @@ Global
|
||||||
{B2347320-308E-4D2B-AEC8-005DFA68B0C9}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
{B2347320-308E-4D2B-AEC8-005DFA68B0C9}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||||
{B2347320-308E-4D2B-AEC8-005DFA68B0C9}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
{B2347320-308E-4D2B-AEC8-005DFA68B0C9}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||||
{B2347320-308E-4D2B-AEC8-005DFA68B0C9}.Release|x86.ActiveCfg = Release|Any CPU
|
{B2347320-308E-4D2B-AEC8-005DFA68B0C9}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{860119ED-3DB1-424D-8D0A-30132A8A7D96}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{860119ED-3DB1-424D-8D0A-30132A8A7D96}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{860119ED-3DB1-424D-8D0A-30132A8A7D96}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||||
|
{860119ED-3DB1-424D-8D0A-30132A8A7D96}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||||
|
{860119ED-3DB1-424D-8D0A-30132A8A7D96}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{860119ED-3DB1-424D-8D0A-30132A8A7D96}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{860119ED-3DB1-424D-8D0A-30132A8A7D96}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{860119ED-3DB1-424D-8D0A-30132A8A7D96}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||||
|
{860119ED-3DB1-424D-8D0A-30132A8A7D96}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||||
|
{860119ED-3DB1-424D-8D0A-30132A8A7D96}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|
@ -591,5 +603,6 @@ Global
|
||||||
{5DE8E4D9-AACD-4B5F-819F-F091383FB996} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1}
|
{5DE8E4D9-AACD-4B5F-819F-F091383FB996} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1}
|
||||||
{2223120F-D675-40DA-8CD8-11DC14A0B2C7} = {DAAE4C74-D06F-4874-A166-33305D2643CE}
|
{2223120F-D675-40DA-8CD8-11DC14A0B2C7} = {DAAE4C74-D06F-4874-A166-33305D2643CE}
|
||||||
{B2347320-308E-4D2B-AEC8-005DFA68B0C9} = {32285FA4-6B46-4D6B-A840-2B13E4C8B58E}
|
{B2347320-308E-4D2B-AEC8-005DFA68B0C9} = {32285FA4-6B46-4D6B-A840-2B13E4C8B58E}
|
||||||
|
{860119ED-3DB1-424D-8D0A-30132A8A7D96} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup>
|
||||||
|
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
|
||||||
|
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.Props" Condition="'$(VSToolsPath)' != ''" />
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<ProjectGuid>860119ed-3db1-424d-8d0a-30132a8a7d96</ProjectGuid>
|
||||||
|
<RootNamespace>Microsoft.AspNet.Mvc.TagHelpers.Test</RootNamespace>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<SchemaVersion>2.0</SchemaVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||||
|
</Project>
|
||||||
|
|
@ -0,0 +1,110 @@
|
||||||
|
// 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;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using Microsoft.AspNet.Http;
|
||||||
|
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||||
|
using Microsoft.AspNet.Mvc.Rendering;
|
||||||
|
using Microsoft.AspNet.Routing;
|
||||||
|
using Microsoft.AspNet.Security.DataProtection;
|
||||||
|
using Microsoft.Framework.OptionsModel;
|
||||||
|
using Moq;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
|
{
|
||||||
|
public class TestableHtmlGenerator : DefaultHtmlGenerator
|
||||||
|
{
|
||||||
|
private IDictionary<string, object> _validationAttributes;
|
||||||
|
|
||||||
|
public TestableHtmlGenerator(IModelMetadataProvider metadataProvider)
|
||||||
|
: this(metadataProvider, Mock.Of<IUrlHelper>())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public TestableHtmlGenerator(IModelMetadataProvider metadataProvider, IUrlHelper urlHelper)
|
||||||
|
: this(
|
||||||
|
metadataProvider,
|
||||||
|
urlHelper,
|
||||||
|
validationAttributes: new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public TestableHtmlGenerator(
|
||||||
|
IModelMetadataProvider metadataProvider,
|
||||||
|
IUrlHelper urlHelper,
|
||||||
|
IDictionary<string, object> validationAttributes)
|
||||||
|
: base(Mock.Of<IActionBindingContextProvider>(), GetAntiForgery(), metadataProvider, urlHelper)
|
||||||
|
{
|
||||||
|
_validationAttributes = validationAttributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IDictionary<string, object> ValidationAttributes
|
||||||
|
{
|
||||||
|
get { return _validationAttributes; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ViewContext GetViewContext(
|
||||||
|
object model,
|
||||||
|
IHtmlGenerator htmlGenerator,
|
||||||
|
IModelMetadataProvider metadataProvider)
|
||||||
|
{
|
||||||
|
var serviceProvider = new Mock<IServiceProvider>();
|
||||||
|
serviceProvider
|
||||||
|
.Setup(provider => provider.GetService(typeof(IHtmlGenerator)))
|
||||||
|
.Returns(htmlGenerator);
|
||||||
|
|
||||||
|
var httpContext = new Mock<HttpContext>();
|
||||||
|
httpContext
|
||||||
|
.Setup(context => context.RequestServices)
|
||||||
|
.Returns(serviceProvider.Object);
|
||||||
|
|
||||||
|
var actionContext = new ActionContext(httpContext.Object, new RouteData(), new ActionDescriptor());
|
||||||
|
var viewData = new ViewDataDictionary(metadataProvider)
|
||||||
|
{
|
||||||
|
Model = model,
|
||||||
|
};
|
||||||
|
var viewContext = new ViewContext(actionContext, Mock.Of<IView>(), viewData, new StringWriter());
|
||||||
|
|
||||||
|
return viewContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override TagBuilder GenerateAntiForgery(ViewContext viewContext)
|
||||||
|
{
|
||||||
|
return new TagBuilder("input")
|
||||||
|
{
|
||||||
|
Attributes =
|
||||||
|
{
|
||||||
|
{ "name", "__RequestVerificationToken" },
|
||||||
|
{ "type", "hidden" },
|
||||||
|
{ "value", "olJlUDjrouRNWLen4tQJhauj1Z1rrvnb3QD65cmQU1Ykqi6S4" }, // 50 chars of a token.
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override IDictionary<string, object> GetValidationAttributes(
|
||||||
|
ViewContext viewContext,
|
||||||
|
ModelMetadata metadata,
|
||||||
|
string name)
|
||||||
|
{
|
||||||
|
return ValidationAttributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static AntiForgery GetAntiForgery()
|
||||||
|
{
|
||||||
|
// AntiForgery must be passed to TestableHtmlGenerator constructor but will never be called.
|
||||||
|
var optionsAccessor = new Mock<IOptionsAccessor<MvcOptions>>();
|
||||||
|
optionsAccessor
|
||||||
|
.SetupGet(o => o.Options)
|
||||||
|
.Returns(new MvcOptions());
|
||||||
|
var antiForgery = new AntiForgery(
|
||||||
|
Mock.Of<IClaimUidExtractor>(),
|
||||||
|
Mock.Of<IDataProtectionProvider>(),
|
||||||
|
Mock.Of<IAntiForgeryAdditionalDataProvider>(),
|
||||||
|
optionsAccessor.Object);
|
||||||
|
|
||||||
|
return antiForgery;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,194 @@
|
||||||
|
// 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;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||||
|
using Microsoft.AspNet.Mvc.Razor;
|
||||||
|
using Microsoft.AspNet.Mvc.Rendering;
|
||||||
|
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
|
{
|
||||||
|
public class TextAreaTagHelperTest
|
||||||
|
{
|
||||||
|
// Model (List<Model> or Model instance), container type (Model or NestModel), model accessor,
|
||||||
|
// property path, expected content.
|
||||||
|
public static TheoryData<object, Type, Func<object>, string, string> TestDataSet
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var modelWithNull = new Model
|
||||||
|
{
|
||||||
|
NestedModel = new NestedModel
|
||||||
|
{
|
||||||
|
Text = null,
|
||||||
|
},
|
||||||
|
Text = null,
|
||||||
|
};
|
||||||
|
var modelWithText = new Model
|
||||||
|
{
|
||||||
|
NestedModel = new NestedModel
|
||||||
|
{
|
||||||
|
Text = "inner text",
|
||||||
|
},
|
||||||
|
Text = "outer text",
|
||||||
|
};
|
||||||
|
var models = new List<Model>
|
||||||
|
{
|
||||||
|
modelWithNull,
|
||||||
|
modelWithText,
|
||||||
|
};
|
||||||
|
|
||||||
|
return new TheoryData<object, Type, Func<object>, string, string>
|
||||||
|
{
|
||||||
|
{ null, typeof(Model), () => null, "Text",
|
||||||
|
Environment.NewLine },
|
||||||
|
|
||||||
|
{ modelWithNull, typeof(Model), () => modelWithNull.Text, "Text",
|
||||||
|
Environment.NewLine },
|
||||||
|
{ modelWithText, typeof(Model), () => modelWithText.Text, "Text",
|
||||||
|
Environment.NewLine + "outer text" },
|
||||||
|
|
||||||
|
{ modelWithNull, typeof(NestedModel), () => modelWithNull.NestedModel.Text, "NestedModel.Text",
|
||||||
|
Environment.NewLine },
|
||||||
|
{ modelWithText, typeof(NestedModel), () => modelWithText.NestedModel.Text, "NestedModel.Text",
|
||||||
|
Environment.NewLine + "inner text" },
|
||||||
|
|
||||||
|
// Top-level indexing does not work end-to-end due to code generation issue #1345.
|
||||||
|
// TODO: Remove above comment when #1345 is fixed.
|
||||||
|
{ models, typeof(Model), () => models[0].Text, "[0].Text",
|
||||||
|
Environment.NewLine },
|
||||||
|
{ models, typeof(Model), () => models[1].Text, "[1].Text",
|
||||||
|
Environment.NewLine + "outer text" },
|
||||||
|
|
||||||
|
{ models, typeof(NestedModel), () => models[0].NestedModel.Text, "[0].NestedModel.Text",
|
||||||
|
Environment.NewLine },
|
||||||
|
{ models, typeof(NestedModel), () => models[1].NestedModel.Text, "[1].NestedModel.Text",
|
||||||
|
Environment.NewLine + "inner text" },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(TestDataSet))]
|
||||||
|
public async Task Process_GeneratesExpectedOutput(
|
||||||
|
object model,
|
||||||
|
Type containerType,
|
||||||
|
Func<object> modelAccessor,
|
||||||
|
string propertyPath,
|
||||||
|
string expectedContent)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var expectedAttributes = new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
{ "class", "form-control" },
|
||||||
|
{ "id", propertyPath },
|
||||||
|
{ "name", propertyPath },
|
||||||
|
{ "valid", "from validation attributes" },
|
||||||
|
};
|
||||||
|
var expectedTagName = "textarea";
|
||||||
|
|
||||||
|
var metadataProvider = new DataAnnotationsModelMetadataProvider();
|
||||||
|
|
||||||
|
// Property name is either nameof(Model.Text) or nameof(NestedModel.Text).
|
||||||
|
var metadata = metadataProvider.GetMetadataForProperty(modelAccessor, containerType, propertyName: "Text");
|
||||||
|
var modelExpression = new ModelExpression(propertyPath, metadata);
|
||||||
|
var tagHelper = new TextAreaTagHelper
|
||||||
|
{
|
||||||
|
For = modelExpression,
|
||||||
|
};
|
||||||
|
|
||||||
|
var tagHelperContext = new TagHelperContext(new Dictionary<string, object>());
|
||||||
|
var htmlAttributes = new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
{ "class", "form-control" },
|
||||||
|
};
|
||||||
|
var output = new TagHelperOutput("original tag name", htmlAttributes, "original content")
|
||||||
|
{
|
||||||
|
SelfClosing = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
var htmlGenerator = new TestableHtmlGenerator(metadataProvider)
|
||||||
|
{
|
||||||
|
ValidationAttributes =
|
||||||
|
{
|
||||||
|
{ "valid", "from validation attributes" },
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var viewContext = TestableHtmlGenerator.GetViewContext(model, htmlGenerator, metadataProvider);
|
||||||
|
var activator = new DefaultTagHelperActivator();
|
||||||
|
activator.Activate(tagHelper, viewContext);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await tagHelper.ProcessAsync(tagHelperContext, output);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(expectedAttributes, output.Attributes);
|
||||||
|
Assert.Equal(expectedContent, output.Content);
|
||||||
|
Assert.False(output.SelfClosing);
|
||||||
|
Assert.Equal(expectedTagName, output.TagName);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task TagHelper_LeavesOutputUnchanged_IfForNotBound()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var expectedAttributes = new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
{ "class", "form-control" },
|
||||||
|
};
|
||||||
|
var expectedContent = "original content";
|
||||||
|
var expectedTagName = "original tag name";
|
||||||
|
|
||||||
|
var metadataProvider = new DataAnnotationsModelMetadataProvider();
|
||||||
|
var metadata = metadataProvider.GetMetadataForProperty(
|
||||||
|
modelAccessor: () => null,
|
||||||
|
containerType: typeof(Model),
|
||||||
|
propertyName: nameof(Model.Text));
|
||||||
|
var modelExpression = new ModelExpression(nameof(Model.Text), metadata);
|
||||||
|
var tagHelper = new TextAreaTagHelper();
|
||||||
|
|
||||||
|
var tagHelperContext = new TagHelperContext(new Dictionary<string, object>());
|
||||||
|
var output = new TagHelperOutput(expectedTagName, expectedAttributes, expectedContent)
|
||||||
|
{
|
||||||
|
SelfClosing = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
var htmlGenerator = new TestableHtmlGenerator(metadataProvider)
|
||||||
|
{
|
||||||
|
ValidationAttributes =
|
||||||
|
{
|
||||||
|
{ "valid", "from validation attributes" },
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Model model = null;
|
||||||
|
var viewContext = TestableHtmlGenerator.GetViewContext(model, htmlGenerator, metadataProvider);
|
||||||
|
var activator = new DefaultTagHelperActivator();
|
||||||
|
activator.Activate(tagHelper, viewContext);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await tagHelper.ProcessAsync(tagHelperContext, output);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(expectedAttributes, output.Attributes);
|
||||||
|
Assert.Equal(expectedContent, output.Content);
|
||||||
|
Assert.True(output.SelfClosing);
|
||||||
|
Assert.Equal(expectedTagName, output.TagName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Model
|
||||||
|
{
|
||||||
|
public string Text { get; set; }
|
||||||
|
|
||||||
|
public NestedModel NestedModel { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private class NestedModel
|
||||||
|
{
|
||||||
|
public string Text { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"commands": {
|
||||||
|
"test": "Xunit.KRunner"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.AspNet.Mvc.TagHelpers": "6.0.0-*",
|
||||||
|
"Microsoft.AspNet.Testing": "1.0.0-*",
|
||||||
|
"Xunit.KRunner": "1.0.0-*"
|
||||||
|
},
|
||||||
|
"frameworks": {
|
||||||
|
"aspnet50": {
|
||||||
|
"dependencies": {
|
||||||
|
"Moq": "4.2.1312.1622"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue