Testing infrastructure and value getter

This commit is contained in:
Jeremy Wall 2022-12-26 21:44:29 -05:00
parent 66d1307f1b
commit f74a051bf8
3 changed files with 64 additions and 10 deletions

View File

@ -6,4 +6,11 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
sycamore = "0.8" wasm-bindgen-test = "0.3"
wasm-bindgen = "0.2.83"
[dependencies.sycamore]
version = "0.8"
[features]
async = ["sycamore/suspense"]

View File

@ -18,8 +18,8 @@ use sycamore::prelude::*;
/// Trait that maps a message and an original state value to a new value. /// 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 /// Implementors of this trait can implement all of their state management
/// logic in one place. /// logic in one place.
pub trait MessageMapper<Msg, Out> { pub trait MessageMapper<Msg, Val> {
fn map(&self, msg: Msg, original: &ReadSignal<Out>) -> Out; fn map<'ctx>(&self, cx: Scope<'ctx>, msg: Msg, original: &'ctx Signal<Val>);
} }
/// Provides the necessary wiring for a centralized state handling /// Provides the necessary wiring for a centralized state handling
@ -55,8 +55,8 @@ where
} }
/// Directly handle a state message without requiring a binding. /// Directly handle a state message without requiring a binding.
pub fn dispatch(&self, msg: Msg) { pub fn dispatch(&'ctx self, cx: Scope<'ctx>, msg: Msg) {
self.signal.set(self.dispatcher.map(msg, self.signal)) self.dispatcher.map(cx, msg, self.signal)
} }
/// Provides a ReadSignal handle for the contained Signal implementation. /// Provides a ReadSignal handle for the contained Signal implementation.
@ -74,7 +74,7 @@ where
) where ) where
F: Fn(Rc<Val>) -> Msg + 'ctx, F: Fn(Rc<Val>) -> 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 /// Helper method to get a memoized value derived from the contained
@ -91,6 +91,14 @@ where
{ {
create_selector(cx, move || selector_factory(self.signal)) create_selector(cx, move || selector_factory(self.signal))
} }
// Helper method to get a non reactive value from the state.
pub fn get_value<F, Val>(&'ctx self, getter_factory: F) -> Val
where
F: Fn(&'ctx ReadSignal<T>) -> Val + 'ctx,
{
getter_factory(self.signal)
}
} }
#[cfg(test)] #[cfg(test)]

View File

@ -12,6 +12,9 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
use super::*; use super::*;
use wasm_bindgen_test::wasm_bindgen_test;
wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
pub enum Msg { pub enum Msg {
UpdateOne(String), UpdateOne(String),
@ -40,8 +43,8 @@ where
pub struct StateMachine(); pub struct StateMachine();
impl MessageMapper<Msg, FakeState> for StateMachine { impl MessageMapper<Msg, FakeState> for StateMachine {
fn map(&self, msg: Msg, original: &ReadSignal<FakeState>) -> FakeState { fn map(&self, _cx: Scope, msg: Msg, original: &Signal<FakeState>) {
match msg { let new_state = match msg {
Msg::UpdateOne(val) => { Msg::UpdateOne(val) => {
let mut new_state = original.get().as_ref().clone(); let mut new_state = original.get().as_ref().clone();
new_state.value_one = val; new_state.value_one = val;
@ -52,7 +55,8 @@ impl MessageMapper<Msg, FakeState> for StateMachine {
new_state.value_two = val; new_state.value_two = val;
new_state new_state
} }
} };
original.set(new_state);
} }
} }
@ -65,7 +69,7 @@ macro_rules! with_scope {
}}; }};
} }
#[test] #[wasm_bindgen_test]
fn test_state_effect_flow() { fn test_state_effect_flow() {
with_scope! {cx, with_scope! {cx,
let state = FakeState { 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);
});
});
});
};
}