Skip to content

Commit

Permalink
Declare default CLI options as global (#216, #215)
Browse files Browse the repository at this point in the history
- describe Cargo aliases in Book

Co-authored-by: Kai Ren <tyranron@gmail.com>
  • Loading branch information
theredfish and tyranron committed May 26, 2022
1 parent c3e8de4 commit 72eff59
Show file tree
Hide file tree
Showing 10 changed files with 273 additions and 10 deletions.
11 changes: 10 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,16 @@ All user visible changes to `cucumber` crate will be documented in this file. Th

### BC Breaks

- Bump up [MSRV] to 1.61 for more clever support of [Cargo feature]s and simplified codegen.
- Bump up [MSRV] to 1.61 for more clever support of [Cargo feature]s and simplified codegen. ([fbd08ec2], [cf055ac0])

### Changed

- Provided default CLI options are now global (allowed to be specified after custom subcommands). ([#216], [#215])

[#215]: /../../issues/215
[#216]: /../../pull/216
[cf055ac0]: /../../commit/cf055ac06c7b72f572882ce15d6a60da92ad60a0
[fbd08ec2]: /../../commit/fbd08ec24dbd036c89f5f0af4d936b616790a166



Expand Down
121 changes: 121 additions & 0 deletions book/src/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,123 @@ async fn main() {



## Aliasing

[Cargo alias] is a neat way to define shortcuts for regularly used customized tests running commands.

```rust
# use std::{convert::Infallible, time::Duration};
#
# use async_trait::async_trait;
# use cucumber::{cli, given, then, when, World, WorldInit};
# use futures::FutureExt as _;
# use tokio::time::sleep;
#
# #[derive(Debug, Default)]
# struct Animal {
# pub hungry: bool,
# }
#
# impl Animal {
# fn feed(&mut self) {
# self.hungry = false;
# }
# }
#
# #[derive(Debug, WorldInit)]
# pub struct AnimalWorld {
# cat: Animal,
# }
#
# #[async_trait(?Send)]
# impl World for AnimalWorld {
# type Error = Infallible;
#
# async fn new() -> Result<Self, Infallible> {
# Ok(Self {
# cat: Animal::default(),
# })
# }
# }
#
# #[given(regex = r"^a (hungry|satiated) cat$")]
# async fn hungry_cat(world: &mut AnimalWorld, state: String) {
# 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) {
# world.cat.feed();
# }
#
# #[then("the cat is not hungry")]
# async fn cat_is_fed(world: &mut AnimalWorld) {
# assert!(!world.cat.hungry);
# }
#
#[derive(clap::Args)]
struct CustomOpts {
#[clap(subcommand)]
command: Option<SubCommand>,
}

#[derive(clap::Subcommand)]
enum SubCommand {
Smoke(Smoke),
}

#[derive(clap::Args)]
struct Smoke {
/// Additional time to wait in before hook.
#[clap(
long,
parse(try_from_str = humantime::parse_duration)
)]
pre_pause: Option<Duration>,
}

#[tokio::main]
async fn main() {
let opts = cli::Opts::<_, _, _, CustomOpts>::parsed();

let pre_pause = if let Some(SubCommand::Smoke(Smoke { pre_pause })) =
opts.custom.command
{
pre_pause
} else {
None
}
.unwrap_or_default();

AnimalWorld::cucumber()
.before(move |_, _, _, _| sleep(pre_pause).boxed_local())
.with_cli(opts)
.run_and_exit("tests/features/book/cli.feature")
.await;
}
```

The alias should be specified in `.cargo/config.toml` file of the project:
```yaml
[alias]
smoke = "test -p cucumber --test cli -- smoke --pre-pause=5s -vv --fail-fast"
```

Now it can be used as:
```bash
cargo smoke
cargo smoke --tags=@hungry
```

> __NOTE__: The default CLI options may be specified after a custom subcommand, because they are defined as [global][1] ones. This may be applied to custom CLI options too, if necessary.



[`cli::Compose`]: https://docs.rs/cucumber/*/cucumber/cli/struct.Compose.html
[`cli::Empty`]: https://docs.rs/cucumber/*/cucumber/cli/struct.Empty.html
[`cucumber`]: https://docs.rs/cucumber
Expand All @@ -162,3 +279,7 @@ async fn main() {
[`Runner::Cli`]: https://docs.rs/cucumber/*/cucumber/trait.Runner.html#associatedtype.Cli
[`Writer`]: architecture/writer.md
[`Writer::Cli`]: https://docs.rs/cucumber/*/cucumber/trait.Writer.html#associatedtype.Cli

[Cargo alias]: https://doc.rust-lang.org/cargo/reference/config.html#alias

[1]: https://docs.rs/clap/latest/clap/struct.Arg.html#method.global
5 changes: 4 additions & 1 deletion codegen/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ All user visible changes to `cucumber-codegen` crate will be documented in this

### BC Breaks

- Bump up [MSRV] to 1.61 for more clever support of [Cargo feature]s and simplified codegen.
- Bump up [MSRV] to 1.61 for more clever support of [Cargo feature]s and simplified codegen. ([fbd08ec2], [cf055ac0])

[cf055ac0]: /../../commit/cf055ac06c7b72f572882ce15d6a60da92ad60a0
[fbd08ec2]: /../../commit/fbd08ec24dbd036c89f5f0af4d936b616790a166



Expand Down
6 changes: 4 additions & 2 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,8 @@ where
short = 'n',
long = "name",
name = "regex",
visible_alias = "scenario-name"
visible_alias = "scenario-name",
global = true
)]
pub re_filter: Option<Regex>,

Expand All @@ -118,7 +119,8 @@ where
short = 't',
long = "tags",
name = "tagexpr",
conflicts_with = "regex"
conflicts_with = "regex",
global = true
)]
pub tags_filter: Option<TagOperation>,

Expand Down
2 changes: 1 addition & 1 deletion src/parser/basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ use super::{Error as ParseError, Parser};
pub struct Cli {
/// Glob pattern to look for feature files with. By default, looks for
/// `*.feature`s in the path configured tests runner.
#[clap(long = "input", short = 'i', name = "glob")]
#[clap(long = "input", short = 'i', name = "glob", global = true)]
pub features: Option<Walker>,
}

Expand Down
4 changes: 2 additions & 2 deletions src/runner/basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,11 @@ use crate::{
pub struct Cli {
/// Number of scenarios to run concurrently. If not specified, uses the
/// value configured in tests runner, or 64 by default.
#[clap(long, short, name = "int")]
#[clap(long, short, name = "int", global = true)]
pub concurrency: Option<usize>,

/// Run tests until the first failure.
#[clap(long)]
#[clap(long, global = true)]
pub fail_fast: bool,
}

Expand Down
9 changes: 7 additions & 2 deletions src/writer/basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,16 @@ pub struct Cli {
///
/// `-v` is default verbosity, `-vv` additionally outputs world on failed
/// steps, `-vvv` additionally outputs step's doc string (if present).
#[clap(short, parse(from_occurrences))]
#[clap(short, parse(from_occurrences), global = true)]
pub verbose: u8,

/// Coloring policy for a console output.
#[clap(long, name = "auto|always|never", default_value = "auto")]
#[clap(
long,
name = "auto|always|never",
default_value = "auto",
global = true
)]
pub color: Coloring,
}

Expand Down
2 changes: 1 addition & 1 deletion src/writer/junit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ pub struct Cli {
///
/// `0` is default verbosity, `1` additionally outputs world on failed
/// steps.
#[clap(long = "junit-v", name = "0|1")]
#[clap(long = "junit-v", name = "0|1", global = true)]
pub verbose: Option<u8>,
}

Expand Down
113 changes: 113 additions & 0 deletions tests/cli.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
use std::{convert::Infallible, panic::AssertUnwindSafe};

use async_trait::async_trait;
use clap::Parser;
use cucumber::{cli, given, WorldInit};
use futures::FutureExt as _;

#[derive(cli::Args)]
struct CustomCli {
#[clap(subcommand)]
command: Option<SubCommand>,
}

#[derive(clap::Subcommand)]
enum SubCommand {
Smoke(Smoke),
}

#[derive(cli::Args)]
struct Smoke {
#[clap(long)]
report_name: String,
}

#[derive(Clone, Copy, Debug, WorldInit)]
struct World;

#[async_trait(?Send)]
impl cucumber::World for World {
type Error = Infallible;

async fn new() -> Result<Self, Self::Error> {
Ok(World)
}
}

#[given("an invalid step")]
fn invalid_step(_world: &mut World) {
assert!(false);
}

// This test uses a subcommand with the global option `--tags` to filter on two
// failing tests and verifies that the error output contains 2 failing steps.
#[tokio::test]
async fn tags_option_filters_all_scenarios_with_subcommand() {
let cli = cli::Opts::<_, _, _, CustomCli>::try_parse_from(&[
"test",
"smoke",
r#"--report-name="smoke.report""#,
"--tags=@all",
])
.expect("Invalid command line");

let res = World::cucumber()
.with_cli(cli)
.run_and_exit("tests/features/cli");

let err = AssertUnwindSafe(res)
.catch_unwind()
.await
.expect_err("should err");
let err = err.downcast_ref::<String>().unwrap();

assert_eq!(err, "2 steps failed");
}

// This test uses a subcommand with the global option `--tags` to filter on one
// failing test and verifies that the error output contains 1 failing step.
#[tokio::test]
async fn tags_option_filters_scenario1_with_subcommand() {
let cli = cli::Opts::<_, _, _, CustomCli>::try_parse_from(&[
"test",
"smoke",
r#"--report-name="smoke.report""#,
"--tags=@scenario-1",
])
.expect("Invalid command line");

let res = World::cucumber()
.with_cli(cli)
.run_and_exit("tests/features/cli");

let err = AssertUnwindSafe(res)
.catch_unwind()
.await
.expect_err("should err");
let err = err.downcast_ref::<String>().unwrap();

assert_eq!(err, "1 step failed");
}

// This test verifies that the global option `--tags` is still available without
// subcommands and that the error output contains 1 failing step.
#[tokio::test]
async fn tags_option_filters_scenario1_no_subcommand() {
let cli = cli::Opts::<_, _, _, CustomCli>::try_parse_from(&[
"test",
"--tags=@scenario-1",
])
.expect("Invalid command line");

let res = World::cucumber()
.with_cli(cli)
.run_and_exit("tests/features/cli");

let err = AssertUnwindSafe(res)
.catch_unwind()
.await
.expect_err("should err");
let err = err.downcast_ref::<String>().unwrap();

assert_eq!(err, "1 step failed");
}
10 changes: 10 additions & 0 deletions tests/features/cli/subcomand_global_option.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Feature: Global option `--tags` with subcommands

@scenario-1 @all
Scenario: Two invalid steps
Given an invalid step
And an invalid step

@scenario-2 @all
Scenario: One invalid step
Given an invalid step

0 comments on commit 72eff59

Please sign in to comment.