From 7c8eeef225494c6aff27e228161fff3fa42e6d5b Mon Sep 17 00:00:00 2001 From: softworkz Date: Sat, 6 Dec 2025 20:04:23 +0100 Subject: [PATCH 01/11] GetPrintersAsync: Increase timeout --- src/ElectronNET.API/API/WebContents.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ElectronNET.API/API/WebContents.cs b/src/ElectronNET.API/API/WebContents.cs index 2ff58856..2b3086a7 100644 --- a/src/ElectronNET.API/API/WebContents.cs +++ b/src/ElectronNET.API/API/WebContents.cs @@ -173,7 +173,7 @@ public bool IsDevToolsFocused() /// Get system printers. /// /// printers - public Task GetPrintersAsync() => this.InvokeAsyncWithTimeout(5_000); + public Task GetPrintersAsync() => this.InvokeAsyncWithTimeout(8_000); /// /// Prints window's web page. From 347c1ef0e4fcb0756130d2445eb86c0091718b6f Mon Sep 17 00:00:00 2001 From: softworkz Date: Sat, 6 Dec 2025 20:06:34 +0100 Subject: [PATCH 02/11] Show_hide_visibility_roundtrip: Don't use shared window --- .../Tests/BrowserWindowTests.cs | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/src/ElectronNET.IntegrationTests/Tests/BrowserWindowTests.cs b/src/ElectronNET.IntegrationTests/Tests/BrowserWindowTests.cs index 0d86d232..a258aec8 100644 --- a/src/ElectronNET.IntegrationTests/Tests/BrowserWindowTests.cs +++ b/src/ElectronNET.IntegrationTests/Tests/BrowserWindowTests.cs @@ -82,12 +82,28 @@ public async Task Can_set_and_get_content_bounds() [Fact(Timeout = 20000)] public async Task Show_hide_visibility_roundtrip() { - this.fx.MainWindow.Show(); - await Task.Delay(500); - (await this.fx.MainWindow.IsVisibleAsync()).Should().BeTrue(); - this.fx.MainWindow.Hide(); - await Task.Delay(500); - (await this.fx.MainWindow.IsVisibleAsync()).Should().BeFalse(); + BrowserWindow window = null; + + try + { + window = await Electron.WindowManager.CreateWindowAsync(new BrowserWindowOptions { Show = true }, "about:blank"); + + await Task.Delay(100); + + window.Show(); + + await Task.Delay(500); + (await window.IsVisibleAsync()).Should().BeTrue(); + + window.Hide(); + await Task.Delay(500); + + (await window.IsVisibleAsync()).Should().BeFalse(); + } + finally + { + window?.Destroy(); + } } [Fact(Timeout = 20000)] From 6246b44d68c22d44a9cb031fd2f8ab0cf6e957ac Mon Sep 17 00:00:00 2001 From: softworkz Date: Sat, 6 Dec 2025 20:08:01 +0100 Subject: [PATCH 03/11] ReadyToShow_event_fires: Destroy window --- .../Tests/BrowserWindowTests.cs | 33 +++++++++++-------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/src/ElectronNET.IntegrationTests/Tests/BrowserWindowTests.cs b/src/ElectronNET.IntegrationTests/Tests/BrowserWindowTests.cs index a258aec8..33c80028 100644 --- a/src/ElectronNET.IntegrationTests/Tests/BrowserWindowTests.cs +++ b/src/ElectronNET.IntegrationTests/Tests/BrowserWindowTests.cs @@ -1,6 +1,5 @@ namespace ElectronNET.IntegrationTests.Tests { - using System.Runtime.InteropServices; using System.Runtime.Versioning; using ElectronNET.API; using ElectronNET.API.Entities; @@ -136,22 +135,28 @@ public async Task MenuBar_auto_hide_and_visibility() [Fact(Timeout = 20000)] public async Task ReadyToShow_event_fires_after_content_ready() { - var window = await Electron.WindowManager.CreateWindowAsync(new BrowserWindowOptions { Show = false }, "about:blank"); - var tcs = new TaskCompletionSource(); - window.OnReadyToShow += () => tcs.TrySetResult(); + BrowserWindow window = null; - // Trigger a navigation and wait for DOM ready so the renderer paints, which emits ready-to-show - var domReadyTcs = new TaskCompletionSource(); - window.WebContents.OnDomReady += () => domReadyTcs.TrySetResult(); - await Task.Delay(500); - await window.WebContents.LoadURLAsync("about:blank"); - await domReadyTcs.Task; + try + { + window = await Electron.WindowManager.CreateWindowAsync(new BrowserWindowOptions { Show = false }, "about:blank"); + var tcs = new TaskCompletionSource(); + window.OnReadyToShow += () => tcs.TrySetResult(); - var completed = await Task.WhenAny(tcs.Task, Task.Delay(3000)); - completed.Should().Be(tcs.Task); + // Trigger a navigation and wait for DOM ready so the renderer paints, which emits ready-to-show + var domReadyTcs = new TaskCompletionSource(); + window.WebContents.OnDomReady += () => domReadyTcs.TrySetResult(); + await Task.Delay(500); + await window.WebContents.LoadURLAsync("about:blank"); + await domReadyTcs.Task; - // Typical usage is to show once ready - window.Show(); + var completed = await Task.WhenAny(tcs.Task, Task.Delay(3000)); + completed.Should().Be(tcs.Task); + } + finally + { + window?.Destroy(); + } } [Fact(Timeout = 20000)] From daa9f399e9d17d7d863205df603e3e9273bdbbfb Mon Sep 17 00:00:00 2001 From: softworkz Date: Sat, 6 Dec 2025 20:09:05 +0100 Subject: [PATCH 04/11] ScreenTests: Remove invalid constraints Negative values for cursor position are not invalid --- src/ElectronNET.IntegrationTests/Tests/ScreenTests.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/ElectronNET.IntegrationTests/Tests/ScreenTests.cs b/src/ElectronNET.IntegrationTests/Tests/ScreenTests.cs index 2ecf9ecb..9de422d4 100644 --- a/src/ElectronNET.IntegrationTests/Tests/ScreenTests.cs +++ b/src/ElectronNET.IntegrationTests/Tests/ScreenTests.cs @@ -37,8 +37,6 @@ public async Task GetCursorScreenPoint_check() { var point = await Electron.Screen.GetCursorScreenPointAsync(); point.Should().NotBeNull(); - point.X.Should().BeGreaterThanOrEqualTo(0); - point.Y.Should().BeGreaterThanOrEqualTo(0); } [SkippableFact(Timeout = 20000)] From c90f003519403b615dc6591c7768638e3f310f85 Mon Sep 17 00:00:00 2001 From: softworkz Date: Sat, 6 Dec 2025 20:11:27 +0100 Subject: [PATCH 05/11] GetSetZoomLevel: Don't use shared window Because we need a visible window and the main window visibility must not be mutated by tests --- .../Tests/WebContentsTests.cs | 34 ++++++++++++++----- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/src/ElectronNET.IntegrationTests/Tests/WebContentsTests.cs b/src/ElectronNET.IntegrationTests/Tests/WebContentsTests.cs index 6c727054..64c44397 100644 --- a/src/ElectronNET.IntegrationTests/Tests/WebContentsTests.cs +++ b/src/ElectronNET.IntegrationTests/Tests/WebContentsTests.cs @@ -97,14 +97,32 @@ public async Task GetSetZoomFactor_check() [SkippableFact(Timeout = 20000)] public async Task GetSetZoomLevel_check() { - Skip.If(Environment.GetEnvironmentVariable("GITHUB_RUN_ID") != null && RuntimeInformation.IsOSPlatform(OSPlatform.Windows), "Skipping test on Windows CI."); - await fx.MainWindow.WebContents.GetZoomLevelAsync(); - var ok = await fx.MainWindow.WebContents.GetZoomLevelAsync(); - ok.Should().Be(0); - fx.MainWindow.WebContents.SetZoomLevel(2); - await Task.Delay(500); - ok = await fx.MainWindow.WebContents.GetZoomLevelAsync(); - ok.Should().Be(2); + BrowserWindow window = null; + + try + { + ////Skip.If(Environment.GetEnvironmentVariable("GITHUB_RUN_ID") != null && RuntimeInformation.IsOSPlatform(OSPlatform.Windows), "Skipping test on Windows CI."); + + window = await Electron.WindowManager.CreateWindowAsync(new BrowserWindowOptions { Show = true }, "about:blank"); + + await Task.Delay(100); + + window.WebContents.SetZoomLevel(0); + await Task.Delay(500); + + var ok = await window.WebContents.GetZoomLevelAsync(); + ok.Should().Be(0); + + window.WebContents.SetZoomLevel(2); + await Task.Delay(500); + + ok = await window.WebContents.GetZoomLevelAsync(); + ok.Should().Be(2); + } + finally + { + window?.Destroy(); + } } [SkippableFact(Timeout = 20000)] From 88d2daacb1dd52d3db8370cb2a775533e3df35c8 Mon Sep 17 00:00:00 2001 From: softworkz Date: Sat, 6 Dec 2025 20:13:08 +0100 Subject: [PATCH 06/11] WebContentsTests: Experiment! Re-enable some tests for CI as experiment --- .../Tests/WebContentsTests.cs | 45 ++++++++++++------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/src/ElectronNET.IntegrationTests/Tests/WebContentsTests.cs b/src/ElectronNET.IntegrationTests/Tests/WebContentsTests.cs index 64c44397..fcbfade5 100644 --- a/src/ElectronNET.IntegrationTests/Tests/WebContentsTests.cs +++ b/src/ElectronNET.IntegrationTests/Tests/WebContentsTests.cs @@ -2,6 +2,7 @@ namespace ElectronNET.IntegrationTests.Tests { + using ElectronNET.API; using ElectronNET.API.Entities; [Collection("ElectronCollection")] @@ -77,7 +78,7 @@ public async Task Can_basic_print() [SkippableFact(Timeout = 20000)] public async Task GetPrintersAsync_check() { - Skip.If(Environment.GetEnvironmentVariable("GITHUB_RUN_ID") != null, "Skipping printer test in CI environment."); + ////Skip.If(Environment.GetEnvironmentVariable("GITHUB_RUN_ID") != null, "Skipping printer test in CI environment."); var info = await fx.MainWindow.WebContents.GetPrintersAsync(); info.Should().NotBeNull(); } @@ -128,17 +129,30 @@ public async Task GetSetZoomLevel_check() [SkippableFact(Timeout = 20000)] public async Task DevTools_check() { - Skip.If(Environment.GetEnvironmentVariable("GITHUB_RUN_ID") != null && RuntimeInformation.IsOSPlatform(OSPlatform.OSX), "Skipping test on macOS CI."); - fx.MainWindow.WebContents.IsDevToolsOpened().Should().BeFalse(); - fx.MainWindow.WebContents.OpenDevTools(); - await Task.Delay(500); - fx.MainWindow.WebContents.IsDevToolsOpened().Should().BeTrue(); - fx.MainWindow.WebContents.CloseDevTools(); - await Task.Delay(500); - fx.MainWindow.WebContents.IsDevToolsOpened().Should().BeFalse(); - fx.MainWindow.WebContents.ToggleDevTools(); - await Task.Delay(500); - fx.MainWindow.WebContents.IsDevToolsOpened().Should().BeTrue(); + BrowserWindow window = null; + + try + { + ////Skip.If(Environment.GetEnvironmentVariable("GITHUB_RUN_ID") != null && RuntimeInformation.IsOSPlatform(OSPlatform.Windows), "Skipping test on Windows CI."); + + window = await Electron.WindowManager.CreateWindowAsync(new BrowserWindowOptions { Show = true }, "about:blank"); + + await Task.Delay(3000); + + window.WebContents.IsDevToolsOpened().Should().BeFalse(); + window.WebContents.OpenDevTools(); + await Task.Delay(5000); + + window.WebContents.IsDevToolsOpened().Should().BeTrue(); + window.WebContents.CloseDevTools(); + await Task.Delay(2000); + + window.WebContents.IsDevToolsOpened().Should().BeFalse(); + } + finally + { + window?.Destroy(); + } } [Fact(Timeout = 20000)] @@ -162,12 +176,11 @@ public async Task GetSetAudioMuted_check() [SkippableFact(Timeout = 20000)] public async Task GetSetUserAgent_check() { - Skip.If(Environment.GetEnvironmentVariable("GITHUB_RUN_ID") != null && RuntimeInformation.IsOSPlatform(OSPlatform.Windows), "Skipping test on Windows CI."); - var ok = await fx.MainWindow.WebContents.GetUserAgentAsync(); - ok.Should().NotBeNullOrEmpty(); + ////Skip.If(Environment.GetEnvironmentVariable("GITHUB_RUN_ID") != null && RuntimeInformation.IsOSPlatform(OSPlatform.Windows), "Skipping test on Windows CI."); + fx.MainWindow.WebContents.SetUserAgent("MyUserAgent/1.0"); await Task.Delay(1000); - ok = await fx.MainWindow.WebContents.GetUserAgentAsync(); + var ok = await fx.MainWindow.WebContents.GetUserAgentAsync(); ok.Should().Be("MyUserAgent/1.0"); } From 9bb2adca78cff2899c3406ed190863cc054375b7 Mon Sep 17 00:00:00 2001 From: softworkz Date: Sat, 6 Dec 2025 20:52:45 +0100 Subject: [PATCH 07/11] Use 3s timeout for GetUserAgentAsync and updat test --- src/ElectronNET.API/API/WebContents.cs | 2 +- src/ElectronNET.IntegrationTests/Tests/WebContentsTests.cs | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/ElectronNET.API/API/WebContents.cs b/src/ElectronNET.API/API/WebContents.cs index 2b3086a7..413893e1 100644 --- a/src/ElectronNET.API/API/WebContents.cs +++ b/src/ElectronNET.API/API/WebContents.cs @@ -388,7 +388,7 @@ public void SetAudioMuted(bool muted) /// Returns string - The user agent for this web page. /// /// - public Task GetUserAgentAsync() => InvokeAsync(); + public Task GetUserAgentAsync() => InvokeAsyncWithTimeout(3000); /// /// Overrides the user agent for this web page. diff --git a/src/ElectronNET.IntegrationTests/Tests/WebContentsTests.cs b/src/ElectronNET.IntegrationTests/Tests/WebContentsTests.cs index fcbfade5..0bb159ca 100644 --- a/src/ElectronNET.IntegrationTests/Tests/WebContentsTests.cs +++ b/src/ElectronNET.IntegrationTests/Tests/WebContentsTests.cs @@ -178,8 +178,12 @@ public async Task GetSetUserAgent_check() { ////Skip.If(Environment.GetEnvironmentVariable("GITHUB_RUN_ID") != null && RuntimeInformation.IsOSPlatform(OSPlatform.Windows), "Skipping test on Windows CI."); + await Task.Delay(1000); + fx.MainWindow.WebContents.SetUserAgent("MyUserAgent/1.0"); + await Task.Delay(1000); + var ok = await fx.MainWindow.WebContents.GetUserAgentAsync(); ok.Should().Be("MyUserAgent/1.0"); } From 2b2b26e13b18d936fcc816221611c921b00c3624 Mon Sep 17 00:00:00 2001 From: softworkz Date: Sat, 6 Dec 2025 21:08:47 +0100 Subject: [PATCH 08/11] GetSetUserAgent --- .../Tests/WebContentsTests.cs | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/ElectronNET.IntegrationTests/Tests/WebContentsTests.cs b/src/ElectronNET.IntegrationTests/Tests/WebContentsTests.cs index 0bb159ca..3e807303 100644 --- a/src/ElectronNET.IntegrationTests/Tests/WebContentsTests.cs +++ b/src/ElectronNET.IntegrationTests/Tests/WebContentsTests.cs @@ -178,14 +178,27 @@ public async Task GetSetUserAgent_check() { ////Skip.If(Environment.GetEnvironmentVariable("GITHUB_RUN_ID") != null && RuntimeInformation.IsOSPlatform(OSPlatform.Windows), "Skipping test on Windows CI."); - await Task.Delay(1000); + BrowserWindow window = null; + + try + { + ////Skip.If(Environment.GetEnvironmentVariable("GITHUB_RUN_ID") != null && RuntimeInformation.IsOSPlatform(OSPlatform.Windows), "Skipping test on Windows CI."); + + window = await Electron.WindowManager.CreateWindowAsync(new BrowserWindowOptions { Show = true }, "about:blank"); - fx.MainWindow.WebContents.SetUserAgent("MyUserAgent/1.0"); + await Task.Delay(3000); + + window.WebContents.SetUserAgent("MyUserAgent/1.0"); - await Task.Delay(1000); + await Task.Delay(1000); - var ok = await fx.MainWindow.WebContents.GetUserAgentAsync(); - ok.Should().Be("MyUserAgent/1.0"); + var ok = await window.WebContents.GetUserAgentAsync(); + ok.Should().Be("MyUserAgent/1.0"); + } + finally + { + window?.Destroy(); + } } } From c97f914e7a0f541eb10b6ca085f5b9241556f15c Mon Sep 17 00:00:00 2001 From: softworkz Date: Sun, 7 Dec 2025 11:20:43 +0100 Subject: [PATCH 09/11] Introduce TimeSpan extensions --- src/ElectronNET.API/API/ApiBase.cs | 10 +-- src/ElectronNET.API/API/WebContents.cs | 7 +- .../Common/TimeSpanExtensions.cs | 74 +++++++++++++++++++ src/ElectronNET.API/ElectronNET.API.csproj | 1 + .../ElectronProcess/ElectronProcessActive.cs | 4 +- .../ElectronFixture.cs | 3 +- .../Tests/AutoUpdaterTests.cs | 5 +- .../Tests/BrowserWindowTests.cs | 59 +++++++-------- .../Tests/CookiesTests.cs | 4 +- .../Tests/IpcMainTests.cs | 7 +- .../Tests/MenuTests.cs | 5 +- .../Tests/NativeThemeTests.cs | 13 ++-- .../Tests/NotificationTests.cs | 5 +- .../Tests/WebContentsTests.cs | 25 ++++--- 14 files changed, 154 insertions(+), 68 deletions(-) create mode 100644 src/ElectronNET.API/Common/TimeSpanExtensions.cs diff --git a/src/ElectronNET.API/API/ApiBase.cs b/src/ElectronNET.API/API/ApiBase.cs index b8af9af4..96f68313 100644 --- a/src/ElectronNET.API/API/ApiBase.cs +++ b/src/ElectronNET.API/API/ApiBase.cs @@ -31,7 +31,7 @@ protected enum SocketEventNameTypes CamelCase, } - private const int InvocationTimeout = 1000; + private static readonly TimeSpan InvocationTimeout = 1000.ms(); private readonly string objectName; private readonly ConcurrentDictionary invocators; @@ -120,7 +120,7 @@ protected Task InvokeAsync(object arg = null, [CallerMemberName] string ca return this.InvokeAsyncWithTimeout(InvocationTimeout, arg, callerName); } - protected Task InvokeAsyncWithTimeout(int invocationTimeout, object arg = null, [CallerMemberName] string callerName = null) + protected Task InvokeAsyncWithTimeout(TimeSpan invocationTimeout, object arg = null, [CallerMemberName] string callerName = null) { Debug.Assert(callerName != null, nameof(callerName) + " != null"); @@ -245,7 +245,7 @@ internal class Invocator : Invocator private readonly Task tcsTask; private TaskCompletionSource tcs; - public Invocator(ApiBase apiBase, string callerName, int timeoutMs, object arg = null) + public Invocator(ApiBase apiBase, string callerName, TimeSpan timeout, object arg = null) { this.tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); this.tcsTask = this.tcs.Task; @@ -306,7 +306,7 @@ public Invocator(ApiBase apiBase, string callerName, int timeoutMs, object arg = _ = apiBase.Id >= 0 ? BridgeConnector.Socket.Emit(messageName, apiBase.Id) : BridgeConnector.Socket.Emit(messageName); } - System.Threading.Tasks.Task.Delay(timeoutMs).ContinueWith(_ => + System.Threading.Tasks.Task.Delay(timeout).ContinueWith(_ => { if (this.tcs != null) { @@ -314,7 +314,7 @@ public Invocator(ApiBase apiBase, string callerName, int timeoutMs, object arg = { if (this.tcs != null) { - var ex = new TimeoutException($"No response after {timeoutMs:D}ms trying to retrieve value {apiBase.objectName}.{callerName}()"); + var ex = new TimeoutException($"No response after {timeout:D}ms trying to retrieve value {apiBase.objectName}.{callerName}()"); this.tcs.TrySetException(ex); this.tcs = null; } diff --git a/src/ElectronNET.API/API/WebContents.cs b/src/ElectronNET.API/API/WebContents.cs index 413893e1..7657b413 100644 --- a/src/ElectronNET.API/API/WebContents.cs +++ b/src/ElectronNET.API/API/WebContents.cs @@ -1,6 +1,7 @@ -using ElectronNET.API.Entities; using System; using System.Threading.Tasks; +using ElectronNET.API.Entities; +using ElectronNET.Common; // ReSharper disable InconsistentNaming @@ -173,7 +174,7 @@ public bool IsDevToolsFocused() /// Get system printers. /// /// printers - public Task GetPrintersAsync() => this.InvokeAsyncWithTimeout(8_000); + public Task GetPrintersAsync() => this.InvokeAsyncWithTimeout(8.seconds()); /// /// Prints window's web page. @@ -388,7 +389,7 @@ public void SetAudioMuted(bool muted) /// Returns string - The user agent for this web page. /// /// - public Task GetUserAgentAsync() => InvokeAsyncWithTimeout(3000); + public Task GetUserAgentAsync() => InvokeAsyncWithTimeout(3.seconds()); /// /// Overrides the user agent for this web page. diff --git a/src/ElectronNET.API/Common/TimeSpanExtensions.cs b/src/ElectronNET.API/Common/TimeSpanExtensions.cs new file mode 100644 index 00000000..b4883a0a --- /dev/null +++ b/src/ElectronNET.API/Common/TimeSpanExtensions.cs @@ -0,0 +1,74 @@ +// +// Copyright © Emby LLC. All rights reserved. +// + +namespace ElectronNET.Common +{ + using System; + using System.Diagnostics.CodeAnalysis; + + /// + /// The TimeSpanExtensions class. + /// + [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1300:Element should begin with upper-case letter", Justification = "OK")] + [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "OK")] + [SuppressMessage("ReSharper", "StyleCop.SA1300", Justification = "OK")] + [SuppressMessage("ReSharper", "InconsistentNaming", Justification = "OK")] + internal static class TimeSpanExtensions + { + public static TimeSpan ms(this int value) + { + return TimeSpan.FromMilliseconds(value); + } + + public static TimeSpan ms(this long value) + { + return TimeSpan.FromMilliseconds(value); + } + + public static TimeSpan seconds(this int value) + { + return TimeSpan.FromSeconds(value); + } + + public static TimeSpan minutes(this int value) + { + return TimeSpan.FromMinutes(value); + } + + public static TimeSpan hours(this int value) + { + return TimeSpan.FromHours(value); + } + + public static TimeSpan days(this int value) + { + return TimeSpan.FromDays(value); + } + + public static TimeSpan ms(this double value) + { + return TimeSpan.FromMilliseconds(value); + } + + public static TimeSpan seconds(this double value) + { + return TimeSpan.FromSeconds(value); + } + + public static TimeSpan minutes(this double value) + { + return TimeSpan.FromMinutes(value); + } + + public static TimeSpan hours(this double value) + { + return TimeSpan.FromHours(value); + } + + public static TimeSpan days(this double value) + { + return TimeSpan.FromDays(value); + } + } +} diff --git a/src/ElectronNET.API/ElectronNET.API.csproj b/src/ElectronNET.API/ElectronNET.API.csproj index 3a500110..b5d97f77 100644 --- a/src/ElectronNET.API/ElectronNET.API.csproj +++ b/src/ElectronNET.API/ElectronNET.API.csproj @@ -35,5 +35,6 @@ + diff --git a/src/ElectronNET.API/Runtime/Services/ElectronProcess/ElectronProcessActive.cs b/src/ElectronNET.API/Runtime/Services/ElectronProcess/ElectronProcessActive.cs index 0cf21c54..61cae062 100644 --- a/src/ElectronNET.API/Runtime/Services/ElectronProcess/ElectronProcessActive.cs +++ b/src/ElectronNET.API/Runtime/Services/ElectronProcess/ElectronProcessActive.cs @@ -76,7 +76,7 @@ private async Task StartInternal(string startCmd, string args, string directoriy { try { - await Task.Delay(10).ConfigureAwait(false); + await Task.Delay(10.ms()).ConfigureAwait(false); Console.Error.WriteLine("[StartInternal]: startCmd: {0}", startCmd); Console.Error.WriteLine("[StartInternal]: args: {0}", args); @@ -85,7 +85,7 @@ private async Task StartInternal(string startCmd, string args, string directoriy this.process.ProcessExited += this.Process_Exited; this.process.Run(startCmd, args, directoriy); - await Task.Delay(500).ConfigureAwait(false); + await Task.Delay(500.ms()).ConfigureAwait(false); Console.Error.WriteLine("[StartInternal]: after run:"); diff --git a/src/ElectronNET.IntegrationTests/ElectronFixture.cs b/src/ElectronNET.IntegrationTests/ElectronFixture.cs index cfaf8df2..59be6c95 100644 --- a/src/ElectronNET.IntegrationTests/ElectronFixture.cs +++ b/src/ElectronNET.IntegrationTests/ElectronFixture.cs @@ -4,6 +4,7 @@ namespace ElectronNET.IntegrationTests using System.Reflection; using ElectronNET.API; using ElectronNET.API.Entities; + using ElectronNET.Common; // Shared fixture that starts Electron runtime once [SuppressMessage("ReSharper", "MethodHasAsyncOverload")] @@ -26,7 +27,7 @@ public async Task InitializeAsync() await runtimeController.Start(); Console.Error.WriteLine("[ElectronFixture] Waiting for Ready..."); - await Task.WhenAny(runtimeController.WaitReadyTask, Task.Delay(TimeSpan.FromSeconds(10))); + await Task.WhenAny(runtimeController.WaitReadyTask, Task.Delay(10.seconds())); if (!runtimeController.WaitReadyTask.IsCompleted) { diff --git a/src/ElectronNET.IntegrationTests/Tests/AutoUpdaterTests.cs b/src/ElectronNET.IntegrationTests/Tests/AutoUpdaterTests.cs index 091b5614..fe061322 100644 --- a/src/ElectronNET.IntegrationTests/Tests/AutoUpdaterTests.cs +++ b/src/ElectronNET.IntegrationTests/Tests/AutoUpdaterTests.cs @@ -2,6 +2,7 @@ { using API; using System.Threading.Tasks; + using ElectronNET.Common; [Collection("ElectronCollection")] public class AutoUpdaterTests @@ -89,7 +90,7 @@ public async Task ChannelAsync_check() var test = await Electron.AutoUpdater.ChannelAsync; test.Should().Be(string.Empty); Electron.AutoUpdater.SetChannel = "beta"; - await Task.Delay(500); + await Task.Delay(500.ms()); test = await Electron.AutoUpdater.ChannelAsync; test.Should().Be("beta"); } @@ -104,7 +105,7 @@ public async Task RequestHeadersAsync_check() var test = await Electron.AutoUpdater.RequestHeadersAsync; test.Should().BeNull(); Electron.AutoUpdater.RequestHeaders = headers; - await Task.Delay(500); + await Task.Delay(500.ms()); test = await Electron.AutoUpdater.RequestHeadersAsync; test.Should().NotBeNull(); test.Count.Should().Be(1); diff --git a/src/ElectronNET.IntegrationTests/Tests/BrowserWindowTests.cs b/src/ElectronNET.IntegrationTests/Tests/BrowserWindowTests.cs index 33c80028..0810f4ad 100644 --- a/src/ElectronNET.IntegrationTests/Tests/BrowserWindowTests.cs +++ b/src/ElectronNET.IntegrationTests/Tests/BrowserWindowTests.cs @@ -3,6 +3,7 @@ namespace ElectronNET.IntegrationTests.Tests using System.Runtime.Versioning; using ElectronNET.API; using ElectronNET.API.Entities; + using ElectronNET.Common; using ElectronNET.IntegrationTests.Common; [Collection("ElectronCollection")] @@ -20,7 +21,7 @@ public async Task Can_set_and_get_title() { const string title = "Integration Test Title"; this.fx.MainWindow.SetTitle(title); - await Task.Delay(500); + await Task.Delay(500.ms()); var roundTrip = await this.fx.MainWindow.GetTitleAsync(); roundTrip.Should().Be(title); } @@ -29,7 +30,7 @@ public async Task Can_set_and_get_title() public async Task Can_resize_and_get_size() { this.fx.MainWindow.SetSize(643, 482); - await Task.Delay(500); + await Task.Delay(500.ms()); var size = await this.fx.MainWindow.GetSizeAsync(); size.Should().HaveCount(2); size[0].Should().Be(643); @@ -42,14 +43,14 @@ public async Task Can_set_progress_bar_and_clear() this.fx.MainWindow.SetProgressBar(0.5); // No direct getter; rely on absence of error. Try changing again. this.fx.MainWindow.SetProgressBar(-1); // clears - await Task.Delay(50); + await Task.Delay(50.ms()); } [SkipOnWslFact(Timeout = 20000)] public async Task Can_set_and_get_position() { this.fx.MainWindow.SetPosition(134, 246); - await Task.Delay(500); + await Task.Delay(500.ms()); var pos = await this.fx.MainWindow.GetPositionAsync(); pos.Should().BeEquivalentTo([134, 246]); } @@ -59,7 +60,7 @@ public async Task Can_set_and_get_bounds() { var bounds = new Rectangle { X = 10, Y = 20, Width = 400, Height = 300 }; this.fx.MainWindow.SetBounds(bounds); - await Task.Delay(500); + await Task.Delay(500.ms()); var round = await this.fx.MainWindow.GetBoundsAsync(); round.Should().BeEquivalentTo(bounds); @@ -72,7 +73,7 @@ public async Task Can_set_and_get_content_bounds() { var bounds = new Rectangle { X = 0, Y = 0, Width = 300, Height = 200 }; this.fx.MainWindow.SetContentBounds(bounds); - await Task.Delay(500); + await Task.Delay(500.ms()); var round = await this.fx.MainWindow.GetContentBoundsAsync(); round.Width.Should().BeGreaterThan(0); round.Height.Should().BeGreaterThan(0); @@ -87,15 +88,15 @@ public async Task Show_hide_visibility_roundtrip() { window = await Electron.WindowManager.CreateWindowAsync(new BrowserWindowOptions { Show = true }, "about:blank"); - await Task.Delay(100); + await Task.Delay(100.ms()); window.Show(); - await Task.Delay(500); + await Task.Delay(500.ms()); (await window.IsVisibleAsync()).Should().BeTrue(); window.Hide(); - await Task.Delay(500); + await Task.Delay(500.ms()); (await window.IsVisibleAsync()).Should().BeFalse(); } @@ -109,10 +110,10 @@ public async Task Show_hide_visibility_roundtrip() public async Task AlwaysOnTop_toggle_and_query() { this.fx.MainWindow.SetAlwaysOnTop(true); - await Task.Delay(500); + await Task.Delay(500.ms()); (await this.fx.MainWindow.IsAlwaysOnTopAsync()).Should().BeTrue(); this.fx.MainWindow.SetAlwaysOnTop(false); - await Task.Delay(500); + await Task.Delay(500.ms()); (await this.fx.MainWindow.IsAlwaysOnTopAsync()).Should().BeFalse(); } @@ -122,13 +123,13 @@ public async Task AlwaysOnTop_toggle_and_query() public async Task MenuBar_auto_hide_and_visibility() { this.fx.MainWindow.SetAutoHideMenuBar(true); - await Task.Delay(500); + await Task.Delay(500.ms()); (await this.fx.MainWindow.IsMenuBarAutoHideAsync()).Should().BeTrue(); this.fx.MainWindow.SetMenuBarVisibility(false); - await Task.Delay(500); + await Task.Delay(500.ms()); (await this.fx.MainWindow.IsMenuBarVisibleAsync()).Should().BeFalse(); this.fx.MainWindow.SetMenuBarVisibility(true); - await Task.Delay(500); + await Task.Delay(500.ms()); (await this.fx.MainWindow.IsMenuBarVisibleAsync()).Should().BeTrue(); } @@ -146,11 +147,11 @@ public async Task ReadyToShow_event_fires_after_content_ready() // Trigger a navigation and wait for DOM ready so the renderer paints, which emits ready-to-show var domReadyTcs = new TaskCompletionSource(); window.WebContents.OnDomReady += () => domReadyTcs.TrySetResult(); - await Task.Delay(500); + await Task.Delay(500.ms()); await window.WebContents.LoadURLAsync("about:blank"); await domReadyTcs.Task; - var completed = await Task.WhenAny(tcs.Task, Task.Delay(3000)); + var completed = await Task.WhenAny(tcs.Task, Task.Delay(3.seconds())); completed.Should().Be(tcs.Task); } finally @@ -169,13 +170,13 @@ public async Task PageTitleUpdated_event_fires_on_title_change() // Navigate and wait for DOM ready, then change the document.title to trigger the event var domReadyTcs = new TaskCompletionSource(); window.WebContents.OnDomReady += () => domReadyTcs.TrySetResult(); - await Task.Delay(500); + await Task.Delay(500.ms()); await window.WebContents.LoadURLAsync("about:blank"); await domReadyTcs.Task; await window.WebContents.ExecuteJavaScriptAsync("document.title='NewTitle';"); // Wait for event up to a short timeout - var completed2 = await Task.WhenAny(tcs.Task, Task.Delay(3000)); + var completed2 = await Task.WhenAny(tcs.Task, Task.Delay(3.seconds())); completed2.Should().Be(tcs.Task); (await tcs.Task).Should().Be("NewTitle"); } @@ -186,9 +187,9 @@ public async Task Resize_event_fires_on_size_change() var window = await Electron.WindowManager.CreateWindowAsync(new BrowserWindowOptions { Show = false }, "about:blank"); var resized = false; window.OnResize += () => resized = true; - await Task.Delay(500); + await Task.Delay(500.ms()); window.SetSize(500, 400); - await Task.Delay(300); + await Task.Delay(300.ms()); resized.Should().BeTrue(); } @@ -197,14 +198,14 @@ public async Task Progress_bar_and_always_on_top_toggle() { var win = this.fx.MainWindow; win.SetProgressBar(0.5); - await Task.Delay(50); + await Task.Delay(50.ms()); win.SetProgressBar(0.8, new ProgressBarOptions()); - await Task.Delay(50); + await Task.Delay(50.ms()); win.SetAlwaysOnTop(true); - await Task.Delay(500); + await Task.Delay(500.ms()); (await win.IsAlwaysOnTopAsync()).Should().BeTrue(); win.SetAlwaysOnTop(false); - await Task.Delay(500); + await Task.Delay(500.ms()); (await win.IsAlwaysOnTopAsync()).Should().BeFalse(); } @@ -215,10 +216,10 @@ public async Task Menu_bar_visibility_and_auto_hide() { var win = this.fx.MainWindow; win.SetAutoHideMenuBar(true); - await Task.Delay(500); + await Task.Delay(500.ms()); (await win.IsMenuBarAutoHideAsync()).Should().BeTrue(); win.SetMenuBarVisibility(true); - await Task.Delay(500); + await Task.Delay(500.ms()); (await win.IsMenuBarVisibleAsync()).Should().BeTrue(); } @@ -228,7 +229,7 @@ public async Task Parent_child_relationship_roundtrip() var child = await Electron.WindowManager.CreateWindowAsync(new BrowserWindowOptions { Show = false, Width = 300, Height = 200 }, "about:blank"); this.fx.MainWindow.SetParentWindow(null); // ensure top-level child.SetParentWindow(this.fx.MainWindow); - await Task.Delay(500); + await Task.Delay(500.ms()); var parent = await child.GetParentWindowAsync(); parent.Id.Should().Be(this.fx.MainWindow.Id); var kids = await this.fx.MainWindow.GetChildWindowsAsync(); @@ -245,14 +246,14 @@ public async Task Represented_filename_and_edited_flags() File.WriteAllText(temp, "test"); win.SetRepresentedFilename(temp); - await Task.Delay(500); + await Task.Delay(500.ms()); var represented = await win.GetRepresentedFilenameAsync(); represented.Should().Be(temp); win.SetDocumentEdited(true); - await Task.Delay(500); + await Task.Delay(500.ms()); var edited = await win.IsDocumentEditedAsync(); edited.Should().BeTrue(); diff --git a/src/ElectronNET.IntegrationTests/Tests/CookiesTests.cs b/src/ElectronNET.IntegrationTests/Tests/CookiesTests.cs index 1cba009b..aea1ef05 100644 --- a/src/ElectronNET.IntegrationTests/Tests/CookiesTests.cs +++ b/src/ElectronNET.IntegrationTests/Tests/CookiesTests.cs @@ -1,5 +1,7 @@ namespace ElectronNET.IntegrationTests.Tests { + using ElectronNET.Common; + [Collection("ElectronCollection")] public class CookiesTests { @@ -20,7 +22,7 @@ public async Task Cookie_set_get_remove_sequence() await this.fx.MainWindow.WebContents.LoadURLAsync("https://example.com"); // Set via renderer for now await this.fx.MainWindow.WebContents.ExecuteJavaScriptAsync("document.cookie='integration_cookie=1;path=/';"); - await Task.Delay(500); + await Task.Delay(500.ms()); changed.Should().BeTrue(); } } diff --git a/src/ElectronNET.IntegrationTests/Tests/IpcMainTests.cs b/src/ElectronNET.IntegrationTests/Tests/IpcMainTests.cs index 60f1d62a..f9a5709e 100644 --- a/src/ElectronNET.IntegrationTests/Tests/IpcMainTests.cs +++ b/src/ElectronNET.IntegrationTests/Tests/IpcMainTests.cs @@ -1,6 +1,7 @@ namespace ElectronNET.IntegrationTests.Tests { using ElectronNET.API; + using ElectronNET.Common; [Collection("ElectronCollection")] public class IpcMainTests @@ -39,7 +40,7 @@ public async Task Ipc_Once_only_fires_once() var count = 0; Electron.IpcMain.Once("ipc-once-test", _ => count++); await this.fx.MainWindow.WebContents.ExecuteJavaScriptAsync("const {ipcRenderer}=require('electron'); ipcRenderer.send('ipc-once-test','a'); ipcRenderer.send('ipc-once-test','b');"); - await Task.Delay(500); + await Task.Delay(500.ms()); count.Should().Be(1); } @@ -50,7 +51,7 @@ public async Task Ipc_RemoveAllListeners_stops_receiving() await Electron.IpcMain.On("ipc-remove-test", _ => fired = true); Electron.IpcMain.RemoveAllListeners("ipc-remove-test"); await this.fx.MainWindow.WebContents.ExecuteJavaScriptAsync("require('electron').ipcRenderer.send('ipc-remove-test','x')"); - await Task.Delay(400); + await Task.Delay(400.ms()); fired.Should().BeFalse(); } @@ -88,7 +89,7 @@ public async Task Ipc_Send_from_main_reaches_renderer() break; } - await Task.Delay(100); + await Task.Delay(100.ms()); } // Normalize possible JSON array ["hello-msg"] case diff --git a/src/ElectronNET.IntegrationTests/Tests/MenuTests.cs b/src/ElectronNET.IntegrationTests/Tests/MenuTests.cs index 85a9d847..627ad259 100644 --- a/src/ElectronNET.IntegrationTests/Tests/MenuTests.cs +++ b/src/ElectronNET.IntegrationTests/Tests/MenuTests.cs @@ -2,6 +2,7 @@ namespace ElectronNET.IntegrationTests.Tests { using ElectronNET.API; using ElectronNET.API.Entities; + using ElectronNET.Common; [Collection("ElectronCollection")] public class MenuTests @@ -33,7 +34,7 @@ public async Task ApplicationMenu_click_invokes_handler() await this.fx.MainWindow.WebContents.ExecuteJavaScriptAsync($"require('electron').ipcRenderer.send('integration-click-application-menu','{targetId}')"); for (int i = 0; i < 20 && !clicked; i++) { - await Task.Delay(100); + await Task.Delay(100.ms()); } clicked.Should().BeTrue(); @@ -52,7 +53,7 @@ public async Task ContextMenu_popup_registers_items() await this.fx.MainWindow.WebContents.ExecuteJavaScriptAsync($"require('electron').ipcRenderer.send('integration-click-context-menu',{win.Id},'{ctxId}')"); for (int i = 0; i < 20 && !ctxClicked; i++) { - await Task.Delay(100); + await Task.Delay(100.ms()); } ctxClicked.Should().BeTrue(); diff --git a/src/ElectronNET.IntegrationTests/Tests/NativeThemeTests.cs b/src/ElectronNET.IntegrationTests/Tests/NativeThemeTests.cs index 4128a223..4e5d8907 100644 --- a/src/ElectronNET.IntegrationTests/Tests/NativeThemeTests.cs +++ b/src/ElectronNET.IntegrationTests/Tests/NativeThemeTests.cs @@ -3,6 +3,7 @@ namespace ElectronNET.IntegrationTests.Tests using System.Runtime.Versioning; using ElectronNET.API; using ElectronNET.API.Entities; + using ElectronNET.Common; [Collection("ElectronCollection")] public class NativeThemeTests @@ -13,19 +14,19 @@ public async Task ThemeSource_roundtrip() // Capture initial _ = await Electron.NativeTheme.ShouldUseDarkColorsAsync(); // Force light - await Task.Delay(50); + await Task.Delay(50.ms()); Electron.NativeTheme.SetThemeSource(ThemeSourceMode.Light); - await Task.Delay(500); + await Task.Delay(500.ms()); var useDarkAfterLight = await Electron.NativeTheme.ShouldUseDarkColorsAsync(); var themeSourceLight = await Electron.NativeTheme.GetThemeSourceAsync(); // Force dark Electron.NativeTheme.SetThemeSource(ThemeSourceMode.Dark); - await Task.Delay(500); + await Task.Delay(500.ms()); var useDarkAfterDark = await Electron.NativeTheme.ShouldUseDarkColorsAsync(); var themeSourceDark = await Electron.NativeTheme.GetThemeSourceAsync(); // Restore system Electron.NativeTheme.SetThemeSource(ThemeSourceMode.System); - await Task.Delay(500); + await Task.Delay(500.ms()); var themeSourceSystem = await Electron.NativeTheme.GetThemeSourceAsync(); // Assertions are tolerant (platform dependent) useDarkAfterLight.Should().BeFalse("forcing Light should result in light colors"); @@ -41,11 +42,11 @@ public async Task Updated_event_fires_on_change() var fired = false; Electron.NativeTheme.Updated += () => fired = true; Electron.NativeTheme.SetThemeSource(ThemeSourceMode.Dark); - await Task.Delay(400); + await Task.Delay(400.ms()); Electron.NativeTheme.SetThemeSource(ThemeSourceMode.Light); for (int i = 0; i < 10 && !fired; i++) { - await Task.Delay(100); + await Task.Delay(100.ms()); } fired.Should().BeTrue(); diff --git a/src/ElectronNET.IntegrationTests/Tests/NotificationTests.cs b/src/ElectronNET.IntegrationTests/Tests/NotificationTests.cs index a974bdc2..e4f9a419 100644 --- a/src/ElectronNET.IntegrationTests/Tests/NotificationTests.cs +++ b/src/ElectronNET.IntegrationTests/Tests/NotificationTests.cs @@ -3,6 +3,7 @@ namespace ElectronNET.IntegrationTests.Tests using System.Runtime.InteropServices; using ElectronNET.API; using ElectronNET.API.Entities; + using ElectronNET.Common; [Collection("ElectronCollection")] public class NotificationTests @@ -17,11 +18,11 @@ public async Task Notification_create_check() var options = new NotificationOptions("Notification Title", "Notification test 123"); options.OnShow = () => tcs.SetResult(); - await Task.Delay(500); + await Task.Delay(500.ms()); Electron.Notification.Show(options); - await Task.WhenAny(tcs.Task, Task.Delay(5_000)); + await Task.WhenAny(tcs.Task, Task.Delay(5.seconds())); tcs.Task.IsCompletedSuccessfully.Should().BeTrue(); } diff --git a/src/ElectronNET.IntegrationTests/Tests/WebContentsTests.cs b/src/ElectronNET.IntegrationTests/Tests/WebContentsTests.cs index 3e807303..61ec8a8a 100644 --- a/src/ElectronNET.IntegrationTests/Tests/WebContentsTests.cs +++ b/src/ElectronNET.IntegrationTests/Tests/WebContentsTests.cs @@ -4,6 +4,7 @@ namespace ElectronNET.IntegrationTests.Tests { using ElectronNET.API; using ElectronNET.API.Entities; + using ElectronNET.Common; [Collection("ElectronCollection")] public class WebContentsTests @@ -40,7 +41,7 @@ public async Task DomReady_event_fires() var fired = false; wc.OnDomReady += () => fired = true; await wc.LoadURLAsync("https://example.com"); - await Task.Delay(500); + await Task.Delay(500.ms()); fired.Should().BeTrue(); } @@ -90,7 +91,7 @@ public async Task GetSetZoomFactor_check() var ok = await fx.MainWindow.WebContents.GetZoomFactorAsync(); ok.Should().BeGreaterThan(0.0); fx.MainWindow.WebContents.SetZoomFactor(2.0); - await Task.Delay(500); + await Task.Delay(500.ms()); ok = await fx.MainWindow.WebContents.GetZoomFactorAsync(); ok.Should().Be(2.0); } @@ -106,16 +107,16 @@ public async Task GetSetZoomLevel_check() window = await Electron.WindowManager.CreateWindowAsync(new BrowserWindowOptions { Show = true }, "about:blank"); - await Task.Delay(100); + await Task.Delay(100.ms()); window.WebContents.SetZoomLevel(0); - await Task.Delay(500); + await Task.Delay(500.ms()); var ok = await window.WebContents.GetZoomLevelAsync(); ok.Should().Be(0); window.WebContents.SetZoomLevel(2); - await Task.Delay(500); + await Task.Delay(500.ms()); ok = await window.WebContents.GetZoomLevelAsync(); ok.Should().Be(2); @@ -137,15 +138,15 @@ public async Task DevTools_check() window = await Electron.WindowManager.CreateWindowAsync(new BrowserWindowOptions { Show = true }, "about:blank"); - await Task.Delay(3000); + await Task.Delay(3.seconds()); window.WebContents.IsDevToolsOpened().Should().BeFalse(); window.WebContents.OpenDevTools(); - await Task.Delay(5000); + await Task.Delay(5.seconds()); window.WebContents.IsDevToolsOpened().Should().BeTrue(); window.WebContents.CloseDevTools(); - await Task.Delay(2000); + await Task.Delay(2.seconds()); window.WebContents.IsDevToolsOpened().Should().BeFalse(); } @@ -159,11 +160,11 @@ public async Task DevTools_check() public async Task GetSetAudioMuted_check() { fx.MainWindow.WebContents.SetAudioMuted(true); - await Task.Delay(500); + await Task.Delay(500.ms()); var ok = await fx.MainWindow.WebContents.IsAudioMutedAsync(); ok.Should().BeTrue(); fx.MainWindow.WebContents.SetAudioMuted(false); - await Task.Delay(500); + await Task.Delay(500.ms()); ok = await fx.MainWindow.WebContents.IsAudioMutedAsync(); ok.Should().BeFalse(); @@ -186,11 +187,11 @@ public async Task GetSetUserAgent_check() window = await Electron.WindowManager.CreateWindowAsync(new BrowserWindowOptions { Show = true }, "about:blank"); - await Task.Delay(3000); + await Task.Delay(3.seconds()); window.WebContents.SetUserAgent("MyUserAgent/1.0"); - await Task.Delay(1000); + await Task.Delay(1.seconds()); var ok = await window.WebContents.GetUserAgentAsync(); ok.Should().Be("MyUserAgent/1.0"); From 153625ba512bc997e6e570c72756d305deaa8b7f Mon Sep 17 00:00:00 2001 From: softworkz Date: Sun, 7 Dec 2025 11:29:50 +0100 Subject: [PATCH 10/11] Introduce IntegrationFactAttribute --- .../Common/IntegrationFactAttribute.cs | 101 ++++++++++++++++++ .../Common/SkipOnWslFactAttribute.cs | 49 --------- .../Tests/AppTests.cs | 25 ++--- .../Tests/AutoUpdaterTests.cs | 25 ++--- .../Tests/BrowserViewTests.cs | 3 +- .../Tests/BrowserWindowTests.cs | 32 +++--- .../Tests/ClipboardTests.cs | 7 +- .../Tests/CookiesTests.cs | 3 +- .../Tests/GlobalShortcutTests.cs | 3 +- .../Tests/HostHookTests.cs | 3 +- .../Tests/IpcMainTests.cs | 11 +- .../Tests/MenuTests.cs | 5 +- .../Tests/MultiEventRegistrationTests.cs | 6 +- .../Tests/NativeImageTests.cs | 9 +- .../Tests/NativeThemeTests.cs | 9 +- .../Tests/NotificationTests.cs | 5 +- .../Tests/ProcessTests.cs | 5 +- .../Tests/ScreenTests.cs | 12 +-- .../Tests/SessionTests.cs | 19 ++-- .../Tests/ShellTests.cs | 3 +- .../Tests/ThumbarButtonTests.cs | 5 +- .../Tests/TrayTests.cs | 3 +- .../Tests/WebContentsTests.cs | 32 +++--- src/ElectronNET.sln.DotSettings | 2 + 24 files changed, 221 insertions(+), 156 deletions(-) create mode 100644 src/ElectronNET.IntegrationTests/Common/IntegrationFactAttribute.cs delete mode 100644 src/ElectronNET.IntegrationTests/Common/SkipOnWslFactAttribute.cs diff --git a/src/ElectronNET.IntegrationTests/Common/IntegrationFactAttribute.cs b/src/ElectronNET.IntegrationTests/Common/IntegrationFactAttribute.cs new file mode 100644 index 00000000..6142bdfd --- /dev/null +++ b/src/ElectronNET.IntegrationTests/Common/IntegrationFactAttribute.cs @@ -0,0 +1,101 @@ +namespace ElectronNET.IntegrationTests.Common +{ + using System.Runtime.InteropServices; + using Xunit.Sdk; + + /// + /// Custom fact attribute with a default timeout of 20 seconds, allowing tests to be skipped on specific environments. + /// + /// + [AttributeUsage(AttributeTargets.Method)] + [XunitTestCaseDiscoverer("Xunit.Sdk.SkippableFactDiscoverer", "Xunit.SkippableFact")] + internal sealed class IntegrationFactAttribute : FactAttribute + { + private static readonly bool IsOnWsl; + + private static readonly bool IsOnCI; + + static IntegrationFactAttribute() + { + IsOnWsl = DetectWsl(); + IsOnCI = DetectCI(); + } + + /// + /// Initializes a new instance of the class. + /// + public IntegrationFactAttribute() + { + this.Timeout = 20_000; + } + + public bool SkipOnWsl { get; set; } + + public bool SkipOnCI { get; set; } + + /// + /// Marks the test so that it will not be run, and gets or sets the skip reason + /// + public override string Skip { + get + { + if (IsOnWsl && this.SkipOnWsl) + { + return "Skipping test on WSL environment."; + } + + if (IsOnCI && this.SkipOnCI) + { + return "Skipping test on CI environment."; + } + + return base.Skip; + } + set + { + base.Skip = value; + } + } + + private static bool DetectWsl() + { + try + { + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + return false; + } + + if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("WSL_DISTRO_NAME")) || + !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("WSL_INTEROP"))) + { + return true; + } + + return false; + } + catch + { + return false; + } + } + + private static bool DetectCI() + { + try + { + if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("TF_BUILD")) || + !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("GITHUB_ACTIONS"))) + { + return true; + } + + return false; + } + catch + { + return false; + } + } + } +} diff --git a/src/ElectronNET.IntegrationTests/Common/SkipOnWslFactAttribute.cs b/src/ElectronNET.IntegrationTests/Common/SkipOnWslFactAttribute.cs deleted file mode 100644 index 8c1a3d02..00000000 --- a/src/ElectronNET.IntegrationTests/Common/SkipOnWslFactAttribute.cs +++ /dev/null @@ -1,49 +0,0 @@ -namespace ElectronNET.IntegrationTests.Common -{ - using System.Runtime.InteropServices; - - [AttributeUsage(AttributeTargets.Method)] - internal sealed class SkipOnWslFactAttribute : FactAttribute - { - private static readonly bool IsOnWsl; - - static SkipOnWslFactAttribute() - { - IsOnWsl = DetectWsl(); - } - - /// - /// Initializes a new instance of the class. - /// - public SkipOnWslFactAttribute() - { - if (IsOnWsl) - { - this.Skip = "Skipping test on WSL environment."; - } - } - - private static bool DetectWsl() - { - try - { - if (!RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - { - return false; - } - - if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("WSL_DISTRO_NAME")) || - !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("WSL_INTEROP"))) - { - return true; - } - - return false; - } - catch - { - return false; - } - } - } -} diff --git a/src/ElectronNET.IntegrationTests/Tests/AppTests.cs b/src/ElectronNET.IntegrationTests/Tests/AppTests.cs index 1a15efdb..ed15a12e 100644 --- a/src/ElectronNET.IntegrationTests/Tests/AppTests.cs +++ b/src/ElectronNET.IntegrationTests/Tests/AppTests.cs @@ -6,6 +6,7 @@ namespace ElectronNET.IntegrationTests.Tests using System.IO; using System.Runtime.Versioning; using System.Threading.Tasks; + using ElectronNET.IntegrationTests.Common; [Collection("ElectronCollection")] public class AppTests @@ -18,7 +19,7 @@ public AppTests(ElectronFixture fx) this.fx = fx; } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task Can_get_app_path() { var path = await Electron.App.GetAppPathAsync(); @@ -26,7 +27,7 @@ public async Task Can_get_app_path() Directory.Exists(path).Should().BeTrue(); } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task Can_get_version_and_locale() { var version = await Electron.App.GetVersionAsync(); @@ -35,7 +36,7 @@ public async Task Can_get_version_and_locale() locale.Should().NotBeNullOrWhiteSpace(); } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task Can_get_special_paths() { var userData = await Electron.App.GetPathAsync(PathName.UserData); @@ -47,7 +48,7 @@ public async Task Can_get_special_paths() Directory.Exists(temp).Should().BeTrue(); } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task Can_get_app_metrics() { var metrics = await Electron.App.GetAppMetricsAsync(); @@ -55,14 +56,14 @@ public async Task Can_get_app_metrics() metrics.Length.Should().BeGreaterThan(0); } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task Can_get_gpu_feature_status() { var status = await Electron.App.GetGpuFeatureStatusAsync(); status.Should().NotBeNull(); } - [SkippableFact(Timeout = 20000)] + [IntegrationFact] [SupportedOSPlatform("macOS")] [SupportedOSPlatform("Windows")] public async Task Can_get_login_item_settings() @@ -71,7 +72,7 @@ public async Task Can_get_login_item_settings() settings.Should().NotBeNull(); } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task CommandLine_append_and_query_switch() { var switchName = "integration-switch"; @@ -80,7 +81,7 @@ public async Task CommandLine_append_and_query_switch() (await Electron.App.CommandLine.GetSwitchValueAsync(switchName)).Should().Be("value123"); } - [SkippableFact(Timeout = 20000)] + [IntegrationFact] [SupportedOSPlatform("macOS")] [SupportedOSPlatform("Windows")] public async Task Accessibility_support_toggle() @@ -91,7 +92,7 @@ public async Task Accessibility_support_toggle() Electron.App.SetAccessibilitySupportEnabled(false); } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task UserAgentFallback_roundtrip() { var original = await Electron.App.UserAgentFallbackAsync; @@ -101,7 +102,7 @@ public async Task UserAgentFallback_roundtrip() Electron.App.UserAgentFallback = original; // restore } - [SkippableFact(Timeout = 20000)] + [IntegrationFact] [SupportedOSPlatform("Linux")] [SupportedOSPlatform("macOS")] public async Task BadgeCount_set_and_reset_where_supported() @@ -113,14 +114,14 @@ public async Task BadgeCount_set_and_reset_where_supported() await Electron.App.SetBadgeCountAsync(0); } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task App_metrics_have_cpu_info() { var metrics = await Electron.App.GetAppMetricsAsync(); metrics[0].Cpu.Should().NotBeNull(); } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task App_gpu_feature_status_has_some_fields() { var status = await Electron.App.GetGpuFeatureStatusAsync(); diff --git a/src/ElectronNET.IntegrationTests/Tests/AutoUpdaterTests.cs b/src/ElectronNET.IntegrationTests/Tests/AutoUpdaterTests.cs index fe061322..8a6a5315 100644 --- a/src/ElectronNET.IntegrationTests/Tests/AutoUpdaterTests.cs +++ b/src/ElectronNET.IntegrationTests/Tests/AutoUpdaterTests.cs @@ -3,6 +3,7 @@ using API; using System.Threading.Tasks; using ElectronNET.Common; + using ElectronNET.IntegrationTests.Common; [Collection("ElectronCollection")] public class AutoUpdaterTests @@ -14,7 +15,7 @@ public AutoUpdaterTests(ElectronFixture fx) this.fx = fx; } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task AutoDownload_check() { Electron.AutoUpdater.AutoDownload = false; @@ -25,7 +26,7 @@ public async Task AutoDownload_check() test2.Should().BeTrue(); } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task AutoInstallOnAppQuit_check() { Electron.AutoUpdater.AutoInstallOnAppQuit = false; @@ -36,7 +37,7 @@ public async Task AutoInstallOnAppQuit_check() test2.Should().BeTrue(); } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task AllowPrerelease_check() { Electron.AutoUpdater.AllowPrerelease = false; @@ -47,7 +48,7 @@ public async Task AllowPrerelease_check() test2.Should().BeTrue(); } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task FullChangelog_check() { Electron.AutoUpdater.FullChangelog = false; @@ -58,7 +59,7 @@ public async Task FullChangelog_check() test2.Should().BeTrue(); } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task AllowDowngrade_check() { Electron.AutoUpdater.AllowDowngrade = false; @@ -69,14 +70,14 @@ public async Task AllowDowngrade_check() test2.Should().BeTrue(); } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task UpdateConfigPath_check() { var test1 = Electron.AutoUpdater.UpdateConfigPath; test1.Should().Be(string.Empty); } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task CurrentVersionAsync_check() { var semver = await Electron.AutoUpdater.CurrentVersionAsync; @@ -84,7 +85,7 @@ public async Task CurrentVersionAsync_check() semver.Major.Should().BeGreaterThan(0); } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task ChannelAsync_check() { var test = await Electron.AutoUpdater.ChannelAsync; @@ -95,7 +96,7 @@ public async Task ChannelAsync_check() test.Should().Be("beta"); } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task RequestHeadersAsync_check() { var headers = new Dictionary @@ -112,21 +113,21 @@ public async Task RequestHeadersAsync_check() test["key1"].Should().Be("value1"); } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task CheckForUpdatesAsync_check() { var test = await Electron.AutoUpdater.CheckForUpdatesAsync(); test.Should().BeNull(); } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task CheckForUpdatesAndNotifyAsync_check() { var test = await Electron.AutoUpdater.CheckForUpdatesAsync(); test.Should().BeNull(); } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task GetFeedURLAsync_check() { var test = await Electron.AutoUpdater.GetFeedURLAsync(); diff --git a/src/ElectronNET.IntegrationTests/Tests/BrowserViewTests.cs b/src/ElectronNET.IntegrationTests/Tests/BrowserViewTests.cs index 8405da75..02f0f726 100644 --- a/src/ElectronNET.IntegrationTests/Tests/BrowserViewTests.cs +++ b/src/ElectronNET.IntegrationTests/Tests/BrowserViewTests.cs @@ -2,6 +2,7 @@ namespace ElectronNET.IntegrationTests.Tests { using ElectronNET.API; using ElectronNET.API.Entities; + using ElectronNET.IntegrationTests.Common; [Collection("ElectronCollection")] public class BrowserViewTests @@ -13,7 +14,7 @@ public BrowserViewTests(ElectronFixture fx) this.fx = fx; } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task Create_browser_view_and_adjust_bounds() { var view = await Electron.WindowManager.CreateBrowserViewAsync(new BrowserViewConstructorOptions()); diff --git a/src/ElectronNET.IntegrationTests/Tests/BrowserWindowTests.cs b/src/ElectronNET.IntegrationTests/Tests/BrowserWindowTests.cs index 0810f4ad..091f33e5 100644 --- a/src/ElectronNET.IntegrationTests/Tests/BrowserWindowTests.cs +++ b/src/ElectronNET.IntegrationTests/Tests/BrowserWindowTests.cs @@ -16,7 +16,7 @@ public BrowserWindowTests(ElectronFixture fx) this.fx = fx; } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task Can_set_and_get_title() { const string title = "Integration Test Title"; @@ -26,7 +26,7 @@ public async Task Can_set_and_get_title() roundTrip.Should().Be(title); } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task Can_resize_and_get_size() { this.fx.MainWindow.SetSize(643, 482); @@ -37,7 +37,7 @@ public async Task Can_resize_and_get_size() size[1].Should().Be(482); } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task Can_set_progress_bar_and_clear() { this.fx.MainWindow.SetProgressBar(0.5); @@ -46,7 +46,7 @@ public async Task Can_set_progress_bar_and_clear() await Task.Delay(50.ms()); } - [SkipOnWslFact(Timeout = 20000)] + [IntegrationFact(SkipOnWsl = true)] public async Task Can_set_and_get_position() { this.fx.MainWindow.SetPosition(134, 246); @@ -55,7 +55,7 @@ public async Task Can_set_and_get_position() pos.Should().BeEquivalentTo([134, 246]); } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task Can_set_and_get_bounds() { var bounds = new Rectangle { X = 10, Y = 20, Width = 400, Height = 300 }; @@ -68,7 +68,7 @@ public async Task Can_set_and_get_bounds() round.Height.Should().Be(300); } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task Can_set_and_get_content_bounds() { var bounds = new Rectangle { X = 0, Y = 0, Width = 300, Height = 200 }; @@ -79,7 +79,7 @@ public async Task Can_set_and_get_content_bounds() round.Height.Should().BeGreaterThan(0); } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task Show_hide_visibility_roundtrip() { BrowserWindow window = null; @@ -106,7 +106,7 @@ public async Task Show_hide_visibility_roundtrip() } } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task AlwaysOnTop_toggle_and_query() { this.fx.MainWindow.SetAlwaysOnTop(true); @@ -117,7 +117,7 @@ public async Task AlwaysOnTop_toggle_and_query() (await this.fx.MainWindow.IsAlwaysOnTopAsync()).Should().BeFalse(); } - [SkippableFact(Timeout = 20000)] + [IntegrationFact] [SupportedOSPlatform("Linux")] [SupportedOSPlatform("Windows")] public async Task MenuBar_auto_hide_and_visibility() @@ -133,7 +133,7 @@ public async Task MenuBar_auto_hide_and_visibility() (await this.fx.MainWindow.IsMenuBarVisibleAsync()).Should().BeTrue(); } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task ReadyToShow_event_fires_after_content_ready() { BrowserWindow window = null; @@ -160,7 +160,7 @@ public async Task ReadyToShow_event_fires_after_content_ready() } } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task PageTitleUpdated_event_fires_on_title_change() { var window = await Electron.WindowManager.CreateWindowAsync(new BrowserWindowOptions { Show = true }, "about:blank"); @@ -181,7 +181,7 @@ public async Task PageTitleUpdated_event_fires_on_title_change() (await tcs.Task).Should().Be("NewTitle"); } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task Resize_event_fires_on_size_change() { var window = await Electron.WindowManager.CreateWindowAsync(new BrowserWindowOptions { Show = false }, "about:blank"); @@ -193,7 +193,7 @@ public async Task Resize_event_fires_on_size_change() resized.Should().BeTrue(); } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task Progress_bar_and_always_on_top_toggle() { var win = this.fx.MainWindow; @@ -209,7 +209,7 @@ public async Task Progress_bar_and_always_on_top_toggle() (await win.IsAlwaysOnTopAsync()).Should().BeFalse(); } - [SkippableFact(Timeout = 20000)] + [IntegrationFact] [SupportedOSPlatform("Linux")] [SupportedOSPlatform("Windows")] public async Task Menu_bar_visibility_and_auto_hide() @@ -223,7 +223,7 @@ public async Task Menu_bar_visibility_and_auto_hide() (await win.IsMenuBarVisibleAsync()).Should().BeTrue(); } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task Parent_child_relationship_roundtrip() { var child = await Electron.WindowManager.CreateWindowAsync(new BrowserWindowOptions { Show = false, Width = 300, Height = 200 }, "about:blank"); @@ -237,7 +237,7 @@ public async Task Parent_child_relationship_roundtrip() child.Destroy(); } - [SkippableFact(Timeout = 20000)] + [IntegrationFact] [SupportedOSPlatform("macOS")] public async Task Represented_filename_and_edited_flags() { diff --git a/src/ElectronNET.IntegrationTests/Tests/ClipboardTests.cs b/src/ElectronNET.IntegrationTests/Tests/ClipboardTests.cs index 17ff0dd6..35ec2645 100644 --- a/src/ElectronNET.IntegrationTests/Tests/ClipboardTests.cs +++ b/src/ElectronNET.IntegrationTests/Tests/ClipboardTests.cs @@ -2,6 +2,7 @@ namespace ElectronNET.IntegrationTests.Tests { using System.Runtime.Versioning; using ElectronNET.API; + using ElectronNET.IntegrationTests.Common; [Collection("ElectronCollection")] public class ClipboardTests @@ -14,7 +15,7 @@ public ClipboardTests(ElectronFixture fx) this.fx = fx; } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task Clipboard_text_roundtrip() { var text = $"Hello Electron {Guid.NewGuid()}"; @@ -23,7 +24,7 @@ public async Task Clipboard_text_roundtrip() read.Should().Be(text); } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task Available_formats_contains_text_after_write() { var text = "FormatsTest"; @@ -32,7 +33,7 @@ public async Task Available_formats_contains_text_after_write() formats.Should().Contain(f => f.Contains("text") || f.Contains("TEXT") || f.Contains("plain")); } - [SkippableFact(Timeout = 20000)] + [IntegrationFact] [SupportedOSPlatform("macOS")] [SupportedOSPlatform("Windows")] public async Task Bookmark_write_and_read() diff --git a/src/ElectronNET.IntegrationTests/Tests/CookiesTests.cs b/src/ElectronNET.IntegrationTests/Tests/CookiesTests.cs index aea1ef05..9202e298 100644 --- a/src/ElectronNET.IntegrationTests/Tests/CookiesTests.cs +++ b/src/ElectronNET.IntegrationTests/Tests/CookiesTests.cs @@ -1,6 +1,7 @@ namespace ElectronNET.IntegrationTests.Tests { using ElectronNET.Common; + using ElectronNET.IntegrationTests.Common; [Collection("ElectronCollection")] public class CookiesTests @@ -12,7 +13,7 @@ public CookiesTests(ElectronFixture fx) this.fx = fx; } - [Fact(Skip = "Cookie set/get requires navigation to domain; skipping until test harness serves page")] + [IntegrationFact(Skip = "Cookie set/get requires navigation to domain; skipping until test harness serves page")] public async Task Cookie_set_get_remove_sequence() { var session = this.fx.MainWindow.WebContents.Session; diff --git a/src/ElectronNET.IntegrationTests/Tests/GlobalShortcutTests.cs b/src/ElectronNET.IntegrationTests/Tests/GlobalShortcutTests.cs index 625618f3..adfafde2 100644 --- a/src/ElectronNET.IntegrationTests/Tests/GlobalShortcutTests.cs +++ b/src/ElectronNET.IntegrationTests/Tests/GlobalShortcutTests.cs @@ -2,11 +2,12 @@ namespace ElectronNET.IntegrationTests.Tests { using System.Runtime.InteropServices; using ElectronNET.API; + using ElectronNET.IntegrationTests.Common; [Collection("ElectronCollection")] public class GlobalShortcutTests { - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task Can_register_and_unregister() { var accel = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? "Cmd+Alt+G" : "Ctrl+Alt+G"; diff --git a/src/ElectronNET.IntegrationTests/Tests/HostHookTests.cs b/src/ElectronNET.IntegrationTests/Tests/HostHookTests.cs index 254f7aa4..bc4e896a 100644 --- a/src/ElectronNET.IntegrationTests/Tests/HostHookTests.cs +++ b/src/ElectronNET.IntegrationTests/Tests/HostHookTests.cs @@ -1,11 +1,12 @@ namespace ElectronNET.IntegrationTests.Tests { using ElectronNET.API; + using ElectronNET.IntegrationTests.Common; [Collection("ElectronCollection")] public class HostHookTests { - [Fact(Skip = "Requires HostHook setup; skipping")] + [IntegrationFact(Skip = "Requires HostHook setup; skipping")] public async Task HostHook_call_returns_value() { var result = await Electron.HostHook.CallAsync("create-excel-file", "."); diff --git a/src/ElectronNET.IntegrationTests/Tests/IpcMainTests.cs b/src/ElectronNET.IntegrationTests/Tests/IpcMainTests.cs index f9a5709e..bd894cd6 100644 --- a/src/ElectronNET.IntegrationTests/Tests/IpcMainTests.cs +++ b/src/ElectronNET.IntegrationTests/Tests/IpcMainTests.cs @@ -2,6 +2,7 @@ namespace ElectronNET.IntegrationTests.Tests { using ElectronNET.API; using ElectronNET.Common; + using ElectronNET.IntegrationTests.Common; [Collection("ElectronCollection")] public class IpcMainTests @@ -13,7 +14,7 @@ public IpcMainTests(ElectronFixture fx) this.fx = fx; } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task Ipc_On_receives_message_from_renderer() { object received = null; @@ -34,7 +35,7 @@ await Electron.IpcMain.On("ipc-on-test", obj => result.Should().Be("payload123"); } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task Ipc_Once_only_fires_once() { var count = 0; @@ -44,7 +45,7 @@ public async Task Ipc_Once_only_fires_once() count.Should().Be(1); } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task Ipc_RemoveAllListeners_stops_receiving() { var fired = false; @@ -55,7 +56,7 @@ public async Task Ipc_RemoveAllListeners_stops_receiving() fired.Should().BeFalse(); } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task Ipc_OnSync_returns_value() { object received = null; @@ -73,7 +74,7 @@ public async Task Ipc_OnSync_returns_value() ret.Should().Be("pong"); } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task Ipc_Send_from_main_reaches_renderer() { // Listener: store raw arg; if Electron packs differently we will normalize later diff --git a/src/ElectronNET.IntegrationTests/Tests/MenuTests.cs b/src/ElectronNET.IntegrationTests/Tests/MenuTests.cs index 627ad259..314c5cf7 100644 --- a/src/ElectronNET.IntegrationTests/Tests/MenuTests.cs +++ b/src/ElectronNET.IntegrationTests/Tests/MenuTests.cs @@ -3,6 +3,7 @@ namespace ElectronNET.IntegrationTests.Tests using ElectronNET.API; using ElectronNET.API.Entities; using ElectronNET.Common; + using ElectronNET.IntegrationTests.Common; [Collection("ElectronCollection")] public class MenuTests @@ -14,7 +15,7 @@ public MenuTests(ElectronFixture fx) this.fx = fx; } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task ApplicationMenu_click_invokes_handler() { var clicked = false; @@ -40,7 +41,7 @@ public async Task ApplicationMenu_click_invokes_handler() clicked.Should().BeTrue(); } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task ContextMenu_popup_registers_items() { var win = this.fx.MainWindow; diff --git a/src/ElectronNET.IntegrationTests/Tests/MultiEventRegistrationTests.cs b/src/ElectronNET.IntegrationTests/Tests/MultiEventRegistrationTests.cs index e00c8509..f66e13fe 100644 --- a/src/ElectronNET.IntegrationTests/Tests/MultiEventRegistrationTests.cs +++ b/src/ElectronNET.IntegrationTests/Tests/MultiEventRegistrationTests.cs @@ -1,5 +1,7 @@ namespace ElectronNET.IntegrationTests.Tests { + using ElectronNET.IntegrationTests.Common; + [Collection("ElectronCollection")] public class MultiEventRegistrationTests { @@ -17,7 +19,7 @@ private static async Task WaitAllOrTimeout(TimeSpan timeout, params Task[] return ReferenceEquals(completed, all) && all.IsCompletedSuccessfully; } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task BrowserWindow_OnResize_multiple_handlers_called() { var win = this.fx.MainWindow; @@ -41,7 +43,7 @@ public async Task BrowserWindow_OnResize_multiple_handlers_called() } } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task WebContents_OnDomReady_multiple_handlers_called() { var wc = this.fx.MainWindow.WebContents; diff --git a/src/ElectronNET.IntegrationTests/Tests/NativeImageTests.cs b/src/ElectronNET.IntegrationTests/Tests/NativeImageTests.cs index fd8cac8a..2a31b35a 100644 --- a/src/ElectronNET.IntegrationTests/Tests/NativeImageTests.cs +++ b/src/ElectronNET.IntegrationTests/Tests/NativeImageTests.cs @@ -5,11 +5,12 @@ namespace ElectronNET.IntegrationTests.Tests { using System.Drawing; using ElectronNET.API.Entities; + using ElectronNET.IntegrationTests.Common; [SupportedOSPlatform("Windows")] public class NativeImageTests { - [SkippableFact(Timeout = 20000)] + [IntegrationFact] public async Task Create_from_bitmap_and_to_png() { using var bmp = new Bitmap(10, 10); @@ -27,7 +28,7 @@ public async Task Create_from_bitmap_and_to_png() png!.Length.Should().BeGreaterThan(0); } - [SkippableFact(Timeout = 20000)] + [IntegrationFact] public async Task Create_from_buffer_and_to_data_url() { // Prepare PNG bytes @@ -46,7 +47,7 @@ public async Task Create_from_buffer_and_to_data_url() dataUrl!.StartsWith("data:image/", StringComparison.Ordinal).Should().BeTrue(); } - [SkippableFact(Timeout = 20000)] + [IntegrationFact] public async Task Resize_and_crop_produce_expected_sizes() { using var bmp = new Bitmap(12, 10); @@ -66,7 +67,7 @@ public async Task Resize_and_crop_produce_expected_sizes() csize.Height.Should().Be(3); } - [SkippableFact(Timeout = 20000)] + [IntegrationFact] public async Task Add_representation_for_scale_factor() { using var bmp = new Bitmap(5, 5); diff --git a/src/ElectronNET.IntegrationTests/Tests/NativeThemeTests.cs b/src/ElectronNET.IntegrationTests/Tests/NativeThemeTests.cs index 4e5d8907..9a11cf56 100644 --- a/src/ElectronNET.IntegrationTests/Tests/NativeThemeTests.cs +++ b/src/ElectronNET.IntegrationTests/Tests/NativeThemeTests.cs @@ -4,11 +4,12 @@ namespace ElectronNET.IntegrationTests.Tests using ElectronNET.API; using ElectronNET.API.Entities; using ElectronNET.Common; + using ElectronNET.IntegrationTests.Common; [Collection("ElectronCollection")] public class NativeThemeTests { - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task ThemeSource_roundtrip() { // Capture initial @@ -36,7 +37,7 @@ public async Task ThemeSource_roundtrip() themeSourceSystem.Should().Be(ThemeSourceMode.System); } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task Updated_event_fires_on_change() { var fired = false; @@ -52,7 +53,7 @@ public async Task Updated_event_fires_on_change() fired.Should().BeTrue(); } - [SkippableFact(Timeout = 20000)] + [IntegrationFact] [SupportedOSPlatform("macOS")] [SupportedOSPlatform("Windows")] public async Task Should_use_high_contrast_colors_check() @@ -61,7 +62,7 @@ public async Task Should_use_high_contrast_colors_check() metrics.Should().Be(false); } - [SkippableFact(Timeout = 20000)] + [IntegrationFact] [SupportedOSPlatform("macOS")] [SupportedOSPlatform("Windows")] public async Task Should_use_inverted_colors_check() diff --git a/src/ElectronNET.IntegrationTests/Tests/NotificationTests.cs b/src/ElectronNET.IntegrationTests/Tests/NotificationTests.cs index e4f9a419..ee066ef0 100644 --- a/src/ElectronNET.IntegrationTests/Tests/NotificationTests.cs +++ b/src/ElectronNET.IntegrationTests/Tests/NotificationTests.cs @@ -4,11 +4,12 @@ namespace ElectronNET.IntegrationTests.Tests using ElectronNET.API; using ElectronNET.API.Entities; using ElectronNET.Common; + using ElectronNET.IntegrationTests.Common; [Collection("ElectronCollection")] public class NotificationTests { - [SkippableFact(Timeout = 20000)] + [IntegrationFact] public async Task Notification_create_check() { Skip.If(RuntimeInformation.IsOSPlatform(OSPlatform.Linux), "Always returns false. Might need full-blown desktop environment"); @@ -27,7 +28,7 @@ public async Task Notification_create_check() tcs.Task.IsCompletedSuccessfully.Should().BeTrue(); } - [SkippableFact(Timeout = 20000)] + [IntegrationFact] public async Task Notification_is_supported_check() { Skip.If(RuntimeInformation.IsOSPlatform(OSPlatform.Linux), "Always returns false. Might need full-blown desktop environment"); diff --git a/src/ElectronNET.IntegrationTests/Tests/ProcessTests.cs b/src/ElectronNET.IntegrationTests/Tests/ProcessTests.cs index 3fe27248..d6c76b12 100644 --- a/src/ElectronNET.IntegrationTests/Tests/ProcessTests.cs +++ b/src/ElectronNET.IntegrationTests/Tests/ProcessTests.cs @@ -1,11 +1,12 @@ namespace ElectronNET.IntegrationTests.Tests { using ElectronNET.API; + using ElectronNET.IntegrationTests.Common; [Collection("ElectronCollection")] public class ProcessTests { - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task Process_info_is_accessible() { // Use renderer to fetch process info and round-trip @@ -14,7 +15,7 @@ public async Task Process_info_is_accessible() result.Should().Be("ok"); } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task Process_properties_are_populated() { var execPath = await Electron.Process.ExecPathAsync; diff --git a/src/ElectronNET.IntegrationTests/Tests/ScreenTests.cs b/src/ElectronNET.IntegrationTests/Tests/ScreenTests.cs index 9de422d4..fa6b4fab 100644 --- a/src/ElectronNET.IntegrationTests/Tests/ScreenTests.cs +++ b/src/ElectronNET.IntegrationTests/Tests/ScreenTests.cs @@ -16,7 +16,7 @@ public ScreenTests(ElectronFixture fx) this.fx = fx; } - [SkipOnWslFact(Timeout = 20000)] + [IntegrationFact(SkipOnWsl = true)] public async Task Primary_display_has_positive_dimensions() { var display = await Electron.Screen.GetPrimaryDisplayAsync(); @@ -24,7 +24,7 @@ public async Task Primary_display_has_positive_dimensions() display.Size.Height.Should().BeGreaterThan(0); } - [SkipOnWslFact(Timeout = 20000)] + [IntegrationFact(SkipOnWsl = true)] public async Task GetAllDisplays_returns_at_least_one() { var displays = await Electron.Screen.GetAllDisplaysAsync(); @@ -32,14 +32,14 @@ public async Task GetAllDisplays_returns_at_least_one() displays.Length.Should().BeGreaterThan(0); } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task GetCursorScreenPoint_check() { var point = await Electron.Screen.GetCursorScreenPointAsync(); point.Should().NotBeNull(); } - [SkippableFact(Timeout = 20000)] + [IntegrationFact] [SupportedOSPlatform("macOS")] public async Task GetMenuBarWorkArea_check() { @@ -51,7 +51,7 @@ public async Task GetMenuBarWorkArea_check() area.Width.Should().BeGreaterThan(0); } - [SkipOnWslFact(Timeout = 20000)] + [IntegrationFact(SkipOnWsl = true)] public async Task GetDisplayNearestPoint_check() { var point = new Point @@ -65,7 +65,7 @@ public async Task GetDisplayNearestPoint_check() display.Size.Height.Should().BeGreaterThan(0); } - [SkipOnWslFact(Timeout = 20000)] + [IntegrationFact(SkipOnWsl = true)] public async Task GetDisplayMatching_check() { var rectangle = new Rectangle diff --git a/src/ElectronNET.IntegrationTests/Tests/SessionTests.cs b/src/ElectronNET.IntegrationTests/Tests/SessionTests.cs index 50243a97..020ecde9 100644 --- a/src/ElectronNET.IntegrationTests/Tests/SessionTests.cs +++ b/src/ElectronNET.IntegrationTests/Tests/SessionTests.cs @@ -1,6 +1,7 @@ namespace ElectronNET.IntegrationTests.Tests { using ElectronNET.API.Entities; + using ElectronNET.IntegrationTests.Common; [Collection("ElectronCollection")] public class SessionTests @@ -12,7 +13,7 @@ public SessionTests(ElectronFixture fx) this.fx = fx; } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task Session_preloads_roundtrip() { var session = this.fx.MainWindow.WebContents.Session; @@ -23,7 +24,7 @@ public async Task Session_preloads_roundtrip() preloadsAfter.Should().Contain("/tmp/preload_dummy.js"); } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task Session_proxy_set_and_resolve() { var session = this.fx.MainWindow.WebContents.Session; @@ -34,7 +35,7 @@ public async Task Session_proxy_set_and_resolve() } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task Session_clear_cache_and_storage_completes() { var session = this.fx.MainWindow.WebContents.Session; @@ -46,7 +47,7 @@ public async Task Session_clear_cache_and_storage_completes() ua.Should().NotBeNullOrWhiteSpace(); } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task Session_preloads_set_multiple_and_clear() { var session = this.fx.MainWindow.WebContents.Session; @@ -59,7 +60,7 @@ public async Task Session_preloads_set_multiple_and_clear() empty.Should().NotContain("/tmp/a.js"); } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task Clear_auth_cache_overloads() { var session = this.fx.MainWindow.WebContents.Session; @@ -67,14 +68,14 @@ public async Task Clear_auth_cache_overloads() await session.ClearAuthCacheAsync(new RemovePassword("password") { Origin = "https://example.com", Username = "user", Password = "pw", Realm = "realm", Scheme = Scheme.basic }); } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task Clear_storage_with_options() { var session = this.fx.MainWindow.WebContents.Session; await session.ClearStorageDataAsync(new ClearStorageDataOptions { Storages = new[] { "cookies" }, Quotas = new[] { "temporary" } }); } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task Enable_disable_network_emulation() { var session = this.fx.MainWindow.WebContents.Session; @@ -82,14 +83,14 @@ public async Task Enable_disable_network_emulation() session.DisableNetworkEmulation(); } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task Flush_storage_data_does_not_throw() { var session = this.fx.MainWindow.WebContents.Session; session.FlushStorageData(); } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task Set_user_agent_affects_new_navigation() { var session = this.fx.MainWindow.WebContents.Session; diff --git a/src/ElectronNET.IntegrationTests/Tests/ShellTests.cs b/src/ElectronNET.IntegrationTests/Tests/ShellTests.cs index ff185db1..3a677928 100644 --- a/src/ElectronNET.IntegrationTests/Tests/ShellTests.cs +++ b/src/ElectronNET.IntegrationTests/Tests/ShellTests.cs @@ -1,11 +1,12 @@ namespace ElectronNET.IntegrationTests.Tests { using ElectronNET.API; + using ElectronNET.IntegrationTests.Common; [Collection("ElectronCollection")] public class ShellTests { - [Fact(Skip = "This can keep the test process hanging until the e-mail window is closed")] + [IntegrationFact(Skip = "This can keep the test process hanging until the e-mail window is closed")] public async Task OpenExternal_invalid_scheme_returns_error_or_empty() { var error = await Electron.Shell.OpenExternalAsync("mailto:test@example.com"); diff --git a/src/ElectronNET.IntegrationTests/Tests/ThumbarButtonTests.cs b/src/ElectronNET.IntegrationTests/Tests/ThumbarButtonTests.cs index 15253bd5..021fe1af 100644 --- a/src/ElectronNET.IntegrationTests/Tests/ThumbarButtonTests.cs +++ b/src/ElectronNET.IntegrationTests/Tests/ThumbarButtonTests.cs @@ -2,6 +2,7 @@ namespace ElectronNET.IntegrationTests.Tests { using System.Runtime.Versioning; using ElectronNET.API.Entities; + using ElectronNET.IntegrationTests.Common; [Collection("ElectronCollection")] public class ThumbarButtonTests @@ -13,7 +14,7 @@ public ThumbarButtonTests(ElectronFixture fx) this.fx = fx; } - [SkippableFact(Timeout = 20000)] + [IntegrationFact] [SupportedOSPlatform("Windows")] public async Task SetThumbarButtons_returns_success() { @@ -22,7 +23,7 @@ public async Task SetThumbarButtons_returns_success() success.Should().BeTrue(); } - [SkippableFact(Timeout = 20000)] + [IntegrationFact] [SupportedOSPlatform("Windows")] public async Task Thumbar_button_click_invokes_callback() { diff --git a/src/ElectronNET.IntegrationTests/Tests/TrayTests.cs b/src/ElectronNET.IntegrationTests/Tests/TrayTests.cs index f5b07ff1..f34b8a4e 100644 --- a/src/ElectronNET.IntegrationTests/Tests/TrayTests.cs +++ b/src/ElectronNET.IntegrationTests/Tests/TrayTests.cs @@ -1,6 +1,7 @@ namespace ElectronNET.IntegrationTests.Tests { using ElectronNET.API; + using ElectronNET.IntegrationTests.Common; [Collection("ElectronCollection")] public class TrayTests @@ -13,7 +14,7 @@ public TrayTests(ElectronFixture fx) this.fx = fx; } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task Can_create_tray_and_destroy() { //await Electron.Tray.Show("assets/icon.png"); diff --git a/src/ElectronNET.IntegrationTests/Tests/WebContentsTests.cs b/src/ElectronNET.IntegrationTests/Tests/WebContentsTests.cs index 61ec8a8a..18f96734 100644 --- a/src/ElectronNET.IntegrationTests/Tests/WebContentsTests.cs +++ b/src/ElectronNET.IntegrationTests/Tests/WebContentsTests.cs @@ -5,6 +5,7 @@ namespace ElectronNET.IntegrationTests.Tests using ElectronNET.API; using ElectronNET.API.Entities; using ElectronNET.Common; + using ElectronNET.IntegrationTests.Common; [Collection("ElectronCollection")] public class WebContentsTests @@ -16,7 +17,7 @@ public WebContentsTests(ElectronFixture fx) this.fx = fx; } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task Can_get_url_after_navigation() { var wc = this.fx.MainWindow.WebContents; @@ -25,7 +26,7 @@ public async Task Can_get_url_after_navigation() url.Should().Contain("example.com"); } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task ExecuteJavaScript_returns_title() { var wc = this.fx.MainWindow.WebContents; @@ -34,7 +35,7 @@ public async Task ExecuteJavaScript_returns_title() title.Should().NotBeNull(); } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task DomReady_event_fires() { var wc = this.fx.MainWindow.WebContents; @@ -45,7 +46,7 @@ public async Task DomReady_event_fires() fired.Should().BeTrue(); } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task Can_print_to_pdf() { var html = "data:text/html,

PDF Test

Electron.NET

"; @@ -67,7 +68,7 @@ public async Task Can_print_to_pdf() } } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task Can_basic_print() { var html = "data:text/html,

Print Test

"; @@ -76,15 +77,14 @@ public async Task Can_basic_print() ok.Should().BeTrue(); } - [SkippableFact(Timeout = 20000)] + [IntegrationFact] public async Task GetPrintersAsync_check() { - ////Skip.If(Environment.GetEnvironmentVariable("GITHUB_RUN_ID") != null, "Skipping printer test in CI environment."); var info = await fx.MainWindow.WebContents.GetPrintersAsync(); info.Should().NotBeNull(); } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task GetSetZoomFactor_check() { await fx.MainWindow.WebContents.GetZoomFactorAsync(); @@ -96,15 +96,13 @@ public async Task GetSetZoomFactor_check() ok.Should().Be(2.0); } - [SkippableFact(Timeout = 20000)] + [IntegrationFact] public async Task GetSetZoomLevel_check() { BrowserWindow window = null; try { - ////Skip.If(Environment.GetEnvironmentVariable("GITHUB_RUN_ID") != null && RuntimeInformation.IsOSPlatform(OSPlatform.Windows), "Skipping test on Windows CI."); - window = await Electron.WindowManager.CreateWindowAsync(new BrowserWindowOptions { Show = true }, "about:blank"); await Task.Delay(100.ms()); @@ -127,15 +125,13 @@ public async Task GetSetZoomLevel_check() } } - [SkippableFact(Timeout = 20000)] + [IntegrationFact] public async Task DevTools_check() { BrowserWindow window = null; try { - ////Skip.If(Environment.GetEnvironmentVariable("GITHUB_RUN_ID") != null && RuntimeInformation.IsOSPlatform(OSPlatform.Windows), "Skipping test on Windows CI."); - window = await Electron.WindowManager.CreateWindowAsync(new BrowserWindowOptions { Show = true }, "about:blank"); await Task.Delay(3.seconds()); @@ -156,7 +152,7 @@ public async Task DevTools_check() } } - [Fact(Timeout = 20000)] + [IntegrationFact] public async Task GetSetAudioMuted_check() { fx.MainWindow.WebContents.SetAudioMuted(true); @@ -174,17 +170,13 @@ public async Task GetSetAudioMuted_check() ok.Should().BeFalse(); } - [SkippableFact(Timeout = 20000)] + [IntegrationFact] public async Task GetSetUserAgent_check() { - ////Skip.If(Environment.GetEnvironmentVariable("GITHUB_RUN_ID") != null && RuntimeInformation.IsOSPlatform(OSPlatform.Windows), "Skipping test on Windows CI."); - BrowserWindow window = null; try { - ////Skip.If(Environment.GetEnvironmentVariable("GITHUB_RUN_ID") != null && RuntimeInformation.IsOSPlatform(OSPlatform.Windows), "Skipping test on Windows CI."); - window = await Electron.WindowManager.CreateWindowAsync(new BrowserWindowOptions { Show = true }, "about:blank"); await Task.Delay(3.seconds()); diff --git a/src/ElectronNET.sln.DotSettings b/src/ElectronNET.sln.DotSettings index 1eab21f2..fd98f554 100644 --- a/src/ElectronNET.sln.DotSettings +++ b/src/ElectronNET.sln.DotSettings @@ -1,6 +1,8 @@  + DO_NOT_SHOW False False + CI True True True From 95e02e2655dfb37ff4ce5f213eaaaa9231c73699 Mon Sep 17 00:00:00 2001 From: softworkz Date: Sun, 7 Dec 2025 11:56:32 +0100 Subject: [PATCH 11/11] Introduce common base class for tests --- .../Common/IntegrationTestBase.cs | 23 ++++++ .../Tests/AppTests.cs | 20 ++--- .../Tests/AutoUpdaterTests.cs | 7 +- .../Tests/BrowserViewTests.cs | 9 +-- .../Tests/BrowserWindowTests.cs | 75 +++++++++---------- .../Tests/ClipboardTests.cs | 12 +-- .../Tests/CookiesTests.cs | 13 ++-- .../Tests/GlobalShortcutTests.cs | 6 +- .../Tests/HostHookTests.cs | 6 +- .../Tests/IpcMainTests.cs | 21 +++--- .../Tests/MenuTests.cs | 13 ++-- .../Tests/MultiEventRegistrationTests.cs | 11 +-- .../Tests/NativeImageTests.cs | 16 ++-- .../Tests/NativeThemeTests.cs | 14 ++-- .../Tests/NotificationTests.cs | 6 +- .../Tests/ProcessTests.cs | 6 +- .../Tests/ScreenTests.cs | 10 +-- .../Tests/SessionTests.cs | 25 +++---- .../Tests/ShellTests.cs | 6 +- .../Tests/ThumbarButtonTests.cs | 15 ++-- .../Tests/TrayTests.cs | 8 +- .../Tests/WebContentsTests.cs | 41 +++++----- 22 files changed, 184 insertions(+), 179 deletions(-) create mode 100644 src/ElectronNET.IntegrationTests/Common/IntegrationTestBase.cs diff --git a/src/ElectronNET.IntegrationTests/Common/IntegrationTestBase.cs b/src/ElectronNET.IntegrationTests/Common/IntegrationTestBase.cs new file mode 100644 index 00000000..5a6407fa --- /dev/null +++ b/src/ElectronNET.IntegrationTests/Common/IntegrationTestBase.cs @@ -0,0 +1,23 @@ +namespace ElectronNET.IntegrationTests.Common +{ + using ElectronNET.API; + using ElectronNET.API.Entities; + + // Base class for integration tests providing shared access to MainWindow and OS platform constants + public abstract class IntegrationTestBase + { + protected IntegrationTestBase(ElectronFixture fixture) + { + Fixture = fixture; + MainWindow = fixture.MainWindow; + } + + protected ElectronFixture Fixture { get; } + protected BrowserWindow MainWindow { get; } + + // Constants for SupportedOSPlatform attributes + public const string Windows = "Windows"; + public const string MacOS = "macOS"; + public const string Linux = "Linux"; + } +} diff --git a/src/ElectronNET.IntegrationTests/Tests/AppTests.cs b/src/ElectronNET.IntegrationTests/Tests/AppTests.cs index ed15a12e..6d962f25 100644 --- a/src/ElectronNET.IntegrationTests/Tests/AppTests.cs +++ b/src/ElectronNET.IntegrationTests/Tests/AppTests.cs @@ -9,14 +9,10 @@ namespace ElectronNET.IntegrationTests.Tests using ElectronNET.IntegrationTests.Common; [Collection("ElectronCollection")] - public class AppTests + public class AppTests : IntegrationTestBase { - // ReSharper disable once NotAccessedField.Local - private readonly ElectronFixture fx; - - public AppTests(ElectronFixture fx) + public AppTests(ElectronFixture fx) : base(fx) { - this.fx = fx; } [IntegrationFact] @@ -64,8 +60,8 @@ public async Task Can_get_gpu_feature_status() } [IntegrationFact] - [SupportedOSPlatform("macOS")] - [SupportedOSPlatform("Windows")] + [SupportedOSPlatform(MacOS)] + [SupportedOSPlatform(Windows)] public async Task Can_get_login_item_settings() { var settings = await Electron.App.GetLoginItemSettingsAsync(); @@ -82,8 +78,8 @@ public async Task CommandLine_append_and_query_switch() } [IntegrationFact] - [SupportedOSPlatform("macOS")] - [SupportedOSPlatform("Windows")] + [SupportedOSPlatform(MacOS)] + [SupportedOSPlatform(Windows)] public async Task Accessibility_support_toggle() { Electron.App.SetAccessibilitySupportEnabled(true); @@ -103,8 +99,8 @@ public async Task UserAgentFallback_roundtrip() } [IntegrationFact] - [SupportedOSPlatform("Linux")] - [SupportedOSPlatform("macOS")] + [SupportedOSPlatform(Linux)] + [SupportedOSPlatform(MacOS)] public async Task BadgeCount_set_and_reset_where_supported() { await Electron.App.SetBadgeCountAsync(2); diff --git a/src/ElectronNET.IntegrationTests/Tests/AutoUpdaterTests.cs b/src/ElectronNET.IntegrationTests/Tests/AutoUpdaterTests.cs index 8a6a5315..65279a89 100644 --- a/src/ElectronNET.IntegrationTests/Tests/AutoUpdaterTests.cs +++ b/src/ElectronNET.IntegrationTests/Tests/AutoUpdaterTests.cs @@ -6,13 +6,10 @@ using ElectronNET.IntegrationTests.Common; [Collection("ElectronCollection")] - public class AutoUpdaterTests + public class AutoUpdaterTests : IntegrationTestBase { - private readonly ElectronFixture fx; - - public AutoUpdaterTests(ElectronFixture fx) + public AutoUpdaterTests(ElectronFixture fx) : base(fx) { - this.fx = fx; } [IntegrationFact] diff --git a/src/ElectronNET.IntegrationTests/Tests/BrowserViewTests.cs b/src/ElectronNET.IntegrationTests/Tests/BrowserViewTests.cs index 02f0f726..6fa25c0b 100644 --- a/src/ElectronNET.IntegrationTests/Tests/BrowserViewTests.cs +++ b/src/ElectronNET.IntegrationTests/Tests/BrowserViewTests.cs @@ -5,20 +5,17 @@ namespace ElectronNET.IntegrationTests.Tests using ElectronNET.IntegrationTests.Common; [Collection("ElectronCollection")] - public class BrowserViewTests + public class BrowserViewTests : IntegrationTestBase { - private readonly ElectronFixture fx; - - public BrowserViewTests(ElectronFixture fx) + public BrowserViewTests(ElectronFixture fx) : base(fx) { - this.fx = fx; } [IntegrationFact] public async Task Create_browser_view_and_adjust_bounds() { var view = await Electron.WindowManager.CreateBrowserViewAsync(new BrowserViewConstructorOptions()); - this.fx.MainWindow.SetBrowserView(view); + this.MainWindow.SetBrowserView(view); view.Bounds = new Rectangle { X = 0, Y = 0, Width = 300, Height = 200 }; // Access bounds again (synchronous property fetch) var current = view.Bounds; diff --git a/src/ElectronNET.IntegrationTests/Tests/BrowserWindowTests.cs b/src/ElectronNET.IntegrationTests/Tests/BrowserWindowTests.cs index 091f33e5..c22b8908 100644 --- a/src/ElectronNET.IntegrationTests/Tests/BrowserWindowTests.cs +++ b/src/ElectronNET.IntegrationTests/Tests/BrowserWindowTests.cs @@ -7,31 +7,28 @@ namespace ElectronNET.IntegrationTests.Tests using ElectronNET.IntegrationTests.Common; [Collection("ElectronCollection")] - public class BrowserWindowTests + public class BrowserWindowTests : IntegrationTestBase { - private readonly ElectronFixture fx; - - public BrowserWindowTests(ElectronFixture fx) + public BrowserWindowTests(ElectronFixture fx) : base(fx) { - this.fx = fx; } [IntegrationFact] public async Task Can_set_and_get_title() { const string title = "Integration Test Title"; - this.fx.MainWindow.SetTitle(title); + this.MainWindow.SetTitle(title); await Task.Delay(500.ms()); - var roundTrip = await this.fx.MainWindow.GetTitleAsync(); + var roundTrip = await this.MainWindow.GetTitleAsync(); roundTrip.Should().Be(title); } [IntegrationFact] public async Task Can_resize_and_get_size() { - this.fx.MainWindow.SetSize(643, 482); + this.MainWindow.SetSize(643, 482); await Task.Delay(500.ms()); - var size = await this.fx.MainWindow.GetSizeAsync(); + var size = await this.MainWindow.GetSizeAsync(); size.Should().HaveCount(2); size[0].Should().Be(643); size[1].Should().Be(482); @@ -40,18 +37,18 @@ public async Task Can_resize_and_get_size() [IntegrationFact] public async Task Can_set_progress_bar_and_clear() { - this.fx.MainWindow.SetProgressBar(0.5); + this.MainWindow.SetProgressBar(0.5); // No direct getter; rely on absence of error. Try changing again. - this.fx.MainWindow.SetProgressBar(-1); // clears + this.MainWindow.SetProgressBar(-1); // clears await Task.Delay(50.ms()); } [IntegrationFact(SkipOnWsl = true)] public async Task Can_set_and_get_position() { - this.fx.MainWindow.SetPosition(134, 246); + this.MainWindow.SetPosition(134, 246); await Task.Delay(500.ms()); - var pos = await this.fx.MainWindow.GetPositionAsync(); + var pos = await this.MainWindow.GetPositionAsync(); pos.Should().BeEquivalentTo([134, 246]); } @@ -59,9 +56,9 @@ public async Task Can_set_and_get_position() public async Task Can_set_and_get_bounds() { var bounds = new Rectangle { X = 10, Y = 20, Width = 400, Height = 300 }; - this.fx.MainWindow.SetBounds(bounds); + this.MainWindow.SetBounds(bounds); await Task.Delay(500.ms()); - var round = await this.fx.MainWindow.GetBoundsAsync(); + var round = await this.MainWindow.GetBoundsAsync(); round.Should().BeEquivalentTo(bounds); round.Width.Should().Be(400); @@ -72,9 +69,9 @@ public async Task Can_set_and_get_bounds() public async Task Can_set_and_get_content_bounds() { var bounds = new Rectangle { X = 0, Y = 0, Width = 300, Height = 200 }; - this.fx.MainWindow.SetContentBounds(bounds); + this.MainWindow.SetContentBounds(bounds); await Task.Delay(500.ms()); - var round = await this.fx.MainWindow.GetContentBoundsAsync(); + var round = await this.MainWindow.GetContentBoundsAsync(); round.Width.Should().BeGreaterThan(0); round.Height.Should().BeGreaterThan(0); } @@ -109,28 +106,28 @@ public async Task Show_hide_visibility_roundtrip() [IntegrationFact] public async Task AlwaysOnTop_toggle_and_query() { - this.fx.MainWindow.SetAlwaysOnTop(true); + this.MainWindow.SetAlwaysOnTop(true); await Task.Delay(500.ms()); - (await this.fx.MainWindow.IsAlwaysOnTopAsync()).Should().BeTrue(); - this.fx.MainWindow.SetAlwaysOnTop(false); + (await this.MainWindow.IsAlwaysOnTopAsync()).Should().BeTrue(); + this.MainWindow.SetAlwaysOnTop(false); await Task.Delay(500.ms()); - (await this.fx.MainWindow.IsAlwaysOnTopAsync()).Should().BeFalse(); + (await this.MainWindow.IsAlwaysOnTopAsync()).Should().BeFalse(); } [IntegrationFact] - [SupportedOSPlatform("Linux")] - [SupportedOSPlatform("Windows")] + [SupportedOSPlatform(Linux)] + [SupportedOSPlatform(Windows)] public async Task MenuBar_auto_hide_and_visibility() { - this.fx.MainWindow.SetAutoHideMenuBar(true); + this.MainWindow.SetAutoHideMenuBar(true); await Task.Delay(500.ms()); - (await this.fx.MainWindow.IsMenuBarAutoHideAsync()).Should().BeTrue(); - this.fx.MainWindow.SetMenuBarVisibility(false); + (await this.MainWindow.IsMenuBarAutoHideAsync()).Should().BeTrue(); + this.MainWindow.SetMenuBarVisibility(false); await Task.Delay(500.ms()); - (await this.fx.MainWindow.IsMenuBarVisibleAsync()).Should().BeFalse(); - this.fx.MainWindow.SetMenuBarVisibility(true); + (await this.MainWindow.IsMenuBarVisibleAsync()).Should().BeFalse(); + this.MainWindow.SetMenuBarVisibility(true); await Task.Delay(500.ms()); - (await this.fx.MainWindow.IsMenuBarVisibleAsync()).Should().BeTrue(); + (await this.MainWindow.IsMenuBarVisibleAsync()).Should().BeTrue(); } [IntegrationFact] @@ -196,7 +193,7 @@ public async Task Resize_event_fires_on_size_change() [IntegrationFact] public async Task Progress_bar_and_always_on_top_toggle() { - var win = this.fx.MainWindow; + var win = this.MainWindow; win.SetProgressBar(0.5); await Task.Delay(50.ms()); win.SetProgressBar(0.8, new ProgressBarOptions()); @@ -210,11 +207,11 @@ public async Task Progress_bar_and_always_on_top_toggle() } [IntegrationFact] - [SupportedOSPlatform("Linux")] - [SupportedOSPlatform("Windows")] + [SupportedOSPlatform(Linux)] + [SupportedOSPlatform(Windows)] public async Task Menu_bar_visibility_and_auto_hide() { - var win = this.fx.MainWindow; + var win = this.MainWindow; win.SetAutoHideMenuBar(true); await Task.Delay(500.ms()); (await win.IsMenuBarAutoHideAsync()).Should().BeTrue(); @@ -227,21 +224,21 @@ public async Task Menu_bar_visibility_and_auto_hide() public async Task Parent_child_relationship_roundtrip() { var child = await Electron.WindowManager.CreateWindowAsync(new BrowserWindowOptions { Show = false, Width = 300, Height = 200 }, "about:blank"); - this.fx.MainWindow.SetParentWindow(null); // ensure top-level - child.SetParentWindow(this.fx.MainWindow); + this.MainWindow.SetParentWindow(null); // ensure top-level + child.SetParentWindow(this.MainWindow); await Task.Delay(500.ms()); var parent = await child.GetParentWindowAsync(); - parent.Id.Should().Be(this.fx.MainWindow.Id); - var kids = await this.fx.MainWindow.GetChildWindowsAsync(); + parent.Id.Should().Be(this.MainWindow.Id); + var kids = await this.MainWindow.GetChildWindowsAsync(); kids.Select(k => k.Id).Should().Contain(child.Id); child.Destroy(); } [IntegrationFact] - [SupportedOSPlatform("macOS")] + [SupportedOSPlatform(MacOS)] public async Task Represented_filename_and_edited_flags() { - var win = this.fx.MainWindow; + var win = this.MainWindow; var temp = Path.Combine(Path.GetTempPath(), "electronnet_test.txt"); File.WriteAllText(temp, "test"); win.SetRepresentedFilename(temp); diff --git a/src/ElectronNET.IntegrationTests/Tests/ClipboardTests.cs b/src/ElectronNET.IntegrationTests/Tests/ClipboardTests.cs index 35ec2645..4ef2a991 100644 --- a/src/ElectronNET.IntegrationTests/Tests/ClipboardTests.cs +++ b/src/ElectronNET.IntegrationTests/Tests/ClipboardTests.cs @@ -5,14 +5,10 @@ namespace ElectronNET.IntegrationTests.Tests using ElectronNET.IntegrationTests.Common; [Collection("ElectronCollection")] - public class ClipboardTests + public class ClipboardTests : IntegrationTestBase { - // ReSharper disable once NotAccessedField.Local - private readonly ElectronFixture fx; - - public ClipboardTests(ElectronFixture fx) + public ClipboardTests(ElectronFixture fx) : base(fx) { - this.fx = fx; } [IntegrationFact] @@ -34,8 +30,8 @@ public async Task Available_formats_contains_text_after_write() } [IntegrationFact] - [SupportedOSPlatform("macOS")] - [SupportedOSPlatform("Windows")] + [SupportedOSPlatform(MacOS)] + [SupportedOSPlatform(Windows)] public async Task Bookmark_write_and_read() { var url = "https://electron-test.com"; diff --git a/src/ElectronNET.IntegrationTests/Tests/CookiesTests.cs b/src/ElectronNET.IntegrationTests/Tests/CookiesTests.cs index 9202e298..1bb6cc92 100644 --- a/src/ElectronNET.IntegrationTests/Tests/CookiesTests.cs +++ b/src/ElectronNET.IntegrationTests/Tests/CookiesTests.cs @@ -4,25 +4,22 @@ namespace ElectronNET.IntegrationTests.Tests using ElectronNET.IntegrationTests.Common; [Collection("ElectronCollection")] - public class CookiesTests + public class CookiesTests : IntegrationTestBase { - private readonly ElectronFixture fx; - - public CookiesTests(ElectronFixture fx) + public CookiesTests(ElectronFixture fx) : base(fx) { - this.fx = fx; } [IntegrationFact(Skip = "Cookie set/get requires navigation to domain; skipping until test harness serves page")] public async Task Cookie_set_get_remove_sequence() { - var session = this.fx.MainWindow.WebContents.Session; + var session = this.MainWindow.WebContents.Session; var changed = false; session.Cookies.OnChanged += (cookie, cause, removed) => changed = true; // Navigate to example.com so cookie domain matches - await this.fx.MainWindow.WebContents.LoadURLAsync("https://example.com"); + await this.MainWindow.WebContents.LoadURLAsync("https://example.com"); // Set via renderer for now - await this.fx.MainWindow.WebContents.ExecuteJavaScriptAsync("document.cookie='integration_cookie=1;path=/';"); + await this.MainWindow.WebContents.ExecuteJavaScriptAsync("document.cookie='integration_cookie=1;path=/';"); await Task.Delay(500.ms()); changed.Should().BeTrue(); } diff --git a/src/ElectronNET.IntegrationTests/Tests/GlobalShortcutTests.cs b/src/ElectronNET.IntegrationTests/Tests/GlobalShortcutTests.cs index adfafde2..942e6311 100644 --- a/src/ElectronNET.IntegrationTests/Tests/GlobalShortcutTests.cs +++ b/src/ElectronNET.IntegrationTests/Tests/GlobalShortcutTests.cs @@ -5,8 +5,12 @@ namespace ElectronNET.IntegrationTests.Tests using ElectronNET.IntegrationTests.Common; [Collection("ElectronCollection")] - public class GlobalShortcutTests + public class GlobalShortcutTests : IntegrationTestBase { + public GlobalShortcutTests(ElectronFixture fx) : base(fx) + { + } + [IntegrationFact] public async Task Can_register_and_unregister() { diff --git a/src/ElectronNET.IntegrationTests/Tests/HostHookTests.cs b/src/ElectronNET.IntegrationTests/Tests/HostHookTests.cs index bc4e896a..05b15495 100644 --- a/src/ElectronNET.IntegrationTests/Tests/HostHookTests.cs +++ b/src/ElectronNET.IntegrationTests/Tests/HostHookTests.cs @@ -4,8 +4,12 @@ namespace ElectronNET.IntegrationTests.Tests using ElectronNET.IntegrationTests.Common; [Collection("ElectronCollection")] - public class HostHookTests + public class HostHookTests : IntegrationTestBase { + public HostHookTests(ElectronFixture fx) : base(fx) + { + } + [IntegrationFact(Skip = "Requires HostHook setup; skipping")] public async Task HostHook_call_returns_value() { diff --git a/src/ElectronNET.IntegrationTests/Tests/IpcMainTests.cs b/src/ElectronNET.IntegrationTests/Tests/IpcMainTests.cs index bd894cd6..6136fa0a 100644 --- a/src/ElectronNET.IntegrationTests/Tests/IpcMainTests.cs +++ b/src/ElectronNET.IntegrationTests/Tests/IpcMainTests.cs @@ -5,13 +5,10 @@ namespace ElectronNET.IntegrationTests.Tests using ElectronNET.IntegrationTests.Common; [Collection("ElectronCollection")] - public class IpcMainTests + public class IpcMainTests : IntegrationTestBase { - private readonly ElectronFixture fx; - - public IpcMainTests(ElectronFixture fx) + public IpcMainTests(ElectronFixture fx) : base(fx) { - this.fx = fx; } [IntegrationFact] @@ -26,7 +23,7 @@ await Electron.IpcMain.On("ipc-on-test", obj => tcs.TrySetResult(obj as string); }); - await this.fx.MainWindow.WebContents.ExecuteJavaScriptAsync("require('electron').ipcRenderer.send('ipc-on-test','payload123')"); + await this.MainWindow.WebContents.ExecuteJavaScriptAsync("require('electron').ipcRenderer.send('ipc-on-test','payload123')"); var result = await tcs.Task.WaitAsync(TimeSpan.FromSeconds(5)); @@ -40,7 +37,7 @@ public async Task Ipc_Once_only_fires_once() { var count = 0; Electron.IpcMain.Once("ipc-once-test", _ => count++); - await this.fx.MainWindow.WebContents.ExecuteJavaScriptAsync("const {ipcRenderer}=require('electron'); ipcRenderer.send('ipc-once-test','a'); ipcRenderer.send('ipc-once-test','b');"); + await this.MainWindow.WebContents.ExecuteJavaScriptAsync("const {ipcRenderer}=require('electron'); ipcRenderer.send('ipc-once-test','a'); ipcRenderer.send('ipc-once-test','b');"); await Task.Delay(500.ms()); count.Should().Be(1); } @@ -51,7 +48,7 @@ public async Task Ipc_RemoveAllListeners_stops_receiving() var fired = false; await Electron.IpcMain.On("ipc-remove-test", _ => fired = true); Electron.IpcMain.RemoveAllListeners("ipc-remove-test"); - await this.fx.MainWindow.WebContents.ExecuteJavaScriptAsync("require('electron').ipcRenderer.send('ipc-remove-test','x')"); + await this.MainWindow.WebContents.ExecuteJavaScriptAsync("require('electron').ipcRenderer.send('ipc-remove-test','x')"); await Task.Delay(400.ms()); fired.Should().BeFalse(); } @@ -66,7 +63,7 @@ public async Task Ipc_OnSync_returns_value() received = obj; return "pong"; }); - var ret = await this.fx.MainWindow.WebContents.ExecuteJavaScriptAsync("require('electron').ipcRenderer.sendSync('ipc-sync-test','ping')"); + var ret = await this.MainWindow.WebContents.ExecuteJavaScriptAsync("require('electron').ipcRenderer.sendSync('ipc-sync-test','ping')"); received.Should().BeOfType(); received.Should().Be("ping"); @@ -78,12 +75,12 @@ public async Task Ipc_OnSync_returns_value() public async Task Ipc_Send_from_main_reaches_renderer() { // Listener: store raw arg; if Electron packs differently we will normalize later - await this.fx.MainWindow.WebContents.ExecuteJavaScriptAsync(@"(function(){ const {ipcRenderer}=require('electron'); ipcRenderer.once('main-to-render',(e,arg)=>{ globalThis.__mainToRender = arg;}); return 'ready'; })();"); - Electron.IpcMain.Send(this.fx.MainWindow, "main-to-render", "hello-msg"); + await this.MainWindow.WebContents.ExecuteJavaScriptAsync(@"(function(){ const {ipcRenderer}=require('electron'); ipcRenderer.once('main-to-render',(e,arg)=>{ globalThis.__mainToRender = arg;}); return 'ready'; })();"); + Electron.IpcMain.Send(this.MainWindow, "main-to-render", "hello-msg"); string value = ""; for (int i = 0; i < 20; i++) { - var jsVal = await this.fx.MainWindow.WebContents.ExecuteJavaScriptAsync("globalThis.__mainToRender === undefined ? '' : (typeof globalThis.__mainToRender === 'string' ? globalThis.__mainToRender : JSON.stringify(globalThis.__mainToRender))"); + var jsVal = await this.MainWindow.WebContents.ExecuteJavaScriptAsync("globalThis.__mainToRender === undefined ? '' : (typeof globalThis.__mainToRender === 'string' ? globalThis.__mainToRender : JSON.stringify(globalThis.__mainToRender))"); value = jsVal?.ToString() ?? ""; if (!string.IsNullOrEmpty(value)) { diff --git a/src/ElectronNET.IntegrationTests/Tests/MenuTests.cs b/src/ElectronNET.IntegrationTests/Tests/MenuTests.cs index 314c5cf7..7c9a900d 100644 --- a/src/ElectronNET.IntegrationTests/Tests/MenuTests.cs +++ b/src/ElectronNET.IntegrationTests/Tests/MenuTests.cs @@ -6,13 +6,10 @@ namespace ElectronNET.IntegrationTests.Tests using ElectronNET.IntegrationTests.Common; [Collection("ElectronCollection")] - public class MenuTests + public class MenuTests : IntegrationTestBase { - private readonly ElectronFixture fx; - - public MenuTests(ElectronFixture fx) + public MenuTests(ElectronFixture fx) : base(fx) { - this.fx = fx; } [IntegrationFact] @@ -32,7 +29,7 @@ public async Task ApplicationMenu_click_invokes_handler() }; Electron.Menu.SetApplicationMenu(items); var targetId = items[0].Submenu[0].Id; - await this.fx.MainWindow.WebContents.ExecuteJavaScriptAsync($"require('electron').ipcRenderer.send('integration-click-application-menu','{targetId}')"); + await this.MainWindow.WebContents.ExecuteJavaScriptAsync($"require('electron').ipcRenderer.send('integration-click-application-menu','{targetId}')"); for (int i = 0; i < 20 && !clicked; i++) { await Task.Delay(100.ms()); @@ -44,14 +41,14 @@ public async Task ApplicationMenu_click_invokes_handler() [IntegrationFact] public async Task ContextMenu_popup_registers_items() { - var win = this.fx.MainWindow; + var win = this.MainWindow; var ctxClicked = false; var ctxItems = new[] { new MenuItem { Label = "Ctx", Click = () => ctxClicked = true } }; Electron.Menu.SetContextMenu(win, ctxItems); var ctxId = ctxItems[0].Id; // simulate popup then click Electron.Menu.ContextMenuPopup(win); - await this.fx.MainWindow.WebContents.ExecuteJavaScriptAsync($"require('electron').ipcRenderer.send('integration-click-context-menu',{win.Id},'{ctxId}')"); + await this.MainWindow.WebContents.ExecuteJavaScriptAsync($"require('electron').ipcRenderer.send('integration-click-context-menu',{win.Id},'{ctxId}')"); for (int i = 0; i < 20 && !ctxClicked; i++) { await Task.Delay(100.ms()); diff --git a/src/ElectronNET.IntegrationTests/Tests/MultiEventRegistrationTests.cs b/src/ElectronNET.IntegrationTests/Tests/MultiEventRegistrationTests.cs index f66e13fe..3d4c421b 100644 --- a/src/ElectronNET.IntegrationTests/Tests/MultiEventRegistrationTests.cs +++ b/src/ElectronNET.IntegrationTests/Tests/MultiEventRegistrationTests.cs @@ -3,13 +3,10 @@ namespace ElectronNET.IntegrationTests.Tests using ElectronNET.IntegrationTests.Common; [Collection("ElectronCollection")] - public class MultiEventRegistrationTests + public class MultiEventRegistrationTests : IntegrationTestBase { - private readonly ElectronFixture fx; - - public MultiEventRegistrationTests(ElectronFixture fx) + public MultiEventRegistrationTests(ElectronFixture fx) : base(fx) { - this.fx = fx; } private static async Task WaitAllOrTimeout(TimeSpan timeout, params Task[] tasks) @@ -22,7 +19,7 @@ private static async Task WaitAllOrTimeout(TimeSpan timeout, params Task[] [IntegrationFact] public async Task BrowserWindow_OnResize_multiple_handlers_called() { - var win = this.fx.MainWindow; + var win = this.MainWindow; var h1 = new TaskCompletionSource(); var h2 = new TaskCompletionSource(); var h3 = new TaskCompletionSource(); @@ -46,7 +43,7 @@ public async Task BrowserWindow_OnResize_multiple_handlers_called() [IntegrationFact] public async Task WebContents_OnDomReady_multiple_handlers_called() { - var wc = this.fx.MainWindow.WebContents; + var wc = this.MainWindow.WebContents; var r1 = new TaskCompletionSource(); var r2 = new TaskCompletionSource(); diff --git a/src/ElectronNET.IntegrationTests/Tests/NativeImageTests.cs b/src/ElectronNET.IntegrationTests/Tests/NativeImageTests.cs index 2a31b35a..e7a09b0c 100644 --- a/src/ElectronNET.IntegrationTests/Tests/NativeImageTests.cs +++ b/src/ElectronNET.IntegrationTests/Tests/NativeImageTests.cs @@ -1,15 +1,19 @@ +using System.Drawing; +using ElectronNET.API.Entities; +using ElectronNET.IntegrationTests.Common; using System.Runtime.Versioning; using RectangleEntity = ElectronNET.API.Entities.Rectangle; namespace ElectronNET.IntegrationTests.Tests { - using System.Drawing; - using ElectronNET.API.Entities; - using ElectronNET.IntegrationTests.Common; - - [SupportedOSPlatform("Windows")] - public class NativeImageTests + [Collection("ElectronCollection")] + [SupportedOSPlatform(Windows)] + public class NativeImageTests : IntegrationTestBase { + public NativeImageTests(ElectronFixture fx) : base(fx) + { + } + [IntegrationFact] public async Task Create_from_bitmap_and_to_png() { diff --git a/src/ElectronNET.IntegrationTests/Tests/NativeThemeTests.cs b/src/ElectronNET.IntegrationTests/Tests/NativeThemeTests.cs index 9a11cf56..9e9bb615 100644 --- a/src/ElectronNET.IntegrationTests/Tests/NativeThemeTests.cs +++ b/src/ElectronNET.IntegrationTests/Tests/NativeThemeTests.cs @@ -7,8 +7,12 @@ namespace ElectronNET.IntegrationTests.Tests using ElectronNET.IntegrationTests.Common; [Collection("ElectronCollection")] - public class NativeThemeTests + public class NativeThemeTests : IntegrationTestBase { + public NativeThemeTests(ElectronFixture fx) : base(fx) + { + } + [IntegrationFact] public async Task ThemeSource_roundtrip() { @@ -54,8 +58,8 @@ public async Task Updated_event_fires_on_change() } [IntegrationFact] - [SupportedOSPlatform("macOS")] - [SupportedOSPlatform("Windows")] + [SupportedOSPlatform(MacOS)] + [SupportedOSPlatform(Windows)] public async Task Should_use_high_contrast_colors_check() { var metrics = await Electron.NativeTheme.ShouldUseHighContrastColorsAsync(); @@ -63,8 +67,8 @@ public async Task Should_use_high_contrast_colors_check() } [IntegrationFact] - [SupportedOSPlatform("macOS")] - [SupportedOSPlatform("Windows")] + [SupportedOSPlatform(MacOS)] + [SupportedOSPlatform(Windows)] public async Task Should_use_inverted_colors_check() { var metrics = await Electron.NativeTheme.ShouldUseInvertedColorSchemeAsync(); diff --git a/src/ElectronNET.IntegrationTests/Tests/NotificationTests.cs b/src/ElectronNET.IntegrationTests/Tests/NotificationTests.cs index ee066ef0..9f8772f4 100644 --- a/src/ElectronNET.IntegrationTests/Tests/NotificationTests.cs +++ b/src/ElectronNET.IntegrationTests/Tests/NotificationTests.cs @@ -7,8 +7,12 @@ namespace ElectronNET.IntegrationTests.Tests using ElectronNET.IntegrationTests.Common; [Collection("ElectronCollection")] - public class NotificationTests + public class NotificationTests : IntegrationTestBase { + public NotificationTests(ElectronFixture fx) : base(fx) + { + } + [IntegrationFact] public async Task Notification_create_check() { diff --git a/src/ElectronNET.IntegrationTests/Tests/ProcessTests.cs b/src/ElectronNET.IntegrationTests/Tests/ProcessTests.cs index d6c76b12..f5a9f485 100644 --- a/src/ElectronNET.IntegrationTests/Tests/ProcessTests.cs +++ b/src/ElectronNET.IntegrationTests/Tests/ProcessTests.cs @@ -4,8 +4,12 @@ namespace ElectronNET.IntegrationTests.Tests using ElectronNET.IntegrationTests.Common; [Collection("ElectronCollection")] - public class ProcessTests + public class ProcessTests : IntegrationTestBase { + public ProcessTests(ElectronFixture fx) : base(fx) + { + } + [IntegrationFact] public async Task Process_info_is_accessible() { diff --git a/src/ElectronNET.IntegrationTests/Tests/ScreenTests.cs b/src/ElectronNET.IntegrationTests/Tests/ScreenTests.cs index fa6b4fab..1ccf1f8b 100644 --- a/src/ElectronNET.IntegrationTests/Tests/ScreenTests.cs +++ b/src/ElectronNET.IntegrationTests/Tests/ScreenTests.cs @@ -6,14 +6,10 @@ namespace ElectronNET.IntegrationTests.Tests using ElectronNET.IntegrationTests.Common; [Collection("ElectronCollection")] - public class ScreenTests + public class ScreenTests : IntegrationTestBase { - // ReSharper disable once NotAccessedField.Local - private readonly ElectronFixture fx; - - public ScreenTests(ElectronFixture fx) + public ScreenTests(ElectronFixture fx) : base(fx) { - this.fx = fx; } [IntegrationFact(SkipOnWsl = true)] @@ -40,7 +36,7 @@ public async Task GetCursorScreenPoint_check() } [IntegrationFact] - [SupportedOSPlatform("macOS")] + [SupportedOSPlatform(MacOS)] public async Task GetMenuBarWorkArea_check() { var area = await Electron.Screen.GetMenuBarWorkAreaAsync(); diff --git a/src/ElectronNET.IntegrationTests/Tests/SessionTests.cs b/src/ElectronNET.IntegrationTests/Tests/SessionTests.cs index 020ecde9..53dad7bb 100644 --- a/src/ElectronNET.IntegrationTests/Tests/SessionTests.cs +++ b/src/ElectronNET.IntegrationTests/Tests/SessionTests.cs @@ -4,19 +4,16 @@ namespace ElectronNET.IntegrationTests.Tests using ElectronNET.IntegrationTests.Common; [Collection("ElectronCollection")] - public class SessionTests + public class SessionTests : IntegrationTestBase { - private readonly ElectronFixture fx; - - public SessionTests(ElectronFixture fx) + public SessionTests(ElectronFixture fx) : base(fx) { - this.fx = fx; } [IntegrationFact] public async Task Session_preloads_roundtrip() { - var session = this.fx.MainWindow.WebContents.Session; + var session = this.MainWindow.WebContents.Session; _ = await session.GetPreloadsAsync(); // Use a dummy path; API should store value session.SetPreloads(new[] { "/tmp/preload_dummy.js" }); @@ -27,7 +24,7 @@ public async Task Session_preloads_roundtrip() [IntegrationFact] public async Task Session_proxy_set_and_resolve() { - var session = this.fx.MainWindow.WebContents.Session; + var session = this.MainWindow.WebContents.Session; // Provide all ctor args (pacScript empty to ignore, proxyRules direct, bypass empty) await session.SetProxyAsync(new ProxyConfig("", "direct://", "")); var proxy = await session.ResolveProxyAsync("https://example.com"); @@ -38,7 +35,7 @@ public async Task Session_proxy_set_and_resolve() [IntegrationFact] public async Task Session_clear_cache_and_storage_completes() { - var session = this.fx.MainWindow.WebContents.Session; + var session = this.MainWindow.WebContents.Session; await session.ClearCacheAsync(); await session.ClearStorageDataAsync(); await session.ClearHostResolverCacheAsync(); @@ -50,7 +47,7 @@ public async Task Session_clear_cache_and_storage_completes() [IntegrationFact] public async Task Session_preloads_set_multiple_and_clear() { - var session = this.fx.MainWindow.WebContents.Session; + var session = this.MainWindow.WebContents.Session; session.SetPreloads(new[] { "/tmp/a.js", "/tmp/b.js" }); var after = await session.GetPreloadsAsync(); after.Should().Contain("/tmp/a.js").And.Contain("/tmp/b.js"); @@ -63,7 +60,7 @@ public async Task Session_preloads_set_multiple_and_clear() [IntegrationFact] public async Task Clear_auth_cache_overloads() { - var session = this.fx.MainWindow.WebContents.Session; + var session = this.MainWindow.WebContents.Session; await session.ClearAuthCacheAsync(); await session.ClearAuthCacheAsync(new RemovePassword("password") { Origin = "https://example.com", Username = "user", Password = "pw", Realm = "realm", Scheme = Scheme.basic }); } @@ -71,14 +68,14 @@ public async Task Clear_auth_cache_overloads() [IntegrationFact] public async Task Clear_storage_with_options() { - var session = this.fx.MainWindow.WebContents.Session; + var session = this.MainWindow.WebContents.Session; await session.ClearStorageDataAsync(new ClearStorageDataOptions { Storages = new[] { "cookies" }, Quotas = new[] { "temporary" } }); } [IntegrationFact] public async Task Enable_disable_network_emulation() { - var session = this.fx.MainWindow.WebContents.Session; + var session = this.MainWindow.WebContents.Session; session.EnableNetworkEmulation(new EnableNetworkEmulationOptions { Offline = false, Latency = 10, DownloadThroughput = 50000, UploadThroughput = 20000 }); session.DisableNetworkEmulation(); } @@ -86,14 +83,14 @@ public async Task Enable_disable_network_emulation() [IntegrationFact] public async Task Flush_storage_data_does_not_throw() { - var session = this.fx.MainWindow.WebContents.Session; + var session = this.MainWindow.WebContents.Session; session.FlushStorageData(); } [IntegrationFact] public async Task Set_user_agent_affects_new_navigation() { - var session = this.fx.MainWindow.WebContents.Session; + var session = this.MainWindow.WebContents.Session; // Set UA and verify via session API (navigator.userAgent on existing WebContents may not reflect the override) session.SetUserAgent("IntegrationAgent/1.0"); var ua = await session.GetUserAgent(); diff --git a/src/ElectronNET.IntegrationTests/Tests/ShellTests.cs b/src/ElectronNET.IntegrationTests/Tests/ShellTests.cs index 3a677928..3a71ad8e 100644 --- a/src/ElectronNET.IntegrationTests/Tests/ShellTests.cs +++ b/src/ElectronNET.IntegrationTests/Tests/ShellTests.cs @@ -4,8 +4,12 @@ namespace ElectronNET.IntegrationTests.Tests using ElectronNET.IntegrationTests.Common; [Collection("ElectronCollection")] - public class ShellTests + public class ShellTests : IntegrationTestBase { + public ShellTests(ElectronFixture fx) : base(fx) + { + } + [IntegrationFact(Skip = "This can keep the test process hanging until the e-mail window is closed")] public async Task OpenExternal_invalid_scheme_returns_error_or_empty() { diff --git a/src/ElectronNET.IntegrationTests/Tests/ThumbarButtonTests.cs b/src/ElectronNET.IntegrationTests/Tests/ThumbarButtonTests.cs index 021fe1af..fbbd6981 100644 --- a/src/ElectronNET.IntegrationTests/Tests/ThumbarButtonTests.cs +++ b/src/ElectronNET.IntegrationTests/Tests/ThumbarButtonTests.cs @@ -5,26 +5,23 @@ namespace ElectronNET.IntegrationTests.Tests using ElectronNET.IntegrationTests.Common; [Collection("ElectronCollection")] - public class ThumbarButtonTests + public class ThumbarButtonTests : IntegrationTestBase { - private readonly ElectronFixture fx; - - public ThumbarButtonTests(ElectronFixture fx) + public ThumbarButtonTests(ElectronFixture fx) : base(fx) { - this.fx = fx; } [IntegrationFact] - [SupportedOSPlatform("Windows")] + [SupportedOSPlatform(Windows)] public async Task SetThumbarButtons_returns_success() { var btn = new ThumbarButton("icon.png") { Tooltip = "Test" }; - var success = await this.fx.MainWindow.SetThumbarButtonsAsync(new[] { btn }); + var success = await this.MainWindow.SetThumbarButtonsAsync(new[] { btn }); success.Should().BeTrue(); } [IntegrationFact] - [SupportedOSPlatform("Windows")] + [SupportedOSPlatform(Windows)] public async Task Thumbar_button_click_invokes_callback() { var icon = Path.Combine(Directory.GetCurrentDirectory(), "ElectronNET.WebApp", "wwwroot", "icon.png"); @@ -35,7 +32,7 @@ public async Task Thumbar_button_click_invokes_callback() var tcs = new TaskCompletionSource(); var btn = new ThumbarButton(icon) { Tooltip = "Test", Flags = new[] { ThumbarButtonFlag.enabled }, Click = () => tcs.TrySetResult(true) }; - var ok = await this.fx.MainWindow.SetThumbarButtonsAsync(new[] { btn }); + var ok = await this.MainWindow.SetThumbarButtonsAsync(new[] { btn }); ok.Should().BeTrue(); } } diff --git a/src/ElectronNET.IntegrationTests/Tests/TrayTests.cs b/src/ElectronNET.IntegrationTests/Tests/TrayTests.cs index f34b8a4e..324534e2 100644 --- a/src/ElectronNET.IntegrationTests/Tests/TrayTests.cs +++ b/src/ElectronNET.IntegrationTests/Tests/TrayTests.cs @@ -4,14 +4,10 @@ namespace ElectronNET.IntegrationTests.Tests using ElectronNET.IntegrationTests.Common; [Collection("ElectronCollection")] - public class TrayTests + public class TrayTests : IntegrationTestBase { - // ReSharper disable once NotAccessedField.Local - private readonly ElectronFixture fx; - - public TrayTests(ElectronFixture fx) + public TrayTests(ElectronFixture fx) : base(fx) { - this.fx = fx; } [IntegrationFact] diff --git a/src/ElectronNET.IntegrationTests/Tests/WebContentsTests.cs b/src/ElectronNET.IntegrationTests/Tests/WebContentsTests.cs index 18f96734..7afae016 100644 --- a/src/ElectronNET.IntegrationTests/Tests/WebContentsTests.cs +++ b/src/ElectronNET.IntegrationTests/Tests/WebContentsTests.cs @@ -8,19 +8,16 @@ namespace ElectronNET.IntegrationTests.Tests using ElectronNET.IntegrationTests.Common; [Collection("ElectronCollection")] - public class WebContentsTests + public class WebContentsTests : IntegrationTestBase { - private readonly ElectronFixture fx; - - public WebContentsTests(ElectronFixture fx) + public WebContentsTests(ElectronFixture fx) : base(fx) { - this.fx = fx; } [IntegrationFact] public async Task Can_get_url_after_navigation() { - var wc = this.fx.MainWindow.WebContents; + var wc = this.MainWindow.WebContents; await wc.LoadURLAsync("https://example.com"); var url = await wc.GetUrl(); url.Should().Contain("example.com"); @@ -29,7 +26,7 @@ public async Task Can_get_url_after_navigation() [IntegrationFact] public async Task ExecuteJavaScript_returns_title() { - var wc = this.fx.MainWindow.WebContents; + var wc = this.MainWindow.WebContents; await wc.LoadURLAsync("https://example.com"); var title = await wc.ExecuteJavaScriptAsync("document.title"); title.Should().NotBeNull(); @@ -38,7 +35,7 @@ public async Task ExecuteJavaScript_returns_title() [IntegrationFact] public async Task DomReady_event_fires() { - var wc = this.fx.MainWindow.WebContents; + var wc = this.MainWindow.WebContents; var fired = false; wc.OnDomReady += () => fired = true; await wc.LoadURLAsync("https://example.com"); @@ -50,11 +47,11 @@ public async Task DomReady_event_fires() public async Task Can_print_to_pdf() { var html = "data:text/html,

PDF Test

Electron.NET

"; - await this.fx.MainWindow.WebContents.LoadURLAsync(html); + await this.MainWindow.WebContents.LoadURLAsync(html); var tmp = Path.Combine(Path.GetTempPath(), $"electronnet_pdf_{Guid.NewGuid():N}.pdf"); try { - var ok = await this.fx.MainWindow.WebContents.PrintToPDFAsync(tmp); + var ok = await this.MainWindow.WebContents.PrintToPDFAsync(tmp); ok.Should().BeTrue(); File.Exists(tmp).Should().BeTrue(); new FileInfo(tmp).Length.Should().BeGreaterThan(0); @@ -72,27 +69,27 @@ public async Task Can_print_to_pdf() public async Task Can_basic_print() { var html = "data:text/html,

Print Test

"; - await this.fx.MainWindow.WebContents.LoadURLAsync(html); - var ok = await this.fx.MainWindow.WebContents.PrintAsync(new PrintOptions { Silent = true, PrintBackground = true }); + await this.MainWindow.WebContents.LoadURLAsync(html); + var ok = await this.MainWindow.WebContents.PrintAsync(new PrintOptions { Silent = true, PrintBackground = true }); ok.Should().BeTrue(); } [IntegrationFact] public async Task GetPrintersAsync_check() { - var info = await fx.MainWindow.WebContents.GetPrintersAsync(); + var info = await this.MainWindow.WebContents.GetPrintersAsync(); info.Should().NotBeNull(); } [IntegrationFact] public async Task GetSetZoomFactor_check() { - await fx.MainWindow.WebContents.GetZoomFactorAsync(); - var ok = await fx.MainWindow.WebContents.GetZoomFactorAsync(); + await this.MainWindow.WebContents.GetZoomFactorAsync(); + var ok = await this.MainWindow.WebContents.GetZoomFactorAsync(); ok.Should().BeGreaterThan(0.0); - fx.MainWindow.WebContents.SetZoomFactor(2.0); + this.MainWindow.WebContents.SetZoomFactor(2.0); await Task.Delay(500.ms()); - ok = await fx.MainWindow.WebContents.GetZoomFactorAsync(); + ok = await this.MainWindow.WebContents.GetZoomFactorAsync(); ok.Should().Be(2.0); } @@ -155,18 +152,18 @@ public async Task DevTools_check() [IntegrationFact] public async Task GetSetAudioMuted_check() { - fx.MainWindow.WebContents.SetAudioMuted(true); + this.MainWindow.WebContents.SetAudioMuted(true); await Task.Delay(500.ms()); - var ok = await fx.MainWindow.WebContents.IsAudioMutedAsync(); + var ok = await this.MainWindow.WebContents.IsAudioMutedAsync(); ok.Should().BeTrue(); - fx.MainWindow.WebContents.SetAudioMuted(false); + this.MainWindow.WebContents.SetAudioMuted(false); await Task.Delay(500.ms()); - ok = await fx.MainWindow.WebContents.IsAudioMutedAsync(); + ok = await this.MainWindow.WebContents.IsAudioMutedAsync(); ok.Should().BeFalse(); // Assuming no audio is playing, IsCurrentlyAudibleAsync should return false // there is no way to play audio in this test - ok = await fx.MainWindow.WebContents.IsCurrentlyAudibleAsync(); + ok = await this.MainWindow.WebContents.IsCurrentlyAudibleAsync(); ok.Should().BeFalse(); }