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}