viewpoint_test_macros/
lib.rs

1//! Proc macros for `Viewpoint` test framework.
2//!
3//! This crate provides the `#[viewpoint::test]` attribute macro for convenient
4//! test setup. It is an optional convenience layer - the primary API is
5//! `TestHarness` from `viewpoint-test`.
6//!
7//! # Example
8//!
9//! ```ignore
10//! use viewpoint_test_macros::test;
11//! use viewpoint_test::Page;
12//!
13//! #[viewpoint_test_macros::test]
14//! async fn my_test(page: Page) -> Result<(), Box<dyn std::error::Error>> {
15//!     page.goto("https://example.com").goto().await?;
16//!     Ok(())
17//! }
18//! ```
19//!
20//! # Scoping
21//!
22//! The macro supports fixture scoping via attributes:
23//!
24//! ```ignore
25//! // Module-scoped browser
26//! #[viewpoint_test_macros::test(scope = "browser", browser = "shared_browser")]
27//! async fn fast_test(page: Page) -> Result<(), Box<dyn std::error::Error>> {
28//!     // ...
29//! }
30//! ```
31
32use proc_macro::TokenStream;
33use syn::{parse_macro_input, ItemFn};
34
35mod test_attr;
36
37/// Attribute macro for `Viewpoint` tests.
38///
39/// This macro transforms async test functions to include `TestHarness` setup
40/// and cleanup. Fixture parameters (Page, `BrowserContext`, Browser) are
41/// automatically extracted from the harness.
42///
43/// # Basic Usage
44///
45/// ```ignore
46/// #[viewpoint_test_macros::test]
47/// async fn my_test(page: Page) -> Result<(), Box<dyn std::error::Error>> {
48///     page.goto("https://example.com").goto().await?;
49///     Ok(())
50/// }
51/// ```
52///
53/// # Configuration Options
54///
55/// - `headless = true|false` - Run browser in headless mode (default: true)
56/// - `timeout = <ms>` - Default timeout in milliseconds (default: 30000)
57/// - `scope = "browser"|"context"` - Fixture scoping level
58/// - `browser = "<fn_name>"` - Function returning shared browser (required when scope = "browser")
59/// - `context = "<fn_name>"` - Function returning shared context (required when scope = "context")
60#[proc_macro_attribute]
61pub fn test(attr: TokenStream, item: TokenStream) -> TokenStream {
62    let args = parse_macro_input!(attr as test_attr::TestArgs);
63    let input = parse_macro_input!(item as ItemFn);
64
65    match test_attr::expand_test(args, input) {
66        Ok(tokens) => tokens.into(),
67        Err(err) => err.to_compile_error().into(),
68    }
69}