fix: PathPicker's File Filtering Function malfunctions (#786)

* fix: PathPicker's File Filtering Function malfunctions

Regular expression validation for adding parameters; the sample reference content is as follows:

[Name,Pattern] → True
[123,.exe,.pdb] → True
[All] → True
[ImageAll] → True
[11,*.txt] → True
*.123 → False
*.txt → False
.png → False
[*.txt] → False
[.txt] → False
[a] → True
[a,b] → True
[a,b,c] → True
[a,*] → True
[a,.] → True
[*] → False
[.] → False
[a,] → False
[,a] → False
[] → False
[a,b,] → False
[a,,b] → False
[a*b] → False
[a.b] → False

* Add PathPicker regex validation and headless tests

Introduces regex-based validation for the PathPicker FileFilter property, supporting both NETSTANDARD2_0 and NET8_0 with conditional compilation. Adds headless UI tests and supporting test views to verify correct and incorrect filter patterns.

* Refactor PathPicker tests and update accessibility

Moved PathPicker unit tests from HeadlessTest.Ursa to Test.Ursa, removing UI-dependent test files and replacing them with direct unit tests for ParseFileTypes. Updated PathPicker.cs to make ParseFileTypes internal for improved testability and adjusted regex method accessibility. Updated project file to remove references to deleted test files.

* Update HeadlessTest.Ursa.csproj

* Update src/Ursa/Controls/PathPicker/PathPicker.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Juster Zhu
2025-10-21 23:03:11 +08:00
committed by GitHub
parent 923e502288
commit c6ac019a4e
3 changed files with 74 additions and 5 deletions

View File

@@ -1,4 +1,5 @@
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Input;
using Avalonia;
using Avalonia.Controls;
@@ -15,9 +16,21 @@ namespace Ursa.Controls;
[TemplatePart(Name = PART_Button, Type = typeof(Button))]
[PseudoClasses(PseudoClassName.PC_Empty)]
public class PathPicker : TemplatedControl
public partial class PathPicker : TemplatedControl
{
private const string PatternsRegexString = @"^\[(?:[^*.,]+|([^*.,]+(,[^,]+)+))\]$";
#if NETSTANDARD2_0
private static readonly Regex _patternsRegex = new (PatternsRegexString);
#endif
#if NET8_0
[GeneratedRegex(PatternsRegexString)]
private static partial Regex GetPatternsRegex();
#endif
public const string PART_Button = "PART_Button";
public static readonly StyledProperty<string> SuggestedStartPathProperty =
AvaloniaProperty.Register<PathPicker, string>(
nameof(SuggestedStartPath), string.Empty);
@@ -181,7 +194,6 @@ public class PathPicker : TemplatedControl
Button.ClickEvent.AddHandler(LaunchPicker, _button);
}
private static string RemoveNewLine(string str)
{
return str.Replace("\r", "")
@@ -220,9 +232,20 @@ public class PathPicker : TemplatedControl
/**
* [ParseFilePickerTypeStr][ParseFilePickerTypeStr]...
*/
private static IReadOnlyList<FilePickerFileType>? ParseFileTypes(string str)
internal static IReadOnlyList<FilePickerFileType>? ParseFileTypes(string str)
{
if (string.IsNullOrWhiteSpace(str)) return null;
if (string.IsNullOrWhiteSpace(str))
return null;
#if NETSTANDARD2_0
if (!_patternsRegex.IsMatch(str))
#endif
#if NET8_0
if (!GetPatternsRegex().IsMatch(str))
#endif
throw new ArgumentException($"{nameof(str)} Invalid parameter, please refer to the following content: [Name, Pattern], such as [123, .exe, .pdb] or [All][ImageAll][11, *.txt]");
string[] separatedStrings = ["[", "][", "]"];
var list = RemoveNewLine(str)
.Replace(" ", string.Empty)

View File

@@ -28,5 +28,4 @@
<ProjectReference Include="..\..\src\Ursa\Ursa.csproj" />
<ProjectReference Include="..\..\src\Ursa.Themes.Semi\Ursa.Themes.Semi.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,47 @@
using Ursa.Controls;
namespace Test.Ursa.PathPickerTests;
public class PathPickerTests
{
[Theory]
[InlineData(".123")]
[InlineData(".txt")]
[InlineData(".png")]
[InlineData("[.txt]")]
[InlineData("[]")]
[InlineData("[.]")]
[InlineData("[a,]")]
[InlineData("[,a]")]
[InlineData("[a,b,]")]
[InlineData("[a,,b]")]
[InlineData("[a*b]")]
[InlineData("[a.b]")]
public void ParseFileTypes_InvalidInputs_ThrowsArgumentException(string input)
{
var exception = Assert.Throws<ArgumentException>(() => PathPicker.ParseFileTypes(input));
Assert.Contains("Invalid parameter, please refer to the following content", exception.Message);
}
[Theory]
[InlineData("[Name,Pattern]")]
[InlineData("[123,.exe,.pdb]")]
[InlineData("[All]")]
[InlineData("[ImageAll]")]
[InlineData("[11,.txt]")]
[InlineData("[a]")]
[InlineData("[a,b]")]
[InlineData("[a,b,c]")]
[InlineData("[a,.]")]
public void ParseFileTypes_ValidInputs_DoesNotThrow(string input)
{
try
{
PathPicker.ParseFileTypes(input);
}
catch (Exception ex)
{
Assert.Fail($"It is expected not to throw an exception, but actually throws one :{ex.Message}");
}
}
}