mirror of
https://github.com/DioxusLabs/dioxus
synced 2024-11-27 14:40:44 +00:00
Merge pull request #995 from Demonthos/mock-event-tests
Mock Desktop Tests
This commit is contained in:
commit
7a620daad7
4 changed files with 461 additions and 3 deletions
|
@ -42,10 +42,10 @@ private = true
|
|||
[tasks.test]
|
||||
dependencies = ["build"]
|
||||
command = "cargo"
|
||||
args = ["test", "--lib", "--bins", "--tests", "--examples", "--workspace", "--exclude", "dioxus-router"]
|
||||
args = ["test", "--lib", "--bins", "--tests", "--examples", "--workspace", "--exclude", "dioxus-router", "--exclude", "dioxus-desktop"]
|
||||
private = true
|
||||
|
||||
[tasks.test-with-browser]
|
||||
env = { CARGO_MAKE_WORKSPACE_INCLUDE_MEMBERS = ["**/packages/router"] }
|
||||
env = { CARGO_MAKE_WORKSPACE_INCLUDE_MEMBERS = ["**/packages/router", "**/packages/desktop"] }
|
||||
private = true
|
||||
workspace = true
|
||||
|
|
|
@ -57,4 +57,17 @@ hot-reload = ["dioxus-hot-reload"]
|
|||
[dev-dependencies]
|
||||
dioxus-core-macro = { path = "../core-macro" }
|
||||
dioxus-hooks = { path = "../hooks" }
|
||||
# image = "0.24.0" # enable this when generating a new desktop image
|
||||
dioxus = { path = "../dioxus" }
|
||||
exitcode = "1.1.2"
|
||||
scraper = "0.16.0"
|
||||
|
||||
# These tests need to be run on the main thread, so they cannot use rust's test harness.
|
||||
[[test]]
|
||||
name = "check_events"
|
||||
path = "headless_tests/events.rs"
|
||||
harness = false
|
||||
|
||||
[[test]]
|
||||
name = "check_rendering"
|
||||
path = "headless_tests/rendering.rs"
|
||||
harness = false
|
||||
|
|
351
packages/desktop/headless_tests/events.rs
Normal file
351
packages/desktop/headless_tests/events.rs
Normal file
|
@ -0,0 +1,351 @@
|
|||
use dioxus::html::geometry::euclid::Vector3D;
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_desktop::DesktopContext;
|
||||
|
||||
pub(crate) fn check_app_exits(app: Component) {
|
||||
use dioxus_desktop::tao::window::WindowBuilder;
|
||||
use dioxus_desktop::Config;
|
||||
// This is a deadman's switch to ensure that the app exits
|
||||
let should_panic = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(true));
|
||||
let should_panic_clone = should_panic.clone();
|
||||
std::thread::spawn(move || {
|
||||
std::thread::sleep(std::time::Duration::from_secs(100));
|
||||
if should_panic_clone.load(std::sync::atomic::Ordering::SeqCst) {
|
||||
std::process::exit(exitcode::SOFTWARE);
|
||||
}
|
||||
});
|
||||
|
||||
dioxus_desktop::launch_cfg(
|
||||
app,
|
||||
Config::new().with_window(WindowBuilder::new().with_visible(false)),
|
||||
);
|
||||
|
||||
should_panic.store(false, std::sync::atomic::Ordering::SeqCst);
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
check_app_exits(app);
|
||||
}
|
||||
|
||||
fn mock_event(cx: &ScopeState, id: &'static str, value: &'static str) {
|
||||
use_effect(cx, (), |_| {
|
||||
let desktop_context: DesktopContext = cx.consume_context().unwrap();
|
||||
async move {
|
||||
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
|
||||
desktop_context.eval(&format!(
|
||||
r#"let element = document.getElementById('{}');
|
||||
// Dispatch a synthetic event
|
||||
const event = {};
|
||||
console.log(element, event);
|
||||
element.dispatchEvent(event);
|
||||
"#,
|
||||
id, value
|
||||
));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
fn app(cx: Scope) -> Element {
|
||||
let desktop_context: DesktopContext = cx.consume_context().unwrap();
|
||||
let recieved_events = use_state(cx, || 0);
|
||||
|
||||
// button
|
||||
mock_event(
|
||||
cx,
|
||||
"button",
|
||||
r#"new MouseEvent("click", {
|
||||
view: window,
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
button: 0,
|
||||
})"#,
|
||||
);
|
||||
// mouse_move_div
|
||||
mock_event(
|
||||
cx,
|
||||
"mouse_move_div",
|
||||
r#"new MouseEvent("mousemove", {
|
||||
view: window,
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
buttons: 2,
|
||||
})"#,
|
||||
);
|
||||
// mouse_click_div
|
||||
mock_event(
|
||||
cx,
|
||||
"mouse_click_div",
|
||||
r#"new MouseEvent("click", {
|
||||
view: window,
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
buttons: 2,
|
||||
button: 2,
|
||||
})"#,
|
||||
);
|
||||
// mouse_dblclick_div
|
||||
mock_event(
|
||||
cx,
|
||||
"mouse_dblclick_div",
|
||||
r#"new MouseEvent("dblclick", {
|
||||
view: window,
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
buttons: 1|2,
|
||||
button: 2,
|
||||
})"#,
|
||||
);
|
||||
// mouse_down_div
|
||||
mock_event(
|
||||
cx,
|
||||
"mouse_down_div",
|
||||
r#"new MouseEvent("mousedown", {
|
||||
view: window,
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
buttons: 2,
|
||||
button: 2,
|
||||
})"#,
|
||||
);
|
||||
// mouse_up_div
|
||||
mock_event(
|
||||
cx,
|
||||
"mouse_up_div",
|
||||
r#"new MouseEvent("mouseup", {
|
||||
view: window,
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
buttons: 0,
|
||||
button: 0,
|
||||
})"#,
|
||||
);
|
||||
// wheel_div
|
||||
mock_event(
|
||||
cx,
|
||||
"wheel_div",
|
||||
r#"new WheelEvent("wheel", {
|
||||
view: window,
|
||||
deltaX: 1.0,
|
||||
deltaY: 2.0,
|
||||
deltaZ: 3.0,
|
||||
deltaMode: 0x00,
|
||||
bubbles: true,
|
||||
})"#,
|
||||
);
|
||||
// key_down_div
|
||||
mock_event(
|
||||
cx,
|
||||
"key_down_div",
|
||||
r#"new KeyboardEvent("keydown", {
|
||||
key: "a",
|
||||
code: "KeyA",
|
||||
location: 0,
|
||||
repeat: true,
|
||||
keyCode: 65,
|
||||
charCode: 97,
|
||||
char: "a",
|
||||
charCode: 0,
|
||||
altKey: false,
|
||||
ctrlKey: false,
|
||||
metaKey: false,
|
||||
shiftKey: false,
|
||||
isComposing: false,
|
||||
which: 65,
|
||||
bubbles: true,
|
||||
})"#,
|
||||
);
|
||||
// key_up_div
|
||||
mock_event(
|
||||
cx,
|
||||
"key_up_div",
|
||||
r#"new KeyboardEvent("keyup", {
|
||||
key: "a",
|
||||
code: "KeyA",
|
||||
location: 0,
|
||||
repeat: false,
|
||||
keyCode: 65,
|
||||
charCode: 97,
|
||||
char: "a",
|
||||
charCode: 0,
|
||||
altKey: false,
|
||||
ctrlKey: false,
|
||||
metaKey: false,
|
||||
shiftKey: false,
|
||||
isComposing: false,
|
||||
which: 65,
|
||||
bubbles: true,
|
||||
})"#,
|
||||
);
|
||||
// key_press_div
|
||||
mock_event(
|
||||
cx,
|
||||
"key_press_div",
|
||||
r#"new KeyboardEvent("keypress", {
|
||||
key: "a",
|
||||
code: "KeyA",
|
||||
location: 0,
|
||||
repeat: false,
|
||||
keyCode: 65,
|
||||
charCode: 97,
|
||||
char: "a",
|
||||
charCode: 0,
|
||||
altKey: false,
|
||||
ctrlKey: false,
|
||||
metaKey: false,
|
||||
shiftKey: false,
|
||||
isComposing: false,
|
||||
which: 65,
|
||||
bubbles: true,
|
||||
})"#,
|
||||
);
|
||||
// focus_in_div
|
||||
mock_event(
|
||||
cx,
|
||||
"focus_in_div",
|
||||
r#"new FocusEvent("focusin", {bubbles: true})"#,
|
||||
);
|
||||
// focus_out_div
|
||||
mock_event(
|
||||
cx,
|
||||
"focus_out_div",
|
||||
r#"new FocusEvent("focusout",{bubbles: true})"#,
|
||||
);
|
||||
|
||||
if **recieved_events == 12 {
|
||||
println!("all events recieved");
|
||||
desktop_context.close();
|
||||
}
|
||||
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
button {
|
||||
id: "button",
|
||||
onclick: move |event| {
|
||||
println!("{:?}", event.data);
|
||||
assert!(event.data.modifiers().is_empty());
|
||||
assert!(event.data.held_buttons().is_empty());
|
||||
assert_eq!(event.data.trigger_button(), Some(dioxus_html::input_data::MouseButton::Primary));
|
||||
recieved_events.modify(|x| *x + 1)
|
||||
},
|
||||
}
|
||||
div {
|
||||
id: "mouse_move_div",
|
||||
onmousemove: move |event| {
|
||||
println!("{:?}", event.data);
|
||||
assert!(event.data.modifiers().is_empty());
|
||||
assert!(event.data.held_buttons().contains(dioxus_html::input_data::MouseButton::Secondary));
|
||||
recieved_events.modify(|x| *x + 1)
|
||||
},
|
||||
}
|
||||
div {
|
||||
id: "mouse_click_div",
|
||||
onclick: move |event| {
|
||||
println!("{:?}", event.data);
|
||||
assert!(event.data.modifiers().is_empty());
|
||||
assert!(event.data.held_buttons().contains(dioxus_html::input_data::MouseButton::Secondary));
|
||||
assert_eq!(event.data.trigger_button(), Some(dioxus_html::input_data::MouseButton::Secondary));
|
||||
recieved_events.modify(|x| *x + 1)
|
||||
},
|
||||
}
|
||||
div{
|
||||
id: "mouse_dblclick_div",
|
||||
ondblclick: move |event| {
|
||||
println!("{:?}", event.data);
|
||||
assert!(event.data.modifiers().is_empty());
|
||||
assert!(event.data.held_buttons().contains(dioxus_html::input_data::MouseButton::Primary));
|
||||
assert!(event.data.held_buttons().contains(dioxus_html::input_data::MouseButton::Secondary));
|
||||
assert_eq!(event.data.trigger_button(), Some(dioxus_html::input_data::MouseButton::Secondary));
|
||||
recieved_events.modify(|x| *x + 1)
|
||||
}
|
||||
}
|
||||
div{
|
||||
id: "mouse_down_div",
|
||||
onmousedown: move |event| {
|
||||
println!("{:?}", event.data);
|
||||
assert!(event.data.modifiers().is_empty());
|
||||
assert!(event.data.held_buttons().contains(dioxus_html::input_data::MouseButton::Secondary));
|
||||
assert_eq!(event.data.trigger_button(), Some(dioxus_html::input_data::MouseButton::Secondary));
|
||||
recieved_events.modify(|x| *x + 1)
|
||||
}
|
||||
}
|
||||
div{
|
||||
id: "mouse_up_div",
|
||||
onmouseup: move |event| {
|
||||
println!("{:?}", event.data);
|
||||
assert!(event.data.modifiers().is_empty());
|
||||
assert!(event.data.held_buttons().is_empty());
|
||||
assert_eq!(event.data.trigger_button(), Some(dioxus_html::input_data::MouseButton::Primary));
|
||||
recieved_events.modify(|x| *x + 1)
|
||||
}
|
||||
}
|
||||
div{
|
||||
id: "wheel_div",
|
||||
width: "100px",
|
||||
height: "100px",
|
||||
background_color: "red",
|
||||
onwheel: move |event| {
|
||||
println!("{:?}", event.data);
|
||||
let dioxus_html::geometry::WheelDelta::Pixels(delta)= event.data.delta()else{
|
||||
panic!("Expected delta to be in pixels")
|
||||
};
|
||||
assert_eq!(delta, Vector3D::new(1.0, 2.0, 3.0));
|
||||
recieved_events.modify(|x| *x + 1)
|
||||
}
|
||||
}
|
||||
input{
|
||||
id: "key_down_div",
|
||||
onkeydown: move |event| {
|
||||
println!("{:?}", event.data);
|
||||
assert!(event.data.modifiers().is_empty());
|
||||
assert_eq!(event.data.key().to_string(), "a");
|
||||
assert_eq!(event.data.code().to_string(), "KeyA");
|
||||
assert_eq!(event.data.location, 0);
|
||||
assert!(event.data.is_auto_repeating());
|
||||
|
||||
recieved_events.modify(|x| *x + 1)
|
||||
}
|
||||
}
|
||||
input{
|
||||
id: "key_up_div",
|
||||
onkeyup: move |event| {
|
||||
println!("{:?}", event.data);
|
||||
assert!(event.data.modifiers().is_empty());
|
||||
assert_eq!(event.data.key().to_string(), "a");
|
||||
assert_eq!(event.data.code().to_string(), "KeyA");
|
||||
assert_eq!(event.data.location, 0);
|
||||
assert!(!event.data.is_auto_repeating());
|
||||
|
||||
recieved_events.modify(|x| *x + 1)
|
||||
}
|
||||
}
|
||||
input{
|
||||
id: "key_press_div",
|
||||
onkeypress: move |event| {
|
||||
println!("{:?}", event.data);
|
||||
assert!(event.data.modifiers().is_empty());
|
||||
assert_eq!(event.data.key().to_string(), "a");
|
||||
assert_eq!(event.data.code().to_string(), "KeyA");
|
||||
assert_eq!(event.data.location, 0);
|
||||
assert!(!event.data.is_auto_repeating());
|
||||
|
||||
recieved_events.modify(|x| *x + 1)
|
||||
}
|
||||
}
|
||||
input{
|
||||
id: "focus_in_div",
|
||||
onfocusin: move |event| {
|
||||
println!("{:?}", event.data);
|
||||
recieved_events.modify(|x| *x + 1)
|
||||
}
|
||||
}
|
||||
input{
|
||||
id: "focus_out_div",
|
||||
onfocusout: move |event| {
|
||||
println!("{:?}", event.data);
|
||||
recieved_events.modify(|x| *x + 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
94
packages/desktop/headless_tests/rendering.rs
Normal file
94
packages/desktop/headless_tests/rendering.rs
Normal file
|
@ -0,0 +1,94 @@
|
|||
use dioxus::prelude::*;
|
||||
use dioxus_desktop::DesktopContext;
|
||||
|
||||
pub(crate) fn check_app_exits(app: Component) {
|
||||
use dioxus_desktop::tao::window::WindowBuilder;
|
||||
use dioxus_desktop::Config;
|
||||
// This is a deadman's switch to ensure that the app exits
|
||||
let should_panic = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(true));
|
||||
let should_panic_clone = should_panic.clone();
|
||||
std::thread::spawn(move || {
|
||||
std::thread::sleep(std::time::Duration::from_secs(100));
|
||||
if should_panic_clone.load(std::sync::atomic::Ordering::SeqCst) {
|
||||
std::process::exit(exitcode::SOFTWARE);
|
||||
}
|
||||
});
|
||||
|
||||
dioxus_desktop::launch_cfg(
|
||||
app,
|
||||
Config::new().with_window(WindowBuilder::new().with_visible(false)),
|
||||
);
|
||||
|
||||
should_panic.store(false, std::sync::atomic::Ordering::SeqCst);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
check_app_exits(check_html_renders);
|
||||
}
|
||||
|
||||
fn use_inner_html(cx: &ScopeState, id: &'static str) -> Option<String> {
|
||||
let value: &UseRef<Option<String>> = use_ref(cx, || None);
|
||||
use_effect(cx, (), |_| {
|
||||
to_owned![value];
|
||||
let desktop_context: DesktopContext = cx.consume_context().unwrap();
|
||||
async move {
|
||||
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
|
||||
let html = desktop_context
|
||||
.eval(&format!(
|
||||
r#"let element = document.getElementById('{}');
|
||||
return element.innerHTML;"#,
|
||||
id
|
||||
))
|
||||
.await;
|
||||
if let Ok(serde_json::Value::String(html)) = html {
|
||||
println!("html: {}", html);
|
||||
value.set(Some(html));
|
||||
}
|
||||
}
|
||||
});
|
||||
value.read().clone()
|
||||
}
|
||||
|
||||
const EXPECTED_HTML: &str = r#"<div id="5" style="width: 100px; height: 100px; color: rgb(0, 0, 0);"><input type="checkbox"><h1>text</h1><div><p>hello world</p></div></div>"#;
|
||||
|
||||
fn check_html_renders(cx: Scope) -> Element {
|
||||
let inner_html = use_inner_html(cx, "main_div");
|
||||
let desktop_context: DesktopContext = cx.consume_context().unwrap();
|
||||
|
||||
if let Some(raw_html) = inner_html.as_deref() {
|
||||
let fragment = scraper::Html::parse_fragment(raw_html);
|
||||
println!("fragment: {:?}", fragment.html());
|
||||
let expected = scraper::Html::parse_fragment(EXPECTED_HTML);
|
||||
println!("fragment: {:?}", expected.html());
|
||||
if fragment == expected {
|
||||
println!("html matches");
|
||||
desktop_context.close();
|
||||
}
|
||||
}
|
||||
|
||||
let dyn_value = 0;
|
||||
let dyn_element = rsx! {
|
||||
div {
|
||||
dangerous_inner_html: "<p>hello world</p>",
|
||||
}
|
||||
};
|
||||
|
||||
render! {
|
||||
div {
|
||||
id: "main_div",
|
||||
div {
|
||||
width: "100px",
|
||||
height: "100px",
|
||||
color: "rgb({dyn_value}, {dyn_value}, {dyn_value})",
|
||||
id: 5,
|
||||
input {
|
||||
"type": "checkbox",
|
||||
},
|
||||
h1 {
|
||||
"text"
|
||||
}
|
||||
dyn_element
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue