Repository: mochajs/mocha

Problem statement:
total should adjust for --grep

when grepping don't display the empty suites



Hints:
It looks like this might already be fixed in 1.0.1.  I testing on websocket.io's test and the total  (3 instead of 17) was  displayed correctly.

```
$ mocha --grep fire -R spec

  websocket server
    connections
    ✓ must fire a connection event 
    ✓ must fire a close event when client closes 
    ✓ must fire a close event when server closes 
  messages
  request path
  client tracking

websocket.io


✔ 3 tests complete (41ms)
```

Just discovered --grep and glad that you already know about this issue. I think it may be worth showing an example for exact matching using ^. I was having an unintended test fire because it included part of a word that I was only looking for exactly in a description.


Patch:
diff --git a/lib/runner.js b/lib/runner.js
--- a/lib/runner.js
+++ b/lib/runner.js
@@ -53,7 +53,8 @@ function Runner(suite) {
 Runner.prototype.__proto__ = EventEmitter.prototype;
 
 /**
- * Run tests with full titles matching `re`.
+ * Run tests with full titles matching `re`. Updates runner.total
+ * with number of tests matched.
  *
  * @param {RegExp} re
  * @return {Runner} for chaining
@@ -63,9 +64,33 @@ Runner.prototype.__proto__ = EventEmitter.prototype;
 Runner.prototype.grep = function(re){
   debug('grep %s', re);
   this._grep = re;
+  this.total = this.grepTotal(this.suite);
+
   return this;
 };
 
+/**
+ * Returns the number of tests matching the grep search for the 
+ * given suite.
+ *
+ * @param {Suite} suite
+ * @return {Number}
+ * @api public
+ */
+
+Runner.prototype.grepTotal = function(suite) {
+  var self = this;
+  var total = 0;
+
+  suite.eachTest(function(test){
+    if (self._grep.test(test.fullTitle())){
+      total++;
+    };
+  });
+
+  return total;
+};
+
 /**
  * Allow the given `arr` of globals.
  *
@@ -346,7 +371,10 @@ Runner.prototype.runSuite = function(suite, fn){
     , i = 0;
 
   debug('run suite %s', suite.fullTitle());
-  this.emit('suite', this.suite = suite);
+
+  if(self.grepTotal(suite)) {
+    this.emit('suite', this.suite = suite);
+  }
 
   function next() {
     var curr = suite.suites[i++];
diff --git a/lib/suite.js b/lib/suite.js
--- a/lib/suite.js
+++ b/lib/suite.js
@@ -245,3 +245,25 @@ Suite.prototype.total = function(){
     return sum + suite.total();
   }, 0) + this.tests.length;
 };
+
+/**
+ * Iterates through each suite recursively to find
+ * all tests. Applies a function in the format
+ * `fn(test)`.
+ *
+ * @param {Function} fn
+ * @return {Suite}
+ * @api public
+ */
+
+Suite.prototype.eachTest = function(fn){
+  var self = this;
+  utils.forEach(self.tests, function(test){
+    fn(test);
+  });
+  utils.forEach(self.suites, function(suite){
+    suite.eachTest(fn);
+  });
+  return this;
+};
+


Test patch:
diff --git a/test/runner.js b/test/runner.js
--- a/test/runner.js
+++ b/test/runner.js
@@ -1,7 +1,8 @@
 
 var mocha = require('../')
   , Suite = mocha.Suite
-  , Runner = mocha.Runner;
+  , Runner = mocha.Runner
+  , Test = mocha.Test;
 
 describe('Runner', function(){
   var suite, runner;
@@ -11,6 +12,27 @@ describe('Runner', function(){
     runner = new Runner(suite);
   })
 
+  describe('.grep()', function(){
+    it('should update the runner.total with number of matched tests', function(){
+      suite.addTest(new Test('im a test about lions'));
+      suite.addTest(new Test('im another test about lions'));
+      suite.addTest(new Test('im a test about bears'));
+      var newRunner = new Runner(suite);
+      newRunner.grep(/lions/);
+      newRunner.total.should.equal(2);
+    })
+  })
+
+  describe('.grepTotal()', function(){
+    it('should return the total number of matched tests', function(){
+      suite.addTest(new Test('im a test about lions'));
+      suite.addTest(new Test('im another test about lions'));
+      suite.addTest(new Test('im a test about bears'));
+      runner.grep(/lions/);
+      runner.grepTotal(suite).should.equal(2);
+    })
+  })
+
   describe('.globals()', function(){
     it('should default to the known globals', function(){
       runner.globals().length.should.be.above(10);
@@ -102,4 +124,4 @@ describe('Runner', function(){
       runner.failHook(hook, err);
     })
   })
-})
\ No newline at end of file
+})
diff --git a/test/suite.js b/test/suite.js
--- a/test/suite.js
+++ b/test/suite.js
@@ -249,4 +249,52 @@ describe('Suite', function(){
       });
     });
   });
+
+  describe('.eachTest(fn)', function(){
+    beforeEach(function(){
+      this.suite = new Suite('A Suite');
+    });
+
+    describe('when there are no nested suites or tests', function(){
+      it('should return 0', function(){
+        var counter = 0;
+        function fn(){
+          counter++;
+        }
+        this.suite.eachTest(fn);
+        counter.should.equal(0);
+      });
+    });
+
+    describe('when there are several tests in the suite', function(){
+      it('should return the number', function(){
+        this.suite.addTest(new Test('a child test'));
+        this.suite.addTest(new Test('another child test'));
+ 
+        var counter = 0;
+        function fn(){
+          counter++;
+        }
+        this.suite.eachTest(fn);
+        counter.should.equal(2);
+      });
+    });
+
+    describe('when there are several levels of nested suites', function(){
+      it('should return the number', function(){
+        this.suite.addTest(new Test('a child test'));
+        var suite = (new Suite('a child suite'));
+        suite.addTest(new Test('a test in a child suite'));
+        this.suite.addSuite(suite);
+ 
+        var counter = 0;
+        function fn(){
+          counter++;
+        }
+        this.suite.eachTest(fn);
+        counter.should.equal(2);
+      });
+    });
+
+  });
 });