cucumber_trellis/
trellis.rs1use super::{result::TestResult, CucumberTest};
2use futures::future::join_all;
3use path_absolutize::Absolutize;
4use std::{
5 path::{PathBuf, Path},
6 env::current_dir,
7};
8
9pub struct CucumberTrellis {
10 path_base: PathBuf,
11 lib_test: bool,
12 tests: Vec<TestResult>,
13}
14
15impl CucumberTrellis {
16 pub fn new(feature_path_base: Option<&Path>) -> Self {
21 let path_base = match feature_path_base {
22 None => current_dir().unwrap().join("tests").join("features"),
23 Some(p) => p.absolutize().unwrap().to_path_buf(),
24 };
25
26 if !path_base.exists() {
27 panic!("Path does not exist: {}", path_base.display());
28 }
29
30 if !path_base.is_dir() {
31 panic!("Path is not a directory: {}", path_base.display());
32 }
33
34 Self {
35 path_base,
36 lib_test: false,
37 tests: Vec::new(),
38 }
40 }
42
43 pub fn add_test<T: CucumberTest>(&mut self) {
45 let name = format!("{}.feature", T::NAME);
46 let feature_path = self.path_base.join(&name);
47
48 if !feature_path.exists() {
49 panic!("Feature file does not exist: {}", feature_path.display());
50 }
51
52 #[cfg(feature = "libtest")]
53 let result = {
54 use cucumber::{
55 parser::basic::Cli as Parser,
56 runner::basic::Cli as Runner,
57 writer::{
58 libtest::{ReportTime, Cli as Writer},
59 Libtest,
60 },
61 cli::Opts,
62 WriterExt,
63 };
64
65 use std::io::stdout;
66
67 let mut cli = Opts::<Parser, Runner, Writer>::parsed();
68 cli.writer.report_time = Some(ReportTime::Plain);
69
70 let mut cucumber = T::cucumber()
71 .with_writer(Libtest::new(stdout()).normalized())
72 .with_cli(cli);
73
74 T::config(&mut cucumber);
75 TestResult::new(async {
76 drop(cucumber.run(feature_path).await);
77 })
78 };
79
80 #[cfg(not(feature = "libtest"))]
81 let result = {
82 let mut cucumber = T::cucumber();
83
84 T::config(&mut cucumber);
85 TestResult::new(async {
86 drop(cucumber.run(feature_path).await);
87 })
88 };
89
90 self.tests.push(result);
91 }
92
93 pub async fn run_tests(self) {
95 join_all(self.tests).await;
96 }
97}
98
99#[cfg(test)]
101mod tests {
102 use super::*;
103 use cucumber::World;
104
105 #[test]
106 fn test_new_trellis_with_current_path() {
107 let path_base = current_dir().unwrap().join("tests").join("features");
108 let trellis = CucumberTrellis::new(None);
109
110 assert_eq!(trellis.path_base, path_base, "path base should be `./tests/features`");
111 assert!(trellis.tests.is_empty(), "no tests should be added");
112 }
113
114 #[test]
115 fn test_new_trellis_with_any_path() {
116 let path_base = current_dir().unwrap().join("tests").join("features");
117 let trellis = CucumberTrellis::new(Some(&path_base));
118
119 assert_eq!(trellis.path_base, path_base, "path base should be `./tests/features`");
120 assert!(trellis.tests.is_empty(), "no tests should be added");
121 }
122
123 #[test]
124 #[should_panic]
125 fn test_new_trellis_with_nonexistent_path() {
126 CucumberTrellis::new(Some(&PathBuf::from("!existent")));
127 }
128
129 #[test]
130 #[should_panic]
131 fn test_new_trellis_with_path_on_file() {
132 CucumberTrellis::new(Some(&PathBuf::from("Cargo.toml")));
133 }
134
135 #[test]
136 fn test_add_test() {
137 #[derive(World, Debug, Default)]
138 pub(in super::super) struct SimpleTest;
139
140 impl CucumberTest for SimpleTest {
141 const NAME: &'static str = "simple-test";
142 }
143
144 let mut trellis = CucumberTrellis::new(None);
145 trellis.add_test::<SimpleTest>();
146 }
147
148 #[test]
149 #[should_panic]
150 fn test_add_test_with_nonexistent_feature_file() {
151 #[derive(World, Debug, Default)]
152 pub(in super::super) struct NoFeatureTest;
153
154 impl CucumberTest for NoFeatureTest {
155 const NAME: &'static str = "!existent";
156 }
157
158 let mut trellis = CucumberTrellis::new(None);
159 trellis.add_test::<NoFeatureTest>();
160 }
161}
162