Quellcode durchsuchen

Fix 'HeadOutlet' getting trimmed away for Blazor WebAssembly prerendered apps after publish (#35419)

Mackinnon Buck vor 4 Jahren
Ursprung
Commit
cdf9caa7ac
18 geänderte Dateien mit 361 neuen und 6 gelöschten Zeilen
  1. 32 2
      AspNetCore.sln
  2. 3 1
      src/Components/Components.slnf
  3. 0 0
      src/Components/Web.JS/dist/Release/blazor.server.js
  4. 6 1
      src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyHostBuilder.cs
  5. 17 0
      src/Components/WebAssembly/testassets/Wasm.Prerendered.Client/App.razor
  6. 20 0
      src/Components/WebAssembly/testassets/Wasm.Prerendered.Client/Program.cs
  7. 27 0
      src/Components/WebAssembly/testassets/Wasm.Prerendered.Client/Properties/launchSettings.json
  8. 11 0
      src/Components/WebAssembly/testassets/Wasm.Prerendered.Client/Wasm.Prerendered.Client.csproj
  9. 1 0
      src/Components/WebAssembly/testassets/Wasm.Prerendered.Client/_Imports.razor
  10. 9 0
      src/Components/WebAssembly/testassets/Wasm.Prerendered.Server/Pages/_Host.cshtml
  11. 29 0
      src/Components/WebAssembly/testassets/Wasm.Prerendered.Server/Pages/_Layout.cshtml
  12. 24 0
      src/Components/WebAssembly/testassets/Wasm.Prerendered.Server/Program.cs
  13. 29 0
      src/Components/WebAssembly/testassets/Wasm.Prerendered.Server/Properties/launchSettings.json
  14. 41 0
      src/Components/WebAssembly/testassets/Wasm.Prerendered.Server/Startup.cs
  15. 28 0
      src/Components/WebAssembly/testassets/Wasm.Prerendered.Server/Wasm.Prerendered.Server.csproj
  16. 8 1
      src/Components/test/E2ETest/Infrastructure/ServerFixtures/AspNetSiteServerFixture.cs
  17. 7 1
      src/Components/test/E2ETest/Microsoft.AspNetCore.Components.E2ETests.csproj
  18. 69 0
      src/Components/test/E2ETest/Tests/WebAssemblyPrerenderedTest.cs

+ 32 - 2
AspNetCore.sln

@@ -1620,9 +1620,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "PhotinoPlatform", "PhotinoP
 EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "testassets", "testassets", "{3EC71A0E-6515-4A5A-B759-F0BCF1BCFC56}"
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PhotinoTestApp", "src\Components\WebView\Samples\PhotinoPlatform\testassets\PhotinoTestApp\PhotinoTestApp.csproj", "{558C46DE-DE16-41D5-8DB7-D6D748E32977}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PhotinoTestApp", "src\Components\WebView\Samples\PhotinoPlatform\testassets\PhotinoTestApp\PhotinoTestApp.csproj", "{558C46DE-DE16-41D5-8DB7-D6D748E32977}"
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Components.WebView.Photino", "src\Components\WebView\Samples\PhotinoPlatform\src\Microsoft.AspNetCore.Components.WebView.Photino.csproj", "{B1AA24A4-5E02-4DC1-B57F-6EB03F91E4DD}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Components.WebView.Photino", "src\Components\WebView\Samples\PhotinoPlatform\src\Microsoft.AspNetCore.Components.WebView.Photino.csproj", "{B1AA24A4-5E02-4DC1-B57F-6EB03F91E4DD}"
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SimpleWebSiteWithWebApplicationBuilderException", "src\Mvc\test\WebSites\SimpleWebSiteWithWebApplicationBuilderException\SimpleWebSiteWithWebApplicationBuilderException.csproj", "{5C641396-7E92-4F5C-A5A1-B4CDF480539B}"
 EndProject
@@ -1642,6 +1642,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "testassets", "testassets",
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Identity.DefaultUI.WebSite", "src\Identity\testassets\Identity.DefaultUI.WebSite\Identity.DefaultUI.WebSite.csproj", "{835A4E0F-A697-4B69-9736-3E99D163C4B9}"
 EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wasm.Prerendered.Client", "src\Components\WebAssembly\testassets\Wasm.Prerendered.Client\Wasm.Prerendered.Client.csproj", "{148A5B4F-C8A3-4468-92F6-51DB5641FB49}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wasm.Prerendered.Server", "src\Components\WebAssembly\testassets\Wasm.Prerendered.Server\Wasm.Prerendered.Server.csproj", "{6D365C86-3158-49F5-A21D-506C1E06E870}"
+EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.App.Analyzer", "src\Framework\Analyzer\src\Microsoft.AspNetCore.App.Analyzers.csproj", "{564CABB8-1B3F-4D9E-909D-260EF2B8614A}"
 EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Analyzer", "Analyzer", "{EE39397E-E4AF-4D3F-9B9C-D637F9222CDD}"
@@ -7839,6 +7843,30 @@ Global
 		{835A4E0F-A697-4B69-9736-3E99D163C4B9}.Release|x64.Build.0 = Release|Any CPU
 		{835A4E0F-A697-4B69-9736-3E99D163C4B9}.Release|x86.ActiveCfg = Release|Any CPU
 		{835A4E0F-A697-4B69-9736-3E99D163C4B9}.Release|x86.Build.0 = Release|Any CPU
+		{148A5B4F-C8A3-4468-92F6-51DB5641FB49}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{148A5B4F-C8A3-4468-92F6-51DB5641FB49}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{148A5B4F-C8A3-4468-92F6-51DB5641FB49}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{148A5B4F-C8A3-4468-92F6-51DB5641FB49}.Debug|x64.Build.0 = Debug|Any CPU
+		{148A5B4F-C8A3-4468-92F6-51DB5641FB49}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{148A5B4F-C8A3-4468-92F6-51DB5641FB49}.Debug|x86.Build.0 = Debug|Any CPU
+		{148A5B4F-C8A3-4468-92F6-51DB5641FB49}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{148A5B4F-C8A3-4468-92F6-51DB5641FB49}.Release|Any CPU.Build.0 = Release|Any CPU
+		{148A5B4F-C8A3-4468-92F6-51DB5641FB49}.Release|x64.ActiveCfg = Release|Any CPU
+		{148A5B4F-C8A3-4468-92F6-51DB5641FB49}.Release|x64.Build.0 = Release|Any CPU
+		{148A5B4F-C8A3-4468-92F6-51DB5641FB49}.Release|x86.ActiveCfg = Release|Any CPU
+		{148A5B4F-C8A3-4468-92F6-51DB5641FB49}.Release|x86.Build.0 = Release|Any CPU
+		{6D365C86-3158-49F5-A21D-506C1E06E870}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{6D365C86-3158-49F5-A21D-506C1E06E870}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{6D365C86-3158-49F5-A21D-506C1E06E870}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{6D365C86-3158-49F5-A21D-506C1E06E870}.Debug|x64.Build.0 = Debug|Any CPU
+		{6D365C86-3158-49F5-A21D-506C1E06E870}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{6D365C86-3158-49F5-A21D-506C1E06E870}.Debug|x86.Build.0 = Debug|Any CPU
+		{6D365C86-3158-49F5-A21D-506C1E06E870}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{6D365C86-3158-49F5-A21D-506C1E06E870}.Release|Any CPU.Build.0 = Release|Any CPU
+		{6D365C86-3158-49F5-A21D-506C1E06E870}.Release|x64.ActiveCfg = Release|Any CPU
+		{6D365C86-3158-49F5-A21D-506C1E06E870}.Release|x64.Build.0 = Release|Any CPU
+		{6D365C86-3158-49F5-A21D-506C1E06E870}.Release|x86.ActiveCfg = Release|Any CPU
+		{6D365C86-3158-49F5-A21D-506C1E06E870}.Release|x86.Build.0 = Release|Any CPU
 		{564CABB8-1B3F-4D9E-909D-260EF2B8614A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{564CABB8-1B3F-4D9E-909D-260EF2B8614A}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{564CABB8-1B3F-4D9E-909D-260EF2B8614A}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -8676,6 +8704,8 @@ Global
 		{514726D2-3D2E-44C1-B056-163E37DE3E8B} = {7B976D8F-EA31-4C0B-97BD-DFD9B3CC86FB}
 		{48526D13-69E2-4409-A57B-C3FA3C64B4F7} = {9F21A235-436E-4020-A076-1DF4F89D0CA0}
 		{835A4E0F-A697-4B69-9736-3E99D163C4B9} = {48526D13-69E2-4409-A57B-C3FA3C64B4F7}
+		{148A5B4F-C8A3-4468-92F6-51DB5641FB49} = {7D2B0799-A634-42AC-AE77-5D167BA51389}
+		{6D365C86-3158-49F5-A21D-506C1E06E870} = {7D2B0799-A634-42AC-AE77-5D167BA51389}
 		{564CABB8-1B3F-4D9E-909D-260EF2B8614A} = {EE39397E-E4AF-4D3F-9B9C-D637F9222CDD}
 		{EE39397E-E4AF-4D3F-9B9C-D637F9222CDD} = {A4C26078-B6D8-4FD8-87A6-7C15A3482038}
 		{CF4CEC18-798D-46EC-B0A0-98D97496590F} = {EE39397E-E4AF-4D3F-9B9C-D637F9222CDD}

+ 3 - 1
src/Components/Components.slnf

@@ -35,6 +35,8 @@
       "src\\Components\\WebAssembly\\testassets\\Wasm.Authentication.Server\\Wasm.Authentication.Server.csproj",
       "src\\Components\\WebAssembly\\testassets\\Wasm.Authentication.Shared\\Wasm.Authentication.Shared.csproj",
       "src\\Components\\WebAssembly\\testassets\\WasmLinkerTest\\WasmLinkerTest.csproj",
+      "src\\Components\\WebAssembly\\testassets\\Wasm.Prerendered.Client\\Wasm.Prerendered.Client.csproj",
+      "src\\Components\\WebAssembly\\testassets\\Wasm.Prerendered.Server\\Wasm.Prerendered.Server.csproj",
       "src\\Components\\WebView\\Samples\\PhotinoPlatform\\src\\Microsoft.AspNetCore.Components.WebView.Photino.csproj",
       "src\\Components\\WebView\\Samples\\PhotinoPlatform\\testassets\\PhotinoTestApp\\PhotinoTestApp.csproj",
       "src\\Components\\WebView\\WebView\\src\\Microsoft.AspNetCore.Components.WebView.csproj",
@@ -140,4 +142,4 @@
       "src\\WebEncoders\\src\\Microsoft.Extensions.WebEncoders.csproj"
     ]
   }
-}
+}

Datei-Diff unterdrückt, da er zu groß ist
+ 0 - 0
src/Components/Web.JS/dist/Release/blazor.server.js


+ 6 - 1
src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyHostBuilder.cs

@@ -39,6 +39,9 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Hosting
         /// <returns>A <see cref="WebAssemblyHostBuilder"/>.</returns>
         [DynamicDependency(nameof(JSInteropMethods.NotifyLocationChanged), typeof(JSInteropMethods))]
         [DynamicDependency(JsonSerialized, typeof(WebEventDescriptor))]
+        // The following dependency prevents HeadOutlet from getting trimmed away in
+        // WebAssembly prerendered apps.
+        [DynamicDependency(Component, typeof(HeadOutlet))]
         public static WebAssemblyHostBuilder CreateDefault(string[]? args = default)
         {
             // We don't use the args for anything right now, but we want to accept them
@@ -112,7 +115,9 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Hosting
                 var componentType = _rootComponentCache.GetRootComponent(registeredComponent.Assembly!, registeredComponent.TypeName!);
                 if (componentType is null)
                 {
-                    continue;
+                    throw new InvalidOperationException(
+                        $"Root component type '{registeredComponent.TypeName}' could not be found in the assembly '{registeredComponent.Assembly}'. " +
+                        $"This is likely a result of trimming (tree shaking).");
                 }
 
                 var definitions = componentDeserializer.GetParameterDefinitions(registeredComponent.ParameterDefinitions!);

+ 17 - 0
src/Components/WebAssembly/testassets/Wasm.Prerendered.Client/App.razor

@@ -0,0 +1,17 @@
+<h3>WebAssembly prerendered app with page title</h3>
+
+<PageTitle>Current count: @_counter</PageTitle>
+
+<p>
+    <span>Current count: @_counter</span><br>
+    <button id="increment-count" @onclick="IncrementCounter">Increment count</button>
+</p>
+
+@code {
+    private int _counter = 0;
+
+    private void IncrementCounter()
+    {
+        _counter++;
+    }
+}

+ 20 - 0
src/Components/WebAssembly/testassets/Wasm.Prerendered.Client/Program.cs

@@ -0,0 +1,20 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
+using System.Net.Http;
+
+namespace Wasm.Prerendered.Client
+{
+    public class Program
+    {
+        public static async Task Main(string[] args)
+        {
+            var builder = WebAssemblyHostBuilder.CreateDefault(args);
+
+            builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
+
+            await builder.Build().RunAsync();
+        }
+    }
+}

+ 27 - 0
src/Components/WebAssembly/testassets/Wasm.Prerendered.Client/Properties/launchSettings.json

@@ -0,0 +1,27 @@
+{
+  "iisSettings": {
+    "windowsAuthentication": false,
+    "anonymousAuthentication": true,
+    "iisExpress": {
+      "applicationUrl": "http://localhost:54194/",
+      "sslPort": 44314
+    }
+  },
+  "profiles": {
+    "Wasm.Prerendered.Client": {
+      "commandName": "Project",
+      "launchBrowser": true,
+      "environmentVariables": {
+        "ASPNETCORE_ENVIRONMENT": "Development"
+      },
+      "applicationUrl": "https://localhost:5001;http://localhost:5000"
+    },
+    "IIS Express": {
+      "commandName": "IISExpress",
+      "launchBrowser": true,
+      "environmentVariables": {
+        "ASPNETCORE_ENVIRONMENT": "Development"
+      }
+    }
+  }
+}

+ 11 - 0
src/Components/WebAssembly/testassets/Wasm.Prerendered.Client/Wasm.Prerendered.Client.csproj

@@ -0,0 +1,11 @@
+<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
+
+  <PropertyGroup>
+    <TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <Reference Include="Microsoft.AspNetCore.Components.WebAssembly" />
+  </ItemGroup>
+
+</Project>

+ 1 - 0
src/Components/WebAssembly/testassets/Wasm.Prerendered.Client/_Imports.razor

@@ -0,0 +1 @@
+@using Microsoft.AspNetCore.Components.Web

+ 9 - 0
src/Components/WebAssembly/testassets/Wasm.Prerendered.Server/Pages/_Host.cshtml

@@ -0,0 +1,9 @@
+@page "/"
+@namespace Wasm.Prerendered.Pages
+@using Wasm.Prerendered.Client
+@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
+@{
+    Layout = "_Layout";
+}
+
+<component type="typeof(App)" render-mode="WebAssemblyPrerendered" />

+ 29 - 0
src/Components/WebAssembly/testassets/Wasm.Prerendered.Server/Pages/_Layout.cshtml

@@ -0,0 +1,29 @@
+@using Microsoft.AspNetCore.Components.Web
+@namespace Wasm.Prerendered.Pages
+@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
+
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="utf-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <base href="~/" />
+    <component type="typeof(HeadOutlet)" render-mode="WebAssemblyPrerendered" />
+</head>
+<body>
+    @RenderBody()
+
+    <script src="_framework/blazor.webassembly.js" autostart="false"></script>
+    <script>
+        function start() {
+            Blazor.start({
+                logLevel: 1 // LogLevel.Debug
+            }).then(function() {
+                window['__aspnetcore__testing__blazor_wasm__started__'] = true;
+            });
+        }
+    </script>
+
+    <button id="start-blazor" onclick="start()">Start Blazor</button>
+</body>
+</html>

+ 24 - 0
src/Components/WebAssembly/testassets/Wasm.Prerendered.Server/Program.cs

@@ -0,0 +1,24 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Collections;
+
+namespace Wasm.Prerendered.Server
+{
+    public class Program
+    {
+        public static void Main(string[] args)
+        {
+            BuildWebHost(args).Run();
+        }
+
+        public static IHost BuildWebHost(string[] args) =>
+            Host.CreateDefaultBuilder(args)
+                .ConfigureWebHostDefaults(webBuilder =>
+                {
+                    webBuilder.UseStaticWebAssets();
+                    webBuilder.UseStartup<Startup>();
+                })
+                .Build();
+    }
+}

+ 29 - 0
src/Components/WebAssembly/testassets/Wasm.Prerendered.Server/Properties/launchSettings.json

@@ -0,0 +1,29 @@
+{
+  "iisSettings": {
+    "windowsAuthentication": false,
+    "anonymousAuthentication": true,
+    "iisExpress": {
+      "applicationUrl": "http://localhost:56500/",
+      "sslPort": 44347
+    }
+  },
+  "profiles": {
+    "Wasm.Prerendered.Server": {
+      "commandName": "Project",
+      "launchBrowser": true,
+      "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
+      "environmentVariables": {
+        "ASPNETCORE_ENVIRONMENT": "Development"
+      },
+      "applicationUrl": "https://localhost:5001;http://localhost:5000"
+    },
+    "IIS Express": {
+      "commandName": "IISExpress",
+      "launchBrowser": true,
+      "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
+      "environmentVariables": {
+        "ASPNETCORE_ENVIRONMENT": "Development"
+      }
+    }
+  }
+}

+ 41 - 0
src/Components/WebAssembly/testassets/Wasm.Prerendered.Server/Startup.cs

@@ -0,0 +1,41 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Wasm.Prerendered.Server
+{
+    public class Startup
+    {
+        public Startup(IConfiguration configuration)
+        {
+            Configuration = configuration;
+        }
+
+        public IConfiguration Configuration { get; }
+
+        public void ConfigureServices(IServiceCollection services)
+        {
+            services.AddRazorPages();
+        }
+
+        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
+        {
+            if (env.IsDevelopment())
+            {
+                app.UseDeveloperExceptionPage();
+                app.UseWebAssemblyDebugging();
+            }
+
+            app.UseHttpsRedirection();
+            app.UseBlazorFrameworkFiles();
+            app.UseStaticFiles();
+
+            app.UseRouting();
+
+            app.UseEndpoints(endpoints =>
+            {
+                endpoints.MapRazorPages();
+                endpoints.MapFallbackToFile("/_Host");
+            });
+        }
+    }
+}

+ 28 - 0
src/Components/WebAssembly/testassets/Wasm.Prerendered.Server/Wasm.Prerendered.Server.csproj

@@ -0,0 +1,28 @@
+<Project Sdk="Microsoft.NET.Sdk.Web">
+
+  <PropertyGroup>
+    <TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
+    <IsShippingPackage>false</IsShippingPackage>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\CustomBasePathApp\CustomBasePathApp.csproj" />
+    <ProjectReference Include="..\Wasm.Prerendered.Client\Wasm.Prerendered.Client.csproj" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <Reference Include="Microsoft.AspNetCore" />
+    <Reference Include="Microsoft.AspNetCore.Mvc" />
+    <Reference Include="Microsoft.AspNetCore.Mvc.Razor" />
+    <Reference Include="Microsoft.AspNetCore.Mvc.RazorPages" />
+    <Reference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" />
+    <Reference Include="Microsoft.AspNetCore.Diagnostics" />
+    <Reference Include="Microsoft.AspNetCore.HttpsPolicy" />
+    <Reference Include="Microsoft.AspNetCore.ResponseCompression" />
+    <Reference Include="Microsoft.Extensions.Hosting" />
+    <Reference Include="Microsoft.Extensions.DependencyInjection" />
+    <!-- Avoid MSB3277 warnings due to dependencies brought in through Microsoft.AspNetCore.Blazor targeting netstandard2.0. -->
+    <Reference Include="System.Text.Json" />
+  </ItemGroup>
+
+</Project>

+ 8 - 1
src/Components/test/E2ETest/Infrastructure/ServerFixtures/AspNetSiteServerFixture.cs

@@ -16,10 +16,14 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures
     {
         public delegate IHost BuildWebHost(string[] args);
 
+        public delegate string GetContentRoot(Assembly assembly);
+
         public Assembly ApplicationAssembly { get; set; }
 
         public BuildWebHost BuildWebHostMethod { get; set; }
 
+        public GetContentRoot GetContentRootMethod { get; set; } = DefaultGetContentRoot;
+
         public AspNetEnvironment Environment { get; set; } = AspNetEnvironment.Production;
 
         public List<string> AdditionalArguments { get; set; } = new List<string> { "--test-execution-mode", "server" };
@@ -33,7 +37,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures
             }
 
             var assembly = ApplicationAssembly ?? BuildWebHostMethod.Method.DeclaringType.Assembly;
-            var sampleSitePath = FindSampleOrTestSitePath(assembly.FullName);
+            var sampleSitePath = DefaultGetContentRoot(assembly);
 
             var host = "127.0.0.1";
             if (E2ETestOptions.Instance.SauceTest)
@@ -48,5 +52,8 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures
                 "--environment", Environment.ToString(),
             }.Concat(AdditionalArguments).ToArray());
         }
+
+        private static string DefaultGetContentRoot(Assembly assembly)
+            => FindSampleOrTestSitePath(assembly.FullName);
     }
 }

+ 7 - 1
src/Components/test/E2ETest/Microsoft.AspNetCore.Components.E2ETests.csproj

@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
 
   <!-- Shared testing infrastructure for running E2E tests using selenium -->
   <Import Condition="'$(SkipTestBuild)' != 'true'" Project="$(SharedSourceRoot)E2ETesting\E2ETesting.props" />
@@ -52,6 +52,7 @@
     <ProjectReference Include="..\..\WebAssembly\testassets\StandaloneApp\StandaloneApp.csproj" />
     <ProjectReference Include="..\testassets\BasicTestApp\BasicTestApp.csproj" />
     <ProjectReference Include="..\testassets\GlobalizationWasmApp\GlobalizationWasmApp.csproj" />
+    <ProjectReference Include="..\..\WebAssembly\testassets\Wasm.Prerendered.Server\Wasm.Prerendered.Server.csproj" />
   </ItemGroup>
 
   <ItemGroup Condition="'$(TestTrimmedApps)' == 'true'">
@@ -73,6 +74,11 @@
       Include="..\..\WebAssembly\testassets\StandaloneApp\StandaloneApp.csproj"
       Targets="Build;Publish"
       Properties="TestTrimmedApps=true;PublishDir=$(MSBuildThisFileDirectory)$(OutputPath)trimmed\StandaloneApp\;" />
+
+    <ProjectReference
+      Include="..\..\WebAssembly\testassets\Wasm.Prerendered.Server\Wasm.Prerendered.Server.csproj"
+      Targets="Build;Publish"
+      Properties="TestTrimmedApps=true;PublishDir=$(MSBuildThisFileDirectory)$(OutputPath)trimmed\Wasm.Prerendered.Server\;" />
   </ItemGroup>
 
   <!-- Shared testing infrastructure for running E2E tests using selenium -->

+ 69 - 0
src/Components/test/E2ETest/Tests/WebAssemblyPrerenderedTest.cs

@@ -0,0 +1,69 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Reflection;
+using Microsoft.AspNetCore.Components.E2ETest.Infrastructure;
+using Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures;
+using Microsoft.AspNetCore.E2ETesting;
+using OpenQA.Selenium;
+using Xunit.Abstractions;
+
+namespace Microsoft.AspNetCore.Components.E2ETest.Tests
+{
+    public class WebAssemblyPrerenderedTest : ServerTestBase<AspNetSiteServerFixture>
+    {
+        public WebAssemblyPrerenderedTest(
+            BrowserFixture browserFixture,
+            AspNetSiteServerFixture serverFixture,
+            ITestOutputHelper output)
+            : base(browserFixture, serverFixture, output)
+        {
+            serverFixture.BuildWebHostMethod = Wasm.Prerendered.Server.Program.BuildWebHost;
+            serverFixture.Environment = AspNetEnvironment.Development;
+
+            var testTrimmedApps = typeof(ToggleExecutionModeServerFixture<>).Assembly
+                .GetCustomAttributes<AssemblyMetadataAttribute>()
+                .First(m => m.Key == "Microsoft.AspNetCore.E2ETesting.TestTrimmedApps")
+                .Value == "true";
+
+            if (testTrimmedApps)
+            {
+                serverFixture.GetContentRootMethod = GetPublishedContentRoot;
+            }
+        }
+
+        [Fact]
+        public void CanPrerenderAndAddHeadOutletRootComponent()
+        {
+            Navigate("/", noReload: true);
+
+            // Verify that the title is updated during prerendering
+            Browser.Equal("Current count: 0", () => Browser.Title);
+            Browser.Click(By.Id("start-blazor"));
+
+            WaitUntilLoaded();
+
+            // Verify that the HeadOutlet root component was added after prerendering
+            Browser.Click(By.Id("increment-count"));
+            Browser.Equal("Current count: 1", () => Browser.Title);
+        }
+
+        private void WaitUntilLoaded()
+        {
+            var jsExecutor = (IJavaScriptExecutor)Browser;
+            Browser.True(() => jsExecutor.ExecuteScript("return window['__aspnetcore__testing__blazor_wasm__started__'];") is not null);
+        }
+
+        private static string GetPublishedContentRoot(Assembly assembly)
+        {
+            var contentRoot = Path.Combine(AppContext.BaseDirectory, "trimmed", assembly.GetName().Name);
+
+            if (!Directory.Exists(contentRoot))
+            {
+                throw new DirectoryNotFoundException($"Test is configured to use trimmed outputs, but trimmed outputs were not found in {contentRoot}.");
+            }
+
+            return contentRoot;
+        }
+    }
+}

Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden.