Cucumber-js with sync, callable steps and parsed arguments.
Quick features:
- Sync step definitions, no more callbacks;
- Call other step from step definitions;
- Parse values such as arrays, objects and decimals;
You should first understand official cucumber before using this library, because it might be confusing at first.
npm install cucumberry --save-dev
Check the full API here.
// Feature:
Feature: Test
Scenario: Test
Given a
Given b 2
// Step defintions:
this.addStep('a', /^a$/, function(){
console.log('a');
this.callStep('b', 1);
})
this.addStep('b', /^b (\d+)$/, function(n){
console.log('b', n);
})
// Output:
// a
// b 1
// b 2
Using fibers your steps now can be sync and also non-blocking. Is suggested to use together with the selenium-sync library.
// Step defintions:
this.addStep('a', /^a$/, function(){
// Using the selenium-sync library
var browser = this.getBrowser();
var facebookW = this.getBrowser().getWindowByTitle('Facebook');
facebookW.focus();
facebookW.waitToLoad();
facebookW.waitForElement('#email');
var test = this.getUserDataByName('test');
facebookW.findEl('#email').sendKeys(test.email);
facebookW.findEl('#pass').sendKeys(test.password);
facebookW.findEl('#loginbutton').click();
browser.waitWindowToBeClosed(facebookW);
// No need to call any callback because when reaches this line
// the above code has already been done. Is sync, remember?
})
Everything is considered to be a pure JavaScript value.
// Feature:
Feature: Test
Scenario: Test
Given A simple number 2
Given A complex object {a: 'abc', b: 3}
Given An array [1, 2, 3]
// Step defintions:
this.addStep('simpleNumber', /^A simple number (\d+)$/, function(n){
console.log('simpleNumber', n * 2); // logs: simpleNumber 4
})
this.addStep('complexObject', /^A complex object (.*)$/, function(obj){
console.log('complexObject', obj.a); // logs: complexObject abc
console.log('complexObject', obj.b * 2); // logs: complexObject 6
})
this.addStep('anArray', /^An array (.*)$/, function(array){
console.log('anArray', array); // logs: anArray [ 1, 2, 3 ]
})
To use this library you have to make several steps.
Will install the dependencies for your example:
npm install cucumber cucumberry lodash --save-dev
This will make the world instance to know about the test context. This is required to make the core.plugins.addStepMethod plugin to work. This enables you to call other steps from step definitions.
// In your "support/hooks.js" file:
var hooks = function(){
// You have to save the context here.
var testContext = this;
this.Before(function(cb){
// And then send it to the world instance
// with the `setTestContext` method.
this.setTestContext(testContext);
cb();
});
};
All patches have place in the *.steps.js
(step definition) files. An example can be shown here:
// in your `all.steps.js`
module.exports = function(){
var cucumberryPlugins = require('cucumberry').plugins;
}
If you want to use all plugins here is the code:
// in your `all.steps.js`
module.exports = function(){
var cucumberryPlugins = require('cucumberry').plugins;
cucumberryPlugins.useAll(this);
}
Or you can use individually in this exact order only:
// in your `all.steps.js`
module.exports = function(){
var cucumberryPlugins = require('cucumberry').plugins;
cucumberryPlugins.parseArguments(this);
cucumberryPlugins.makeFiber(this);
cucumberryPlugins.addStepMethod(this);
}
Your tests feature will send JavaScript objects to the steps definition. For example an 1.1
argument will be automatically converted into a decimal number, but "1.1"
will be a string.
// in your `all.steps.js`
module.exports = function(){
var cucumberryPlugins = require('cucumberry').plugins;
cucumberryPlugins.parseArguments(this);
}
Make all the Given
methods sync with fibers. After you do this the steps will
run sync and no need to call the callback
function when the step ends.
// in your `all.steps.js`
module.exports = function(){
var cucumberryPlugins = require('cucumberry').plugins;
cucumberryPlugins.makeFiber(this);
}
In order to have the new addStep
method in the step test context you need
to do this. This is required to call the step from another step.
// in your `all.steps.js`
module.exports = function(){
var cucumberryPlugins = require('cucumberry').plugins;
cucumberryPlugins.addStepMethod(this);
}
This will add a method to your world instance named callStep
which enables you
to call a step.
// In your `support/World.js` file:
// Load cucumber and lodash.
var cucumberry = require('cucumberry');
var cucumberryWorld = cucumberry.World;
var _ = require('lodash');
// Create your own constructor.
var World = function(cb){
// Call the cucumberryWorld constructor. Is required!
cucumberryWorld.apply(this, arguments);
}
// Inherit the static methods.
_.extend(World, cucumberryWorld);
// Add here your own static World methods.
World.myStaticMethod = function(){
// Do something!
}
// Inherit the instance methods.
_.extend(World.prototype, cucumberryWorld.prototype, {
// Add here your own static World methods.
myInstanceMethod: function(){
// Do something!
}
})
module.exports = {
World : World,
}
Your step definition should look like this:
// in your `all.steps.js`
module.exports = function(){
var cucumberryPlugins = require('cucumberry').plugins;
cucumberryPlugins.useAll(this);
this.addStep('a', /^a$/, function(){
console.log('a');
})
}
The API is like this:
this.addStep(uniqueStepName, regexMatch, function);
Notice that the function will not get any callback
argument at the end as it's an sync
function.
Let's add another step b
and change step a
to call step b
(notice the this.callStep('b', 1);
part):
// in your `all.steps.js`
module.exports = function(){
var cucumberryPlugins = require('cucumberry').plugins;
cucumberryPlugins.useAll(this);
this.addStep('a', /^a$/, function(){
console.log('a');
this.callStep('b', 1);
})
this.addStep('b', /^b (\d+)$/, function(n){
console.log('b', n);
})
}
The callStep
API looks like this:
this.callStep(uniqueStepName, arguments);
Create a file named test.feature
Feature: Test
Scenario: Test
Given a
Given b 2
Run it with your cucumber-js
command and the output will be:
a // From a called from the test.feature directly
b 1 // From b but called by step a
b 2 // From b called from the test.feature directly
- Read the official cucumber docs.