badeball / cypress-cucumber-preprocessor

Run cucumber/gherkin-syntaxed specs with Cypress
MIT License
1.32k stars 149 forks source link

Inconsistent handling of string quotation marks between an embedded string in a step definition and strings in Cucumber data tables #1252

Closed osmolyar closed 1 week ago

osmolyar commented 1 week ago

Current behavior

The handling of string quotation marks for an embedded string in a step definition vs. strings in Cucumber data tables is inconsistent, which means the same entries in an Examples table can't be used for both purposes.

When a {string} is expected in a step definition, the string must be written either explicitly with single quotes, e.g.

Then I view scenario 'E2E Scenario 1' in the new project

or it can be parametrized to refer to a column in the Examples table, but then the entry in the Examples table must contain the single quotes, e.g,

Then I view scenario \<scenarioName> in the new project

  Examples:
  |scenarioName    |
  |'E2E Scenario 1'|

If you try to parametrize it but the Examples table entry doesn't have the quotation marks, it says the step is not recognized.

However, when using step data tables, the single quotations are handled implicitly, since all inputs in examples and data tables are read as string anyway, e.g.

And I save the scenario with the following project attributes

  | AliasId        | <aliasId>        |
  | Phase          | <phase>          |
  | Indication     | <indication>     |
  | Compound       | <compound>       |
  | ScenarioName   | <scenarioName>   |

In this case it works fine if the Examples table entry is like this, with no quotation marks:

  Examples:
  |scenarioName   |
  |E2E Scenario 1 |

This means that either the strings embedded inline in the step definitions can't be parametrized to the Examples table, or two different entries, one with and one without quotation marks, need to be created in the examples table.

Desired behavior

Ideally the strings embedded in Step definitions would assume that a parameter that expects a {string} is always getting passed a string without needing to put it in quotation marks. This is the way normal Cucumber works.

Versions

"@badeball/cypress-cucumber-preprocessor": "^21.0.0", "cypress": "^13.14.2", "@cypress/webpack-preprocessor": "^6.0.2",

Checklist

badeball commented 1 week ago

This is the way normal Cucumber works.

Which Cucumber implementation are you referring to here? There are multiple, as seen here.

osmolyar commented 1 week ago

Which Cucumber implementation are you referring to here? There are multiple, as seen here.

The ones I've had experience with are Cucumber.js (as part of WebdriverIO integration) and Specflow - I see that the Cucumber.js readme does give an example with inline quotation marks for steps defined as above, (as well as data tables examples without).

Now on further testing I see that with cypress-cucumber-preprocessor, quotation marks are also not required as with the other mentioned implementations if the steps are defined using the regex type of format as follows, instead of the string format as in the original issue statement.

Then(/^the number of trial records retrieved is (.*)$/, function (total) {

So it seems it may be consistent with other implementations after all, and I'm ok with closing the issue.

From a logical perspective though, there seems to be no way to define an expected inline parameter as something other than a {string} with the first type of format definition, as far as I can tell, for example for steps like

When I edit the Sites Activated value for row '1' and month '3' to '8'

When( 'I edit the Sites Activated value for row {string} and month {string} to {string}', function (row, month, value) {

or When I edit the specified regions with the following Region properties - expect sites activated warning true or false: 'true'

When('I edit the specified regions with the following Region properties - expect sites activated warning true or false: {string}', function (sitesActivatedEdited, table) {

I was unable to define the params to be numbers, or boolean. If there is a way to do so with this type of step that would be lovely but if there isn't, in principle it would be nice for the step definitions to be handled internally consistently with the regex type step definitions and step data tables, assuming that string inputs are strings.

badeball commented 1 week ago

There is no inconsistency here - you're just misunderstanding the relationship between cucumber expressions and regular expressions.

The following example will work fine in this implementation, as well as cucumber-js.

# example.feature
Feature:
  Scenario:
    Given a <value>
      Examples:
        | value |
        | step  |

// example.js
Given(/a (.*)/, (foo) => {});

The following example will not work in either.

# example.feature
Feature:
  Scenario:
    Given a <value>
      Examples:
        | value |
        | step  |

// example.js
Given("a {string}", (foo) => {});

.. and that is because the cucumber expressions is translated into a regular expression akin to /a (?:'|")(.*)(?:'|")/ and not /a (.*)/.

Closing as this isn't an issue with the preprocessor, but rather intended behavior.

osmolyar commented 1 week ago

Thanks for the explanation, "and that is because the cucumber expressions is translated into a regular expression akin to /a (?:'|")(.)(?:'|")/ and not /a (.)/." that makes sense.

Yes, I was in agreement that on further testing the behaviors of Cucumber.js and cypress-cucumber-preprocessor were equivalent when tested with the regular expression format of step definition, so agree with closing the issue.

I do maintain that there's an inconsistency (for both Cucumber.js and the preprocessor) between the handling of cucumber expressions when they're passed inside a step data table vs. inline in the step definition because with the data tables it behaves the same way as with the regular expression (.*) definition. Maybe it would have made sense for the cucumber expressions to be translated to (.*) since there's no way to pass anything other than a string anyway.

badeball commented 1 week ago

Maybe it would have made sense for the cucumber expressions to be translated to (.*) since there's no way to pass anything other than a string anyway.

There are absolutely other things that you can pass into a cucumber expression, as seen in the documentation,

For instance, the following example will run perfectly fine.

# example.feature
Feature:
  Scenario:
    When I type <value>
      Examples:
        | value |
        | 1     |

// example.js
When("I type {int}", (number) => {});

Furthermore, given the following example:

# example.feature
Feature:
  Scenario:
    When I type <value>
      Examples:
        | value |
        | 1     |

// example.js
When("I type {int}", (number) => {});
When("I type {string}", (string) => {});

.. can you give me a good reason for why this should not invoke the first step definition, but rather the second?

osmolyar commented 1 week ago

Ah, got it now, thanks. So in fact the behavior I was after (strings without quotation marks) can be obtained with this usage of anonymous expression type

When('I view scenario {} in the new project', function (scenarioName) {

(Leaving this here in case anyone else comes across the same confusion.)