testing_library_dom/queries/
label_text.rs1use web_sys::HtmlElement;
2
3use crate::{
4 build_queries,
5 error::QueryError,
6 label_helpers::{get_labels, get_real_labels},
7 matches::{fuzzy_matches, make_normalizer, matches},
8 query_all_by_attribute,
9 types::{Matcher, MatcherOptions, NormalizerOptions, SelectorMatcherOptions},
10 util::node_list_to_vec,
11};
12
13pub fn _query_all_by_label_text<M: Into<Matcher>>(
14 container: &HtmlElement,
15 text: M,
16 options: SelectorMatcherOptions,
17) -> Result<Vec<HtmlElement>, QueryError> {
18 let text = text.into();
19 let selector = options.selector.unwrap_or("*".into());
20 let matcher = match options.exact.unwrap_or(true) {
21 true => matches,
22 false => fuzzy_matches,
23 };
24 let match_normalizer = make_normalizer(NormalizerOptions {
25 trim: options.trim,
26 collapse_whitespace: options.collapse_whitespace,
27 normalizer: options.normalizer,
28 })?;
29
30 let mut matching_labelled_elements = node_list_to_vec::<HtmlElement>(
31 container
32 .query_selector_all("*")
33 .map_err(QueryError::JsError)?,
34 )
35 .into_iter()
36 .filter(|element| {
37 !get_real_labels(element).is_empty() || element.has_attribute("aria-labelledby")
38 })
39 .fold(vec![], |mut labelled_elements, labelled_element| {
40 let label_list = get_labels(container, &labelled_element, Some(selector.clone()));
41
42 for label in &label_list {
43 if let Some(form_control) = label.form_control.as_ref() {
44 if matcher(
45 label.content.clone(),
46 label.form_control.as_deref(),
47 &text,
48 match_normalizer.as_ref(),
49 ) {
50 labelled_elements.push(form_control.clone());
51 }
52 }
53 }
54
55 let labels_value = label_list
56 .into_iter()
57 .filter_map(|label| label.content)
58 .collect::<Vec<_>>();
59
60 if matcher(
61 Some(labels_value.join(" ")),
62 Some(labelled_element.as_ref()),
63 &text,
64 match_normalizer.as_ref(),
65 ) {
66 labelled_elements.push(labelled_element.clone());
67 }
68
69 if labels_value.len() > 1 {
70 for (index, label_value) in labels_value.iter().enumerate() {
71 if matcher(
72 Some(label_value.clone()),
73 Some(labelled_element.as_ref()),
74 &text,
75 match_normalizer.as_ref(),
76 ) {
77 labelled_elements.push(labelled_element.clone());
78 }
79
80 let labels_filtered = labels_value
81 .clone()
82 .splice(index..index + 1, vec![])
83 .collect::<Vec<_>>();
84
85 if labels_filtered.len() > 1
86 && matcher(
87 Some(labels_filtered.join(" ")),
88 Some(labelled_element.as_ref()),
89 &text,
90 match_normalizer.as_ref(),
91 )
92 {
93 labelled_elements.push(labelled_element.clone());
94 }
95 }
96 }
97
98 labelled_elements
99 });
100
101 matching_labelled_elements.append(&mut query_all_by_attribute(
102 "aria-label".into(),
103 container,
104 text,
105 MatcherOptions {
106 exact: options.exact,
107 normalizer: Some(match_normalizer),
108 trim: None,
109 collapse_whitespace: None,
110 suggest: None,
111 },
112 )?);
113
114 let mut unique_matching_labelled_elements = vec![];
115 for element in matching_labelled_elements {
116 if !element.matches(&selector).unwrap_or(false) {
117 continue;
118 }
119
120 if !unique_matching_labelled_elements.contains(&element) {
121 unique_matching_labelled_elements.push(element);
122 }
123 }
124
125 Ok(unique_matching_labelled_elements)
126}
127
128fn get_multiple_error(
131 _container: &HtmlElement,
132 text: Matcher,
133 _options: SelectorMatcherOptions,
134) -> Result<String, QueryError> {
135 Ok(format!(
136 "Found multiple elements with the label text: {text}"
137 ))
138}
139
140fn get_missing_error(
141 _container: &HtmlElement,
142 text: Matcher,
143 _options: SelectorMatcherOptions,
144) -> Result<String, QueryError> {
145 Ok(format!(
146 "Unable to find a label with the text of: {text}"
148 ))
149}
150
151build_queries!(
152 _query_all_by_label_text,
153 get_multiple_error,
154 get_missing_error,
155 label_text,
156 crate::types::Matcher,
157 crate::types::SelectorMatcherOptions
158);
159
160pub use internal::{
161 find_all_by_label_text, find_by_label_text, get_all_by_label_text, get_by_label_text,
162 query_all_by_label_text, query_by_label_text,
163};