use std::{borrow::Cow, fmt};
use async_trait::async_trait;
use rootcause::Report;
use thirtyfour::WebDriver;
use crate::{BrowserTimeouts, ElementQueryWaitConfig};
#[async_trait]
pub trait BrowserTest<Context = (), TestError = rootcause::markers::Dynamic>: Send + Sync
where
Context: Sync + ?Sized,
TestError: ?Sized,
{
fn name(&self) -> Cow<'_, str>;
fn timeouts(&self) -> Option<BrowserTimeouts> {
None
}
fn element_query_wait(&self) -> Option<ElementQueryWaitConfig> {
None
}
async fn run(&self, driver: &WebDriver, context: &Context) -> Result<(), Report<TestError>>;
}
pub struct BrowserTests<Context = (), TestError = rootcause::markers::Dynamic>
where
Context: Sync + ?Sized,
TestError: ?Sized,
{
tests: Vec<Box<dyn BrowserTest<Context, TestError>>>,
}
impl<Context, TestError> BrowserTests<Context, TestError>
where
Context: Sync + ?Sized,
TestError: ?Sized,
{
#[must_use]
pub const fn new() -> Self {
Self { tests: Vec::new() }
}
#[must_use]
pub fn with<T>(mut self, test: T) -> Self
where
T: BrowserTest<Context, TestError> + 'static,
{
self.push(test);
self
}
pub fn push<T>(&mut self, test: T) -> &mut Self
where
T: BrowserTest<Context, TestError> + 'static,
{
self.tests.push(Box::new(test));
self
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.tests.is_empty()
}
pub(crate) fn into_vec(self) -> Vec<Box<dyn BrowserTest<Context, TestError>>> {
self.tests
}
}
impl<Context, TestError> Default for BrowserTests<Context, TestError>
where
Context: Sync + ?Sized,
TestError: ?Sized,
{
fn default() -> Self {
Self::new()
}
}
impl<Context, TestError> fmt::Debug for BrowserTests<Context, TestError>
where
Context: Sync + ?Sized,
TestError: ?Sized,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("BrowserTests")
.field("tests", &BrowserTestNames(&self.tests))
.finish()
}
}
struct BrowserTestNames<'a, Context, TestError>(&'a [Box<dyn BrowserTest<Context, TestError>>])
where
Context: Sync + ?Sized,
TestError: ?Sized;
impl<Context, TestError> fmt::Debug for BrowserTestNames<'_, Context, TestError>
where
Context: Sync + ?Sized,
TestError: ?Sized,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_list()
.entries(self.0.iter().map(|test| test.name()))
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
struct NamedTest(&'static str);
#[async_trait::async_trait]
impl BrowserTest for NamedTest {
fn name(&self) -> Cow<'_, str> {
Cow::Borrowed(self.0)
}
async fn run(&self, _driver: &WebDriver, _context: &()) -> Result<(), Report> {
Ok(())
}
}
#[test]
fn browser_tests_debug_prints_test_names() {
let tests = BrowserTests::new()
.with(NamedTest("opens home page"))
.with(NamedTest("search works"));
assert_eq!(
format!("{tests:?}"),
r#"BrowserTests { tests: ["opens home page", "search works"] }"#
);
}
}