3 releases (breaking)
0.3.0 | Jan 4, 2024 |
---|---|
0.2.0 | Mar 8, 2022 |
0.1.0 | Mar 6, 2022 |
#531 in WebAssembly
43KB
457 lines
Frontest
A lightweight library to query and assert DOM.
Frontest is heavily inspired by dom-testing-library and react-testing-library. It provides a set of queries that you can use to quickly find your elements in document with respect to accessibility priorities.
Example:
use frontest::prelude::*;
use gloo::utils::{body, document};
let div = document().create_element("div").unwrap();
div.set_inner_html(
r#"<div>
<label>
I will start testing my frontend!
<button>
Take the red pill
</button>
</label>
<label>
It's too problematic dude...
<button>
Take the blue pill
</button>
</label>
</div>"#,
);
body().append_child(&div).unwrap();
let go_to_matrix = div
.get(&HasRole("button").and(Not(HasLabel("It's too problematic dude..."))))
.unwrap();
go_to_matrix.click();
body().remove_child(&div).unwrap();
About testing:
This library aims to allow developers to test their application in a way that a user would interact with it. For this purpose it is recommended to prioritize certain queries above another. Currently only two matchers are implemented. More will be available in future releases. Matchers should be prioritized like so:
HasRole
Should always be used where possible. It allows accessing elements that are exposed into accessibility tree.HasLabel
Also should be used where possible. Is supported by screen readers and allows for easier focusing elements.HasPlaceholder
Not as great option as predecessors, however still a better alternative thanHasText
for accessible elements.HasText
Can be used to select non-interactive components or further restrict other queries.
Matchers:
Matchers are predicates for HtmlElement
. They return true
if given element suffices some criteria
or false
otherwise.
Using the matcher Not
and methods from Joinable
trait it is possible to combine multiple matchers into
a logical expression.
You can easily implement your own Matcher
s.
struct IsHidden;
impl Matcher for IsHidden {
fn matches(&self, elem: &HtmlElement) -> bool {
elem.hidden()
}
}
let div = document().create_element("div").unwrap();
div.set_inner_html(
r#"<button hidden>
Yayyy frontend in rust!
</button>"#
);
div.append_child(&div).unwrap();
assert!(div.get(&IsHidden).is_some());
body().remove_child(&div).unwrap();
Integration:
Tests should be run using wasm-bindgen-test. It allows running them directly in browsers or in node-js.
Currently this crate provides a render
function that allows for quickly rendering any html
created with yew
.
It was choosen to render the html instead of directly taking a component so it is easier to wrap them with ContextProvider
and so on.
Example:
#[function_component(Incrementable)]
fn incrementable() -> Html {
let counter = use_state(|| 0);
let onclick = {
let counter = counter.clone();
Callback::from(move |_| counter.set(*counter + 1))
};
html! {
<div>
<p>{ format!("Value: {}", *counter) }</p>
<button {onclick}>{ "Add" }</button>
</div>
}
}
use frontest::prelude::*;
use frontest::yew::render;
#[wasm_bindgen_test]
async fn clicking_on_button_should_increment_value() {
let mount = render(html! { <Incrementable /> }).await;
let value = mount.get(&HasText("Value:")).unwrap();
let button = mount.get(&HasRole("button")).unwrap();
assert_eq!("Value: 0", value.inner_text());
button.click();
assert_eq!("Value: 1", value.inner_text());
body().remove_child(&mount).unwrap();
}
Warning:
wasm-bindgen-test
runs all tests sequentially and let them manipulate real DOM.
However it doesn't recreate full DOM for each test, so things done in one test may impact others.
Always make sure you are doing a proper cleanup of DOM after your tests eg. remove mounted child element.
Hopefully in future this library will provide some kind of RAII for running tests.
Dependencies
~13–21MB
~298K SLoC