Permalink
Browse files

fix(cucumber): Add support for generators as step definitions

closes #9
  • Loading branch information...
InvictusMB committed Jan 14, 2017
1 parent 164cf89 commit 30fd6528df1e5928e0d41ed4e7615c3085adf847
Showing with 39 additions and 11 deletions.
  1. +1 −0 package.json
  2. +38 −11 src/serenity-cucumber/webdriver_synchroniser.ts
View
@@ -41,6 +41,7 @@
"@types/node": "6.0.53",
"@types/selenium-webdriver": "2.53.37",
"@types/stack-trace": "0.0.28",
"co": "4.6.0",
"graceful-fs": "4.1.11",
"is-generator": "1.0.3",
"lodash": "4.17.2",
@@ -1,5 +1,6 @@
import { Deferred } from '../serenity/recording/async';
import withArityOf = require('util-arity');
import co from 'co';
import { StepDefinitions } from 'cucumber';
import * as webdriver from 'selenium-webdriver';
@@ -36,7 +37,7 @@ export function synchronise (cucumber: StepDefinitions, controlFlow: webdriver.p
function synchronisingStepGenerator (pattern: string, options: any, code?: SomeFunction) {
let originalStep = code || options,
synchronised = mimic(originalStep, synchronisedStep(originalStep));
synchronised = mimicArity(originalStep, synchronisedStep(originalStep));
let params = !! code
? [ pattern, options, synchronised ]
@@ -45,33 +46,32 @@ export function synchronise (cucumber: StepDefinitions, controlFlow: webdriver.p
return originalStepGenerator.apply(cucumber, params);
}
return mimic (originalStepGenerator, synchronisingStepGenerator);
return mimicArity (originalStepGenerator, synchronisingStepGenerator);
}
/**
* Provides a synchronised wrapper around the user-defined step
*
* @param originalStep
* @return {(args:...[any])=>Promise<void>}
* @return {(...args:any[])=>(Promise<void> | void)}
*/
function synchronisedStep (originalStep: SomeFunction) {
return function (...args: any[]) {
return mimicInterface(originalStep, function stepWrapper (...args: any[]) {
let deferred = new Deferred<void>(),
context = this;
if (isGeneratorFn(originalStep)) {
throw 'Synchronizing e6 generator style steps is not supported by serenity-js yet';
originalStep = co.wrap(originalStep);
}
controlFlow
.execute(() => originalStep.apply(context, args) )
.then(deferred.resolve, deferred.reject);
if (!hasCallbackInterface(originalStep, args)) {
return deferred.promise;
}
};
return deferred.promise;
});
}
}
@@ -87,21 +87,48 @@ function hasCallbackInterface(step: SomeFunction, params: any[]): boolean {
return step.length === params.length;
}
/**
* Makes the pretender function conform to original function interface (i.e. callback- or promise-based)
*
* @param original
* @param pretender
* @return {StepDefinition}
*/
function mimicInterface (original: StepDefinition, pretender: SomeFunctionReturningPromise): StepDefinition {
return function stepWrapper (...args: any[]): Promise<void> | void {
let context = this,
result = pretender.apply(context, args);
if (!hasCallbackInterface(original, args)) {
return result;
}
};
}
/**
* Makes the pretender function of the same arity as the original one to deceive cucumber.
*
* @param original
* @param pretender
* @return {(args:...[any])=>any}
* @return {(...args:any[])=>any}
*/
function mimic (original: SomeFunction, pretender: SomeFunction): SomeFunction {
function mimicArity (original: SomeFunction, pretender: SomeFunction): SomeFunction {
return withArityOf(original.length, pretender);
}
interface SomeFunction {
(...args: any[]): any;
}
interface SomeFunctionReturningPromise {
(...args: any[]): Promise<void>;
}
interface StepDefinition {
(...args: any[]): Promise<void> | void;
}
interface StepGenerator {
(pattern: string, options: any, code?: any): void;
}

0 comments on commit 30fd652

Please sign in to comment.