alexkrechik / VSCucumberAutoComplete

Cucumber (Gherkin) Full Support Extension for VSCode
https://marketplace.visualstudio.com/items?itemName=alexkrechik.cucumberautocomplete
MIT License
331 stars 79 forks source link

It actually works with Codeception (PHP) - But there's a gotcha. #475

Open Luc45 opened 7 months ago

Luc45 commented 7 months ago

Describe the bug At first, this extension doesn't seem to work with Codeception, as reported in this issue: https://github.com/alexkrechik/VSCucumberAutoComplete/issues/324

It turns out it's just a small Regex gimmick.

I've adapted gserver/src/steps.handler.js's getStepRegExp to run on Developer Tools for easier debugging. You can just copy and paste this on your Developer tools:

//Actually, we dont care what the symbols are before our 'Gherkin' word
//But they shouldn't end with letter
const startPart = '^((?:[^\'"\/]*?[^\\w])|.{0})';

//All the steps should be declared using any gherkin keyword. We should get first 'gherkin' word
const gherkinPart = `(Given|When|Then)`;

//All the symbols, except of symbols, using as step start and letters, could be between gherkin word and our step
const nonStepStartSymbols = `[^\/'"\`\\w]*?`;

// Step part getting
const stepRegExSymbol = '/';
// Step text could be placed between '/' symbols (ex. in JS) or between quotes, like in Java
const stepStart = stepRegExSymbol ? `(${stepRegExSymbol})` : `(\/|'|"|\`)`;
// ref to RegEx Example: https://regex101.com/r/mS1zJ8/1
// Use a RegEx that peeks ahead to ensure escape character can still work, like `\'`.
const stepBody = `((?:(?=(?:\\\\)*)\\\\.|.)*?)`;
//Step should be ended with same symbol it begins
const stepEnd = stepRegExSymbol ? stepRegExSymbol : '\\3';

//Our RegExp will be case-insensitive to support cases like TypeScript (...@when...)
const r = new RegExp(startPart + gherkinPart + nonStepStartSymbols + stepStart + stepBody + stepEnd, 'i');

Then run:

It expects to find the step definition wrapped in JS or Java-style regex delimitators, which are / or backticks. However, Codeception generates the step definition without regex delimitators:

What it expects:

/**
 * @Then /I should see :arg1/
 */

How Codeception generates step definitions:

/**
 * @Then I should see :arg1
 */

Is it possible to update that Regex to accept a step definition without a Regex delimitator? If you're afraid it could lead to false positives, maybe it could be a opt-in setting that those using Codeception should enable?

Luc45 commented 7 months ago

This works for me for single-line step definitions:

gserver/src/steps.handler.ts

if (this.settings.cucumberautocomplete.requireDelimiters) {
    // Step part getting
    const { stepRegExSymbol } = this.settings.cucumberautocomplete;
    // Step text could be placed between '/' symbols (ex. in JS) or between quotes, like in Java
    const stepStart = stepRegExSymbol ? `(${stepRegExSymbol})` : `(\/|'|"|\`)`;
    // ref to RegEx Example: https://regex101.com/r/mS1zJ8/1
    // Use a RegEx that peeks ahead to ensure escape character can still work, like `\'`.
    const stepBody = `((?:(?=(?:\\\\)*)\\\\.|.)*?)`;
    //Step should be ended with same symbol it begins
    const stepEnd = stepRegExSymbol ? stepRegExSymbol : '\\3';

    //Our RegExp will be case-insensitive to support cases like TypeScript (...@when...)
    const r = new RegExp(startPart + gherkinPart + nonStepStartSymbols + stepStart + stepBody + stepEnd, 'i');
} else {
    const stepBody = `((?:(?=(?:\\\\)*)\\\\.|.)*?)`;
    // Without delimiters, the step should be ended with a new line.
    const stepEnd = '\n';

    //Our RegExp will be case-insensitive to support cases like TypeScript (...@when...)
    const r = new RegExp(startPart + gherkinPart + nonStepStartSymbols + stepBody + stepEnd, 'i');
}

gserver/src/typings.d.ts

interface Settings {
    cucumberautocomplete: {
        ...
        requireDelimiters?: boolean
    }
}

client/package.json

"cucumberautocomplete.requireDelimiters": {
    "description": "Whether the step definitions should have regex delimiters.",
    "type": "boolean",
    "required": false,
    "default": true
},

If you'd like I could open a PR with this approach. Based on your experience, is it fine to use \n as a delimiter for the step definition, or should it support multi-lines?

Luc45 commented 7 months ago

If it's possible to come up with a regex that matches with/without delimiters that'd be ideal, but I'm not sure what are the other use-cases to cover, so I was just playing with it