diff --git a/eng/helix/content/RunTests/TestRunner.cs b/eng/helix/content/RunTests/TestRunner.cs index 6e1fe013fce3..dbc62d769bdf 100644 --- a/eng/helix/content/RunTests/TestRunner.cs +++ b/eng/helix/content/RunTests/TestRunner.cs @@ -64,6 +64,9 @@ public bool SetupEnvironment() File.Copy("default.runner.json", "xunit.runner.json"); } + DisplayContents(Path.Combine(Options.DotnetRoot, "host", "fxr")); + DisplayContents(Path.Combine(Options.DotnetRoot, "shared", "Microsoft.NETCore.App")); + return true; } catch (Exception e) diff --git a/src/ProjectTemplates/BlazorTemplates.Tests/AssemblyInfo.AssemblyFixtures.cs b/src/ProjectTemplates/BlazorTemplates.Tests/AssemblyInfo.AssemblyFixtures.cs index 3eb30b095263..7e6e39bbf8d2 100644 --- a/src/ProjectTemplates/BlazorTemplates.Tests/AssemblyInfo.AssemblyFixtures.cs +++ b/src/ProjectTemplates/BlazorTemplates.Tests/AssemblyInfo.AssemblyFixtures.cs @@ -7,5 +7,4 @@ using Templates.Test.Helpers; [assembly: AssemblyFixture(typeof(ProjectFactoryFixture))] -[assembly: AssemblyFixture(typeof(PlaywrightFixture))] diff --git a/src/ProjectTemplates/BlazorTemplates.Tests/BlazorServerTemplateTest.cs b/src/ProjectTemplates/BlazorTemplates.Tests/BlazorServerTemplateTest.cs index 24fe3fa75aa6..c6175650ab83 100644 --- a/src/ProjectTemplates/BlazorTemplates.Tests/BlazorServerTemplateTest.cs +++ b/src/ProjectTemplates/BlazorTemplates.Tests/BlazorServerTemplateTest.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.Linq; using System.Net; -using System.Runtime.InteropServices; using System.Threading.Tasks; using Microsoft.AspNetCore.BrowserTesting; using Microsoft.AspNetCore.Testing; @@ -19,60 +18,37 @@ namespace Templates.Test { public class BlazorServerTemplateTest : BlazorTemplateTest { - public BlazorServerTemplateTest(ProjectFactoryFixture projectFactory, PlaywrightFixture fixture, ITestOutputHelper output) - : base(fixture) + public BlazorServerTemplateTest(ProjectFactoryFixture projectFactory) + : base(projectFactory) { - ProjectFactory = projectFactory; ; - Output = output; - BrowserContextInfo = new ContextInformation(CreateFactory(output)); } - public ProjectFactoryFixture ProjectFactory { get; set; } - public ITestOutputHelper Output { get; } - public ContextInformation BrowserContextInfo { get; } - public Project Project { get; private set; } - + public override string ProjectType { get; } = "blazorserver"; [Theory] [InlineData(BrowserKind.Chromium)] [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/30761")] public async Task BlazorServerTemplateWorks_NoAuth(BrowserKind browserKind) { - // Additional arguments are needed. See: https://github.com/dotnet/aspnetcore/issues/24278 - Environment.SetEnvironmentVariable("EnableDefaultScopedCssItems", "true"); - - Project = await ProjectFactory.GetOrCreateProject("blazorservernoauth" + browserKind.ToString(), Output); - - var createResult = await Project.RunDotNetNewAsync("blazorserver"); - Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", Project, createResult)); - - var publishResult = await Project.RunDotNetPublishAsync(); - Assert.True(0 == publishResult.ExitCode, ErrorMessages.GetFailedProcessMessage("publish", Project, publishResult)); + var project = await CreateBuildPublishAsync("blazorservernoauth" + browserKind); - // 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)); - - await using var browser = Fixture.BrowserManager.IsAvailable(browserKind) ? - await Fixture.BrowserManager.GetBrowserInstance(browserKind, BrowserContextInfo) : + await using var browser = BrowserManager.IsAvailable(browserKind) ? + await BrowserManager.GetBrowserInstance(browserKind, BrowserContextInfo) : null; - using (var aspNetProcess = Project.StartBuiltProjectAsync()) + using (var aspNetProcess = project.StartBuiltProjectAsync()) { Assert.False( aspNetProcess.Process.HasExited, - ErrorMessages.GetFailedProcessMessageOrEmpty("Run built project", Project, aspNetProcess.Process)); + ErrorMessages.GetFailedProcessMessageOrEmpty("Run built project", project, aspNetProcess.Process)); await aspNetProcess.AssertStatusCode("/", HttpStatusCode.OK, "text/html"); - if (Fixture.BrowserManager.IsAvailable(browserKind)) + if (BrowserManager.IsAvailable(browserKind)) { var page = await browser.NewPageAsync(); await aspNetProcess.VisitInBrowserAsync(page); - await TestBasicNavigation(page); + await TestBasicNavigation(project, page); await page.CloseAsync(); } else @@ -81,18 +57,18 @@ await Fixture.BrowserManager.GetBrowserInstance(browserKind, BrowserContextInfo) } } - using (var aspNetProcess = Project.StartPublishedProjectAsync()) + using (var aspNetProcess = project.StartPublishedProjectAsync()) { Assert.False( aspNetProcess.Process.HasExited, - ErrorMessages.GetFailedProcessMessageOrEmpty("Run published project", Project, aspNetProcess.Process)); + ErrorMessages.GetFailedProcessMessageOrEmpty("Run published project", project, aspNetProcess.Process)); await aspNetProcess.AssertStatusCode("/", HttpStatusCode.OK, "text/html"); - if (Fixture.BrowserManager.IsAvailable(browserKind)) + if (BrowserManager.IsAvailable(browserKind)) { var page = await browser.NewPageAsync(); await aspNetProcess.VisitInBrowserAsync(page); - await TestBasicNavigation(page); + await TestBasicNavigation(project, page); await page.CloseAsync(); } else @@ -111,40 +87,24 @@ await Fixture.BrowserManager.GetBrowserInstance(browserKind, BrowserContextInfo) [SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/30825", Queues = "All.OSX")] public async Task BlazorServerTemplateWorks_IndividualAuth(BrowserKind browserKind, bool useLocalDB) { - // Additional arguments are needed. See: https://github.com/dotnet/aspnetcore/issues/24278 - Environment.SetEnvironmentVariable("EnableDefaultScopedCssItems", "true"); - - Project = await ProjectFactory.GetOrCreateProject("blazorserverindividual" + browserKind + (useLocalDB ? "uld" : ""), Output); - - var createResult = await Project.RunDotNetNewAsync("blazorserver", auth: "Individual", useLocalDB: useLocalDB); - Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", Project, createResult)); + var project = await CreateBuildPublishAsync("blazorserverindividual" + browserKind + (useLocalDB ? "uld" : "")); - 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)); - - var browser = !Fixture.BrowserManager.IsAvailable(browserKind) ? + var browser = !BrowserManager.IsAvailable(browserKind) ? null : - await Fixture.BrowserManager.GetBrowserInstance(browserKind, BrowserContextInfo); + await BrowserManager.GetBrowserInstance(browserKind, BrowserContextInfo); - using (var aspNetProcess = Project.StartBuiltProjectAsync()) + using (var aspNetProcess = project.StartBuiltProjectAsync()) { Assert.False( aspNetProcess.Process.HasExited, - ErrorMessages.GetFailedProcessMessageOrEmpty("Run built project", Project, aspNetProcess.Process)); + ErrorMessages.GetFailedProcessMessageOrEmpty("Run built project", project, aspNetProcess.Process)); await aspNetProcess.AssertStatusCode("/", HttpStatusCode.OK, "text/html"); - if (Fixture.BrowserManager.IsAvailable(browserKind)) + if (BrowserManager.IsAvailable(browserKind)) { var page = await browser.NewPageAsync(); await aspNetProcess.VisitInBrowserAsync(page); - await TestBasicNavigation(page); + await TestBasicNavigation(project, page); await page.CloseAsync(); } else @@ -153,18 +113,18 @@ public async Task BlazorServerTemplateWorks_IndividualAuth(BrowserKind browserKi } } - using (var aspNetProcess = Project.StartPublishedProjectAsync()) + using (var aspNetProcess = project.StartPublishedProjectAsync()) { Assert.False( aspNetProcess.Process.HasExited, - ErrorMessages.GetFailedProcessMessageOrEmpty("Run published project", Project, aspNetProcess.Process)); + ErrorMessages.GetFailedProcessMessageOrEmpty("Run published project", project, aspNetProcess.Process)); await aspNetProcess.AssertStatusCode("/", HttpStatusCode.OK, "text/html"); - if (Fixture.BrowserManager.IsAvailable(browserKind)) + if (BrowserManager.IsAvailable(browserKind)) { var page = await browser.NewPageAsync(); await aspNetProcess.VisitInBrowserAsync(page); - await TestBasicNavigation(page); + await TestBasicNavigation(project, page); await page.CloseAsync(); } else @@ -174,7 +134,7 @@ public async Task BlazorServerTemplateWorks_IndividualAuth(BrowserKind browserKi } } - private async Task TestBasicNavigation(IPage page) + private async Task TestBasicNavigation(Project project, IPage page) { var socket = BrowserContextInfo.Pages[page].WebSockets.SingleOrDefault() ?? (await page.WaitForEventAsync(PageEvent.WebSocket)).WebSocket; @@ -189,7 +149,7 @@ private async Task TestBasicNavigation(IPage page) await page.WaitForSelectorAsync("ul"); // element gets project ID injected into it during template execution - Assert.Equal(Project.ProjectName.Trim(), (await page.GetTitleAsync()).Trim()); + Assert.Equal(project.ProjectName.Trim(), (await page.GetTitleAsync()).Trim()); // Initially displays the home page await page.WaitForSelectorAsync("h1 >> text=Hello, world!"); @@ -218,22 +178,8 @@ private async Task TestBasicNavigation(IPage page) [InlineData("SingleOrg", new string[] { "--called-api-url \"https://graph.microsoft.com\"", "--called-api-scopes user.readwrite" })] [InlineData("SingleOrg", new string[] { "--calls-graph" })] [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/30882")] - public async Task BlazorServerTemplat_IdentityWeb_BuildAndPublish(string auth, string[] args) - { - Project = await ProjectFactory.GetOrCreateProject("blazorserveridweb" + Guid.NewGuid().ToString().Substring(0, 10).ToLowerInvariant(), Output); + public Task BlazorServerTemplate_IdentityWeb_BuildAndPublish(string auth, string[] args) + => CreateBuildPublishAsync("blazorserveridweb" + Guid.NewGuid().ToString().Substring(0, 10).ToLowerInvariant(), auth, args); - var createResult = await Project.RunDotNetNewAsync("blazorserver", auth: auth, args: args); - Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", Project, createResult)); - - var publishResult = await Project.RunDotNetPublishAsync(); - Assert.True(0 == publishResult.ExitCode, ErrorMessages.GetFailedProcessMessage("publish", Project, publishResult)); - - // Run dotnet build after publish. The reason is that one uses Config = Debug and the other uses Config = Release - // The output from publish will go into bin/Release/netcoreappX.Y/publish and won't be affected by calling build - // later, while the opposite is not true. - - var buildResult = await Project.RunDotNetBuildAsync(); - Assert.True(0 == buildResult.ExitCode, ErrorMessages.GetFailedProcessMessage("build", Project, buildResult)); - } } } diff --git a/src/ProjectTemplates/BlazorTemplates.Tests/BlazorTemplateTest.cs b/src/ProjectTemplates/BlazorTemplates.Tests/BlazorTemplateTest.cs index 4057e99fec6a..121960ee852c 100644 --- a/src/ProjectTemplates/BlazorTemplates.Tests/BlazorTemplateTest.cs +++ b/src/ProjectTemplates/BlazorTemplates.Tests/BlazorTemplateTest.cs @@ -1,49 +1,146 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; +using System.Diagnostics; +using System.IO; +using System.Reflection; using System.Runtime.InteropServices; +using System.Threading.Tasks; using Microsoft.AspNetCore.BrowserTesting; +using Microsoft.AspNetCore.Testing; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Testing; -using ProjectTemplates.Tests.Infrastructure; +using Templates.Test.Helpers; using Xunit; using Xunit.Abstractions; namespace Templates.Test { - public class BlazorTemplateTest + public abstract class BlazorTemplateTest : LoggedTest, IAsyncLifetime { - public BlazorTemplateTest(PlaywrightFixture<BlazorServerTemplateTest> browserFixture) + public const int BUILDCREATEPUBLISH_PRIORITY = -1000; + + public BlazorTemplateTest(ProjectFactoryFixture projectFactory) + { + ProjectFactory = projectFactory; + } + + public ProjectFactoryFixture ProjectFactory { get; set; } + public ContextInformation BrowserContextInfo { get; protected set; } + public BrowserManager BrowserManager { get; private set; } + + private ITestOutputHelper _output; + public ITestOutputHelper Output { - Fixture = browserFixture; + get + { + if (_output == null) + { + _output = new TestOutputLogger(Logger); + } + return _output; + } } + + public abstract string ProjectType { get; } + private static readonly bool _isCIEnvironment = + !string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("ContinuousIntegrationBuild")); + + protected async override Task InitializeCoreAsync(TestContext context) + { + BrowserManager = await BrowserManager.CreateAsync(CreateConfiguration(), LoggerFactory); + BrowserContextInfo = new ContextInformation(LoggerFactory); + _output = new TestOutputLogger(Logger); + } + + public Task InitializeAsync() => Task.CompletedTask; + + public Task DisposeAsync() => BrowserManager.DisposeAsync(); + + private static IConfiguration CreateConfiguration() + { + var basePath = Path.GetDirectoryName(typeof(BlazorTemplateTest).Assembly.Location); + var os = Environment.OSVersion.Platform switch + { + PlatformID.Win32NT => "win", + PlatformID.Unix => "linux", + PlatformID.MacOSX => "osx", + _ => null + }; + + var builder = new ConfigurationBuilder() + .AddJsonFile(Path.Combine(basePath, "playwrightSettings.json")) + .AddJsonFile(Path.Combine(basePath, $"playwrightSettings.{os}.json"), optional: true); + + if (_isCIEnvironment) + { + builder.AddJsonFile(Path.Combine(basePath, "playwrightSettings.ci.json"), optional: true) + .AddJsonFile(Path.Combine(basePath, $"playwrightSettings.ci.{os}.json"), optional: true); + } - public PlaywrightFixture<BlazorServerTemplateTest> Fixture { get; } + if (Debugger.IsAttached) + { + builder.AddJsonFile(Path.Combine(basePath, "playwrightSettings.debug.json"), optional: true); + } + return builder.Build(); + } - public static ILoggerFactory CreateFactory(ITestOutputHelper output) + protected async Task<Project> CreateBuildPublishAsync(string projectName, string auth = null, string[] args = null, string targetFramework = null, bool serverProject = false, bool onlyCreate = false) { - var testSink = new TestSink(); - testSink.MessageLogged += LogMessage; - var loggerFactory = new TestLoggerFactory(testSink, enabled: true); - return loggerFactory; + // Additional arguments are needed. See: https://github.com/dotnet/aspnetcore/issues/24278 + Environment.SetEnvironmentVariable("EnableDefaultScopedCssItems", "true"); - void LogMessage(WriteContext ctx) + var project = await ProjectFactory.GetOrCreateProject(projectName, Output); + if (targetFramework != null) { - output.WriteLine($"{MapLogLevel(ctx)}: [Browser]{ctx.Message}"); + project.TargetFramework = targetFramework; + } - static string MapLogLevel(WriteContext obj) => obj.LogLevel switch + var createResult = await project.RunDotNetNewAsync(ProjectType, auth: auth, args: args); + Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", project, createResult)); + + if (!onlyCreate) + { + var targetProject = project; + if (serverProject) { - LogLevel.Trace => "trace", - LogLevel.Debug => "dbug", - LogLevel.Information => "info", - LogLevel.Warning => "warn", - LogLevel.Error => "error", - LogLevel.Critical => "crit", - LogLevel.None => "info", - _ => "info" - }; + targetProject = GetSubProject(project, "Server", $"{project.ProjectName}.Server"); + } + + var publishResult = await targetProject.RunDotNetPublishAsync(noRestore: false); + Assert.True(0 == publishResult.ExitCode, ErrorMessages.GetFailedProcessMessage("publish", targetProject, 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 targetProject.RunDotNetBuildAsync(); + Assert.True(0 == buildResult.ExitCode, ErrorMessages.GetFailedProcessMessage("build", targetProject, buildResult)); } + + return project; + } + + protected static Project GetSubProject(Project project, string projectDirectory, string projectName) + { + var subProjectDirectory = Path.Combine(project.TemplateOutputDir, projectDirectory); + if (!Directory.Exists(subProjectDirectory)) + { + throw new DirectoryNotFoundException($"Directory {subProjectDirectory} was not found."); + } + + var subProject = new Project + { + Output = project.Output, + DiagnosticsMessageSink = project.DiagnosticsMessageSink, + ProjectName = projectName, + TemplateOutputDir = subProjectDirectory, + }; + + return subProject; } public static bool TryValidateBrowserRequired(BrowserKind browserKind, bool isRequired, out string error) @@ -57,7 +154,7 @@ protected void EnsureBrowserAvailable(BrowserKind browserKind) Assert.False( TryValidateBrowserRequired( browserKind, - isRequired: !Fixture.BrowserManager.IsExplicitlyDisabled(browserKind), + isRequired: !BrowserManager.IsExplicitlyDisabled(browserKind), out var errorMessage), errorMessage); } diff --git a/src/ProjectTemplates/BlazorTemplates.Tests/BlazorWasmTemplateTest.cs b/src/ProjectTemplates/BlazorTemplates.Tests/BlazorWasmTemplateTest.cs index 9d2252ce079a..a36a86f4d174 100644 --- a/src/ProjectTemplates/BlazorTemplates.Tests/BlazorWasmTemplateTest.cs +++ b/src/ProjectTemplates/BlazorTemplates.Tests/BlazorWasmTemplateTest.cs @@ -17,7 +17,6 @@ using Microsoft.Extensions.CommandLineUtils; using Newtonsoft.Json.Linq; using PlaywrightSharp; -using ProjectTemplates.Tests.Infrastructure; using Templates.Test.Helpers; using Xunit; using Xunit.Abstractions; @@ -26,52 +25,31 @@ namespace Templates.Test { public class BlazorWasmTemplateTest : BlazorTemplateTest { - public BlazorWasmTemplateTest(ProjectFactoryFixture projectFactory, PlaywrightFixture<BlazorServerTemplateTest> browserFixture, ITestOutputHelper output) - : base(browserFixture) - { - ProjectFactory = projectFactory; - Output = output; - BrowserContextInfo = new ContextInformation(CreateFactory(output)); - } - - public ProjectFactoryFixture ProjectFactory { get; set; } + public BlazorWasmTemplateTest(ProjectFactoryFixture projectFactory) + : base(projectFactory) { } - public ITestOutputHelper Output { get; } - - public ContextInformation BrowserContextInfo { get; } + public override string ProjectType { get; } = "blazorwasm"; [Theory] [InlineData(BrowserKind.Chromium)] [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/30882")] public async Task BlazorWasmStandaloneTemplate_Works(BrowserKind browserKind) { - // Additional arguments are needed. See: https://github.com/dotnet/aspnetcore/issues/24278 - Environment.SetEnvironmentVariable("EnableDefaultScopedCssItems", "true"); - - var project = await ProjectFactory.GetOrCreateProject("blazorstandalone" + browserKind, Output); - - var createResult = await project.RunDotNetNewAsync("blazorwasm"); - Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", project, createResult)); - - var publishResult = await project.RunDotNetPublishAsync(); - Assert.True(0 == publishResult.ExitCode, ErrorMessages.GetFailedProcessMessage("publish", project, publishResult)); + var project = await CreateBuildPublishAsync("blazorstandalone" + browserKind); // 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"); - var buildResult = await project.RunDotNetBuildAsync(); - Assert.True(0 == buildResult.ExitCode, ErrorMessages.GetFailedProcessMessage("build", project, buildResult)); - await BuildAndRunTest(project.ProjectName, project, browserKind); var (serveProcess, listeningUri) = RunPublishedStandaloneBlazorProject(project); using (serveProcess) { Output.WriteLine($"Opening browser at {listeningUri}..."); - if (Fixture.BrowserManager.IsAvailable(browserKind)) + if (BrowserManager.IsAvailable(browserKind)) { - await using var browser = await Fixture.BrowserManager.GetBrowserInstance(browserKind, BrowserContextInfo); + await using var browser = await BrowserManager.GetBrowserInstance(browserKind, BrowserContextInfo); var page = await NavigateToPage(browser, listeningUri); await TestBasicNavigation(project.ProjectName, page); } @@ -94,21 +72,10 @@ private async Task<IPage> NavigateToPage(IBrowserContext browser, string listeni [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/30882")] public async Task BlazorWasmHostedTemplate_Works(BrowserKind browserKind) { - // Additional arguments are needed. See: https://github.com/dotnet/aspnetcore/issues/24278 - Environment.SetEnvironmentVariable("EnableDefaultScopedCssItems", "true"); - - var project = await ProjectFactory.GetOrCreateProject("blazorhosted" + browserKind, Output); - var createResult = await project.RunDotNetNewAsync("blazorwasm", args: new[] { "--hosted" }); - Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", project, createResult)); + var project = await CreateBuildPublishAsync("blazorhosted" + BrowserKind.Chromium, args: new[] { "--hosted" }, serverProject: true); var serverProject = GetSubProject(project, "Server", $"{project.ProjectName}.Server"); - var publishResult = await serverProject.RunDotNetPublishAsync(); - Assert.True(0 == publishResult.ExitCode, ErrorMessages.GetFailedProcessMessage("publish", serverProject, publishResult)); - - var buildResult = await serverProject.RunDotNetBuildAsync(); - Assert.True(0 == buildResult.ExitCode, ErrorMessages.GetFailedProcessMessage("build", serverProject, buildResult)); - await BuildAndRunTest(project.ProjectName, serverProject, browserKind); using var aspNetProcess = serverProject.StartPublishedProjectAsync(); @@ -120,9 +87,9 @@ public async Task BlazorWasmHostedTemplate_Works(BrowserKind browserKind) await aspNetProcess.AssertStatusCode("/", HttpStatusCode.OK, "text/html"); await AssertCompressionFormat(aspNetProcess, "br"); - if (Fixture.BrowserManager.IsAvailable(browserKind)) + if (BrowserManager.IsAvailable(browserKind)) { - await using var browser = await Fixture.BrowserManager.GetBrowserInstance(browserKind, BrowserContextInfo); + await using var browser = await BrowserManager.GetBrowserInstance(browserKind, BrowserContextInfo); var page = await browser.NewPageAsync(); await aspNetProcess.VisitInBrowserAsync(page); await TestBasicNavigation(project.ProjectName, page); @@ -154,28 +121,16 @@ private static async Task AssertCompressionFormat(AspNetProcess aspNetProcess, s [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/30882")] public async Task BlazorWasmStandalonePwaTemplate_Works(BrowserKind browserKind) { - // Additional arguments are needed. See: https://github.com/dotnet/aspnetcore/issues/24278 - Environment.SetEnvironmentVariable("EnableDefaultScopedCssItems", "true"); - - var project = await ProjectFactory.GetOrCreateProject("blazorstandalonepwa", Output); - - var createResult = await project.RunDotNetNewAsync("blazorwasm", args: new[] { "--pwa" }); - Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", project, createResult)); - - var publishResult = await project.RunDotNetPublishAsync(); - Assert.True(0 == publishResult.ExitCode, ErrorMessages.GetFailedProcessMessage("publish", project, publishResult)); - - var buildResult = await project.RunDotNetBuildAsync(); - Assert.True(0 == buildResult.ExitCode, ErrorMessages.GetFailedProcessMessage("build", project, buildResult)); + var project = await CreateBuildPublishAsync("blazorstandalonepwa", args: new[] { "--pwa" }); await BuildAndRunTest(project.ProjectName, project, browserKind); ValidatePublishedServiceWorker(project); - if (Fixture.BrowserManager.IsAvailable(browserKind)) + if (BrowserManager.IsAvailable(browserKind)) { var (serveProcess, listeningUri) = RunPublishedStandaloneBlazorProject(project); - await using var browser = await Fixture.BrowserManager.GetBrowserInstance(browserKind, BrowserContextInfo); + await using var browser = await BrowserManager.GetBrowserInstance(browserKind, BrowserContextInfo); Output.WriteLine($"Opening browser at {listeningUri}..."); var page = await NavigateToPage(browser, listeningUri); using (serveProcess) @@ -202,30 +157,18 @@ public async Task BlazorWasmStandalonePwaTemplate_Works(BrowserKind browserKind) [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/30882")] public async Task BlazorWasmHostedPwaTemplate_Works(BrowserKind browserKind) { - // Additional arguments are needed. See: https://github.com/dotnet/aspnetcore/issues/24278 - Environment.SetEnvironmentVariable("EnableDefaultScopedCssItems", "true"); - - var project = await ProjectFactory.GetOrCreateProject("blazorhostedpwa", Output); - - var createResult = await project.RunDotNetNewAsync("blazorwasm", args: new[] { "--hosted", "--pwa" }); - Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", project, createResult)); + var project = await CreateBuildPublishAsync("blazorhostedpwa", args: new[] { "--hosted", "--pwa" }, serverProject: true); var serverProject = GetSubProject(project, "Server", $"{project.ProjectName}.Server"); - var publishResult = await serverProject.RunDotNetPublishAsync(); - Assert.True(0 == publishResult.ExitCode, ErrorMessages.GetFailedProcessMessage("publish", serverProject, publishResult)); - - var buildResult = await serverProject.RunDotNetBuildAsync(); - Assert.True(0 == buildResult.ExitCode, ErrorMessages.GetFailedProcessMessage("build", serverProject, buildResult)); - await BuildAndRunTest(project.ProjectName, serverProject, browserKind); ValidatePublishedServiceWorker(serverProject); string listeningUri = null; - if (Fixture.BrowserManager.IsAvailable(browserKind)) + if (BrowserManager.IsAvailable(browserKind)) { - await using var browser = await Fixture.BrowserManager.GetBrowserInstance(browserKind, BrowserContextInfo); + await using var browser = await BrowserManager.GetBrowserInstance(browserKind, BrowserContextInfo); IPage page = null; using (var aspNetProcess = serverProject.StartPublishedProjectAsync()) { @@ -286,28 +229,24 @@ private void ValidatePublishedServiceWorker(Project project) [OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX)] [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/30700")] public Task BlazorWasmHostedTemplate_IndividualAuth_Works_WithLocalDB(BrowserKind browserKind) - { - return BlazorWasmHostedTemplate_IndividualAuth_Works(browserKind, true); - } + => BlazorWasmHostedTemplate_IndividualAuth_Works(browserKind, true); + + // This test depends on BlazorWasmTemplate_CreateBuildPublish_IndividualAuthNoLocalDb running first [Theory] [InlineData(BrowserKind.Chromium)] [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/30820")] [SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/30825", Queues = "All.OSX")] public Task BlazorWasmHostedTemplate_IndividualAuth_Works_WithOutLocalDB(BrowserKind browserKind) - { - return BlazorWasmHostedTemplate_IndividualAuth_Works(browserKind, false); - } + => BlazorWasmHostedTemplate_IndividualAuth_Works(browserKind, false); - private async Task BlazorWasmHostedTemplate_IndividualAuth_Works(BrowserKind browserKind, bool useLocalDb) + private async Task<Project> CreateBuildPublishIndividualAuthProject(BrowserKind browserKind, bool useLocalDb) { // Additional arguments are needed. See: https://github.com/dotnet/aspnetcore/issues/24278 Environment.SetEnvironmentVariable("EnableDefaultScopedCssItems", "true"); - var project = await ProjectFactory.GetOrCreateProject("blazorhostedindividual" + browserKind + (useLocalDb ? "uld" : ""), Output); - - var createResult = await project.RunDotNetNewAsync("blazorwasm", args: new[] { "--hosted", "-au", "Individual", useLocalDb ? "-uld" : "" }); - Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", project, createResult)); + var project = await CreateBuildPublishAsync("blazorhostedindividual" + browserKind + (useLocalDb ? "uld" : ""), + args: new[] { "--hosted", "-au", "Individual", useLocalDb ? "-uld" : "" }); var serverProject = GetSubProject(project, "Server", $"{project.ProjectName}.Server"); @@ -344,11 +283,20 @@ private async Task BlazorWasmHostedTemplate_IndividualAuth_Works(BrowserKind bro Assert.True(0 == dbUpdateResult.ExitCode, ErrorMessages.GetFailedProcessMessage("update database", serverProject, dbUpdateResult)); } + return project; + } + + private async Task BlazorWasmHostedTemplate_IndividualAuth_Works(BrowserKind browserKind, bool useLocalDb) + { + var project = await CreateBuildPublishIndividualAuthProject(browserKind, useLocalDb: useLocalDb); + + var serverProject = GetSubProject(project, "Server", $"{project.ProjectName}.Server"); + await BuildAndRunTest(project.ProjectName, serverProject, browserKind, usesAuth: true); UpdatePublishedSettings(serverProject); - if (Fixture.BrowserManager.IsAvailable(browserKind)) + if (BrowserManager.IsAvailable(browserKind)) { using var aspNetProcess = serverProject.StartPublishedProjectAsync(); @@ -358,7 +306,7 @@ private async Task BlazorWasmHostedTemplate_IndividualAuth_Works(BrowserKind bro await aspNetProcess.AssertStatusCode("/", HttpStatusCode.OK, "text/html"); - await using var browser = await Fixture.BrowserManager.GetBrowserInstance(browserKind, BrowserContextInfo); + await using var browser = await BrowserManager.GetBrowserInstance(browserKind, BrowserContextInfo); var page = await browser.NewPageAsync(); await aspNetProcess.VisitInBrowserAsync(page); await TestBasicNavigation(project.ProjectName, page, usesAuth: true); @@ -374,12 +322,7 @@ private async Task BlazorWasmHostedTemplate_IndividualAuth_Works(BrowserKind bro [InlineData(BrowserKind.Chromium, Skip = "https://github.com/dotnet/aspnetcore/issues/28596")] public async Task BlazorWasmStandaloneTemplate_IndividualAuth_Works(BrowserKind browserKind) { - // Additional arguments are needed. See: https://github.com/dotnet/aspnetcore/issues/24278 - Environment.SetEnvironmentVariable("EnableDefaultScopedCssItems", "true"); - - var project = await ProjectFactory.GetOrCreateProject("blazorstandaloneindividual" + browserKind, Output); - - var createResult = await project.RunDotNetNewAsync("blazorwasm", args: new[] { + var project = await CreateBuildPublishAsync("blazorstandaloneindividual" + browserKind, args: new[] { "-au", "Individual", "--authority", @@ -388,18 +331,6 @@ public async Task BlazorWasmStandaloneTemplate_IndividualAuth_Works(BrowserKind "sample-client-id" }); - Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", project, createResult)); - - var publishResult = await project.RunDotNetPublishAsync(); - Assert.True(0 == publishResult.ExitCode, ErrorMessages.GetFailedProcessMessage("publish", project, publishResult)); - - // Run dotnet build after publish. The reason is that one uses Config = Debug and the other uses Config = Release - // The output from publish will go into bin/Release/netcoreappX.Y/publish and won't be affected by calling build - // later, while the opposite is not true. - - var buildResult = await project.RunDotNetBuildAsync(); - Assert.True(0 == buildResult.ExitCode, ErrorMessages.GetFailedProcessMessage("build", project, buildResult)); - // We don't want to test the auth flow as we don't have the required settings to talk to a third-party IdP // but we want to make sure that we are able to run the app without errors. // That will at least test that we are able to initialize and retrieve the configuration from the IdP @@ -410,7 +341,7 @@ public async Task BlazorWasmStandaloneTemplate_IndividualAuth_Works(BrowserKind using (serveProcess) { Output.WriteLine($"Opening browser at {listeningUri}..."); - await using var browser = await Fixture.BrowserManager.GetBrowserInstance(browserKind, BrowserContextInfo); + await using var browser = await BrowserManager.GetBrowserInstance(browserKind, BrowserContextInfo); var page = await NavigateToPage(browser, listeningUri); await TestBasicNavigation(project.ProjectName, page); await page.CloseAsync(); @@ -489,25 +420,8 @@ public TemplateInstance(string name, params string[] arguments) [Theory] [MemberData(nameof(TemplateData))] [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/30880")] - public async Task BlazorWasmHostedTemplate_AzureActiveDirectoryTemplate_Works(TemplateInstance instance) - { - var project = await ProjectFactory.GetOrCreateProject(instance.Name, Output); - project.TargetFramework = "netstandard2.1"; - - var createResult = await project.RunDotNetNewAsync("blazorwasm", args: instance.Arguments); - - Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", project, createResult)); - - var publishResult = await project.RunDotNetPublishAsync(); - Assert.True(0 == publishResult.ExitCode, ErrorMessages.GetFailedProcessMessage("publish", project, publishResult)); - - // Run dotnet build after publish. The reason is that one uses Config = Debug and the other uses Config = Release - // The output from publish will go into bin/Release/netcoreappX.Y/publish and won't be affected by calling build - // later, while the opposite is not true. - - var buildResult = await project.RunDotNetBuildAsync(); - Assert.True(0 == buildResult.ExitCode, ErrorMessages.GetFailedProcessMessage("build", project, buildResult)); - } + public Task BlazorWasmHostedTemplate_AzureActiveDirectoryTemplate_Works(TemplateInstance instance) + => CreateBuildPublishAsync(instance.Name, args: instance.Arguments, targetFramework: "netstandard2.1"); protected async Task BuildAndRunTest(string appName, Project project, BrowserKind browserKind, bool usesAuth = false) { @@ -518,9 +432,9 @@ protected async Task BuildAndRunTest(string appName, Project project, BrowserKin ErrorMessages.GetFailedProcessMessageOrEmpty("Run built project", project, aspNetProcess.Process)); await aspNetProcess.AssertStatusCode("/", HttpStatusCode.OK, "text/html"); - if (Fixture.BrowserManager.IsAvailable(browserKind)) + if (BrowserManager.IsAvailable(browserKind)) { - await using var browser = await Fixture.BrowserManager.GetBrowserInstance(browserKind, BrowserContextInfo); + await using var browser = await BrowserManager.GetBrowserInstance(browserKind, BrowserContextInfo); var page = await browser.NewPageAsync(); await aspNetProcess.VisitInBrowserAsync(page); await TestBasicNavigation(appName, page, usesAuth); @@ -617,25 +531,6 @@ private string ReadFile(string basePath, string path) return File.ReadAllText(Path.Combine(basePath, path)); } - private Project GetSubProject(Project project, string projectDirectory, string projectName) - { - var subProjectDirectory = Path.Combine(project.TemplateOutputDir, projectDirectory); - if (!Directory.Exists(subProjectDirectory)) - { - throw new DirectoryNotFoundException($"Directory {subProjectDirectory} was not found."); - } - - var subProject = new Project - { - Output = project.Output, - DiagnosticsMessageSink = project.DiagnosticsMessageSink, - ProjectName = projectName, - TemplateOutputDir = subProjectDirectory, - }; - - return subProject; - } - private void UpdatePublishedSettings(Project serverProject) { // Hijack here the config file to use the development key during publish. @@ -674,7 +569,7 @@ private void UpdatePublishedSettings(Project serverProject) args = "--roll-forward LatestMajor " + args; // dotnet-serve targets net5.0 by default } - var serveProcess = ProcessEx.Run(Output, publishDir, command, args); + var serveProcess = ProcessEx.Run(TestOutputHelper, publishDir, command, args); var listeningUri = ResolveListeningUrl(serveProcess); return (serveProcess, listeningUri); } diff --git a/src/ProjectTemplates/Shared/ProjectFactoryFixture.cs b/src/ProjectTemplates/Shared/ProjectFactoryFixture.cs index 2f25714fd64b..52fbed63dd00 100644 --- a/src/ProjectTemplates/Shared/ProjectFactoryFixture.cs +++ b/src/ProjectTemplates/Shared/ProjectFactoryFixture.cs @@ -26,6 +26,12 @@ public ProjectFactoryFixture(IMessageSink diagnosticsMessageSink) public async Task<Project> GetOrCreateProject(string projectKey, ITestOutputHelper output) { await TemplatePackageInstaller.EnsureTemplatingEngineInitializedAsync(output); + // Different tests may have different output helpers, so need to fix up the output to write to the correct log + if (_projects.TryGetValue(projectKey, out var project)) + { + project.Output = output; + return project; + } return _projects.GetOrAdd( projectKey, (key, outputHelper) => diff --git a/src/Testing/src/LoggedTest/LoggedTestBase.cs b/src/Testing/src/LoggedTest/LoggedTestBase.cs index 362904b3e38a..25f7f37dde72 100644 --- a/src/Testing/src/LoggedTest/LoggedTestBase.cs +++ b/src/Testing/src/LoggedTest/LoggedTestBase.cs @@ -95,7 +95,15 @@ public virtual void Initialize(TestContext context, MethodInfo methodInfo, objec _initializationException = ExceptionDispatchInfo.Capture(e); } } - + + public virtual Task InitializeAsync(TestContext context, MethodInfo methodInfo, object[] testMethodArguments, ITestOutputHelper testOutputHelper) + { + Initialize(context, methodInfo, testMethodArguments, testOutputHelper); + return InitializeCoreAsync(context); + } + + protected virtual Task InitializeCoreAsync(TestContext context) => Task.CompletedTask; + public virtual void Dispose() { if (_testLog == null) @@ -114,9 +122,7 @@ Task ITestMethodLifecycle.OnTestStartAsync(TestContext context, CancellationToke { Context = context; - - Initialize(context, context.TestMethod, context.MethodArguments, context.Output); - return Task.CompletedTask; + return InitializeAsync(context, context.TestMethod, context.MethodArguments, context.Output); } Task ITestMethodLifecycle.OnTestEndAsync(TestContext context, Exception exception, CancellationToken cancellationToken)