chimpy
Version:
Develop acceptance tests & end-to-end tests with realtime feedback.
300 lines (240 loc) • 10.4 kB
JavaScript
;
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;