Coder Social home page Coder Social logo

cucumber / cucumber-expressions Goto Github PK

View Code? Open in Web Editor NEW
139.0 71.0 48.0 9.87 MB

Human friendly alternative to Regular Expressions

License: MIT License

Makefile 0.04% Go 16.15% Ruby 12.64% Java 30.10% TypeScript 13.39% C# 14.17% Python 13.50%
dotnet go java javascript python ruby polyglot-release tidelift

cucumber-expressions's Introduction

test-go test-java test-javascript test-python test-ruby test-dotnet

Cucumber Expressions

Cucumber Expressions is an alternative to Regular Expressions with a more intuitive syntax.

Try Cucumber Expressions in your browser.

Cucumber supports both Cucumber Expressions and Regular Expressions for defining Step Definitions, but you cannot mix Cucumber Expression syntax with Regular Expression syntax in the same expression.

On platforms that don't have a literal syntax for regular expressions (such as Java), Cucumber will create a Cucumber Expression by default. To use Regular Expressions, add anchors (starting with ^ and ending with $) or forward slashes (/). For more information, see Cucumber Expression - Java Heuristics.

Introduction

Let's write a Cucumber Expression that matches the following Gherkin step (the Given keyword has been removed here, as it's not part of the match).

I have 42 cucumbers in my belly

The simplest Cucumber Expression that matches that text would be the text itself, but we can also write a more generic expression, with an int output parameter:

I have {int} cucumbers in my belly

When the text is matched against that expression, the number 42 is extracted from the {int} output parameter and passed as an argument to the step definition.

The following text would not match the expression:

I have 42.5 cucumbers in my belly

This is because 42.5 has a decimal part, and doesn't fit into an int. Let's change the output parameter to float instead:

I have {float} cucumbers in my belly

Now the expression will match the text, and the float 42.5 is extracted.

Parameter types

Text between curly braces reference a parameter type. Cucumber comes with the following built-in parameter types:

Parameter Type Description
{int} Matches integers, for example 71 or -19. Converts to a 32-bit signed integer if the platform supports it.
{float} Matches floats, for example 3.6, .8 or -9.2. Converts to a 32 bit float if the platform supports it.
{word} Matches words without whitespace, for example banana (but not banana split).
{string} Matches single-quoted or double-quoted strings, for example "banana split" or 'banana split' (but not banana split). Only the text between the quotes will be extracted. The quotes themselves are discarded. Empty pairs of quotes are valid and will be matched and passed to step code as empty strings.
{} anonymous Matches anything (/.*/).
{bigdecimal} Matches the same as {float}, but converts to a BigDecimal if the platform supports it.
{double} Matches the same as {float}, but converts to a 64 bit float if the platform supports it.
{biginteger} Matches the same as {int}, but converts to a BigInteger if the platform supports it.
{byte} Matches the same as {int}, but converts to an 8 bit signed integer if the platform supports it.
{short} Matches the same as {int}, but converts to a 16 bit signed integer if the platform supports it.
{long} Matches the same as {int}, but converts to a 64 bit signed integer if the platform supports it.

Java

The Anonymous Parameter

The anonymous parameter type will be converted to the parameter type of the step definition using an object mapper. Cucumber comes with a built-in object mapper that can handle all numeric types as well as. Enum.

To automatically convert to other types it is recommended to install an object mapper. See cucumber-java - Default Transformers to learn how.

Number formats

Java supports parsing localised numbers. I.e. in your English feature file you can format a-thousand-and-one-tenth as '1,000.1; while in French you would format it as '1.000,1'.

Parsing is facilitated by Javas DecimalFormat and includes support for the scientific notation. Unfortunately the default localisation include symbols that can not be easily written on a regular keyboard. So a few substitutions are made:

  • The minus sign is always hyphen-minus - (ascii 45).
  • If the decimal separator is a period (. ascii 46) the thousands separator is a comma (, ascii 44). So '1 000.1' and '1’000.1' should always be written as '1,000.1'.
  • If the decimal separator is a comma (, ascii 44) the thousands separator is a period (. ascii 46). So '1 000,1' or '1’000,1' should always be written as '1.000,1'.

If support for your preferred language could be improved, please create an issue!

Custom Parameter types

Cucumber Expressions can be extended so they automatically convert output parameters to your own types. Consider this Cucumber Expression:

I have a {color} ball

If we want the {color} output parameter to be converted to a Color object, we can define a custom parameter type in Cucumber's configuration.

The table below explains the various arguments you can pass when defining a parameter type.

Argument Description
name The name the parameter type will be recognised by in output parameters.
regexp A regexp that will match the parameter. May include capture groups.
type The return type of the transformer.
transformer A function or method that transforms the match from the regexp. Must have arity 1 if the regexp doesn't have any capture groups. Otherwise the arity must match the number of capture groups in regexp.
useForSnippets / use_for_snippets Defaults to true. That means this parameter type will be used to generate snippets for undefined steps. If the regexp frequently matches text you don't intend to be used as arguments, disable its use for snippets with false.
preferForRegexpMatch / prefer_for_regexp_match Defaults to false. Set to true if you have step definitions that use regular expressions, and you want this parameter type to take precedence over others during a match.

Java

@ParameterType("red|blue|yellow")  // regexp
public Color color(String color){  // type, name (from method)
    return new Color(color);       // transformer function
}

Kotlin

@ParameterType("red|blue|yellow")   // regexp
fun color(color: String): Color {   // name (from method), type
    return Color(color)             // transformer function
}                                    

Scala

ParameterType("color", "red|blue|yellow") { color: String => // name, regexp
    Color(color)                                             // transformer function, type
}                                    

JavaScript / TypeScript

import { defineParameterType } from '@cucumber/cucumber'

defineParameterType({
    name: 'color',
    regexp: /red|blue|yellow/,
    transformer: s => new Color(s)
})

The transformer function may return a Promise.

Ruby

ParameterType(
  name:        'color',
  regexp:      /red|blue|yellow/,
  type:        Color,
  transformer: ->(s) { Color.new(s) }
)

.NET / SpecFlow

[StepArgumentTransformation("(red|blue|yellow)")]
public Color ConvertColor(string colorValue)
{
    return new Color(colorValue);
}

Note: Currently the parameter name cannot be customized, so the custom parameters can only be used with the type name, e.g. {Color}.

Python

ParameterType(
  name=        "color",
  regexp=      "red|blue|yellow",
  type=        Color,
  transformer= lambda s: Color(s),
)

Optional text

It's grammatically incorrect to say 1 cucumbers, so we should make the plural s optional. That can be done by surrounding the optional text with parentheses:

I have {int} cucumber(s) in my belly

That expression would match this text:

I have 1 cucumber in my belly

It would also match this text:

I have 42 cucumbers in my belly

In Regular Expressions, parentheses indicate a capture group, but in Cucumber Expressions they mean optional text.

Alternative text

Sometimes you want to relax your language, to make it flow better. For example:

I have {int} cucumber(s) in my belly/stomach

This would match either of those texts:

I have 1 cucumber in my belly
I have 1 cucumber in my stomach
I have 42 cucumbers in my stomach
I have 42 cucumbers in my belly

Alternative text only works when there is no whitespace between the alternative parts.

Escaping

If you ever need to match () or {} literally, you can escape the opening ( or { with a backslash:

I have {int} \{what} cucumber(s) in my belly \(amazing!)

This expression would match the following examples:

I have 1 {what} cucumber in my belly (amazing!)
I have 42 {what} cucumbers in my belly (amazing!)

You may have to escape the \ character itself with another \, depending on your programming language. For example, in Java, you have to use escape character \ with another backslash.

I have {int} \\{what} cucumber(s) in my belly \\(amazing!)

Then this expression would match the following example:

I have 1 \{what} cucumber in my belly \(amazing!)
I have 42 \{what} cucumbers in my belly \(amazing!)

The / character will always be interpreted as an alternative, unless escaped, such as with \/.

Architecture

See ARCHITECTURE.md

Acknowledgements

The Cucumber Expression syntax is inspired by similar expression syntaxes in other BDD tools, such as Turnip, Behat and Behave.

Big thanks to Jonas Nicklas, Konstantin Kudryashov and Jens Engel for implementing those libraries.

The Tiny-Compiler-Parser tutorial by Yehonathan Sharvit inspired the design of the Cucumber expression parser.

cucumber-expressions's People

Contributors

aslakhellesoy avatar aurelien-reeves avatar brasmusson avatar charlierudolph avatar cukebot avatar davidjgoss avatar dmboyd avatar gasparnagy avatar jamis avatar jkronegg avatar jsa34 avatar kieran-ryan avatar lsuski avatar luke-hill avatar mattwynne avatar mpkorstanje avatar mxygem avatar nhojpatrick avatar olleolleolle avatar renovate-bot avatar renovate[bot] avatar savkk avatar scolej avatar svenwin avatar tommywo avatar tooky avatar tsundberg avatar unional avatar vearutop avatar vincent-psarga avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cucumber-expressions's Issues

LICENSE file is empty

The LICENSE file is currently empty; differing from other repositories in the Cucumber organisation.

👓 What did you see?

Empty LICENSE file.

✅ What did you expect to see?

MIT LICENSE specified, similar to other Cucumber repositories.

Nested capture groups are not passed to custom param definition transformer

👓 What did you see?

When creating a custom param definition, the transformer funcion does not received nested capturing groups.

✅ What did you expect to see?

As javascript developer, I expect regex capturing groups to conform Regex Ecamscript standard and capture nested groups

📦 Which tool/library version are you using?

https://www.npmjs.com/package/@cucumber/cucumber-expressions/v/16.1.2

🔬 How could we reproduce it?

Create a new npm package with your prefered typescript setup (the bug is also recreated with plain javascript).

In the package.json, install @cucumber/[email protected] dependency

Define a custom parameter with nested captured groups:

import { defineParameterType } from '@cucumber/cucumber';

defineParameterType({
  name: 'foo',
  regexp: /((foo))/,
  transformer: function (
    firstGroup: string,
    secondGroup: string
  ): string[] {
   return [firstGroup, secondGroup]
  },
});

Then, write any step definition and feature involving this custom definition

import { Given, Then, When } from '@cucumber/cucumber';

Given('{foo}', function(foo: string[]): void { console.log(foo) });
When('bar', function(): void {});
Then('baz', function(): void {});
Given foo
When bar
Then baz

Then, create a minimal config testing the feature processing the custom parameter and the step definitions and have a look at the console. Instead of ['foo', 'foo'], ['foo', undefined] is printed instead.

📚 Any additional context?

It seems the capturing groups are internally represented as a tree. Cucumber relies on TreeRegexp to create a GroupBuilder for the purpose of capturing regex groups. These groups are represented as a tree and only the root ones are passed to the custom definition transformer, ignoring any children groups.

Having a look at the Argument class:

public getValue<T>(thisObj: unknown): T | null {
  const groupValues = this.group ? this.group.values : null
  return this.parameterType.transform(thisObj, groupValues)
}

group.values are passed to the transformer:

export default class Group {
  constructor(
    public readonly value: string,
    public readonly start: number | undefined,
    public readonly end: number | undefined,
    public readonly children: readonly Group[]
  ) {}

  get values(): string[] | null {
    return (this.children.length === 0 ? [this] : this.children).map((g) => g.value)
  }
}

As you can see granchildren groups are never included. I would expect nested groups to be included as Ecmascript Regex spec states


This text was originally generated from a template, then edited by hand. You can modify the template here.

Expression for an argument list

🤔 What's the problem you're trying to solve?

In some cases it might be helpful to have a way of declaring several instances of one type as an expression, e.g.
Given the customer selects product(s) {product}+
which could match something like:

Given the customer selects product bike
Given the customer selects products bike, car
Given the customer selects products bike, car, skateboard

and for a better flow maybe also with a filler:

Given the customer selects products bike and car
Given the customer selects products bike, car or skateboard

What already works is something like this:

  val separators = arrayOf(",", ";", "and", "or")

  @ParameterType("^(?:(?:,|;|and|or)?\s?\w+)+$")
  fun products(product: String): List<Product> {
    return product.split(*separators)
      .map { it.trim() }
      .map { Product(it) }
  }

This has the disadvantage, that the regex (if specific things still should be checked) might get complex, since it also has to include all separators and the possibility of them being there or not. Also, the complete string (incl. separators) is highlighted in the feature files. Hence, having that as a feature of cucumber expressions would be nice, since it might make the used regex easier. And might also help with the highlighting in the feature files, since the separators could be displayed in another color.

✨ What's your proposed solution?

I could imagine two different ways. For both ways the definition of the parameter type might be kind of the same like it is now (define how the transformation is done for one single element), but it could define separators:

  @ParameterType(regex = "\\w+", separators=[",", ";", "and", "or"])
  fun product(product: String): List<Product> {
    return Product(it)
  }

Option one: a special notation for marking an expression as "vararg", e.g. {name}+

If the + is provided, the method signature must contain a collection type:

 @Given("the customer selects product(s) {product}+")
  fun givenCustomerSelectsProduct(product: List<Product>) { \* ... *\ }

Option two: no special expression syntax - instead it depends on the method signature if it is a single value or a collection of values is provided.

So both would be possible:

 @Given("the customer selects product(s) {product}")
  fun givenCustomerSelectsProduct(product: Product) { \* ... *\ }

and

 @Given("the customer selects product(s) {product}")
  fun givenCustomerSelectsProduct(product: List<Product>) { \* ... *\ }

Improve Expression creation performance

👓 What did you see?

I have a project tested with Cucumber 7.9.0:

  • about 400 test scenarios
  • about 150 step defs
  • about 30 parameter types

The Cucumber tests run in about 10.2 seconds on average : that's not so bad (we have plain Java, no Spring, no Mockito). But we wanted to have an even faster feedback loop for the developer.

Thus, we made some profiling and found that ExpressionFactory.createExpression(String) was eating a lot of CPU (this method is called more than 64'000 times for our project).

✅ What did you expect to see?

We improved the code with two different variants and made some benchmark using JMH microbenchmark framework :

  • creationExpression0 is the original method
  • creationExpression1 is a small update in which we replaced some regexp conditions by startsWith/endsWith conditions
  • creationExpression2 is a full rewrite with regexp conditions replaced by character testing conditions

The benchmark results are the following :

Benchmark Mode Cnt Score Error Units
ExpressionFactoryBenchmark.createExpression0 thrpt 25 234230,747 ± 4693,108 ops/s
ExpressionFactoryBenchmark.createExpression1 thrpt 25 347290,522 ± 4297,255 ops/s
ExpressionFactoryBenchmark.createExpression2 thrpt 25 356142,244 ± 9833,928 ops/s

When using the createExpression2 variant on our project, the cucumber tests run in 9.0 seconds on average. That's a 1.2 second improvement (12%).

Thus, we suggest to replace current ExpressionFactory.createExpression(String) implementation by the one from our createExpression2.

You can find in annex the three different variants, unit testing to ensure that all three variants behave the same and the JMH benchmark code.

cucumberexpressions.zip

📦 Which tool/library version are you using?

I'm using Cucumber 7.9.0 with the following Maven dependencies:

    <dependency>
        <groupId>io.cucumber</groupId>
        <artifactId>cucumber-java</artifactId>
        <version>${cucumber.version}</version>
        <scope>test</scope>
        <exclusions>
            <exclusion><!-- version 1.1.2 from cucumber conflicts with version 1.1.0 from junit -->
                <groupId>org.apiguardian</groupId>
                <artifactId>apiguardian-api</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <dependency>
        <groupId>io.cucumber</groupId>
        <artifactId>cucumber-junit-platform-engine</artifactId>
        <version>${cucumber.version}</version>
        <scope>test</scope>
        <exclusions>
            <exclusion><!-- conflicts with the version from junit-platform-suite -->
                <groupId>org.junit.platform</groupId>
                <artifactId>junit-platform-engine</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <dependency>
        <groupId>io.cucumber</groupId>
        <artifactId>cucumber-picocontainer</artifactId>
        <version>${cucumber.version}</version>
        <scope>test</scope>
    </dependency>

🔬 How could we reproduce it?

The JMH micro benchmark is provided in ExpressionFactoryBenchmark.

Steps to reproduce the behavior:

  1. Create a blank Maven project with the pom.xml above and JUnit5
  2. Run the micro benchmark

Using alternates within escaped parentheses doesn't work

👓 What did you see?

In this expression:

I run cucumber-js \(installed locally/globally\)

The phrase I run cucumber-js installed locally doesn't match.

The generated regular expression is shown as:

/^I run cucumber-js \(installed (?:locally|globally\))$/

✅ What did you expect to see?

Instead of the generated regular expression

/^I run cucumber-js \(installed (?:locally|globally\))$/

I think it should be

/^I run cucumber-js \(installed (?:locally|globally)\)$/

I guess we're gobbling up the trailing \) as part of the word globally

📦 Which version are you using?

cucumber-expressions javascript 15.0.1

🔬 How could we reproduce it?

https://cucumber.github.io/cucumber-expressions/?advanced=1&expression=I%20run%20cucumber-js%20%5C%28installed%20locally%2Fglobally%5C%29&step=I%20run%20cucumber-js%20installed%20locally

🤔 Anything else?

Discovered in cucumber/cucumber-js#1926

Add the ability to ignore a cucumber expression parameter just like a non capturing regex

🤔 What's the problem you're trying to solve?

Currently, there is no way to ignore a cucumber expression parameter, even though it is possible to do so in a normal regex. Thus, I think it would be nice to be able to have access to that feature in cucmber expressions as well.

✨ What's your proposed solution?

My proposed solution is simply to add the same mechanism as with a non-capturing regex group (for example (?:.*?)) like this, for example:

@Given( "{string} Database Entry with {?string} {string} has {string} = {string}" )
public void databaseEntryWithIdHasString( String tableName, String id, String columnName, String value) {
    ...
}

As you can see in my snippet, the cucumber expression {?string} is ignored, as it is just a string that will be used to define the name of the id. It's just a way to add more clarity to my features without the need of creating a new step definition for each and every way we can describe our column id.

Currently, I need to add it to my method signature like the snippet below and get warnings that my parameter idName is not used, which requires me to suppress the warning (we are using SonarQube and this kind of warning decreases our code quality):

@SuppressWarnings( "unused" )
@Given( "{string} Database Entry with {string} {string} has {string} = {string}" )
public void databaseEntryWithIdHasString( String tableName, String idName, String id, String columnName, String value) {
    ...
}

I think that in the parser that you are using to convert cucumber expressions to their actual parameter value, you can simply filter out the ones that start with a question mark, as simple as that.

⛏ Have you considered any alternatives or workarounds?

Yes, I have considered using a regex instead, but I feel like that adding the feature would be pretty low-effort, and I'd rather keep using cucumber expressions, since the reason why they exist is to make the step definitions more readable (I want to keep my step definitions readable).

📚 Any additional context?

Feature: named capture groups

I've been looking at how cucumber-expressions could be adopted in Behat.

One feature of our existing two pattern syntaxes (regex and turnip) is that they can name the arguments.

e.g.

@When /^I eat (?<count>[0-9]+) (?<fruit>.*)$/
@When I eat :count :fruit

This allows Behat to match arguments based on name as well as position, letting users transpose the argument order if necessary.

That can be useful if multiple patterns are attached to one step, something allowed in Behat:

/**
 * @When I eat :count :fruit
 * @When :count of the :fruit are eaten
 * @When I don't eat any :fruit
 */
public function myStepDef(string $fruit, int $count=0): void

The new feature would be for Cucumber Expressions to capture argument names:

  1. Define a syntax for adding names to expressions (e.g. {fruit:string})
  2. Retain that name in the generated regex as a named group
  3. Attach the name to the matched Argument value objects that the parser outputs (with an accessor method)

Then it would be up to the Cucumber implementation to either use the names for matching to the step definition, or to ignore them and use the order directly as currently happens.

Unable to parse negative number in Norwegian

Attempting to use a negative number in a step that expects a{double}, in a test case written in Norwegian, doesn't work.

Minimal example:

# language: no

  Egenskap: Betalingseksempel

  Scenario: Negativ rest

    Gitt at du har 10,04 kr
    Når du betaler 12,15 kr
    Så skal du ha -2,11 kr igjen
package com.example.stepdefs;

import io.cucumber.java.no.Gitt;
import io.cucumber.java.no.Når;
import io.cucumber.java.no.;

import static org.junit.jupiter.api.Assertions.assertEquals;

public class PaymentStepDefs {

    private double balance;

    @Gitt("at du har {double} kr")
    public void initialBalance(double balance) {
        this.balance = balance;
    }

    @Når("du betaler {double} kr")
    public void makePayment(double payment) {
        this.balance = this.balance - payment;
    }

    @("skal du ha {double} kr igjen")
    public void assertRemainingBalance(double expected) {
        assertEquals(expected, this.balance);
    }
}

👓 What did you see?

Cucumber is unable to parse the number:

io.cucumber.core.exception.CucumberException: Could not convert arguments for step [skal du ha {double} kr igjen] defined at 'com.example.stepdefx.PaymentStepDefs.assertRemainingBalance(double)'.

	at io.cucumber.core.runner.PickleStepDefinitionMatch.couldNotConvertArguments(PickleStepDefinitionMatch.java:112)
	at io.cucumber.core.runner.PickleStepDefinitionMatch.runStep(PickleStepDefinitionMatch.java:56)
	at io.cucumber.core.runner.ExecutionMode$1.execute(ExecutionMode.java:10)
	at io.cucumber.core.runner.TestStep.executeStep(TestStep.java:84)
	at io.cucumber.core.runner.TestStep.run(TestStep.java:56)
	at io.cucumber.core.runner.PickleStepTestStep.run(PickleStepTestStep.java:51)
	at io.cucumber.core.runner.TestCase.run(TestCase.java:84)
	at io.cucumber.core.runner.Runner.runPickle(Runner.java:75)
	at io.cucumber.junit.platform.engine.CucumberEngineExecutionContext.lambda$runTestCase$4(CucumberEngineExecutionContext.java:112)
	at io.cucumber.core.runtime.CucumberExecutionContext.lambda$runTestCase$5(CucumberExecutionContext.java:137)
	at io.cucumber.core.runtime.RethrowingThrowableCollector.executeAndThrow(RethrowingThrowableCollector.java:23)
	at io.cucumber.core.runtime.CucumberExecutionContext.runTestCase(CucumberExecutionContext.java:137)
	at io.cucumber.junit.platform.engine.CucumberEngineExecutionContext.runTestCase(CucumberEngineExecutionContext.java:109)
	at io.cucumber.junit.platform.engine.NodeDescriptor$PickleDescriptor.execute(NodeDescriptor.java:168)
	at io.cucumber.junit.platform.engine.NodeDescriptor$PickleDescriptor.execute(NodeDescriptor.java:90)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
Caused by: io.cucumber.cucumberexpressions.CucumberExpressionException: Failed to parse number
	at io.cucumber.cucumberexpressions.NumberParser.parse(NumberParser.java:41)
	at io.cucumber.cucumberexpressions.NumberParser.parseDouble(NumberParser.java:21)
	at io.cucumber.cucumberexpressions.BuiltInParameterTransformer.doTransform(BuiltInParameterTransformer.java:80)
	at io.cucumber.cucumberexpressions.BuiltInParameterTransformer.transform(BuiltInParameterTransformer.java:22)
	at io.cucumber.cucumberexpressions.ParameterTypeRegistry$8.transform(ParameterTypeRegistry.java:114)
	at io.cucumber.cucumberexpressions.ParameterTypeRegistry$8.transform(ParameterTypeRegistry.java:111)
	at io.cucumber.cucumberexpressions.ParameterType$TransformerAdaptor.transform(ParameterType.java:299)
	at io.cucumber.cucumberexpressions.ParameterType.transform(ParameterType.java:259)
	at io.cucumber.cucumberexpressions.Argument.getValue(Argument.java:42)
	at io.cucumber.core.stepexpression.ExpressionArgument.getValue(ExpressionArgument.java:17)
	at io.cucumber.core.runner.PickleStepDefinitionMatch.runStep(PickleStepDefinitionMatch.java:47)
	... 15 more
Caused by: java.text.ParseException: Unparseable number: "-2,11"
	at java.base/java.text.NumberFormat.parse(NumberFormat.java:434)
	at io.cucumber.cucumberexpressions.NumberParser.parse(NumberParser.java:39)
	... 25 more

✅ What did you expect to see?

The text should be parsed to double value -2.11.

📦 Which tool/library version are you using?

Cucumber-java 12.15

Move "try" into a dedicated project

🤔 What's the problem you've observed?

The "try" as part of cucumber-expression javascript implementation oversize it and add a lot of dependency to cucumber-expression itself. It could live on its own, being inside its own repo, or within a dedicated folder within this repo

Refs. #104 (comment)

✨ Do you have a proposal for making it better?

I would put it its in own repo actually, thus it would not interfere with cucumber-expression at all


This text was originally generated from a template, then edited by hand. You can modify the template here.

Use YAML instead of embedded JSON in acceptance test data

Is your feature request related to a problem? Please describe.

The various testdata/**/*.yaml files have expected fields that are strings containing JSON. I find this hard to read, and it also means the tests have to parse JSON after parsing YAML.

Describe the solution you'd like

I'd like the test data to just be YAML. So instad of this:

expression: mice/rats
expected: |-
{"type": "EXPRESSION_NODE", "start": 0, "end": 9, "nodes": [
{"type": "ALTERNATION_NODE", "start": 0, "end": 9, "nodes": [
{"type": "ALTERNATIVE_NODE", "start": 0, "end": 4, "nodes": [
{"type": "TEXT_NODE", "start": 0, "end": 4, "token": "mice"}
]},
{"type": "ALTERNATIVE_NODE", "start": 5, "end": 9, "nodes": [
{"type": "TEXT_NODE", "start": 5, "end": 9, "token": "rats"}
]}
]}
]}

We'd have this instead:

expression: mice/rats
expectation:
  type: EXPRESSION_NODE
  start: 0
  end: 9
  nodes:
  - type: ALTERNATION_NODE
    start: 0
    end: 9
    nodes:
    - type: ALTERNATIVE_NODE
      start: 0
      end: 4
      nodes:
      - type: TEXT_NODE
        start: 0
        end: 4
        token: mice
    - type: ALTERNATIVE_NODE
      start: 5
      end: 9
      nodes:
      - type: TEXT_NODE
        start: 5
        end: 9
        token: rats

Any objections if I convert the test data? (I'd also change JSON.parse(expectation.expected) to just expectation.expected in the tests.

My main reason for doing this is to just tidy up a bit before people start implementing the Python / .NET ports...

WDYT @mpkorstanje?

RegEx "expressions" and defineParameterType feature file examples ?

🤔 What's the problem you're trying to solve?

I have been looking over the cucumber git repo for regex "expressions" (javascript)
defineParameterType seems interesting but I do not see any simple usage examples only unit test cases.
https://github.com/cucumber/cucumber-expressions/tree/5af7076298a9c2b5455e63f1c0de90ddc9f1c1a4#javascript--typescript

✨ What's your proposed solution?

The entire ParameterTypeRegistry + defineParameterType seems a bit complex when you explore it.
Showing off how to use it in a feature file would help adoption/integration.

public defineParameterType(parameterType: ParameterType<unknown>) {

⛏ Have you considered any alternatives or workarounds?

I am currently using Regex strings directly in the feature file step definitions. I was looking here to find better ways of doing things.

📚 Any additional context?

None

This text was originally generated from a template, then edited by hand. You can modify the template here.

java: Not all tests are executed

🤔 What's the problem you've observed?

private static List<Path> acceptance_tests_pass() throws IOException {
List<Path> paths = new ArrayList<>();
newDirectoryStream(Paths.get("..", "testdata", "cucumber-expression", "matching")).forEach(paths::add);
paths.sort(Comparator.naturalOrder());
return singletonList(Paths.get("..", "testdata", "cucumber-expression", "matching", "matches-byte.yaml"));
}

Because of this not all acceptance tests are executed. This pattern may be repeated elsewhere.

Terraform Compliance v1.3.31, Alternative text bug?

👓 What did you see?

Alternative text fails in this scenario:

Feature: vm compliance

    Scenario: vm Sku compliance
        Given I have azurerm_virtual_machine defined
        Then it must have "vm_size"
        And its value must be Standard_DS1_v2/Standard_DS2_v1

Terminal Output:

Failure: vm_size property in azurerm_virtual_machine.vm resource does not match with ^Standard_DS1_v2/Standard_DS2_v1$ case insensitive regex. It is set to Standard_DS1_v2.
        And its value must be Standard_DS1_v2/Standard_DS2_v1
          Failure:

✅ What did you expect to see?

Scenario to pass.

📦 Which version are you using?

Terraform v1.1.7
Terraform Compliance v1.3.31
PowerShell 7.2.1
Win 11 OS, up to date.

🔬 How could we reproduce it?

Steps to reproduce the behavior:

  1. Install Terraform Compliance v1.3.31

  2. Extract plan.zip to folder, contains plan file and plan.json and z.feature
    plan.zip

  3. Run command 'terraform-compliance -p ./plan -f ./'
    5.. See error in terminal output.

🤔 Anything else?

Typo in error message for use of alternation inside an optional

Repeated 'the' within the error message for use of an alternation inside an optional.

👓 What did you see?

Through an issue raised on the Visual Studio Code extension (cucumber/vscode#142), it was observed that the error message for use of an alternation inside an optional includes a repeated 'the' in its second line:

An alternation can not be used inside an optional.
You can use '\/' to escape the the '/'

✅ What did you expect to see?

Single 'the':

An alternation can not be used inside an optional.
You can use '\/' to escape the '/'

📦 Which tool/library version are you using?

Cucumber Expressions: 16.1.2
Cucumber Visual Studio Code extension: 1.8.0

🔬 How could we reproduce it?

  1. Install the Visual Studio Code extension
  2. Create a step definition with an alternation inside an optional
import { When } from 'cypress-cucumber-preprocessor/steps'
const { wait } = cy

When('entro no detalhe d(a/o) {string}', () => {
  wait('@apisRequests')
})
  1. Check the logs of the Cucumber Language Server in the Output window

Golang big.Float is not a big decimal

🤔 What's the problem you've observed?

I am considering using the golang implementation of cucumber-expressions in https://github.com/regen-network/gocuke and noticed that bigdecimal is mapped to https://pkg.go.dev/math/big#Float.

The documentation for big.Float states that this is structured as sign × mantissa × 2**exponent. This is not a decimal number! It is a base-2 floating point number, not base-10 as required for decimals.

Java does provide a proper BigDecimal where the documentation clearly states that the structure is unscaledValue × 10-scale.

✨ Do you have a proposal for making it better?

The mapping of bigdecimal to big.Float should be removed in the golang package and consumers should be allowed to register a correct decimal implementation (none is provided by the standard library).

Also in the golang implementation, it seems like it's an error to override a built-in type mapping. It might be worth reconsidering this in case users want to swap out other type mappings.

📚 Any additional context?

Two correct golang decimal implementations are: https://pkg.go.dev/github.com/cockroachdb/apd/v3 and https://pkg.go.dev/github.com/ericlagergren/decimal/v3.


This text was originally generated from a template, then edited by hand. You can modify the template here.

Request for non-capturing groups in Cucumber Expressions

Hello,

It would be nice if Cucumber Expressions support non-capturing groups.
My intension is to have a mapping for the following step:

When I order 2 apples

and the step definitions mapping to be something like that

@When("I order {int} [fruit]")
public void orderFruits(int fruitNumber){
........
}

Where the square brackets will denote non-capturing group and the expression [{context_description}] would match any text, but wont pass it as a parameter to the step deifinition method

Define default parameter types for a different platform

The @cucumber/language-service module is able to extract Cucumber Expressions from source code (currently Java and TypeScript, but other languages can be added with relatively little effort).

However, if a user is using @cucumber/language-service (which is written in TypeScript) with Java, they may have Cucumber Expressions that uses parameter types that are only defined for Cucumber-JVM (such as {double} or {bigdecimal}).

These expressions will cause a parse error unless the registry is instantiated with those parameter types.

One way to support this is to add a defineDefaultParameterTypes(registry, platform) function where the value of platform would be used to decide what parameter types to define.

In order to implement this consistently on all platforms, we could add new acceptance tests in testdata/cucumber-expression/matching/*.yml with an additional platform property to indicate what platform to use.

Also see #42

Deprecate Java-specific types (biginteger, bigdecimal, byte, short, long, double)

Is your feature request related to a problem? Please describe.

While working on #43 it struck me that all the parameter types that are java-specific (biginteger, bigdecimal, byte, short, long, double) don't need an explicit parameter type:

An {int} parameter can be transformed to any of the following classes:

  • java.lang.Byte
  • java.lang.Short
  • java.lang.Integer
  • java.lang.Long
  • java.math.BigInteger

A {float} parameter can be transformed to any of the following classes:

  • java.lang.Float
  • java.lang.Doublet
  • java.math.BigDecimal

Deciding what class to transform to would first use the class from the corresponding method parameter's Java type. If the Java type isn't available (e.g. for dynamically typed languages), the appropriate type would be chosen based on the string value - we'd try to fit it into the "biggest" type. E.g. "127" becomes java.lang.Byte, "128" becomes java.lang.Short etc.

A similar algorithm could be used for float.

The main reason I want to do this is to solve the "different parameter types for different platforms" problem described in #43, but with a cleaner (IMO) solution.

Another reason I want to do this is to avoid having different parameter types for different platforms, which makes it harder to implement cross-platform tools (such as the Cucumber Language Server).

Describe the solution you'd like

The gist of the solution is described above, but I'd also like this to be backwards compatible. Using any of the deprecated types will print a warning, encouraging users to switch to {int} or {float}. Implementation-wise, the deprecated types would just be aliases for the {int} or {float} types.

Describe alternatives you've considered
#43 is an alternative I have considered.

Additional context
Add any other context or screenshots about the feature request here.

[python] Package "click" has a new version which breaks linting in CI

👓 What did you see?

python CI broke on using pyflakes, I think, and in doing so, reported a traceback.

ImportError: cannot import name '_unicodefun' from 'click'

This issue in another Python-using project describes the problem:
dask/distributed#6013

black....................................................................Failed
- hook id: black
- exit code: 1

Traceback (most recent call last):
  File "/home/runner/.cache/pre-commit/repoa_4yao8n/py_env-python3.10/bin/black", line 8, in <module>
    sys.exit(patched_main())
  File "/home/runner/.cache/pre-commit/repoa_4yao8n/py_env-python3.10/lib/python3.10/site-packages/black/__init__.py", line 1372, in patched_main
    patch_click()
  File "/home/runner/.cache/pre-commit/repoa_4yao8n/py_env-python3.10/lib/python3.10/site-packages/black/__init__.py", line 1358, in patch_click
    from click import _unicodefun
ImportError: cannot import name '_unicodefun' from 'click' (/home/runner/.cache/pre-commit/repoa_4yao8n/py_env-python3.10/lib/python3.10/site-packages/click/__init__.py)

flake8...................................................................Passed
Error: Process completed with exit code 1.

✅ What did you expect to see?

A passing CI for Python.

📦 Which tool/library version are you using?

Well, this is here, in this repo, in CI.

🔬 How could we reproduce it?

See
https://github.com/cucumber/cucumber-expressions/runs/5733063101?check_suite_focus=true

📚 Any additional context?

ChainAlert: npm package release (15.0.0) has no matching tag in this repo

Dear @cucumber/cucumber-expressions maintainers,
Thank you for your contribution to the open-source community.

This issue was automatically created to inform you a new version (15.0.0) of @cucumber/cucumber-expressions was published without a matching tag in this repo.

Our service monitors the open-source ecosystem and informs popular packages' owners in case of potentially harmful activity.
If you find this behavior legitimate, kindly close and ignore this issue. Read more

Tidy up contributor guide / local development environment

We want to make sure this project is convenient to hack on for new contributors.

We currently have the Makefiles imported from the monorepo, which can build all language versions at once. Do we still need those?

Should we provide a Docker environment (or multiple per-language environments) to help contributors build the projects?

The CONTRIBUTING.md file is empty.

Return optionals as part of a Cucumber Expression match

🤔 What's the problem you're trying to solve?

I want to provide syntax highlighting (LSP semantic tokens) for the parts of a Gherkin step that is optional in the corresponding Cucumber Expression.

✨ What's your proposed solution?

Use named capture groups for optionals, as explained in cucumber/language-service#57

The return type of match would change from Argument[] | null to Match | null:

export type Match = {
  arguments: readonly Argument[]
  optionals: readonly Optional[]
}

export type Optional = {
  group: Group
}

⛏ Have you considered any alternatives or workarounds?

If we want to support this, I can't think of any other option.

Python implementation of Cucumber Expressions

The Behave team (led by @jenisys) would like to add support for Cucumber Expressions in Behave.

The first step would be to implement Cucumber Expressions in Python.
There are four existing implementations in this repository (Go, Ruby, TypeScript and Java). The Python implementation should follow the structure of the existing implementations as closely as possible. This will make maintenance easier in the future.

Every implementation uses a shared set of test data, which are YAML files with input and expected output for different parts of the parser.

The parser consists of multiple components, and it may be easier to implement the library in the following order:

  • CucumberExpressionTokenizer
  • CucumberExpressionParser
  • TreeRegexp
  • CucumberExpression
  • CucumberExpressionGenerator

I would also recommend using TDD to guide the implementation. That is - start by writing the test for a component (reading in thest data from the relevant YAML file). Make the tests pass one by one, by making incremental improvements to the code.

If contributors are working in a team, it may be easier to collaborate if all contributors are committing to the same feature branch in the same repo (as opposed to using individual forks). Please add comment here if you want commit access so you can work on a branch in this repo.

Bad CucumberExpression creation performance

👓 What did you see?

On my project with about 150 stepdefs and about 400 test scenarios, the IntelliJ profiler says the CucumberExpression.<init> method takes 25.9% of the total CPU time. This is because the method is called for all step defs and for all test scenarios. I think the performance could be better.

✅ What did you expect to see?

I expect CucumberExpression.<init> to avoid unnecessary processing (contributes to #2035).

I understand that cucumber-java8 can introduce dynamic behavior which requires parsing the expressions for each test scenario. However, I think we can safely cache everything that is constant and does not depend on cucumber-java8. I identitifed the following performance improvement points in CucumberExpression:

  • TreeRegex creation: in CucumberExpression constructor, this object serves to get some "metadata" about a regular expression itself (i.e. not depending on context). Thus, two identical regular expressions will lead to the same TreeRegp, so the creation is cacheable.

    The original code:

    this.treeRegexp = new TreeRegexp(pattern);
    

    could be replaced by (treeRegexps is a static Map<String, TreeRegexp>):

    this.treeRegexp = treeRegexps.computeIfAbsent(pattern, TreeRegexp::new);
    
  • calls to escapeRegex in the rewriteToRegex method are done on the Node.text() content: two identical Node.text() will lead to the same escaped result, independently of the context. Thus, the result of escapeRegex is cacheable.

    The original code:

    return escapeRegex(node.text());
    

    can be replaced by (escapedTexts is a static Map<String, String>):

    return escapedTexts.computeIfAbsent(node.text(), CucumberExpression::escapeRegex);
    

These two optimization points lead to four combinations to be benchmarked (original version is createExpression0). The benchmark consists in creating 400 times five different expressions:

Benchmark cached calls to escapeRegex cached TreeRegex creation ops/s
CucumberExpressionBenchmark.createExpression0 no no 153,024 ± 13,800
CucumberExpressionBenchmark.createExpression1 yes no 181,960 ± 12,133
CucumberExpressionBenchmark.createExpression2 no yes 186,236 ± 11,232
CucumberExpressionBenchmark.createExpression3 yes yes 219,890 ± 12,365

Caching the TreeRegex creation lead to 22% performance improvement and using both methods lead to 44% performance improvement.

On a real project with about 150 stepdefs and 400 test scenarios, the IntelliJ Profiler runs is about 7700 ms and says that CucumberExpression.<init> is:

  • 25.9% of the total CPU time with the original version (1994 ms)
  • 15.7% of the total CPU time with both optimizations enabled (1209 ms, i.e. that's a 785 ms improvement on total time, or 10%)

I suggest to use the variant createExpression3 and I would be happy to propose a PR.

📦 Which tool/library version are you using?

Cucumber 7.10.1

🔬 How could we reproduce it?

The benchmark with the four variants is in
cucumberexpressions.zip

Steps to reproduce the behavior:

  1. Create a Maven project with the following dependencies:

     <dependency>
         <groupId>io.cucumber</groupId>
         <artifactId>cucumber-java</artifactId>
         <version>${cucumber.version}</version>
         <scope>test</scope>
     </dependency>
     <dependency>
         <groupId>io.cucumber</groupId>
         <artifactId>cucumber-junit-platform-engine</artifactId>
         <version>${cucumber.version}</version>
         <scope>test</scope>
     </dependency>
     <dependency>
         <groupId>io.cucumber</groupId>
         <artifactId>cucumber-picocontainer</artifactId>
         <version>${cucumber.version}</version>
         <scope>test</scope>
     </dependency>
     <dependency>
         <groupId>org.openjdk.jmh</groupId>
         <artifactId>jmh-generator-annprocess</artifactId>
         <version>1.36</version>
         <scope>test</scope>
     </dependency>
    
  2. Run the benchmark

Released Ruby gem is missing LICENSE file.

I am working on updating a few packages in the Ruby cucumber stack in Fedora and I noticed the license file is missing from this library now, that wasn't the case with v14.

From what I understand the file should be present in the released gem as it's a copy of the software.

Not 100% sure how a proper fix should look like.

AFAICT, to build and then unpack/install the gem properly, the license has to be either on the same level with the .gemspec file in the Ruby gem or some level below. A file cannot be referenced a level up from gemspec, so doing something like s.files << "../LICENSE" is not going to work for unpacking the gem. Perhaps a Rakefile target that copies the LICENSE first and then builds the gem so that something like s.files << "./LICENSE" would then be OK.

Allow free-form text in optionals

🤔 What's the problem you're trying to solve?

I'd like to have optional free-form text in my steps:

For instance:

Then error 123 is reported (because reasons)

The "because reasons" string isn't fixed, the content of the parenthesis varies depending on the scenario.

✨ What's your proposed solution?

It would be nice to either:

  • have a way to use some kind of "catch-all" structure (here .*) in optionals:
    error {int} is reported( \\(.*)
  • or allow parameters in optionals
    error {int} is reported( \\({})

⛏ Have you considered any alternatives or workarounds?

I tried using an anonymous parameter:

error {int} is reported( \\(){}

and ignore the related parameter in the step definition.

but this is not possible in my case, since I also have a step for:

error {int} is reported {int} times( \\(){}

which causes an ambiguous step definition.

This is why I'd need to put the anonymous part within the optional:

error {int} is reported( \\({})

But then it is an illegal expression (parameters are not allowed in optionals).

I also tried using a comment at the end of the expression:

Then error 123 is reported #(because reasons)

but comments are only allowed as a start of a new line.

The only resort is to use a regex. But the expressions then get quite ugly and unreadable (the examples above are simplified, my real expressions are more complex).
Or move the free-form text to its own line, but it is definitely less concise and sort of breaks the scenario flow.

These two workarounds work, but are suboptimal as they unfortunately make for less readable code.

📚 Any additional context?

I recently used cucumber v4.5.4 (cucumber-java), and the following expressions just worked for my use case:

error {int} is reported( \\(.*)`
error {int} is reported {int} time(s)( \\(.*)

This stopped working when migrating to cucumber v7.8.0!

Update Grammar in Architecture

I'm implementing Cucumber Expressions parser in Rust: WIP. And grammar described in the ARCHITECTURE.md really bothers me for couple of reasons.

  1. Lookbehind and lookahead in alternation definition
    EBNF describes context-free grammars and (correct me if I'm wrong) shouldn't have lookaheads and lookbehinds. This also means that described grammar may have non-linear complexity.
  2. Provided grammar describes superset of Cucumber Expressions and I don't really see reasons to do it.

So implementing Cucumber Expression parser according to the docs leads to unnecessary performance overhead for no obvious reasons.

Can't we just provide exact grammar for Cucumber Expressions without Note: section?


If I didn't miss anything provided grammar should be exact Cucumber Expressions

cucumber-expression     = (alternation
                           | optional
                           | parameter
                           | text)*
text                    = (- text-to-escape) | ('\', text-to-escape)
text-to-escape          = '(' | '{' | '/' | '\' 

alternation             = single-alternation, ('/', single_alternation)+
single-alternation      = ((text-in-alternative+, optional*) 
                            | (optional+, text-in-alternative+))+
text-in-alternative     = (- alternative-to-escape) | ('\', alternative-to-escape)
alternative-to-escape   = ' ' | '(' | '{' | '/' | '\'

optional                = '(', text-in-optional+, ')'
text-in-optional        = (- optional-to-escape) | ('\', optional-to-escape)
optional-to-escape      = '(' | ')' | '{' | '/' | '\'

parameter               = '{', name*, '}'
name                    = (- name_to_escape) | ('\', name-to-escape)
name-to-escape          = '{' | '}' | '(' | '/' | '\'

Missing source for source map

👓 What did you see?

The source map reference the TypeScript source code but they are not included in the package.

e.g. in ./dist/esm/src/index.js.map:

{...,"sources":["../../../src/index.ts"],...}

But since the source are not included in the package, when bundling with webpack, it fails with:

WARNING in ../../node_modules/.pnpm/@[email protected]/node_modules/@cucumber/cucumber-expressions/dist/esm/src/index.js
Module Warning (from ../../node_modules/.pnpm/[email protected][email protected]/node_modules/source-map-loader/dist/cjs.js):
Failed to parse source map from '...\node_modules\.pnpm\@[email protected]\node_modules\@cucumber\cucumber-expressions\src\index.ts' file: Error: ENOENT: no such file or directory, open '...\node_modules\.pnpm\@[email protected]\node_modules\@cucumber\cucumber-expressions\src\index.ts'

✅ What did you expect to see?

Include the TypeScript source code in the package. i.e.:

// package.json
{
  "files": [
    ...,
    "src"  // <-- add this
  ]
}

📦 Which tool/library version are you using?

@cucumber/cucumber-expressions: 16.1.2

🔬 How could we reproduce it?

This involves webpack, will need to find time to create a repro.
But the issue itself should be simple enough to understand directly.

Action Required: Fix Renovate Configuration

There is an error with this repository's Renovate configuration that needs to be fixed. As a precaution, Renovate will stop PRs until it is resolved.

Location: ^*.gemspec
Error type: Invalid regular expression: ^*.gemspec

Typescript step definition not matched

👓 What did you see?

Creating step definitions like below are not matched to the feature.
It works when making it a regular expression (instead of cucumber expression) as mentioned in Docs.

✅ What did you expect to see?

Step definitions should be matched correctly to the feature without having to use a regular expression.

📦 Which tool/library version are you using?

WebdriverIO 7.16.13
Cucumber 7.3.2
Cucumber-expressions 15.0.2
TypeScript 4.5.5

🔬 How could we reproduce it?

Not working:

Given("The user selects {string} from the (country|language) dropdown", function(toBeSelectedValue: string, dropdownType: string){
    console.log(toBeSelectedValue)
    console.log(dropdownType)
})

Working:

Given(/^The user selects (.*) from the (country|hospital) dropdown$/, function(toBeSelectedValue: string, dropdownType: string){
    console.log(toBeSelectedValue)
    console.log(dropdownType)
})

Feature file:

Feature: Select dropdown value

Scenario: A value is selected
    Given The user selects "Norway" from the country dropdown

more powerful optional text

🤔 What's the problem you're trying to solve?

Currently optional text is only good for a plural s and the like.
So I have {int} cucumber(s) in my belly matches I have 1 cucumber in my belly.

I want to implement even less variants of identical test steps by having a more powerful optional text.

✨ What's your proposed solution?

  • I also wish I should not see the population (anymore) to match I should not see the population.
    (no extra space at the end of the text)
  • And I wish I should be (back) on the start page to match I should be on the start page.
    (no double space in the text)
  • And I wish sehe ich (wieder) den/die Breadcrumb(s) {string} to match sehe ich den Breadcrumb "Start".
    (still mixable with alternative text)

⛏ Have you considered any alternatives or workarounds?

Ugly workarounds:

  • cripple the test specs by adding extra spaces, e.g. I am on the start page (space at the end)
  • cripple the expressions by putting spaces in brackets, e.g. I am on the start page( again)
  • restricting test spec writer's language to be less natural (without “again”, etc.)
  • additional test step calling the other variant
  • workaround to convert such (more powerful) expressions to classic cucumber expressions (code here)

📚 Any additional context?

To be honest, I am somewhat surprised this doesn't work out of the box. See also the original discussion in another project.


This text was originally generated from a template, then edited by hand. You can modify the template here.

Dependency Dashboard

This issue provides visibility into Renovate updates and their statuses. Learn more

This repository currently has no open or pending branches.


  • Check this box to trigger a request for Renovate to run again on this repository

Remove Python example illustrating how to define parameter types

👓 What did you see?

Instructions about how to define a parameter type in Python: https://github.com/cucumber/cucumber-expressions#python

This is misleading, because Behave has not yet integrated this library into its code. Cucumber Expressions are not supported in Behave.

✅ What did you expect to see?

Behave is not yetusing this library. It doesn't support Cucumber Expressions yet, nor does it have an API for defining parameter types. See behave/behave#968 for more details.

📦 Which tool/library version are you using?

Behave v1.2.7.dev2

`end` as a method name / prop can cause some issues

🤔 What's the problem you've observed?

In many of the AST sub-classes, such as Node there are a variety of attributes passed in. One of these is called end or derivations thereof. This name "end" can be problematic in some languages (Such as ruby). A few examples are below
Java - https://github.com/cucumber/cucumber-expressions/blob/main/java/src/main/java/io/cucumber/cucumberexpressions/Ast.java#L64
Ruby - https://github.com/cucumber/cucumber-expressions/blob/main/ruby/lib/cucumber/cucumber_expressions/ast.rb#L38
JS - https://github.com/cucumber/cucumber-expressions/blob/main/javascript/src/Ast.ts#L77

✨ Do you have a proposal for making it better?

It seems as though we often use the hash representation methods, and not directly using the getters. Therefore I propose removing all getters and just rely on using the hash methods. Failing this maybe altering start/end to starting and ending

📚 Any additional context?


This text was originally generated from a template, then edited by hand. You can modify the template here.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.