diff --git a/.azure/pipelines/helix-matrix.yml b/.azure/pipelines/helix-matrix.yml index 558afc7bbaa2..520fb47cdf77 100644 --- a/.azure/pipelines/helix-matrix.yml +++ b/.azure/pipelines/helix-matrix.yml @@ -1,5 +1,9 @@ # We only want to run full helix matrix on main -pr: none +pr: + autoCancel: true + branches: + include: + - '*' trigger: none schedules: # Cron timezone is UTC. diff --git a/eng/helix/content/RunTests/Program.cs b/eng/helix/content/RunTests/Program.cs index 349ab29b8a03..4de3d6537319 100644 --- a/eng/helix/content/RunTests/Program.cs +++ b/eng/helix/content/RunTests/Program.cs @@ -27,6 +27,12 @@ static async Task Main(string[] args) { keepGoing = runner.InstallAspNetRefIfNeeded(); } +#if INSTALLPLAYWRIGHT + if (keepGoing) + { + keepGoing = await runner.InstallPlaywrightAsync(); + } +#endif runner.DisplayContents(); diff --git a/eng/helix/content/RunTests/RunTests.csproj b/eng/helix/content/RunTests/RunTests.csproj index 7fc0c8c63888..948c3b72dce6 100644 --- a/eng/helix/content/RunTests/RunTests.csproj +++ b/eng/helix/content/RunTests/RunTests.csproj @@ -3,9 +3,11 @@ Exe net6.0 + $(DefineConstants);INSTALLPLAYWRIGHT + diff --git a/eng/helix/content/RunTests/RunTestsOptions.cs b/eng/helix/content/RunTests/RunTestsOptions.cs index 031326ba8a14..beaf08f19595 100644 --- a/eng/helix/content/RunTests/RunTestsOptions.cs +++ b/eng/helix/content/RunTests/RunTestsOptions.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.CommandLine; +using System.Globalization; using System.IO; using System.Runtime.InteropServices; using System.Threading.Tasks; @@ -67,7 +68,7 @@ public static RunTestsOptions Parse(string[] args) Quarantined = parseResult.ValueForOption("--quarantined"), RuntimeVersion = sharedFxVersion, Target = parseResult.ValueForOption("--target"), - Timeout = TimeSpan.Parse(parseResult.ValueForOption("--helixTimeout")), + Timeout = TimeSpan.Parse(parseResult.ValueForOption("--helixTimeout"), CultureInfo.InvariantCulture), // When targeting pack builds, it has exactly the same version as the shared framework. AspNetRef = $"Microsoft.AspNetCore.App.Ref.{sharedFxVersion}.nupkg", diff --git a/eng/helix/content/RunTests/TestRunner.cs b/eng/helix/content/RunTests/TestRunner.cs index 2aeb1c5abca7..2a48452d7356 100644 --- a/eng/helix/content/RunTests/TestRunner.cs +++ b/eng/helix/content/RunTests/TestRunner.cs @@ -8,6 +8,9 @@ using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; +#if INSTALLPLAYWRIGHT +using PlaywrightSharp; +#endif namespace RunTests { @@ -40,6 +43,13 @@ public bool SetupEnvironment() Console.WriteLine($"Set DotNetEfFullPath: {dotnetEFFullPath}"); EnvironmentVariables.Add("DotNetEfFullPath", dotnetEFFullPath); +#if INSTALLPLAYWRIGHT + // Playwright will download and look for browsers to this directory + var playwrightBrowsers = Path.Combine(helixDir, "ms-playwright"); + Console.WriteLine($"Setting PLAYWRIGHT_BROWSERS_PATH: {playwrightBrowsers}"); + EnvironmentVariables.Add("PLAYWRIGHT_BROWSERS_PATH", playwrightBrowsers); +#endif + Console.WriteLine($"Creating nuget restore directory: {nugetRestore}"); Directory.CreateDirectory(nugetRestore); @@ -80,6 +90,23 @@ public void DisplayContents(string path = "./") } } +#if INSTALLPLAYWRIGHT + public async Task InstallPlaywrightAsync() + { + try + { + Console.WriteLine($"Installing Playwright to {EnvironmentVariables["PLAYWRIGHT_BROWSERS_PATH"]}"); + await Playwright.InstallAsync(EnvironmentVariables["PLAYWRIGHT_BROWSERS_PATH"]); + return true; + } + catch (Exception e) + { + Console.WriteLine($"Exception installing playwright: {e.ToString()}"); + return false; + } + } +#endif + public async Task InstallAspNetAppIfNeededAsync() { try @@ -136,6 +163,14 @@ await ProcessUtil.RunAsync($"{Options.DotnetRoot}/dotnet", throwOnError: false, cancellationToken: new CancellationTokenSource(TimeSpan.FromMinutes(2)).Token); + await ProcessUtil.RunAsync($"{Options.DotnetRoot}/dotnet", + $"tool install dotnet-serve --tool-path {Options.HELIX_WORKITEM_ROOT}", + environmentVariables: EnvironmentVariables, + outputDataReceived: Console.WriteLine, + errorDataReceived: Console.Error.WriteLine, + throwOnError: false, + cancellationToken: new CancellationTokenSource(TimeSpan.FromMinutes(2)).Token); + // ';' is the path separator on Windows, and ':' on Unix Options.Path += OperatingSystem.IsWindows() ? ";" : ":"; Options.Path += $"{Environment.GetEnvironmentVariable("DOTNET_CLI_HOME")}/.dotnet/tools"; @@ -231,7 +266,7 @@ public async Task RunTestsAsync() { // Timeout test run 5 minutes before the Helix job would timeout var cts = new CancellationTokenSource(Options.Timeout.Subtract(TimeSpan.FromMinutes(5))); - var commonTestArgs = $"test {Options.Target} --logger:xunit --logger:\"console;verbosity=normal\" --blame \"CollectHangDump;TestTimeout=5m\""; + var commonTestArgs = $"test {Options.Target} --logger:xunit --logger:\"console;verbosity=normal\" --blame \"CollectHangDump;TestTimeout=15m\""; if (Options.Quarantined) { Console.WriteLine("Running quarantined tests."); diff --git a/eng/helix/content/installPlaywrightReqs.ps1 b/eng/helix/content/installPlaywrightReqs.ps1 new file mode 100644 index 000000000000..2342511cdb4c --- /dev/null +++ b/eng/helix/content/installPlaywrightReqs.ps1 @@ -0,0 +1,12 @@ +Write-Host "Set-ExecutionPolicy Bypass -Scope Process" +Set-ExecutionPolicy Bypass -Scope Process +if ((Get-WindowsOptionalFeature -FeatureName ServerMediaFoundation -Online).State -eq "Enabled") { + Write-Host "ServerMediaFoundation feature already enabled." +} else { + Write-Host "Enable-WindowsOptionalFeature -Online -FeatureName ServerMediaFoundation (For Playwright)" + try { + Enable-WindowsOptionalFeature -Online -FeatureName ServerMediaFoundation + } catch { + Write-Host "Enable-WindowsOptionalFeature -Online -FeatureName ServerMediaFoundation threw an exception: $PSItem" + } +} diff --git a/eng/helix/content/runtests.ps1 b/eng/helix/content/runtests.ps1 index a6a7da67eede..2c0cb0b3a01b 100644 --- a/eng/helix/content/runtests.ps1 +++ b/eng/helix/content/runtests.ps1 @@ -8,11 +8,13 @@ param( [string]$Quarantined, [string]$EF, [string]$HelixTimeout, + [string]$InstallPlaywright, [string]$FeedCred ) $env:DOTNET_SKIP_FIRST_TIME_EXPERIENCE = 1 $env:DOTNET_MULTILEVEL_LOOKUP = 0 +$env:PLAYWRIGHT_BROWSERS_PATH = "$currentDirectory\ms-playwright" $currentDirectory = Get-Location $envPath = "$env:PATH;$env:HELIX_CORRELATION_PAYLOAD\node\bin" @@ -53,7 +55,6 @@ function InstallDotnetSDKAndRuntime([string]$Feed, [string]$FeedCredParam) { Write-Host "Set PATH to: $env:PATH" $success = InvokeInstallDotnet ". eng\common\tools.ps1; InstallDotNet $env:DOTNET_ROOT $SdkVersion $Arch `'`' `$true `'$Feed`' `'$FeedCredParam`' `$true" - if (!$success) { Write-Host "Retrying..." continue @@ -79,15 +80,15 @@ if ([string]::IsNullOrEmpty($FeedCred)) { InstallDotnetSDKAndRuntime "https://dotnetclimsrc.blob.core.windows.net/dotnet" $FeedCred } -Write-Host "Restore: dotnet restore RunTests\RunTests.csproj --ignore-failed-sources" -dotnet restore RunTests\RunTests.csproj --ignore-failed-sources +Write-Host "Restore: dotnet restore RunTests\RunTests.csproj --ignore-failed-sources /p:InstallPlaywright=$InstallPlaywright" +dotnet restore RunTests\RunTests.csproj --ignore-failed-sources /p:InstallPlaywright=$InstallPlaywright if ($LastExitCode -ne 0) { exit $LastExitCode } -Write-Host "Running tests: dotnet run --no-restore --project RunTests\RunTests.csproj -- --target $Target --runtime $AspRuntimeVersion --queue $Queue --arch $Arch --quarantined $Quarantined --ef $EF --helixTimeout $HelixTimeout" -dotnet run --no-restore --project RunTests\RunTests.csproj -- --target $Target --runtime $AspRuntimeVersion --queue $Queue --arch $Arch --quarantined $Quarantined --ef $EF --helixTimeout $HelixTimeout +Write-Host "Running tests: dotnet run --no-restore /p:InstallPlaywright=$InstallPlaywright --project RunTests\RunTests.csproj -- --target $Target --runtime $AspRuntimeVersion --queue $Queue --arch $Arch --quarantined $Quarantined --ef $EF --helixTimeout $HelixTimeout" +dotnet run --no-restore /p:InstallPlaywright=$InstallPlaywright --project RunTests\RunTests.csproj -- --target $Target --runtime $AspRuntimeVersion --queue $Queue --arch $Arch --quarantined $Quarantined --ef $EF --helixTimeout $HelixTimeout Write-Host "Finished running tests: exit_code=$LastExitCode" exit $LastExitCode diff --git a/eng/helix/content/runtests.sh b/eng/helix/content/runtests.sh index 30697a488fe5..1a6ee3a3125f 100644 --- a/eng/helix/content/runtests.sh +++ b/eng/helix/content/runtests.sh @@ -25,6 +25,9 @@ export DOTNET_CLI_HOME="$DIR/.home$RANDOM" export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 +# Set playwright browser path +export PLAYWRIGHT_BROWSERS_PATH="$DIR/ms-playwright" + RESET="\033[0m" RED="\033[0;31m" YELLOW="\033[0;33m" @@ -49,7 +52,7 @@ if [[ -z "${10:-}" ]]; then } else echo "InstallDotNet $DOTNET_ROOT $dotnet_sdk_version '' '' true https://dotnetclimsrc.blob.core.windows.net/dotnet ..." - InstallDotNet $DOTNET_ROOT $dotnet_sdk_version "" "" true https://dotnetclimsrc.blob.core.windows.net/dotnet ${10} || { + InstallDotNet $DOTNET_ROOT $dotnet_sdk_version "" "" true https://dotnetclimsrc.blob.core.windows.net/dotnet ${11} || { exit_code=$? Write-PipelineTelemetryError -Category 'InitializeToolset' -Message "dotnet-install.sh failed (exit code '$exit_code')." >&2 ExitWithExitCode $exit_code @@ -57,7 +60,7 @@ else echo echo "InstallDotNet $DOTNET_ROOT $dotnet_runtime_version '' dotnet true https://dotnetclimsrc.blob.core.windows.net/dotnet ..." - InstallDotNet $DOTNET_ROOT $dotnet_runtime_version "" dotnet true https://dotnetclimsrc.blob.core.windows.net/dotnet ${10} || { + InstallDotNet $DOTNET_ROOT $dotnet_runtime_version "" dotnet true https://dotnetclimsrc.blob.core.windows.net/dotnet ${11} || { exit_code=$? Write-PipelineTelemetryError -Category 'InitializeToolset' -Message "dotnet-install.sh failed (exit code '$exit_code')." >&2 ExitWithExitCode $exit_code diff --git a/eng/targets/Helix.Common.props b/eng/targets/Helix.Common.props index 451a4d93834b..c3fe23fb1241 100644 --- a/eng/targets/Helix.Common.props +++ b/eng/targets/Helix.Common.props @@ -8,14 +8,6 @@ - - - - - - - - diff --git a/eng/targets/Helix.targets b/eng/targets/Helix.targets index 955e68fd4add..9592591f1d87 100644 --- a/eng/targets/Helix.targets +++ b/eng/targets/Helix.targets @@ -8,6 +8,24 @@ + + Windows.7.Amd64.Open;Windows.81.Amd64.Open + + + + + + + + + + + + + + + + @@ -163,8 +181,8 @@ SharedFxVersion because that's needed even when the targeting pack isn't building. Use the BrowserDebugHost transport package as a sentinel for the non-shipping version of the NETCoreApp shared framework. --> - call runtests.cmd $(TargetFileName) $(NETCoreSdkVersion) $(MicrosoftNETCoreBrowserDebugHostTransportVersion) $(SharedFxVersion) $(_HelixFriendlyNameTargetQueue) $(TargetArchitecture) $(RunQuarantinedTests) $(DotnetEfVersion) $(HelixTimeout) $(DotNetRuntimeSourceFeedKey) - ./runtests.sh $(TargetFileName) $(NETCoreSdkVersion) $(MicrosoftNETCoreBrowserDebugHostTransportVersion) $(SharedFxVersion) $(_HelixFriendlyNameTargetQueue) $(TargetArchitecture) $(RunQuarantinedTests) $(DotnetEfVersion) $(HelixTimeout) $(DotNetRuntimeSourceFeedKey) + call runtests.cmd $(TargetFileName) $(NETCoreSdkVersion) $(MicrosoftNETCoreBrowserDebugHostTransportVersion) $(SharedFxVersion) $(_HelixFriendlyNameTargetQueue) $(TargetArchitecture) $(RunQuarantinedTests) $(DotnetEfVersion) $(HelixTimeout) $(TestDependsOnPlaywright) $(DotNetRuntimeSourceFeedKey) + ./runtests.sh $(TargetFileName) $(NETCoreSdkVersion) $(MicrosoftNETCoreBrowserDebugHostTransportVersion) $(SharedFxVersion) $(_HelixFriendlyNameTargetQueue) $(TargetArchitecture) $(RunQuarantinedTests) $(DotnetEfVersion) $(HelixTimeout) $(TestDependsOnPlaywright) $(DotNetRuntimeSourceFeedKey) $(HelixCommand) $(HelixTimeout) diff --git a/src/ProjectTemplates/BlazorTemplates.Tests/BlazorServerTemplateTest.cs b/src/ProjectTemplates/BlazorTemplates.Tests/BlazorServerTemplateTest.cs index de27f48c4cb6..0fc61a174167 100644 --- a/src/ProjectTemplates/BlazorTemplates.Tests/BlazorServerTemplateTest.cs +++ b/src/ProjectTemplates/BlazorTemplates.Tests/BlazorServerTemplateTest.cs @@ -8,6 +8,7 @@ using System.Runtime.InteropServices; using System.Threading.Tasks; using Microsoft.AspNetCore.BrowserTesting; +using Microsoft.AspNetCore.Testing; using PlaywrightSharp; using ProjectTemplates.Tests.Infrastructure; using Templates.Test.Helpers; diff --git a/src/ProjectTemplates/BlazorTemplates.Tests/BlazorTemplates.Tests.csproj b/src/ProjectTemplates/BlazorTemplates.Tests/BlazorTemplates.Tests.csproj index f932b3471666..8001d254d00c 100644 --- a/src/ProjectTemplates/BlazorTemplates.Tests/BlazorTemplates.Tests.csproj +++ b/src/ProjectTemplates/BlazorTemplates.Tests/BlazorTemplates.Tests.csproj @@ -6,10 +6,8 @@ true true - - false + true true - @@ -18,6 +16,8 @@ TestTemplates\ $([MSBuild]::EnsureTrailingSlash('$(RepoRoot)'))obj\template-restore\ true + true + true diff --git a/src/ProjectTemplates/BlazorTemplates.Tests/BlazorWasmTemplateTest.cs b/src/ProjectTemplates/BlazorTemplates.Tests/BlazorWasmTemplateTest.cs index 2f52d3e2aa30..abad1b3c4b4e 100644 --- a/src/ProjectTemplates/BlazorTemplates.Tests/BlazorWasmTemplateTest.cs +++ b/src/ProjectTemplates/BlazorTemplates.Tests/BlazorWasmTemplateTest.cs @@ -79,6 +79,8 @@ public async Task BlazorWasmStandaloneTemplate_Works(BrowserKind browserKind) EnsureBrowserAvailable(browserKind); } } + + Assert.True(0 == 1, "Fail to get logs"); } private async Task NavigateToPage(IBrowserContext browser, string listeningUri) @@ -654,7 +656,19 @@ private void UpdatePublishedSettings(Project serverProject) Output.WriteLine("Running dotnet serve on published output..."); var developmentCertificate = DevelopmentCertificate.Create(project.TemplateOutputDir); - var serveProcess = ProcessEx.Run(Output, publishDir, DotNetMuxer.MuxerPathOrDefault(), $"serve -S --pfx \"{developmentCertificate.CertificatePath}\" --pfx-pwd \"{developmentCertificate.CertificatePassword}\" --port 0"); + var args = $"-S --pfx \"{developmentCertificate.CertificatePath}\" --pfx-pwd \"{developmentCertificate.CertificatePassword}\" --port 0"; + var command = DotNetMuxer.MuxerPathOrDefault(); + if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable("HELIX_DIR"))) + { + args = $"serve " + args; + } + else + { + command = "dotnet-serve"; + args = "--roll-forward LatestMajor " + args; // dotnet-serve targets net5.0 by default + } + + var serveProcess = ProcessEx.Run(Output, publishDir, command, args); var listeningUri = ResolveListeningUrl(serveProcess); return (serveProcess, listeningUri); } diff --git a/src/ProjectTemplates/Shared/Project.cs b/src/ProjectTemplates/Shared/Project.cs index 68af9da053a1..bf2d1082587b 100644 --- a/src/ProjectTemplates/Shared/Project.cs +++ b/src/ProjectTemplates/Shared/Project.cs @@ -231,11 +231,7 @@ internal async Task RunDotNetEfUpdateDatabaseAsync() { var assembly = typeof(ProjectFactoryFixture).Assembly; - var dotNetEfFullPath = assembly.GetCustomAttributes() - .First(attribute => attribute.Key == "DotNetEfFullPath") - .Value; - - var args = $"\"{dotNetEfFullPath}\" --verbose --no-build database update"; + var args = "--verbose --no-build database update"; // Only run one instance of 'dotnet new' at once, as a workaround for // https://github.com/aspnet/templating/issues/63 @@ -243,7 +239,17 @@ internal async Task RunDotNetEfUpdateDatabaseAsync() try { Output.WriteLine("Acquired DotNetNewLock"); - using var result = ProcessEx.Run(Output, TemplateOutputDir, DotNetMuxer.MuxerPathOrDefault(), args); + var command = DotNetMuxer.MuxerPathOrDefault(); + if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable("DotNetEfFullPath"))) + { + args = $"\"{DotNetEfFullPath}\" " + args; + } + else + { + command = "dotnet-ef"; + } + + using var result = ProcessEx.Run(Output, TemplateOutputDir, command, args); await result.Exited; return new ProcessResult(result); } diff --git a/src/Servers/IIS/IIS/test/Directory.Build.props b/src/Servers/IIS/IIS/test/Directory.Build.props index 6f6f92116853..8b196e74ad18 100644 --- a/src/Servers/IIS/IIS/test/Directory.Build.props +++ b/src/Servers/IIS/IIS/test/Directory.Build.props @@ -8,6 +8,7 @@ true + true diff --git a/src/Shared/BrowserTesting/src/BrowserManager.cs b/src/Shared/BrowserTesting/src/BrowserManager.cs index ede459195318..20f970736e27 100644 --- a/src/Shared/BrowserTesting/src/BrowserManager.cs +++ b/src/Shared/BrowserTesting/src/BrowserManager.cs @@ -46,7 +46,7 @@ private async Task InitializeAsync() async Task InitializeCore() { - Playwright = await PlaywrightSharp.Playwright.CreateAsync(_loggerFactory, debug: "pw:api"); + Playwright = await PlaywrightSharp.Playwright.CreateAsync(_loggerFactory/*, debug: "pw:api"*/); foreach (var (browserName, options) in _browserManagerConfiguration.BrowserOptions) { if (!_launchBrowsers.ContainsKey(browserName)) diff --git a/src/Shared/BrowserTesting/src/ContextInformation.cs b/src/Shared/BrowserTesting/src/ContextInformation.cs index 07cadc4bf0f5..7aec84d972de 100644 --- a/src/Shared/BrowserTesting/src/ContextInformation.cs +++ b/src/Shared/BrowserTesting/src/ContextInformation.cs @@ -55,10 +55,25 @@ internal BrowserContextOptions ConfigureUniqueHarPath(BrowserContextOptions brow if (browserContextOptions?.RecordHar?.Path != null) { var identifier = Guid.NewGuid().ToString("N"); - browserContextOptions.RecordHar.Path = Path.Combine(browserContextOptions.RecordHar.Path, $"{identifier}.har"); + var harDirectory = Environment.GetEnvironmentVariable("HELIX_WORKITEM_UPLOAD_ROOT"); + if (string.IsNullOrEmpty(harDirectory)) + { + harDirectory = browserContextOptions.RecordHar.Path; + } + + browserContextOptions.RecordHar.Path = Path.Combine(harDirectory, $"{identifier}.har"); _harPath = browserContextOptions.RecordHar.Path; } + if (browserContextOptions?.RecordVideo?.Dir != null) + { + var uploadDir = Environment.GetEnvironmentVariable("HELIX_WORKITEM_UPLOAD_ROOT"); + if (!string.IsNullOrEmpty(uploadDir)) + { + browserContextOptions.RecordVideo.Dir = uploadDir; + } + } + return browserContextOptions; } }