File: Cohost\CohostWrapWithTagEndpointTest.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;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Test.Common;
using Microsoft.AspNetCore.Razor.Test.Common.Editor;
using Microsoft.CodeAnalysis.ExternalAccess.Razor;
using Microsoft.CodeAnalysis.Razor.Protocol;
using Microsoft.VisualStudio.Razor.LanguageClient.WrapWithTag;
using Roslyn.Test.Utilities;
using Xunit;
using Xunit.Abstractions;
 
namespace Microsoft.VisualStudio.Razor.LanguageClient.Cohost;
 
public class CohostWrapWithTagEndpointTest(ITestOutputHelper testOutputHelper) : CohostEndpointTestBase(testOutputHelper)
{
    [Fact]
    public async Task InsideHtml()
    {
        await VerifyWrapWithTagAsync(
            input: """
                <div>
                    [||]
                </div>
                """,
            expected: """
                <div>
                    <p></p>
                </div>
                """,
            htmlResponse: new VSInternalWrapWithTagResponse(
                LspFactory.CreateSingleLineRange(start: (1, 4), length: 0),
                [LspFactory.CreateTextEdit(position: (1, 4), "<p></p>")]
            ));
    }
 
    [Fact]
    public async Task HtmlInCSharp()
    {
        await VerifyWrapWithTagAsync(
            input: """
                @if (true)
                {
                    [|<p></p>|]
                }
                """,
            expected: """
                @if (true)
                {
                    <div><p></p></div>
                }
                """,
            htmlResponse: new VSInternalWrapWithTagResponse(
                LspFactory.CreateSingleLineRange(start: (1, 4), length: 0),
                [
                    LspFactory.CreateTextEdit(position: (2, 4), "<div>"),
                    LspFactory.CreateTextEdit(position: (2, 11), "</div>")
                ]
            ));
    }
 
    [Fact]
    public async Task HtmlInCSharp_WithWhitespace()
    {
        await VerifyWrapWithTagAsync(
            input: """
                @if (true)
                {
                    [| <p></p> |]
                }
                """,
            expected: """
                @if (true)
                {
                    <div><p></p></div>
                }
                """,
            htmlResponse: new VSInternalWrapWithTagResponse(
                LspFactory.CreateSingleLineRange(start: (1, 4), length: 0),
                [
                    LspFactory.CreateTextEdit(2, 4, 2, 5, "<div>"),
                    LspFactory.CreateTextEdit(2, 12, 2, 13, "</div>")
                ]
            ));
    }
 
    [Fact]
    public async Task NotInHtmlInCSharp_WithNewline()
    {
        await VerifyWrapWithTagAsync(
            input: """
                @if (true)
                {[|
                    <p></p>|]
                }
                """,
            expected: null,
            htmlResponse: null);
    }
 
    [Fact]
    public async Task RazorBlockStart()
    {
        await VerifyWrapWithTagAsync(
            input: """
                [|@if (true) { }
                <div>
                </div>|]
                """,
            expected: """
                <div>
                    @if (true) { }
                    <div>
                    </div>
                </div>
                """,
            htmlResponse: new VSInternalWrapWithTagResponse(
                LspFactory.CreateSingleLineRange(start: (1, 4), length: 0),
                [
                    LspFactory.CreateTextEdit(position: (0, 0), $"<div>{Environment.NewLine}    "),
                    LspFactory.CreateTextEdit(position: (1, 0), "    "),
                    LspFactory.CreateTextEdit(position: (2, 0), "    "),
                    LspFactory.CreateTextEdit(position: (2, 6), $"{Environment.NewLine}</div>")
                ]
            ));
    }
 
    [Fact]
    public async Task NotInCodeBlock()
    {
        await VerifyWrapWithTagAsync(
            input: """
                @code {
                    [||]
                }
                """,
            htmlResponse: null,
            expected: null);
    }
 
    [Fact]
    public async Task ImplicitExpression()
    {
        await VerifyWrapWithTagAsync(
            input: """
                <div>
                    @[||]currentCount
                </div>
                """,
            expected: """
                <div>
                    <span>@currentCount</span>
                </div>
                """,
            htmlResponse: new VSInternalWrapWithTagResponse(
                LspFactory.CreateSingleLineRange(start: (1, 5), length: 13),
                [LspFactory.CreateTextEdit(1, 4, 1, 17, "<span>@currentCount</span>")]
            ));
    }
 
    [Fact]
    public async Task HtmlWithTildes()
    {
        await VerifyWrapWithTagAsync(
            input: """
                <div>
                    @[||]currentCount
                </div>
                """,
            htmlDocument: """
                <div>
                    /*~~~~~~~~~*/
                </div>
                """,
            expected: """
                <div>
                    <span>@currentCount</span>
                </div>
                """,
            htmlResponse: new VSInternalWrapWithTagResponse(
                LspFactory.CreateSingleLineRange(start: (1, 5), length: 13),
                [LspFactory.CreateTextEdit(1, 4, 1, 17, "<span>/*~~~~~~~~~*/</span>")]
            ));
    }
 
    private async Task VerifyWrapWithTagAsync(TestCode input, string? expected, VSInternalWrapWithTagResponse? htmlResponse, string? htmlDocument = null)
    {
        var document = CreateProjectAndRazorDocument(input.Text);
        var sourceText = await document.GetTextAsync(DisposalToken);
 
        var requestInvoker = new TestHtmlRequestInvoker([(LanguageServerConstants.RazorWrapWithTagEndpoint, htmlResponse)]);
 
        var documentUri = document.CreateUri();
        var documentManager = new TestDocumentManager();
        if (htmlDocument is not null)
        {
            var snapshot = new StringTextSnapshot(htmlDocument);
            var buffer = new TestTextBuffer(snapshot);
            var htmlSnapshot = new HtmlVirtualDocumentSnapshot(documentUri, snapshot, hostDocumentSyncVersion: 1, state: null);
            var documentSnapshot = new TestLSPDocumentSnapshot(documentUri, version: 1, htmlSnapshot);
            documentManager.AddDocument(documentUri, documentSnapshot);
        }
 
        var endpoint = new CohostWrapWithTagEndpoint(RemoteServiceInvoker, requestInvoker, documentManager, IncompatibleProjectService, LoggerFactory);
 
        var request = new VSInternalWrapWithTagParams(
            sourceText.GetRange(input.Span),
            "div",
            new FormattingOptions(),
            new VersionedTextDocumentIdentifier()
            {
                DocumentUri = new(documentUri)
            });
 
        var result = await endpoint.GetTestAccessor().HandleRequestAsync(request, document, DisposalToken);
 
        if (expected is null)
        {
            Assert.Null(result);
        }
        else
        {
            Assert.NotNull(result);
 
            var changedDoc = sourceText.WithChanges(result.TextEdits.Select(sourceText.GetTextChange));
            AssertEx.EqualOrDiff(expected, changedDoc.ToString());
        }
    }
}