public void TagHelperDescriptor_CanBeSerialized() { // Arrange var descriptor = new TagHelperDescriptor( prefix: "prefix:", tagName: "tag name", typeName: "type name", assemblyName: "assembly name", attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(), requiredAttributes: new[] { "required attribute one", "required attribute two" }); var expectedSerializedDescriptor = $"{{\"{ nameof(TagHelperDescriptor.Prefix) }\":\"prefix:\"," + $"\"{ nameof(TagHelperDescriptor.TagName) }\":\"tag name\"," + $"\"{ nameof(TagHelperDescriptor.FullTagName) }\":\"prefix:tag name\"," + $"\"{ nameof(TagHelperDescriptor.TypeName) }\":\"type name\"," + $"\"{ nameof(TagHelperDescriptor.AssemblyName) }\":\"assembly name\"," + $"\"{ nameof(TagHelperDescriptor.Attributes) }\":[]," + $"\"{ nameof(TagHelperDescriptor.RequiredAttributes) }\":" + "[\"required attribute one\",\"required attribute two\"]}"; // Act var serializedDescriptor = JsonConvert.SerializeObject(descriptor); // Assert Assert.Equal(expectedSerializedDescriptor, serializedDescriptor, StringComparer.Ordinal); }
public override int GetHashCode(TagHelperDescriptor descriptor) { var hashCodeCombiner = HashCodeCombiner.Start() .Add(base.GetHashCode(descriptor)) .Add(descriptor.TagName, StringComparer.Ordinal) .Add(descriptor.Prefix, StringComparer.Ordinal); if (descriptor.DesignTimeDescriptor != null) { hashCodeCombiner.Add( TagHelperDesignTimeDescriptorComparer.Default.GetHashCode(descriptor.DesignTimeDescriptor)); } foreach (var requiredAttribute in descriptor.RequiredAttributes) { hashCodeCombiner.Add(requiredAttribute, StringComparer.Ordinal); } if (descriptor.AllowedChildren != null) { foreach (var child in descriptor.AllowedChildren) { hashCodeCombiner.Add(child, StringComparer.Ordinal); } } foreach (var attribute in descriptor.Attributes) { hashCodeCombiner.Add(TagHelperAttributeDescriptorComparer.Default.GetHashCode(attribute)); } return hashCodeCombiner.CombinedHash; }
public void TagHelperDescriptor_CanBeSerialized() { // Arrange var descriptor = new TagHelperDescriptor( prefix: "prefix:", tagName: "tag name", typeName: "type name", assemblyName: "assembly name", attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(), requiredAttributes: new[] { "required attribute one", "required attribute two" }, designTimeDescriptor: new TagHelperDesignTimeDescriptor("usage summary", "usage remarks", "some-tag")); var expectedSerializedDescriptor = $"{{\"{ nameof(TagHelperDescriptor.Prefix) }\":\"prefix:\"," + $"\"{ nameof(TagHelperDescriptor.TagName) }\":\"tag name\"," + $"\"{ nameof(TagHelperDescriptor.FullTagName) }\":\"prefix:tag name\"," + $"\"{ nameof(TagHelperDescriptor.TypeName) }\":\"type name\"," + $"\"{ nameof(TagHelperDescriptor.AssemblyName) }\":\"assembly name\"," + $"\"{ nameof(TagHelperDescriptor.Attributes) }\":[]," + $"\"{ nameof(TagHelperDescriptor.RequiredAttributes) }\":" + "[\"required attribute one\",\"required attribute two\"]," + $"\"{ nameof(TagHelperDescriptor.DesignTimeDescriptor) }\":{{"+ $"\"{ nameof(TagHelperDesignTimeDescriptor.Summary) }\":\"usage summary\"," + $"\"{ nameof(TagHelperDesignTimeDescriptor.Remarks) }\":\"usage remarks\"," + $"\"{ nameof(TagHelperDesignTimeDescriptor.OutputElementHint) }\":\"some-tag\"}}}}"; // Act var serializedDescriptor = JsonConvert.SerializeObject(descriptor); // Assert Assert.Equal(expectedSerializedDescriptor, serializedDescriptor, StringComparer.Ordinal); }
public override bool Equals(TagHelperDescriptor descriptorX, TagHelperDescriptor descriptorY) { if (descriptorX == descriptorY) { return true; } return base.Equals(descriptorX, descriptorY) && // Normal comparer doesn't care about the case, required attribute order, allowed children order, // attributes or prefixes. In tests we do. string.Equals(descriptorX.TagName, descriptorY.TagName, StringComparison.Ordinal) && string.Equals(descriptorX.Prefix, descriptorY.Prefix, StringComparison.Ordinal) && Enumerable.SequenceEqual( descriptorX.RequiredAttributes, descriptorY.RequiredAttributes, StringComparer.Ordinal) && (descriptorX.AllowedChildren == descriptorY.AllowedChildren || Enumerable.SequenceEqual( descriptorX.AllowedChildren, descriptorY.AllowedChildren, StringComparer.Ordinal)) && descriptorX.Attributes.SequenceEqual( descriptorY.Attributes, TagHelperAttributeDescriptorComparer.Default) && TagHelperDesignTimeDescriptorComparer.Default.Equals( descriptorX.DesignTimeDescriptor, descriptorY.DesignTimeDescriptor); }
public void TagHelpers_CanReplaceAttributeCodeGeneratorLogic() { // Arrange var inputTypePropertyInfo = typeof(TestType).GetProperty("Type"); var checkedPropertyInfo = typeof(TestType).GetProperty("Checked"); var tagHelperDescriptors = new TagHelperDescriptor[] { new TagHelperDescriptor("p", "PTagHelper", "SomeAssembly"), new TagHelperDescriptor("input", "InputTagHelper", "SomeAssembly", new TagHelperAttributeDescriptor[] { new TagHelperAttributeDescriptor("type", inputTypePropertyInfo) }), new TagHelperDescriptor("input", "InputTagHelper2", "SomeAssembly", new TagHelperAttributeDescriptor[] { new TagHelperAttributeDescriptor("type", inputTypePropertyInfo), new TagHelperAttributeDescriptor("checked", checkedPropertyInfo) }) }; // Act & Assert RunTagHelperTest(testName: "BasicTagHelpers", baseLineName: "BasicTagHelpers.CustomAttributeCodeGenerator", tagHelperDescriptors: tagHelperDescriptors, hostConfig: (host) => { return new CodeBuilderReplacingHost(host); }); }
public void TagHelperDescriptor_WithAttributes_CanBeSerialized() { // Arrange var descriptor = new TagHelperDescriptor { Prefix = "prefix:", TagName = "tag name", TypeName = "type name", AssemblyName = "assembly name", Attributes = new[] { new TagHelperAttributeDescriptor { Name = "attribute one", PropertyName = "property name", TypeName = "property type name" }, new TagHelperAttributeDescriptor { Name = "attribute two", PropertyName = "property name", TypeName = typeof(string).FullName }, }, TagStructure = TagStructure.NormalOrSelfClosing }; var expectedSerializedDescriptor = $"{{\"{ nameof(TagHelperDescriptor.Prefix) }\":\"prefix:\"," + $"\"{ nameof(TagHelperDescriptor.TagName) }\":\"tag name\"," + $"\"{ nameof(TagHelperDescriptor.FullTagName) }\":\"prefix:tag name\"," + $"\"{ nameof(TagHelperDescriptor.TypeName) }\":\"type name\"," + $"\"{ nameof(TagHelperDescriptor.AssemblyName) }\":\"assembly name\"," + $"\"{ nameof(TagHelperDescriptor.Attributes) }\":[" + $"{{\"{ nameof(TagHelperAttributeDescriptor.IsIndexer) }\":false," + $"\"{ nameof(TagHelperAttributeDescriptor.IsStringProperty) }\":false," + $"\"{ nameof(TagHelperAttributeDescriptor.Name) }\":\"attribute one\"," + $"\"{ nameof(TagHelperAttributeDescriptor.PropertyName) }\":\"property name\"," + $"\"{ nameof(TagHelperAttributeDescriptor.TypeName) }\":\"property type name\"," + $"\"{ nameof(TagHelperAttributeDescriptor.DesignTimeDescriptor) }\":null}}," + $"{{\"{ nameof(TagHelperAttributeDescriptor.IsIndexer) }\":false," + $"\"{ nameof(TagHelperAttributeDescriptor.IsStringProperty) }\":true," + $"\"{ nameof(TagHelperAttributeDescriptor.Name) }\":\"attribute two\"," + $"\"{ nameof(TagHelperAttributeDescriptor.PropertyName) }\":\"property name\"," + $"\"{ nameof(TagHelperAttributeDescriptor.TypeName) }\":\"{ typeof(string).FullName }\"," + $"\"{ nameof(TagHelperAttributeDescriptor.DesignTimeDescriptor) }\":null}}]," + $"\"{ nameof(TagHelperDescriptor.RequiredAttributes) }\":[]," + $"\"{ nameof(TagHelperDescriptor.AllowedChildren) }\":null," + $"\"{ nameof(TagHelperDescriptor.TagStructure) }\":1," + $"\"{ nameof(TagHelperDescriptor.DesignTimeDescriptor) }\":null}}"; // Act var serializedDescriptor = JsonConvert.SerializeObject(descriptor); // Assert Assert.Equal(expectedSerializedDescriptor, serializedDescriptor, StringComparer.Ordinal); }
public void TagHelperDescriptor_WithAttributes_CanBeSerialized() { // Arrange var descriptor = new TagHelperDescriptor( prefix: "prefix:", tagName: "tag name", typeName: "type name", assemblyName: "assembly name", attributes: new[] { new TagHelperAttributeDescriptor( name: "attribute one", propertyName: "property name", typeName: "property type name", isIndexer: false, designTimeDescriptor: null), new TagHelperAttributeDescriptor( name: "attribute two", propertyName: "property name", typeName: typeof(string).FullName, isIndexer: false, designTimeDescriptor: null), }, requiredAttributes: Enumerable.Empty<string>(), designTimeDescriptor: null); var expectedSerializedDescriptor = $"{{\"{ nameof(TagHelperDescriptor.Prefix) }\":\"prefix:\"," + $"\"{ nameof(TagHelperDescriptor.TagName) }\":\"tag name\"," + $"\"{ nameof(TagHelperDescriptor.FullTagName) }\":\"prefix:tag name\"," + $"\"{ nameof(TagHelperDescriptor.TypeName) }\":\"type name\"," + $"\"{ nameof(TagHelperDescriptor.AssemblyName) }\":\"assembly name\"," + $"\"{ nameof(TagHelperDescriptor.Attributes) }\":[" + $"{{\"{ nameof(TagHelperAttributeDescriptor.IsIndexer) }\":false," + $"\"{ nameof(TagHelperAttributeDescriptor.IsStringProperty) }\":false," + $"\"{ nameof(TagHelperAttributeDescriptor.Name) }\":\"attribute one\"," + $"\"{ nameof(TagHelperAttributeDescriptor.PropertyName) }\":\"property name\"," + $"\"{ nameof(TagHelperAttributeDescriptor.TypeName) }\":\"property type name\"," + $"\"{ nameof(TagHelperAttributeDescriptor.DesignTimeDescriptor) }\":null}}," + $"{{\"{ nameof(TagHelperAttributeDescriptor.IsIndexer) }\":false," + $"\"{ nameof(TagHelperAttributeDescriptor.IsStringProperty) }\":true," + $"\"{ nameof(TagHelperAttributeDescriptor.Name) }\":\"attribute two\"," + $"\"{ nameof(TagHelperAttributeDescriptor.PropertyName) }\":\"property name\"," + $"\"{ nameof(TagHelperAttributeDescriptor.TypeName) }\":\"{ typeof(string).FullName }\"," + $"\"{ nameof(TagHelperAttributeDescriptor.DesignTimeDescriptor) }\":null}}]," + $"\"{ nameof(TagHelperDescriptor.RequiredAttributes) }\":[]," + $"\"{ nameof(TagHelperDescriptor.DesignTimeDescriptor) }\":null}}"; // Act var serializedDescriptor = JsonConvert.SerializeObject(descriptor); // Assert Assert.Equal(expectedSerializedDescriptor, serializedDescriptor, StringComparer.Ordinal); }
public void Rewrite_CanHandleInvalidChildrenWithWhitespace() { // Arrange var factory = CreateDefaultSpanFactory(); var blockFactory = new BlockFactory(factory); var documentContent = $"<p>{Environment.NewLine} <strong>{Environment.NewLine} Hello" + $"{Environment.NewLine} </strong>{Environment.NewLine}</p>"; var newLineLength = Environment.NewLine.Length; var expectedErrors = new[] { new RazorError( RazorResources.FormatTagHelperParseTreeRewriter_InvalidNestedTag("strong", "p", "br"), absoluteIndex: 7 + newLineLength, lineIndex: 1, columnIndex: 9 + newLineLength, length: 8), new RazorError( RazorResources.FormatTagHelperParseTreeRewriter_CannotHaveNonTagContent("p", "br"), absoluteIndex: 23 + newLineLength * 2, lineIndex: 2, columnIndex: 23 + newLineLength * 2, length: 5), new RazorError( RazorResources.FormatTagHelperParseTreeRewriter_InvalidNestedTag("strong", "p", "br"), absoluteIndex: 32 + newLineLength * 3, lineIndex: 3, columnIndex: 32 + newLineLength * 3, length: 9), }; var expectedOutput = new MarkupBlock( new MarkupTagHelperBlock("p", factory.Markup(Environment.NewLine + " "), blockFactory.MarkupTagBlock("<strong>"), factory.Markup(Environment.NewLine + " Hello" + Environment.NewLine + " "), blockFactory.MarkupTagBlock("</strong>"), factory.Markup(Environment.NewLine))); var descriptors = new TagHelperDescriptor[] { new TagHelperDescriptor { TagName = "p", TypeName = "PTagHelper", AssemblyName = "SomeAssembly", AllowedChildren = new[] { "br" }, } }; var descriptorProvider = new TagHelperDescriptorProvider(descriptors); // Act & Assert EvaluateData(descriptorProvider, documentContent, expectedOutput, expectedErrors); }
public void TagHelperDescriptor_CanBeSerialized() { // Arrange var descriptor = new TagHelperDescriptor { Prefix = "prefix:", TagName = "tag name", TypeName = "type name", AssemblyName = "assembly name", RequiredAttributes = new[] { "required attribute one", "required attribute two" }, AllowedChildren = new[] { "allowed child one" }, RequiredParent = "parent name", DesignTimeDescriptor = new TagHelperDesignTimeDescriptor { Summary = "usage summary", Remarks = "usage remarks", OutputElementHint = "some-tag" }, }; var expectedSerializedDescriptor = $"{{\"{ nameof(TagHelperDescriptor.Prefix) }\":\"prefix:\"," + $"\"{ nameof(TagHelperDescriptor.TagName) }\":\"tag name\"," + $"\"{ nameof(TagHelperDescriptor.FullTagName) }\":\"prefix:tag name\"," + $"\"{ nameof(TagHelperDescriptor.TypeName) }\":\"type name\"," + $"\"{ nameof(TagHelperDescriptor.AssemblyName) }\":\"assembly name\"," + $"\"{ nameof(TagHelperDescriptor.Attributes) }\":[]," + $"\"{ nameof(TagHelperDescriptor.RequiredAttributes) }\":" + "[\"required attribute one\",\"required attribute two\"]," + $"\"{ nameof(TagHelperDescriptor.AllowedChildren) }\":[\"allowed child one\"]," + $"\"{ nameof(TagHelperDescriptor.RequiredParent) }\":\"parent name\"," + $"\"{ nameof(TagHelperDescriptor.TagStructure) }\":0," + $"\"{ nameof(TagHelperDescriptor.DesignTimeDescriptor) }\":{{"+ $"\"{ nameof(TagHelperDesignTimeDescriptor.Summary) }\":\"usage summary\"," + $"\"{ nameof(TagHelperDesignTimeDescriptor.Remarks) }\":\"usage remarks\"," + $"\"{ nameof(TagHelperDesignTimeDescriptor.OutputElementHint) }\":\"some-tag\"}}}}"; // Act var serializedDescriptor = JsonConvert.SerializeObject(descriptor); // Assert Assert.Equal(expectedSerializedDescriptor, serializedDescriptor, StringComparer.Ordinal); }
public override int GetHashCode(TagHelperDescriptor descriptor) { var hashCodeCombiner = HashCodeCombiner.Start() .Add(base.GetHashCode(descriptor)) .Add(descriptor.TagName, StringComparer.Ordinal) .Add(descriptor.Prefix); foreach (var requiredAttribute in descriptor.RequiredAttributes) { hashCodeCombiner.Add(requiredAttribute, StringComparer.Ordinal); } foreach (var attribute in descriptor.Attributes) { hashCodeCombiner.Add(CaseSensitiveTagHelperAttributeDescriptorComparer.Default.GetHashCode(attribute)); } return hashCodeCombiner.CombinedHash; }
public override bool Equals(TagHelperDescriptor descriptorX, TagHelperDescriptor descriptorY) { if (descriptorX == descriptorY) { return true; } return base.Equals(descriptorX, descriptorY) && // Normal comparer doesn't care about the case, required attribute order, attributes or prefixes. // In tests we do. string.Equals(descriptorX.TagName, descriptorY.TagName, StringComparison.Ordinal) && string.Equals(descriptorX.Prefix, descriptorY.Prefix, StringComparison.Ordinal) && Enumerable.SequenceEqual( descriptorX.RequiredAttributes, descriptorY.RequiredAttributes, StringComparer.Ordinal) && descriptorX.Attributes.SequenceEqual( descriptorY.Attributes, CaseSensitiveTagHelperAttributeDescriptorComparer.Default); }
public void GetDescriptors_ReturnsCatchAllsWithEveryTagName() { // Arrange var divDescriptor = new TagHelperDescriptor { TagName = "div", TypeName = "foo1", AssemblyName = "SomeAssembly", }; var spanDescriptor = new TagHelperDescriptor { TagName = "span", TypeName = "foo2", AssemblyName = "SomeAssembly", }; var catchAllDescriptor = new TagHelperDescriptor { TagName = TagHelperDescriptorProvider.ElementCatchAllTarget, TypeName = "foo3", AssemblyName = "SomeAssembly", }; var descriptors = new TagHelperDescriptor[] { divDescriptor, spanDescriptor, catchAllDescriptor }; var provider = new TagHelperDescriptorProvider(descriptors); // Act var divDescriptors = provider.GetDescriptors("div", attributeNames: Enumerable.Empty <string>()); var spanDescriptors = provider.GetDescriptors("span", attributeNames: Enumerable.Empty <string>()); // Assert // For divs Assert.Equal(2, divDescriptors.Count()); Assert.Contains(divDescriptor, divDescriptors); Assert.Contains(catchAllDescriptor, divDescriptors); // For spans Assert.Equal(2, spanDescriptors.Count()); Assert.Contains(spanDescriptor, spanDescriptors); Assert.Contains(catchAllDescriptor, spanDescriptors); }
private void Register(TagHelperDescriptor descriptor) { HashSet <TagHelperDescriptor> descriptorSet; if (_tagHelperPrefix == null) { _tagHelperPrefix = descriptor.Prefix; } var registrationKey = string.Equals(descriptor.TagName, CatchAllDescriptorTarget, StringComparison.Ordinal) ? CatchAllDescriptorTarget : descriptor.FullTagName; // Ensure there's a HashSet to add the descriptor to. if (!_registrations.TryGetValue(registrationKey, out descriptorSet)) { descriptorSet = new HashSet <TagHelperDescriptor>(TagHelperDescriptorComparer.Default); _registrations[registrationKey] = descriptorSet; } descriptorSet.Add(descriptor); }
public void GetDescriptors_ReturnsNothingForUnregisteredTags() { // Arrange var divDescriptor = new TagHelperDescriptor { TagName = "div", TypeName = "foo1", AssemblyName = "SomeAssembly", }; var spanDescriptor = new TagHelperDescriptor { TagName = "span", TypeName = "foo2", AssemblyName = "SomeAssembly", }; var descriptors = new TagHelperDescriptor[] { divDescriptor, spanDescriptor }; var provider = new TagHelperDescriptorProvider(descriptors); // Act var retrievedDescriptors = provider.GetDescriptors("foo", attributeNames: Enumerable.Empty <string>()); // Assert Assert.Empty(retrievedDescriptors); }
public void Rewrite_UnderstandsMinimizedAttributes( string documentContent, MarkupBlock expectedOutput, RazorError[] expectedErrors) { // Arrange var descriptors = new TagHelperDescriptor[] { new TagHelperDescriptor( tagName: "input", typeName: "InputTagHelper1", assemblyName: "SomeAssembly", attributes: new[] { new TagHelperAttributeDescriptor( "bound-required-string", "BoundRequiredString", typeof(string).FullName, isIndexer: false, designTimeDescriptor: null) }, requiredAttributes: new[] { "unbound-required" }), new TagHelperDescriptor( tagName: "input", typeName: "InputTagHelper1", assemblyName: "SomeAssembly", attributes: new[] { new TagHelperAttributeDescriptor( "bound-required-string", "BoundRequiredString", typeof(string).FullName, isIndexer: false, designTimeDescriptor: null) }, requiredAttributes: new[] { "bound-required-string" }), new TagHelperDescriptor( tagName: "input", typeName: "InputTagHelper2", assemblyName: "SomeAssembly", attributes: new[] { new TagHelperAttributeDescriptor( "bound-required-int", "BoundRequiredInt", typeof(int).FullName, isIndexer: false, designTimeDescriptor: null) }, requiredAttributes: new[] { "bound-required-int" }), new TagHelperDescriptor( tagName: "input", typeName: "InputTagHelper3", assemblyName: "SomeAssembly", attributes: new[] { new TagHelperAttributeDescriptor( "int-dictionary", "DictionaryOfIntProperty", typeof(IDictionary<string, int>).FullName, isIndexer: false, designTimeDescriptor: null), new TagHelperAttributeDescriptor( "string-dictionary", "DictionaryOfStringProperty", typeof(IDictionary<string, string>).FullName, isIndexer: false, designTimeDescriptor: null), new TagHelperAttributeDescriptor( "int-prefix-", "DictionaryOfIntProperty", typeof(int).FullName, isIndexer: true, designTimeDescriptor: null), new TagHelperAttributeDescriptor( "string-prefix-", "DictionaryOfStringProperty", typeof(string).FullName, isIndexer: true, designTimeDescriptor: null), }, requiredAttributes: Enumerable.Empty<string>()), new TagHelperDescriptor( tagName: "p", typeName: "PTagHelper", assemblyName: "SomeAssembly", attributes: new[] { new TagHelperAttributeDescriptor( "bound-string", "BoundRequiredString", typeof(string).FullName, isIndexer: false, designTimeDescriptor: null), new TagHelperAttributeDescriptor( "bound-int", "BoundRequiredString", typeof(int).FullName, isIndexer: false, designTimeDescriptor: null) }, requiredAttributes: Enumerable.Empty<string>()), }; var descriptorProvider = new TagHelperDescriptorProvider(descriptors); // Act & Assert EvaluateData(descriptorProvider, documentContent, expectedOutput, expectedErrors); }
private void Register(TagHelperDescriptor descriptor) { HashSet<TagHelperDescriptor> descriptorSet; if (_tagHelperPrefix == null) { _tagHelperPrefix = descriptor.Prefix; } var registrationKey = string.Equals(descriptor.TagName, CatchAllDescriptorTarget, StringComparison.Ordinal) ? CatchAllDescriptorTarget : descriptor.FullTagName; // Ensure there's a HashSet to add the descriptor to. if (!_registrations.TryGetValue(registrationKey, out descriptorSet)) { descriptorSet = new HashSet<TagHelperDescriptor>(TagHelperDescriptorComparer.Default); _registrations[registrationKey] = descriptorSet; } descriptorSet.Add(descriptor); }
public void CreateDescriptors_CreatesDesignTimeDescriptorsWithOutputElementHint( Type tagHelperType, TagHelperDescriptor[] expectedDescriptors) { // Arrange var errorSink = new ErrorSink(); // Act var descriptors = TagHelperDescriptorFactory.CreateDescriptors( AssemblyName, GetTypeInfo(tagHelperType), designTime: true, errorSink: errorSink); // Assert Assert.Empty(errorSink.Errors); // We don't care about order. Mono returns reflected attributes differently so we need to ensure order // doesn't matter by sorting. descriptors = descriptors.OrderBy(descriptor => descriptor.TagName); Assert.Equal(expectedDescriptors, descriptors, CaseSensitiveTagHelperDescriptorComparer.Default); }
private static bool MatchesLookupInfo(TagHelperDescriptor descriptor, LookupInfo lookupInfo) { if (!string.Equals(descriptor.AssemblyName, lookupInfo.AssemblyName, StringComparison.Ordinal)) { return false; } // We need to escape the TypePattern so we can choose to only allow specific regex. var escaped = Regex.Escape(lookupInfo.TypePattern); // We surround the escaped with ^ and $ in order ot ensure a regex match matches the entire // string. We also replace any '*' or '?' characters with regex to match appropriate content. // '*' matches 0 or more characters lazily and '?' matches 1 character. var pattern = "^" + escaped.Replace(@"\?", ".").Replace(@"\*", ".*?") + "$"; var regex = new Regex(pattern, RegexOptions.Singleline); return regex.IsMatch(descriptor.TypeName); }
public void GetDescriptors_ReturnsCatchAllsWithEveryTagName() { // Arrange var divDescriptor = new TagHelperDescriptor { TagName = "div", TypeName = "foo1", AssemblyName = "SomeAssembly", }; var spanDescriptor = new TagHelperDescriptor { TagName = "span", TypeName = "foo2", AssemblyName = "SomeAssembly", }; var catchAllDescriptor = new TagHelperDescriptor { TagName = TagHelperDescriptorProvider.ElementCatchAllTarget, TypeName = "foo3", AssemblyName = "SomeAssembly", }; var descriptors = new TagHelperDescriptor[] { divDescriptor, spanDescriptor, catchAllDescriptor }; var provider = new TagHelperDescriptorProvider(descriptors); // Act var divDescriptors = provider.GetDescriptors("div", attributeNames: Enumerable.Empty<string>()); var spanDescriptors = provider.GetDescriptors("span", attributeNames: Enumerable.Empty<string>()); // Assert // For divs Assert.Equal(2, divDescriptors.Count()); Assert.Contains(divDescriptor, divDescriptors); Assert.Contains(catchAllDescriptor, divDescriptors); // For spans Assert.Equal(2, spanDescriptors.Count()); Assert.Contains(spanDescriptor, spanDescriptors); Assert.Contains(catchAllDescriptor, spanDescriptors); }
public void TagHelperDescriptor_WithIndexerAttributes_CanBeDeserialized() { // Arrange var serializedDescriptor = $"{{\"{ nameof(TagHelperDescriptor.Prefix) }\":\"prefix:\"," + $"\"{ nameof(TagHelperDescriptor.TagName) }\":\"tag name\"," + $"\"{ nameof(TagHelperDescriptor.FullTagName) }\":\"prefix:tag name\"," + $"\"{ nameof(TagHelperDescriptor.TypeName) }\":\"type name\"," + $"\"{ nameof(TagHelperDescriptor.AssemblyName) }\":\"assembly name\"," + $"\"{ nameof(TagHelperDescriptor.Attributes) }\":[" + $"{{\"{ nameof(TagHelperAttributeDescriptor.IsIndexer) }\":true," + $"\"{ nameof(TagHelperAttributeDescriptor.IsStringProperty) }\":false," + $"\"{ nameof(TagHelperAttributeDescriptor.Name) }\":\"attribute one\"," + $"\"{ nameof(TagHelperAttributeDescriptor.PropertyName) }\":\"property name\"," + $"\"{ nameof(TagHelperAttributeDescriptor.TypeName) }\":\"property type name\"," + $"\"{ nameof(TagHelperAttributeDescriptor.DesignTimeDescriptor) }\":null}}," + $"{{\"{ nameof(TagHelperAttributeDescriptor.IsIndexer) }\":true," + $"\"{ nameof(TagHelperAttributeDescriptor.IsStringProperty) }\":true," + $"\"{ nameof(TagHelperAttributeDescriptor.Name) }\":\"attribute two\"," + $"\"{ nameof(TagHelperAttributeDescriptor.PropertyName) }\":\"property name\"," + $"\"{ nameof(TagHelperAttributeDescriptor.TypeName) }\":\"{ typeof(string).FullName }\"," + $"\"{ nameof(TagHelperAttributeDescriptor.DesignTimeDescriptor) }\":null}}]," + $"\"{ nameof(TagHelperDescriptor.RequiredAttributes) }\":[]," + $"\"{ nameof(TagHelperDescriptor.AllowedChildren) }\":null," + $"\"{nameof(TagHelperDescriptor.TagStructure)}\":1," + $"\"{ nameof(TagHelperDescriptor.DesignTimeDescriptor) }\":null}}"; var expectedDescriptor = new TagHelperDescriptor { Prefix = "prefix:", TagName = "tag name", TypeName = "type name", AssemblyName = "assembly name", Attributes = new[] { new TagHelperAttributeDescriptor { Name = "attribute one", PropertyName = "property name", TypeName = "property type name", IsIndexer = true }, new TagHelperAttributeDescriptor { Name = "attribute two", PropertyName = "property name", TypeName = typeof(string).FullName, IsIndexer = true } }, TagStructure = TagStructure.NormalOrSelfClosing }; // Act var descriptor = JsonConvert.DeserializeObject <TagHelperDescriptor>(serializedDescriptor); // Assert Assert.NotNull(descriptor); Assert.Equal(expectedDescriptor.Prefix, descriptor.Prefix, StringComparer.Ordinal); Assert.Equal(expectedDescriptor.TagName, descriptor.TagName, StringComparer.Ordinal); Assert.Equal(expectedDescriptor.FullTagName, descriptor.FullTagName, StringComparer.Ordinal); Assert.Equal(expectedDescriptor.TypeName, descriptor.TypeName, StringComparer.Ordinal); Assert.Equal(expectedDescriptor.AssemblyName, descriptor.AssemblyName, StringComparer.Ordinal); Assert.Equal(expectedDescriptor.Attributes, descriptor.Attributes, TagHelperAttributeDescriptorComparer.Default); Assert.Empty(descriptor.RequiredAttributes); }
public void Rewrite_RequiredAttributeDescriptorsCreateMalformedTagHelperBlocksCorrectly( string documentContent, MarkupBlock expectedOutput, RazorError[] expectedErrors) { // Arrange var descriptors = new TagHelperDescriptor[] { new TagHelperDescriptor( tagName: "p", typeName: "pTagHelper", assemblyName: "SomeAssembly", attributes: new TagHelperAttributeDescriptor[0], requiredAttributes: new[] { "class" }) }; var descriptorProvider = new TagHelperDescriptorProvider(descriptors); // Act & Assert EvaluateData(descriptorProvider, documentContent, expectedOutput, expectedErrors); }
public void Rewrite_CreatesErrorForEmptyTagHelperBoundAttributes( string documentContent, MarkupBlock expectedOutput, RazorError[] expectedErrors) { // Arrange var descriptors = new TagHelperDescriptor[] { new TagHelperDescriptor( tagName: "myth", typeName: "mythTagHelper", assemblyName: "SomeAssembly", attributes: new[] { new TagHelperAttributeDescriptor( name: "bound", propertyName: "Bound", typeName: typeof(bool).FullName, isIndexer: false), new TagHelperAttributeDescriptor( name: "name", propertyName: "Name", typeName: typeof(string).FullName, isIndexer: false) }) }; var descriptorProvider = new TagHelperDescriptorProvider(descriptors); // Act & Assert EvaluateData(descriptorProvider, documentContent, expectedOutput, expectedErrors); }
public void TagHelperParseTreeRewriter_CreatesMarkupCodeSpansForNonStringTagHelperAttributes( string documentContent, MarkupBlock expectedOutput) { // Arrange var descriptors = new TagHelperDescriptor[] { new TagHelperDescriptor { TagName = "person", TypeName = "PersonTagHelper", AssemblyName = "personAssembly", Attributes = new[] { new TagHelperAttributeDescriptor { Name = "age", PropertyName = "Age", TypeName = typeof(int).FullName }, new TagHelperAttributeDescriptor { Name = "birthday", PropertyName = "BirthDay", TypeName = typeof(DateTime).FullName }, new TagHelperAttributeDescriptor { Name = "name", PropertyName = "Name", TypeName = typeof(string).FullName } } } }; var providerContext = new TagHelperDescriptorProvider(descriptors); // Act & Assert EvaluateData(providerContext, documentContent, expectedOutput, expectedErrors: Enumerable.Empty<RazorError>()); }
internal static string GetVariableName(TagHelperDescriptor descriptor) { return "__" + descriptor.TypeName.Replace('.', '_'); }
public void TagHelperDescriptor_WithIndexerAttributes_CanBeDeserialized() { // Arrange var serializedDescriptor = $"{{\"{ nameof(TagHelperDescriptor.Prefix) }\":\"prefix:\"," + $"\"{ nameof(TagHelperDescriptor.TagName) }\":\"tag name\"," + $"\"{ nameof(TagHelperDescriptor.FullTagName) }\":\"prefix:tag name\"," + $"\"{ nameof(TagHelperDescriptor.TypeName) }\":\"type name\"," + $"\"{ nameof(TagHelperDescriptor.AssemblyName) }\":\"assembly name\"," + $"\"{ nameof(TagHelperDescriptor.Attributes) }\":[" + $"{{\"{ nameof(TagHelperAttributeDescriptor.IsIndexer) }\":true," + $"\"{ nameof(TagHelperAttributeDescriptor.IsStringProperty) }\":false," + $"\"{ nameof(TagHelperAttributeDescriptor.Name) }\":\"attribute one\"," + $"\"{ nameof(TagHelperAttributeDescriptor.PropertyName) }\":\"property name\"," + $"\"{ nameof(TagHelperAttributeDescriptor.TypeName) }\":\"property type name\"," + $"\"{ nameof(TagHelperAttributeDescriptor.DesignTimeDescriptor) }\":null}}," + $"{{\"{ nameof(TagHelperAttributeDescriptor.IsIndexer) }\":true," + $"\"{ nameof(TagHelperAttributeDescriptor.IsStringProperty) }\":true," + $"\"{ nameof(TagHelperAttributeDescriptor.Name) }\":\"attribute two\"," + $"\"{ nameof(TagHelperAttributeDescriptor.PropertyName) }\":\"property name\"," + $"\"{ nameof(TagHelperAttributeDescriptor.TypeName) }\":\"{ typeof(string).FullName }\"," + $"\"{ nameof(TagHelperAttributeDescriptor.DesignTimeDescriptor) }\":null}}]," + $"\"{ nameof(TagHelperDescriptor.RequiredAttributes) }\":[]," + $"\"{ nameof(TagHelperDescriptor.DesignTimeDescriptor) }\":null}}"; var expectedDescriptor = new TagHelperDescriptor( prefix: "prefix:", tagName: "tag name", typeName: "type name", assemblyName: "assembly name", attributes: new[] { new TagHelperAttributeDescriptor( name: "attribute one", propertyName: "property name", typeName: "property type name", isIndexer: true, designTimeDescriptor: null), new TagHelperAttributeDescriptor( name: "attribute two", propertyName: "property name", typeName: typeof(string).FullName, isIndexer: true, designTimeDescriptor: null), }, requiredAttributes: Enumerable.Empty <string>(), designTimeDescriptor: null); // Act var descriptor = JsonConvert.DeserializeObject <TagHelperDescriptor>(serializedDescriptor); // Assert Assert.NotNull(descriptor); Assert.Equal(expectedDescriptor.Prefix, descriptor.Prefix, StringComparer.Ordinal); Assert.Equal(expectedDescriptor.TagName, descriptor.TagName, StringComparer.Ordinal); Assert.Equal(expectedDescriptor.FullTagName, descriptor.FullTagName, StringComparer.Ordinal); Assert.Equal(expectedDescriptor.TypeName, descriptor.TypeName, StringComparer.Ordinal); Assert.Equal(expectedDescriptor.AssemblyName, descriptor.AssemblyName, StringComparer.Ordinal); Assert.Equal(2, descriptor.Attributes.Count); Assert.Equal(expectedDescriptor.Attributes[0].IsIndexer, descriptor.Attributes[0].IsIndexer); Assert.Equal(expectedDescriptor.Attributes[0].IsStringProperty, descriptor.Attributes[0].IsStringProperty); Assert.Equal(expectedDescriptor.Attributes[0].Name, descriptor.Attributes[0].Name, StringComparer.Ordinal); Assert.Equal( expectedDescriptor.Attributes[0].PropertyName, descriptor.Attributes[0].PropertyName, StringComparer.Ordinal); Assert.Equal( expectedDescriptor.Attributes[0].TypeName, descriptor.Attributes[0].TypeName, StringComparer.Ordinal); Assert.Equal(expectedDescriptor.Attributes[1].IsIndexer, descriptor.Attributes[1].IsIndexer); Assert.Equal(expectedDescriptor.Attributes[1].IsStringProperty, descriptor.Attributes[1].IsStringProperty); Assert.Equal(expectedDescriptor.Attributes[1].Name, descriptor.Attributes[1].Name, StringComparer.Ordinal); Assert.Equal( expectedDescriptor.Attributes[1].PropertyName, descriptor.Attributes[1].PropertyName, StringComparer.Ordinal); Assert.Equal( expectedDescriptor.Attributes[1].TypeName, descriptor.Attributes[1].TypeName, StringComparer.Ordinal); Assert.Empty(descriptor.RequiredAttributes); }
public void Rewrite_AllowsCompatibleTagStructures( string documentContent, TagStructure structure1, TagStructure structure2, MarkupBlock expectedOutput) { // Arrange var factory = CreateDefaultSpanFactory(); var blockFactory = new BlockFactory(factory); var descriptors = new TagHelperDescriptor[] { new TagHelperDescriptor { TagName = "input", TypeName = "InputTagHelper1", AssemblyName = "SomeAssembly", TagStructure = structure1 }, new TagHelperDescriptor { TagName = "input", TypeName = "InputTagHelper2", AssemblyName = "SomeAssembly", TagStructure = structure2 } }; var descriptorProvider = new TagHelperDescriptorProvider(descriptors); // Act & Assert EvaluateData(descriptorProvider, documentContent, expectedOutput, expectedErrors: new RazorError[0]); }
public void TagHelperDescriptor_WithAttributes_CanBeDeserialized() { // Arrange var serializedDescriptor = $"{{\"{ nameof(TagHelperDescriptor.Prefix) }\":\"prefix:\"," + $"\"{ nameof(TagHelperDescriptor.TagName) }\":\"tag name\"," + $"\"{ nameof(TagHelperDescriptor.FullTagName) }\":\"prefix:tag name\"," + $"\"{ nameof(TagHelperDescriptor.TypeName) }\":\"type name\"," + $"\"{ nameof(TagHelperDescriptor.AssemblyName) }\":\"assembly name\"," + $"\"{ nameof(TagHelperDescriptor.Attributes) }\":[" + $"{{\"{ nameof(TagHelperAttributeDescriptor.IsIndexer) }\":false," + $"\"{ nameof(TagHelperAttributeDescriptor.IsStringProperty) }\":false," + $"\"{ nameof(TagHelperAttributeDescriptor.Name) }\":\"attribute one\"," + $"\"{ nameof(TagHelperAttributeDescriptor.PropertyName) }\":\"property name\"," + $"\"{ nameof(TagHelperAttributeDescriptor.TypeName) }\":\"property type name\"," + $"\"{ nameof(TagHelperAttributeDescriptor.DesignTimeDescriptor) }\":null}}," + $"{{\"{ nameof(TagHelperAttributeDescriptor.IsIndexer) }\":false," + $"\"{ nameof(TagHelperAttributeDescriptor.IsStringProperty) }\":true," + $"\"{ nameof(TagHelperAttributeDescriptor.Name) }\":\"attribute two\"," + $"\"{ nameof(TagHelperAttributeDescriptor.PropertyName) }\":\"property name\"," + $"\"{ nameof(TagHelperAttributeDescriptor.TypeName) }\":\"{ typeof(string).FullName }\"," + $"\"{ nameof(TagHelperAttributeDescriptor.DesignTimeDescriptor) }\":null}}]," + $"\"{ nameof(TagHelperDescriptor.RequiredAttributes) }\":[]," + $"\"{ nameof(TagHelperDescriptor.AllowedChildren) }\":null," + $"\"{nameof(TagHelperDescriptor.TagStructure)}\":0," + $"\"{ nameof(TagHelperDescriptor.DesignTimeDescriptor) }\":null}}"; var expectedDescriptor = new TagHelperDescriptor { Prefix = "prefix:", TagName = "tag name", TypeName = "type name", AssemblyName = "assembly name", Attributes = new[] { new TagHelperAttributeDescriptor { Name = "attribute one", PropertyName = "property name", TypeName = "property type name" }, new TagHelperAttributeDescriptor { Name = "attribute two", PropertyName = "property name", TypeName = typeof(string).FullName }, }, AllowedChildren = new[] { "allowed child one", "allowed child two" } }; // Act var descriptor = JsonConvert.DeserializeObject<TagHelperDescriptor>(serializedDescriptor); // Assert Assert.NotNull(descriptor); Assert.Equal(expectedDescriptor.Prefix, descriptor.Prefix, StringComparer.Ordinal); Assert.Equal(expectedDescriptor.TagName, descriptor.TagName, StringComparer.Ordinal); Assert.Equal(expectedDescriptor.FullTagName, descriptor.FullTagName, StringComparer.Ordinal); Assert.Equal(expectedDescriptor.TypeName, descriptor.TypeName, StringComparer.Ordinal); Assert.Equal(expectedDescriptor.AssemblyName, descriptor.AssemblyName, StringComparer.Ordinal); Assert.Equal(expectedDescriptor.Attributes, descriptor.Attributes, TagHelperAttributeDescriptorComparer.Default); Assert.Empty(descriptor.RequiredAttributes); }
public void TagHelperParseTreeRewriter_CreatesMarkupCodeSpansForNonStringTagHelperAttributes( string documentContent, MarkupBlock expectedOutput) { // Arrange var descriptors = new TagHelperDescriptor[] { new TagHelperDescriptor("person", "PersonTagHelper", "personAssembly", attributes: new[] { new TagHelperAttributeDescriptor("age", "Age", typeof(int).FullName, isIndexer: false), new TagHelperAttributeDescriptor( "birthday", "BirthDay", typeof(DateTime).FullName, isIndexer: false), new TagHelperAttributeDescriptor("name", "Name", typeof(string).FullName, isIndexer: false), }) }; var providerContext = new TagHelperDescriptorProvider(descriptors); // Act & Assert EvaluateData(providerContext, documentContent, expectedOutput, expectedErrors: Enumerable.Empty<RazorError>()); }
public void Rewrite_CreatesErrorForEmptyTagHelperBoundAttributes( string documentContent, MarkupBlock expectedOutput, RazorError[] expectedErrors) { // Arrange var descriptors = new TagHelperDescriptor[] { new TagHelperDescriptor { TagName = "myth", TypeName = "mythTagHelper", AssemblyName = "SomeAssembly", Attributes = new[] { new TagHelperAttributeDescriptor { Name = "bound", PropertyName = "Bound", TypeName = typeof(bool).FullName }, new TagHelperAttributeDescriptor { Name = "name", PropertyName = "Name", TypeName = typeof(string).FullName } } } }; var descriptorProvider = new TagHelperDescriptorProvider(descriptors); // Act & Assert EvaluateData(descriptorProvider, documentContent, expectedOutput, expectedErrors); }
public void TagHelpers_WithinHelpersAndSections_GeneratesExpectedOutput() { // Arrange var propertyInfo = typeof(TestType).GetProperty("BoundProperty"); var tagHelperDescriptors = new TagHelperDescriptor[] { new TagHelperDescriptor { TagName = "MyTagHelper", TypeName = "MyTagHelper", AssemblyName = "SomeAssembly", Attributes = new [] { new TagHelperAttributeDescriptor("BoundProperty", propertyInfo) } }, new TagHelperDescriptor { TagName = "NestedTagHelper", TypeName = "NestedTagHelper", AssemblyName = "SomeAssembly" } }; // Act & Assert RunTagHelperTest("TagHelpersInSection", tagHelperDescriptors: tagHelperDescriptors); }
public void Rewrite_UnderstandsMinimizedAttributes( string documentContent, MarkupBlock expectedOutput, RazorError[] expectedErrors) { // Arrange var descriptors = new TagHelperDescriptor[] { new TagHelperDescriptor { TagName = "input", TypeName = "InputTagHelper1", AssemblyName = "SomeAssembly", Attributes = new[] { new TagHelperAttributeDescriptor { Name = "bound-required-string", PropertyName = "BoundRequiredString", TypeName = typeof(string).FullName } }, RequiredAttributes = new[] { "unbound-required" } }, new TagHelperDescriptor { TagName = "input", TypeName = "InputTagHelper1", AssemblyName = "SomeAssembly", Attributes = new[] { new TagHelperAttributeDescriptor { Name = "bound-required-string", PropertyName = "BoundRequiredString", TypeName = typeof(string).FullName } }, RequiredAttributes = new[] { "bound-required-string" } }, new TagHelperDescriptor { TagName = "input", TypeName = "InputTagHelper2", AssemblyName = "SomeAssembly", Attributes = new[] { new TagHelperAttributeDescriptor { Name = "bound-required-int", PropertyName = "BoundRequiredInt", TypeName = typeof(int).FullName } }, RequiredAttributes = new[] { "bound-required-int" } }, new TagHelperDescriptor { TagName = "input", TypeName = "InputTagHelper3", AssemblyName = "SomeAssembly", Attributes = new[] { new TagHelperAttributeDescriptor { Name = "int-dictionary", PropertyName ="DictionaryOfIntProperty", TypeName = typeof(IDictionary<string, int>).FullName }, new TagHelperAttributeDescriptor { Name = "string-dictionary", PropertyName = "DictionaryOfStringProperty", TypeName = typeof(IDictionary<string, string>).FullName }, new TagHelperAttributeDescriptor { Name = "int-prefix-", PropertyName = "DictionaryOfIntProperty", TypeName = typeof(int).FullName, IsIndexer = true }, new TagHelperAttributeDescriptor { Name = "string-prefix-", PropertyName = "DictionaryOfStringProperty", TypeName = typeof(string).FullName, IsIndexer = true } } }, new TagHelperDescriptor { TagName = "p", TypeName = "PTagHelper", AssemblyName = "SomeAssembly", Attributes = new[] { new TagHelperAttributeDescriptor { Name = "bound-string", PropertyName = "BoundRequiredString", TypeName = typeof(string).FullName }, new TagHelperAttributeDescriptor { Name = "bound-int", PropertyName = "BoundRequiredString", TypeName = typeof(int).FullName } } } }; var descriptorProvider = new TagHelperDescriptorProvider(descriptors); // Act & Assert EvaluateData(descriptorProvider, documentContent, expectedOutput, expectedErrors); }
public void GetDescriptors_ReturnsNothingForUnregisteredTags() { // Arrange var divDescriptor = new TagHelperDescriptor { TagName = "div", TypeName = "foo1", AssemblyName = "SomeAssembly", }; var spanDescriptor = new TagHelperDescriptor { TagName = "span", TypeName = "foo2", AssemblyName = "SomeAssembly", }; var descriptors = new TagHelperDescriptor[] { divDescriptor, spanDescriptor }; var provider = new TagHelperDescriptorProvider(descriptors); // Act var retrievedDescriptors = provider.GetDescriptors("foo", attributeNames: Enumerable.Empty<string>()); // Assert Assert.Empty(retrievedDescriptors); }
public void Rewrite_CanHandleWithoutEndTagTagStructure(string documentContent, MarkupBlock expectedOutput) { // Arrange var descriptors = new TagHelperDescriptor[] { new TagHelperDescriptor { TagName = "input", TypeName = "InputTagHelper", AssemblyName = "SomeAssembly", TagStructure = TagStructure.WithoutEndTag, } }; var descriptorProvider = new TagHelperDescriptorProvider(descriptors); // Act & Assert EvaluateData(descriptorProvider, documentContent, expectedOutput, expectedErrors: new RazorError[0]); }
public void GetDescriptors_DuplicateDescriptorsAreNotPartOfTagHelperDescriptorPool() { // Arrange var divDescriptor = new TagHelperDescriptor { TagName = "div", TypeName = "foo1", AssemblyName = "SomeAssembly", }; var descriptors = new TagHelperDescriptor[] { divDescriptor, divDescriptor }; var provider = new TagHelperDescriptorProvider(descriptors); // Act var retrievedDescriptors = provider.GetDescriptors("div", attributeNames: Enumerable.Empty<string>()); // Assert var descriptor = Assert.Single(retrievedDescriptors); Assert.Same(divDescriptor, descriptor); }
public void DescriptorResolver_ResolvesMultipleTypes() { // Arrange var resolver = new TestTagHelperDescriptorResolver( new LookupBasedTagHelperTypeResolver( new Dictionary<string, IEnumerable<Type>>(StringComparer.OrdinalIgnoreCase) { { AssemblyName, new Type[]{ Valid_PlainTagHelperType, Valid_InheritedTagHelperType } }, })); var expectedDescriptors = new TagHelperDescriptor[] { Valid_PlainTagHelperDescriptor, Valid_InheritedTagHelperDescriptor }; // Act var descriptors = resolver.Resolve("*, " + AssemblyName).ToArray(); // Assert Assert.Equal(descriptors.Length, 2); Assert.Equal(expectedDescriptors, descriptors, CaseSensitiveTagHelperDescriptorComparer.Default); }