File: FindAllReferencesTests.cs
Web Access
Project: src\src\Razor\src\Razor\test\Microsoft.VisualStudio.Razor.IntegrationTests\Microsoft.VisualStudio.Razor.IntegrationTests.csproj (Microsoft.VisualStudio.Razor.IntegrationTests)
// 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.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.VisualStudio.Shell.TableControl;
using Microsoft.VisualStudio.Shell.TableManager;
using Xunit;
using Xunit.Abstractions;
 
namespace Microsoft.VisualStudio.Razor.IntegrationTests;
 
public class FindAllReferencesTests(ITestOutputHelper testOutputHelper) : AbstractRazorEditorTest(testOutputHelper)
{
    [IdeFact]
    public async Task FindAllReferences_CSharpInRazor()
    {
        // Open the file
        await TestServices.SolutionExplorer.OpenFileAsync(RazorProjectConstants.BlazorProjectName, RazorProjectConstants.CounterRazorFile, ControlledHangMitigatingCancellationToken);
 
        await TestServices.Editor.PlaceCaretAsync("IncrementCount", charsOffset: -1, ControlledHangMitigatingCancellationToken);
 
        await TestServices.Editor.WaitForComponentClassificationAsync(ControlledHangMitigatingCancellationToken);
 
        // Act
        await TestServices.Editor.InvokeFindAllReferencesAsync(ControlledHangMitigatingCancellationToken);
 
        // Assert
        var results = await TestServices.FindReferencesWindow.WaitForContentsAsync(ControlledHangMitigatingCancellationToken, expected: 2);
 
        var orderedResults = OrderResults(results);
 
        Assert.Collection(
            orderedResults,
            new Action<TableEntry>[]
            {
                reference =>
                {
                    Assert.Equal(expected: "<button class=\"btn btn-primary\" @onclick=\"IncrementCount\">Click me</button>", actual: reference.Code);
                    Assert.Equal(expected: "Counter.razor", Path.GetFileName(reference.DocumentName));
                },
                reference =>
                {
                    Assert.Equal(expected: "private void IncrementCount()", actual: reference.Code);
                    Assert.Equal(expected: "Counter.razor", Path.GetFileName(reference.DocumentName));
                },
            });
 
        await TestServices.FindReferencesWindow.CloseToolWindowAsync(ControlledHangMitigatingCancellationToken);
    }
 
    [IdeFact]
    public async Task FindAllReferences_Component_FromRazor()
    {
        // Open the file
        await TestServices.SolutionExplorer.OpenFileAsync(RazorProjectConstants.BlazorProjectName, RazorProjectConstants.IndexRazorFile, ControlledHangMitigatingCancellationToken);
 
        await TestServices.Editor.PlaceCaretAsync("Prompt", charsOffset: 0, ControlledHangMitigatingCancellationToken);
 
        await TestServices.Editor.WaitForComponentClassificationAsync(ControlledHangMitigatingCancellationToken);
 
        // Act
        await TestServices.Editor.InvokeFindAllReferencesAsync(ControlledHangMitigatingCancellationToken);
 
        // Assert
        var results = await TestServices.FindReferencesWindow.WaitForContentsAsync(ControlledHangMitigatingCancellationToken, expected: 2);
 
        // Don't care about order, but Assert.Collection does
        var orderedResults = OrderResults(results);
 
        Assert.Collection(
            orderedResults,
            reference =>
            {
                Assert.Equal("Index.razor", reference.DocumentName);
                Assert.Equal("<SurveyPrompt Title=\"How is Blazor working for you?\" />", reference.Code);
            },
            reference =>
            {
                Assert.Equal("SurveyPrompt.razor", reference.DocumentName);
                Assert.Equal("<div class=\"alert alert-secondary mt-4\">", reference.Code);
            }
        );
 
        await TestServices.FindReferencesWindow.CloseToolWindowAsync(ControlledHangMitigatingCancellationToken);
    }
 
    [IdeFact]
    public async Task FindAllReferences_Component_FromCSharp()
    {
        // Open the file
        await TestServices.SolutionExplorer.OpenFileAsync(RazorProjectConstants.BlazorProjectName, "Program.cs", ControlledHangMitigatingCancellationToken);
 
        await TestServices.Editor.SetTextAsync("""
            using BlazorProject.Shared;
 
            typeof(Surv$$eyPrompt).ToString();
            """, ControlledHangMitigatingCancellationToken);
 
        // Act
        await TestServices.Editor.InvokeFindAllReferencesAsync(ControlledHangMitigatingCancellationToken);
 
        // Assert
        var results = await TestServices.FindReferencesWindow.WaitForContentsAsync(ControlledHangMitigatingCancellationToken, expected: 2);
 
        // Don't care about order, but Assert.Collection does
        var orderedResults = OrderResults(results);
 
        Assert.Collection(
            orderedResults,
            reference =>
            {
                Assert.Equal("Index.razor", reference.DocumentName);
                Assert.Equal("<SurveyPrompt Title=\"How is Blazor working for you?\" />", reference.Code);
            },
            reference =>
            {
                Assert.Equal("Program.cs", reference.DocumentName);
                Assert.Equal("typeof(SurveyPrompt).ToString();", reference.Code);
            },
            reference =>
            {
                Assert.Equal("SurveyPrompt.razor", reference.DocumentName);
                Assert.Equal("<div class=\"alert alert-secondary mt-4\">", reference.Code);
            }
        );
 
        await TestServices.FindReferencesWindow.CloseToolWindowAsync(ControlledHangMitigatingCancellationToken);
    }
 
    [IdeFact]
    public async Task FindAllReferences_ComponentAttribute_FromRazor()
    {
        // Open the file
        await TestServices.SolutionExplorer.OpenFileAsync(RazorProjectConstants.BlazorProjectName, RazorProjectConstants.IndexRazorFile, ControlledHangMitigatingCancellationToken);
 
        await TestServices.Editor.PlaceCaretAsync("Title=", charsOffset: -1, ControlledHangMitigatingCancellationToken);
 
        await TestServices.Editor.WaitForComponentClassificationAsync(ControlledHangMitigatingCancellationToken);
 
        // Act
        await TestServices.Editor.InvokeFindAllReferencesAsync(ControlledHangMitigatingCancellationToken);
 
        // Assert
        var results = await TestServices.FindReferencesWindow.WaitForContentsAsync(ControlledHangMitigatingCancellationToken, expected: 3);
 
        // Don't care about order, but Assert.Collection does
        var orderedResults = OrderResults(results);
 
        Assert.Collection(
            orderedResults,
            reference =>
            {
                Assert.Equal("Index.razor", reference.DocumentName);
                Assert.Equal("<SurveyPrompt Title=\"How is Blazor working for you?\" />", reference.Code);
            },
            reference =>
            {
                Assert.Equal("SurveyPrompt.razor", reference.DocumentName);
                Assert.Equal("<strong>@Title</strong>", reference.Code);
            },
            reference =>
            {
                Assert.Equal("SurveyPrompt.razor", reference.DocumentName);
                Assert.Equal("public string? Title { get; set; }", reference.Code);
            }
        );
 
        await TestServices.FindReferencesWindow.CloseToolWindowAsync(ControlledHangMitigatingCancellationToken);
    }
 
    [IdeFact]
    public async Task FindAllReferences_ComponentAttribute_FromCSharpInRazor()
    {
        // Open the file
        await TestServices.SolutionExplorer.OpenFileAsync(RazorProjectConstants.BlazorProjectName, RazorProjectConstants.SurveyPromptFile, ControlledHangMitigatingCancellationToken);
 
        await TestServices.Editor.PlaceCaretAsync("Title", charsOffset: 0, occurrence: 2, extendSelection: false, selectBlock: false, ControlledHangMitigatingCancellationToken);
 
        // This is annoying, but if we do the FAR too quickly, we just get results from the current file
        await Task.Delay(500);
 
        // Act
        await TestServices.Editor.InvokeFindAllReferencesAsync(ControlledHangMitigatingCancellationToken);
 
        // Assert
        var results = await TestServices.FindReferencesWindow.WaitForContentsAsync(ControlledHangMitigatingCancellationToken, expected: 3);
 
        var orderedResults = OrderResults(results);
 
        Assert.Collection(
            orderedResults,
            reference =>
            {
                Assert.Equal(expected: "Index.razor", reference.DocumentName);
                Assert.Equal(expected: "<SurveyPrompt Title=\"How is Blazor working for you?\" />", reference.Code);
            },
            reference =>
            {
                Assert.Equal(expected: "SurveyPrompt.razor", reference.DocumentName);
                Assert.Equal(expected: "<strong>@Title</strong>", reference.Code);
            },
            reference =>
            {
                Assert.Equal(expected: "SurveyPrompt.razor", reference.DocumentName);
                Assert.Equal(expected: "public string? Title { get; set; }", reference.Code);
            }
        );
 
        await TestServices.FindReferencesWindow.CloseToolWindowAsync(ControlledHangMitigatingCancellationToken);
    }
 
    [IdeFact]
    public async Task FindAllReferences_ComponentAttribute_FromCSharpInCSharp()
    {
        // Create the file
        await TestServices.SolutionExplorer.AddFileAsync(RazorProjectConstants.BlazorProjectName,
            "MyComponent.razor",
            """
                @MyProperty
                """,
            open: false,
            cancellationToken: ControlledHangMitigatingCancellationToken);
 
        await TestServices.SolutionExplorer.AddFileAsync(RazorProjectConstants.BlazorProjectName,
            "MyComponent.razor.cs",
            """
                namespace BlazorProject;
 
                public partial class MyComponent
                {
                    [Microsoft.AspNetCore.Components.ParameterAttribute]
                    public string? MyProperty { get; set; }
                }
 
                """,
            open: false,
            cancellationToken: ControlledHangMitigatingCancellationToken);
 
        await TestServices.SolutionExplorer.AddFileAsync(RazorProjectConstants.BlazorProjectName,
            "MyPage.razor",
            """
                <MyComponent MyProperty="123" />
                """,
            open: true,
            cancellationToken: ControlledHangMitigatingCancellationToken);
 
        await TestServices.Editor.WaitForComponentClassificationAsync(ControlledHangMitigatingCancellationToken);
 
        await TestServices.SolutionExplorer.OpenFileAsync(RazorProjectConstants.BlazorProjectName, "MyComponent.razor.cs", ControlledHangMitigatingCancellationToken);
 
        await TestServices.Editor.PlaceCaretAsync("MyProperty", charsOffset: -1, ControlledHangMitigatingCancellationToken);
 
        // This is annoying, but if we do the FAR too quickly, we just get one result from the current file
        await Task.Delay(500);
 
        // Act
        await TestServices.Editor.InvokeFindAllReferencesAsync(ControlledHangMitigatingCancellationToken);
 
        // Assert
        var results = await TestServices.FindReferencesWindow.WaitForContentsAsync(ControlledHangMitigatingCancellationToken, expected: 3);
 
        var orderedResults = OrderResults(results);
 
        Assert.Collection(
            orderedResults,
            reference =>
            {
                Assert.Equal(expected: "@MyProperty", actual: reference.Code);
                Assert.Equal(expected: "MyComponent.razor", Path.GetFileName(reference.DocumentName));
            },
            reference =>
            {
                Assert.Equal(expected: "public string? MyProperty { get; set; }", actual: reference.Code);
                Assert.Equal(expected: "MyComponent.razor.cs", Path.GetFileName(reference.DocumentName));
            },
            reference =>
            {
                Assert.Equal(expected: "<MyComponent MyProperty=\"123\" />", actual: reference.Code);
                Assert.Equal(expected: "MyPage.razor", Path.GetFileName(reference.DocumentName));
            });
 
        await TestServices.FindReferencesWindow.CloseToolWindowAsync(ControlledHangMitigatingCancellationToken);
    }
 
    private static IEnumerable<TableEntry> OrderResults(ImmutableArray<ITableEntryHandle2> results)
    {
        var orderedResults = results.Select(r =>
        {
            Assert.True(r.TryGetValue(StandardTableKeyNames.Text, out string code));
            Assert.True(r.TryGetValue(StandardTableKeyNames.DocumentName, out string documentName));
 
            return new TableEntry(code, Path.GetFileName(documentName));
        }).OrderBy(r => r.DocumentName).ThenBy(r => r.Code).ToArray();
 
        return orderedResults;
    }
 
    internal record TableEntry(string Code, string DocumentName);
}