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