UNPKG

chimpy

Version:

Develop acceptance tests & end-to-end tests with realtime feedback.

300 lines (240 loc) 10.4 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck")); var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var path = require('path'); var cp = require('child-process-debug'); var processHelper = require('./../process-helper.js'); var log = require('./../log'); var _ = require('underscore'); var booleanHelper = require('../boolean-helper'); var fs = require('fs-extra'); /** * Run Cucumber specs * * @param {Function} callback * @api public */ var Cucumber = function Cucumber(_options) { var _this = this; (0, _classCallCheck2["default"])(this, Cucumber); (0, _defineProperty2["default"])(this, "start", function (callback) { var args = _this._getExecOptions(_this.options); if (!fs.existsSync(_this.options.path)) { var infoMessage = "[chimp][cucumber] Directory ".concat(_this.options.path, " does not exist. Not running"); if (booleanHelper.isTruthy(_this.options['fail-when-no-tests-run'])) { callback(infoMessage); } else { log.info(infoMessage); callback(); } return; } log.debug('[chimp][cucumber] Running with', args); var opts = { env: process.env, silent: true }; var port; if (booleanHelper.isTruthy(_this.options.debugCucumber)) { port = parseInt(_this.options.debugCucumber, 10); if (port > 1) { opts.execArgv = ['--debug=' + port]; } else { opts.execArgv = ['--debug']; } } if (booleanHelper.isTruthy(_this.options.debugBrkCucumber)) { port = parseInt(_this.options.debugBrkCucumber, 10); if (port > 1) { opts.execArgv = ['--debug-brk=' + port]; } else { opts.execArgv = ['--debug-brk']; } } _this.cucumberChild = cp.fork(path.join(__dirname, 'cucumber-wrapper.js'), args, opts); if (booleanHelper.isTruthy(_this.options.conditionOutput)) { _this.cucumberChild.stdout.on('data', function (data) { _this._conditionMessage(data.toString()); }); } else { _this.cucumberChild.stdout.pipe(process.stdout); } _this.cucumberChild.stderr.pipe(process.stderr); process.stdin.pipe(_this.cucumberChild.stdin); var jsonResults = null; _this.cucumberChild.on('message', function (res) { log.debug('[chimp][cucumber] Received message from cucumber child. Result:', res); jsonResults = res; }); _this.cucumberChild.on('close', function (code) { log.debug('[chimp][cucumber] Closed with code', code); if (!_this.cucumberChild.stopping) { log.debug('[chimp][cucumber] Cucumber not in a stopping state'); if (_this.options.jsonOutput && jsonResults && JSON.parse(jsonResults).length) { var dir = path.dirname(_this.options.jsonOutput); log.debug('[chimp][cucumber] Ensuring directory exists', dir); fs.mkdirsSync(dir); log.debug('[chimp][cucumber] Writing json results to', _this.options.jsonOutput); fs.writeFileSync(_this.options.jsonOutput, jsonResults); log.debug('[chimp][cucumber] Finished writing results'); } var failWhenNoTestsRun = booleanHelper.isTruthy(_this.options['fail-when-no-tests-run']); var noTestsFound = jsonResults === null || JSON.parse(jsonResults).length === 0; callback(code !== 0 || code === 0 && noTestsFound && failWhenNoTestsRun ? 'Cucumber steps failed' : null, jsonResults); } }); }); (0, _defineProperty2["default"])(this, "interrupt", function (callback) { log.debug('[chimp][cucumber] interrupting cucumber'); if (!_this.cucumberChild) { log.debug('[chimp][cucumber] no child to interrupt'); return callback(); } _this.cucumberChild.stopping = true; var options = { child: _this.cucumberChild, prefix: 'cucumber' }; processHelper.kill(options, function (err, res) { _this.cucumberChild = null; if (callback) { callback(err, res); } }); }); (0, _defineProperty2["default"])(this, "_getRecommendedFilename", function (line) { var stepType = line.match(/this\.(Given|When|Then)/)[1]; var recommendedFilename = stepType + ' ' + line.match(/\^(.*)\$/)[1]; recommendedFilename = recommendedFilename.replace(/".*"/g, '#'); recommendedFilename = recommendedFilename.replace(/\(.*\)/g, '#'); recommendedFilename = recommendedFilename.replace(/\\/g, ''); recommendedFilename = recommendedFilename.replace(/\$/g, ''); recommendedFilename = recommendedFilename.replace(/ /g, _this.options.recommendedFilenameSeparator || ' '); return recommendedFilename; }); (0, _defineProperty2["default"])(this, "_conditionOutput", function (message) { if (message.indexOf('callback.pending()') === -1) { process.stdout.write(message); return; } try { var defaultText = 'Write code here that turns the phrase above into concrete actions'; var replacementText = 'Write the automation code here'; var tab = ''; var self = _this; _.each(message.split('\n'), function (eachLine) { if (booleanHelper.isTruthy(self.options.singleSnippetPerFile) && eachLine.match(/this\./)) { process.stdout.write('// Recommended filename: '.gray + (self._getRecommendedFilename(eachLine) + '.js\n').cyan); process.stdout.write('module.exports = function() {\n'.yellow); tab = ' '; } var line = eachLine; line = line.replace(defaultText, replacementText); line = line.replace('callback.pending()', 'pending()'); line = line.replace(', callback', ''); line = line.replace('callback', ''); process.stdout.write(tab + line + '\n'); if (booleanHelper.isTruthy(self.options.singleSnippetPerFile) && line.match(/}\);/)) { process.stdout.write('};\n'.yellow); tab = ''; } }); } catch (e) { log.debug('[chimp][cucumber] Error conditioning message', e); process.stdout.write(message); } }); (0, _defineProperty2["default"])(this, "_conditionMessage", function (message) { if (_this.options.debug) { log.debug(message); return; } // output any strings that don't contain a stack trace if (message.indexOf(' at') === -1) { _this._conditionOutput(message); return; } var msg = ''; var basePath = path.resolve('.', _this.options.path); var basePathParent = path.resolve(basePath, '..'); var TAB = ' '; try { _.each(message.split('\n'), function (line) { var trimmedLine = line.trim(); var relativePathLine = line.replace(basePathParent + path.sep, ''); // filter out some known unnecessary lines // console.error('[' + line + ']'); if (trimmedLine.indexOf('node_modules') !== -1) { return; } // for stack trace lines if (trimmedLine.indexOf('at') === 0) { msg += relativePathLine.yellow + '\n'; } else { // or other lines that start with a tab (cucumber repeats errors at the end) if (line.indexOf(TAB) !== -1) { msg += relativePathLine.yellow + '\n'; } else { msg += relativePathLine.magenta + '\n'; } } }); process.stdout.write(msg); } catch (e) { log.debug('[chimp][cucumber] Error conditioning console out', e); process.stdout.write(message); } }); (0, _defineProperty2["default"])(this, "_getExecOptions", function (options) { var execOptions = ['node', path.resolve(__dirname, path.join('..', '..', '..', 'node_modules', '.bin', 'cucumber.js'))]; // XXX a feature may be defined at the start or end // XXX do other options also get passed with this command? var features = options._.splice(2).toString() || options.features || options.path; if (features.indexOf(',') !== -1) { features = features.split(','); _.each(features, function (feature) { execOptions.push(feature); }); } else { execOptions.push(features); } execOptions.push('-r'); execOptions.push(path.resolve(__dirname, path.join('../chimp-helper.js'))); execOptions.push('-r'); execOptions.push(path.resolve(__dirname, path.join('world.js'))); if (!options.domainOnly) { execOptions.push('-r'); execOptions.push(path.resolve(__dirname, path.join('hooks.js'))); } if (!options.r && !options.require) { execOptions.push('-r'); execOptions.push(options.path); } // See: https://github.com/cucumber/cucumber-js/blob/v0.9.2/lib/cucumber/cli.js var allowedCucumberJsOptions = { "long": ['version', 'backtrace', 'compiler', 'dry-run', 'fail-fast', 'format', 'no-colors', 'no-snippets', 'no-source', 'profile', 'require', 'snippet-syntax', 'strict', 'tags', 'help', 'name'], "short": ['v', 'b', 'd', 'f', 'p', 'r', 'S', 't'] }; _.forEach(options, function (eachOptionValues, optionName) { var optionValues = _.isArray(eachOptionValues) ? eachOptionValues : [eachOptionValues]; if (_.contains(allowedCucumberJsOptions["long"], optionName)) { _.forEach(optionValues, function (optionValue) { execOptions.push('--' + optionName); if (['dry-run', 'fail-fast', 'no-colors', 'no-snippets', 'no-source', 'strict', 'backtrace'].indexOf(optionName) === -1) { execOptions.push(optionValue.toString()); } }); } else if (_.contains(allowedCucumberJsOptions["short"], optionName)) { _.forEach(optionValues, function (optionValue) { execOptions.push('-' + optionName); if (['d', 'S', 'b'].indexOf(optionName) === -1) { execOptions.push(optionValue.toString()); } }); } else if (_.last(optionValues) === false && _.contains(allowedCucumberJsOptions["long"], 'no-' + optionName)) { execOptions.push('--no-' + optionName); } }); log.debug('[chimp][cucumber] Cucumber exec options are: ', execOptions); return execOptions; }); this.options = _options; this.cucumberChild = null; }; module.exports = Cucumber;