#![allow(
clippy::missing_errors_doc,
clippy::missing_panics_doc,
clippy::must_use_candidate,
clippy::doc_markdown,
clippy::module_name_repetitions,
clippy::cast_possible_truncation,
clippy::cast_precision_loss,
clippy::cast_sign_loss,
clippy::redundant_closure_for_method_calls,
clippy::implicit_clone,
clippy::too_many_lines,
clippy::uninlined_format_args,
clippy::type_complexity,
clippy::unnecessary_map_or,
clippy::match_same_arms,
clippy::should_implement_trait,
clippy::unnecessary_wraps,
clippy::unused_async,
clippy::items_after_statements,
clippy::needless_pass_by_value,
clippy::single_match_else,
clippy::vec_init_then_push,
clippy::from_over_into,
clippy::single_char_pattern,
clippy::ptr_arg,
clippy::unnecessary_sort_by,
clippy::collapsible_match,
clippy::if_same_then_else,
clippy::single_match
)]
#![allow(unused_imports)]
extern crate self as ferridriver_bdd;
pub use ferridriver_bdd_macros::{after, before, given, param_type, step, then, when};
pub use inventory;
pub mod data_table;
pub mod executor;
pub mod expression;
pub mod feature;
pub mod filter;
pub mod hook;
pub mod param_type;
pub mod registry;
pub mod scenario;
pub mod snippet;
pub mod step;
pub mod steps;
pub mod translate;
pub mod world;
pub mod prelude {
pub use crate::step::{DataTable, StepError, StepParam};
pub use crate::step_err;
pub use crate::world::BrowserWorld;
pub use ferridriver_bdd_macros::{after, before, given, param_type, step, then, when};
pub use ferridriver::Page;
}
#[macro_export]
macro_rules! step_err {
($($arg:tt)*) => {
$crate::step::StepError::from(format!($($arg)*))
};
}
#[macro_export]
macro_rules! bdd_main {
() => {
fn main() {
$crate::run_bdd_harness();
}
};
}
pub fn run_bdd_harness() {
use std::sync::Arc;
ferridriver_test::logging::init_from_env();
let rt = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.expect("failed to build tokio runtime");
let exit_code = rt.block_on(async {
let overrides = ferridriver_test::parse_common_cli_args();
let mut config = ferridriver_test::config::resolve_config(&overrides).unwrap_or_else(|e| {
eprintln!("config error: {e}");
std::process::exit(1);
});
let feature_patterns = std::env::var("FERRIDRIVER_FEATURES")
.ok()
.map(|s| s.split(',').map(String::from).collect::<Vec<_>>())
.unwrap_or_else(|| vec!["features/**/*.feature".to_string()]);
if config.features.is_empty() {
config.features = feature_patterns;
}
if let Ok(tags) = std::env::var("FERRIDRIVER_TAGS") {
if config.tags.is_none() {
config.tags = Some(tags);
}
}
if let Some(ref tags) = overrides.bdd_tags {
config.tags = Some(tags.clone());
}
if overrides.bdd_dry_run {
config.dry_run = true;
}
if overrides.bdd_fail_fast {
config.fail_fast = true;
}
if let Some(t) = overrides.bdd_step_timeout {
config.timeout = t;
}
if overrides.bdd_strict {
config.strict = true;
}
if let Some(ref order) = overrides.bdd_order {
config.order = order.clone();
}
if overrides.bdd_language.is_some() {
config.language = overrides.bdd_language.clone();
}
let feature_set = match feature::FeatureSet::discover_and_parse(&config.features, &config.test_ignore) {
Ok(fs) => fs,
Err(e) => {
eprintln!("feature discovery error: {e}");
return 1;
},
};
if feature_set.features.is_empty() {
eprintln!("no feature files found matching: {:?}", config.features);
return 0;
}
let registry = Arc::new(registry::StepRegistry::build());
let plan = translate::translate_features(&feature_set, registry, &config);
if plan.total_tests == 0 {
eprintln!("no scenarios found");
return 0;
}
config.has_bdd = true;
let mut runner = ferridriver_test::runner::TestRunner::new(config, overrides);
runner.run(plan).await
});
std::process::exit(exit_code);
}