File: Cohost\OrganizeUsingsCommandTest.cs
Web Access
Project: src\src\Razor\src\Razor\test\Microsoft.VisualStudio.LanguageServices.Razor.UnitTests\Microsoft.VisualStudio.LanguageServices.Razor.UnitTests.csproj (Microsoft.VisualStudio.LanguageServices.Razor.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.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.Test.Common;
using Roslyn.Test.Utilities;
using Xunit;
using Xunit.Abstractions;
 
namespace Microsoft.VisualStudio.Razor.LanguageClient.Cohost;
 
public class OrganizeUsingsCommandTest(ITestOutputHelper testOutputHelper) : CohostEndpointTestBase(testOutputHelper)
{
    [Fact]
    public Task SortOnly_NoUnused()
        => VerifyRemoveAndSortUsingsAsync(
            input: """
                @using System.Text
                @using System
                @using Microsoft.AspNetCore.Components.Forms
 
                <div></div>
 
                @code
                {
                    public void M(StringBuilder b)
                    {
                        Console.WriteLine("");
                    }
                }
                """,
            expected: """
                @using System
                @using System.Text
 
                <div></div>
 
                @code
                {
                    public void M(StringBuilder b)
                    {
                        Console.WriteLine("");
                    }
                }
                """);
 
    [Fact]
    public Task RemoveOnly_AlreadySorted()
        => VerifyRemoveAndSortUsingsAsync(
            input: """
                @using System
                @using System.Text
 
                <div></div>
                """,
            expected: """
 
                <div></div>
                """);
 
    [Fact]
    public Task RemoveAndSort_Combined()
        => VerifyRemoveAndSortUsingsAsync(
            input: """
                @using System.Text
                @using System.Buffers
                @using System
 
                <div></div>
 
                @code
                {
                    public void M()
                    {
                        Console.WriteLine("");
                    }
                }
                """,
            expected: """
                @using System
 
                <div></div>
 
                @code
                {
                    public void M()
                    {
                        Console.WriteLine("");
                    }
                }
                """);
 
    [Fact]
    public Task RemoveAndSort_Combined2()
    => VerifyRemoveAndSortUsingsAsync(
        input: """
                @using System.Text
                @using System.Buffers
                @using System
 
                <div></div>
 
                @code
                {
                    public void M(StringBuilder sv)
                    {
                        Console.WriteLine("");
                    }
                }
                """,
        expected: """
                @using System
                @using System.Text
 
                <div></div>
 
                @code
                {
                    public void M(StringBuilder sv)
                    {
                        Console.WriteLine("");
                    }
                }
                """);
 
    [Fact]
    public Task NoUsings_NoEdits()
        => VerifyRemoveAndSortUsingsAsync(
            input: """
                <div>Hello World</div>
                """,
            expected: """
                <div>Hello World</div>
                """);
 
    [Fact]
    public Task AllUnused_AllRemoved()
        => VerifyRemoveAndSortUsingsAsync(
            input: """
                @using System.Text
                @using System.Buffers
 
                <div></div>
                """,
            expected: """
 
                <div></div>
                """);
 
    [Fact]
    public Task SystemUsings_SortedFirst()
        => VerifyRemoveAndSortUsingsAsync(
            input: """
                @using Microsoft.AspNetCore.Components.Forms
                @using System.Text
                @using System
 
                <div></div>
 
                <PageTitle></PageTitle>
 
                @code
                {
                    public void M(StringBuilder b)
                    {
                        Console.WriteLine("");
                    }
                }
                """,
            expected: """
                @using System
                @using System.Text
 
                <div></div>
 
                <PageTitle></PageTitle>
 
                @code
                {
                    public void M(StringBuilder b)
                    {
                        Console.WriteLine("");
                    }
                }
                """);
 
    [Fact]
    public Task SingleUsing_Used_NoChange()
        => VerifyRemoveAndSortUsingsAsync(
            input: """
                @using System
 
                <div></div>
 
                @code
                {
                    public void M()
                    {
                        Console.WriteLine("");
                    }
                }
                """,
            expected: """
                @using System
 
                <div></div>
 
                @code
                {
                    public void M()
                    {
                        Console.WriteLine("");
                    }
                }
                """);
 
    [Fact]
    public Task PageDirective_BeforeUsings()
        => VerifyRemoveAndSortUsingsAsync(
            input: """
                @page "/mypage"
                @using System.Text
                @using System
 
                <div></div>
 
                @code
                {
                    public void M(StringBuilder b)
                    {
                        Console.WriteLine("");
                    }
                }
                """,
            expected: """
                @page "/mypage"
                @using System
                @using System.Text
 
                <div></div>
 
                @code
                {
                    public void M(StringBuilder b)
                    {
                        Console.WriteLine("");
                    }
                }
                """);
 
    [Fact]
    public Task PageDirective_BetweenUsings()
        => VerifyRemoveAndSortUsingsAsync(
            input: """
                @using System.Text
                @page "/mypage"
                @using System
 
                <div></div>
 
                @code
                {
                    public void M(StringBuilder b)
                    {
                        Console.WriteLine("");
                    }
                }
                """,
            expected: """
                @using System
                @using System.Text
                @page "/mypage"
 
                <div></div>
 
                @code
                {
                    public void M(StringBuilder b)
                    {
                        Console.WriteLine("");
                    }
                }
                """);
 
    [Fact]
    public Task UsingsSpreadAcrossFile_UnusedRemoved()
        => VerifyRemoveAndSortUsingsAsync(
            input: """
                @using System.Text
                @using System
 
                <div></div>
 
                @using System.Buffers
 
                @code
                {
                    public void M(StringBuilder b)
                    {
                        Console.WriteLine("");
                    }
                }
                """,
            expected: """
                @using System
                @using System.Text
 
                <div></div>
 
 
                @code
                {
                    public void M(StringBuilder b)
                    {
                        Console.WriteLine("");
                    }
                }
                """);
 
    [Fact]
    public Task UsingsSpreadAcrossFile_AllUsed_ConsolidatedIntoFirstGroup()
        => VerifyRemoveAndSortUsingsAsync(
            input: """
                @using System.Text
                @using System
 
                <div></div>
 
                @using System.Buffers
 
                @code
                {
                    public void M(StringBuilder b, ArrayPool<byte> pool)
                    {
                        Console.WriteLine("");
                    }
                }
                """,
            expected: """
                @using System
                @using System.Buffers
                @using System.Text
 
                <div></div>
 
 
                @code
                {
                    public void M(StringBuilder b, ArrayPool<byte> pool)
                    {
                        Console.WriteLine("");
                    }
                }
                """);
 
    [Fact]
    public Task SystemUsings_SortedBeforeNonSystem()
        => VerifyRemoveAndSortUsingsAsync(
            input: """
                @using My.Custom.Namespace
                @using System.Text
                @using System
 
                <div></div>
 
                <MyComponent />
 
                @code
                {
                    public void M(StringBuilder b)
                    {
                        Console.WriteLine("");
                    }
                }
                """,
            expected: """
                @using System
                @using System.Text
                @using My.Custom.Namespace
 
                <div></div>
 
                <MyComponent />
 
                @code
                {
                    public void M(StringBuilder b)
                    {
                        Console.WriteLine("");
                    }
                }
                """,
            additionalFiles: [
                (FilePath("MyComponent.razor"), """
                    @namespace My.Custom.Namespace
 
                    <div>My Component</div>
                    """)]);
 
    [Fact]
    public Task SortOnly_AlreadySorted()
        => VerifySortUsingsAsync(
            input: """
                @using System
                @using System.Text
 
                <div></div>
 
                @code
                {
                    public void M(StringBuilder b)
                    {
                        Console.WriteLine("");
                    }
                }
                """,
            expected: """
                @using System
                @using System.Text
 
                <div></div>
 
                @code
                {
                    public void M(StringBuilder b)
                    {
                        Console.WriteLine("");
                    }
                }
                """);
 
    [Fact]
    public Task SortOnly_Unsorted()
        => VerifySortUsingsAsync(
            input: """
                @using System.Text
                @using System
 
                <div></div>
 
                @code
                {
                    public void M(StringBuilder b)
                    {
                        Console.WriteLine("");
                    }
                }
                """,
            expected: """
                @using System
                @using System.Text
 
                <div></div>
 
                @code
                {
                    public void M(StringBuilder b)
                    {
                        Console.WriteLine("");
                    }
                }
                """);
 
    [Fact]
    public Task SortOnly_SpreadAcrossFile()
        => VerifySortUsingsAsync(
            input: """
                @using System.Text
                @using System
 
                <div></div>
 
                @using System.Buffers
 
                @code
                {
                    public void M(StringBuilder b, ArrayPool<byte> pool)
                    {
                        Console.WriteLine("");
                    }
                }
                """,
            expected: """
                @using System
                @using System.Buffers
                @using System.Text
 
                <div></div>
 
 
                @code
                {
                    public void M(StringBuilder b, ArrayPool<byte> pool)
                    {
                        Console.WriteLine("");
                    }
                }
                """);
 
    [Fact]
    public Task AlreadySorted_NoUnused()
        => VerifyRemoveAndSortUsingsAsync(
            input: """
                @using System
                @using System.Text
 
                <div></div>
 
                @code
                {
                    public void M(StringBuilder b)
                    {
                        Console.WriteLine("");
                    }
                }
                """,
            expected: """
                @using System
                @using System.Text
 
                <div></div>
 
                @code
                {
                    public void M(StringBuilder b)
                    {
                        Console.WriteLine("");
                    }
                }
                """);
 
    [Fact]
    public Task Legacy_UsingsWithSemicolons()
        => VerifySortUsingsAsync(
            input: """
                @using System.Text;
                @using System;
 
                <div></div>
                """,
            expected: """
                @using System;
                @using System.Text;
 
                <div></div>
                """,
            fileKind: RazorFileKind.Legacy);
 
    [Fact]
    public Task Legacy_UsingsWithSemicolons_MultipleDirectives()
        => VerifySortUsingsAsync(
            input: """
                @using System.Text;
                @using System.Buffers;
                @using System;
 
                <div></div>
                """,
            expected: """
                @using System;
                @using System.Buffers;
                @using System.Text;
 
                <div></div>
                """,
            fileKind: RazorFileKind.Legacy);
 
    [Fact]
    public Task Legacy_AddTagHelper_NotRemoved()
      => VerifyRemoveAndSortUsingsAsync(
          input: """
                @using System.Text.RegularExpressions;
                @addTagHelper *, UnusedAssembly
                @using System
 
                <div></div>
                """,
          expected: """
                @addTagHelper *, UnusedAssembly
 
                <div></div>
                """,
          fileKind: RazorFileKind.Legacy);
 
    [Fact]
    public Task DuplicateUsings_Removed()
        => VerifyRemoveAndSortUsingsAsync(
            input: """
                @using System
                @using System.Text
                @using System
 
                <div></div>
 
                @code
                {
                    public void M(StringBuilder b)
                    {
                        Console.WriteLine("");
                    }
                }
                """,
            expected: """
                @using System
                @using System.Text
 
                <div></div>
 
                @code
                {
                    public void M(StringBuilder b)
                    {
                        Console.WriteLine("");
                    }
                }
                """);
 
    private async Task VerifyRemoveAndSortUsingsAsync(TestCode input, string expected, (string filePath, string contents)[]? additionalFiles = null, RazorFileKind? fileKind = null)
    {
        var document = CreateProjectAndRazorDocument(input.Text, fileKind: fileKind, additionalFiles: additionalFiles);
 
        var command = new OrganizeUsingsCommand(RemoteServiceInvoker);
        var accessor = command.GetTestAccessor();
 
        Assert.True(accessor.QueryRemoveAndSortUsings());
 
        var edits = await accessor.ExecuteRemoveAndSortUsingsAsync(document.Project.Solution, document.Id, DisposalToken);
 
        var sourceText = await document.GetTextAsync(DisposalToken);
        var result = sourceText.WithChanges(edits).ToString();
 
        AssertEx.EqualOrDiff(expected, result);
    }
 
    private async Task VerifySortUsingsAsync(TestCode input, string expected, (string filePath, string contents)[]? additionalFiles = null, RazorFileKind? fileKind = null)
    {
        var document = CreateProjectAndRazorDocument(input.Text, fileKind: fileKind, additionalFiles: additionalFiles);
 
        var command = new OrganizeUsingsCommand(RemoteServiceInvoker);
        var accessor = command.GetTestAccessor();
 
        Assert.True(accessor.QuerySortUsings());
 
        var edits = await accessor.ExecuteSortUsingsAsync(document.Project.Solution, document.Id, DisposalToken);
 
        var sourceText = await document.GetTextAsync(DisposalToken);
        var result = sourceText.WithChanges(edits).ToString();
 
        AssertEx.EqualOrDiff(expected, result);
    }
}