Skip to content
Permalink
Browse files
feat(cucumber): @serenity-js/cucumber adapter re-write
affects: @serenity-js/core, @serenity-js/cucumber, @integration/cucumber, @integration/testing-tools

The new version will make it possible to support scenario outlines (#168, #220) and provide
additional context in the report
  • Loading branch information
jan-molak committed Jul 29, 2018
1 parent 5c2508d commit e19c358dbbc6b0a64d0190988f5a289fc1289b38
Showing with 1,756 additions and 425 deletions.
  1. +19 −0 Makefile
  2. +7 −0 integration/cucumber/.gitignore
  3. +11 −0 integration/cucumber/features/passing_scenario.feature
  4. +2 −0 integration/cucumber/features/step_definitions/README.md
  5. +33 −0 integration/cucumber/features/step_definitions/callback.steps.ts
  6. +33 −0 integration/cucumber/features/step_definitions/promise.steps.ts
  7. +31 −0 integration/cucumber/features/step_definitions/synchronous.steps.ts
  8. +6 −0 integration/cucumber/features/support/after_hook.ts
  9. +6 −0 integration/cucumber/features/support/before_hook.ts
  10. +13 −0 integration/cucumber/features/support/configure_serenity.ts
  11. +43 −0 integration/cucumber/package.json
  12. +59 −0 integration/cucumber/spec/recognises_passing_scenario.spec.ts
  13. +19 −0 integration/cucumber/src/cucumber.ts
  14. +1 −0 integration/cucumber/src/index.ts
  15. +13 −0 integration/cucumber/tsconfig.json
  16. +7 −0 integration/testing-tools/.gitignore
  17. +49 −0 integration/testing-tools/package.json
  18. +24 −0 integration/testing-tools/src/child-process-reporter/ChildProcessReporter.ts
  19. +6 −0 integration/testing-tools/src/child-process-reporter/DTO.ts
  20. +2 −0 integration/testing-tools/src/child-process-reporter/index.ts
  21. +8 −0 integration/testing-tools/src/expect/index.ts
  22. +28 −0 integration/testing-tools/src/expect/tiny-types/equals.ts
  23. +5 −0 integration/testing-tools/src/expect/tiny-types/index.ts
  24. +3 −0 integration/testing-tools/src/index.ts
  25. +8 −0 integration/testing-tools/src/spawner/SpawnResult.ts
  26. +23 −0 integration/testing-tools/src/spawner/debug.ts
  27. +3 −0 integration/testing-tools/src/spawner/index.ts
  28. +42 −0 integration/testing-tools/src/spawner/spawner.ts
  29. +20 −0 integration/testing-tools/tsconfig.json
  30. +3 −2 lerna.json
  31. +12 −10 package.json
  32. +3 −4 packages/core/.npmignore
  33. +6 −4 packages/core/package.json
  34. +1 −1 packages/core/spec/Recorder.ts
  35. +2 −2 packages/core/spec/{domain → }/model/Duration.spec.ts
  36. +16 −3 packages/core/spec/{domain → model}/Tag.spec.ts
  37. +2 −3 packages/core/spec/{domain → model}/Timestamp.spec.ts
  38. +61 −0 packages/core/spec/model/outcomes.spec.ts
  39. +0 −9 packages/core/spec/screenplay/activities/ActivityDescriber.spec.ts
  40. +4 −19 packages/core/spec/screenplay/actor/Actor.spec.ts
  41. +1 −1 packages/core/spec/stage/StageManager.spec.ts
  42. +4 −3 packages/core/spec/stage/crew/artifact-archiver/ArtifactArchiver.spec.ts
  43. +1 −1 packages/core/spec/stage/crew/given.ts
  44. +1 −1 packages/core/spec/stage/crew/samples.ts
  45. +12 −16 packages/core/spec/stage/crew/serenity-bdd-reporter/SerenityBDDReporter.spec.ts
  46. +9 −17 packages/core/spec/stage/crew/serenity-bdd-reporter/SerenityBDDReporter/reporting_activities.spec.ts
  47. +3 −15 packages/core/spec/stage/crew/serenity-bdd-reporter/SerenityBDDReporter/tagging_scenarios.spec.ts
  48. +2 −0 packages/core/src/Serenity.ts
  49. +0 −14 packages/core/src/domain/events/ActivityBegins.ts
  50. +0 −16 packages/core/src/domain/events/ActivityFinished.ts
  51. +0 −15 packages/core/src/domain/events/ArtifactGenerated.ts
  52. +0 −16 packages/core/src/domain/events/ExecutionContextPropertyDetected.ts
  53. +0 −14 packages/core/src/domain/events/SceneBegins.ts
  54. +0 −16 packages/core/src/domain/events/SceneFinished.ts
  55. +0 −2 packages/core/src/domain/index.ts
  56. +0 −10 packages/core/src/domain/model/ActivityDetails.ts
  57. +0 −3 packages/core/src/domain/model/Name.ts
  58. +0 −16 packages/core/src/domain/model/ScenarioDetails.ts
  59. +0 −3 packages/core/src/domain/model/TestRunnerType.ts
  60. +0 −78 packages/core/src/domain/model/outcomes.ts
  61. +0 −7 packages/core/src/domain/model/tags/BrowserTag.ts
  62. +0 −7 packages/core/src/domain/model/tags/CapabilityTag.ts
  63. +0 −7 packages/core/src/domain/model/tags/FeatureTag.ts
  64. +0 −7 packages/core/src/domain/model/tags/IssueTag.ts
  65. +0 −7 packages/core/src/domain/model/tags/ManualTag.ts
  66. +0 −11 packages/core/src/domain/model/tags/Tag.ts
  67. +0 −7 packages/core/src/domain/model/tags/ThemeTag.ts
  68. +24 −0 packages/core/src/events/ActivityFinished.ts
  69. +21 −0 packages/core/src/events/ActivityStarts.ts
  70. +22 −0 packages/core/src/events/ArtifactGenerated.ts
  71. +1 −1 packages/core/src/{domain → }/events/AsyncOperationAttempted.ts
  72. +14 −0 packages/core/src/events/AsyncOperationCompleted.ts
  73. 0 packages/core/src/{domain → }/events/DomainEvent.ts
  74. +24 −0 packages/core/src/events/SceneFinished.ts
  75. +21 −0 packages/core/src/events/SceneStarts.ts
  76. +8 −1 packages/core/src/{domain → }/events/SceneTagged.ts
  77. +22 −0 packages/core/src/events/TestRunnerDetected.ts
  78. +3 −3 packages/core/src/{domain → }/events/index.ts
  79. +8 −2 packages/core/src/io/Artifact.ts
  80. +1 −0 packages/core/src/io/FileExtension.ts
  81. +7 −1 packages/core/src/io/FileSystemLocation.ts
  82. +6 −1 packages/core/src/io/FileType.ts
  83. +1 −0 packages/core/src/io/MimeType.ts
  84. +6 −0 packages/core/src/io/Path.ts
  85. +16 −0 packages/core/src/model/ActivityDetails.ts
  86. +1 −0 packages/core/src/{domain → }/model/Category.ts
  87. 0 packages/core/src/{domain → }/model/Duration.ts
  88. +5 −0 packages/core/src/model/Name.ts
  89. 0 packages/core/src/{domain → }/model/Photo.ts
  90. +23 −0 packages/core/src/model/ScenarioDetails.ts
  91. +7 −2 packages/core/src/{domain → }/model/Timestamp.ts
  92. +0 −1 packages/core/src/{domain → }/model/index.ts
  93. +171 −0 packages/core/src/model/outcomes.ts
  94. +20 −0 packages/core/src/model/tags/Tag.ts
  95. 0 packages/core/src/{domain → }/model/tags/Tags.ts
  96. +3 −0 packages/core/src/model/tags/index.ts
  97. +4 −2 packages/core/src/{domain/model/tags → model/tags/types}/ArbitraryTag.ts
  98. +9 −0 packages/core/src/model/tags/types/BrowserTag.ts
  99. +9 −0 packages/core/src/model/tags/types/CapabilityTag.ts
  100. +4 −2 packages/core/src/{domain/model/tags → model/tags/types}/ContextTag.ts
  101. +9 −0 packages/core/src/model/tags/types/FeatureTag.ts
  102. +9 −0 packages/core/src/model/tags/types/IssueTag.ts
  103. +9 −0 packages/core/src/model/tags/types/ManualTag.ts
  104. +9 −0 packages/core/src/model/tags/types/ThemeTag.ts
  105. +0 −2 packages/core/src/{domain/model/tags → model/tags/types}/index.ts
  106. +1 −1 packages/core/src/screenplay/activities/ActivityDescriber.ts
  107. +1 −1 packages/core/src/screenplay/activities/OutcomeMatcher.ts
  108. +3 −2 packages/core/src/screenplay/activities/TrackedActivity.ts
  109. +1 −1 packages/core/src/stage/Clock.ts
  110. +1 −1 packages/core/src/stage/StageCrewMember.ts
  111. +3 −3 packages/core/src/stage/StageManager.ts
  112. +2 −1 packages/core/src/stage/crew/artifact-archiver/ArtifactArchiver.ts
  113. +16 −0 packages/core/src/stage/crew/debug-reporter/DebugReporter.ts
  114. +1 −0 packages/core/src/stage/crew/debug-reporter/index.ts
  115. +1 −0 packages/core/src/stage/crew/index.ts
  116. +4 −6 packages/core/src/stage/crew/serenity-bdd-reporter/ScenarioReport.ts
  117. +1 −1 packages/core/src/stage/crew/serenity-bdd-reporter/ScenarioReports.ts
  118. +16 −21 packages/core/src/stage/crew/serenity-bdd-reporter/SerenityBDDReporter.ts
  119. +9 −0 packages/cucumber/.gitignore
  120. +15 −0 packages/cucumber/.npmignore
  121. +201 −0 packages/cucumber/LICENSE.md
  122. +1 −0 packages/cucumber/NOTICE.md
  123. +6 −0 packages/cucumber/README.md
  124. +75 −0 packages/cucumber/package.json
  125. +3 −0 packages/cucumber/register.js
  126. +1 −0 packages/cucumber/src/index.ts
  127. +133 −0 packages/cucumber/src/listener/Notifier.ts
  128. +1 −0 packages/cucumber/src/listener/index.ts
  129. +65 −0 packages/cucumber/src/listener/listener.ts
  130. +6 −0 packages/cucumber/src/register.ts
  131. +21 −0 packages/cucumber/tsconfig.json
  132. +4 −2 tslint.json
@@ -0,0 +1,19 @@
default: clean compile

clean:
npm run clean

lint:
npm run lint

test:
npm test

compile:
npm run compile

integration-test:
npm run integration-test

verify:
npm run verify
@@ -0,0 +1,7 @@
# Node
node_modules
*.log

# Build artifacts
.nyc_output
lib
@@ -0,0 +1,11 @@
Feature: Serenity/JS recognises a passing scenario

In order to correctly report the outcome of a scenario
Serenity/JS should recognise when a scenario passes

Scenario: A passing scenario

A scenario where all the steps pass

Given a step that passes

@@ -0,0 +1,2 @@
# Notes
- [Cucumber.js step definitions](https://github.com/cucumber/cucumber-js/blob/509e64fd5d9835c9101b33a642e6ccac459025b8/docs/support_files/step_definitions.md)
@@ -0,0 +1,33 @@
import { TableDefinition } from 'cucumber';

type Callback = (error?: Error, pending?: string) => void;

export = function() {
this.Given(/^.*step (?:.*) passes$/, function(done: Callback) {
done();
});

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

this.Given(/^.*step (?:.*) explicitly pending$/, function(done: Callback) {
done(void 0, 'pending');
});

this.Given(/^.*step (?:.*) explicitly skipped/, function(done: Callback) {
done(void 0, 'skipped');
});

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

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

this.Given(/^.*step times out$/, { timeout: 100 }, function(done: Callback) {
setTimeout(done, 1000);
});
};
@@ -0,0 +1,33 @@
import { TableDefinition } from 'cucumber';

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

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

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

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

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

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

this.Given(/^.*step times out$/, { timeout: 100 }, function() {
return new Promise((resolve, reject) => {
setTimeout(resolve, 1000);
});
});
};
@@ -0,0 +1,31 @@
import { TableDefinition } from 'cucumber';

export = function() {
this.Given(/^.*step (?:.*) passes$/, function() {
return void 0;
});

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

this.Given(/^.*step (?:.*) explicitly pending$/, function() {
return 'pending';
});

this.Given(/^.*step (?:.*) explicitly skipped/, function() {
return 'skipped';
});

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

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

this.Given(/^.*step times out$/, { timeout: 100 }, function(done: (error: Error, pending: string) => void) {
setTimeout(done, 1000);
});
};
@@ -0,0 +1,6 @@
module.exports = function() {

this.After(() => {
// no-op
});
};
@@ -0,0 +1,6 @@
module.exports = function() {

this.Before(() => {
// no-op
});
};
@@ -0,0 +1,13 @@
import { ChildProcessReporter } from '@integration/testing-tools';
import { serenity } from '@serenity-js/core';
import { DebugReporter } from '@serenity-js/core/lib/stage';

module.exports = function() {

this.setDefaultTimeout(5000);

serenity.stageManager.register(
new ChildProcessReporter(),
new DebugReporter(),
);
};
@@ -0,0 +1,43 @@
{
"name": "@integration/cucumber",
"version": "0.0.0",
"description": "Cucumber JS integration tests",
"author": {
"name" : "Jan Molak",
"email" : "jan.molak@smartcodeltd.co.uk",
"url" : "https://janmolak.com"
},
"homepage": "http://serenity-js.org",
"license": "Apache-2.0",
"private": true,
"config": {
"access": "private"
},
"main": "lib/index.js",
"typings": "lib/index.d.ts",
"scripts": {
"clean": "rimraf lib",
"lint": "tslint --project tsconfig.json --config ../../tslint.json --format stylish",
"test": "mocha --opts ../../mocha.opts 'spec/**/*.spec.*'"
},
"repository": {
"type": "git",
"url": "https://github.com/jan-molak/serenity-js.git"
},
"bugs": {
"url": "https://github.com/jan-molak/serenity-js/issues"
},
"engines": {
"node": ">= 6.9.x",
"npm": ">= 3"
},
"devDependencies": {
"@integration/testing-tools": "*",
"@serenity-js/core": "*",
"@serenity-js/cucumber": "*",
"@types/cucumber": "1.3.3",
"cucumber": "1.3.3",
"mocha": "5.2.0",
"mocha-testdata": "1.2.0"
}
}
@@ -0,0 +1,59 @@
import { expect, ifExitCodeIsOtherThan, logOutput } from '@integration/testing-tools';
import { ActivityFinished, ActivityStarts, SceneFinished, SceneStarts, SceneTagged, TestRunnerDetected } from '@serenity-js/core/lib/events';
import { ExecutionSuccessful, FeatureTag, Name } from '@serenity-js/core/lib/model';

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

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

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

this.timeout(5000);

given([
'synchronous',
'promise',
'callback',
]).
it('recognises a passing scenario', (stepInterface: string) =>
cucumber(
'--require', 'features/support/configure_serenity.ts',
// '--require', 'features/support/before_hook.ts',
// '--require', 'features/support/after_hook.ts',
'--require', `features/step_definitions/${ stepInterface }.steps.ts`,
'--require', 'node_modules/@serenity-js/cucumber/register.js',
'features/passing_scenario.feature',
).
then(ifExitCodeIsOtherThan(0, logOutput)).
then(res => {
expect(res.exitCode).to.equal(0);

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

expect(res.events[0]).to.be.instanceOf(SceneStarts)
.and.have.property('value')
.that.has.property('name')
.equal(new Name('A passing scenario'));

expect(res.events[1]).to.be.instanceOf(TestRunnerDetected)
.and.have.property('value').equal(new Name('Cucumber'));

expect(res.events[2]).to.be.instanceOf(SceneTagged)
.and.have.property('tag')
.equal(new FeatureTag('Serenity/JS recognises a passing scenario'));

expect(res.events[3]).to.be.instanceOf(ActivityStarts)
.and.have.property('value')
.that.has.property('name')
.equal(new Name('Given a step that passes'));

expect(res.events[4]).to.be.instanceOf(ActivityFinished)
.and.have.property('outcome')
.equal(new ExecutionSuccessful());

expect(res.events[5]).to.be.instanceOf(SceneFinished)
.and.have.property('outcome')
.equal(new ExecutionSuccessful());
}));
});
@@ -0,0 +1,19 @@
import { spawner, SpawnResult } from '@integration/testing-tools';
import * as path from 'path';

const cucumberExecutable = path.resolve(
require.resolve('cucumber/package.json'),
'..',
'bin',
'cucumber.js',
);

const cucumberSpawner = spawner(
cucumberExecutable,
{ cwd: path.resolve(__dirname, '..') },
);

export const cucumber = (...params: string[]): Promise<SpawnResult> => cucumberSpawner(
'--compiler', 'ts:ts-node/register',
...params,
);
@@ -0,0 +1 @@
export * from './cucumber';
@@ -0,0 +1,13 @@
{
"compilerOptions": {
"target": "es5",
"lib": [ "es5", "es6" ],
"module": "commonjs",
"sourceMap": true,
"declaration": true
},

"exclude": [
"node_modules"
]
}
@@ -0,0 +1,7 @@
# Node
node_modules
*.log

# Build artifacts
.nyc_output
lib
@@ -0,0 +1,49 @@
{
"name": "@integration/testing-tools",
"version": "0.0.0",
"description": "Internal module to help test integration of Serenity/JS with test runners and frameworks",
"author": {
"name" : "Jan Molak",
"email" : "jan.molak@smartcodeltd.co.uk",
"url" : "https://janmolak.com"
},
"homepage": "http://serenity-js.org",
"license": "Apache-2.0",
"private": true,
"config": {
"access": "private"
},
"main": "lib/index.js",
"typings": "lib/index.d.ts",
"keywords": [
"serenity-js",
"internal"
],
"scripts": {
"clean": "rimraf lib",
"lint": "tslint --project tsconfig.json --config ../../tslint.json --format stylish",
"compile": "tsc --project tsconfig.json"
},
"repository": {
"type": "git",
"url": "https://github.com/jan-molak/serenity-js.git"
},
"bugs": {
"url": "https://github.com/jan-molak/serenity-js/issues"
},
"engines": {
"node": ">= 6.9.x",
"npm": ">= 3"
},
"peerDependencies": {
"@serenity-js/core": "*"
},
"devDependencies": {
"@serenity-js/core": "*"
},
"dependencies": {
"chai": "4.1.2",
"chai-as-promised": "7.1.1",
"tiny-types": "1.10.3"
}
}
@@ -0,0 +1,24 @@
import { DomainEvent } from '@serenity-js/core/lib/events';
import { StageCrewMember, StageManager } from '@serenity-js/core/lib/stage';
import { Serialised } from 'tiny-types';
import { DTO } from './DTO';

export class ChildProcessReporter implements StageCrewMember {
private stageManager: StageManager;

assignTo(stageManager: StageManager) {
this.stageManager = stageManager;
this.stageManager.register(this);
}

notifyOf(event: DomainEvent): void {
process.send(this.serialised(event));
}

private serialised(event: DomainEvent): DTO<DomainEvent> {
return ({
type: event.constructor.name,
value: event.toJSON() as Serialised<DomainEvent>,
});
}
}

0 comments on commit e19c358

Please sign in to comment.