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
15fn 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
34fn 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
48pub 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
74pub struct ElementSelector {
78 pub by: By,
80 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 pub fn new(by: By) -> Self {
93 Self {
94 by,
95 filters: Vec::new(),
96 }
97 }
98
99 pub fn add_filter(&mut self, f: impl ElementPredicate + 'static) {
101 self.add_box_filter(DynElementPredicate::boxed(f));
102 }
103
104 pub fn add_box_filter(&mut self, f: Box<DynElementPredicate>) {
106 self.filters.push(f);
107 }
108}
109
110#[derive(Debug)]
115pub enum ElementQuerySource {
116 Driver(Arc<SessionHandle>),
118 Element(WebElement),
120}
121
122#[derive(Debug, Clone)]
124pub enum ElementQueryWaitOptions {
125 WaitDefault,
127 Wait {
129 timeout: Duration,
131 interval: Duration,
133 },
134 NoWait,
136}
137
138impl Default for ElementQueryWaitOptions {
139 fn default() -> Self {
140 Self::WaitDefault
141 }
142}
143
144#[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 pub fn ignore_errors(mut self, ignore_errors: bool) -> Self {
159 self.ignore_errors = Some(ignore_errors);
160 self
161 }
162
163 pub fn set_ignore_errors(mut self, ignore_errors: Option<bool>) -> Self {
165 self.ignore_errors = ignore_errors;
166 self
167 }
168
169 pub fn description(mut self, description: impl IntoArcStr) -> Self {
171 self.description = Some(description.into());
172 self
173 }
174
175 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 pub fn wait(mut self, wait_option: ElementQueryWaitOptions) -> Self {
183 self.wait = Some(wait_option);
184 self
185 }
186
187 pub fn set_wait(mut self, wait_option: Option<ElementQueryWaitOptions>) -> Self {
189 self.wait = wait_option;
190 self
191 }
192}
193
194#[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 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 pub fn options(mut self, options: ElementQueryOptions) -> Self {
257 self.options = options;
258
259 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 pub fn desc(mut self, description: &str) -> Self {
273 self.options = self.options.description(description);
274 self
275 }
276
277 pub fn ignore_errors(mut self, ignore: bool) -> Self {
282 self.options = self.options.ignore_errors(ignore);
283 self
284 }
285
286 pub fn with_poller(mut self, poller: Arc<dyn IntoElementPoller + Send + Sync>) -> Self {
293 self.poller = poller;
294 self
295 }
296
297 pub fn wait(self, timeout: Duration, interval: Duration) -> Self {
301 self.with_poller(Arc::new(ElementPollerWithTimeout::new(timeout, interval)))
302 }
303
304 pub fn nowait(self) -> Self {
307 self.with_poller(Arc::new(ElementPollerNoWait))
308 }
309
310 fn add_selector(mut self, selector: ElementSelector) -> Self {
317 self.selectors.push(selector);
318 self
319 }
320
321 pub fn or(self, by: By) -> Self {
325 self.add_selector(ElementSelector::new(by))
326 }
327
328 pub async fn exists(&self) -> WebDriverResult<bool> {
334 let elements = self.run_poller(true, false).await?;
335 Ok(!elements.is_empty())
336 }
337
338 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 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 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 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 pub async fn any(&self) -> WebDriverResult<Vec<WebElement>> {
397 self.run_poller(false, false).await
398 }
399
400 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 #[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 pub async fn all_from_selector(&self) -> WebDriverResult<Vec<WebElement>> {
423 self.run_poller(true, false).await
424 }
425
426 #[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 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 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 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 if short_circuit && (stop_on_miss == new_elements.is_empty()) {
486 return Ok(new_elements);
487 }
488
489 for element in new_elements {
491 elements.insert(element.element_id(), element);
492 }
493 }
494
495 if stop_on_miss == elements.is_empty() {
497 return Ok(elements.into_values().collect());
498 }
499
500 if !poller.tick().await {
502 return Ok(elements.into_values().collect());
503 }
504 }
505 }
506
507 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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
900pub trait ElementQueryable {
902 fn query(&self, by: By) -> ElementQuery;
904}
905
906impl ElementQueryable for WebElement {
907 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 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)]
939async fn _test_is_send() -> WebDriverResult<()> {
941 use crate::prelude::*;
942
943 fn is_send<T: Send>() {}
945 fn is_send_val<T: Send>(_val: &T) {}
946
947 let selector = ElementSelector::new(By::Css("div"));
949 is_send_val(&filter_elements(Vec::new(), &selector.filters));
950
951 let caps = DesiredCapabilities::chrome();
953 let driver = WebDriver::new("http://localhost:4444", caps).await?;
954
955 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}