Make `TagHelperAttribute` immutable.

- Remove a lot of the ugly generic work done in `TagHelperAttributeList`/`ReadOnlyTagHelperAttributeList`.
- Removed error cases where we'd check on addition of `TagHelperAttribute`s that their `name` was not `null`. This was due to the pre-existing `string` indexer for `TagHelperAttributeList`. It no longer sets `TagHelperAttribute`s directly, instead it's `[key] = value` (instead of `[key] = new TagHelperAttribute(...))`.
- Updated tests to account for new immutability/string indexer format.
- Removed copy ctor from `TagHelperAttribute` since its immutability kills the value.
- Changed `ReadOnlyTagHelperAttributeList` to inherit from `ReadOnlyCollection`

#604
This commit is contained in:
N. Taylor Mullen 2016-01-29 15:46:12 -08:00
parent 49c553ec7b
commit fc99082ea8
20 changed files with 373 additions and 722 deletions

View File

@ -362,38 +362,6 @@ namespace Microsoft.AspNetCore.Razor.Runtime
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidAttributePrefixNotNull"), p0, p1, p2, p3, p4);
}
/// <summary>
/// Cannot add a '{0}' with a null '{1}'.
/// </summary>
internal static string TagHelperAttributeList_CannotAddWithNullName
{
get { return GetString("TagHelperAttributeList_CannotAddWithNullName"); }
}
/// <summary>
/// Cannot add a '{0}' with a null '{1}'.
/// </summary>
internal static string FormatTagHelperAttributeList_CannotAddWithNullName(object p0, object p1)
{
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperAttributeList_CannotAddWithNullName"), p0, p1);
}
/// <summary>
/// Cannot add a {0} with inconsistent names. The {1} property '{2}' must match the location '{3}'.
/// </summary>
internal static string TagHelperAttributeList_CannotAddAttribute
{
get { return GetString("TagHelperAttributeList_CannotAddAttribute"); }
}
/// <summary>
/// Cannot add a {0} with inconsistent names. The {1} property '{2}' must match the location '{3}'.
/// </summary>
internal static string FormatTagHelperAttributeList_CannotAddAttribute(object p0, object p1, object p2, object p3)
{
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperAttributeList_CannotAddAttribute"), p0, p1, p2, p3);
}
/// <summary>
/// Invalid '{0}' tag name '{1}' for tag helper '{2}'. Tag helpers cannot restrict child elements that contain a '{3}' character.
/// </summary>

View File

@ -183,12 +183,6 @@
<data name="TagHelperDescriptorFactory_InvalidAttributePrefixNotNull" xml:space="preserve">
<value>Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must be null unless property type implements '{4}'.</value>
</data>
<data name="TagHelperAttributeList_CannotAddWithNullName" xml:space="preserve">
<value>Cannot add a '{0}' with a null '{1}'.</value>
</data>
<data name="TagHelperAttributeList_CannotAddAttribute" xml:space="preserve">
<value>Cannot add a {0} with inconsistent names. The {1} property '{2}' must match the location '{3}'.</value>
</data>
<data name="TagHelperDescriptorFactory_InvalidRestrictChildrenAttributeName" xml:space="preserve">
<value>Invalid '{0}' tag name '{1}' for tag helper '{2}'. Tag helpers cannot restrict child elements that contain a '{3}' character.</value>
</data>

View File

@ -94,7 +94,7 @@ namespace Microsoft.AspNetCore.Razor.Runtime.TagHelpers
_endTagHelperWritingScope = endTagHelperWritingScope;
TagMode = tagMode;
HTMLAttributes = new TagHelperAttributeList();
HtmlAttributes = new TagHelperAttributeList();
AllAttributes = new TagHelperAttributeList();
TagName = tagName;
Items = items;
@ -125,7 +125,7 @@ namespace Microsoft.AspNetCore.Razor.Runtime.TagHelpers
/// <summary>
/// HTML attributes.
/// </summary>
public TagHelperAttributeList HTMLAttributes { get; }
public TagHelperAttributeList HtmlAttributes { get; }
/// <summary>
/// <see cref="ITagHelper"/> bound attributes and HTML attributes.
@ -173,7 +173,7 @@ namespace Microsoft.AspNetCore.Razor.Runtime.TagHelpers
}
/// <summary>
/// Tracks the minimized HTML attribute in <see cref="AllAttributes"/> and <see cref="HTMLAttributes"/>.
/// Tracks the minimized HTML attribute in <see cref="AllAttributes"/> and <see cref="HtmlAttributes"/>.
/// </summary>
/// <param name="name">The minimized HTML attribute name.</param>
public void AddMinimizedHtmlAttribute(string name)
@ -183,22 +183,13 @@ namespace Microsoft.AspNetCore.Razor.Runtime.TagHelpers
throw new ArgumentNullException(nameof(name));
}
HTMLAttributes.Add(
new TagHelperAttribute
{
Name = name,
Minimized = true
});
AllAttributes.Add(
new TagHelperAttribute
{
Name = name,
Minimized = true
});
var attribute = new TagHelperAttribute(name);
HtmlAttributes.Add(attribute);
AllAttributes.Add(attribute);
}
/// <summary>
/// Tracks the HTML attribute in <see cref="AllAttributes"/> and <see cref="HTMLAttributes"/>.
/// Tracks the HTML attribute in <see cref="AllAttributes"/> and <see cref="HtmlAttributes"/>.
/// </summary>
/// <param name="name">The HTML attribute name.</param>
/// <param name="value">The HTML attribute value.</param>
@ -209,7 +200,7 @@ namespace Microsoft.AspNetCore.Razor.Runtime.TagHelpers
throw new ArgumentNullException(nameof(name));
}
HTMLAttributes.Add(name, value);
HtmlAttributes.Add(name, value);
AllAttributes.Add(name, value);
}

View File

@ -41,7 +41,7 @@ namespace Microsoft.AspNetCore.Razor.Runtime.TagHelpers
var tagHelperOutput = new TagHelperOutput(
executionContext.TagName,
executionContext.HTMLAttributes,
executionContext.HtmlAttributes,
executionContext.GetChildContentAsync)
{
TagMode = executionContext.TagMode,

View File

@ -1,29 +0,0 @@
// 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;
namespace Microsoft.AspNetCore.Razor.TagHelpers
{
/// <summary>
/// A read-only HTML tag helper attribute.
/// </summary>
public interface IReadOnlyTagHelperAttribute : IEquatable<IReadOnlyTagHelperAttribute>
{
/// <summary>
/// Gets the name of the attribute.
/// </summary>
string Name { get; }
/// <summary>
/// Gets the value of the attribute.
/// </summary>
object Value { get; }
/// <summary>
/// Gets an indication whether the attribute is minimized or not.
/// </summary>
/// <remarks>If <c>true</c>, <see cref="Value"/> will be ignored.</remarks>
bool Minimized { get; }
}
}

View File

@ -2,73 +2,52 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
namespace Microsoft.AspNetCore.Razor.TagHelpers
{
/// <summary>
/// A read-only collection of <typeparamref name="TAttribute"/>s.
/// A read-only collection of <see cref="TagHelperAttribute"/>s.
/// </summary>
/// <typeparam name="TAttribute">
/// The type of <see cref="IReadOnlyTagHelperAttribute"/>s in the collection.
/// <typeparam name="TagHelperAttribute">
/// The type of <see cref="TagHelperAttribute"/>s in the collection.
/// </typeparam>
public class ReadOnlyTagHelperAttributeList<TAttribute> : IReadOnlyList<TAttribute>
where TAttribute : IReadOnlyTagHelperAttribute
public abstract class ReadOnlyTagHelperAttributeList : ReadOnlyCollection<TagHelperAttribute>
{
private static readonly IReadOnlyList<TAttribute> EmptyList =
#if NET451
new TAttribute[0];
#else
Array.Empty<TAttribute>();
#endif
private static readonly IReadOnlyList<TagHelperAttribute> EmptyList = new TagHelperAttribute[0];
/// <summary>
/// Instantiates a new instance of <see cref="ReadOnlyTagHelperAttributeList{TAttribute}"/> with an empty
/// Instantiates a new instance of <see cref="ReadOnlyTagHelperAttributeList"/> with an empty
/// collection.
/// </summary>
protected ReadOnlyTagHelperAttributeList()
: base(new List<TagHelperAttribute>())
{
Attributes = new List<TAttribute>();
}
/// <summary>
/// Instantiates a new instance of <see cref="ReadOnlyTagHelperAttributeList{TAttribute}"/> with the specified
/// Instantiates a new instance of <see cref="ReadOnlyTagHelperAttributeList"/> with the specified
/// <paramref name="attributes"/>.
/// </summary>
/// <param name="attributes">The collection to wrap.</param>
public ReadOnlyTagHelperAttributeList(IEnumerable<TAttribute> attributes)
public ReadOnlyTagHelperAttributeList(IEnumerable<TagHelperAttribute> attributes)
: base(new List<TagHelperAttribute>(attributes))
{
if (attributes == null)
{
throw new ArgumentNullException(nameof(attributes));
}
Attributes = new List<TAttribute>(attributes);
}
/// <summary>
/// The underlying collection of <typeparamref name="TAttribute"/>s.
/// </summary>
/// <remarks>Intended for use in a non-read-only subclass. Changes to this <see cref="List{TAttribute}"/> will
/// affect all getters that <see cref="ReadOnlyTagHelperAttributeList{TAttribute}"/> provides.</remarks>
protected List<TAttribute> Attributes { get; }
/// <inheritdoc />
public TAttribute this[int index] => Attributes[index];
/// <summary>
/// Gets the first <typeparamref name="TAttribute"/> with <see cref="IReadOnlyTagHelperAttribute.Name"/>
/// Gets the first <see cref="TagHelperAttribute"/> with <see cref="TagHelperAttribute.Name"/>
/// matching <paramref name="name"/>.
/// </summary>
/// <param name="name">
/// The <see cref="IReadOnlyTagHelperAttribute.Name"/> of the <typeparamref name="TAttribute"/> to get.
/// The <see cref="TagHelperAttribute.Name"/> of the <see cref="TagHelperAttribute"/> to get.
/// </param>
/// <returns>The first <typeparamref name="TAttribute"/> with <see cref="IReadOnlyTagHelperAttribute.Name"/>
/// <returns>The first <see cref="TagHelperAttribute"/> with <see cref="TagHelperAttribute.Name"/>
/// matching <paramref name="name"/>.
/// </returns>
/// <remarks><paramref name="name"/> is compared case-insensitively.</remarks>
public TAttribute this[string name]
public TagHelperAttribute this[string name]
{
get
{
@ -78,106 +57,47 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
}
// Perf: Avoid allocating enumerator
for (var i = 0; i < Attributes.Count; i++)
for (var i = 0; i < Items.Count; i++)
{
if (NameEquals(name, Attributes[i]))
if (NameEquals(name, Items[i]))
{
return Attributes[i];
return Items[i];
}
}
return default(TAttribute);
return null;
}
}
/// <inheritdoc />
public int Count => Attributes.Count;
/// <summary>
/// Determines whether a <typeparamref name="TAttribute"/> matching <paramref name="item"/> exists in the
/// collection.
/// Determines whether a <see cref="TagHelperAttribute"/> with <see cref="TagHelperAttribute.Name"/>
/// matching <paramref name="name"/> exists in the collection.
/// </summary>
/// <param name="item">The <typeparamref name="TAttribute"/> to locate.</param>
/// <param name="name">The <see cref="TagHelperAttribute.Name"/> of the
/// <see cref="TagHelperAttribute"/> to get.</param>
/// <returns>
/// <c>true</c> if an <typeparamref name="TAttribute"/> matching <paramref name="item"/> exists in the
/// collection; otherwise, <c>false</c>.
/// </returns>
/// <remarks>
/// <paramref name="item"/>s <see cref="IReadOnlyTagHelperAttribute.Name"/> is compared case-insensitively.
/// </remarks>
public bool Contains(TAttribute item)
{
if (item == null)
{
throw new ArgumentNullException(nameof(item));
}
return Attributes.Contains(item);
}
/// <summary>
/// Determines whether a <typeparamref name="TAttribute"/> with the same
/// <see cref="IReadOnlyTagHelperAttribute.Name"/> exists in the collection.
/// </summary>
/// <param name="name">The <see cref="IReadOnlyTagHelperAttribute.Name"/> of the
/// <typeparamref name="TAttribute"/> to get.</param>
/// <returns>
/// <c>true</c> if a <typeparamref name="TAttribute"/> with the same
/// <see cref="IReadOnlyTagHelperAttribute.Name"/> exists in the collection; otherwise, <c>false</c>.
/// <c>true</c> if a <see cref="TagHelperAttribute"/> with the same
/// <see cref="TagHelperAttribute.Name"/> exists in the collection; otherwise, <c>false</c>.
/// </returns>
/// <remarks><paramref name="name"/> is compared case-insensitively.</remarks>
public bool ContainsName(string name)
{
if (name == null)
{
throw new ArgumentNullException(nameof(name));
}
// Perf: Avoid allocating enumerator
for (var i = 0; i < Attributes.Count; i++)
{
if (NameEquals(name, Attributes[i]))
{
return true;
}
}
return false;
return this[name] != null;
}
/// <summary>
/// Searches for a <typeparamref name="TAttribute"/> matching <paramref name="item"/> in the collection and
/// returns the zero-based index of the first occurrence.
/// </summary>
/// <param name="item">The <typeparamref name="TAttribute"/> to locate.</param>
/// <returns>The zero-based index of the first occurrence of a <typeparamref name="TAttribute"/> matching
/// <paramref name="item"/> in the collection, if found; otherwise, 1.</returns>
/// <remarks>
/// <paramref name="item"/>s <see cref="IReadOnlyTagHelperAttribute.Name"/> is compared case-insensitively.
/// </remarks>
public int IndexOf(TAttribute item)
{
if (item == null)
{
throw new ArgumentNullException(nameof(item));
}
return Attributes.IndexOf(item);
}
/// <summary>
/// Retrieves the first <typeparamref name="TAttribute"/> with <see cref="IReadOnlyTagHelperAttribute.Name"/>
/// Retrieves the first <see cref="TagHelperAttribute"/> with <see cref="TagHelperAttribute.Name"/>
/// matching <paramref name="name"/>.
/// </summary>
/// <param name="name">The <see cref="IReadOnlyTagHelperAttribute.Name"/> of the
/// <typeparamref name="TAttribute"/> to get.</param>
/// <param name="attribute">When this method returns, the first <typeparamref name="TAttribute"/> with
/// <see cref="IReadOnlyTagHelperAttribute.Name"/> matching <paramref name="name"/>, if found; otherwise,
/// <param name="name">The <see cref="TagHelperAttribute.Name"/> of the
/// <see cref="TagHelperAttribute"/> to get.</param>
/// <param name="attribute">When this method returns, the first <see cref="TagHelperAttribute"/> with
/// <see cref="TagHelperAttribute.Name"/> matching <paramref name="name"/>, if found; otherwise,
/// <c>null</c>.</param>
/// <returns><c>true</c> if a <typeparamref name="TAttribute"/> with the same
/// <see cref="IReadOnlyTagHelperAttribute.Name"/> exists in the collection; otherwise, <c>false</c>.</returns>
/// <returns><c>true</c> if a <see cref="TagHelperAttribute"/> with the same
/// <see cref="TagHelperAttribute.Name"/> exists in the collection; otherwise, <c>false</c>.</returns>
/// <remarks><paramref name="name"/> is compared case-insensitively.</remarks>
public bool TryGetAttribute(string name, out TAttribute attribute)
public bool TryGetAttribute(string name, out TagHelperAttribute attribute)
{
if (name == null)
{
@ -190,17 +110,17 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
}
/// <summary>
/// Retrieves <typeparamref name="TAttribute"/>s in the collection with
/// <see cref="IReadOnlyTagHelperAttribute.Name"/> matching <paramref name="name"/>.
/// Retrieves <see cref="TagHelperAttribute"/>s in the collection with
/// <see cref="TagHelperAttribute.Name"/> matching <paramref name="name"/>.
/// </summary>
/// <param name="name">The <see cref="IReadOnlyTagHelperAttribute.Name"/> of the
/// <typeparamref name="TAttribute"/>s to get.</param>
/// <param name="attributes">When this method returns, the <typeparamref name="TAttribute"/>s with
/// <see cref="IReadOnlyTagHelperAttribute.Name"/> matching <paramref name="name"/>.</param>
/// <returns><c>true</c> if at least one <typeparamref name="TAttribute"/> with the same
/// <see cref="IReadOnlyTagHelperAttribute.Name"/> exists in the collection; otherwise, <c>false</c>.</returns>
/// <param name="name">The <see cref="TagHelperAttribute.Name"/> of the
/// <see cref="TagHelperAttribute"/>s to get.</param>
/// <param name="attributes">When this method returns, the <see cref="TagHelperAttribute"/>s with
/// <see cref="TagHelperAttribute.Name"/> matching <paramref name="name"/>.</param>
/// <returns><c>true</c> if at least one <see cref="TagHelperAttribute"/> with the same
/// <see cref="TagHelperAttribute.Name"/> exists in the collection; otherwise, <c>false</c>.</returns>
/// <remarks><paramref name="name"/> is compared case-insensitively.</remarks>
public bool TryGetAttributes(string name, out IReadOnlyList<TAttribute> attributes)
public bool TryGetAttributes(string name, out IReadOnlyList<TagHelperAttribute> attributes)
{
if (name == null)
{
@ -208,17 +128,17 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
}
// Perf: Avoid allocating enumerator
List<TAttribute> matchedAttributes = null;
for (var i = 0; i < Attributes.Count; i++)
List<TagHelperAttribute> matchedAttributes = null;
for (var i = 0; i < Items.Count; i++)
{
if (NameEquals(name, Attributes[i]))
if (NameEquals(name, Items[i]))
{
if (matchedAttributes == null)
{
matchedAttributes = new List<TAttribute>();
matchedAttributes = new List<TagHelperAttribute>();
}
matchedAttributes.Add(Attributes[i]);
matchedAttributes.Add(Items[i]);
}
}
attributes = matchedAttributes ?? EmptyList;
@ -226,16 +146,30 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
return matchedAttributes != null;
}
/// <inheritdoc />
IEnumerator IEnumerable.GetEnumerator()
/// <summary>
/// Searches for a <see cref="TagHelperAttribute"/> who's <see cref="TagHelperAttribute.Name"/>
/// case-insensitively matches <paramref name="name"/> and returns the zero-based index of the first
/// occurrence.
/// </summary>
/// <param name="name">The <see cref="TagHelperAttribute.Name"/> to locate in the collection.</param>
/// <returns>The zero-based index of the first matching <see cref="TagHelperAttribute"/> within the collection,
/// if found; otherwise, -1.</returns>
public int IndexOfName(string name)
{
return GetEnumerator();
}
if (name == null)
{
throw new ArgumentNullException(nameof(name));
}
/// <inheritdoc />
public IEnumerator<TAttribute> GetEnumerator()
{
return Attributes.GetEnumerator();
for (var i = 0; i < Items.Count; i++)
{
if (NameEquals(name, Items[i]))
{
return i;
}
}
return -1;
}
/// <summary>
@ -246,7 +180,7 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
/// <param name="attribute">The attribute to compare against.</param>
/// <returns><c>true</c> if <paramref name="name"/> case-insensitively matches <paramref name="attribute"/>s
/// <see cref="TagHelperAttribute.Name"/>.</returns>
protected static bool NameEquals(string name, TAttribute attribute)
protected static bool NameEquals(string name, TagHelperAttribute attribute)
{
if (attribute == null)
{

View File

@ -2,83 +2,77 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.Extensions.Internal;
namespace Microsoft.AspNetCore.Razor.TagHelpers
{
/// <summary>
/// An HTML tag helper attribute.
/// </summary>
public class TagHelperAttribute : IReadOnlyTagHelperAttribute
public class TagHelperAttribute
{
private static readonly int TypeHashCode = typeof(TagHelperAttribute).GetHashCode();
/// <summary>
/// Instantiates a new instance of <see cref="TagHelperAttribute"/>.
/// Instantiates a new instance of <see cref="TagHelperAttribute"/> with the specified <paramref name="name"/>.
/// <see cref="Minimized"/> is set to <c>true</c> and <see cref="Value"/> to <c>null</c>.
/// </summary>
public TagHelperAttribute()
/// <param name="name">The <see cref="Name"/> of the attribute.</param>
public TagHelperAttribute(string name)
: this(name, value: null, minimized: true)
{
}
/// <summary>
/// Instantiates a new instance of <see cref="TagHelperAttribute" /> with values provided by the given
/// <paramref name="attribute"/>.
/// </summary>
/// <param name="attribute">A <see cref="IReadOnlyTagHelperAttribute"/> whose values should be copied.</param>
public TagHelperAttribute(IReadOnlyTagHelperAttribute attribute)
: this(attribute?.Name, attribute?.Value)
{
if (attribute == null)
{
throw new ArgumentNullException(nameof(attribute));
}
Minimized = attribute.Minimized;
}
/// <summary>
/// Instantiates a new instance of <see cref="TagHelperAttribute"/> with the specified <paramref name="name"/>
/// and <paramref name="value"/>.
/// and <paramref name="value"/>. <see cref="Minimized"/> is set to <c>false</c>.
/// </summary>
/// <param name="name">The <see cref="Name"/> of the attribute.</param>
/// <param name="value">The <see cref="Value"/> of the attribute.</param>
public TagHelperAttribute(string name, object value)
: this(name, value, minimized: false)
{
}
/// <summary>
/// Instantiates a new instance of <see cref="TagHelperAttribute"/> with the specified <paramref name="name"/>,
/// <paramref name="value"/> and <paramref name="minimized"/>.
/// </summary>
/// <param name="name">The <see cref="Name"/> of the new instance.</param>
/// <param name="value">The <see cref="Value"/> of the new instance.</param>
/// <param name="minimized">The <see cref="Minimized"/> value of the new instance.</param>
/// <remarks>If <paramref name="minimized"/> is <c>true</c>, <paramref name="value"/> is ignored when this
/// instance is rendered.</remarks>
public TagHelperAttribute(string name, object value, bool minimized)
{
if (name == null)
{
throw new ArgumentNullException(nameof(name));
}
Name = name;
Value = value;
Minimized = minimized;
}
/// <summary>
/// Gets or sets the name of the attribute.
/// Gets the name of the attribute.
/// </summary>
public string Name { get; set; }
public string Name { get; }
/// <summary>
/// Gets or sets the value of the attribute.
/// Gets the value of the attribute.
/// </summary>
public object Value { get; set; }
public object Value { get; }
/// <summary>
/// Gets or sets an indication whether the attribute is minimized or not.
/// Gets an indication whether the attribute is minimized or not.
/// </summary>
/// <remarks>If <c>true</c>, <see cref="Value"/> will be ignored.</remarks>
public bool Minimized { get; set; }
public bool Minimized { get; }
/// <summary>
/// Converts the specified <paramref name="value"/> into a <see cref="TagHelperAttribute"/>.
/// </summary>
/// <param name="value">The <see cref="Value"/> of the created <see cref="TagHelperAttribute"/>.</param>
/// <remarks>Created <see cref="TagHelperAttribute"/>s <see cref="Name"/> is set to <c>null</c>.</remarks>
public static implicit operator TagHelperAttribute(string value)
{
return new TagHelperAttribute
{
Value = value
};
}
/// <inheritdoc />
/// <remarks><see cref="Name"/> is compared case-insensitively.</remarks>
public bool Equals(IReadOnlyTagHelperAttribute other)
public bool Equals(TagHelperAttribute other)
{
return
other != null &&
@ -90,7 +84,7 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
/// <inheritdoc />
public override bool Equals(object obj)
{
var other = obj as IReadOnlyTagHelperAttribute;
var other = obj as TagHelperAttribute;
return Equals(other);
}
@ -98,7 +92,12 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
/// <inheritdoc />
public override int GetHashCode()
{
return TypeHashCode;
var hashCodeCombiner = HashCodeCombiner.Start();
hashCodeCombiner.Add(Name, StringComparer.Ordinal);
hashCodeCombiner.Add(Value);
hashCodeCombiner.Add(Minimized);
return hashCodeCombiner.CombinedHash;
}
}
}

View File

@ -3,14 +3,13 @@
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Razor.Runtime;
namespace Microsoft.AspNetCore.Razor.TagHelpers
{
/// <summary>
/// A collection of <see cref="TagHelperAttribute"/>s.
/// </summary>
public class TagHelperAttributeList : ReadOnlyTagHelperAttributeList<TagHelperAttribute>, IList<TagHelperAttribute>
public class TagHelperAttributeList : ReadOnlyTagHelperAttributeList, IList<TagHelperAttribute>
{
/// <summary>
/// Instantiates a new instance of <see cref="TagHelperAttributeList"/> with an empty collection.
@ -51,123 +50,76 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
throw new ArgumentNullException(nameof(value));
}
if (value.Name == null)
{
throw new ArgumentException(
Resources.FormatTagHelperAttributeList_CannotAddWithNullName(
typeof(TagHelperAttribute).FullName,
nameof(TagHelperAttribute.Name)),
nameof(value));
}
Attributes[index] = value;
Items[index] = value;
}
}
/// <summary>
/// Gets the first <see cref="TagHelperAttribute"/> with <see cref="TagHelperAttribute.Name"/> matching
/// <paramref name="name"/>. When setting, replaces the first matching
/// <see cref="TagHelperAttribute"/> with the specified <paramref name="value"/> and removes any additional
/// matching <see cref="TagHelperAttribute"/>s. If a matching <see cref="TagHelperAttribute"/> is not found,
/// adds the specified <paramref name="value"/> to the end of the collection.
/// </summary>
/// Replaces the first <see cref="TagHelperAttribute"/> with <see cref="TagHelperAttribute.Name"/> matching
/// <paramref name="name"/> and removes any additional matching <see cref="TagHelperAttribute"/>s. If a
/// matching <see cref="TagHelperAttribute"/> is not found, adds a <see cref="TagHelperAttribute"/> with
/// <paramref name="name"/> and <paramref name="value"/> to the end of the collection.</summary>
/// <param name="name">
/// The <see cref="TagHelperAttribute.Name"/> of the <see cref="TagHelperAttribute"/> to get or set.
/// The <see cref="TagHelperAttribute.Name"/> of the <see cref="TagHelperAttribute"/> to set.
/// </param>
/// <returns>The first <see cref="TagHelperAttribute"/> with <see cref="TagHelperAttribute.Name"/> matching
/// <paramref name="name"/>.
/// </returns>
/// <remarks><paramref name="name"/> is compared case-insensitively. When setting,
/// <see cref="TagHelperAttribute"/>s <see cref="TagHelperAttribute.Name"/> must be <c>null</c> or
/// case-insensitively match the specified <paramref name="name"/>.</remarks>
/// <example>
/// <code>
/// var attributes = new TagHelperAttributeList();
///
/// // Will "value" be converted to a TagHelperAttribute with a null Name
/// attributes["name"] = "value";
///
/// // TagHelperAttribute.Name must match the specified name.
/// attributes["name"] = new TagHelperAttribute("name", "value");
/// </code>
/// </example>
public new TagHelperAttribute this[string name]
/// <param name="value">
/// The <see cref="TagHelperAttribute.Value"/> to set.
/// </param>
/// <remarks><paramref name="name"/> is compared case-insensitively.</remarks>
public void SetAttribute(string name, object value)
{
get
{
if (name == null)
{
throw new ArgumentNullException(nameof(name));
}
var attribute = new TagHelperAttribute(name, value);
SetAttribute(attribute);
}
return base[name];
/// <summary>
/// Replaces the first <see cref="TagHelperAttribute"/> with <see cref="TagHelperAttribute.Name"/> matching
/// <paramref name="attribute"/>'s <see cref="TagHelperAttribute.Name"/> and removes any additional matching
/// <see cref="TagHelperAttribute"/>s. If a matching <see cref="TagHelperAttribute"/> is not found, adds the
/// specified <paramref name="attribute"/> to the end of the collection.
/// </summary>
/// <param name="attribute">
/// The <see cref="TagHelperAttribute"/> to set.
/// </param>
/// <remarks><paramref name="attribute"/>'s <see cref="TagHelperAttribute.Name"/> is compared
/// case-insensitively.</remarks>
public void SetAttribute(TagHelperAttribute attribute)
{
if (attribute == null)
{
throw new ArgumentNullException(nameof(attribute));
}
set
var attributeReplaced = false;
// Perf: Avoid allocating enumerator
for (var i = 0; i < Items.Count; i++)
{
if (name == null)
if (NameEquals(attribute.Name, Items[i]))
{
throw new ArgumentNullException(nameof(name));
}
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
// Name will be null if user attempts to set the attribute via an implicit conversion:
// output.Attributes["someName"] = "someValue"
if (value.Name == null)
{
value.Name = name;
}
else if (!NameEquals(name, value))
{
throw new ArgumentException(
Resources.FormatTagHelperAttributeList_CannotAddAttribute(
nameof(TagHelperAttribute),
nameof(TagHelperAttribute.Name),
value.Name,
name),
nameof(name));
}
var attributeReplaced = false;
// Perf: Avoid allocating enumerator
for (var i = 0; i < Attributes.Count; i++)
{
if (NameEquals(name, Attributes[i]))
// We replace the first attribute with the provided attribute, remove all the rest.
if (!attributeReplaced)
{
// We replace the first attribute with the provided value, remove all the rest.
if (!attributeReplaced)
{
// We replace the first attribute we find with the same name.
Attributes[i] = value;
attributeReplaced = true;
}
else
{
Attributes.RemoveAt(i--);
}
// We replace the first attribute we find with the same name.
Items[i] = attribute;
attributeReplaced = true;
}
else
{
Items.RemoveAt(i--);
}
}
}
// If we didn't replace an attribute value we should add value to the end of the collection.
if (!attributeReplaced)
{
Add(value);
}
// If we didn't replace an attribute value we should add value to the end of the collection.
if (!attributeReplaced)
{
Add(attribute);
}
}
/// <inheritdoc />
bool ICollection<TagHelperAttribute>.IsReadOnly
{
get
{
return false;
}
}
bool ICollection<TagHelperAttribute>.IsReadOnly => false;
/// <summary>
/// Adds a <see cref="TagHelperAttribute"/> to the end of the collection with the specified
@ -177,18 +129,11 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
/// <param name="value">The <see cref="TagHelperAttribute.Value"/> of the attribute to add.</param>
public void Add(string name, object value)
{
if (name == null)
{
throw new ArgumentNullException(nameof(name));
}
Attributes.Add(new TagHelperAttribute(name, value));
var attribute = new TagHelperAttribute(name, value);
Items.Add(attribute);
}
/// <inheritdoc />
/// <remarks>
/// <paramref name="attribute"/>'s <see cref="TagHelperAttribute.Name"/> must not be <c>null</c>.
/// </remarks>
public void Add(TagHelperAttribute attribute)
{
if (attribute == null)
@ -196,22 +141,10 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
throw new ArgumentNullException(nameof(attribute));
}
if (attribute.Name == null)
{
throw new ArgumentException(
Resources.FormatTagHelperAttributeList_CannotAddWithNullName(
typeof(TagHelperAttribute).FullName,
nameof(TagHelperAttribute.Name)),
nameof(attribute));
}
Attributes.Add(attribute);
Items.Add(attribute);
}
/// <inheritdoc />
/// <remarks>
/// <paramref name="attribute"/>'s <see cref="TagHelperAttribute.Name"/> must not be <c>null</c>.
/// </remarks>
public void Insert(int index, TagHelperAttribute attribute)
{
if (attribute == null)
@ -219,27 +152,7 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
throw new ArgumentNullException(nameof(attribute));
}
if (attribute.Name == null)
{
throw new ArgumentException(
Resources.FormatTagHelperAttributeList_CannotAddWithNullName(
typeof(TagHelperAttribute).FullName,
nameof(TagHelperAttribute.Name)),
nameof(attribute));
}
Attributes.Insert(index, attribute);
}
/// <inheritdoc />
public void CopyTo(TagHelperAttribute[] array, int index)
{
if (array == null)
{
throw new ArgumentNullException(nameof(array));
}
Attributes.CopyTo(array, index);
Items.Insert(index, attribute);
}
/// <inheritdoc />
@ -253,13 +166,13 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
throw new ArgumentNullException(nameof(attribute));
}
return Attributes.Remove(attribute);
return Items.Remove(attribute);
}
/// <inheritdoc />
public void RemoveAt(int index)
{
Attributes.RemoveAt(index);
Items.RemoveAt(index);
}
/// <summary>
@ -282,11 +195,11 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
// Perf: Avoid allocating enumerator
var removedAtLeastOne = false;
for (var i = Attributes.Count - 1; i >= 0; i--)
for (var i = Items.Count - 1; i >= 0; i--)
{
if (NameEquals(name, Attributes[i]))
if (NameEquals(name, Items[i]))
{
Attributes.RemoveAt(i);
Items.RemoveAt(i);
removedAtLeastOne = true;
}
}
@ -297,7 +210,7 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
/// <inheritdoc />
public void Clear()
{
Attributes.Clear();
Items.Clear();
}
}
}

View File

@ -11,8 +11,8 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
/// </summary>
public class TagHelperContext
{
private ReadOnlyTagHelperAttributeList<IReadOnlyTagHelperAttribute> _allAttributes;
private IEnumerable<IReadOnlyTagHelperAttribute> _allAttributesData;
private ReadOnlyTagHelperAttributeList _allAttributes;
private IEnumerable<TagHelperAttribute> _allAttributesData;
/// <summary>
/// Instantiates a new <see cref="TagHelperContext"/>.
@ -22,7 +22,7 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
/// <param name="uniqueId">The unique identifier for the source element this <see cref="TagHelperContext" />
/// applies to.</param>
public TagHelperContext(
IEnumerable<IReadOnlyTagHelperAttribute> allAttributes,
IEnumerable<TagHelperAttribute> allAttributes,
IDictionary<object, object> items,
string uniqueId)
{
@ -49,13 +49,13 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
/// <summary>
/// Every attribute associated with the current HTML element.
/// </summary>
public ReadOnlyTagHelperAttributeList<IReadOnlyTagHelperAttribute> AllAttributes
public ReadOnlyTagHelperAttributeList AllAttributes
{
get
{
if (_allAttributes == null)
{
_allAttributes = new ReadOnlyTagHelperAttributeList<IReadOnlyTagHelperAttribute>(_allAttributesData);
_allAttributes = new TagHelperAttributeList(_allAttributesData);
}
return _allAttributes;

View File

@ -20,6 +20,10 @@
"Microsoft.Extensions.CopyOnWriteDictionary.Sources": {
"type": "build",
"version": "1.0.0-*"
},
"Microsoft.Extensions.HashCodeCombiner.Sources": {
"type": "build",
"version": "1.0.0-*"
}
},
"frameworks": {
@ -28,12 +32,7 @@
"System.Xml": "4.0.0.0",
"System.Xml.Linq": "4.0.0.0"
},
"dependencies": {
"Microsoft.Extensions.HashCodeCombiner.Sources": {
"type": "build",
"version": "1.0.0-*"
}
}
"dependencies": {}
},
"dotnet5.4": {
"dependencies": {

View File

@ -244,7 +244,7 @@ namespace Microsoft.AspNetCore.Razor.CodeGenerators
var renderedBoundAttributeNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
// Go through the HTML attributes in source order, assigning to properties or indexers or adding to
// TagHelperExecutionContext.HTMLAttributes' as we go.
// TagHelperExecutionContext.HtmlAttributes' as we go.
foreach (var attribute in chunkAttributes)
{
var attributeName = attribute.Key;

View File

@ -6,7 +6,7 @@ using System.Collections.Generic;
namespace Microsoft.AspNetCore.Razor.TagHelpers
{
public class CaseSensitiveTagHelperAttributeComparer : IEqualityComparer<IReadOnlyTagHelperAttribute>
public class CaseSensitiveTagHelperAttributeComparer : IEqualityComparer<TagHelperAttribute>
{
public readonly static CaseSensitiveTagHelperAttributeComparer Default =
new CaseSensitiveTagHelperAttributeComparer();
@ -15,7 +15,7 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
{
}
public bool Equals(IReadOnlyTagHelperAttribute attributeX, IReadOnlyTagHelperAttribute attributeY)
public bool Equals(TagHelperAttribute attributeX, TagHelperAttribute attributeY)
{
if (attributeX == attributeY)
{
@ -29,7 +29,7 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
(attributeX.Minimized || Equals(attributeX.Value, attributeY.Value));
}
public int GetHashCode(IReadOnlyTagHelperAttribute attribute)
public int GetHashCode(TagHelperAttribute attribute)
{
return attribute.GetHashCode();
}

View File

@ -289,13 +289,13 @@ namespace Microsoft.AspNetCore.Razor.Runtime.TagHelpers
{
// Arrange
var executionContext = new TagHelperExecutionContext("p", TagMode.StartTagAndEndTag);
executionContext.HTMLAttributes[originalName] = "hello";
executionContext.HtmlAttributes.SetAttribute(originalName, "hello");
// Act
executionContext.HTMLAttributes[updatedName] = "something else";
executionContext.HtmlAttributes.SetAttribute(updatedName, "something else");
// Assert
var attribute = Assert.Single(executionContext.HTMLAttributes);
var attribute = Assert.Single(executionContext.HtmlAttributes);
Assert.Equal(new TagHelperAttribute(originalName, "something else"), attribute);
}
@ -305,10 +305,10 @@ namespace Microsoft.AspNetCore.Razor.Runtime.TagHelpers
{
// Arrange
var executionContext = new TagHelperExecutionContext("p", tagMode: TagMode.StartTagAndEndTag);
executionContext.AllAttributes.Add(originalName, value: false);
executionContext.AllAttributes.SetAttribute(originalName, value: false);
// Act
executionContext.AllAttributes[updatedName].Value = true;
executionContext.AllAttributes.SetAttribute(updatedName, true);
// Assert
var attribute = Assert.Single(executionContext.AllAttributes);
@ -316,7 +316,7 @@ namespace Microsoft.AspNetCore.Razor.Runtime.TagHelpers
}
[Fact]
public void AddHtmlAttribute_MaintainsHTMLAttributes()
public void AddHtmlAttribute_MaintainsHtmlAttributes()
{
// Arrange
var executionContext = new TagHelperExecutionContext("p", TagMode.StartTagAndEndTag);
@ -333,19 +333,19 @@ namespace Microsoft.AspNetCore.Razor.Runtime.TagHelpers
// Assert
Assert.Equal(
expectedAttributes,
executionContext.HTMLAttributes,
executionContext.HtmlAttributes,
CaseSensitiveTagHelperAttributeComparer.Default);
}
[Fact]
public void AddMinimizedHtmlAttribute_MaintainsHTMLAttributes()
public void AddMinimizedHtmlAttribute_MaintainsHtmlAttributes()
{
// Arrange
var executionContext = new TagHelperExecutionContext("input", tagMode: TagMode.StartTagOnly);
var expectedAttributes = new TagHelperAttributeList
{
["checked"] = new TagHelperAttribute { Name = "checked", Minimized = true },
["visible"] = new TagHelperAttribute { Name = "visible", Minimized = true }
new TagHelperAttribute("checked"),
new TagHelperAttribute("visible"),
};
// Act
@ -355,12 +355,12 @@ namespace Microsoft.AspNetCore.Razor.Runtime.TagHelpers
// Assert
Assert.Equal(
expectedAttributes,
executionContext.HTMLAttributes,
executionContext.HtmlAttributes,
CaseSensitiveTagHelperAttributeComparer.Default);
}
[Fact]
public void AddMinimizedHtmlAttribute_MaintainsHTMLAttributes_SomeMinimized()
public void AddMinimizedHtmlAttribute_MaintainsHtmlAttributes_SomeMinimized()
{
// Arrange
var executionContext = new TagHelperExecutionContext("input", tagMode: TagMode.SelfClosing);
@ -369,8 +369,8 @@ namespace Microsoft.AspNetCore.Razor.Runtime.TagHelpers
{ "class", "btn" },
{ "foo", "bar" }
};
expectedAttributes.Add(new TagHelperAttribute { Name = "checked", Minimized = true });
expectedAttributes.Add(new TagHelperAttribute { Name = "visible", Minimized = true });
expectedAttributes.Add(new TagHelperAttribute(name: "checked"));
expectedAttributes.Add(new TagHelperAttribute(name: "visible"));
// Act
executionContext.AddHtmlAttribute("class", "btn");
@ -381,7 +381,7 @@ namespace Microsoft.AspNetCore.Razor.Runtime.TagHelpers
// Assert
Assert.Equal(
expectedAttributes,
executionContext.HTMLAttributes,
executionContext.HtmlAttributes,
CaseSensitiveTagHelperAttributeComparer.Default);
}

View File

@ -216,10 +216,10 @@ namespace Microsoft.AspNetCore.Razor.Runtime.TagHelpers
output.TagName = "foo";
TagHelperAttribute classAttribute;
if (output.Attributes.TryGetAttribute("class", out classAttribute))
var classIndex = output.Attributes.IndexOfName("class");
if (classIndex != -1)
{
classAttribute.Value = "somethingelse";
output.Attributes[classIndex] = new TagHelperAttribute("class", "somethingelse");
}
output.Attributes.Add("hello", "world");

View File

@ -10,6 +10,50 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
{
public class ReadOnlyTagHelperAttributeListTest
{
public static TheoryData IndexOfNameData
{
get
{
var first = new TagHelperAttribute("First", "First Value");
var second = new TagHelperAttribute("Second", "Second Value");
var third = new TagHelperAttribute("Third", "Third Value");
return new TheoryData<
IEnumerable<TagHelperAttribute>, // initialAttributes
string, // nameToLookup
int> // expectedIndex
{
{ new[] { first }, first.Name, 0 },
{ new[] { first, second }, first.Name, 0 },
{ new[] { first, second }, second.Name.ToUpper(), 1 },
{ new[] { first, second, third}, second.Name, 1 },
{ new[] { first, second, third }, third.Name.ToLower(), 2 },
{ new[] { first, first, second, third}, first.Name, 0 },
// Bad lookups
{ new[] { first, second, third}, "bad", -1 },
{ new[] { first, first, first}, first.Name + "bad", -1 },
};
}
}
[Theory]
[MemberData(nameof(IndexOfNameData))]
public void IndexOfName_ReturnsExpectedValue(
IEnumerable<TagHelperAttribute> initialAttributes,
string nameToLookup,
int expectedIndex)
{
// Arrange
var attributes = new TagHelperAttributeList(initialAttributes);
// Act
var index = attributes.IndexOfName(nameToLookup);
// Assert
Assert.Equal(expectedIndex, index);
}
public static TheoryData IntIndexerData
{
get
@ -19,9 +63,9 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
var third = new TagHelperAttribute("Third", "Third Value");
return new TheoryData<
IEnumerable<IReadOnlyTagHelperAttribute>, // initialAttributes
IEnumerable<TagHelperAttribute>, // initialAttributes
int, // indexToLookup
IReadOnlyTagHelperAttribute> // expectedAttribute
TagHelperAttribute> // expectedAttribute
{
{ new[] { first }, 0, first },
{ new[] { first, second }, 0, first },
@ -36,12 +80,12 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
[Theory]
[MemberData(nameof(IntIndexerData))]
public void IntIndexer_ReturnsExpectedAttribute(
IEnumerable<IReadOnlyTagHelperAttribute> initialAttributes,
IEnumerable<TagHelperAttribute> initialAttributes,
int indexToLookup,
IReadOnlyTagHelperAttribute expectedAttribute)
TagHelperAttribute expectedAttribute)
{
// Arrange
var attributes = new ReadOnlyTagHelperAttributeList<IReadOnlyTagHelperAttribute>(initialAttributes);
var attributes = new TagHelperAttributeList(initialAttributes);
// Act
var attribute = attributes[indexToLookup];
@ -63,7 +107,7 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
public void IntIndexer_ThrowsIfInvalidIndex(int index)
{
// Arrange
var attributes = new ReadOnlyTagHelperAttributeList<IReadOnlyTagHelperAttribute>(
var attributes = new TagHelperAttributeList(
new[]
{
new TagHelperAttribute("a", "av"),
@ -85,9 +129,9 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
var C = new TagHelperAttribute("CName", "CName Value");
return new TheoryData<
IEnumerable<IReadOnlyTagHelperAttribute>, // initialAttributes
IEnumerable<TagHelperAttribute>, // initialAttributes
string, // nameToLookup
IReadOnlyTagHelperAttribute> // expectedAttribute
TagHelperAttribute> // expectedAttribute
{
{ new[] { A }, "AName", A },
{ new[] { A }, "AnAmE", A },
@ -128,12 +172,12 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
[Theory]
[MemberData(nameof(StringIndexerData))]
public void StringIndexer_ReturnsExpectedAttribute(
IEnumerable<IReadOnlyTagHelperAttribute> initialAttributes,
IEnumerable<TagHelperAttribute> initialAttributes,
string nameToLookup,
IReadOnlyTagHelperAttribute expectedAttribute)
TagHelperAttribute expectedAttribute)
{
// Arrange
var attributes = new ReadOnlyTagHelperAttributeList<IReadOnlyTagHelperAttribute>(initialAttributes);
var attributes = new TagHelperAttributeList(initialAttributes);
// Act
var attribute = attributes[nameToLookup];
@ -146,12 +190,12 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
public void Count_ReturnsNumberOfAttributes()
{
// Arrange
var attributes = new ReadOnlyTagHelperAttributeList<IReadOnlyTagHelperAttribute>(
var attributes = new TagHelperAttributeList(
new[]
{
new TagHelperAttribute(),
new TagHelperAttribute(),
new TagHelperAttribute()
new TagHelperAttribute("A"),
new TagHelperAttribute("B"),
new TagHelperAttribute("C")
});
// Act
@ -172,8 +216,8 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
var C = new TagHelperAttribute("CName", "CName Value");
return new TheoryData<
IEnumerable<IReadOnlyTagHelperAttribute>, // initialAttributes
IReadOnlyTagHelperAttribute, // attributeToLookup
IEnumerable<TagHelperAttribute>, // initialAttributes
TagHelperAttribute, // attributeToLookup
bool> // expected
{
{ new[] { A }, A, true },
@ -255,12 +299,12 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
[Theory]
[MemberData(nameof(ContainsData))]
public void Contains_ReturnsExpectedResult(
IEnumerable<IReadOnlyTagHelperAttribute> initialAttributes,
IReadOnlyTagHelperAttribute attributeToLookup,
IEnumerable<TagHelperAttribute> initialAttributes,
TagHelperAttribute attributeToLookup,
bool expected)
{
// Arrange
var attributes = new ReadOnlyTagHelperAttributeList<IReadOnlyTagHelperAttribute>(initialAttributes);
var attributes = new TagHelperAttributeList(initialAttributes);
// Act
var contains = attributes.Contains(attributeToLookup);
@ -280,7 +324,7 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
var C = new TagHelperAttribute("CName", "CName Value");
return new TheoryData<
IEnumerable<IReadOnlyTagHelperAttribute>, // initialAttributes
IEnumerable<TagHelperAttribute>, // initialAttributes
string, // nameToLookup
bool> // expected
{
@ -331,12 +375,12 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
[Theory]
[MemberData(nameof(ContainsNameData))]
public void ContainsName_ReturnsExpectedResult(
IEnumerable<IReadOnlyTagHelperAttribute> initialAttributes,
IEnumerable<TagHelperAttribute> initialAttributes,
string nameToLookup,
bool expected)
{
// Arrange
var attributes = new ReadOnlyTagHelperAttributeList<IReadOnlyTagHelperAttribute>(initialAttributes);
var attributes = new TagHelperAttributeList(initialAttributes);
// Act
var contains = attributes.ContainsName(nameToLookup);
@ -356,8 +400,8 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
var C = new TagHelperAttribute("CName", "CName Value");
return new TheoryData<
IEnumerable<IReadOnlyTagHelperAttribute>, // initialAttributes
IReadOnlyTagHelperAttribute, // attributeToLookup
IEnumerable<TagHelperAttribute>, // initialAttributes
TagHelperAttribute, // attributeToLookup
int> // expected
{
{ new[] { A }, A, 0 },
@ -439,12 +483,12 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
[Theory]
[MemberData(nameof(IndexOfData))]
public void IndexOf_ReturnsExpectedResult(
IEnumerable<IReadOnlyTagHelperAttribute> initialAttributes,
IReadOnlyTagHelperAttribute attributeToLookup,
IEnumerable<TagHelperAttribute> initialAttributes,
TagHelperAttribute attributeToLookup,
int expected)
{
// Arrange
var attributes = new ReadOnlyTagHelperAttributeList<IReadOnlyTagHelperAttribute>(initialAttributes);
var attributes = new TagHelperAttributeList(initialAttributes);
// Act
var index = attributes.IndexOf(attributeToLookup);
@ -464,9 +508,9 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
var C = new TagHelperAttribute("CName", "CName Value");
return new TheoryData<
IEnumerable<IReadOnlyTagHelperAttribute>, // initialAttributes
IEnumerable<TagHelperAttribute>, // initialAttributes
string, // nameToLookup
IReadOnlyTagHelperAttribute, // expectedAttribute
TagHelperAttribute, // expectedAttribute
bool> // expectedResult
{
{ new[] { A }, "AName", A, true },
@ -508,14 +552,14 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
[Theory]
[MemberData(nameof(TryGetAttributeData))]
public void TryGetAttribute_ReturnsExpectedValueAndAttribute(
IEnumerable<IReadOnlyTagHelperAttribute> initialAttributes,
IEnumerable<TagHelperAttribute> initialAttributes,
string nameToLookup,
IReadOnlyTagHelperAttribute expectedAttribute,
TagHelperAttribute expectedAttribute,
bool expectedResult)
{
// Arrange
var attributes = new ReadOnlyTagHelperAttributeList<IReadOnlyTagHelperAttribute>(initialAttributes);
IReadOnlyTagHelperAttribute attribute;
var attributes = new TagHelperAttributeList(initialAttributes);
TagHelperAttribute attribute;
// Act
var result = attributes.TryGetAttribute(nameToLookup, out attribute);
@ -536,9 +580,9 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
var C = new TagHelperAttribute("CName", "CName Value");
return new TheoryData<
IEnumerable<IReadOnlyTagHelperAttribute>, // initialAttributes
IEnumerable<TagHelperAttribute>, // initialAttributes
string, // nameToLookup
IEnumerable<IReadOnlyTagHelperAttribute>, // expectedAttributes
IEnumerable<TagHelperAttribute>, // expectedAttributes
bool> // expectedResult
{
{ new[] { A }, "AName", new[] { A }, true },
@ -563,16 +607,16 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
{ new[] { C, B, A3, A }, "AName", new[] { A3, A }, true },
// Null expected lookups
{ new[] { A }, "_AName_", Enumerable.Empty<IReadOnlyTagHelperAttribute>(), false },
{ new[] { A }, "completely different", Enumerable.Empty<IReadOnlyTagHelperAttribute>(), false },
{ new[] { A, B }, "_AName_", Enumerable.Empty<IReadOnlyTagHelperAttribute>(), false },
{ new[] { A, B }, "completely different", Enumerable.Empty<IReadOnlyTagHelperAttribute>(), false },
{ new[] { A, B, C }, "_BName_", Enumerable.Empty<IReadOnlyTagHelperAttribute>(), false },
{ new[] { A, B, C }, "way different", Enumerable.Empty<IReadOnlyTagHelperAttribute>(), false },
{ new[] { A, A2, B, C }, "_cnamE_", Enumerable.Empty<IReadOnlyTagHelperAttribute>(), false },
{ new[] { A, A2, B, C }, "way different", Enumerable.Empty<IReadOnlyTagHelperAttribute>(), false },
{ new[] { A, A2, A3, B, C }, "_cnamE_", Enumerable.Empty<IReadOnlyTagHelperAttribute>(), false },
{ new[] { A, A2, A3, B, C }, "different", Enumerable.Empty<IReadOnlyTagHelperAttribute>(), false },
{ new[] { A }, "_AName_", Enumerable.Empty<TagHelperAttribute>(), false },
{ new[] { A }, "completely different", Enumerable.Empty<TagHelperAttribute>(), false },
{ new[] { A, B }, "_AName_", Enumerable.Empty<TagHelperAttribute>(), false },
{ new[] { A, B }, "completely different", Enumerable.Empty<TagHelperAttribute>(), false },
{ new[] { A, B, C }, "_BName_", Enumerable.Empty<TagHelperAttribute>(), false },
{ new[] { A, B, C }, "way different", Enumerable.Empty<TagHelperAttribute>(), false },
{ new[] { A, A2, B, C }, "_cnamE_", Enumerable.Empty<TagHelperAttribute>(), false },
{ new[] { A, A2, B, C }, "way different", Enumerable.Empty<TagHelperAttribute>(), false },
{ new[] { A, A2, A3, B, C }, "_cnamE_", Enumerable.Empty<TagHelperAttribute>(), false },
{ new[] { A, A2, A3, B, C }, "different", Enumerable.Empty<TagHelperAttribute>(), false },
};
}
}
@ -580,14 +624,14 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
[Theory]
[MemberData(nameof(TryGetAttributesData))]
public void TryGetAttributes_ReturnsExpectedValueAndAttribute(
IEnumerable<IReadOnlyTagHelperAttribute> initialAttributes,
IEnumerable<TagHelperAttribute> initialAttributes,
string nameToLookup,
IEnumerable<IReadOnlyTagHelperAttribute> expectedAttributes,
IEnumerable<TagHelperAttribute> expectedAttributes,
bool expectedResult)
{
// Arrange
var attributes = new ReadOnlyTagHelperAttributeList<IReadOnlyTagHelperAttribute>(initialAttributes);
IReadOnlyList<IReadOnlyTagHelperAttribute> resolvedAttributes;
var attributes = new TagHelperAttributeList(initialAttributes);
IReadOnlyList<TagHelperAttribute> resolvedAttributes;
// Act
var result = attributes.TryGetAttributes(nameToLookup, out resolvedAttributes);
@ -632,7 +676,7 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
public void ModifyingUnderlyingAttributes_AffectsExposedAttributes()
{
// Arrange
var attributes = new TestableReadOnlyTagHelperAttributes(Enumerable.Empty<IReadOnlyTagHelperAttribute>());
var attributes = new TestableReadOnlyTagHelperAttributes(Enumerable.Empty<TagHelperAttribute>());
var expectedAttributes = new[]
{
new TagHelperAttribute("A", "AV"),
@ -650,12 +694,12 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
[Theory]
[MemberData(nameof(IntIndexerData))]
public void ModifyingUnderlyingAttributes_IntIndexer_ReturnsExpectedResult(
IEnumerable<IReadOnlyTagHelperAttribute> initialAttributes,
IEnumerable<TagHelperAttribute> initialAttributes,
int indexToLookup,
IReadOnlyTagHelperAttribute expectedAttribute)
TagHelperAttribute expectedAttribute)
{
// Arrange
var attributes = new TestableReadOnlyTagHelperAttributes(Enumerable.Empty<IReadOnlyTagHelperAttribute>());
var attributes = new TestableReadOnlyTagHelperAttributes(Enumerable.Empty<TagHelperAttribute>());
attributes.PublicAttributes.AddRange(initialAttributes);
// Act
@ -668,12 +712,12 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
[Theory]
[MemberData(nameof(StringIndexerData))]
public void ModifyingUnderlyingAttributes_StringIndexer_ReturnsExpectedResult(
IEnumerable<IReadOnlyTagHelperAttribute> initialAttributes,
IEnumerable<TagHelperAttribute> initialAttributes,
string nameToLookup,
IReadOnlyTagHelperAttribute expectedAttribute)
TagHelperAttribute expectedAttribute)
{
// Arrange
var attributes = new TestableReadOnlyTagHelperAttributes(Enumerable.Empty<IReadOnlyTagHelperAttribute>());
var attributes = new TestableReadOnlyTagHelperAttributes(Enumerable.Empty<TagHelperAttribute>());
attributes.PublicAttributes.AddRange(initialAttributes);
// Act
@ -683,18 +727,18 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
Assert.Equal(expectedAttribute, attribute, CaseSensitiveTagHelperAttributeComparer.Default);
}
private class TestableReadOnlyTagHelperAttributes : ReadOnlyTagHelperAttributeList<IReadOnlyTagHelperAttribute>
private class TestableReadOnlyTagHelperAttributes : ReadOnlyTagHelperAttributeList
{
public TestableReadOnlyTagHelperAttributes(IEnumerable<IReadOnlyTagHelperAttribute> attributes)
public TestableReadOnlyTagHelperAttributes(IEnumerable<TagHelperAttribute> attributes)
: base(attributes)
{
}
public List<IReadOnlyTagHelperAttribute> PublicAttributes
public List<TagHelperAttribute> PublicAttributes
{
get
{
return Attributes;
return (List<TagHelperAttribute>)Items;
}
}
}

View File

@ -133,64 +133,49 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
get
{
var A = new TagHelperAttribute("AName", "AName Value");
var ASet = new TagHelperAttribute("aname", "AName Set Value");
var ASet1 = new TagHelperAttribute("AName", "AName Set Value");
var ASet2 = new TagHelperAttribute("AnAmE", "AName Set Value");
var A2 = new TagHelperAttribute("aname", "AName Second Value");
var A2Set = new TagHelperAttribute("aname", "AName Second Set Value");
var A3 = new TagHelperAttribute("AName", "AName Third Value");
var A3Set = new TagHelperAttribute("aname", "AName Third Set Value");
var B = new TagHelperAttribute("BName", "BName Value");
var BSet = new TagHelperAttribute("bname", "BName Set Value");
var BSet1 = new TagHelperAttribute("BName", "BName Set Value");
var BSet2 = new TagHelperAttribute("BnAmE", "BName Set Value");
var C = new TagHelperAttribute("CName", "CName Value");
var CSet = new TagHelperAttribute("cname", "CName Set Value");
var CSet1 = new TagHelperAttribute("CName", "CName Set Value");
var CSet2 = new TagHelperAttribute("cnamE", "CName Set Value");
var set = new TagHelperAttribute("Set", "Set Value");
return new TheoryData<
IEnumerable<TagHelperAttribute>, // initialAttributes
string, // keyToSet
TagHelperAttribute, // setValue
object, // setValue
IEnumerable<TagHelperAttribute>> // expectedAttributes
{
{ new[] { A }, "AName", ASet, new[] { ASet } },
{ new[] { A }, "AnAmE", ASet, new[] { ASet } },
{ new[] { A }, "AnAmE", "AV", new[] { new TagHelperAttribute("AnAmE", "AV") } },
{ new[] { A, B }, "AName", ASet, new[] { ASet, B } },
{ new[] { A, B }, "AnAmE", ASet, new[] { ASet, B } },
{ new[] { A, B }, "AnAmE", "AV", new[] { new TagHelperAttribute("AnAmE", "AV"), B } },
{ new[] { A, B }, "BName", BSet, new[] { A, BSet } },
{ new[] { A, B }, "BnAmE", BSet, new[] { A, BSet } },
{ new[] { A, B }, "BnAmE", "BV", new[] { A, new TagHelperAttribute("BnAmE", "BV") } },
{ new[] { A, B, C }, "BName", BSet, new[] { A, BSet, C } },
{ new[] { A, B, C }, "bname", BSet, new[] { A, BSet, C } },
{ new[] { A, B, C }, "bname", "BV", new[] { A, new TagHelperAttribute("bname", "BV"), C } },
{ new[] { A, B, C }, "CName", CSet, new[] { A, B, CSet } },
{ new[] { A, B, C }, "cnamE", CSet, new[] { A, B, CSet } },
{ new[] { A, B, C }, "cnamE", "CV", new[] { A, B, new TagHelperAttribute("cnamE", "CV") } },
{ Enumerable.Empty<TagHelperAttribute>(), "Set", set, new[] { set } },
{ new[] { B }, "Set", set, new[] { B, set } },
{ new[] { B }, "Set", "Set Value", new[] { B, set } },
{ new[] { A, B }, "Set", set, new[] { A, B, set } },
{ new[] { A, B }, "Set", "Set Value", new[] { A, B, set } },
{ new[] { A }, "AName", ASet1.Value, new[] { ASet1 } },
{ new[] { A }, "AnAmE", ASet2.Value, new[] { ASet2 } },
{ new[] { A, B }, "AName", ASet1.Value, new[] { ASet1, B } },
{ new[] { A, B }, "AnAmE", ASet2.Value, new[] { ASet2, B } },
{ new[] { A, B }, "BName", BSet1.Value, new[] { A, BSet1 } },
{ new[] { A, B }, "BnAmE", BSet2.Value, new[] { A, BSet2 } },
{ new[] { A, B, C }, "BName", BSet1.Value, new[] { A, BSet1, C } },
{ new[] { A, B, C }, "BnAmE", BSet2.Value, new[] { A, BSet2, C } },
{ new[] { A, B, C }, "CName", CSet1.Value, new[] { A, B, CSet1 } },
{ new[] { A, B, C }, "cnamE", CSet2.Value, new[] { A, B, CSet2 } },
{ Enumerable.Empty<TagHelperAttribute>(), "Set", set.Value, new[] { set } },
{ new[] { B }, "Set", set.Value, new[] { B, set } },
{ new[] { A, B }, "Set", set.Value, new[] { A, B, set } },
// Multiple elements same name
{ new[] { A, B, A2, C }, "AName", ASet, new[] { ASet, B, C } },
{ new[] { A, B, A2, C }, "aname", ASet, new[] { ASet, B, C } },
{ new[] { A, B, A2, C }, "aname", "av", new[] { new TagHelperAttribute("aname", "av"), B, C } },
{ new[] { B, A2, A }, "aname", A2Set, new[] { B, A2Set } },
{ new[] { B, A2, A }, "aname", "av", new[] { B, new TagHelperAttribute("aname", "av") } },
{ new[] { B, A2, A, C }, "AName", A2Set, new[] { B, A2Set, C } },
{ new[] { B, A2, A, C }, "AName", "av", new[] { B, new TagHelperAttribute("AName", "av"), C } },
{ new[] { A, A3 }, "AName", ASet, new[] { ASet } },
{ new[] { A, A3 }, "AName", "av", new[] { new TagHelperAttribute("AName", "av") } },
{ new[] { A3, A }, "aname", A3Set, new[] { A3Set } },
{ new[] { A3, A }, "aname", "av", new[] { new TagHelperAttribute("aname", "av") } },
{ new[] { A, A2, A3 }, "AName", ASet, new[] { ASet } },
{ new[] { A, A2, A3 }, "AName", "av", new[] { new TagHelperAttribute("AName", "av") } },
{ new[] { C, B, A3, A }, "AName", A3Set, new[] { C, B, A3Set } },
{ new[] { C, B, A3, A }, "AName", "av", new[] { C, B, new TagHelperAttribute("AName", "av") } },
{ new[] { A, A2, A3 }, "BNamE", BSet, new[] { A, A2, A3, BSet } },
{ new[] { A, A2, A3 }, "bname", "BName Set Value", new[] { A, A2, A3, BSet } },
{ new[] { A, A2, A3, B, C }, "Set", set, new[] { A, A2, A3, B, C, set } },
{ new[] { A, A2, A3, B, C }, "Set", "Set Value", new[] { A, A2, A3, B, C, set } },
{ new[] { A, B, A2, C }, "AName", ASet1.Value, new[] { ASet1, B, C } },
{ new[] { A, B, A2, C }, "AnAmE", ASet2.Value, new[] { ASet2, B, C } },
{ new[] { B, A2, A }, "AName", ASet1.Value, new[] { B, ASet1 } },
{ new[] { B, A2, A, C }, "AnAmE", ASet2.Value, new[] { B, ASet2, C } },
{ new[] { A, A3 }, "aname", A3Set.Value, new[] { A3Set } },
{ new[] { A3, A }, "aname", A3Set.Value, new[] { A3Set } },
{ new[] { A, A2, A3 }, "AName", ASet1.Value, new[] { ASet1 } },
{ new[] { A, A2, A3 }, "BName", BSet1.Value, new[] { A, A2, A3, BSet1 } },
{ new[] { A, A2, A3, B, C }, "Set", set.Value, new[] { A, A2, A3, B, C, set } },
};
}
}
@ -200,14 +185,14 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
public void StringIndexer_SetsAttributeAtExpectedLocation(
IEnumerable<TagHelperAttribute> initialAttributes,
string keyToSet,
TagHelperAttribute setValue,
object setValue,
IEnumerable<TagHelperAttribute> expectedAttributes)
{
// Arrange
var attributes = new TagHelperAttributeList(initialAttributes);
// Act
attributes[keyToSet] = setValue;
attributes.SetAttribute(keyToSet, setValue);
// Assert
Assert.Equal(expectedAttributes, attributes, CaseSensitiveTagHelperAttributeComparer.Default);
@ -230,42 +215,6 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
});
}
public static TheoryData StringIndexerSetterThrowData
{
get
{
// attributes
return new TheoryData<TagHelperAttributeList>
{
{ new TagHelperAttributeList() },
{ new TagHelperAttributeList { { "something", "a value" } } },
{ new TagHelperAttributeList { { "somethingelse", "a value" } } },
{ new TagHelperAttributeList { { "SomethingElse", "a value" } } },
{ new TagHelperAttributeList { { "something", "a value" }, { "somethingelse", "a value" } } },
{ new TagHelperAttributeList { { "SomethingElse", "a value" }, { "somethingelse", "a value" } } }
};
}
}
[Theory]
[MemberData(nameof(StringIndexerSetterThrowData))]
public void StringIndexer_Setter_ThrowsIfUnmatchingKey(
TagHelperAttributeList attributes)
{
// Arrange
var expectedMessage = $"Cannot add a {nameof(TagHelperAttribute)} with inconsistent names. The " +
$"{nameof(TagHelperAttribute.Name)} property 'somethingelse' must match the location 'something'." +
$"{Environment.NewLine}Parameter name: name";
// Act & Assert
var exception = Assert.Throws<ArgumentException>("name", () =>
{
attributes["something"] = new TagHelperAttribute("somethingelse", "value");
});
Assert.Equal(expectedMessage, exception.Message);
}
[Fact]
public void ICollection_IsReadOnly_ReturnsFalse()
{
@ -316,37 +265,6 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
Assert.Equal(expectedAttributes, attributes, CaseSensitiveTagHelperAttributeComparer.Default);
}
[Fact]
public void Add_ThrowsWhenNameAndValueAreNull()
{
// Arrange
var attributes = new TagHelperAttributeList();
var expectedMessage = $"Cannot add a '{typeof(TagHelperAttribute).FullName}' with a null " +
$"'{nameof(TagHelperAttribute.Name)}'.{Environment.NewLine}Parameter name: attribute";
// Act & Assert
var exception = Assert.Throws<ArgumentException>("attribute",
() => attributes.Add(new TagHelperAttribute()));
Assert.Equal(expectedMessage, exception.Message);
}
[Fact]
public void Add_ThrowsWhenNameIsNull()
{
// Arrange
var attributes = new TagHelperAttributeList();
var expectedMessage = $"Cannot add a '{typeof(TagHelperAttribute).FullName}' with a null " +
$"'{nameof(TagHelperAttribute.Name)}'.{Environment.NewLine}Parameter name: attribute";
// Act & Assert
var exception = Assert.Throws<ArgumentException>("attribute",
() => attributes.Add(new TagHelperAttribute
{
Value = "Anything"
}));
Assert.Equal(expectedMessage, exception.Message);
}
public static TheoryData InsertData
{
get
@ -391,23 +309,6 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
Assert.Equal(expectedAttributes, attributes, CaseSensitiveTagHelperAttributeComparer.Default);
}
[Fact]
public void Insert_ThrowsWhenNameIsNull()
{
// Arrange
var attributes = new TagHelperAttributeList();
var expectedMessage = $"Cannot add a '{typeof(TagHelperAttribute).FullName}' with a null " +
$"'{nameof(TagHelperAttribute.Name)}'.{Environment.NewLine}Parameter name: attribute";
// Act & Assert
var exception = Assert.Throws<ArgumentException>("attribute",
() => attributes.Insert(0, new TagHelperAttribute
{
Value = "Anything"
}));
Assert.Equal(expectedMessage, exception.Message);
}
[Fact]
public void Insert_ThrowsWhenIndexIsOutOfRange()
{
@ -585,17 +486,17 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
{ new[] { C, B, A3, A }, new TagHelperAttribute("AName", "av"), new[] { C, B, A }, true },
// Null expected lookups
{ Enumerable.Empty<TagHelperAttribute>(), "_0_", Enumerable.Empty<TagHelperAttribute>(), false },
{ new[] { A }, "_AName_", new[] { A }, false },
{ new[] { A }, "completely different", new[] { A }, false },
{ new[] { A, B }, "_AName_", new[] { A, B }, false },
{ new[] { A, B }, "completely different", new[] { A, B }, false },
{ new[] { A, B, C }, "_BName_", new[] { A, B, C }, false },
{ new[] { A, B, C }, "completely different", new[] { A, B, C }, false },
{ new[] { A, A2, B, C }, "_cnamE_", new[] { A, A2, B, C }, false },
{ new[] { A, A2, B, C }, "completely different", new[] { A, A2, B, C }, false },
{ new[] { A, A2, A3, B, C }, "_cnamE_", new[] { A, A2, A3, B, C }, false },
{ new[] { A, A2, A3, B, C }, "completely different", new[] { A, A2, A3, B, C }, false },
{ Enumerable.Empty<TagHelperAttribute>(), new TagHelperAttribute("DoesNotExist", "_0_"), Enumerable.Empty<TagHelperAttribute>(), false },
{ new[] { A }, new TagHelperAttribute("DoesNotExist", "_AName_"), new[] { A }, false },
{ new[] { A }, new TagHelperAttribute("DoesNotExist", "completely different"), new[] { A }, false },
{ new[] { A, B }, new TagHelperAttribute("DoesNotExist", "_AName_"), new[] { A, B }, false },
{ new[] { A, B }, new TagHelperAttribute("DoesNotExist", "completely different"), new[] { A, B }, false },
{ new[] { A, B, C }, new TagHelperAttribute("DoesNotExist", "_BName_"), new[] { A, B, C }, false },
{ new[] { A, B, C }, new TagHelperAttribute("DoesNotExist", "completely different"), new[] { A, B, C }, false },
{ new[] { A, A2, B, C }, new TagHelperAttribute("DoesNotExist", "_cnamE_"), new[] { A, A2, B, C }, false },
{ new[] { A, A2, B, C }, new TagHelperAttribute("DoesNotExist", "completely different"), new[] { A, A2, B, C }, false },
{ new[] { A, A2, A3, B, C }, new TagHelperAttribute("DoesNotExist", "_cnamE_"), new[] { A, A2, A3, B, C }, false },
{ new[] { A, A2, A3, B, C }, new TagHelperAttribute("DoesNotExist", "completely different"), new[] { A, A2, A3, B, C }, false },
};
}
}

View File

@ -1,36 +0,0 @@
// 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 Xunit;
namespace Microsoft.AspNetCore.Razor.TagHelpers
{
public class TagHelperAttributeTest
{
public static TheoryData CopyConstructorData
{
get
{
return new TheoryData<IReadOnlyTagHelperAttribute>
{
new TagHelperAttribute("hello", "world") { Minimized = false },
new TagHelperAttribute("checked", value: null) { Minimized = true },
};
}
}
[Theory]
[MemberData(nameof(CopyConstructorData))]
public void CopyConstructorCopiesValuesAsExpected(IReadOnlyTagHelperAttribute readOnlyTagHelperAttribute)
{
// Act
var tagHelperAttribute = new TagHelperAttribute(readOnlyTagHelperAttribute);
// Assert
Assert.Equal(
readOnlyTagHelperAttribute,
tagHelperAttribute,
CaseSensitiveTagHelperAttributeComparer.Default);
}
}
}

View File

@ -3,9 +3,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Runtime.TagHelpers;
using Xunit;
namespace Microsoft.AspNetCore.Razor.TagHelpers
@ -23,7 +20,7 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
// Act
var context = new TagHelperContext(
allAttributes: Enumerable.Empty<IReadOnlyTagHelperAttribute>(),
allAttributes: new TagHelperAttributeList(),
items: expectedItems,
uniqueId: string.Empty);

View File

@ -347,7 +347,7 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
(cachedResult, encoder) => Task.FromResult<TagHelperContent>(new DefaultTagHelperContent()));
// Act
tagHelperOutput.Attributes[updateName] = "super button";
tagHelperOutput.Attributes.SetAttribute(updateName, "super button");
// Assert
var attribute = Assert.Single(tagHelperOutput.Attributes);
@ -434,11 +434,7 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
tagName: "p",
attributes: new TagHelperAttributeList()
{
["test"] = new TagHelperAttribute
{
Name = "test",
Minimized = true
},
new TagHelperAttribute("test"),
},
tagMode: TagMode.StartTagAndEndTag,
preElement: null,
@ -453,16 +449,8 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
tagName: "p",
attributes: new TagHelperAttributeList()
{
["test"] = new TagHelperAttribute
{
Name = "test",
Minimized = true
},
["test2"] = new TagHelperAttribute
{
Name = "test2",
Minimized = true
},
new TagHelperAttribute("test"),
new TagHelperAttribute("test2"),
},
tagMode: TagMode.StartTagAndEndTag,
preElement: null,
@ -477,12 +465,8 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
tagName: "p",
attributes: new TagHelperAttributeList()
{
["first"] = "unminimized",
["test"] = new TagHelperAttribute
{
Name = "test",
Minimized = true
},
new TagHelperAttribute("first", "unminimized"),
new TagHelperAttribute("test"),
},
tagMode: TagMode.StartTagAndEndTag,
preElement: null,
@ -497,12 +481,8 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
tagName: "p",
attributes: new TagHelperAttributeList()
{
["test"] = new TagHelperAttribute
{
Name = "test",
Minimized = true
},
["last"] = "unminimized",
new TagHelperAttribute("test"),
new TagHelperAttribute("last", "unminimized"),
},
tagMode: TagMode.StartTagAndEndTag,
preElement: null,

View File

@ -33,11 +33,7 @@
"dnxcore50": {
"imports": "portable-net451+win8",
"dependencies": {
"xunit.runner.aspnet": "2.0.0-aspnet-*",
"Microsoft.Extensions.HashCodeCombiner.Sources": {
"type": "build",
"version": "1.0.0-*"
}
"xunit.runner.aspnet": "2.0.0-aspnet-*"
}
}
}