ソースを参照

Fix http/https launch profile creation in project templates (#42143)

* Updated http/https profile definitions in project templates
* Update all project tests to cover http/https profile creation
* Normalize on inferred array types
* Moved ProjectTemplate.Tests to sub-dir
* Split out project templates test projects to reduce Helix job run time

Fixes #42138
Damian Edwards 3 年 前
コミット
b20ced2520
72 ファイル変更1089 行追加454 行削除
  1. 39 1
      AspNetCore.sln
  2. 0 10
      src/ProjectTemplates/BlazorTemplates.Tests/AssemblyInfo.AssemblyFixtures.cs
  3. 3 3
      src/ProjectTemplates/BlazorTemplates.Tests/BlazorServerTemplateTest.cs
  4. 4 2
      src/ProjectTemplates/ProjectTemplates.slnf
  5. 3 1
      src/ProjectTemplates/ProjectTemplatesNoDeps.slnf
  6. 2 1
      src/ProjectTemplates/Shared/ArgConstants.cs
  7. 0 0
      src/ProjectTemplates/Shared/AssemblyInfo.AssemblyFixtures.cs
  8. 58 0
      src/ProjectTemplates/Shared/Project.cs
  9. 1 1
      src/ProjectTemplates/Shared/TemplatePackageInstaller.cs
  10. 9 1
      src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/.template.config/template.json
  11. 2 2
      src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Program.Main.cs
  12. 1 1
      src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Program.cs
  13. 3 3
      src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Properties/launchSettings.json
  14. 9 1
      src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/.template.config/template.json
  15. 16 6
      src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Client/Properties/launchSettings.json
  16. 2 2
      src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Program.Main.cs
  17. 2 2
      src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Program.cs
  18. 16 6
      src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Properties/launchSettings.json
  19. 4 0
      src/ProjectTemplates/Web.ProjectTemplates/content/EmptyWeb-CSharp/.template.config/template.json
  20. 4 4
      src/ProjectTemplates/Web.ProjectTemplates/content/EmptyWeb-CSharp/Properties/launchSettings.json
  21. 4 0
      src/ProjectTemplates/Web.ProjectTemplates/content/EmptyWeb-FSharp/.template.config/template.json
  22. 4 4
      src/ProjectTemplates/Web.ProjectTemplates/content/EmptyWeb-FSharp/Properties/launchSettings.json
  23. 9 1
      src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/.template.config/template.json
  24. 2 2
      src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/Program.Main.cs
  25. 1 1
      src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/Program.cs
  26. 4 4
      src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/Properties/launchSettings.json
  27. 9 1
      src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/.template.config/template.json
  28. 2 2
      src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/Program.Main.cs
  29. 1 1
      src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/Program.cs
  30. 3 3
      src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/Properties/launchSettings.json
  31. 4 0
      src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-FSharp/.template.config/template.json
  32. 1 1
      src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-FSharp/Program.fs
  33. 4 4
      src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-FSharp/Properties/launchSettings.json
  34. 9 1
      src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/.template.config/template.json
  35. 1 1
      src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Program.Main.cs
  36. 1 1
      src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Program.MinimalAPIs.OrgOrIndividualB2CAuth.cs
  37. 1 1
      src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Program.MinimalAPIs.WindowsOrNoAuth.cs
  38. 1 1
      src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Program.cs
  39. 6 6
      src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Properties/launchSettings.json
  40. 4 0
      src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-FSharp/.template.config/template.json
  41. 1 1
      src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-FSharp/Program.fs
  42. 4 12
      src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-FSharp/Properties/launchSettings.json
  43. 1 16
      src/ProjectTemplates/scripts/.gitignore
  44. 1 1
      src/ProjectTemplates/scripts/Test-Template.ps1
  45. 0 59
      src/ProjectTemplates/test/BlazorServerTemplateTest.cs
  46. 0 0
      src/ProjectTemplates/test/Templates.Blazor.Tests/.gitattributes
  47. 93 0
      src/ProjectTemplates/test/Templates.Blazor.Tests/BlazorServerTemplateTest.cs
  48. 16 1
      src/ProjectTemplates/test/Templates.Blazor.Tests/BlazorTemplateTest.cs
  49. 125 52
      src/ProjectTemplates/test/Templates.Blazor.Tests/BlazorWasmTemplateTest.cs
  50. 68 0
      src/ProjectTemplates/test/Templates.Blazor.Tests/Templates.Blazor.Tests.csproj
  51. 1 0
      src/ProjectTemplates/test/Templates.Mvc.Tests/.gitattributes
  52. 68 14
      src/ProjectTemplates/test/Templates.Mvc.Tests/MvcTemplateTest.cs
  53. 65 24
      src/ProjectTemplates/test/Templates.Mvc.Tests/RazorPagesTemplateTest.cs
  54. 68 0
      src/ProjectTemplates/test/Templates.Mvc.Tests/Templates.Mvc.Tests.csproj
  55. 289 0
      src/ProjectTemplates/test/Templates.Mvc.Tests/WebApiTemplateTest.cs
  56. 1 0
      src/ProjectTemplates/test/Templates.Tests/.gitattributes
  57. 0 0
      src/ProjectTemplates/test/Templates.Tests/BaselineTest.cs
  58. 0 0
      src/ProjectTemplates/test/Templates.Tests/ByteOrderMarkTest.cs
  59. 26 0
      src/ProjectTemplates/test/Templates.Tests/EmptyWebTemplateTest.cs
  60. 3 0
      src/ProjectTemplates/test/Templates.Tests/GrpcTemplateTest.cs
  61. 1 1
      src/ProjectTemplates/test/Templates.Tests/IdentityUIPackageTest.cs
  62. 0 0
      src/ProjectTemplates/test/Templates.Tests/ItemTemplateTests/BlazorServerTests.cs
  63. 0 0
      src/ProjectTemplates/test/Templates.Tests/RazorClassLibraryTemplateTest.cs
  64. 0 0
      src/ProjectTemplates/test/Templates.Tests/SpaTemplatesTest.cs
  65. 8 8
      src/ProjectTemplates/test/Templates.Tests/Templates.Tests.csproj
  66. 1 1
      src/ProjectTemplates/test/Templates.Tests/WorkerTemplateTest.cs
  67. 0 0
      src/ProjectTemplates/test/Templates.Tests/e2eTestSettings.ci.json
  68. 0 0
      src/ProjectTemplates/test/Templates.Tests/e2eTestSettings.json
  69. 0 0
      src/ProjectTemplates/test/Templates.Tests/package.json
  70. 0 0
      src/ProjectTemplates/test/Templates.Tests/template-baselines.json
  71. 0 0
      src/ProjectTemplates/test/Templates.Tests/yarn.lock
  72. 0 182
      src/ProjectTemplates/test/WebApiTemplateTest.cs

+ 39 - 1
AspNetCore.sln

@@ -996,7 +996,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.Web.Client
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.Web.ItemTemplates", "src\ProjectTemplates\Web.ItemTemplates\Microsoft.DotNet.Web.ItemTemplates.csproj", "{D0E73F40-0DA9-42A9-8A88-EED8D5EFBEFB}"
 EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProjectTemplates.Tests", "src\ProjectTemplates\test\ProjectTemplates.Tests.csproj", "{6DE03095-7EAC-41DF-8AE4-3018ED29BC61}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Templates.Tests", "src\ProjectTemplates\test\Templates.Tests\Templates.Tests.csproj", "{6DE03095-7EAC-41DF-8AE4-3018ED29BC61}"
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorTemplates.Tests", "src\ProjectTemplates\BlazorTemplates.Tests\BlazorTemplates.Tests.csproj", "{18CE63FC-3BFE-47DF-A8D7-9D716FEB04C9}"
 EndProject
@@ -1736,6 +1736,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TestInfrastructure", "TestI
 		src\ProjectTemplates\TestInfrastructure\runtimeconfig.norollforward.json = src\ProjectTemplates\TestInfrastructure\runtimeconfig.norollforward.json
 	EndProjectSection
 EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Templates.Mvc.Tests", "src\ProjectTemplates\test\Templates.Mvc.Tests\Templates.Mvc.Tests.csproj", "{AA7445F5-BD28-400C-8507-E2E0D3CF7D7E}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Templates.Blazor.Tests", "src\ProjectTemplates\test\Templates.Blazor.Tests\Templates.Blazor.Tests.csproj", "{281BF9DB-7B8A-446B-9611-10A60903F125}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -10385,6 +10389,38 @@ Global
 		{89896261-C5DD-4901-BCA7-7A5F718BC008}.Release|x64.Build.0 = Release|Any CPU
 		{89896261-C5DD-4901-BCA7-7A5F718BC008}.Release|x86.ActiveCfg = Release|Any CPU
 		{89896261-C5DD-4901-BCA7-7A5F718BC008}.Release|x86.Build.0 = Release|Any CPU
+		{AA7445F5-BD28-400C-8507-E2E0D3CF7D7E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{AA7445F5-BD28-400C-8507-E2E0D3CF7D7E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{AA7445F5-BD28-400C-8507-E2E0D3CF7D7E}.Debug|arm64.ActiveCfg = Debug|Any CPU
+		{AA7445F5-BD28-400C-8507-E2E0D3CF7D7E}.Debug|arm64.Build.0 = Debug|Any CPU
+		{AA7445F5-BD28-400C-8507-E2E0D3CF7D7E}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{AA7445F5-BD28-400C-8507-E2E0D3CF7D7E}.Debug|x64.Build.0 = Debug|Any CPU
+		{AA7445F5-BD28-400C-8507-E2E0D3CF7D7E}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{AA7445F5-BD28-400C-8507-E2E0D3CF7D7E}.Debug|x86.Build.0 = Debug|Any CPU
+		{AA7445F5-BD28-400C-8507-E2E0D3CF7D7E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{AA7445F5-BD28-400C-8507-E2E0D3CF7D7E}.Release|Any CPU.Build.0 = Release|Any CPU
+		{AA7445F5-BD28-400C-8507-E2E0D3CF7D7E}.Release|arm64.ActiveCfg = Release|Any CPU
+		{AA7445F5-BD28-400C-8507-E2E0D3CF7D7E}.Release|arm64.Build.0 = Release|Any CPU
+		{AA7445F5-BD28-400C-8507-E2E0D3CF7D7E}.Release|x64.ActiveCfg = Release|Any CPU
+		{AA7445F5-BD28-400C-8507-E2E0D3CF7D7E}.Release|x64.Build.0 = Release|Any CPU
+		{AA7445F5-BD28-400C-8507-E2E0D3CF7D7E}.Release|x86.ActiveCfg = Release|Any CPU
+		{AA7445F5-BD28-400C-8507-E2E0D3CF7D7E}.Release|x86.Build.0 = Release|Any CPU
+		{281BF9DB-7B8A-446B-9611-10A60903F125}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{281BF9DB-7B8A-446B-9611-10A60903F125}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{281BF9DB-7B8A-446B-9611-10A60903F125}.Debug|arm64.ActiveCfg = Debug|Any CPU
+		{281BF9DB-7B8A-446B-9611-10A60903F125}.Debug|arm64.Build.0 = Debug|Any CPU
+		{281BF9DB-7B8A-446B-9611-10A60903F125}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{281BF9DB-7B8A-446B-9611-10A60903F125}.Debug|x64.Build.0 = Debug|Any CPU
+		{281BF9DB-7B8A-446B-9611-10A60903F125}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{281BF9DB-7B8A-446B-9611-10A60903F125}.Debug|x86.Build.0 = Debug|Any CPU
+		{281BF9DB-7B8A-446B-9611-10A60903F125}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{281BF9DB-7B8A-446B-9611-10A60903F125}.Release|Any CPU.Build.0 = Release|Any CPU
+		{281BF9DB-7B8A-446B-9611-10A60903F125}.Release|arm64.ActiveCfg = Release|Any CPU
+		{281BF9DB-7B8A-446B-9611-10A60903F125}.Release|arm64.Build.0 = Release|Any CPU
+		{281BF9DB-7B8A-446B-9611-10A60903F125}.Release|x64.ActiveCfg = Release|Any CPU
+		{281BF9DB-7B8A-446B-9611-10A60903F125}.Release|x64.Build.0 = Release|Any CPU
+		{281BF9DB-7B8A-446B-9611-10A60903F125}.Release|x86.ActiveCfg = Release|Any CPU
+		{281BF9DB-7B8A-446B-9611-10A60903F125}.Release|x86.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
@@ -11242,6 +11278,8 @@ Global
 		{7F079E92-32D5-4257-B95B-CFFB0D49C160} = {7FD32066-C831-4E29-978C-9A2215E85C67}
 		{89896261-C5DD-4901-BCA7-7A5F718BC008} = {AB4B9E75-719C-4589-B852-20FBFD727730}
 		{F0FBA346-D8BC-4FAE-A4B2-85B33FA23055} = {08D53E58-4AAE-40C4-8497-63EC8664F304}
+		{AA7445F5-BD28-400C-8507-E2E0D3CF7D7E} = {08D53E58-4AAE-40C4-8497-63EC8664F304}
+		{281BF9DB-7B8A-446B-9611-10A60903F125} = {08D53E58-4AAE-40C4-8497-63EC8664F304}
 	EndGlobalSection
 	GlobalSection(ExtensibilityGlobals) = postSolution
 		SolutionGuid = {3E8720B3-DBDD-498C-B383-2CC32A054E8F}

+ 0 - 10
src/ProjectTemplates/BlazorTemplates.Tests/AssemblyInfo.AssemblyFixtures.cs

@@ -1,10 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using Microsoft.AspNetCore.Testing;
-using ProjectTemplates.Tests.Infrastructure;
-using Templates.Test;
-using Templates.Test.Helpers;
-
-[assembly: AssemblyFixture(typeof(ProjectFactoryFixture))]
-

+ 3 - 3
src/ProjectTemplates/BlazorTemplates.Tests/BlazorServerTemplateTest.cs

@@ -182,10 +182,10 @@ public class BlazorServerTemplateTest : BlazorTemplateTest
 
     [Theory(Skip = "https://github.com/dotnet/aspnetcore/issues/30882")]
     [InlineData("IndividualB2C", null)]
-    [InlineData("IndividualB2C", new string[] { "--called-api-url \"https://graph.microsoft.com\"", "--called-api-scopes user.readwrite" })]
+    [InlineData("IndividualB2C", new [] { "--called-api-url \"https://graph.microsoft.com\"", "--called-api-scopes user.readwrite" })]
     [InlineData("SingleOrg", null)]
-    [InlineData("SingleOrg", new string[] { "--called-api-url \"https://graph.microsoft.com\"", "--called-api-scopes user.readwrite" })]
-    [InlineData("SingleOrg", new string[] { "--calls-graph" })]
+    [InlineData("SingleOrg", new [] { "--called-api-url \"https://graph.microsoft.com\"", "--called-api-scopes user.readwrite" })]
+    [InlineData("SingleOrg", new [] { "--calls-graph" })]
     public Task BlazorServerTemplate_IdentityWeb_BuildAndPublish(string auth, string[] args)
         => CreateBuildPublishAsync(auth, args);
 }

+ 4 - 2
src/ProjectTemplates/ProjectTemplates.slnf

@@ -66,7 +66,9 @@
       "src\\ProjectTemplates\\Web.Client.ItemTemplates\\Microsoft.DotNet.Web.Client.ItemTemplates.csproj",
       "src\\ProjectTemplates\\Web.ItemTemplates\\Microsoft.DotNet.Web.ItemTemplates.csproj",
       "src\\ProjectTemplates\\Web.ProjectTemplates\\Microsoft.DotNet.Web.ProjectTemplates.csproj",
-      "src\\ProjectTemplates\\test\\ProjectTemplates.Tests.csproj",
+      "src\\ProjectTemplates\\test\\Templates.Tests\\Templates.Tests.csproj",
+      "src\\ProjectTemplates\\test\\Templates.Mvc.Tests\\Templates.Mvc.Tests.csproj",
+      "src\\ProjectTemplates\\test\\Templates.Blazor.Tests\\Templates.Blazor.Tests.csproj",
       "src\\Razor\\Razor.Runtime\\src\\Microsoft.AspNetCore.Razor.Runtime.csproj",
       "src\\Razor\\Razor\\src\\Microsoft.AspNetCore.Razor.csproj",
       "src\\Security\\Authentication\\Cookies\\src\\Microsoft.AspNetCore.Authentication.Cookies.csproj",
@@ -92,4 +94,4 @@
       "src\\submodules\\spa-templates\\src\\Microsoft.DotNet.Web.Spa.ProjectTemplates.csproj"
     ]
   }
-}
+}

+ 3 - 1
src/ProjectTemplates/ProjectTemplatesNoDeps.slnf

@@ -7,7 +7,9 @@
       "src\\ProjectTemplates\\Web.ItemTemplates\\Microsoft.DotNet.Web.ItemTemplates.csproj",
       "src\\ProjectTemplates\\Web.ProjectTemplates\\Microsoft.DotNet.Web.ProjectTemplates.csproj",
       "src\\submodules\\spa-templates\\src\\Microsoft.DotNet.Web.Spa.ProjectTemplates.csproj",
-      "src\\ProjectTemplates\\test\\ProjectTemplates.Tests.csproj",
+      "src\\ProjectTemplates\\test\\Templates.Tests\\Templates.Tests.csproj",
+      "src\\ProjectTemplates\\test\\Templates.Mvc.Tests\\Templates.Mvc.Tests.csproj",
+      "src\\ProjectTemplates\\test\\Templates.Blazor.Tests\\Templates.Blazor.Tests.csproj",
       "src\\Shared\\BrowserTesting\\src\\Microsoft.AspNetCore.BrowserTesting.csproj"
     ]
   }

+ 2 - 1
src/ProjectTemplates/test/ArgConstants.cs → src/ProjectTemplates/Shared/ArgConstants.cs

@@ -1,7 +1,7 @@
 // Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 
-namespace Templates.Test;
+namespace Templates.Test.Helpers;
 
 internal static class ArgConstants
 {
@@ -24,4 +24,5 @@ internal static class ArgConstants
     public const string TenantId = "--tenant-id";
     public const string AadB2cInstance = "--aad-b2c-instance";
     public const string UseLocalDb = "-uld";
+    public const string NoHttps = "--no-https";
 }

+ 0 - 0
src/ProjectTemplates/test/AssemblyInfo.AssemblyFixtures.cs → src/ProjectTemplates/Shared/AssemblyInfo.AssemblyFixtures.cs


+ 58 - 0
src/ProjectTemplates/Shared/Project.cs

@@ -7,6 +7,7 @@ using System.Diagnostics;
 using System.IO;
 using System.Linq;
 using System.Reflection;
+using System.Text.Json;
 using System.Threading;
 using System.Threading.Tasks;
 using Microsoft.AspNetCore.Internal;
@@ -288,6 +289,63 @@ public class Project : IDisposable
         }
     }
 
+    public async Task<Project> VerifyLaunchSettings(string[] expectedLaunchProfileNames)
+    {
+        var launchSettingsFiles = Directory.EnumerateFiles(TemplateOutputDir, "launchSettings.json", SearchOption.AllDirectories);
+
+        foreach (var filePath in launchSettingsFiles)
+        {
+            using var launchSettingsFile = File.OpenRead(filePath);
+            using var launchSettings = await JsonDocument.ParseAsync(launchSettingsFile);
+
+            var profiles = launchSettings.RootElement.GetProperty("profiles");
+            var profilesEnumerator = profiles.EnumerateObject().GetEnumerator();
+
+            foreach (var expectedProfileName in expectedLaunchProfileNames)
+            {
+                Assert.True(profilesEnumerator.MoveNext());
+
+                var actualProfile = profilesEnumerator.Current;
+
+                // Launch profile names are case sensitive
+                Assert.Equal(expectedProfileName, actualProfile.Name, StringComparer.Ordinal);
+
+                if (actualProfile.Value.GetProperty("commandName").GetString() == "Project")
+                {
+                    var applicationUrl = actualProfile.Value.GetProperty("applicationUrl");
+                    if (string.Equals(expectedProfileName, "http", StringComparison.Ordinal))
+                    {
+                        Assert.DoesNotContain("https://", applicationUrl.GetString());
+                    }
+
+                    if (string.Equals(expectedProfileName, "https", StringComparison.Ordinal))
+                    {
+                        Assert.StartsWith("https://", applicationUrl.GetString());
+                    }
+                }
+            }
+
+            // Check there are no more launch profiles defined
+            Assert.False(profilesEnumerator.MoveNext());
+
+            if (launchSettings.RootElement.TryGetProperty("iisSettings", out var iisSettings)
+                && iisSettings.TryGetProperty("iisExpress", out var iisExpressSettings))
+            {
+                var iisSslPort = iisExpressSettings.GetProperty("sslPort").GetInt32();
+                if (expectedLaunchProfileNames.Contains("https"))
+                {
+                    Assert.True(iisSslPort >= 44300 && iisSslPort <= 44399, $"IIS Express port was expected to be >= 44300 and <= 44399 but was {iisSslPort} in file {filePath}");
+                }
+                else
+                {
+                    Assert.Equal(0, iisSslPort);
+                }
+            }
+        }
+
+        return this;
+    }
+
     public string ReadFile(string path)
     {
         AssertFileExists(path, shouldExist: true);

+ 1 - 1
src/ProjectTemplates/Shared/TemplatePackageInstaller.cs

@@ -110,7 +110,7 @@ internal static class TemplatePackageInstaller
         foreach (var packagePath in builtPackages)
         {
             output.WriteLine($"Installing templates package {packagePath}...");
-            var result = await RunDotNetNew(output, $"--install \"{packagePath}\"");
+            var result = await RunDotNetNew(output, $"install \"{packagePath}\"");
             Assert.True(result.ExitCode == 0, result.GetFormattedOutput());
         }
 

+ 9 - 1
src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/.template.config/template.json

@@ -434,7 +434,15 @@
     },
     "RequiresHttps": {
       "type": "computed",
-      "value": "(OrganizationalAuth || IndividualB2C || !NoHttps)"
+      "value": "(OrganizationalAuth || IndividualB2CAuth)"
+    },
+    "HasHttpProfile": {
+      "type": "computed",
+      "value": "(!RequiresHttps)"
+    },
+    "HasHttpsProfile": {
+      "type": "computed",
+      "value": "(RequiresHttps || !NoHttps)"
     },
     "NoHttps": {
       "type": "parameter",

+ 2 - 2
src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Program.Main.cs

@@ -138,7 +138,7 @@ public class Program
         #endif
         {
             app.UseExceptionHandler("/Error");
-        #if (RequiresHttps)
+        #if (HasHttpsProfile)
             // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
             app.UseHsts();
         }
@@ -166,4 +166,4 @@ public class Program
 
         app.Run();
     }
-}
+}

+ 1 - 1
src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Program.cs

@@ -132,7 +132,7 @@ if (!app.Environment.IsDevelopment())
 #endif
 {
     app.UseExceptionHandler("/Error");
-#if (RequiresHttps)
+#if (HasHttpsProfile)
     // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
     app.UseHsts();
 }

+ 3 - 3
src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Properties/launchSettings.json

@@ -9,7 +9,7 @@
     //#endif
     "iisExpress": {
       "applicationUrl": "http://localhost:8080",
-      //#if(RequiresHttps)
+      //#if (HasHttpsProfile)
       "sslPort": 44300
       //#else
       "sslPort": 0
@@ -17,7 +17,7 @@
     }
   },
   "profiles": {
-    //#if(!RequiresHttps)
+    //#if (HasHttpProfile)
     "http": {
       "commandName": "Project",
       "dotnetRunMessages": true,
@@ -28,7 +28,7 @@
       }
     },
     //#endif
-    //#if(!NoHttps)
+    //#if (HasHttpsProfile)
     "https": {
       "commandName": "Project",
       "dotnetRunMessages": true,

+ 9 - 1
src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/.template.config/template.json

@@ -559,7 +559,15 @@
     },
     "RequiresHttps": {
       "type": "computed",
-      "value": "(OrganizationalAuth || IndividualAuth || !NoHttps)"
+      "value": "(OrganizationalAuth || IndividualAuth)"
+    },
+    "HasHttpProfile": {
+      "type": "computed",
+      "value": "(!RequiresHttps)"
+    },
+    "HasHttpsProfile": {
+      "type": "computed",
+      "value": "(RequiresHttps || !NoHttps)"
     },
     "NoHttps": {
       "type": "parameter",

+ 16 - 6
src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Client/Properties/launchSettings.json

@@ -9,7 +9,7 @@
     //#endif
     "iisExpress": {
       "applicationUrl": "http://localhost:8080",
-      //#if(RequiresHttps)
+      //#if (HasHttpsProfile)
       "sslPort": 44300
       //#else
       "sslPort": 0
@@ -17,20 +17,30 @@
     }
   },
   "profiles": {
-    "ComponentsWebAssembly-CSharp": {
+    //#if (HasHttpProfile)
+    "http": {
       "commandName": "Project",
       "dotnetRunMessages": true,
       "launchBrowser": true,
       "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
-      //#if(RequiresHttps)
-      "applicationUrl": "https://localhost:5001;http://localhost:5000",
-      //#else
       "applicationUrl": "http://localhost:5000",
-      //#endif
       "environmentVariables": {
         "ASPNETCORE_ENVIRONMENT": "Development"
       }
     },
+    //#endif
+    //#if (HasHttpsProfile)
+    "https": {
+      "commandName": "Project",
+      "dotnetRunMessages": true,
+      "launchBrowser": true,
+      "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
+      "applicationUrl": "https://localhost:5001;http://localhost:5000",
+      "environmentVariables": {
+        "ASPNETCORE_ENVIRONMENT": "Development"
+      }
+    },
+    //#endif
     "IIS Express": {
       "commandName": "IISExpress",
       "launchBrowser": true,

+ 2 - 2
src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Program.Main.cs

@@ -90,13 +90,13 @@ public class Program
         else
         {
             app.UseExceptionHandler("/Error");
-        #if (RequiresHttps)
+        #if (HasHttpsProfile)
             // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
             app.UseHsts();
         #endif
         }
 
-        #if (RequiresHttps)
+        #if (HasHttpsProfile)
         app.UseHttpsRedirection();
 
         #endif

+ 2 - 2
src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Program.cs

@@ -84,13 +84,13 @@ if (app.Environment.IsDevelopment())
 else
 {
     app.UseExceptionHandler("/Error");
-#if (RequiresHttps)
+#if (HasHttpsProfile)
     // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
     app.UseHsts();
 #endif
 }
 
-#if (RequiresHttps)
+#if (HasHttpsProfile)
 app.UseHttpsRedirection();
 
 #endif

+ 16 - 6
src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Properties/launchSettings.json

@@ -4,7 +4,7 @@
       "anonymousAuthentication": true,
       "iisExpress": {
         "applicationUrl": "http://localhost:8080",
-        //#if(RequiresHttps)
+        //#if (HasHttpsProfile)
         "sslPort": 44300
         //#else
         "sslPort": 0
@@ -12,20 +12,30 @@
       }
     },
     "profiles": {
-      "ComponentsWebAssembly-CSharp.Server": {
+      //#if (HasHttpProfile)
+      "http": {
         "commandName": "Project",
         "dotnetRunMessages": true,
         "launchBrowser": true,
         "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
-        //#if(RequiresHttps)
-        "applicationUrl": "https://localhost:5001;http://localhost:5000",
-        //#else
         "applicationUrl": "http://localhost:5000",
-        //#endif
         "environmentVariables": {
           "ASPNETCORE_ENVIRONMENT": "Development"
         }
       },
+      //#endif
+      //#if (HasHttpsProfile)
+      "https": {
+        "commandName": "Project",
+        "dotnetRunMessages": true,
+        "launchBrowser": true,
+        "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
+        "applicationUrl": "https://localhost:5001;http://localhost:5000",
+        "environmentVariables": {
+          "ASPNETCORE_ENVIRONMENT": "Development"
+        }
+      },
+      //#endif
       "IIS Express": {
         "commandName": "IISExpress",
         "launchBrowser": true,

+ 4 - 0
src/ProjectTemplates/Web.ProjectTemplates/content/EmptyWeb-CSharp/.template.config/template.json

@@ -166,6 +166,10 @@
       "description": "If specified, skips the automatic restore of the project on create.",
       "defaultValue": "false"
     },
+    "HasHttpsProfile": {
+      "type": "computed",
+      "value": "(!NoHttps)"
+    },
     "NoHttps": {
       "type": "parameter",
       "datatype": "bool",

+ 4 - 4
src/ProjectTemplates/Web.ProjectTemplates/content/EmptyWeb-CSharp/Properties/launchSettings.json

@@ -9,10 +9,10 @@
     //#endif
     "iisExpress": {
       "applicationUrl": "http://localhost:8080",
-      //#if(NoHttps)
-      "sslPort": 0
-      //#else
+      //#if (HasHttpsProfile)
       "sslPort": 44300
+      //#else
+      "sslPort": 0
       //#endif
     }
   },
@@ -26,7 +26,7 @@
         "ASPNETCORE_ENVIRONMENT": "Development"
       }
     },
-    //#if(!NoHttps)
+    //#if (HasHttpsProfile)
     "https": {
       "commandName": "Project",
       "dotnetRunMessages": true,

+ 4 - 0
src/ProjectTemplates/Web.ProjectTemplates/content/EmptyWeb-FSharp/.template.config/template.json

@@ -147,6 +147,10 @@
       "description": "If specified, skips the automatic restore of the project on create.",
       "defaultValue": "false"
     },
+    "HasHttpsProfile": {
+      "type": "computed",
+      "value": "(!NoHttps)"
+    },
     "NoHttps": {
       "type": "parameter",
       "datatype": "bool",

+ 4 - 4
src/ProjectTemplates/Web.ProjectTemplates/content/EmptyWeb-FSharp/Properties/launchSettings.json

@@ -4,10 +4,10 @@
     "anonymousAuthentication": true,
     "iisExpress": {
       "applicationUrl": "http://localhost:8080",
-      //#if(NoHttps)
-      "sslPort": 0
-      //#else
+      //#if (HasHttpsProfile)
       "sslPort": 44300
+      //#else
+      "sslPort": 0
       //#endif
     }
   },
@@ -21,7 +21,7 @@
         "ASPNETCORE_ENVIRONMENT": "Development"
       }
     },
-    //#if(!NoHttps)
+    //#if (HasHttpsProfile)
     "https": {
       "commandName": "Project",
       "dotnetRunMessages": true,

+ 9 - 1
src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/.template.config/template.json

@@ -356,7 +356,15 @@
     },
     "RequiresHttps": {
       "type": "computed",
-      "value": "(OrganizationalAuth || IndividualB2C || !NoHttps)"
+      "value": "(OrganizationalAuth || IndividualB2CAuth)"
+    },
+    "HasHttpProfile": {
+      "type": "computed",
+      "value": "(!RequiresHttps)"
+    },
+    "HasHttpsProfile": {
+      "type": "computed",
+      "value": "(RequiresHttps || !NoHttps)"
     },
     "NoHttps": {
       "type": "parameter",

+ 2 - 2
src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/Program.Main.cs

@@ -127,7 +127,7 @@ public class Program
         #endif
         {
             app.UseExceptionHandler("/Error");
-        #if (RequiresHttps)
+        #if (HasHttpsProfile)
             // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
             app.UseHsts();
         }
@@ -152,4 +152,4 @@ public class Program
 
         app.Run();
     }
-}
+}

+ 1 - 1
src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/Program.cs

@@ -121,7 +121,7 @@ if (!app.Environment.IsDevelopment())
 #endif
 {
     app.UseExceptionHandler("/Error");
-#if (RequiresHttps)
+#if (HasHttpsProfile)
     // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
     app.UseHsts();
 }

+ 4 - 4
src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/Properties/launchSettings.json

@@ -1,4 +1,4 @@
-{
+{
   "iisSettings": {
     //#if (WindowsAuth)
     "windowsAuthentication": true,
@@ -9,7 +9,7 @@
     //#endif
     "iisExpress": {
       "applicationUrl": "http://localhost:8080",
-      //#if(RequiresHttps)
+      //#if (HasHttpsProfile)
       "sslPort": 44300
       //#else
       "sslPort": 0
@@ -17,7 +17,7 @@
     }
   },
   "profiles": {
-    //#if(!RequiresHttps)
+    //#if (HasHttpProfile)
     "http": {
       "commandName": "Project",
       "dotnetRunMessages": true,
@@ -28,7 +28,7 @@
       }
     },
     //#endif
-    //#if(!NoHttps)
+    //#if (HasHttpsProfile)
     "https": {
       "commandName": "Project",
       "dotnetRunMessages": true,

+ 9 - 1
src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/.template.config/template.json

@@ -346,7 +346,15 @@
     },
     "RequiresHttps": {
       "type": "computed",
-      "value": "(OrganizationalAuth || IndividualB2C || !NoHttps)"
+      "value": "(OrganizationalAuth || IndividualB2CAuth)"
+    },
+    "HasHttpProfile": {
+      "type": "computed",
+      "value": "(!RequiresHttps)"
+    },
+    "HasHttpsProfile": {
+      "type": "computed",
+      "value": "(RequiresHttps || !NoHttps)"
     },
     "NoHttps": {
       "type": "parameter",

+ 2 - 2
src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/Program.Main.cs

@@ -129,7 +129,7 @@ public class Program
         #endif
         {
             app.UseExceptionHandler("/Home/Error");
-        #if (RequiresHttps)
+        #if (HasHttpsProfile)
             // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
             app.UseHsts();
         }
@@ -156,4 +156,4 @@ public class Program
 
         app.Run();
     }
-}
+}

+ 1 - 1
src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/Program.cs

@@ -123,7 +123,7 @@ if (!app.Environment.IsDevelopment())
 #endif
 {
     app.UseExceptionHandler("/Home/Error");
-#if (RequiresHttps)
+#if (HasHttpsProfile)
     // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
     app.UseHsts();
 }

+ 3 - 3
src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/Properties/launchSettings.json

@@ -9,7 +9,7 @@
     //#endif
     "iisExpress": {
       "applicationUrl": "http://localhost:8080",
-      //#if(RequiresHttps)
+      //#if (HasHttpsProfile)
       "sslPort": 44300
       //#else
       "sslPort": 0
@@ -17,7 +17,7 @@
     }
   },
   "profiles": {
-    //#if(!RequiresHttps)
+    //#if (HasHttpProfile)
     "http": {
       "commandName": "Project",
       "dotnetRunMessages": true,
@@ -28,7 +28,7 @@
       }
     },
     //#endif
-    //#if(!NoHttps)
+    //#if (HasHttpsProfile)
     "https": {
       "commandName": "Project",
       "dotnetRunMessages": true,

+ 4 - 0
src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-FSharp/.template.config/template.json

@@ -152,6 +152,10 @@
       "description": "If specified, skips the automatic restore of the project on create.",
       "defaultValue": "false"
     },
+    "HasHttpsProfile": {
+      "type": "computed",
+      "value": "(!NoHttps)"
+    },
     "NoHttps": {
       "type": "parameter",
       "datatype": "bool",

+ 1 - 1
src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-FSharp/Program.fs

@@ -36,7 +36,7 @@ module Program =
 
         if not (builder.Environment.IsDevelopment()) then
             app.UseExceptionHandler("/Home/Error")
-#if !NoHttps
+#if HasHttpsProfile
             app.UseHsts() |> ignore // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
 
         app.UseHttpsRedirection()

+ 4 - 4
src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-FSharp/Properties/launchSettings.json

@@ -9,10 +9,10 @@
     //#endif
     "iisExpress": {
       "applicationUrl": "http://localhost:8080",
-      //#if(NoHttps)
-      "sslPort": 0
-      //#else
+      //#if (HasHttpsProfile)
       "sslPort": 44300
+      //#else
+      "sslPort": 0
       //#endif
     }
   },
@@ -26,7 +26,7 @@
         "ASPNETCORE_ENVIRONMENT": "Development"
       }
     },
-    //#if(!NoHttps)
+    //#if (HasHttpsProfile)
     "https": {
       "commandName": "Project",
       "dotnetRunMessages": true,

+ 9 - 1
src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/.template.config/template.json

@@ -294,7 +294,15 @@
     },
     "RequiresHttps": {
       "type": "computed",
-      "value": "(OrganizationalAuth || IndividualB2C || !NoHttps)"
+      "value": "(OrganizationalAuth || IndividualB2CAuth)"
+    },
+    "HasHttpProfile": {
+      "type": "computed",
+      "value": "(!RequiresHttps)"
+    },
+    "HasHttpsProfile": {
+      "type": "computed",
+      "value": "(RequiresHttps || !NoHttps)"
     },
     "NoHttps": {
       "type": "parameter",

+ 1 - 1
src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Program.Main.cs

@@ -87,7 +87,7 @@ public class Program
             app.UseSwaggerUI();
         }
         #endif
-        #if (RequiresHttps)
+        #if (HasHttpsProfile)
 
         app.UseHttpsRedirection();
         #endif

+ 1 - 1
src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Program.MinimalAPIs.OrgOrIndividualB2CAuth.cs

@@ -59,7 +59,7 @@ if (app.Environment.IsDevelopment())
     app.UseSwaggerUI();
 }
 #endif
-#if (RequiresHttps)
+#if (HasHttpsProfile)
 
 app.UseHttpsRedirection();
 #endif

+ 1 - 1
src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Program.MinimalAPIs.WindowsOrNoAuth.cs

@@ -35,7 +35,7 @@ if (app.Environment.IsDevelopment())
     app.UseSwaggerUI();
 }
 #endif
-#if (RequiresHttps)
+#if (HasHttpsProfile)
 
 app.UseHttpsRedirection();
 #endif

+ 1 - 1
src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Program.cs

@@ -72,7 +72,7 @@ if (app.Environment.IsDevelopment())
     app.UseSwaggerUI();
 }
 #endif
-#if (RequiresHttps)
+#if (HasHttpsProfile)
 
 app.UseHttpsRedirection();
 #endif

+ 6 - 6
src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Properties/launchSettings.json

@@ -10,7 +10,7 @@
     //#endif
     "iisExpress": {
       "applicationUrl": "http://localhost:8080",
-      //#if(RequiresHttps)
+      //#if (HasHttpsProfile)
       "sslPort": 44300
       //#else
       "sslPort": 0
@@ -18,12 +18,12 @@
     }
   },
   "profiles": {
-    //#if(!RequiresHttps)
+    //#if (HasHttpProfile)
     "http": {
       "commandName": "Project",
       "dotnetRunMessages": true,
       "launchBrowser": true,
-      //#if(EnableOpenAPI)
+      //#if (EnableOpenAPI)
       "launchUrl": "swagger",
       //#else
       "launchUrl": "weatherforecast",
@@ -34,12 +34,12 @@
       }
     },
     //#endif
-    //#if(!NoHttps)
+    //#if (HasHttpsProfile)
     "https": {
       "commandName": "Project",
       "dotnetRunMessages": true,
       "launchBrowser": true,
-      //#if(EnableOpenAPI)
+      //#if (EnableOpenAPI)
       "launchUrl": "swagger",
       //#else
       "launchUrl": "weatherforecast",
@@ -53,7 +53,7 @@
     "IIS Express": {
       "commandName": "IISExpress",
       "launchBrowser": true,
-      //#if(EnableOpenAPI)
+      //#if (EnableOpenAPI)
       "launchUrl": "swagger",
       //#else
       "launchUrl": "weatherforecast",

+ 4 - 0
src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-FSharp/.template.config/template.json

@@ -147,6 +147,10 @@
       "description": "If specified, skips the automatic restore of the project on create.",
       "defaultValue": "false"
     },
+    "HasHttpsProfile": {
+      "type": "computed",
+      "value": "(!NoHttps)"
+    },
     "NoHttps": {
       "type": "parameter",
       "datatype": "bool",

+ 1 - 1
src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-FSharp/Program.fs

@@ -28,7 +28,7 @@ module Program =
 
         let app = builder.Build()
 
-#if !NoHttps
+#if HasHttpsProfile
         app.UseHttpsRedirection()
 #endif
 

+ 4 - 12
src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-FSharp/Properties/launchSettings.json

@@ -10,10 +10,10 @@
     //#endif
     "iisExpress": {
       "applicationUrl": "http://localhost:8080",
-      //#if(NoHttps)
-      "sslPort": 0
-      //#else
+      //#if (HasHttpsProfile)
       "sslPort": 44300
+      //#else
+      "sslPort": 0
       //#endif
     }
   },
@@ -22,26 +22,18 @@
       "commandName": "Project",
       "dotnetRunMessages": true,
       "launchBrowser": true,
-      //#if(EnableOpenAPI)
-      "launchUrl": "swagger",
-      //#else
       "launchUrl": "weatherforecast",
-      //#endif
       "applicationUrl": "http://localhost:5000",
       "environmentVariables": {
         "ASPNETCORE_ENVIRONMENT": "Development"
       }
     },
-    //#if(!NoHttps)
+    //#if (HasHttpsProfile)
     "https": {
       "commandName": "Project",
       "dotnetRunMessages": true,
       "launchBrowser": true,
-      //#if(EnableOpenAPI)
-      "launchUrl": "swagger",
-      //#else
       "launchUrl": "weatherforecast",
-      //#endif
       "applicationUrl": "https://localhost:5001;http://localhost:5000",
       "environmentVariables": {
         "ASPNETCORE_ENVIRONMENT": "Development"

+ 1 - 16
src/ProjectTemplates/scripts/.gitignore

@@ -1,17 +1,2 @@
 package-lock.json
-tmp/
-CustomHive/
-angular/
-blazorserver/
-blazorwasm/
-mvc/
-mvcorgauth/
-razor/
-react/
-reactredux/
-web/
-webapp/
-webapi/
-webapimin/
-worker/
-grpc/
+**/

+ 1 - 1
src/ProjectTemplates/scripts/Test-Template.ps1

@@ -21,7 +21,7 @@ function Test-Template($templateName, $templateArgs, $templateNupkg, $isBlazorWa
         Pop-Location
     }
 
-    Run-DotnetNew "--install", "$PSScriptRoot/../../../artifacts/packages/Debug/Shipping/$templateNupkg"
+    Run-DotnetNew "install", "$PSScriptRoot/../../../artifacts/packages/Debug/Shipping/$templateNupkg"
 
     New-Item -ErrorAction Ignore -Path $tmpDir -ItemType Directory
     Push-Location $tmpDir

+ 0 - 59
src/ProjectTemplates/test/BlazorServerTemplateTest.cs

@@ -1,59 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Threading.Tasks;
-using Microsoft.AspNetCore.Testing;
-using Templates.Test.Helpers;
-using Xunit;
-using Xunit.Abstractions;
-using Xunit.Sdk;
-
-namespace Templates.Test;
-
-public class BlazorServerTemplateTest : BlazorTemplateTest
-{
-    public BlazorServerTemplateTest(ProjectFactoryFixture projectFactory)
-        : base(projectFactory)
-    {
-    }
-
-    public override string ProjectType { get; } = "blazorserver";
-
-    [Fact]
-    public Task BlazorServerTemplateWorks_NoAuth() => CreateBuildPublishAsync();
-
-    [Fact]
-    public Task BlazorServerTemplateWorks_ProgamMainNoAuth() => CreateBuildPublishAsync(args: new[] { ArgConstants.UseProgramMain });
-
-    [ConditionalTheory]
-    [InlineData("Individual", null)]
-    [InlineData("Individual", new string[] { ArgConstants.UseProgramMain })]
-    [SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/30825", Queues = "All.OSX")]
-    public Task BlazorServerTemplateWorks_IndividualAuth(string auth, string[] args) => CreateBuildPublishAsync(auth, args: args);
-
-    [ConditionalTheory]
-    [InlineData("Individual", new string[] { ArgConstants.UseLocalDb })]
-    [InlineData("Individual", new string[] { ArgConstants.UseProgramMain, ArgConstants.UseLocalDb })]
-    [OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX, SkipReason = "No LocalDb on non-Windows")]
-    public Task BlazorServerTemplateWorks_IndividualAuth_LocalDb(string auth, string[] args) => CreateBuildPublishAsync(auth, args: args);
-
-    [Theory]
-    [InlineData("IndividualB2C", null)]
-    [InlineData("IndividualB2C", new[] { ArgConstants.UseProgramMain })]
-    [InlineData("IndividualB2C", new[] { ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite })]
-    [InlineData("IndividualB2C", new[] { ArgConstants.UseProgramMain, ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite })]
-    public Task BlazorServerTemplate_IdentityWeb_BuildAndPublish_IndividualB2C(string auth, string[] args) => CreateBuildPublishAsync(auth, args);
-
-    [Theory]
-    [InlineData("SingleOrg", null)]
-    [InlineData("SingleOrg", new[] { ArgConstants.UseProgramMain })]
-    [InlineData("SingleOrg", new[] { ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite })]
-    [InlineData("SingleOrg", new[] { ArgConstants.UseProgramMain, ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite })]
-    [InlineData("SingleOrg", new[] { ArgConstants.CallsGraph })]
-    [InlineData("SingleOrg", new[] { ArgConstants.UseProgramMain, ArgConstants.CallsGraph })]
-    public Task BlazorServerTemplate_IdentityWeb_BuildAndPublish_SingleOrg(string auth, string[] args) => CreateBuildPublishAsync(auth, args);
-}

+ 0 - 0
src/ProjectTemplates/test/.gitattributes → src/ProjectTemplates/test/Templates.Blazor.Tests/.gitattributes


+ 93 - 0
src/ProjectTemplates/test/Templates.Blazor.Tests/BlazorServerTemplateTest.cs

@@ -0,0 +1,93 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Testing;
+using Templates.Test.Helpers;
+using Xunit;
+using Xunit.Abstractions;
+using Xunit.Sdk;
+
+namespace Templates.Blazor.Test;
+
+public class BlazorServerTemplateTest : BlazorTemplateTest
+{
+    public BlazorServerTemplateTest(ProjectFactoryFixture projectFactory)
+        : base(projectFactory)
+    {
+    }
+
+    public override string ProjectType { get; } = "blazorserver";
+
+    [Fact]
+    public Task BlazorServerTemplateWorks_NoAuth() => CreateBuildPublishAsync();
+
+    [Fact]
+    public Task BlazorServerTemplate_NoHttps_Works_NoAuth() => CreateBuildPublishAsync(args: new[] { ArgConstants.NoHttps });
+
+    [Fact]
+    public Task BlazorServerTemplateWorks_ProgamMainNoAuth() => CreateBuildPublishAsync(args: new[] { ArgConstants.UseProgramMain });
+
+    [Fact]
+    public Task BlazorServerTemplate_NoHttps_Works_ProgamMainNoAuth() => CreateBuildPublishAsync(args: new[] { ArgConstants.UseProgramMain, ArgConstants.NoHttps});
+
+    [ConditionalTheory]
+    [InlineData("Individual", null)]
+    [InlineData("Individual", new [] { ArgConstants.UseProgramMain })]
+    [SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/30825", Queues = "All.OSX")]
+    public Task BlazorServerTemplateWorks_IndividualAuth(string auth, string[] args) => CreateBuildPublishAsync(auth, args: args);
+
+    [ConditionalTheory]
+    [InlineData("Individual", new[] { ArgConstants.NoHttps })]
+    [InlineData("Individual", new [] { ArgConstants.UseProgramMain, ArgConstants.NoHttps })]
+    [SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/30825", Queues = "All.OSX")]
+    public Task BlazorServerTemplateWorks_IndividualAuth_NoHttps(string auth, string[] args) => CreateBuildPublishAsync(auth, args: args);
+
+    [ConditionalTheory]
+    [InlineData("Individual", new [] { ArgConstants.UseLocalDb })]
+    [InlineData("Individual", new [] { ArgConstants.UseProgramMain, ArgConstants.UseLocalDb })]
+    [OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX, SkipReason = "No LocalDb on non-Windows")]
+    public Task BlazorServerTemplateWorks_IndividualAuth_LocalDb(string auth, string[] args) => CreateBuildPublishAsync(auth, args: args);
+
+    [ConditionalTheory]
+    [InlineData("Individual", new[] { ArgConstants.UseLocalDb, ArgConstants.NoHttps })]
+    [InlineData("Individual", new[] { ArgConstants.UseProgramMain, ArgConstants.UseLocalDb, ArgConstants.NoHttps })]
+    [OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX, SkipReason = "No LocalDb on non-Windows")]
+    public Task BlazorServerTemplateWorks_IndividualAuth_NoHttps_LocalDb(string auth, string[] args) => CreateBuildPublishAsync(auth, args: args);
+
+    [Theory]
+    [InlineData("IndividualB2C", null)]
+    [InlineData("IndividualB2C", new[] { ArgConstants.UseProgramMain })]
+    [InlineData("IndividualB2C", new[] { ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite })]
+    [InlineData("IndividualB2C", new[] { ArgConstants.UseProgramMain, ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite })]
+    public Task BlazorServerTemplate_IdentityWeb_BuildAndPublish_IndividualB2C(string auth, string[] args) => CreateBuildPublishAsync(auth, args);
+
+    [Theory]
+    [InlineData("IndividualB2C", null)]
+    [InlineData("IndividualB2C", new[] { ArgConstants.UseProgramMain, ArgConstants.NoHttps })]
+    [InlineData("IndividualB2C", new[] { ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite, ArgConstants.NoHttps })]
+    [InlineData("IndividualB2C", new[] { ArgConstants.UseProgramMain, ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite, ArgConstants.NoHttps })]
+    public Task BlazorServerTemplate_IdentityWeb_BuildAndPublish_IndividualB2C_NoHttps(string auth, string[] args) => CreateBuildPublishAsync(auth, args);
+
+    [Theory]
+    [InlineData("SingleOrg", null)]
+    [InlineData("SingleOrg", new[] { ArgConstants.UseProgramMain })]
+    [InlineData("SingleOrg", new[] { ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite })]
+    [InlineData("SingleOrg", new[] { ArgConstants.UseProgramMain, ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite })]
+    [InlineData("SingleOrg", new[] { ArgConstants.CallsGraph })]
+    [InlineData("SingleOrg", new[] { ArgConstants.UseProgramMain, ArgConstants.CallsGraph })]
+    public Task BlazorServerTemplate_IdentityWeb_BuildAndPublish_SingleOrg(string auth, string[] args) => CreateBuildPublishAsync(auth, args);
+
+    [Theory]
+    [InlineData("SingleOrg", null)]
+    [InlineData("SingleOrg", new[] { ArgConstants.UseProgramMain, ArgConstants.NoHttps })]
+    [InlineData("SingleOrg", new[] { ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite, ArgConstants.NoHttps })]
+    [InlineData("SingleOrg", new[] { ArgConstants.UseProgramMain, ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite, ArgConstants.NoHttps })]
+    [InlineData("SingleOrg", new[] { ArgConstants.CallsGraph, ArgConstants.NoHttps })]
+    [InlineData("SingleOrg", new[] { ArgConstants.UseProgramMain, ArgConstants.CallsGraph, ArgConstants.NoHttps })]
+    public Task BlazorServerTemplate_IdentityWeb_BuildAndPublish_SingleOrg_NoHttps(string auth, string[] args) => CreateBuildPublishAsync(auth, args);
+}

+ 16 - 1
src/ProjectTemplates/test/BlazorTemplateTest.cs → src/ProjectTemplates/test/Templates.Blazor.Tests/BlazorTemplateTest.cs

@@ -15,7 +15,7 @@ using Templates.Test.Helpers;
 using Xunit;
 using Xunit.Abstractions;
 
-namespace Templates.Test;
+namespace Templates.Blazor.Test;
 
 public abstract class BlazorTemplateTest : LoggedTest
 {
@@ -55,6 +55,21 @@ public abstract class BlazorTemplateTest : LoggedTest
         var createResult = await project.RunDotNetNewAsync(ProjectType, auth: auth, args: args, errorOnRestoreError: false);
         Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", project, createResult));
 
+        if (serverProject || auth is null)
+        {
+            // External auth mechanisms (which is any auth at all for Blazor WASM) require https to work and thus don't honor the --no-https flag
+            var requiresHttps = string.Equals(auth, "IndividualB2C", StringComparison.OrdinalIgnoreCase)
+                                || string.Equals(auth, "SingleOrg", StringComparison.OrdinalIgnoreCase)
+                                || (!serverProject && auth is not null);
+            var noHttps = args?.Contains(ArgConstants.NoHttps) ?? false;
+            var expectedLaunchProfileNames = requiresHttps
+                ? new[] { "https", "IIS Express" }
+                : noHttps
+                    ? new[] { "http", "IIS Express" }
+                    : new[] { "http", "https", "IIS Express" };
+            await project.VerifyLaunchSettings(expectedLaunchProfileNames);
+        }
+
         if (!onlyCreate)
         {
             var targetProject = project;

+ 125 - 52
src/ProjectTemplates/test/BlazorWasmTemplateTest.cs → src/ProjectTemplates/test/Templates.Blazor.Tests/BlazorWasmTemplateTest.cs

@@ -15,7 +15,7 @@ using Xunit;
 using Xunit.Abstractions;
 using Xunit.Sdk;
 
-namespace Templates.Test;
+namespace Templates.Blazor.Test;
 
 public class BlazorWasmTemplateTest : BlazorTemplateTest
 {
@@ -35,13 +35,38 @@ public class BlazorWasmTemplateTest : BlazorTemplateTest
     }
 
     [Fact]
-    public Task BlazorWasmHostedTemplateCanCreateBuildPublish() => CreateBuildPublishAsync(args: new[] { ArgConstants.Hosted }, serverProject: true);
+    public async Task BlazorWasmStandaloneTemplateNoHttpsCanCreateBuildPublish()
+    {
+        var project = await CreateBuildPublishAsync(args: new[] { ArgConstants.NoHttps });
+
+        // The service worker assets manifest isn't generated for non-PWA projects
+        var publishDir = Path.Combine(project.TemplatePublishDir, "wwwroot");
+        Assert.False(File.Exists(Path.Combine(publishDir, "service-worker-assets.js")), "Non-PWA templates should not produce service-worker-assets.js");
+    }
+
+    [Fact]
+    public Task BlazorWasmHostedTemplateCanCreateBuildPublish()
+        => CreateBuildPublishAsync(args: new[] { ArgConstants.Hosted }, serverProject: true);
+
+    [Fact]
+    public Task BlazorWasmHostedTemplateNoHttpsCanCreateBuildPublish()
+        => CreateBuildPublishAsync(args: new[] { ArgConstants.Hosted, ArgConstants.NoHttps }, serverProject: true);
+
+    [Fact]
+    public Task BlazorWasmHostedTemplateWithProgamMainCanCreateBuildPublish()
+        => CreateBuildPublishAsync(args: new[] { ArgConstants.UseProgramMain, ArgConstants.Hosted }, serverProject: true);
+
+    [Fact]
+    public Task BlazorWasmHostedTemplateNoHttpsWithProgamMainCanCreateBuildPublish()
+        => CreateBuildPublishAsync(args: new[] { ArgConstants.UseProgramMain, ArgConstants.Hosted, ArgConstants.NoHttps }, serverProject: true);
 
     [Fact]
-    public Task BlazorWasmHostedTemplateWithProgamMainCanCreateBuildPublish() => CreateBuildPublishAsync(args: new[] { ArgConstants.UseProgramMain, ArgConstants.Hosted }, serverProject: true);
+    public Task BlazorWasmStandalonePwaTemplateCanCreateBuildPublish()
+        => CreateBuildPublishAsync(args: new[] { ArgConstants.Pwa });
 
     [Fact]
-    public Task BlazorWasmStandalonePwaTemplateCanCreateBuildPublish() => CreateBuildPublishAsync(args: new[] { ArgConstants.Pwa });
+    public Task BlazorWasmStandalonePwaTemplateNoHttpsCanCreateBuildPublish()
+        => CreateBuildPublishAsync(args: new[] { ArgConstants.Pwa, ArgConstants.NoHttps });
 
     [Fact]
     public async Task BlazorWasmHostedPwaTemplateCanCreateBuildPublish()
@@ -53,6 +78,16 @@ public class BlazorWasmTemplateTest : BlazorTemplateTest
         ValidatePublishedServiceWorker(serverProject);
     }
 
+    [Fact]
+    public async Task BlazorWasmHostedPwaTemplateNoHttpsCanCreateBuildPublish()
+    {
+        var project = await CreateBuildPublishAsync(args: new[] { ArgConstants.Hosted, ArgConstants.Pwa, ArgConstants.NoHttps }, serverProject: true);
+
+        var serverProject = GetSubProject(project, "Server", $"{project.ProjectName}.Server");
+
+        ValidatePublishedServiceWorker(serverProject);
+    }
+
     private void ValidatePublishedServiceWorker(Project project)
     {
         var publishDir = Path.Combine(project.TemplatePublishDir, "wwwroot");
@@ -81,31 +116,56 @@ public class BlazorWasmTemplateTest : BlazorTemplateTest
     // LocalDB doesn't work on non Windows platforms
     [OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX)]
     public Task BlazorWasmHostedTemplate_IndividualAuth_Works_WithLocalDB()
-        => BlazorWasmHostedTemplate_IndividualAuth_Works(true, false);
+        => BlazorWasmHostedTemplate_IndividualAuth_Works(true, false, false);
+
+    [ConditionalFact]
+    [SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/34554", Queues = "Windows.10.Arm64v8.Open")]
+    // LocalDB doesn't work on non Windows platforms
+    [OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX)]
+    public Task BlazorWasmHostedTemplate_IndividualAuth_NoHttps_Works_WithLocalDB()
+        => BlazorWasmHostedTemplate_IndividualAuth_Works(true, false, true);
 
     [ConditionalFact]
     [SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/34554", Queues = "Windows.10.Arm64v8.Open")]
     public Task BlazorWasmHostedTemplate_IndividualAuth_Works_WithOutLocalDB()
-        => BlazorWasmHostedTemplate_IndividualAuth_Works(false, false);
+        => BlazorWasmHostedTemplate_IndividualAuth_Works(false, false, false);
+
+    [ConditionalFact]
+    [SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/34554", Queues = "Windows.10.Arm64v8.Open")]
+    public Task BlazorWasmHostedTemplate_IndividualAuth_NoHttps_Works_WithOutLocalDB()
+        => BlazorWasmHostedTemplate_IndividualAuth_Works(false, false, false);
 
     [ConditionalFact]
     [SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/34554", Queues = "Windows.10.Arm64v8.Open")]
     // LocalDB doesn't work on non Windows platforms
     [OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX)]
     public Task BlazorWasmHostedTemplate_IndividualAuth_Works_WithLocalDB_ProgramMain()
-        => BlazorWasmHostedTemplate_IndividualAuth_Works(true, true);
+        => BlazorWasmHostedTemplate_IndividualAuth_Works(true, true, false);
+
+    [ConditionalFact]
+    [SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/34554", Queues = "Windows.10.Arm64v8.Open")]
+    // LocalDB doesn't work on non Windows platforms
+    [OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX)]
+    public Task BlazorWasmHostedTemplate_IndividualAuth_NoHttps_Works_WithLocalDB_ProgramMain()
+        => BlazorWasmHostedTemplate_IndividualAuth_Works(true, true, true);
 
     [ConditionalFact]
     [SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/34554", Queues = "Windows.10.Arm64v8.Open")]
     public Task BlazorWasmHostedTemplate_IndividualAuth_Works_WithOutLocalDB_ProgramMain()
-        => BlazorWasmHostedTemplate_IndividualAuth_Works(false, true);
+        => BlazorWasmHostedTemplate_IndividualAuth_Works(false, true, false);
 
-    private async Task<Project> CreateBuildPublishIndividualAuthProject(bool useLocalDb, bool useProgramMain = false)
+    [ConditionalFact]
+    [SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/34554", Queues = "Windows.10.Arm64v8.Open")]
+    public Task BlazorWasmHostedTemplate_IndividualAuth_NoHttps_Works_WithOutLocalDB_ProgramMain()
+        => BlazorWasmHostedTemplate_IndividualAuth_Works(false, true, true);
+
+    private async Task<Project> CreateBuildPublishIndividualAuthProject(bool useLocalDb, bool useProgramMain = false, bool noHttps = false)
     {
         // Additional arguments are needed. See: https://github.com/dotnet/aspnetcore/issues/24278
         Environment.SetEnvironmentVariable("EnableDefaultScopedCssItems", "true");
 
-        var project = await CreateBuildPublishAsync("Individual", args: new[] { ArgConstants.Hosted, useLocalDb ? "-uld" : "", useProgramMain ? ArgConstants.UseProgramMain : "" });
+        var args = new[] { ArgConstants.Hosted, useLocalDb ? "-uld" : "", useProgramMain ? ArgConstants.UseProgramMain : "", noHttps ? ArgConstants.NoHttps : "" };
+        var project = await CreateBuildPublishAsync("Individual", args: args);
 
         var serverProject = GetSubProject(project, "Server", $"{project.ProjectName}.Server");
 
@@ -139,9 +199,9 @@ public class BlazorWasmTemplateTest : BlazorTemplateTest
         return project;
     }
 
-    private async Task BlazorWasmHostedTemplate_IndividualAuth_Works(bool useLocalDb, bool useProgramMain)
+    private async Task BlazorWasmHostedTemplate_IndividualAuth_Works(bool useLocalDb, bool useProgramMain, bool noHttps)
     {
-        var project = await CreateBuildPublishIndividualAuthProject(useLocalDb: useLocalDb, useProgramMain: useProgramMain);
+        var project = await CreateBuildPublishIndividualAuthProject(useLocalDb: useLocalDb, useProgramMain: useProgramMain, noHttps: noHttps);
 
         var serverProject = GetSubProject(project, "Server", $"{project.ProjectName}.Server");
     }
@@ -157,11 +217,22 @@ public class BlazorWasmTemplateTest : BlazorTemplateTest
             });
     }
 
+    [Fact]
+    public async Task BlazorWasmStandaloneTemplate_NoHttps_IndividualAuth_CreateBuildPublish()
+    {
+        var project = await CreateBuildPublishAsync("Individual", args: new[] {
+                "--authority",
+                "https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration",
+                ArgConstants.ClientId,
+                "sample-client-id",
+                ArgConstants.NoHttps
+            });
+    }
+
     public static TheoryData<TemplateInstance> TemplateDataIndividualB2C => new TheoryData<TemplateInstance>
         {
-            new TemplateInstance(
-                "blazorwasmhostedaadb2c", "-ho",
-                ArgConstants.Auth, "IndividualB2C",
+            new TemplateInstance("blazorwasmhostedaadb2c", "IndividualB2C",
+                ArgConstants.Hosted,
                 ArgConstants.AadB2cInstance, "example.b2clogin.com",
                 "-ssp", "b2c_1_siupin",
                 ArgConstants.ClientId, "clientId",
@@ -169,9 +240,8 @@ public class BlazorWasmTemplateTest : BlazorTemplateTest
                 ArgConstants.DefaultScope, "full",
                 ArgConstants.AppIdUri, "ApiUri",
                 ArgConstants.AppIdClientId, "1234123413241324"),
-            new TemplateInstance(
-                "blazorwasmhostedaadb2c_program_main", "-ho",
-                ArgConstants.Auth, "IndividualB2C",
+            new TemplateInstance("blazorwasmhostedaadb2c_program_main", "IndividualB2C",
+                ArgConstants.Hosted,
                 ArgConstants.AadB2cInstance, "example.b2clogin.com",
                 "-ssp", "b2c_1_siupin",
                 ArgConstants.ClientId, "clientId",
@@ -180,16 +250,12 @@ public class BlazorWasmTemplateTest : BlazorTemplateTest
                 ArgConstants.AppIdUri, "ApiUri",
                 ArgConstants.AppIdClientId, "1234123413241324",
                 ArgConstants.UseProgramMain),
-            new TemplateInstance(
-                "blazorwasmstandaloneaadb2c",
-                ArgConstants.Auth, "IndividualB2C",
+            new TemplateInstance("blazorwasmstandaloneaadb2c", "IndividualB2C",
                 ArgConstants.AadB2cInstance, "example.b2clogin.com",
                 "-ssp", "b2c_1_siupin",
                 ArgConstants.ClientId, "clientId",
                 ArgConstants.Domain, "my-domain"),
-            new TemplateInstance(
-                "blazorwasmstandaloneaadb2c_program_main",
-                ArgConstants.Auth, "IndividualB2C",
+            new TemplateInstance("blazorwasmstandaloneaadb2c_program_main", "IndividualB2C",
                 ArgConstants.AadB2cInstance, "example.b2clogin.com",
                 "-ssp", "b2c_1_siupin",
                 ArgConstants.ClientId, "clientId",
@@ -199,18 +265,16 @@ public class BlazorWasmTemplateTest : BlazorTemplateTest
 
     public static TheoryData<TemplateInstance> TemplateDataSingleOrg => new TheoryData<TemplateInstance>
         {
-            new TemplateInstance(
-                "blazorwasmhostedaad", "-ho",
-                ArgConstants.Auth, "SingleOrg",
+            new TemplateInstance("blazorwasmhostedaad", "SingleOrg",
+                ArgConstants.Hosted,
                 ArgConstants.Domain, "my-domain",
                 ArgConstants.TenantId, "tenantId",
                 ArgConstants.ClientId, "clientId",
                 ArgConstants.DefaultScope, "full",
                 ArgConstants.AppIdUri, "ApiUri",
                 ArgConstants.AppIdClientId, "1234123413241324"),
-            new TemplateInstance(
-                "blazorwasmhostedaadgraph", "-ho",
-                ArgConstants.Auth, "SingleOrg",
+            new TemplateInstance("blazorwasmhostedaadgraph", "SingleOrg",
+                ArgConstants.Hosted,
                 ArgConstants.CallsGraph,
                 ArgConstants.Domain, "my-domain",
                 ArgConstants.TenantId, "tenantId",
@@ -218,9 +282,8 @@ public class BlazorWasmTemplateTest : BlazorTemplateTest
                 ArgConstants.DefaultScope, "full",
                 ArgConstants.AppIdUri, "ApiUri",
                 ArgConstants.AppIdClientId, "1234123413241324"),
-            new TemplateInstance(
-                "blazorwasmhostedaadapi", "-ho",
-                ArgConstants.Auth, "SingleOrg",
+            new TemplateInstance("blazorwasmhostedaadapi", "SingleOrg",
+                ArgConstants.Hosted,
                 ArgConstants.CalledApiUrl, "\"https://graph.microsoft.com\"",
                 ArgConstants.CalledApiScopes, "user.readwrite",
                 ArgConstants.Domain, "my-domain",
@@ -229,9 +292,7 @@ public class BlazorWasmTemplateTest : BlazorTemplateTest
                 ArgConstants.DefaultScope, "full",
                 ArgConstants.AppIdUri, "ApiUri",
                 ArgConstants.AppIdClientId, "1234123413241324"),
-            new TemplateInstance(
-                "blazorwasmstandaloneaad",
-                ArgConstants.Auth, "SingleOrg",
+            new TemplateInstance("blazorwasmstandaloneaad", "SingleOrg",
                 ArgConstants.Domain, "my-domain",
                 ArgConstants.TenantId, "tenantId",
                 ArgConstants.ClientId, "clientId"),
@@ -239,9 +300,8 @@ public class BlazorWasmTemplateTest : BlazorTemplateTest
 
     public static TheoryData<TemplateInstance> TemplateDataSingleOrgProgramMain => new TheoryData<TemplateInstance>
         {
-            new TemplateInstance(
-                "blazorwasmhostedaad_program_main", "-ho",
-                ArgConstants.Auth, "SingleOrg",
+            new TemplateInstance("blazorwasmhostedaad_program_main", "SingleOrg",
+                ArgConstants.Hosted,
                 ArgConstants.Domain, "my-domain",
                 ArgConstants.TenantId, "tenantId",
                 ArgConstants.ClientId, "clientId",
@@ -249,9 +309,8 @@ public class BlazorWasmTemplateTest : BlazorTemplateTest
                 ArgConstants.AppIdUri, "ApiUri",
                 ArgConstants.AppIdClientId, "1234123413241324",
                 ArgConstants.UseProgramMain),
-            new TemplateInstance(
-                "blazorwasmhostedaadgraph_program_main", "-ho",
-                ArgConstants.Auth, "SingleOrg",
+            new TemplateInstance("blazorwasmhostedaadgraph_program_main", "SingleOrg",
+                ArgConstants.Hosted,
                 ArgConstants.CallsGraph,
                 ArgConstants.Domain, "my-domain",
                 ArgConstants.TenantId, "tenantId",
@@ -260,9 +319,8 @@ public class BlazorWasmTemplateTest : BlazorTemplateTest
                 ArgConstants.AppIdUri, "ApiUri",
                 ArgConstants.AppIdClientId, "1234123413241324",
                 ArgConstants.UseProgramMain),
-            new TemplateInstance(
-                "blazorwasmhostedaadapi_program_main", "-ho",
-                ArgConstants.Auth, "SingleOrg",
+            new TemplateInstance("blazorwasmhostedaadapi_program_main", "SingleOrg",
+                ArgConstants.Hosted,
                 ArgConstants.CalledApiUrl, "\"https://graph.microsoft.com\"",
                 ArgConstants.CalledApiScopes, "user.readwrite",
                 ArgConstants.Domain, "my-domain",
@@ -272,9 +330,7 @@ public class BlazorWasmTemplateTest : BlazorTemplateTest
                 ArgConstants.AppIdUri, "ApiUri",
                 ArgConstants.AppIdClientId, "1234123413241324",
                 ArgConstants.UseProgramMain),
-            new TemplateInstance(
-                "blazorwasmstandaloneaad_program_main",
-                ArgConstants.Auth, "SingleOrg",
+            new TemplateInstance("blazorwasmstandaloneaad_program_main", "SingleOrg",
                 ArgConstants.Domain, "my-domain",
                 ArgConstants.TenantId, "tenantId",
                 ArgConstants.ClientId, "clientId",
@@ -283,30 +339,47 @@ public class BlazorWasmTemplateTest : BlazorTemplateTest
 
     public class TemplateInstance
     {
-        public TemplateInstance(string name, params string[] arguments)
+        public TemplateInstance(string name, string auth, params string[] arguments)
         {
             Name = name;
+            Auth = auth;
             Arguments = arguments;
         }
 
         public string Name { get; }
+        public string Auth { get; }
         public string[] Arguments { get; }
     }
 
     [ConditionalTheory]
     [MemberData(nameof(TemplateDataIndividualB2C))]
     public Task BlazorWasmHostedTemplate_AzureActiveDirectoryTemplate_IndividualB2C_Works(TemplateInstance instance)
-        => CreateBuildPublishAsync(args: instance.Arguments, targetFramework: "netstandard2.1");
+        => CreateBuildPublishAsync(auth: instance.Auth, args: instance.Arguments, targetFramework: "netstandard2.1");
+
+    [ConditionalTheory]
+    [MemberData(nameof(TemplateDataIndividualB2C))]
+    public Task BlazorWasmHostedTemplate_AzureActiveDirectoryTemplate_IndividualB2C_NoHttps_Works(TemplateInstance instance)
+        => CreateBuildPublishAsync(auth: instance.Auth, args: instance.Arguments.Union(new[] { ArgConstants.NoHttps }).ToArray(), targetFramework: "netstandard2.1");
 
     [ConditionalTheory]
     [MemberData(nameof(TemplateDataSingleOrg))]
     public Task BlazorWasmHostedTemplate_AzureActiveDirectoryTemplate_SingleOrg_Works(TemplateInstance instance)
-        => CreateBuildPublishAsync(args: instance.Arguments, targetFramework: "netstandard2.1");
+        => CreateBuildPublishAsync(auth: instance.Auth, args: instance.Arguments, targetFramework: "netstandard2.1");
+
+    [ConditionalTheory]
+    [MemberData(nameof(TemplateDataSingleOrg))]
+    public Task BlazorWasmHostedTemplate_AzureActiveDirectoryTemplate_SingleOrg_NoHttps_Works(TemplateInstance instance)
+        => CreateBuildPublishAsync(auth: instance.Auth, args: instance.Arguments.Union(new[] { ArgConstants.NoHttps }).ToArray(), targetFramework: "netstandard2.1");
 
     [ConditionalTheory]
     [MemberData(nameof(TemplateDataSingleOrgProgramMain))]
     public Task BlazorWasmHostedTemplate_AzureActiveDirectoryTemplate_SingleOrg_ProgramMain_Works(TemplateInstance instance)
-        => CreateBuildPublishAsync(args: instance.Arguments, targetFramework: "netstandard2.1");
+        => CreateBuildPublishAsync(auth: instance.Auth, args: instance.Arguments, targetFramework: "netstandard2.1");
+
+    [ConditionalTheory]
+    [MemberData(nameof(TemplateDataSingleOrgProgramMain))]
+    public Task BlazorWasmHostedTemplate_AzureActiveDirectoryTemplate_SingleOrg_NoHttps_ProgramMain_Works(TemplateInstance instance)
+        => CreateBuildPublishAsync(auth: instance.Auth, args: instance.Arguments.Union(new[] { ArgConstants.NoHttps }).ToArray(), targetFramework: "netstandard2.1");
 
     private string ReadFile(string basePath, string path)
     {

+ 68 - 0
src/ProjectTemplates/test/Templates.Blazor.Tests/Templates.Blazor.Tests.csproj

@@ -0,0 +1,68 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <!-- Shared testing infrastructure for running E2E tests using selenium -->
+  <Import Condition="'$(SkipTestBuild)' != 'true'" Project="$(SharedSourceRoot)E2ETesting\E2ETesting.props" />
+
+  <PropertyGroup>
+    <TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
+    <TestGroupName>ProjectTemplates.E2ETests</TestGroupName>
+    <DefineConstants>$(DefineConstants);XPLAT</DefineConstants>
+
+    <RunTemplateTests Condition="'$(RunTemplateTests)' == ''">true</RunTemplateTests>
+    <SkipTests Condition="'$(RunTemplateTests)' != 'true'">true</SkipTests>
+
+    <BaseOutputPath />
+    <OutputPath />
+
+    <!-- Properties that affect test runs -->
+    <!-- TestTemplateCreationFolder is the folder where the templates will be created. Will point out to $(OutputDir)$(TestTemplateCreationFolder) -->
+    <TestTemplateCreationFolder>TestTemplates\</TestTemplateCreationFolder>
+    <TestPackageRestorePath>$([MSBuild]::EnsureTrailingSlash('$(RepoRoot)'))obj\template-restore\</TestPackageRestorePath>
+    <TestDependsOnAspNetPackages>true</TestDependsOnAspNetPackages>
+    <SkipHelixQueues>
+      $(HelixQueueArmDebian11);
+    </SkipHelixQueues>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <Compile Include="$(SharedSourceRoot)CommandLineUtils\**\*.cs" />
+    <Compile Include="$(SharedSourceRoot)CertificateGeneration\**\*.cs" LinkBase="shared\CertificateGeneration" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <Compile Include="$(SharedSourceRoot)Process\*.cs" LinkBase="shared\Process" />
+    <Compile Include="..\..\Shared\**" LinkBase="Helpers" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <!-- We need the actual templates in the output directory for tests to verify file encodings. -->
+    <Content Include="..\..\Web.ProjectTemplates\**" LinkBase="Assets\Web.ProjectTemplates" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <HelixContent Include="$(OutputPath)$(TargetFramework)Assets\**" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <Reference Include="AngleSharp" />
+    <Reference Include="System.Net.Http" />
+    <ProjectReference Include="$(RepoRoot)src\Framework\App.Runtime\src\Microsoft.AspNetCore.App.Runtime.csproj"
+      Private="false"
+      ReferenceOutputAssembly="false"
+      SkipGetTargetFrameworkProperties="true" />
+
+    <ProjectReference Include="$(RepoRoot)src\Hosting\Server.IntegrationTesting\src\Microsoft.AspNetCore.Server.IntegrationTesting.csproj" />
+    <ProjectReference Include="$(RepoRoot)src\Shared\BrowserTesting\src\Microsoft.AspNetCore.BrowserTesting.csproj" />
+    <ProjectReference Include="../../Web.ProjectTemplates/Microsoft.DotNet.Web.ProjectTemplates.csproj"
+      Private="false"
+      ReferenceOutputAssembly="false"
+      SkipGetTargetFrameworkProperties="true" />
+  </ItemGroup>
+
+  <PropertyGroup>
+    <PreserveExistingLogsInOutput Condition="'$(PreserveExistingLogsInOutput)' == '' AND '$(ContinuousIntegrationBuild)' == 'true'">true</PreserveExistingLogsInOutput>
+    <PreserveExistingLogsInOutput Condition="'$(PreserveExistingLogsInOutput)' == ''">false</PreserveExistingLogsInOutput>
+  </PropertyGroup>
+
+  <!-- Shared testing infrastructure for running E2E tests -->
+  <Import Condition="'$(SkipTestBuild)' != 'true'" Project="..\..\TestInfrastructure\PrepareForTest.targets" />
+</Project>

+ 1 - 0
src/ProjectTemplates/test/Templates.Mvc.Tests/.gitattributes

@@ -0,0 +1 @@
+.json diff

+ 68 - 14
src/ProjectTemplates/test/MvcTemplateTest.cs → src/ProjectTemplates/test/Templates.Mvc.Tests/MvcTemplateTest.cs

@@ -5,7 +5,7 @@ using Microsoft.AspNetCore.Testing;
 using Templates.Test.Helpers;
 using Xunit.Abstractions;
 
-namespace Templates.Test;
+namespace Templates.Mvc.Test;
 
 public class MvcTemplateTest : LoggedTest
 {
@@ -32,14 +32,25 @@ public class MvcTemplateTest : LoggedTest
     [Fact]
     public async Task MvcTemplate_NoAuthFSharp() => await MvcTemplateCore(languageOverride: "F#");
 
+    [Fact]
+    public async Task MvcTemplate_NoAuthNoHttpsFSharp() => await MvcTemplateCore(languageOverride: "F#", args: new[] { ArgConstants.NoHttps } );
+
     [ConditionalFact]
     [SkipOnHelix("Cert failure, https://github.com/dotnet/aspnetcore/issues/28090", Queues = "All.OSX;" + HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64 + HelixConstants.DebianAmd64)]
     public async Task MvcTemplate_NoAuthCSharp() => await MvcTemplateCore(languageOverride: null);
 
+    [ConditionalFact]
+    [SkipOnHelix("Cert failure, https://github.com/dotnet/aspnetcore/issues/28090", Queues = "All.OSX;" + HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64 + HelixConstants.DebianAmd64)]
+    public async Task MvcTemplate_NoAuthNoHttpsCSharp() => await MvcTemplateCore(languageOverride: null, new[] { ArgConstants.NoHttps });
+
     [ConditionalFact]
     [SkipOnHelix("Cert failure, https://github.com/dotnet/aspnetcore/issues/28090", Queues = "All.OSX;" + HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64 + HelixConstants.DebianAmd64)]
     public async Task MvcTemplate_ProgramMainNoAuthCSharp() => await MvcTemplateCore(languageOverride: null, new[] { ArgConstants.UseProgramMain });
 
+    [ConditionalFact]
+    [SkipOnHelix("Cert failure, https://github.com/dotnet/aspnetcore/issues/28090", Queues = "All.OSX;" + HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64 + HelixConstants.DebianAmd64)]
+    public async Task MvcTemplate_ProgramMainNoAuthNoHttpsCSharp() => await MvcTemplateCore(languageOverride: null, new[] { ArgConstants.UseProgramMain, ArgConstants.NoHttps });
+
     private async Task MvcTemplateCore(string languageOverride, string[] args = null)
     {
         var project = await ProjectFactory.CreateProject(Output);
@@ -47,6 +58,12 @@ public class MvcTemplateTest : LoggedTest
         var createResult = await project.RunDotNetNewAsync("mvc", language: languageOverride, args: args);
         Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", project, createResult));
 
+        var noHttps = args?.Contains(ArgConstants.NoHttps) ?? false;
+        var expectedLaunchProfileNames = noHttps
+            ? new[] { "http", "IIS Express" }
+            : new[] { "http", "https", "IIS Express" };
+        await project.VerifyLaunchSettings(expectedLaunchProfileNames);
+
         var projectExtension = languageOverride == "F#" ? "fsproj" : "csproj";
         var projectFileContents = project.ReadFile($"{project.ProjectName}.{projectExtension}");
         Assert.DoesNotContain(".db", projectFileContents);
@@ -113,26 +130,41 @@ public class MvcTemplateTest : LoggedTest
     }
 
     [ConditionalTheory]
-    [InlineData(false)]
-    [InlineData(true)]
+    [InlineData(false, false)]
+    [InlineData(false, true)]
+    [InlineData(true, false)]
+    [InlineData(true, true)]
     [SkipOnHelix("Cert failure, https://github.com/dotnet/aspnetcore/issues/28090", Queues = "All.OSX;" + HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64 + HelixConstants.DebianAmd64)]
     [OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX, SkipReason = "No LocalDb on non-Windows")]
-    public Task MvcTemplate_IndividualAuth_LocalDb(bool useProgramMain) => MvcTemplate_IndividualAuth_Core(useLocalDB: true, useProgramMain);
+    public Task MvcTemplate_IndividualAuth_LocalDb(bool useProgramMain, bool noHttps) => MvcTemplate_IndividualAuth_Core(useLocalDB: true, useProgramMain, noHttps);
 
     [ConditionalTheory]
-    [InlineData(false)]
-    [InlineData(true)]
+    [InlineData(false, false)]
+    [InlineData(false, true)]
+    [InlineData(true, false)]
+    [InlineData(true, true)]
     [SkipOnHelix("Cert failure, https://github.com/dotnet/aspnetcore/issues/28090", Queues = "All.OSX;" + HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64 + HelixConstants.DebianAmd64)]
-    public Task MvcTemplate_IndividualAuth(bool useProgramMain) => MvcTemplate_IndividualAuth_Core(useLocalDB: false, useProgramMain);
+    public Task MvcTemplate_IndividualAuth(bool useProgramMain, bool noHttps) => MvcTemplate_IndividualAuth_Core(useLocalDB: false, useProgramMain, noHttps);
 
-    private async Task MvcTemplate_IndividualAuth_Core(bool useLocalDB, bool useProgramMain)
+    private async Task MvcTemplate_IndividualAuth_Core(bool useLocalDB, bool useProgramMain, bool noHttps)
     {
         var project = await ProjectFactory.CreateProject(Output);
 
-        var args = useProgramMain ? new[] { ArgConstants.UseProgramMain } : null;
+        var args = useProgramMain
+            ? noHttps
+                ? new[] { ArgConstants.UseProgramMain, ArgConstants.NoHttps }
+                : new[] { ArgConstants.UseProgramMain }
+            : noHttps
+                ? new[] { ArgConstants.NoHttps }
+                : null;
         var createResult = await project.RunDotNetNewAsync("mvc", auth: "Individual", useLocalDB: useLocalDB, args: args);
         Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", project, createResult));
 
+        var expectedLaunchProfileNames = noHttps
+            ? new[] { "http", "IIS Express" }
+            : new[] { "http", "https", "IIS Express" };
+        await project.VerifyLaunchSettings(expectedLaunchProfileNames);
+
         var projectFileContents = project.ReadFile($"{project.ProjectName}.csproj");
         if (!useLocalDB)
         {
@@ -158,7 +190,7 @@ public class MvcTemplateTest : LoggedTest
                 new Page
                 {
                     Url = PageUrls.ForgotPassword,
-                    Links = new string [] {
+                    Links = new [] {
                         PageUrls.HomeUrl,
                         PageUrls.HomeUrl,
                         PageUrls.PrivacyUrl,
@@ -170,7 +202,7 @@ public class MvcTemplateTest : LoggedTest
                 new Page
                 {
                     Url = PageUrls.HomeUrl,
-                    Links = new string[] {
+                    Links = new [] {
                         PageUrls.HomeUrl,
                         PageUrls.HomeUrl,
                         PageUrls.PrivacyUrl,
@@ -183,7 +215,7 @@ public class MvcTemplateTest : LoggedTest
                 new Page
                 {
                     Url = PageUrls.PrivacyFullUrl,
-                    Links = new string[] {
+                    Links = new [] {
                         PageUrls.HomeUrl,
                         PageUrls.HomeUrl,
                         PageUrls.PrivacyUrl,
@@ -195,7 +227,7 @@ public class MvcTemplateTest : LoggedTest
                 new Page
                 {
                     Url = PageUrls.LoginUrl,
-                    Links = new string[] {
+                    Links = new [] {
                         PageUrls.HomeUrl,
                         PageUrls.HomeUrl,
                         PageUrls.PrivacyUrl,
@@ -210,7 +242,7 @@ public class MvcTemplateTest : LoggedTest
                 new Page
                 {
                     Url = PageUrls.RegisterUrl,
-                    Links = new string [] {
+                    Links = new [] {
                         PageUrls.HomeUrl,
                         PageUrls.HomeUrl,
                         PageUrls.PrivacyUrl,
@@ -297,6 +329,14 @@ public class MvcTemplateTest : LoggedTest
     [InlineData("IndividualB2C", new[] { ArgConstants.UseProgramMain, ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite })]
     public Task MvcTemplate_IdentityWeb_IndividualB2C_BuildsAndPublishes(string auth, string[] args) => MvcTemplateBuildsAndPublishes(auth: auth, args: args);
 
+    [ConditionalTheory]
+    [SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/28090", Queues = HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)]
+    [InlineData("IndividualB2C", null)]
+    [InlineData("IndividualB2C", new[] { ArgConstants.UseProgramMain, ArgConstants.NoHttps })]
+    [InlineData("IndividualB2C", new[] { ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite, ArgConstants.NoHttps })]
+    [InlineData("IndividualB2C", new[] { ArgConstants.UseProgramMain, ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite, ArgConstants.NoHttps })]
+    public Task MvcTemplate_IdentityWeb_IndividualB2C_NoHttps_BuildsAndPublishes(string auth, string[] args) => MvcTemplateBuildsAndPublishes(auth: auth, args: args);
+
     [ConditionalTheory]
     [SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/28090", Queues = HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)]
     [InlineData("SingleOrg", null)]
@@ -307,6 +347,16 @@ public class MvcTemplateTest : LoggedTest
     [InlineData("SingleOrg", new[] { ArgConstants.UseProgramMain, ArgConstants.CallsGraph })]
     public Task MvcTemplate_IdentityWeb_SingleOrg_BuildsAndPublishes(string auth, string[] args) => MvcTemplateBuildsAndPublishes(auth: auth, args: args);
 
+    [ConditionalTheory]
+    [SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/28090", Queues = HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)]
+    [InlineData("SingleOrg", null)]
+    [InlineData("SingleOrg", new[] { ArgConstants.UseProgramMain, ArgConstants.NoHttps })]
+    [InlineData("SingleOrg", new[] { ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite, ArgConstants.NoHttps })]
+    [InlineData("SingleOrg", new[] { ArgConstants.UseProgramMain, ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite, ArgConstants.NoHttps })]
+    [InlineData("SingleOrg", new[] { ArgConstants.CallsGraph, ArgConstants.NoHttps })]
+    [InlineData("SingleOrg", new[] { ArgConstants.UseProgramMain, ArgConstants.CallsGraph, ArgConstants.NoHttps })]
+    public Task MvcTemplate_IdentityWeb_SingleOrg_NoHttps_BuildsAndPublishes(string auth, string[] args) => MvcTemplateBuildsAndPublishes(auth: auth, args: args);
+
     private async Task<Project> MvcTemplateBuildsAndPublishes(string auth, string[] args)
     {
         var project = await ProjectFactory.CreateProject(Output);
@@ -314,6 +364,10 @@ public class MvcTemplateTest : LoggedTest
         var createResult = await project.RunDotNetNewAsync("mvc", auth: auth, args: args);
         Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", project, createResult));
 
+        // Identity Web auth requires https and thus ignores the --no-https option if passed so there should never be an 'http' profile
+        var expectedLaunchProfileNames = new[] { "https", "IIS Express" };
+        await project.VerifyLaunchSettings(expectedLaunchProfileNames);
+
         // Verify building in debug works
         var buildResult = await project.RunDotNetBuildAsync();
         Assert.True(0 == buildResult.ExitCode, ErrorMessages.GetFailedProcessMessage("build", project, buildResult));

+ 65 - 24
src/ProjectTemplates/test/RazorPagesTemplateTest.cs → src/ProjectTemplates/test/Templates.Mvc.Tests/RazorPagesTemplateTest.cs

@@ -10,7 +10,7 @@ using Templates.Test.Helpers;
 using Xunit;
 using Xunit.Abstractions;
 
-namespace Templates.Test;
+namespace Templates.Mvc.Test;
 
 public class RazorPagesTemplateTest : LoggedTest
 {
@@ -36,16 +36,29 @@ public class RazorPagesTemplateTest : LoggedTest
 
     [ConditionalTheory]
     [SkipOnHelix("Cert failure, https://github.com/dotnet/aspnetcore/issues/28090", Queues = "All.OSX;" + HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)]
-    [InlineData(true)]
-    [InlineData(false)]
-    public async Task RazorPagesTemplate_NoAuth(bool useProgramMain)
+    [InlineData(true, false)]
+    [InlineData(true, true)]
+    [InlineData(false, false)]
+    [InlineData(false, true)]
+    public async Task RazorPagesTemplate_NoAuth(bool useProgramMain, bool noHttps)
     {
         var project = await ProjectFactory.CreateProject(Output);
 
-        var args = useProgramMain ? new[] { ArgConstants.UseProgramMain } : null;
+        var args = useProgramMain
+            ? noHttps
+                ? new[] { ArgConstants.UseProgramMain, ArgConstants.NoHttps }
+                : new[] { ArgConstants.UseProgramMain }
+            : noHttps
+                ? new[] { ArgConstants.NoHttps }
+                : null;
         var createResult = await project.RunDotNetNewAsync("razor", args: args);
         Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("razor", project, createResult));
 
+        var expectedLaunchProfileNames = noHttps
+            ? new[] { "http", "IIS Express" }
+            : new[] { "http", "https", "IIS Express" };
+        await project.VerifyLaunchSettings(expectedLaunchProfileNames);
+
         var projectFileContents = ReadFile(project.TemplateOutputDir, $"{project.ProjectName}.csproj");
         Assert.DoesNotContain(".db", projectFileContents);
         Assert.DoesNotContain("Microsoft.EntityFrameworkCore.Tools", projectFileContents);
@@ -68,7 +81,7 @@ public class RazorPagesTemplateTest : LoggedTest
                 new Page
                 {
                     Url = PageUrls.HomeUrl,
-                    Links = new string[] {
+                    Links = new [] {
                         PageUrls.HomeUrl,
                         PageUrls.HomeUrl,
                         PageUrls.PrivacyUrl,
@@ -79,7 +92,7 @@ public class RazorPagesTemplateTest : LoggedTest
                 new Page
                 {
                     Url = PageUrls.PrivacyUrl,
-                    Links = new string[] {
+                    Links = new [] {
                         PageUrls.HomeUrl,
                         PageUrls.HomeUrl,
                         PageUrls.PrivacyUrl,
@@ -107,26 +120,42 @@ public class RazorPagesTemplateTest : LoggedTest
     }
 
     [ConditionalTheory]
-    [InlineData(false)]
-    [InlineData(true)]
+    [InlineData(false, false)]
+    [InlineData(false, true)]
+    [InlineData(true, false)]
+    [InlineData(true, true)]
     [SkipOnHelix("Cert failure, https://github.com/dotnet/aspnetcore/issues/28090", Queues = "All.OSX;" + HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64 + HelixConstants.DebianAmd64)]
     [OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX, SkipReason = "No LocalDb on non-Windows")]
-    public Task RazorPagesTemplate_IndividualAuth_LocalDb(bool useProgramMain) => RazorPagesTemplate_IndividualAuth_Core(useLocalDB: true, useProgramMain);
+    public Task RazorPagesTemplate_IndividualAuth_LocalDb(bool useProgramMain, bool noHttps) => RazorPagesTemplate_IndividualAuth_Core(useLocalDB: true, useProgramMain, noHttps);
 
     [ConditionalTheory]
-    [InlineData(false)]
-    [InlineData(true)]
+    [InlineData(false, false)]
+    [InlineData(false, true)]
+    [InlineData(true, false)]
+    [InlineData(true, true)]
     [SkipOnHelix("Cert failure, https://github.com/dotnet/aspnetcore/issues/28090", Queues = "All.OSX;" + HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64 + HelixConstants.DebianAmd64)]
-    public Task RazorPagesTemplate_IndividualAuth(bool useProgramMain) => RazorPagesTemplate_IndividualAuth_Core(useLocalDB: false, useProgramMain);
+    public Task RazorPagesTemplate_IndividualAuth(bool useProgramMain, bool noHttps) => RazorPagesTemplate_IndividualAuth_Core(useLocalDB: false, useProgramMain, noHttps);
 
-    private async Task RazorPagesTemplate_IndividualAuth_Core(bool useLocalDB, bool useProgramMain)
+    private async Task RazorPagesTemplate_IndividualAuth_Core(bool useLocalDB, bool useProgramMain, bool noHttps)
     {
         var project = await ProjectFactory.CreateProject(Output);
 
-        var args = useProgramMain ? new[] { ArgConstants.UseProgramMain } : null;
+        var args = useProgramMain
+            ? noHttps
+                ? new[] { ArgConstants.UseProgramMain, ArgConstants.NoHttps }
+                : new[] { ArgConstants.UseProgramMain }
+            : noHttps
+                ? new[] { ArgConstants.NoHttps }
+                : null;
         var createResult = await project.RunDotNetNewAsync("razor", auth: "Individual", useLocalDB: useLocalDB, args: args);
         Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", project, createResult));
 
+        // Individual auth supports no https OK
+        var expectedLaunchProfileNames = noHttps
+            ? new[] { "http", "IIS Express" }
+            : new[] { "http", "https", "IIS Express" };
+        await project.VerifyLaunchSettings(expectedLaunchProfileNames);
+
         var projectFileContents = ReadFile(project.TemplateOutputDir, $"{project.ProjectName}.csproj");
         if (!useLocalDB)
         {
@@ -152,7 +181,7 @@ public class RazorPagesTemplateTest : LoggedTest
                 new Page
                 {
                     Url = PageUrls.ForgotPassword,
-                    Links = new string [] {
+                    Links = new [] {
                         PageUrls.HomeUrl,
                         PageUrls.HomeUrl,
                         PageUrls.PrivacyUrl,
@@ -164,7 +193,7 @@ public class RazorPagesTemplateTest : LoggedTest
                 new Page
                 {
                     Url = PageUrls.HomeUrl,
-                    Links = new string[] {
+                    Links = new [] {
                         PageUrls.HomeUrl,
                         PageUrls.HomeUrl,
                         PageUrls.PrivacyUrl,
@@ -177,7 +206,7 @@ public class RazorPagesTemplateTest : LoggedTest
                 new Page
                 {
                     Url = PageUrls.PrivacyUrl,
-                    Links = new string[] {
+                    Links = new [] {
                         PageUrls.HomeUrl,
                         PageUrls.HomeUrl,
                         PageUrls.PrivacyUrl,
@@ -189,7 +218,7 @@ public class RazorPagesTemplateTest : LoggedTest
                 new Page
                 {
                     Url = PageUrls.LoginUrl,
-                    Links = new string[] {
+                    Links = new [] {
                         PageUrls.HomeUrl,
                         PageUrls.HomeUrl,
                         PageUrls.PrivacyUrl,
@@ -204,7 +233,7 @@ public class RazorPagesTemplateTest : LoggedTest
                 new Page
                 {
                     Url = PageUrls.RegisterUrl,
-                    Links = new string [] {
+                    Links = new [] {
                         PageUrls.HomeUrl,
                         PageUrls.HomeUrl,
                         PageUrls.PrivacyUrl,
@@ -239,30 +268,42 @@ public class RazorPagesTemplateTest : LoggedTest
     [SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/28090", Queues = HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)]
     [InlineData("IndividualB2C", null)]
     [InlineData("IndividualB2C", new[] { ArgConstants.UseProgramMain })]
+    [InlineData("IndividualB2C", new[] { ArgConstants.UseProgramMain, ArgConstants.NoHttps })]
     [InlineData("IndividualB2C", new[] { ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite })]
+    [InlineData("IndividualB2C", new[] { ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite, ArgConstants.NoHttps })]
     [InlineData("IndividualB2C", new[] { ArgConstants.UseProgramMain, ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite })]
-    public Task RazorPagesTemplate_IdentityWeb_IndividualB2C_BuildsAndPublishes(string auth, string[] args) => BuildAndPublishRazorPagesTemplate(auth: auth, args: args);
+    [InlineData("IndividualB2C", new[] { ArgConstants.UseProgramMain, ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite, ArgConstants.NoHttps })]
+    public Task RazorPagesTemplate_IdentityWeb_IndividualB2C_BuildsAndPublishes(string auth, string[] args) => BuildAndPublishRazorPagesTemplateIdentityWeb(auth: auth, args: args);
 
     [ConditionalTheory]
     [SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/28090", Queues = HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)]
     [InlineData("SingleOrg", null)]
     [InlineData("SingleOrg", new[] { ArgConstants.UseProgramMain })]
+    [InlineData("SingleOrg", new[] { ArgConstants.UseProgramMain, ArgConstants.NoHttps })]
     [InlineData("SingleOrg", new[] { ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite })]
+    [InlineData("SingleOrg", new[] { ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite, ArgConstants.NoHttps })]
     [InlineData("SingleOrg", new[] { ArgConstants.UseProgramMain, ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite })]
-    public Task RazorPagesTemplate_IdentityWeb_SingleOrg_BuildsAndPublishes(string auth, string[] args) => BuildAndPublishRazorPagesTemplate(auth: auth, args: args);
+    [InlineData("SingleOrg", new[] { ArgConstants.UseProgramMain, ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite, ArgConstants.NoHttps })]
+    public Task RazorPagesTemplate_IdentityWeb_SingleOrg_BuildsAndPublishes(string auth, string[] args) => BuildAndPublishRazorPagesTemplateIdentityWeb(auth: auth, args: args);
 
     [ConditionalTheory]
     [InlineData("SingleOrg", new[] { ArgConstants.CallsGraph })]
     [InlineData("SingleOrg", new[] { ArgConstants.UseProgramMain, ArgConstants.CallsGraph })]
-    public Task RazorPagesTemplate_IdentityWeb_SingleOrg_CallsGraph_BuildsAndPublishes(string auth, string[] args) => BuildAndPublishRazorPagesTemplate(auth: auth, args: args);
+    [InlineData("SingleOrg", new[] { ArgConstants.CallsGraph, ArgConstants.NoHttps })]
+    [InlineData("SingleOrg", new[] { ArgConstants.UseProgramMain, ArgConstants.CallsGraph, ArgConstants.NoHttps })]
+    public Task RazorPagesTemplate_IdentityWeb_SingleOrg_CallsGraph_BuildsAndPublishes(string auth, string[] args) => BuildAndPublishRazorPagesTemplateIdentityWeb(auth: auth, args: args);
 
-    private async Task<Project> BuildAndPublishRazorPagesTemplate(string auth, string[] args)
+    private async Task<Project> BuildAndPublishRazorPagesTemplateIdentityWeb(string auth, string[] args)
     {
         var project = await ProjectFactory.CreateProject(Output);
 
         var createResult = await project.RunDotNetNewAsync("razor", auth: auth, args: args);
         Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", project, createResult));
 
+        // Identity Web auth requires https and thus ignores the --no-https option if passed so there should never be an 'http' profile
+        var expectedLaunchProfileNames = new[] { "https", "IIS Express" };
+        await project.VerifyLaunchSettings(expectedLaunchProfileNames);
+
         // Verify building in debug works
         var buildResult = await project.RunDotNetBuildAsync();
         Assert.True(0 == buildResult.ExitCode, ErrorMessages.GetFailedProcessMessage("build", project, buildResult));

+ 68 - 0
src/ProjectTemplates/test/Templates.Mvc.Tests/Templates.Mvc.Tests.csproj

@@ -0,0 +1,68 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <!-- Shared testing infrastructure for running E2E tests using selenium -->
+  <Import Condition="'$(SkipTestBuild)' != 'true'" Project="$(SharedSourceRoot)E2ETesting\E2ETesting.props" />
+
+  <PropertyGroup>
+    <TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
+    <TestGroupName>ProjectTemplates.E2ETests</TestGroupName>
+    <DefineConstants>$(DefineConstants);XPLAT</DefineConstants>
+
+    <RunTemplateTests Condition="'$(RunTemplateTests)' == ''">true</RunTemplateTests>
+    <SkipTests Condition="'$(RunTemplateTests)' != 'true'">true</SkipTests>
+
+    <BaseOutputPath />
+    <OutputPath />
+
+    <!-- Properties that affect test runs -->
+    <!-- TestTemplateCreationFolder is the folder where the templates will be created. Will point out to $(OutputDir)$(TestTemplateCreationFolder) -->
+    <TestTemplateCreationFolder>TestTemplates\</TestTemplateCreationFolder>
+    <TestPackageRestorePath>$([MSBuild]::EnsureTrailingSlash('$(RepoRoot)'))obj\template-restore\</TestPackageRestorePath>
+    <TestDependsOnAspNetPackages>true</TestDependsOnAspNetPackages>
+    <SkipHelixQueues>
+      $(HelixQueueArmDebian11);
+    </SkipHelixQueues>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <Compile Include="$(SharedSourceRoot)CommandLineUtils\**\*.cs" />
+    <Compile Include="$(SharedSourceRoot)CertificateGeneration\**\*.cs" LinkBase="shared\CertificateGeneration" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <Compile Include="$(SharedSourceRoot)Process\*.cs" LinkBase="shared\Process" />
+    <Compile Include="..\..\Shared\**" LinkBase="Helpers" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <!-- We need the actual templates in the output directory for tests to verify file encodings. -->
+    <Content Include="..\..\Web.ProjectTemplates\**" LinkBase="Assets\Web.ProjectTemplates" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <HelixContent Include="$(OutputPath)$(TargetFramework)Assets\**" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <Reference Include="AngleSharp" />
+    <Reference Include="System.Net.Http" />
+    <ProjectReference Include="$(RepoRoot)src\Framework\App.Runtime\src\Microsoft.AspNetCore.App.Runtime.csproj"
+      Private="false"
+      ReferenceOutputAssembly="false"
+      SkipGetTargetFrameworkProperties="true" />
+
+    <ProjectReference Include="$(RepoRoot)src\Hosting\Server.IntegrationTesting\src\Microsoft.AspNetCore.Server.IntegrationTesting.csproj" />
+    <ProjectReference Include="$(RepoRoot)src\Shared\BrowserTesting\src\Microsoft.AspNetCore.BrowserTesting.csproj" />
+    <ProjectReference Include="../../Web.ProjectTemplates/Microsoft.DotNet.Web.ProjectTemplates.csproj"
+      Private="false"
+      ReferenceOutputAssembly="false"
+      SkipGetTargetFrameworkProperties="true" />
+  </ItemGroup>
+
+  <PropertyGroup>
+    <PreserveExistingLogsInOutput Condition="'$(PreserveExistingLogsInOutput)' == '' AND '$(ContinuousIntegrationBuild)' == 'true'">true</PreserveExistingLogsInOutput>
+    <PreserveExistingLogsInOutput Condition="'$(PreserveExistingLogsInOutput)' == ''">false</PreserveExistingLogsInOutput>
+  </PropertyGroup>
+
+  <!-- Shared testing infrastructure for running E2E tests -->
+  <Import Condition="'$(SkipTestBuild)' != 'true'" Project="..\..\TestInfrastructure\PrepareForTest.targets" />
+</Project>

+ 289 - 0
src/ProjectTemplates/test/Templates.Mvc.Tests/WebApiTemplateTest.cs

@@ -0,0 +1,289 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Security.Principal;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Testing;
+using Newtonsoft.Json;
+using Templates.Test.Helpers;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace Templates.Mvc.Test;
+
+public class WebApiTemplateTest : LoggedTest
+{
+    public WebApiTemplateTest(ProjectFactoryFixture factoryFixture)
+    {
+        FactoryFixture = factoryFixture;
+    }
+
+    public ProjectFactoryFixture FactoryFixture { get; }
+
+    private ITestOutputHelper _output;
+    public ITestOutputHelper Output
+    {
+        get
+        {
+            if (_output == null)
+            {
+                _output = new TestOutputLogger(Logger);
+            }
+            return _output;
+        }
+    }
+
+    [ConditionalTheory]
+    [SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/28090", Queues = HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)]
+    [InlineData("IndividualB2C", null)]
+    [InlineData("IndividualB2C", new [] { ArgConstants.UseMinimalApis })]
+    [InlineData("IndividualB2C", new [] { ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite })]
+    [InlineData("IndividualB2C", new [] { ArgConstants.UseMinimalApis, ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite })]
+    public Task WebApiTemplateCSharp_IdentityWeb_IndividualB2C_BuildsAndPublishes(string auth, string[] args) => PublishAndBuildWebApiTemplate(languageOverride: null, auth: auth, args: args);
+
+    [ConditionalTheory]
+    [SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/28090", Queues = HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)]
+    [InlineData("IndividualB2C", null)]
+    [InlineData("IndividualB2C", new [] { ArgConstants.UseMinimalApis, ArgConstants.NoHttps })]
+    [InlineData("IndividualB2C", new [] { ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite, ArgConstants.NoHttps })]
+    [InlineData("IndividualB2C", new [] { ArgConstants.UseMinimalApis, ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite, ArgConstants.NoHttps })]
+    public Task WebApiTemplateCSharp_IdentityWeb_IndividualB2C_NoHttps_BuildsAndPublishes(string auth, string[] args) => PublishAndBuildWebApiTemplate(languageOverride: null, auth: auth, args: args);
+
+    [ConditionalTheory]
+    [SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/28090", Queues = HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)]
+    [InlineData("IndividualB2C", null)]
+    [InlineData("IndividualB2C", new [] { ArgConstants.UseProgramMain })]
+    [InlineData("IndividualB2C", new [] { ArgConstants.UseProgramMain, ArgConstants.UseMinimalApis })]
+    [InlineData("IndividualB2C", new [] { ArgConstants.UseProgramMain, ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite })]
+    [InlineData("IndividualB2C", new [] { ArgConstants.UseProgramMain, ArgConstants.UseMinimalApis, ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite })]
+    public Task WebApiTemplateCSharp_IdentityWeb_IndividualB2C_ProgramMain_BuildsAndPublishes(string auth, string[] args) => PublishAndBuildWebApiTemplate(languageOverride: null, auth: auth, args: args);
+
+    [ConditionalTheory]
+    [SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/28090", Queues = HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)]
+    [InlineData("IndividualB2C", null)]
+    [InlineData("IndividualB2C", new [] { ArgConstants.UseProgramMain, ArgConstants.NoHttps })]
+    [InlineData("IndividualB2C", new [] { ArgConstants.UseProgramMain, ArgConstants.UseMinimalApis, ArgConstants.NoHttps })]
+    [InlineData("IndividualB2C", new [] { ArgConstants.UseProgramMain, ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite, ArgConstants.NoHttps })]
+    [InlineData("IndividualB2C", new [] { ArgConstants.UseProgramMain, ArgConstants.UseMinimalApis, ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite, ArgConstants.NoHttps })]
+    public Task WebApiTemplateCSharp_IdentityWeb_IndividualB2C_ProgramMain_NoHttps_BuildsAndPublishes(string auth, string[] args) => PublishAndBuildWebApiTemplate(languageOverride: null, auth: auth, args: args);
+
+    [ConditionalTheory]
+    [SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/28090", Queues = HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)]
+    [InlineData("SingleOrg", null)]
+    [InlineData("SingleOrg", new [] { ArgConstants.UseMinimalApis })]
+    [InlineData("SingleOrg", new [] { ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite })]
+    [InlineData("SingleOrg", new [] { ArgConstants.UseMinimalApis, ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite })]
+    [InlineData("SingleOrg", new [] { ArgConstants.CallsGraph })]
+    [InlineData("SingleOrg", new [] { ArgConstants.UseMinimalApis, ArgConstants.CallsGraph })]
+    public Task WebApiTemplateCSharp_IdentityWeb_SingleOrg_BuildsAndPublishes(string auth, string[] args) => PublishAndBuildWebApiTemplate(languageOverride: null, auth: auth, args: args);
+
+    [ConditionalTheory]
+    [SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/28090", Queues = HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)]
+    [InlineData("SingleOrg", null)]
+    [InlineData("SingleOrg", new [] { ArgConstants.UseMinimalApis, ArgConstants.NoHttps })]
+    [InlineData("SingleOrg", new [] { ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite, ArgConstants.NoHttps })]
+    [InlineData("SingleOrg", new [] { ArgConstants.UseMinimalApis, ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite, ArgConstants.NoHttps })]
+    [InlineData("SingleOrg", new [] { ArgConstants.CallsGraph, ArgConstants.NoHttps })]
+    [InlineData("SingleOrg", new [] { ArgConstants.UseMinimalApis, ArgConstants.CallsGraph, ArgConstants.NoHttps })]
+    public Task WebApiTemplateCSharp_IdentityWeb_SingleOrg_NoHttps_BuildsAndPublishes(string auth, string[] args) => PublishAndBuildWebApiTemplate(languageOverride: null, auth: auth, args: args);
+
+    [ConditionalTheory]
+    [SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/28090", Queues = HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)]
+    [InlineData("SingleOrg", null)]
+    [InlineData("SingleOrg", new [] { ArgConstants.UseProgramMain })]
+    [InlineData("SingleOrg", new [] { ArgConstants.UseProgramMain, ArgConstants.UseMinimalApis })]
+    [InlineData("SingleOrg", new [] { ArgConstants.UseProgramMain, ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite })]
+    [InlineData("SingleOrg", new [] { ArgConstants.UseProgramMain, ArgConstants.UseMinimalApis, ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite })]
+    [InlineData("SingleOrg", new [] { ArgConstants.UseProgramMain, ArgConstants.CallsGraph })]
+    [InlineData("SingleOrg", new [] { ArgConstants.UseProgramMain, ArgConstants.UseMinimalApis, ArgConstants.CallsGraph })]
+    public Task WebApiTemplateCSharp_IdentityWeb_SingleOrg_ProgramMain_BuildsAndPublishes(string auth, string[] args) => PublishAndBuildWebApiTemplate(languageOverride: null, auth: auth, args: args);
+
+    [ConditionalTheory]
+    [SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/28090", Queues = HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)]
+    [InlineData("SingleOrg", null)]
+    [InlineData("SingleOrg", new [] { ArgConstants.UseProgramMain, ArgConstants.NoHttps })]
+    [InlineData("SingleOrg", new [] { ArgConstants.UseProgramMain, ArgConstants.UseMinimalApis, ArgConstants.NoHttps })]
+    [InlineData("SingleOrg", new [] { ArgConstants.UseProgramMain, ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite, ArgConstants.NoHttps })]
+    [InlineData("SingleOrg", new [] { ArgConstants.UseProgramMain, ArgConstants.UseMinimalApis, ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite, ArgConstants.NoHttps })]
+    [InlineData("SingleOrg", new [] { ArgConstants.UseProgramMain, ArgConstants.CallsGraph, ArgConstants.NoHttps })]
+    [InlineData("SingleOrg", new [] { ArgConstants.UseProgramMain, ArgConstants.UseMinimalApis, ArgConstants.CallsGraph, ArgConstants.NoHttps })]
+    public Task WebApiTemplateCSharp_IdentityWeb_SingleOrg_ProgramMain_NoHttps_BuildsAndPublishes(string auth, string[] args) => PublishAndBuildWebApiTemplate(languageOverride: null, auth: auth, args: args);
+
+    [Fact]
+    public Task WebApiTemplateFSharp() => WebApiTemplateCore(languageOverride: "F#");
+
+    [Fact]
+    public Task WebApiTemplateNoHttpsFSharp() => WebApiTemplateCore(languageOverride: "F#", args: new[] { ArgConstants.NoHttps } );
+
+    [ConditionalFact]
+    [SkipOnHelix("Cert failure, https://github.com/dotnet/aspnetcore/issues/28090", Queues = "All.OSX;" + HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)]
+    public Task WebApiTemplateCSharp() => WebApiTemplateCore(languageOverride: null);
+
+    [ConditionalFact]
+    [SkipOnHelix("Cert failure, https://github.com/dotnet/aspnetcore/issues/28090", Queues = "All.OSX;" + HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)]
+    public Task WebApiTemplateNoHttpsCSharp() => WebApiTemplateCore(languageOverride: null, new[] { ArgConstants.NoHttps });
+
+    [ConditionalFact]
+    [SkipOnHelix("Cert failure, https://github.com/dotnet/aspnetcore/issues/28090", Queues = "All.OSX;" + HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)]
+    public Task WebApiTemplateProgramMainCSharp() => WebApiTemplateCore(languageOverride: null, args: new[] { ArgConstants.UseProgramMain });
+
+    [ConditionalFact]
+    [SkipOnHelix("Cert failure, https://github.com/dotnet/aspnetcore/issues/28090", Queues = "All.OSX;" + HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)]
+    public Task WebApiTemplateProgramMainNoHttpsCSharp() => WebApiTemplateCore(languageOverride: null, args: new[] { ArgConstants.UseProgramMain, ArgConstants.NoHttps });
+
+    [ConditionalFact]
+    [SkipOnHelix("Cert failure, https://github.com/dotnet/aspnetcore/issues/28090", Queues = "All.OSX;" + HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)]
+    public Task WebApiTemplateMinimalApisCSharp() => WebApiTemplateCore(languageOverride: null, args: new[] { ArgConstants.UseMinimalApis });
+
+    [ConditionalFact]
+    [SkipOnHelix("Cert failure, https://github.com/dotnet/aspnetcore/issues/28090", Queues = "All.OSX;" + HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)]
+    public Task WebApiTemplateMinimalApisNoHttpsCSharp() => WebApiTemplateCore(languageOverride: null, args: new[] { ArgConstants.UseMinimalApis, ArgConstants.NoHttps });
+
+    [ConditionalFact]
+    [SkipOnHelix("Cert failure, https://github.com/dotnet/aspnetcore/issues/28090", Queues = "All.OSX;" + HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)]
+    public Task WebApiTemplateProgramMainMinimalApisCSharp() => WebApiTemplateCore(languageOverride: null, args: new[] { ArgConstants.UseProgramMain, ArgConstants.UseMinimalApis });
+
+    [ConditionalFact]
+    [SkipOnHelix("Cert failure, https://github.com/dotnet/aspnetcore/issues/28090", Queues = "All.OSX;" + HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)]
+    public Task WebApiTemplateProgramMainMinimalApisNoHttpsCSharp() => WebApiTemplateCore(languageOverride: null, args: new[] { ArgConstants.UseProgramMain, ArgConstants.UseMinimalApis, ArgConstants.NoHttps });
+
+    [ConditionalTheory]
+    [InlineData(false, false)]
+    [InlineData(false, true)]
+    [InlineData(true, true)]
+    [InlineData(true, false)]
+    [SkipOnHelix("Cert failure, https://github.com/dotnet/aspnetcore/issues/28090", Queues = "All.OSX;" + HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)]
+    public async Task WebApiTemplateCSharp_WithoutOpenAPI(bool useProgramMain, bool useMinimalApis)
+    {
+        var project = await FactoryFixture.CreateProject(Output);
+
+        var args = useProgramMain
+            ? useMinimalApis
+                ? new[] { ArgConstants.UseProgramMain, ArgConstants.UseMinimalApis, ArgConstants.NoOpenApi }
+                : new[] { ArgConstants.UseProgramMain, ArgConstants.NoOpenApi }
+            : useMinimalApis
+                ? new[] { ArgConstants.UseMinimalApis, ArgConstants.NoOpenApi }
+                : new[] { ArgConstants.NoOpenApi };
+        var createResult = await project.RunDotNetNewAsync("webapi", args: args);
+        Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", project, createResult));
+
+        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.AssertNotFound("swagger");
+    }
+
+    [ConditionalTheory]
+    [InlineData(false, false)]
+    [InlineData(false, true)]
+    [InlineData(true, true)]
+    [InlineData(true, false)]
+    [SkipOnHelix("Cert failure, https://github.com/dotnet/aspnetcore/issues/28090", Queues = "All.OSX;" + HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)]
+    public async Task WebApiTemplateCSharpNoHttps_WithoutOpenAPI(bool useProgramMain, bool useMinimalApis)
+    {
+        var project = await FactoryFixture.CreateProject(Output);
+
+        var args = useProgramMain
+            ? useMinimalApis
+                ? new[] { ArgConstants.UseProgramMain, ArgConstants.UseMinimalApis, ArgConstants.NoOpenApi, ArgConstants.NoHttps }
+                : new[] { ArgConstants.UseProgramMain, ArgConstants.NoOpenApi, ArgConstants.NoHttps }
+            : useMinimalApis
+                ? new[] { ArgConstants.UseMinimalApis, ArgConstants.NoOpenApi, ArgConstants.NoHttps }
+                : new[] { ArgConstants.NoOpenApi, ArgConstants.NoHttps };
+        var createResult = await project.RunDotNetNewAsync("webapi", args: args);
+        Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", project, createResult));
+
+        var noHttps = args.Contains(ArgConstants.NoHttps);
+        var expectedLaunchProfileNames = noHttps
+            ? new[] { "http", "IIS Express" }
+            : new[] { "http", "https", "IIS Express" };
+        await project.VerifyLaunchSettings(expectedLaunchProfileNames);
+
+        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.AssertNotFound("swagger");
+    }
+
+    private async Task<Project> PublishAndBuildWebApiTemplate(string languageOverride, string auth, string[] args = null)
+    {
+        var project = await FactoryFixture.CreateProject(Output);
+
+        var createResult = await project.RunDotNetNewAsync("webapi", language: languageOverride, auth: auth, args: args);
+        Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", project, createResult));
+
+        // External auth mechanisms require https to work and thus don't honor the --no-https flag
+        var requiresHttps = string.Equals(auth, "IndividualB2C", StringComparison.OrdinalIgnoreCase)
+                            || string.Equals(auth, "SingleOrg", StringComparison.OrdinalIgnoreCase);
+        var noHttps = args?.Contains(ArgConstants.NoHttps) ?? false;
+        var expectedLaunchProfileNames = requiresHttps
+            ? new[] { "https", "IIS Express" }
+            : noHttps
+                ? new[] { "http", "IIS Express" }
+                : new[] { "http", "https", "IIS Express" };
+        await project.VerifyLaunchSettings(expectedLaunchProfileNames);
+
+        // Avoid the F# compiler. See https://github.com/dotnet/aspnetcore/issues/14022
+        if (languageOverride != null)
+        {
+            return project;
+        }
+
+        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));
+
+        return project;
+    }
+
+    private async Task WebApiTemplateCore(string languageOverride, string[] args = null)
+    {
+        var project = await PublishAndBuildWebApiTemplate(languageOverride, null, args);
+
+        // Avoid the F# compiler. See https://github.com/dotnet/aspnetcore/issues/14022
+        if (languageOverride != null)
+        {
+            return;
+        }
+
+        using (var aspNetProcess = project.StartBuiltProjectAsync())
+        {
+            Assert.False(
+                aspNetProcess.Process.HasExited,
+                ErrorMessages.GetFailedProcessMessageOrEmpty("Run built project", project, aspNetProcess.Process));
+
+            await aspNetProcess.AssertOk("weatherforecast");
+            await aspNetProcess.AssertOk("swagger");
+            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");
+            // Swagger is only available in Development
+            await aspNetProcess.AssertNotFound("swagger");
+            await aspNetProcess.AssertNotFound("/");
+        }
+    }
+}

+ 1 - 0
src/ProjectTemplates/test/Templates.Tests/.gitattributes

@@ -0,0 +1 @@
+.json diff

+ 0 - 0
src/ProjectTemplates/test/BaselineTest.cs → src/ProjectTemplates/test/Templates.Tests/BaselineTest.cs


+ 0 - 0
src/ProjectTemplates/test/ByteOrderMarkTest.cs → src/ProjectTemplates/test/Templates.Tests/ByteOrderMarkTest.cs


+ 26 - 0
src/ProjectTemplates/test/EmptyWebTemplateTest.cs → src/ProjectTemplates/test/Templates.Tests/EmptyWebTemplateTest.cs

@@ -38,6 +38,13 @@ public class EmptyWebTemplateTest : LoggedTest
         await EmtpyTemplateCore(languageOverride: null);
     }
 
+    [ConditionalFact]
+    [SkipOnHelix("Cert failure, https://github.com/dotnet/aspnetcore/issues/28090", Queues = "All.OSX;" + HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)]
+    public async Task EmptyWebTemplateNoHttpsCSharp()
+    {
+        await EmtpyTemplateCore(languageOverride: null, args: new[] { ArgConstants.NoHttps });
+    }
+
     [ConditionalFact]
     [SkipOnHelix("Cert failure, https://github.com/dotnet/aspnetcore/issues/28090", Queues = "All.OSX;" + HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)]
     public async Task EmptyWebTemplateProgramMainCSharp()
@@ -45,12 +52,25 @@ public class EmptyWebTemplateTest : LoggedTest
         await EmtpyTemplateCore(languageOverride: null, args: new[] { ArgConstants.UseProgramMain });
     }
 
+    [ConditionalFact]
+    [SkipOnHelix("Cert failure, https://github.com/dotnet/aspnetcore/issues/28090", Queues = "All.OSX;" + HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)]
+    public async Task EmptyWebTemplateProgramMainNoHttpsCSharp()
+    {
+        await EmtpyTemplateCore(languageOverride: null, args: new[] { ArgConstants.UseProgramMain, ArgConstants.NoHttps });
+    }
+
     [Fact]
     public async Task EmptyWebTemplateFSharp()
     {
         await EmtpyTemplateCore("F#");
     }
 
+    [Fact]
+    public async Task EmptyWebTemplateNoHttpsFSharp()
+    {
+        await EmtpyTemplateCore("F#", args: new[] { ArgConstants.NoHttps });
+    }
+
     private async Task EmtpyTemplateCore(string languageOverride, string[] args = null)
     {
         var project = await ProjectFactory.CreateProject(Output);
@@ -58,6 +78,12 @@ public class EmptyWebTemplateTest : LoggedTest
         var createResult = await project.RunDotNetNewAsync("web", args: args, language: languageOverride);
         Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", project, createResult));
 
+        var noHttps = args?.Contains(ArgConstants.NoHttps) ?? false;
+        var expectedLaunchProfileNames = noHttps
+            ? new[] { "http", "IIS Express" }
+            : new[] { "http", "https", "IIS Express" };
+        await project.VerifyLaunchSettings(expectedLaunchProfileNames);
+
         // Avoid the F# compiler. See https://github.com/dotnet/aspnetcore/issues/14022
         if (languageOverride != null)
         {

+ 3 - 0
src/ProjectTemplates/test/GrpcTemplateTest.cs → src/ProjectTemplates/test/Templates.Tests/GrpcTemplateTest.cs

@@ -48,6 +48,9 @@ public class GrpcTemplateTest : LoggedTest
         var createResult = await project.RunDotNetNewAsync("grpc", args: args);
         Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", project, createResult));
 
+        var expectedLaunchProfileNames = new[] { "http", "https" };
+        await project.VerifyLaunchSettings(expectedLaunchProfileNames);
+
         var publishResult = await project.RunDotNetPublishAsync();
         Assert.True(0 == publishResult.ExitCode, ErrorMessages.GetFailedProcessMessage("publish", project, publishResult));
 

+ 1 - 1
src/ProjectTemplates/test/IdentityUIPackageTest.cs → src/ProjectTemplates/test/Templates.Tests/IdentityUIPackageTest.cs

@@ -34,7 +34,7 @@ public class IdentityUIPackageTest : LoggedTest
         }
     }
 
-    public static string[] Bootstrap5ContentFiles { get; } = new string[]
+    public static string[] Bootstrap5ContentFiles { get; } = new []
     {
             "Identity/favicon.ico",
             "Identity/css/site.css",

+ 0 - 0
src/ProjectTemplates/test/ItemTemplateTests/BlazorServerTests.cs → src/ProjectTemplates/test/Templates.Tests/ItemTemplateTests/BlazorServerTests.cs


+ 0 - 0
src/ProjectTemplates/test/RazorClassLibraryTemplateTest.cs → src/ProjectTemplates/test/Templates.Tests/RazorClassLibraryTemplateTest.cs


+ 0 - 0
src/ProjectTemplates/test/SpaTemplatesTest.cs → src/ProjectTemplates/test/Templates.Tests/SpaTemplatesTest.cs


+ 8 - 8
src/ProjectTemplates/test/ProjectTemplates.Tests.csproj → src/ProjectTemplates/test/Templates.Tests/Templates.Tests.csproj

@@ -31,15 +31,15 @@
   <ItemGroup>
     <EmbeddedResource Include="template-baselines.json" />
     <Compile Include="$(SharedSourceRoot)Process\*.cs" LinkBase="shared\Process" />
-    <Compile Include="..\Shared\**" LinkBase="Helpers" />
+    <Compile Include="..\..\Shared\**" LinkBase="Helpers" />
   </ItemGroup>
 
   <ItemGroup>
     <!-- We need the actual templates in the output directory for tests to verify file encodings. -->
-    <Content Include="..\Web.ProjectTemplates\**" LinkBase="Assets\Web.ProjectTemplates" />
+    <Content Include="..\..\Web.ProjectTemplates\**" LinkBase="Assets\Web.ProjectTemplates" />
     <Content Include="$(RepoRoot)src\submodules\spa-templates\src\**" LinkBase="Assets\Web.Spa.ProjectTemplates" />
-    <Content Include="..\Web.ItemTemplates\**" LinkBase="Assets\Web.ItemTemplates" />
-    <Content Include="..\Web.Client.ItemTemplates\**" LinkBase="Assets\Web.Client.ItemTemplates" />
+    <Content Include="..\..\Web.ItemTemplates\**" LinkBase="Assets\Web.ItemTemplates" />
+    <Content Include="..\..\Web.Client.ItemTemplates\**" LinkBase="Assets\Web.Client.ItemTemplates" />
   </ItemGroup>
 
   <ItemGroup>
@@ -56,15 +56,15 @@
 
     <ProjectReference Include="$(RepoRoot)src\Hosting\Server.IntegrationTesting\src\Microsoft.AspNetCore.Server.IntegrationTesting.csproj" />
     <ProjectReference Include="$(RepoRoot)src\Shared\BrowserTesting\src\Microsoft.AspNetCore.BrowserTesting.csproj" />
-    <ProjectReference Include="../Web.Client.ItemTemplates/Microsoft.DotNet.Web.Client.ItemTemplates.csproj"
+    <ProjectReference Include="../../Web.Client.ItemTemplates/Microsoft.DotNet.Web.Client.ItemTemplates.csproj"
       Private="false"
       ReferenceOutputAssembly="false"
       SkipGetTargetFrameworkProperties="true" />
-    <ProjectReference Include="../Web.ItemTemplates/Microsoft.DotNet.Web.ItemTemplates.csproj"
+    <ProjectReference Include="../../Web.ItemTemplates/Microsoft.DotNet.Web.ItemTemplates.csproj"
       Private="false"
       ReferenceOutputAssembly="false"
       SkipGetTargetFrameworkProperties="true" />
-    <ProjectReference Include="../Web.ProjectTemplates/Microsoft.DotNet.Web.ProjectTemplates.csproj"
+    <ProjectReference Include="../../Web.ProjectTemplates/Microsoft.DotNet.Web.ProjectTemplates.csproj"
       Private="false"
       ReferenceOutputAssembly="false"
       SkipGetTargetFrameworkProperties="true" />
@@ -80,5 +80,5 @@
   </PropertyGroup>
 
   <!-- Shared testing infrastructure for running E2E tests -->
-  <Import Condition="'$(SkipTestBuild)' != 'true'" Project="..\TestInfrastructure\PrepareForTest.targets" />
+  <Import Condition="'$(SkipTestBuild)' != 'true'" Project="..\..\TestInfrastructure\PrepareForTest.targets" />
 </Project>

+ 1 - 1
src/ProjectTemplates/test/WorkerTemplateTest.cs → src/ProjectTemplates/test/Templates.Tests/WorkerTemplateTest.cs

@@ -33,7 +33,7 @@ public class WorkerTemplateTest : LoggedTest
     [ConditionalTheory]
     [OSSkipCondition(OperatingSystems.Linux, SkipReason = "https://github.com/dotnet/sdk/issues/12831")]
     [InlineData("C#", null)]
-    [InlineData("C#", new string[] { ArgConstants.UseProgramMain })]
+    [InlineData("C#", new [] { ArgConstants.UseProgramMain })]
     [InlineData("F#", null)]
     [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/25404")]
     public async Task WorkerTemplateAsync(string language, string[] args)

+ 0 - 0
src/ProjectTemplates/test/e2eTestSettings.ci.json → src/ProjectTemplates/test/Templates.Tests/e2eTestSettings.ci.json


+ 0 - 0
src/ProjectTemplates/test/e2eTestSettings.json → src/ProjectTemplates/test/Templates.Tests/e2eTestSettings.json


+ 0 - 0
src/ProjectTemplates/test/package.json → src/ProjectTemplates/test/Templates.Tests/package.json


+ 0 - 0
src/ProjectTemplates/test/template-baselines.json → src/ProjectTemplates/test/Templates.Tests/template-baselines.json


+ 0 - 0
src/ProjectTemplates/test/yarn.lock → src/ProjectTemplates/test/Templates.Tests/yarn.lock


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

@@ -1,182 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System;
-using System.Threading.Tasks;
-using Microsoft.AspNetCore.Testing;
-using Templates.Test.Helpers;
-using Xunit;
-using Xunit.Abstractions;
-
-namespace Templates.Test;
-
-public class WebApiTemplateTest : LoggedTest
-{
-    public WebApiTemplateTest(ProjectFactoryFixture factoryFixture)
-    {
-        FactoryFixture = factoryFixture;
-    }
-
-    public ProjectFactoryFixture FactoryFixture { get; }
-
-    private ITestOutputHelper _output;
-    public ITestOutputHelper Output
-    {
-        get
-        {
-            if (_output == null)
-            {
-                _output = new TestOutputLogger(Logger);
-            }
-            return _output;
-        }
-    }
-
-    [ConditionalTheory]
-    [SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/28090", Queues = HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)]
-    [InlineData("IndividualB2C", null)]
-    [InlineData("IndividualB2C", new string[] { ArgConstants.UseMinimalApis })]
-    [InlineData("IndividualB2C", new string[] { ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite })]
-    [InlineData("IndividualB2C", new string[] { ArgConstants.UseMinimalApis, ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite })]
-    public Task WebApiTemplateCSharp_IdentityWeb_IndividualB2C_BuildsAndPublishes(string auth, string[] args) => PublishAndBuildWebApiTemplate(languageOverride: null, auth: auth, args: args);
-
-    [ConditionalTheory]
-    [SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/28090", Queues = HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)]
-    [InlineData("IndividualB2C", null)]
-    [InlineData("IndividualB2C", new string[] { ArgConstants.UseProgramMain })]
-    [InlineData("IndividualB2C", new string[] { ArgConstants.UseProgramMain, ArgConstants.UseMinimalApis })]
-    [InlineData("IndividualB2C", new string[] { ArgConstants.UseProgramMain, ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite })]
-    [InlineData("IndividualB2C", new string[] { ArgConstants.UseProgramMain, ArgConstants.UseMinimalApis, ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite })]
-    public Task WebApiTemplateCSharp_IdentityWeb_IndividualB2C_ProgramMain_BuildsAndPublishes(string auth, string[] args) => PublishAndBuildWebApiTemplate(languageOverride: null, auth: auth, args: args);
-
-    [ConditionalTheory]
-    [SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/28090", Queues = HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)]
-    [InlineData("SingleOrg", null)]
-    [InlineData("SingleOrg", new string[] { ArgConstants.UseMinimalApis })]
-    [InlineData("SingleOrg", new string[] { ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite })]
-    [InlineData("SingleOrg", new string[] { ArgConstants.UseMinimalApis, ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite })]
-    [InlineData("SingleOrg", new string[] { ArgConstants.CallsGraph })]
-    [InlineData("SingleOrg", new string[] { ArgConstants.UseMinimalApis, ArgConstants.CallsGraph })]
-    public Task WebApiTemplateCSharp_IdentityWeb_SingleOrg_BuildsAndPublishes(string auth, string[] args) => PublishAndBuildWebApiTemplate(languageOverride: null, auth: auth, args: args);
-
-    [ConditionalTheory]
-    [SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/28090", Queues = HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)]
-    [InlineData("SingleOrg", null)]
-    [InlineData("SingleOrg", new string[] { ArgConstants.UseProgramMain })]
-    [InlineData("SingleOrg", new string[] { ArgConstants.UseProgramMain, ArgConstants.UseMinimalApis })]
-    [InlineData("SingleOrg", new string[] { ArgConstants.UseProgramMain, ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite })]
-    [InlineData("SingleOrg", new string[] { ArgConstants.UseProgramMain, ArgConstants.UseMinimalApis, ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite })]
-    [InlineData("SingleOrg", new string[] { ArgConstants.UseProgramMain, ArgConstants.CallsGraph })]
-    [InlineData("SingleOrg", new string[] { ArgConstants.UseProgramMain, ArgConstants.UseMinimalApis, ArgConstants.CallsGraph })]
-    public Task WebApiTemplateCSharp_IdentityWeb_SingleOrg_ProgramMain_BuildsAndPublishes(string auth, string[] args) => PublishAndBuildWebApiTemplate(languageOverride: null, auth: auth, args: args);
-
-    [Fact]
-    public Task WebApiTemplateFSharp() => WebApiTemplateCore(languageOverride: "F#");
-
-    [ConditionalFact]
-    [SkipOnHelix("Cert failure, https://github.com/dotnet/aspnetcore/issues/28090", Queues = "All.OSX;" + HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)]
-    public Task WebApiTemplateCSharp() => WebApiTemplateCore(languageOverride: null);
-
-    [ConditionalFact]
-    [SkipOnHelix("Cert failure, https://github.com/dotnet/aspnetcore/issues/28090", Queues = "All.OSX;" + HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)]
-    public Task WebApiTemplateProgramMainCSharp() => WebApiTemplateCore(languageOverride: null, args: new[] { ArgConstants.UseProgramMain });
-
-    [ConditionalFact]
-    [SkipOnHelix("Cert failure, https://github.com/dotnet/aspnetcore/issues/28090", Queues = "All.OSX;" + HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)]
-    public Task WebApiTemplateMinimalApisCSharp() => WebApiTemplateCore(languageOverride: null, args: new[] { ArgConstants.UseMinimalApis });
-
-    [ConditionalFact]
-    [SkipOnHelix("Cert failure, https://github.com/dotnet/aspnetcore/issues/28090", Queues = "All.OSX;" + HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)]
-    public Task WebApiTemplateProgramMainMinimalApisCSharp() => WebApiTemplateCore(languageOverride: null, args: new[] { ArgConstants.UseProgramMain, ArgConstants.UseMinimalApis });
-
-    [ConditionalTheory]
-    [InlineData(false, false)]
-    [InlineData(false, true)]
-    [InlineData(true, true)]
-    [InlineData(true, false)]
-    [SkipOnHelix("Cert failure, https://github.com/dotnet/aspnetcore/issues/28090", Queues = "All.OSX;" + HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)]
-    public async Task WebApiTemplateCSharp_WithoutOpenAPI(bool useProgramMain, bool useMinimalApis)
-    {
-        var project = await FactoryFixture.CreateProject(Output);
-
-        var args = useProgramMain
-            ? useMinimalApis
-                ? new[] { ArgConstants.UseProgramMain, ArgConstants.UseMinimalApis, ArgConstants.NoOpenApi }
-                : new[] { ArgConstants.UseProgramMain, ArgConstants.NoOpenApi }
-            : useMinimalApis
-                ? new[] { ArgConstants.UseMinimalApis, ArgConstants.NoOpenApi }
-                : new[] { ArgConstants.NoOpenApi };
-        var createResult = await project.RunDotNetNewAsync("webapi", args: args);
-        Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", project, createResult));
-
-        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.AssertNotFound("swagger");
-    }
-
-    private async Task<Project> PublishAndBuildWebApiTemplate(string languageOverride, string auth, string[] args = null)
-    {
-        var project = await FactoryFixture.CreateProject(Output);
-
-        var createResult = await project.RunDotNetNewAsync("webapi", language: languageOverride, auth: auth, args: args);
-        Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", project, createResult));
-
-        // Avoid the F# compiler. See https://github.com/dotnet/aspnetcore/issues/14022
-        if (languageOverride != null)
-        {
-            return project;
-        }
-
-        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));
-
-        return project;
-    }
-
-    private async Task WebApiTemplateCore(string languageOverride, string[] args = null)
-    {
-        var project = await PublishAndBuildWebApiTemplate(languageOverride, null, args);
-
-        // Avoid the F# compiler. See https://github.com/dotnet/aspnetcore/issues/14022
-        if (languageOverride != null)
-        {
-            return;
-        }
-
-        using (var aspNetProcess = project.StartBuiltProjectAsync())
-        {
-            Assert.False(
-                aspNetProcess.Process.HasExited,
-                ErrorMessages.GetFailedProcessMessageOrEmpty("Run built project", project, aspNetProcess.Process));
-
-            await aspNetProcess.AssertOk("weatherforecast");
-            await aspNetProcess.AssertOk("swagger");
-            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");
-            // Swagger is only available in Development
-            await aspNetProcess.AssertNotFound("swagger");
-            await aspNetProcess.AssertNotFound("/");
-        }
-    }
-}