Home Reference Source

src/index.js

'use strict';

// Node.js core modules
var url = require('url');
var util = require('util');

// Userland modules
var JiraApi = require('jira-client');



/**
 * @constructor
 * @param {JiraApiWithXrayOptions} options
 */
function JiraApiWithXray(options) {
  if (!(this instanceof JiraApiWithXray)) {
    return new JiraApiWithXray(options);
  }

  var opts = options || {};

  // Invoke the super constructor
  JiraApi.call(this, opts);

  this.xrayVersion = opts.xrayVersion || '1.0';
}

util.inherits(JiraApiWithXray, JiraApi);


/**
 * @typedef JiraApiWithXrayOptions
 * @type {object}
 * @property {string} [protocol=http] - What protocol to use to connect to
 * Jira? Ex: http|https
 * @property {string} host - What host is this tool connecting to for the Jira
 * instance? Ex: jira.somehost.com
 * @property {string} [port] - What port is this tool connecting to Jira with? Only needed for
 * none standard ports. Ex: 8080, 3000, etc
 * @property {string} [username] - Specify a username for this tool to authenticate all
 * requests with.
 * @property {string} [password] - Specify a password for this tool to authenticate all
 * requests with.
 * @property {string} [apiVersion=2] - What version of the Jira REST API is the instance the
 * tool is connecting to?
 * @property {string} [base] - What other url parts exist, if any, before the rest/api/
 * section?
 * @property {string} [intermediatePath] - If specified, overwrites the default rest/api/version
 * section of the uri
 * @property {boolean} [strictSSL=true] - Does this tool require each request to be
 * authenticated?  Defaults to true.
 * @property {function} [request] - What method does this tool use to make its requests?
 * Defaults to request from request-promise
 * @property {number} [timeout] - Integer containing the number of milliseconds to wait for a
 * server to send response headers (and start the response body) before aborting the request. Note
 * that if the underlying TCP connection cannot be established, the OS-wide TCP connection timeout
 * will overrule the timeout option ([the default in Linux can be anywhere from 20-120 *
 * seconds](http://www.sekuda.com/overriding_the_default_linux_kernel_20_second_tcp_socket_connect_timeout)).
 * @property {string} [webhookVersion=1.0] - What webhook version does this api wrapper need to
 * hit?
 * @property {string} [greenhopperVersion=1.0] - What webhook version does this api wrapper need
 * to hit?
 * @property {OAuth} [oauth] - Specify an OAuth object for this tool to authenticate all requests
 * using OAuth.
 * @property {string} [bearer] - Specify a OAuth bearer token to authenticate all requests with.
 * @property {string} [xrayVersion=1.0] - What version of the "Xray for Jira" REST API is the
 * instance the tool is connecting to?
 */

/**
 * @typedef OAuth
 * @type {object}
 * @property {string} consumer_key - The consumer entered in Jira Preferences.
 * @property {string} consumer_secret - The private RSA file.
 * @property {string} access_token - The generated access token.
 * @property {string} access_token_secret - The generated access toke secret.
 * @property {string} signature_method [signature_method=RSA-SHA1] - OAuth signurate methode
 * Possible values RSA-SHA1, HMAC-SHA1, PLAINTEXT. Jira Cloud supports only RSA-SHA1.
 */


/**
 * Creates a URI object for a given pathName within the "Xray for Jira" REST API
 * @param {makeXrayUriOptions} [options] - An options object specifying uri information
 */
JiraApiWithXray.prototype.makeXrayUri = function(options) {
  var opts = options || {};
  var intermediateToUse = this.intermediatePath || opts.intermediatePath;
  var tempPath = intermediateToUse || '/rest/raven/' + this.xrayVersion;
  var uri = url.format({
    protocol: this.protocol,
    hostname: this.host,
    port: this.port,
    pathname: this.base + tempPath + opts.pathname,
  });
  return decodeURIComponent(uri);
};

/**
 * @typedef makeXrayUriOptions
 * @type {object}
 * @property {string} pathname - The url after the /rest/raven/version
 * @property {string} intermediatePath - If specified will overwrite the /rest/raven/version section
 */


/*jshint ignore:start */
/**
 * Import Test Execution results into "Xray for Jira" using the Xray JSON format.
 * @see https://confluence.xpand-addons.com/display/XRAY/Import+Execution+Results+-+REST#ImportExecutionResults-REST-XrayJSONresults
 * @param {TestExecXrayJson} testExecResults - The results of the Test Execution in the Xray JSON format
 */
/*jshint ignore:end */
JiraApiWithXray.prototype.importExecResultsFromXray = function(testExecResults) {
  return this.doRequest(
    this.makeRequestHeader(
      this.makeXrayUri({
        method: 'POST',
        pathname: '/import/execution',
        body: testExecResults
      })
    )
  );
};

/*jshint ignore:start */
/**
 * @typedef TestExecXrayJson
 * @type {object}
 * @see https://confluence.xpand-addons.com/display/XRAY/Import+Execution+Results#ImportExecutionResults-XrayJSONformat
 * @property {string} [testExecutionKey] - The Jira issue key for an existing Test Execution that should be updated. If omitted, a new Test Execution is created automatically.
 * @property {TestExecXrayJsonInfo} [info] - Test Execution metadata
 * @property {TestExecXrayJsonTestRun[]} tests - The results of the Test Run
 */
/*jshint ignore:end */

/**
 * @typedef TestExecXrayJsonInfo
 * @type {object}
 * @property {string} [project] - The Jira project ID
 * @property {string} summary - Summary of the Test Execution
 * @property {string} [description] - Description of the Test Execution
 * @property {string} [user] - The Jira user logging the Test Execution results
 * @property {string} [version] - Version under test
 * @property {string} [revision] - The revision/build/branch under test
 * @property {string} [startDate] - The ISO date timestamp at which the Test Execution started
 * @property {string} [finishDate] - The ISO date timestamp at which the Test Execution finished
 * @property {string} [testPlanKey] - The Jira issue key for the associated Test Plan
 * @property {string[]} [testEnvironments] - The environmental configuration under test
 */

/**
 * @typedef TestExecXrayJsonTestRun
 * @type {object}
 * @property {string} testKey - The Jira issue key for the associated Test
 * @property {string} status - The "PASS" or "FAIL" status for the Test Run as a whole
 * @property {string} [comment] - A message about this Test Run
 * @property {string} [start] - The ISO date timestamp at which this Test Run started
 * @property {string} [finish] - The ISO date timestamp at which this Test Run finished
 * @property {string} [executedBy] - The Jira user who executed this Test Run
 * @property {TestExecXrayJsonEvidence[]} [evidences] - Attachments (e.g. screenshots) proving the Test Run's failure
 * @property {TestExecXrayJsonResult[]} [results] - Detailed results of individual test cases within this Test Run
 * @property {string[]} [examples] - The "PASS" or "FAIL" status for each of the Test Run's examples
 * @property {TestExecXrayJsonTestStep[]} [steps] - Details about each Test step and its outcome
 * @property {string[]} [defects] - The Jira issue keys for any associated bugs recorded during this Test Run
 */

/**
 * @typedef TestExecXrayJsonEvidence
 * @type {object}
 * @property {string} data - The base64-encoded string of data representing this attachment
 * @property {string} filename - The basename of the file associated with this attachment
 * @property {string} [contentType] - The MIME type associated with this attachment, e.g. "image/jpeg"
 */

/**
 * @typedef TestExecXrayJsonResult
 * @type {object}
 * @property {string} name - The name of the test case
 * @property {string} status - The "PASS" or "FAIL" status of this test case
 * @property {number} [duration] - The duration (milliseconds? seconds?)
 * @property {string} [log] - Any log messages that came out of this test case
 * @property {string[]} [examples] - The "PASS" or "FAIL" status for each of this test case's examples
 */

/**
 * @typedef TestExecXrayJsonTestStep
 * @type {object}
 * @property {string} status - The "PASS" or "FAIL" status of this step
 * @property {string} [comment] - A message about this step
 * @property {TestExecXrayJsonEvidence[]} [evidences] - Attachments (e.g. screenshots) proving this step's failure
 */


/**
 * Import Test Execution results into "Xray for Jira" using one of the supported JSON formats.
 * @access private
 * @param {string} type - The type of JSON result to be imported. Must be one of: "cucumber" or "behave".
 * @param {TestExecCucumberJson|TestExecBehaveJson} results - The results of the Test Execution
 * @param {JiraIssueCreationJson} [issueData] - Customized field data for creating a new Test Execution issue in Jira
 */
JiraApiWithXray.prototype._importExecResultsFromJson = function(type, results, issueData) {
  var hasIssueData = !!issueData;
  var requestOptions = {
        method: 'POST',
        pathname: '/import/execution/' + type + (hasIssueData ? '/multipart' : '')
      };

  if (hasIssueData) {
    requestOptions.formData = {
      result: results,
      info: issueData
    };
  }
  else {
    requestOptions.body = results;
  }

  return this.doRequest(
    this.makeRequestHeader(
      this.makeXrayUri(
        requestOptions
      )
    )
  );
};

/**
 * @external {JiraCloudIssueCreationJson} https://developer.atlassian.com/cloud/jira/platform/rest/#api-api-2-issue-post
 */

/*jshint ignore:start */
/**
 * @external {JiraServerIssueCreationJson} https://docs.atlassian.com/software/jira/docs/api/REST/7.9.0/#api/2/issue-createIssue
 */
/*jshint ignore:end */

/**
 * @typedef JiraIssueCreationJson
 * @type {JiraCloudIssueCreationJson|JiraServerIssueCreationJson}
 */


/*jshint ignore:start */
/**
 * Import Test Execution results into "Xray for Jira" using the Cucumber JSON format.
 * @see https://confluence.xpand-addons.com/display/XRAY/Import+Execution+Results+-+REST#ImportExecutionResults-REST-CucumberJSONresults
 * @see https://confluence.xpand-addons.com/display/XRAY/Import+Execution+Results+-+REST#ImportExecutionResults-REST-CucumberJSONresultsMultipart
 * @param {TestExecCucumberJson} testExecResults - The results of the Test Execution in the Cucumber JSON format
 * @param {JiraIssueCreationJson} [issueData] - Customized field data for creating a new Test Execution issue in Jira
 */
/*jshint ignore:end */
JiraApiWithXray.prototype.importExecResultsFromCucumber = function(testExecResults, issueData) {
  return this._importExecResultsFromJson('cucumber', testExecResults, issueData);
};

/*jshint ignore:start */
/**
 * @external {TestExecCucumberJson} https://confluence.xpand-addons.com/display/XRAY/Import+Execution+Results#ImportExecutionResults-CucumberJSONoutputformat
 */
/*jshint ignore:end */


/*jshint ignore:start */
/**
 * Import Test Execution results into "Xray for Jira" using the Behave JSON format.
 * @see https://confluence.xpand-addons.com/display/XRAY/Import+Execution+Results+-+REST#ImportExecutionResults-REST-BehaveJSONresults
 * @see https://confluence.xpand-addons.com/display/XRAY/Import+Execution+Results+-+REST#ImportExecutionResults-REST-BehaveJSONresultsMultipart
 * @param {TestExecBehaveJson} testExecResults - The results of the Test Execution in the Behave JSON format
 * @param {JiraIssueCreationJson} [issueData] - Customized field data for creating a new Test Execution issue in Jira
 */
/*jshint ignore:end */
JiraApiWithXray.prototype.importExecResultsFromBehave = function(testExecResults, issueData) {
  return this._importExecResultsFromJson('behave', testExecResults, issueData);
};

/**
 * @external {TestExecBehaveJson} https://github.com/behave/behave/blob/master/behave/formatter/json.py
 */


/**
 * Import Test Execution results into "Xray for Jira" using one of the supported XML formats.
 * @access private
 * @param {string} type - The type of XML result to be imported. Must be one of: "junit", "testng", "nunit", or "robot".
 * @param {TestExecJUnitXml|TestExecTestNGXml|TestExecNUnitXml|TestExecRobotXml|string|Buffer|ReadableStream} results -
 * The results of the Test Execution in an XML formatted string
 * @param {XrayImportQueryParams} [query] - Field data for creating a new Test Execution issue in Jira.
 * Either `query` or `issueData` must be provided but not both.
 * @param {JiraIssueCreationJson} [issueData] - Customized field data for creating a new Test Execution issue in Jira.
 * Either `query` or `issueData` must be provided but not both.
 */
JiraApiWithXray.prototype._importExecResultsFromXml = function(type, results, query, issueData) {
  var hasQuery = !!query;
  var hasIssueData = !hasQuery && !!issueData;
  var requestOptions = {
        method: 'POST',
        pathname: '/import/execution/' + type + (hasIssueData ? '/multipart' : ''),
        formData: {
          file: results
        }
      };

  if (hasQuery) {
    var queryOptions = Object.assign({}, query);

    var testEnvs = queryOptions.testEnvironments || [];
    if (typeof testEnvs === 'string') {
      testEnvs = testEnvs.split(/\s*;\s*/);
    }
    if (Array.isArray(testEnvs)) {
      testEnvs = testEnvs.filter(function(te) { return !!te.replace(/^\s+|\s+$/g, ''); });
    }
    if (testEnvs.length) {
      queryOptions.testEnvironments = testEnvs.join(';');
    }

    requestOptions.query = queryOptions;
  }
  else if (hasIssueData) {
    requestOptions.form.info = issueData;
  }
  else {
    throw new TypeError('Must provide either the "query" or "issueData" parameter');
  }

  return this.doRequest(
    this.makeRequestHeader(
      this.makeXrayUri(
        requestOptions
      )
    )
  );
};

/**
 * @typedef XrayImportQueryParams
 * @type object
 * @property {string} [testExecKey] - The Jira issue key for an existing Test Execution that should be updated. If
 * omitted, a new Test Execution is created automatically in the Jira project indicated by `projectKey`. Either
 * `testExecKey` or `projectKey` must be provided.
 * @property {string} [projectKey] - The Jira project key where the new Test Execution issue should be created. Not
 * needed if `testExecKey` is provided. Either `testExecKey` or `projectKey` must be provided.
 * @property {string} [testPlanKey] - The Jira issue key for the associated Test Plan
 * @property {string|string[]} [testEnvironments] - The list of test environments. If provided as a string, items must
 * be delimited by a ";".
 * @property {string} [revision] - Source code and documentation version used in the Test Execution
 * @property {string} [fixVersion] - The Jira "Fix Version" to be associated with the Test Execution
 */


/*jshint ignore:start */
/**
 * Import Test Execution results into "Xray for Jira" using the JUnit XML format.
 * @see https://confluence.xpand-addons.com/display/XRAY/Import+Execution+Results+-+REST#ImportExecutionResults-REST-JUnitXMLresults
 * @see https://confluence.xpand-addons.com/display/XRAY/Import+Execution+Results+-+REST#ImportExecutionResults-REST-JUnitXMLresultsMultipart
 * @param {TestExecJUnitXml|string|Buffer|ReadableStream} testExecResults - The results of the Test Execution in the
 * JUnit XML formatted string
 * @param {XrayImportQueryParams} [query] - Field data for creating a new Test Execution issue in Jira.
 * Either `query` or `issueData` must be provided but not both.
 * @param {JiraIssueCreationJson} [issueData] - Customized field data for creating a new Test Execution issue in Jira.
 * Either `query` or `issueData` must be provided but not both.
 */
/*jshint ignore:end */
JiraApiWithXray.prototype.importExecResultsFromJUnit = function(testExecResults, query, issueData) {
  return this._importExecResultsFromXml('junit', testExecResults, query, issueData);
};

/*jshint ignore:start */
/**
 * @external {TestExecJUnitXml} https://confluence.xpand-addons.com/display/XRAY/Import+Execution+Results#ImportExecutionResults-JUnitXMLoutputformat
 */
/*jshint ignore:end */


/*jshint ignore:start */
/**
 * Import Test Execution results into "Xray for Jira" using the TestNG XML format.
 * @see https://confluence.xpand-addons.com/display/XRAY/Import+Execution+Results+-+REST#ImportExecutionResults-REST-TestNGXMLresults
 * @see https://confluence.xpand-addons.com/display/XRAY/Import+Execution+Results+-+REST#ImportExecutionResults-REST-TestNGXMLresultsMultipart
 * @param {TestExecTestNGXml|string|Buffer|ReadableStream} testExecResults - The results of the Test Execution in the
 * TestNG XML formatted string
 * @param {XrayImportQueryParams} [query] - Field data for creating a new Test Execution issue in Jira.
 * Either `query` or `issueData` must be provided but not both.
 * @param {JiraIssueCreationJson} [issueData] - Customized field data for creating a new Test Execution issue in Jira.
 * Either `query` or `issueData` must be provided but not both.
 */
/*jshint ignore:end */
JiraApiWithXray.prototype.importExecResultsFromTestNG = function(testExecResults, query, issueData) {
  return this._importExecResultsFromXml('testng', testExecResults, query, issueData);
};

/*jshint ignore:start */
/**
 * @external {TestExecTestNGXml} https://confluence.xpand-addons.com/display/XRAY/Import+Execution+Results#ImportExecutionResults-TestNGXMLoutputformat
 */
/*jshint ignore:end */


/*jshint ignore:start */
/**
 * Import Test Execution results into "Xray for Jira" using the NUnit XML format.
 * @see https://confluence.xpand-addons.com/display/XRAY/Import+Execution+Results+-+REST#ImportExecutionResults-REST-NUnitXMLresults
 * @see https://confluence.xpand-addons.com/display/XRAY/Import+Execution+Results+-+REST#ImportExecutionResults-REST-NUnitXMLresultsMultipart
 * @param {TestExecNUnitXml|string|Buffer|ReadableStream} testExecResults - The results of the Test Execution in the NUnit XML formatted string
 * @param {XrayImportQueryParams} [query] - Field data for creating a new Test Execution issue in Jira.
 * Either `query` or `issueData` must be provided but not both.
 * @param {JiraIssueCreationJson} [issueData] - Customized field data for creating a new Test Execution issue in Jira.
 * Either `query` or `issueData` must be provided but not both.
 */
/*jshint ignore:end */
JiraApiWithXray.prototype.importExecResultsFromNUnit = function(testExecResults, query, issueData) {
  return this._importExecResultsFromXml('nunit', testExecResults, query, issueData);
};

/*jshint ignore:start */
/**
 * @external {TestExecNUnit30Xml} https://confluence.xpand-addons.com/display/XRAY/Import+Execution+Results#ImportExecutionResults-NUnitv3.0XMLoutputformat
 */
/*jshint ignore:end */

/*jshint ignore:start */
/**
 * @external {TestExecNUnit26Xml} https://confluence.xpand-addons.com/display/XRAY/Import+Execution+Results#ImportExecutionResults-NUnitv2.6XMLoutputformat
 */
/*jshint ignore:end */

/**
 * @typedef TestExecNUnitXml
 * @type {TestExecNUnit30Xml|TestExecNUnit26Xml}
 */


/*jshint ignore:start */
/**
 * Import Test Execution results into "Xray for Jira" using the Robot Framework XML format.
 * @see https://confluence.xpand-addons.com/display/XRAY/Import+Execution+Results+-+REST#ImportExecutionResults-REST-RobotFrameworkXMLresults
 * @see https://confluence.xpand-addons.com/display/XRAY/Import+Execution+Results+-+REST#ImportExecutionResults-REST-RobotFrameworkXMLresultsMultipart
 * @param {TestExecRobotXml|string|Buffer|ReadableStream} testExecResults - The results of the Test Execution in the
 * Robot Framework XML formatted string
 * @param {XrayImportQueryParams} [query] - Field data for creating a new Test Execution issue in Jira.
 * Either `query` or `issueData` must be provided but not both.
 * @param {JiraIssueCreationJson} [issueData] - Customized field data for creating a new Test Execution issue in Jira.
 * Either `query` or `issueData` must be provided but not both.
 */
/*jshint ignore:end */
JiraApiWithXray.prototype.importExecResultsFromRobot = function(testExecResults, query, issueData) {
  return this._importExecResultsFromXml('robot', testExecResults, query, issueData);
};

/**
 * @external {TestExecRobotXml} https://github.com/robotframework/robotframework/tree/master/doc/schema
 */


/*jshint ignore:start */
/**
 * Import multiple Test Execution results into "Xray for Jira" using a bundled compressed file (e.g. ZIP).
 * @see https://confluence.xpand-addons.com/display/XRAY/Import+Execution+Results+-+REST#ImportExecutionResults-REST-MultipleExecutionResults
 * @param {string|Buffer|ReadableStream} testExecResults - The results of the multiple Test Executions
 */
/*jshint ignore:end */
JiraApiWithXray.prototype.importMultipleExecResults = function(testExecResults) {
  return this.doRequest(
    this.makeRequestHeader(
      this.makeXrayUri({
        method: 'POST',
        pathname: '/import/execution/bundle',
        formData: {
          file: testExecResults
        }
      })
    )
  );
};



// Export
module.exports = JiraApiWithXray;