You are reading the documentation for gurkencheck 0.0.10. Go to version 0.0.10.

gurkencheck A linter for Gherkin feature files.

gurkencheck

A linter for Gherkin feature files. It reads your .feature files and tells you where they drift from the conventions your team has agreed on.

Install#

npm install --save-dev gurkencheck

Get started#

Run it. With no configuration file, gurkencheck uses its recommended rules: the ones that catch a mistake rather than express a preference — an empty file, a scenario with no name, a variable that will never be substituted. Nothing in that set depends on how you lay a file out, so it should be quiet on a codebase that has never been linted.

npx gurkencheck

When you want something different, create a file called .gurkencheckrc and list the rules you want. A configuration file replaces the recommended set rather than adding to it, so every rule is off until you switch it on.

A rule is set to "on", "warn" or "off". "warn" reports exactly the same findings but does not fail the run, which is what you want for a rule the team is working towards rather than enforcing.

{
  "no-unnamed-features": "on",
  "no-unnamed-scenarios": "on",
  "no-trailing-spaces": "on",
  "indentation": ["on", {"Feature": 0, "Scenario": 2, "Step": 4}]
}

With no paths given, it searches the current directory for .feature files. It exits with 0 when there is nothing worse than a warning, 1 when a rule set to "on" was broken, and 2 when it could not run at all.

Feature files in another language#

Gherkin is translated into dozens of languages. A file says which one it is written in with a header on its first line:

# language: fr
Fonctionnalité: Se déconnecter

  Scénario: Se déconnecter
    Quand Ulrick se déconnecte

If every file in your project is written in the same language, set it once instead, with --language fr or a language key in your configuration file. A header in a file always wins over that setting, so a project can be mostly one language with exceptions.

Sharing a configuration#

Build on top of another configuration with extends. What your file says wins over what it extends, and later entries in a list win over earlier ones.

{
  "extends": "gurkencheck:recommended",
  "indentation": ["on", {"Step": 4}],
  "no-trailing-spaces": "off"
}

An entry is one of three things:

EntryWhat it means
gurkencheck:recommendedThe built-in recommended rules.
./team/.gurkencheckrcAnother file, resolved from the file doing the extending.
@acme/gurkencheck-configAn installed package exporting a configuration, as JSON or as a module.

Command line options#

OptionWhat it does
-f, --formatOutput format: stylish (the default), json, junit, sarif or tap, or the path to a formatter of your own.
-c, --configPath to a configuration file, if it is not .gurkencheckrc in the current directory.
-i, --ignoreComma separated globs to skip. Overrides .gurkencheckignore.
-r, --rulesdirA directory holding your own rules. May be given more than once.
-l, --languageThe dialect to read files in when they carry no # language: header.
-h, --helpShow the options.
-v, --versionShow the version number.

Reading the output#

Findings go to stdout, so gurkencheck > report.json and gurkencheck | less work. Anything that stops the linter running — a bad option, an invalid configuration — goes to stderr, so it never lands in a redirected report.

Each finding is printed as line:column    message    rule. Both numbers start at 1, so an editor can underline exactly the right text. A finding about a whole file or a whole line — a missing new line at the end of the file, say — shows the line on its own. The json format carries the same line and column fields, in the same shape eslint's JSON formatter uses, so tools built around that already understand it.

Counting what is in your files#

The same files answer a different question. Before a team agrees a convention it helps to know what it already has: how many test cases will really run, how much of the step vocabulary is shared, and how much of it is the same sentence written twice. gurkencheck stats counts your feature files rather than checking them.

npx gurkencheck stats

It exits 0 whatever it finds, so it can sit in a build without ever failing one. The word stats has to come first, before any option, because the command has a --format of its own whose values have nothing to do with the linter's. Feature file statistics goes through what each number means.

Skipping files#

Put one glob per line in a .gurkencheckignore file, or pass --ignore on the command line. Without either, node_modules is skipped and everything else is checked.

A pattern that matches a directory skips everything below it, the same way .gitignore and .eslintignore work, so build is enough and you do not have to write build/**. Blank lines and lines starting with # are ignored.

Reporting to GitHub code scanning#

--format sarif writes a SARIF 2.1.0 log, which GitHub reads directly. Upload it and each finding is shown inline on the pull request that introduced it.

- run: npx gurkencheck --format sarif > gurkencheck.sarif
  continue-on-error: true

- uses: github/codeql-action/upload-sarif@v3
  with:
    sarif_file: gurkencheck.sarif

Paths in the log are relative to the directory gurkencheck ran in, which is what code scanning needs in order to match a finding to a file in the repository.

Switching a rule off for one place#

Ignoring a whole file is often too blunt: one long step name should not cost you every other check in that file. Write a comment in the feature file instead.

# gurkencheck-disable-next-line name-length
  Scenario: A name that is long for a good reason and stays that way

# gurkencheck-disable use-and, name-length
  ... everything below here skips those two rules ...
# gurkencheck-enable use-and

# gurkencheck-disable-file no-trailing-spaces
DirectiveWhat it covers
gurkencheck-disable-next-lineThe line directly below the comment.
gurkencheck-disableFrom the comment to the end of the file, or to the next gurkencheck-enable.
gurkencheck-enableResumes the rules a gurkencheck-disable switched off.
gurkencheck-disable-fileThe whole file, wherever the comment appears.

Name the rules you mean, separated by commas or spaces. A directive naming no rules covers all of them. Comments inside a doc string are text and are left alone.

The 5 always-on rules cannot be switched off this way. A file that breaks one of them cannot be read at all, so hiding the message would leave nothing in its place.

Rules you switch on#

These are all off by default. Each page shows an example that passes and one that fails.

Always on#

These 5 are not really settings. They describe things Gherkin itself refuses to read, so a file that breaks one of them cannot be checked at all.

Writing your own formatter#

Pass a path or a package name to --format. The module exports a function taking the results; it may print the output itself, or return it as a string and let gurkencheck print it.

// count.mjs
export default function count(results) {
  const findings = results.reduce((total, file) => total + file.errors.length, 0);
  return `${findings} findings in ${results.length} files`;
}
npx gurkencheck --format ./count.mjs

Each result is {filePath, errors}, and each error is {message, rule, line, column, severity}. A default export, a printResults export, or a module that is itself the function all work.

Writing your own rule#

Point --rulesdir at a directory of your own modules. Each one exports an object with a name and a run function, and gets called once per file.

// rules/no-lorem.js
const name = 'no-lorem';

export default {
  name,
  run(feature, file) {
    if (feature === undefined) return [];
    return feature.children
      .filter((child) => child.scenario?.name.includes('lorem'))
      .map((child) => ({
        message: 'Placeholder text left in a scenario name',
        rule: name,
        line: child.scenario.location.line,
      }));
  },
};

run may also be async and return a promise, for a rule that has to wait for something — reading a file, or asking an issue tracker whether a tag refers to a real ticket. Files are checked one after another, so rules see a predictable order.

Then switch it on by name, the same as any built-in rule:

npx gurkencheck --rulesdir ./rules