Custom Parser
Let's start by implementing a custom Parser
which statically emits a single feature for execution.
Parser
represents anything that emits a Stream
of features.
extern crate cucumber; extern crate futures; extern crate tokio; use std::{path::PathBuf, time::Duration}; use cucumber::{cli, gherkin, given, parser, then, when, World}; use futures::{future, stream}; use tokio::time::sleep; #[derive(Debug, Default)] struct Animal { pub hungry: bool, } impl Animal { fn feed(&mut self) { self.hungry = false; } } #[derive(Debug, Default, World)] pub struct AnimalWorld { cat: Animal, } #[given(regex = r"^a (hungry|satiated) cat$")] async fn hungry_cat(world: &mut AnimalWorld, state: String) { sleep(Duration::from_secs(2)).await; match state.as_str() { "hungry" => world.cat.hungry = true, "satiated" => world.cat.hungry = false, _ => unreachable!(), } } #[when("I feed the cat")] async fn feed_cat(world: &mut AnimalWorld) { sleep(Duration::from_secs(2)).await; world.cat.feed(); } #[then("the cat is not hungry")] async fn cat_is_fed(world: &mut AnimalWorld) { sleep(Duration::from_secs(2)).await; assert!(!world.cat.hungry); } struct CustomParser; impl<I> cucumber::Parser<I> for CustomParser { type Cli = cli::Empty; // we provide no CLI options type Output = stream::Once<future::Ready<parser::Result<gherkin::Feature>>>; fn parse(self, _: I, _: Self::Cli) -> Self::Output { let keyword = "Feature"; let name = "Animal feature"; stream::once(future::ok(gherkin::Feature { keyword: keyword.into(), name: name.into(), description: None, background: None, scenarios: vec![gherkin::Scenario { keyword: "Scenario".into(), name: "If we feed a hungry cat it won't be hungry".into(), description: None, steps: vec![ gherkin::Step { keyword: "Given ".into(), ty: gherkin::StepType::Given, value: "a hungry cat".into(), docstring: None, table: None, span: gherkin::Span { start: 5, end: 18 }, position: gherkin::LineCol { line: 3, col: 5 }, }, gherkin::Step { keyword: "When ".into(), ty: gherkin::StepType::When, value: "I feed the cat".into(), docstring: None, table: None, span: gherkin::Span { start: 5, end: 19 }, position: gherkin::LineCol { line: 4, col: 5 }, }, gherkin::Step { keyword: "Then ".into(), ty: gherkin::StepType::Then, value: "the cat is not hungry".into(), docstring: None, table: None, span: gherkin::Span { start: 5, end: 26 }, position: gherkin::LineCol { line: 5, col: 5 }, }, ], examples: vec![], tags: vec![], span: gherkin::Span { start: 3, end: 52 }, position: gherkin::LineCol { line: 2, col: 3 }, }], rules: vec![], tags: vec![], span: gherkin::Span { start: 1, end: 23 }, position: gherkin::LineCol { line: 1, col: 1 }, path: Some(PathBuf::from(file!())), })) } } #[tokio::main] async fn main() { AnimalWorld::cucumber::<&str>() // aiding type inference .with_parser(CustomParser) .run_and_exit("tests/features/book") // path doesn't actually matter .await; // here due to our implementation }