run = function (options, callback) {
// validate all options. it is to be noted that `options` parameter is option and is polymorphic
(!callback && _.isFunction(options)) && (
(callback = options),
(options = {})
);
!_.isFunction(callback) && (callback = _.noop);
var emitter = new EventEmitter(), // @todo: create a new inherited constructor
runner = new runtime.Runner();
// get the configuration from various sources
getOptions(options, function (err, options) {
if (err) {
return callback(err);
}
// ensure that the collection option is present before starting a run
if (!_.isObject(options.collection)) {
return callback(new Error('newman: expecting a collection to run'));
}
// store summary object and other relevant information inside the emitter
emitter.summary = new RunSummary(emitter, options);
// to store the exported content from reporters
emitter.exports = [];
// now start the run!
runner.run(options.collection, {
stopOnFailure: options.bail, // LOL, you just got trolled ¯\_(ツ)_/¯
abortOnFailure: options.abortOnFailure, // used in integration tests, to be considered for a future release
iterationCount: options.iterationCount,
environment: options.environment,
globals: options.globals,
entrypoint: options.folder,
data: options.iterationData,
delay: {
item: options.delayRequest
},
// todo: add support for more types of timeouts, currently only request is supported
timeout: options.timeoutRequest ? { request: options.timeoutRequest } : undefined,
fileResolver: fs,
requester: {
cookieJar: request.jar(),
followRedirects: _.has(options, 'ignoreRedirects') ? !options.ignoreRedirects : undefined,
strictSSL: _.has(options, 'insecure') ? !options.insecure : undefined
},
certificates: options.sslClientCert && new sdk.CertificateList({}, [{
name: 'client-cert',
matches: [sdk.UrlMatchPattern.MATCH_ALL_URLS],
key: { src: options.sslClientKey },
cert: { src: options.sslClientCert },
passphrase: options.sslClientPassphrase
}])
}, function (err, run) {
var callbacks = {},
// ensure that the reporter option type polymorphism is handled
reporters = _.isString(options.reporters) ? [options.reporters] : options.reporters;
// emit events for all the callbacks triggered by the runtime
_.forEach(runtimeEvents, function (definition, eventName) {
// intercept each runtime.* callback and expose a global object based event
callbacks[eventName] = function (err, cursor) {
var args = arguments,
obj = { cursor: cursor };
// convert the arguments into an object by taking the key name reference from the definition
// object
_.forEach(definition, function (key, index) {
obj[key] = args[index + 2]; // first two are err, cursor
});
args = [eventName, err, obj];
emitter.emit.apply(emitter, args); // eslint-disable-line prefer-spread
};
});
// add non generic callback handling
_.assignIn(callbacks, {
/**
* Bubbles up console messages.
*
* @param {Object} cursor - The run cursor instance.
* @param {String} level - The level of console logging [error, silent, etc].
* @returns {*}
*/
console: function (cursor, level) {
emitter.emit('console', null, { ...
...
Newman can be easily used within your JavaScript projects as a NodeJS module. The entire set of Newman CLI functionality is available
for programmatic use as well. The following example runs a collection by reading a JSON collection file stored on disk.
```javascript
var newman = require('newman'); // require newman in your project
// call newman.run to pass `options` object and wait for callback
newman.run({
collection: require('./sample-collection.json'),
reporters: 'cli'
}, function (err) {
if (err) { throw err; }
console.log('collection run complete!');
});
```
...
function RunSummary(emitter, options) {
// keep a copy of this instance since, we need to refer to this from various events
var summary = this;
// and store the trackers and failures in the summary object itself
_.assign(summary, /** @lends RunSummary.prototype */ {
/**
* The collection that is being executed.
*
* @type {Collection}
*/
collection: _.get(options, 'collection'),
/**
* The environment that is being used during the run
* @type {VariableScope}
*
*/
environment: _.get(options, 'environment'),
/**
* Global variables being used during the run
* @type {VariableScope}
*/
globals: _.get(options, 'globals'),
/**
* Holds information related to the run.
*/
run: {
/**
* Holds the statistics of the run. Each property in it is the item being tracked and has three numeric
* properties - total, failed, pending
*
* @type {Object.<Object>}
*/
stats: {
iterations: {},
items: {},
scripts: {},
prerequests: {},
requests: {},
tests: {},
assertions: {},
testScripts: {},
prerequestScripts: {}
},
/**
* Stores all generic timing information
*
* @type {Object}
*/
timings: {
/**
* The average response time of the run
*
* @type {number}
*/
responseAverage: 0
},
/**
* Stores detailed information about the order of execution, request, response and assertions
*
* @type {Array<Object>}
*/
executions: [],
/**
* Stores information on data transfer made during the collection
*
* @type {Object}
*/
transfers: {
/**
* The total data received as response to every request
*
* @type {number}
*/
responseTotal: 0
},
/**
* An array of all errors encountered during the run
*
* @type {Array.<Error>}
*/
failures: [],
/**
* This stores any fatal error during the run that caused the run to abort prematurely.
*
* @type {Error}
*/
error: null
}
});
// track run timings (start and end)
RunSummary.attachTimingTrackers(this, emitter);
// accumulate statistics on all event
// for all types of events track the counters for the event and its corresponding "before" counterpart
RunSummary.attachStatisticTrackers(this, emitter);
// accumulate statistics on requests - such as size and time
RunSummary.attachRequestTracker(this, emitter);
// accumulate errors (failures) from all events
RunSummary.attachFailureTrackers(this, emitter);
// accumulate all execution specific data in collection
RunSummary.attachReportingTrackers(this, emitter);
}
n/a
version = function () { console.info(version); }
n/a
dimension = function () { var tty, width, height; try { tty = require('tty'); } catch (e) { tty = null; } if (tty && tty.isatty(1) && tty.isatty(2)) { if (process.stdout.getWindowSize) { width = process.stdout.getWindowSize(1)[0]; height = process.stdout.getWindowSize(1)[1]; } else if (tty.getWindowSize) { width = tty.getWindowSize()[1]; height = tty.getWindowSize()[0]; } else if (process.stdout.columns && process.stdout.rows) { height = process.stdout.rows; width = process.stdout.columns; } } return { exists: !(Boolean(process.env.CI) || !process.stdout.isTTY), width: width, height: height }; }
...
/**
* A CLI utility helper method that generates a color inspector function for CLI reports.
*
* @param {Object} runOptions - The set of run options acquired via the runner.
* @return {Function} - A function to perform utils.inspect, given a sample item, under pre-existing options.
*/
inspector: function (runOptions) {
var dimension = cliUtils.dimension(),
options = {
colors: !(runOptions.noColor || cliUtils.noTTY(runOptions.color)),
// note that similar dimension calculation is in utils.wrapper
breakLength: ((dimension.exists && (dimension.width > 20)) ? dimension.width : 60) - 16
};
return function (item) {
...
inspector = function (runOptions) { var dimension = cliUtils.dimension(), options = { colors: !(runOptions.noColor || cliUtils.noTTY(runOptions.color)), // note that similar dimension calculation is in utils.wrapper breakLength: ((dimension.exists && (dimension.width > 20)) ? dimension.width : 60) - 16 }; return function (item) { return inspect(item, options); }; }
n/a
noTTY = function (color) { return !color && (Boolean(process.env.CI) || !process.stdout.isTTY); }
...
*
* @param {Object} runOptions - The set of run options acquired via the runner.
* @return {Function} - A function to perform utils.inspect, given a sample item, under pre-existing options.
*/
inspector: function (runOptions) {
var dimension = cliUtils.dimension(),
options = {
colors: !(runOptions.noColor || cliUtils.noTTY(runOptions.color)),
// note that similar dimension calculation is in utils.wrapper
breakLength: ((dimension.exists && (dimension.width > 20)) ? dimension.width : 60) - 16
};
return function (item) {
return inspect(item, options);
};
...
padLeft = function (nr, n, str) { return Array(n - String(nr).length + 1).join(str || '0') + nr; }
n/a
symbols = function (disableUnicode) { if (disableUnicode) { return subsets.plainText; } if (IS_WINDOWS) { // modify symbols for windows platforms return subsets.encoded; } return subsets.regular; }
n/a
wrapper = function () { var dimension = cliUtils.dimension(), // note that similar dimension calculation is in utils.wrapper width = ((dimension.exists && (dimension.width > 20)) ? dimension.width : 60) - 6; return function (text, indent) { return wrap(text, { indent: indent, width: width, cut: true }); }; }
n/a
(callback) => { var iswin = (/^win/).test(process.platform), home = iswin ? process.env.USERPROFILE : process.env.HOME, configFiles = []; configFiles.push(join(process.cwd(), '.' + FILE_NAME)); home && configFiles.push(join(home, '.' + POSTMAN_CONFIG_DIR, FILE_NAME)); !iswin && configFiles.push(join('/etc', POSTMAN_CONFIG_DIR, FILE_NAME)); async.mapSeries(configFiles, (path, cb) => { fs.readFile(path, (err, data) => { if (err) { return cb(null, {}); // err masked to avoid overpopulating terminal with missing .newmanrc messages } data && data.toString && (data = data.toString()); try { return cb(null, parseJson(data)); } catch (e) { return cb(_.set(e, 'help', `The file at ${path} contains invalid data.`)); } }); }, (err, files) => { if (err) { return callback(err); } return callback(null, _.merge.apply(this, files)); }); }
n/a
function RunSummary(emitter, options) {
// keep a copy of this instance since, we need to refer to this from various events
var summary = this;
// and store the trackers and failures in the summary object itself
_.assign(summary, /** @lends RunSummary.prototype */ {
/**
* The collection that is being executed.
*
* @type {Collection}
*/
collection: _.get(options, 'collection'),
/**
* The environment that is being used during the run
* @type {VariableScope}
*
*/
environment: _.get(options, 'environment'),
/**
* Global variables being used during the run
* @type {VariableScope}
*/
globals: _.get(options, 'globals'),
/**
* Holds information related to the run.
*/
run: {
/**
* Holds the statistics of the run. Each property in it is the item being tracked and has three numeric
* properties - total, failed, pending
*
* @type {Object.<Object>}
*/
stats: {
iterations: {},
items: {},
scripts: {},
prerequests: {},
requests: {},
tests: {},
assertions: {},
testScripts: {},
prerequestScripts: {}
},
/**
* Stores all generic timing information
*
* @type {Object}
*/
timings: {
/**
* The average response time of the run
*
* @type {number}
*/
responseAverage: 0
},
/**
* Stores detailed information about the order of execution, request, response and assertions
*
* @type {Array<Object>}
*/
executions: [],
/**
* Stores information on data transfer made during the collection
*
* @type {Object}
*/
transfers: {
/**
* The total data received as response to every request
*
* @type {number}
*/
responseTotal: 0
},
/**
* An array of all errors encountered during the run
*
* @type {Array.<Error>}
*/
failures: [],
/**
* This stores any fatal error during the run that caused the run to abort prematurely.
*
* @type {Error}
*/
error: null
}
});
// track run timings (start and end)
RunSummary.attachTimingTrackers(this, emitter);
// accumulate statistics on all event
// for all types of events track the counters for the event and its corresponding "before" counterpart
RunSummary.attachStatisticTrackers(this, emitter);
// accumulate statistics on requests - such as size and time
RunSummary.attachRequestTracker(this, emitter);
// accumulate errors (failures) from all events
RunSummary.attachFailureTrackers(this, emitter);
// accumulate all execution specific data in collection
RunSummary.attachReportingTrackers(this, emitter);
}
n/a
attachFailureTrackers = function (summary, emitter) { var eventsToTrack = ['beforeIteration', 'iteration', 'beforeItem', 'item', 'beforeScript', 'script', 'beforePrerequest', 'prerequest', 'beforeRequest', 'request', 'beforeTest', 'test', 'beforeAssertion', 'assertion']; // accumulate failures of all events // NOTE that surrogate events (which throw duplicate arguments) are not recorded _.forEach(eventsToTrack, function (event) { // push failures sent from "before" events emitter.on(event, function (err, o) { if (!err) { return; } var item = o && o.item, source = event; // in case of user script error, point to the line and column of the script and its type if (event === 'script') { o.event && (source = o.event.listen + '-script'); if (err.stacktrace && err.stacktrace[0] && err.stacktrace[0].lineNumber) { source += (':' + (err.stacktrace[0].lineNumber - 2)); err.stacktrace[0].columnNumber && (source += (':' + err.stacktrace[0].columnNumber)); } } // assertion errors need to know which assertion in the test was this else if (event === 'assertion') { _.has(err, 'index') && (source += (':' + (err.index + 1))); source += ' in test-script'; } // if this is a plain error, convert it to serialised error if (err.stack && !err.stacktrace) { err = new SerialiseError(err, true); } summary.run.failures.push({ error: err, at: source, source: item || undefined, parent: item && item.__parent && item.__parent.__parent || undefined, cursor: o.cursor || {} }); }); }); }
...
// for all types of events track the counters for the event and its corresponding "before" counterpart
RunSummary.attachStatisticTrackers(this, emitter);
// accumulate statistics on requests - such as size and time
RunSummary.attachRequestTracker(this, emitter);
// accumulate errors (failures) from all events
RunSummary.attachFailureTrackers(this, emitter);
// accumulate all execution specific data in collection
RunSummary.attachReportingTrackers(this, emitter);
};
_.assign(RunSummary, {
attachReportingTrackers: function (summary, emitter) {
...
attachReportingTrackers = function (summary, emitter) { var cache = {}, executions = summary.run.executions; emitter.on('beforeItem', function (err, o) { if (!_.get(o, 'cursor.ref')) { return; } cache[o.cursor.ref] = _.assignIn(cache[o.cursor.ref] || {}, { cursor: o.cursor, item: o.item }); }); // save all responses in executions array emitter.on('request', function (err, o) { if (!_.get(o, 'cursor.ref')) { return; } var execution = cache[o.cursor.ref] = (cache[o.cursor.ref] || {}); executions.push(_.assignIn(execution, { cursor: o.cursor, request: o.request, response: _.omit(o.response, 'stream'), id: _.get(o, 'item.id') }, err && { requestError: err || undefined })); }); // save all script execution errors in each execution emitter.on('script', function (err, o) { if (!_.get(o, 'cursor.ref')) { return; } var execution = cache[o.cursor.ref] = (cache[o.cursor.ref] || {}), eventName = o && o.event && (o.event.listen + 'Script'); // store the script error corresponding to the script event name err && (execution && eventName) && (execution[eventName] || (execution[eventName] = [])).push({ error: err }); }); // save all assertions in each execution emitter.on('assertion', function (err, o) { if (!_.get(o, 'cursor.ref')) { return; } var execution = cache[o.cursor.ref] = (cache[o.cursor.ref] || {}); if (!execution) { return; } (execution.assertions || (execution.assertions = [])).push({ assertion: o.assertion, error: err || undefined }); }); }
...
// accumulate statistics on requests - such as size and time
RunSummary.attachRequestTracker(this, emitter);
// accumulate errors (failures) from all events
RunSummary.attachFailureTrackers(this, emitter);
// accumulate all execution specific data in collection
RunSummary.attachReportingTrackers(this, emitter);
};
_.assign(RunSummary, {
attachReportingTrackers: function (summary, emitter) {
var cache = {},
executions = summary.run.executions;
...
attachRequestTracker = function (summary, emitter) { // accumulate statistics on requests emitter.on('request', function (err, o) { if (err || !(o && o.response)) { return; } var size = _.isFunction(o.response.size) && o.response.size(), time = o.response.responseTime, requestCount = summary.run.stats.requests.total; // compute the response size total size && (summary.run.transfers.responseTotal += (size.body || 0 + size.headers || 0)); // compute average response time time && (summary.run.timings.responseAverage = ((summary.run.timings.responseAverage * (requestCount - 1) + time) / requestCount)); }); }
...
RunSummary.attachTimingTrackers(this, emitter);
// accumulate statistics on all event
// for all types of events track the counters for the event and its corresponding "before" counterpart
RunSummary.attachStatisticTrackers(this, emitter);
// accumulate statistics on requests - such as size and time
RunSummary.attachRequestTracker(this, emitter);
// accumulate errors (failures) from all events
RunSummary.attachFailureTrackers(this, emitter);
// accumulate all execution specific data in collection
RunSummary.attachReportingTrackers(this, emitter);
};
...
attachStatisticTrackers = function (summary, emitter) { // accumulate statistics on all event // for all types of events track the counters for the event and its corresponding "before" counterpart _.forEach(summary.run.stats, function (tracker, name) { // the actual event names are singular than their plural trackers, so we make the name singular name = name.slice(0, -1); // remove last character // populate initial values of trackers _.assign(tracker, { total: 0, pending: 0, failed: 0 }); // Set up common listeners for a set of events, which tracks how many times they were executed and records // the ones which had an error passed as first argument emitter.on(_.camelCase('before-' + name), function () { tracker.pending += 1; }); emitter.on(name, function (err) { // check pending so that, it does not negate for items that do not have a `before` counterpart tracker.pending && (tracker.pending -= 1); err && (tracker.failed += 1); tracker.total += 1; }); }); }
...
});
// track run timings (start and end)
RunSummary.attachTimingTrackers(this, emitter);
// accumulate statistics on all event
// for all types of events track the counters for the event and its corresponding "before" counterpart
RunSummary.attachStatisticTrackers(this, emitter);
// accumulate statistics on requests - such as size and time
RunSummary.attachRequestTracker(this, emitter);
// accumulate errors (failures) from all events
RunSummary.attachFailureTrackers(this, emitter);
...
attachTimingTrackers = function (summary, emitter) { // mark the point when the run started // also mark the point when run completed and also store error if needed emitter.on('start', function () { summary.run.timings.started = Date.now(); }); emitter.on('beforeDone', function () { summary.run.timings.completed = Date.now(); }); emitter.on('done', function (err) { err && (summary.error = err); }); }
...
* @type {Error}
*/
error: null
}
});
// track run timings (start and end)
RunSummary.attachTimingTrackers(this, emitter);
// accumulate statistics on all event
// for all types of events track the counters for the event and its corresponding "before" counterpart
RunSummary.attachStatisticTrackers(this, emitter);
// accumulate statistics on requests - such as size and time
RunSummary.attachRequestTracker(this, emitter);
...
fetch = function (location, options, callback) { !callback && _.isFunction(options) && (callback = options, options = {}); return (/^https?:\/\/.*/).test(location) ? // Load from URL request.get({ url: location }, (err, response, body) => { if (err) { return callback(err); } return callback(null, body); }) : fs.readFile(location, function (err, value) { if (err) { return callback(err); } return callback(null, value.toString()); }); }
...
* the array of iteration data objects.
* @param {Function} callback - The function invoked to indicate the end of the iteration data loading routine.
* @returns {*}
*/
iterationData: function (location, callback) {
if (_.isArray(location)) { return callback(null, location); }
util.fetch(location, function (err, data) {
if (err) {
return callback(err);
}
// Try loading as a JSON, fallback to CSV. @todo: switch to file extension based loading.
async.waterfall([
(cb) => {
...
fetchJson = function (location, options, callback) { !callback && _.isFunction(options) && (callback = options, options = {}); return (/^https?:\/\/.*/).test(location) ? // Load from URL request.get({ url: location, json: true, headers: { 'User-Agent': USER_AGENT_VALUE } }, (err, response, body) => { if (err) { return callback(_.set(err, 'help', `unable to fetch data from url "${location}"`)); } try { _.isString(body) && (body = parseJson(body)); } catch (e) { return callback(_.set(e, 'help', `the url "${location}" did not provide valid JSON data`)); } return callback(null, body); }) : fs.readFile(location, function (err, value) { if (err) { return callback(_.set(err, 'help', `unable to read data from file "${location}"`)); } try { value = parseJson(value.toString()); } catch (e) { return callback(_.set(e, 'help', `the file at ${location} does not contain valid JSON data`)); } return callback(null, value); }); }
...
*
* @param {String} type - The type of data to load.
* @param {String} location - The location to load from (file path or URL).
* @param {function} cb - The callback function whose invocation marks the end of the external load routine.
* @returns {*}
*/
externalLoader = function (type, location, cb) {
return _.isString(location) ? util.fetchJson(location, function (err, data) {
if (err) {
return cb(err);
}
return cb(null, extractModel(data, type));
}) : cb(null, extractModel(location, type));
},
...
filesize = function (bytes) { return filesize(bytes || 0, FILESIZE_OPTIONS); }
n/a
getFullName = function (item, separator) { if (_.isEmpty(item) || !_.isFunction(item.parent) || !_.isFunction(item.forEachParent)) { return; } var chain = []; item.forEachParent(function (parent) { chain.unshift(parent.name || parent.id); }); item.parent() && chain.push(item.name); // Add the current item only if it is not the collection instance return chain.join(_.isString(separator) ? separator : SEP); }
n/a
prettyms = function (ms) { return (ms < 1998) ? `${parseInt(ms, 10)}ms` : prettyms(ms || 0); }
n/a