Skip to content
Permalink
Browse files
fix(cucumber): a sequence of activities is correctly reported
affects: @serenity-js/core, @serenity-js-examples/cucumber
  • Loading branch information
jan-molak committed Jul 30, 2018
1 parent 60b3b98 commit b66b266e3ff474ae201e3687da9be7831fab9b42
@@ -0,0 +1,17 @@
Reporting Results

Narrative:
In order to quickly learn how to use Serenity/JS with Cucumber
As a busy developer
I'd like to see examples demonstrating how Serenity/JS reports the result of executing Cucumber test scenarios.

This note is called _"the narrative"_. It can be used to provide the context around the business capability of your
product ("Reporting Results" in this case) and its features that help to enable this capability.

**Please note:** While [Cucumber](https://github.com/cucumber/cucumber-js) allows you to capture a description
of each feature in the `.feature` file, [Serenity/JS](https://serenityjs.org) allows us to group those `.feature`
files in directories corresponding to "epics", "themes" or "business capabilities" of your system and provide
each one of those with additional context using this `narrative.txt` file.

**By the way:** Did you notice that you can use **[markdown syntax](https://www.markdownguide.org/)** to better express
your thoughts?
@@ -0,0 +1,21 @@
Feature: Reports passing scenarios

In order to see how Serenity/JS reports a passing Cucumber scenario
As a curious developer
I'd like to see an example implementation

Scenario: A passing scenario

Here's an example of a passing scenario.
The rule here is simple:
- provided that all the steps pass
- the scenario passes as well.

Did you notice that we've used [markdown](https://www.markdownguide.org/) here again?

This comes in handy when you want to link to external files such as architecture diagrams, wireframes or additional
documentation.

Given a step that passes
When another step passes
Then the third step passes

This file was deleted.

@@ -3,23 +3,23 @@ import { TableDefinition } from 'cucumber';
export = function() {
// todo: removes the ones that are not necessary

this.Given(/^.*step (?:.*) passes$/, function() {
this.Given(/^.*step.*passes$/, function() {
// return Promise.resolve();
});

this.Given(/^.*step (?:.*) fails$/, function() {
this.Given(/^.*step.*fails$/, function() {
return Promise.reject(new Error(`Something's wrong`));
});

this.Given(/^.*step (?:.*) marked as pending/, function() {
this.Given(/^.*step.*marked as pending/, function() {
return Promise.resolve('pending');
});

this.Given(/^.*step (?:.*) receives a table:$/, function(data: TableDefinition) {
this.Given(/^.*step.*receives a table:$/, function(data: TableDefinition) {
return Promise.resolve();
});

this.Given(/^.*step (?:.*) receives a doc string:$/, function(docstring: string) {
this.Given(/^.*step:.*receives a doc string:$/, function(docstring: string) {
return Promise.resolve();
});

@@ -8,7 +8,7 @@ module.exports = function() {

// todo: implement serenity.configure(...)
const crewMembers = [
new ArtifactArchiver(new FileSystem(new Path('./target'))),
new ArtifactArchiver(new FileSystem(new Path('./target/site/serenity'))),
new SerenityBDDReporter(),
new DebugReporter(),
];
@@ -16,10 +16,12 @@
"main": "lib/index.js",
"typings": "lib/index.d.ts",
"scripts": {
"clean": "rimraf lib",
"clean": "rimraf target",
"lint": "tslint --project tsconfig.json --config ../../tslint.json --format stylish",
"pretest": "serenity update",
"test": "cucumberjs --compiler ts:ts-node/register --require node_modules/@serenity-js/cucumber/register.js --require ./features/step_definitions/sample.steps.ts --require './features/support/configure_serenity.ts'"
"test:update-serenity": "serenity update",
"test:acceptance": "cucumberjs --compiler ts:ts-node/register --require node_modules/@serenity-js/cucumber/register.js --require ./features/step_definitions/sample.steps.ts --require './features/support/configure_serenity.ts'",
"test:report": "serenity run",
"test": "failsafe clean test:update-serenity test:acceptance test:report"
},
"repository": {
"type": "git",
@@ -42,6 +42,28 @@ describe('SerenityBDDReporter', () => {
expect(report.testSteps[0].result).to.equal('SUCCESS');
});

it('reports the outcome of a sequence of several activities', () => {
const pickACard = new ActivityDetails(new Name('Pick the default credit card'));
const makePayment = new ActivityDetails(new Name('Make the payment'));

given(reporter).isNotifiedOfFollowingEvents(
new SceneStarts(defaultCardScenario),
new ActivityStarts(pickACard),
new ActivityFinished(pickACard, new ExecutionSuccessful()),
new ActivityStarts(makePayment),
new ActivityFinished(makePayment, new ExecutionSuccessful()),
new SceneFinished(defaultCardScenario, new ExecutionSuccessful()),
);

const report: SerenityBDDReport = stageManager.notifyOf.firstCall.lastArg.artifact.contents;

expect(report.testSteps).to.have.lengthOf(2);
expect(report.testSteps[0].description).to.equal(pickACard.name.value);
expect(report.testSteps[0].result).to.equal('SUCCESS');
expect(report.testSteps[1].description).to.equal(makePayment.name.value);
expect(report.testSteps[1].result).to.equal('SUCCESS');
});

it('reports the outcome of nested activities', () => {
const pickACard = new ActivityDetails(new Name('Pick the default credit card'));
const viewListOfCards = new ActivityDetails(new Name('View the list of available cards'));
@@ -100,11 +100,22 @@ export class ScenarioReport {
screenshots: [],
};

this.activities.mostRecentlyAccessedItem().children.push(activityReport as any);
this.activities.last().children.push(activityReport as any);
this.activities.push(activityReport);
});
}

activityFinished(value: ActivityDetails, outcome: Outcome, time: Timestamp) {
return this.withMutated(report => this.mapOutcome(outcome, (result: string, error?: ErrorDetails) => {

const activityReport = this.activities.pop();

activityReport.result = result;
activityReport.exception = error;
activityReport.duration = Timestamp.fromMillisecondTimestamp(activityReport.startTime).diff(time).milliseconds;
}));
}

backgroundDetected(name: Name, description: Description) {
return this.withMutated(report => {
report.backgroundTitle = name.value;
@@ -124,17 +135,6 @@ export class ScenarioReport {
});
}

activityFinished(value: ActivityDetails, outcome: Outcome, time: Timestamp) {
return this.withMutated(report => this.mapOutcome(outcome, (result: string, error?: ErrorDetails) => {

const activityReport = this.activities.pop();

activityReport.result = result;
activityReport.exception = error;
activityReport.duration = Timestamp.fromMillisecondTimestamp(activityReport.startTime).diff(time).milliseconds;
}));
}

executionFinishedWith(outcome: Outcome): ScenarioReport {
return this.withMutated(report => this.mapOutcome(outcome, (result: string, error: ErrorDetails = undefined) => {
report.result = result;
@@ -193,6 +193,10 @@ class Stack<T> {
return this.mostRecent;
}

last() {
return this.items[this.items.length - 1];
}

mostRecentlyAccessedItem() {
return this.mostRecent;
}

0 comments on commit b66b266

Please sign in to comment.