platform-helpers/runner-options-validator.js

const rootRequire = require('rpcm-root-require');
const fse = require('fs-extra');
const lodash = require('lodash');
const path = require('path');
const showdown = require('showdown').setOption('tables', 'true');
const converter = new showdown.Converter();
rootRequire('/platform-helpers/string-extensions');

/**
 * Runner options validator
 *
 * @module
 * @static
 * @requires rpcm-root-require
 * @requires fs-extra
 * @requires lodash
 * @requires string-extensions
 */
class RunnerOptionsValidator {
  /**
   * Validates the options provided when the test Runner is called
   *
   * @param {object} object with the runner options
   * @static
   * @testFile tests\self-tests\unit-tests\runner-options-validator.spec.js
   * @throws Throws the specific error related to the provided invalid option.
   */
  static validate = (opts) => {
    opts = opts || {};
    opts.enableProxy = !!opts.enableProxy;
    opts.enableTrace = !!opts.enableTrace;
    opts.reportOnly = !!opts.reportOnly;
    opts.showInBrowser = opts.showInBrowser !== false;
    opts.runFullCrossBrowser = !!opts.runFullCrossBrowser;
    // crossbrowser is always true if fullcrossbrowser is true
    opts.runCrossBrowser = !!opts.runCrossBrowser || opts.runFullCrossBrowser;
    opts.extraProxyCommands = opts.extraProxyCommands || '';
    // cross browser is run remotely, always, and the proxy also currently requires it, as set up
    opts.runRemotely = !!opts.runRemotely || opts.runCrossBrowser || opts.enableProxy;
    opts.showDurations = !!opts.showDurations;
    opts.enableSauceConnect = !!opts.enableSauceConnect || !!opts.enableProxy;

    opts.summaryReportTitle = opts.summaryReportTitle || '';
    opts.reportContext = opts.reportContext || '';
    opts.summaryContext = opts.summaryContext || '';
    opts.hasReportContext = (opts.reportContext && opts.reportContext !== '') || false;
    opts.hasSummaryContext = (opts.summaryContext && opts.summaryContext !== '') || false;

    opts.runRetries = opts.runRetries || 0;
    if (isNaN(opts.runRetries) || opts.runRetries < 0 || Array.isArray(opts.runRetries) || typeof opts.runRetries === 'boolean') opts.runRetries = 0;
    opts.runRetries = parseInt(opts.runRetries);

    if (!Array.isArray(opts.blockPatterns)) opts.blockPatterns = [];
    if (!Array.isArray(opts.urlRewrites)) opts.urlRewrites = [];
    if (!Array.isArray(opts.helpers)) opts.helpers = [];

    if (typeof opts.extraProxyCommands !== 'string') throw new Error('The parameter \'extraProxyCommands\' needs to be a string!');
    if (typeof opts.summaryReportTitle !== 'string') throw new Error('The parameter \'summaryReportTitle\' needs to be a string!');
    if (typeof opts.reportContext !== 'string') throw new Error('The parameter \'reportContext\' needs to be a string!');
    if (typeof opts.summaryContext !== 'string') throw new Error('The parameter \'summaryContext\' needs to be a string!');
    if (typeof opts.enableProxy !== 'boolean') throw new Error('The parameter \'enableProxy\' needs to be a boolean!');
    if (typeof opts.enableTrace !== 'boolean') throw new Error('The parameter \'enableTrace\' needs to be a boolean!');
    if (typeof opts.runFullCrossBrowser !== 'boolean') throw new Error('The parameter \'runFullCrossBrowser\' needs to be a boolean!');
    if (typeof opts.runCrossBrowser !== 'boolean') throw new Error('The parameter \'runCrossBrowser\' needs to be a boolean!');
    if (typeof opts.runRemotely !== 'boolean') throw new Error('The parameter \'runRemotely\' needs to be a boolean!');
    if (typeof opts.showDurations !== 'boolean') throw new Error('The parameter \'showDurations\' needs to be a boolean!');
    if (typeof opts.enableSauceConnect !== 'boolean') throw new Error('The parameter \'enableSauceConnect\' needs to be a boolean!');

    /**
     * REPORT INFO
     */
    opts.reportContext = converter.makeHtml(opts.reportContext);
    opts.summaryContext = converter.makeHtml(opts.summaryContext);

    /**
     * TEST FILES SPECS
     */
    if (!Array.isArray(opts.specs)) throw new Error('Please provide an array with at least one test file to run!');
    if (opts.specs.length < 1) throw new Error('Please provide an array with at least one test file to run!');
    opts.specs.forEach((spec, index) => {
      if (lodash.isString(spec)) {
        opts.specs[index] = { testFile: spec };
      }
      if (lodash.isPlainObject(spec)) {
        if (!fse.existsSync('{0}/{1}'.format(rootRequire.rootPath, spec.testFile))) { throw new Error('Please provide a path to an existing test file! File not found: {0}'.format(spec.testFile)); }
      }
    });

    /**
     * BLOCK PATTERNS
     */
    if (opts.blockPatterns.length > 0) {
      opts.blockPatterns.forEach(blockPattern => {
        if (!lodash.isPlainObject(blockPattern)) { throw new Error('Each item in the \'blockPatterns\' array should be an object'); }
        if (!blockPattern.pattern && !blockPattern.type) { throw new Error('Each item in the \'blockPatterns\' array should have following compulsory properties: \'pattern\' and \'type\''); }
      });
    }

    /**
     * URL REWRITES
     */
    if (opts.urlRewrites.length > 0) {
      opts.urlRewrites.forEach(urlRewrite => {
        if (!lodash.isPlainObject(urlRewrite)) { throw new Error('Each item in the \'urlRewrites\' array should be an object'); }
        if (!urlRewrite.pattern && !urlRewrite.type && !urlRewrite.target && !urlRewrite.replacement) { throw new Error('Each item in the \'urlRewrites\' array should have following compulsory properties: \'pattern\', \'type\', \'target\' and \'replacement\''); }
      });
    }

    /**
     * TRACE DATA
     *
     * These are options but they have no requirements except if 'enableTrace' is true.
     * If they are specified they will be used to generate a trace. The trace generation fails if the data provided is invalid.
     */
    // opts.traceAccount
    // opts.traceProfile
    if ((opts.enableTrace && !opts.traceAccount) || (opts.enableTrace && !opts.traceProfile)) {
      throw new Error('The parameter \'traceAccount\' and \'traceProfile\' are compulsory when Trace is enabled!');
    }

    /**
     * HELPERS
     */
    // remove duplicates
    opts.helpers = [...new Set(opts.helpers)];
    // remove inexistent files
    opts.helpers = opts.helpers.filter(file => fse.existsSync('{0}/{1}'.format(rootRequire.rootPath, file)));
    // copy helpers to temp folder (not sure why, maybe remove later if not needed)
    opts.helpers.forEach(helper => fse.copySync(helper, '{0}/tmp/tests/helpers/{1}'.format(rootRequire.rootPath, path.basename(helper))));
  };
}

module.exports = RunnerOptionsValidator;