File: GoToDefinitionTests.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.Threading.Tasks;
using Xunit;
using Xunit.Abstractions;
 
namespace Microsoft.VisualStudio.Razor.IntegrationTests;
 
public class GoToDefinitionTests(ITestOutputHelper testOutputHelper) : AbstractRazorEditorTest(testOutputHelper)
{
    [IdeFact]
    public async Task GoToDefinition_MethodInSameFile()
    {
        // Open the file
        await TestServices.SolutionExplorer.OpenFileAsync(RazorProjectConstants.BlazorProjectName, RazorProjectConstants.CounterRazorFile, ControlledHangMitigatingCancellationToken);
 
        await TestServices.Editor.PlaceCaretAsync("IncrementCount", charsOffset: -1, ControlledHangMitigatingCancellationToken);
 
        // Act
        await TestServices.Editor.InvokeGoToDefinitionAsync(ControlledHangMitigatingCancellationToken);
 
        // Assert
        await TestServices.Editor.WaitForCurrentLineTextAsync("private void IncrementCount()", ControlledHangMitigatingCancellationToken);
    }
 
    [IdeFact]
    public async Task GoToDefinition_CSharpClass()
    {
        // Open the file
        await TestServices.SolutionExplorer.OpenFileAsync(RazorProjectConstants.BlazorProjectName, RazorProjectConstants.IndexRazorFile, ControlledHangMitigatingCancellationToken);
 
        // Change text to refer back to Program class
        await TestServices.Editor.SetTextAsync(@"<SurveyPrompt Title=""@nameof(Program)", ControlledHangMitigatingCancellationToken);
        await TestServices.Editor.PlaceCaretAsync("Program", charsOffset: -1, ControlledHangMitigatingCancellationToken);
 
        // Act
        await TestServices.Editor.InvokeGoToDefinitionAsync(ControlledHangMitigatingCancellationToken);
 
        // Assert
        await TestServices.Editor.WaitForActiveWindowAsync("Program.cs", ControlledHangMitigatingCancellationToken);
 
        await TestServices.Editor.CloseCodeFileAsync(RazorProjectConstants.BlazorProjectName, "Program.cs", saveFile: false, ControlledHangMitigatingCancellationToken);
    }
 
    [IdeFact]
    public async Task GoToDefinition_Component()
    {
        // Open the file
        await TestServices.SolutionExplorer.OpenFileAsync(RazorProjectConstants.BlazorProjectName, RazorProjectConstants.IndexRazorFile, ControlledHangMitigatingCancellationToken);
 
        await TestServices.Editor.PlaceCaretAsync("SurveyPrompt", charsOffset: -1, ControlledHangMitigatingCancellationToken);
 
        // Act
        await TestServices.Editor.InvokeGoToDefinitionAsync(ControlledHangMitigatingCancellationToken);
 
        // Assert
        await TestServices.Editor.WaitForActiveWindowAsync("SurveyPrompt.razor", ControlledHangMitigatingCancellationToken);
    }
 
    [IdeFact]
    public async Task GoToDefinition_ComponentAttribute_InOtherRazorFile()
    {
        // Open the file
        await TestServices.SolutionExplorer.OpenFileAsync(RazorProjectConstants.BlazorProjectName, RazorProjectConstants.IndexRazorFile, ControlledHangMitigatingCancellationToken);
 
        await TestServices.Editor.PlaceCaretAsync("Title=", charsOffset: -1, ControlledHangMitigatingCancellationToken);
 
        // Act
        await TestServices.Editor.InvokeGoToDefinitionAsync(ControlledHangMitigatingCancellationToken);
 
        // Assert
        await TestServices.Editor.WaitForActiveWindowAsync("SurveyPrompt.razor", ControlledHangMitigatingCancellationToken);
        await TestServices.Editor.WaitForCurrentLineTextAsync("public string? Title { get; set; }", ControlledHangMitigatingCancellationToken);
    }
 
    [IdeFact]
    public async Task GoToDefinition_ComponentAttribute_InSameRazorFile()
    {
        // Create the file
        await TestServices.SolutionExplorer.AddFileAsync(RazorProjectConstants.BlazorProjectName,
            "MyComponent.razor",
            """
            <MyComponent MyProperty="123" />
 
            @code {
                [Microsoft.AspNetCore.Components.ParameterAttribute]
                public string? MyProperty { get; set; }
            }
 
            """,
            open: true,
            cancellationToken: ControlledHangMitigatingCancellationToken);
 
        await TestServices.Editor.WaitForComponentClassificationAsync(ControlledHangMitigatingCancellationToken);
 
        await TestServices.Editor.PlaceCaretAsync("MyProperty=", charsOffset: -1, ControlledHangMitigatingCancellationToken);
 
        // Act
        await TestServices.Editor.InvokeGoToDefinitionAsync(ControlledHangMitigatingCancellationToken);
 
        // Assert
        await TestServices.Editor.WaitForActiveWindowAsync("MyComponent.razor", ControlledHangMitigatingCancellationToken);
        await TestServices.Editor.WaitForCurrentLineTextAsync("public string? MyProperty { get; set; }", ControlledHangMitigatingCancellationToken);
    }
 
    [IdeFact]
    public async Task GoToDefinition_ComponentAttribute_Minimized()
    {
        // Create the file
        await TestServices.SolutionExplorer.AddFileAsync(RazorProjectConstants.BlazorProjectName,
            "MyComponent.razor",
            """
            <MyComponent MyProperty />
 
            @code {
                [Microsoft.AspNetCore.Components.ParameterAttribute]
                public bool MyProperty { get; set; }
            }
 
            """,
            open: true,
            cancellationToken: ControlledHangMitigatingCancellationToken);
 
        await TestServices.Editor.WaitForComponentClassificationAsync(ControlledHangMitigatingCancellationToken);
 
        await TestServices.Editor.PlaceCaretAsync("MyProperty", charsOffset: -1, ControlledHangMitigatingCancellationToken);
 
        // Act
        await TestServices.Editor.InvokeGoToDefinitionAsync(ControlledHangMitigatingCancellationToken);
 
        // Assert
        await TestServices.Editor.WaitForActiveWindowAsync("MyComponent.razor", ControlledHangMitigatingCancellationToken);
        await TestServices.Editor.WaitForCurrentLineTextAsync("public bool MyProperty { get; set; }", ControlledHangMitigatingCancellationToken);
    }
 
    [IdeFact]
    public async Task GoToDefinition_ComponentAttribute_InCSharpFile()
    {
        // Create the files
        const string MyComponentPath = "MyComponent.cs";
        const string MyPagePath = "MyPage.razor";
 
        await TestServices.SolutionExplorer.AddFileAsync(RazorProjectConstants.BlazorProjectName,
            MyComponentPath,
            """
            using Microsoft.AspNetCore.Components;
 
            namespace BlazorProject;
 
            public class MyComponent : ComponentBase
            {
                [Parameter] public string? MyProperty { get; set; }
            }
            """,
            cancellationToken: ControlledHangMitigatingCancellationToken);
 
        await TestServices.SolutionExplorer.BuildSolutionAndWaitAsync(ControlledHangMitigatingCancellationToken);
        await TestServices.Workspace.WaitForProjectSystemAsync(ControlledHangMitigatingCancellationToken);
 
        await TestServices.SolutionExplorer.AddFileAsync(RazorProjectConstants.BlazorProjectName,
            MyPagePath,
            """
            <MyComponent MyProperty="123" />
            """,
            open: true,
            cancellationToken: ControlledHangMitigatingCancellationToken);
 
        await TestServices.Editor.WaitForComponentClassificationAsync(ControlledHangMitigatingCancellationToken);
 
        await TestServices.Editor.PlaceCaretAsync("MyProperty=", charsOffset: -1, ControlledHangMitigatingCancellationToken);
 
        // Act
        await TestServices.Editor.InvokeGoToDefinitionAsync(ControlledHangMitigatingCancellationToken);
 
        // Assert
        await TestServices.Editor.WaitForActiveWindowAsync(MyComponentPath, ControlledHangMitigatingCancellationToken);
        await TestServices.Editor.WaitForCurrentLineTextAsync("[Parameter] public string? MyProperty { get; set; }", ControlledHangMitigatingCancellationToken);
    }
 
    [IdeFact]
    public async Task GoToDefinition_ComponentAttribute_InReferencedAssembly()
    {
        // Open the file
        await TestServices.SolutionExplorer.OpenFileAsync(RazorProjectConstants.BlazorProjectName, RazorProjectConstants.NavMenuFile, ControlledHangMitigatingCancellationToken);
 
        await TestServices.Editor.PlaceCaretAsync("Match=", charsOffset: -1, ControlledHangMitigatingCancellationToken);
 
        // Act
        await TestServices.Editor.InvokeGoToDefinitionAsync(ControlledHangMitigatingCancellationToken);
 
        // Assert
        await TestServices.Editor.WaitForActiveWindowByFileAsync("NavLink.cs", ControlledHangMitigatingCancellationToken);
    }
 
    [IdeFact]
    public async Task GoToDefinition_ComponentAttribute_GenericComponent()
    {
        // Create the files
        await TestServices.SolutionExplorer.AddFileAsync(RazorProjectConstants.BlazorProjectName,
            "MyComponent.cs",
            """
            using Microsoft.AspNetCore.Components;
 
            namespace BlazorProject
            {
                public class MyComponent<TItem> : ComponentBase
                {
                    [Parameter] public TItem Item { get; set; }
                }
            }
            """,
            cancellationToken: ControlledHangMitigatingCancellationToken);
 
        var position = await TestServices.SolutionExplorer.AddFileAsync(RazorProjectConstants.BlazorProjectName,
            "MyPage.razor",
            """
            <MyComponent TItem=string It$$em="@("hi")"/>
            """,
            open: true,
            cancellationToken: ControlledHangMitigatingCancellationToken);
 
        await TestServices.Editor.WaitForComponentClassificationAsync(ControlledHangMitigatingCancellationToken);
 
        await TestServices.Editor.PlaceCaretAsync(position, ControlledHangMitigatingCancellationToken);
 
        // Act
        await TestServices.Editor.InvokeGoToDefinitionAsync(ControlledHangMitigatingCancellationToken);
 
        // Assert
        await TestServices.Editor.WaitForActiveWindowAsync("MyComponent.cs", ControlledHangMitigatingCancellationToken);
        await TestServices.Editor.WaitForCurrentLineTextAsync("[Parameter] public TItem Item { get; set; }", ControlledHangMitigatingCancellationToken);
    }
 
    [IdeFact]
    public async Task GoToDefinition_ComponentAttribute_CascadingGenericComponentWithConstraints()
    {
        // Create the files
        await TestServices.SolutionExplorer.AddFileAsync(RazorProjectConstants.BlazorProjectName,
            "MyComponent.cs",
            """
            using Microsoft.AspNetCore.Components;
 
            namespace BlazorProject
            {
                [CascadingTypeParameter(nameof(TItem))]
                public class Grid<TItem> : ComponentBase
                {
                    [Parameter] public RenderFragment ColumnsTemplate { get; set; }
                }
 
                public abstract partial class BaseColumn<TItem> : ComponentBase where TItem : class
                {
                    [CascadingParameter]
                    internal Grid<TItem> Grid { get; set; }
                }
 
                public class Column<TItem> : BaseColumn<TItem>, IGridFieldColumn<TItem> where TItem : class
                {
                    [Parameter]
                    public string FieldName { get; set; }
                }
 
                internal interface IGridFieldColumn<TItem> where TItem : class
                {
                }
 
                public class WeatherForecast { }
            }
            """,
            cancellationToken: ControlledHangMitigatingCancellationToken);
 
        var position = await TestServices.SolutionExplorer.AddFileAsync(RazorProjectConstants.BlazorProjectName,
            "MyPage.razor",
            """
            <Grid TItem="WeatherForecast" Items="@(Array.Empty<WeatherForecast>())">
                <ColumnsTemplate>
                    <Column Title="Date" Fie$$ldName="Date" Format="d" Width="10rem" />
                </ColumnsTemplate>
            </Grid>
            """,
            open: true,
            cancellationToken: ControlledHangMitigatingCancellationToken);
 
        await TestServices.Editor.WaitForComponentClassificationAsync(ControlledHangMitigatingCancellationToken);
 
        await TestServices.Editor.PlaceCaretAsync(position, ControlledHangMitigatingCancellationToken);
 
        // Act
        await TestServices.Editor.InvokeGoToDefinitionAsync(ControlledHangMitigatingCancellationToken);
 
        // Assert
        await TestServices.Editor.WaitForActiveWindowAsync("MyComponent.cs", ControlledHangMitigatingCancellationToken);
        await TestServices.Editor.WaitForCurrentLineTextAsync("public string FieldName { get; set; }", ControlledHangMitigatingCancellationToken);
    }
 
    [IdeFact]
    public async Task GoToDefinition_ComponentAttribute_BoundAttribute()
    {
        // Create the files
        await TestServices.SolutionExplorer.AddFileAsync(RazorProjectConstants.BlazorProjectName,
            "MyComponent.razor",
            """
            <div></div>
 
            @code
            {
                [Parameter]
                public string? Value { get; set; }
 
                [Parameter]
                public EventCallback<string?> ValueChanged { get; set; }
            }
            """,
            cancellationToken: ControlledHangMitigatingCancellationToken);
 
        await TestServices.SolutionExplorer.AddFileAsync(RazorProjectConstants.BlazorProjectName,
            "MyPage.razor",
            """
            <MyComponent @bind-Value="value"></MyComponent>
 
            @code{
                string? value = "";
            }
            """,
            open: true,
            cancellationToken: ControlledHangMitigatingCancellationToken);
 
        await TestServices.Editor.WaitForComponentClassificationAsync(ControlledHangMitigatingCancellationToken);
 
        await TestServices.Editor.PlaceCaretAsync("Value=", charsOffset: -1, ControlledHangMitigatingCancellationToken);
 
        // Act
        await TestServices.Editor.InvokeGoToDefinitionAsync(ControlledHangMitigatingCancellationToken);
 
        // Assert
        await TestServices.Editor.WaitForActiveWindowAsync("MyComponent.razor", ControlledHangMitigatingCancellationToken);
        await TestServices.Editor.WaitForCurrentLineTextAsync("public string? Value { get; set; }", ControlledHangMitigatingCancellationToken);
    }
 
    [IdeFact]
    public async Task GoToDefinition_ComponentAttribute_WriteOnlyProperty()
    {
        // Create the file
        await TestServices.SolutionExplorer.AddFileAsync(RazorProjectConstants.BlazorProjectName,
            "MyComponent.razor",
            """
            <MyComponent MyProperty="123" />
 
            @code {
                [Microsoft.AspNetCore.Components.ParameterAttribute]
                public string? MyProperty { set { } }
            }
            """,
            open: true,
            cancellationToken: ControlledHangMitigatingCancellationToken);
 
        await TestServices.Editor.WaitForComponentClassificationAsync(ControlledHangMitigatingCancellationToken);
 
        await TestServices.Editor.PlaceCaretAsync("MyProperty=", charsOffset: -1, ControlledHangMitigatingCancellationToken);
 
        // Act
        await TestServices.Editor.InvokeGoToDefinitionAsync(ControlledHangMitigatingCancellationToken);
 
        // Assert
        await TestServices.Editor.WaitForActiveWindowAsync("MyComponent.razor", ControlledHangMitigatingCancellationToken);
        await TestServices.Editor.WaitForCurrentLineTextAsync("public string? MyProperty { set { } }", ControlledHangMitigatingCancellationToken);
    }
 
    [IdeTheory]
    [InlineData("MyProperty:get")]
    [InlineData("MyProperty:set")]
    [InlineData("MyProperty:after")]
    public async Task GoToDefinition_ComponentAttribute_BindSet(string attribute)
    {
        // Create the file
        await TestServices.SolutionExplorer.AddFileAsync(RazorProjectConstants.BlazorProjectName,
            "MyComponent.razor",
            """
            <MyComponent @bind-MyProperty:get="value" @bind-MyProperty:set="(value) => { text = value; }" @bind-MyProperty:after="After" />
            @code {
                private string value = "";
                [Microsoft.AspNetCore.Components.ParameterAttribute]
                public string? MyProperty { get; set; }
                public void After()
                {
                }
            }
            """,
            open: true,
            cancellationToken: ControlledHangMitigatingCancellationToken);
 
        await TestServices.Editor.WaitForComponentClassificationAsync(ControlledHangMitigatingCancellationToken);
 
        await TestServices.Editor.PlaceCaretAsync(attribute, charsOffset: -1, ControlledHangMitigatingCancellationToken);
 
        // Act
        await TestServices.Editor.InvokeGoToDefinitionAsync(ControlledHangMitigatingCancellationToken);
 
        // Assert
        await TestServices.Editor.WaitForActiveWindowAsync("MyComponent.razor", ControlledHangMitigatingCancellationToken);
        await TestServices.Editor.WaitForCurrentLineTextAsync("public string? MyProperty { get; set; }", ControlledHangMitigatingCancellationToken);
    }
 
    [IdeFact]
    public async Task GoToDefinition_ComponentFromCSharp()
    {
        // Open the file
        await TestServices.SolutionExplorer.OpenFileAsync(RazorProjectConstants.BlazorProjectName, "Program.cs", ControlledHangMitigatingCancellationToken);
 
        // Change text to refer back to Program class
        await TestServices.Editor.SetTextAsync("""
            using BlazorProject.Shared;
 
            typeof(Surv$$eyPrompt).ToString();
            """, ControlledHangMitigatingCancellationToken);
 
        // Act
        await TestServices.Editor.InvokeGoToDefinitionAsync(ControlledHangMitigatingCancellationToken);
 
        // Assert
        await TestServices.Editor.WaitForActiveWindowAsync("SurveyPrompt.razor", ControlledHangMitigatingCancellationToken);
 
        await TestServices.Editor.CloseCodeFileAsync(RazorProjectConstants.BlazorProjectName, "Program.cs", saveFile: false, ControlledHangMitigatingCancellationToken);
    }
 
    [IdeFact]
    public async Task GoToDefinition_ComponentFromCSharpInRazor()
    {
        // Open the file
        await TestServices.SolutionExplorer.OpenFileAsync(RazorProjectConstants.BlazorProjectName, RazorProjectConstants.IndexRazorFile, ControlledHangMitigatingCancellationToken);
 
        // Change text to refer back to Program class
        await TestServices.Editor.SetTextAsync("@nameof(Surv$$eyPrompt)", ControlledHangMitigatingCancellationToken);
 
        // Act
        await TestServices.Editor.InvokeGoToDefinitionAsync(ControlledHangMitigatingCancellationToken);
 
        // Assert
        await TestServices.Editor.WaitForActiveWindowAsync("SurveyPrompt.razor", ControlledHangMitigatingCancellationToken);
    }
}