Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions BrowseRouter/Model/Args.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,15 @@ public static (string, string) SplitPathAndArgs(string s)
return (path, args);
}

// If not quoted, split on first space to separate path from args
int spaceIndex = s.IndexOf(' ');
if (spaceIndex > 0)
{
string path = s[..spaceIndex];
string args = s[(spaceIndex + 1)..];
return (path, args);
}

// The single executable without any other arguments.
return (s, "");
}
Expand Down
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>latest</LangVersion>
<Version>0.17.1</Version>
<Version>0.18.0</Version>
<Product>BrowseRouter</Product>
<Copyright>EnduraByte LLC 2022-2025</Copyright>
<!-- Reduces flagging as malware -->
Expand Down
27 changes: 27 additions & 0 deletions Tests/BrowseRouter.Tests/ArgsTests/GetPathAndArgsMethod.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,31 @@ public void ReturnsInputAsPath_WhenInputContainsInvalidQuotes()
// Assert
result.Should().Be((input, ""));
}

[Fact]
public void SplitsUnquotedPathWithArgs_OnFirstSpace()
{
// Arrange - Issue #94: User wants to pass args with {url} tag to browser
// e.g., "firefox.exe ext+container:name=Work&url={url}"
string input = "firefox.exe ext+container:name=Work&url={url}";

// Act
var result = Args.SplitPathAndArgs(input);

// Assert - Should split on first space, treating rest as args
result.Should().Be(("firefox.exe", "ext+container:name=Work&url={url}"));
}

[Fact]
public void SplitsUnquotedPathWithArgs_PreservesMultipleArgs()
{
// Arrange
string input = "firefox.exe --new-window --url {url}";

// Act
var result = Args.SplitPathAndArgs(input);

// Assert
result.Should().Be(("firefox.exe", "--new-window --url {url}"));
}
}
43 changes: 43 additions & 0 deletions Tests/BrowseRouter.Tests/Services/BrowserServiceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,47 @@ public async Task HandlesUrl(string url)

spy.LastPath.Should().Be("fake-browser.exe");
}

[Fact]
public async Task SubstitutesUrlTagInUnquotedBrowserArgs()
{
// Issue #94: User configures browser with args containing {url} tag
// e.g., for Firefox containers: "firefox.exe ext+container:name=Work&url={url}"
var config = BrowseRouter.Config.Config.Empty with
{
Browsers = new Dictionary<string, string>
{
["ff-work"] = "firefox.exe ext+container:name=Work&url={url}",
},
};
CatchAllConfig.AddTo(config);

var spy = new SpyProcessService();
await new BrowserService(new ConfigService(config), new EmptyNotifyService(), spy)
.LaunchAsync("https://example.com/path", "Fake Window");

spy.LastPath.Should().Be("firefox.exe");
spy.LastArgs.Should().Be("ext+container:name=Work&url=https://example.com/path");
}

[Fact]
public async Task SubstitutesUrlTagInQuotedBrowserArgs()
{
// When browser path is quoted, args with {url} should still work
var config = BrowseRouter.Config.Config.Empty with
{
Browsers = new Dictionary<string, string>
{
["ff-work"] = "\"firefox.exe\" ext+container:name=Work&url={url}",
},
};
CatchAllConfig.AddTo(config);

var spy = new SpyProcessService();
await new BrowserService(new ConfigService(config), new EmptyNotifyService(), spy)
.LaunchAsync("https://example.com/path", "Fake Window");

spy.LastPath.Should().Be("firefox.exe");
spy.LastArgs.Should().Be("ext+container:name=Work&url=https://example.com/path");
}
}