parent
df81f8be57
commit
be73cd77bf
|
|
@ -0,0 +1,185 @@
|
|||
// Copyright (c) .NET Foundation. 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 Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Microsoft.AspNetCore.Mvc.Routing;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||
using Microsoft.AspNetCore.Razor.TagHelpers;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="ITagHelper"/> implementation targeting <button> elements.
|
||||
/// </summary>
|
||||
[HtmlTargetElement("button", Attributes = ActionAttributeName)]
|
||||
[HtmlTargetElement("button", Attributes = ControllerAttributeName)]
|
||||
[HtmlTargetElement("button", Attributes = AreaAttributeName)]
|
||||
[HtmlTargetElement("button", Attributes = RouteAttributeName)]
|
||||
[HtmlTargetElement("button", Attributes = RouteValuesDictionaryName)]
|
||||
[HtmlTargetElement("button", Attributes = RouteValuesPrefix + "*")]
|
||||
public class ButtonTagHelper : TagHelper
|
||||
{
|
||||
private const string ActionAttributeName = "asp-action";
|
||||
private const string ControllerAttributeName = "asp-controller";
|
||||
private const string AreaAttributeName = "asp-area";
|
||||
private const string RouteAttributeName = "asp-route";
|
||||
private const string RouteValuesDictionaryName = "asp-all-route-data";
|
||||
private const string RouteValuesPrefix = "asp-route-";
|
||||
private const string FormAction = "formaction";
|
||||
private IDictionary<string, string> _routeValues;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="ButtonTagHelper"/>.
|
||||
/// </summary>
|
||||
/// <param name="urlHelperFactory">The <see cref="IUrlHelperFactory"/>.</param>
|
||||
public ButtonTagHelper(IUrlHelperFactory urlHelperFactory)
|
||||
{
|
||||
UrlHelperFactory = urlHelperFactory;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int Order => -1000;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="Rendering.ViewContext"/> for the current request.
|
||||
/// </summary>
|
||||
[HtmlAttributeNotBound]
|
||||
[ViewContext]
|
||||
public ViewContext ViewContext { get; set; }
|
||||
|
||||
protected IUrlHelperFactory UrlHelperFactory { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of the action method.
|
||||
/// </summary>
|
||||
[HtmlAttributeName(ActionAttributeName)]
|
||||
public string Action { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of the controller.
|
||||
/// </summary>
|
||||
[HtmlAttributeName(ControllerAttributeName)]
|
||||
public string Controller { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of the area.
|
||||
/// </summary>
|
||||
[HtmlAttributeName(AreaAttributeName)]
|
||||
public string Area { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Name of the route.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Must be <c>null</c> if <see cref="Action"/> or <see cref="Controller"/> is non-<c>null</c>.
|
||||
/// </remarks>
|
||||
[HtmlAttributeName(RouteAttributeName)]
|
||||
public string Route { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Additional parameters for the route.
|
||||
/// </summary>
|
||||
[HtmlAttributeName(RouteValuesDictionaryName, DictionaryAttributePrefix = RouteValuesPrefix)]
|
||||
public IDictionary<string, string> RouteValues
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_routeValues == null)
|
||||
{
|
||||
_routeValues = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
return _routeValues;
|
||||
}
|
||||
set
|
||||
{
|
||||
_routeValues = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <remarks>Does nothing if user provides an <c>formaction</c> attribute.</remarks>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// Thrown if <c>formaction</c> attribute is provided and <see cref="Action"/>, <see cref="Controller"/>,
|
||||
/// or <see cref="Route"/> are non-<c>null</c> or if the user provided <c>asp-route-*</c> attributes.
|
||||
/// Also thrown if <see cref="Route"/> and one or both of <see cref="Action"/> and <see cref="Controller"/>
|
||||
/// are non-<c>null</c>
|
||||
/// </exception>
|
||||
public override void Process(TagHelperContext context, TagHelperOutput output)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
if (output == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(output));
|
||||
}
|
||||
|
||||
// If "formaction" is already set, it means the user is attempting to use a normal button.
|
||||
if (output.Attributes.ContainsName(FormAction))
|
||||
{
|
||||
if (Action != null || Controller != null || Area != null || Route != null || RouteValues.Count != 0)
|
||||
{
|
||||
// User specified a formaction and one of the bound attributes; can't determine the formaction attribute.
|
||||
throw new InvalidOperationException(
|
||||
Resources.FormatButtonTagHelper_CannotOverrideFormAction(
|
||||
"<button>",
|
||||
ActionAttributeName,
|
||||
ControllerAttributeName,
|
||||
AreaAttributeName,
|
||||
RouteAttributeName,
|
||||
RouteValuesPrefix,
|
||||
FormAction));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RouteValueDictionary routeValues = null;
|
||||
if (_routeValues != null && _routeValues.Count > 0)
|
||||
{
|
||||
routeValues = new RouteValueDictionary(_routeValues);
|
||||
}
|
||||
|
||||
if (Area != null)
|
||||
{
|
||||
if (routeValues == null)
|
||||
{
|
||||
routeValues = new RouteValueDictionary();
|
||||
}
|
||||
|
||||
// Unconditionally replace any value from asp-route-area.
|
||||
routeValues["area"] = Area;
|
||||
}
|
||||
|
||||
if (Route == null)
|
||||
{
|
||||
var urlHelper = UrlHelperFactory.GetUrlHelper(ViewContext);
|
||||
var url = urlHelper.Action(Action, Controller, routeValues);
|
||||
output.Attributes.SetAttribute(FormAction, url);
|
||||
}
|
||||
else if (Action != null || Controller != null)
|
||||
{
|
||||
// Route and Action or Controller were specified. Can't determine the formaction attribute.
|
||||
throw new InvalidOperationException(
|
||||
Resources.FormatButtonTagHelper_CannotDetermineFormActionRouteActionOrControllerSpecified(
|
||||
"<button>",
|
||||
RouteAttributeName,
|
||||
ActionAttributeName,
|
||||
ControllerAttributeName,
|
||||
FormAction));
|
||||
}
|
||||
else
|
||||
{
|
||||
var urlHelper = UrlHelperFactory.GetUrlHelper(ViewContext);
|
||||
var url = urlHelper.RouteUrl(Route, routeValues);
|
||||
output.Attributes.SetAttribute(FormAction, url);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -42,6 +42,70 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
return string.Format(CultureInfo.CurrentCulture, GetString("AnchorTagHelper_CannotOverrideHref"), p0, p1, p2, p3, p4, p5, p6, p7, p8, p9);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cannot determine an '{4}' attribute for {0}. A {0} with a specified '{1}' must not have an '{2}' or '{3}' attribute.
|
||||
/// </summary>
|
||||
internal static string ButtonTagHelper_CannotDetermineFormActionRouteActionOrControllerSpecified
|
||||
{
|
||||
get { return GetString("ButtonTagHelper_CannotDetermineFormActionRouteActionOrControllerSpecified"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cannot determine an '{4}' attribute for {0}. A {0} with a specified '{1}' must not have an '{2}' or '{3}' attribute.
|
||||
/// </summary>
|
||||
internal static string FormatButtonTagHelper_CannotDetermineFormActionRouteActionOrControllerSpecified(object p0, object p1, object p2, object p3, object p4)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("ButtonTagHelper_CannotDetermineFormActionRouteActionOrControllerSpecified"), p0, p1, p2, p3, p4);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cannot override the '{6}' attribute for {0}. A {0} with a specified '{6}' must not have attributes starting with '{5}' or an '{1}', '{2}', '{3}', or '{4}' attribute.
|
||||
/// </summary>
|
||||
internal static string ButtonTagHelper_CannotOverrideFormAction
|
||||
{
|
||||
get { return GetString("ButtonTagHelper_CannotOverrideFormAction"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cannot override the '{6}' attribute for {0}. A {0} with a specified '{6}' must not have attributes starting with '{5}' or an '{1}', '{2}', '{3}', or '{4}' attribute.
|
||||
/// </summary>
|
||||
internal static string FormatButtonTagHelper_CannotOverrideFormAction(object p0, object p1, object p2, object p3, object p4, object p5, object p6)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("ButtonTagHelper_CannotOverrideFormAction"), p0, p1, p2, p3, p4, p5, p6);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cannot determine an '{4}' attribute for {0}. An {0} with a specified '{1}' must not have an '{2}' or '{3}' attribute.
|
||||
/// </summary>
|
||||
internal static string SubmitTagHelper_CannotDetermineFormActionRouteActionOrControllerSpecified
|
||||
{
|
||||
get { return GetString("SubmitTagHelper_CannotDetermineFormActionRouteActionOrControllerSpecified"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cannot determine an '{4}' attribute for {0}. An {0} with a specified '{1}' must not have an '{2}' or '{3}' attribute.
|
||||
/// </summary>
|
||||
internal static string FormatSubmitTagHelper_CannotDetermineFormActionRouteActionOrControllerSpecified(object p0, object p1, object p2, object p3, object p4)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("SubmitTagHelper_CannotDetermineFormActionRouteActionOrControllerSpecified"), p0, p1, p2, p3, p4);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cannot override the '{6}' attribute for {0}. An {0} with a specified '{6}' must not have attributes starting with '{5}' or an '{1}', '{2}', '{3}', or '{4}' attribute.
|
||||
/// </summary>
|
||||
internal static string SubmitTagHelper_CannotOverrideFormAction
|
||||
{
|
||||
get { return GetString("SubmitTagHelper_CannotOverrideFormAction"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cannot override the '{6}' attribute for {0}. An {0} with a specified '{6}' must not have attributes starting with '{5}' or an '{1}', '{2}', '{3}', or '{4}' attribute.
|
||||
/// </summary>
|
||||
internal static string FormatSubmitTagHelper_CannotOverrideFormAction(object p0, object p1, object p2, object p3, object p4, object p5, object p6)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("SubmitTagHelper_CannotOverrideFormAction"), p0, p1, p2, p3, p4, p5, p6);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cannot override the '{1}' attribute for {0}. A {0} with a specified '{1}' must not have attributes starting with '{6}' or an '{2}' or '{3}' or '{4}' or '{5}' attribute.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -147,4 +147,16 @@
|
|||
<data name="PropertyOfTypeCannotBeNull" xml:space="preserve">
|
||||
<value>The '{0}' property of '{1}' must not be null.</value>
|
||||
</data>
|
||||
<data name="ButtonTagHelper_CannotOverrideFormAction" xml:space="preserve">
|
||||
<value>Cannot override the '{6}' attribute for {0}. A {0} with a specified '{6}' must not have attributes starting with '{5}' or an '{1}', '{2}', '{3}', or '{4}' attribute.</value>
|
||||
</data>
|
||||
<data name="ButtonTagHelper_CannotDetermineFormActionRouteActionOrControllerSpecified" xml:space="preserve">
|
||||
<value>Cannot determine a '{4}' attribute for {0}. A {0} with a specified '{1}' must not have an '{2}' or '{3}' attribute.</value>
|
||||
</data>
|
||||
<data name="SubmitTagHelper_CannotDetermineFormActionRouteActionOrControllerSpecified" xml:space="preserve">
|
||||
<value>Cannot determine a '{4}' attribute for {0}. An {0} with a specified '{1}' must not have an '{2}' or '{3}' attribute.</value>
|
||||
</data>
|
||||
<data name="SubmitTagHelper_CannotOverrideFormAction" xml:space="preserve">
|
||||
<value>Cannot override the '{6}' attribute for {0}. An {0} with a specified '{6}' must not have attributes starting with '{5}' or an '{1}', '{2}', '{3}', or '{4}' attribute.</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -0,0 +1,193 @@
|
|||
// Copyright (c) .NET Foundation. 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 Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Microsoft.AspNetCore.Mvc.Routing;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||
using Microsoft.AspNetCore.Razor.TagHelpers;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="ITagHelper"/> implementation targeting <input> elements with <c>type</c> attribute set to <c>submit</c>.
|
||||
/// </summary>
|
||||
[HtmlTargetElement("input", Attributes = ActionAttributeSelector)]
|
||||
[HtmlTargetElement("input", Attributes = ControllerAttributeSelector)]
|
||||
[HtmlTargetElement("input", Attributes = AreaAttributeSelector)]
|
||||
[HtmlTargetElement("input", Attributes = RouteAttributeSelector)]
|
||||
[HtmlTargetElement("input", Attributes = RouteValuesDictionarySelector)]
|
||||
[HtmlTargetElement("input", Attributes = RouteValuesSelector)]
|
||||
public class SubmitTagHelper : TagHelper
|
||||
{
|
||||
private const string ActionAttributeSelector = TypeSelector + ActionAttributeName;
|
||||
private const string ControllerAttributeSelector = TypeSelector + ControllerAttributeName;
|
||||
private const string AreaAttributeSelector = TypeSelector + AreaAttributeName;
|
||||
private const string RouteAttributeSelector = TypeSelector + RouteAttributeName;
|
||||
private const string RouteValuesDictionarySelector = TypeSelector + RouteValuesDictionaryName;
|
||||
private const string RouteValuesSelector = TypeSelector + RouteValuesPrefix + "*";
|
||||
private const string TypeSelector = "[type=submit], ";
|
||||
|
||||
private const string ActionAttributeName = "asp-action";
|
||||
private const string ControllerAttributeName = "asp-controller";
|
||||
private const string AreaAttributeName = "asp-area";
|
||||
private const string RouteAttributeName = "asp-route";
|
||||
private const string RouteValuesDictionaryName = "asp-all-route-data";
|
||||
private const string RouteValuesPrefix = "asp-route-";
|
||||
private const string FormAction = "formaction";
|
||||
private IDictionary<string, string> _routeValues;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="SubmitTagHelper"/>.
|
||||
/// </summary>
|
||||
/// <param name="urlHelperFactory">The <see cref="IUrlHelperFactory"/>.</param>
|
||||
public SubmitTagHelper(IUrlHelperFactory urlHelperFactory)
|
||||
{
|
||||
UrlHelperFactory = urlHelperFactory;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int Order => -1000;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="Rendering.ViewContext"/> for the current request.
|
||||
/// </summary>
|
||||
[HtmlAttributeNotBound]
|
||||
[ViewContext]
|
||||
public ViewContext ViewContext { get; set; }
|
||||
|
||||
protected IUrlHelperFactory UrlHelperFactory { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of the action method.
|
||||
/// </summary>
|
||||
[HtmlAttributeName(ActionAttributeName)]
|
||||
public string Action { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of the controller.
|
||||
/// </summary>
|
||||
[HtmlAttributeName(ControllerAttributeName)]
|
||||
public string Controller { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of the area.
|
||||
/// </summary>
|
||||
[HtmlAttributeName(AreaAttributeName)]
|
||||
public string Area { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Name of the route.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Must be <c>null</c> if <see cref="Action"/> or <see cref="Controller"/> is non-<c>null</c>.
|
||||
/// </remarks>
|
||||
[HtmlAttributeName(RouteAttributeName)]
|
||||
public string Route { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Additional parameters for the route.
|
||||
/// </summary>
|
||||
[HtmlAttributeName(RouteValuesDictionaryName, DictionaryAttributePrefix = RouteValuesPrefix)]
|
||||
public IDictionary<string, string> RouteValues
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_routeValues == null)
|
||||
{
|
||||
_routeValues = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
return _routeValues;
|
||||
}
|
||||
set
|
||||
{
|
||||
_routeValues = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <remarks>Does nothing if user provides an <c>formaction</c> attribute.</remarks>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// Thrown if <c>formaction</c> attribute is provided and <see cref="Action"/>, <see cref="Controller"/>,
|
||||
/// or <see cref="Route"/> are non-<c>null</c> or if the user provided <c>asp-route-*</c> attributes.
|
||||
/// Also thrown if <see cref="Route"/> and one or both of <see cref="Action"/> and <see cref="Controller"/>
|
||||
/// are non-<c>null</c>
|
||||
/// </exception>
|
||||
public override void Process(TagHelperContext context, TagHelperOutput output)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
if (output == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(output));
|
||||
}
|
||||
|
||||
// If "formaction" is already set, it means the user is attempting to use a normal submit input.
|
||||
if (output.Attributes.ContainsName(FormAction))
|
||||
{
|
||||
if (Action != null || Controller != null || Area != null || Route != null || RouteValues.Count != 0)
|
||||
{
|
||||
// User specified a formaction and one of the bound attributes; can't determine the formaction attribute.
|
||||
throw new InvalidOperationException(
|
||||
Resources.FormatSubmitTagHelper_CannotOverrideFormAction(
|
||||
"<input>",
|
||||
ActionAttributeName,
|
||||
ControllerAttributeName,
|
||||
AreaAttributeName,
|
||||
RouteAttributeName,
|
||||
RouteValuesPrefix,
|
||||
FormAction));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RouteValueDictionary routeValues = null;
|
||||
if (_routeValues != null && _routeValues.Count > 0)
|
||||
{
|
||||
routeValues = new RouteValueDictionary(_routeValues);
|
||||
}
|
||||
|
||||
if (Area != null)
|
||||
{
|
||||
if (routeValues == null)
|
||||
{
|
||||
routeValues = new RouteValueDictionary();
|
||||
}
|
||||
|
||||
// Unconditionally replace any value from asp-route-area.
|
||||
routeValues["area"] = Area;
|
||||
}
|
||||
|
||||
if (Route == null)
|
||||
{
|
||||
var urlHelper = UrlHelperFactory.GetUrlHelper(ViewContext);
|
||||
var url = urlHelper.Action(Action, Controller, routeValues);
|
||||
output.Attributes.SetAttribute(FormAction, url);
|
||||
}
|
||||
else if (Action != null || Controller != null)
|
||||
{
|
||||
// Route and Action or Controller were specified. Can't determine the formaction attribute.
|
||||
throw new InvalidOperationException(
|
||||
Resources.FormatSubmitTagHelper_CannotDetermineFormActionRouteActionOrControllerSpecified(
|
||||
"<input>",
|
||||
RouteAttributeName,
|
||||
ActionAttributeName,
|
||||
ControllerAttributeName,
|
||||
FormAction));
|
||||
}
|
||||
else
|
||||
{
|
||||
var urlHelper = UrlHelperFactory.GetUrlHelper(ViewContext);
|
||||
var url = urlHelper.RouteUrl(Route, routeValues);
|
||||
output.Attributes.SetAttribute(FormAction, url);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,381 @@
|
|||
// Copyright (c) .NET Foundation. 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.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.AspNetCore.Mvc.Routing;
|
||||
using Microsoft.AspNetCore.Razor.TagHelpers;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
{
|
||||
public class ButtonTagHelperTest
|
||||
{
|
||||
[Fact]
|
||||
public async Task ProcessAsync_GeneratesExpectedOutput()
|
||||
{
|
||||
// Arrange
|
||||
var expectedTagName = "not-button";
|
||||
var metadataProvider = new TestModelMetadataProvider();
|
||||
|
||||
var tagHelperContext = new TagHelperContext(
|
||||
allAttributes: new TagHelperAttributeList
|
||||
{
|
||||
{ "id", "mybutton" },
|
||||
{ "asp-route-name", "value" },
|
||||
{ "asp-action", "index" },
|
||||
{ "asp-controller", "home" },
|
||||
},
|
||||
items: new Dictionary<object, object>(),
|
||||
uniqueId: "test");
|
||||
var output = new TagHelperOutput(
|
||||
expectedTagName,
|
||||
attributes: new TagHelperAttributeList
|
||||
{
|
||||
{ "id", "mybutton" },
|
||||
},
|
||||
getChildContentAsync: (useCachedResult, encoder) =>
|
||||
{
|
||||
var tagHelperContent = new DefaultTagHelperContent();
|
||||
tagHelperContent.SetContent("Something Else");
|
||||
return Task.FromResult<TagHelperContent>(tagHelperContent);
|
||||
});
|
||||
output.Content.SetContent("Something");
|
||||
|
||||
var urlHelper = new Mock<IUrlHelper>();
|
||||
urlHelper
|
||||
.Setup(mock => mock.Action(It.IsAny<UrlActionContext>())).Returns("home/index").Verifiable();
|
||||
|
||||
var urlHelperFactory = new Mock<IUrlHelperFactory>();
|
||||
urlHelperFactory
|
||||
.Setup(f => f.GetUrlHelper(It.IsAny<ActionContext>()))
|
||||
.Returns(urlHelper.Object);
|
||||
|
||||
var htmlGenerator = new TestableHtmlGenerator(metadataProvider, urlHelper.Object);
|
||||
var viewContext = TestableHtmlGenerator.GetViewContext(model: null,
|
||||
htmlGenerator: htmlGenerator,
|
||||
metadataProvider: metadataProvider);
|
||||
var buttonTagHelper = new ButtonTagHelper(urlHelperFactory.Object)
|
||||
{
|
||||
Action = "index",
|
||||
Controller = "home",
|
||||
RouteValues =
|
||||
{
|
||||
{ "name", "value" },
|
||||
},
|
||||
ViewContext = viewContext,
|
||||
};
|
||||
|
||||
// Act
|
||||
await buttonTagHelper.ProcessAsync(tagHelperContext, output);
|
||||
|
||||
// Assert
|
||||
urlHelper.Verify();
|
||||
Assert.Equal(2, output.Attributes.Count);
|
||||
var attribute = Assert.Single(output.Attributes, attr => attr.Name.Equals("id"));
|
||||
Assert.Equal("mybutton", attribute.Value);
|
||||
attribute = Assert.Single(output.Attributes, attr => attr.Name.Equals("formaction"));
|
||||
Assert.Equal("home/index", attribute.Value);
|
||||
Assert.Equal("Something", output.Content.GetContent());
|
||||
Assert.Equal(expectedTagName, output.TagName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ProcessAsync_CallsIntoRouteLinkWithExpectedParameters()
|
||||
{
|
||||
// Arrange
|
||||
var context = new TagHelperContext(
|
||||
allAttributes: new TagHelperAttributeList(
|
||||
Enumerable.Empty<TagHelperAttribute>()),
|
||||
items: new Dictionary<object, object>(),
|
||||
uniqueId: "test");
|
||||
var output = new TagHelperOutput(
|
||||
"button",
|
||||
attributes: new TagHelperAttributeList(),
|
||||
getChildContentAsync: (useCachedResult, encoder) =>
|
||||
{
|
||||
var tagHelperContent = new DefaultTagHelperContent();
|
||||
tagHelperContent.SetContent("Something");
|
||||
return Task.FromResult<TagHelperContent>(tagHelperContent);
|
||||
});
|
||||
output.Content.SetContent(string.Empty);
|
||||
|
||||
var urlHelper = new Mock<IUrlHelper>();
|
||||
urlHelper
|
||||
.Setup(mock => mock.RouteUrl(It.IsAny<UrlRouteContext>())).Returns("home/index").Verifiable();
|
||||
|
||||
var urlHelperFactory = new Mock<IUrlHelperFactory>();
|
||||
urlHelperFactory
|
||||
.Setup(f => f.GetUrlHelper(It.IsAny<ActionContext>()))
|
||||
.Returns(urlHelper.Object);
|
||||
|
||||
var metadataProvider = new TestModelMetadataProvider();
|
||||
var htmlGenerator = new TestableHtmlGenerator(metadataProvider, urlHelper.Object);
|
||||
var viewContext = TestableHtmlGenerator.GetViewContext(model: null,
|
||||
htmlGenerator: htmlGenerator,
|
||||
metadataProvider: metadataProvider);
|
||||
var buttonTagHelper = new ButtonTagHelper(urlHelperFactory.Object)
|
||||
{
|
||||
Route = "Default",
|
||||
ViewContext = viewContext,
|
||||
RouteValues =
|
||||
{
|
||||
{ "name", "value" },
|
||||
},
|
||||
};
|
||||
|
||||
// Act
|
||||
await buttonTagHelper.ProcessAsync(context, output);
|
||||
|
||||
// Assert
|
||||
urlHelper.Verify();
|
||||
Assert.Equal("button", output.TagName);
|
||||
Assert.Equal(1, output.Attributes.Count);
|
||||
var attribute = Assert.Single(output.Attributes, attr => attr.Name.Equals("formaction"));
|
||||
Assert.Equal("home/index", attribute.Value);
|
||||
Assert.True(output.Content.GetContent().Length == 0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ProcessAsync_AddsAreaToRouteValuesAndCallsIntoActionLinkWithExpectedParameters()
|
||||
{
|
||||
// Arrange
|
||||
var context = new TagHelperContext(
|
||||
allAttributes: new TagHelperAttributeList(
|
||||
Enumerable.Empty<TagHelperAttribute>()),
|
||||
items: new Dictionary<object, object>(),
|
||||
uniqueId: "test");
|
||||
var output = new TagHelperOutput(
|
||||
"button",
|
||||
attributes: new TagHelperAttributeList(),
|
||||
getChildContentAsync: (useCachedResult, encoder) =>
|
||||
{
|
||||
var tagHelperContent = new DefaultTagHelperContent();
|
||||
tagHelperContent.SetContent("Something");
|
||||
return Task.FromResult<TagHelperContent>(tagHelperContent);
|
||||
});
|
||||
output.Content.SetContent(string.Empty);
|
||||
|
||||
var expectedRouteValues = new RouteValueDictionary(new Dictionary<string, string> { { "area", "Admin" } });
|
||||
var urlHelper = new Mock<IUrlHelper>();
|
||||
urlHelper
|
||||
.Setup(mock => mock.Action(It.IsAny<UrlActionContext>()))
|
||||
.Returns("admin/dashboard/index")
|
||||
.Callback<UrlActionContext>(param => Assert.Equal(param.Values, expectedRouteValues))
|
||||
.Verifiable();
|
||||
|
||||
var urlHelperFactory = new Mock<IUrlHelperFactory>();
|
||||
urlHelperFactory
|
||||
.Setup(f => f.GetUrlHelper(It.IsAny<ActionContext>()))
|
||||
.Returns(urlHelper.Object);
|
||||
|
||||
var buttonTagHelper = new ButtonTagHelper(urlHelperFactory.Object)
|
||||
{
|
||||
Action = "Index",
|
||||
Controller = "Dashboard",
|
||||
Area = "Admin",
|
||||
};
|
||||
|
||||
// Act
|
||||
await buttonTagHelper.ProcessAsync(context, output);
|
||||
|
||||
// Assert
|
||||
urlHelper.Verify();
|
||||
Assert.Equal("button", output.TagName);
|
||||
Assert.Equal(1, output.Attributes.Count);
|
||||
var attribute = Assert.Single(output.Attributes, attr => attr.Name.Equals("formaction"));
|
||||
Assert.Equal("admin/dashboard/index", attribute.Value);
|
||||
Assert.True(output.Content.GetContent().Length == 0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ProcessAsync_AspAreaOverridesAspRouteArea()
|
||||
{
|
||||
// Arrange
|
||||
var context = new TagHelperContext(
|
||||
allAttributes: new TagHelperAttributeList(
|
||||
Enumerable.Empty<TagHelperAttribute>()),
|
||||
items: new Dictionary<object, object>(),
|
||||
uniqueId: "test");
|
||||
var output = new TagHelperOutput(
|
||||
"button",
|
||||
attributes: new TagHelperAttributeList(),
|
||||
getChildContentAsync: (useCachedResult, encoder) =>
|
||||
{
|
||||
var tagHelperContent = new DefaultTagHelperContent();
|
||||
tagHelperContent.SetContent("Something");
|
||||
return Task.FromResult<TagHelperContent>(tagHelperContent);
|
||||
});
|
||||
output.Content.SetContent(string.Empty);
|
||||
|
||||
var expectedRouteValues = new RouteValueDictionary(new Dictionary<string, string> { { "area", "Admin" } });
|
||||
var urlHelper = new Mock<IUrlHelper>();
|
||||
urlHelper
|
||||
.Setup(mock => mock.Action(It.IsAny<UrlActionContext>()))
|
||||
.Returns("admin/dashboard/index")
|
||||
.Callback<UrlActionContext>(param => Assert.Equal(param.Values, expectedRouteValues))
|
||||
.Verifiable();
|
||||
|
||||
var urlHelperFactory = new Mock<IUrlHelperFactory>();
|
||||
urlHelperFactory
|
||||
.Setup(f => f.GetUrlHelper(It.IsAny<ActionContext>()))
|
||||
.Returns(urlHelper.Object);
|
||||
|
||||
var buttonTagHelper = new ButtonTagHelper(urlHelperFactory.Object)
|
||||
{
|
||||
Action = "Index",
|
||||
Controller = "Dashboard",
|
||||
Area = "Admin",
|
||||
RouteValues = new Dictionary<string, string> { { "area", "Home" } }
|
||||
};
|
||||
|
||||
// Act
|
||||
await buttonTagHelper.ProcessAsync(context, output);
|
||||
|
||||
// Assert
|
||||
urlHelper.Verify();
|
||||
Assert.Equal("button", output.TagName);
|
||||
Assert.Equal(1, output.Attributes.Count);
|
||||
var attribute = Assert.Single(output.Attributes, attr => attr.Name.Equals("formaction"));
|
||||
Assert.Equal("admin/dashboard/index", attribute.Value);
|
||||
Assert.True(output.Content.GetContent().Length == 0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ProcessAsync_EmptyStringOnAspAreaIsPassedThroughToRouteValues()
|
||||
{
|
||||
// Arrange
|
||||
var context = new TagHelperContext(
|
||||
allAttributes: new TagHelperAttributeList(
|
||||
Enumerable.Empty<TagHelperAttribute>()),
|
||||
items: new Dictionary<object, object>(),
|
||||
uniqueId: "test");
|
||||
var output = new TagHelperOutput(
|
||||
"button",
|
||||
attributes: new TagHelperAttributeList(),
|
||||
getChildContentAsync: (useCachedResult, encoder) =>
|
||||
{
|
||||
var tagHelperContent = new DefaultTagHelperContent();
|
||||
tagHelperContent.SetContent("Something");
|
||||
return Task.FromResult<TagHelperContent>(tagHelperContent);
|
||||
});
|
||||
output.Content.SetContent(string.Empty);
|
||||
|
||||
var expectedRouteValues = new RouteValueDictionary(new Dictionary<string, string> { { "area", string.Empty } });
|
||||
var urlHelper = new Mock<IUrlHelper>();
|
||||
urlHelper
|
||||
.Setup(mock => mock.Action(It.IsAny<UrlActionContext>()))
|
||||
.Returns("admin/dashboard/index")
|
||||
.Callback<UrlActionContext>(param => Assert.Equal(param.Values, expectedRouteValues))
|
||||
.Verifiable();
|
||||
|
||||
var urlHelperFactory = new Mock<IUrlHelperFactory>();
|
||||
urlHelperFactory
|
||||
.Setup(f => f.GetUrlHelper(It.IsAny<ActionContext>()))
|
||||
.Returns(urlHelper.Object);
|
||||
|
||||
var buttonTagHelper = new ButtonTagHelper(urlHelperFactory.Object)
|
||||
{
|
||||
Action = "Index",
|
||||
Controller = "Dashboard",
|
||||
Area = string.Empty,
|
||||
};
|
||||
|
||||
// Act
|
||||
await buttonTagHelper.ProcessAsync(context, output);
|
||||
|
||||
// Assert
|
||||
urlHelper.Verify();
|
||||
Assert.Equal("button", output.TagName);
|
||||
Assert.Equal(1, output.Attributes.Count);
|
||||
var attribute = Assert.Single(output.Attributes, attr => attr.Name.Equals("formaction"));
|
||||
Assert.Equal("admin/dashboard/index", attribute.Value);
|
||||
Assert.True(output.Content.GetContent().Length == 0);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("Action")]
|
||||
[InlineData("Controller")]
|
||||
[InlineData("Route")]
|
||||
[InlineData("asp-route-")]
|
||||
public async Task ProcessAsync_ThrowsIfFormActionConflictsWithBoundAttributes(string propertyName)
|
||||
{
|
||||
// Arrange
|
||||
var urlHelperFactory = new Mock<IUrlHelperFactory>().Object;
|
||||
|
||||
var buttonTagHelper = new ButtonTagHelper(urlHelperFactory);
|
||||
|
||||
var output = new TagHelperOutput(
|
||||
"button",
|
||||
attributes: new TagHelperAttributeList
|
||||
{
|
||||
{ "formaction", "my-action" }
|
||||
},
|
||||
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(null));
|
||||
if (propertyName == "asp-route-")
|
||||
{
|
||||
buttonTagHelper.RouteValues.Add("name", "value");
|
||||
}
|
||||
else
|
||||
{
|
||||
typeof(ButtonTagHelper).GetProperty(propertyName).SetValue(buttonTagHelper, "Home");
|
||||
}
|
||||
|
||||
var expectedErrorMessage = "Cannot override the 'formaction' attribute for <button>. A <button> with a specified " +
|
||||
"'formaction' must not have attributes starting with 'asp-route-' or an " +
|
||||
"'asp-action', 'asp-controller', 'asp-area', or 'asp-route' attribute.";
|
||||
|
||||
var context = new TagHelperContext(
|
||||
allAttributes: new TagHelperAttributeList(
|
||||
Enumerable.Empty<TagHelperAttribute>()),
|
||||
items: new Dictionary<object, object>(),
|
||||
uniqueId: "test");
|
||||
|
||||
// Act & Assert
|
||||
var ex = await Assert.ThrowsAsync<InvalidOperationException>(
|
||||
() => buttonTagHelper.ProcessAsync(context, output));
|
||||
|
||||
Assert.Equal(expectedErrorMessage, ex.Message);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("Action")]
|
||||
[InlineData("Controller")]
|
||||
public async Task ProcessAsync_ThrowsIfRouteAndActionOrControllerProvided(string propertyName)
|
||||
{
|
||||
// Arrange
|
||||
var urlHelperFactory = new Mock<IUrlHelperFactory>().Object;
|
||||
|
||||
var buttonTagHelper = new ButtonTagHelper(urlHelperFactory)
|
||||
{
|
||||
Route = "Default",
|
||||
};
|
||||
|
||||
typeof(ButtonTagHelper).GetProperty(propertyName).SetValue(buttonTagHelper, "Home");
|
||||
var output = new TagHelperOutput(
|
||||
"button",
|
||||
attributes: new TagHelperAttributeList(),
|
||||
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(null));
|
||||
var expectedErrorMessage = "Cannot determine a 'formaction' attribute for <button>. A <button> with a specified " +
|
||||
"'asp-route' must not have an 'asp-action' or 'asp-controller' attribute.";
|
||||
|
||||
var context = new TagHelperContext(
|
||||
allAttributes: new TagHelperAttributeList(
|
||||
Enumerable.Empty<TagHelperAttribute>()),
|
||||
items: new Dictionary<object, object>(),
|
||||
uniqueId: "test");
|
||||
|
||||
// Act & Assert
|
||||
var ex = await Assert.ThrowsAsync<InvalidOperationException>(
|
||||
() => buttonTagHelper.ProcessAsync(context, output));
|
||||
|
||||
Assert.Equal(expectedErrorMessage, ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,385 @@
|
|||
// Copyright (c) .NET Foundation. 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.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.AspNetCore.Mvc.Routing;
|
||||
using Microsoft.AspNetCore.Razor.TagHelpers;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
{
|
||||
public class SubmitTagHelperTest
|
||||
{
|
||||
[Fact]
|
||||
public async Task ProcessAsync_GeneratesExpectedOutput()
|
||||
{
|
||||
// Arrange
|
||||
var expectedTagName = "not-input";
|
||||
var metadataProvider = new TestModelMetadataProvider();
|
||||
|
||||
var tagHelperContext = new TagHelperContext(
|
||||
allAttributes: new TagHelperAttributeList
|
||||
{
|
||||
{ "id", "myinput" },
|
||||
{ "type", "submit" },
|
||||
{ "asp-route-name", "value" },
|
||||
{ "asp-action", "index" },
|
||||
{ "asp-controller", "home" },
|
||||
},
|
||||
items: new Dictionary<object, object>(),
|
||||
uniqueId: "test");
|
||||
var output = new TagHelperOutput(
|
||||
expectedTagName,
|
||||
attributes: new TagHelperAttributeList
|
||||
{
|
||||
{ "id", "myinput" },
|
||||
{ "type", "submit" },
|
||||
},
|
||||
getChildContentAsync: (useCachedResult, encoder) =>
|
||||
{
|
||||
var tagHelperContent = new DefaultTagHelperContent();
|
||||
tagHelperContent.SetContent("Something Else");
|
||||
return Task.FromResult<TagHelperContent>(tagHelperContent);
|
||||
});
|
||||
output.Content.SetContent("Something");
|
||||
|
||||
var urlHelper = new Mock<IUrlHelper>();
|
||||
urlHelper
|
||||
.Setup(mock => mock.Action(It.IsAny<UrlActionContext>())).Returns("home/index").Verifiable();
|
||||
|
||||
var urlHelperFactory = new Mock<IUrlHelperFactory>();
|
||||
urlHelperFactory
|
||||
.Setup(f => f.GetUrlHelper(It.IsAny<ActionContext>()))
|
||||
.Returns(urlHelper.Object);
|
||||
|
||||
var htmlGenerator = new TestableHtmlGenerator(metadataProvider, urlHelper.Object);
|
||||
var viewContext = TestableHtmlGenerator.GetViewContext(model: null,
|
||||
htmlGenerator: htmlGenerator,
|
||||
metadataProvider: metadataProvider);
|
||||
var submitTagHelper = new SubmitTagHelper(urlHelperFactory.Object)
|
||||
{
|
||||
Action = "index",
|
||||
Controller = "home",
|
||||
RouteValues =
|
||||
{
|
||||
{ "name", "value" },
|
||||
},
|
||||
ViewContext = viewContext,
|
||||
};
|
||||
|
||||
// Act
|
||||
await submitTagHelper.ProcessAsync(tagHelperContext, output);
|
||||
|
||||
// Assert
|
||||
urlHelper.Verify();
|
||||
Assert.Equal(3, output.Attributes.Count);
|
||||
var attribute = Assert.Single(output.Attributes, attr => attr.Name.Equals("id"));
|
||||
Assert.Equal("myinput", attribute.Value);
|
||||
attribute = Assert.Single(output.Attributes, attr => attr.Name.Equals("type"));
|
||||
Assert.Equal("submit", attribute.Value);
|
||||
attribute = Assert.Single(output.Attributes, attr => attr.Name.Equals("formaction"));
|
||||
Assert.Equal("home/index", attribute.Value);
|
||||
Assert.Equal("Something", output.Content.GetContent());
|
||||
Assert.Equal(expectedTagName, output.TagName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ProcessAsync_CallsIntoRouteLinkWithExpectedParameters()
|
||||
{
|
||||
// Arrange
|
||||
var context = new TagHelperContext(
|
||||
allAttributes: new TagHelperAttributeList(
|
||||
Enumerable.Empty<TagHelperAttribute>()),
|
||||
items: new Dictionary<object, object>(),
|
||||
uniqueId: "test");
|
||||
var output = new TagHelperOutput(
|
||||
"input",
|
||||
attributes: new TagHelperAttributeList(),
|
||||
getChildContentAsync: (useCachedResult, encoder) =>
|
||||
{
|
||||
var tagHelperContent = new DefaultTagHelperContent();
|
||||
tagHelperContent.SetContent("Something");
|
||||
return Task.FromResult<TagHelperContent>(tagHelperContent);
|
||||
});
|
||||
output.Content.SetContent(string.Empty);
|
||||
|
||||
var urlHelper = new Mock<IUrlHelper>();
|
||||
urlHelper
|
||||
.Setup(mock => mock.RouteUrl(It.IsAny<UrlRouteContext>())).Returns("home/index").Verifiable();
|
||||
|
||||
var urlHelperFactory = new Mock<IUrlHelperFactory>();
|
||||
urlHelperFactory
|
||||
.Setup(f => f.GetUrlHelper(It.IsAny<ActionContext>()))
|
||||
.Returns(urlHelper.Object);
|
||||
|
||||
var metadataProvider = new TestModelMetadataProvider();
|
||||
var htmlGenerator = new TestableHtmlGenerator(metadataProvider, urlHelper.Object);
|
||||
var viewContext = TestableHtmlGenerator.GetViewContext(model: null,
|
||||
htmlGenerator: htmlGenerator,
|
||||
metadataProvider: metadataProvider);
|
||||
var submitTagHelper = new SubmitTagHelper(urlHelperFactory.Object)
|
||||
{
|
||||
Route = "Default",
|
||||
ViewContext = viewContext,
|
||||
RouteValues =
|
||||
{
|
||||
{ "name", "value" },
|
||||
},
|
||||
};
|
||||
|
||||
// Act
|
||||
await submitTagHelper.ProcessAsync(context, output);
|
||||
|
||||
// Assert
|
||||
urlHelper.Verify();
|
||||
Assert.Equal("input", output.TagName);
|
||||
Assert.Equal(1, output.Attributes.Count);
|
||||
var attribute = Assert.Single(output.Attributes, attr => attr.Name.Equals("formaction"));
|
||||
Assert.Equal("home/index", attribute.Value);
|
||||
Assert.True(output.Content.GetContent().Length == 0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ProcessAsync_AddsAreaToRouteValuesAndCallsIntoActionLinkWithExpectedParameters()
|
||||
{
|
||||
// Arrange
|
||||
var context = new TagHelperContext(
|
||||
allAttributes: new TagHelperAttributeList(
|
||||
Enumerable.Empty<TagHelperAttribute>()),
|
||||
items: new Dictionary<object, object>(),
|
||||
uniqueId: "test");
|
||||
var output = new TagHelperOutput(
|
||||
"input",
|
||||
attributes: new TagHelperAttributeList(),
|
||||
getChildContentAsync: (useCachedResult, encoder) =>
|
||||
{
|
||||
var tagHelperContent = new DefaultTagHelperContent();
|
||||
tagHelperContent.SetContent("Something");
|
||||
return Task.FromResult<TagHelperContent>(tagHelperContent);
|
||||
});
|
||||
output.Content.SetContent(string.Empty);
|
||||
|
||||
var expectedRouteValues = new RouteValueDictionary(new Dictionary<string, string> { { "area", "Admin" } });
|
||||
var urlHelper = new Mock<IUrlHelper>();
|
||||
urlHelper
|
||||
.Setup(mock => mock.Action(It.IsAny<UrlActionContext>()))
|
||||
.Returns("admin/dashboard/index")
|
||||
.Callback<UrlActionContext>(param => Assert.Equal(param.Values, expectedRouteValues))
|
||||
.Verifiable();
|
||||
|
||||
var urlHelperFactory = new Mock<IUrlHelperFactory>();
|
||||
urlHelperFactory
|
||||
.Setup(f => f.GetUrlHelper(It.IsAny<ActionContext>()))
|
||||
.Returns(urlHelper.Object);
|
||||
|
||||
var submitTagHelper = new SubmitTagHelper(urlHelperFactory.Object)
|
||||
{
|
||||
Action = "Index",
|
||||
Controller = "Dashboard",
|
||||
Area = "Admin",
|
||||
};
|
||||
|
||||
// Act
|
||||
await submitTagHelper.ProcessAsync(context, output);
|
||||
|
||||
// Assert
|
||||
urlHelper.Verify();
|
||||
Assert.Equal("input", output.TagName);
|
||||
Assert.Equal(1, output.Attributes.Count);
|
||||
var attribute = Assert.Single(output.Attributes, attr => attr.Name.Equals("formaction"));
|
||||
Assert.Equal("admin/dashboard/index", attribute.Value);
|
||||
Assert.True(output.Content.GetContent().Length == 0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ProcessAsync_AspAreaOverridesAspRouteArea()
|
||||
{
|
||||
// Arrange
|
||||
var context = new TagHelperContext(
|
||||
allAttributes: new TagHelperAttributeList(
|
||||
Enumerable.Empty<TagHelperAttribute>()),
|
||||
items: new Dictionary<object, object>(),
|
||||
uniqueId: "test");
|
||||
var output = new TagHelperOutput(
|
||||
"input",
|
||||
attributes: new TagHelperAttributeList(),
|
||||
getChildContentAsync: (useCachedResult, encoder) =>
|
||||
{
|
||||
var tagHelperContent = new DefaultTagHelperContent();
|
||||
tagHelperContent.SetContent("Something");
|
||||
return Task.FromResult<TagHelperContent>(tagHelperContent);
|
||||
});
|
||||
output.Content.SetContent(string.Empty);
|
||||
|
||||
var expectedRouteValues = new RouteValueDictionary(new Dictionary<string, string> { { "area", "Admin" } });
|
||||
var urlHelper = new Mock<IUrlHelper>();
|
||||
urlHelper
|
||||
.Setup(mock => mock.Action(It.IsAny<UrlActionContext>()))
|
||||
.Returns("admin/dashboard/index")
|
||||
.Callback<UrlActionContext>(param => Assert.Equal(param.Values, expectedRouteValues))
|
||||
.Verifiable();
|
||||
|
||||
var urlHelperFactory = new Mock<IUrlHelperFactory>();
|
||||
urlHelperFactory
|
||||
.Setup(f => f.GetUrlHelper(It.IsAny<ActionContext>()))
|
||||
.Returns(urlHelper.Object);
|
||||
|
||||
var submitTagHelper = new SubmitTagHelper(urlHelperFactory.Object)
|
||||
{
|
||||
Action = "Index",
|
||||
Controller = "Dashboard",
|
||||
Area = "Admin",
|
||||
RouteValues = new Dictionary<string, string> { { "area", "Home" } }
|
||||
};
|
||||
|
||||
// Act
|
||||
await submitTagHelper.ProcessAsync(context, output);
|
||||
|
||||
// Assert
|
||||
urlHelper.Verify();
|
||||
Assert.Equal("input", output.TagName);
|
||||
Assert.Equal(1, output.Attributes.Count);
|
||||
var attribute = Assert.Single(output.Attributes, attr => attr.Name.Equals("formaction"));
|
||||
Assert.Equal("admin/dashboard/index", attribute.Value);
|
||||
Assert.True(output.Content.GetContent().Length == 0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ProcessAsync_EmptyStringOnAspAreaIsPassedThroughToRouteValues()
|
||||
{
|
||||
// Arrange
|
||||
var context = new TagHelperContext(
|
||||
allAttributes: new TagHelperAttributeList(
|
||||
Enumerable.Empty<TagHelperAttribute>()),
|
||||
items: new Dictionary<object, object>(),
|
||||
uniqueId: "test");
|
||||
var output = new TagHelperOutput(
|
||||
"input",
|
||||
attributes: new TagHelperAttributeList(),
|
||||
getChildContentAsync: (useCachedResult, encoder) =>
|
||||
{
|
||||
var tagHelperContent = new DefaultTagHelperContent();
|
||||
tagHelperContent.SetContent("Something");
|
||||
return Task.FromResult<TagHelperContent>(tagHelperContent);
|
||||
});
|
||||
output.Content.SetContent(string.Empty);
|
||||
|
||||
var expectedRouteValues = new RouteValueDictionary(new Dictionary<string, string> { { "area", string.Empty } });
|
||||
var urlHelper = new Mock<IUrlHelper>();
|
||||
urlHelper
|
||||
.Setup(mock => mock.Action(It.IsAny<UrlActionContext>()))
|
||||
.Returns("admin/dashboard/index")
|
||||
.Callback<UrlActionContext>(param => Assert.Equal(param.Values, expectedRouteValues))
|
||||
.Verifiable();
|
||||
|
||||
var urlHelperFactory = new Mock<IUrlHelperFactory>();
|
||||
urlHelperFactory
|
||||
.Setup(f => f.GetUrlHelper(It.IsAny<ActionContext>()))
|
||||
.Returns(urlHelper.Object);
|
||||
|
||||
var submitTagHelper = new SubmitTagHelper(urlHelperFactory.Object)
|
||||
{
|
||||
Action = "Index",
|
||||
Controller = "Dashboard",
|
||||
Area = string.Empty,
|
||||
};
|
||||
|
||||
// Act
|
||||
await submitTagHelper.ProcessAsync(context, output);
|
||||
|
||||
// Assert
|
||||
urlHelper.Verify();
|
||||
Assert.Equal("input", output.TagName);
|
||||
Assert.Equal(1, output.Attributes.Count);
|
||||
var attribute = Assert.Single(output.Attributes, attr => attr.Name.Equals("formaction"));
|
||||
Assert.Equal("admin/dashboard/index", attribute.Value);
|
||||
Assert.True(output.Content.GetContent().Length == 0);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("Action")]
|
||||
[InlineData("Controller")]
|
||||
[InlineData("Route")]
|
||||
[InlineData("asp-route-")]
|
||||
public async Task ProcessAsync_ThrowsIfFormActionConflictsWithBoundAttributes(string propertyName)
|
||||
{
|
||||
// Arrange
|
||||
var urlHelperFactory = new Mock<IUrlHelperFactory>().Object;
|
||||
|
||||
var submitTagHelper = new SubmitTagHelper(urlHelperFactory);
|
||||
|
||||
var output = new TagHelperOutput(
|
||||
"input",
|
||||
attributes: new TagHelperAttributeList
|
||||
{
|
||||
{ "formaction", "my-action" }
|
||||
},
|
||||
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(null));
|
||||
if (propertyName == "asp-route-")
|
||||
{
|
||||
submitTagHelper.RouteValues.Add("name", "value");
|
||||
}
|
||||
else
|
||||
{
|
||||
typeof(SubmitTagHelper).GetProperty(propertyName).SetValue(submitTagHelper, "Home");
|
||||
}
|
||||
|
||||
var expectedErrorMessage = "Cannot override the 'formaction' attribute for <input>. An <input> with a specified " +
|
||||
"'formaction' must not have attributes starting with 'asp-route-' or an " +
|
||||
"'asp-action', 'asp-controller', 'asp-area', or 'asp-route' attribute.";
|
||||
|
||||
var context = new TagHelperContext(
|
||||
allAttributes: new TagHelperAttributeList(
|
||||
Enumerable.Empty<TagHelperAttribute>()),
|
||||
items: new Dictionary<object, object>(),
|
||||
uniqueId: "test");
|
||||
|
||||
// Act & Assert
|
||||
var ex = await Assert.ThrowsAsync<InvalidOperationException>(
|
||||
() => submitTagHelper.ProcessAsync(context, output));
|
||||
|
||||
Assert.Equal(expectedErrorMessage, ex.Message);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("Action")]
|
||||
[InlineData("Controller")]
|
||||
public async Task ProcessAsync_ThrowsIfRouteAndActionOrControllerProvided(string propertyName)
|
||||
{
|
||||
// Arrange
|
||||
var urlHelperFactory = new Mock<IUrlHelperFactory>().Object;
|
||||
|
||||
var submitTagHelper = new SubmitTagHelper(urlHelperFactory)
|
||||
{
|
||||
Route = "Default",
|
||||
};
|
||||
|
||||
typeof(SubmitTagHelper).GetProperty(propertyName).SetValue(submitTagHelper, "Home");
|
||||
var output = new TagHelperOutput(
|
||||
"input",
|
||||
attributes: new TagHelperAttributeList(),
|
||||
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(null));
|
||||
var expectedErrorMessage = "Cannot determine a 'formaction' attribute for <input>. An <input> with a specified " +
|
||||
"'asp-route' must not have an 'asp-action' or 'asp-controller' attribute.";
|
||||
|
||||
var context = new TagHelperContext(
|
||||
allAttributes: new TagHelperAttributeList(
|
||||
Enumerable.Empty<TagHelperAttribute>()),
|
||||
items: new Dictionary<object, object>(),
|
||||
uniqueId: "test");
|
||||
|
||||
// Act & Assert
|
||||
var ex = await Assert.ThrowsAsync<InvalidOperationException>(
|
||||
() => submitTagHelper.ProcessAsync(context, output));
|
||||
|
||||
Assert.Equal(expectedErrorMessage, ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue