1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
//! Utilities for writing tests for Silkenweb apps.
use silkenweb::{document::Document, dom::DefaultDom, task::render_now};
use silkenweb_base::document;
use wasm_bindgen::{JsCast, UnwrapThrowExt};

/// Setup a browser test.
///
/// This will cleanup the test when `drop`ed.
#[must_use]
pub struct BrowserTest;

impl BrowserTest {
    /// Setup a test.
    ///
    /// Actions:
    ///
    /// - Clear the render queue.
    /// - Check this isn't a nested browser test.
    /// - Unmount any mounted elements.
    /// - Create a new mount point with `id` = `mount_point_id`.
    pub async fn new(mount_point_id: &str) -> Self {
        // Clear the render queue
        render_now().await;

        if try_html_element(APP_CONTAINER_ID).is_some() {
            panic!("`Test` cannot be nested")
        }

        DefaultDom::unmount_all();

        let app_container = document::create_element("div");
        app_container.set_id(APP_CONTAINER_ID);

        let mount_point = document::create_element("div");
        mount_point.set_id(mount_point_id);
        app_container
            .append_child(&mount_point)
            .expect_throw("Couldn't add mount point to app container");

        let body = document::body().expect_throw("Couldn't get document body");
        body.append_child(&app_container)
            .expect_throw("Couldn't add app container to body");

        Self
    }

    /// Get the HTML of a test.
    pub fn html(&self) -> String {
        html_element(APP_CONTAINER_ID).inner_html()
    }
}

impl Drop for BrowserTest {
    fn drop(&mut self) {
        DefaultDom::unmount_all();
        html_element(APP_CONTAINER_ID).remove();
    }
}

/// Find an element by `id`
///
/// # Panics
///
/// This panics if the element is not found, or is not an [`HtmlElement`]
///
/// [`HtmlElement`]: web_sys::HtmlElement
pub fn html_element(id: &str) -> web_sys::HtmlElement {
    try_html_element(id).expect_throw("Element not found")
}

/// Find an element by `id`
///
/// This returns `None` if the element is not found.
///
/// # Panics
///
/// This panics if the element is found, and is not an [`HtmlElement`]
///
/// [`HtmlElement`]: web_sys::HtmlElement
pub fn try_html_element(id: &str) -> Option<web_sys::HtmlElement> {
    document::query_selector(&format!("#{id}"))
        .expect_throw("Error searching for element")
        .map(|elem| {
            elem.dyn_into()
                .expect_throw("Element was not an `HTMLElement")
        })
}

const APP_CONTAINER_ID: &str = "silkenweb-test-mount-point";