File: DocumentClassifierPassBaseTest.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.Immutable;
using Microsoft.AspNetCore.Razor.Language.CodeGeneration;
using Microsoft.AspNetCore.Razor.Language.Intermediate;
using Xunit;
using static Microsoft.AspNetCore.Razor.Language.Intermediate.IntermediateNodeAssert;
 
namespace Microsoft.AspNetCore.Razor.Language;
 
public class DocumentClassifierPassBaseTest : RazorProjectEngineTestBase
{
    protected override RazorLanguageVersion Version => RazorLanguageVersion.Latest;
 
    [Fact]
    public void Execute_HasDocumentKind_IgnoresDocument()
    {
        // Arrange
        var codeDocument = ProjectEngine.CreateEmptyCodeDocument();
 
        var documentNode = new DocumentIntermediateNode()
        {
            DocumentKind = "ignore",
            Options = codeDocument.CodeGenerationOptions
        };
 
        // Act
        ProjectEngine.ExecutePass<TestDocumentClassifierPass>(codeDocument, documentNode);
 
        // Assert
        Assert.Equal("ignore", documentNode.DocumentKind);
        NoChildren(documentNode);
    }
 
    [Fact]
    public void Execute_NoMatch_IgnoresDocument()
    {
        // Arrange
        var codeDocument = ProjectEngine.CreateEmptyCodeDocument();
 
        var documentNode = new DocumentIntermediateNode()
        {
            Options = codeDocument.CodeGenerationOptions
        };
 
        // Act
        ProjectEngine.ExecutePass<TestDocumentClassifierPass>(codeDocument, documentNode, () => new() { ShouldMatch = false });
 
        // Assert
        Assert.Null(documentNode.DocumentKind);
        NoChildren(documentNode);
    }
 
    [Fact]
    public void Execute_Match_AddsGlobalTargetExtensions()
    {
        // Arrange
        var expected = new ICodeTargetExtension[]
        {
            new MyExtension1(),
            new MyExtension2(),
        };
 
        var projectEngine = RazorProjectEngine.CreateEmpty(builder =>
        {
            foreach (var extension in expected)
            {
                builder.AddTargetExtension(extension);
            }
        });
 
        var codeDocument = projectEngine.CreateEmptyCodeDocument();
 
        var documentNode = new DocumentIntermediateNode()
        {
            Options = codeDocument.CodeGenerationOptions
        };
 
        ImmutableArray<ICodeTargetExtension> extensions = default;
 
        // Act
        projectEngine.ExecutePass<TestDocumentClassifierPass>(codeDocument, documentNode,
            () => new()
            {
                CodeTargetCallback = builder =>
                {
                    extensions = builder.TargetExtensions.ToImmutable();
                }
            });
 
        // Assert
        Assert.Equal(expected, extensions);
    }
 
    [Fact]
    public void Execute_Match_SetsDocumentType_AndCreatesStructure()
    {
        // Arrange
        var codeDocument = ProjectEngine.CreateEmptyCodeDocument();
 
        var documentNode = new DocumentIntermediateNode()
        {
            Options = codeDocument.CodeGenerationOptions
        };
 
        // Act
        ProjectEngine.ExecutePass<TestDocumentClassifierPass>(codeDocument, documentNode);
 
        // Assert
        Assert.Equal("test", documentNode.DocumentKind);
        Assert.NotNull(documentNode.Target);
 
        var @namespace = SingleChild<NamespaceDeclarationIntermediateNode>(documentNode);
        var @class = SingleChild<ClassDeclarationIntermediateNode>(@namespace);
        var method = SingleChild<MethodDeclarationIntermediateNode>(@class);
        NoChildren(method);
    }
 
    [Fact]
    public void Execute_AddsUsingsToNamespace()
    {
        // Arrange
        var codeDocument = ProjectEngine.CreateEmptyCodeDocument();
 
        var documentNode = new DocumentIntermediateNode()
        {
            Options = codeDocument.CodeGenerationOptions
        };
 
        var builder = IntermediateNodeBuilder.Create(documentNode);
        builder.Add(new UsingDirectiveIntermediateNode());
 
        // Act
        ProjectEngine.ExecutePass<TestDocumentClassifierPass>(codeDocument, documentNode);
 
        // Assert
        var @namespace = SingleChild<NamespaceDeclarationIntermediateNode>(documentNode);
        Children(
            @namespace,
            n => Assert.IsType<UsingDirectiveIntermediateNode>(n),
            n => Assert.IsType<ClassDeclarationIntermediateNode>(n));
    }
 
    [Fact]
    public void Execute_AddsTheRestToMethod()
    {
        // Arrange
        var codeDocument = ProjectEngine.CreateEmptyCodeDocument();
 
        var documentNode = new DocumentIntermediateNode()
        {
            Options = codeDocument.CodeGenerationOptions
        };
 
        var builder = IntermediateNodeBuilder.Create(documentNode);
        builder.Add(new HtmlContentIntermediateNode());
        builder.Add(new CSharpCodeIntermediateNode());
 
        // Act
        ProjectEngine.ExecutePass<TestDocumentClassifierPass>(codeDocument, documentNode);
 
        // Assert
        var @namespace = SingleChild<NamespaceDeclarationIntermediateNode>(documentNode);
        var @class = SingleChild<ClassDeclarationIntermediateNode>(@namespace);
        var method = SingleChild<MethodDeclarationIntermediateNode>(@class);
        Children(
            method,
            n => Assert.IsType<HtmlContentIntermediateNode>(n),
            n => Assert.IsType<CSharpCodeIntermediateNode>(n));
    }
 
    [Fact]
    public void Execute_CanInitializeDefaults()
    {
        // Arrange
        var codeDocument = ProjectEngine.CreateEmptyCodeDocument();
 
        var documentNode = new DocumentIntermediateNode()
        {
            Options = codeDocument.CodeGenerationOptions
        };
 
        var builder = IntermediateNodeBuilder.Create(documentNode);
        builder.Add(new HtmlContentIntermediateNode());
        builder.Add(new CSharpCodeIntermediateNode());
 
        // Act
        ProjectEngine.ExecutePass<TestDocumentClassifierPass>(codeDocument, documentNode,
            () => new()
            {
                Namespace = "TestNamespace",
                Class = "TestClass",
                Method = "TestMethod"
            });
 
        // Assert
        var @namespace = SingleChild<NamespaceDeclarationIntermediateNode>(documentNode);
        Assert.Equal("TestNamespace", @namespace.Name);
 
        var @class = SingleChild<ClassDeclarationIntermediateNode>(@namespace);
        Assert.Equal("TestClass", @class.Name);
 
        var method = SingleChild<MethodDeclarationIntermediateNode>(@class);
        Assert.Equal("TestMethod", method.Name);
    }
 
    [Fact]
    public void Execute_AddsPrimaryAnnotations()
    {
        // Arrange
        var codeDocument = ProjectEngine.CreateEmptyCodeDocument();
 
        var documentNode = new DocumentIntermediateNode()
        {
            Options = codeDocument.CodeGenerationOptions
        };
 
        var builder = IntermediateNodeBuilder.Create(documentNode);
        builder.Add(new HtmlContentIntermediateNode());
        builder.Add(new CSharpCodeIntermediateNode());
 
        // Act
        ProjectEngine.ExecutePass<TestDocumentClassifierPass>(codeDocument, documentNode,
            () => new()
            {
                Namespace = "TestNamespace",
                Class = "TestClass",
                Method = "TestMethod"
            });
 
        // Assert
        var @namespace = SingleChild<NamespaceDeclarationIntermediateNode>(documentNode);
        Assert.True(@namespace.IsPrimaryNamespace);
 
        var @class = SingleChild<ClassDeclarationIntermediateNode>(@namespace);
        Assert.True(@class.IsPrimaryClass);
 
        var method = SingleChild<MethodDeclarationIntermediateNode>(@class);
        Assert.True(method.IsPrimaryMethod);
    }
 
    private class TestDocumentClassifierPass : DocumentClassifierPassBase
    {
        public override int Order => DefaultFeatureOrder;
 
        public bool ShouldMatch { get; set; } = true;
 
        public Action<CodeTargetBuilder>? CodeTargetCallback { get; set; }
 
        public string? Namespace { get; set; }
 
        public string? Class { get; set; }
 
        public string? Method { get; set; }
 
        protected override string DocumentKind => "test";
 
        protected override bool IsMatch(RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode)
        {
            return ShouldMatch;
        }
 
        protected override void OnDocumentStructureCreated(
            RazorCodeDocument codeDocument,
            NamespaceDeclarationIntermediateNode @namespace,
            ClassDeclarationIntermediateNode @class,
            MethodDeclarationIntermediateNode method)
        {
            @namespace.Name = Namespace;
            @class.Name = Class;
            @method.Name = Method;
        }
 
        protected override CodeTarget CreateTarget(RazorCodeDocument codeDocument)
            => CodeTarget.CreateDefault(codeDocument, builder =>
            {
                foreach (var extension in TargetExtensions)
                {
                    builder.TargetExtensions.Add(extension);
                }
 
                CodeTargetCallback?.Invoke(builder);
            });
    }
 
    private class MyExtension1 : ICodeTargetExtension
    {
    }
 
    private class MyExtension2 : ICodeTargetExtension
    {
    }
}