Skip to content

Instantly share code, notes, and snippets.

@MatthewBarker
Created February 26, 2015 15:38
Show Gist options
  • Save MatthewBarker/770114dd534aae3642d4 to your computer and use it in GitHub Desktop.
Save MatthewBarker/770114dd534aae3642d4 to your computer and use it in GitHub Desktop.
Provides simplified Gherkin style syntax for Mocha tests by extending the functions in the standard 'BDD' interface.
/*global define, Mocha*/
/**
Provides simplified Gherkin style syntax for Mocha tests by extending the functions in the standard 'BDD' interface.
** See {@link http://mochajs.org/#bdd-interface} for Mocha documentation.
** Feature and Scenario act as a wrapper for 'describe'
** Given, When, Then, But & And act as a wrapper for 'it'
** Feature, Scenario, Given, When, Then, But & And functions are capitalised as otherwise some of them would conflict with CoffeeScript keywords.
@module bdd-feature
@author Matt Barker
@requires mocha
@example <caption>Writing tests</caption>
Feature('Barbarian life goals', function () {
Scenario('What is best in life?', function () {
var barbarian,
enemies;
Given('Conan the barbarian', function () {
barbarian = new Barbarian('Conan');
});
And('many enemies', function () {
enemies = new Army(100);
});
When('crush your enemies', function () {
barbarian.crush(enemies);
});
Then('see them driven before you', function () {
enemies.haveRunAway.should.be.true;
});
And('hear the lamentations of their women', function () {
enemies.women.areLamenting.should.be.true;
});
});
});
@example <caption>Running tests</caption>
var should = chai.should();
mocha.setup('bdd-feature');
require(['spec/my-test'], function () {
mocha.run();
});
*/
define(['mocha'], function (mocha) {
'use strict';
var Suite = Mocha.Suite,
Test = Mocha.Test;
// Existing functionality from the 'bdd' interface
function escapeRe(text) {
var matchOperators = /[|\\{}()[\]^$+*?.]/g;
if (typeof text !== 'string') {
throw new TypeError('Expected a string');
}
return text.replace(matchOperators, '\\$&');
}
function bddFeature(suite) {
var suites = [suite];
suite.on('pre-require', function (context, file, mocha) {
// New functionality for 'bdd-feature'
/**
Create a step function.
@param {string} prefix - The text that the step should start with
@param {string} title - Descriptive text
@param {function} base - Base function from the 'bdd' interface
@param {function} callback - Callback function
@return {object} The suite if base=describe, the test if base=it
@private
@memberof module:bdd-feature
*/
function createStep(prefix, title, base, callback) {
if (title.toLowerCase().indexOf(prefix.toLowerCase()) !== 0) {
title = prefix + title;
}
return base.apply(this, [title, callback]);
}
/**
Describe a feature.
The title will be prefixed with 'Feature: ' if it doesn't already start with it.
@param {string} title - Descriptive text
@param {function} callback - Callback function
@memberof module:bdd-feature
*/
context.Feature = function (title, callback) {
return createStep('Feature: ', title, context.describe, callback);
};
/**
By appending .only() you may tell Mocha to run only the specified feature.
@param {string} title - Descriptive text
@param {function} callback - Callback function
@see #context.Feature
@memberof module:bdd-feature
*/
context.Feature.only = function (title, callback) {
return createStep('Feature: ', title, context.describe.only, callback);
};
/**
By appending .skip() you may tell Mocha to ignore the feature.
@param {string} title - Descriptive text
@param {function} callback - Callback function
@see #context.Feature
@memberof module:bdd-feature
*/
context.Feature.skip = function (title, callback) {
return createStep('Feature: ', title, context.describe.skip, callback);
};
/**
Describe a scenario.
The title will be prefixed with 'Scenario: ' if it doesn't already start with it.
@param {string} title - Descriptive text
@param {function} callback - Callback function
@memberof module:bdd-feature
*/
context.Scenario = function (title, callback) {
return createStep('Scenario: ', title, context.describe, callback);
};
/**
By appending .only() you may tell Mocha to run only the specified scenario.
@param {string} title - Descriptive text
@param {function} callback - Callback function
@see #context.Scenario
@memberof module:bdd-feature
*/
context.Scenario.only = function (title, callback) {
return createStep('Scenario: ', title, context.describe.only, callback);
};
/**
By appending .skip() you may tell Mocha to ignore the scenario.
@param {string} title - Descriptive text
@param {function} callback - Callback function
@see #context.Scenario
@memberof module:bdd-feature
*/
context.Scenario.skip = function (title, callback) {
return createStep('Scenario: ', title, context.describe.skip, callback);
};
/**
Describe a 'given' step.
The title will be prefixed with 'Given ' if it doesn't already start with it.
@param {string} title - Descriptive text
@param {function} callback - Callback function
@memberof module:bdd-feature
*/
context.Given = function (title, callback) {
return createStep('Given ', title, context.it, callback);
};
/**
Describe a 'when' step.
The title will be prefixed with 'When ' if it doesn't already start with it.
@param {string} title - Descriptive text
@param {function} callback - Callback function
@memberof module:bdd-feature
*/
context.When = function (title, callback) {
return createStep('When ', title, context.it, callback);
};
/**
Describe a 'then' step.
The title will be prefixed with 'Then ' if it doesn't already start with it.
@param {string} title - Descriptive text
@param {function} callback - Callback function
@memberof module:bdd-feature
*/
context.Then = function (title, callback) {
return createStep('Then ', title, context.it, callback);
};
/**
Describe a 'but' step.
The title will be prefixed with 'But ' if it doesn't already start with it.
@param {string} title - Descriptive text
@param {function} callback - Callback function
@memberof module:bdd-feature
*/
context.But = function (title, callback) {
return createStep('But ', title, context.it, callback);
};
/**
By appending .skip() you may tell Mocha to ignore this step.
This only applies to 'and' & 'but' steps as 'given', 'when' & 'then' are required.
@param {string} title - Descriptive text
@param {function} callback - Callback function
@see #context.But
@memberof module:bdd-feature
*/
context.But.skip = function (title, callback) {
return createStep('But ', title, context.it.skip, callback);
};
/**
Describe an 'and' step.
The title will be prefixed with 'And ' if it doesn't already start with it.
@param {string} title - Descriptive text
@param {function} callback - Callback function
@memberof module:bdd-feature
*/
context.And = function (title, callback) {
return createStep('And ', title, context.it, callback);
};
/**
By appending .skip() you may tell Mocha to ignore this step.
This only applies to 'and' & 'but' steps as 'given', 'when' & 'then' are required.
@param {string} title - Descriptive text
@param {function} callback - Callback function
@see #context.And
@memberof module:bdd-feature
*/
context.And.skip = function (title, callback) {
return createStep('And ', title, context.it.skip, callback);
};
// Existing functionality from the 'bdd' interface
function runWithSuite(suite) {
return function run() {
suite.run();
};
}
context.run = mocha.options.delay && runWithSuite(suite);
context.before = function (title, callback) {
suites[0].beforeAll(title, callback);
};
context.after = function (title, callback) {
suites[0].afterAll(title, callback);
};
context.beforeEach = function (title, callback) {
suites[0].beforeEach(title, callback);
};
context.afterEach = function (title, callback) {
suites[0].afterEach(title, callback);
};
context.describe = context.context = function (title, callback) {
var suite = Suite.create(suites[0], title);
suite.file = file;
suites.unshift(suite);
callback.call(suite);
suites.shift();
return suite;
};
context.describe.only = function (title, callback) {
var suite = context.describe(title, callback);
mocha.grep(suite.fullTitle());
return suite;
};
context.xdescribe = context.xcontext = context.describe.skip = function (title, callback) {
var suite = Suite.create(suites[0], title);
suite.pending = true;
suites.unshift(suite);
callback.call(suite);
suites.shift();
};
context.it = function (title, callback) {
var suite = suites[0],
test;
if (suite.pending) {
callback = null;
}
test = new Test(title, callback);
test.file = file;
suite.addTest(test);
return test;
};
context.it.only = function (title, callback) {
var test = context.it(title, callback),
reString = '^' + escapeRe(test.fullTitle()) + '$';
mocha.grep(new RegExp(reString));
return test;
};
context.xit = context.xspecify = context.it.skip = function (title) {
context.it(title);
};
});
}
Mocha.interfaces['bdd-feature'] = bddFeature;
return mocha;
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment