File: Browser\BrowserTests.cs
Web Access
Project: ..\..\..\test\dotnet-watch.Tests\dotnet-watch.Tests.csproj (dotnet-watch.Tests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Text.Json;
 
namespace Microsoft.DotNet.Watch.UnitTests;
 
public class BrowserTests(ITestOutputHelper logger) : DotNetWatchTestBase(logger)
{
    [Fact]
    public async Task LaunchesBrowserOnStart()
    {
        var testAsset = TestAssets.CopyTestAsset("WatchBrowserLaunchApp")
            .WithSource();
 
        App.Start(testAsset, [], testFlags: TestFlags.MockBrowser);
 
        // check that all app output is printed out:
        await App.WaitForOutputLineContaining("Content root path:");
 
        Assert.Contains(App.Process.Output, line => line.Contains("Application started. Press Ctrl+C to shut down."));
        Assert.Contains(App.Process.Output, line => line.Contains("Hosting environment: Development"));
 
        // Verify we launched the browser.
        App.AssertOutputContains(MessageDescriptor.LaunchingBrowser.GetMessage("https://localhost:5001", ""));
    }
 
    [PlatformSpecificFact(TestPlatforms.Windows)] // https://github.com/dotnet/aspnetcore/issues/63759
    public async Task BrowserDiagnostics()
    {
        var testAsset = TestAssets.CopyTestAsset("WatchRazorWithDeps")
                .WithSource();
 
        App.UseTestBrowser();
 
        var url = $"http://localhost:{TestOptions.GetTestPort()}";
        var tfm = ToolsetInfo.CurrentTargetFramework;
 
        App.Start(testAsset, ["--urls", url], relativeProjectDirectory: "RazorApp", testFlags: TestFlags.ReadKeyFromStdin);
 
        await App.WaitForOutputLineContaining(MessageDescriptor.ConfiguredToUseBrowserRefresh);
        await App.WaitForOutputLineContaining(MessageDescriptor.ConfiguredToLaunchBrowser);
        await App.WaitForOutputLineContaining(MessageDescriptor.WaitingForChanges);
 
        // Verify the browser has been launched.
        await App.WaitUntilOutputContains($"🧪 Test browser opened at '{url}'.");
 
        // Verify the browser connected to the refresh server.
        await App.WaitUntilOutputContains(MessageDescriptor.ConnectedToRefreshServer, "Browser #1");
 
        App.Process.ClearOutput();
 
        var homePagePath = Path.Combine(testAsset.Path, "RazorApp", "Components", "Pages", "Home.razor");
        
        // rude edit:
        UpdateSourceFile(homePagePath, src => src.Replace("/* member placeholder */", """
            public virtual int F() => 1;
            """));
 
        var errorMessage = $"{homePagePath}(13,9): error ENC0023: Adding an abstract method or overriding an inherited method requires restarting the application.";
        var jsonErrorMessage = JsonSerializer.Serialize(errorMessage);
 
        await App.WaitForOutputLineContaining(errorMessage);
 
        await App.WaitForOutputLineContaining("Do you want to restart your app?");
 
        await App.WaitUntilOutputContains($$"""
            🧪 Received: {"type":"HotReloadDiagnosticsv1","diagnostics":[{{jsonErrorMessage}}]}
            """);
 
        // auto restart next time:
        App.SendKey('a');
 
        // browser page is reloaded when the app restarts:
        await App.WaitForOutputLineContaining(MessageDescriptor.ReloadingBrowser, $"RazorApp ({tfm})");
 
        // browser page was reloaded after the app restarted:
        await App.WaitUntilOutputContains("""
            🧪 Received: Reload
            """);
 
        await App.WaitForOutputLineContaining(MessageDescriptor.WaitingForChanges);
 
        // another rude edit:
        UpdateSourceFile(homePagePath, src => src.Replace("public virtual int F() => 1;", "/* member placeholder */"));
 
        errorMessage = $"{homePagePath}(11,5): error ENC0033: Deleting method 'F()' requires restarting the application.";
        await App.WaitForOutputLineContaining("[auto-restart] " + errorMessage);
 
        await App.WaitUntilOutputContains($$"""
            🧪 Received: {"type":"HotReloadDiagnosticsv1","diagnostics":["Restarting application to apply changes ..."]}
            """);
 
        await App.WaitForOutputLineContaining(MessageDescriptor.WaitingForChanges);
 
        // browser page was reloaded after the app restarted:
        await App.WaitUntilOutputContains("""
            🧪 Received: Reload
            """);
 
        // valid edit:
        UpdateSourceFile(homePagePath, src => src.Replace("/* member placeholder */", "public int F() => 1;"));
 
        await App.WaitForOutputLineContaining(MessageDescriptor.HotReloadSucceeded);
 
        await App.WaitUntilOutputContains($$"""
            🧪 Received: {"type":"AspNetCoreHotReloadApplied"}
            """);
    }
}