#![cfg(target_arch = "wasm32")]
use dioxus::prelude::*;
use gloo_timers::future::TimeoutFuture;
use taino_edit_core::{Attrs, Node, NodeSpec, Schema, SchemaBuilder};
use taino_edit_dioxus::{EditorState, TainoEditor, ViewPlugins};
use taino_edit_extensions::{build_schema_with, Paragraph, Table};
use taino_edit_table_view::TableView;
use wasm_bindgen::JsCast;
use wasm_bindgen_test::*;
wasm_bindgen_test_configure!(run_in_browser);
async fn settle() {
for _ in 0..12 {
TimeoutFuture::new(8).await;
}
}
fn schema() -> Schema {
let base = SchemaBuilder::new()
.node(
"doc",
NodeSpec {
content: Some("block+".into()),
..Default::default()
},
)
.node(
"text",
NodeSpec {
group: Some("inline".into()),
..Default::default()
},
);
build_schema_with(base, &[&Paragraph, &Table], "doc").unwrap()
}
fn paragraph_doc(s: &Schema) -> Node {
let txt = s.text("Hello Dioxus", vec![]).unwrap();
let p = s
.node("paragraph", Default::default(), vec![txt], vec![])
.unwrap();
s.node("doc", Default::default(), vec![p], vec![]).unwrap()
}
fn cell(s: &Schema, t: &str) -> Node {
let txt = s.text(t, vec![]).unwrap();
let p = s
.node("paragraph", Default::default(), vec![txt], vec![])
.unwrap();
s.node("table_cell", Attrs::new(), vec![p], vec![]).unwrap()
}
fn table_doc(s: &Schema) -> Node {
let row = s
.node(
"table_row",
Default::default(),
vec![cell(s, "a"), cell(s, "b")],
vec![],
)
.unwrap();
let table = s
.node("table", Default::default(), vec![row], vec![])
.unwrap();
s.node("doc", Default::default(), vec![table], vec![])
.unwrap()
}
fn host() -> web_sys::Element {
let document = web_sys::window().unwrap().document().unwrap();
let host = document.create_element("div").unwrap();
document.body().unwrap().append_child(&host).unwrap();
host
}
fn launch(app: fn() -> Element) -> web_sys::Element {
let host = host();
let vdom = VirtualDom::new(app);
dioxus_web::launch::launch_virtual_dom(
vdom,
dioxus_web::Config::new().rootelement(host.clone()),
);
host
}
#[component]
fn ParagraphApp() -> Element {
let state = use_signal(|| {
let s = schema();
EditorState::new(paragraph_doc(&s), s)
});
rsx! { TainoEditor { state } }
}
#[component]
fn TableApp() -> Element {
let state = use_signal(|| {
let s = schema();
EditorState::new(table_doc(&s), s)
});
rsx! {
TainoEditor {
state,
plugins: ViewPlugins::new(vec![Box::new(TableView::new())]),
}
}
}
#[wasm_bindgen_test]
async fn component_mounts_initial_document() {
let host = launch(ParagraphApp);
settle().await;
let inner = host.inner_html();
assert!(
inner.contains("<p>Hello Dioxus</p>"),
"expected mounted paragraph, got: {inner}"
);
let editor = host
.query_selector(".taino-editor")
.unwrap()
.expect("editor div mounted");
assert_eq!(
editor.get_attribute("contenteditable").as_deref(),
Some("true"),
"TainoEditor must enable contenteditable"
);
}
#[wasm_bindgen_test]
async fn drag_extension_after_selection_mirror_is_not_clipped() {
let host = launch(ParagraphApp);
settle().await;
let editor: web_sys::Element = host
.query_selector(".taino-editor")
.unwrap()
.expect("editor div mounted")
.dyn_into()
.unwrap();
let _ = editor.unchecked_ref::<web_sys::HtmlElement>().focus();
let text: web_sys::Node = editor
.query_selector("p")
.unwrap()
.expect("paragraph mounted")
.first_child()
.expect("text node");
let dom_sel = web_sys::window().unwrap().get_selection().unwrap().unwrap();
dom_sel.set_base_and_extent(&text, 0, &text, 5).unwrap();
let document = web_sys::window().unwrap().document().unwrap();
let ev = web_sys::Event::new("selectionchange").unwrap();
let _ = document.dispatch_event(&ev);
dom_sel.set_base_and_extent(&text, 0, &text, 12).unwrap();
settle().await;
let dom_sel = web_sys::window().unwrap().get_selection().unwrap().unwrap();
assert_eq!(
dom_sel.focus_offset(),
12,
"the effect must not clip a selection the user extended after the mirror"
);
}
#[wasm_bindgen_test]
async fn component_with_table_view_plugin_renders_the_table() {
let host = launch(TableApp);
settle().await;
let inner = host.inner_html();
assert!(
inner.contains("<table>"),
"expected a rendered table, got: {inner}"
);
let cells = host.query_selector_all("td").unwrap().length();
assert_eq!(cells, 2, "the 1×2 table should render two cells: {inner}");
let editor: web_sys::Element = host
.query_selector(".taino-editor")
.unwrap()
.expect("editor div mounted")
.dyn_into()
.unwrap();
assert!(editor.has_attribute("contenteditable"));
}