twreporter-react
Version:
React-Redux site for The Reporter Foundation in Taiwan
1,751 lines (1,362 loc) • 706 kB
JavaScript
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
var recorder = require('./lib/recorder');
module.exports = require('./lib/scope');
module.exports.recorder = {
rec : recorder.record
, clear : recorder.clear
, play : recorder.outputs
};
module.exports.back = require('./lib/back');
module.exports.restore = recorder.restore;
},{"./lib/back":2,"./lib/recorder":8,"./lib/scope":10}],2:[function(require,module,exports){
(function (process){
'use strict';
var nock = require('./scope');
var recorder = require('./recorder');
var format = require('util').format;
var mkdirp = require('mkdirp');
var path = require('path');
var expect = require('chai').expect;
var debug = require('debug')('nock.back');
var _mode = null;
var fs;
try {
fs = require('fs');
} catch(err) {
// do nothing, probably in browser
}
/**
* nock the current function with the fixture given
*
* @param {string} fixtureName - the name of the fixture, e.x. 'foo.json'
* @param {object} options - [optional], extra options for nock with, e.x. { assert: true }
* @param {function} nockedFn - the callback function to be executed with the given fixture being loaded,
* the function will be called with { scopes: loaded_nocks || [] } set as this
*
*
* List of options:
*
* @param {function} before - a preprocessing function, gets called before nock.define
* @param {function} after - a postprocessing function, gets called after nock.define
* @param {function} afterRecord - a postprocessing function, gets called after recording. Is passed the array
* of scopes recorded and should return the array scopes to save to the fixture
*
*/
function Back (fixtureName, options, nockedFn) {
if(!Back.fixtures) {
throw new Error( 'Back requires nock.back.fixtures to be set\n' +
'Ex:\n' +
'\trequire(nock).back.fixtures = \'/path/to/fixures/\'');
}
if( arguments.length === 2 ) {
nockedFn = options;
options = {};
}
_mode.setup();
var fixture = path.join(Back.fixtures, fixtureName)
, context = _mode.start(fixture, options);
var nockDone = function () {
_mode.finish(fixture, options, context);
};
debug('context:', context);
nockedFn.call(context, nockDone);
}
/*******************************************************************************
* Modes *
*******************************************************************************/
var wild = {
setup: function () {
nock.cleanAll();
recorder.restore();
nock.activate();
nock.enableNetConnect();
},
start: function () {
return load(); //don't load anything but get correct context
},
finish: function () {
//nothing to do
}
};
var dryrun = {
setup: function () {
recorder.restore();
nock.cleanAll();
nock.activate();
// We have to explicitly enable net connectivity as by default it's off.
nock.enableNetConnect();
},
start: function (fixture, options) {
var contexts = load(fixture, options);
nock.enableNetConnect();
return contexts;
},
finish: function () {
//nothing to do
}
};
var record = {
setup: function () {
recorder.restore();
recorder.clear();
nock.cleanAll();
nock.activate();
nock.disableNetConnect();
},
start: function (fixture, options) {
if (! fs) {
throw new Error('no fs');
}
var context = load(fixture, options);
if( !context.isLoaded ) {
recorder.record({
dont_print: true,
output_objects: true
});
context.isRecording = true;
}
return context;
},
finish: function (fixture, options, context) {
if( context.isRecording ) {
var outputs = recorder.outputs();
if( typeof options.afterRecord === 'function' ) {
outputs = options.afterRecord(outputs);
}
outputs = JSON.stringify(outputs, null, 4);
debug('recorder outputs:', outputs);
mkdirp.sync(path.dirname(fixture));
fs.writeFileSync(fixture, outputs);
}
}
};
var lockdown = {
setup: function () {
recorder.restore();
recorder.clear();
nock.cleanAll();
nock.activate();
nock.disableNetConnect();
},
start: function (fixture, options) {
return load(fixture, options);
},
finish: function () {
//nothing to do
}
};
function load (fixture, options) {
var context = {
scopes : [],
assertScopesFinished: function () {
assertScopes(this.scopes, fixture);
}
};
if( fixture && fixtureExists(fixture) ) {
var scopes = nock.loadDefs(fixture);
applyHook(scopes, options.before);
scopes = nock.define(scopes);
applyHook(scopes, options.after);
context.scopes = scopes;
context.isLoaded = true;
}
return context;
}
function applyHook(scopes, fn) {
if( !fn ) {
return;
}
if( typeof fn !== 'function' ) {
throw new Error ('processing hooks must be a function');
}
scopes.forEach(fn);
}
function fixtureExists(fixture) {
if (! fs) {
throw new Error('no fs');
}
return fs.existsSync(fixture);
}
function assertScopes (scopes, fixture) {
scopes.forEach(function (scope) {
expect( scope.isDone() )
.to.be.equal(
true,
format('%j was not used, consider removing %s to rerecord fixture', scope.pendingMocks(), fixture)
);
});
}
var Modes = {
wild: wild, //all requests go out to the internet, dont replay anything, doesnt record anything
dryrun: dryrun, //use recorded nocks, allow http calls, doesnt record anything, useful for writing new tests (default)
record: record, //use recorded nocks, record new nocks
lockdown: lockdown, //use recorded nocks, disables all http calls even when not nocked, doesnt record
};
Back.setMode = function(mode) {
if( !Modes.hasOwnProperty(mode) ) {
throw new Error ('some usage error');
}
Back.currentMode = mode;
debug('New nock back mode:', Back.currentMode);
_mode = Modes[mode];
_mode.setup();
};
Back.fixtures = null;
Back.currentMode = null;
Back.setMode(process.env.NOCK_BACK_MODE || 'dryrun');
module.exports = exports = Back;
}).call(this,require('_process'))
},{"./recorder":8,"./scope":10,"_process":25,"chai":56,"debug":91,"fs":12,"mkdirp":98,"path":24,"util":54}],3:[function(require,module,exports){
(function (Buffer){
'use strict';
var _ = require('lodash');
var debug = require('debug')('nock.common');
/**
* Normalizes the request options so that it always has `host` property.
*
* @param {Object} options - a parsed options object of the request
*/
var normalizeRequestOptions = function(options) {
options.proto = options.proto || (options._https_ ? 'https': 'http');
options.port = options.port || ((options.proto === 'http') ? 80 : 443);
if (options.host) {
debug('options.host:', options.host);
if (! options.hostname) {
if (options.host.split(':').length == 2) {
options.hostname = options.host.split(':')[0];
} else {
options.hostname = options.host;
}
}
}
debug('options.hostname in the end: %j', options.hostname);
options.host = (options.hostname || 'localhost') + ':' + options.port;
debug('options.host in the end: %j', options.host);
/// lowercase host names
['hostname', 'host'].forEach(function(attr) {
if (options[attr]) {
options[attr] = options[attr].toLowerCase();
}
})
return options;
};
/**
* Returns true if the data contained in buffer is binary which in this case means
* that it cannot be reconstructed from its utf8 representation.
*
* @param {Object} buffer - a Buffer object
*/
var isBinaryBuffer = function(buffer) {
if(!Buffer.isBuffer(buffer)) {
return false;
}
// Test if the buffer can be reconstructed verbatim from its utf8 encoding.
var utfEncodedBuffer = buffer.toString('utf8');
var reconstructedBuffer = new Buffer(utfEncodedBuffer, 'utf8');
var compareBuffers = function(lhs, rhs) {
if(lhs.length !== rhs.length) {
return false;
}
for(var i = 0; i < lhs.length; ++i) {
if(lhs[i] !== rhs[i]) {
return false;
}
}
return true;
};
// If the buffers are *not* equal then this is a "binary buffer"
// meaning that it cannot be faitfully represented in utf8.
return !compareBuffers(buffer, reconstructedBuffer);
};
/**
* If the chunks are Buffer objects then it returns a single Buffer object with the data from all the chunks.
* If the chunks are strings then it returns a single string value with data from all the chunks.
*
* @param {Array} chunks - an array of Buffer objects or strings
*/
var mergeChunks = function(chunks) {
if(_.isEmpty(chunks)) {
return new Buffer(0);
}
// We assume that all chunks are Buffer objects if the first is buffer object.
var areBuffers = Buffer.isBuffer(_.first(chunks));
if(!areBuffers) {
// When the chunks are not buffers we assume that they are strings.
return chunks.join('');
}
// Merge all the buffers into a single Buffer object.
return Buffer.concat(chunks);
};
// Array where all information about all the overridden requests are held.
var requestOverride = [];
/**
* Overrides the current `request` function of `http` and `https` modules with
* our own version which intercepts issues HTTP/HTTPS requests and forwards them
* to the given `newRequest` function.
*
* @param {Function} newRequest - a function handling requests; it accepts four arguments:
* - proto - a string with the overridden module's protocol name (either `http` or `https`)
* - overriddenRequest - the overridden module's request function already bound to module's object
* - options - the options of the issued request
* - callback - the callback of the issued request
*/
var overrideRequests = function(newRequest) {
debug('overriding requests');
['http', 'https'].forEach(function(proto) {
debug('- overriding request for', proto);
var moduleName = proto, // 1 to 1 match of protocol and module is fortunate :)
module = {
http: require('http'),
https: require('https')
}[moduleName],
overriddenRequest = module.request;
if(requestOverride[moduleName]) {
throw new Error('Module\'s request already overridden for ' + moduleName + ' protocol.');
}
// Store the properties of the overridden request so that it can be restored later on.
requestOverride[moduleName] = {
module: module,
request: overriddenRequest
};
module.request = function(options, callback) {
// debug('request options:', options);
return newRequest(proto, overriddenRequest.bind(module), options, callback);
};
debug('- overridden request for', proto);
});
};
/**
* Restores `request` function of `http` and `https` modules to values they
* held before they were overridden by us.
*/
var restoreOverriddenRequests = function() {
debug('restoring requests');
// Restore any overridden requests.
_(requestOverride).keys().each(function(proto) {
debug('- restoring request for', proto);
var override = requestOverride[proto];
if(override) {
override.module.request = override.request;
debug('- restored request for', proto);
}
});
requestOverride = [];
};
function stringifyRequest(options, body) {
var method = options.method || 'GET';
if (body && typeof(body) !== 'string') {
body = body.toString();
}
var port = options.port;
if (! port) port = (options.proto == 'https' ? '443' : '80');
if (options.proto == 'https' && port == '443' ||
options.proto == 'http' && port == '80') {
port = '';
}
if (port) port = ':' + port;
return method + ' ' + options.proto + '://' + options.hostname + port + options.path + ' ' + body;
}
function isContentEncoded(headers) {
if(!headers) {
return false;
}
var contentEncoding = headers['content-encoding'];
return _.isString(contentEncoding) && contentEncoding !== '';
}
var headersFieldNamesToLowerCase = function(headers) {
if(!_.isObject(headers)) {
return headers;
}
// For each key in the headers, delete its value and reinsert it with lower-case key.
// Keys represent headers field names.
var lowerCaseHeaders = {};
_(headers).keys().each(function(fieldName) {
var lowerCaseFieldName = fieldName.toLowerCase();
if(!_.isUndefined(lowerCaseHeaders[lowerCaseFieldName])) {
throw new Error('Failed to convert header keys to lower case due to field name conflict: ' + lowerCaseFieldName);
}
lowerCaseHeaders[lowerCaseFieldName] = headers[fieldName];
});
return lowerCaseHeaders;
};
var headersFieldsArrayToLowerCase = function (headers) {
return _.uniq(_.map(headers, function (fieldName) {
return fieldName.toLowerCase();
}));
};
/**
* Deletes the given `fieldName` property from `headers` object by performing
* case-insensitive search through keys.
*
* @headers {Object} headers - object of header field names and values
* @fieldName {String} field name - string with the case-insensitive field name
*/
var deleteHeadersField = function(headers, fieldNameToDelete) {
if(!_.isObject(headers) || !_.isString(fieldNameToDelete)) {
return;
}
var lowerCaseFieldNameToDelete = fieldNameToDelete.toLowerCase();
// Search through the headers and delete all values whose field name matches the given field name.
_(headers).keys().each(function(fieldName) {
var lowerCaseFieldName = fieldName.toLowerCase();
if(lowerCaseFieldName === lowerCaseFieldNameToDelete) {
delete headers[fieldName];
// We don't stop here but continue in order to remove *all* matching field names
// (even though if seen regorously there shouldn't be any)
}
});
};
exports.normalizeRequestOptions = normalizeRequestOptions;
exports.isBinaryBuffer = isBinaryBuffer;
exports.mergeChunks = mergeChunks;
exports.overrideRequests = overrideRequests;
exports.restoreOverriddenRequests = restoreOverriddenRequests;
exports.stringifyRequest = stringifyRequest;
exports.isContentEncoded = isContentEncoded;
exports.headersFieldNamesToLowerCase = headersFieldNamesToLowerCase;
exports.headersFieldsArrayToLowerCase = headersFieldsArrayToLowerCase;
exports.deleteHeadersField = deleteHeadersField;
}).call(this,require("buffer").Buffer)
},{"buffer":15,"debug":91,"http":44,"https":20,"lodash":97}],4:[function(require,module,exports){
(function (Buffer,process){
'use strict';
/**
* Creates a stream which becomes the response body of the interceptor when a
* delay is set. The stream outputs the intended body and EOF after the delay.
*
* @param {String|Buffer|Stream} body - the body to write/pipe out
* @param {Integer} ms - The delay in milliseconds
* @constructor
*/
module.exports = DelayedBody;
var Transform = require('stream').Transform;
var EventEmitter = require('events').EventEmitter;
var noop = function () {};
var util = require('util');
var timers = require('timers');
function isStream(obj) {
var is = obj && (typeof a !== 'string') && (! Buffer.isBuffer(obj)) && (typeof obj.setEncoding === 'function');
return is;
}
if (!Transform) {
// for barebones compatibility for node < 0.10
var FakeTransformStream = function () {
EventEmitter.call(this);
};
util.inherits(FakeTransformStream, EventEmitter);
FakeTransformStream.prototype.pause = noop;
FakeTransformStream.prototype.resume = noop;
FakeTransformStream.prototype.setEncoding = noop;
FakeTransformStream.prototype.write = function (chunk, encoding) {
var self = this;
process.nextTick(function () {
self.emit('data', chunk, encoding);
});
};
FakeTransformStream.prototype.end = function (chunk) {
var self = this;
if (chunk) {
self.write(chunk);
}
process.nextTick(function () {
self.emit('end');
});
};
Transform = FakeTransformStream;
}
function DelayedBody(ms, body) {
Transform.call(this);
var self = this;
var data = '';
var ended = false;
if (isStream(body)) {
body.on('data', function (chunk) {
data += Buffer.isBuffer(chunk) ? chunk.toString() : chunk;
});
body.once('end', function () {
ended = true;
});
body.resume();
}
setTimeout(function () {
if (isStream(body) && !ended) {
body.once('end', function () {
self.end(data);
});
} else {
self.end(data || body);
}
}, ms);
}
util.inherits(DelayedBody, Transform);
DelayedBody.prototype._transform = function (chunk, encoding, cb) {
this.push(chunk);
process.nextTick(cb);
};
}).call(this,{"isBuffer":require("../node_modules/browserify/node_modules/insert-module-globals/node_modules/is-buffer/index.js")},require('_process'))
},{"../node_modules/browserify/node_modules/insert-module-globals/node_modules/is-buffer/index.js":22,"_process":25,"events":19,"stream":43,"timers":50,"util":54}],5:[function(require,module,exports){
(function (process){
'use strict';
/**
* @module nock/intercepts
*/
var RequestOverrider = require('./request_overrider'),
common = require('./common'),
url = require('url'),
inherits = require('util').inherits,
http = require('http'),
parse = require('url').parse,
_ = require('lodash'),
debug = require('debug')('nock.intercept'),
timers = require('timers'),
EventEmitter = require('events').EventEmitter;
/**
* @name NetConnectNotAllowedError
* @private
* @desc Error trying to make a connection when disabled external access.
* @class
* @example
* nock.disableNetConnect();
* http.get('http://zombo.com');
* // throw NetConnectNotAllowedError
*/
function NetConnectNotAllowedError(host, path) {
Error.call(this);
this.name = 'NetConnectNotAllowedError';
this.message = 'Nock: Not allow net connect for "' + host + path + '"';
Error.captureStackTrace(this, this.constructor);
}
inherits(NetConnectNotAllowedError, Error);
var allInterceptors = {},
allowNetConnect;
/**
* Enabled real request.
* @public
* @param {String|RegExp} matcher=RegExp.new('.*') Expression to match
* @example
* // Enables all real requests
* nock.enableNetConnect();
* @example
* // Enables real requests for url that matches google
* nock.enableNetConnect('google');
* @example
* // Enables real requests for url that matches google and amazon
* nock.enableNetConnect(/(google|amazon)/);
*/
function enableNetConnect(matcher) {
if (_.isString(matcher)) {
allowNetConnect = new RegExp(matcher);
} else if (_.isObject(matcher) && _.isFunction(matcher.test)) {
allowNetConnect = matcher;
} else {
allowNetConnect = /.*/;
}
}
function isEnabledForNetConnect(options) {
common.normalizeRequestOptions(options);
var enabled = allowNetConnect && allowNetConnect.test(options.host);
debug('Net connect', enabled ? '' : 'not', 'enabled for', options.host);
return enabled;
}
/**
* Disable all real requests.
* @public
* @param {String|RegExp} matcher=RegExp.new('.*') Expression to match
* @example
* nock.disableNetConnect();
*/
function disableNetConnect() {
allowNetConnect = undefined;
}
function isOn() {
return !isOff();
}
function isOff() {
return process.env.NOCK_OFF === 'true';
}
function add(key, interceptor, scope, scopeOptions, host) {
if (! allInterceptors.hasOwnProperty(key)) {
allInterceptors[key] = [];
}
interceptor.__nock_scope = scope;
// We need scope's key and scope options for scope filtering function (if defined)
interceptor.__nock_scopeKey = key;
interceptor.__nock_scopeOptions = scopeOptions;
// We need scope's host for setting correct request headers for filtered scopes.
interceptor.__nock_scopeHost = host;
interceptor.interceptionCounter = 0;
allInterceptors[key].push(interceptor);
}
function remove(interceptor) {
if (interceptor.__nock_scope.shouldPersist()) {
return;
}
interceptor.counter -= 1;
if (interceptor.counter > 0) {
return;
}
var key = interceptor._key.split(' '),
u = url.parse(key[1]),
hostKey = u.protocol + '//' + u.host,
interceptors = allInterceptors[hostKey],
thisInterceptor;
if (interceptors) {
for(var i = 0; i < interceptors.length; i++) {
thisInterceptor = interceptors[i];
if (thisInterceptor === interceptor) {
interceptors.splice(i, 1);
break;
}
}
}
}
function removeAll() {
allInterceptors = {};
}
function hasHadInterceptors(options) {
var basePath,
isBasePathMatched,
isScopedMatched;
debug('hasHadInterceptors', options.host, options.hostname);
common.normalizeRequestOptions(options);
basePath = options.proto + '://' + options.host;
debug('looking for interceptors for basepath %j', basePath);
_.each(allInterceptors, function(interceptor, key) {
if (key === basePath) {
isBasePathMatched = true;
// false to short circuit the .each
return false;
}
_.each(interceptor, function(scope) {
var filteringScope = scope.__nock_scopeOptions.filteringScope;
// If scope filtering function is defined and returns a truthy value
// then we have to treat this as a match.
if (filteringScope && filteringScope(basePath)) {
debug('found matching scope interceptor');
// Keep the filtered scope (its key) to signal the rest of the module
// that this wasn't an exact but filtered match.
scope.__nock_filteredScope = scope.__nock_scopeKey;
isScopedMatched = true;
// Break out of _.each for scopes.
return false;
}
});
// Returning falsy value here (which will happen if we have found our matching interceptor)
// will break out of _.each for all interceptors.
return !isScopedMatched;
});
return (isScopedMatched || isBasePathMatched);
}
function interceptorsFor(options) {
var basePath;
common.normalizeRequestOptions(options);
debug('interceptors for %j', options.host);
basePath = options.proto + '://' + options.host;
debug('filtering interceptors for basepath', basePath);
// First try to use filteringScope if any of the interceptors has it defined.
var matchingInterceptor;
_.each(allInterceptors, function(interceptor, key) {
_.each(interceptor, function(scope) {
var filteringScope = scope.__nock_scopeOptions.filteringScope;
// If scope filtering function is defined and returns a truthy value
// then we have to treat this as a match.
if(filteringScope && filteringScope(basePath)) {
debug('found matching scope interceptor');
// Keep the filtered scope (its key) to signal the rest of the module
// that this wasn't an exact but filtered match.
scope.__nock_filteredScope = scope.__nock_scopeKey;
matchingInterceptor = interceptor;
// Break out of _.each for scopes.
return false;
}
});
// Returning falsy value here (which will happen if we have found our matching interceptor)
// will break out of _.each for all interceptors.
return !matchingInterceptor;
});
if(matchingInterceptor) {
return matchingInterceptor;
}
return allInterceptors[basePath] || [];
}
function removeInterceptor(options) {
var baseUrl, key, method, proto;
proto = options.proto ? options.proto : 'http';
common.normalizeRequestOptions(options);
baseUrl = proto + '://' + options.host;
if (allInterceptors[baseUrl] && allInterceptors[baseUrl].length > 0) {
if (options.path) {
method = options.method && options.method.toUpperCase() || 'GET';
key = method + ' ' + baseUrl + (options.path || '/');
for (var i = 0; i < allInterceptors[baseUrl].length; i++) {
if (allInterceptors[baseUrl][i]._key === key) {
allInterceptors[baseUrl].splice(i, 1);
break;
}
}
} else {
allInterceptors[baseUrl].length = 0;
}
return true;
}
return false;
}
// Variable where we keep the ClientRequest we have overridden
// (which might or might not be node's original http.ClientRequest)
var originalClientRequest;
function ErroringClientRequest(error) {
if (http.OutgoingMessage) http.OutgoingMessage.call(this);
process.nextTick(function() {
this.emit('error', error);
}.bind(this));
}
if (http.ClientRequest) {
inherits(ErroringClientRequest, http.ClientRequest);
}
function overrideClientRequest() {
debug('Overriding ClientRequest');
if(originalClientRequest) {
throw new Error('Nock already overrode http.ClientRequest');
}
// ----- Extending http.ClientRequest
// Define the overriding client request that nock uses internally.
function OverriddenClientRequest(options, cb) {
if (http.OutgoingMessage) http.OutgoingMessage.call(this);
if (isOn() && hasHadInterceptors(options)) {
// Filter the interceptors per request options.
var interceptors = interceptorsFor(options);
debug('using', interceptors.length, 'interceptors');
// Use filtered interceptors to intercept requests.
var overrider = RequestOverrider(this, options, interceptors, remove, cb);
for(var propName in overrider) {
if (overrider.hasOwnProperty(propName)) {
this[propName] = overrider[propName];
}
}
} else {
debug('falling back to original ClientRequest');
// Fallback to original ClientRequest if nock is off or the net connection is enabled.
if(isOff() || isEnabledForNetConnect(options)) {
http.request.apply(this, arguments);
} else {
timers.setImmediate(function () {
var error = new NetConnectNotAllowedError(options.host, options.path);
this.emit('error', error);
}.bind(this));
}
}
}
if (http.ClientRequest) {
inherits(OverriddenClientRequest, http.ClientRequest);
} else {
inherits(OverriddenClientRequest, EventEmitter);
}
// Override the http module's request but keep the original so that we can use it and later restore it.
// NOTE: We only override http.ClientRequest as https module also uses it.
originalClientRequest = http.ClientRequest;
http.ClientRequest = OverriddenClientRequest;
debug('ClientRequest overridden');
}
function restoreOverriddenClientRequest() {
debug('restoring overriden ClientRequest');
// Restore the ClientRequest we have overridden.
if(!originalClientRequest) {
debug('- ClientRequest was not overridden');
} else {
http.ClientRequest = originalClientRequest;
originalClientRequest = undefined;
debug('- ClientRequest restored');
}
}
function isActive() {
// If ClientRequest has been overwritten by Nock then originalClientRequest is not undefined.
// This means that Nock has been activated.
return !_.isUndefined(originalClientRequest);
}
function isDone() {
return _.every(allInterceptors, function(interceptors) {
return _.every(interceptors, function(interceptor) {
return interceptor.__nock_scope.isDone();
});
});
}
function pendingMocks() {
return _.reduce(allInterceptors, function(result, interceptors) {
for (var interceptor in interceptors) {
result = result.concat(interceptors[interceptor].__nock_scope.pendingMocks());
}
return result;
}, []);
}
function activate() {
if(originalClientRequest) {
throw new Error('Nock already active');
}
overrideClientRequest();
// ----- Overriding http.request and https.request:
common.overrideRequests(function(proto, overriddenRequest, options, callback) {
// NOTE: overriddenRequest is already bound to its module.
var req,
res;
if (typeof options === 'string') {
options = parse(options);
}
options.proto = proto;
if (isOn() && hasHadInterceptors(options)) {
var interceptors,
matches = false,
allowUnmocked = false;
interceptors = interceptorsFor(options);
interceptors.forEach(function(interceptor) {
if (! allowUnmocked && interceptor.options.allowUnmocked) { allowUnmocked = true; }
if (interceptor.matchIndependentOfBody(options)) { matches = true; }
});
if (! matches && allowUnmocked) {
if (proto === 'https') {
var ClientRequest = http.ClientRequest;
http.ClientRequest = originalClientRequest;
req = overriddenRequest(options, callback);
http.ClientRequest = ClientRequest;
} else {
req = overriddenRequest(options, callback);
}
return req;
}
// NOTE: Since we already overrode the http.ClientRequest we are in fact constructing
// our own OverriddenClientRequest.
req = new http.ClientRequest(options);
res = RequestOverrider(req, options, interceptors, remove);
if (callback) {
res.on('response', callback);
}
return req;
} else {
if (isOff() || isEnabledForNetConnect(options)) {
return overriddenRequest(options, callback);
} else {
var error = new NetConnectNotAllowedError(options.host, options.path);
return new ErroringClientRequest(error);
}
}
});
}
activate();
module.exports = add;
module.exports.removeAll = removeAll;
module.exports.removeInterceptor = removeInterceptor;
module.exports.isOn = isOn;
module.exports.activate = activate;
module.exports.isActive = isActive;
module.exports.isDone = isDone;
module.exports.pendingMocks = pendingMocks;
module.exports.enableNetConnect = enableNetConnect;
module.exports.disableNetConnect = disableNetConnect;
module.exports.overrideClientRequest = overrideClientRequest;
module.exports.restoreOverriddenClientRequest = restoreOverriddenClientRequest;
}).call(this,require('_process'))
},{"./common":3,"./request_overrider":9,"_process":25,"debug":91,"events":19,"http":44,"lodash":97,"timers":50,"url":51,"util":54}],6:[function(require,module,exports){
(function (Buffer){
'use strict';
var deepEqual = require('deep-equal');
var qs = require('querystring');
module.exports =
function matchBody(spec, body) {
if (typeof spec === 'undefined') {
return true;
}
var options = this || {};
if (Buffer.isBuffer(body)) {
body = body.toString();
}
//strip line endings from both so that we get a match no matter what OS we are running on
body = body.replace(/\r?\n|\r/g, '');
if (spec instanceof RegExp) {
return body.match(spec);
}
if (typeof spec === "string") {
spec = spec.replace(/\r?\n|\r/g, '');
}
// try to transform body to json
var json;
if (typeof spec === 'object' || typeof spec === 'function') {
try { json = JSON.parse(body);} catch(err) {}
if (json !== undefined) {
body = json;
}
else
if (options.headers) {
var contentType = options.headers['Content-Type'] ||
options.headers['content-type'];
if (contentType && contentType.match(/application\/x-www-form-urlencoded/)) {
body = qs.parse(body);
}
}
}
if (typeof spec === "function") {
return spec.call(this, body);
}
return deepEqual(spec, body, { strict: true });
};
}).call(this,{"isBuffer":require("../node_modules/browserify/node_modules/insert-module-globals/node_modules/is-buffer/index.js")})
},{"../node_modules/browserify/node_modules/insert-module-globals/node_modules/is-buffer/index.js":22,"deep-equal":94,"querystring":29}],7:[function(require,module,exports){
'use strict';
var _ = require("lodash");
function mixin(a, b) {
if (! a) { a = {}; }
if (! b) {b = {}; }
a = _.cloneDeep(a);
for(var prop in b) {
a[prop] = b[prop];
}
return a;
}
module.exports = mixin;
},{"lodash":97}],8:[function(require,module,exports){
(function (Buffer){
'use strict';
var inspect = require('util').inspect;
var parse = require('url').parse;
var common = require('./common');
var intercept = require('./intercept');
var debug = require('debug')('nock.recorder');
var _ = require('lodash');
var Stream = require('stream');
var URL = require('url');
var SEPARATOR = '\n<<<<<<-- cut here -->>>>>>\n';
var recordingInProgress = false;
var outputs = [];
function getScope(options) {
common.normalizeRequestOptions(options);
var scope = [];
if (options._https_) {
scope.push('https://');
} else {
scope.push('http://');
}
scope.push(options.host);
// If a non-standard port wasn't specified in options.host, include it from options.port.
if(options.host.indexOf(':') === -1 &&
options.port &&
((options._https_ && options.port.toString() !== '443') ||
(!options._https_ && options.port.toString() !== '80'))) {
scope.push(':');
scope.push(options.port);
}
return scope.join('');
}
function getMethod(options) {
return (options.method || 'GET');
}
var getBodyFromChunks = function(chunks, headers) {
// If we have headers and there is content-encoding it means that
// the body shouldn't be merged but instead persisted as an array
// of hex strings so that the responses can be mocked one by one.
if(common.isContentEncoded(headers)) {
return _.map(chunks, function(chunk) {
if(!Buffer.isBuffer(chunk)) {
if (typeof chunk === 'string') {
chunk = new Buffer(chunk);
} else {
throw new Error('content-encoded responses must all be binary buffers');
}
}
return chunk.toString('hex');
});
}
var mergedBuffer = common.mergeChunks(chunks);
// The merged buffer can be one of three things:
// 1. A binary buffer which then has to be recorded as a hex string.
// 2. A string buffer which represents a JSON object.
// 3. A string buffer which doesn't represent a JSON object.
if(common.isBinaryBuffer(mergedBuffer)) {
return mergedBuffer.toString('hex');
} else {
var maybeStringifiedJson = mergedBuffer.toString('utf8');
try {
return JSON.parse(maybeStringifiedJson);
} catch(err) {
return maybeStringifiedJson;
}
}
};
function generateRequestAndResponseObject(req, bodyChunks, options, res, dataChunks) {
options.path = req.path;
return {
scope: getScope(options),
method: getMethod(options),
path: options.path,
body: getBodyFromChunks(bodyChunks),
status: res.statusCode,
response: getBodyFromChunks(dataChunks, res.headers),
headers: res.headers,
reqheaders: req._headers
};
}
function generateRequestAndResponse(req, bodyChunks, options, res, dataChunks) {
var requestBody = getBodyFromChunks(bodyChunks);
var responseBody = getBodyFromChunks(dataChunks, res.headers);
// Remove any query params from options.path so they can be added in the query() function
var path = options.path;
var queryIndex = 0;
var queryObj = {};
if ((queryIndex = req.path.indexOf('?')) !== -1) {
path = path.substring(0, queryIndex);
// Create the query() object
var queries = req.path.slice(queryIndex + 1).split('&');
for (var i = 0; i < queries.length; i++) {
var query = queries[i].split('=');
queryObj[query[0]] = query[1];
}
}
var ret = [];
ret.push('\nnock(\'');
ret.push(getScope(options));
ret.push('\')\n');
ret.push(' .');
ret.push(getMethod(options).toLowerCase());
ret.push('(\'');
ret.push(path);
ret.push("'");
if (requestBody) {
ret.push(', ');
ret.push(JSON.stringify(requestBody));
}
ret.push(")\n");
if (req.headers) {
for (var k in req.headers) {
ret.push(' .matchHeader(' + JSON.stringify(k) + ', ' + JSON.stringify(req.headers[k]) + ')\n');
}
}
if (queryIndex !== -1) {
ret.push(' .query(');
ret.push(JSON.stringify(queryObj));
ret.push(')\n');
}
ret.push(' .reply(');
ret.push(res.statusCode.toString());
ret.push(', ');
ret.push(JSON.stringify(responseBody));
if (res.headers) {
ret.push(', ');
ret.push(inspect(res.headers));
}
ret.push(');\n');
return ret.join('');
}
// This module variable is used to identify a unique recording ID in order to skip
// spurious requests that sometimes happen. This problem has been, so far,
// exclusively detected in nock's unit testing where 'checks if callback is specified'
// interferes with other tests as its t.end() is invoked without waiting for request
// to finish (which is the point of the test).
var currentRecordingId = 0;
function record(rec_options) {
// Set the new current recording ID and capture its value in this instance of record().
currentRecordingId = currentRecordingId + 1;
var thisRecordingId = currentRecordingId;
debug('start recording', thisRecordingId, JSON.stringify(rec_options));
// Trying to start recording with recording already in progress implies an error
// in the recording configuration (double recording makes no sense and used to lead
// to duplicates in output)
if(recordingInProgress) {
throw new Error('Nock recording already in progress');
}
recordingInProgress = true;
// Originaly the parameters was a dont_print boolean flag.
// To keep the existing code compatible we take that case into account.
var optionsIsObject = typeof rec_options === 'object';
var dont_print = (typeof rec_options === 'boolean' && rec_options) ||
(optionsIsObject && rec_options.dont_print);
var output_objects = optionsIsObject && rec_options.output_objects;
var enable_reqheaders_recording = optionsIsObject && rec_options.enable_reqheaders_recording;
var logging = (optionsIsObject && rec_options.logging) || console.log;
var use_separator = true;
if (optionsIsObject && _.has(rec_options, 'use_separator')) {
use_separator = rec_options.use_separator;
}
debug(thisRecordingId, 'restoring overridden requests before new overrides');
// To preserve backward compatibility (starting recording wasn't throwing if nock was already active)
// we restore any requests that may have been overridden by other parts of nock (e.g. intercept)
// NOTE: This is hacky as hell but it keeps the backward compatibility *and* allows correct
// behavior in the face of other modules also overriding ClientRequest.
common.restoreOverriddenRequests();
// We restore ClientRequest as it messes with recording of modules that also override ClientRequest (e.g. xhr2)
intercept.restoreOverriddenClientRequest();
// We override the requests so that we can save information on them before executing.
common.overrideRequests(function(proto, overriddenRequest, options, callback) {
var bodyChunks = [];
if (typeof options == 'string') {
var url = URL.parse(options);
options = {
hostname: url.hostname,
method: 'GET',
port: url.port,
path: url.path
};
}
// Node 0.11 https.request calls http.request -- don't want to record things
// twice.
if (options._recording) {
return overriddenRequest(options, callback);
}
options._recording = true;
var req = overriddenRequest(options, function(res) {
debug(thisRecordingId, 'intercepting', proto, 'request to record');
if (typeof options === 'string') {
options = parse(options);
}
// We put our 'end' listener to the front of the listener array.
res.once('end', function() {
debug(thisRecordingId, proto, 'intercepted request ended');
var out;
if(output_objects) {
out = generateRequestAndResponseObject(req, bodyChunks, options, res, dataChunks);
if(out.reqheaders) {
// We never record user-agent headers as they are worse than useless -
// they actually make testing more difficult without providing any benefit (see README)
common.deleteHeadersField(out.reqheaders, 'user-agent');
// Remove request headers completely unless it was explicitly enabled by the user (see README)
if(!enable_reqheaders_recording) {
delete out.reqheaders;
}
}
} else {
out = generateRequestAndResponse(req, bodyChunks, options, res, dataChunks);
}
debug('out:', out);
// Check that the request was made during the current recording.
// If it hasn't then skip it. There is no other simple way to handle
// this as it depends on the timing of requests and responses. Throwing
// will make some recordings/unit tests faily randomly depending on how
// fast/slow the response arrived.
// If you are seeing this error then you need to make sure that all
// the requests made during a single recording session finish before
// ending the same recording session.
if(thisRecordingId !== currentRecordingId) {
debug('skipping recording of an out-of-order request', out);
return;
}
outputs.push(out);
if (!dont_print) {
if (use_separator) {
logging(SEPARATOR + out + SEPARATOR);
} else {
logging(out);
}
}
});
var dataChunks = [];
var encoding;
// We need to be aware of changes to the stream's encoding so that we
// don't accidentally mangle the data.
var setEncoding = res.setEncoding;
res.setEncoding = function (newEncoding) {
encoding = newEncoding;
return setEncoding.apply(this, arguments);
};
// Give the actual client a chance to setup its listeners.
// We will use the listener information to figure out
// how we need to feed the intercepted data back to the client.
if (callback) {
callback(res, options, callback);
}
// Handle clients that listen to 'readable' by intercepting them
// and feeding them the data manually.
var readableListeners = res.listeners('readable');
if (!_.isEmpty(readableListeners)) {
debug('handle readable listeners');
// We will replace the client's listeners with our own and manually
// invoke them.
_.each(readableListeners, function(listener) {
res.removeListener('readable', listener);
});
// Repleace the actual Stream.Readable prototype 'read' function
// so that we can control what the client listener will be reading.
var prototypeRead = Stream.Readable.prototype.read;
var currentReadIndex = 0;
res.read = function() {
debug(thisRecordingId, 'client reading data on', proto, dataChunks.length);
// Feed the data to the client through from our collected data chunks.
if (currentReadIndex < dataChunks.length) {
debug('chunk', chunk, 'read');
var chunk = dataChunks[currentReadIndex];
++currentReadIndex;
return chunk;
} else {
debug('no more chunks to read');
return null;
}
};
// Put our own listener instead of the removed client listener.
var onReadable = function(data) {
debug(thisRecordingId, 'new readable data on', proto);
var chunk;
// Use the prototypeRead function to actually read the data.
while (null !== (chunk = prototypeRead.call(res))) {
debug('read', chunk);
dataChunks.push(chunk);
}
// Manually invoke the user listeners emulating 'readable' event.
_.each(readableListeners, function(listener) {
listener();
});
};
res.on('readable', onReadable);
} else {
// In all other cases we (for now at least) fall back on intercepting
// 'data' events.
debug('fall back on our original implementation');
// Since we gave client the chance to setup its listeners
// before us, we need to remove them and setup our own.
var dataListeners = res.listeners('data');
_.each(dataListeners, function(listener) {
res.removeListener('data', listener);
});
var onData = function(data) {
debug(thisRecordingId, 'new data chunk on', proto);
if (encoding) {
data = new Buffer(data, encoding);
}
dataChunks.push(data);
// Manually invoke the user listeners emulating 'data' event.
_.each(dataListeners, function(listener) {
listener(data);
});
};
res.on('data', onData);
}
debug('finished setting up intercepting');
if (proto === 'https') {
options._https_ = true;
}
});
var oldWrite = req.write;
req.write = function(data, encoding) {
if ('undefined' !== typeof(data)) {
if (data) {
debug(thisRecordingId, 'new', proto, 'body chunk');
if (! Buffer.isBuffer(data)) {
data = new Buffer(data, encoding);
}
bodyChunks.push(data);
}
oldWrite.call(req, data);
}
};
return req;
});
}
// Restores *all* the overridden http/https modules' properties.
function restore() {
debug(currentRecordingId, 'restoring all the overridden http/https properties');
common.restoreOverriddenRequests();
intercept.restoreOverriddenClientRequest();
recordingInProgress = false;
}
function clear() {
outputs = [];
}
exports.record = record;
exports.outputs = function() {
return outputs;
};
exports.restore = restore;
exports.clear = clear;
}).call(this,require("buffer").Buffer)
},{"./common":3,"./intercept":5,"buffer":15,"debug":91,"lodash":97,"stream":43,"url":51,"util":54}],9:[function(require,module,exports){
(function (process,Buffer){
'use strict';
var EventEmitter = require('events').EventEmitter,
http = require('http'),
propagate = require('propagate'),
DelayedBody = require('./delayed_body'),
IncomingMessage = http.IncomingMessage,
ClientRequest = http.ClientRequest,
common = require('./common'),
Socket = require('./socket'),
_ = require('lodash'),
debug = require('debug')('nock.request_overrider'),
timers = require('timers'),
ReadableStream = require('stream').Readable;
function getHeader(request, name) {
if (!request._headers) {
return;
}
var key = name.toLowerCase();
return request._headers[key];
}
function setHeader(request, name, value) {
debug('setHeader', name, value);
var key = name.toLowerCase();
request._headers = request._headers || {};
request._headerNames = request._headerNames || {};
request._removedHeader = request._removedHeader || {};
request._headers[key] = value;
request._headerNames[key] = name;
if (name == 'expect' && value == '100-continue') {
timers.setImmediate(function() {
debug('continue');
request.emit('continue');
});
}
}
function isStream(obj) {
var is = obj && (typeof obj !== 'string') && (!Buffer.isBuffer(obj)) && (typeof obj.setEncoding === 'function');
return is;
}
// Sets request headers of the given request. This is needed during both matching phase
// (in case header filters were specified) and mocking phase (to correctly pass mocked
// request headers).
function setRequestHeaders(req, options, interceptor) {
// We mock request headers if these were specified.
if (interceptor.reqheaders) {
var reqheaders = interceptor.reqheaders;
_(interceptor.reqheaders).keys().each(function(key) {
setHeader(req, key, reqheaders[key]);
});
}
// If a filtered scope is being used we have to use scope's host
// in the header, otherwise 'host' header won't match.
// NOTE: We use lower-case header field names throught Nock.
var HOST_HEADER = 'host';
if(interceptor.__nock_filteredScope && interceptor.__nock_scopeHost) {
if(options && options.headers) {
options.headers[HOST_HEADER] = interceptor.__nock_scopeHost;
}
setHeader(req, HOST_HEADER, interceptor.__nock_scopeHost);
} else {
// For all other cases, we always add host header equal to the
// requested host unless it was already defined.
if (options.host && !getHeader(req, HOST_HEADER)) {
var hostHeader = options.host;
if (options.port === 80 || options.port === 443) {
hostHeader = hostHeader.split(':')[0];
}
setHeader(req, HOST_HEADER, hostHeader);
}
}
}
function RequestOverrider(req, options, interceptors, remove, cb) {
var response;
if (IncomingMessage) {
response = new IncomingMessage(new EventEmitter());
} else {
response = new ReadableStream();
response._read = function() {};
}
var requestBodyBuffers = [],
originalInterceptors = interceptors,
aborted,
emitError,
end,
ended,
headers,
keys,
key,
i,
l;
// We may be changing the options object and we don't want those
// changes affecting the user so we use a clone of the object.
options = _.clone(options) || {};
response.req = req;
if (options.headers) {
//