'use strict'; var _ = require('lodash'); var os = require('os'); var cp = require('child_process'); var tty = require('tty'); var path = require('path'); var Promise = require('bluebird'); var debug = require('debug')('cypress:cli'); var util = require('../util'); var info = require('../tasks/info'); var xvfb = require('./xvfb'); var _require = require('../errors'), throwFormErrorText = _require.throwFormErrorText, errors = _require.errors; var isXlibOrLibudevRe = /^(Xlib|libudev)/; function needsStderrPipe(needsXvfb) { return needsXvfb && os.platform() === 'linux'; } function getStdio(needsXvfb) { // https://github.com/cypress-io/cypress/issues/921 if (needsStderrPipe(needsXvfb)) { // returning pipe here so we can massage stderr // and remove garbage from Xlib and libuv // due to starting the XVFB process on linux return ['inherit', 'inherit', 'pipe']; } return 'inherit'; } module.exports = { start: function start(args) { var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var needsXvfb = xvfb.isNeeded(); debug('needs XVFB?', needsXvfb); // always push cwd into the args args = [].concat(args, '--cwd', process.cwd()); _.defaults(options, { detached: false, stdio: getStdio(needsXvfb) }); var spawn = function spawn() { return new Promise(function (resolve, reject) { var cypressPath = info.getPathToExecutable(); if (options.dev) { // if we're in dev then reset // the launch cmd to be 'npm run dev' cypressPath = 'node'; args.unshift(path.resolve(__dirname, '..', '..', '..', 'scripts', 'start.js')); } debug('spawning Cypress %s', cypressPath); debug('spawn args %j', args, options); // strip dev out of child process options options = _.omit(options, 'dev'); // when running in electron in windows // it never supports color but we're // going to force it anyway as long // as our parent cli process can support // colors! // // also when we are in linux and using the 'pipe' // option our process.stderr.isTTY will not be true // which ends up disabling the colors =( if (util.supportsColor()) { process.env.FORCE_COLOR = 1; process.env.DEBUG_COLORS = 1; process.env.MOCHA_COLORS = 1; } // if we needed to pipe stderr and we're currently // a tty on stderr if (needsStderrPipe(needsXvfb) && tty.isatty(2)) { // then force stderr tty // // this is necessary because we want our child // electron browser to behave _THE SAME WAY_ as // if we aren't using pipe. pipe is necessary only // to filter out garbage on stderr -____________- process.env.FORCE_STDERR_TTY = 1; } var child = cp.spawn(cypressPath, args, options); child.on('close', resolve); child.on('error', reject); // if this is defined then we are manually piping for linux // to filter out the garbage child.stderr && child.stderr.on('data', function (data) { // bail if this is a line from xlib or libudev if (isXlibOrLibudevRe.test(data.toString())) { return; } // else pass it along! process.stderr.write(data); }); if (options.detached) { child.unref(); } }); }; var userFriendlySpawn = function userFriendlySpawn() { return spawn().catch(throwFormErrorText(errors.unexpected)); }; if (needsXvfb) { return xvfb.start().then(userFriendlySpawn).finally(xvfb.stop); } else { return userFriendlySpawn(); } } };