From 79693f8cffb487c4cde226269c55db48c5cadabc Mon Sep 17 00:00:00 2001 From: Martin Date: Tue, 14 Apr 2026 23:37:30 +0100 Subject: [PATCH] Minor fixes for tests (#4686) * Suggested method for correcting integration tests * Use static atomics for simplicity This is consistent with the test immediately below. * Add assertions to some tests, comments to others This should fix a handful of tests that didn't correctly do their test. In other places, comments should make it clearer *why* tests are how they are. * Be consistent with existing imports * remove bad test * nit * fix compile errrs --------- Co-authored-by: Jonathan Kelley --- packages/core/tests/bubble_error.rs | 29 ------------- .../tests/conditional_formatted_attributes.rs | 3 +- packages/core/tests/diff_keyed_list.rs | 2 +- packages/core/tests/diff_unkeyed_list.rs | 2 +- packages/core/tests/error_boundary.rs | 19 ++++++++- packages/core/tests/fuzzing.rs | 8 ++++ packages/core/tests/miri_full_app.rs | 2 + packages/core/tests/miri_simple.rs | 3 ++ packages/core/tests/miri_stress.rs | 3 ++ packages/core/tests/suspense.rs | 32 -------------- packages/signals/tests/create.rs | 42 ++++++++++++++----- packages/signals/tests/memo.rs | 6 ++- 12 files changed, 74 insertions(+), 77 deletions(-) delete mode 100644 packages/core/tests/bubble_error.rs diff --git a/packages/core/tests/bubble_error.rs b/packages/core/tests/bubble_error.rs deleted file mode 100644 index 814c293f6..000000000 --- a/packages/core/tests/bubble_error.rs +++ /dev/null @@ -1,29 +0,0 @@ -//! we should properly bubble up errors from components - -use dioxus::prelude::*; -use dioxus_core::generation; - -fn app() -> Element { - let raw = match generation() % 2 { - 0 => "123.123", - 1 => "123.123.123", - _ => unreachable!(), - }; - - let value = raw.parse::().unwrap_or(123.123); - - rsx! { div { "hello {value}" } } -} - -#[test] -fn bubbles_error() { - let mut dom = VirtualDom::new(app); - - { - let _edits = dom.rebuild_to_vec(); - } - - dom.mark_dirty(ScopeId::APP); - - _ = dom.render_immediate_to_vec(); -} diff --git a/packages/core/tests/conditional_formatted_attributes.rs b/packages/core/tests/conditional_formatted_attributes.rs index 26b9cf557..bd06f971e 100644 --- a/packages/core/tests/conditional_formatted_attributes.rs +++ b/packages/core/tests/conditional_formatted_attributes.rs @@ -1,7 +1,8 @@ use dioxus::prelude::*; /// Make sure that rsx! handles conditional attributes with one formatted branch correctly -/// Regression test for https://github.com/DioxusLabs/dioxus/issues/2997 +/// Regression test for https://github.com/DioxusLabs/dioxus/issues/2997. There are no assertions in +/// this test as it primarily checks that the RSX can be compiled correctly. #[test] fn partially_formatted_conditional_attribute() { let width = "1px"; diff --git a/packages/core/tests/diff_keyed_list.rs b/packages/core/tests/diff_keyed_list.rs index 3e2aebdf9..c77a5a0df 100644 --- a/packages/core/tests/diff_keyed_list.rs +++ b/packages/core/tests/diff_keyed_list.rs @@ -2,7 +2,7 @@ //! //! These tests only verify that the diffing algorithm works properly for single components. //! -//! It does not validated that component lifecycles work properly. This is done in another test file. +//! It does not validate that component lifecycles work properly. This is done in another test file. use dioxus::dioxus_core::{ElementId, Mutation::*}; use dioxus::prelude::*; diff --git a/packages/core/tests/diff_unkeyed_list.rs b/packages/core/tests/diff_unkeyed_list.rs index cb093e697..47c43e0c3 100644 --- a/packages/core/tests/diff_unkeyed_list.rs +++ b/packages/core/tests/diff_unkeyed_list.rs @@ -464,7 +464,7 @@ fn replace_and_add_items() { ); } - // Rerendering adds an a static template + // Rerendering adds a static template { dom.mark_dirty(ScopeId::APP); let edits = dom.render_immediate_to_vec(); diff --git a/packages/core/tests/error_boundary.rs b/packages/core/tests/error_boundary.rs index 89c06469c..7309f1148 100644 --- a/packages/core/tests/error_boundary.rs +++ b/packages/core/tests/error_boundary.rs @@ -44,7 +44,7 @@ fn clear_error_boundary() { #[component] pub fn ThrowsError() -> Element { - if THREW_ERROR.load(std::sync::atomic::Ordering::SeqCst) { + if !THREW_ERROR.load(std::sync::atomic::Ordering::SeqCst) { THREW_ERROR.store(true, std::sync::atomic::Ordering::SeqCst); Err(CapturedError::from_display("This is an error").into()) } else { @@ -71,7 +71,22 @@ fn clear_error_boundary() { let mut dom = VirtualDom::new(App); dom.rebuild(&mut dioxus_core::NoOpMutations); - let out = dioxus_ssr::render(&dom); + // The DOM will now contain no text - the error was thrown by ThrowsError and caught by the + // ErrorBoundary. This calls `needs_update()`, but the update hasn't been processed yet. + dom.render_immediate(&mut dioxus_core::NoOpMutations); + // The ErrorBoundary has now called its `handle_error` handler. The DOM contains the string + // "We cleared it", and `needs_update()` has been called again (by `error.clear_errors()`) + + dom.render_immediate(&mut dioxus_core::NoOpMutations); + // `ThrowsError` is re-rendered, but this time does not throw an error, so at the end the DOM + // contains "We should see this" + + let out = dioxus_ssr::render(&dom); assert_eq!(out, "We should see this"); + + // There should be no errors left in the error boundary + dom.in_scope(ScopeId::APP, || { + assert!(consume_context::().error().is_none()) + }) } diff --git a/packages/core/tests/fuzzing.rs b/packages/core/tests/fuzzing.rs index a26bc08cc..ad6389ce0 100644 --- a/packages/core/tests/fuzzing.rs +++ b/packages/core/tests/fuzzing.rs @@ -287,6 +287,10 @@ fn create() { let mut vdom = VirtualDom::new_with_props(create_random_element, DepthProps { depth: 0, root: true }); vdom.rebuild(&mut NoOpMutations); + + vdom.in_scope(ScopeId::APP, || { + assert!(consume_context::().error().is_none()) + }) } } @@ -317,6 +321,10 @@ fn diff() { )); } } + + vdom.in_scope(ScopeId::APP, || { + assert!(consume_context::().error().is_none()) + }) } } diff --git a/packages/core/tests/miri_full_app.rs b/packages/core/tests/miri_full_app.rs index 1196b4263..883a7bedd 100644 --- a/packages/core/tests/miri_full_app.rs +++ b/packages/core/tests/miri_full_app.rs @@ -3,6 +3,8 @@ use dioxus_core::ElementId; use dioxus_elements::SerializedHtmlEventConverter; use std::{any::Any, rc::Rc}; +// This test is intended to be run with Miri, and contains no assertions. If it completes under +// Miri, it has passed. #[test] fn miri_rollover() { set_event_converter(Box::new(SerializedHtmlEventConverter)); diff --git a/packages/core/tests/miri_simple.rs b/packages/core/tests/miri_simple.rs index 8d7f6f343..aa5c6216b 100644 --- a/packages/core/tests/miri_simple.rs +++ b/packages/core/tests/miri_simple.rs @@ -1,6 +1,9 @@ use dioxus::prelude::*; use dioxus_core::generation; +// The tests in this file are intended to be run with Miri, and contain no assertions. If they +// complete under Miri, they have passed. + #[test] fn app_drops() { fn app() -> Element { diff --git a/packages/core/tests/miri_stress.rs b/packages/core/tests/miri_stress.rs index 5659395ac..e2a7d2762 100644 --- a/packages/core/tests/miri_stress.rs +++ b/packages/core/tests/miri_stress.rs @@ -5,6 +5,9 @@ use std::rc::Rc; use dioxus::prelude::*; use dioxus_core::{generation, NoOpMutations}; +// The tests in this file are intended to be run with Miri, so not all of them contain assertions. +// If the tests complete under Miri, they have passed. + /// This test checks that we should release all memory used by the virtualdom when it exits. /// /// When miri runs, it'll let us know if we leaked or aliased. diff --git a/packages/core/tests/suspense.rs b/packages/core/tests/suspense.rs index 1e2dce058..1be1aaadf 100644 --- a/packages/core/tests/suspense.rs +++ b/packages/core/tests/suspense.rs @@ -217,22 +217,6 @@ fn suspended_nodes_dont_trigger_effects() { } } - #[component] - fn RerendersFrequently() -> Element { - let mut count = use_signal(|| 0); - - use_future(move || async move { - for _ in 0..100 { - tokio::time::sleep(std::time::Duration::from_millis(10)).await; - count.set(count() + 1); - } - }); - - rsx! { - div { "rerenders frequently" } - } - } - #[component] fn Child() -> Element { let mut future_resolved = use_signal(|| false); @@ -474,22 +458,6 @@ fn toggle_suspense() { fn nested_suspense_resolves_client() { use Mutation::*; - async fn poll_three_times() { - // Poll each task 3 times - let mut count = 0; - poll_fn(|cx| { - println!("polling... {}", count); - if count < 3 { - count += 1; - cx.waker().wake_by_ref(); - Poll::Pending - } else { - Poll::Ready(()) - } - }) - .await; - } - fn app() -> Element { rsx! { SuspenseBoundary { diff --git a/packages/signals/tests/create.rs b/packages/signals/tests/create.rs index c322a78e2..d765bffbd 100644 --- a/packages/signals/tests/create.rs +++ b/packages/signals/tests/create.rs @@ -1,7 +1,7 @@ #![allow(unused, non_upper_case_globals, non_snake_case)] use dioxus::prelude::*; -use dioxus_core::{generation, ElementId, NoOpMutations}; +use dioxus_core::{generation, ElementId, Mutation, NoOpMutations}; use dioxus_signals::*; #[test] @@ -22,35 +22,57 @@ fn create_signals_global() { } } - dom.rebuild_in_place(); - fn create_without_cx() -> Signal { Signal::new("hello world".to_string()) } + + let muts = dom.rebuild_to_vec(); + + // 11 edits: 10x CreateTextNode and 1x AppendChildren. These assertions rely on the VirtualDOM's + // logic, but doing this means not introducing a dependency on a renderer. + assert_eq!(11, muts.edits.len()); + for i in 0..10 { + assert_eq!( + &muts.edits[i], + &Mutation::CreateTextNode { + value: ("hello world".to_string()), + id: ElementId(i + 1) + } + ); + } + assert_eq!( + &muts.edits[10], + &Mutation::AppendChildren { + id: ElementId(0), + m: 10 + } + ) } #[test] fn deref_signal() { + use std::sync::atomic::{AtomicBool, Ordering}; + static STRINGS_MATCH: AtomicBool = AtomicBool::new(false); + let mut dom = VirtualDom::new(|| { - rsx! { - for _ in 0..10 { - Child {} - } - } + rsx! { Child {} } }); fn Child() -> Element { let signal = Signal::new("hello world".to_string()); // You can call signals like functions to get a Ref of their value. - assert_eq!(&*signal(), "hello world"); + let result = &*signal(); + STRINGS_MATCH.store(result.eq("hello world"), Ordering::Relaxed); rsx! { - "hello world" + "arbitrary text" } } dom.rebuild_in_place(); + + assert!(STRINGS_MATCH.load(Ordering::Relaxed)); } #[test] diff --git a/packages/signals/tests/memo.rs b/packages/signals/tests/memo.rs index 9601a9f66..a9b488022 100644 --- a/packages/signals/tests/memo.rs +++ b/packages/signals/tests/memo.rs @@ -1,8 +1,8 @@ #![allow(unused, non_upper_case_globals, non_snake_case)] use dioxus::html::p; use dioxus::prelude::*; -use dioxus_core::NoOpMutations; use dioxus_core::{generation, ElementId}; +use dioxus_core::{NoOpMutations, ScopeState}; use dioxus_signals::*; use std::cell::RefCell; use std::collections::HashMap; @@ -141,6 +141,10 @@ fn memos_prevents_component_rerun() { assert_eq!(current_counter.component, 2); assert_eq!(current_counter.memo, 3); } + + dom.in_scope(ScopeId(3), || { + assert!(consume_context::().error().is_none()) + }); } // Regression test for https://github.com/DioxusLabs/dioxus/issues/2990