releases/tests/Paca.Tests/Browser/CommandPaletteTests.cs

128 lines
4.7 KiB
C#

using Paca.App.Browser.Features;
namespace Paca.Tests.Browser;
// ── T2-1 명령 팔레트: 등록·검색·실행 ──
public class CommandPaletteTests
{
private sealed class CommandHost
{
public List<string> Executed = new();
public List<string> Tabs = new() { "https://a.com", "https://b.com" };
public List<string> History = new() { "https://news.com" };
public CommandPalette Build()
{
var palette = new CommandPalette();
palette.Register("새 탭", "tab new open", () => Executed.Add("newtab"));
palette.Register("탭 닫기", "close tab", () => Executed.Add("close"));
palette.Register("전체화면", "fullscreen f11", () => Executed.Add("fs"));
palette.Register("리더 모드", "reader reading", () => Executed.Add("reader"));
palette.Register("다크 모드", "dark theme boost", () => Executed.Add("dark"));
palette.RegisterSeparator();
// 동적 소스: 탭/히스토리 → 명령으로
palette.RegisterDynamic(() => Tabs.Select(t => new PaletteCommand($"탭: {t}", "tab go", () => Executed.Add("go:" + t))).ToList());
palette.RegisterDynamic(() => History.Select(h => new PaletteCommand($"기록: {h}", "history", () => Executed.Add("hist:" + h))).ToList());
return palette;
}
}
[Fact]
public void 등록된_명령을_검색한다_라벨_키워드_부분일치()
{
var p = new CommandHost().Build();
var hits = p.Search("리더");
Assert.Single(hits);
Assert.Equal("리더 모드", hits[0].Title);
hits = p.Search("F11"); // 키워드 검색
Assert.Single(hits);
hits = p.Search("TAB"); // 대소문자 무시
Assert.Contains(hits, h => h.Title == "새 탭");
}
[Fact]
public void 동적_소스_탭과_기록도_결과에_포함된다()
{
var p = new CommandHost().Build();
var hits = p.Search("탭:");
Assert.Equal(2, hits.Count);
hits = p.Search("기록:");
Assert.Single(hits);
}
[Fact]
public void 실행은_콜백을_호출한다()
{
var host = new CommandHost();
var p = host.Build();
p.Execute(p.Search("전체").Single());
Assert.Equal(new[] { "fs" }, host.Executed);
}
[Fact]
public void 빈_질의는_정적_명령_전체를_순서대로_반환()
{
var p = new CommandHost().Build();
var hits = p.Search("");
Assert.Equal("새 탭", hits[0].Title);
Assert.Equal("다크 모드", hits[^1].Title); // 동적 제외, 정적만
}
[Fact]
public void 매치_순위는_시작_일치_우선()
{
var p = new CommandPalette();
p.Register("탭 닫기", "close", () => { });
p.Register("닫힌 탭 복원", "reopen closed", () => { });
var hits = p.Search("탭");
Assert.Equal("탭 닫기", hits[0].Title); // "탭"으로 시작하는 것이 우선
}
}
// ── T2-7 Boost: 사이트별 강제 다크/커스텀 CSS 영속 ──
public class BoostStoreTests : IDisposable
{
private readonly string _path = Path.Combine(Path.GetTempPath(), $"boost_{Guid.NewGuid():N}.json");
[Fact]
public void 기본은_빈_부스트()
{
var s = new BoostStore(_path);
Assert.False(s.Get("example.com").ForceDark);
Assert.Equal("", s.Get("example.com").CustomCss);
}
[Fact]
public void 강제다크와_CSS를_저장하면_영속된다()
{
var s = new BoostStore(_path);
s.Set("example.com", forceDark: true, css: "body{zoom:1.1}");
var re = new BoostStore(_path);
Assert.True(re.Get("example.com").ForceDark);
Assert.Equal("body{zoom:1.1}", re.Get("example.com").CustomCss);
}
[Fact]
public void www_정규화_및_빈_부스트는_CSS를_만들지_않는다()
{
var s = new BoostStore(_path);
s.Set("www.example.com", true, "");
Assert.True(s.Get("example.com").ForceDark); // www 정규화
var js = BoostCssBuilder.BuildScript("plain.org", s.Get("plain.org"));
Assert.Equal("", js); // 미설정 사이트 → 빈 스크립트
}
[Fact]
public void 강제다크_스크립트는_반전필터를_포함하고_이미지_비디오를_보호한다()
{
var js = BoostCssBuilder.BuildScript("d.com", new BoostSettings(ForceDark: true, "h1{color:red}"));
Assert.Contains("invert(1)", js);
Assert.Contains("img", js); // 미디어 보호 셀렉터
Assert.Contains("video", js);
Assert.Contains("h1{color:red}", js);
}
public void Dispose() { try { File.Delete(_path); } catch { } }
}