using System; using System.IO; using System.Text.RegularExpressions; using System.Threading.Tasks; using Paca.Core.QuickActions; using Xunit; namespace Paca.Tests.App; public class QuickActionNextGenUxAndAnimationGateTests { private static readonly string SolutionRoot = Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, "..", "..", "..", "..", "..")); private readonly string _xamlPath; private readonly string _csPath; public QuickActionNextGenUxAndAnimationGateTests() { _xamlPath = Path.Combine(SolutionRoot, "src", "Paca.App", "QuickActionLauncherWindow.xaml"); _csPath = Path.Combine(SolutionRoot, "src", "Paca.App", "QuickActionLauncherWindow.xaml.cs"); } [Fact] public void Gate01_QuickActionFuzzyMatcher_MatchesSubsequenceAndTypos() { // Subsequence abbreviation tests Assert.True(QuickActionFuzzyMatcher.IsSubsequenceMatch("sct", "screenshot")); Assert.True(QuickActionFuzzyMatcher.IsSubsequenceMatch("dwn", "download_manager")); Assert.True(QuickActionFuzzyMatcher.IsSubsequenceMatch("clp", "clipboard")); Assert.False(QuickActionFuzzyMatcher.IsSubsequenceMatch("xyz", "screenshot")); // Levenshtein distance tests int dist1 = QuickActionFuzzyMatcher.LevenshteinDistance("screenshot", "screnshot"); // 1 deletion Assert.Equal(1, dist1); int dist2 = QuickActionFuzzyMatcher.LevenshteinDistance("download", "downlaod"); // 2 transposition Assert.True(dist2 <= 2); // Search score evaluation int scoreSub = QuickActionFuzzyMatcher.CalculateFuzzyScore("sct", "screenshot"); Assert.True(scoreSub > 0, "Subsequence should yield positive score"); int scoreTypo = QuickActionFuzzyMatcher.CalculateFuzzyScore("screnshot", "screenshot"); Assert.True(scoreTypo > 0, "Typo with distance 1 should yield positive score"); } [Fact] public void Gate02_QuickActionUsageTracker_RecordsExecutionAndBoostsScore() { string tempFile = Path.Combine(Path.GetTempPath(), $"paca_usage_test_{Guid.NewGuid():N}.json"); try { var tracker = new QuickActionUsageTracker(tempFile); Assert.Equal(0, tracker.GetScoreBoost("browser.new_tab")); // Record execution tracker.RecordExecution("browser.new_tab"); tracker.RecordExecution("browser.new_tab"); tracker.RecordExecution("sys.screenshot_full"); // Verify count & boosts Assert.True(tracker.GetScoreBoost("browser.new_tab") >= 100); Assert.True(tracker.GetScoreBoost("sys.screenshot_full") >= 50); Assert.Equal(0, tracker.GetScoreBoost("media.unrecorded_action")); // Verify persistence Assert.True(File.Exists(tempFile)); var tracker2 = new QuickActionUsageTracker(tempFile); Assert.True(tracker2.GetScoreBoost("browser.new_tab") >= 100); // Recent actions list var recent = tracker2.GetRecentActionIds(5); Assert.Contains("browser.new_tab", recent); Assert.Contains("sys.screenshot_full", recent); } finally { if (File.Exists(tempFile)) File.Delete(tempFile); } } [Fact] public async Task Gate03_QuickActionParameterRouter_RoutesSubCommandsCorrectly() { var router = new QuickActionParameterRouter(); // 1. Open URL Assert.True(router.TryParseCommand("open https://example.com", out var openAction)); Assert.NotNull(openAction); Assert.Contains("https://example.com", openAction.Title); // 2. Base64 encode Assert.True(router.TryParseCommand("b64 hello", out var b64Action)); Assert.NotNull(b64Action); string? copied = null; var ctx = new QuickActionContext(ClipboardSetter: text => copied = text); Assert.NotNull(b64Action.Handler); var result = await b64Action.Handler.Invoke(ctx); Assert.True(result.Success); Assert.Equal("aGVsbG8=", copied); // 3. Base64 decode Assert.True(router.TryParseCommand("b64d aGVsbG8=", out var b64dAction)); Assert.NotNull(b64dAction); Assert.NotNull(b64dAction.Handler); var resultD = await b64dAction.Handler.Invoke(ctx); Assert.True(resultD.Success); Assert.Equal("hello", copied); // 4. SHA-256 hash Assert.True(router.TryParseCommand("hash test", out var hashAction)); Assert.NotNull(hashAction); Assert.True(hashAction.Title.Contains("SHA-256") || hashAction.Description?.Contains("SHA-256") == true); // 5. Normal non-command query should return false Assert.False(router.TryParseCommand("새 탭 열기", out _)); } [Fact] public void Gate04_QuickActionLauncher_ContainsScaleAndFadeAnimationResources() { Assert.True(File.Exists(_xamlPath), $"XAML not found at {_xamlPath}"); string xaml = File.ReadAllText(_xamlPath); // 1. Check window width is 860 for split view Assert.Contains("Width=\"860\"", xaml); // 2. Check ScaleTransform and TranslateTransform are defined in RootCard Assert.Contains("