diff --git a/packages/hoppscotch-selfhost-desktop/src-tauri/Cargo.lock b/packages/hoppscotch-selfhost-desktop/src-tauri/Cargo.lock index d593aca11..d1d767f1f 100644 --- a/packages/hoppscotch-selfhost-desktop/src-tauri/Cargo.lock +++ b/packages/hoppscotch-selfhost-desktop/src-tauri/Cargo.lock @@ -56,6 +56,55 @@ dependencies = [ "libc", ] +[[package]] +name = "anstream" +version = "0.6.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" + +[[package]] +name = "anstyle-parse" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] + [[package]] name = "anyhow" version = "1.0.86" @@ -163,9 +212,9 @@ dependencies = [ [[package]] name = "brotli" -version = "3.5.0" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d640d25bc63c50fb1f0b545ffd80207d2e10a4c965530809b40ba3386825c391" +checksum = "74f7971dbd9326d58187408ab83117d8ac1bb9c17b085fdacd1cf2f598719b6b" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -174,9 +223,9 @@ dependencies = [ [[package]] name = "brotli-decompressor" -version = "2.5.1" +version = "4.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e2e4afe60d7dd600fdd3de8d0f08c2b7ec039712e3b6137ff98b7004e82de4f" +checksum = "9a45bd2e4095a8b518033b128020dd4a55aab1c0a381ba4404a472630f4bc362" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -366,6 +415,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" +[[package]] +name = "colorchoice" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" + [[package]] name = "combine" version = "4.6.7" @@ -713,6 +768,29 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "env_filter" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "humantime", + "log", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -776,6 +854,15 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "fluent-uri" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17c704e9dbe1ddd863da1e6ff3567795087b1eb201ce80d8fa81162e1516500d" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "fnv" version = "1.0.7" @@ -1300,7 +1387,9 @@ version = "24.8.2" dependencies = [ "cocoa 0.25.0", "dashmap", + "env_logger", "hex_color", + "log", "objc", "reqwest", "serde", @@ -1310,6 +1399,7 @@ dependencies = [ "tauri-plugin-deep-link", "tauri-plugin-store", "tauri-plugin-window-state", + "thiserror", "tokio", "tokio-util", "url", @@ -1371,6 +1461,12 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + [[package]] name = "hyper" version = "0.14.28" @@ -1544,6 +1640,12 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + [[package]] name = "itoa" version = "0.4.8" @@ -1619,6 +1721,29 @@ dependencies = [ "thiserror", ] +[[package]] +name = "json-patch" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b1fb8864823fad91877e6caea0baca82e49e8db50f8e5c9f9a453e27d3330fc" +dependencies = [ + "jsonptr", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "jsonptr" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c6e529149475ca0b2820835d3dce8fcc41c6b943ca608d32f35b449255e4627" +dependencies = [ + "fluent-uri", + "serde", + "serde_json", +] + [[package]] name = "kuchikiki" version = "0.8.2" @@ -1678,9 +1803,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "loom" @@ -2837,9 +2962,9 @@ dependencies = [ [[package]] name = "serialize-to-javascript" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9823f2d3b6a81d98228151fdeaf848206a7855a7a042bbf9bf870449a66cafb" +checksum = "04f3666a07a197cdb77cdf306c32be9b7f598d7060d50cfd4d5aa04bfd92f6c5" dependencies = [ "serde", "serde_json", @@ -2848,13 +2973,13 @@ dependencies = [ [[package]] name = "serialize-to-javascript-impl" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74064874e9f6a15f04c1f3cb627902d0e6b410abbf36668afa873c61889f1763" +checksum = "772ee033c0916d670af7860b6e1ef7d658a4629a6d0b4c8c3e67f09b3765b75d" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.66", ] [[package]] @@ -3029,15 +3154,11 @@ checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" [[package]] name = "sys-locale" -version = "0.2.4" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8a11bd9c338fdba09f7881ab41551932ad42e405f61d01e8406baea71c07aee" +checksum = "e801cf239ecd6ccd71f03d270d67dd53d13e90aab208bf4b8fe4ad957ea949b0" dependencies = [ - "js-sys", "libc", - "wasm-bindgen", - "web-sys", - "windows-sys 0.45.0", ] [[package]] @@ -3164,9 +3285,9 @@ checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" [[package]] name = "tauri" -version = "1.6.7" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67c7177b6be45bbb875aa239578f5adc982a1b3d5ea5b315ccd100aeb0043374" +checksum = "0e33e3ba00a3b05eb6c57ef135781717d33728b48acf914bb05629e74d897d29" dependencies = [ "anyhow", "bytes", @@ -3227,7 +3348,7 @@ dependencies = [ "cargo_toml", "dirs-next", "heck 0.5.0", - "json-patch", + "json-patch 1.4.0", "semver", "serde", "serde_json", @@ -3238,14 +3359,14 @@ dependencies = [ [[package]] name = "tauri-codegen" -version = "1.4.3" +version = "1.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3a1d90db526a8cdfd54444ad3f34d8d4d58fa5c536463915942393743bd06f8" +checksum = "93a9e3f5cebf779a63bf24903e714ec91196c307d8249a0008b882424328bcda" dependencies = [ "base64 0.21.7", "brotli", "ico", - "json-patch", + "json-patch 2.0.0", "plist", "png", "proc-macro2", @@ -3264,9 +3385,9 @@ dependencies = [ [[package]] name = "tauri-macros" -version = "1.4.4" +version = "1.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a582d75414250122e4a597b9dd7d3c910a2c77906648fc2ac9353845ff0feec" +checksum = "d1d0e989f54fe06c5ef0875c5e19cf96453d099a0a774d5192ab47e80471cdab" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -3320,9 +3441,9 @@ dependencies = [ [[package]] name = "tauri-runtime" -version = "0.14.3" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd7ffddf36d450791018e63a3ddf54979b9581d9644c584a5fb5611e6b5f20b4" +checksum = "f33fda7d213e239077fad52e96c6b734cecedb30c2382118b64f94cb5103ff3a" dependencies = [ "gtk", "http", @@ -3341,9 +3462,9 @@ dependencies = [ [[package]] name = "tauri-runtime-wry" -version = "0.14.8" +version = "0.14.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1989b3b4d611f5428b3414a4abae6fa6df30c7eb8ed33250ca90a5f7e5bb3655" +checksum = "18c447dcd9b0f09c7dc4b752cc33e72788805bfd761fbda5692d30c48289efec" dependencies = [ "cocoa 0.24.1", "gtk", @@ -3361,9 +3482,9 @@ dependencies = [ [[package]] name = "tauri-utils" -version = "1.5.4" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "450b17a7102e5d46d4bdabae0d1590fd27953e704e691fc081f06c06d2253b35" +checksum = "83a0c939e88d82903a0a7dfb28388b12a3c03504d6bd6086550edaa3b6d8beaa" dependencies = [ "brotli", "ctor", @@ -3372,7 +3493,7 @@ dependencies = [ "heck 0.5.0", "html5ever", "infer", - "json-patch", + "json-patch 2.0.0", "kuchikiki", "log", "memchr", @@ -3430,18 +3551,18 @@ checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c" [[package]] name = "thiserror" -version = "1.0.61" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.61" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", @@ -3762,6 +3883,12 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + [[package]] name = "uuid" version = "1.8.0" @@ -4153,15 +4280,6 @@ dependencies = [ "windows_x86_64_msvc 0.42.2", ] -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", -] - [[package]] name = "windows-sys" version = "0.48.0" @@ -4180,21 +4298,6 @@ dependencies = [ "windows-targets 0.52.5", ] -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - [[package]] name = "windows-targets" version = "0.48.5" diff --git a/packages/hoppscotch-selfhost-desktop/src-tauri/Cargo.toml b/packages/hoppscotch-selfhost-desktop/src-tauri/Cargo.toml index d134ac93e..fc394a3d0 100644 --- a/packages/hoppscotch-selfhost-desktop/src-tauri/Cargo.toml +++ b/packages/hoppscotch-selfhost-desktop/src-tauri/Cargo.toml @@ -21,7 +21,6 @@ tauri = { version = "1.5.3", features = [ "shell-open", "window-start-dragging", "http-multipart", - "devtools" ] } tauri-plugin-store = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" } tauri-plugin-deep-link = { git = "https://github.com/FabianLars/tauri-plugin-deep-link", branch = "main" } @@ -34,6 +33,12 @@ serde = { version = "1.0.203", features = ["derive"] } dashmap = "5.5.3" tokio = { version = "1.38.0", features = ["macros"] } tokio-util = "0.7.11" +log = "0.4.22" +thiserror = "1.0.63" + +[dev-dependencies] +tauri = { version = "1.5.3", features = ["devtools", "test"] } +env_logger = "0.11.5" [target.'cfg(target_os = "macos")'.dependencies] cocoa = "0.25.0" diff --git a/packages/hoppscotch-selfhost-desktop/src-tauri/src/interop/mod.rs b/packages/hoppscotch-selfhost-desktop/src-tauri/src/interop/mod.rs new file mode 100644 index 000000000..91973a8e1 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src-tauri/src/interop/mod.rs @@ -0,0 +1 @@ +pub(crate) mod startup; diff --git a/packages/hoppscotch-selfhost-desktop/src-tauri/src/interop/startup/error.rs b/packages/hoppscotch-selfhost-desktop/src-tauri/src/interop/startup/error.rs new file mode 100644 index 000000000..d8ba3fa42 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src-tauri/src/interop/startup/error.rs @@ -0,0 +1,53 @@ +/// Error handling module for startup-related operations. +/// +/// This module defines custom error types and a result type used for startup process of the app. +/// Essentially provides a way to handle and communicate errors +/// that may occur during the initialization and window management phases. +use serde::Serialize; +use thiserror::Error; + +/// Represents errors related to window lookup failures. +/// +/// Provide more specific information about which window that could not be found. +/// +/// Derives `Serialize` mainly for sending it over to the frontend for info/logging purposes. +#[derive(Debug, Error, Serialize)] +pub(crate) enum WindowNotFoundError { + /// Indicates that the `main` window of the app could not be found. + /// + /// This typically occurs if there's a mismatch between the expected + /// window labels and the actual windows created by the application. + #[error("No window labeled 'main' found")] + Main, +} + +/// Represents errors that can occur during the startup process. +/// +/// Derives `Serialize` mainly for sending it over to the frontend for info/logging purposes. +#[derive(Debug, Error, Serialize)] +pub(crate) enum StartupError { + /// Represents errors related to window lookup failures. + #[error("Window not found: {0}")] + WindowNotFound(WindowNotFoundError), + + /// Represents a general error from the Tauri runtime. + /// + /// This variant is used for any errors originating from Tauri that don't + /// fit into more specific categories. + #[error("Tauri error: {0}")] + Tauri(String), +} + +/// Functions that are part of the startup process should return this result type. +/// This allows for consistent error handling and makes it clear that the function +/// is part of the startup flow. +/// +/// ``` +/// use your_crate::error::{StartupResult, StartupError}; +/// +/// fn some_startup_function() -> StartupResult<()> { +/// // Function implementation +/// Ok(()) +/// } +/// ``` +pub(crate) type StartupResult = std::result::Result; diff --git a/packages/hoppscotch-selfhost-desktop/src-tauri/src/interop/startup/init.rs b/packages/hoppscotch-selfhost-desktop/src-tauri/src/interop/startup/init.rs new file mode 100644 index 000000000..fe67125e2 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src-tauri/src/interop/startup/init.rs @@ -0,0 +1,186 @@ +use log::{error, info}; +use tauri::{Manager, Runtime, Window}; + +use super::error::{StartupError, StartupResult, WindowNotFoundError}; +/// Shows the `main` labeled application window. +/// +/// This function is designed to be called as a Tauri command. +/// +/// # Arguments +/// +/// * `window` - A `Window` instance representing the current window. This is automatically +/// provided by Tauri when the command is invoked. +/// +/// # Returns +/// +/// Returns a `StartupResult<(), String>`: +/// - `Ok(())` if showing main window operation succeed. +/// - `Err(StartupError)` containing an error message if any operation fails. +/// +/// # Errors +/// +/// This function will return an error if: +/// - The "main" window is not found. +/// - Showing the main window fails. +/// +/// # Example +/// +/// ```rust,no_run +/// #[tauri::command] +/// async fn invoke_interop_startup_init(window: tauri::Window) { +/// match interop_startup_init(window).await { +/// Ok(_) => println!("`main` window shown successfully"), +/// Err(e) => eprintln!("Error: {}", e), +/// } +/// } +/// ``` +#[tauri::command] +pub async fn interop_startup_init(window: Window) -> StartupResult<()> { + let main_window = window.get_window("main").ok_or_else(|| { + error!("No window labeled 'main' found"); + StartupError::WindowNotFound(WindowNotFoundError::Main) + })?; + + main_window.show().map_err(|e| { + error!("Failed to show `main` window: {}", e); + StartupError::Tauri(format!("Failed to show `main` window: {}", e)) + })?; + + info!("`main` window shown successfully"); + + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_json::json; + use tauri::test::{assert_ipc_response, mock_builder, mock_context, noop_assets}; + use tauri::{InvokePayload, WindowBuilder, WindowUrl}; + + fn create_app(builder: tauri::Builder) -> tauri::App { + builder + .invoke_handler(tauri::generate_handler![interop_startup_init]) + .build(mock_context(noop_assets())) + .expect("failed to build mock app") + } + + /// Test: Main window shown successfully in isolation + /// + /// Rationale: + /// This test verifies the core functionality of `interop_startup_init`. + /// A failure indicates a fundamental issue with the app's initialization process. + /// + /// Context: + /// The "main" window is typically the primary interface, + /// so ensuring it shows correctly is important. + /// + /// Key Points: + /// - We use a mock Tauri application to isolate the window showing behavior. + /// - The test focuses solely on the "main" window to verify the basic case works correctly. + /// + /// Assumptions: + /// - The Tauri runtime is functioning correctly. + /// - A window labeled "main" exists in the application. + /// For this see `tauri.conf.json`: + /// ```json + /// { + /// ... + /// "label": "main", + /// "title": "Hoppscotch", + /// ... + /// ... + /// } + /// ``` + /// + /// Implications of Failure: + /// 1. The window labeling system is broken. + /// 2. There's an issue with Tauri's window management. + /// 3. The `interop_startup_init` function is not correctly implemented. + #[tokio::test] + async fn test_interop_startup_init_main_window_shown_successfully() { + let app = create_app(mock_builder()); + + let window = app.get_window("main").expect("`main` window not found"); + + let result = interop_startup_init(window).await; + + assert!(result.is_ok(), "Expected Ok, but got {:?}", result); + } + + /// Test: Main window found and shown amongst other windows + /// + /// Rationale: + /// This test ensures `interop_startup_init` can correctly identify and show the main window + /// in a more complex scenario with multiple windows. + /// + /// Context: + /// As applications grow, they may introduce additional windows for various purposes. The ability + /// to consistently identify and manipulate the main window is important for maintaining + /// expected behavior. + /// + /// Key Points: + /// - We create an additional "other" window to simulate another window. + /// - The test verifies that the presence of other windows doesn't interfere with main window operations. + /// + /// Assumptions: + /// - The window labeling system consistently identifies the "main" window regardless of other windows. + /// - The order of window creation doesn't affect the ability to find the main window. + /// + /// Implications of Failure: + /// 1. The window identification logic breaks with multiple windows. + #[tokio::test] + async fn test_interop_startup_init_main_window_found_amongst_others() { + let app = create_app(mock_builder()); + + let _ = WindowBuilder::new(&app, "other", WindowUrl::default()) + .build() + .expect("Failed to create other window"); + + let window = app.get_window("other").expect("`other` window not found"); + + let result = interop_startup_init(window).await; + + assert!(result.is_ok(), "Expected `Ok(())`, but got {:?}", result); + } + + /// Test: IPC invocation of interop startup init + /// + /// Rationale: + /// This test makes sure that `interop_startup_init` can be correctly invoked through Tauri's IPC mechanism. + /// It's important because it verifies the integration between the Rust backend and the frontend + /// that would typically call this function. + /// + /// Context: + /// This test simulates scenarios where operations are initiated from the frontend via IPC calls. + /// + /// Key Points: + /// - We're testing the IPC invocation, not just the direct function call. + /// - This verifies both the function's behavior and its correct registration with Tauri's IPC system. + /// + /// Assumptions: + /// - The Tauri IPC system is functioning correctly. + /// - The `interop_startup_init` function is properly registered as a Tauri command. + /// + /// Implications of Failure: + /// 1. There's a mismatch between how the frontend tries to call the function and how it's implemented. + /// 2. The Tauri command registration is incorrect. + /// 3. The function isn't properly handling the IPC context. + #[tokio::test] + async fn test_ipc_interop_startup_init() { + let app = create_app(mock_builder()); + + let window = app.get_window("main").expect("main window not found"); + + let payload = InvokePayload { + cmd: "interop_startup_init".into(), + tauri_module: None, + callback: tauri::api::ipc::CallbackFn(0), + error: tauri::api::ipc::CallbackFn(1), + inner: json!(null), + invoke_key: Some("__invoke-key__".to_string()), + }; + + assert_ipc_response(&window, payload, Ok(())); + } +} diff --git a/packages/hoppscotch-selfhost-desktop/src-tauri/src/interop/startup/mod.rs b/packages/hoppscotch-selfhost-desktop/src-tauri/src/interop/startup/mod.rs new file mode 100644 index 000000000..32de9ce44 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src-tauri/src/interop/startup/mod.rs @@ -0,0 +1,7 @@ +//! Startup management module. +//! +//! This module contains functionality related to managing the application's startup +//! like controlling visibility and lifecycle of the main application windows. + +pub(crate) mod init; +pub(crate) mod error; diff --git a/packages/hoppscotch-selfhost-desktop/src-tauri/src/main.rs b/packages/hoppscotch-selfhost-desktop/src-tauri/src/main.rs index 1c280b9a1..89145a241 100644 --- a/packages/hoppscotch-selfhost-desktop/src-tauri/src/main.rs +++ b/packages/hoppscotch-selfhost-desktop/src-tauri/src/main.rs @@ -16,6 +16,7 @@ mod mac; mod win; mod interceptor; +mod interop; use tauri::Manager; @@ -23,7 +24,31 @@ fn main() { tauri_plugin_deep_link::prepare("io.hoppscotch.desktop"); tauri::Builder::default() - .plugin(tauri_plugin_window_state::Builder::default().build()) + .invoke_handler(tauri::generate_handler![ + interop::startup::init::interop_startup_init + ]) + .plugin( + tauri_plugin_window_state::Builder::default() + .with_state_flags( + // NOTE: + // The app (window labeled "main") manages its visible state via `interop_startup_init`. + // See `tauri.conf.json`: + // ```json + // { + // "label": "main", + // "title": "Hoppscotch", + // ... + // ... + // "visible": false, // This is the important part. + // ... + // ... + // } + // ``` + tauri_plugin_window_state::StateFlags::all() + & !tauri_plugin_window_state::StateFlags::VISIBLE, + ) + .build(), + ) .plugin(tauri_plugin_store::Builder::default().build()) .plugin(interceptor::init()) .setup(|app| { diff --git a/packages/hoppscotch-selfhost-desktop/src-tauri/tauri.conf.json b/packages/hoppscotch-selfhost-desktop/src-tauri/tauri.conf.json index 64fc3a375..73474e84a 100644 --- a/packages/hoppscotch-selfhost-desktop/src-tauri/tauri.conf.json +++ b/packages/hoppscotch-selfhost-desktop/src-tauri/tauri.conf.json @@ -56,9 +56,11 @@ }, "windows": [ { + "label": "main", + "title": "Hoppscotch", + "visible": false, "fullscreen": false, "resizable": true, - "title": "Hoppscotch", "width": 800, "height": 600, "fileDropEnabled": false diff --git a/packages/hoppscotch-selfhost-desktop/src/interop.ts b/packages/hoppscotch-selfhost-desktop/src/interop.ts new file mode 100644 index 000000000..a681f2ff4 --- /dev/null +++ b/packages/hoppscotch-selfhost-desktop/src/interop.ts @@ -0,0 +1,41 @@ +import { invoke } from "@tauri-apps/api/tauri"; +import { onMounted } from "vue"; +import { HoppModule } from "@hoppscotch/common/modules" + +/** + * Initializes the interop startup process. + * + * This function invokes a Tauri command to perform necessary startup operations. + * It's a bridge, specifically calling `interop_startup_init` function defined in + * `src-tauri/src/interop/startup/init.rs`. + * + * @returns A promise that resolves when the startup initialization is complete. + * @throws Will throw an error if the Tauri command fails for any reason. + */ +async function interopStartupInit(): Promise { + return invoke("interop_startup_init"); +} + +/** + * HoppModule for handling interop operations and + * is responsible for initializing interop-startup related functionality. + */ +export const interopModule: HoppModule = { + /** + * Executes when the root component is set up. + * + * This function is called during the application's initialization process, + * and it also uses Vue's `onMounted` hook so the interop-startup occurs + * **after** the component has been mounted in the DOM. + */ + onRootSetup: () => { + onMounted(async () => { + try { + await interopStartupInit(); + console.log("Interop startup initialization completed successfully"); + } catch (error) { + console.error("Failed to initialize interop startup:", error); + } + }); + }, +}; diff --git a/packages/hoppscotch-selfhost-desktop/src/main.ts b/packages/hoppscotch-selfhost-desktop/src/main.ts index 07dd44b4d..c322a7c54 100644 --- a/packages/hoppscotch-selfhost-desktop/src/main.ts +++ b/packages/hoppscotch-selfhost-desktop/src/main.ts @@ -14,6 +14,7 @@ import { appWindow } from "@tauri-apps/api/window" import { stdFooterItems } from "@hoppscotch/common/platform/std/ui/footerItem" import { stdSupportOptionItems } from "@hoppscotch/common/platform/std/ui/supportOptionsItem" import { ioDef } from "./platform/io" +import { interopModule } from "./interop" const headerPaddingLeft = ref("0px") const headerPaddingTop = ref("0px") @@ -45,6 +46,9 @@ const headerPaddingTop = ref("0px") settings: settingsDef, history: historyDef, }, + addedHoppModules: [ + interopModule + ], interceptors: { default: "native", interceptors: [