From f74a051bf85330aef926a457d31e33f983966781 Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Mon, 26 Dec 2022 21:44:29 -0500 Subject: [PATCH] Testing infrastructure and value getter --- Cargo.toml | 9 ++++++++- src/lib.rs | 18 +++++++++++++----- src/tests/mod.rs | 47 +++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 64 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9a18308..c83fc08 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,4 +6,11 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -sycamore = "0.8" \ No newline at end of file +wasm-bindgen-test = "0.3" +wasm-bindgen = "0.2.83" + +[dependencies.sycamore] +version = "0.8" + +[features] +async = ["sycamore/suspense"] \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 122ef76..fa2396c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,8 +18,8 @@ use sycamore::prelude::*; /// Trait that maps a message and an original state value to a new value. /// Implementors of this trait can implement all of their state management /// logic in one place. -pub trait MessageMapper { - fn map(&self, msg: Msg, original: &ReadSignal) -> Out; +pub trait MessageMapper { + fn map<'ctx>(&self, cx: Scope<'ctx>, msg: Msg, original: &'ctx Signal); } /// Provides the necessary wiring for a centralized state handling @@ -55,8 +55,8 @@ where } /// Directly handle a state message without requiring a binding. - pub fn dispatch(&self, msg: Msg) { - self.signal.set(self.dispatcher.map(msg, self.signal)) + pub fn dispatch(&'ctx self, cx: Scope<'ctx>, msg: Msg) { + self.dispatcher.map(cx, msg, self.signal) } /// Provides a ReadSignal handle for the contained Signal implementation. @@ -74,7 +74,7 @@ where ) where F: Fn(Rc) -> Msg + 'ctx, { - create_effect(cx, move || self.dispatch(message_fn(trigger.get()))); + create_effect(cx, move || self.dispatch(cx, message_fn(trigger.get()))); } /// Helper method to get a memoized value derived from the contained @@ -91,6 +91,14 @@ where { create_selector(cx, move || selector_factory(self.signal)) } + + // Helper method to get a non reactive value from the state. + pub fn get_value(&'ctx self, getter_factory: F) -> Val + where + F: Fn(&'ctx ReadSignal) -> Val + 'ctx, + { + getter_factory(self.signal) + } } #[cfg(test)] diff --git a/src/tests/mod.rs b/src/tests/mod.rs index c3ac169..ce10be6 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -12,6 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. use super::*; +use wasm_bindgen_test::wasm_bindgen_test; + +wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); pub enum Msg { UpdateOne(String), @@ -40,8 +43,8 @@ where pub struct StateMachine(); impl MessageMapper for StateMachine { - fn map(&self, msg: Msg, original: &ReadSignal) -> FakeState { - match msg { + fn map(&self, _cx: Scope, msg: Msg, original: &Signal) { + let new_state = match msg { Msg::UpdateOne(val) => { let mut new_state = original.get().as_ref().clone(); new_state.value_one = val; @@ -52,7 +55,8 @@ impl MessageMapper for StateMachine { new_state.value_two = val; new_state } - } + }; + original.set(new_state); } } @@ -65,7 +69,7 @@ macro_rules! with_scope { }}; } -#[test] +#[wasm_bindgen_test] fn test_state_effect_flow() { with_scope! {cx, let state = FakeState { @@ -94,3 +98,38 @@ fn test_state_effect_flow() { }); }; } + +#[cfg(feature = "async")] +#[wasm_bindgen_test] +fn test_state_effect_flow_async() { + use sycamore::futures::spawn_local_scoped; + with_scope! {cx, + let state = FakeState { + value_one: "foo".to_owned(), + value_two: 0, + }; + + let handler = Handler::new(cx, state, StateMachine()); + + create_child_scope(cx, |cx| { + let form_val = create_signal(cx, handler.read_signal().get_untracked().value_one.clone()); + + handler.bind_trigger(cx, form_val, |val| Msg::UpdateOne((*val).clone())); + + form_val.set("bar".to_owned()); + + assert_eq!(handler.read_signal().get_untracked().value_one, "bar".to_owned()); + + create_child_scope(cx, |cx| { + let form_val = create_signal(cx, 0); + + handler.bind_trigger(cx, form_val, |val| Msg::UpdateTwo(*val)); + + spawn_local_scoped(cx, async { + form_val.set(1); + assert_eq!(handler.read_signal().get_untracked().value_two, 1); + }); + }); + }); + }; +}