diff --git a/src/ProjectTemplates/BlazorTemplates.Tests/AssemblyInfo.AssemblyFixtures.cs b/src/ProjectTemplates/BlazorTemplates.Tests/AssemblyInfo.AssemblyFixtures.cs index 3eb30b095263..e3d87ba98ee3 100644 --- a/src/ProjectTemplates/BlazorTemplates.Tests/AssemblyInfo.AssemblyFixtures.cs +++ b/src/ProjectTemplates/BlazorTemplates.Tests/AssemblyInfo.AssemblyFixtures.cs @@ -5,7 +5,8 @@ using ProjectTemplates.Tests.Infrastructure; using Templates.Test; using Templates.Test.Helpers; +using Xunit; [assembly: AssemblyFixture(typeof(ProjectFactoryFixture))] -[assembly: AssemblyFixture(typeof(PlaywrightFixture))] +[assembly: CollectionBehavior(DisableTestParallelization = true)] diff --git a/src/ProjectTemplates/BlazorTemplates.Tests/BlazorServerTemplateTest.cs b/src/ProjectTemplates/BlazorTemplates.Tests/BlazorServerTemplateTest.cs index 24fe3fa75aa6..4c2a196a32c5 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; @@ -17,62 +16,47 @@ namespace Templates.Test { + [TestCaseOrderer("Templates.Test.PriorityOrderer", "BlazorTemplates.Tests")] 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"; + // This test is required to run before BlazorServerTemplateWorks_NoAuth to create and build the project + // If this test is quarantined, BlazorServerTemplateWorks_NoAuth must be quarantined as well + [Fact, TestPriority(BUILDCREATEPUBLISH_PRIORITY)] + public Task BlazorServerTemplate_CreateBuildPublish_NoAuth() + => CreateBuildPublishAsync("blazorservernoauth" + BrowserKind.Chromium.ToString()); + // This tests depends on BlazorServerTemplate_CreateBuildPublish_NoAuth running first [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"); + var project = await ProjectFactory.GetOrCreateProject("blazorservernoauth" + browserKind, Output); - 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)); - - // 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 +65,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 @@ -102,49 +86,41 @@ await Fixture.BrowserManager.GetBrowserInstance(browserKind, BrowserContextInfo) } } + // This test is required to run before BlazorServerTemplateWorks_IndividualAuth to create and build the project + // If this test is quarantined, BlazorServerTemplateWorks_IndividualAuth must be quarantined as well + [Theory, TestPriority(BUILDCREATEPUBLISH_PRIORITY)] + [MemberData(nameof(BlazorServerTemplateWorks_IndividualAuthData))] + public Task BlazorServerTemplate_CreateBuildPublish_IndividualAuth(BrowserKind browserKind, bool useLocalDB) + => CreateBuildPublishAsync("blazorserverindividual" + browserKind + (useLocalDB ? "uld" : "")); + public static IEnumerable BlazorServerTemplateWorks_IndividualAuthData => BrowserManager.WithBrowsers(new[] { BrowserKind.Chromium }, true, false); + // This tests depends on BlazorServerTemplate_CreateBuildPublish_IndividualAuth running first [Theory] [MemberData(nameof(BlazorServerTemplateWorks_IndividualAuthData))] [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/30807")] [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 ProjectFactory.GetOrCreateProject("blazorserverindividual" + browserKind + (useLocalDB ? "uld" : ""), Output); - 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 +129,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 +150,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 +165,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!"); @@ -211,29 +187,15 @@ private async Task TestBasicNavigation(IPage page) Assert.Equal(5, (await page.QuerySelectorAllAsync("p+table>tbody>tr")).Count()); } - [Theory] + [Theory, TestPriority(BUILDCREATEPUBLISH_PRIORITY)] [InlineData("IndividualB2C", null)] [InlineData("IndividualB2C", new string[] { "--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" })] [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..dee39da69f57 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: !serverProject); + 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..356ac48bb551 100644 --- a/src/ProjectTemplates/BlazorTemplates.Tests/BlazorWasmTemplateTest.cs +++ b/src/ProjectTemplates/BlazorTemplates.Tests/BlazorWasmTemplateTest.cs @@ -17,61 +17,50 @@ using Microsoft.Extensions.CommandLineUtils; using Newtonsoft.Json.Linq; using PlaywrightSharp; -using ProjectTemplates.Tests.Infrastructure; using Templates.Test.Helpers; using Xunit; using Xunit.Abstractions; namespace Templates.Test { + [TestCaseOrderer("Templates.Test.PriorityOrderer", "BlazorTemplates.Tests")] 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 BlazorWasmTemplateTest(ProjectFactoryFixture projectFactory) + : base(projectFactory) { } - public ProjectFactoryFixture ProjectFactory { get; set; } + public override string ProjectType { get; } = "blazorwasm"; - public ITestOutputHelper Output { get; } + // This test is required to run before BlazorWasmStandaloneTemplate_Works to create and build the project + // If this test is quarantined, BlazorWasmStandaloneTemplate_Works must be quarantined as well + [Theory, TestPriority(BUILDCREATEPUBLISH_PRIORITY)] + [InlineData(BrowserKind.Chromium)] + public async Task BlazorWasmTemplate_CreateBuildPublish_Standalone(BrowserKind browserKind) + { + var project = await CreateBuildPublishAsync("blazorstandalone" + browserKind); - public ContextInformation BrowserContextInfo { get; } + // 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"); + } + // This test depends on BlazorWasmTemplate_CreateBuildPublish_Standalone running first [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)); - - // 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); } @@ -89,10 +78,11 @@ private async Task<IPage> NavigateToPage(IBrowserContext browser, string listeni return page; } - [Theory] + // This test is required to run before BlazorWasmHostedTemplate_Works to create and build the project + // If this test is quarantined, BlazorWasmHostedTemplate_Works must be quarantined as well + [Theory, TestPriority(BUILDCREATEPUBLISH_PRIORITY)] [InlineData(BrowserKind.Chromium)] - [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/30882")] - public async Task BlazorWasmHostedTemplate_Works(BrowserKind browserKind) + public async Task BlazorWasmTemplate_CreateBuildPublish_Hosted(BrowserKind browserKind) { // Additional arguments are needed. See: https://github.com/dotnet/aspnetcore/issues/24278 Environment.SetEnvironmentVariable("EnableDefaultScopedCssItems", "true"); @@ -107,7 +97,19 @@ public async Task BlazorWasmHostedTemplate_Works(BrowserKind browserKind) 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)); + Assert.True(0 == buildResult.ExitCode, ErrorMessages.GetFailedProcessMessage("build", serverProject, buildResult)); + } + // => CreateBuildPublishAsync("blazorhosted" + BrowserKind.Chromium, args: new[] { "--hosted" }, serverProject: true); + + // This test depends on BlazorWasmTemplate_CreateBuildPublish_Hosted running first + [Theory] + [InlineData(BrowserKind.Chromium)] + [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/30882")] + public async Task BlazorWasmHostedTemplate_Works(BrowserKind browserKind) + { + var project = await ProjectFactory.GetOrCreateProject("blazorhosted" + browserKind, Output); + + var serverProject = GetSubProject(project, "Server", $"{project.ProjectName}.Server"); await BuildAndRunTest(project.ProjectName, serverProject, browserKind); @@ -120,9 +122,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); @@ -149,33 +151,28 @@ private static async Task AssertCompressionFormat(AspNetProcess aspNetProcess, s Assert.Equal(expectedEncoding, response.Content.Headers.ContentEncoding.Single()); } + // This test is required to run before BlazorWasmStandalonePwaTemplate_Works to create and build the project + // If this test is quarantined, BlazorWasmStandalonePwaTemplate_Works must be quarantined as well + [Fact, TestPriority(BUILDCREATEPUBLISH_PRIORITY)] + public Task BlazorWasmTemplate_CreateBuildPublish_StandalonePwa() + => CreateBuildPublishAsync("blazorstandalonepwa", args: new[] { "--pwa" }); + + // This test depends on BlazorWasmTemplate_CreateBuildPublish_StandalonePwa running first [Theory] [InlineData(BrowserKind.Chromium)] [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)); - 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) @@ -197,35 +194,30 @@ public async Task BlazorWasmStandalonePwaTemplate_Works(BrowserKind browserKind) } } + // This test is required to run before BlazorWasmHostedPwaTemplate_Works to create and build the project + // If this test is quarantined, BlazorWasmHostedPwaTemplate_Works must be quarantined as well + [Fact, TestPriority(BUILDCREATEPUBLISH_PRIORITY)] + public Task BlazorWasmTemplate_CreateBuildPublish_HostedPwa() + => CreateBuildPublishAsync("blazorhostedpwa", args: new[] { "--hosted", "--pwa" }, serverProject: true); + + // This test depends on BlazorWasmTemplate_CreateBuildPublish_HostedPwa running first [Theory] [InlineData(BrowserKind.Chromium)] [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 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()) { @@ -280,34 +272,46 @@ private void ValidatePublishedServiceWorker(Project project) Assert.True(serviceWorkerContents.Contains($"/* Manifest version: {serviceWorkerAssetsManifestVersion} */", StringComparison.Ordinal)); } + // This test is required to run before BlazorWasmHostedTemplate_IndividualAuth_Works_WithLocalDB to create and build the project + // If this test is quarantined, BlazorWasmHostedTemplate_IndividualAuth_Works_WithLocalDB must be quarantined as well + [ConditionalTheory, TestPriority(BUILDCREATEPUBLISH_PRIORITY)] + [InlineData(BrowserKind.Chromium)] + //// LocalDB doesn't work on non Windows platforms + [OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX)] + public Task BlazorWasmTemplate_CreateBuildPublish_IndividualAuthLocalDb(BrowserKind browserKind) + => CreateBuildPublishIndividualAuthProject(browserKind, useLocalDb: true); + + // This test depends on BlazorWasmTemplate_CreateBuildPublish_IndividualAuthLocalDb running first [ConditionalTheory] [InlineData(BrowserKind.Chromium)] - // LocalDB doesn't work on non Windows platforms + //// LocalDB doesn't work on non Windows platforms [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 is required to run before BlazorWasmHostedTemplate_IndividualAuth_Works_WithOutLocalDB to create and build the project + // If this test is quarantined, BlazorWasmHostedTemplate_IndividualAuth_Works_WithOutLocalDB must be quarantined as well + [ConditionalTheory, TestPriority(BUILDCREATEPUBLISH_PRIORITY)] + [InlineData(BrowserKind.Chromium)] + public Task BlazorWasmTemplate_CreateBuildPublish_IndividualAuthNoLocalDb(BrowserKind browserKind) + => CreateBuildPublishIndividualAuthProject(browserKind, useLocalDb: false); + // 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 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"); @@ -343,12 +347,19 @@ private async Task BlazorWasmHostedTemplate_IndividualAuth_Works(BrowserKind bro var dbUpdateResult = await serverProject.RunDotNetEfUpdateDatabaseAsync(); Assert.True(0 == dbUpdateResult.ExitCode, ErrorMessages.GetFailedProcessMessage("update database", serverProject, dbUpdateResult)); } + } + + private async Task BlazorWasmHostedTemplate_IndividualAuth_Works(BrowserKind browserKind, bool useLocalDb) + { + var project = await ProjectFactory.GetOrCreateProject("blazorhostedindividual" + browserKind + (useLocalDb ? "uld" : ""), Output); + + 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 +369,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); @@ -370,16 +381,12 @@ private async Task BlazorWasmHostedTemplate_IndividualAuth_Works(BrowserKind bro } } - [Theory] - [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[] { + // This test is required to run before BlazorWasmStandaloneTemplate_IndividualAuth_Works to create and build the project + // If this test is quarantined, BlazorWasmStandaloneTemplate_IndividualAuth_Works must be quarantined as well + [Theory, TestPriority(BUILDCREATEPUBLISH_PRIORITY)] + [InlineData(BrowserKind.Chromium)] + public Task BlazorWasmStandaloneTemplate_CreateBuildPublish_IndividualAuth(BrowserKind browserKind) + => CreateBuildPublishAsync("blazorstandaloneindividual" + browserKind, args: new[] { "-au", "Individual", "--authority", @@ -388,17 +395,12 @@ 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)); + // This test depends on BlazorWasmStandaloneTemplate_CreateBuildPublish_IndividualAuth running first + [Theory] + [InlineData(BrowserKind.Chromium, Skip = "https://github.com/dotnet/aspnetcore/issues/28596")] + public async Task BlazorWasmStandaloneTemplate_IndividualAuth_Works(BrowserKind browserKind) + { + var project = await ProjectFactory.GetOrCreateProject("blazorstandaloneindividual" + browserKind, Output); // 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. @@ -410,7 +412,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 +491,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 +503,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 +602,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 +640,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/BlazorTemplates.Tests/PriorityOrderer.cs b/src/ProjectTemplates/BlazorTemplates.Tests/PriorityOrderer.cs new file mode 100644 index 000000000000..e1ebea357e3e --- /dev/null +++ b/src/ProjectTemplates/BlazorTemplates.Tests/PriorityOrderer.cs @@ -0,0 +1,42 @@ +using System.Collections.Generic; +using System.Linq; +using Xunit.Abstractions; +using Xunit.Sdk; + +namespace Templates.Test +{ + public class PriorityOrderer : ITestCaseOrderer + { + public IEnumerable<TTestCase> OrderTestCases<TTestCase>( + IEnumerable<TTestCase> testCases) where TTestCase : ITestCase + { + string assemblyName = typeof(TestPriorityAttribute).AssemblyQualifiedName!; + var sortedMethods = new SortedDictionary<int, List<TTestCase>>(); + foreach (TTestCase testCase in testCases) + { + int priority = testCase.TestMethod.Method + .GetCustomAttributes(assemblyName) + .FirstOrDefault() + ?.GetNamedArgument<int>(nameof(TestPriorityAttribute.Priority)) ?? 0; + + GetOrCreate(sortedMethods, priority).Add(testCase); + } + + foreach (TTestCase testCase in + sortedMethods.Keys.SelectMany( + priority => sortedMethods[priority].OrderBy( + testCase => testCase.TestMethod.Method.Name))) + { + yield return testCase; + } + } + + private static TValue GetOrCreate<TKey, TValue>( + IDictionary<TKey, TValue> dictionary, TKey key) + where TKey : struct + where TValue : new() => + dictionary.TryGetValue(key, out TValue result) + ? result + : (dictionary[key] = new TValue()); + } +} diff --git a/src/ProjectTemplates/BlazorTemplates.Tests/TestPriorityAttribute.cs b/src/ProjectTemplates/BlazorTemplates.Tests/TestPriorityAttribute.cs new file mode 100644 index 000000000000..2ee0775515b8 --- /dev/null +++ b/src/ProjectTemplates/BlazorTemplates.Tests/TestPriorityAttribute.cs @@ -0,0 +1,11 @@ +using System; + +namespace Templates.Test +{ + [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] + public class TestPriorityAttribute : Attribute + { + public int Priority { get; private set; } + public TestPriorityAttribute(int priority) => Priority = priority; + } +} 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)