update wasm-bindgen testing approaches

This commit is contained in:
Greg Johnston 2024-06-02 13:51:47 -04:00
parent 88ab9693db
commit c360f0ed0d
6 changed files with 79 additions and 53 deletions

View File

@ -18,3 +18,4 @@ console_error_panic_hook = "0.1.7"
wasm-bindgen = "0.2"
wasm-bindgen-test = "0.3.0"
web-sys = "0.3"
gloo-timers = { version = "0.3", features = ["futures"] }

View File

@ -1,19 +1,21 @@
use counter::*;
use leptos::*;
use gloo_timers::future::TimeoutFuture;
use leptos::mount::mount_to;
use leptos::prelude::*;
use wasm_bindgen::JsCast;
use wasm_bindgen_test::*;
wasm_bindgen_test_configure!(run_in_browser);
#[wasm_bindgen_test]
fn clear() {
let document = leptos::document();
async fn clear() {
let document = document();
let test_wrapper = document.create_element("section").unwrap();
let _ = document.body().unwrap().append_child(&test_wrapper);
// start by rendering our counter and mounting it to the DOM
// note that we start at the initial value of 10
mount_to(
let _dispose = mount_to(
test_wrapper.clone().unchecked_into(),
|| view! { <SimpleCounter initial_value=10 step=1/> },
);
@ -30,59 +32,63 @@ fn clear() {
// now let's click the `clear` button
clear.click();
// the reactive system is built on top of the async system, so changes are not reflected
// synchronously in the DOM
// in order to detect the changes here, we'll just yield for a brief time after each change,
// allowing the effects that update the view to run
TimeoutFuture::new(10).await;
// now let's test the <div> against the expected value
// we can do this by testing its `outerHTML`
let runtime = create_runtime();
assert_eq!(
div.outer_html(),
// here we spawn a mini reactive system, just to render the
// test case
{
// it's as if we're creating it with a value of 0, right?
let (value, _set_value) = create_signal(0);
assert_eq!(div.outer_html(), {
// it's as if we're creating it with a value of 0, right?
let (value, _set_value) = signal(0);
// we can remove the event listeners because they're not rendered to HTML
view! {
<div>
<button>"Clear"</button>
<button>"-1"</button>
<span>"Value: " {value} "!"</span>
<button>"+1"</button>
</div>
}
// the view returned an HtmlElement<Div>, which is a smart pointer for
// a DOM element. So we can still just call .outer_html()
.outer_html()
// we can remove the event listeners because they're not rendered to HTML
view! {
<div>
<button>"Clear"</button>
<button>"-1"</button>
<span>"Value: " {value} "!"</span>
<button>"+1"</button>
</div>
}
);
// Leptos supports multiple backend renderers for HTML elements
// .into_view() here is just a convenient way of specifying "use the regular DOM renderer"
.into_view()
// views are lazy -- they describe a DOM tree but don't create it yet
// calling .build() will actually build the DOM elements
.build()
// .build() returned an ElementState, which is a smart pointer for
// a DOM element. So we can still just call .outer_html(), which access the outerHTML on
// the actual DOM element
.outer_html()
});
// There's actually an easier way to do this...
// We can just test against a <SimpleCounter/> with the initial value 0
assert_eq!(test_wrapper.inner_html(), {
let comparison_wrapper = document.create_element("section").unwrap();
leptos::mount_to(
let _dispose = mount_to(
comparison_wrapper.clone().unchecked_into(),
|| view! { <SimpleCounter initial_value=0 step=1/>},
);
comparison_wrapper.inner_html()
});
runtime.dispose();
}
#[wasm_bindgen_test]
fn inc() {
let document = leptos::document();
async fn inc() {
let document = document();
let test_wrapper = document.create_element("section").unwrap();
let _ = document.body().unwrap().append_child(&test_wrapper);
mount_to(
let _dispose = mount_to(
test_wrapper.clone().unchecked_into(),
|| view! { <SimpleCounter initial_value=0 step=1/> },
);
// You can do testing with vanilla DOM operations
let _document = leptos::document();
let div = test_wrapper.query_selector("div").unwrap().unwrap();
let clear = div
.first_child()
@ -108,6 +114,8 @@ fn inc() {
inc.click();
inc.click();
TimeoutFuture::new(10).await;
assert_eq!(text.text_content(), Some("Value: 2!".to_string()));
dec.click();
@ -115,19 +123,21 @@ fn inc() {
dec.click();
dec.click();
TimeoutFuture::new(10).await;
assert_eq!(text.text_content(), Some("Value: -2!".to_string()));
clear.click();
assert_eq!(text.text_content(), Some("Value: 0!".to_string()));
TimeoutFuture::new(10).await;
let runtime = create_runtime();
assert_eq!(text.text_content(), Some("Value: 0!".to_string()));
// Or you can test against a sample view!
assert_eq!(
div.outer_html(),
{
let (value, _) = create_signal(0);
let (value, _) = signal(0);
view! {
<div>
<button>"Clear"</button>
@ -137,16 +147,20 @@ fn inc() {
</div>
}
}
.into_view()
.build()
.outer_html()
);
inc.click();
TimeoutFuture::new(10).await;
assert_eq!(
div.outer_html(),
{
// because we've clicked, it's as if the signal is starting at 1
let (value, _) = create_signal(1);
let (value, _) = signal(1);
view! {
<div>
<button>"Clear"</button>
@ -156,8 +170,8 @@ fn inc() {
</div>
}
}
.into_view()
.build()
.outer_html()
);
runtime.dispose();
}

View File

@ -10,8 +10,6 @@ lto = true
[dependencies]
leptos = { path = "../../leptos" }
console_log = "1"
log = "0.4"
console_error_panic_hook = "0.1.7"
[dev-dependencies]
@ -19,6 +17,7 @@ wasm-bindgen = "0.2"
wasm-bindgen-test = "0.3.34"
pretty_assertions = "1.3.0"
rstest = "0.17.0"
gloo-timers = { version = "0.3", features = ["futures"] }
[dev-dependencies.web-sys]
features = ["HtmlElement", "XPathResult"]

View File

@ -1,9 +1,7 @@
use counter_without_macros::counter;
use leptos::*;
/// Show the counter
pub fn main() {
_ = console_log::init_with_level(log::Level::Debug);
console_error_panic_hook::set_once();
mount::mount_to_body(|| counter(0, 1))
leptos::mount::mount_to_body(|| counter(0, 1))
}

View File

@ -1,5 +1,6 @@
use counter_without_macros::counter;
use leptos::*;
use gloo_timers::future::TimeoutFuture;
use leptos::prelude::*;
use pretty_assertions::assert_eq;
use wasm_bindgen::JsCast;
use wasm_bindgen_test::*;
@ -8,27 +9,32 @@ use web_sys::HtmlElement;
wasm_bindgen_test_configure!(run_in_browser);
#[wasm_bindgen_test]
fn should_increment_counter() {
async fn should_increment_counter() {
open_counter();
click_increment();
click_increment();
// reactive changes run asynchronously, so yield briefly before observing the DOM
TimeoutFuture::new(10).await;
assert_eq!(see_text(), Some("Value: 2!".to_string()));
}
#[wasm_bindgen_test]
fn should_decrement_counter() {
async fn should_decrement_counter() {
open_counter();
click_decrement();
click_decrement();
TimeoutFuture::new(10).await;
assert_eq!(see_text(), Some("Value: -2!".to_string()));
}
#[wasm_bindgen_test]
fn should_clear_counter() {
async fn should_clear_counter() {
open_counter();
click_increment();
@ -36,18 +42,18 @@ fn should_clear_counter() {
click_clear();
TimeoutFuture::new(10).await;
assert_eq!(see_text(), Some("Value: 0!".to_string()));
}
fn open_counter() {
remove_existing_counter();
mount_to_body(move || counter(0, 1));
leptos::mount::mount_to_body(move || counter(0, 1));
}
fn remove_existing_counter() {
if let Some(counter) =
leptos::document().query_selector("body div").unwrap()
{
if let Some(counter) = document().query_selector("body div").unwrap() {
counter.remove();
}
}
@ -74,7 +80,7 @@ fn see_text() -> Option<String> {
fn find_by_text(text: &str) -> HtmlElement {
let xpath = format!("//*[text()='{}']", text);
let document = leptos::document();
let document = document();
document
.evaluate(&xpath, &document)
.unwrap()

View File

@ -12,7 +12,7 @@ use const_str_slice_concat::{
const_concat, const_concat_with_prefix, str_from_buffer,
};
use next_tuple::NextTuple;
use std::marker::PhantomData;
use std::{marker::PhantomData, ops::Deref};
mod custom;
mod elements;
@ -413,6 +413,14 @@ pub struct ElementState<At, Ch, R: Renderer> {
rndr: PhantomData<R>,
}
impl<At, Ch, R: Renderer> Deref for ElementState<At, Ch, R> {
type Target = R::Element;
fn deref(&self) -> &Self::Target {
&self.el
}
}
impl<At, Ch, R> Mountable<R> for ElementState<At, Ch, R>
where
R: Renderer,