Beispiel #1
0
        /// <summary>
        /// Instantiates a new <see cref="RewritingContext"/>.
        /// </summary>
        public RewritingContext(Block syntaxTree, ParserErrorSink errorSink)
        {
            _errors    = new List <RazorError>();
            SyntaxTree = syntaxTree;

            ErrorSink = errorSink;
        }
        public void RenderAttributeValue_RendersModelExpressionsCorrectly(string modelExpressionType,
                                                                          string propertyType,
                                                                          string expectedValue)
        {
            // Arrange
            var renderer = new MvcTagHelperAttributeValueCodeRenderer(
                new GeneratedTagHelperAttributeContext
                {
                    ModelExpressionTypeName = modelExpressionType,
                    CreateModelExpressionMethodName = "SomeMethod"
                });
            var attributeDescriptor = new TagHelperAttributeDescriptor("MyAttribute", "SomeProperty", propertyType);
            var writer = new CSharpCodeWriter();
            var generatorContext = new CodeGeneratorContext(host: null,
                                                            className: string.Empty,
                                                            rootNamespace: string.Empty,
                                                            sourceFile: string.Empty,
                                                            shouldGenerateLinePragmas: true);
            var errorSink = new ParserErrorSink();
            var context = new CodeBuilderContext(generatorContext, errorSink);

            // Act
            renderer.RenderAttributeValue(attributeDescriptor, writer, context,
            (codeWriter) =>
            {
                codeWriter.Write("MyValue");
            },
            complexValue: false);

            // Assert
            Assert.Equal(expectedValue, writer.GenerateCode());
        }
        public void Compile_ReturnsFailedResultIfParseFails()
        {
            // Arrange
            var errorSink = new ParserErrorSink();
            errorSink.OnError(new RazorError("some message", 1, 1, 1, 1));
            var generatorResult = new GeneratorResults(
                    new Block(new BlockBuilder { Type = BlockType.Comment }),
                    Enumerable.Empty<TagHelperDescriptor>(),
                    errorSink,
                    new CodeBuilderResult("", new LineMapping[0]),
                    new CodeTree());
            var host = new Mock<IMvcRazorHost>();
            host.Setup(h => h.GenerateCode(It.IsAny<string>(), It.IsAny<Stream>()))
                .Returns(generatorResult)
                .Verifiable();

            var fileInfo = new Mock<IFileInfo>();
            fileInfo.Setup(f => f.CreateReadStream())
                    .Returns(Stream.Null);

            var compiler = new Mock<ICompilationService>(MockBehavior.Strict);
            var relativeFileInfo = new RelativeFileInfo(fileInfo.Object, @"Views\index\home.cshtml");
            var razorService = new RazorCompilationService(compiler.Object, host.Object);

            // Act
            var result = razorService.Compile(relativeFileInfo);

            // Assert
            Assert.NotNull(result.CompilationFailure);
            var message = Assert.Single(result.CompilationFailure.Messages);
            Assert.Equal("some message", message.Message);
            host.Verify();
        }
Beispiel #4
0
        /// <summary>
        /// Instantiates a new <see cref="RewritingContext"/>.
        /// </summary>
        public RewritingContext(Block syntaxTree, ParserErrorSink errorSink)
        {
            _errors = new List<RazorError>();
            SyntaxTree = syntaxTree;

            ErrorSink = errorSink;
        }
Beispiel #5
0
        /// <summary>
        /// Returns a sequence of <see cref="TagHelperDescriptor"/>s for tag helpers that are registered in the
        /// specified <paramref name="documentRoot"/>.
        /// </summary>
        /// <param name="documentRoot">The <see cref="Block"/> to scan for tag helper registrations in.</param>
        /// <param name="errorSink">Used to manage <see cref="RazorError"/>s encountered during the Razor parsing
        /// phase.</param>
        /// <returns><see cref="TagHelperDescriptor"/>s that are applicable to the <paramref name="documentRoot"/>
        /// </returns>
        protected virtual IEnumerable <TagHelperDescriptor> GetTagHelperDescriptors([NotNull] Block documentRoot,
                                                                                    [NotNull] ParserErrorSink errorSink)
        {
            var addOrRemoveTagHelperSpanVisitor =
                new AddOrRemoveTagHelperSpanVisitor(TagHelperDescriptorResolver, errorSink);

            return(addOrRemoveTagHelperSpanVisitor.GetDescriptors(documentRoot));
        }
        private static IEnumerable<TargetElementAttribute> GetValidTargetElementAttributes(
            TypeInfo typeInfo,
            ParserErrorSink errorSink)
        {
            var targetElementAttributes = typeInfo.GetCustomAttributes<TargetElementAttribute>(inherit: false);

            return targetElementAttributes.Where(attribute => ValidTargetElementAttributeNames(attribute, errorSink));
        }
Beispiel #7
0
        private ParserResults ParseCore(ITextDocument input)
        {
            // Setup the parser context
            var errorSink = new ParserErrorSink();
            var context   = new ParserContext(input, CodeParser, MarkupParser, MarkupParser, errorSink)
            {
                DesignTimeMode = DesignTimeMode
            };

            MarkupParser.Context = context;
            CodeParser.Context   = context;

            // Execute the parse
            MarkupParser.ParseDocument();

            // Get the result
            var results = context.CompleteParse();

            // Rewrite whitespace if supported
            var rewritingContext = new RewritingContext(results.Document, errorSink);

            foreach (ISyntaxTreeRewriter rewriter in Optimizers)
            {
                rewriter.Rewrite(rewritingContext);
            }

            var descriptors = Enumerable.Empty <TagHelperDescriptor>();

            if (TagHelperDescriptorResolver != null)
            {
                descriptors = GetTagHelperDescriptors(rewritingContext.SyntaxTree, rewritingContext.ErrorSink);
                var tagHelperProvider = new TagHelperDescriptorProvider(descriptors);

                var tagHelperParseTreeRewriter = new TagHelperParseTreeRewriter(tagHelperProvider);
                // Rewrite the document to utilize tag helpers
                tagHelperParseTreeRewriter.Rewrite(rewritingContext);
            }

            var syntaxTree = rewritingContext.SyntaxTree;

            // Link the leaf nodes into a chain
            Span prev = null;

            foreach (Span node in syntaxTree.Flatten())
            {
                node.Previous = prev;
                if (prev != null)
                {
                    prev.Next = node;
                }
                prev = node;
            }

            // Return the new result
            return(new ParserResults(syntaxTree, descriptors, errorSink));
        }
        public void VisitThrowsOnNullVisitor()
        {
            ParserVisitor target = null;
            var errorSink = new ParserErrorSink();
            var results = new ParserResults(new BlockBuilder() { Type = BlockType.Comment }.Build(),
                                            Enumerable.Empty<TagHelperDescriptor>(),
                                            errorSink);

            Assert.Throws<ArgumentNullException>("self", () => target.Visit(results));
        }
Beispiel #9
0
 public void ConstructorAcceptsActiveParserIfIsSameAsEitherCodeOrMarkupParser()
 {
     var codeParser = new CSharpCodeParser();
     var markupParser = new HtmlMarkupParser();
     var errorSink = new ParserErrorSink();
     new ParserContext(
         new SeekableTextReader(TextReader.Null), codeParser, markupParser, codeParser, errorSink);
     new ParserContext(
         new SeekableTextReader(TextReader.Null), codeParser, markupParser, markupParser, errorSink);
 }
Beispiel #10
0
 // Internal for testing.
 internal CodeBuilderContext(RazorEngineHost host,
                             string className,
                             string rootNamespace,
                             string sourceFile,
                             bool shouldGenerateLinePragmas,
                             ParserErrorSink errorSink)
     : base(host, className, rootNamespace, sourceFile, shouldGenerateLinePragmas)
 {
     ErrorSink = errorSink;
     ExpressionRenderingMode = ExpressionRenderingMode.WriteToOutput;
 }
Beispiel #11
0
 public virtual ParserContext CreateParserContext(ITextDocument input,
                                                  ParserBase codeParser,
                                                  ParserBase markupParser,
                                                  ParserErrorSink errorSink)
 {
     return new ParserContext(input,
                              codeParser,
                              markupParser,
                              SelectActiveParser(codeParser, markupParser),
                              errorSink);
 }
        public void VisitSendsDocumentToVisitor()
        {
            // Arrange
            Mock<ParserVisitor> targetMock = new Mock<ParserVisitor>();
            var root = new BlockBuilder() { Type = BlockType.Comment }.Build();
            var errorSink = new ParserErrorSink();
            var results = new ParserResults(root,
                                            Enumerable.Empty<TagHelperDescriptor>(),
                                            errorSink);

            // Act
            targetMock.Object.Visit(results);

            // Assert
            targetMock.Verify(v => v.VisitBlock(root));
        }
Beispiel #13
0
        public ParserContext([NotNull] ITextDocument source,
                             [NotNull] ParserBase codeParser,
                             [NotNull] ParserBase markupParser,
                             [NotNull] ParserBase activeParser,
                             [NotNull] ParserErrorSink errorSink)
        {
            if (activeParser != codeParser && activeParser != markupParser)
            {
                throw new ArgumentException(RazorResources.ActiveParser_Must_Be_Code_Or_Markup_Parser, "activeParser");
            }

            CaptureOwnerTask();

            Source       = new TextDocumentReader(source);
            CodeParser   = codeParser;
            MarkupParser = markupParser;
            ActiveParser = activeParser;
            _errorSink   = errorSink;
        }
Beispiel #14
0
        public ParserContext([NotNull] ITextDocument source,
                             [NotNull] ParserBase codeParser,
                             [NotNull] ParserBase markupParser,
                             [NotNull] ParserBase activeParser,
                             [NotNull] ParserErrorSink errorSink)
        {
            if (activeParser != codeParser && activeParser != markupParser)
            {
                throw new ArgumentException(RazorResources.ActiveParser_Must_Be_Code_Or_Markup_Parser, "activeParser");
            }

            CaptureOwnerTask();

            Source = new TextDocumentReader(source);
            CodeParser = codeParser;
            MarkupParser = markupParser;
            ActiveParser = activeParser;
            _errorSink = errorSink;
        }
        public void VisitSendsErrorsToVisitor()
        {
            // Arrange
            Mock<ParserVisitor> targetMock = new Mock<ParserVisitor>();
            var root = new BlockBuilder() { Type = BlockType.Comment }.Build();
            var errorSink = new ParserErrorSink();
            List<RazorError> errors = new List<RazorError>
            {
                new RazorError("Foo", 1, 0, 1),
                new RazorError("Bar", 2, 0, 2),
            };
            foreach (var error in errors)
            {
                errorSink.OnError(error);
            }
            var results = new ParserResults(root, Enumerable.Empty<TagHelperDescriptor>(), errorSink);

            // Act
            targetMock.Object.Visit(results);

            // Assert
            targetMock.Verify(v => v.VisitError(errors[0]));
            targetMock.Verify(v => v.VisitError(errors[1]));
        }
        private void EvaluateData(TagHelperDescriptorProvider provider,
                                  string documentContent,
                                  MarkupBlock expectedOutput,
                                  IEnumerable<RazorError> expectedErrors)
        {
            var errorSink = new ParserErrorSink();
            var results = ParseDocument(documentContent, errorSink);
            var rewritingContext = new RewritingContext(results.Document, errorSink);
            new TagHelperParseTreeRewriter(provider).Rewrite(rewritingContext);
            var rewritten = rewritingContext.SyntaxTree;
            var actualErrors = errorSink.Errors.OrderBy(error => error.Location.AbsoluteIndex)
                                               .ToList();

            EvaluateRazorErrors(actualErrors, expectedErrors.ToList());
            EvaluateParseTree(rewritten, expectedOutput);
        }
 public override ParserContext CreateParserContext(ITextDocument input,
                                                   ParserBase codeParser,
                                                   ParserBase markupParser,
                                                   ParserErrorSink errorSink)
 {
     return base.CreateParserContext(input, codeParser, markupParser, errorSink);
 }
Beispiel #18
0
        private ParserResults ParseCore(ITextDocument input)
        {
            // Setup the parser context
            var errorSink = new ParserErrorSink();
            var context = new ParserContext(input, CodeParser, MarkupParser, MarkupParser, errorSink)
            {
                DesignTimeMode = DesignTimeMode
            };

            MarkupParser.Context = context;
            CodeParser.Context = context;

            // Execute the parse
            MarkupParser.ParseDocument();

            // Get the result
            var results = context.CompleteParse();

            // Rewrite whitespace if supported
            var rewritingContext = new RewritingContext(results.Document, errorSink);
            foreach (ISyntaxTreeRewriter rewriter in Optimizers)
            {
                rewriter.Rewrite(rewritingContext);
            }

            var descriptors = Enumerable.Empty<TagHelperDescriptor>();

            if (TagHelperDescriptorResolver != null)
            {
                descriptors = GetTagHelperDescriptors(rewritingContext.SyntaxTree, rewritingContext.ErrorSink);
                var tagHelperProvider = new TagHelperDescriptorProvider(descriptors);

                var tagHelperParseTreeRewriter = new TagHelperParseTreeRewriter(tagHelperProvider);
                // Rewrite the document to utilize tag helpers
                tagHelperParseTreeRewriter.Rewrite(rewritingContext);
            }

            var syntaxTree = rewritingContext.SyntaxTree;

            // Link the leaf nodes into a chain
            Span prev = null;
            foreach (Span node in syntaxTree.Flatten())
            {
                node.Previous = prev;
                if (prev != null)
                {
                    prev.Next = node;
                }
                prev = node;
            }

            // Return the new result
            return new ParserResults(syntaxTree, descriptors, errorSink);
        }
        public void CreateDescriptor_BuildsDescriptorsFromSimpleTypes()
        {
            // Arrange
            var errorSink = new ParserErrorSink();
            var objectAssemblyName = typeof(object).GetTypeInfo().Assembly.GetName().Name;
            var expectedDescriptor =
                new TagHelperDescriptor("object", "System.Object", objectAssemblyName);

            // Act
            var descriptors = TagHelperDescriptorFactory.CreateDescriptors(
                objectAssemblyName,
                typeof(object),
                errorSink);

            // Assert
            Assert.Empty(errorSink.Errors);
            var descriptor = Assert.Single(descriptors);
            Assert.Equal(expectedDescriptor, descriptor, CaseSensitiveTagHelperDescriptorComparer.Default);
        }
Beispiel #20
0
 /// <summary>
 /// Instantiates a new instance of the <see cref="CodeBuilderContext"/> object.
 /// </summary>
 /// <param name="generatorContext">A <see cref="CodeGeneratorContext"/> to copy information from.</param>
 /// <param name="errorSink">
 /// The <see cref="ParserErrorSink"/> used to collect <see cref="Parser.SyntaxTree.RazorError"/>s encountered
 /// when parsing the current Razor document.
 /// </param>
 public CodeBuilderContext(CodeGeneratorContext generatorContext, ParserErrorSink errorSink)
     : base(generatorContext)
 {
     ErrorSink = errorSink;
     ExpressionRenderingMode = ExpressionRenderingMode.WriteToOutput;
 }
Beispiel #21
0
            protected override TagHelperDescriptorResolutionContext GetTagHelperDescriptorResolutionContext(
                IEnumerable<TagHelperDirectiveDescriptor> descriptors,
                ParserErrorSink errorSink)
            {
                var directivesToImport = MergeDirectiveDescriptors(descriptors, _globalImportDirectiveDescriptors);

                return base.GetTagHelperDescriptorResolutionContext(directivesToImport, errorSink);
            }
Beispiel #22
0
 protected virtual ParserResults ParseDocument(string document,
                                               bool designTimeParser,
                                               ParserErrorSink errorSink)
 {
     return RunParse(document,
                     parser => parser.ParseDocument,
                     designTimeParser,
                     parserSelector: c => c.MarkupParser,
                     errorSink: errorSink);
 }
Beispiel #23
0
        protected virtual ParserResults RunParse(string document,
                                                 Func<ParserBase, Action> parserActionSelector,
                                                 bool designTimeParser,
                                                 Func<ParserContext, ParserBase> parserSelector = null,
                                                 ParserErrorSink errorSink = null)
        {
            parserSelector = parserSelector ?? (c => c.ActiveParser);
            errorSink = errorSink ?? new ParserErrorSink();

            // Create the source
            ParserResults results = null;
            using (var reader = new SeekableTextReader(document))
            {
                try
                {
                    var codeParser = CreateCodeParser();
                    var markupParser = CreateMarkupParser();
                    var context = CreateParserContext(reader, codeParser, markupParser, errorSink);
                    context.DesignTimeMode = designTimeParser;

                    codeParser.Context = context;
                    markupParser.Context = context;

                    // Run the parser
                    parserActionSelector(parserSelector(context))();
                    results = context.CompleteParse();
                }
                finally
                {
                    if (results != null && results.Document != null)
                    {
                        WriteTraceLine(String.Empty);
                        WriteTraceLine("Actual Parse Tree:");
                        WriteNode(0, results.Document);
                    }
                }
            }
            return results;
        }
        public void ValidTargetElementAttributeNames_CreatesErrorOnInvalidNames(
            string name, string[] expectedErrorMessages)
        {
            // Arrange
            var errorSink = new ParserErrorSink();
            var attribute = new TargetElementAttribute(name);

            // Act
            TagHelperDescriptorFactory.ValidTargetElementAttributeNames(attribute, errorSink);

            // Assert
            var errors = errorSink.Errors.ToArray();
            for (var i = 0; i < errors.Length; i++)
            {
                Assert.Equal(expectedErrorMessages[i], errors[i].Message);
                Assert.Equal(SourceLocation.Zero, errors[i].Location);
            }
        }
        public void CreateDescriptor_OnlyAcceptsPropertiesWithPublicGetAndSet()
        {
            // Arrange
            var errorSink = new ParserErrorSink();
            var validProperty = typeof(PrivateAccessorTagHelper).GetProperty(
                nameof(PrivateAccessorTagHelper.ValidAttribute));
            var expectedDescriptor = new TagHelperDescriptor(
                "private-accessor",
                typeof(PrivateAccessorTagHelper).FullName,
                AssemblyName,
                new[] {
                    new TagHelperAttributeDescriptor(
                        "valid-attribute", validProperty)
                });

            // Act
            var descriptors = TagHelperDescriptorFactory.CreateDescriptors(
                AssemblyName,
                typeof(PrivateAccessorTagHelper),
                errorSink);

            // Assert
            Assert.Empty(errorSink.Errors);
            var descriptor = Assert.Single(descriptors);
            Assert.Equal(expectedDescriptor, descriptor, CaseSensitiveTagHelperDescriptorComparer.Default);
        }
        public void CreateDescriptor_DoesntResolveInheritedTagNames()
        {
            // Arrange
            var errorSink = new ParserErrorSink();
            var validProp = typeof(InheritedMultiTagTagHelper).GetProperty(nameof(InheritedMultiTagTagHelper.ValidAttribute));
            var expectedDescriptor = new TagHelperDescriptor(
                    "inherited-multi-tag",
                    typeof(InheritedMultiTagTagHelper).FullName,
                    AssemblyName,
                    new[] {
                        new TagHelperAttributeDescriptor("valid-attribute", validProp)
                    });

            // Act
            var descriptors = TagHelperDescriptorFactory.CreateDescriptors(
                AssemblyName,
                typeof(InheritedMultiTagTagHelper),
                errorSink);

            // Assert
            Assert.Empty(errorSink.Errors);
            var descriptor = Assert.Single(descriptors);
            Assert.Equal(expectedDescriptor, descriptor, CaseSensitiveTagHelperDescriptorComparer.Default);
        }
        /// <summary>
        /// Internal for testing.
        /// </summary>
        internal static bool ValidTargetElementAttributeNames(
            TargetElementAttribute attribute,
            ParserErrorSink errorSink)
        {
            var validTagName = ValidateName(attribute.Tag, targetingAttributes: false, errorSink: errorSink);
            var validAttributeNames = true;
            var attributeNames = GetCommaSeparatedValues(attribute.Attributes);

            foreach (var attributeName in attributeNames)
            {
                if (!ValidateName(attributeName, targetingAttributes: true, errorSink: errorSink))
                {
                    validAttributeNames = false;
                }
            }

            return validTagName && validAttributeNames;
        }
        public void CreateDescriptor_OverridesTagNameFromAttribute()
        {
            // Arrange
            var errorSink = new ParserErrorSink();
            var expectedDescriptors = new[] {
                new TagHelperDescriptor("data-condition",
                                        typeof(OverrideNameTagHelper).FullName,
                                        AssemblyName),
            };

            // Act
            var descriptors = TagHelperDescriptorFactory.CreateDescriptors(
                AssemblyName,
                typeof(OverrideNameTagHelper),
                errorSink);

            // Assert
            Assert.Empty(errorSink.Errors);
            Assert.Equal(expectedDescriptors, descriptors, CaseSensitiveTagHelperDescriptorComparer.Default);
        }
        private static bool ValidateName(
            string name,
            bool targetingAttributes,
            ParserErrorSink errorSink)
        {
            var targetName = targetingAttributes ?
                Resources.TagHelperDescriptorFactory_Attribute :
                Resources.TagHelperDescriptorFactory_Tag;
            var validName = true;

            if (string.IsNullOrWhiteSpace(name))
            {
                errorSink.OnError(
                    SourceLocation.Zero,
                    Resources.FormatTargetElementAttribute_NameCannotBeNullOrWhitespace(targetName));

                validName = false;
            }
            else
            {
                foreach (var character in name)
                {
                    if (char.IsWhiteSpace(character) ||
                        InvalidNonWhitespaceNameCharacters.Contains(character))
                    {
                        errorSink.OnError(
                            SourceLocation.Zero,
                            Resources.FormatTargetElementAttribute_InvalidName(
                                targetName.ToLower(),
                                name,
                                character));

                        validName = false;
                    }
                }
            }

            return validName;
        }
        public void CreateDescriptor_BuildsDescriptorsWithConventionNames()
        {
            // Arrange
            var errorSink = new ParserErrorSink();
            var intProperty = typeof(SingleAttributeTagHelper).GetProperty(nameof(SingleAttributeTagHelper.IntAttribute));
            var expectedDescriptor = new TagHelperDescriptor(
                "single-attribute",
                typeof(SingleAttributeTagHelper).FullName,
                AssemblyName,
                new[] {
                    new TagHelperAttributeDescriptor("int-attribute", intProperty)
                });

            // Act
            var descriptors = TagHelperDescriptorFactory.CreateDescriptors(
                AssemblyName,
                typeof(SingleAttributeTagHelper),
                new ParserErrorSink());

            // Assert
            Assert.Empty(errorSink.Errors);
            var descriptor = Assert.Single(descriptors);
            Assert.Equal(expectedDescriptor, descriptor, CaseSensitiveTagHelperDescriptorComparer.Default);
        }
Beispiel #31
0
 public GlobalImportTagHelperDirectiveSpanVisitor(
     ITagHelperDescriptorResolver descriptorResolver,
     IEnumerable<TagHelperDirectiveDescriptor> globalImportDirectiveDescriptors,
     ParserErrorSink errorSink)
     : base(descriptorResolver, errorSink)
 {
     _globalImportDirectiveDescriptors = globalImportDirectiveDescriptors;
 }
        public void CreateDescriptor_ResolvesMultipleTagHelperDescriptorsFromSingleType()
        {
            // Arrange
            var errorSink = new ParserErrorSink();
            var validProp = typeof(MultiTagTagHelper).GetProperty(nameof(MultiTagTagHelper.ValidAttribute));
            var expectedDescriptors = new[] {
                new TagHelperDescriptor(
                    "p",
                    typeof(MultiTagTagHelper).FullName,
                    AssemblyName,
                    new[] {
                        new TagHelperAttributeDescriptor("valid-attribute", validProp)
                    }),
                new TagHelperDescriptor(
                    "div",
                    typeof(MultiTagTagHelper).FullName,
                    AssemblyName,
                    new[] {
                        new TagHelperAttributeDescriptor("valid-attribute", validProp)
                    })
            };

            // Act
            var descriptors = TagHelperDescriptorFactory.CreateDescriptors(
                AssemblyName,
                typeof(MultiTagTagHelper),
                errorSink);

            // Assert
            Assert.Empty(errorSink.Errors);
            Assert.Equal(expectedDescriptors, descriptors, CaseSensitiveTagHelperDescriptorComparer.Default);
        }
Beispiel #33
0
 protected virtual ParserResults ParseDocument(string document, ParserErrorSink errorSink)
 {
     return ParseDocument(document, designTimeParser: false, errorSink: errorSink);
 }
        public void CreateDescriptor_IgnoresDuplicateTagNamesFromAttribute()
        {
            // Arrange
            var errorSink = new ParserErrorSink();
            var expectedDescriptors = new[] {
                new TagHelperDescriptor(
                    "p",
                    typeof(DuplicateTagNameTagHelper).FullName,
                    AssemblyName),
                new TagHelperDescriptor(
                    "div",
                    typeof(DuplicateTagNameTagHelper).FullName,
                    AssemblyName)
            };

            // Act
            var descriptors = TagHelperDescriptorFactory.CreateDescriptors(
                AssemblyName,
                typeof(DuplicateTagNameTagHelper),
                errorSink);

            // Assert
            Assert.Empty(errorSink.Errors);
            Assert.Equal(expectedDescriptors, descriptors, CaseSensitiveTagHelperDescriptorComparer.Default);
        }