diff --git a/.editorconfig b/.editorconfig
index 938ad15..ea31bbe 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -60,6 +60,10 @@ dotnet_naming_rule.interface_names_must_begin_with_I.symbols = interfaces
dotnet_naming_rule.interface_names_must_begin_with_I.style = pascal_begin_with_I_style
dotnet_naming_rule.interface_names_must_begin_with_I.severity = warning
+dotnet_naming_rule.private_const_fields_none.symbols = private_const_fields
+dotnet_naming_rule.private_const_fields_none.style = camel_begin_with_underscore_style
+dotnet_naming_rule.private_const_fields_none.severity = none
+
dotnet_naming_rule.private_and_protected_fields_must_begin_with_underscore.symbols = private_fields
dotnet_naming_rule.private_and_protected_fields_must_begin_with_underscore.style = camel_begin_with_underscore_style
dotnet_naming_rule.private_and_protected_fields_must_begin_with_underscore.severity = warning
@@ -79,6 +83,10 @@ dotnet_naming_symbols.interfaces.applicable_accessibilities = *
dotnet_naming_symbols.private_fields.applicable_kinds = field
dotnet_naming_symbols.private_fields.applicable_accessibilities = private,protected,protected_internal
+dotnet_naming_symbols.private_const_fields.applicable_kinds = field
+dotnet_naming_symbols.private_const_fields.applicable_accessibilities = private, protected, protected_internal
+dotnet_naming_symbols.private_const_fields.required_modifiers = const
+
dotnet_naming_symbols.public_and_internal_members.applicable_kinds = class,struct,enum,property,method,event,delegate,field
dotnet_naming_symbols.public_and_internal_members.applicable_accessibilities = public,internal
diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml
index be4ae53..07c521f 100644
--- a/.github/workflows/windows.yml
+++ b/.github/workflows/windows.yml
@@ -13,7 +13,7 @@ concurrency:
jobs:
Build:
name: Build on self-hosted
- runs-on: m2mini-win11
+ runs-on: M4700
permissions:
contents: write
defaults:
diff --git a/BrowseRouter/Config/IConfigLoader.cs b/BrowseRouter/Config/Loaders/IConfigLoader.cs
similarity index 61%
rename from BrowseRouter/Config/IConfigLoader.cs
rename to BrowseRouter/Config/Loaders/IConfigLoader.cs
index 82ae51b..4f45877 100644
--- a/BrowseRouter/Config/IConfigLoader.cs
+++ b/BrowseRouter/Config/Loaders/IConfigLoader.cs
@@ -1,4 +1,4 @@
-namespace BrowseRouter.Config;
+namespace BrowseRouter.Config.Loaders;
internal interface IConfigLoader
{
diff --git a/BrowseRouter/Config/IniConfigLoader.cs b/BrowseRouter/Config/Loaders/IniConfigLoader.cs
similarity index 97%
rename from BrowseRouter/Config/IniConfigLoader.cs
rename to BrowseRouter/Config/Loaders/IniConfigLoader.cs
index fc680ef..a39c469 100644
--- a/BrowseRouter/Config/IniConfigLoader.cs
+++ b/BrowseRouter/Config/Loaders/IniConfigLoader.cs
@@ -1,4 +1,4 @@
-namespace BrowseRouter.Config;
+namespace BrowseRouter.Config.Loaders;
internal class IniConfigLoader(string path) : IConfigLoader
{
diff --git a/BrowseRouter/Config/JsonConfigLoader.cs b/BrowseRouter/Config/Loaders/JsonConfigLoader.cs
similarity index 87%
rename from BrowseRouter/Config/JsonConfigLoader.cs
rename to BrowseRouter/Config/Loaders/JsonConfigLoader.cs
index ce07568..80dc996 100644
--- a/BrowseRouter/Config/JsonConfigLoader.cs
+++ b/BrowseRouter/Config/Loaders/JsonConfigLoader.cs
@@ -1,6 +1,6 @@
using System.Text.Json;
-namespace BrowseRouter.Config;
+namespace BrowseRouter.Config.Loaders;
internal class JsonConfigLoader(string path) : IConfigLoader
{
diff --git a/BrowseRouter/Config/ConfigServiceFactory.cs b/BrowseRouter/Infrastructure/ConfigServiceFactory.cs
similarity index 78%
rename from BrowseRouter/Config/ConfigServiceFactory.cs
rename to BrowseRouter/Infrastructure/ConfigServiceFactory.cs
index 4015f5e..09e2363 100644
--- a/BrowseRouter/Config/ConfigServiceFactory.cs
+++ b/BrowseRouter/Infrastructure/ConfigServiceFactory.cs
@@ -1,6 +1,8 @@
-using BrowseRouter.Infrastructure;
+using BrowseRouter.Config;
+using BrowseRouter.Config.Loaders;
+using BrowseRouter.Services;
-namespace BrowseRouter.Config;
+namespace BrowseRouter.Infrastructure;
public static class ConfigServiceFactory
{
diff --git a/BrowseRouter/Interop/Win32/Kernel32.cs b/BrowseRouter/Interop/Win32/Kernel32.cs
index ba0a087..255eb64 100644
--- a/BrowseRouter/Interop/Win32/Kernel32.cs
+++ b/BrowseRouter/Interop/Win32/Kernel32.cs
@@ -12,8 +12,5 @@ public static class Kernel32
[DllImport("kernel32.dll")]
private static extern bool AttachConsole(uint dwProcessId);
- ///
- /// This enables e.g. showing --help in Terminal / cmd text even though this is a WinExe app.
- ///
public static void AttachToParentConsole() => AttachConsole(ATTACH_PARENT_PROCESS);
}
\ No newline at end of file
diff --git a/BrowseRouter/Program.cs b/BrowseRouter/Program.cs
index b483016..fb5c583 100644
--- a/BrowseRouter/Program.cs
+++ b/BrowseRouter/Program.cs
@@ -1,5 +1,4 @@
-using System.Runtime.InteropServices;
-using BrowseRouter.Config;
+using BrowseRouter.Config;
using BrowseRouter.Infrastructure;
using BrowseRouter.Interop.Win32;
using BrowseRouter.Services;
@@ -8,14 +7,14 @@ namespace BrowseRouter;
public static class Program
{
-
private static async Task Main(string[] args)
{
+ // This enables e.g. showing --help in Terminal / cmd text even though this is a WinExe app.
Kernel32.AttachToParentConsole();
if (args.Length == 0)
{
- await new DefaultBrowserService(new NotifyService(false)).RegisterOrUnregisterAsync();
+ await new DefaultBrowserService(new NotifyService()).RegisterOrUnregisterAsync();
return;
}
@@ -28,24 +27,25 @@ private static async Task Main(string[] args)
private static async Task RunAsync(string arg)
{
- Func getIsOption = () => arg.StartsWith('-') || arg.StartsWith('/');
-
- bool isOption = getIsOption();
- while (getIsOption())
+ bool isOption = GetIsOption(arg);
+ while (GetIsOption(arg))
{
arg = arg[1..];
}
if (isOption)
{
- await RunOption(arg);
+ await RunOptionAsync(arg);
return;
}
await LaunchUrlAsyc(arg);
+
}
- private static async Task RunOption(string arg)
+ private static bool GetIsOption(string arg) => arg.StartsWith('-') || arg.StartsWith('/');
+
+ private static async Task RunOptionAsync(string arg)
{
if (string.Equals(arg, "h") || string.Equals(arg, "help"))
{
@@ -55,13 +55,13 @@ private static async Task RunOption(string arg)
if (string.Equals(arg, "r") || string.Equals(arg, "register"))
{
- await new DefaultBrowserService(new NotifyService(false)).RegisterAsync();
+ await new DefaultBrowserService(new NotifyService()).RegisterAsync();
return true;
}
if (string.Equals(arg, "u") || string.Equals(arg, "unregister"))
{
- await new DefaultBrowserService(new NotifyService(false)).UnregisterAsync();
+ await new DefaultBrowserService(new NotifyService()).UnregisterAsync();
return true;
}
@@ -70,22 +70,20 @@ private static async Task RunOption(string arg)
private static async Task LaunchUrlAsyc(string url)
{
- // Get the window title for whichever application is opening the URL.
- ProcessService processService = new();
- if (!processService.TryGetParentProcessTitle(out string windowTitle))
- windowTitle = User32.GetActiveWindowTitle(); //if it didn't work we get the current foreground window name instead
-
- IConfigService configService = await ConfigServiceFactory.CreateAsync();
- Log.Preference = configService.GetLogPreference();
+ IConfigService config = await ConfigServiceFactory.CreateAsync();
+ Log.Preference = config.GetLogPreference();
- NotifyPreference notifyPref = configService.GetNotifyPreference();
+ NotifyPreference notifyPref = config.GetNotifyPreference();
INotifyService notifier = notifyPref.IsEnabled switch
{
true => new NotifyService(notifyPref.IsSilent),
false => new EmptyNotifyService()
};
- await new BrowserService(configService, notifier).LaunchAsync(url, windowTitle);
+ IBrowserService browsers = new BrowserService(config, notifier, new ProcessService());
+ ILaunchService launchService = new LaunchService(browsers, new ProcessService(), new WindowTitleService());
+
+ await launchService.LaunchUrlAsyc(url);
}
private static void ShowHelp()
diff --git a/BrowseRouter/Services/BrowserService.cs b/BrowseRouter/Services/BrowserService.cs
index 2aa9858..9fdcc2a 100644
--- a/BrowseRouter/Services/BrowserService.cs
+++ b/BrowseRouter/Services/BrowserService.cs
@@ -1,11 +1,15 @@
-using System.Diagnostics;
-using BrowseRouter.Config;
+using BrowseRouter.Config;
using BrowseRouter.Infrastructure;
using BrowseRouter.Model;
namespace BrowseRouter.Services;
-public class BrowserService(IConfigService config, INotifyService notifier)
+public interface IBrowserService
+{
+ Task LaunchAsync(string rawUrl, string windowTitle);
+}
+
+public class BrowserService(IConfigService config, INotifyService notifier, IProcessService process) : IBrowserService
{
public async Task LaunchAsync(string rawUrl, string windowTitle)
{
@@ -16,7 +20,7 @@ public async Task LaunchAsync(string rawUrl, string windowTitle)
List filters = await config.GetFiltersAsync();
bool didFilter = FilterPreference.TryApply(filters, rawUrl, out string url);
-
+
if (didFilter)
Log.Write($"Filtered URL: {rawUrl} -> {url}");
@@ -50,16 +54,16 @@ public async Task LaunchAsync(string rawUrl, string windowTitle)
Log.Write($"Launching {path} with args \"{args}\"");
string name = GetAppName(path);
-
+
path = Environment.ExpandEnvironmentVariables(path);
- if (!Actions.TryRun(() => Process.Start(path, args)))
+ if (!Actions.TryRun(() => process.Start(path, args)))
{
await notifier.NotifyAsync($"Error", $"Could not open {name}. Please check the log for more details.");
return;
}
- await notifier.NotifyAsync($"Opening {name}", $"{(didFilter ? "Filtered ":"")}URL: {url}");
+ await notifier.NotifyAsync($"Opening {name}", $"{(didFilter ? "Filtered " : "")}URL: {url}");
}
catch (Exception e)
{
diff --git a/BrowseRouter/Config/ConfigService.cs b/BrowseRouter/Services/ConfigService.cs
similarity index 94%
rename from BrowseRouter/Config/ConfigService.cs
rename to BrowseRouter/Services/ConfigService.cs
index a75dc5d..bf7668b 100644
--- a/BrowseRouter/Config/ConfigService.cs
+++ b/BrowseRouter/Services/ConfigService.cs
@@ -1,10 +1,11 @@
using System.Text.Json;
+using BrowseRouter.Config;
using BrowseRouter.Infrastructure;
using BrowseRouter.Model;
-namespace BrowseRouter.Config;
+namespace BrowseRouter.Services;
-internal class ConfigService(Config config) : IConfigService
+internal class ConfigService(Config.Config config) : IConfigService
{
public NotifyPreference GetNotifyPreference() => new()
{
diff --git a/BrowseRouter/Services/LaunchService.cs b/BrowseRouter/Services/LaunchService.cs
new file mode 100644
index 0000000..dd62ebb
--- /dev/null
+++ b/BrowseRouter/Services/LaunchService.cs
@@ -0,0 +1,23 @@
+namespace BrowseRouter.Services;
+
+public interface ILaunchService
+{
+ Task LaunchUrlAsyc(string url);
+}
+
+public class LaunchService(
+ IBrowserService browsers,
+ IProcessService processes,
+ IWindowTitleService titles
+) : ILaunchService
+{
+ public async Task LaunchUrlAsyc(string url)
+ {
+ // Get the window title for whichever application is opening the URL.
+ if (!processes.TryGetParentProcessTitle(out string windowTitle))
+ // If it didn't work, fallback to the current foreground window name
+ windowTitle = titles.GetWindowTitle();
+
+ await browsers.LaunchAsync(url, windowTitle);
+ }
+}
diff --git a/BrowseRouter/Services/NotifyService.cs b/BrowseRouter/Services/NotifyService.cs
index 3520f5f..7fbbb5e 100644
--- a/BrowseRouter/Services/NotifyService.cs
+++ b/BrowseRouter/Services/NotifyService.cs
@@ -23,13 +23,14 @@ public class NotifyService : INotifyService
private static nint _hIcon;
private static nint _hInstance = Kernel32.GetModuleHandle(App.ExePath);
- private readonly bool _isSilent;
+ private readonly bool _noSound;
- public NotifyService(bool isSilent)
+ public NotifyService(bool noSound = false)
{
- _isSilent = isSilent;
+ _noSound = noSound;
LoadIcon();
}
+
private static bool LoadIcon(int size = 512) =>
Comctl32.LoadIconWithScaleDown(_hInstance, Icon.Application, size, size, out _hIcon) != 0;
@@ -38,7 +39,7 @@ public async Task NotifyAsync(string title, string message)
// Create a dummy window handle
nint hWnd = CreateDummyWindow();
- NotifyIconData nid = GetNid(hWnd, title, message, _isSilent);
+ NotifyIconData nid = GetNid(hWnd, title, message, _noSound);
// Add the icon. This also adds it to the system tray.
Shell32.Shell_NotifyIcon(Shell32.NIM_ADD, ref nid);
@@ -76,7 +77,7 @@ public void Remove(NotifyIconData nid)
Shell32.Shell_NotifyIcon(Shell32.NIM_DELETE, ref nid);
}
- private static NotifyIconData GetNid(nint hWnd, string title, string message, bool isSilent) => new NotifyIconData
+ private static NotifyIconData GetNid(nint hWnd, string title, string message, bool noSound) => new NotifyIconData
{
cbSize = Marshal.SizeOf(typeof(NotifyIconData)),
hWnd = hWnd,
@@ -88,7 +89,7 @@ public void Remove(NotifyIconData nid)
szTip = "BrowseRouter",
szInfo = message,
szInfoTitle = title,
- dwInfoFlags = Shell32.NIIF_USER | Shell32.NIIF_LARGE_ICON | (isSilent ? Shell32.NIIF_NOSOUND : 0x00000000),
+ dwInfoFlags = Shell32.NIIF_USER | Shell32.NIIF_LARGE_ICON | (noSound ? Shell32.NIIF_NOSOUND : 0x00000000),
dwState = 0, // For the popup to be shown, the system tray icon must not be hidden, but we can hide it immediately after
dwStateMask = Shell32.NIS_HIDDEN,
uVersion = Shell32.NOTIFYICON_VERSION_4
diff --git a/BrowseRouter/Services/ProcessService.cs b/BrowseRouter/Services/ProcessService.cs
index 0aa97c6..c66bc5d 100644
--- a/BrowseRouter/Services/ProcessService.cs
+++ b/BrowseRouter/Services/ProcessService.cs
@@ -1,55 +1,57 @@
-using System.ComponentModel;
-using System.Diagnostics;
-using System.Runtime.InteropServices;
+using System.Diagnostics;
using BrowseRouter.Interop.Win32;
-namespace BrowseRouter.Services
+namespace BrowseRouter.Services;
+
+public interface IProcessService
{
+ void Start(string path, string args);
+
+ ///
+ /// Try to get the name of the parent process of the current process.
+ ///
+ /// The name of the parent process main window title (may be empty) and the specific process name.
+ /// True if the name was succesfully found, False otherwise.
+ bool TryGetParentProcessTitle(out string parentProcessTitle);
+}
- public interface IProcessService
+public class ProcessService : IProcessService
+{
+ public void Start(string path, string args)
{
- ///
- /// Try to get the name of the parent process of the current process.
- ///
- /// The name of the parent process main window title (may be empty) and the specific process name.
- /// True if the name was succesfully found, False otherwise.
- bool TryGetParentProcessTitle(out string parentProcessTitle);
+ Process.Start(path, args);
}
- public class ProcessService : IProcessService
+ public bool TryGetParentProcessTitle(out string parentProcessTitle)
{
- public bool TryGetParentProcessTitle(out string parentProcessTitle)
+ Process? parentProcess = GetParentProcess();
+ if (parentProcess is null || parentProcess.MainWindowTitle == string.Empty && parentProcess.ProcessName == string.Empty)
{
- Process? parentProcess = GetParentProcess();
- if (parentProcess is null || parentProcess.MainWindowTitle == string.Empty && parentProcess.ProcessName == string.Empty)
- {
- parentProcessTitle = string.Empty;
- return false;
- }
-
- parentProcessTitle = parentProcess.MainWindowTitle + " -> " + parentProcess.ProcessName;
- return true;
+ parentProcessTitle = string.Empty;
+ return false;
}
- ///
- /// Gets the parent process of the current process.
- ///
- /// An instance of the Process class.
- public static Process? GetParentProcess()
- {
- return BasicProcessInfo.GetParentProcess(Process.GetCurrentProcess().Handle);
- }
+ parentProcessTitle = parentProcess.MainWindowTitle + " -> " + parentProcess.ProcessName;
+ return true;
+ }
- ///
- /// Gets the parent process of specified process.
- ///
- /// The process id.
- /// An instance of the Process class.
- public static Process? GetParentProcess(int id)
- {
- Process process = Process.GetProcessById(id);
- return BasicProcessInfo.GetParentProcess(process.Handle);
- }
+ ///
+ /// Gets the parent process of the current process.
+ ///
+ /// An instance of the Process class.
+ public static Process? GetParentProcess()
+ {
+ return BasicProcessInfo.GetParentProcess(Process.GetCurrentProcess().Handle);
+ }
+ ///
+ /// Gets the parent process of specified process.
+ ///
+ /// The process id.
+ /// An instance of the Process class.
+ public static Process? GetParentProcess(int id)
+ {
+ Process process = Process.GetProcessById(id);
+ return BasicProcessInfo.GetParentProcess(process.Handle);
}
}
diff --git a/BrowseRouter/Services/WindowTitleService.cs b/BrowseRouter/Services/WindowTitleService.cs
new file mode 100644
index 0000000..ec15fc5
--- /dev/null
+++ b/BrowseRouter/Services/WindowTitleService.cs
@@ -0,0 +1,13 @@
+using BrowseRouter.Interop.Win32;
+
+namespace BrowseRouter.Services;
+
+public interface IWindowTitleService
+{
+ string GetWindowTitle();
+}
+
+public class WindowTitleService : IWindowTitleService
+{
+ public string GetWindowTitle() => User32.GetActiveWindowTitle();
+}
diff --git a/BrowseRouter/config.json b/BrowseRouter/config.json
index 528a4de..f47132f 100644
--- a/BrowseRouter/config.json
+++ b/BrowseRouter/config.json
@@ -1,7 +1,8 @@
{
+ // Comments are supported with // syntax
"notify": {
"enabled": true,
- "silent": false
+ "silent": false // Suppress sound
},
"log": {
"enabled": true
diff --git a/BrowseRouter/filters.json b/BrowseRouter/filters.json
index c6b7f72..1ccb46f 100644
--- a/BrowseRouter/filters.json
+++ b/BrowseRouter/filters.json
@@ -1,4 +1,5 @@
[
+ // Comments are supported with // syntax
{
"name": "Remove urchin tracking",
"find": "(.*)[&?]utm_source=[^&]+.*",
@@ -8,7 +9,7 @@
{
"name": "Bypass Teams Safelinks",
"find": ".*teams\\.cdn\\.office\\.net.*url=([^&]+).*",
- "replace": "$1",
+ "replace": "unescape($1)", // urldecode the URL. Cleans up the notification
"priority": 2
},
{
diff --git a/Directory.Build.props b/Directory.Build.props
index e4e24c4..d1e0e08 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -5,7 +5,7 @@
enable
enable
latest
- 0.15.4
+ 0.15.5
BrowseRouter
EnduraByte LLC 2025
diff --git a/Tests/BrowseRouter.Tests/UrlPreferenceExtensionsTests.cs b/Tests/BrowseRouter.Tests/Config/UrlPreferenceExtensionsTests.cs
similarity index 98%
rename from Tests/BrowseRouter.Tests/UrlPreferenceExtensionsTests.cs
rename to Tests/BrowseRouter.Tests/Config/UrlPreferenceExtensionsTests.cs
index ba8f70d..95ec54e 100644
--- a/Tests/BrowseRouter.Tests/UrlPreferenceExtensionsTests.cs
+++ b/Tests/BrowseRouter.Tests/Config/UrlPreferenceExtensionsTests.cs
@@ -1,7 +1,7 @@
using BrowseRouter.Config;
using BrowseRouter.Model;
-namespace BrowserRouter.Tests;
+namespace BrowseRouter.Tests.Config;
public class UrlPreferenceExtensionsTests
{
diff --git a/Tests/BrowseRouter.Tests/Services/BrowserServiceTests.cs b/Tests/BrowseRouter.Tests/Services/BrowserServiceTests.cs
new file mode 100644
index 0000000..e650e44
--- /dev/null
+++ b/Tests/BrowseRouter.Tests/Services/BrowserServiceTests.cs
@@ -0,0 +1,37 @@
+using BrowseRouter.Config;
+using BrowseRouter.Services;
+
+namespace BrowseRouter.Tests.Services;
+
+public class BrowserServiceTests : IAsyncLifetime
+{
+ private BrowseRouter.Config.Config _config = null!; // Set in InitializeAsync
+
+ public Task InitializeAsync()
+ {
+ _config = BrowseRouter.Config.Config.Empty with
+ {
+ Browsers = new Dictionary
+ {
+ ["fake"] = "fake-browser.exe",
+ },
+ };
+
+ CatchAllConfig.AddTo(_config);
+ return Task.CompletedTask;
+ }
+
+ public Task DisposeAsync() => Task.CompletedTask;
+
+ [Theory]
+ [InlineData("https://example.com")]
+ [InlineData("https://statics.teams.cdn.office.net/evergreen-assets/safelinks/1/atp-safelinks.html?url=https%3A%2F%2Fwww.example.org%2Fpath%2F")]
+ public async Task HandlesUrl(string url)
+ {
+ var spy = new SpyProcessService();
+ await new BrowserService(new ConfigService(_config), new EmptyNotifyService(), spy)
+ .LaunchAsync(url, "Fake Window");
+
+ spy.LastPath.Should().Be("fake-browser.exe");
+ }
+}
\ No newline at end of file
diff --git a/Tests/BrowseRouter.Tests/ConfigServiceTests.cs b/Tests/BrowseRouter.Tests/Services/ConfigServiceTests.cs
similarity index 88%
rename from Tests/BrowseRouter.Tests/ConfigServiceTests.cs
rename to Tests/BrowseRouter.Tests/Services/ConfigServiceTests.cs
index b8a6765..5e7da0b 100644
--- a/Tests/BrowseRouter.Tests/ConfigServiceTests.cs
+++ b/Tests/BrowseRouter.Tests/Services/ConfigServiceTests.cs
@@ -1,13 +1,14 @@
using BrowseRouter.Config;
+using BrowseRouter.Services;
-namespace BrowserRouter.Tests;
+namespace BrowseRouter.Tests.Services;
public class ConfigServiceTests
{
[Fact]
public void GetUrlPreferences_NoBrowsers_ReturnsEmpty()
{
- var config = Config.Empty with
+ var config = BrowseRouter.Config.Config.Empty with
{
Browsers = []
};
@@ -20,7 +21,7 @@ public void GetUrlPreferences_NoBrowsers_ReturnsEmpty()
[Fact]
public void GetUrlPreferences_LoadsUrls()
{
- var config = Config.Empty with
+ var config = BrowseRouter.Config.Config.Empty with
{
Browsers = new Dictionary
{
@@ -54,7 +55,7 @@ public void GetUrlPreferences_LoadsUrls()
[Fact]
public void GetUrlPreferences_ConfigTypeUrls_AppendsCatchAll()
{
- var config = Config.Empty with
+ var config = BrowseRouter.Config.Config.Empty with
{
Browsers = new Dictionary
{
diff --git a/Tests/BrowseRouter.Tests/TestDoubles/SpyProcessService.cs b/Tests/BrowseRouter.Tests/TestDoubles/SpyProcessService.cs
new file mode 100644
index 0000000..f002bab
--- /dev/null
+++ b/Tests/BrowseRouter.Tests/TestDoubles/SpyProcessService.cs
@@ -0,0 +1,21 @@
+using BrowseRouter.Services;
+
+namespace BrowseRouter.Tests.TestDoubles;
+
+internal class SpyProcessService : IProcessService
+{
+ public string? LastPath { get; private set; }
+ public string? LastArgs { get; private set; }
+
+ public void Start(string path, string args)
+ {
+ LastPath = path;
+ LastArgs = args;
+ }
+
+ public bool TryGetParentProcessTitle(out string parentProcessTitle)
+ {
+ parentProcessTitle = string.Empty;
+ return false;
+ }
+}
diff --git a/Tests/BrowseRouter.Tests/Usings.cs b/Tests/BrowseRouter.Tests/Usings.cs
index 04106bd..b36ecd1 100644
--- a/Tests/BrowseRouter.Tests/Usings.cs
+++ b/Tests/BrowseRouter.Tests/Usings.cs
@@ -1,3 +1,3 @@
-global using BrowseRouter;
+global using BrowseRouter.Tests.TestDoubles;
global using Xunit;
global using FluentAssertions;