Skip to content

Commit

Permalink
fix(cucumber): Add support for generators as step definitions
Browse files Browse the repository at this point in the history
closes #9
  • Loading branch information
InvictusMB committed Jan 14, 2017
1 parent 164cf89 commit 30fd652
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 11 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
49 changes: 38 additions & 11 deletions src/serenity-cucumber/webdriver_synchroniser.ts
Original file line number Diff line number Diff line change
@@ -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';

Expand Down Expand Up @@ -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 ]
Expand All @@ -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;
});
}
}

Expand All @@ -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.