Permalink
Browse files

feat(serenity-cucumber): Support for generator steps

Closes #13 and closes #15
  • Loading branch information...
jan-molak committed Jan 20, 2017
2 parents 9548dab + 77c5ce5 commit f3fc2766bf2ba7db7f0d3ad779b639386c6d6060
View
@@ -0,0 +1,2 @@
--compilers ts:ts-node/register
--reporter spec
View
@@ -19,10 +19,10 @@
"commit": "git-cz",
"webdriver-manager": "webdriver-manager",
"webdriver:update": "npm run webdriver-manager update",
"spec:api": "nyc --report-dir=./staging/reports/coverage/api mocha --opts ./spec/mocha.opts 'spec/api/**/*.spec.*'",
"spec:api": "nyc --report-dir=./staging/reports/coverage/api mocha --opts ./mocha.opts 'spec/api/**/*.spec.*'",
"spec:cookbook": "nyc --report-dir=./staging/reports/coverage/cookbook protractor ./spec/protractor-cookbook.conf.js --params.browsers=chrome",
"spec:cookbook:pull-request": "nyc --report-dir=./staging/reports/coverage/cookbook protractor ./spec/protractor-cookbook.conf.js --params.browsers=phantomjs",
"spec:integration": "nyc --report-dir=./staging/reports/coverage/integration mocha --opts ./spec/mocha.opts 'spec/integration/*/*.ts'",
"spec:integration": "nyc --report-dir=./staging/reports/coverage/integration mocha --opts ./mocha.opts 'spec/integration/*/*.ts'",
"spec": "npm run spec:api && npm run spec:cookbook && npm run spec:integration",
"spec:pull-request": "npm run spec:api && npm run spec:cookbook:pull-request && npm run spec:integration",
"coverage:merge": "istanbul-merge --out=./staging/reports/coverage/coverage-final.json ./staging/reports/coverage/**/coverage-final.json",
@@ -44,6 +44,7 @@
"@types/node": "6.0.53",
"@types/selenium-webdriver": "2.53.39",
"@types/stack-trace": "0.0.28",
"co": "4.6.0",
"graceful-fs": "4.1.11",
"is-generator": "1.0.3",
"lodash": "4.17.2",
@@ -66,7 +67,7 @@
"@types/sinon-chai": "2.7.27",
"chai": "3.5.0",
"chai-as-promised": "6.0.0",
"chai-smoothie": "^0.1.2",
"chai-smoothie": "^0.2.0",
"commitizen": "2.9.2",
"coveralls": "2.11.15",
"cucumber": "1.3.1",
@@ -1,4 +1,13 @@
Feature: Recognises a failing scenario
Scenario: A failing scenario
Given a step that fails
Scenario: A failing scenario with a synchronous interface
Given a step that fails with a synchronous interface
Scenario: A failing scenario with a callback interface
Given a step that fails with a callback interface
Scenario: A failing scenario with a promise interface
Given a step that fails with a promise interface
Scenario: A failing scenario with a generator interface
Given a step that fails with a generator interface
@@ -1,4 +1,13 @@
Feature: Recognises a passing scenario
Scenario: A passing scenario
Given a step that passes
Scenario: A passing scenario with a synchronous interface
Given a step that passes with a synchronous interface
Scenario: A passing scenario with a callback interface
Given a step that passes with a callback interface
Scenario: A passing scenario with a promise interface
Given a step that passes with a promise interface
Scenario: A passing scenario with a generator interface
Given a step that passes with a generator interface

This file was deleted.

Oops, something went wrong.
@@ -0,0 +1,13 @@
Feature: Recognises an explicitly pending scenarios
Scenario: An explicitly pending synchronous scenario
Given a pending step with a synchronous interface
Scenario: An explicitly pending callback scenario
Given a pending step with a callback interface
Scenario: An explicitly pending promise scenario
Given a pending step with a promise interface
Scenario: An explicitly pending generator scenario
Given a pending step with a generator interface
@@ -0,0 +1,4 @@
Feature: Recognises an implicitly pending scenario
Scenario: An implicitly pending scenario
Given a scenario with no defined steps
@@ -1,10 +1,109 @@
enum StepInterface {
SYNCHRONOUS,
CALLBACK,
PROMISE,
GENERATOR,
}
enum StepResult {
SUCCESS,
FAILURE,
PENDING,
}
function createFailingStep(stepInterface: StepInterface) {
switch (stepInterface) {
case StepInterface.CALLBACK:
return cb => {
process.nextTick(cb.bind(null, new Error('Assertion failed')));
};
case StepInterface.PROMISE:
return () => {
return new Promise((resolve, reject) => {
process.nextTick(() => {
reject(new Error('Assertion failed'));
});
});
};
case StepInterface.GENERATOR:
return function*() {
yield new Promise(process.nextTick);
throw new Error('Assertion failed');
};
case StepInterface.SYNCHRONOUS:
default:
return () => {
throw new Error('Assertion failed');
};
}
}
function createPassingStep(stepInterface: StepInterface, result: StepResult) {
const resultValue = StepResult[result].toLowerCase();
switch (stepInterface) {
case StepInterface.CALLBACK:
return cb => {
process.nextTick(cb.bind(null, null, resultValue));
};
case StepInterface.PROMISE:
return () => {
return new Promise(resolve => process.nextTick(() => resolve(resultValue)));
};
case StepInterface.GENERATOR:
return function*() {
yield new Promise(process.nextTick);
return resultValue;
};
case StepInterface.SYNCHRONOUS:
default:
return () => {
return resultValue;
};
}
}
function createStep(stepInterface: StepInterface, result: StepResult) {
if (result === StepResult.FAILURE) {
return createFailingStep(stepInterface);
}
return createPassingStep(stepInterface, result);
}
export = function () {
this.Given(/^a step that passes$/, () => {
// pass
});
this.Given(/^a step that passes with a synchronous interface$/,
createStep(StepInterface.SYNCHRONOUS, StepResult.SUCCESS));
this.Given(/^a step that passes with a callback interface$/,
createStep(StepInterface.CALLBACK, StepResult.SUCCESS));
this.Given(/^a step that passes with a promise interface$/,
createStep(StepInterface.PROMISE, StepResult.SUCCESS));
this.Given(/^a step that passes with a generator interface$/,
createStep(StepInterface.GENERATOR, StepResult.SUCCESS));
this.Given(/^a step that fails with a synchronous interface$/,
createStep(StepInterface.SYNCHRONOUS, StepResult.FAILURE));
this.Given(/^a step that fails with a callback interface$/,
createStep(StepInterface.CALLBACK, StepResult.FAILURE));
this.Given(/^a step that fails with a promise interface$/,
createStep(StepInterface.PROMISE, StepResult.FAILURE));
this.Given(/^a step that fails with a generator interface$/,
createStep(StepInterface.GENERATOR, StepResult.FAILURE));
this.Given(/^a pending step with a synchronous interface$/,
createStep(StepInterface.SYNCHRONOUS, StepResult.PENDING));
this.Given(/^a pending step with a callback interface$/,
createStep(StepInterface.CALLBACK, StepResult.PENDING));
this.Given(/^a pending step with a promise interface$/,
createStep(StepInterface.PROMISE, StepResult.PENDING));
this.Given(/^a step that fails$/, () => {
throw new Error('Assertion failed');
});
this.Given(/^a pending step with a generator interface$/,
createStep(StepInterface.GENERATOR, StepResult.PENDING));
};
@@ -1,53 +1,58 @@
import expect = require('../../expect');
import * as _ from 'lodash';
import { Result, SceneFinished } from '../../../src/serenity/domain';
import { spawner } from '../../support/spawner';
describe('When working with Cucumber', function () {
this.timeout(30 * 1000); // it might take a while to start up the selenium server
const protractor = spawner(
const protractorSpawner = spawner(
process.cwd() + '/node_modules/.bin/protractor',
{ cwd: __dirname, silent: true },
);
const protractor = (specs: string) => protractorSpawner('protractor.conf.js',
'--specs', specs,
'--cucumberOpts.tags', '~@wip',
'--disableChecks', // needed until https://github.com/angular/protractor/issues/3978 is fixed
);
describe('Serenity/JS', () => {
const stepApiTypes = 4;
const messagesPerStep = 4;
const messagesPerFeature = stepApiTypes * messagesPerStep;
it ('reports passing scenarios', () => {
let spawned = protractor('protractor.conf.js',
'--specs', '**/*passing_scenario.feature',
);
let spawned = protractor('**/*passing_scenario.feature');
return expect(spawned.result).to.be.eventually.fulfilled.then(() => {
expect(spawned.messages).to.have.lengthOf(4);
expect(spawned.messages).to.have.lengthOf(messagesPerFeature);
let lastMessage = spawned.messages.pop();
let lastMessages = _.chunk(spawned.messages, messagesPerStep).map(chunk => chunk.pop());
expect(lastMessage).to.be.instanceOf(SceneFinished);
expect(Result[lastMessage.value.result]).to.equal(Result[Result.SUCCESS]);
lastMessages.forEach(lastMessage => {
expect(lastMessage).to.be.instanceOf(SceneFinished);
expect(Result[lastMessage.value.result]).to.equal(Result[Result.SUCCESS]);
});
});
});
it ('ignores skipped scenarios', () => {
let spawned = protractor('protractor.conf.js',
'--specs', '**/*skipped_scenario.feature',
'--cucumberOpts.tags', '~@wip',
'--disableChecks', // until https://github.com/angular/protractor/issues/3978 is fixed
);
let spawned = protractor('**/*skipped_scenario.feature');
return expect(spawned.result).to.be.eventually.fulfilled.then(() => {
expect(spawned.messages).to.have.lengthOf(0);
});
});
it ('reports pending scenarios', () => {
let spawned = protractor('protractor.conf.js',
'--specs', '**/*pending_scenario.feature',
);
it ('reports implicitly pending scenarios', () => {
let spawned = protractor('**/*implicitly_pending_scenario.feature');
return expect(spawned.result).to.be.eventually.fulfilled.then(() => {
expect(spawned.messages).to.have.lengthOf(4);
expect(spawned.messages).to.have.lengthOf(messagesPerStep);
let lastMessage = spawned.messages.pop();
@@ -56,20 +61,33 @@ describe('When working with Cucumber', function () {
});
});
it('reports failing scenarios', () => {
it ('reports explicitly pending scenarios', () => {
let spawned = protractor('**/*explicitly_pending_scenario.feature');
return expect(spawned.result).to.be.eventually.fulfilled.then(() => {
expect(spawned.messages).to.have.lengthOf(messagesPerFeature);
let spawned = protractor('protractor.conf.js',
'--specs', '**/*failing_scenario.feature',
'--disableChecks', // until https://github.com/angular/protractor/issues/3978 is fixed
);
let lastMessages = _.chunk(spawned.messages, messagesPerStep).map(chunk => chunk.pop());
lastMessages.forEach(lastMessage => {
expect(lastMessage).to.be.instanceOf(SceneFinished);
expect(Result[lastMessage.value.result]).to.equal(Result[Result.PENDING]);
});
});
});
it('reports failing scenarios', () => {
let spawned = protractor('**/*failing_scenario.feature');
return expect(spawned.result).to.be.eventually.rejected.then(() => {
expect(spawned.messages).to.have.lengthOf(4);
expect(spawned.messages).to.have.lengthOf(messagesPerFeature);
let lastMessage = spawned.messages.pop();
let lastMessages = _.chunk(spawned.messages, messagesPerStep).map(chunk => chunk.pop());
expect(lastMessage).to.be.instanceOf(SceneFinished);
expect(Result[lastMessage.value.result]).to.equal(Result[Result.FAILURE]);
lastMessages.forEach(lastMessage => {
expect(lastMessage).to.be.instanceOf(SceneFinished);
expect(Result[lastMessage.value.result]).to.equal(Result[Result.FAILURE]);
});
});
});
});
View

This file was deleted.

Oops, something went wrong.
Oops, something went wrong.

0 comments on commit f3fc276

Please sign in to comment.