thirtyfour/extensions/query/
element_query.rs

1use super::conditions::{collect_arg_slice, handle_errors, negate};
2use super::{conditions, ElementPollerNoWait, ElementPollerWithTimeout, IntoElementPoller};
3use crate::error::{WebDriverError, WebDriverErrorInner};
4use crate::prelude::WebDriverResult;
5use crate::session::handle::SessionHandle;
6use crate::IntoArcStr;
7use crate::{By, DynElementPredicate, ElementPredicate, WebElement};
8use indexmap::IndexMap;
9use std::borrow::Cow;
10use std::fmt::{Debug, Display, Formatter, Write};
11use std::sync::Arc;
12use std::time::Duration;
13use stringmatch::Needle;
14
15/// Get String containing comma-separated list of selectors used.
16fn get_selector_summary(selectors: &[ElementSelector]) -> String {
17    struct Criteria<'a>(&'a [ElementSelector]);
18
19    impl Display for Criteria<'_> {
20        fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
21            for (i, by) in self.0.iter().map(|s| &s.by).enumerate() {
22                if i != 0 {
23                    f.write_char(',')?
24                }
25                Display::fmt(by, f)?;
26            }
27            Ok(())
28        }
29    }
30
31    format!("[{}]", Criteria(selectors))
32}
33
34/// Helper function to return the NoSuchElement error struct.
35fn no_such_element(selectors: &[ElementSelector], description: &str) -> WebDriverError {
36    let element_description: Cow<str> = if description.is_empty() {
37        "element(s)".into()
38    } else {
39        format!("'{description}' element(s)").into()
40    };
41
42    crate::error::no_such_element(format!(
43        "no such element: {element_description} not found using selectors: {}",
44        get_selector_summary(selectors)
45    ))
46}
47
48/// Filter the specified elements using the specified filters.
49pub async fn filter_elements<'a, I, P, Ref>(
50    mut elements: Vec<WebElement>,
51    filters: I,
52) -> WebDriverResult<Vec<WebElement>>
53where
54    I: IntoIterator<Item = Ref>,
55    Ref: AsRef<P>,
56    P: ElementPredicate + ?Sized,
57{
58    for func in filters {
59        let tmp_elements = std::mem::take(&mut elements);
60        for element in tmp_elements {
61            if func.as_ref().call(element.clone()).await? {
62                elements.push(element);
63            }
64        }
65
66        if elements.is_empty() {
67            break;
68        }
69    }
70
71    Ok(elements)
72}
73
74/// An ElementSelector contains a selector method (By) as well as zero or more filters.
75/// The filters will be applied to any elements matched by the selector.
76/// Selectors and filters all run in full on every poll iteration.
77pub struct ElementSelector {
78    /// The selector to use.
79    pub by: By,
80    /// The filters for this element selector.
81    pub filters: Vec<Box<DynElementPredicate>>,
82}
83
84impl Debug for ElementSelector {
85    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
86        f.debug_struct("ElementSelector").field("by", &self.by).finish()
87    }
88}
89
90impl ElementSelector {
91    /// Create a new `ElementSelector`.
92    pub fn new(by: By) -> Self {
93        Self {
94            by,
95            filters: Vec::new(),
96        }
97    }
98
99    /// Add the specified filter to the list of filters for this selector.
100    pub fn add_filter(&mut self, f: impl ElementPredicate + 'static) {
101        self.add_box_filter(DynElementPredicate::boxed(f));
102    }
103
104    /// Add the specified filter to the list of filters for this selector.
105    pub fn add_box_filter(&mut self, f: Box<DynElementPredicate>) {
106        self.filters.push(f);
107    }
108}
109
110/// Elements can be queried from either a WebDriver or from a WebElement.
111/// The command issued to the webdriver will differ depending on the source,
112/// i.e. FindElement vs FindElementFromElement etc. but the ElementQuery
113/// interface is the same for both.
114#[derive(Debug)]
115pub enum ElementQuerySource {
116    /// Execute a query from the `WebDriver` instance.
117    Driver(Arc<SessionHandle>),
118    /// Execute a query using the specified `WebElement` as the base.
119    Element(WebElement),
120}
121
122/// Options for wait characteristics for an element query.
123#[derive(Debug, Clone)]
124pub enum ElementQueryWaitOptions {
125    /// Use the default poller.
126    WaitDefault,
127    /// Use a poller with the specified timeout and interval.
128    Wait {
129        /// The timeout for this poller.
130        timeout: Duration,
131        /// The minimum interval between attempts.
132        interval: Duration,
133    },
134    /// Do not wait. This uses a poller that quits immediately.
135    NoWait,
136}
137
138impl Default for ElementQueryWaitOptions {
139    fn default() -> Self {
140        Self::WaitDefault
141    }
142}
143
144/// All options applicable to an ElementQuery.
145///
146/// These are stored in a separate struct so that they can be constructed
147/// separately and applied to an ElementQuery in bulk if required.
148#[derive(Debug, Default, Clone)]
149#[non_exhaustive]
150pub struct ElementQueryOptions {
151    ignore_errors: Option<bool>,
152    description: Option<Arc<str>>,
153    wait: Option<ElementQueryWaitOptions>,
154}
155
156impl ElementQueryOptions {
157    /// Set whether to ignore errors when querying elements.
158    pub fn ignore_errors(mut self, ignore_errors: bool) -> Self {
159        self.ignore_errors = Some(ignore_errors);
160        self
161    }
162
163    /// Set whether to ignore errors when querying elements.
164    pub fn set_ignore_errors(mut self, ignore_errors: Option<bool>) -> Self {
165        self.ignore_errors = ignore_errors;
166        self
167    }
168
169    /// Set the description to be used in error messages for this element query.
170    pub fn description(mut self, description: impl IntoArcStr) -> Self {
171        self.description = Some(description.into());
172        self
173    }
174
175    /// Set the description to be used in error messages for this element query.
176    pub fn set_description<T: Into<Arc<str>>>(mut self, description: Option<T>) -> Self {
177        self.description = description.map(|x| x.into());
178        self
179    }
180
181    /// Set the wait options for this element query.
182    pub fn wait(mut self, wait_option: ElementQueryWaitOptions) -> Self {
183        self.wait = Some(wait_option);
184        self
185    }
186
187    /// Set the wait options for this element query.
188    pub fn set_wait(mut self, wait_option: Option<ElementQueryWaitOptions>) -> Self {
189        self.wait = wait_option;
190        self
191    }
192}
193
194/// High-level interface for performing powerful element queries using a
195/// builder pattern.
196///
197/// # Example:
198/// ```no_run
199/// # use thirtyfour::prelude::*;
200/// # use thirtyfour::support::block_on;
201/// #
202/// # fn main() -> WebDriverResult<()> {
203/// #     block_on(async {
204/// #         let caps = DesiredCapabilities::chrome();
205/// #         let mut driver = WebDriver::new("http://localhost:4444", caps).await?;
206/// #         driver.goto("http://localhost:8000").await?;
207/// // WebDriver::query() example.
208/// let elem = driver.query(By::Css("div[data-section='section-buttons']")).first().await?;
209/// // WebElement::query() example.
210/// let elem_button = elem.query(By::Id("button1")).first().await?;
211/// #         assert_eq!(elem_button.tag_name().await?, "button");
212/// #         driver.quit().await?;
213/// #         Ok(())
214/// #     })
215/// # }
216/// ```
217#[derive(Debug)]
218pub struct ElementQuery {
219    source: ElementQuerySource,
220    poller: Arc<dyn IntoElementPoller + Send + Sync>,
221    selectors: Vec<ElementSelector>,
222    options: ElementQueryOptions,
223}
224
225macro_rules! disallow_empty {
226    ($elements: expr, $self: expr) => {
227        if $elements.is_empty() {
228            let desc: &str = $self.options.description.as_deref().unwrap_or("");
229            Err(no_such_element(&$self.selectors, desc))
230        } else {
231            Ok($elements)
232        }
233    };
234}
235
236impl ElementQuery {
237    /// Create a new `ElementQuery`.
238    ///
239    /// See `WebDriver::query()` or `WebElement::query()` rather than instantiating
240    /// this directly.
241    pub fn new(
242        source: ElementQuerySource,
243        by: By,
244        poller: Arc<dyn IntoElementPoller + Send + Sync>,
245    ) -> Self {
246        let selector = ElementSelector::new(by);
247        Self {
248            source,
249            poller,
250            selectors: vec![selector],
251            options: ElementQueryOptions::default(),
252        }
253    }
254
255    /// Provide the options to use with this query.
256    pub fn options(mut self, options: ElementQueryOptions) -> Self {
257        self.options = options;
258
259        // Apply wait options.
260        match self.options.wait {
261            None | Some(ElementQueryWaitOptions::WaitDefault) => self,
262            Some(ElementQueryWaitOptions::Wait {
263                timeout,
264                interval,
265            }) => self.wait(timeout, interval),
266            Some(ElementQueryWaitOptions::NoWait) => self.nowait(),
267        }
268    }
269
270    /// Provide a name that will be included in the error message if the query was not successful.
271    /// This is useful for providing more context about this particular query.
272    pub fn desc(mut self, description: &str) -> Self {
273        self.options = self.options.description(description);
274        self
275    }
276
277    /// By default, a query will ignore any errors that occur while polling for the desired
278    /// element(s).
279    /// However, this behaviour can be modified so that the waiter will return
280    /// early if an error is returned from thirtyfour.
281    pub fn ignore_errors(mut self, ignore: bool) -> Self {
282        self.options = self.options.ignore_errors(ignore);
283        self
284    }
285
286    //
287    // Poller / Waiter
288    //
289
290    /// Use the specified ElementPoller for this ElementQuery.
291    /// This will not affect the default ElementPoller used for other queries.
292    pub fn with_poller(mut self, poller: Arc<dyn IntoElementPoller + Send + Sync>) -> Self {
293        self.poller = poller;
294        self
295    }
296
297    /// Force this ElementQuery to wait for the specified timeout, polling once
298    /// after each interval. This will override the poller for this
299    /// ElementQuery only.
300    pub fn wait(self, timeout: Duration, interval: Duration) -> Self {
301        self.with_poller(Arc::new(ElementPollerWithTimeout::new(timeout, interval)))
302    }
303
304    /// Force this ElementQuery to not wait for the specified condition(s).
305    /// This will override the poller for this ElementQuery only.
306    pub fn nowait(self) -> Self {
307        self.with_poller(Arc::new(ElementPollerNoWait))
308    }
309
310    //
311    // Selectors
312    //
313
314    /// Add the specified selector to this ElementQuery. Callers should use
315    /// the `or()` method instead.
316    fn add_selector(mut self, selector: ElementSelector) -> Self {
317        self.selectors.push(selector);
318        self
319    }
320
321    /// Add a new selector to this ElementQuery. All conditions specified after
322    /// this selector (up until the next `or()` method) will apply to this
323    /// selector.
324    pub fn or(self, by: By) -> Self {
325        self.add_selector(ElementSelector::new(by))
326    }
327
328    //
329    // Retrievers
330    //
331
332    /// Return true if an element matches any selector (including filters), otherwise false.
333    pub async fn exists(&self) -> WebDriverResult<bool> {
334        let elements = self.run_poller(true, false).await?;
335        Ok(!elements.is_empty())
336    }
337
338    /// Return true if no element matches any selector (including filters), otherwise false.
339    pub async fn not_exists(&self) -> WebDriverResult<bool> {
340        let elements = self.run_poller(false, true).await?;
341        Ok(elements.is_empty())
342    }
343
344    /// Return the first WebElement that matches any selector (including filters).
345    ///
346    /// Returns None if no elements match.
347    pub async fn first_opt(&self) -> WebDriverResult<Option<WebElement>> {
348        let elements = self.run_poller(true, false).await?;
349        Ok(elements.into_iter().next())
350    }
351
352    /// Return only the first WebElement that matches any selector (including filters).
353    ///
354    /// Returns Err(WebDriverError::NoSuchElement) if no elements match.
355    pub async fn first(&self) -> WebDriverResult<WebElement> {
356        let mut elements = self.run_poller(true, false).await?;
357
358        if elements.is_empty() {
359            let desc: &str = self.options.description.as_deref().unwrap_or("");
360            let err = no_such_element(&self.selectors, desc);
361            Err(err)
362        } else {
363            Ok(elements.remove(0))
364        }
365    }
366
367    /// Return only a single WebElement that matches any selector (including filters).
368    ///
369    /// This method requires that only one element was found, and will return
370    /// Err(WebDriverError::NoSuchElement) if the number of elements found after processing
371    /// all selectors was not equal to 1.
372    ///
373    /// This is useful because sometimes your element query is not specific enough and
374    /// might accidentally match multiple elements. This is a common source of bugs in
375    /// automated tests, because the first element might not be the one you expect.
376    ///
377    /// By requiring that only one element is matched, you can be more sure that it is the
378    /// one you intended.
379    pub async fn single(&self) -> WebDriverResult<WebElement> {
380        let mut elements = self.run_poller(false, false).await?;
381
382        if elements.len() == 1 {
383            Ok(elements.remove(0))
384        } else {
385            let desc: &str = self.options.description.as_deref().unwrap_or("");
386            let err = no_such_element(&self.selectors, desc);
387            Err(err)
388        }
389    }
390
391    /// Return all WebElements that match any selector (including filters).
392    ///
393    /// This will return when at least one element is found, after processing all selectors.
394    ///
395    /// Returns an empty Vec if no elements match.
396    pub async fn any(&self) -> WebDriverResult<Vec<WebElement>> {
397        self.run_poller(false, false).await
398    }
399
400    /// Return all WebElements that match any selector (including filters).
401    ///
402    /// This will return when at least one element is found, after processing all selectors.
403    ///
404    /// Returns Err(WebDriverError::NoSuchElement) if no elements match.
405    pub async fn any_required(&self) -> WebDriverResult<Vec<WebElement>> {
406        let elements = self.run_poller(false, false).await?;
407        disallow_empty!(elements, self)
408    }
409
410    /// Return all WebElements that match any single selector (including filters).
411    #[deprecated(since = "0.32.0", note = "use all_from_selector() instead")]
412    pub async fn all(&self) -> WebDriverResult<Vec<WebElement>> {
413        self.all_from_selector().await
414    }
415
416    /// Return all WebElements that match a single selector (including filters).
417    ///
418    /// This will return when at least one element is found from any selector, without
419    /// processing other selectors afterwards.
420    ///
421    /// Returns an empty Vec if no elements match.
422    pub async fn all_from_selector(&self) -> WebDriverResult<Vec<WebElement>> {
423        self.run_poller(true, false).await
424    }
425
426    /// Return all WebElements that match any single selector (including filters).
427    #[deprecated(since = "0.32.0", note = "use all_from_selector_required() instead")]
428    pub async fn all_required(&self) -> WebDriverResult<Vec<WebElement>> {
429        self.all_from_selector_required().await
430    }
431
432    /// Return all WebElements that match any single selector (including filters).
433    ///
434    /// This will return when at least one element is found from any selector, without
435    /// processing other selectors afterwards.
436    ///
437    /// Returns Err(WebDriverError::NoSuchElement) if no elements match.
438    pub async fn all_from_selector_required(&self) -> WebDriverResult<Vec<WebElement>> {
439        let elements = self.run_poller(true, false).await?;
440        disallow_empty!(elements, self)
441    }
442
443    /// Run the poller for this ElementQuery and return the Vec of WebElements matched.
444    ///
445    /// NOTE: This function doesn't return a no_such_element error and the caller must handle it.
446    ///
447    /// The parameters are as follows:
448    /// - `short_circuit`:
449    ///   - if true, return as soon as any selector meets the condition.
450    ///     The elements returned will be only the elements from that selector.
451    ///   - if false, only check the condition (and possibly return) after processing all selectors.
452    /// - `stop_on_miss`:
453    ///   - if true, the condition is true if no elements were found.
454    ///   - if false, the condition is true if at least one element was found.
455    ///
456    async fn run_poller(
457        &self,
458        short_circuit: bool,
459        stop_on_miss: bool,
460    ) -> WebDriverResult<Vec<WebElement>> {
461        let desc: &str = self.options.description.as_deref().unwrap_or("");
462        let no_such_element_error = no_such_element(&self.selectors, desc);
463        if self.selectors.is_empty() {
464            return Err(no_such_element_error);
465        }
466
467        // Start the poller.
468        let mut poller = self.poller.start();
469
470        let mut elements = IndexMap::new();
471        loop {
472            for selector in &self.selectors {
473                let mut new_elements =
474                    match self.fetch_elements_from_source(selector.by.clone()).await {
475                        Ok(x) => x,
476                        Err(e) if matches!(*e, WebDriverErrorInner::NoSuchElement(_)) => Vec::new(),
477                        Err(e) => return Err(e),
478                    };
479
480                if !new_elements.is_empty() {
481                    new_elements = filter_elements(new_elements, &selector.filters).await?;
482                }
483
484                // Stop early?
485                if short_circuit && (stop_on_miss == new_elements.is_empty()) {
486                    return Ok(new_elements);
487                }
488
489                // Collect elements, excluding duplicates.
490                for element in new_elements {
491                    elements.insert(element.element_id(), element);
492                }
493            }
494
495            // Once all selectors have been processed, check if we have a match.
496            if stop_on_miss == elements.is_empty() {
497                return Ok(elements.into_values().collect());
498            }
499
500            // On timeout, return any elements found so far.
501            if !poller.tick().await {
502                return Ok(elements.into_values().collect());
503            }
504        }
505    }
506
507    /// Execute the specified selector and return any matched WebElements.
508    async fn fetch_elements_from_source(&self, by: By) -> WebDriverResult<Vec<WebElement>> {
509        match &self.source {
510            ElementQuerySource::Driver(driver) => driver.find_all(by).await,
511            ElementQuerySource::Element(element) => element.find_all(by).await,
512        }
513    }
514
515    //
516    // Filters
517    //
518
519    /// Add the specified ElementPredicate to the last selector.
520    pub fn with_filter(mut self, f: impl ElementPredicate + 'static) -> Self {
521        if let Some(selector) = self.selectors.last_mut() {
522            selector.add_filter(f);
523        }
524        self
525    }
526
527    //
528    // Advance selectors
529    //
530
531    /// Only match elements that are enabled.
532    pub fn and_enabled(self) -> Self {
533        let ignore_errors = self.options.ignore_errors.unwrap_or_default();
534        self.with_filter(conditions::element_is_enabled(ignore_errors))
535    }
536
537    /// Only match elements that are NOT enabled.
538    pub fn and_not_enabled(self) -> Self {
539        let ignore_errors = self.options.ignore_errors.unwrap_or_default();
540        self.with_filter(conditions::element_is_not_enabled(ignore_errors))
541    }
542
543    /// Only match elements that are selected.
544    pub fn and_selected(self) -> Self {
545        let ignore_errors = self.options.ignore_errors.unwrap_or_default();
546        self.with_filter(conditions::element_is_selected(ignore_errors))
547    }
548
549    /// Only match elements that are NOT selected.
550    pub fn and_not_selected(self) -> Self {
551        let ignore_errors = self.options.ignore_errors.unwrap_or_default();
552        self.with_filter(conditions::element_is_not_selected(ignore_errors))
553    }
554
555    /// Only match elements that are displayed.
556    pub fn and_displayed(self) -> Self {
557        let ignore_errors = self.options.ignore_errors.unwrap_or_default();
558        self.with_filter(conditions::element_is_displayed(ignore_errors))
559    }
560
561    /// Only match elements that are NOT displayed.
562    pub fn and_not_displayed(self) -> Self {
563        let ignore_errors = self.options.ignore_errors.unwrap_or_default();
564        self.with_filter(conditions::element_is_not_displayed(ignore_errors))
565    }
566
567    /// Only match elements that are clickable.
568    pub fn and_clickable(self) -> Self {
569        let ignore_errors = self.options.ignore_errors.unwrap_or_default();
570        self.with_filter(conditions::element_is_clickable(ignore_errors))
571    }
572
573    /// Only match elements that are NOT clickable.
574    pub fn and_not_clickable(self) -> Self {
575        let ignore_errors = self.options.ignore_errors.unwrap_or_default();
576        self.with_filter(conditions::element_is_not_clickable(ignore_errors))
577    }
578
579    //
580    // By alternative helper selectors
581    //
582
583    /// Only match elements that have the specified text.
584    /// See the `Needle` documentation for more details on text matching rules.
585    pub fn with_text<N>(self, text: N) -> Self
586    where
587        N: Needle + Clone + Send + Sync + 'static,
588    {
589        let ignore_errors = self.options.ignore_errors.unwrap_or_default();
590        self.with_filter(conditions::element_has_text(text, ignore_errors))
591    }
592
593    /// Only match elements that do not have the specified text.
594    /// See the `Needle` documentation for more details on text matching rules.
595    pub fn without_text<N>(self, text: N) -> Self
596    where
597        N: Needle + Clone + Send + Sync + 'static,
598    {
599        let ignore_errors = self.options.ignore_errors.unwrap_or_default();
600        self.with_filter(conditions::element_lacks_text(text, ignore_errors))
601    }
602
603    /// Only match elements that have the specified id.
604    /// See the `Needle` documentation for more details on text matching rules.
605    pub fn with_id<N>(self, id: N) -> Self
606    where
607        N: Needle + Clone + Send + Sync + 'static,
608    {
609        let ignore_errors = self.options.ignore_errors.unwrap_or_default();
610        self.with_filter(move |elem: WebElement| {
611            let id = id.clone();
612            async move {
613                match elem.id().await {
614                    Ok(Some(x)) => Ok(id.is_match(&x)),
615                    Ok(None) => Ok(false),
616                    Err(e) => handle_errors(Err(e), ignore_errors),
617                }
618            }
619        })
620    }
621
622    /// Only match elements that do not have the specified id.
623    /// See the `Needle` documentation for more details on text matching rules.
624    pub fn without_id<N>(self, id: N) -> Self
625    where
626        N: Needle + Clone + Send + Sync + 'static,
627    {
628        let ignore_errors = self.options.ignore_errors.unwrap_or_default();
629        self.with_filter(move |elem: WebElement| {
630            let id = id.clone();
631            async move {
632                match elem.id().await {
633                    Ok(Some(x)) => Ok(!id.is_match(&x)),
634                    Ok(None) => Ok(true),
635                    Err(e) => handle_errors(Err(e), ignore_errors),
636                }
637            }
638        })
639    }
640
641    /// Only match elements that contain the specified class name.
642    /// See the `Needle` documentation for more details on text matching rules.
643    pub fn with_class<N>(self, class_name: N) -> Self
644    where
645        N: Needle + Clone + Send + Sync + 'static,
646    {
647        let ignore_errors = self.options.ignore_errors.unwrap_or_default();
648        self.with_filter(conditions::element_has_class(class_name, ignore_errors))
649    }
650
651    /// Only match elements that do not contain the specified class name.
652    /// See the `Needle` documentation for more details on text matching rules.
653    pub fn without_class<N>(self, class_name: N) -> Self
654    where
655        N: Needle + Clone + Send + Sync + 'static,
656    {
657        let ignore_errors = self.options.ignore_errors.unwrap_or_default();
658        self.with_filter(conditions::element_lacks_class(class_name, ignore_errors))
659    }
660
661    /// Only match elements that have the specified tag.
662    /// See the `Needle` documentation for more details on text matching rules.
663    pub fn with_tag<N>(self, tag_name: N) -> Self
664    where
665        N: Needle + Clone + Send + Sync + 'static,
666    {
667        let ignore_errors = self.options.ignore_errors.unwrap_or_default();
668        self.with_filter(move |elem: WebElement| {
669            let tag_name = tag_name.clone();
670            async move {
671                handle_errors(elem.tag_name().await.map(|x| tag_name.is_match(&x)), ignore_errors)
672            }
673        })
674    }
675
676    /// Only match elements that do not have the specified tag.
677    /// See the `Needle` documentation for more details on text matching rules.
678    pub fn without_tag<N>(self, tag_name: N) -> Self
679    where
680        N: Needle + Clone + Send + Sync + 'static,
681    {
682        let ignore_errors = self.options.ignore_errors.unwrap_or_default();
683        self.with_filter(move |elem: WebElement| {
684            let tag_name = tag_name.clone();
685            async move {
686                negate(elem.tag_name().await.map(|x| tag_name.is_match(&x)), ignore_errors)
687            }
688        })
689    }
690
691    /// Only match elements that have the specified value.
692    /// See the `Needle` documentation for more details on text matching rules.
693    pub fn with_value<N>(self, value: N) -> Self
694    where
695        N: Needle + Clone + Send + Sync + 'static,
696    {
697        let ignore_errors = self.options.ignore_errors.unwrap_or_default();
698        self.with_filter(conditions::element_has_value(value, ignore_errors))
699    }
700
701    /// Only match elements that do not have the specified value.
702    /// See the `Needle` documentation for more details on text matching rules.
703    pub fn without_value<N>(self, value: N) -> Self
704    where
705        N: Needle + Clone + Send + Sync + 'static,
706    {
707        let ignore_errors = self.options.ignore_errors.unwrap_or_default();
708        self.with_filter(conditions::element_lacks_value(value, ignore_errors))
709    }
710
711    /// Only match elements that have the specified attribute with the specified value.
712    /// See the `Needle` documentation for more details on text matching rules.
713    pub fn with_attribute<S, N>(self, attribute_name: S, value: N) -> Self
714    where
715        S: IntoArcStr,
716        N: Needle + Clone + Send + Sync + 'static,
717    {
718        let ignore_errors = self.options.ignore_errors.unwrap_or_default();
719        self.with_filter(conditions::element_has_attribute(
720            attribute_name.into(),
721            value,
722            ignore_errors,
723        ))
724    }
725
726    /// Only match elements that do not have the specified attribute with the specified value.
727    /// See the `Needle` documentation for more details on text matching rules.
728    pub fn without_attribute<S, N>(self, attribute_name: S, value: N) -> Self
729    where
730        S: IntoArcStr,
731        N: Needle + Clone + Send + Sync + 'static,
732    {
733        let ignore_errors = self.options.ignore_errors.unwrap_or_default();
734        self.with_filter(conditions::element_lacks_attribute(
735            attribute_name.into(),
736            value,
737            ignore_errors,
738        ))
739    }
740
741    /// Only match elements that have all the specified attributes with the specified values.
742    /// See the `Needle` documentation for more details on text matching rules.
743    pub fn with_attributes<S, N>(self, desired_attributes: impl IntoIterator<Item = (S, N)>) -> Self
744    where
745        S: IntoArcStr,
746        N: Needle + Send + Sync + 'static,
747    {
748        let ignore_errors = self.options.ignore_errors.unwrap_or_default();
749        self.with_filter(conditions::element_has_attributes(
750            collect_arg_slice(desired_attributes),
751            ignore_errors,
752        ))
753    }
754
755    /// Only match elements that do not have any of the specified attributes with the specified
756    /// values. See the `Needle` documentation for more details on text matching rules.
757    pub fn without_attributes<S, N>(
758        self,
759        desired_attributes: impl IntoIterator<Item = (S, N)>,
760    ) -> Self
761    where
762        S: IntoArcStr,
763        N: Needle + Send + Sync + 'static,
764    {
765        let ignore_errors = self.options.ignore_errors.unwrap_or_default();
766        self.with_filter(conditions::element_lacks_attributes(
767            collect_arg_slice(desired_attributes),
768            ignore_errors,
769        ))
770    }
771
772    /// Only match elements that have the specified property with the specified value.
773    /// See the `Needle` documentation for more details on text matching rules.
774    pub fn with_property<S, N>(self, property_name: S, value: N) -> Self
775    where
776        S: IntoArcStr,
777        N: Needle + Clone + Send + Sync + 'static,
778    {
779        let ignore_errors = self.options.ignore_errors.unwrap_or_default();
780        self.with_filter(conditions::element_has_property(
781            property_name.into(),
782            value,
783            ignore_errors,
784        ))
785    }
786
787    /// Only match elements that do not have the specified property with the specified value.
788    /// See the `Needle` documentation for more details on text matching rules.
789    pub fn without_property<S, N>(self, property_name: S, value: N) -> Self
790    where
791        S: IntoArcStr,
792        N: Needle + Clone + Send + Sync + 'static,
793    {
794        let ignore_errors = self.options.ignore_errors.unwrap_or_default();
795        self.with_filter(conditions::element_lacks_property(
796            property_name.into(),
797            value,
798            ignore_errors,
799        ))
800    }
801
802    /// Only match elements that have all the specified properties with the specified value.
803    /// See the `Needle` documentation for more details on text matching rules.
804    pub fn with_properties<S, N>(self, desired_properties: impl IntoIterator<Item = (S, N)>) -> Self
805    where
806        S: IntoArcStr,
807        N: Needle + Send + Sync + 'static,
808    {
809        let ignore_errors = self.options.ignore_errors.unwrap_or_default();
810        self.with_filter(conditions::element_has_properties(
811            collect_arg_slice(desired_properties),
812            ignore_errors,
813        ))
814    }
815
816    /// Only match elements that do not have any of the specified properties with the specified
817    /// value. See the `Needle` documentation for more details on text matching rules.
818    pub fn without_properties<S, N>(
819        self,
820        desired_properties: impl IntoIterator<Item = (S, N)>,
821    ) -> Self
822    where
823        S: IntoArcStr,
824        N: Needle + Send + Sync + 'static,
825    {
826        let ignore_errors = self.options.ignore_errors.unwrap_or_default();
827        self.with_filter(conditions::element_lacks_properties(
828            collect_arg_slice(desired_properties),
829            ignore_errors,
830        ))
831    }
832
833    /// Only match elements that have the specified CSS property with the specified value.
834    /// See the `Needle` documentation for more details on text matching rules.
835    pub fn with_css_property<S, N>(self, css_property_name: S, value: N) -> Self
836    where
837        S: IntoArcStr,
838        N: Needle + Clone + Send + Sync + 'static,
839    {
840        let ignore_errors = self.options.ignore_errors.unwrap_or_default();
841        self.with_filter(conditions::element_has_css_property(
842            css_property_name.into(),
843            value,
844            ignore_errors,
845        ))
846    }
847
848    /// Only match elements that do not have the specified CSS property with the specified value.
849    /// See the `Needle` documentation for more details on text matching rules.
850    pub fn without_css_property<S, N>(self, css_property_name: S, value: N) -> Self
851    where
852        S: IntoArcStr,
853        N: Needle + Clone + Send + Sync + 'static,
854    {
855        let ignore_errors = self.options.ignore_errors.unwrap_or_default();
856        self.with_filter(conditions::element_lacks_css_property(
857            css_property_name.into(),
858            value,
859            ignore_errors,
860        ))
861    }
862
863    /// Only match elements that have all the specified CSS properties with the
864    /// specified values.
865    /// See the `Needle` documentation for more details on text matching rules.
866    pub fn with_css_properties<S, N>(
867        self,
868        desired_css_properties: impl IntoIterator<Item = (S, N)>,
869    ) -> Self
870    where
871        S: IntoArcStr,
872        N: Needle + Send + Sync + 'static,
873    {
874        let ignore_errors = self.options.ignore_errors.unwrap_or_default();
875        self.with_filter(conditions::element_has_css_properties(
876            collect_arg_slice(desired_css_properties),
877            ignore_errors,
878        ))
879    }
880
881    /// Only match elements that do not have any of the specified CSS properties with the
882    /// specified values.
883    /// See the `Needle` documentation for more details on text matching rules.
884    pub fn without_css_properties<S, N>(
885        self,
886        desired_css_properties: impl IntoIterator<Item = (S, N)>,
887    ) -> Self
888    where
889        S: IntoArcStr,
890        N: Needle + Send + Sync + 'static,
891    {
892        let ignore_errors = self.options.ignore_errors.unwrap_or_default();
893        self.with_filter(conditions::element_lacks_css_properties(
894            collect_arg_slice(desired_css_properties),
895            ignore_errors,
896        ))
897    }
898}
899
900/// Trait for enabling the ElementQuery interface.
901pub trait ElementQueryable {
902    /// Start an element query using the specified selector.
903    fn query(&self, by: By) -> ElementQuery;
904}
905
906impl ElementQueryable for WebElement {
907    /// Return an ElementQuery instance for more executing powerful element queries.
908    ///
909    /// This uses the builder pattern to construct queries that will return one or
910    /// more elements, depending on the method specified at the end of the chain.
911    ///
912    /// See [`ElementQuery`] for more documentation.
913    fn query(&self, by: By) -> ElementQuery {
914        ElementQuery::new(
915            ElementQuerySource::Element(self.clone()),
916            by,
917            self.handle.config().poller.clone(),
918        )
919    }
920}
921
922impl ElementQueryable for Arc<SessionHandle> {
923    /// Return an ElementQuery instance for more executing powerful element queries.
924    ///
925    /// This uses the builder pattern to construct queries that will return one or
926    /// more elements, depending on the method specified at the end of the chain.
927    ///
928    /// See [`ElementQuery`] for more documentation.
929    fn query(&self, by: By) -> ElementQuery {
930        ElementQuery::new(
931            ElementQuerySource::Driver(self.clone()),
932            by,
933            self.config().poller.clone(),
934        )
935    }
936}
937
938#[cfg(test)]
939/// This function checks if the public async methods implement Send. It is not intended to be executed.
940async fn _test_is_send() -> WebDriverResult<()> {
941    use crate::prelude::*;
942
943    // Helper methods
944    fn is_send<T: Send>() {}
945    fn is_send_val<T: Send>(_val: &T) {}
946
947    // ElementSelector
948    let selector = ElementSelector::new(By::Css("div"));
949    is_send_val(&filter_elements(Vec::new(), &selector.filters));
950
951    // Pre values
952    let caps = DesiredCapabilities::chrome();
953    let driver = WebDriver::new("http://localhost:4444", caps).await?;
954
955    // ElementQuery
956    let query = driver.query(By::Css("div"));
957    is_send_val(&query.exists());
958    is_send_val(&query.not_exists());
959    is_send_val(&query.first());
960    is_send_val(&query.all_from_selector());
961    is_send_val(&query.all_from_selector_required());
962
963    Ok(())
964}