Skip to content

Commit

Permalink
Added a simple implementation for examples
Browse files Browse the repository at this point in the history
  • Loading branch information
stefanpieck authored and bbqsrc committed Sep 21, 2020
1 parent 2a09353 commit 212408b
Show file tree
Hide file tree
Showing 7 changed files with 114 additions and 18 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@
**/*.rs.bk
Cargo.lock
.idea/
.vscode/launch.json
3 changes: 2 additions & 1 deletion src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use super::ExampleValues;
use std::{fmt::Display, rc::Rc};

#[derive(Debug, Clone, PartialEq, Eq)]
Expand Down Expand Up @@ -73,7 +74,7 @@ pub enum StepEvent {

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ScenarioEvent {
Starting,
Starting(ExampleValues),
Background(Rc<gherkin::Step>, StepEvent),
Step(Rc<gherkin::Step>, StepEvent),
Skipped,
Expand Down
64 changes: 64 additions & 0 deletions src/examples.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ExampleValues {
keys: Vec<String>,
values: Vec<String>,
}

impl ExampleValues {
/// When no examples exist a vector with one empty ExampleValues struct is returned.
pub fn from_examples(examples: &Option<gherkin::Examples>) -> Vec<ExampleValues> {
match examples {
Some(examples) => {
let mut rows = Vec::with_capacity(examples.table.rows.len());
for row_index in 1..examples.table.rows.len() {
rows.push(ExampleValues::new(
&examples.table.rows.first().unwrap().to_vec(),
&examples.table.rows.get(row_index).unwrap().to_vec(),
))
}
rows
}
None => vec![ExampleValues::empty()],
}
}

pub fn new(keys: &Vec<String>, values: &Vec<String>) -> ExampleValues {
ExampleValues {
keys: keys.into_iter().map(|val| format!("<{}>", val)).collect(),
values: values.to_vec(),
}
}

pub fn empty() -> ExampleValues {
ExampleValues {
keys: vec![],
values: vec![],
}
}

pub fn is_empty(&self) -> bool {
self.keys.is_empty()
}

pub fn insert_values(&self, step: &String) -> String {
let mut modified = step.to_owned();
for index in 0..self.keys.len() {
let search = self.keys.get(index).unwrap_or(&String::new()).to_owned();
let replace_with = self.values.get(index).unwrap_or(&String::new()).to_owned();
modified = modified.replace(&search, &replace_with);
}
modified
}

pub fn as_string(&self) -> String {
let mut values = Vec::with_capacity(self.keys.len());
for index in 0..self.keys.len() {
values.push(format!(
"{} = {}",
self.keys.get(index).unwrap_or(&String::new()),
self.values.get(index).unwrap_or(&String::new())
));
}
values.join(", ")
}
}
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ mod macros;
mod collection;
mod cucumber;
mod event;
mod examples;
mod output;
mod regex;
mod runner;
Expand All @@ -27,6 +28,7 @@ use async_trait::async_trait;
use std::panic::UnwindSafe;

pub use cucumber::Cucumber;
pub use examples::ExampleValues;
pub use steps::Steps;

const TEST_SKIPPED: &str = "Cucumber: test skipped";
Expand Down
19 changes: 11 additions & 8 deletions src/output/default.rs
Original file line number Diff line number Diff line change
Expand Up @@ -322,17 +322,20 @@ impl BasicOutput {
event: ScenarioEvent,
) {
match event {
ScenarioEvent::Starting => {
ScenarioEvent::Starting(example_values) => {
self.scenarios.total += 1;
let cmt = self.file_line_col(feature.path.as_ref(), scenario.position);
let text = if example_values.is_empty() {
format!("Scenario: {} ", &scenario.name)
} else {
format!(
"Scenario: {}\nUsing example: {}",
&scenario.name,
&example_values.as_string(),
)
};
let indent = if rule.is_some() { " " } else { " " };
self.writeln_cmt(
&format!("Scenario: {}", &scenario.name),
&cmt,
indent,
termcolor::Color::White,
true,
);
self.writeln_cmt(&text, &cmt, indent, termcolor::Color::White, true);
}
ScenarioEvent::Background(step, event) => {
self.handle_step(feature, rule, scenario, step, event, true)
Expand Down
30 changes: 21 additions & 9 deletions src/runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,14 @@ use crate::collection::StepsCollection;
use crate::event::*;
use crate::{World, TEST_SKIPPED};

use super::ExampleValues;

pub(crate) type PanicError = Box<(dyn Any + Send + 'static)>;
pub(crate) type TestFuture<W> = Pin<Box<dyn Future<Output = Result<W, PanicError>>>>;

pub type BasicStepFn<W> = Rc<dyn Fn(W, Rc<gherkin::Step>) -> TestFuture<W> + UnwindSafe>;
pub type RegexStepFn<W> = Rc<dyn Fn(W, Vec<String>, Rc<gherkin::Step>) -> TestFuture<W> + UnwindSafe>;
pub type RegexStepFn<W> =
Rc<dyn Fn(W, Vec<String>, Rc<gherkin::Step>) -> TestFuture<W> + UnwindSafe>;

pub enum TestFunction<W> {
Basic(BasicStepFn<W>),
Expand Down Expand Up @@ -125,13 +128,17 @@ impl<W: World> Runner<W> {
yield FeatureEvent::Starting;

for scenario in feature.scenarios.iter() {
let this = Rc::clone(&self);
let scenario = Rc::new(scenario.clone());
let examples = ExampleValues::from_examples(&scenario.examples);
for example_values in examples {
let this = Rc::clone(&self);
let scenario = Rc::new(scenario.clone());

let mut stream = this.run_scenario(Rc::clone(&scenario), Rc::clone(&feature));
let mut stream = this.run_scenario(Rc::clone(&scenario), Rc::clone(&feature), example_values);

while let Some(event) = stream.next().await {
yield FeatureEvent::Scenario(Rc::clone(&scenario), event);
while let Some(event) = stream.next().await {
yield FeatureEvent::Scenario(Rc::clone(&scenario), event);

}
}
}

Expand Down Expand Up @@ -164,7 +171,7 @@ impl<W: World> Runner<W> {
let this = Rc::clone(&self);
let scenario = Rc::new(scenario.clone());

let mut stream = this.run_scenario(Rc::clone(&scenario), Rc::clone(&feature));
let mut stream = this.run_scenario(Rc::clone(&scenario), Rc::clone(&feature), ExampleValues::empty());

while let Some(event) = stream.next().await {
match event {
Expand All @@ -185,9 +192,10 @@ impl<W: World> Runner<W> {
self: Rc<Self>,
scenario: Rc<gherkin::Scenario>,
feature: Rc<gherkin::Feature>,
example: super::ExampleValues,
) -> ScenarioStream {
Box::pin(stream! {
yield ScenarioEvent::Starting;
yield ScenarioEvent::Starting(example.clone());
let mut world = Some(W::new().await.unwrap());

if let Some(steps) = feature.background.as_ref().map(|x| &x.steps) {
Expand Down Expand Up @@ -225,7 +233,11 @@ impl<W: World> Runner<W> {
for step in scenario.steps.iter() {
let this = Rc::clone(&self);

let step = Rc::new(step.clone());
let mut step = step.clone();
if !example.is_empty() {
step.value = example.insert_values(&step.value);
}
let step = Rc::new(step);
let result = this.run_step(Rc::clone(&step), world.take().unwrap()).await;

match result {
Expand Down
13 changes: 13 additions & 0 deletions tests/cucumber_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,19 @@ mod example_steps {
assert_eq!(matches[1], "implement");
world
},
)
.given_regex(r"a number (\d+)", |mut world, matches, _step| {
world.foo = matches[1].to_owned();
world
})
.then_regex(
r"twice that number should be (\d+)",
|world, matches, _step| {
let to_check = world.foo.parse::<i32>().unwrap();
let expected = matches[1].parse::<i32>().unwrap();
assert_eq!(to_check * 2, expected);
world
},
);

builder
Expand Down

0 comments on commit 212408b

Please sign in to comment.