TagHelperOutput extension methods for class value manipulation (#6565)
* Added TagHelperOutput extension methods for class value manipulation
This commit is contained in:
parent
4f18d99d02
commit
db397d812b
|
|
@ -164,6 +164,20 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
internal static string FormatFormActionTagHelper_CannotOverrideFormAction(object p0, object p1, object p2, object p3, object p4, object p5, object p6, object p7, object p8, object p9)
|
internal static string FormatFormActionTagHelper_CannotOverrideFormAction(object p0, object p1, object p2, object p3, object p4, object p5, object p6, object p7, object p8, object p9)
|
||||||
=> string.Format(CultureInfo.CurrentCulture, GetString("FormActionTagHelper_CannotOverrideFormAction"), p0, p1, p2, p3, p4, p5, p6, p7, p8, p9);
|
=> string.Format(CultureInfo.CurrentCulture, GetString("FormActionTagHelper_CannotOverrideFormAction"), p0, p1, p2, p3, p4, p5, p6, p7, p8, p9);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Value cannot contain whitespace.
|
||||||
|
/// </summary>
|
||||||
|
internal static string ArgumentCannotContainHtmlSpace
|
||||||
|
{
|
||||||
|
get => GetString("ArgumentCannotContainHtmlSpace");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Value cannot contain whitespace.
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatArgumentCannotContainHtmlSpace()
|
||||||
|
=> GetString("ArgumentCannotContainHtmlSpace");
|
||||||
|
|
||||||
private static string GetString(string name, params string[] formatterNames)
|
private static string GetString(string name, params string[] formatterNames)
|
||||||
{
|
{
|
||||||
var value = _resourceManager.GetString(name);
|
var value = _resourceManager.GetString(name);
|
||||||
|
|
|
||||||
|
|
@ -150,4 +150,7 @@
|
||||||
<data name="FormActionTagHelper_CannotOverrideFormAction" xml:space="preserve">
|
<data name="FormActionTagHelper_CannotOverrideFormAction" xml:space="preserve">
|
||||||
<value>Cannot override the '{0}' attribute for <{1}>. <{1}> elements with a specified '{0}' must not have attributes starting with '{2}' or an '{3}', '{4}', '{5}', '{6}', '{7}', '{8}' or '{9}' attribute.</value>
|
<value>Cannot override the '{0}' attribute for <{1}>. <{1}> elements with a specified '{0}' must not have attributes starting with '{2}' or an '{3}', '{4}', '{5}', '{6}', '{7}', '{8}' or '{9}' attribute.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="ArgumentCannotContainHtmlSpace" xml:space="preserve">
|
||||||
|
<value>Value cannot contain HTML space characters.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -18,6 +18,8 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class TagHelperOutputExtensions
|
public static class TagHelperOutputExtensions
|
||||||
{
|
{
|
||||||
|
private static readonly char[] SpaceChars = { '\u0020', '\u0009', '\u000A', '\u000C', '\u000D' };
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Copies a user-provided attribute from <paramref name="context"/>'s
|
/// Copies a user-provided attribute from <paramref name="context"/>'s
|
||||||
/// <see cref="TagHelperContext.AllAttributes"/> to <paramref name="tagHelperOutput"/>'s
|
/// <see cref="TagHelperContext.AllAttributes"/> to <paramref name="tagHelperOutput"/>'s
|
||||||
|
|
@ -154,6 +156,167 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds the given <paramref name="classValue"/> to the <paramref name="tagHelperOutput"/>'s
|
||||||
|
/// <see cref="TagHelperOutput.Attributes"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tagHelperOutput">The <see cref="TagHelperOutput"/> this method extends.</param>
|
||||||
|
/// <param name="classValue">The class value to add.</param>
|
||||||
|
/// <param name="htmlEncoder">The current HTML encoder.</param>
|
||||||
|
public static void AddClass(
|
||||||
|
this TagHelperOutput tagHelperOutput,
|
||||||
|
string classValue,
|
||||||
|
HtmlEncoder htmlEncoder)
|
||||||
|
{
|
||||||
|
if (tagHelperOutput == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(tagHelperOutput));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(classValue))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var encodedSpaceChars = SpaceChars.Where(x => !x.Equals('\u0020')).Select(x => htmlEncoder.Encode(x.ToString())).ToArray();
|
||||||
|
|
||||||
|
if (SpaceChars.Any(classValue.Contains) || encodedSpaceChars.Any(value => classValue.IndexOf(value, StringComparison.Ordinal) >= 0))
|
||||||
|
{
|
||||||
|
throw new ArgumentException(Resources.ArgumentCannotContainHtmlSpace, nameof(classValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tagHelperOutput.Attributes.TryGetAttribute("class", out TagHelperAttribute classAttribute))
|
||||||
|
{
|
||||||
|
tagHelperOutput.Attributes.Add("class", classValue);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var currentClassValue = ExtractClassValue(classAttribute, htmlEncoder);
|
||||||
|
|
||||||
|
var encodedClassValue = htmlEncoder.Encode(classValue);
|
||||||
|
|
||||||
|
if (string.Equals(currentClassValue, encodedClassValue, StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var arrayOfClasses = currentClassValue.Split(SpaceChars, StringSplitOptions.RemoveEmptyEntries)
|
||||||
|
.SelectMany(perhapsEncoded => perhapsEncoded.Split(encodedSpaceChars, StringSplitOptions.RemoveEmptyEntries))
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
if (arrayOfClasses.Contains(encodedClassValue, StringComparer.Ordinal))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var newClassAttribute = new TagHelperAttribute(
|
||||||
|
classAttribute.Name,
|
||||||
|
new HtmlString($"{currentClassValue} {encodedClassValue}"),
|
||||||
|
classAttribute.ValueStyle);
|
||||||
|
|
||||||
|
tagHelperOutput.Attributes.SetAttribute(newClassAttribute);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes the given <paramref name="classValue"/> from the <paramref name="tagHelperOutput"/>'s
|
||||||
|
/// <see cref="TagHelperOutput.Attributes"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tagHelperOutput">The <see cref="TagHelperOutput"/> this method extends.</param>
|
||||||
|
/// <param name="classValue">The class value to remove.</param>
|
||||||
|
/// <param name="htmlEncoder">The current HTML encoder.</param>
|
||||||
|
public static void RemoveClass(
|
||||||
|
this TagHelperOutput tagHelperOutput,
|
||||||
|
string classValue,
|
||||||
|
HtmlEncoder htmlEncoder)
|
||||||
|
{
|
||||||
|
if (tagHelperOutput == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(tagHelperOutput));
|
||||||
|
}
|
||||||
|
|
||||||
|
var encodedSpaceChars = SpaceChars.Where(x => !x.Equals('\u0020')).Select(x => htmlEncoder.Encode(x.ToString())).ToArray();
|
||||||
|
|
||||||
|
if (SpaceChars.Any(classValue.Contains) || encodedSpaceChars.Any(value => classValue.IndexOf(value, StringComparison.Ordinal) >= 0))
|
||||||
|
{
|
||||||
|
throw new ArgumentException(Resources.ArgumentCannotContainHtmlSpace, nameof(classValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tagHelperOutput.Attributes.TryGetAttribute("class", out TagHelperAttribute classAttribute))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var currentClassValue = ExtractClassValue(classAttribute, htmlEncoder);
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(currentClassValue))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var encodedClassValue = htmlEncoder.Encode(classValue);
|
||||||
|
|
||||||
|
if (string.Equals(currentClassValue, encodedClassValue, StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
tagHelperOutput.Attributes.Remove(tagHelperOutput.Attributes["class"]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!currentClassValue.Contains(encodedClassValue))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var listOfClasses = currentClassValue.Split(SpaceChars, StringSplitOptions.RemoveEmptyEntries)
|
||||||
|
.SelectMany(perhapsEncoded => perhapsEncoded.Split(encodedSpaceChars, StringSplitOptions.RemoveEmptyEntries))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
if (!listOfClasses.Contains(encodedClassValue))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
listOfClasses.RemoveAll(x => x.Equals(encodedClassValue));
|
||||||
|
|
||||||
|
if (listOfClasses.Any())
|
||||||
|
{
|
||||||
|
var joinedClasses = new HtmlString(string.Join(" ", listOfClasses));
|
||||||
|
tagHelperOutput.Attributes.SetAttribute(classAttribute.Name, joinedClasses);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tagHelperOutput.Attributes.Remove(tagHelperOutput.Attributes["class"]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string ExtractClassValue(
|
||||||
|
TagHelperAttribute classAttribute,
|
||||||
|
HtmlEncoder htmlEncoder)
|
||||||
|
{
|
||||||
|
string extractedClassValue;
|
||||||
|
switch (classAttribute.Value)
|
||||||
|
{
|
||||||
|
case string valueAsString:
|
||||||
|
extractedClassValue = htmlEncoder.Encode(valueAsString);
|
||||||
|
break;
|
||||||
|
case HtmlString valueAsHtmlString:
|
||||||
|
extractedClassValue = valueAsHtmlString.Value;
|
||||||
|
break;
|
||||||
|
case IHtmlContent htmlContent:
|
||||||
|
using (var stringWriter = new StringWriter())
|
||||||
|
{
|
||||||
|
htmlContent.WriteTo(stringWriter, htmlEncoder);
|
||||||
|
extractedClassValue = stringWriter.ToString();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
extractedClassValue = htmlEncoder.Encode(classAttribute.Value?.ToString());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
var currentClassValue = extractedClassValue ?? string.Empty;
|
||||||
|
return currentClassValue;
|
||||||
|
}
|
||||||
|
|
||||||
private static void CopyHtmlAttribute(
|
private static void CopyHtmlAttribute(
|
||||||
int allAttributeIndex,
|
int allAttributeIndex,
|
||||||
TagHelperOutput tagHelperOutput,
|
TagHelperOutput tagHelperOutput,
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,12 @@ using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||||
|
using Microsoft.AspNetCore.Html;
|
||||||
using Microsoft.AspNetCore.Razor.TagHelpers;
|
using Microsoft.AspNetCore.Razor.TagHelpers;
|
||||||
using Microsoft.AspNetCore.Razor.TagHelpers.Testing;
|
using Microsoft.AspNetCore.Razor.TagHelpers.Testing;
|
||||||
using Microsoft.AspNetCore.Testing;
|
using Microsoft.AspNetCore.Testing;
|
||||||
|
using Microsoft.Extensions.WebEncoders.Testing;
|
||||||
|
using Microsoft.AspNetCore.Mvc.TestCommon;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
|
|
@ -968,5 +971,184 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
attribute = Assert.Single(tagHelperOutput.Attributes, attr => attr.Name.Equals("for"));
|
attribute = Assert.Single(tagHelperOutput.Attributes, attr => attr.Name.Equals("for"));
|
||||||
Assert.Equal(expectedBuilderAttribute.Value, attribute.Value);
|
Assert.Equal(expectedBuilderAttribute.Value, attribute.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Single_AddClass()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var expectedValue = "class=\"HtmlEncode[[btn]]\"";
|
||||||
|
var htmlEncoder = new HtmlTestEncoder();
|
||||||
|
|
||||||
|
var tagHelperOutput = new TagHelperOutput(
|
||||||
|
tagName: "p",
|
||||||
|
attributes: new TagHelperAttributeList(),
|
||||||
|
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(
|
||||||
|
new DefaultTagHelperContent()));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
tagHelperOutput.AddClass("btn", htmlEncoder);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var classAttribute = Assert.Single(tagHelperOutput.Attributes, attr => attr.Name.Equals("class"));
|
||||||
|
Assert.Equal(expectedValue, HtmlContentUtilities.HtmlContentToString(classAttribute));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Multiple_AddClass()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var expectedValue = "class=\"HtmlEncode[[btn]] HtmlEncode[[btn-primary]]\"";
|
||||||
|
var htmlEncoder = new HtmlTestEncoder();
|
||||||
|
|
||||||
|
var tagHelperOutput = new TagHelperOutput(
|
||||||
|
tagName: "p",
|
||||||
|
attributes: new TagHelperAttributeList(),
|
||||||
|
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(
|
||||||
|
new DefaultTagHelperContent()));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
tagHelperOutput.AddClass("btn", htmlEncoder);
|
||||||
|
tagHelperOutput.AddClass("btn-primary", htmlEncoder);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var classAttribute = Assert.Single(tagHelperOutput.Attributes, attr => attr.Name.Equals("class"));
|
||||||
|
Assert.Equal(expectedValue, HtmlContentUtilities.HtmlContentToString(classAttribute));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Multiple_AddClass_RemoveClass_RemovesAllButOne()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var expectedValue = "class=\"HtmlEncode[[btn]]\"";
|
||||||
|
var htmlEncoder = new HtmlTestEncoder();
|
||||||
|
|
||||||
|
var tagHelperOutput = new TagHelperOutput(
|
||||||
|
tagName: "p",
|
||||||
|
attributes: new TagHelperAttributeList(),
|
||||||
|
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(
|
||||||
|
new DefaultTagHelperContent()));
|
||||||
|
|
||||||
|
tagHelperOutput.AddClass("btn", htmlEncoder);
|
||||||
|
tagHelperOutput.AddClass("btn-success", htmlEncoder);
|
||||||
|
tagHelperOutput.AddClass("btn-primary", htmlEncoder);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
tagHelperOutput.RemoveClass("btn-success", htmlEncoder);
|
||||||
|
tagHelperOutput.RemoveClass("btn-primary", htmlEncoder);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var classAttribute = Assert.Single(tagHelperOutput.Attributes, attr => attr.Name.Equals("class"));
|
||||||
|
Assert.Equal(expectedValue, HtmlContentUtilities.HtmlContentToString(classAttribute));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AddClass_RemoveClass_ContainsSpace()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var classValue = "btn btn-success";
|
||||||
|
var expected = new ArgumentException(Resources.ArgumentCannotContainHtmlSpace, nameof(classValue)).Message;
|
||||||
|
var htmlEncoder = new HtmlTestEncoder();
|
||||||
|
|
||||||
|
var tagHelperOutput = new TagHelperOutput(
|
||||||
|
tagName: "p",
|
||||||
|
attributes: new TagHelperAttributeList(),
|
||||||
|
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(
|
||||||
|
new DefaultTagHelperContent()));
|
||||||
|
|
||||||
|
// Act and Assert
|
||||||
|
var exceptionAdd = Assert.Throws<ArgumentException>(() => tagHelperOutput.AddClass(classValue, htmlEncoder));
|
||||||
|
var exceptionRemove = Assert.Throws<ArgumentException>(() => tagHelperOutput.RemoveClass(classValue, htmlEncoder));
|
||||||
|
Assert.Equal(expected, exceptionAdd.Message);
|
||||||
|
Assert.Equal(expected, exceptionRemove.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Single_RemoveClass_RemovesDuplicates_RemovesEntirely()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var htmlEncoder = new HtmlTestEncoder();
|
||||||
|
|
||||||
|
var tagHelperOutput = new TagHelperOutput(
|
||||||
|
tagName: "p",
|
||||||
|
attributes: new TagHelperAttributeList(),
|
||||||
|
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(
|
||||||
|
new DefaultTagHelperContent()));
|
||||||
|
|
||||||
|
tagHelperOutput.Attributes.SetAttribute("class", new HtmlString("HtmlEncode[[btn]] HtmlEncode[[btn]]"));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
tagHelperOutput.RemoveClass("btn", htmlEncoder);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var classAttribute = tagHelperOutput.Attributes["class"];
|
||||||
|
Assert.Null(classAttribute);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Single_RemoveClass_RemovesDuplicates()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var expectedValue = "class=\"HtmlEncode[[btn-primary]]\"";
|
||||||
|
var htmlEncoder = new HtmlTestEncoder();
|
||||||
|
|
||||||
|
var tagHelperOutput = new TagHelperOutput(
|
||||||
|
tagName: "p",
|
||||||
|
attributes: new TagHelperAttributeList(),
|
||||||
|
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(
|
||||||
|
new DefaultTagHelperContent()));
|
||||||
|
|
||||||
|
tagHelperOutput.Attributes.SetAttribute("class", new HtmlString("HtmlEncode[[btn]] HtmlEncode[[btn-primary]] HtmlEncode[[btn]]"));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
tagHelperOutput.RemoveClass("btn", htmlEncoder);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var classAttribute = Assert.Single(tagHelperOutput.Attributes, attr => attr.Name.Equals("class"));
|
||||||
|
Assert.Equal(expectedValue, HtmlContentUtilities.HtmlContentToString(classAttribute));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Single_RemoveClass_RemovesEntirely()
|
||||||
|
{
|
||||||
|
var htmlEncoder = new HtmlTestEncoder();
|
||||||
|
|
||||||
|
var tagHelperOutput = new TagHelperOutput(
|
||||||
|
tagName: "p",
|
||||||
|
attributes: new TagHelperAttributeList(),
|
||||||
|
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(
|
||||||
|
new DefaultTagHelperContent()));
|
||||||
|
|
||||||
|
tagHelperOutput.Attributes.SetAttribute("class", new HtmlString("HtmlEncode[[btn]]"));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
tagHelperOutput.RemoveClass("btn", htmlEncoder);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var classAttribute = tagHelperOutput.Attributes["class"];
|
||||||
|
Assert.Null(classAttribute);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Single_RemoveClass()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var expectedValue = "class=\"HtmlEncode[[btn]]\"";
|
||||||
|
var htmlEncoder = new HtmlTestEncoder();
|
||||||
|
|
||||||
|
var tagHelperOutput = new TagHelperOutput(
|
||||||
|
tagName: "p",
|
||||||
|
attributes: new TagHelperAttributeList(),
|
||||||
|
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(
|
||||||
|
new DefaultTagHelperContent()));
|
||||||
|
|
||||||
|
tagHelperOutput.Attributes.SetAttribute("class", new HtmlString("HtmlEncode[[btn]] HtmlEncode[[btn-primary]]"));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
tagHelperOutput.RemoveClass("btn-primary", htmlEncoder);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var classAttribute = Assert.Single(tagHelperOutput.Attributes, attr => attr.Name.Equals("class"));
|
||||||
|
Assert.Equal(expectedValue, HtmlContentUtilities.HtmlContentToString(classAttribute));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue