dominator_testing/
barrier_testing.rs

1use crate::DominatorTestingError;
2use std::fmt::{Debug, Formatter};
3use std::time::Duration;
4use web_sys::NodeList;
5
6pub enum Condition {
7    AtLeastCount(u32),
8    AtMostCount(u32),
9    Fn(Box<dyn Fn(NodeList) -> bool>),
10}
11
12impl Debug for Condition {
13    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
14        match self {
15            Condition::AtLeastCount(count) => write!(f, "AtLeastCount({})", count),
16            Condition::AtMostCount(count) => write!(f, "AtMostCount({})", count),
17            Condition::Fn(_) => write!(f, "Fn"),
18        }
19    }
20}
21
22/// Waits for a certain condition to be true for the given query, or times out.
23///
24/// # Example:
25/// ```rust no_compile,ignore
26/// async fn my_test() {
27///     // my_button.click() should trigger an asynchronous action that eventually results
28///     // in the expected dom structure
29///     wait_for_query_selector_condition(".my-class", Condition::AtLeastCount(1), Duration::from_millis(200).await.unwrap();
30///     wait_for_query_selector_condition(".my-class", Condition::Fn(Box::new(|node_list: NodeList| node_list.len() > 0)), Duration::from_millis(200).await.unwrap();
31/// }
32/// ```
33pub async fn wait_for_query_selector_all_condition(
34    query: &str,
35    condition: Condition,
36    timeout: Duration,
37) -> Result<(), DominatorTestingError> {
38    barrier(
39        move || {
40            let elements = web_sys::window()
41                .unwrap()
42                .document()
43                .unwrap()
44                .query_selector_all(query)
45                .unwrap();
46
47            match &condition {
48                Condition::AtLeastCount(count) => elements.length() >= *count,
49                Condition::AtMostCount(count) => elements.length() <= *count,
50                Condition::Fn(fn_) => fn_(elements),
51            }
52        },
53        timeout,
54        format!("query: {query}"),
55    )
56    .await
57}
58
59/// Utility for asynchronously yielding while waiting for a condition to be met.
60/// Supports timeout
61pub async fn barrier(
62    mut expr: impl FnMut() -> bool,
63    timeout: Duration,
64    label: impl ToString,
65) -> Result<(), DominatorTestingError> {
66    let started_at = web_time::Instant::now();
67
68    loop {
69        crate::async_yield().await;
70
71        if expr() {
72            break Ok(());
73        }
74
75        if started_at.elapsed() > timeout {
76            break Err(DominatorTestingError::BarrierTimeOut(label.to_string()));
77        }
78    }
79}