Repository: node-nock/nock
Pull request #973 (2017-09-19 23:02:18)

[Fix 835] allowUnmocked for regex host and route
First commit is a failing test for https://github.com/node-nock/nock/pull/965
Second Commit Fixes https://github.com/node-nock/nock/issues/835

coveralls: 
[![Coverage Status](https://coveralls.io/builds/13344996/badge)](https://coveralls.io/builds/13344996)

Coverage increased (+0.02%) to 92.685% when pulling **c75557a672d71a9246caae76dceb5fe90ce037ca on cmswalker:835-allowUnmocked-for-regex** into **e91168820b6289484059e523cba4fc1c1e2e9948 on node-nock:master**.


Patch:
diff --git a/lib/interceptor.js b/lib/interceptor.js
index caab1a8..aab06dd 100755
--- a/lib/interceptor.js
+++ b/lib/interceptor.js
@@ -1,19 +1,19 @@
 'use strict';
 
-var mixin           = require('./mixin')
-    , matchBody       = require('./match_body')
-    , common          = require('./common')
-    , _               = require('lodash')
-    , debug           = require('debug')('nock.interceptor')
-    , stringify       = require('json-stringify-safe')
-    , util            = require('util')
-    , qs              = require('qs');
+var mixin = require('./mixin')
+    , matchBody = require('./match_body')
+    , common = require('./common')
+    , _ = require('lodash')
+    , debug = require('debug')('nock.interceptor')
+    , stringify = require('json-stringify-safe')
+    , util = require('util')
+    , qs = require('qs');
 
 var fs;
 
 try {
     fs = require('fs');
-} catch(err) {
+} catch (err) {
     // do nothing, we're in the browser
 }
 
@@ -102,8 +102,8 @@ Interceptor.prototype.reply = function reply(statusCode, body, rawHeaders) {
     //  If the content is not encoded we may need to transform the response body.
     //  Otherwise we leave it as it is.
     if (!common.isContentEncoded(this.headers)) {
-        if (body && typeof(body) !== 'string' &&
-            typeof(body) !== 'function' &&
+        if (body && typeof (body) !== 'string' &&
+            typeof (body) !== 'function' &&
             !Buffer.isBuffer(body) &&
             !common.isStream(body)) {
             try {
@@ -117,7 +117,7 @@ Interceptor.prototype.reply = function reply(statusCode, body, rawHeaders) {
                 if (this.scope.contentLen) {
                     this.headers['content-length'] = body.length;
                 }
-            } catch(err) {
+            } catch (err) {
                 throw new Error('Error encoding response body into JSON');
             }
         }
@@ -130,7 +130,7 @@ Interceptor.prototype.reply = function reply(statusCode, body, rawHeaders) {
 };
 
 Interceptor.prototype.replyWithFile = function replyWithFile(statusCode, filePath, headers) {
-    if (! fs) {
+    if (!fs) {
         throw new Error('No fs');
     }
     var readStream = fs.createReadStream(filePath);
@@ -143,7 +143,7 @@ Interceptor.prototype.replyWithFile = function replyWithFile(statusCode, filePat
 // https://github.com/pgte/nock/issues/163
 Interceptor.prototype.reqheaderMatches = function reqheaderMatches(options, key) {
     //  We don't try to match request headers if these weren't even specified in the request.
-    if (! options.headers) {
+    if (!options.headers) {
         return true;
     }
 
@@ -158,12 +158,11 @@ Interceptor.prototype.reqheaderMatches = function reqheaderMatches(options, key)
     //  NOTE: We use lower-case header field names throughout Nock.
     if (key === 'host' &&
         (_.isUndefined(header) ||
-        _.isUndefined(reqHeader)))
-    {
+            _.isUndefined(reqHeader))) {
         return true;
     }
 
-    if (!_.isUndefined(reqHeader) && !_.isUndefined(header)) {
+    if (reqHeader && header) {
         if (_.isFunction(reqHeader)) {
             return reqHeader(header);
         } else if (common.matchStringOrRegexp(header, reqHeader)) {
@@ -193,14 +192,14 @@ Interceptor.prototype.match = function match(options, body, hostNameOnly) {
     if (this.scope.transformPathFunction) {
         path = this.scope.transformPathFunction(path);
     }
-    if (typeof(body) !== 'string') {
+    if (typeof (body) !== 'string') {
         body = body.toString();
     }
     if (this.scope.transformRequestBodyFunction) {
         body = this.scope.transformRequestBodyFunction(body, this._requestBody);
     }
 
-    var checkHeaders = function(header) {
+    var checkHeaders = function (header) {
         if (_.isFunction(header.value)) {
             return header.value(options.getHeader(header.name));
         }
@@ -214,7 +213,7 @@ Interceptor.prototype.match = function match(options, body, hostNameOnly) {
     }
 
     var reqHeadersMatch =
-        ! this.reqheaders ||
+        !this.reqheaders ||
         Object.keys(this.reqheaders).every(this.reqheaderMatches.bind(this, options));
 
     if (!reqHeadersMatch) {
@@ -264,9 +263,9 @@ Interceptor.prototype.match = function match(options, body, hostNameOnly) {
         // Only check for query string matches if this.queries is an object
         if (_.isObject(this.queries)) {
 
-            if(_.isFunction(this.queries)){
+            if (_.isFunction(this.queries)) {
                 matchQueries = this.queries(queries);
-            }else {
+            } else {
                 // Make sure that you have an equal number of keys. We are
                 // looping through the passed query params and not the expected values
                 // if the user passes fewer query params than expected but all values
@@ -284,45 +283,44 @@ Interceptor.prototype.match = function match(options, body, hostNameOnly) {
                         var expVal = self.queries[key];
                         var isMatch = true;
                         if (val === undefined || expVal === undefined) {
-                        isMatch = false;
-                    } else if (expVal instanceof RegExp) {
-                      isMatch = common.matchStringOrRegexp(val, expVal);
-                    } else if (_.isArray(expVal) || _.isObject(expVal)) {
-                      isMatch = _.isEqual(val, expVal);
-                    } else {
-                      isMatch = common.matchStringOrRegexp(val, expVal);
-                    }
-                matchQueries = matchQueries && !!isMatch;
-                });
+                            isMatch = false;
+                        } else if (expVal instanceof RegExp) {
+                            isMatch = common.matchStringOrRegexp(val, expVal);
+                        } else if (_.isArray(expVal) || _.isObject(expVal)) {
+                            isMatch = _.isEqual(val, expVal);
+                        } else {
+                            isMatch = common.matchStringOrRegexp(val, expVal);
+                        }
+                        matchQueries = matchQueries && !!isMatch;
+                    });
                 }
                 debug('matchQueries: %j', matchQueries);
             }
         }
 
         // Remove the query string from the path
-        if(queryIndex !== -1) {
+        if (queryIndex !== -1) {
             path = path.substr(0, queryIndex);
         }
     }
 
     if (typeof this.uri === 'function') {
         matches = matchQueries &&
-        method.toUpperCase() + ' ' + proto + '://' + options.host === this.baseUri &&
-        this.uri.call(this, path);
+            method.toUpperCase() + ' ' + proto + '://' + options.host === this.baseUri &&
+            this.uri.call(this, path);
     } else {
         matches = method === this.method &&
-        common.matchStringOrRegexp(matchKey, this.basePath) &&
-        common.matchStringOrRegexp(path, this.path) &&
-        matchQueries;
+            common.matchStringOrRegexp(matchKey, this.basePath) &&
+            common.matchStringOrRegexp(path, this.path) &&
+            matchQueries;
     }
 
     // special logger for query()
     if (queryIndex !== -1) {
-        this.scope.logger('matching ' + method + ' ' + matchKey + path + '?' + queryString 
-        + ' to ' + this._key + ' with query(' + stringify(this.queries) + '): ' + matches);
+        this.scope.logger('matching ' + matchKey + '?' + queryString + ' to ' + this._key +
+            ' with query(' + stringify(this.queries) + '): ' + matches);
     } else {
-        this.scope.logger('matching ' + method + ' ' + matchKey + path 
-        + ' to ' + this._key + ': ' + matches);
+        this.scope.logger('matching ' + matchKey + ' to ' + this._key + ': ' + matches);
     }
 
     if (matches) {
@@ -336,6 +334,8 @@ Interceptor.prototype.match = function match(options, body, hostNameOnly) {
 };
 
 Interceptor.prototype.matchIndependentOfBody = function matchIndependentOfBody(options) {
+    var isRegex = _.isRegExp(this.__nock_scopeKey) && _.isRegExp(this.path);
+
     var method = (options.method || 'GET').toUpperCase()
         , path = options.path
         , proto = options.proto;
@@ -344,7 +344,7 @@ Interceptor.prototype.matchIndependentOfBody = function matchIndependentOfBody(o
         path = this.scope.transformPathFunction(path);
     }
 
-    var checkHeaders = function(header) {
+    var checkHeaders = function (header) {
         return options.getHeader && common.matchStringOrRegexp(options.getHeader(header.name), header.value);
     };
 
@@ -353,8 +353,14 @@ Interceptor.prototype.matchIndependentOfBody = function matchIndependentOfBody(o
         return false;
     }
 
+    var comparisonKey = isRegex ? this.__nock_scopeKey : this._key;
     var matchKey = method + ' ' + proto + '://' + options.host + path;
-    return this._key === matchKey;
+
+    if (isRegex) {
+        return !!matchKey.match(comparisonKey) && !!path.match(this.path);
+    }
+
+    return comparisonKey === matchKey;
 };
 
 Interceptor.prototype.filteringPath = function filteringPath() {
@@ -405,7 +411,7 @@ Interceptor.prototype.query = function query(queries) {
         this.queries = queries;
     }
 
-    if(_.isFunction(queries)){
+    if (_.isFunction(queries)) {
         this.queries = queries;
         return this;
     }
@@ -522,7 +528,7 @@ Interceptor.prototype.delayConnection = function delayConnection(ms) {
     return this;
 };
 
-Interceptor.prototype.getTotalDelay =function getTotalDelay() {
+Interceptor.prototype.getTotalDelay = function getTotalDelay() {
     return this.delayInMs + this.delayConnectionInMs;
 };
 
@@ -535,4 +541,4 @@ Interceptor.prototype.getTotalDelay =function getTotalDelay() {
 Interceptor.prototype.socketDelay = function socketDelay(ms) {
     this.socketDelayInMs = ms;
     return this;
-};
+};
\ No newline at end of file


Test patch:
diff --git a/tests/test_intercept.js b/tests/test_intercept.js
index 9866eac..716ed51 100755
--- a/tests/test_intercept.js
+++ b/tests/test_intercept.js
@@ -5069,6 +5069,60 @@ test('query(true) will match when the path has no query', function (t) {
   })
 });
 
+test('match domain and path using regexp (#835)', function (t) {
+  nock.cleanAll();
+  var imgResponse = 'Matched Images Page';
+
+  var scope = nock(/google/)
+    .get(/img/)
+    .reply(200, imgResponse);
+
+  mikealRequest.get('http://www.google.com/imghp?hl=en', function (err, res, body) {
+    scope.done();
+    t.type(err, 'null');
+    t.equal(res.statusCode, 200);
+    t.equal(body, imgResponse);
+    t.end();
+  });
+});
+
+test('match multiple paths to domain using regexp with allowUnmocked (#835)', function (t) {
+  nock.cleanAll();
+
+  var nockOpts = { allowUnmocked: true };
+  var searchResponse = 'Matched Google Search Results Page';
+  var imgResponse = 'Matched Google Images Page';
+
+  var scope1 = nock(/google/, nockOpts)
+    .get(/imghp/)
+    .reply(200, imgResponse);
+
+  var scope2 = nock(/google/, nockOpts)
+    .get(/search\?/)
+    .reply(200, searchResponse);
+
+
+  mikealRequest.get('http://www.google.com', function (err, res, body) {
+    t.type(err, 'null');
+    t.equal(res.statusCode, 200);
+
+    mikealRequest.get('http://www.google.com/imghp?hl=en', function (err, res, body) {
+      scope1.done();
+      t.type(err, 'null');
+      t.equal(res.statusCode, 200);
+      t.equal(body, imgResponse);
+
+      mikealRequest.get('http://www.google.com/search?q=pugs', function (err, res, body) {
+        scope2.done();
+        t.type(err, 'null');
+        t.equal(res.statusCode, 200);
+        t.equal(body, searchResponse);
+        t.end();
+      });
+    });
+  });
+});
+
 test("teardown", function(t) {
   var leaks = Object.keys(global)
     .splice(globalCount, Number.MAX_VALUE);