Files
personal-toolbox/PersonalToolBox.Tests/ViewModels/MainViewModelTests.cs
home-PC 92beb46f22 Phase 4: 全局快捷键拦截 (Win32 API)
- 新增 HotKeyManager (Helpers/), 使用 user32.dll RegisterHotKey/UnregisterHotKey 注册系统级快捷键

- 支持 Ctrl/Alt/Shift/Win 修饰键组合, 单字符键和命名键(F1-F12等)解析

- MainWindow.OnSourceInitialized 挂载 HwndSource.AddHook 拦截 WM_HOTKEY 消息

- 启动时自动注册所有含快捷键的工具, 工具增删改后自动重新注册

- 快捷键冲突时记录 Win32 错误码, 无效格式打印警告

- 新增 12 个 HotKeyManager 单元测试 (73 tests total)
2026-05-09 23:07:44 +08:00

191 lines
5.5 KiB
C#

using Moq;
using Microsoft.Extensions.DependencyInjection;
using PersonalToolBox.Helpers;
using PersonalToolBox.Models;
using PersonalToolBox.Services;
using PersonalToolBox.ViewModels;
namespace PersonalToolBox.Tests.ViewModels;
public class MainViewModelTests
{
private readonly Mock<ILogService> _logServiceMock = new();
private readonly Mock<IDataService> _dataServiceMock = new();
private readonly Mock<IProcessExecutionService> _processServiceMock = new();
private readonly Mock<IServiceProvider> _serviceProviderMock = new();
private readonly Mock<HotKeyManager> _hotKeyManagerMock;
private readonly AppConfig _config;
public MainViewModelTests()
{
_config = new AppConfig
{
Theme = "Dark",
Categories =
[
new() { Id = "1", Name = "开发工具" },
new() { Id = "2", Name = "系统工具" }
],
Tools =
[
new() { Id = "t1", Name = "VS Code", CategoryId = "1", IsValid = true },
new() { Id = "t2", Name = "Postman", CategoryId = "1", IsValid = true },
new() { Id = "t3", Name = "任务管理器", CategoryId = "2", IsValid = true },
new() { Id = "t4", Name = "失效工具", CategoryId = "1", ExecutablePath = @"C:\missing.exe", IsValid = false }
]
};
_hotKeyManagerMock = new Mock<HotKeyManager>(_logServiceMock.Object, _processServiceMock.Object);
_dataServiceMock.Setup(d => d.Config).Returns(_config);
}
private MainViewModel CreateViewModel() =>
new(_logServiceMock.Object, _dataServiceMock.Object, _processServiceMock.Object,
_serviceProviderMock.Object, _hotKeyManagerMock.Object);
[Fact]
public void Constructor_LoadsCategories_IncludingAll()
{
var vm = CreateViewModel();
Assert.Equal(3, vm.Categories.Count);
Assert.Equal("全部", vm.Categories[0].Name);
Assert.Equal("开发工具", vm.Categories[1].Name);
Assert.Equal("系统工具", vm.Categories[2].Name);
}
[Fact]
public void Constructor_LoadsAllTools()
{
var vm = CreateViewModel();
Assert.Equal(4, vm.Tools.Count);
}
[Fact]
public void Default_CategoryIsAll_ShowsAllTools()
{
var vm = CreateViewModel();
Assert.Equal(4, vm.FilteredTools.Count);
}
[Fact]
public void SelectCategory_FiltersByCategory()
{
var vm = CreateViewModel();
vm.SelectedCategory = vm.Categories[1];
Assert.Equal(3, vm.FilteredTools.Count);
}
[Fact]
public void SearchText_FiltersByName()
{
var vm = CreateViewModel();
vm.SearchText = "post";
Assert.Single(vm.FilteredTools);
Assert.Equal("Postman", vm.FilteredTools[0].Name);
}
[Fact]
public void SearchText_CaseInsensitive()
{
var vm = CreateViewModel();
vm.SearchText = "POSTMAN";
Assert.Single(vm.FilteredTools);
Assert.Equal("Postman", vm.FilteredTools[0].Name);
}
[Fact]
public void SearchText_And_Category_FilterCombined()
{
var vm = CreateViewModel();
vm.SelectedCategory = vm.Categories[1];
vm.SearchText = "code";
Assert.Single(vm.FilteredTools);
Assert.Equal("VS Code", vm.FilteredTools[0].Name);
}
[Fact]
public void SearchText_Empty_ShowsAllInCategory()
{
var vm = CreateViewModel();
vm.SelectedCategory = vm.Categories[2];
vm.SearchText = "";
Assert.Single(vm.FilteredTools);
Assert.Equal("任务管理器", vm.FilteredTools[0].Name);
}
[Fact]
public void SearchText_NoMatch_ReturnsEmpty()
{
var vm = CreateViewModel();
vm.SearchText = "nonexistent";
Assert.Empty(vm.FilteredTools);
}
[Fact]
public void InvalidTools_StillAppearInFilteredResults()
{
var vm = CreateViewModel();
vm.SelectedCategory = vm.Categories[1];
var invalid = vm.FilteredTools.FirstOrDefault(t => t.Name == "失效工具");
Assert.NotNull(invalid);
Assert.False(invalid.IsValid);
}
[Fact]
public void ClearLogsCommand_CallsLogServiceClear()
{
var vm = CreateViewModel();
vm.ClearLogsCommand.Execute(null);
_logServiceMock.Verify(x => x.Clear(), Times.Once);
}
[Fact]
public void ToggleTheme_SwitchesDarkToLight()
{
var vm = CreateViewModel();
Assert.Equal("Dark", vm.CurrentTheme);
vm.ToggleThemeCommand.Execute(null);
Assert.Equal("Light", vm.CurrentTheme);
}
[Fact]
public void ToggleTheme_CallsSave()
{
var vm = CreateViewModel();
vm.ToggleThemeCommand.Execute(null);
_dataServiceMock.Verify(x => x.Save(), Times.Once);
}
[Fact]
public void ExecuteTool_CallsProcessService()
{
var vm = CreateViewModel();
var tool = vm.Tools[0];
vm.ExecuteToolCommand.Execute(tool);
_processServiceMock.Verify(x => x.Execute(tool), Times.Once);
}
[Fact]
public void ExecuteTool_Null_DoesNotCallProcessService()
{
var vm = CreateViewModel();
vm.ExecuteToolCommand.Execute(null);
_processServiceMock.Verify(x => x.Execute(It.IsAny<ToolItem>()), Times.Never);
}
[Fact]
public void RefreshData_ReloadsFromConfig()
{
var vm = CreateViewModel();
_config.Tools.Add(new ToolItem { Name = "NewTool" });
vm.RefreshData();
Assert.Equal(5, vm.Tools.Count);
}
}