Permalink
Browse files

Initial working version.

  • Loading branch information...
0 parents commit 93e3e231db0fd8fab8bc2fb14500c108904a5ca1 @lightsofapollo lightsofapollo committed Feb 4, 2012
Showing with 634 additions and 0 deletions.
  1. +1 −0 .gitignore
  2. +2 −0 Makefile
  3. +3 −0 README.md
  4. +129 −0 lib/mocha-hooks.js
  5. +29 −0 lib/sinon-mocha.js
  6. +38 −0 lib/sinon/wrap-method.js
  7. +27 −0 package.json
  8. +4 −0 spec/helper.js
  9. +149 −0 spec/mocha-hooks-spec.js
  10. +77 −0 spec/sinon-mocha-spec.js
  11. +107 −0 spec/sinon/wrap-method-spec.js
  12. +68 −0 watchr.js
@@ -0,0 +1 @@
+node_modules
@@ -0,0 +1,2 @@
+test::
+ mocha --require $(PWD)/spec/helper.js --reporter spec spec/*.js
@@ -0,0 +1,3 @@
+# Sinon Mocha
+
+Sinon Mocha extension
@@ -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;
@@ -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;
@@ -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;
@@ -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"
+ }
+}
+
+
@@ -0,0 +1,4 @@
+/* globals expect, sinon */
+
+expect = require('expect.js');
+sinon = require('sinon');
@@ -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');
+ });
+
+ });
+
+});
Oops, something went wrong.

0 comments on commit 93e3e23

Please sign in to comment.