diff --git a/src-tauri/src/notifications.rs b/src-tauri/src/notifications.rs index 5b12d89..590fc35 100644 --- a/src-tauri/src/notifications.rs +++ b/src-tauri/src/notifications.rs @@ -38,6 +38,46 @@ fn format_simple_notification(title: &str, body: &str) -> String { format!("{}\n\n{}", title, body) } +/// Build notify-send command for testing (doesn't execute) +#[cfg(test)] +fn build_notify_send_command(title: &str, body: &str) -> (String, Vec) { + ( + "notify-send".to_string(), + vec![ + title.to_string(), + body.to_string(), + "--urgency=normal".to_string(), + "--app-name=Hikari Desktop".to_string(), + ], + ) +} + +/// Build Windows PowerShell command for testing (doesn't execute) +#[cfg(test)] +fn build_windows_powershell_command(title: &str, body: &str) -> (String, Vec) { + let script = generate_powershell_toast_script(title, body); + ( + "pwsh.exe".to_string(), + vec![ + "-NoProfile".to_string(), + "-WindowStyle".to_string(), + "Hidden".to_string(), + "-Command".to_string(), + script, + ], + ) +} + +/// Build simple notification command for testing (doesn't execute) +#[cfg(test)] +fn build_simple_notification_command(title: &str, body: &str) -> (String, Vec) { + let message = format_simple_notification(title, body); + ( + "cmd.exe".to_string(), + vec!["/c".to_string(), "msg".to_string(), "*".to_string(), message], + ) +} + #[command] pub async fn send_notify_send(title: String, body: String) -> Result<(), String> { // Use notify-send for Linux/WSL @@ -210,4 +250,141 @@ mod tests { assert!(script.contains("`\"Quoted`\" `\"Multiple`\" `\"Times`\"")); assert!(script.contains("`\"More`\" `\"Quotes`\" `\"Here`\"")); } + + // E2E Integration Tests - Command Structure Verification + + #[test] + fn test_e2e_notify_send_command_structure() { + let (command, args) = build_notify_send_command("Test Title", "Test Body"); + + assert_eq!(command, "notify-send"); + assert_eq!(args.len(), 4); + assert_eq!(args[0], "Test Title"); + assert_eq!(args[1], "Test Body"); + assert_eq!(args[2], "--urgency=normal"); + assert_eq!(args[3], "--app-name=Hikari Desktop"); + } + + #[test] + fn test_e2e_notify_send_with_special_chars() { + let (command, args) = + build_notify_send_command("Title with \"quotes\"", "Body\nwith\nnewlines"); + + assert_eq!(command, "notify-send"); + assert_eq!(args[0], "Title with \"quotes\""); + assert_eq!(args[1], "Body\nwith\nnewlines"); + // notify-send handles these directly + } + + #[test] + fn test_e2e_windows_powershell_command_structure() { + let (command, args) = build_windows_powershell_command("Test Title", "Test Body"); + + assert_eq!(command, "pwsh.exe"); + assert_eq!(args.len(), 5); + assert_eq!(args[0], "-NoProfile"); + assert_eq!(args[1], "-WindowStyle"); + assert_eq!(args[2], "Hidden"); + assert_eq!(args[3], "-Command"); + + // Verify the script in args[4] contains expected elements + let script = &args[4]; + assert!(script.contains("Test Title")); + assert!(script.contains("Test Body")); + assert!(script.contains("Hikari Desktop")); + assert!(script.contains("ToastNotification")); + } + + #[test] + fn test_e2e_windows_powershell_quote_escaping() { + let (_, args) = + build_windows_powershell_command("Title with \"quotes\"", "Body with \"quotes\""); + + let script = &args[4]; + // Verify quotes are properly escaped in the PowerShell script + assert!(script.contains("Title with `\"quotes`\"")); + assert!(script.contains("Body with `\"quotes`\"")); + } + + #[test] + fn test_e2e_simple_notification_command_structure() { + let (command, args) = build_simple_notification_command("Test Title", "Test Body"); + + assert_eq!(command, "cmd.exe"); + assert_eq!(args.len(), 4); + assert_eq!(args[0], "/c"); + assert_eq!(args[1], "msg"); + assert_eq!(args[2], "*"); + assert_eq!(args[3], "Test Title\n\nTest Body"); + } + + #[test] + fn test_e2e_simple_notification_multiline() { + let (_, args) = + build_simple_notification_command("Multi\nLine\nTitle", "Multi\nLine\nBody"); + + let message = &args[3]; + assert!(message.contains("Multi\nLine\nTitle")); + assert!(message.contains("\n\n")); + assert!(message.contains("Multi\nLine\nBody")); + } + + #[test] + fn test_e2e_command_consistency_across_platforms() { + // Test that different platforms use consistent parameters + let title = "Consistency Test"; + let body = "Testing cross-platform consistency"; + + // Linux command + let (notify_cmd, notify_args) = build_notify_send_command(title, body); + assert!(notify_cmd.contains("notify")); + assert!(notify_args.iter().any(|arg| arg.contains("Hikari Desktop"))); + + // Windows PowerShell command + let (ps_cmd, ps_args) = build_windows_powershell_command(title, body); + assert!(ps_cmd.contains("pwsh") || ps_cmd.contains("powershell")); + let ps_script = &ps_args[4]; + assert!(ps_script.contains("Hikari Desktop")); + + // Windows simple command + let (msg_cmd, msg_args) = build_simple_notification_command(title, body); + assert!(msg_cmd.contains("cmd")); + assert!(msg_args[3].contains(title)); + assert!(msg_args[3].contains(body)); + } + + #[test] + fn test_e2e_unicode_support_across_platforms() { + let title = "日本語 Title"; + let body = "Unicode: 🎉"; + + // Verify all platforms preserve unicode + let (_, notify_args) = build_notify_send_command(title, body); + assert_eq!(notify_args[0], title); + assert_eq!(notify_args[1], body); + + let (_, ps_args) = build_windows_powershell_command(title, body); + let ps_script = &ps_args[4]; + assert!(ps_script.contains(title)); + assert!(ps_script.contains(body)); + + let (_, msg_args) = build_simple_notification_command(title, body); + assert!(msg_args[3].contains(title)); + assert!(msg_args[3].contains(body)); + } + + #[test] + fn test_e2e_empty_input_handling() { + // Test that empty inputs are handled gracefully + let (_, notify_args) = build_notify_send_command("", ""); + assert_eq!(notify_args[0], ""); + assert_eq!(notify_args[1], ""); + + let (_, ps_args) = build_windows_powershell_command("", ""); + let ps_script = &ps_args[4]; + assert!(ps_script.contains("Hikari Desktop")); // Still has app name + + let (_, msg_args) = build_simple_notification_command("", ""); + assert_eq!(msg_args[3], "\n\n"); + } } diff --git a/src/lib/components/McpManagementPanel.svelte b/src/lib/components/McpManagementPanel.svelte index aa798b1..81f4970 100644 --- a/src/lib/components/McpManagementPanel.svelte +++ b/src/lib/components/McpManagementPanel.svelte @@ -12,7 +12,7 @@ command: string | null; url: string | null; transport: string; // "stdio", "http", or "sse" - env: any | null; + env: Record | null; status: string | null; // "Connected" or "Failed to connect" } @@ -402,8 +402,8 @@
- {#if $isLoading} + {#if isLoading}
Loading sessions...
- {:else if $sessions.length === 0} + {:else if sessions.length === 0}
{:else}
- {#each $sessions as session (session.id)} + {#each sessions as session (session.id)}
{#if showClearAllConfirm} -
(showClearAllConfirm = false)} @@ -459,7 +471,6 @@ tabindex="0" onkeydown={(e) => e.key === "Escape" && (showClearAllConfirm = false)} > -
e.stopPropagation()} diff --git a/src/lib/components/TodoPanel.svelte b/src/lib/components/TodoPanel.svelte index 6245f31..7125de7 100644 --- a/src/lib/components/TodoPanel.svelte +++ b/src/lib/components/TodoPanel.svelte @@ -1,5 +1,5 @@