diff --git a/src/Components/WebView/test/E2ETest/BasicBlazorHybridTest.cs b/src/Components/WebView/Samples/PhotinoPlatform/testassets/PhotinoTestApp/BasicTest.cs similarity index 91% rename from src/Components/WebView/test/E2ETest/BasicBlazorHybridTest.cs rename to src/Components/WebView/Samples/PhotinoPlatform/testassets/PhotinoTestApp/BasicTest.cs index df64496c253c..6f6c140b2f9c 100644 --- a/src/Components/WebView/test/E2ETest/BasicBlazorHybridTest.cs +++ b/src/Components/WebView/Samples/PhotinoPlatform/testassets/PhotinoTestApp/BasicTest.cs @@ -7,11 +7,11 @@ using Microsoft.Extensions.DependencyInjection; using PhotinoNET; -namespace Microsoft.AspNetCore.Components.WebViewE2E.Test; - -public class BasicBlazorHybridTest +public class BasicTest : IExecutionMode { private string _latestControlDivValue; + const int MaxWaitTimes = 60; + const int WaitTimeInMS = 250; public void Run() { @@ -19,8 +19,8 @@ public void Run() // that is necessary for the functioning of this test is the "Test passed" at the end of this method. Console.WriteLine($"Current directory: {Environment.CurrentDirectory}"); - Console.WriteLine($"Current assembly: {typeof(Program).Assembly.Location}"); - var thisProgramDir = Path.GetDirectoryName(typeof(Program).Assembly.Location); + Console.WriteLine($"Current assembly: {typeof(BasicTest).Assembly.Location}"); + var thisProgramDir = Path.GetDirectoryName(typeof(BasicTest).Assembly.Location); // Add correct runtime sub-folder to PATH to ensure native files are discovered (this is supposed to happen automatically, but somehow it doesn't...) var newNativePath = Path.Combine(thisProgramDir, "runtimes", RuntimeInformation.RuntimeIdentifier, "native"); @@ -66,7 +66,7 @@ public void Run() }; Console.WriteLine($"Setting up root components..."); - mainWindow.RootComponents.Add("root"); + mainWindow.RootComponents.Add("root"); Console.WriteLine($"Running window..."); @@ -82,6 +82,10 @@ public void Run() { isWebViewReady = true; } + else if (msg.StartsWith("wvt:ConsoleLog:", StringComparison.Ordinal)) + { + Console.WriteLine($"[WebView JS] {msg.Substring("wvt:ConsoleLog:".Length)}"); + } else if (msg.StartsWith(NewControlDivValueMessage, StringComparison.Ordinal)) { _latestControlDivValue = msg.Substring(NewControlDivValueMessage.Length + 1); @@ -100,7 +104,7 @@ public void Run() // 1. Wait for WebView ready Console.WriteLine($"Waiting for WebView ready..."); - var isWebViewReadyRetriesLeft = 20; + var isWebViewReadyRetriesLeft = 40; while (!isWebViewReady) { Console.WriteLine($"WebView not ready yet, waiting 1sec..."); @@ -161,10 +165,7 @@ public void Run() Console.WriteLine($"Test passed? {testPassed}"); } - const int MaxWaitTimes = 30; - const int WaitTimeInMS = 250; - - public async Task WaitForControlDiv(PhotinoWindow photinoWindow, string controlValueToWaitFor) + private async Task WaitForControlDiv(PhotinoWindow photinoWindow, string controlValueToWaitFor) { for (var i = 0; i < MaxWaitTimes; i++) diff --git a/src/Components/WebView/Samples/PhotinoPlatform/testassets/PhotinoTestApp/IExecutionMode.cs b/src/Components/WebView/Samples/PhotinoPlatform/testassets/PhotinoTestApp/IExecutionMode.cs new file mode 100644 index 000000000000..2fd85c0bb355 --- /dev/null +++ b/src/Components/WebView/Samples/PhotinoPlatform/testassets/PhotinoTestApp/IExecutionMode.cs @@ -0,0 +1,7 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +public interface IExecutionMode +{ + void Run(); +} \ No newline at end of file diff --git a/src/Components/WebView/Samples/PhotinoPlatform/testassets/PhotinoTestApp/LaunchSample.cs b/src/Components/WebView/Samples/PhotinoPlatform/testassets/PhotinoTestApp/LaunchSample.cs new file mode 100644 index 000000000000..48ea207761ef --- /dev/null +++ b/src/Components/WebView/Samples/PhotinoPlatform/testassets/PhotinoTestApp/LaunchSample.cs @@ -0,0 +1,38 @@ +// 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.Http; +using Microsoft.AspNetCore.Components.Web; +using Microsoft.AspNetCore.Components.WebView.Photino; +using Microsoft.Extensions.DependencyInjection; + +public class LaunchSample : IExecutionMode +{ + public void Run() + { + var serviceCollection = new ServiceCollection(); + serviceCollection.AddBlazorWebView(); + serviceCollection.AddSingleton(); + + var mainWindow = new BlazorWindow( + title: "Hello, world!", + hostPage: "wwwroot/webviewhost.html", + services: serviceCollection.BuildServiceProvider(), + pathBase: "/subdir"); // The content in BasicTestApp assumes this + + AppDomain.CurrentDomain.UnhandledException += (sender, error) => + { + Console.Write( + "Fatal exception" + Environment.NewLine + + error.ExceptionObject.ToString() + Environment.NewLine); + }; + + mainWindow.RootComponents.Add("root"); + mainWindow.RootComponents.RegisterForJavaScript("my-dynamic-root-component"); + mainWindow.RootComponents.RegisterForJavaScript( + "component-with-many-parameters", + javaScriptInitializer: "myJsRootComponentInitializers.testInitializer"); + + mainWindow.Run(); + } +} diff --git a/src/Components/WebView/test/E2ETest/Pages/TestPage.razor b/src/Components/WebView/Samples/PhotinoPlatform/testassets/PhotinoTestApp/Pages/TestPage.razor similarity index 100% rename from src/Components/WebView/test/E2ETest/Pages/TestPage.razor rename to src/Components/WebView/Samples/PhotinoPlatform/testassets/PhotinoTestApp/Pages/TestPage.razor diff --git a/src/Components/WebView/Samples/PhotinoPlatform/testassets/PhotinoTestApp/PhotinoTestApp.csproj b/src/Components/WebView/Samples/PhotinoPlatform/testassets/PhotinoTestApp/PhotinoTestApp.csproj index b006f79206a7..c57aae7e9ae8 100644 --- a/src/Components/WebView/Samples/PhotinoPlatform/testassets/PhotinoTestApp/PhotinoTestApp.csproj +++ b/src/Components/WebView/Samples/PhotinoPlatform/testassets/PhotinoTestApp/PhotinoTestApp.csproj @@ -5,7 +5,7 @@ Exe false false - <_WebViewAssetsBasePath>..\..\..\..\..\Web.JS\dist\Release\ + <_WebViewAssetsBasePath>..\..\..\..\..\Web.JS\dist\$(Configuration)\ <_BlazorModulesFilePath>..\..\..\..\WebView\src\blazor.modules.json diff --git a/src/Components/WebView/Samples/PhotinoPlatform/testassets/PhotinoTestApp/Program.cs b/src/Components/WebView/Samples/PhotinoPlatform/testassets/PhotinoTestApp/Program.cs index c87f69d19819..48991b989c0e 100644 --- a/src/Components/WebView/Samples/PhotinoPlatform/testassets/PhotinoTestApp/Program.cs +++ b/src/Components/WebView/Samples/PhotinoPlatform/testassets/PhotinoTestApp/Program.cs @@ -1,11 +1,6 @@ // 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.Http; -using Microsoft.AspNetCore.Components.Web; -using Microsoft.AspNetCore.Components.WebView.Photino; -using Microsoft.Extensions.DependencyInjection; - namespace PhotinoTestApp; class Program @@ -13,29 +8,21 @@ class Program [STAThread] static void Main(string[] args) { - var serviceCollection = new ServiceCollection(); - serviceCollection.AddBlazorWebView(); - serviceCollection.AddSingleton(); - - var mainWindow = new BlazorWindow( - title: "Hello, world!", - hostPage: "wwwroot/webviewhost.html", - services: serviceCollection.BuildServiceProvider(), - pathBase: "/subdir"); // The content in BasicTestApp assumes this - - AppDomain.CurrentDomain.UnhandledException += (sender, error) => + if (args.Length < 1) { - Console.Write( - "Fatal exception" + Environment.NewLine + - error.ExceptionObject.ToString() + Environment.NewLine); - }; - - mainWindow.RootComponents.Add("root"); - mainWindow.RootComponents.RegisterForJavaScript("my-dynamic-root-component"); - mainWindow.RootComponents.RegisterForJavaScript( - "component-with-many-parameters", - javaScriptInitializer: "myJsRootComponentInitializers.testInitializer"); - - mainWindow.Run(); + var sample = new LaunchSample(); + sample.Run(); + return; + } + var testScenario = args[0]; + if (testScenario == "--basic-test") + { + var basic = new BasicTest(); + basic.Run(); + } + else + { + throw new ArgumentException($"Scenario {testScenario} is unknown.", nameof(args)); + } } } diff --git a/src/Components/WebView/test/E2ETest/wwwroot/webviewtesthost.html b/src/Components/WebView/Samples/PhotinoPlatform/testassets/PhotinoTestApp/wwwroot/webviewtesthost.html similarity index 99% rename from src/Components/WebView/test/E2ETest/wwwroot/webviewtesthost.html rename to src/Components/WebView/Samples/PhotinoPlatform/testassets/PhotinoTestApp/wwwroot/webviewtesthost.html index c3923f2e8525..1fb3e600b288 100644 --- a/src/Components/WebView/test/E2ETest/wwwroot/webviewtesthost.html +++ b/src/Components/WebView/Samples/PhotinoPlatform/testassets/PhotinoTestApp/wwwroot/webviewtesthost.html @@ -61,4 +61,4 @@ - + \ No newline at end of file diff --git a/src/Components/WebView/test/E2ETest/Microsoft.AspNetCore.Components.WebViewE2E.Test.csproj b/src/Components/WebView/test/E2ETest/Microsoft.AspNetCore.Components.WebViewE2E.Test.csproj index fc4ade975e4b..a6d6ed01f472 100644 --- a/src/Components/WebView/test/E2ETest/Microsoft.AspNetCore.Components.WebViewE2E.Test.csproj +++ b/src/Components/WebView/test/E2ETest/Microsoft.AspNetCore.Components.WebViewE2E.Test.csproj @@ -1,22 +1,16 @@ - + $(DefaultNetCoreTargetFramework) - Exe - - @(NoWarn);CS8002 - - Microsoft.AspNetCore.Components.WebViewE2E.Test.Program - + + - - PreserveNewest - + diff --git a/src/Components/WebView/test/E2ETest/Program.cs b/src/Components/WebView/test/E2ETest/Program.cs deleted file mode 100644 index 62042aebbdfc..000000000000 --- a/src/Components/WebView/test/E2ETest/Program.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace Microsoft.AspNetCore.Components.WebViewE2E.Test; - -class Program -{ - // Yes, this is a Program.Main() inside of a test project! This project is a regular xUnit.net test project, but - // some tests also launch this project as a regular executable to launch UI tests. To achieve this, the CSPROJ - // has the property set to indicate that _this_ is the Program.Main() to use when launching as - // an executable. - [STAThread] - static void Main(string[] args) - { - try - { - // Future idea: To support multiple tests, the 'args' could specify which test to run, and this code could run - // different types/methods/args to control variations of that. Then in WebViewManagerE2ETests the arg could - // be specified for each variation when launching this executable. But, for now, we have only 1 test, so no need - // for extra complexity. - - var basicBlazorHybridTest = new BasicBlazorHybridTest(); - basicBlazorHybridTest.Run(); - } - catch (Exception ex) - { - Console.WriteLine($"Exception while running {typeof(BasicBlazorHybridTest).FullName}: {ex}"); - } - } -} diff --git a/src/Components/WebView/test/E2ETest/WebViewManagerE2ETests.cs b/src/Components/WebView/test/E2ETest/WebViewManagerE2ETests.cs index e997d5dd7bc6..0c6b8807c49f 100644 --- a/src/Components/WebView/test/E2ETest/WebViewManagerE2ETests.cs +++ b/src/Components/WebView/test/E2ETest/WebViewManagerE2ETests.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using Microsoft.AspNetCore.InternalTesting; +using Xunit.Abstractions; namespace Microsoft.AspNetCore.Components.WebViewE2E.Test; @@ -18,32 +19,59 @@ public class WebViewManagerE2ETests [ConditionalFact] [OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX, SkipReason = "On Helix/Ubuntu the native Photino assemblies can't be found, and on macOS it can't detect when the WebView is ready")] - [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/50802")] public async Task CanLaunchPhotinoWebViewAndClickButton() { - var photinoTestProgramExePath = typeof(WebViewManagerE2ETests).Assembly.Location; + // With the project reference, PhotinoTestApp.dll should be copied to the same directory as this test assembly + var testAssemblyDirectory = Path.GetDirectoryName(typeof(WebViewManagerE2ETests).Assembly.Location)!; + var photinoTestAppPath = Path.Combine(testAssemblyDirectory, "PhotinoTestApp.dll"); - // This test launches this very test assembly as an executable so that the Photino UI window - // can launch and be automated. See the comment in Program.Main() for more info. + if (!File.Exists(photinoTestAppPath)) + { + throw new FileNotFoundException($"Could not find PhotinoTestApp.dll at: {photinoTestAppPath}. " + + "Ensure the PhotinoTestApp project reference is properly configured."); + } + + // This test launches the PhotinoTestApp sample as an executable so that the Photino UI window + // can launch and be automated. var photinoProcess = new Process() { - StartInfo = new ProcessStartInfo - { - WorkingDirectory = Path.GetDirectoryName(photinoTestProgramExePath), - FileName = "dotnet", - Arguments = $"\"{photinoTestProgramExePath}\"", - RedirectStandardOutput = true, - }, + StartInfo = new ProcessStartInfo + { + WorkingDirectory = Path.GetDirectoryName(photinoTestAppPath), + FileName = "dotnet", + Arguments = $"\"{photinoTestAppPath}\" --basic-test", + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false, + }, }; photinoProcess.Start(); var testProgramOutput = photinoProcess.StandardOutput.ReadToEnd(); + var testProgramError = photinoProcess.StandardError.ReadToEnd(); await photinoProcess.WaitForExitAsync().TimeoutAfter(TimeSpan.FromSeconds(30)); - // The test app reports its own results by calling Console.WriteLine(), so here we only need to verify that - // the test internally believes it passed (and we trust it!). - Assert.Contains($"Test passed? {true}", testProgramOutput); + // Use Assert.True with a custom message to include the full output in the failure message + var expectedMessage = $"Test passed? {true}"; + var testPassed = testProgramOutput.Contains(expectedMessage); + + if (!testPassed) + { + var errorInfo = $"Process exit code: {photinoProcess.ExitCode}\n" + + $"Working directory: {photinoProcess.StartInfo.WorkingDirectory}\n" + + $"Command: {photinoProcess.StartInfo.FileName} {photinoProcess.StartInfo.Arguments}\n" + + $"PhotinoTestApp path: {photinoTestAppPath}\n\n" + + $"=== Full Standard Output ===\n{testProgramOutput}\n" + + $"=== End of Standard Output ===\n"; + + if (!string.IsNullOrEmpty(testProgramError)) + { + errorInfo += $"\n=== Standard Error ===\n{testProgramError}\n=== End of Standard Error ==="; + } + + Assert.True(testPassed, $"Expected to find '{expectedMessage}' in PhotinoTestApp output.\n\n{errorInfo}"); + } } } diff --git a/src/Components/WebView/test/E2ETest/_Imports.razor b/src/Components/WebView/test/E2ETest/_Imports.razor deleted file mode 100644 index 6ba5da5dbac6..000000000000 --- a/src/Components/WebView/test/E2ETest/_Imports.razor +++ /dev/null @@ -1,7 +0,0 @@ -@using System.Net.Http -@using System.Net.Http.Json -@using Microsoft.AspNetCore.Components.Forms -@using Microsoft.AspNetCore.Components.Routing -@using Microsoft.AspNetCore.Components.Web -@using Microsoft.AspNetCore.Components.Web.Virtualization -@using Microsoft.JSInterop diff --git a/src/Components/WebView/test/E2ETest/wwwroot/css/app.css b/src/Components/WebView/test/E2ETest/wwwroot/css/app.css deleted file mode 100644 index d54c39e60390..000000000000 --- a/src/Components/WebView/test/E2ETest/wwwroot/css/app.css +++ /dev/null @@ -1,23 +0,0 @@ -#blazor-error-ui { - background: lightyellow; - bottom: 0; - box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); - box-sizing: border-box; - display: none; - left: 0; - padding: 0.6rem 1.25rem 0.7rem 1.25rem; - position: fixed; - width: 100%; - z-index: 1000; -} - -#blazor-error-ui .dismiss { - cursor: pointer; - position: absolute; - right: 0.75rem; - top: 0.5rem; -} - -html { - font-family: Arial, Helvetica, sans-serif; -}