@serenity-js/cucumber
Version:
Serenity/JS test runner adapter for seamless integration with any version of Cucumber.js, facilitating BDD-style test automation and leveraging Serenity/JS reporting capabilities
149 lines • 5.91 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.CucumberOptions = void 0;
const core_1 = require("@serenity-js/core");
const io_1 = require("@serenity-js/core/lib/io");
/**
* @private
*/
class CucumberOptions {
finder;
fileSystem;
config;
constructor(finder, fileSystem, config) {
this.finder = finder;
this.fileSystem = fileSystem;
this.config = config;
}
isStrict() {
return this.asBoolean('strict', true);
}
asArgumentsForCucumber(version) {
return Object.keys(this.config)
.reduce((acc, option) => isNotEmpty(this.config[option])
? acc.concat(this.optionToValues(option, this.config[option], version))
: acc,
// Cucumber ignores the first two arguments anyway, but let's add them for completeness
// https://github.com/cucumber/cucumber-js/blob/d74bc45ba98132bdd0af62e0e52d1fe9ff017006/src/cli/helpers.js#L15
['node', 'cucumber-js'])
.concat(this.config.rerun && this.fileSystem.exists(io_1.Path.from(this.config.rerun)) ? this.config.rerun : []);
}
asArray(value) {
if (value === undefined) {
return [];
}
if (Array.isArray(value)) {
return Array.from(value);
}
return [value];
}
asCucumberApiConfiguration() {
// https://github.com/cucumber/cucumber-js/blob/main/docs/configuration.md
return {
dryRun: this.config.dryRun,
forceExit: false,
failFast: this.config.failFast,
format: this.asArray(this.config.format),
formatOptions: this.config.formatOptions,
paths: this.asArray(this.config.paths)
.flatMap(glob => this.absolutePathsToFilesMatching(glob)),
import: this.asArray(this.config.import)
.flatMap(glob => this.absolutePathsToFilesMatching(glob)),
require: this.asArray(this.config.require)
.flatMap(glob => this.absolutePathsToFilesMatching(glob)),
requireModule: this.asArray(this.config.requireModule),
language: this.config.language,
name: this.asArray(this.config.name),
publish: false,
retry: this.config.retry,
retryTagFilter: this.config.retryTagFilter,
strict: this.config.strict,
tags: this.asArray(this.config.tags).join(' and '),
worldParameters: this.config.worldParameters, // Cucumber typings rely on `type-fest` and we don't need another dependency to define one type
// order: PickleOrder
// parallel: number, // this only works when Cucumber is the runner, in which scenario CucumberCLIAdapter is not used anyway
};
}
absolutePathsToFilesMatching(glob) {
const matchingPaths = this.finder.filesMatching(glob);
if (matchingPaths.length === 0) {
throw new core_1.ConfigurationError(`No files found matching the pattern ${glob}`);
}
return matchingPaths.map(path => path.value);
}
optionToValues(option, value, version) {
const cliOption = this.asCliOptionName(option);
switch (true) {
case cliOption === 'tags' && version.isAtLeast(new io_1.Version('2.0.0')) && value !== false:
return this.valuesToArgs(cliOption, this.tagsToCucumberExpressions(listOf(value)));
case cliOption === 'rerun':
return []; // ignore since we're appending the rerun file anyway
case typeof value === 'boolean':
return listOf(this.flagToArg(cliOption, value));
case this.isObject(value):
return this.valuesToArgs(cliOption, JSON.stringify(value, undefined, 0));
default:
return this.valuesToArgs(cliOption, listOf(value));
}
}
asBoolean(key, defaultValue) {
if (typeof this.config[key] === 'boolean') {
return this.config[key];
}
if (typeof this.config[negated(key)] === 'boolean') {
return !this.config[negated(key)];
}
return defaultValue;
}
isObject(value) {
return typeof value === 'object'
&& Array.isArray(value) === false
&& Object.prototype.toString.call(value) === '[object Object]';
}
/**
* Converts camelCase option names to kebab-case.
*/
asCliOptionName(option) {
return option
.replaceAll(/([\da-z]|(?=[A-Z]))([A-Z])/g, '$1-$2')
.toLowerCase();
}
tagsToCucumberExpressions(tags) {
return tags.filter(tag => !!tag.replace)
.map(tag => tag.replaceAll('~', 'not '))
.join(' and ');
}
flagToArg(option, value) {
switch (true) {
case !!value:
return `--${option}`;
case isNegated(option) && !value:
return `--${option.replace(/^no-/, '')}`;
default:
return `--no-${option}`;
}
}
valuesToArgs(option, values) {
return listOf(values)
.map(value => [`--${option}`, value])
.reduce((acc, tuple) => acc.concat(tuple), []);
}
}
exports.CucumberOptions = CucumberOptions;
function isNegated(optionName) {
return optionName.startsWith('no-');
}
// this method will need to be smarter if it was to be public, i.e. to avoid double negatives like noStrict=false
function negated(name) {
return 'no' + name.charAt(0).toUpperCase() + name.slice(1);
}
function isNotEmpty(value) {
return value !== undefined
&& value !== null
&& value !== ''
&& !(Array.isArray(value) && value.length === 0);
}
function listOf(valueOrValues) {
return [].concat(valueOrValues).filter(isNotEmpty);
}
//# sourceMappingURL=CucumberOptions.js.map