Repository: mochajs/mocha

Problem statement:
html reporter link to specific test is broken on some special characters
If the test title contains some special characters (for instance $ or () ) then its link in the HTML report does not work.

IMO the fix should be escaping regex [here](https://github.com/mochajs/mocha/blob/908d8dbe15b5ef5f4a5658f6cc870bf563e80717/lib/reporters/html.js#L192) before calling encodeURIComponent(s)



Hints:
I can reproduce the bug.

Digging deeper, the issue is caused because the `grep` query string argument is passed verbatim into the `RegExp` constructor, [here](https://github.com/mochajs/mocha/blob/master/support/tail.js):

``` javascript
if (query.grep) mocha.grep(new RegExp(query.grep));
```

Because the grep argument isn't escaped, it produces regex objects like:

``` javascript
/test that $.jQuery works/
```

Which of course don't work.

Patch incoming.


Patch:
diff --git a/lib/reporters/html.js b/lib/reporters/html.js
--- a/lib/reporters/html.js
+++ b/lib/reporters/html.js
@@ -5,6 +5,7 @@
 var Base = require('./base')
   , utils = require('../utils')
   , Progress = require('../browser/progress')
+  , escapeRe = require('../browser/escape-string-regexp')
   , escape = utils.escape;
 
 /**
@@ -200,7 +201,7 @@ var makeUrl = function makeUrl(s) {
     search = search.replace(/[?&]grep=[^&\s]*/g, '').replace(/^&/, '?');
   }
 
-  return window.location.pathname + (search ? search + '&' : '?' ) + 'grep=' + encodeURIComponent(s);
+  return window.location.pathname + (search ? search + '&' : '?' ) + 'grep=' + encodeURIComponent(escapeRe(s));
 };
 
 /**


Test patch:
diff --git a/test/browser/ui.html b/test/browser/ui.html
new file mode 100644
--- /dev/null
+++ b/test/browser/ui.html
@@ -0,0 +1,46 @@
+<html>
+  <head>
+    <title>Mocha</title>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <link rel="stylesheet" href="../../mocha.css" />
+    <script src="../../mocha.js"></script>
+    <script>mocha.setup('bdd')</script>
+    <script>
+      function assert(expr, msg) {
+        if (!expr) throw new Error(msg || 'failed');
+      }
+    </script>
+    <script src="ui.js"></script>
+  </head>
+  <body>
+    <div id="mocha"></div>
+  <script>
+      (function(window) {
+        var location = window.location;
+        mocha.checkLeaks();
+        var runner = mocha.run();
+        var count = 0;
+        setTimeout(run, 1000);
+
+        function run() {
+          var regex = [
+              '',                                  // All
+              '%5C%24%5C.jQuery',                  // $.jQuery
+              '%5C%24%5C.jQuery%20%5C.on%5C(%5C)', // $.jQuery .on()
+            ]
+            , qs = location.search.replace('?grep=', '')
+            , re = ~qs.indexOf('%') ? qs : decodeURIComponent(qs)
+            , grep = regex[regex.indexOf(re) + 1]
+            , anchors = document.getElementsByTagName('a');
+
+          // Locate first 'a' element w/ matching grep param; click it
+          for (var i = 0; i < anchors.length; i++) {
+            if (anchors[i].href && anchors[i].href.indexOf(grep) > -1)
+              return void anchors[i].click();
+          }
+        }
+      })(window);
+  </script>
+  </body>
+</html>
diff --git a/test/browser/ui.js b/test/browser/ui.js
new file mode 100644
--- /dev/null
+++ b/test/browser/ui.js
@@ -0,0 +1,31 @@
+// test titles containing regex-conflicting characters
+
+// leading $
+describe('$.jQuery', function() {
+  // parens
+  describe('.on()', function () {
+    it('should set an event', function() {
+      assert(true);
+    });
+  });
+
+  describe('.off()', function () {
+    it('should remove an event', function () {
+
+    });
+  });
+});
+
+// another generic describe block to verify it is absent
+// when greeping on $.jQuery
+describe('@Array', function() {
+  it('.pop()', function() {
+    assert(true);
+  });
+  it('.push()', function() {
+    assert(true);
+  });
+  it('.length', function() {
+    assert(true);
+  });
+});