Skip to main content

ferridriver_test/expect/
page.rs

1//! Snapshot / screenshot / aria matchers for `Expect<Arc<Page>>`. The
2//! url / title matchers live in [`ferridriver_expect::page`].
3
4use std::future::Future;
5use std::sync::Arc;
6use std::time::Duration;
7
8use ferridriver::Page;
9use ferridriver_expect::{Expect, ExpectContext, MatchError, poll_until as expect_poll_until};
10
11use crate::model::TestFailure;
12
13fn page_ctx(method: &'static str, is_not: bool) -> ExpectContext {
14  ExpectContext {
15    method,
16    subject: "page".into(),
17    is_not,
18  }
19}
20
21async fn poll_until_test<F, Fut>(timeout: Duration, ctx: ExpectContext, check: F) -> Result<(), TestFailure>
22where
23  F: FnMut() -> Fut,
24  Fut: Future<Output = Result<(), MatchError>>,
25{
26  expect_poll_until(timeout, ctx, check).await.map_err(Into::into)
27}
28
29/// Snapshot matchers for `expect(page)`. Import via
30/// `use ferridriver_test::expect::PageSnapshotMatchers;`.
31#[allow(async_fn_in_trait)]
32pub trait PageSnapshotMatchers {
33  async fn to_have_screenshot(&self, name: &str) -> Result<(), TestFailure>;
34  async fn to_match_aria_snapshot(&self, expected: &str) -> Result<(), TestFailure>;
35}
36
37impl PageSnapshotMatchers for Expect<'_, Arc<Page>> {
38  async fn to_have_screenshot(&self, name: &str) -> Result<(), TestFailure> {
39    let page = self.subject;
40    let actual_png = page
41      .screenshot(ferridriver::options::ScreenshotOptions::default())
42      .await
43      .map_err(|e| TestFailure {
44        message: format!("page screenshot failed: {e}"),
45        stack: None,
46        diff: None,
47        screenshot: None,
48      })?;
49
50    crate::snapshot::compare_screenshot_png(&actual_png, name)
51  }
52
53  async fn to_match_aria_snapshot(&self, expected: &str) -> Result<(), TestFailure> {
54    let page = self.subject;
55    let is_not = self.is_not;
56    let expected = expected.to_string();
57
58    poll_until_test(self.timeout, page_ctx("toMatchAriaSnapshot", is_not), || {
59      let expected = expected.clone();
60      async move {
61        let snapshot = page
62          .snapshot_for_ai(ferridriver::snapshot::SnapshotOptions {
63            depth: None,
64            track: None,
65          })
66          .await
67          .map_err(|e| MatchError::new("(aria snapshot)", format!("error: {e}")))?;
68
69        let contains = snapshot.full.contains(&expected);
70        if contains == is_not {
71          Err(MatchError::new(
72            format!("{}\n{expected}", if is_not { "not matching" } else { "matching" }),
73            snapshot.full[..snapshot.full.len().min(500)].to_string(),
74          ))
75        } else {
76          Ok(())
77        }
78      }
79    })
80    .await
81  }
82}