diff --git a/eng/Npm.Workspace.nodeproj b/eng/Npm.Workspace.nodeproj
index 04e2ae3da87d..81c004313ac1 100644
--- a/eng/Npm.Workspace.nodeproj
+++ b/eng/Npm.Workspace.nodeproj
@@ -44,7 +44,6 @@
@@ -65,7 +64,9 @@
-
+
<_NpmGeneratedPackages Include="$(PackageOutputPath)/*.tgz" />
diff --git a/eng/tools/GenerateFiles/Directory.Build.targets.in b/eng/tools/GenerateFiles/Directory.Build.targets.in
index a3e5d4742794..a056fb9edcfa 100644
--- a/eng/tools/GenerateFiles/Directory.Build.targets.in
+++ b/eng/tools/GenerateFiles/Directory.Build.targets.in
@@ -122,8 +122,9 @@
- false
- false
+
+ false
+ false
false
diff --git a/src/ProjectTemplates/Shared/Project.cs b/src/ProjectTemplates/Shared/Project.cs
index 3b1c0588c96a..ed00a32f7f8e 100644
--- a/src/ProjectTemplates/Shared/Project.cs
+++ b/src/ProjectTemplates/Shared/Project.cs
@@ -66,13 +66,19 @@ internal async Task RunDotNetNewAsync(
bool useLocalDB = false,
bool noHttps = false,
bool errorOnRestoreError = true,
+ bool isItemTemplate = false,
string[] args = null,
// Used to set special options in MSBuild
IDictionary environmentVariables = null)
{
- var hiveArg = $" --debug:disable-sdk-templates --debug:custom-hive \"{TemplatePackageInstaller.CustomHivePath}\"";
+ var hiveArg = $"--debug:disable-sdk-templates --debug:custom-hive \"{TemplatePackageInstaller.CustomHivePath}\"";
var argString = $"new {templateName} {hiveArg}";
environmentVariables ??= new Dictionary();
+ if (!isItemTemplate)
+ {
+ argString += " --no-restore";
+ }
+
if (!string.IsNullOrEmpty(auth))
{
argString += $" --auth {auth}";
@@ -113,18 +119,30 @@ internal async Task RunDotNetNewAsync(
Directory.Delete(TemplateOutputDir, recursive: true);
}
- using var execution = ProcessEx.Run(Output, AppContext.BaseDirectory, DotNetMuxer.MuxerPathOrDefault(), argString, environmentVariables);
- await execution.Exited;
+ using var createExecution = ProcessEx.Run(Output, AppContext.BaseDirectory, DotNetMuxer.MuxerPathOrDefault(), argString, environmentVariables);
+ await createExecution.Exited;
- var result = new ProcessResult(execution);
+ var createResult = new ProcessResult(createExecution);
+ Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create", this, createResult));
- // Because dotnet new automatically restores but silently ignores restore errors, need to handle restore errors explicitly
- if (errorOnRestoreError && (execution.Output.Contains("Restore failed.") || execution.Error.Contains("Restore failed.")))
+ if (!isItemTemplate)
{
- result.ExitCode = -1;
- }
+ argString = "restore /bl";
+ using var restoreExecution = ProcessEx.Run(Output, TemplateOutputDir, DotNetMuxer.MuxerPathOrDefault(), argString, environmentVariables);
+ await restoreExecution.Exited;
- Assert.True(0 == result.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", this, result));
+ var restoreResult = new ProcessResult(restoreExecution);
+
+ // Because dotnet new automatically restores but silently ignores restore errors, need to handle restore errors explicitly
+ if (errorOnRestoreError && (restoreExecution.Output.Contains("Restore failed.") || restoreExecution.Error.Contains("Restore failed.")))
+ {
+ restoreResult.ExitCode = -1;
+ }
+
+ CaptureBinLogOnFailure(restoreExecution);
+
+ Assert.True(0 == restoreResult.ExitCode, ErrorMessages.GetFailedProcessMessage("restore", this, restoreResult));
+ }
}
internal async Task RunDotNetPublishAsync(IDictionary packageOptions = null, string additionalArgs = null, bool noRestore = true)
diff --git a/src/ProjectTemplates/TestInfrastructure/Directory.Build.props.in b/src/ProjectTemplates/TestInfrastructure/Directory.Build.props.in
index 88c7d6173021..31b2ee9d6124 100644
--- a/src/ProjectTemplates/TestInfrastructure/Directory.Build.props.in
+++ b/src/ProjectTemplates/TestInfrastructure/Directory.Build.props.in
@@ -3,6 +3,20 @@
${RepoRoot}
${ArtifactsBinDir}
+
+
+ ${TargetingPackLayoutRoot}
+ ${SharedFrameworkLayoutRoot}
+
+
+ true
+ true
diff --git a/src/ProjectTemplates/TestInfrastructure/PrepareForTest.targets b/src/ProjectTemplates/TestInfrastructure/PrepareForTest.targets
index 156b26ffb9cb..4e1a98035f07 100644
--- a/src/ProjectTemplates/TestInfrastructure/PrepareForTest.targets
+++ b/src/ProjectTemplates/TestInfrastructure/PrepareForTest.targets
@@ -26,6 +26,9 @@
$([MSBuild]::NormalizePath('$(OutputPath)$(TestTemplateCreationFolder)'))
$(TestTemplateCreationFolder)\Hives\$([System.Guid]::NewGuid())\.templateengine
+ $(TestTemplateCreationFolder)dotnet\
+ <_DotNetHostFileName>dotnet
+ <_DotNetHostFileName Condition="$([MSBuild]::IsOSPlatform(`Windows`))">dotnet.exe
@@ -48,10 +51,16 @@
<_Parameter1>TestTemplateCreationFolder
<_Parameter2>$(TestTemplateCreationFolder)
+
<_Parameter1>CustomTemplateHivePath
<_Parameter2>$(CustomTemplateHivePath)
+
+
+ <_Parameter1>DotNetHostOverride
+ <_Parameter2>$(TemplateTestDotNetRoot)$(_DotNetHostFileName)
+
@@ -72,6 +81,20 @@
+
+ <_FilesToCopy Include="$(LocalDotNetRoot)$(_DotNetHostFileName)" />
+ <_FilesToCopy Include="$(LocalDotNetRoot)host\**\*" DestinationRelativeFolder="host\" />
+ <_FilesToCopy Include="$(LocalDotNetRoot)shared\**\*" DestinationRelativeFolder="shared\" />
+ <_FilesToCopy Include="$(LocalDotNetRoot)sdk\**\*" DestinationRelativeFolder="sdk\" />
+ <_FilesToCopy Include="$(SharedFrameworkLayoutRoot)\**\*" />
+
+ <_DestinationFiles Include="@(_FilesToCopy->'$(TemplateTestDotNetRoot)%(DestinationRelativeFolder)%(RecursiveDir)%(Filename)%(Extension)')" />
+
+
+
+
@@ -84,7 +107,7 @@
diff --git a/src/ProjectTemplates/test/Templates.Blazor.Tests/BlazorTemplateTest.cs b/src/ProjectTemplates/test/Templates.Blazor.Tests/BlazorTemplateTest.cs
index cfded6021e29..f9bc0a14f181 100644
--- a/src/ProjectTemplates/test/Templates.Blazor.Tests/BlazorTemplateTest.cs
+++ b/src/ProjectTemplates/test/Templates.Blazor.Tests/BlazorTemplateTest.cs
@@ -1,13 +1,10 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System;
-using System.IO;
using System.Runtime.InteropServices;
-using System.Threading.Tasks;
using Microsoft.AspNetCore.BrowserTesting;
+using Microsoft.Playwright;
using Templates.Test.Helpers;
-using Xunit;
namespace BlazorTemplates.Tests;
@@ -18,14 +15,19 @@ public abstract class BlazorTemplateTest : BrowserTestBase
public BlazorTemplateTest(ProjectFactoryFixture projectFactory)
{
ProjectFactory = projectFactory;
- Microsoft.Playwright.Program.Main(new[] { "install" });
+ Microsoft.Playwright.Program.Main(["install"]);
}
public ProjectFactoryFixture ProjectFactory { get; set; }
public abstract string ProjectType { get; }
- protected async Task CreateBuildPublishAsync(string auth = null, string[] args = null, string targetFramework = null, bool serverProject = false, bool onlyCreate = false)
+ protected async Task CreateBuildPublishAsync(
+ string auth = null,
+ string[] args = null,
+ string targetFramework = null,
+ Func getTargetProject = null,
+ bool onlyCreate = false)
{
// Additional arguments are needed. See: https://github.com/dotnet/aspnetcore/issues/24278
Environment.SetEnvironmentVariable("EnableDefaultScopedCssItems", "true");
@@ -38,21 +40,17 @@ protected async Task CreateBuildPublishAsync(string auth = null, string
await project.RunDotNetNewAsync(ProjectType, auth: auth, args: args);
+ project = getTargetProject?.Invoke(project) ?? project;
+
if (!onlyCreate)
{
- var targetProject = project;
- if (serverProject)
- {
- targetProject = GetSubProject(project, "Server", $"{project.ProjectName}.Server");
- }
-
- await targetProject.RunDotNetPublishAsync(noRestore: false);
+ await project.RunDotNetPublishAsync(noRestore: false);
// 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.
- await targetProject.RunDotNetBuildAsync();
+ await project.RunDotNetBuildAsync();
}
return project;
@@ -83,6 +81,138 @@ public static bool TryValidateBrowserRequired(BrowserKind browserKind, bool isRe
return isRequired;
}
+ protected async Task TestBasicInteractionInNewPageAsync(
+ BrowserKind browserKind,
+ string listeningUri,
+ string appName,
+ BlazorTemplatePages pagesToExclude = BlazorTemplatePages.None,
+ bool usesAuth = false)
+ {
+ if (!BrowserManager.IsAvailable(browserKind))
+ {
+ EnsureBrowserAvailable(browserKind);
+ return;
+ }
+
+ await using var browser = await BrowserManager.GetBrowserInstance(browserKind, BrowserContextInfo);
+ var page = await browser.NewPageAsync();
+
+ Output.WriteLine($"Opening browser at {listeningUri}...");
+ await page.GotoAsync(listeningUri, new() { WaitUntil = WaitUntilState.NetworkIdle });
+
+ await TestBasicInteractionAsync(page, appName, pagesToExclude, usesAuth);
+
+ await page.CloseAsync();
+ }
+
+ protected async Task TestBasicInteractionAsync(
+ IPage page,
+ string appName,
+ BlazorTemplatePages pagesToExclude = BlazorTemplatePages.None,
+ bool usesAuth = false)
+ {
+ await page.WaitForSelectorAsync("nav");
+
+ if (!pagesToExclude.HasFlag(BlazorTemplatePages.Home))
+ {
+ // Initially displays the home page
+ await page.WaitForSelectorAsync("h1 >> text=Hello, world!");
+
+ Assert.Equal("Home", (await page.TitleAsync()).Trim());
+ }
+
+ if (!pagesToExclude.HasFlag(BlazorTemplatePages.Counter))
+ {
+ // Can navigate to the counter page
+ await Task.WhenAll(
+ page.WaitForNavigationAsync(new() { UrlString = "**/counter" }),
+ page.WaitForSelectorAsync("h1 >> text=Counter"),
+ page.WaitForSelectorAsync("p >> text=Current count: 0"),
+ page.ClickAsync("a[href=counter]"));
+
+ // Clicking the counter button works
+ await IncrementCounterAsync(page);
+ }
+
+ if (usesAuth)
+ {
+ await Task.WhenAll(
+ page.WaitForNavigationAsync(new() { UrlString = "**/Identity/Account/Login**", WaitUntil = WaitUntilState.NetworkIdle }),
+ page.ClickAsync("text=Log in"));
+
+ await Task.WhenAll(
+ page.WaitForSelectorAsync("[name=\"Input.Email\"]"),
+ page.WaitForNavigationAsync(new() { UrlString = "**/Identity/Account/Register**", WaitUntil = WaitUntilState.NetworkIdle }),
+ page.ClickAsync("text=Register as a new user"));
+
+ var userName = $"{Guid.NewGuid()}@example.com";
+ var password = "[PLACEHOLDER]-1a";
+
+ await page.TypeAsync("[name=\"Input.Email\"]", userName);
+ await page.TypeAsync("[name=\"Input.Password\"]", password);
+ await page.TypeAsync("[name=\"Input.ConfirmPassword\"]", password);
+
+ // We will be redirected to the RegisterConfirmation
+ await Task.WhenAll(
+ page.WaitForNavigationAsync(new() { UrlString = "**/Identity/Account/RegisterConfirmation**", WaitUntil = WaitUntilState.NetworkIdle }),
+ page.ClickAsync("#registerSubmit"));
+
+ // We will be redirected to the ConfirmEmail
+ await Task.WhenAll(
+ page.WaitForNavigationAsync(new() { UrlString = "**/Identity/Account/ConfirmEmail**", WaitUntil = WaitUntilState.NetworkIdle }),
+ page.ClickAsync("text=Click here to confirm your account"));
+
+ // Now we can login
+ await page.ClickAsync("text=Login");
+ await page.WaitForSelectorAsync("[name=\"Input.Email\"]");
+ await page.TypeAsync("[name=\"Input.Email\"]", userName);
+ await page.TypeAsync("[name=\"Input.Password\"]", password);
+ await page.ClickAsync("#login-submit");
+
+ // Need to navigate to fetch page
+ await page.GotoAsync(new Uri(page.Url).GetLeftPart(UriPartial.Authority));
+ Assert.Equal(appName.Trim(), (await page.TitleAsync()).Trim());
+ }
+
+ if (!pagesToExclude.HasFlag(BlazorTemplatePages.Weather))
+ {
+ await page.ClickAsync("a[href=weather]");
+ await page.WaitForSelectorAsync("h1 >> text=Weather");
+
+ // Asynchronously loads and displays the table of weather forecasts
+ await page.WaitForSelectorAsync("table>tbody>tr");
+ Assert.Equal(5, await page.Locator("p+table>tbody>tr").CountAsync());
+ }
+
+ static async Task IncrementCounterAsync(IPage page)
+ {
+ // Allow multiple click attempts because some interactive render modes
+ // won't be immediately available
+ const int MaxIncrementAttempts = 5;
+ const float IncrementTimeoutMilliseconds = 3000f;
+ for (var i = 0; i < MaxIncrementAttempts; i++)
+ {
+ await page.ClickAsync("p+button >> text=Click me");
+ try
+ {
+ await page.WaitForSelectorAsync("p >> text=Current count: 1", new()
+ {
+ Timeout = IncrementTimeoutMilliseconds,
+ });
+
+ // The counter successfully incremented, so we're done
+ return;
+ }
+ catch (TimeoutException)
+ {
+ // The counter did not increment; try again
+ }
+ }
+
+ Assert.Fail($"The counter did not increment after {MaxIncrementAttempts} attempts");
+ }
+ }
+
protected void EnsureBrowserAvailable(BrowserKind browserKind)
{
Assert.False(
@@ -92,4 +222,14 @@ protected void EnsureBrowserAvailable(BrowserKind browserKind)
out var errorMessage),
errorMessage);
}
+
+ [Flags]
+ protected enum BlazorTemplatePages
+ {
+ None = 0,
+ Home = 1,
+ Counter = 2,
+ Weather = 4,
+ All = ~0,
+ }
}
diff --git a/src/ProjectTemplates/test/Templates.Blazor.Tests/BlazorWasmTemplateTest.cs b/src/ProjectTemplates/test/Templates.Blazor.Tests/BlazorWasmTemplateTest.cs
index aadcabbea21b..1329beb45531 100644
--- a/src/ProjectTemplates/test/Templates.Blazor.Tests/BlazorWasmTemplateTest.cs
+++ b/src/ProjectTemplates/test/Templates.Blazor.Tests/BlazorWasmTemplateTest.cs
@@ -2,130 +2,81 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Net;
-using System.Net.Http;
-using System.Net.Http.Headers;
using System.Text.Json;
using System.Text.RegularExpressions;
using Microsoft.AspNetCore.BrowserTesting;
using Microsoft.AspNetCore.Internal;
-using Microsoft.AspNetCore.InternalTesting;
using Microsoft.Extensions.CommandLineUtils;
using Microsoft.Playwright;
using Templates.Test.Helpers;
namespace BlazorTemplates.Tests;
-public class BlazorWasmTemplateTest : BlazorTemplateTest
+public class BlazorWasmTemplateTest(ProjectFactoryFixture projectFactory) : BlazorTemplateTest(projectFactory)
{
- public BlazorWasmTemplateTest(ProjectFactoryFixture projectFactory)
- : base(projectFactory) { }
-
public override string ProjectType { get; } = "blazorwasm";
[Theory]
- [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/47225")]
[InlineData(BrowserKind.Chromium)]
public async Task BlazorWasmStandaloneTemplate_Works(BrowserKind browserKind)
{
var project = await CreateBuildPublishAsync();
+ var appName = project.ProjectName;
// 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");
- await BuildAndRunTest(project.ProjectName, project, browserKind);
+ // Test the built project
+ using (var aspNetProcess = project.StartBuiltProjectAsync())
+ {
+ Assert.False(
+ aspNetProcess.Process.HasExited,
+ ErrorMessages.GetFailedProcessMessageOrEmpty("Run built project", project, aspNetProcess.Process));
+ await aspNetProcess.AssertStatusCode("/", HttpStatusCode.OK, "text/html");
+ await TestBasicInteractionInNewPageAsync(browserKind, aspNetProcess.ListeningUri.AbsoluteUri, appName);
+ }
+
+ // Test the published project
var (serveProcess, listeningUri) = RunPublishedStandaloneBlazorProject(project);
using (serveProcess)
{
- Output.WriteLine($"Opening browser at {listeningUri}...");
- if (BrowserManager.IsAvailable(browserKind))
- {
- await using var browser = await BrowserManager.GetBrowserInstance(browserKind, BrowserContextInfo);
- var page = await NavigateToPage(browser, listeningUri);
- await TestBasicNavigation(project.ProjectName, page);
- }
- else
- {
- EnsureBrowserAvailable(browserKind);
- }
+ await TestBasicInteractionInNewPageAsync(browserKind, listeningUri, appName);
}
}
- private static async Task NavigateToPage(IBrowserContext browser, string listeningUri)
- {
- var page = await browser.NewPageAsync();
- await page.GotoAsync(listeningUri, new() { WaitUntil = WaitUntilState.NetworkIdle });
- return page;
- }
-
- [Theory(Skip="https://github.com/dotnet/aspnetcore/issues/46430")]
+ [Theory]
[InlineData(BrowserKind.Chromium)]
- public async Task BlazorWasmHostedTemplate_Works(BrowserKind browserKind)
+ public async Task BlazorWasmStandalonePwaTemplate_Works(BrowserKind browserKind)
{
- var project = await CreateBuildPublishAsync(args: new[] { "--hosted" }, serverProject: true);
-
- var serverProject = GetSubProject(project, "Server", $"{project.ProjectName}.Server");
-
- await BuildAndRunTest(project.ProjectName, serverProject, browserKind);
-
- using var aspNetProcess = serverProject.StartPublishedProjectAsync();
-
- Assert.False(
- aspNetProcess.Process.HasExited,
- ErrorMessages.GetFailedProcessMessageOrEmpty("Run published project", serverProject, aspNetProcess.Process));
+ var project = await CreateBuildPublishAsync(args: ["--pwa"]);
+ var appName = project.ProjectName;
- await aspNetProcess.AssertStatusCode("/", HttpStatusCode.OK, "text/html");
- await AssertCompressionFormat(aspNetProcess, "br");
-
- if (BrowserManager.IsAvailable(browserKind))
- {
- await using var browser = await BrowserManager.GetBrowserInstance(browserKind, BrowserContextInfo);
- var page = await browser.NewPageAsync();
- await aspNetProcess.VisitInBrowserAsync(page);
- await TestBasicNavigation(project.ProjectName, page);
- }
- else
- {
- EnsureBrowserAvailable(browserKind);
- }
- }
-
- private static async Task AssertCompressionFormat(AspNetProcess aspNetProcess, string expectedEncoding)
- {
- var response = await aspNetProcess.SendRequest(() =>
+ // Test the built project
+ using (var aspNetProcess = project.StartBuiltProjectAsync())
{
- var request = new HttpRequestMessage(HttpMethod.Get, new Uri(aspNetProcess.ListeningUri, "/_framework/blazor.boot.json"));
- // These are the same as chrome
- request.Headers.AcceptEncoding.Clear();
- request.Headers.AcceptEncoding.Add(StringWithQualityHeaderValue.Parse("gzip"));
- request.Headers.AcceptEncoding.Add(StringWithQualityHeaderValue.Parse("deflate"));
- request.Headers.AcceptEncoding.Add(StringWithQualityHeaderValue.Parse("br"));
-
- return request;
- });
- Assert.Equal(expectedEncoding, response.Content.Headers.ContentEncoding.Single());
- }
-
- [Theory(Skip = "https://github.com/dotnet/aspnetcore/issues/45736")]
- [InlineData(BrowserKind.Chromium)]
- public async Task BlazorWasmStandalonePwaTemplate_Works(BrowserKind browserKind)
- {
- var project = await CreateBuildPublishAsync(args: new[] { "--pwa" });
+ Assert.False(
+ aspNetProcess.Process.HasExited,
+ ErrorMessages.GetFailedProcessMessageOrEmpty("Run built project", project, aspNetProcess.Process));
- await BuildAndRunTest(project.ProjectName, project, browserKind);
+ await aspNetProcess.AssertStatusCode("/", HttpStatusCode.OK, "text/html");
+ await TestBasicInteractionInNewPageAsync(browserKind, aspNetProcess.ListeningUri.AbsoluteUri, appName);
+ }
ValidatePublishedServiceWorker(project);
+ // Test the published project
if (BrowserManager.IsAvailable(browserKind))
{
var (serveProcess, listeningUri) = RunPublishedStandaloneBlazorProject(project);
await using var browser = await BrowserManager.GetBrowserInstance(browserKind, BrowserContextInfo);
Output.WriteLine($"Opening browser at {listeningUri}...");
- var page = await NavigateToPage(browser, listeningUri);
+ var page = await browser.NewPageAsync();
+ await page.GotoAsync(listeningUri, new() { WaitUntil = WaitUntilState.NetworkIdle });
using (serveProcess)
{
- await TestBasicNavigation(project.ProjectName, page);
+ await TestBasicInteractionAsync(page, project.ProjectName);
}
// The PWA template supports offline use. By now, the browser should have cached everything it needs,
@@ -133,7 +84,7 @@ public async Task BlazorWasmStandalonePwaTemplate_Works(BrowserKind browserKind)
await page.GotoAsync("about:blank");
await browser.SetOfflineAsync(true);
await page.GotoAsync(listeningUri);
- await TestBasicNavigation(project.ProjectName, page, skipFetchData: true);
+ await TestBasicInteractionAsync(page, project.ProjectName, pagesToExclude: BlazorTemplatePages.Weather);
await page.CloseAsync();
}
else
@@ -142,52 +93,10 @@ public async Task BlazorWasmStandalonePwaTemplate_Works(BrowserKind browserKind)
}
}
- [Theory(Skip = "https://github.com/dotnet/aspnetcore/issues/45736")]
- [InlineData(BrowserKind.Chromium)]
- public async Task BlazorWasmHostedPwaTemplate_Works(BrowserKind browserKind)
- {
- var project = await CreateBuildPublishAsync(args: new[] { "--hosted", "--pwa" }, serverProject: true);
-
- var serverProject = GetSubProject(project, "Server", $"{project.ProjectName}.Server");
-
- await BuildAndRunTest(project.ProjectName, serverProject, browserKind);
-
- ValidatePublishedServiceWorker(serverProject);
-
- string listeningUri = null;
- if (BrowserManager.IsAvailable(browserKind))
- {
- await using var browser = await BrowserManager.GetBrowserInstance(browserKind, BrowserContextInfo);
- IPage page = null;
- using (var aspNetProcess = serverProject.StartPublishedProjectAsync())
- {
- Assert.False(
- aspNetProcess.Process.HasExited,
- ErrorMessages.GetFailedProcessMessageOrEmpty("Run published project", serverProject, aspNetProcess.Process));
-
- await aspNetProcess.AssertStatusCode("/", HttpStatusCode.OK, "text/html");
- page = await browser.NewPageAsync();
- await aspNetProcess.VisitInBrowserAsync(page);
- await TestBasicNavigation(project.ProjectName, page);
-
- // Note: we don't want to use aspNetProcess.ListeningUri because that isn't necessarily the HTTPS URI
- listeningUri = new Uri(page.Url).GetLeftPart(UriPartial.Authority);
- }
-
- // The PWA template supports offline use. By now, the browser should have cached everything it needs,
- // so we can continue working even without the server.
- // Since this is the hosted project, backend APIs won't work offline, so we need to skip "fetchdata"
- await page.GotoAsync("about:blank");
- await browser.SetOfflineAsync(true);
- await page.GotoAsync(listeningUri);
- await TestBasicNavigation(project.ProjectName, page, skipFetchData: true);
- await page.CloseAsync();
- }
- else
- {
- EnsureBrowserAvailable(browserKind);
- }
- }
+ [Theory]
+ [MemberData(nameof(TemplateData))]
+ public Task BlazorWasmStandaloneTemplate_AzureActiveDirectoryTemplate_Works(TemplateInstance instance)
+ => CreateBuildPublishAsync(args: instance.Arguments, targetFramework: "netstandard2.1");
private static void ValidatePublishedServiceWorker(Project project)
{
@@ -210,50 +119,19 @@ private static void ValidatePublishedServiceWorker(Project project)
var serviceWorkerAssetsManifestVersionJson = serviceWorkerAssetsManifestVersionMatch.Groups[1].Captures[0].Value;
var serviceWorkerAssetsManifestVersion = JsonSerializer.Deserialize(serviceWorkerAssetsManifestVersionJson);
Assert.True(serviceWorkerContents.Contains($"/* Manifest version: {serviceWorkerAssetsManifestVersion} */", StringComparison.Ordinal));
+
+ static string ReadFile(string basePath, string path)
+ {
+ var fullPath = Path.Combine(basePath, path);
+ var doesExist = File.Exists(fullPath);
+
+ Assert.True(doesExist, $"Expected file to exist, but it doesn't: {path}");
+ return File.ReadAllText(Path.Combine(basePath, path));
+ }
}
public static TheoryData TemplateData => new TheoryData
{
- new TemplateInstance(
- "blazorwasmhostedaadb2c", "-ho",
- "-au", "IndividualB2C",
- "--aad-b2c-instance", "example.b2clogin.com",
- "-ssp", "b2c_1_siupin",
- "--client-id", "clientId",
- "--domain", "my-domain",
- "--default-scope", "full",
- "--app-id-uri", "ApiUri",
- "--api-client-id", "1234123413241324"),
- new TemplateInstance(
- "blazorwasmhostedaad", "-ho",
- "-au", "SingleOrg",
- "--domain", "my-domain",
- "--tenant-id", "tenantId",
- "--client-id", "clientId",
- "--default-scope", "full",
- "--app-id-uri", "ApiUri",
- "--api-client-id", "1234123413241324"),
- new TemplateInstance(
- "blazorwasmhostedaadgraph", "-ho",
- "-au", "SingleOrg",
- "--calls-graph",
- "--domain", "my-domain",
- "--tenant-id", "tenantId",
- "--client-id", "clientId",
- "--default-scope", "full",
- "--app-id-uri", "ApiUri",
- "--api-client-id", "1234123413241324"),
- new TemplateInstance(
- "blazorwasmhostedaadapi", "-ho",
- "-au", "SingleOrg",
- "--called-api-url", "\"https://graph.microsoft.com\"",
- "--called-api-scopes", "user.readwrite",
- "--domain", "my-domain",
- "--tenant-id", "tenantId",
- "--client-id", "clientId",
- "--default-scope", "full",
- "--app-id-uri", "ApiUri",
- "--api-client-id", "1234123413241324"),
new TemplateInstance(
"blazorwasmstandaloneaadb2c",
"-au", "IndividualB2C",
@@ -281,160 +159,50 @@ public TemplateInstance(string name, params string[] arguments)
public string[] Arguments { get; }
}
- [Theory(Skip = "https://github.com/dotnet/aspnetcore/issues/37782")]
- [MemberData(nameof(TemplateData))]
- public Task BlazorWasmHostedTemplate_AzureActiveDirectoryTemplate_Works(TemplateInstance instance)
- => CreateBuildPublishAsync(args: instance.Arguments, targetFramework: "netstandard2.1");
-
- protected async Task BuildAndRunTest(string appName, Project project, BrowserKind browserKind, bool usesAuth = false)
- {
- using var aspNetProcess = project.StartBuiltProjectAsync();
-
- Assert.False(
- aspNetProcess.Process.HasExited,
- ErrorMessages.GetFailedProcessMessageOrEmpty("Run built project", project, aspNetProcess.Process));
-
- await aspNetProcess.AssertStatusCode("/", HttpStatusCode.OK, "text/html");
- if (BrowserManager.IsAvailable(browserKind))
- {
- await using var browser = await BrowserManager.GetBrowserInstance(browserKind, BrowserContextInfo);
- var page = await browser.NewPageAsync();
- await aspNetProcess.VisitInBrowserAsync(page);
- await TestBasicNavigation(appName, page, usesAuth);
- await page.CloseAsync();
- }
- else
- {
- EnsureBrowserAvailable(browserKind);
- }
- }
-
- private static async Task TestBasicNavigation(string appName, IPage page, bool usesAuth = false, bool skipFetchData = false)
- {
- await page.WaitForSelectorAsync("nav");
-
- // Initially displays the home page
- await page.WaitForSelectorAsync("h1 >> text=Hello, world!");
-
- Assert.Equal("Home", (await page.TitleAsync()).Trim());
-
- // Can navigate to the counter page
- await Task.WhenAll(
- page.WaitForNavigationAsync(new() { UrlString = "**/counter" }),
- page.WaitForSelectorAsync("h1 >> text=Counter"),
- page.WaitForSelectorAsync("p >> text=Current count: 0"),
- page.ClickAsync("a[href=counter]"));
-
- // Clicking the counter button works
- await Task.WhenAll(
- page.WaitForSelectorAsync("p >> text=Current count: 1"),
- page.ClickAsync("p+button >> text=Click me"));
-
- if (usesAuth)
- {
- await Task.WhenAll(
- page.WaitForNavigationAsync(new() { UrlString = "**/Identity/Account/Login**", WaitUntil = WaitUntilState.NetworkIdle }),
- page.ClickAsync("text=Log in"));
-
- await Task.WhenAll(
- page.WaitForSelectorAsync("[name=\"Input.Email\"]"),
- page.WaitForNavigationAsync(new() { UrlString = "**/Identity/Account/Register**", WaitUntil = WaitUntilState.NetworkIdle }),
- page.ClickAsync("text=Register as a new user"));
-
- var userName = $"{Guid.NewGuid()}@example.com";
- var password = "[PLACEHOLDER]-1a";
-
- await page.TypeAsync("[name=\"Input.Email\"]", userName);
- await page.TypeAsync("[name=\"Input.Password\"]", password);
- await page.TypeAsync("[name=\"Input.ConfirmPassword\"]", password);
-
- // We will be redirected to the RegisterConfirmation
- await Task.WhenAll(
- page.WaitForNavigationAsync(new() { UrlString = "**/Identity/Account/RegisterConfirmation**", WaitUntil = WaitUntilState.NetworkIdle }),
- page.ClickAsync("#registerSubmit"));
-
- // We will be redirected to the ConfirmEmail
- await Task.WhenAll(
- page.WaitForNavigationAsync(new() { UrlString = "**/Identity/Account/ConfirmEmail**", WaitUntil = WaitUntilState.NetworkIdle }),
- page.ClickAsync("text=Click here to confirm your account"));
-
- // Now we can login
- await page.ClickAsync("text=Login");
- await page.WaitForSelectorAsync("[name=\"Input.Email\"]");
- await page.TypeAsync("[name=\"Input.Email\"]", userName);
- await page.TypeAsync("[name=\"Input.Password\"]", password);
- await page.ClickAsync("#login-submit");
-
- // Need to navigate to fetch page
- await page.GotoAsync(new Uri(page.Url).GetLeftPart(UriPartial.Authority));
- Assert.Equal(appName.Trim(), (await page.TitleAsync()).Trim());
- }
-
- if (!skipFetchData)
- {
- await page.ClickAsync("a[href=weather]");
- await page.WaitForSelectorAsync("h1 >> text=Weather");
-
- // Asynchronously loads and displays the table of weather forecasts
- await page.WaitForSelectorAsync("table>tbody>tr");
- Assert.Equal(5, await page.Locator("p+table>tbody>tr").CountAsync());
- }
- }
-
- private static string ReadFile(string basePath, string path)
- {
- var fullPath = Path.Combine(basePath, path);
- var doesExist = File.Exists(fullPath);
-
- Assert.True(doesExist, $"Expected file to exist, but it doesn't: {path}");
- return File.ReadAllText(Path.Combine(basePath, path));
- }
-
private (ProcessEx, string url) RunPublishedStandaloneBlazorProject(Project project)
{
var publishDir = Path.Combine(project.TemplatePublishDir, "wwwroot");
Output.WriteLine("Running dotnet serve on published output...");
- var developmentCertificate = DevelopmentCertificate.Create(project.TemplateOutputDir);
- var args = $"-S --pfx \"{developmentCertificate.CertificatePath}\" --pfx-pwd \"{developmentCertificate.CertificatePassword}\" --port 0";
var command = DotNetMuxer.MuxerPathOrDefault();
+ string args;
if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable("HELIX_DIR")))
{
- args = $"serve " + args;
+ args = $"serve ";
}
else
{
command = "dotnet-serve";
- args = "--roll-forward LatestMajor " + args; // dotnet-serve targets net5.0 by default
+ args = "--roll-forward LatestMajor"; // dotnet-serve targets net5.0 by default
}
var serveProcess = ProcessEx.Run(TestOutputHelper, publishDir, command, args);
var listeningUri = ResolveListeningUrl(serveProcess);
return (serveProcess, listeningUri);
- }
- private static string ResolveListeningUrl(ProcessEx process)
- {
- var buffer = new List();
- try
+ static string ResolveListeningUrl(ProcessEx process)
{
- foreach (var line in process.OutputLinesAsEnumerable)
+ var buffer = new List();
+ try
{
- if (line != null)
+ foreach (var line in process.OutputLinesAsEnumerable)
{
- buffer.Add(line);
- if (line.Trim().Contains("https://", StringComparison.Ordinal) || line.Trim().Contains("http://", StringComparison.Ordinal))
+ if (line != null)
{
- return line.Trim();
+ buffer.Add(line);
+ if (line.Trim().Contains("https://", StringComparison.Ordinal) || line.Trim().Contains("http://", StringComparison.Ordinal))
+ {
+ return line.Trim();
+ }
}
}
}
- }
- catch (OperationCanceledException)
- {
- }
+ catch (OperationCanceledException)
+ {
+ }
- throw new InvalidOperationException(@$"Couldn't find listening url:
-{string.Join(Environment.NewLine, buffer.Append(process.Error))}");
+ throw new InvalidOperationException(
+ $"Couldn't find listening url:\n{string.Join(Environment.NewLine, buffer.Append(process.Error))}");
+ }
}
}
diff --git a/src/ProjectTemplates/test/Templates.Blazor.Tests/BlazorWebTemplateTest.cs b/src/ProjectTemplates/test/Templates.Blazor.Tests/BlazorWebTemplateTest.cs
new file mode 100644
index 000000000000..dc2cf5b63982
--- /dev/null
+++ b/src/ProjectTemplates/test/Templates.Blazor.Tests/BlazorWebTemplateTest.cs
@@ -0,0 +1,95 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Net;
+using System.Net.Http;
+using System.Net.Http.Headers;
+using Microsoft.AspNetCore.BrowserTesting;
+using Microsoft.AspNetCore.InternalTesting;
+using Templates.Test.Helpers;
+
+namespace BlazorTemplates.Tests;
+
+public class BlazorWebTemplateTest(ProjectFactoryFixture projectFactory) : BlazorTemplateTest(projectFactory)
+{
+ public override string ProjectType => "blazor";
+
+ [ConditionalTheory]
+ [SkipNonHelix]
+ [InlineData(BrowserKind.Chromium, "None")]
+ [InlineData(BrowserKind.Chromium, "Server")]
+ [InlineData(BrowserKind.Chromium, "WebAssembly")]
+ [InlineData(BrowserKind.Chromium, "Auto")]
+ public async Task BlazorWebTemplate_Works(BrowserKind browserKind, string interactivityOption)
+ {
+ var project = await CreateBuildPublishAsync(
+ args: ["-int", interactivityOption],
+ getTargetProject: GetTargetProject);
+
+ // There won't be a counter page when the 'None' interactivity option is used
+ var pagesToExclude = interactivityOption is "None"
+ ? BlazorTemplatePages.Counter
+ : BlazorTemplatePages.None;
+
+ var appName = project.ProjectName;
+
+ // Test the built project
+ using (var aspNetProcess = project.StartBuiltProjectAsync())
+ {
+ Assert.False(
+ aspNetProcess.Process.HasExited,
+ ErrorMessages.GetFailedProcessMessageOrEmpty("Run built project", project, aspNetProcess.Process));
+
+ await aspNetProcess.AssertStatusCode("/", HttpStatusCode.OK, "text/html");
+ await TestBasicInteractionInNewPageAsync(browserKind, aspNetProcess.ListeningUri.AbsoluteUri, appName, pagesToExclude);
+ }
+
+ // Test the published project
+ using (var aspNetProcess = project.StartPublishedProjectAsync())
+ {
+ Assert.False(
+ aspNetProcess.Process.HasExited,
+ ErrorMessages.GetFailedProcessMessageOrEmpty("Run published project", project, aspNetProcess.Process));
+
+ await aspNetProcess.AssertStatusCode("/", HttpStatusCode.OK, "text/html");
+
+ if (HasClientProject())
+ {
+ await AssertWebAssemblyCompressionFormatAsync(aspNetProcess, "br");
+ }
+
+ await TestBasicInteractionInNewPageAsync(browserKind, aspNetProcess.ListeningUri.AbsoluteUri, appName, pagesToExclude);
+ }
+
+ bool HasClientProject()
+ => interactivityOption is "WebAssembly" or "Auto";
+
+ Project GetTargetProject(Project rootProject)
+ {
+ if (HasClientProject())
+ {
+ // Multiple projects were created, so we need to specifically select the server
+ // project to be used
+ return GetSubProject(rootProject, rootProject.ProjectName, rootProject.ProjectName);
+ }
+
+ // In other cases, just use the root project
+ return rootProject;
+ }
+ }
+
+ private static async Task AssertWebAssemblyCompressionFormatAsync(AspNetProcess aspNetProcess, string expectedEncoding)
+ {
+ var response = await aspNetProcess.SendRequest(() =>
+ {
+ var request = new HttpRequestMessage(HttpMethod.Get, new Uri(aspNetProcess.ListeningUri, "/_framework/blazor.boot.json"));
+ // These are the same as chrome
+ request.Headers.AcceptEncoding.Clear();
+ request.Headers.AcceptEncoding.Add(StringWithQualityHeaderValue.Parse("gzip"));
+ request.Headers.AcceptEncoding.Add(StringWithQualityHeaderValue.Parse("deflate"));
+ request.Headers.AcceptEncoding.Add(StringWithQualityHeaderValue.Parse("br"));
+ return request;
+ });
+ Assert.Equal(expectedEncoding, response.Content.Headers.ContentEncoding.Single());
+ }
+}
diff --git a/src/ProjectTemplates/test/Templates.Blazor.Tests/Templates.Blazor.Tests.csproj b/src/ProjectTemplates/test/Templates.Blazor.Tests/Templates.Blazor.Tests.csproj
index 1db85d5286fe..933f42dd9423 100644
--- a/src/ProjectTemplates/test/Templates.Blazor.Tests/Templates.Blazor.Tests.csproj
+++ b/src/ProjectTemplates/test/Templates.Blazor.Tests/Templates.Blazor.Tests.csproj
@@ -54,8 +54,10 @@
-
-
+
diff --git a/src/ProjectTemplates/test/Templates.Blazor.WebAssembly.Auth.Tests/BlazorWasmTemplateAuthTest.cs b/src/ProjectTemplates/test/Templates.Blazor.WebAssembly.Auth.Tests/BlazorWasmTemplateAuthTest.cs
index 8e00c09a1e14..540c22178863 100644
--- a/src/ProjectTemplates/test/Templates.Blazor.WebAssembly.Auth.Tests/BlazorWasmTemplateAuthTest.cs
+++ b/src/ProjectTemplates/test/Templates.Blazor.WebAssembly.Auth.Tests/BlazorWasmTemplateAuthTest.cs
@@ -1,19 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Text.Json;
-using System.Text.RegularExpressions;
-using System.Threading.Tasks;
using Microsoft.AspNetCore.InternalTesting;
-using Newtonsoft.Json.Linq;
using Templates.Test.Helpers;
-using Xunit;
-using Xunit.Abstractions;
-using Xunit.Sdk;
namespace Templates.Blazor.Test;
@@ -95,31 +84,31 @@ public TemplateInstance(string name, string auth, params string[] arguments)
[ConditionalTheory]
[MemberData(nameof(TemplateDataIndividualB2C))]
- public Task BlazorWasmHostedTemplate_AzureActiveDirectoryTemplate_IndividualB2C_Works(TemplateInstance instance)
+ public Task BlazorWasmStandaloneTemplate_AzureActiveDirectoryTemplate_IndividualB2C_Works(TemplateInstance instance)
=> CreateBuildPublishAsync(auth: instance.Auth, args: instance.Arguments, targetFramework: "netstandard2.1");
[ConditionalTheory]
[MemberData(nameof(TemplateDataIndividualB2C))]
- public Task BlazorWasmHostedTemplate_AzureActiveDirectoryTemplate_IndividualB2C_NoHttps_Works(TemplateInstance instance)
+ public Task BlazorWasmStandaloneTemplate_AzureActiveDirectoryTemplate_IndividualB2C_NoHttps_Works(TemplateInstance instance)
=> CreateBuildPublishAsync(auth: instance.Auth, args: instance.Arguments.Union(new[] { ArgConstants.NoHttps }).ToArray(), targetFramework: "netstandard2.1");
[ConditionalTheory]
[MemberData(nameof(TemplateDataSingleOrg))]
- public Task BlazorWasmHostedTemplate_AzureActiveDirectoryTemplate_SingleOrg_Works(TemplateInstance instance)
+ public Task BlazorWasmStandaloneTemplate_AzureActiveDirectoryTemplate_SingleOrg_Works(TemplateInstance instance)
=> CreateBuildPublishAsync(auth: instance.Auth, args: instance.Arguments, targetFramework: "netstandard2.1");
[ConditionalTheory]
[MemberData(nameof(TemplateDataSingleOrg))]
- public Task BlazorWasmHostedTemplate_AzureActiveDirectoryTemplate_SingleOrg_NoHttps_Works(TemplateInstance instance)
+ public Task BlazorWasmStandaloneTemplate_AzureActiveDirectoryTemplate_SingleOrg_NoHttps_Works(TemplateInstance instance)
=> CreateBuildPublishAsync(auth: instance.Auth, args: instance.Arguments.Union(new[] { ArgConstants.NoHttps }).ToArray(), targetFramework: "netstandard2.1");
[ConditionalTheory]
[MemberData(nameof(TemplateDataSingleOrgProgramMain))]
- public Task BlazorWasmHostedTemplate_AzureActiveDirectoryTemplate_SingleOrg_ProgramMain_Works(TemplateInstance instance)
+ public Task BlazorWasmStandaloneTemplate_AzureActiveDirectoryTemplate_SingleOrg_ProgramMain_Works(TemplateInstance instance)
=> CreateBuildPublishAsync(auth: instance.Auth, args: instance.Arguments, targetFramework: "netstandard2.1");
[ConditionalTheory]
[MemberData(nameof(TemplateDataSingleOrgProgramMain))]
- public Task BlazorWasmHostedTemplate_AzureActiveDirectoryTemplate_SingleOrg_NoHttps_ProgramMain_Works(TemplateInstance instance)
+ public Task BlazorWasmStandaloneTemplate_AzureActiveDirectoryTemplate_SingleOrg_NoHttps_ProgramMain_Works(TemplateInstance instance)
=> CreateBuildPublishAsync(auth: instance.Auth, args: instance.Arguments.Union(new[] { ArgConstants.NoHttps }).ToArray(), targetFramework: "netstandard2.1");
}
diff --git a/src/ProjectTemplates/test/Templates.Tests/ItemTemplateTests/BlazorServerTests.cs b/src/ProjectTemplates/test/Templates.Tests/ItemTemplateTests/BlazorServerTests.cs
index f520e6cb600a..3035cd16d5d6 100644
--- a/src/ProjectTemplates/test/Templates.Tests/ItemTemplateTests/BlazorServerTests.cs
+++ b/src/ProjectTemplates/test/Templates.Tests/ItemTemplateTests/BlazorServerTests.cs
@@ -27,7 +27,7 @@ public async Task BlazorServerItemTemplate()
{
Project = await ProjectFactory.CreateProject(Output);
- await Project.RunDotNetNewAsync("razorcomponent --name Different");
+ await Project.RunDotNetNewAsync("razorcomponent --name Different", isItemTemplate: true);
Project.AssertFileExists("Different.razor", shouldExist: true);
Assert.Contains("Different
", Project.ReadFile("Different.razor"));
diff --git a/src/Shared/CommandLineUtils/Utilities/DotNetMuxer.cs b/src/Shared/CommandLineUtils/Utilities/DotNetMuxer.cs
index d9f6b8c1f665..b65817648e96 100644
--- a/src/Shared/CommandLineUtils/Utilities/DotNetMuxer.cs
+++ b/src/Shared/CommandLineUtils/Utilities/DotNetMuxer.cs
@@ -9,6 +9,8 @@
using System;
using System.Diagnostics;
using System.IO;
+using System.Linq;
+using System.Reflection;
using System.Runtime.InteropServices;
namespace Microsoft.Extensions.CommandLineUtils;
@@ -22,7 +24,7 @@ internal static class DotNetMuxer
static DotNetMuxer()
{
- MuxerPath = TryFindMuxerPath(Process.GetCurrentProcess().MainModule?.FileName);
+ MuxerPath = TryFindMuxerPath();
}
///
@@ -38,18 +40,45 @@ static DotNetMuxer()
public static string MuxerPathOrDefault()
=> MuxerPath ?? MuxerName;
- internal static string? TryFindMuxerPath(string? mainModule)
+ private static string? TryFindMuxerPath()
{
- var fileName = MuxerName;
+ // If not running on Helix, use a custom .NET host, if specified.
+ // This allows test projects to use a .NET host with the custom-built
+ // ASP.NET Core shared framework.
+ if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable("helix")))
+ {
+ var dotNetHostOverride = typeof(DotNetMuxer).Assembly.GetCustomAttributes()
+ .SingleOrDefault(a => a.Key == "DotNetHostOverride")?.Value;
+ if (dotNetHostOverride is not null)
+ {
+ return dotNetHostOverride;
+ }
+ }
+
+ var expectedFileName = MuxerName;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
- fileName += ".exe";
+ expectedFileName += ".exe";
+ }
+
+ // If the currently running process is dotnet(.exe), return that path
+ var mainModuleFullPath = Process.GetCurrentProcess().MainModule?.FileName;
+ var mainModuleFileName = Path.GetFileName(mainModuleFullPath);
+ if (string.Equals(expectedFileName, mainModuleFileName, StringComparison.OrdinalIgnoreCase))
+ {
+ return mainModuleFullPath;
}
- if (!string.IsNullOrEmpty(mainModule)
- && string.Equals(Path.GetFileName(mainModule!), fileName, StringComparison.OrdinalIgnoreCase))
+ // The currently running process may not be dotnet(.exe). For example,
+ // it might be "testhost(.exe)" when running tests.
+ // In this case, we can get the location where the CLR is installed,
+ // and find dotnet(.exe) relative to that path.
+ var runtimeDirectory = RuntimeEnvironment.GetRuntimeDirectory();
+ var candidateDotNetExePath = Path.Combine(runtimeDirectory, "..", "..", "..", expectedFileName);
+ if (File.Exists(candidateDotNetExePath))
{
- return mainModule;
+ var normalizedPath = Path.GetFullPath(candidateDotNetExePath);
+ return normalizedPath;
}
return null;
diff --git a/src/Shared/test/Shared.Tests/DotNetMuxerTests.cs b/src/Shared/test/Shared.Tests/DotNetMuxerTests.cs
index 193f9582ac12..27b223f6e46f 100644
--- a/src/Shared/test/Shared.Tests/DotNetMuxerTests.cs
+++ b/src/Shared/test/Shared.Tests/DotNetMuxerTests.cs
@@ -14,28 +14,11 @@ public class DotNetMuxerTests
[Fact]
public void FindsTheMuxer()
{
-
- var muxerPath = DotNetMuxer.TryFindMuxerPath(GetDotnetPath());
+ var muxerPath = DotNetMuxer.MuxerPath;
Assert.NotNull(muxerPath);
Assert.True(File.Exists(muxerPath), "The file did not exist");
Assert.True(Path.IsPathRooted(muxerPath), "The path should be rooted");
Assert.Equal("dotnet", Path.GetFileNameWithoutExtension(muxerPath), ignoreCase: true);
-
- static string GetDotnetPath()
- {
- // Process.MainModule is app[.exe] and not `dotnet`. We can instead calculate the dotnet SDK path
- // by looking at the shared fx directory instead.
- // depsFile = /dotnet/shared/Microsoft.NETCore.App/6.0-preview2/Microsoft.NETCore.App.deps.json
- var depsFile = (string)AppContext.GetData("FX_DEPS_FILE");
- return Path.GetFullPath(Path.Combine(Path.GetDirectoryName(depsFile), "..", "..", "..", "dotnet" + (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ".exe" : "")));
- }
- }
-
- [Fact]
- public void ReturnsNullIfMainModuleIsNotDotNet()
- {
- var muxerPath = DotNetMuxer.TryFindMuxerPath(@"d:\some-path\testhost.exe");
- Assert.Null(muxerPath);
}
}
#endif