Skip to main content

ferridriver_test/
context.rs

1//! Test context: the single object passed to every `#[ferritest]` function.
2//!
3//! Wraps `FixturePool` and provides typed getters for built-in fixtures.
4//! This is the Rust equivalent of Playwright's destructured `{ page, browser, context, testInfo }`.
5//!
6//! ```ignore
7//! #[ferritest]
8//! async fn my_test(ctx: TestContext) {
9//!     let page = ctx.page().await?;
10//!     page.goto("https://example.com", None).await?;
11//! }
12//! ```
13
14use std::any::Any;
15use std::sync::Arc;
16
17use crate::fixture::FixturePool;
18use crate::model::{TestFailure, TestInfo};
19
20/// Context object passed to every `#[ferritest]` test function.
21///
22/// Provides typed access to all built-in fixtures (page, browser, context, test_info)
23/// and raw access to the underlying `FixturePool` for custom fixtures.
24#[derive(Clone)]
25pub struct TestContext {
26  pool: FixturePool,
27}
28
29impl TestContext {
30  /// Create a new `TestContext` wrapping a `FixturePool`.
31  pub fn new(pool: FixturePool) -> Self {
32    Self { pool }
33  }
34
35  /// Get the `Page` fixture (test-scoped, fresh per test).
36  pub async fn page(&self) -> Result<Arc<ferridriver::Page>, TestFailure> {
37    self
38      .pool
39      .get::<ferridriver::Page>("page")
40      .await
41      .map_err(TestFailure::from)
42  }
43
44  /// Get the `Browser` fixture (worker-scoped, shared across tests in a worker).
45  pub async fn browser(&self) -> Result<Arc<ferridriver::Browser>, TestFailure> {
46    self
47      .pool
48      .get::<ferridriver::Browser>("browser")
49      .await
50      .map_err(TestFailure::from)
51  }
52
53  /// Get the `BrowserContext` fixture (test-scoped).
54  pub async fn browser_context(&self) -> Result<Arc<ferridriver::ContextRef>, TestFailure> {
55    self
56      .pool
57      .get::<ferridriver::ContextRef>("context")
58      .await
59      .map_err(TestFailure::from)
60  }
61
62  /// Get the `TestInfo` fixture (test-scoped runtime context).
63  pub async fn test_info(&self) -> Result<Arc<TestInfo>, TestFailure> {
64    self.pool.get::<TestInfo>("test_info").await.map_err(TestFailure::from)
65  }
66
67  /// Resolve a custom `#[fixture]` (or any fixture) by name, returning `Arc<T>`.
68  ///
69  /// Dependencies resolve lazily and cache for the fixture's scope:
70  ///
71  /// ```ignore
72  /// let users = ctx.get::<Vec<User>>("seeded_users").await?;
73  /// ```
74  pub async fn get<T: Any + Send + Sync>(&self, name: &str) -> Result<Arc<T>, TestFailure> {
75    self.pool.get::<T>(name).await.map_err(TestFailure::from)
76  }
77
78  /// Access the underlying `FixturePool` directly (for custom fixtures).
79  pub fn pool(&self) -> &FixturePool {
80    &self.pool
81  }
82}