File: Components\ComponentDuplicateAttributeDiagnosticPassTest.cs
Web Access
Project: src\src\Razor\src\Compiler\Microsoft.AspNetCore.Razor.Language\test\Microsoft.AspNetCore.Razor.Language.UnitTests.csproj (Microsoft.AspNetCore.Razor.Language.UnitTests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Razor.Language.Intermediate;
using Xunit;
 
namespace Microsoft.AspNetCore.Razor.Language.Components;
 
// These tests are really basic and cover a few different structures. The components part of this
// pass is hard to unit test, so component cases are covered by integration tests.
public class ComponentDuplicateAttributeDiagnosticPassTest
{
    public ComponentDuplicateAttributeDiagnosticPassTest()
    {
        ProjectEngine = RazorProjectEngine.Create(
            RazorConfiguration.Default,
            RazorProjectFileSystem.Create(Environment.CurrentDirectory),
            b =>
            {
                // Don't run the markup mutating passes.
                b.Features.Remove(b.Features.OfType<ComponentMarkupDiagnosticPass>().Single());
                b.Features.Remove(b.Features.OfType<ComponentMarkupBlockPass>().Single());
                b.Features.Remove(b.Features.OfType<ComponentMarkupEncodingPass>().Single());
            });
        Engine = ProjectEngine.Engine;
 
        Pass = new ComponentMarkupDiagnosticPass()
        {
            Engine = Engine
        };
    }
 
    private RazorProjectEngine ProjectEngine { get; }
 
    private RazorEngine Engine { get; }
 
    private ComponentMarkupDiagnosticPass Pass { get; }
 
    [Fact]
    public void Execute_NoDuplicates()
    {
        // Arrange
        var document = CreateDocument(@"
<html>
  <head cool=""beans"">
    <div></div>
    <ul a=""d"" b="""" c>
      <li id=""15""></li>
    </ul>
  </head>
</html>");
 
        var documentNode = Lower(document);
 
        // Act
        Pass.Execute(document, documentNode);
 
        // Assert
        Assert.Empty(documentNode.GetAllDiagnostics());
    }
 
    [Fact]
    public void Execute_FindDuplicate()
    {
        // Arrange
        var document = CreateDocument(@"
<html>
  <head cool=""beans"">
    <div></div>
    <ul a=""d"" b="""" c a=""another"">
      <li id=""15""></li>
    </ul>
  </head>
</html>");
 
        var documentNode = Lower(document);
 
        // Act
        Pass.Execute(document, documentNode);
 
        // Assert
        var diagnostic = Assert.Single(documentNode.GetAllDiagnostics());
        Assert.Equal(ComponentDiagnosticFactory.DuplicateMarkupAttribute.Id, diagnostic.Id);
 
        var node = documentNode.FindDescendantNodes<HtmlAttributeIntermediateNode>().Where(n => n.HasDiagnostics).Single();
        Assert.Equal("a", node.AttributeName);
        Assert.Equal(node.Source, diagnostic.Span);
    }
 
    [Fact]
    public void Execute_FindDuplicate_CaseInsensitive()
    {
        // Arrange
        var document = CreateDocument(@"
<html>
  <head cool=""beans"">
    <div></div>
    <ul attr=""d"" b="""" c ATTR=""another"">
      <li id=""15""></li>
    </ul>
  </head>
</html>");
 
        var documentNode = Lower(document);
 
        // Act
        Pass.Execute(document, documentNode);
 
        // Assert
        var diagnostic = Assert.Single(documentNode.GetAllDiagnostics());
        Assert.Equal(ComponentDiagnosticFactory.DuplicateMarkupAttribute.Id, diagnostic.Id);
 
        var node = documentNode.FindDescendantNodes<HtmlAttributeIntermediateNode>().Where(n => n.HasDiagnostics).Single();
        Assert.Equal("attr", node.AttributeName);
        Assert.Equal(node.Source, diagnostic.Span);
    }
 
    [Fact]
    public void Execute_FindDuplicate_Multiple()
    {
        // Arrange
        var document = CreateDocument(@"
<html>
  <head cool=""beans"">
    <div></div>
    <ul attr=""d"" b="""" c attr=""another"" attr>
      <li id=""15""></li>
    </ul>
  </head>
</html>");
 
        var documentNode = Lower(document);
 
        // Act
        Pass.Execute(document, documentNode);
 
        // Assert
        var diagnostics = documentNode.GetAllDiagnostics();
        var nodes = documentNode.FindDescendantNodes<HtmlAttributeIntermediateNode>().Where(n => n.HasDiagnostics).ToArray();
 
        Assert.Equal(2, diagnostics.Length);
        Assert.Equal(2, nodes.Length);
 
        for (var i = 0; i < 2; i++)
        {
            var diagnostic = diagnostics[i];
            var node = nodes[i];
 
            Assert.Equal(ComponentDiagnosticFactory.DuplicateMarkupAttribute.Id, diagnostic.Id);
            Assert.Equal("attr", node.AttributeName);
            Assert.Equal(node.Source, diagnostic.Span);
        }
    }
 
    private RazorCodeDocument CreateDocument(string content)
    {
        // Normalize newlines since we are testing lengths of things.
        content = content.Replace("\r", "");
        content = content.Replace("\n", "\r\n");
 
        var source = RazorSourceDocument.Create(content, "test.cshtml");
        return ProjectEngine.CreateCodeDocument(source, RazorFileKind.Component);
    }
 
    private DocumentIntermediateNode Lower(RazorCodeDocument codeDocument)
    {
        foreach (var phase in Engine.Phases)
        {
            if (phase is IRazorCSharpLoweringPhase)
            {
                break;
            }
 
            codeDocument = phase.Execute(codeDocument);
        }
 
        var document = codeDocument.GetRequiredDocumentNode();
        Engine.GetFeatures<ComponentDocumentClassifierPass>().Single().Execute(codeDocument, document);
        return document;
    }
}