Skip to content

Commit

Permalink
Initial working version.
Browse files Browse the repository at this point in the history
  • Loading branch information
lightsofapollo committed Feb 4, 2012
0 parents commit 93e3e23
Show file tree
Hide file tree
Showing 12 changed files with 634 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
@@ -0,0 +1 @@
node_modules
2 changes: 2 additions & 0 deletions Makefile
@@ -0,0 +1,2 @@
test::
mocha --require $(PWD)/spec/helper.js --reporter spec spec/*.js
3 changes: 3 additions & 0 deletions README.md
@@ -0,0 +1,3 @@
# Sinon Mocha

Sinon Mocha extension
129 changes: 129 additions & 0 deletions lib/mocha-hooks.js
@@ -0,0 +1,129 @@
/* TODO:
* This should probably exist inside mocha itself
* or be bundled as a separate package.
*/
var MochaHooks;

MochaHooks = {

_beforeEaches: [],
_afterEaches: [],
_enhanced: false,

/**
* Enhances mocha instance with global before/after hooks.
*
* @param {Mocha} mocha optional mocha instance
*/
enhance: function(mocha){
var runSuite;

if(typeof(mocha) === 'undefined'){
mocha = MochaHooks.findMocha();
}

runSuite = mocha.Runner.prototype.runSuite;
mocha.Runner.prototype.runSuite = function(suite){
var beforeEachHook;

if(suite.title === '' && !MochaHooks._enhanced){
beforeEachHook = new mocha.Hook('"before each" hook', function(){
MochaHooks.runBeforeEach(this);
});

//This is a push so we just use normal afterEach
suite.afterEach(function(){
MochaHooks.runAfterEach(this);
});

beforeEachHook.parent = suite;
beforeEachHook.timeout(suite.timeout());
suite._beforeEach.unshift(beforeEachHook);

MochaHooks._enhanced = true;
}
return runSuite.apply(this, arguments);
};
},

/**
* Adds a global beforeEach hook
*
* @param {Function} hook will be called before each test
*/
beforeEach: function(fn){
this._beforeEaches.push(fn);
},

/**
* Adds a global afterEach hook
*
* @param {Function} hook will be called after each test
*/
afterEach: function(fn){
this._afterEaches.push(fn);
},

/**
* Executes all hooks for a particular type
* in a given context.
*
* @param {String} type name of the property in MochaHooks
* @param {Object} context context to execute hooks in
*/
_runHooksFor: function(type, context){
var i = 0,
store = MochaHooks[type],
len = store.length;

for(i = 0; i < len; i++){
store[i].call(context);
}
},

/**
* Executes all beforeEach hooks in context
*
* @param {Object} context
*/
runBeforeEach: function(context){
MochaHooks._runHooksFor('_beforeEaches', context);
},

/**
* Executes all afterEach hooks in context
*
* @param {Object} context
*/
runAfterEach: function(context){
MochaHooks._runHooksFor('_afterEaches', context);
},

/**
* Finds the used mocha runtime and returns it.
*
* This is a hack that searches through require.cache...
*
* @return {Mocha}
*/
findMocha: function(){
var objectCache = require.cache,
key,
mochaMatch = /mocha\/index\.js$/,
mocha,
runSuite;

for(key in objectCache){
if(objectCache.hasOwnProperty(key)){
if(key.match(mochaMatch)){
mocha = require(key);
}
}
}

return mocha;
},

};

module.exports = exports = MochaHooks;
29 changes: 29 additions & 0 deletions lib/sinon-mocha.js
@@ -0,0 +1,29 @@


var SinonMocha,
WrapMethod = require('./sinon/wrap-method'),
Hooks = require('./mocha-hooks');

SinonMocha = {

sinon: null,

enhance: function(sinon, mocha){
SinonMocha.sinon = sinon;
Hooks.enhance(mocha);
Hooks.beforeEach(SinonMocha.beforeEach);
Hooks.afterEach(SinonMocha.afterEach);
},

beforeEach: function(){
WrapMethod.enhance(SinonMocha.sinon);
},

afterEach: function(){
WrapMethod.restoreWrappedMethods();
sinon.wrapMethod.restore();
}

};

module.exports = exports = SinonMocha;
38 changes: 38 additions & 0 deletions lib/sinon/wrap-method.js
@@ -0,0 +1,38 @@
var WrapMethod = {};

WrapMethod.enhance = function(sinon){
var wrap = sinon.wrapMethod;

WrapMethod.sinonInstance = sinon;
WrapMethod.originalWrapMethod = wrap;

sinon.wrapMethod = WrapMethod.wrapMethod;
};

WrapMethod.wrapMethod = function(){
var result = WrapMethod.originalWrapMethod.apply(this, arguments);
WrapMethod.objects.push(result);

return result;
};

WrapMethod.wrapMethod.restore = function(){
WrapMethod.sinonInstance.wrapMethod = WrapMethod.originalWrapMethod;
};

WrapMethod.restoreWrappedMethods = function(){
var i = 0, len = WrapMethod.objects.length, obj;


for(i, len; i < len; i++){
obj = WrapMethod.objects[i];
if(obj.restore){
obj.restore();
}
}

WrapMethod.objects.length = 0;
};

WrapMethod.objects = [];
module.exports = exports = WrapMethod;
27 changes: 27 additions & 0 deletions package.json
@@ -0,0 +1,27 @@
{
"name": "sinon-mocha",
"version": "0.0.1",
"author": "James Lal <james@lightsofapollo.com>",
"description": "Automatic restore for spies & mocks for mocha",
"main": "./lib/sinon-mocha",
"repository": {
"type": "git",
"url": "https://github.com/lightsofapollo/node-sinon-mocha.git"
},
"keywords": [
"spec",
"mocha",
"sinon"
],
"devDependencies": {
"mocha" : "*",
"expect.js" : "*",
"sinon": "~1.3"
},
"license": "MIT",
"engine": {
"node": ">=0.4"
}
}


4 changes: 4 additions & 0 deletions spec/helper.js
@@ -0,0 +1,4 @@
/* globals expect, sinon */

expect = require('expect.js');
sinon = require('sinon');
149 changes: 149 additions & 0 deletions spec/mocha-hooks-spec.js
@@ -0,0 +1,149 @@
var Hooks = require('../lib/mocha-hooks');

Hooks.enhance();

describe("mocha", function(){
var originalBefore, originalAfter;

before(function(){
originalBefore = Hooks._beforeEaches;
originalAfter = Hooks._afterEaches;
Hooks._beforeEaches = [];
Hooks._afterEaches = [];
});

after(function(){
Hooks._beforeEaches = originalBefore;
Hooks._afterEaches = originalAfter;
});

describe(".enhance", function(){

describe("hooks firing", function(){

var hooks = [], fn = function(){
return function(){};
};

before(function(){

hooks = [];
Hooks.beforeEach(function(){
hooks.push('before - ' + this.title);
});

Hooks.afterEach(function(){
hooks.push('after - ' + this.title);
});
});

it("one", fn());

describe("testing nesting", function(){
it("two", fn());

describe("deep nesting", function(){
it("three", fn());
});
});


after(function(){
Hooks._beforeEaches.pop();
Hooks._afterEaches.pop();

//It should fire hooks in correct order
expect(hooks).to.eql([
'before - one',
'after - one',
'before - two',
'after - two',
'before - three',
'after - three'
]);
});

});

});

describe(".beforeEach", function(){
var fn = function(){};

it("should add function to _beforeEaches", function(){
Hooks.beforeEach(fn);
expect(Hooks._beforeEaches).to.contain(fn);
Hooks._beforeEaches.pop();
});

});

describe(".afterEach", function(){
var fn = function(){};

it("should add function to _afterEaches", function(){
Hooks.afterEach(fn);
expect(Hooks._afterEaches).to.contain(fn);
Hooks._afterEaches.pop();
});
});


describe('runners', function(){
var runners = ['runBeforeEach', 'runAfterEach'],
stores = {
'runBeforeEach': '_beforeEaches',
'runAfterEach': '_afterEaches'
}, i;

for(i = 0; i < runners.length; i++){
(function(name){

describe("." + name, function(){
var store = stores[name],
stub = {
hook: function(){}
}, context = {};

before(function(){
sinon.stub(stub, 'hook');
});

beforeEach(function(){
Hooks[store].push(stub.hook);
Hooks[name](context);
});

afterEach(function(){
Hooks[store].pop();
});

it("should have added hook", function(){
expect(Hooks[store]).to.contain(stub.hook);
});

it("should have called stub in correct context", function(){
var call;
expect(stub.hook.called).to.be(true);

call = stub.hook.getCall(0);

expect(call.thisValue).to.be(context);
});

});

}(runners[i]));
}

});

describe(".findMocha", function(){

it("should return mocha", function(){
expect(Hooks.findMocha().Runner).to.be.a('function');
});

});

});

0 comments on commit 93e3e23

Please sign in to comment.