viewpoint_test/expect/
soft_locator.rs

1//! Soft locator assertion methods.
2//!
3//! This module contains all the assertion methods for `SoftLocatorAssertions`.
4
5use std::sync::{Arc, Mutex};
6
7use viewpoint_core::AriaSnapshot;
8
9use super::locator::LocatorAssertions;
10use super::soft::SoftAssertionError;
11
12/// Soft assertions for locators.
13///
14/// These assertions collect failures instead of failing immediately.
15pub struct SoftLocatorAssertions<'a> {
16    pub(super) assertions: LocatorAssertions<'a>,
17    pub(super) errors: Arc<Mutex<Vec<SoftAssertionError>>>,
18}
19
20/// Helper macro to reduce boilerplate in soft assertion methods.
21macro_rules! soft_assert {
22    ($self:expr, $method:ident, $assertion_name:expr) => {
23        match $self.assertions.$method().await {
24            Ok(()) => {}
25            Err(e) => {
26                $self
27                    .errors
28                    .lock()
29                    .unwrap()
30                    .push(SoftAssertionError::new($assertion_name, e.to_string()));
31            }
32        }
33    };
34    ($self:expr, $method:ident, $assertion_name:expr, expected: $expected:expr) => {
35        match $self.assertions.$method(&$expected).await {
36            Ok(()) => {}
37            Err(e) => {
38                $self.errors.lock().unwrap().push(
39                    SoftAssertionError::new($assertion_name, e.to_string())
40                        .with_expected($expected.to_string()),
41                );
42            }
43        }
44    };
45}
46
47impl SoftLocatorAssertions<'_> {
48    /// Assert element is visible (soft).
49    pub async fn to_be_visible(&self) {
50        soft_assert!(self, to_be_visible, "to_be_visible");
51    }
52
53    /// Assert element is hidden (soft).
54    pub async fn to_be_hidden(&self) {
55        soft_assert!(self, to_be_hidden, "to_be_hidden");
56    }
57
58    /// Assert element is enabled (soft).
59    pub async fn to_be_enabled(&self) {
60        soft_assert!(self, to_be_enabled, "to_be_enabled");
61    }
62
63    /// Assert element is disabled (soft).
64    pub async fn to_be_disabled(&self) {
65        soft_assert!(self, to_be_disabled, "to_be_disabled");
66    }
67
68    /// Assert element is checked (soft).
69    pub async fn to_be_checked(&self) {
70        soft_assert!(self, to_be_checked, "to_be_checked");
71    }
72
73    /// Assert element has text (soft).
74    pub async fn to_have_text(&self, expected: impl AsRef<str>) {
75        let expected_str = expected.as_ref().to_string();
76        soft_assert!(self, to_have_text, "to_have_text", expected: expected_str);
77    }
78
79    /// Assert element contains text (soft).
80    pub async fn to_contain_text(&self, expected: impl AsRef<str>) {
81        let expected_str = expected.as_ref().to_string();
82        soft_assert!(self, to_contain_text, "to_contain_text", expected: expected_str);
83    }
84
85    /// Assert element has value (soft).
86    pub async fn to_have_value(&self, expected: impl AsRef<str>) {
87        let expected_str = expected.as_ref().to_string();
88        soft_assert!(self, to_have_value, "to_have_value", expected: expected_str);
89    }
90
91    /// Assert element has attribute (soft).
92    pub async fn to_have_attribute(&self, name: impl AsRef<str>, value: impl AsRef<str>) {
93        let name_str = name.as_ref().to_string();
94        let value_str = value.as_ref().to_string();
95        match self
96            .assertions
97            .to_have_attribute(&name_str, &value_str)
98            .await
99        {
100            Ok(()) => {}
101            Err(e) => {
102                self.errors.lock().unwrap().push(
103                    SoftAssertionError::new(
104                        format!("to_have_attribute({name_str})"),
105                        e.to_string(),
106                    )
107                    .with_expected(&value_str),
108                );
109            }
110        }
111    }
112
113    /// Assert element has class (soft).
114    pub async fn to_have_class(&self, class_name: impl AsRef<str>) {
115        let class_str = class_name.as_ref().to_string();
116        soft_assert!(self, to_have_class, "to_have_class", expected: class_str);
117    }
118
119    /// Assert element has id (soft).
120    pub async fn to_have_id(&self, expected: impl AsRef<str>) {
121        let expected_str = expected.as_ref().to_string();
122        soft_assert!(self, to_have_id, "to_have_id", expected: expected_str);
123    }
124
125    /// Assert element count (soft).
126    pub async fn to_have_count(&self, expected: usize) {
127        match self.assertions.to_have_count(expected).await {
128            Ok(()) => {}
129            Err(e) => {
130                self.errors.lock().unwrap().push(
131                    SoftAssertionError::new("to_have_count", e.to_string())
132                        .with_expected(expected.to_string()),
133                );
134            }
135        }
136    }
137
138    /// Assert element count is greater than a value (soft).
139    pub async fn to_have_count_greater_than(&self, n: usize) {
140        match self.assertions.to_have_count_greater_than(n).await {
141            Ok(()) => {}
142            Err(e) => {
143                self.errors.lock().unwrap().push(
144                    SoftAssertionError::new("to_have_count_greater_than", e.to_string())
145                        .with_expected(format!("> {n}")),
146                );
147            }
148        }
149    }
150
151    /// Assert element count is less than a value (soft).
152    pub async fn to_have_count_less_than(&self, n: usize) {
153        match self.assertions.to_have_count_less_than(n).await {
154            Ok(()) => {}
155            Err(e) => {
156                self.errors.lock().unwrap().push(
157                    SoftAssertionError::new("to_have_count_less_than", e.to_string())
158                        .with_expected(format!("< {n}")),
159                );
160            }
161        }
162    }
163
164    /// Assert element count is at least a value (soft).
165    pub async fn to_have_count_at_least(&self, n: usize) {
166        match self.assertions.to_have_count_at_least(n).await {
167            Ok(()) => {}
168            Err(e) => {
169                self.errors.lock().unwrap().push(
170                    SoftAssertionError::new("to_have_count_at_least", e.to_string())
171                        .with_expected(format!(">= {n}")),
172                );
173            }
174        }
175    }
176
177    /// Assert element count is at most a value (soft).
178    pub async fn to_have_count_at_most(&self, n: usize) {
179        match self.assertions.to_have_count_at_most(n).await {
180            Ok(()) => {}
181            Err(e) => {
182                self.errors.lock().unwrap().push(
183                    SoftAssertionError::new("to_have_count_at_most", e.to_string())
184                        .with_expected(format!("<= {n}")),
185                );
186            }
187        }
188    }
189
190    /// Assert element's ARIA snapshot matches expected structure (soft).
191    pub async fn to_match_aria_snapshot(&self, expected: &AriaSnapshot) {
192        match self.assertions.to_match_aria_snapshot(expected).await {
193            Ok(()) => {}
194            Err(e) => {
195                self.errors.lock().unwrap().push(
196                    SoftAssertionError::new("to_match_aria_snapshot", e.to_string())
197                        .with_expected(expected.to_yaml()),
198                );
199            }
200        }
201    }
202
203    /// Assert element's ARIA snapshot matches expected YAML string (soft).
204    pub async fn to_match_aria_snapshot_yaml(&self, expected_yaml: &str) {
205        match self
206            .assertions
207            .to_match_aria_snapshot_yaml(expected_yaml)
208            .await
209        {
210            Ok(()) => {}
211            Err(e) => {
212                self.errors.lock().unwrap().push(
213                    SoftAssertionError::new("to_match_aria_snapshot_yaml", e.to_string())
214                        .with_expected(expected_yaml.to_string()),
215                );
216            }
217        }
218    }
219
220    /// Assert elements have texts (soft).
221    pub async fn to_have_texts(&self, expected: &[&str]) {
222        match self.assertions.to_have_texts(expected).await {
223            Ok(()) => {}
224            Err(e) => {
225                self.errors.lock().unwrap().push(
226                    SoftAssertionError::new("to_have_texts", e.to_string())
227                        .with_expected(format!("{expected:?}")),
228                );
229            }
230        }
231    }
232
233    /// Assert elements contain texts (soft).
234    pub async fn to_contain_texts(&self, expected: &[&str]) {
235        match self.assertions.to_contain_texts(expected).await {
236            Ok(()) => {}
237            Err(e) => {
238                self.errors.lock().unwrap().push(
239                    SoftAssertionError::new("to_contain_texts", e.to_string())
240                        .with_expected(format!("{expected:?}")),
241                );
242            }
243        }
244    }
245
246    /// Assert element has all specified classes (soft).
247    pub async fn to_have_classes(&self, expected_classes: &[&str]) {
248        match self.assertions.to_have_classes(expected_classes).await {
249            Ok(()) => {}
250            Err(e) => {
251                self.errors.lock().unwrap().push(
252                    SoftAssertionError::new("to_have_classes", e.to_string())
253                        .with_expected(format!("{expected_classes:?}")),
254                );
255            }
256        }
257    }
258
259    /// Assert multi-select element has specified values (soft).
260    pub async fn to_have_values(&self, expected: &[&str]) {
261        match self.assertions.to_have_values(expected).await {
262            Ok(()) => {}
263            Err(e) => {
264                self.errors.lock().unwrap().push(
265                    SoftAssertionError::new("to_have_values", e.to_string())
266                        .with_expected(format!("{expected:?}")),
267                );
268            }
269        }
270    }
271}