browser_test/
test_case.rs1use std::{borrow::Cow, fmt};
2
3use async_trait::async_trait;
4use rootcause::Report;
5use thirtyfour::WebDriver;
6
7use crate::{BrowserTimeouts, ElementQueryWaitConfig};
8
9#[async_trait]
11pub trait BrowserTest<Context = (), TestError = rootcause::markers::Dynamic>: Send + Sync
12where
13 Context: Sync + ?Sized,
14 TestError: ?Sized,
15{
16 fn name(&self) -> Cow<'_, str>;
21
22 fn timeouts(&self) -> Option<BrowserTimeouts> {
26 None
27 }
28
29 fn element_query_wait(&self) -> Option<ElementQueryWaitConfig> {
33 None
34 }
35
36 async fn run(&self, driver: &WebDriver, context: &Context) -> Result<(), Report<TestError>>;
38}
39
40pub struct BrowserTests<Context = (), TestError = rootcause::markers::Dynamic>
72where
73 Context: Sync + ?Sized,
74 TestError: ?Sized,
75{
76 tests: Vec<Box<dyn BrowserTest<Context, TestError>>>,
77}
78
79impl<Context, TestError> BrowserTests<Context, TestError>
80where
81 Context: Sync + ?Sized,
82 TestError: ?Sized,
83{
84 #[must_use]
86 pub const fn new() -> Self {
87 Self { tests: Vec::new() }
88 }
89
90 #[must_use]
92 pub fn with<T>(mut self, test: T) -> Self
93 where
94 T: BrowserTest<Context, TestError> + 'static,
95 {
96 self.push(test);
97 self
98 }
99
100 pub fn push<T>(&mut self, test: T) -> &mut Self
102 where
103 T: BrowserTest<Context, TestError> + 'static,
104 {
105 self.tests.push(Box::new(test));
106 self
107 }
108
109 #[must_use]
111 pub fn is_empty(&self) -> bool {
112 self.tests.is_empty()
113 }
114
115 pub(crate) fn into_vec(self) -> Vec<Box<dyn BrowserTest<Context, TestError>>> {
116 self.tests
117 }
118}
119
120impl<Context, TestError> Default for BrowserTests<Context, TestError>
121where
122 Context: Sync + ?Sized,
123 TestError: ?Sized,
124{
125 fn default() -> Self {
126 Self::new()
127 }
128}
129
130impl<Context, TestError> fmt::Debug for BrowserTests<Context, TestError>
131where
132 Context: Sync + ?Sized,
133 TestError: ?Sized,
134{
135 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
136 f.debug_struct("BrowserTests")
137 .field("tests", &BrowserTestNames(&self.tests))
138 .finish()
139 }
140}
141
142struct BrowserTestNames<'a, Context, TestError>(&'a [Box<dyn BrowserTest<Context, TestError>>])
143where
144 Context: Sync + ?Sized,
145 TestError: ?Sized;
146
147impl<Context, TestError> fmt::Debug for BrowserTestNames<'_, Context, TestError>
148where
149 Context: Sync + ?Sized,
150 TestError: ?Sized,
151{
152 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
153 f.debug_list()
154 .entries(self.0.iter().map(|test| test.name()))
155 .finish()
156 }
157}
158
159#[cfg(test)]
160mod tests {
161 use super::*;
162
163 struct NamedTest(&'static str);
164
165 #[async_trait::async_trait]
166 impl BrowserTest for NamedTest {
167 fn name(&self) -> Cow<'_, str> {
168 Cow::Borrowed(self.0)
169 }
170
171 async fn run(&self, _driver: &WebDriver, _context: &()) -> Result<(), Report> {
172 Ok(())
173 }
174 }
175
176 #[test]
177 fn browser_tests_debug_prints_test_names() {
178 let tests = BrowserTests::new()
179 .with(NamedTest("opens home page"))
180 .with(NamedTest("search works"));
181
182 assert_eq!(
183 format!("{tests:?}"),
184 r#"BrowserTests { tests: ["opens home page", "search works"] }"#
185 );
186 }
187}