Просмотр исходного кода

Changes to allow building in 3.1 branch

* Undo overzealous merge choices
* Undo temporary changes applied when part of the repo was building in the blazor-wasm branch
* Skip SPA template tests in 3.1
Pranav K 5 лет назад
Родитель
Сommit
ae2017f31e
35 измененных файлов с 818 добавлено и 106 удалено
  1. 0 8
      NuGet.config
  2. 1 0
      eng/Baseline.Designer.props
  3. 1 1
      eng/scripts/CodeCheck.ps1
  4. 5 5
      src/Components/Components.sln
  5. 5 0
      src/Components/ComponentsNoDeps.slnf
  6. 2 2
      src/Components/WebAssembly/Authentication.Msal/src/Microsoft.Authentication.WebAssembly.Msal.csproj
  7. 7 1
      src/Components/WebAssembly/Build/src/Microsoft.AspNetCore.Components.WebAssembly.Build.csproj
  8. 1 1
      src/Components/WebAssembly/Build/src/ReferenceFromSource.props
  9. 3 0
      src/Components/WebAssembly/Directory.Build.props
  10. 2 2
      src/Components/WebAssembly/WebAssembly.Authentication/src/Microsoft.AspNetCore.Components.WebAssembly.Authentication.csproj
  11. 1 4
      src/Components/WebAssembly/WebAssembly/src/Microsoft.AspNetCore.Components.WebAssembly.csproj
  12. 2 2
      src/Components/WebAssembly/WebAssembly/test/TestWebAssemblyJSRuntimeInvoker.cs
  13. 6 0
      src/Components/WebAssembly/testassets/Directory.Build.props
  14. 1 4
      src/Components/WebAssembly/testassets/Wasm.Authentication.Server/Wasm.Authentication.Server.csproj
  15. 1 0
      src/Framework/src/Microsoft.AspNetCore.App.Runtime.csproj
  16. 0 1
      src/Mvc/Mvc.NewtonsoftJson/src/JsonArrayPool.cs
  17. 7 12
      src/ProjectTemplates/ComponentsWebAssembly.ProjectTemplates/Microsoft.AspNetCore.Components.WebAssembly.Templates.csproj
  18. 69 13
      src/ProjectTemplates/ProjectTemplates.sln
  19. 1 0
      src/ProjectTemplates/ProjectTemplatesNoDeps.slnf
  20. 11 1
      src/ProjectTemplates/test/BaselineTest.cs
  21. 73 0
      src/ProjectTemplates/test/EmptyWebTemplateTest.cs
  22. 89 0
      src/ProjectTemplates/test/GrpcTemplateTest.cs
  23. 2 2
      src/ProjectTemplates/test/Helpers/AspNetProcess.cs
  24. 27 34
      src/ProjectTemplates/test/Helpers/Project.cs
  25. 190 0
      src/ProjectTemplates/test/IdentityUIPackageTest.cs
  26. 1 4
      src/ProjectTemplates/test/Infrastructure/GenerateTestProps.targets
  27. 0 8
      src/ProjectTemplates/test/Infrastructure/TemplateTests.props.in
  28. 36 0
      src/ProjectTemplates/test/ItemTemplateTests/BlazorServerTests.cs
  29. 1 1
      src/ProjectTemplates/test/ProjectTemplates.Tests.csproj
  30. 62 0
      src/ProjectTemplates/test/RazorClassLibraryTemplateTest.cs
  31. 30 0
      src/ProjectTemplates/test/SpaTemplateTest/AngularTemplateTest.cs
  32. 23 0
      src/ProjectTemplates/test/SpaTemplateTest/ReactReduxTemplateTest.cs
  33. 32 0
      src/ProjectTemplates/test/SpaTemplateTest/ReactTemplateTest.cs
  34. 70 0
      src/ProjectTemplates/test/WebApiTemplateTest.cs
  35. 56 0
      src/ProjectTemplates/test/WorkerTemplateTest.cs

+ 0 - 8
NuGet.config

@@ -4,19 +4,11 @@
     <clear />
     <!--Begin: Package sources managed by Dependency Flow automation. Do not edit the sources below.-->
     <add key="darc-pub-dotnet-corefx-8a3ffed" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/darc-pub-dotnet-corefx-8a3ffed5/nuget/v3/index.json" />
-    <add key="darc-pub-dotnet-corefx-66409e3" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/darc-pub-dotnet-corefx-66409e39/nuget/v3/index.json" />
-    <add key="darc-pub-dotnet-blazor-cc44960" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/darc-pub-dotnet-blazor-cc449601/nuget/v3/index.json" />
     <!--End: Package sources managed by Dependency Flow automation. Do not edit the sources above.-->
     <add key="dotnet-core" value="https://dotnetfeed.blob.core.windows.net/dotnet-core/index.json" />
     <add key="dotnet-eng" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-eng/nuget/v3/index.json" />
     <add key="dotnet3.1" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet3.1/nuget/v3/index.json" />
     <add key="dotnet3.1-transport" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet3.1-transport/nuget/v3/index.json" />
     <add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
-    <add key="dotnet-tools" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json" />
-    <add key="dotnet-eng" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-eng/nuget/v3/index.json" />
-    <add key="dotnet3.1" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet3.1/nuget/v3/index.json" />
-    <add key="dotnet3.1-transport" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet3.1-transport/nuget/v3/index.json" />
-    <add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
-    <add key="blazor-wasm" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet3.1-blazor/nuget/v3/index.json" />
   </packageSources>
 </configuration>

+ 1 - 0
eng/Baseline.Designer.props

@@ -253,6 +253,7 @@
   </PropertyGroup>
   <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Components.WebAssembly' AND '$(TargetFramework)' == 'netstandard2.1' ">
     <BaselinePackageReference Include="Microsoft.JSInterop.WebAssembly" Version="[3.2.0, )" />
+    <BaselinePackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.HttpHandler" Version="[3.2.0, )" />
     <BaselinePackageReference Include="Microsoft.AspNetCore.Components.Web" Version="[3.1.3, )" />
     <BaselinePackageReference Include="Microsoft.Extensions.Configuration.Json" Version="[3.1.3, )" />
     <BaselinePackageReference Include="Microsoft.Extensions.Logging" Version="[3.1.3, )" />

+ 1 - 1
eng/scripts/CodeCheck.ps1

@@ -138,7 +138,7 @@ try {
     Get-ChildItem "$repoRoot/*.sln" -Recurse `
         | ? {
             # These .sln files are used by the templating engine.
-            ($_.Name -ne "BlazorServerWeb_CSharp.sln")
+            (($_.Name -ne "BlazorServerWeb_CSharp.sln") -and ($_.Name -ne 'ComponentsWebAssembly-CSharp.sln'))
         } `
         | % {
         Write-Host "  Checking $(Split-Path -Leaf $_)"

+ 5 - 5
src/Components/Components.sln

@@ -25,15 +25,15 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Compon
 EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "testassets", "testassets", "{A7ABAC29-F73F-456D-AE54-46842CFC2E10}"
 EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HostedInAspNet.Client", "Blazor\testassets\HostedInAspNet.Client\HostedInAspNet.Client.csproj", "{FD37F740-A654-4117-BFB6-9112CE4C1D3B}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HostedInAspNet.Client", "WebAssembly\testassets\HostedInAspNet.Client\HostedInAspNet.Client.csproj", "{FD37F740-A654-4117-BFB6-9112CE4C1D3B}"
 EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HostedInAspNet.Server", "Blazor\testassets\HostedInAspNet.Server\HostedInAspNet.Server.csproj", "{C1E2C117-BE47-4E29-94B3-753262D97A5C}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HostedInAspNet.Server", "WebAssembly\testassets\HostedInAspNet.Server\HostedInAspNet.Server.csproj", "{C1E2C117-BE47-4E29-94B3-753262D97A5C}"
 EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MonoSanity", "Blazor\testassets\MonoSanity\MonoSanity.csproj", "{F16C1A7C-A2BD-4EB1-8BC8-23B1375F3B9E}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MonoSanity", "WebAssembly\testassets\MonoSanity\MonoSanity.csproj", "{F16C1A7C-A2BD-4EB1-8BC8-23B1375F3B9E}"
 EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MonoSanityClient", "Blazor\testassets\MonoSanityClient\MonoSanityClient.csproj", "{1C4BF2D3-44A8-4A71-B031-15B983663CB0}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MonoSanityClient", "WebAssembly\testassets\MonoSanityClient\MonoSanityClient.csproj", "{1C4BF2D3-44A8-4A71-B031-15B983663CB0}"
 EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StandaloneApp", "Blazor\testassets\StandaloneApp\StandaloneApp.csproj", "{C0FFB29E-4696-4875-9039-E5FA1AC5A42A}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StandaloneApp", "WebAssembly\testassets\StandaloneApp\StandaloneApp.csproj", "{C0FFB29E-4696-4875-9039-E5FA1AC5A42A}"
 EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Web", "Web", "{A27FF193-195B-4474-8E6C-840B2E339373}"
 EndProject

+ 5 - 0
src/Components/ComponentsNoDeps.slnf

@@ -30,6 +30,11 @@
       "WebAssembly\\WebAssemblyHttpHandler\\src\\Microsoft.AspNetCore.Components.WebAssembly.HttpHandler.csproj",
       "WebAssembly\\WebAssembly\\src\\Microsoft.AspNetCore.Components.WebAssembly.csproj",
       "WebAssembly\\WebAssembly\\test\\Microsoft.AspNetCore.Components.WebAssembly.Tests.csproj",
+      "WebAssembly\\testassets\\HostedInAspNet.Client\\HostedInAspNet.Client.csproj",
+      "WebAssembly\\testassets\\HostedInAspNet.Server\\HostedInAspNet.Server.csproj",
+      "WebAssembly\\testassets\\MonoSanityClient\\MonoSanityClient.csproj",
+      "WebAssembly\\testassets\\MonoSanity\\MonoSanity.csproj",
+      "WebAssembly\\testassets\\StandaloneApp\\StandaloneApp.csproj",
       "Web\\src\\Microsoft.AspNetCore.Components.Web.csproj",
       "Web\\test\\Microsoft.AspNetCore.Components.Web.Tests.csproj",
       "benchmarkapps\\Wasm.Performance\\Driver\\Wasm.Performance.Driver.csproj",

+ 2 - 2
src/Components/WebAssembly/Authentication.Msal/src/Microsoft.Authentication.WebAssembly.Msal.csproj

@@ -39,7 +39,7 @@
     <UpToDateCheckOutput Include="@(YarnOutputs)" Set="StaticWebassets" />
   </ItemGroup>
 
-  <Target Name="_CreateInteropHash" BeforeTargets="CompileInterop" Condition="'$(DesignTimeBuild)' != 'true'">
+  <Target Name="_CreateInteropHash" BeforeTargets="CompileInterop" Condition="'$(BuildNodeJS)' != 'false' AND '$(DesignTimeBuild)' != 'true'">
 
     <PropertyGroup>
       <InteropCompilationCacheFile>$(IntermediateOutputPath)interop.cache</InteropCompilationCacheFile>
@@ -57,7 +57,7 @@
 
   </Target>
 
-  <Target Name="CompileInterop" Condition="'$(DesignTimeBuild)' != 'true'" Inputs="$(InteropCompilationCacheFile)" Outputs="@(YarnOutputs)">
+  <Target Name="CompileInterop" Condition="'$(BuildNodeJS)' != 'false' AND '$(DesignTimeBuild)' != 'true'" Inputs="$(InteropCompilationCacheFile)" Outputs="@(YarnOutputs)">
     <Yarn Command="install --mutex network" WorkingDirectory="$(YarnWorkingDir)" />
     <Yarn Command="run build:release" WorkingDirectory="$(YarnWorkingDir)" Condition="'$(Configuration)' == 'Release'" />
     <Yarn Command="run build:debug" WorkingDirectory="$(YarnWorkingDir)" Condition="'$(Configuration)' == 'Debug'" />

+ 7 - 1
src/Components/WebAssembly/Build/src/Microsoft.AspNetCore.Components.WebAssembly.Build.csproj

@@ -40,6 +40,11 @@
       Private="false"
       Condition="'$(BuildNodeJS)' != 'false' and '$(BuildingInsideVisualStudio)' != 'true'" />
 
+    <ProjectReference
+      Include="..\..\Compression\src\Microsoft.AspNetCore.Components.WebAssembly.Build.BrotliCompression.csproj"
+      ReferenceOutputAssemblies="false"
+      Condition="'$(TargetFramework)' == '$(DefaultNetCoreTargetFramework)'" />
+
     <Reference Include="Microsoft.Build.Framework" ExcludeAssets="Runtime" />
     <Reference Include="Microsoft.Build.Utilities.Core"  ExcludeAssets="Runtime" />
     <Reference Include="System.Reflection.Metadata" Condition="'$(TargetFramework)' == 'net46'" />
@@ -82,7 +87,8 @@
     Name="GetBrotliTools"
     BeforeTargets="Build;GenerateNuspec"
     Inputs="@(_BrotliToolPathInput)"
-    Outputs="@(_BrotliToolPathOutput)">
+    Outputs="@(_BrotliToolPathOutput)"
+    Condition="'$(DotNetBuildFromSource)' != 'true'">
     <ItemGroup>
       <_BrotliToolsPath Include="$(MSBuildThisFileDirectory)bin\$(Configuration)\tools\compression\" />
     </ItemGroup>

+ 1 - 1
src/Components/WebAssembly/Build/src/ReferenceFromSource.props

@@ -20,7 +20,7 @@
     <Reference Include="Microsoft.AspNetCore.Components.WebAssembly.Runtime" />
   </ItemGroup>
 
-  <Target Name="_BuildBlazorBuildProject" BeforeTargets="ResolveProjectReferences">
+  <Target Name="_BuildBlazorBuildProject" BeforeTargets="ResolveProjectReferences" Condition="'$(NoBuild)' != 'true'">
     <!--
       The Blazor.Build project cross-compiles and we need the output of all TFMs to be available in the build output.
       We can't represent this in any good way using ProjectReference elements, but we can try and build it instead.

+ 3 - 0
src/Components/WebAssembly/Directory.Build.props

@@ -3,5 +3,8 @@
   <PropertyGroup>
     <!-- Override version labels -->
     <VersionPrefix>$(ComponentsWebAssemblyVersionPrefix)</VersionPrefix>
+
+    <!-- Avoid source build issues with WebAssembly -->
+    <ExcludeFromSourceBuild>true</ExcludeFromSourceBuild>
   </PropertyGroup>
 </Project>

+ 2 - 2
src/Components/WebAssembly/WebAssembly.Authentication/src/Microsoft.AspNetCore.Components.WebAssembly.Authentication.csproj

@@ -40,7 +40,7 @@
     <UpToDateCheckOutput Include="@(YarnOutputs)" Set="StaticWebassets" />
   </ItemGroup>
 
-  <Target Name="_CreateInteropHash" BeforeTargets="CompileInterop" Condition="'$(DesignTimeBuild)' != 'true'">
+  <Target Name="_CreateInteropHash" BeforeTargets="CompileInterop" Condition="'$(BuildNodeJS)' != 'false' AND '$(DesignTimeBuild)' != 'true'">
 
     <PropertyGroup>
       <InteropCompilationCacheFile>$(IntermediateOutputPath)interop.cache</InteropCompilationCacheFile>
@@ -58,7 +58,7 @@
 
   </Target>
 
-  <Target Name="CompileInterop" Condition="'$(DesignTimeBuild)' != 'true'" Inputs="$(InteropCompilationCacheFile)" Outputs="@(YarnOutputs)">
+  <Target Name="CompileInterop" Condition="'$(BuildNodeJS)' != 'false' AND '$(DesignTimeBuild)' != 'true'" Inputs="$(InteropCompilationCacheFile)" Outputs="@(YarnOutputs)">
     <Yarn Command="install --mutex network" WorkingDirectory="$(YarnWorkingDir)" />
     <Yarn Command="run build:release" WorkingDirectory="$(YarnWorkingDir)" Condition="'$(Configuration)' == 'Release'" />
     <Yarn Command="run build:debug" WorkingDirectory="$(YarnWorkingDir)" Condition="'$(Configuration)' == 'Debug'" />

+ 1 - 4
src/Components/WebAssembly/WebAssembly/src/Microsoft.AspNetCore.Components.WebAssembly.csproj

@@ -13,9 +13,7 @@
     <Reference Include="Microsoft.Extensions.Configuration.Json" />
     <Reference Include="Microsoft.Extensions.Logging" />
     <Reference Include="Microsoft.JSInterop.WebAssembly" />
-    <ProjectReference
-      Include="..\..\WebAssemblyHttpHandler\src\Microsoft.AspNetCore.Components.WebAssembly.HttpHandler.csproj"
-      IncludeAssets="compile" />
+    <Reference Include="Microsoft.AspNetCore.Components.WebAssembly.HttpHandler" IncludeAssets="compile" />
   </ItemGroup>
 
   <ItemGroup>
@@ -30,5 +28,4 @@
     <InternalsVisibleTo Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication.Tests" />
     <InternalsVisibleTo Include="BasicTestApp" />
   </ItemGroup>
-
 </Project>

+ 2 - 2
src/Components/WebAssembly/WebAssembly/test/TestWebAssemblyJSRuntimeInvoker.cs

@@ -23,10 +23,10 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Hosting
                     return (TResult)(object)_environment;
                 case "Blazor._internal.getConfig":
                     return (TResult)(object)null;
-                case "Blazor._internal.navigationManager.getBaseURI":
+                case "Blazor._internal.navigationManager.getUnmarshalledBaseURI":
                     var testUri = "https://www.example.com/awesome-part-that-will-be-truncated-in-tests";
                     return (TResult)(object)testUri;
-                case "Blazor._internal.navigationManager.getLocationHref":
+                case "Blazor._internal.navigationManager.getUnmarshalledLocationHref":
                     var testHref = "https://www.example.com/awesome-part-that-will-be-truncated-in-tests/cool";
                     return (TResult)(object)testHref;
                 default:

+ 6 - 0
src/Components/WebAssembly/testassets/Directory.Build.props

@@ -0,0 +1,6 @@
+<Project>
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory)..\, Directory.Build.props))\Directory.Build.props" />
+  <PropertyGroup>
+    <IsPackable>false</IsPackable>
+  </PropertyGroup>
+</Project>

+ 1 - 4
src/Components/WebAssembly/testassets/Wasm.Authentication.Server/Wasm.Authentication.Server.csproj

@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk.Web">
 
   <PropertyGroup>
     <TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
@@ -10,9 +10,6 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <!-- Remove the reference to Microsoft.AspNetCore.App from IdentityServer4 -->
-    <FrameworkReference Remove="Microsoft.AspNetCore.App" />
-
     <Reference Include="Microsoft.AspNetCore" />
     <Reference Include="Microsoft.AspNetCore.Diagnostics" />
     <Reference Include="Microsoft.AspNetCore.ApiAuthorization.IdentityServer" />

+ 1 - 0
src/Framework/src/Microsoft.AspNetCore.App.Runtime.csproj

@@ -175,6 +175,7 @@ This package is an internal implementation of the .NET Core SDK and is not meant
       _BatchCopyToRedistLayout;
       _CreateInternalSharedFxArchive;
       _CreateRedistSharedFxArchive;
+      _InstallFrameworkIntoLocalDotNet;
     </CoreBuildDependsOn>
     <CrossGenDependsOn>
       ResolveReferences;

+ 0 - 1
src/Mvc/Mvc.NewtonsoftJson/src/JsonArrayPool.cs

@@ -37,4 +37,3 @@ namespace Microsoft.AspNetCore.Mvc.NewtonsoftJson
         }
     }
 }
- 

+ 7 - 12
src/ProjectTemplates/ComponentsWebAssembly.ProjectTemplates/Microsoft.AspNetCore.Components.WebAssembly.Templates.csproj

@@ -14,18 +14,6 @@
     <!-- Lists the versions of dependencies not built in this repo. Packages produced from this repo should be listed as a PackageVersionVariableReference.  -->
     <GeneratedContentProperties>
       DefaultNetCoreTargetFramework=$(DefaultNetCoreTargetFramework);
-      MicrosoftAspNetCoreComponentsWebAssemblyPackageVersion=$(MicrosoftAspNetCoreComponentsWebAssemblyPackageVersion);
-      MicrosoftAspNetCoreComponentsWebAssemblyBuildPackageVersion=$(MicrosoftAspNetCoreComponentsWebAssemblyBuildPackageVersion);
-      MicrosoftAspNetCoreComponentsWebAssemblyDevServerPackageVersion=$(MicrosoftAspNetCoreComponentsWebAssemblyDevServerPackageVersion);
-      MicrosoftAspNetCoreBlazorHttpClientPackageVersion=$(MicrosoftAspNetCoreBlazorHttpClientPackageVersion);
-      MicrosoftAspNetCoreComponentsWebAssemblyServerPackageVersion=$(MicrosoftAspNetCoreComponentsWebAssemblyServerPackageVersion);
-      MicrosoftAspNetCoreComponentsAuthorizationPackageVersion=$(MicrosoftAspNetCoreComponentsAuthorizationPackageVersion);
-      MicrosoftAspNetCoreDiagnosticsEntityFrameworkCorePackageVersion=$(MicrosoftAspNetCoreDiagnosticsEntityFrameworkCorePackageVersion);
-      MicrosoftAspNetCoreIdentityEntityFrameworkCorePackageVersion=$(MicrosoftAspNetCoreIdentityEntityFrameworkCorePackageVersion);
-      MicrosoftAspNetCoreAuthenticationAzureADUIPackageVersion=$(MicrosoftAspNetCoreAuthenticationAzureADUIPackageVersion);
-      MicrosoftAspNetCoreAuthenticationAzureADB2CUIPackageVersion=$(MicrosoftAspNetCoreAuthenticationAzureADB2CUIPackageVersion);
-      MicrosoftAspNetCoreIdentityUIPackageVersion=$(MicrosoftAspNetCoreIdentityUIPackageVersion);
-      MicrosoftAspNetCoreApiAuthorizationIdentityServerPackageVersion=$(MicrosoftAspNetCoreApiAuthorizationIdentityServerPackageVersion);
       MicrosoftEntityFrameworkCoreSqlServerPackageVersion=$(MicrosoftEntityFrameworkCoreSqlServerPackageVersion);
       MicrosoftEntityFrameworkCoreSqlitePackageVersion=$(MicrosoftEntityFrameworkCoreSqlitePackageVersion);
       MicrosoftEntityFrameworkCoreToolsPackageVersion=$(MicrosoftEntityFrameworkCoreToolsPackageVersion);
@@ -36,12 +24,19 @@
 
   <ItemGroup>
     <!-- These projects product packages that the templates depend on. See GenerateContent.targets -->
+    <PackageVersionVariableReference Include="$(RepoRoot)src\Azure\AzureAD\Authentication.AzureAD.UI\src\Microsoft.AspNetCore.Authentication.AzureAD.UI.csproj" />
+    <PackageVersionVariableReference Include="$(RepoRoot)src\Azure\AzureAD\Authentication.AzureADB2C.UI\src\Microsoft.AspNetCore.Authentication.AzureADB2C.UI.csproj" />
+    <PackageVersionVariableReference Include="$(RepoRoot)src\Identity\EntityFrameworkCore\src\Microsoft.AspNetCore.Identity.EntityFrameworkCore.csproj" />
+    <PackageVersionVariableReference Include="$(RepoRoot)src\Identity\ApiAuthorization.IdentityServer\src\Microsoft.AspNetCore.ApiAuthorization.IdentityServer.csproj" />
+    <PackageVersionVariableReference Include="$(RepoRoot)src\Identity\UI\src\Microsoft.AspNetCore.Identity.UI.csproj" />
+    <PackageVersionVariableReference Include="$(RepoRoot)src\Middleware\Diagnostics.EntityFrameworkCore\src\Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.csproj" />
     <PackageVersionVariableReference Include="$(ComponentsWebAssemblyProjectsRoot)WebAssembly\src\Microsoft.AspNetCore.Components.WebAssembly.csproj" />
     <PackageVersionVariableReference Include="$(ComponentsWebAssemblyProjectsRoot)Build\src\Microsoft.AspNetCore.Components.WebAssembly.Build.csproj" />
     <PackageVersionVariableReference Include="$(ComponentsWebAssemblyProjectsRoot)DevServer\src\Microsoft.AspNetCore.Components.WebAssembly.DevServer.csproj" />
     <PackageVersionVariableReference Include="$(ComponentsWebAssemblyProjectsRoot)Server\src\Microsoft.AspNetCore.Components.WebAssembly.Server.csproj" />
     <PackageVersionVariableReference Include="$(ComponentsWebAssemblyProjectsRoot)WebAssembly.Authentication\src\Microsoft.AspNetCore.Components.WebAssembly.Authentication.csproj" />
     <PackageVersionVariableReference Include="$(ComponentsWebAssemblyProjectsRoot)Authentication.Msal\src\Microsoft.Authentication.WebAssembly.Msal.csproj" />
+
   </ItemGroup>
 
   <ItemGroup>

+ 69 - 13
src/ProjectTemplates/ProjectTemplates.sln

@@ -3,6 +3,14 @@ Microsoft Visual Studio Solution File, Format Version 12.00
 # Visual Studio Version 16
 VisualStudioVersion = 16.0.28627.84
 MinimumVisualStudioVersion = 15.0.26124.0
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.Web.ProjectTemplates", "Web.ProjectTemplates\Microsoft.DotNet.Web.ProjectTemplates.csproj", "{3D3DE8B3-6B54-4CF4-82B0-718E0009A4E5}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.Web.Spa.ProjectTemplates", "Web.Spa.ProjectTemplates\Microsoft.DotNet.Web.Spa.ProjectTemplates.csproj", "{96251C41-7953-46DC-B131-5A070640959A}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.Web.Client.ItemTemplates", "Web.Client.ItemTemplates\Microsoft.DotNet.Web.Client.ItemTemplates.csproj", "{92F0615B-4C8F-456C-86C0-39384BB7031E}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.Web.ItemTemplates", "Web.ItemTemplates\Microsoft.DotNet.Web.ItemTemplates.csproj", "{BC03F087-9B6F-4A66-9571-B0C5C204A101}"
+EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProjectTemplates.Tests", "test\ProjectTemplates.Tests.csproj", "{AF371A60-8A85-4ADF-BE44-0F2B94234DB1}"
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetToolsInstaller", "testassets\DotNetToolsInstaller\DotNetToolsInstaller.csproj", "{4B971DBF-6B07-4DC5-914D-4D5681F220CC}"
@@ -169,7 +177,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.ApiAut
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.SpaServices.Extensions", "..\Middleware\SpaServices.Extensions\src\Microsoft.AspNetCore.SpaServices.Extensions.csproj", "{06D0D7B2-EDA3-45A2-A060-AB791ED1DB80}"
 EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Components.WebAssembly.Templates", "ComponentsWebAssembly.ProjectTemplates\Microsoft.AspNetCore.Components.WebAssembly.Templates.csproj", "{F2870943-14EA-4AD2-AC01-E4E66E0B63F4}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Components.WebAssembly.Templates", "ComponentsWebAssembly.ProjectTemplates\Microsoft.AspNetCore.Components.WebAssembly.Templates.csproj", "{5AE7B4C4-790D-4809-A5FE-D7303DFBC11B}"
 EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -181,6 +189,54 @@ Global
 		Release|x86 = Release|x86
 	EndGlobalSection
 	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{3D3DE8B3-6B54-4CF4-82B0-718E0009A4E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{3D3DE8B3-6B54-4CF4-82B0-718E0009A4E5}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{3D3DE8B3-6B54-4CF4-82B0-718E0009A4E5}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{3D3DE8B3-6B54-4CF4-82B0-718E0009A4E5}.Debug|x64.Build.0 = Debug|Any CPU
+		{3D3DE8B3-6B54-4CF4-82B0-718E0009A4E5}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{3D3DE8B3-6B54-4CF4-82B0-718E0009A4E5}.Debug|x86.Build.0 = Debug|Any CPU
+		{3D3DE8B3-6B54-4CF4-82B0-718E0009A4E5}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{3D3DE8B3-6B54-4CF4-82B0-718E0009A4E5}.Release|Any CPU.Build.0 = Release|Any CPU
+		{3D3DE8B3-6B54-4CF4-82B0-718E0009A4E5}.Release|x64.ActiveCfg = Release|Any CPU
+		{3D3DE8B3-6B54-4CF4-82B0-718E0009A4E5}.Release|x64.Build.0 = Release|Any CPU
+		{3D3DE8B3-6B54-4CF4-82B0-718E0009A4E5}.Release|x86.ActiveCfg = Release|Any CPU
+		{3D3DE8B3-6B54-4CF4-82B0-718E0009A4E5}.Release|x86.Build.0 = Release|Any CPU
+		{96251C41-7953-46DC-B131-5A070640959A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{96251C41-7953-46DC-B131-5A070640959A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{96251C41-7953-46DC-B131-5A070640959A}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{96251C41-7953-46DC-B131-5A070640959A}.Debug|x64.Build.0 = Debug|Any CPU
+		{96251C41-7953-46DC-B131-5A070640959A}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{96251C41-7953-46DC-B131-5A070640959A}.Debug|x86.Build.0 = Debug|Any CPU
+		{96251C41-7953-46DC-B131-5A070640959A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{96251C41-7953-46DC-B131-5A070640959A}.Release|Any CPU.Build.0 = Release|Any CPU
+		{96251C41-7953-46DC-B131-5A070640959A}.Release|x64.ActiveCfg = Release|Any CPU
+		{96251C41-7953-46DC-B131-5A070640959A}.Release|x64.Build.0 = Release|Any CPU
+		{96251C41-7953-46DC-B131-5A070640959A}.Release|x86.ActiveCfg = Release|Any CPU
+		{96251C41-7953-46DC-B131-5A070640959A}.Release|x86.Build.0 = Release|Any CPU
+		{92F0615B-4C8F-456C-86C0-39384BB7031E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{92F0615B-4C8F-456C-86C0-39384BB7031E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{92F0615B-4C8F-456C-86C0-39384BB7031E}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{92F0615B-4C8F-456C-86C0-39384BB7031E}.Debug|x64.Build.0 = Debug|Any CPU
+		{92F0615B-4C8F-456C-86C0-39384BB7031E}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{92F0615B-4C8F-456C-86C0-39384BB7031E}.Debug|x86.Build.0 = Debug|Any CPU
+		{92F0615B-4C8F-456C-86C0-39384BB7031E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{92F0615B-4C8F-456C-86C0-39384BB7031E}.Release|Any CPU.Build.0 = Release|Any CPU
+		{92F0615B-4C8F-456C-86C0-39384BB7031E}.Release|x64.ActiveCfg = Release|Any CPU
+		{92F0615B-4C8F-456C-86C0-39384BB7031E}.Release|x64.Build.0 = Release|Any CPU
+		{92F0615B-4C8F-456C-86C0-39384BB7031E}.Release|x86.ActiveCfg = Release|Any CPU
+		{92F0615B-4C8F-456C-86C0-39384BB7031E}.Release|x86.Build.0 = Release|Any CPU
+		{BC03F087-9B6F-4A66-9571-B0C5C204A101}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{BC03F087-9B6F-4A66-9571-B0C5C204A101}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{BC03F087-9B6F-4A66-9571-B0C5C204A101}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{BC03F087-9B6F-4A66-9571-B0C5C204A101}.Debug|x64.Build.0 = Debug|Any CPU
+		{BC03F087-9B6F-4A66-9571-B0C5C204A101}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{BC03F087-9B6F-4A66-9571-B0C5C204A101}.Debug|x86.Build.0 = Debug|Any CPU
+		{BC03F087-9B6F-4A66-9571-B0C5C204A101}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{BC03F087-9B6F-4A66-9571-B0C5C204A101}.Release|Any CPU.Build.0 = Release|Any CPU
+		{BC03F087-9B6F-4A66-9571-B0C5C204A101}.Release|x64.ActiveCfg = Release|Any CPU
+		{BC03F087-9B6F-4A66-9571-B0C5C204A101}.Release|x64.Build.0 = Release|Any CPU
+		{BC03F087-9B6F-4A66-9571-B0C5C204A101}.Release|x86.ActiveCfg = Release|Any CPU
+		{BC03F087-9B6F-4A66-9571-B0C5C204A101}.Release|x86.Build.0 = Release|Any CPU
 		{AF371A60-8A85-4ADF-BE44-0F2B94234DB1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{AF371A60-8A85-4ADF-BE44-0F2B94234DB1}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{AF371A60-8A85-4ADF-BE44-0F2B94234DB1}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -1165,18 +1221,18 @@ Global
 		{06D0D7B2-EDA3-45A2-A060-AB791ED1DB80}.Release|x64.Build.0 = Release|Any CPU
 		{06D0D7B2-EDA3-45A2-A060-AB791ED1DB80}.Release|x86.ActiveCfg = Release|Any CPU
 		{06D0D7B2-EDA3-45A2-A060-AB791ED1DB80}.Release|x86.Build.0 = Release|Any CPU
-		{F2870943-14EA-4AD2-AC01-E4E66E0B63F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{F2870943-14EA-4AD2-AC01-E4E66E0B63F4}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{F2870943-14EA-4AD2-AC01-E4E66E0B63F4}.Debug|x64.ActiveCfg = Debug|Any CPU
-		{F2870943-14EA-4AD2-AC01-E4E66E0B63F4}.Debug|x64.Build.0 = Debug|Any CPU
-		{F2870943-14EA-4AD2-AC01-E4E66E0B63F4}.Debug|x86.ActiveCfg = Debug|Any CPU
-		{F2870943-14EA-4AD2-AC01-E4E66E0B63F4}.Debug|x86.Build.0 = Debug|Any CPU
-		{F2870943-14EA-4AD2-AC01-E4E66E0B63F4}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{F2870943-14EA-4AD2-AC01-E4E66E0B63F4}.Release|Any CPU.Build.0 = Release|Any CPU
-		{F2870943-14EA-4AD2-AC01-E4E66E0B63F4}.Release|x64.ActiveCfg = Release|Any CPU
-		{F2870943-14EA-4AD2-AC01-E4E66E0B63F4}.Release|x64.Build.0 = Release|Any CPU
-		{F2870943-14EA-4AD2-AC01-E4E66E0B63F4}.Release|x86.ActiveCfg = Release|Any CPU
-		{F2870943-14EA-4AD2-AC01-E4E66E0B63F4}.Release|x86.Build.0 = Release|Any CPU
+		{5AE7B4C4-790D-4809-A5FE-D7303DFBC11B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{5AE7B4C4-790D-4809-A5FE-D7303DFBC11B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{5AE7B4C4-790D-4809-A5FE-D7303DFBC11B}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{5AE7B4C4-790D-4809-A5FE-D7303DFBC11B}.Debug|x64.Build.0 = Debug|Any CPU
+		{5AE7B4C4-790D-4809-A5FE-D7303DFBC11B}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{5AE7B4C4-790D-4809-A5FE-D7303DFBC11B}.Debug|x86.Build.0 = Debug|Any CPU
+		{5AE7B4C4-790D-4809-A5FE-D7303DFBC11B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{5AE7B4C4-790D-4809-A5FE-D7303DFBC11B}.Release|Any CPU.Build.0 = Release|Any CPU
+		{5AE7B4C4-790D-4809-A5FE-D7303DFBC11B}.Release|x64.ActiveCfg = Release|Any CPU
+		{5AE7B4C4-790D-4809-A5FE-D7303DFBC11B}.Release|x64.Build.0 = Release|Any CPU
+		{5AE7B4C4-790D-4809-A5FE-D7303DFBC11B}.Release|x86.ActiveCfg = Release|Any CPU
+		{5AE7B4C4-790D-4809-A5FE-D7303DFBC11B}.Release|x86.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE

+ 1 - 0
src/ProjectTemplates/ProjectTemplatesNoDeps.slnf

@@ -2,6 +2,7 @@
   "solution": {
     "path": "ProjectTemplates.sln",
     "projects": [
+      "ComponentsWebAssembly.ProjectTemplates\\Microsoft.AspNetCore.Components.WebAssembly.Templates.csproj",
       "Web.Client.ItemTemplates\\Microsoft.DotNet.Web.Client.ItemTemplates.csproj",
       "Web.ItemTemplates\\Microsoft.DotNet.Web.ItemTemplates.csproj",
       "Web.ProjectTemplates\\Microsoft.DotNet.Web.ProjectTemplates.csproj",

+ 11 - 1
src/ProjectTemplates/test/BaselineTest.cs

@@ -122,6 +122,16 @@ namespace Templates.Test
                 text += "supportpagesandviewstrue";
             }
 
+            if (arguments.Contains("-ho"))
+            {
+                text += "hosted";
+            }
+
+            if (arguments.Contains("--pwa"))
+            {
+                text += "pwa";
+            }
+
             return text;
         }
 
@@ -140,4 +150,4 @@ namespace Templates.Test
             }
         }
     }
-}
+}

+ 73 - 0
src/ProjectTemplates/test/EmptyWebTemplateTest.cs

@@ -0,0 +1,73 @@
+// 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.Threading.Tasks;
+using Templates.Test.Helpers;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace Templates.Test
+{
+    public class EmptyWebTemplateTest
+    {
+        public EmptyWebTemplateTest(ProjectFactoryFixture projectFactory, ITestOutputHelper output)
+        {
+            ProjectFactory = projectFactory;
+            Output = output;
+        }
+
+        public Project Project { get; set; }
+
+        public ProjectFactoryFixture ProjectFactory { get; }
+
+        public ITestOutputHelper Output { get; }
+
+        [Fact]
+        public async Task EmptyWebTemplateCSharp()
+        {
+            await EmtpyTemplateCore(languageOverride: null);
+        }
+
+        [Fact]
+        public async Task EmptyWebTemplateFSharp()
+        {
+            await EmtpyTemplateCore("F#");
+        }
+
+        private async Task EmtpyTemplateCore(string languageOverride)
+        {
+            Project = await ProjectFactory.GetOrCreateProject("empty" + (languageOverride == "F#" ? "fsharp" : "csharp"), Output);
+
+            var createResult = await Project.RunDotNetNewAsync("web", language: languageOverride);
+            Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", Project, createResult));
+
+            var publishResult = await Project.RunDotNetPublishAsync();
+            Assert.True(0 == publishResult.ExitCode, ErrorMessages.GetFailedProcessMessage("publish", Project, publishResult));
+
+            // Run dotnet build after publish. The reason is that one uses Config = Debug and the other uses Config = Release
+            // The output from publish will go into bin/Release/netcoreappX.Y/publish and won't be affected by calling build
+            // later, while the opposite is not true.
+
+            var buildResult = await Project.RunDotNetBuildAsync();
+            Assert.True(0 == buildResult.ExitCode, ErrorMessages.GetFailedProcessMessage("build", Project, buildResult));
+
+            using (var aspNetProcess = Project.StartBuiltProjectAsync())
+            {
+                Assert.False(
+                   aspNetProcess.Process.HasExited,
+                   ErrorMessages.GetFailedProcessMessageOrEmpty("Run built project", Project, aspNetProcess.Process));
+
+                await aspNetProcess.AssertOk("/");
+            }
+
+            using (var aspNetProcess = Project.StartPublishedProjectAsync())
+            {
+                Assert.False(
+                    aspNetProcess.Process.HasExited,
+                    ErrorMessages.GetFailedProcessMessageOrEmpty("Run published project", Project, aspNetProcess.Process));
+
+                await aspNetProcess.AssertOk("/");
+            }
+        }
+    }
+}

+ 89 - 0
src/ProjectTemplates/test/GrpcTemplateTest.cs

@@ -0,0 +1,89 @@
+// 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.Runtime.InteropServices;
+using System.Threading.Tasks;
+using Templates.Test.Helpers;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace Templates.Test
+{
+    public class GrpcTemplateTest
+    {
+        public GrpcTemplateTest(ProjectFactoryFixture projectFactory, ITestOutputHelper output)
+        {
+            ProjectFactory = projectFactory;
+            Output = output;
+        }
+
+        public Project Project { get; set; }
+
+        public ProjectFactoryFixture ProjectFactory { get; }
+        public ITestOutputHelper Output { get; }
+
+        [Fact]
+        public async Task GrpcTemplate()
+        {
+            Project = await ProjectFactory.GetOrCreateProject("grpc", Output);
+
+            var createResult = await Project.RunDotNetNewAsync("grpc");
+            Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", Project, createResult));
+
+            var publishResult = await Project.RunDotNetPublishAsync();
+            Assert.True(0 == publishResult.ExitCode, ErrorMessages.GetFailedProcessMessage("publish", Project, publishResult));
+
+            var buildResult = await Project.RunDotNetBuildAsync();
+            Assert.True(0 == buildResult.ExitCode, ErrorMessages.GetFailedProcessMessage("build", Project, buildResult));
+
+            using (var serverProcess = Project.StartBuiltProjectAsync())
+            {
+                // These templates are HTTPS + HTTP/2 only which is not supported on Mac due to missing ALPN support.
+                // https://github.com/aspnet/AspNetCore/issues/11061
+                if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
+                {
+                    Assert.True(serverProcess.Process.HasExited, "built");
+                    Assert.Contains("System.NotSupportedException: HTTP/2 over TLS is not supported on macOS due to missing ALPN support.",
+                        ErrorMessages.GetFailedProcessMessageOrEmpty("Run built service", Project, serverProcess.Process));
+                }
+                else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && Environment.OSVersion.Version < new Version(6, 2))
+                {
+                    Assert.True(serverProcess.Process.HasExited, "built");
+                    Assert.Contains("System.NotSupportedException: HTTP/2 over TLS is not supported on Windows 7 due to missing ALPN support.",
+                        ErrorMessages.GetFailedProcessMessageOrEmpty("Run built service", Project, serverProcess.Process));
+                }
+                else
+                {
+                    Assert.False(
+                        serverProcess.Process.HasExited,
+                        ErrorMessages.GetFailedProcessMessageOrEmpty("Run built service", Project, serverProcess.Process));
+                }
+            }
+
+            using (var aspNetProcess = Project.StartPublishedProjectAsync())
+            {
+                // These templates are HTTPS + HTTP/2 only which is not supported on Mac due to missing ALPN support.
+                // https://github.com/aspnet/AspNetCore/issues/11061
+                if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
+                {
+                    Assert.True(aspNetProcess.Process.HasExited, "published");
+                    Assert.Contains("System.NotSupportedException: HTTP/2 over TLS is not supported on macOS due to missing ALPN support.",
+                        ErrorMessages.GetFailedProcessMessageOrEmpty("Run published service", Project, aspNetProcess.Process));
+                }
+                else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && Environment.OSVersion.Version < new Version(6, 2))
+                {
+                    Assert.True(aspNetProcess.Process.HasExited, "published");
+                    Assert.Contains("System.NotSupportedException: HTTP/2 over TLS is not supported on Windows 7 due to missing ALPN support.",
+                        ErrorMessages.GetFailedProcessMessageOrEmpty("Run published service", Project, aspNetProcess.Process));
+                }
+                else
+                {
+                    Assert.False(
+                        aspNetProcess.Process.HasExited,
+                        ErrorMessages.GetFailedProcessMessageOrEmpty("Run published service", Project, aspNetProcess.Process));
+                }
+            }
+        }
+    }
+}

+ 2 - 2
src/ProjectTemplates/test/Helpers/AspNetProcess.cs

@@ -36,7 +36,7 @@ namespace Templates.Test.Helpers
             string workingDirectory,
             string dllPath,
             IDictionary<string, string> environmentVariables,
-            bool published = true,
+            bool published,
             bool hasListeningUri = true)
         {
             _output = output;
@@ -55,7 +55,7 @@ namespace Templates.Test.Helpers
 
             output.WriteLine("Running ASP.NET application...");
 
-            var arguments = published ? $"exec {dllPath}" : "run";
+            var arguments = published ? $"exec {dllPath}" : "run --no-build";
             Process = ProcessEx.Run(output, workingDirectory, DotNetMuxer.MuxerPathOrDefault(), arguments, envVars: environmentVariables);
             if (hasListeningUri)
             {

+ 27 - 34
src/ProjectTemplates/test/Helpers/Project.cs

@@ -164,39 +164,6 @@ namespace Templates.Test.Helpers
             return new AspNetProcess(Output, TemplateServerDir, projectDll, environment, published: false);
         }
 
-        internal AspNetProcess StartBuiltClientAsync(AspNetProcess serverProcess)
-        {
-            var environment = new Dictionary<string, string>
-            {
-                ["ASPNETCORE_ENVIRONMENT"] = "Development"
-            };
-
-            var projectDll = Path.Combine(TemplateClientDebugDir, $"{ProjectName}.Client.dll {serverProcess.ListeningUri.Port}");
-            return new AspNetProcess(Output, TemplateOutputDir, projectDll, environment, hasListeningUri: false);
-        }
-
-        internal AspNetProcess StartPublishedServerAsync()
-        {
-            var environment = new Dictionary<string, string>
-            {
-                ["ASPNETCORE_URLS"] = _urls,
-            };
-
-            var projectDll = $"{ProjectName}.Server.dll";
-            return new AspNetProcess(Output, TemplateServerReleaseDir, projectDll, environment);
-        }
-
-        internal AspNetProcess StartPublishedClientAsync()
-        {
-            var environment = new Dictionary<string, string>
-            {
-                ["ASPNETCORE_URLS"] = _urls,
-            };
-
-            var projectDll = $"{ProjectName}.Client.dll";
-            return new AspNetProcess(Output, TemplateClientReleaseDir, projectDll, environment);
-        }
-
         internal AspNetProcess StartBuiltProjectAsync(bool hasListeningUri = true)
         {
             var environment = new Dictionary<string, string>
@@ -209,6 +176,32 @@ namespace Templates.Test.Helpers
                 ["ASPNETCORE_Logging__Console__IncludeScopes"] = "true",
             };
 
+            var launchSettingsJson = Path.Combine(TemplateOutputDir, "Properties", "launchSettings.json");
+            if (File.Exists(launchSettingsJson))
+            {
+                // When executing "dotnet run", the launch urls specified in the app's launchSettings.json have higher precedence
+                // than ambient environment variables. When present, we have to edit this file to allow the application to pick random ports.
+
+                var original = File.ReadAllText(launchSettingsJson);
+                var updated = original.Replace(
+                    "\"applicationUrl\": \"https://localhost:5001;http://localhost:5000\"",
+                    $"\"applicationUrl\": \"{_urls}\"");
+
+                if (updated == original)
+                {
+                    Output.WriteLine("applicationUrl is not specified in launchSettings.json");
+                }
+                else
+                {
+                    Output.WriteLine("Updating applicationUrl in launchSettings.json");
+                    File.WriteAllText(launchSettingsJson, updated);
+                }
+            }
+            else
+            {
+                Output.WriteLine("No launchSettings.json found to update.");
+            }
+
             var projectDll = Path.Combine(TemplateBuildDir, $"{ProjectName}.dll");
             return new AspNetProcess(Output, TemplateOutputDir, projectDll, environment, published: false, hasListeningUri: hasListeningUri);
         }
@@ -225,7 +218,7 @@ namespace Templates.Test.Helpers
             };
 
             var projectDll = $"{ProjectName}.dll";
-            return new AspNetProcess(Output, TemplatePublishDir, projectDll, environment, hasListeningUri: hasListeningUri);
+            return new AspNetProcess(Output, TemplatePublishDir, projectDll, environment, published: true, hasListeningUri: hasListeningUri);
         }
 
         internal async Task<ProcessEx> RestoreWithRetryAsync(ITestOutputHelper output, string workingDirectory)

+ 190 - 0
src/ProjectTemplates/test/IdentityUIPackageTest.cs

@@ -0,0 +1,190 @@
+// 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.Collections.Generic;
+using System.IO;
+using System.Net;
+using System.Threading.Tasks;
+using Templates.Test.Helpers;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace Templates.Test
+{
+    public class IdentityUIPackageTest
+    {
+        public IdentityUIPackageTest(ProjectFactoryFixture projectFactory, ITestOutputHelper output)
+        {
+            ProjectFactory = projectFactory;
+            Output = output;
+        }
+
+        public Project Project { get; set; }
+
+        public ProjectFactoryFixture ProjectFactory { get; set; }
+
+        public ITestOutputHelper Output { get; }
+
+        public static TheoryData<IDictionary<string, string>, string, string[]> MSBuildIdentityUIPackageOptions
+        {
+            get
+            {
+                var data = new TheoryData<IDictionary<string, string>, string, string[]>();
+
+                data.Add(new Dictionary<string, string>
+                {
+                    ["IdentityUIFrameworkVersion"] = "Bootstrap3"
+                },
+                "Bootstrap v3.4.1",
+                Bootstrap3ContentFiles);
+
+                data.Add(new Dictionary<string, string>(), "Bootstrap v4.3.1", Bootstrap4ContentFiles);
+
+                return data;
+            }
+        }
+
+        public static string[] Bootstrap3ContentFiles { get; } = new string[]
+        {
+            "Identity/css/site.css",
+            "Identity/js/site.js",
+            "Identity/lib/bootstrap/dist/css/bootstrap-theme.css",
+            "Identity/lib/bootstrap/dist/css/bootstrap-theme.css.map",
+            "Identity/lib/bootstrap/dist/css/bootstrap-theme.min.css",
+            "Identity/lib/bootstrap/dist/css/bootstrap-theme.min.css.map",
+            "Identity/lib/bootstrap/dist/css/bootstrap.css",
+            "Identity/lib/bootstrap/dist/css/bootstrap.css.map",
+            "Identity/lib/bootstrap/dist/css/bootstrap.min.css",
+            "Identity/lib/bootstrap/dist/css/bootstrap.min.css.map",
+            "Identity/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.eot",
+            "Identity/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.svg",
+            "Identity/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.ttf",
+            "Identity/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff",
+            "Identity/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff2",
+            "Identity/lib/bootstrap/dist/js/bootstrap.js",
+            "Identity/lib/bootstrap/dist/js/bootstrap.min.js",
+            "Identity/lib/bootstrap/dist/js/npm.js",
+            "Identity/lib/jquery/LICENSE.txt",
+            "Identity/lib/jquery/dist/jquery.js",
+            "Identity/lib/jquery/dist/jquery.min.js",
+            "Identity/lib/jquery/dist/jquery.min.map",
+            "Identity/lib/jquery-validation/LICENSE.md",
+            "Identity/lib/jquery-validation/dist/additional-methods.js",
+            "Identity/lib/jquery-validation/dist/additional-methods.min.js",
+            "Identity/lib/jquery-validation/dist/jquery.validate.js",
+            "Identity/lib/jquery-validation/dist/jquery.validate.min.js",
+            "Identity/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js",
+            "Identity/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js",
+            "Identity/lib/jquery-validation-unobtrusive/LICENSE.txt",
+        };
+
+        public static string[] Bootstrap4ContentFiles { get; } = new string[]
+        {
+            "Identity/favicon.ico",
+            "Identity/css/site.css",
+            "Identity/js/site.js",
+            "Identity/lib/bootstrap/dist/css/bootstrap-grid.css",
+            "Identity/lib/bootstrap/dist/css/bootstrap-grid.css.map",
+            "Identity/lib/bootstrap/dist/css/bootstrap-grid.min.css",
+            "Identity/lib/bootstrap/dist/css/bootstrap-grid.min.css.map",
+            "Identity/lib/bootstrap/dist/css/bootstrap-reboot.css",
+            "Identity/lib/bootstrap/dist/css/bootstrap-reboot.css.map",
+            "Identity/lib/bootstrap/dist/css/bootstrap-reboot.min.css",
+            "Identity/lib/bootstrap/dist/css/bootstrap-reboot.min.css.map",
+            "Identity/lib/bootstrap/dist/css/bootstrap.css",
+            "Identity/lib/bootstrap/dist/css/bootstrap.css.map",
+            "Identity/lib/bootstrap/dist/css/bootstrap.min.css",
+            "Identity/lib/bootstrap/dist/css/bootstrap.min.css.map",
+            "Identity/lib/bootstrap/dist/js/bootstrap.bundle.js",
+            "Identity/lib/bootstrap/dist/js/bootstrap.bundle.js.map",
+            "Identity/lib/bootstrap/dist/js/bootstrap.bundle.min.js",
+            "Identity/lib/bootstrap/dist/js/bootstrap.bundle.min.js.map",
+            "Identity/lib/bootstrap/dist/js/bootstrap.js",
+            "Identity/lib/bootstrap/dist/js/bootstrap.js.map",
+            "Identity/lib/bootstrap/dist/js/bootstrap.min.js",
+            "Identity/lib/bootstrap/dist/js/bootstrap.min.js.map",
+            "Identity/lib/jquery/LICENSE.txt",
+            "Identity/lib/jquery/dist/jquery.js",
+            "Identity/lib/jquery/dist/jquery.min.js",
+            "Identity/lib/jquery/dist/jquery.min.map",
+            "Identity/lib/jquery-validation/LICENSE.md",
+            "Identity/lib/jquery-validation/dist/additional-methods.js",
+            "Identity/lib/jquery-validation/dist/additional-methods.min.js",
+            "Identity/lib/jquery-validation/dist/jquery.validate.js",
+            "Identity/lib/jquery-validation/dist/jquery.validate.min.js",
+            "Identity/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js",
+            "Identity/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js",
+            "Identity/lib/jquery-validation-unobtrusive/LICENSE.txt",
+        };
+
+        [Theory]
+        [MemberData(nameof(MSBuildIdentityUIPackageOptions))]
+        public async Task IdentityUIPackage_WorksWithDifferentOptions(IDictionary<string, string> packageOptions, string versionValidator, string[] expectedFiles)
+        {
+            Project = await ProjectFactory.GetOrCreateProject("identityuipackage" + string.Concat(packageOptions.Values), Output);
+            var useLocalDB = false;
+
+            var createResult = await Project.RunDotNetNewAsync("razor", auth: "Individual", useLocalDB: useLocalDB, environmentVariables: packageOptions);
+            Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", Project, createResult));
+
+            var projectFileContents = ReadFile(Project.TemplateOutputDir, $"{Project.ProjectName}.csproj");
+            Assert.Contains(".db", projectFileContents);
+
+            var publishResult = await Project.RunDotNetPublishAsync(packageOptions: packageOptions);
+            Assert.True(0 == publishResult.ExitCode, ErrorMessages.GetFailedProcessMessage("publish", Project, publishResult));
+
+            // Run dotnet build after publish. The reason is that one uses Config = Debug and the other uses Config = Release
+            // The output from publish will go into bin/Release/netcoreappX.Y/publish and won't be affected by calling build
+            // later, while the opposite is not true.
+
+            var buildResult = await Project.RunDotNetBuildAsync(packageOptions: packageOptions);
+            Assert.True(0 == buildResult.ExitCode, ErrorMessages.GetFailedProcessMessage("build", Project, buildResult));
+
+            var migrationsResult = await Project.RunDotNetEfCreateMigrationAsync("razorpages");
+            Assert.True(0 == migrationsResult.ExitCode, ErrorMessages.GetFailedProcessMessage("run EF migrations", Project, migrationsResult));
+            Project.AssertEmptyMigration("razorpages");
+
+            using (var aspNetProcess = Project.StartBuiltProjectAsync())
+            {
+                Assert.False(
+                    aspNetProcess.Process.HasExited,
+                    ErrorMessages.GetFailedProcessMessageOrEmpty("Run built project", Project, aspNetProcess.Process));
+
+                var response = await aspNetProcess.SendRequest("/Identity/lib/bootstrap/dist/css/bootstrap.css");
+                Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+                Assert.Contains(versionValidator, await response.Content.ReadAsStringAsync());
+                await ValidatePublishedFiles(aspNetProcess, expectedFiles);
+            }
+
+            using (var aspNetProcess = Project.StartPublishedProjectAsync())
+            {
+                Assert.False(
+                    aspNetProcess.Process.HasExited,
+                    ErrorMessages.GetFailedProcessMessageOrEmpty("Run built project", Project, aspNetProcess.Process));
+
+                var response = await aspNetProcess.SendRequest("/Identity/lib/bootstrap/dist/css/bootstrap.css");
+                Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+                Assert.Contains(versionValidator, await response.Content.ReadAsStringAsync());
+                await ValidatePublishedFiles(aspNetProcess, expectedFiles);
+            }
+        }
+
+        private async Task ValidatePublishedFiles(AspNetProcess aspNetProcess, string[] expectedContentFiles)
+        {
+            foreach (var file in expectedContentFiles)
+            {
+                var response = await aspNetProcess.SendRequest(file);
+                Assert.True(response?.StatusCode == HttpStatusCode.OK, $"Couldn't find file '{file}'");
+            }
+        }
+
+        private string ReadFile(string basePath, string path)
+        {
+            var fullPath = Path.Combine(basePath, path);
+            var doesExist = File.Exists(fullPath);
+
+            Assert.True(doesExist, $"Expected file to exist, but it doesn't: {path}");
+            return File.ReadAllText(Path.Combine(basePath, path));
+        }
+    }
+}

+ 1 - 4
src/ProjectTemplates/test/Infrastructure/GenerateTestProps.targets

@@ -16,9 +16,6 @@
     <PropertyGroup>
       <MicrosoftAspNetCoreAppRefPackageVersion Condition="'$(IsTargetingPackBuilding)' != 'false'">%(_TargetingPackVersionInfo.PackageVersion)</MicrosoftAspNetCoreAppRefPackageVersion>
       <MicrosoftAspNetCoreAppRefPackageVersion Condition="'$(IsTargetingPackBuilding)' == 'false'">$(TargetingPackVersionPrefix)</MicrosoftAspNetCoreAppRefPackageVersion>
-
-    <!-- For blazor-wasm we want the runtime to match the baseline, as we want to target the latest released asp.net core app -->
-    <MicrosoftAspNetCoreAppRuntimePackageVersion>$(AspNetCoreBaselineVersion)</MicrosoftAspNetCoreAppRuntimePackageVersion>
     </PropertyGroup>
 
     <!-- Runtime and Ref packs may have separate versions. -->
@@ -37,7 +34,7 @@
         MicrosoftNETCorePlatformsPackageVersion=$(MicrosoftNETCorePlatformsPackageVersion);
         MicrosoftNETSdkRazorPackageVersion=$(MicrosoftNETSdkRazorPackageVersion);
         MicrosoftAspNetCoreAppRefPackageVersion=$(MicrosoftAspNetCoreAppRefPackageVersion);
-        MicrosoftAspNetCoreAppRuntimePackageVersion=$(MicrosoftAspNetCoreAppRuntimePackageVersion);
+        MicrosoftAspNetCoreAppRuntimePackageVersion=@(_RuntimePackageVersionInfo->'%(PackageVersion)');
         SupportedRuntimeIdentifiers=$(SupportedRuntimeIdentifiers);
         DefaultNetCoreTargetFramework=$(DefaultNetCoreTargetFramework);
       </PropsProperties>

+ 0 - 8
src/ProjectTemplates/test/Infrastructure/TemplateTests.props.in

@@ -40,14 +40,6 @@
       IsImplicitlyDefined="true" />
   </ItemGroup>
 
-  <!-- Temporarily remove the components analyzer due to build issues -->
-  <Target Name="RemoveComponentsAnalyzer" BeforeTargets="CoreCompile">
-    <ItemGroup>
-      <_AnalyzerToRemove Include="@(Analyzer)" Condition="'%(Analyzer.NuGetPackageId)' == 'Microsoft.AspNetCore.Components.Analyzers'" />
-      <Analyzer Remove="@(_AnalyzerToRemove)" />
-    </ItemGroup>
-  </Target>
-
   <ItemGroup Condition="'$(UsingMicrosoftNETSdkWeb)' == 'true' OR '$(RazorSdkCurrentVersionProps)' != ''">
     <!--
       Use the Razor SDK as a package reference. The version of the .NET Core SDK we build with often contains a version of the Razor SDK

+ 36 - 0
src/ProjectTemplates/test/ItemTemplateTests/BlazorServerTests.cs

@@ -0,0 +1,36 @@
+// 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.Threading.Tasks;
+using Templates.Test.Helpers;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace Templates.Items.Test
+{
+    public class BlazorServerTest
+    {
+        public BlazorServerTest(ProjectFactoryFixture projectFactory, ITestOutputHelper output)
+        {
+            ProjectFactory = projectFactory;
+            Output = output;
+        }
+
+        public Project Project { get; set; }
+
+        public ProjectFactoryFixture ProjectFactory { get; }
+        public ITestOutputHelper Output { get; }
+
+        [Fact]
+        public async Task BlazorServerItemTemplate()
+        {
+            Project = await ProjectFactory.GetOrCreateProject("razorcomponentitem", Output);
+
+            var createResult = await Project.RunDotNetNewAsync("razorcomponent --name Different");
+            Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create", Project, createResult));
+
+            Project.AssertFileExists("Different.razor", shouldExist: true);
+            Assert.Contains("<h3>Different</h3>", Project.ReadFile("Different.razor"));
+        }
+    }
+}

+ 1 - 1
src/ProjectTemplates/test/ProjectTemplates.Tests.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 Project="$(SharedSourceRoot)E2ETesting\E2ETesting.props" />

+ 62 - 0
src/ProjectTemplates/test/RazorClassLibraryTemplateTest.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 System.Threading.Tasks;
+using Templates.Test.Helpers;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace Templates.Test
+{
+    public class RazorClassLibraryTemplateTest
+    {
+        public RazorClassLibraryTemplateTest(ProjectFactoryFixture projectFactory, ITestOutputHelper output)
+        {
+            ProjectFactory = projectFactory;
+            Output = output;
+        }
+
+        public Project Project { get; set; }
+
+        public ProjectFactoryFixture ProjectFactory { get; }
+        public ITestOutputHelper Output { get; }
+
+        [Fact]
+        public async Task RazorClassLibraryTemplate_WithViews_Async()
+        {
+            Project = await ProjectFactory.GetOrCreateProject("razorclasslibwithviews", Output);
+
+            var createResult = await Project.RunDotNetNewAsync("razorclasslib", args: new[] { "--support-pages-and-views", "true" });
+            Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", Project, createResult));
+
+            var publishResult = await Project.RunDotNetPublishAsync();
+            Assert.True(0 == publishResult.ExitCode, ErrorMessages.GetFailedProcessMessage("publish", Project, publishResult));
+
+            // Run dotnet build after publish. The reason is that one uses Config = Debug and the other uses Config = Release
+            // The output from publish will go into bin/Release/netcoreappX.Y/publish and won't be affected by calling build
+            // later, while the opposite is not true.
+
+            var buildResult = await Project.RunDotNetBuildAsync();
+            Assert.True(0 == buildResult.ExitCode, ErrorMessages.GetFailedProcessMessage("build", Project, buildResult));
+        }
+
+        [Fact]
+        public async Task RazorClassLibraryTemplateAsync()
+        {
+            Project = await ProjectFactory.GetOrCreateProject("razorclasslib", Output);
+
+            var createResult = await Project.RunDotNetNewAsync("razorclasslib");
+            Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", Project, createResult));
+
+            var publishResult = await Project.RunDotNetPublishAsync();
+            Assert.True(0 == publishResult.ExitCode, ErrorMessages.GetFailedProcessMessage("publish", Project, publishResult));
+
+            // Run dotnet build after publish. The reason is that one uses Config = Debug and the other uses Config = Release
+            // The output from publish will go into bin/Release/netcoreappX.Y/publish and won't be affected by calling build
+            // later, while the opposite is not true.
+
+            var buildResult = await Project.RunDotNetBuildAsync();
+            Assert.True(0 == buildResult.ExitCode, ErrorMessages.GetFailedProcessMessage("build", Project, buildResult));
+        }
+    }
+}

+ 30 - 0
src/ProjectTemplates/test/SpaTemplateTest/AngularTemplateTest.cs

@@ -0,0 +1,30 @@
+// 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.Threading.Tasks;
+using Microsoft.AspNetCore.E2ETesting;
+using Microsoft.AspNetCore.Testing;
+using Templates.Test.Helpers;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace Templates.Test.SpaTemplateTest
+{
+    public class AngularTemplateTest : SpaTemplateTestBase
+    {
+        public AngularTemplateTest(ProjectFactoryFixture projectFactory, BrowserFixture browserFixture, ITestOutputHelper output)
+            : base(projectFactory, browserFixture, output) { }
+
+        [Fact(Skip = "Skipping SPA tests in 3.1")]
+        public Task AngularTemplate_Works()
+            => SpaTemplateImplAsync("angularnoauth", "angular", useLocalDb: false, usesAuth: false);
+
+        [Fact(Skip = "Skipping SPA tests in 3.1")]
+        public Task AngularTemplate_IndividualAuth_Works()
+            => SpaTemplateImplAsync("angularindividual", "angular", useLocalDb: false, usesAuth: true);
+
+        [Fact(Skip = "Skipping SPA tests in 3.1")]
+        public Task AngularTemplate_IndividualAuth_Works_LocalDb()
+            => SpaTemplateImplAsync("angularindividualuld", "angular", useLocalDb: true, usesAuth: true);
+    }
+}

+ 23 - 0
src/ProjectTemplates/test/SpaTemplateTest/ReactReduxTemplateTest.cs

@@ -0,0 +1,23 @@
+// 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.Threading.Tasks;
+using Microsoft.AspNetCore.E2ETesting;
+using Templates.Test.Helpers;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace Templates.Test.SpaTemplateTest
+{
+    public class ReactReduxTemplateTest : SpaTemplateTestBase
+    {
+        public ReactReduxTemplateTest(ProjectFactoryFixture projectFactory, BrowserFixture browserFixture, ITestOutputHelper output)
+            : base(projectFactory, browserFixture, output)
+        {
+        }
+
+        [Fact(Skip = "Skipping SPA tests in 3.1")]
+        public Task ReactReduxTemplate_Works_NetCore()
+            => SpaTemplateImplAsync("reactredux", "reactredux", useLocalDb: false, usesAuth: false);
+    }
+}

+ 32 - 0
src/ProjectTemplates/test/SpaTemplateTest/ReactTemplateTest.cs

@@ -0,0 +1,32 @@
+// 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.Threading.Tasks;
+using Microsoft.AspNetCore.E2ETesting;
+using Microsoft.AspNetCore.Testing;
+using Templates.Test.Helpers;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace Templates.Test.SpaTemplateTest
+{
+    public class ReactTemplateTest : SpaTemplateTestBase
+    {
+        public ReactTemplateTest(ProjectFactoryFixture projectFactory, BrowserFixture browserFixture, ITestOutputHelper output)
+            : base(projectFactory, browserFixture, output)
+        {
+        }
+
+        [Fact(Skip = "Skipping SPA tests in 3.1")]
+        public Task ReactTemplate_Works_NetCore()
+            => SpaTemplateImplAsync("reactnoauth", "react", useLocalDb: false, usesAuth: false);
+
+        [Fact(Skip = "Skipping SPA tests in 3.1")]
+        public Task ReactTemplate_IndividualAuth_NetCore()
+            => SpaTemplateImplAsync("reactindividual", "react", useLocalDb: false, usesAuth: true);
+
+        [Fact(Skip = "Skipping SPA tests in 3.1")]
+        public Task ReactTemplate_IndividualAuth_NetCore_LocalDb()
+            => SpaTemplateImplAsync("reactindividualuld", "react", useLocalDb: true, usesAuth: true);
+    }
+}

+ 70 - 0
src/ProjectTemplates/test/WebApiTemplateTest.cs

@@ -0,0 +1,70 @@
+// 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.Threading.Tasks;
+using Templates.Test.Helpers;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace Templates.Test
+{
+    public class WebApiTemplateTest
+    {
+        public WebApiTemplateTest(ProjectFactoryFixture factoryFixture, ITestOutputHelper output)
+        {
+            FactoryFixture = factoryFixture;
+            Output = output;
+        }
+
+        public ProjectFactoryFixture FactoryFixture { get; }
+
+        public ITestOutputHelper Output { get; }
+
+        public Project Project { get; set; }
+
+        [Fact]
+        public async Task WebApiTemplateFSharp() => await WebApiTemplateCore(languageOverride: "F#");
+
+        [Fact]
+        public async Task WebApiTemplateCSharp() => await WebApiTemplateCore(languageOverride: null);
+
+        private async Task WebApiTemplateCore(string languageOverride)
+        {
+            Project = await FactoryFixture.GetOrCreateProject("webapi" + (languageOverride == "F#" ? "fsharp" : "csharp"), Output);
+
+            var createResult = await Project.RunDotNetNewAsync("webapi", language: languageOverride);
+            Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", Project, createResult));
+
+            var publishResult = await Project.RunDotNetPublishAsync();
+            Assert.True(0 == publishResult.ExitCode, ErrorMessages.GetFailedProcessMessage("publish", Project, publishResult));
+
+            // Run dotnet build after publish. The reason is that one uses Config = Debug and the other uses Config = Release
+            // The output from publish will go into bin/Release/netcoreappX.Y/publish and won't be affected by calling build
+            // later, while the opposite is not true.
+
+            var buildResult = await Project.RunDotNetBuildAsync();
+            Assert.True(0 == buildResult.ExitCode, ErrorMessages.GetFailedProcessMessage("build", Project, buildResult));
+
+            using (var aspNetProcess = Project.StartBuiltProjectAsync())
+            {
+                Assert.False(
+                    aspNetProcess.Process.HasExited,
+                    ErrorMessages.GetFailedProcessMessageOrEmpty("Run built project", Project, aspNetProcess.Process));
+
+                await aspNetProcess.AssertOk("weatherforecast");
+                await aspNetProcess.AssertNotFound("/");
+            }
+
+            using (var aspNetProcess = Project.StartPublishedProjectAsync())
+            {
+                Assert.False(
+                    aspNetProcess.Process.HasExited,
+                    ErrorMessages.GetFailedProcessMessageOrEmpty("Run published project", Project, aspNetProcess.Process));
+
+
+                await aspNetProcess.AssertOk("weatherforecast");
+                await aspNetProcess.AssertNotFound("/");
+            }
+        }
+    }
+}

+ 56 - 0
src/ProjectTemplates/test/WorkerTemplateTest.cs

@@ -0,0 +1,56 @@
+// 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.Threading.Tasks;
+using Templates.Test.Helpers;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace Templates.Test
+{
+    public class WorkerTemplateTest
+    {
+        public WorkerTemplateTest(ProjectFactoryFixture projectFactory, ITestOutputHelper output)
+        {
+            ProjectFactory = projectFactory;
+            Output = output;
+        }
+
+        public Project Project { get; set; }
+        public ProjectFactoryFixture ProjectFactory { get; }
+        public ITestOutputHelper Output { get; }
+
+        [Fact]
+        public async Task WorkerTemplateAsync()
+        {
+            Project = await ProjectFactory.GetOrCreateProject("worker", Output);
+
+            var createResult = await Project.RunDotNetNewAsync("worker");
+            Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", Project, createResult));
+
+            var publishResult = await Project.RunDotNetPublishAsync();
+            Assert.True(0 == publishResult.ExitCode, ErrorMessages.GetFailedProcessMessage("publish", Project, publishResult));
+
+            // Run dotnet build after publish. The reason is that one uses Config = Debug and the other uses Config = Release
+            // The output from publish will go into bin/Release/netcoreappX.Y/publish and won't be affected by calling build
+            // later, while the opposite is not true.
+
+            var buildResult = await Project.RunDotNetBuildAsync();
+            Assert.True(0 == buildResult.ExitCode, ErrorMessages.GetFailedProcessMessage("build", Project, buildResult));
+
+            using (var aspNetProcess = Project.StartBuiltProjectAsync(hasListeningUri: false))
+            {
+                Assert.False(
+                    aspNetProcess.Process.HasExited,
+                    ErrorMessages.GetFailedProcessMessageOrEmpty("Run built project", Project, aspNetProcess.Process));
+            }
+
+            using (var aspNetProcess = Project.StartPublishedProjectAsync(hasListeningUri: false))
+            {
+                Assert.False(
+                    aspNetProcess.Process.HasExited,
+                    ErrorMessages.GetFailedProcessMessageOrEmpty("Run published project", Project, aspNetProcess.Process));
+            }
+        }
+    }
+}