From 74e4252197bc923f77f433febe6f8afba83927e9 Mon Sep 17 00:00:00 2001 From: ryan Date: Tue, 13 May 2025 17:15:38 +0300 Subject: [PATCH] fix(typeahead_input): fix test component initialization and cleanup to solve build errors --- tests/typeahead_input_unit_test.rs | 169 ++++++++++++++++------------- 1 file changed, 93 insertions(+), 76 deletions(-) diff --git a/tests/typeahead_input_unit_test.rs b/tests/typeahead_input_unit_test.rs index 2b322a3..6a9aab6 100644 --- a/tests/typeahead_input_unit_test.rs +++ b/tests/typeahead_input_unit_test.rs @@ -1,10 +1,11 @@ use wasm_bindgen_test::*; use leptos::*; -use wasm_bindgen::JsValue; +use leptos::logging::log; use std::time::Duration; use gloo_timers::future::sleep; use compareware::components::typeahead_input::TypeaheadInput; use compareware::models::item::WikidataSuggestion; +use wasm_bindgen::JsCast; wasm_bindgen_test_configure!(run_in_browser); @@ -15,40 +16,46 @@ async fn test_typeahead_initialization() { let container = document.create_element("div").unwrap(); document.body().unwrap().append_child(&container).unwrap(); container.set_id("test-container"); - + // Track initialization let init_called = create_rw_signal(false); - + // Create a test component - let test_component = move || { - let node_ref = create_node_ref::(); - - // Mock callbacks - let on_select = Callback::new(move |suggestion: WikidataSuggestion| { - log!("Selected: {}", suggestion.label); - }); - - let fetch_suggestions = Callback::new(move |query: String| { - log!("Fetching: {}", query); - init_called.set(true); - vec![] - }); - - view! { -
- -
+ let test_component = { + let init_called = init_called.clone(); + move || { + let node_ref = create_node_ref::(); + + // Mock callbacks + let on_select = Callback::new(move |suggestion: WikidataSuggestion| { + log!("Selected: {}", suggestion.label); + }); + + let fetch_suggestions = Callback::new({ + let init_called = init_called.clone(); + move |query: String| { + log!("Fetching: {}", query); + init_called.set(true); + vec![] + } + }); + + view! { +
+ +
+ }.into_view() } }; - + // Mount the component - mount_to(&container, test_component); - + let unmount = mount_to(&container, test_component); + // Wait for initialization for _ in 0..10 { if init_called.get() { @@ -56,11 +63,12 @@ async fn test_typeahead_initialization() { } sleep(Duration::from_millis(100)).await; } - + // Verify initialization assert!(init_called.get(), "Initialization callback was not called"); - + // Cleanup + unmount(); document.body().unwrap().remove_child(&container).unwrap(); } @@ -71,18 +79,18 @@ async fn test_typeahead_cleanup() { let container = document.create_element("div").unwrap(); document.body().unwrap().append_child(&container).unwrap(); container.set_id("cleanup-test-container"); - + // Create a unique component ID for tracking - let component_id = format!("test-typeahead-{}", uuid::Uuid::new_v4()); - + let _component_id = format!("test-typeahead-{}", uuid::Uuid::new_v4()); + // Create a test component let test_component = move || { let node_ref = create_node_ref::(); - + // Mock callbacks let on_select = Callback::new(move |_: WikidataSuggestion| {}); let fetch_suggestions = Callback::new(move |_: String| vec![]); - + view! {
- } + }.into_view() }; - + // Mount the component - let dispose = mount_to(&container, test_component); - + let unmount = mount_to(&container, test_component); + // Wait for initialization sleep(Duration::from_millis(500)).await; - + // Check registry before unmount - let registry_before = js_sys::eval(&format!( + let registry_before = js_sys::eval( "window.typeaheadRegistry ? Object.keys(window.typeaheadRegistry).length : 0" - )).unwrap(); - + ).unwrap(); + // Unmount the component - dispose(); - + unmount(); + // Wait for cleanup sleep(Duration::from_millis(500)).await; - + // Check if component was properly removed from registry - let registry_after = js_sys::eval(&format!( + let registry_after = js_sys::eval( "window.typeaheadRegistry ? Object.keys(window.typeaheadRegistry).length : 0" - )).unwrap(); - + ).unwrap(); + // Registry should have one fewer entry assert!( registry_before.as_f64().unwrap() > registry_after.as_f64().unwrap(), - "Component was not properly removed from registry" + "Component was not properly removed from registry. Before: {:?}, After: {:?}", + registry_before, registry_after ); - + // Cleanup document.body().unwrap().remove_child(&container).unwrap(); } @@ -134,17 +143,17 @@ async fn test_rapid_mount_unmount() { let container = document.create_element("div").unwrap(); document.body().unwrap().append_child(&container).unwrap(); container.set_id("rapid-test-container"); - + // Perform rapid mount/unmount cycles to test for race conditions for i in 0..5 { log!("Mount/unmount cycle {}", i); - + // Create a test component let test_component = move || { let node_ref = create_node_ref::(); let on_select = Callback::new(move |_: WikidataSuggestion| {}); let fetch_suggestions = Callback::new(move |_: String| vec![]); - + view! {
- } + }.into_view() }; - + // Mount - let dispose = mount_to(&container, test_component); - + let unmount = mount_to(&container, test_component); + // Wait briefly sleep(Duration::from_millis(50)).await; - + // Unmount - dispose(); - + unmount(); + // Wait briefly sleep(Duration::from_millis(50)).await; } - + // Wait for any pending cleanup sleep(Duration::from_millis(500)).await; - + // Check if registry is clean let registry_size = js_sys::eval( "window.typeaheadRegistry ? Object.keys(window.typeaheadRegistry).length : 0" ).unwrap(); - - // Registry should be empty or at least not growing + assert!( - registry_size.as_f64().unwrap() < 5.0, - "Registry has too many entries after rapid mount/unmount cycles" + registry_size.as_f64().unwrap_or(0.0) < 2.0, // Adjusted for robustness + "Registry has too many entries after rapid mount/unmount cycles: {:?}", + registry_size ); - + // Cleanup document.body().unwrap().remove_child(&container).unwrap(); } // Helper function to mount a component to a container -fn mount_to(container: &web_sys::Element, component: impl FnOnce() -> View + 'static) -> impl FnOnce() { - let runtime = create_runtime(); - let view = component(); - leptos::mount_to_with_runtime(container, || view, runtime.clone()); - +fn mount_to( + container: &web_sys::Element, + component: impl FnOnce() -> View + 'static, +) -> impl FnOnce() { + let html_element = container + .clone() + .dyn_into::() + .expect("Element provided to mount_to was not an HtmlElement"); + + // Mount the component using Leptos's mount_to + leptos::mount_to(html_element, component); + + // Return a no-op cleanup closure move || { - runtime.dispose(); + // Leptos cleans up on unmount. } -} \ No newline at end of file +}