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}