Skip to content
Permalink
Browse files
feat(cucumber): support for Scenario Outlines
affects: @serenity-js/core, @serenity-js/cucumber, @integration/cucumber,
@integration/testing-tools, @serenity-js-examples/cucumber

ISSUES CLOSED: Closes #168 and closes #220. Partially addresses #162.
  • Loading branch information
jan-molak committed Aug 26, 2018
1 parent 76c165a commit 616640d09afe608e122e64949b60bc88ea2ca8fa
Showing 98 changed files with 2,554 additions and 236 deletions.
@@ -14,3 +14,4 @@ _book
target/
*.log
packages/*/CHANGELOG.md
packages/*/lib
@@ -17,3 +17,6 @@ integration-test:

verify:
npm run verify

report:
npm run report
@@ -0,0 +1,74 @@
Feature: Reports scenario outlines

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

Scenario Outline: People who make Serenity happen

Here's an example of a scenario outline.
Outlines are useful when you need to run the exact same scenario multiple times
for different inputs and/or expected outputs.

When <Developer> makes a contribution of:
| value |
| time |
| talent |
| great code |
Then they help bring serenity to fellow devs

@set-1
Examples: Serenity/JS contributors

Here are some of the amazing people who have contributed their time and talent to the Serenity/JS project

| Developer |
| jan-molak |
| InvictusMB |
| asnov |
| wakaleo |
| mustafa-turab-ali |
| ttretau |
| nbarrett |
| monkeyjabs |
| danscotton |
| quantang |
| wswebcreation |
| AlexandrosD |

@set-2
Examples: Serenity BDD contributors

Here are the people behind Serenity BDD Core module

| Developer |
| wakaleo |
| YamStranger |
| cliviu |
| verhagen |
| mikezx6r |
| hazmeister |
| schmurgon |
| jeffjensen |
| bmwsedee |
| vikramvi |
| ovenal |
| David-Paterson |
| kutzi |
| x-hovo-x |
| marcin-caban |
| nbarrett |
| CoeJoder |
| HNygard |
| lijsselstein |
| eskape |
| ihostage |
| willhuang85 |
| joxerTMD |
| jordanbragg |
| marek5050 |
| hypery2k |
| scormaq |
| ptillemans |
| robertzett |
| ricardorlg |
@@ -17,7 +17,7 @@ export = function() {
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();
});

@@ -26,4 +26,12 @@ export = function() {
setTimeout(resolve, 1000);
});
});

this.When(/^(.*) makes a contribution/, function(developerName: string, dataTable: TableDefinition) {
return Promise.resolve();
});

this.Then(/^.*help bring serenity to fellow devs$/, function() {
return Promise.resolve();
});
};
@@ -19,7 +19,7 @@
"clean": "rimraf target",
"lint": "tslint --project tsconfig.json --config ../../tslint.json --format stylish",
"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: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",
"verify": "npm test || true"
@@ -0,0 +1,13 @@
Feature: Serenity/JS recognises scenario outlines

Scenario Outline: Sample outline

Given a step that <result>

Examples: Example results

Description of the examples

| result |
| passes |
| fails |
@@ -28,8 +28,6 @@ describe('@serenity-js/cucumber', function() {
then(res => {
expect(res.exitCode).to.equal(1);

expect(res.events).to.have.lengthOf(6);

Pick.from(res.events)
.next(SceneStarts, event => expect(event.value.name).to.equal(new Name('A passing scenario')))
.next(TestRunnerDetected, event => expect(event.value).to.equal(new Name('Cucumber')))
@@ -27,8 +27,6 @@ describe('@serenity-js/cucumber', function() {
then(res => {
expect(res.exitCode).to.equal(0);

expect(res.events).to.have.lengthOf(6);

Pick.from(res.events)
.next(ActivityStarts, event => expect(event.value.name).to.equal(new Name(
'Given a step that receives a table:\n' +
@@ -1,5 +1,5 @@
import { expect, ifExitCodeIsOtherThan, logOutput } from '@integration/testing-tools';
import { SceneDescriptionDetected, SceneStarts } from '@serenity-js/core/lib/events';
import { FeatureNarrativeDetected, SceneDescriptionDetected, SceneStarts } from '@serenity-js/core/lib/events';
import { Description, Name } from '@serenity-js/core/lib/model';

import 'mocha';
@@ -27,10 +27,14 @@ describe('@serenity-js/cucumber', function() {
then(res => {
expect(res.exitCode).to.equal(0);

expect(res.events).to.have.lengthOf(7);

Pick.from(res.events)
.next(SceneStarts, event => expect(event.value.name).to.equal(new Name('First scenario')))
.next(FeatureNarrativeDetected, event => {
expect(event.description).to.equal(new Description(
'In order to accurately report the scenario\n' +
'Serenity/JS should recognise all of its important parts',
));
})
.next(SceneDescriptionDetected, event => {
expect(event.description).to.equal(new Description(
'A scenario where all the steps pass\nIs reported as passing',
@@ -27,8 +27,6 @@ describe('@serenity-js/cucumber', function() {
then(res => {
expect(res.exitCode).to.equal(0);

expect(res.events).to.have.lengthOf(6);

Pick.from(res.events)
.next(ActivityStarts, event => expect(event.value.name).to.equal(new Name(
'Given a step that receives a doc string:\n' +
@@ -34,8 +34,6 @@ describe('@serenity-js/cucumber', function() {
then(res => {
expect(res.exitCode).to.equal(1);

expect(res.events).to.have.lengthOf(6);

Pick.from(res.events)
.next(SceneStarts, event => expect(event.value.name).to.equal(new Name('A failing scenario')))
.next(TestRunnerDetected, event => expect(event.value).to.equal(new Name('Cucumber')))
@@ -27,8 +27,6 @@ describe('@serenity-js/cucumber', function() {
then(res => {
expect(res.exitCode).to.equal(0);

expect(res.events).to.have.lengthOf(6);

Pick.from(res.events)
.next(SceneStarts, event => expect(event.value.name).to.equal(new Name('A passing scenario')))
.next(TestRunnerDetected, event => expect(event.value).to.equal(new Name('Cucumber')))
@@ -35,8 +35,6 @@ describe('@serenity-js/cucumber', function() {
then(res => {
expect(res.exitCode).to.equal(0);

expect(res.events).to.have.lengthOf(6);

Pick.from(res.events)
.next(SceneStarts, event => expect(event.value.name).to.equal(new Name('A scenario with steps marked as pending')))
.next(TestRunnerDetected, event => expect(event.value).to.equal(new Name('Cucumber')))
@@ -64,8 +62,6 @@ describe('@serenity-js/cucumber', function() {
then(res => {
expect(res.exitCode).to.equal(0);

expect(res.events).to.have.lengthOf(6);

Pick.from(res.events)
.next(SceneStarts, event => expect(event.value.name).to.equal(new Name('A scenario with steps that have not been implemented yet')))
.next(TestRunnerDetected, event => expect(event.value).to.equal(new Name('Cucumber')))
@@ -94,8 +90,6 @@ describe('@serenity-js/cucumber', function() {
then(res => {
expect(res.exitCode).to.equal(0);

expect(res.events).to.have.lengthOf(12);

Pick.from(res.events)
.next(SceneStarts, event => expect(event.value.name).to.equal(new Name('A scenario which tag marks it as pending')))
.next(TestRunnerDetected, event => expect(event.value).to.equal(new Name('Cucumber')))
@@ -0,0 +1,86 @@
import { expect, ifExitCodeIsOtherThan, logOutput } from '@integration/testing-tools';
import { SceneFinished, SceneParametersDetected, SceneSequenceDetected, SceneStarts } from '@serenity-js/core/lib/events';
import { Category, Description, Name } from '@serenity-js/core/lib/model';

import 'mocha';
import { given } from 'mocha-testdata';

import { cucumber, Pick } from '../src';

describe('@serenity-js/cucumber', function() {

this.timeout(5000);

given([
'promise',
'callback',
'synchronous',
]).
it('recognises scenario outlines as sequences of scenes', (stepInterface: string) =>
cucumber(
'--require', 'features/support/configure_serenity.ts',
'--require', `features/step_definitions/${ stepInterface }.steps.ts`,
'--require', 'node_modules/@serenity-js/cucumber/register.js',
'features/scenario_outlines.feature',
).
then(ifExitCodeIsOtherThan(1, logOutput)).
then(res => {
expect(res.exitCode).to.equal(1);

const
expectedScenarioName = new Name('Sample outline'),
expectedScenarioCategory = new Category('Serenity/JS recognises scenario outlines'),
outlineLine = 3,
firstScenarioLine = 12,
secondScenarioLine = 13,
expectedExamplesName = new Name('Example results'),
expectedExamplesDescription = new Description('Description of the examples');

Pick.from(res.events)
.next(SceneSequenceDetected, event => {
expect(event.value.name).to.equal(expectedScenarioName);
expect(event.value.category).to.equal(expectedScenarioCategory);
expect(event.value.location.line).to.equal(outlineLine);
})
.next(SceneParametersDetected, event => {
expect(event.scenario.name).to.equal(expectedScenarioName);
expect(event.scenario.category).to.equal(expectedScenarioCategory);
expect(event.value.name).to.equal(expectedExamplesName);
expect(event.value.description).to.equal(expectedExamplesDescription);
expect(event.value.values).to.deep.equal({ result: 'passes' });
})
.next(SceneStarts, event => {
expect(event.value.name).to.equal(expectedScenarioName);
expect(event.value.category).to.equal(expectedScenarioCategory);
expect(event.value.location.line).to.equal(firstScenarioLine);
})
.next(SceneFinished, event => {
expect(event.value.name).to.equal(expectedScenarioName);
expect(event.value.category).to.equal(expectedScenarioCategory);
expect(event.value.location.line).to.equal(firstScenarioLine);
})
.next(SceneSequenceDetected, event => {
expect(event.value.name).to.equal(expectedScenarioName);
expect(event.value.category).to.equal(expectedScenarioCategory);
expect(event.value.location.line).to.equal(outlineLine);
})
.next(SceneParametersDetected, event => {
expect(event.scenario.name).to.equal(expectedScenarioName);
expect(event.scenario.category).to.equal(expectedScenarioCategory);
expect(event.value.name).to.equal(expectedExamplesName);
expect(event.value.description).to.equal(expectedExamplesDescription);
expect(event.value.values).to.deep.equal({ result: 'fails' });
})
.next(SceneStarts, event => {
expect(event.value.name).to.equal(expectedScenarioName);
expect(event.value.category).to.equal(expectedScenarioCategory);
expect(event.value.location.line).to.equal(secondScenarioLine);
})
.next(SceneFinished, event => {
expect(event.value.name).to.equal(expectedScenarioName);
expect(event.value.category).to.equal(expectedScenarioCategory);
expect(event.value.location.line).to.equal(secondScenarioLine);
})
;
}));
});
@@ -1,6 +1,6 @@
import { expect, ifExitCodeIsOtherThan, logOutput } from '@integration/testing-tools';
import { SceneDescriptionDetected, SceneFinished, SceneTagged } from '@serenity-js/core/lib/events';
import { CapabilityTag, Description, ExecutionSuccessful, FeatureTag, ThemeTag } from '@serenity-js/core/lib/model';
import { SceneTagged } from '@serenity-js/core/lib/events';
import { CapabilityTag, FeatureTag, ThemeTag } from '@serenity-js/core/lib/model';

import 'mocha';
import { given } from 'mocha-testdata';
@@ -33,8 +33,6 @@ describe('@serenity-js/cucumber', function() {
then(res => {
expect(res.exitCode).to.equal(1);

expect(res.events).to.have.lengthOf(6);

Pick.from(res.events)
.next(SceneStarts, event => expect(event.value.name).to.equal(new Name('A timed out scenario')))
.next(TestRunnerDetected, event => expect(event.value).to.equal(new Name('Cucumber')))
@@ -44,6 +44,6 @@
"dependencies": {
"chai": "4.1.2",
"chai-as-promised": "7.1.1",
"tiny-types": "1.10.3"
"tiny-types": "1.11.2"
}
}

Some generated files are not rendered by default. Learn more.

0 comments on commit 616640d

Please sign in to comment.