diff --git a/cli/lib/errors.js b/cli/lib/errors.js index 7beb8261ef..5ecd9a263c 100644 --- a/cli/lib/errors.js +++ b/cli/lib/errors.js @@ -1,13 +1,18 @@ const os = require('os') +const chalk = require('chalk') const Promise = require('bluebird') const getos = Promise.promisify(require('getos')) const { stripIndent, stripIndents } = require('common-tags') const { merge } = require('ramda') +const util = require('./util') + const issuesUrl = 'https://github.com/cypress-io/cypress/issues' const docsUrl = 'https://on.cypress.io' const requiredDependenciesUrl = `${docsUrl}/required-dependencies` +const pkgVersion = util.pkgVersion() + // common errors Cypress application can encounter const failedDownload = { description: 'The Cypress App could not be downloaded.', @@ -24,56 +29,56 @@ const failedToUnzip = { } const missingApp = { - description: 'No version of Cypress executable installed', + description: 'No version of Cypress executable is installed.', solution: stripIndent` - Please reinstall Cypress and run the app again. - If the problem persists, search for an existing issue or open a GitHub issue at - - ${issuesUrl} + \nPlease reinstall Cypress by running: ${chalk.cyan('cypress install')} `, } const missingXvfb = { - description: 'Looks like your system is missing a must have dependency: XVFB', + description: 'Your system is missing the dependency: XVFB', solution: stripIndent` Install XVFB and run Cypress again. + Our CI documentation provides more information how to configure dependencies + `, + footer: stripIndent` + Read our doc on CI dependencies for more information: ${requiredDependenciesUrl} - `, + `, } const missingDependency = { - description: 'Problem running Cypress application', + description: 'We could not run Cypress.', // this message is too Linux specific solution: stripIndent` This is usually caused by a missing library or dependency. + The error below should indicate which dependency is missing. - Read our doc on CI dependencies for more information: ${requiredDependenciesUrl} `, } const versionMismatch = { - description: 'Installed version does not match package version', + description: 'Installed version does not match package version.', solution: 'Install Cypress and verify app again', } const unexpected = { - description: 'An unexpected error occurred while verifying the Cypress executable', + description: 'An unexpected error occurred while verifying the Cypress executable.', solution: stripIndent` - Please search Cypress documentation for possible solutions + Please search Cypress documentation for possible solutions: ${docsUrl} - Find if there is a GitHub issue describing this crash + Check if there is a GitHub issue describing this crash: ${issuesUrl} - Consider opening a new issue, if you are the first to discover this + Consider opening a new issue. `, - printStack: true, } const getOsVersion = () => { @@ -89,8 +94,8 @@ const getOsVersion = () => { function getPlatformInfo () { return getOsVersion() .then((version) => stripIndent` - Platform: ${os.platform()} - Version: ${version} + Platform: ${os.platform()} (${version}) + Cypress Version: ${pkgVersion} `) } @@ -99,39 +104,70 @@ function addPlatformInformation (info) { .then((platform) => merge(info, { platform })) } -function formError (info, error) { - return addPlatformInformation(info) - .then((info) => merge(error, info)) -} - -function formErrorText (info, error) { +function formErrorText (info, msg) { const hr = '----------' - return formError(info, error) - .then((info) => stripIndents` - ${hr} - ${info.description} - ${info.solution} - ${hr} - ${info.message} - ${info.printStack ? info.stack : ''} - ${hr} - ${info.platform} - `) + return addPlatformInformation(info) + .then((obj) => { + const formatted = [] + + function add (msg) { + formatted.push( + stripIndents`${msg}` + ) + } + + add(` + ${obj.description} + + ${obj.solution} + + `) + + if (msg) { + add(` + ${hr} + + ${msg} + + `) + } + + add(` + ${hr} + + ${obj.platform} + `) + + if (obj.footer) { + add(` + + ${hr} + + ${obj.footer} + `) + } + + return formatted.join('\n') + }) } const raise = (text) => { - throw new Error(text) + const err = new Error(text) + err.known = true + throw err } -const throwDetailedError = (info) => (error) => - formErrorText(info, error) +const throwFormErrorText = (info) => (msg) => { + return formErrorText(info, msg) .then(raise) +} module.exports = { - formError, + raise, + // formError, formErrorText, - throwDetailedError, + throwFormErrorText, errors: { missingXvfb, missingApp, diff --git a/cli/lib/exec/open.js b/cli/lib/exec/open.js index 26463d60fd..1aae5c6931 100644 --- a/cli/lib/exec/open.js +++ b/cli/lib/exec/open.js @@ -1,7 +1,7 @@ -const downloadUtils = require('../download/utils') -const spawn = require('./spawn') const debug = require('debug')('cypress:cli') const util = require('../util') +const spawn = require('./spawn') +const { verify } = require('../tasks/verify') module.exports = { start (options = {}) { @@ -30,11 +30,11 @@ module.exports = { debug('opening from options %j', options) debug('command line arguments %j', args) - return downloadUtils.verify() + return verify() .then(() => { return spawn.start(args, { detached: Boolean(options.detached), - stdio: ['ignore', 'ignore', 'ignore'], + stdio: ['ignore', 'inherit', 'ignore'], }) }) }, diff --git a/cli/lib/exec/run.js b/cli/lib/exec/run.js index f5c6c62dd2..8a6f9b785b 100644 --- a/cli/lib/exec/run.js +++ b/cli/lib/exec/run.js @@ -1,7 +1,7 @@ const _ = require('lodash') const debug = require('debug')('cypress:cli') -const downloadUtils = require('../download/utils') const spawn = require('./spawn') +const { verify } = require('../tasks/verify') const processRunOptions = (options = {}) => { const args = ['--run-project', options.project] @@ -86,6 +86,6 @@ module.exports = { project: process.cwd(), }) - return downloadUtils.verify().then(run(options)) + return verify().then(run(options)) }, } diff --git a/cli/lib/exec/spawn.js b/cli/lib/exec/spawn.js index 5dd27cf6be..119f9ed30b 100644 --- a/cli/lib/exec/spawn.js +++ b/cli/lib/exec/spawn.js @@ -5,7 +5,7 @@ const debug = require('debug')('cypress:cli') const downloadUtils = require('../download/utils') const xvfb = require('./xvfb') -const { throwDetailedError, errors } = require('../errors') +const { throwFormErrorText, errors } = require('../errors') module.exports = { start (args, options = {}) { @@ -23,7 +23,7 @@ module.exports = { debug('spawn args %j', args) const child = cp.spawn(cypressPath, args, options) - child.on('exit', resolve) + child.on('close', resolve) child.on('error', reject) if (options.detached) { @@ -33,7 +33,7 @@ module.exports = { } const userFriendlySpawn = () => - spawn().catch(throwDetailedError(errors.unexpected)) + spawn().catch(throwFormErrorText(errors.unexpected)) const needsXvfb = xvfb.isNeeded() debug('needs XVFB?', needsXvfb) diff --git a/cli/lib/exec/xvfb.js b/cli/lib/exec/xvfb.js index 0dd0aa0c76..9cf3f526f4 100644 --- a/cli/lib/exec/xvfb.js +++ b/cli/lib/exec/xvfb.js @@ -3,7 +3,7 @@ const Promise = require('bluebird') const Xvfb = require('@cypress/xvfb') const R = require('ramda') const debug = require('debug')('cypress:cli') -const { throwDetailedError, errors } = require('../errors') +const { throwFormErrorText, errors } = require('../errors') const xvfb = Promise.promisifyAll(new Xvfb({ silent: true })) @@ -13,7 +13,7 @@ module.exports = { start () { debug('Starting XVFB') return xvfb.startAsync() - .catch(throwDetailedError(errors.missingXvfb)) + .catch(throwFormErrorText(errors.missingXvfb)) }, stop () {