/* eslint-disable max-classes-per-file */ const _ = require('lodash'), scopeLibraries = { JSON: require('liquid-json'), _: require('lodash3').noConflict(), CryptoJS: require('crypto-js'), atob: require('atob'), btoa: require('btoa'), tv4: require('tv4'), xml2Json: require('./xml2Json'), Backbone: require('backbone'), cheerio: require('cheerio') }, LEGACY_GLOBS = [ 'tests', 'globals', 'environment', 'data', 'request', 'responseCookies', 'responseHeaders', 'responseTime', 'responseCode', 'responseBody', 'iteration', 'postman', // scope libraries 'JSON', '_', 'CryptoJS', 'atob', 'btoa', 'tv4', 'xml2Json', 'Backbone', 'cheerio' ], E = '', FUNCTION = 'function', TARGET_TEST = 'test', LEGACY_ASSERTION_ERROR_MESSAGE_PREFIX = 'expected ', LEGACY_ASSERTION_ERROR_MESSAGE_SUFFIX = ' to be truthy', /** * Different modes for a request body. * * @enum {String} */ REQUEST_MODES = { RAW: 'raw', URLENCODED: 'urlencoded', FORMDATA: 'formdata', FILE: 'file' }; function getRequestBody (request) { var mode = _.get(request, 'body.mode'), body = _.get(request, 'body'), empty = body ? body.isEmpty() : true, content, computedBody; if (empty) { return; } content = body[mode]; if (_.isFunction(content && content.all)) { content = content.all(); } if (mode === REQUEST_MODES.RAW) { computedBody = { body: content }; } else if (mode === REQUEST_MODES.URLENCODED) { computedBody = { form: _.reduce(content, function (accumulator, param) { if (param.disabled) { return accumulator; } // This is actually pretty simple, // If the variable already exists in the accumulator, we need to make the value an Array with // all the variable values inside it. if (accumulator[param.key]) { _.isArray(accumulator[param.key]) ? accumulator[param.key].push(param.value) : (accumulator[param.key] = [accumulator[param.key], param.value]); } else { accumulator[param.key] = param.value; } return accumulator; }, {}) }; } else if (request.body.mode === REQUEST_MODES.FORMDATA) { computedBody = { formData: _.reduce(content, function (accumulator, param) { if (param.disabled) { return accumulator; } // This is actually pretty simple, // If the variable already exists in the accumulator, we need to make the value an Array with // all the variable values inside it. if (accumulator[param.key]) { _.isArray(accumulator[param.key]) ? accumulator[param.key].push(param.value) : (accumulator[param.key] = [accumulator[param.key], param.value]); } else { accumulator[param.key] = param.value; } return accumulator; }, {}) }; } else if (request.body.mode === REQUEST_MODES.FILE) { computedBody = { body: _.get(request, 'body.file.content') }; } return computedBody; } /** * Raises a single assertion event with an array of assertions from legacy `tests` object. * * @param {Uniscope} scope - * @param {Object} pmapi - * @param {Function} onAssertion - */ function raiseAssertionEvent (scope, pmapi, onAssertion) { var tests = scope._imports && scope._imports.tests, assertionIndex = pmapi.test.index(), assertions; if (_.isEmpty(tests)) { return; } assertions = _.map(tests, function (value, key) { var assertionName = String(key), passed = Boolean(value), assertionError = null; // fake an assertion error for legacy tests if (!passed) { assertionError = new Error(LEGACY_ASSERTION_ERROR_MESSAGE_PREFIX + String(value) + LEGACY_ASSERTION_ERROR_MESSAGE_SUFFIX); assertionError.name = 'AssertionError'; } // @todo Move getAssertionObject function from pmapi-setup-runner.js to a common place and reuse it here too return { name: assertionName, skipped: false, passed: passed, error: assertionError, index: assertionIndex++ }; }); onAssertion(assertions); } class PostmanLegacyInterface { /** * @param {Object} execution - * @param {Object} globalvars - */ constructor (execution, globalvars) { this.__execution = execution; this.__environment = globalvars.environment; this.__globals = globalvars.globals; } setEnvironmentVariable (key, value) { if ((value === false || value) && typeof (value && value.toString) === FUNCTION) { value = value.toString(); } this.__environment[key] = value; return this.__execution.environment.set(key, value); } getEnvironmentVariable (key) { return this.__execution.environment.get(key); } clearEnvironmentVariables () { for (var prop in this.__environment) { if (Object.hasOwnProperty.call(this.__environment, prop)) { delete this.__environment[prop]; } } return this.__execution.environment.clear(); } clearEnvironmentVariable (key) { key && (delete this.__environment[key]); return this.__execution.environment.unset(key); } setGlobalVariable (key, value) { if ((value === false || value) && typeof (value && value.toString) === FUNCTION) { value = value.toString(); } this.__globals[key] = value; return this.__execution.globals.set(key, value); } getGlobalVariable (key) { return this.__execution.globals.get(key); } clearGlobalVariables () { for (var prop in this.__globals) { if (Object.hasOwnProperty.call(this.__globals, prop)) { delete this.__globals[prop]; } } return this.__execution.globals.clear(); } clearGlobalVariable (key) { key && (delete this.__globals[key]); return this.__execution.globals.unset(key); } setNextRequest (what) { this.__execution.return && (this.__execution.return.nextRequest = what); } } /** * @constructor * @extends {PostmanLegacyInterface} */ class PostmanLegacyTestInterface extends PostmanLegacyInterface { /** * @param {String} cookieName - * @returns {Object} */ getResponseCookie (cookieName) { return this.__execution.cookies ? this.__execution.cookies.one(String(cookieName)) : undefined; } /** * @param {String} headerName - * @returns {String} */ getResponseHeader (headerName) { var header = (this.__execution.response && this.__execution.response.headers) && this.__execution.response.headers.one(headerName); return header ? header.value : undefined; } } module.exports = { /** * * @param {Uniscope} scope - * @param {Execution} execution - * * @note ensure that globalvars variables added here are added as part of the LEGACY_GLOBS array */ setup (scope, execution) { /** * @name SandboxGlobals * @type {Object} */ var globalvars = _.assign({}, scopeLibraries); // set global variables that are exposed in legacy interface // --------------------------------------------------------- // 1. set the tests object (irrespective of target) /** * Store your assertions in this object * * @memberOf SandboxGlobals * @type {Object} */ globalvars.tests = {}; // 2. set common environment, globals and data /** * All global variables at the initial stages when the script ran * * @memberOf SandboxGlobals * @type {Object} */ globalvars.globals = execution.globals.syncVariablesTo(); /** * All environment variables at the initial stages when script ran * * @memberOf SandboxGlobals * @type {Object} */ globalvars.environment = execution.environment.syncVariablesTo(); /** * The data object if it was passed during a collection run * * @memberOf SandboxGlobals * @type {Object} */ globalvars.data = execution.data || (execution.data = {}); // 3. set the request object in legacy structure /** * The request that will be sent (or has been sent) * * @memberOf SandboxGlobals * @type {Object} */ globalvars.request = execution.request ? { id: execution.legacy ? execution.legacy._itemId : undefined, name: execution.legacy ? execution.legacy._itemName : undefined, description: execution.request.description ? execution.request.description.toString() : undefined, headers: execution.request.headers.toObject(true, false, true, true), method: execution.request.method, url: execution.request.url.toString(), data: (function (request) { var body = getRequestBody(request); return body ? (body.form || body.formData || body.body || {}) : {}; }(execution.request)) } : {}; // 4. set the response related objects if (execution.target === TARGET_TEST) { /** * Stores the response cookies * * @memberOf SandboxGlobals * @type {Array.} */ globalvars.responseCookies = execution.cookies || []; /** * Stores the response headers with the keys being case sensitive * * @memberOf SandboxGlobals * @type {Array.} */ globalvars.responseHeaders = {}; execution.response && execution.response.headers.each(function (header) { header && !header.disabled && (globalvars.responseHeaders[header.key] = header.value); }); /** * @memberOf SandboxGlobals * @type {Number} */ globalvars.responseTime = execution.response ? execution.response.responseTime : NaN; /** * @memberOf SandboxGlobals * @type {Number} */ globalvars.responseCode = execution.response ? _.clone(execution.response.details()) : { code: NaN, name: E, details: E }; /** * @memberOf SandboxGlobals * @type {String} */ globalvars.responseBody = execution.response ? execution.response.text() : undefined; } // 5. add the iteration information globalvars.iteration = _.isObject(execution.cursor) ? execution.cursor.iteration : 0; // 6. create the postman interface object /** * @memberOf SandboxGlobals * @type {PostmanLegacyInterface} */ globalvars.postman = new (execution.target === TARGET_TEST ? PostmanLegacyTestInterface : PostmanLegacyInterface)(execution, globalvars); // make a final pass to ensure that the global variables are present // all the globals are now added to scope scope.import(globalvars); globalvars = null; // dereference // add a flag to ensure that when teardown is called, it does not tear down a scope that was never setup scope.__postman_legacy_setup = true; }, teardown (scope) { if (!scope.__postman_legacy_setup) { return; } for (var i = 0, ii = LEGACY_GLOBS.length; i < ii; i++) { scope.unset(LEGACY_GLOBS[i]); } scope.__postman_legacy_setup = false; }, /** * This is the place where we should put all the tasks * that need to be executed after the completion of script execution * * @param {Uniscope} scope - * @param {Object} pmapi - * @param {Function} onAssertion - */ finish (scope, pmapi, onAssertion) { if (!scope.__postman_legacy_setup) { return; } raiseAssertionEvent(scope, pmapi, onAssertion); } };