@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
252 lines • 10.7 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.CucumberCLIAdapter = void 0;
const io_1 = require("@serenity-js/core/lib/io");
const model_1 = require("@serenity-js/core/lib/model");
const path = __importStar(require("path")); // eslint-disable-line unicorn/import-style
const CucumberOptions_1 = require("./CucumberOptions");
/**
* Allows for programmatic execution of Cucumber test scenarios.
*
* ## Learn more
* - [`TestRunnerAdapter`](https://serenity-js.org/api/core-adapter/interface/TestRunnerAdapter/)
*
* @group Integration
*/
class CucumberCLIAdapter {
loader;
output;
pathsToScenarios = [];
options;
constructor(config, loader, fileSystem, output) {
this.loader = loader;
this.output = output;
this.options = new CucumberOptions_1.CucumberOptions(new io_1.FileFinder(io_1.Path.from(this.loader.cwd)), fileSystem, config);
}
/**
* Scenario success threshold for this test runner, calculated based on [`CucumberConfig`](https://serenity-js.org/api/cucumber-adapter/interface/CucumberConfig/)
*/
successThreshold() {
return this.options.isStrict()
? model_1.ExecutionIgnored
: model_1.ImplementationPending;
}
/**
* Loads feature files.
*
* @param pathsToScenarios
* Absolute or relative paths to feature files
*/
async load(pathsToScenarios) {
this.pathsToScenarios = pathsToScenarios.map(maybeAbsolutePathToScenario => {
// Ensure paths provided to Cucumber are relative
// see https://github.com/cucumber/cucumber-js/issues/1900
return path.isAbsolute(maybeAbsolutePathToScenario)
? path.relative(this.loader.cwd, maybeAbsolutePathToScenario)
: maybeAbsolutePathToScenario;
});
// todo: implement loading, so parsing feature files to determine how many executable we have
}
/**
* Returns the number of loaded scenarios
*
* @throws [`LogicError`](https://serenity-js.org/api/core/class/LogicError/)
* If called before `load`
*/
scenarioCount() {
// todo: we should count the actual executable scenarios to avoid launching a WebdriverIO worked
// for a feature file without any scenarios.
return this.pathsToScenarios.length;
// if (this.totalScenarios === undefined) {
// throw new LogicError('Make sure to call `load` before calling `scenarioCount`');
// }
//
// return this.totalScenarios;
}
/**
* Instructs Cucumber to execute feature files located at `pathsToScenarios`
*/
async run() {
const version = this.loader.hasAvailable('@cucumber/cucumber')
? this.loader.versionOf('@cucumber/cucumber')
: this.loader.versionOf('cucumber');
const serenityListener = this.loader.resolve('@serenity-js/cucumber');
return this.runScenarios(version, serenityListener, this.pathsToScenarios);
}
runScenarios(version, serenityListener, pathsToScenarios) {
if (version.isAtLeast(new io_1.Version('10.0.0'))) {
return this.runWithCucumber10(serenityListener, pathsToScenarios);
}
if (version.isAtLeast(new io_1.Version('9.0.0'))) {
return this.runWithCucumber8JavaScriptApi(serenityListener, pathsToScenarios);
}
if (version.isAtLeast(new io_1.Version('8.7.0'))) {
return this.runWithCucumber8JavaScriptApi(serenityListener, pathsToScenarios);
}
const argv = this.options.asArgumentsForCucumber(version);
if (version.isAtLeast(new io_1.Version('8.0.0'))) {
return this.runWithCucumber8(argv, serenityListener, pathsToScenarios);
}
if (version.isAtLeast(new io_1.Version('7.0.0'))) {
return this.runWithCucumber7(argv, serenityListener, pathsToScenarios);
}
if (version.isAtLeast(new io_1.Version('3.0.0'))) {
return this.runWithCucumber3to6(argv, serenityListener, pathsToScenarios);
}
if (version.isAtLeast(new io_1.Version('2.0.0'))) {
return this.runWithCucumber2(argv, serenityListener, pathsToScenarios);
}
return this.runWithCucumber0to1(argv, serenityListener, pathsToScenarios);
}
async runWithCucumber10(pathToSerenityListener, pathsToScenarios) {
const output = this.output.get();
const serenityListenerUrl = io_1.Path.from(pathToSerenityListener).toFileURL().href;
const outputUrl = output.value() ?? undefined;
// https://github.com/cucumber/cucumber-js/blob/main/docs/deprecations.md#ambiguous-colons-in-formats
// https://github.com/cucumber/cucumber-js/issues/2326#issuecomment-1711701382
return await this.runWithCucumberApi([
serenityListenerUrl,
outputUrl,
], pathsToScenarios, output);
}
// https://github.com/cucumber/cucumber-js/blob/main/docs/deprecations.md
async runWithCucumber8JavaScriptApi(pathToSerenityListener, pathsToScenarios) {
const output = this.output.get();
return await this.runWithCucumberApi(`${pathToSerenityListener}:${output.value()}`, pathsToScenarios, output);
}
async runWithCucumberApi(serenityFormatter, pathsToScenarios, output) {
const configuration = this.options.asCucumberApiConfiguration();
const { loadConfiguration, loadSupport, runCucumber } = this.loader.require('@cucumber/cucumber/api');
// https://github.com/cucumber/cucumber-js/blob/main/src/api/environment.ts
const environment = {
cwd: this.loader.cwd,
stdout: process.stdout,
stderr: process.stderr,
env: process.env,
debug: false,
};
configuration.format.push(serenityFormatter);
configuration.paths = pathsToScenarios;
// https://github.com/cucumber/cucumber-js/blob/main/src/configuration/types.ts
const { runConfiguration } = await loadConfiguration({ provided: configuration }, environment);
try {
// load the support code upfront
const support = await loadSupport(runConfiguration, environment);
// run cucumber, using the support code we loaded already
const { success } = await runCucumber({ ...runConfiguration, support }, environment);
await output.cleanUp();
return success;
}
catch (error) {
await output.cleanUp();
throw error;
}
}
runWithCucumber8(argv, pathToSerenityListener, pathsToScenarios) {
const cucumber = this.loader.require('@cucumber/cucumber');
const output = this.output.get();
return new cucumber.Cli({
argv: argv.concat('--format', `${pathToSerenityListener}:${output.value()}`, ...pathsToScenarios),
cwd: this.loader.cwd,
stdout: process.stdout,
stderr: process.stderr,
env: process.env,
})
.run()
.then(cleanUpAndPassThrough(output), cleanUpAndReThrow(output));
}
runWithCucumber7(argv, pathToSerenityListener, pathsToScenarios) {
const cucumber = this.loader.require('@cucumber/cucumber');
const output = this.output.get();
return new cucumber.Cli({
argv: argv.concat('--format', `${pathToSerenityListener}:${output.value()}`, ...pathsToScenarios),
cwd: this.loader.cwd,
stdout: process.stdout,
})
.run()
.then(cleanUpAndPassThrough(output), cleanUpAndReThrow(output));
}
runWithCucumber3to6(argv, pathToSerenityListener, pathsToScenarios) {
const cucumber = this.loader.require('cucumber');
const output = this.output.get();
return new cucumber.Cli({
argv: argv.concat('--format', `${pathToSerenityListener}:${output.value()}`, ...pathsToScenarios),
cwd: this.loader.cwd,
stdout: process.stdout,
})
.run()
.then(cleanUpAndPassThrough(output), cleanUpAndReThrow(output));
}
runWithCucumber2(argv, pathToSerenityListener, pathsToScenarios) {
const cucumber = this.loader.require('cucumber');
return new cucumber.Cli({
argv: argv.concat('--require', pathToSerenityListener, ...pathsToScenarios),
cwd: this.loader.cwd,
stdout: process.stdout,
}).run();
}
runWithCucumber0to1(argv, pathToSerenityListener, pathsToScenarios) {
return new Promise((resolve, reject) => {
this.loader.require('cucumber')
.Cli(argv.concat('--require', pathToSerenityListener, ...pathsToScenarios))
.run((wasSuccessful) => resolve());
});
}
}
exports.CucumberCLIAdapter = CucumberCLIAdapter;
/**
* @private
*/
function cleanUpAndPassThrough(output) {
return (result) => {
return output.cleanUp()
.then(() => result);
};
}
/**
* @private
*/
function cleanUpAndReThrow(output) {
return (error) => {
return output.cleanUp()
.then(() => {
throw error;
}, ignoredError => {
throw error;
});
};
}
//# sourceMappingURL=CucumberCLIAdapter.js.map