perspective_viewer/js/testing.rs
1// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
2// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
3// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
4// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
5// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
6// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
7// ┃ Copyright (c) 2017, the Perspective Authors. ┃
8// ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
9// ┃ This file is part of the Perspective library, distributed under the terms ┃
10// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
13#[cfg(test)]
14use wasm_bindgen::prelude::*;
15
16// Must use inline build because the test runner does not import itself in
17// the browser with `type=module` which causes `import.meta` calls to fail,
18// and `currentScript` does not resolve dynamic imports so the polyfill
19// for `import.meta` does not work either.
20#[cfg(test)]
21#[wasm_bindgen(inline_js = "
22
23 export async function worker() {
24 await import('/dist/wasm/test/perspective.js');
25 return window.perspective.worker();
26 }
27
28")]
29extern "C" {
30 fn worker() -> js_sys::Promise;
31}
32
33// /// Generate a test `Table`, but only create teh webworker once or the tests
34// /// will figuratively literally run forever.
35// #[cfg(test)]
36// pub async fn get_mock_table() -> JsPerspectiveTable {
37// thread_local! {
38// static WORKER: RefCell<Option<JsPerspectiveWorker>> =
39// RefCell::new(None); }
40
41// let worker: JsPerspectiveWorker = match WORKER.with(|x|
42// x.borrow().clone()) { Some(x) => x,
43// None => JsFuture::from(worker()).await.unwrap().unchecked_into(),
44// };
45
46// WORKER.with(|x| {
47// *x.borrow_mut() = Some(worker.clone());
48// });
49
50// worker
51// .table(
52// json!({
53// "A": [1, 2, 3]
54// })
55// .unchecked_into(),
56// )
57// .await
58// .unwrap()
59// }
60
61/// A macro which set a property called `weak_link` on the container
62/// `Properties` when `cfg(test)`, such that unit tests may send messages to a
63/// component.
64///
65/// This macro needs to be called in `create()` on any Component which needs to
66/// receive messages in a test.
67#[macro_export]
68macro_rules! enable_weak_link_test {
69 ($props:expr, $link:expr) => {
70 #[cfg(test)]
71 {
72 *$props.weak_link.borrow_mut() = Some($link.clone());
73 }
74 };
75}
76
77/// A macro which derives a `yew::Component` for an arbitrary HTML snippet and
78/// mounts it, for testing.
79#[macro_export]
80macro_rules! test_html {
81 ($($html:tt)*) => {{
82 use wasm_bindgen::JsCast;
83 use yew::prelude::*;
84
85 struct TestElement {}
86
87 #[derive(Properties, PartialEq)]
88 struct TestElementProps {
89 html: Html,
90 root: NodeRef,
91 }
92
93 impl Component for TestElement {
94 type Message = ();
95 type Properties = TestElementProps;
96
97 fn create(_ctx: &Context<Self>) -> Self {
98 TestElement {}
99 }
100
101 fn update(&mut self, _ctx: &Context<Self>, _msg: Self::Message) -> bool {
102 false
103 }
104
105 fn changed(&mut self, _ctx: &Context<Self>, _old: &Self::Properties) -> bool {
106 true
107 }
108
109 fn view(&self, ctx: &Context<Self>) -> Html {
110 html! {
111 <>
112 <style>
113 { "#test{position:absolute;top:0;bottom:0;left:0;right:0;}" }
114 </style>
115 <div ref={ ctx.props().root.clone() }>
116 { ctx.props().html.clone() }
117 </div>
118 </>
119 }
120 }
121 }
122
123 let document = ::perspective_js::utils::global::document();
124 let body = document.body().unwrap();
125 let div = document.create_element("div").unwrap();
126 body.append_child(&div).unwrap();
127
128 let init = web_sys::ShadowRootInit::new(web_sys::ShadowRootMode::Open);
129 let shadow_root = div
130 .attach_shadow(&init)
131 .unwrap()
132 .unchecked_into::<web_sys::Element>();
133
134 let root = NodeRef::default();
135 let props = TestElementProps {
136 html: html!{ $($html)* },
137 root: root.clone(),
138 };
139
140 yew::Renderer::<TestElement>::with_root_and_props(shadow_root, props).render();
141 request_animation_frame().await;
142 root.cast::<web_sys::HtmlElement>()
143 .unwrap()
144 .children()
145 .get_with_index(0)
146 .unwrap()
147 .unchecked_into::<web_sys::HtmlElement>()
148 }}
149}