From b7abe1ed90da94f30e9bb19571aac8819eba6421 Mon Sep 17 00:00:00 2001 From: Javier Calvarro Nelson Date: Thu, 10 Dec 2020 22:49:49 +0100 Subject: [PATCH] [Blazor] Fix publish with different combinations of SWA base paths (#28539) Description #28017 In 5.0.1 we fixed a publish scenario where Blazor webassembly wasn't respecting the StaticWebAssetBasePath defined for the project. Unfortunately the logic for handling the path composition was not robust enough and while it fixed the publish output layout, it introduced an integration issue later on at runtime. The current fix makes the handling of the path more tolerant to initial and final slashes and includes a new end to end test that validates this scenario. Customer Impact Customers can't host blazor applications outside the root path (/) Regression? Yes. Worked in 3.2 Risk Low. We've included additional tests and E2E automation to verify this case. Validation Automated Manual --- AspNetCore.sln | 43 ++++++++---- src/Components/ComponentsNoDeps.slnf | 3 +- .../WasmPublishIntegrationTest.cs | 24 ++++--- ....NET.Sdk.BlazorWebAssembly.Current.targets | 2 +- .../testassets/CustomBasePathApp/App.razor | 1 + .../CustomBasePathApp.csproj | 30 +++++++++ .../testassets/CustomBasePathApp/Program.cs | 22 +++++++ .../wwwroot/img/red-square.png | Bin 0 -> 201 bytes .../CustomBasePathApp/wwwroot/index.html | 14 ++++ .../HostedInAspNet.Server.csproj | 1 + .../HostedInAspNet.Server/Startup.cs | 31 ++++++++- .../Tests/HostedInAlternativeBasePathTest.cs | 45 +++++++++++++ .../E2ETest/Tests/MultipleHostedAppTest.cs | 62 ++++++++++++++++++ 13 files changed, 252 insertions(+), 26 deletions(-) create mode 100644 src/Components/WebAssembly/testassets/CustomBasePathApp/App.razor create mode 100644 src/Components/WebAssembly/testassets/CustomBasePathApp/CustomBasePathApp.csproj create mode 100644 src/Components/WebAssembly/testassets/CustomBasePathApp/Program.cs create mode 100644 src/Components/WebAssembly/testassets/CustomBasePathApp/wwwroot/img/red-square.png create mode 100644 src/Components/WebAssembly/testassets/CustomBasePathApp/wwwroot/index.html create mode 100644 src/Components/test/E2ETest/Tests/HostedInAlternativeBasePathTest.cs create mode 100644 src/Components/test/E2ETest/Tests/MultipleHostedAppTest.cs diff --git a/AspNetCore.sln b/AspNetCore.sln index ceb029889f..06f5b1b4c6 100644 --- a/AspNetCore.sln +++ b/AspNetCore.sln @@ -1503,7 +1503,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.App.Un EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Localization", "Localization", "{3D34C81F-2CB5-459E-87E9-0CC04757A2A0}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GlobalizationWasmApp", "src\Components\test\testassets\GlobalizationWasmApp\GlobalizationWasmApp.csproj", "{04CFE286-6D32-41EF-8887-4B5F8086A365}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GlobalizationWasmApp", "src\Components\test\testassets\GlobalizationWasmApp\GlobalizationWasmApp.csproj", "{04CFE286-6D32-41EF-8887-4B5F8086A365}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Localization.Abstractions", "src\Localization\Abstractions\src\Microsoft.Extensions.Localization.Abstractions.csproj", "{FEF97646-9BC9-4D1B-A939-784D915C18A4}" EndProject @@ -1539,6 +1539,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sdk", "Sdk", "{E83B0BCC-A8E EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HostingStartup", "src\SiteExtensions\Sdk\HostingStartup\HostingStartup.csproj", "{5D6F99C5-D292-4459-B8BD-8E4AD42E1B21}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CustomBasePathApp", "src\Components\WebAssembly\testassets\CustomBasePathApp\CustomBasePathApp.csproj", "{E2461809-D2EA-436D-B5C3-8A9EE0A283B8}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -7199,18 +7201,6 @@ Global {BAD47859-95DF-4C8F-9AF7-C48B68F478A1}.Release|x64.Build.0 = Release|Any CPU {BAD47859-95DF-4C8F-9AF7-C48B68F478A1}.Release|x86.ActiveCfg = Release|Any CPU {BAD47859-95DF-4C8F-9AF7-C48B68F478A1}.Release|x86.Build.0 = Release|Any CPU - {04CFE286-6D32-41EF-8887-4B5F8086A365}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {04CFE286-6D32-41EF-8887-4B5F8086A365}.Debug|Any CPU.Build.0 = Debug|Any CPU - {04CFE286-6D32-41EF-8887-4B5F8086A365}.Debug|x64.ActiveCfg = Debug|Any CPU - {04CFE286-6D32-41EF-8887-4B5F8086A365}.Debug|x64.Build.0 = Debug|Any CPU - {04CFE286-6D32-41EF-8887-4B5F8086A365}.Debug|x86.ActiveCfg = Debug|Any CPU - {04CFE286-6D32-41EF-8887-4B5F8086A365}.Debug|x86.Build.0 = Debug|Any CPU - {04CFE286-6D32-41EF-8887-4B5F8086A365}.Release|Any CPU.ActiveCfg = Release|Any CPU - {04CFE286-6D32-41EF-8887-4B5F8086A365}.Release|Any CPU.Build.0 = Release|Any CPU - {04CFE286-6D32-41EF-8887-4B5F8086A365}.Release|x64.ActiveCfg = Release|Any CPU - {04CFE286-6D32-41EF-8887-4B5F8086A365}.Release|x64.Build.0 = Release|Any CPU - {04CFE286-6D32-41EF-8887-4B5F8086A365}.Release|x86.ActiveCfg = Release|Any CPU - {04CFE286-6D32-41EF-8887-4B5F8086A365}.Release|x86.Build.0 = Release|Any CPU {010A9638-F20E-4FE6-A186-85732BFC9CB0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {010A9638-F20E-4FE6-A186-85732BFC9CB0}.Debug|Any CPU.Build.0 = Debug|Any CPU {010A9638-F20E-4FE6-A186-85732BFC9CB0}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -7223,6 +7213,18 @@ Global {010A9638-F20E-4FE6-A186-85732BFC9CB0}.Release|x64.Build.0 = Release|Any CPU {010A9638-F20E-4FE6-A186-85732BFC9CB0}.Release|x86.ActiveCfg = Release|Any CPU {010A9638-F20E-4FE6-A186-85732BFC9CB0}.Release|x86.Build.0 = Release|Any CPU + {04CFE286-6D32-41EF-8887-4B5F8086A365}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {04CFE286-6D32-41EF-8887-4B5F8086A365}.Debug|Any CPU.Build.0 = Debug|Any CPU + {04CFE286-6D32-41EF-8887-4B5F8086A365}.Debug|x64.ActiveCfg = Debug|Any CPU + {04CFE286-6D32-41EF-8887-4B5F8086A365}.Debug|x64.Build.0 = Debug|Any CPU + {04CFE286-6D32-41EF-8887-4B5F8086A365}.Debug|x86.ActiveCfg = Debug|Any CPU + {04CFE286-6D32-41EF-8887-4B5F8086A365}.Debug|x86.Build.0 = Debug|Any CPU + {04CFE286-6D32-41EF-8887-4B5F8086A365}.Release|Any CPU.ActiveCfg = Release|Any CPU + {04CFE286-6D32-41EF-8887-4B5F8086A365}.Release|Any CPU.Build.0 = Release|Any CPU + {04CFE286-6D32-41EF-8887-4B5F8086A365}.Release|x64.ActiveCfg = Release|Any CPU + {04CFE286-6D32-41EF-8887-4B5F8086A365}.Release|x64.Build.0 = Release|Any CPU + {04CFE286-6D32-41EF-8887-4B5F8086A365}.Release|x86.ActiveCfg = Release|Any CPU + {04CFE286-6D32-41EF-8887-4B5F8086A365}.Release|x86.Build.0 = Release|Any CPU {FEF97646-9BC9-4D1B-A939-784D915C18A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FEF97646-9BC9-4D1B-A939-784D915C18A4}.Debug|Any CPU.Build.0 = Debug|Any CPU {FEF97646-9BC9-4D1B-A939-784D915C18A4}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -7367,6 +7369,18 @@ Global {5D6F99C5-D292-4459-B8BD-8E4AD42E1B21}.Release|x64.Build.0 = Release|Any CPU {5D6F99C5-D292-4459-B8BD-8E4AD42E1B21}.Release|x86.ActiveCfg = Release|Any CPU {5D6F99C5-D292-4459-B8BD-8E4AD42E1B21}.Release|x86.Build.0 = Release|Any CPU + {E2461809-D2EA-436D-B5C3-8A9EE0A283B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E2461809-D2EA-436D-B5C3-8A9EE0A283B8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E2461809-D2EA-436D-B5C3-8A9EE0A283B8}.Debug|x64.ActiveCfg = Debug|Any CPU + {E2461809-D2EA-436D-B5C3-8A9EE0A283B8}.Debug|x64.Build.0 = Debug|Any CPU + {E2461809-D2EA-436D-B5C3-8A9EE0A283B8}.Debug|x86.ActiveCfg = Debug|Any CPU + {E2461809-D2EA-436D-B5C3-8A9EE0A283B8}.Debug|x86.Build.0 = Debug|Any CPU + {E2461809-D2EA-436D-B5C3-8A9EE0A283B8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E2461809-D2EA-436D-B5C3-8A9EE0A283B8}.Release|Any CPU.Build.0 = Release|Any CPU + {E2461809-D2EA-436D-B5C3-8A9EE0A283B8}.Release|x64.ActiveCfg = Release|Any CPU + {E2461809-D2EA-436D-B5C3-8A9EE0A283B8}.Release|x64.Build.0 = Release|Any CPU + {E2461809-D2EA-436D-B5C3-8A9EE0A283B8}.Release|x86.ActiveCfg = Release|Any CPU + {E2461809-D2EA-436D-B5C3-8A9EE0A283B8}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -8117,10 +8131,10 @@ Global {37329855-01B8-4B03-9765-1A941B06E43C} = {8C15FD04-7F90-43FC-B488-023432FE3CE1} {D3246226-BC1A-47F1-8E3E-C3380A8F13FB} = {8C15FD04-7F90-43FC-B488-023432FE3CE1} {B06ADD57-E855-4D8C-85DC-B323509AE540} = {898F7E0B-1671-42CB-9DFB-689AFF212ED3} - {04CFE286-6D32-41EF-8887-4B5F8086A365} = {6126DCE4-9692-4EE2-B240-C65743572995} {BAD47859-95DF-4C8F-9AF7-C48B68F478A1} = {A4C26078-B6D8-4FD8-87A6-7C15A3482038} {010A9638-F20E-4FE6-A186-85732BFC9CB0} = {A4C26078-B6D8-4FD8-87A6-7C15A3482038} {3D34C81F-2CB5-459E-87E9-0CC04757A2A0} = {017429CC-C5FB-48B4-9C46-034E29EE2F06} + {04CFE286-6D32-41EF-8887-4B5F8086A365} = {6126DCE4-9692-4EE2-B240-C65743572995} {FEF97646-9BC9-4D1B-A939-784D915C18A4} = {3D34C81F-2CB5-459E-87E9-0CC04757A2A0} {839CE175-E0D9-43B9-9FA8-F32C47E7F56B} = {3D34C81F-2CB5-459E-87E9-0CC04757A2A0} {50BF2926-7435-4F4B-88A9-3D0EDEB67FC8} = {3D34C81F-2CB5-459E-87E9-0CC04757A2A0} @@ -8138,6 +8152,7 @@ Global {545751D5-71FC-4889-A3A0-BBD731DBA18A} = {56B45580-B089-424E-A847-A6115D591950} {E83B0BCC-A8E0-4FBD-BE51-9A533C9CB972} = {DFC4F588-B4B4-484B-AB93-B36721374AD3} {5D6F99C5-D292-4459-B8BD-8E4AD42E1B21} = {E83B0BCC-A8E0-4FBD-BE51-9A533C9CB972} + {E2461809-D2EA-436D-B5C3-8A9EE0A283B8} = {7D2B0799-A634-42AC-AE77-5D167BA51389} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {3E8720B3-DBDD-498C-B383-2CC32A054E8F} diff --git a/src/Components/ComponentsNoDeps.slnf b/src/Components/ComponentsNoDeps.slnf index 0596b3bc64..7cf830b329 100644 --- a/src/Components/ComponentsNoDeps.slnf +++ b/src/Components/ComponentsNoDeps.slnf @@ -29,6 +29,7 @@ "src\\Components\\WebAssembly\\WebAssembly.Authentication\\test\\Microsoft.AspNetCore.Components.WebAssembly.Authentication.Tests.csproj", "src\\Components\\WebAssembly\\WebAssembly\\src\\Microsoft.AspNetCore.Components.WebAssembly.csproj", "src\\Components\\WebAssembly\\WebAssembly\\test\\Microsoft.AspNetCore.Components.WebAssembly.Tests.csproj", + "src\\Components\\WebAssembly\\testassets\\CustomBasePathApp\\CustomBasePathApp.csproj", "src\\Components\\WebAssembly\\testassets\\HostedInAspNet.Client\\HostedInAspNet.Client.csproj", "src\\Components\\WebAssembly\\testassets\\HostedInAspNet.Server\\HostedInAspNet.Server.csproj", "src\\Components\\WebAssembly\\testassets\\StandaloneApp\\StandaloneApp.csproj", @@ -48,4 +49,4 @@ "src\\Components\\test\\testassets\\TestServer\\Components.TestServer.csproj" ] } -} +} \ No newline at end of file diff --git a/src/Components/WebAssembly/Sdk/integrationtests/WasmPublishIntegrationTest.cs b/src/Components/WebAssembly/Sdk/integrationtests/WasmPublishIntegrationTest.cs index 57aa766733..7e14ca0936 100644 --- a/src/Components/WebAssembly/Sdk/integrationtests/WasmPublishIntegrationTest.cs +++ b/src/Components/WebAssembly/Sdk/integrationtests/WasmPublishIntegrationTest.cs @@ -252,14 +252,18 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests VerifyCompression(result, blazorPublishDirectory); } - [Fact] - public async Task Publish_WithStaticWebBasePathWorks() + [Theory] + [InlineData("different-path")] + [InlineData("/different-path")] + [InlineData("different-path/")] + [InlineData("/different-path/")] + public async Task Publish_WithStaticWebBasePathWorks(string basePath) { // Arrange using var project = ProjectDirectory.Create("blazorwasm", "razorclasslibrary"); project.AddProjectFileContent( -@" - different-path/ +$@" + {basePath} "); var result = await MSBuildProcessManager.DotnetMSBuild(project, "Publish"); @@ -300,14 +304,18 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests staticWebAssetsBasePath: "different-path"); } - [Fact] - public async Task Publish_Hosted_WithStaticWebBasePathWorks() + [Theory] + [InlineData("different-path")] + [InlineData("/different-path")] + [InlineData("different-path/")] + [InlineData("/different-path/")] + public async Task Publish_Hosted_WithStaticWebBasePathWorks(string basePath) { using var project = ProjectDirectory.Create("blazorhosted", additionalProjects: new[] { "blazorwasm", "razorclasslibrary", }); var wasmProject = project.GetSibling("blazorwasm"); wasmProject.AddProjectFileContent( -@" - different-path/ +$@" + {basePath} "); var result = await MSBuildProcessManager.DotnetMSBuild(project, "Publish"); diff --git a/src/Components/WebAssembly/Sdk/src/targets/Microsoft.NET.Sdk.BlazorWebAssembly.Current.targets b/src/Components/WebAssembly/Sdk/src/targets/Microsoft.NET.Sdk.BlazorWebAssembly.Current.targets index d8be7b59b1..df0d41cfc1 100644 --- a/src/Components/WebAssembly/Sdk/src/targets/Microsoft.NET.Sdk.BlazorWebAssembly.Current.targets +++ b/src/Components/WebAssembly/Sdk/src/targets/Microsoft.NET.Sdk.BlazorWebAssembly.Current.targets @@ -457,7 +457,7 @@ Copyright (c) .NET Foundation. All rights reserved. - <_BlazorPublishOutputPath Condition="'$(StaticWebAssetBasePath)' != '/'">wwwroot\$(StaticWebAssetBasePath.Replace('/', '\')) + <_BlazorPublishOutputPath Condition="'$(StaticWebAssetBasePath)' != '/'">wwwroot\$(StaticWebAssetBasePath.Replace('/', '\').Trim('\'))\ <_BlazorPublishOutputPath Condition="'$(StaticWebAssetBasePath)' == '/'">wwwroot\ <_BlazorFrameworkPublishPath>$(_BlazorPublishOutputPath)_framework\ diff --git a/src/Components/WebAssembly/testassets/CustomBasePathApp/App.razor b/src/Components/WebAssembly/testassets/CustomBasePathApp/App.razor new file mode 100644 index 0000000000..5eff8bf23f --- /dev/null +++ b/src/Components/WebAssembly/testassets/CustomBasePathApp/App.razor @@ -0,0 +1 @@ +Image from custom path: diff --git a/src/Components/WebAssembly/testassets/CustomBasePathApp/CustomBasePathApp.csproj b/src/Components/WebAssembly/testassets/CustomBasePathApp/CustomBasePathApp.csproj new file mode 100644 index 0000000000..5b551e0c2f --- /dev/null +++ b/src/Components/WebAssembly/testassets/CustomBasePathApp/CustomBasePathApp.csproj @@ -0,0 +1,30 @@ + + + + $(DefaultNetCoreTargetFramework) + /app + + + + + + + + + + + <_BclFile Include="$(ComponentsWebAssemblyBaseClassLibraryPath)*" /> + <_BclFile Include="$(ComponentsWebAssemblyBaseClassLibraryFacadesPath)*" /> + <_BclFile Include="$(ComponentsWebAssemblyFrameworkPath)*" /> + + + + + + + + + + + + diff --git a/src/Components/WebAssembly/testassets/CustomBasePathApp/Program.cs b/src/Components/WebAssembly/testassets/CustomBasePathApp/Program.cs new file mode 100644 index 0000000000..c28f5292b6 --- /dev/null +++ b/src/Components/WebAssembly/testassets/CustomBasePathApp/Program.cs @@ -0,0 +1,22 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Net.Http; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Components.WebAssembly.Hosting; +using Microsoft.Extensions.DependencyInjection; + +namespace CustomBasePathApp +{ + public class Program + { + public static async Task Main(string[] args) + { + var builder = WebAssemblyHostBuilder.CreateDefault(args); + builder.RootComponents.Add("app"); + + await builder.Build().RunAsync(); + } + } +} diff --git a/src/Components/WebAssembly/testassets/CustomBasePathApp/wwwroot/img/red-square.png b/src/Components/WebAssembly/testassets/CustomBasePathApp/wwwroot/img/red-square.png new file mode 100644 index 0000000000000000000000000000000000000000..25e7ed354ec7cf3628f0340df9025ee58e362dcd GIT binary patch literal 201 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1SD0tpLGH$#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=CqbAk63)r1AkM80YEY7?R=q_Piq}g8>i2M$VU;|IIynI7Oz} zf#=Dco9j8Ruw*frHqLVJU7!{qyn+PoZyST(3{n0U`FEA@D*|m|@O1TaS?83{1OOI1 BI${6- literal 0 HcmV?d00001 diff --git a/src/Components/WebAssembly/testassets/CustomBasePathApp/wwwroot/index.html b/src/Components/WebAssembly/testassets/CustomBasePathApp/wwwroot/index.html new file mode 100644 index 0000000000..73ab907e44 --- /dev/null +++ b/src/Components/WebAssembly/testassets/CustomBasePathApp/wwwroot/index.html @@ -0,0 +1,14 @@ + + + + + + + App loaded on custom path + + + + Loading... + + + diff --git a/src/Components/WebAssembly/testassets/HostedInAspNet.Server/HostedInAspNet.Server.csproj b/src/Components/WebAssembly/testassets/HostedInAspNet.Server/HostedInAspNet.Server.csproj index caf24b9387..de8c9cf8c6 100644 --- a/src/Components/WebAssembly/testassets/HostedInAspNet.Server/HostedInAspNet.Server.csproj +++ b/src/Components/WebAssembly/testassets/HostedInAspNet.Server/HostedInAspNet.Server.csproj @@ -5,6 +5,7 @@ + diff --git a/src/Components/WebAssembly/testassets/HostedInAspNet.Server/Startup.cs b/src/Components/WebAssembly/testassets/HostedInAspNet.Server/Startup.cs index 66b236ddf8..b82fe9a424 100644 --- a/src/Components/WebAssembly/testassets/HostedInAspNet.Server/Startup.cs +++ b/src/Components/WebAssembly/testassets/HostedInAspNet.Server/Startup.cs @@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -10,6 +11,13 @@ namespace HostedInAspNet.Server { public class Startup { + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + public IConfiguration Configuration { get; } + // This method gets called by the runtime. Use this method to add services to the container. // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 public void ConfigureServices(IServiceCollection services) @@ -20,6 +28,8 @@ namespace HostedInAspNet.Server // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env, BootResourceRequestLog bootResourceRequestLog) { + var mapAlternativePathApp = Configuration.GetValue("UseAlternativeBasePath"); + var mapAllApps = Configuration.GetValue("MapAllApps"); app.Use((context, next) => { // This is used by E2E tests to verify that the correct resources were fetched, @@ -39,14 +49,31 @@ namespace HostedInAspNet.Server app.UseWebAssemblyDebugging(); } - app.UseBlazorFrameworkFiles(); + if (mapAllApps || mapAlternativePathApp) + { + app.UseBlazorFrameworkFiles("/app"); + } + + if (mapAllApps || !mapAlternativePathApp) + { + app.UseBlazorFrameworkFiles(); + } + app.UseStaticFiles(); app.UseRouting(); app.UseEndpoints(endpoints => { - endpoints.MapFallbackToFile("index.html"); + if (mapAllApps || mapAlternativePathApp) + { + endpoints.MapFallbackToFile("/app/{**slug:nonfile}", "app/index.html"); + } + + if (mapAllApps || !mapAlternativePathApp) + { + endpoints.MapFallbackToFile("index.html"); + } }); } } diff --git a/src/Components/test/E2ETest/Tests/HostedInAlternativeBasePathTest.cs b/src/Components/test/E2ETest/Tests/HostedInAlternativeBasePathTest.cs new file mode 100644 index 0000000000..072ad27b28 --- /dev/null +++ b/src/Components/test/E2ETest/Tests/HostedInAlternativeBasePathTest.cs @@ -0,0 +1,45 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNetCore.Components.E2ETest.Infrastructure; +using Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures; +using Microsoft.AspNetCore.E2ETesting; +using OpenQA.Selenium; +using Xunit; +using Xunit.Abstractions; + +namespace Microsoft.AspNetCore.Components.E2ETest.Tests +{ + public class HostedInAlternativeBasePathTest : ServerTestBase + { + public HostedInAlternativeBasePathTest( + BrowserFixture browserFixture, + AspNetSiteServerFixture serverFixture, + ITestOutputHelper output) + : base(browserFixture, serverFixture, output) + { + serverFixture.AdditionalArguments.AddRange(new[] { "--UseAlternativeBasePath", "true" }); + serverFixture.BuildWebHostMethod = HostedInAspNet.Server.Program.BuildWebHost; + serverFixture.Environment = AspNetEnvironment.Development; + } + + protected override void InitializeAsyncCore() + { + Navigate("/app/", noReload: true); + WaitUntilLoaded(); + } + + [Fact] + public void CanLoadBlazorAppFromSubPath() + { + Assert.Equal("App loaded on custom path", Browser.Title); + Assert.Equal(0, Browser.GetBrowserLogs(LogLevel.Severe).Count); + } + + private void WaitUntilLoaded() + { + var app = Browser.Exists(By.TagName("app")); + Browser.NotEqual("Loading...", () => app.Text); + } + } +} diff --git a/src/Components/test/E2ETest/Tests/MultipleHostedAppTest.cs b/src/Components/test/E2ETest/Tests/MultipleHostedAppTest.cs new file mode 100644 index 0000000000..e216203dd1 --- /dev/null +++ b/src/Components/test/E2ETest/Tests/MultipleHostedAppTest.cs @@ -0,0 +1,62 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNetCore.Components.E2ETest.Infrastructure; +using Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures; +using Microsoft.AspNetCore.E2ETesting; +using OpenQA.Selenium; +using Xunit; +using Xunit.Abstractions; + +namespace Microsoft.AspNetCore.Components.E2ETest.Tests +{ + public class MultipleHostedAppTest: ServerTestBase + { + public MultipleHostedAppTest( + BrowserFixture browserFixture, + AspNetSiteServerFixture serverFixture, + ITestOutputHelper output) + : base(browserFixture, serverFixture, output) + { + serverFixture.AdditionalArguments.AddRange(new[] { "--MapAllApps", "true" }); + serverFixture.BuildWebHostMethod = HostedInAspNet.Server.Program.BuildWebHost; + serverFixture.Environment = AspNetEnvironment.Development; + } + + protected override void InitializeAsyncCore() + { + Navigate("/", noReload: true); + WaitUntilLoaded(); + } + + [Fact] + public void CanLoadBlazorAppFromSubPath() + { + Navigate("/app/"); + WaitUntilLoaded(); + Assert.Equal("App loaded on custom path", Browser.Title); + Assert.Equal(0, Browser.GetBrowserLogs(LogLevel.Severe).Count); + } + + [Fact] + public void HasTitle() + { + Assert.Equal("Sample Blazor app", Browser.Title); + } + + [Fact] + public void ServesStaticAssetsFromClientAppWebRoot() + { + var javascriptExecutor = (IJavaScriptExecutor)Browser; + var bootstrapTooltipType = javascriptExecutor + .ExecuteScript("return window.customJsWasLoaded;"); + Assert.True((bool)bootstrapTooltipType); + } + + private void WaitUntilLoaded() + { + var app = Browser.Exists(By.TagName("app")); + Browser.NotEqual("Loading...", () => app.Text); + } + } +}