mod common;
use common::obtain_result_by_id;
use std::rc::Rc;
use wasm_bindgen_test::*;
use yew::functional::{
use_context, use_effect, use_mut_ref, use_state, FunctionComponent, FunctionProvider,
};
use yew::{html, Children, ContextProvider, Html, Properties};
wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
#[wasm_bindgen_test]
fn use_context_scoping_works() {
#[derive(Clone, Debug, PartialEq)]
struct ExampleContext(String);
struct UseContextFunctionOuter {}
struct UseContextFunctionInner {}
struct ExpectNoContextFunction {}
type UseContextComponent = FunctionComponent<UseContextFunctionOuter>;
type UseContextComponentInner = FunctionComponent<UseContextFunctionInner>;
type ExpectNoContextComponent = FunctionComponent<ExpectNoContextFunction>;
impl FunctionProvider for ExpectNoContextFunction {
type TProps = ();
fn run(_props: &Self::TProps) -> Html {
if use_context::<ExampleContext>().is_some() {
console_log!(
"Context should be None here, but was {:?}!",
use_context::<ExampleContext>().unwrap()
);
};
return html! {
<div></div>
};
}
}
impl FunctionProvider for UseContextFunctionOuter {
type TProps = ();
fn run(_props: &Self::TProps) -> Html {
type ExampleContextProvider = ContextProvider<ExampleContext>;
return html! {
<div>
<ExampleContextProvider context={ExampleContext("wrong1".into())}>
<div>{"ignored"}</div>
</ExampleContextProvider>
<ExampleContextProvider context={ExampleContext("wrong2".into())}>
<ExampleContextProvider context={ExampleContext("correct".into())}>
<ExampleContextProvider context={ExampleContext("wrong1".into())}>
<div>{"ignored"}</div>
</ExampleContextProvider>
<UseContextComponentInner />
</ExampleContextProvider>
</ExampleContextProvider>
<ExampleContextProvider context={ExampleContext("wrong3".into())}>
<div>{"ignored"}</div>
</ExampleContextProvider>
<ExpectNoContextComponent />
</div>
};
}
}
impl FunctionProvider for UseContextFunctionInner {
type TProps = ();
fn run(_props: &Self::TProps) -> Html {
let context = use_context::<ExampleContext>();
return html! {
<div id="result">{ &context.unwrap().0 }</div>
};
}
}
yew::start_app_in_element::<UseContextComponent>(
gloo_utils::document().get_element_by_id("output").unwrap(),
);
let result: String = obtain_result_by_id("result");
assert_eq!("correct", result);
}
#[wasm_bindgen_test]
fn use_context_works_with_multiple_types() {
#[derive(Clone, Debug, PartialEq)]
struct ContextA(u32);
#[derive(Clone, Debug, PartialEq)]
struct ContextB(u32);
struct Test1Function;
impl FunctionProvider for Test1Function {
type TProps = ();
fn run(_props: &Self::TProps) -> Html {
assert_eq!(use_context::<ContextA>(), Some(ContextA(2)));
assert_eq!(use_context::<ContextB>(), Some(ContextB(1)));
return html! {};
}
}
type Test1 = FunctionComponent<Test1Function>;
struct Test2Function;
impl FunctionProvider for Test2Function {
type TProps = ();
fn run(_props: &Self::TProps) -> Html {
assert_eq!(use_context::<ContextA>(), Some(ContextA(0)));
assert_eq!(use_context::<ContextB>(), Some(ContextB(1)));
return html! {};
}
}
type Test2 = FunctionComponent<Test2Function>;
struct Test3Function;
impl FunctionProvider for Test3Function {
type TProps = ();
fn run(_props: &Self::TProps) -> Html {
assert_eq!(use_context::<ContextA>(), Some(ContextA(0)));
assert_eq!(use_context::<ContextB>(), None);
return html! {};
}
}
type Test3 = FunctionComponent<Test3Function>;
struct Test4Function;
impl FunctionProvider for Test4Function {
type TProps = ();
fn run(_props: &Self::TProps) -> Html {
assert_eq!(use_context::<ContextA>(), None);
assert_eq!(use_context::<ContextB>(), None);
return html! {};
}
}
type Test4 = FunctionComponent<Test4Function>;
struct TestFunction;
impl FunctionProvider for TestFunction {
type TProps = ();
fn run(_props: &Self::TProps) -> Html {
type ContextAProvider = ContextProvider<ContextA>;
type ContextBProvider = ContextProvider<ContextB>;
return html! {
<div>
<ContextAProvider context={ContextA(0)}>
<ContextBProvider context={ContextB(1)}>
<ContextAProvider context={ContextA(2)}>
<Test1/>
</ContextAProvider>
<Test2/>
</ContextBProvider>
<Test3/>
</ContextAProvider>
<Test4 />
</div>
};
}
}
type TestComponent = FunctionComponent<TestFunction>;
yew::start_app_in_element::<TestComponent>(
gloo_utils::document().get_element_by_id("output").unwrap(),
);
}
#[wasm_bindgen_test]
fn use_context_update_works() {
#[derive(Clone, Debug, PartialEq)]
struct MyContext(String);
#[derive(Clone, Debug, PartialEq, Properties)]
struct RenderCounterProps {
id: String,
children: Children,
}
struct RenderCounterFunction;
impl FunctionProvider for RenderCounterFunction {
type TProps = RenderCounterProps;
fn run(props: &Self::TProps) -> Html {
let counter = use_mut_ref(|| 0);
*counter.borrow_mut() += 1;
return html! {
<>
<div id={props.id.clone()}>
{ format!("total: {}", counter.borrow()) }
</div>
{ props.children.clone() }
</>
};
}
}
type RenderCounter = FunctionComponent<RenderCounterFunction>;
#[derive(Clone, Debug, PartialEq, Properties)]
struct ContextOutletProps {
id: String,
#[prop_or_default]
magic: usize,
}
struct ContextOutletFunction;
impl FunctionProvider for ContextOutletFunction {
type TProps = ContextOutletProps;
fn run(props: &Self::TProps) -> Html {
let counter = use_mut_ref(|| 0);
*counter.borrow_mut() += 1;
let ctx = use_context::<Rc<MyContext>>().expect("context not passed down");
return html! {
<>
<div>{ format!("magic: {}\n", props.magic) }</div>
<div id={props.id.clone()}>
{ format!("current: {}, total: {}", ctx.0, counter.borrow()) }
</div>
</>
};
}
}
type ContextOutlet = FunctionComponent<ContextOutletFunction>;
struct TestFunction;
impl FunctionProvider for TestFunction {
type TProps = ();
fn run(_props: &Self::TProps) -> Html {
type MyContextProvider = ContextProvider<Rc<MyContext>>;
let ctx = use_state(|| MyContext("hello".into()));
let rendered = use_mut_ref(|| 0);
let magic_rc = use_state(|| 0);
let magic: usize = *magic_rc;
{
let ctx = ctx.clone();
use_effect(move || {
let count = *rendered.borrow();
match count {
0 => {
ctx.set(MyContext("world".into()));
*rendered.borrow_mut() += 1;
}
1 => {
magic_rc.set(1);
*rendered.borrow_mut() += 1;
}
2 => {
ctx.set(MyContext("hello world!".into()));
*rendered.borrow_mut() += 1;
}
_ => (),
};
|| {}
});
}
return html! {
<MyContextProvider context={Rc::new((*ctx).clone())}>
<RenderCounter id="test-0">
<ContextOutlet id="test-1"/>
<ContextOutlet id="test-2" {magic}/>
</RenderCounter>
</MyContextProvider>
};
}
}
type TestComponent = FunctionComponent<TestFunction>;
yew::start_app_in_element::<TestComponent>(
gloo_utils::document().get_element_by_id("output").unwrap(),
);
assert_eq!(obtain_result_by_id("test-0"), "total: 4");
assert_eq!(
obtain_result_by_id("test-1"),
"current: hello world!, total: 3"
);
assert_eq!(
obtain_result_by_id("test-2"),
"current: hello world!, total: 4"
);
}