allure_core/
lib.rs

1//! Allure Core - Core types and runtime for Allure test reporting.
2//!
3//! This crate provides the foundational types and runtime infrastructure for
4//! generating Allure test reports in Rust. It includes:
5//!
6//! - The complete Allure data model (test results, steps, attachments, etc.)
7//! - Enum types for status, stage, severity, and other classifications
8//! - A file writer for outputting results to the `allure-results` directory
9//! - Runtime context management for tracking test execution state
10//!
11//! # Example
12//!
13//! ```no_run
14//! use allure_core::{configure, runtime, enums::Severity};
15//!
16//! // Initialize the Allure runtime
17//! configure()
18//!     .results_dir("allure-results")
19//!     .clean_results(true)
20//!     .init()
21//!     .unwrap();
22//!
23//! // In a test, you can use the runtime API
24//! runtime::epic("My Epic");
25//! runtime::feature("My Feature");
26//! runtime::severity(Severity::Critical);
27//!
28//! runtime::step("Do something", || {
29//!     // test code here
30//! });
31//! ```
32
33pub mod enums;
34pub mod error;
35pub mod model;
36pub mod runtime;
37pub mod writer;
38
39// Re-exports for convenience
40pub use enums::{ContentType, LabelName, LinkType, ParameterMode, Severity, Stage, Status};
41pub use error::{AllureError, AllureResult};
42pub use model::{
43    Attachment, Category, FixtureResult, Label, Link, Parameter, StatusDetails, StepResult,
44    TestResult, TestResultContainer,
45};
46pub use runtime::{
47    allure_id, attach_binary, attach_file, attach_json, attach_text, configure, description,
48    description_html, display_name, epic, feature, flaky, issue, known_issue, label, link,
49    log_step, muted, owner, parameter, parent_suite, run_test, severity, step, story, sub_suite,
50    suite, tag, tags, test_case_id, title, tms, with_context, with_test_context, AllureConfig,
51    AllureConfigBuilder, TestContext,
52};
53pub use writer::{compute_history_id, generate_uuid, AllureWriter, DEFAULT_RESULTS_DIR};
54
55// Re-export futures for async panic handling in macros
56#[cfg(feature = "async")]
57pub use futures;
58
59/// BDD-style step functions for behavior-driven testing.
60pub mod bdd {
61    use crate::runtime::step;
62
63    /// Executes a "Given" step (precondition).
64    pub fn given<F, R>(description: impl Into<String>, body: F) -> R
65    where
66        F: FnOnce() -> R,
67    {
68        step(format!("Given {}", description.into()), body)
69    }
70
71    /// Executes a "When" step (action).
72    pub fn when<F, R>(description: impl Into<String>, body: F) -> R
73    where
74        F: FnOnce() -> R,
75    {
76        step(format!("When {}", description.into()), body)
77    }
78
79    /// Executes a "Then" step (assertion).
80    pub fn then<F, R>(description: impl Into<String>, body: F) -> R
81    where
82        F: FnOnce() -> R,
83    {
84        step(format!("Then {}", description.into()), body)
85    }
86
87    /// Executes an "And" step (continuation).
88    pub fn and<F, R>(description: impl Into<String>, body: F) -> R
89    where
90        F: FnOnce() -> R,
91    {
92        step(format!("And {}", description.into()), body)
93    }
94
95    /// Executes a "But" step (negative continuation).
96    pub fn but<F, R>(description: impl Into<String>, body: F) -> R
97    where
98        F: FnOnce() -> R,
99    {
100        step(format!("But {}", description.into()), body)
101    }
102}
103
104/// Attachment helper module with convenience functions.
105pub mod attachment {
106    use crate::enums::ContentType;
107    use crate::runtime::{attach_binary, attach_file as attach_file_fn, attach_json, attach_text};
108
109    /// Attaches text content.
110    pub fn text(name: impl Into<String>, content: impl AsRef<str>) {
111        attach_text(name, content);
112    }
113
114    /// Attaches JSON content.
115    pub fn json<T: serde::Serialize>(name: impl Into<String>, value: &T) {
116        attach_json(name, value);
117    }
118
119    /// Attaches binary content.
120    pub fn binary(name: impl Into<String>, content: &[u8], content_type: ContentType) {
121        attach_binary(name, content, content_type);
122    }
123
124    /// Attaches a file from the filesystem.
125    pub fn file(
126        name: impl Into<String>,
127        path: impl AsRef<std::path::Path>,
128        content_type: Option<ContentType>,
129    ) {
130        attach_file_fn(name, path, content_type);
131    }
132
133    /// Attaches a PNG image.
134    pub fn png(name: impl Into<String>, content: &[u8]) {
135        attach_binary(name, content, ContentType::Png);
136    }
137
138    /// Attaches a JPEG image.
139    pub fn jpeg(name: impl Into<String>, content: &[u8]) {
140        attach_binary(name, content, ContentType::Jpeg);
141    }
142
143    /// Attaches HTML content.
144    pub fn html(name: impl Into<String>, content: impl AsRef<str>) {
145        attach_binary(name, content.as_ref().as_bytes(), ContentType::Html);
146    }
147
148    /// Attaches XML content.
149    pub fn xml(name: impl Into<String>, content: impl AsRef<str>) {
150        attach_binary(name, content.as_ref().as_bytes(), ContentType::Xml);
151    }
152
153    /// Attaches CSV content.
154    pub fn csv(name: impl Into<String>, content: impl AsRef<str>) {
155        attach_binary(name, content.as_ref().as_bytes(), ContentType::Csv);
156    }
157}
158
159/// Environment info builder for generating `environment.properties`.
160pub struct EnvironmentBuilder {
161    properties: Vec<(String, String)>,
162    results_dir: String,
163}
164
165impl EnvironmentBuilder {
166    /// Creates a new environment builder.
167    pub fn new() -> Self {
168        Self {
169            properties: Vec::new(),
170            results_dir: DEFAULT_RESULTS_DIR.to_string(),
171        }
172    }
173
174    /// Sets the results directory.
175    pub fn results_dir(mut self, path: impl Into<String>) -> Self {
176        self.results_dir = path.into();
177        self
178    }
179
180    /// Adds a key-value pair.
181    pub fn set(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
182        self.properties.push((key.into(), value.into()));
183        self
184    }
185
186    /// Adds a key-value pair from an environment variable.
187    pub fn set_from_env(mut self, key: impl Into<String>, env_var: &str) -> Self {
188        if let Ok(value) = std::env::var(env_var) {
189            self.properties.push((key.into(), value));
190        }
191        self
192    }
193
194    /// Writes the environment.properties file.
195    pub fn write(self) -> std::io::Result<std::path::PathBuf> {
196        let writer = AllureWriter::with_results_dir(&self.results_dir);
197        writer.write_environment(&self.properties)
198    }
199}
200
201impl Default for EnvironmentBuilder {
202    fn default() -> Self {
203        Self::new()
204    }
205}
206
207/// Creates a new environment builder.
208pub fn environment() -> EnvironmentBuilder {
209    EnvironmentBuilder::new()
210}
211
212/// Categories configuration builder.
213pub struct CategoriesBuilder {
214    categories: Vec<Category>,
215    results_dir: String,
216}
217
218impl CategoriesBuilder {
219    /// Creates a new categories builder.
220    pub fn new() -> Self {
221        Self {
222            categories: Vec::new(),
223            results_dir: DEFAULT_RESULTS_DIR.to_string(),
224        }
225    }
226
227    /// Sets the results directory.
228    pub fn results_dir(mut self, path: impl Into<String>) -> Self {
229        self.results_dir = path.into();
230        self
231    }
232
233    /// Adds a category.
234    pub fn with_category(mut self, category: Category) -> Self {
235        self.categories.push(category);
236        self
237    }
238
239    /// Adds the default product defects category.
240    pub fn with_product_defects(mut self) -> Self {
241        self.categories
242            .push(Category::new("Product defects").with_status(Status::Failed));
243        self
244    }
245
246    /// Adds the default test defects category.
247    pub fn with_test_defects(mut self) -> Self {
248        self.categories
249            .push(Category::new("Test defects").with_status(Status::Broken));
250        self
251    }
252
253    /// Writes the categories.json file.
254    pub fn write(self) -> std::io::Result<std::path::PathBuf> {
255        let writer = AllureWriter::with_results_dir(&self.results_dir);
256        writer.write_categories(&self.categories)
257    }
258}
259
260impl Default for CategoriesBuilder {
261    fn default() -> Self {
262        Self::new()
263    }
264}
265
266/// Creates a new categories builder.
267pub fn categories() -> CategoriesBuilder {
268    CategoriesBuilder::new()
269}
270
271#[cfg(test)]
272mod tests {
273    use super::*;
274
275    #[test]
276    fn test_bdd_step_names() {
277        // Just verify the API compiles and returns values correctly
278        let result = bdd::given("a value", || 42);
279        assert_eq!(result, 42);
280
281        let result = bdd::when("something happens", || "ok");
282        assert_eq!(result, "ok");
283
284        let result = bdd::then("we check", || true);
285        assert!(result);
286    }
287
288    #[test]
289    fn test_environment_builder() {
290        let builder = environment().set("key1", "value1").set("key2", "value2");
291
292        assert_eq!(builder.properties.len(), 2);
293    }
294
295    #[test]
296    fn test_categories_builder() {
297        let builder = categories()
298            .with_product_defects()
299            .with_test_defects()
300            .with_category(Category::new("Custom").with_status(Status::Skipped));
301
302        assert_eq!(builder.categories.len(), 3);
303    }
304}