@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
130 lines • 8.49 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.cucumberEventProtocolAdapter = cucumberEventProtocolAdapter;
const core_1 = require("@serenity-js/core");
const errors_1 = require("@serenity-js/core/lib/errors");
const io_1 = require("@serenity-js/core/lib/io");
const model_1 = require("@serenity-js/core/lib/model");
const tiny_types_1 = require("tiny-types");
const errors_2 = require("../../errors");
const gherkin_1 = require("./gherkin");
/**
* @private
*/
function cucumberEventProtocolAdapter({ serenity, notifier, mapper, cache }) {
return class CucumberEventProtocolAdapter {
// note: exported class expression can't have private properties
log;
constructor({ eventBroadcaster, log }) {
this.log = log;
eventBroadcaster.on('gherkin-document', ({ uri, document }) => {
(0, tiny_types_1.ensure)('gherkin-document :: uri', uri, (0, tiny_types_1.isDefined)());
(0, tiny_types_1.ensure)('gherkin-document :: document', document, (0, tiny_types_1.isDefined)());
const path = new io_1.Path(uri);
cache.set(path, mapper.map(document, path)); // eslint-disable-line unicorn/no-array-method-this-argument
});
eventBroadcaster.on('test-case-prepared', ({ steps, sourceLocation }) => {
(0, tiny_types_1.ensure)('test-case-prepared :: steps', steps, (0, tiny_types_1.isDefined)());
(0, tiny_types_1.ensure)('test-case-prepared :: sourceLocation', sourceLocation, (0, tiny_types_1.isDefined)());
const path = new io_1.Path(sourceLocation.uri), map = cache.get(path), scenario = map.get(gherkin_1.Scenario).onLine(sourceLocation.line);
if (scenario.outline) {
const outline = map.get(gherkin_1.ScenarioOutline).onLine(scenario.outline.line);
map.set(new gherkin_1.ScenarioOutline(outline.location, outline.name, outline.description, outline.steps, outline.parameters)).onLine(scenario.outline.line);
}
map.set(new gherkin_1.Scenario(scenario.location, scenario.name, scenario.description, interleaveStepsAndHooks(scenario.steps, steps), scenario.tags, scenario.outline)).onLine(sourceLocation.line);
});
eventBroadcaster.on('test-case-started', ({ sourceLocation }) => {
(0, tiny_types_1.ensure)('test-case-started :: sourceLocation', sourceLocation, (0, tiny_types_1.isDefined)());
const map = cache.get(new io_1.Path(sourceLocation.uri)), scenario = map.get(gherkin_1.Scenario).onLine(sourceLocation.line), sceneId = serenity.assignNewSceneId();
if (scenario.outline) {
const outline = map.get(gherkin_1.ScenarioOutline).onLine(scenario.outline.line);
notifier.outlineDetected(sceneId, scenario, outline, map.getFirst(gherkin_1.Feature));
}
notifier.scenarioStarts(sceneId, scenario, map.getFirst(gherkin_1.Feature));
});
eventBroadcaster.on('test-step-started', ({ index, testCase }) => {
(0, tiny_types_1.ensure)('test-step-started :: index', index, (0, tiny_types_1.isDefined)());
(0, tiny_types_1.ensure)('test-step-started :: testCase', testCase, (0, tiny_types_1.isDefined)());
const map = cache.get(new io_1.Path(testCase.sourceLocation.uri)), scenario = map.get(gherkin_1.Scenario).onLine(testCase.sourceLocation.line), step = scenario.steps[index];
if (step instanceof gherkin_1.Step) { // ignore hooks
notifier.stepStarts(step);
}
});
eventBroadcaster.on('test-step-finished', ({ index, result, testCase }) => {
(0, tiny_types_1.ensure)('test-step-finished :: index', index, (0, tiny_types_1.isDefined)());
(0, tiny_types_1.ensure)('test-step-finished :: result', result, (0, tiny_types_1.isDefined)());
(0, tiny_types_1.ensure)('test-step-finished :: testCase', testCase, (0, tiny_types_1.isDefined)());
const map = cache.get(new io_1.Path(testCase.sourceLocation.uri)), scenario = map.get(gherkin_1.Scenario).onLine(testCase.sourceLocation.line), step = scenario.steps[index];
if (step instanceof gherkin_1.Step) { // ignore hooks
notifier.stepFinished(step, this.outcomeFrom(result));
}
});
eventBroadcaster.on('test-case-finished', ({ result, sourceLocation }) => {
(0, tiny_types_1.ensure)('test-case-finished :: result', result, (0, tiny_types_1.isDefined)());
(0, tiny_types_1.ensure)('test-case-finished :: sourceLocation', sourceLocation, (0, tiny_types_1.isDefined)());
const map = cache.get(new io_1.Path(sourceLocation.uri)), scenario = map.get(gherkin_1.Scenario).onLine(sourceLocation.line), nonHookSteps = scenario.steps.filter(step => step instanceof gherkin_1.Step);
const outcome = nonHookSteps.length > 0
? this.outcomeFrom(result)
: new model_1.ImplementationPending(new errors_1.ImplementationPendingError(`"${scenario.name.value}" has no test steps`));
notifier.scenarioFinished(scenario, map.getFirst(gherkin_1.Feature), outcome);
});
}
outcomeFrom(result) {
const error = !!result.exception && this.errorFrom(result.exception);
switch (result.status) {
case 'undefined':
return new model_1.ImplementationPending(new errors_1.ImplementationPendingError('Step not implemented'));
case 'ambiguous':
case 'failed':
switch (true) {
case error instanceof errors_1.AssertionError: return new model_1.ExecutionFailedWithAssertionError(error);
case error instanceof errors_1.TestCompromisedError: return new model_1.ExecutionCompromised(error);
default: return new model_1.ExecutionFailedWithError(error);
}
case 'pending':
return new model_1.ImplementationPending(new errors_1.ImplementationPendingError('Step not implemented'));
case 'skipped':
return new model_1.ExecutionSkipped();
// case 'passed':
default:
return new model_1.ExecutionSuccessful();
}
}
errorFrom(maybeError) {
switch (true) {
case maybeError instanceof core_1.RuntimeError:
return maybeError;
case maybeError instanceof Error && maybeError.name === 'AssertionError' && maybeError.message && hasOwnProperty(maybeError, 'expected') && hasOwnProperty(maybeError, 'actual'):
return serenity.createError(errors_1.AssertionError, {
message: maybeError.message,
diff: {
expected: maybeError.expected,
actual: maybeError.actual,
},
cause: maybeError
});
case typeof maybeError === 'string' && maybeError.startsWith('Multiple step definitions match'):
return new errors_2.AmbiguousStepDefinitionError(maybeError);
default:
return errors_1.ErrorSerialiser.deserialiseFromStackTrace(maybeError);
}
}
};
}
/**
* @private
*/
function interleaveStepsAndHooks(steps, stepsLocations) {
const isAHook = (stepLocations) => stepLocations.actionLocation && !stepLocations.sourceLocation, matching = (location) => (step) => step.location.path.equals(new io_1.Path(location.sourceLocation.uri)) &&
step.location.line === location.sourceLocation.line;
return stepsLocations.map(location => isAHook(location)
? new gherkin_1.Hook(new io_1.FileSystemLocation(new io_1.Path(location.actionLocation.uri), location.actionLocation.line), new model_1.Name('Setup'))
: steps.find(matching(location)));
}
/**
* @private
*/
function hasOwnProperty(value, fieldName) {
return Object.prototype.hasOwnProperty.call(value, fieldName);
}
//# sourceMappingURL=CucumberEventProtocolAdapter.js.map
;