mirror of
https://github.com/cypress-io/cypress.git
synced 2026-01-30 19:18:45 -06:00
close #1984 improve error logging for `cypress verify` close #1985 remove "skipping install" message to local users, keep in CI
248 lines
5.7 KiB
JavaScript
248 lines
5.7 KiB
JavaScript
const _ = require('lodash')
|
|
const R = require('ramda')
|
|
const os = require('os')
|
|
const tty = require('tty')
|
|
const path = require('path')
|
|
const isCi = require('is-ci')
|
|
const execa = require('execa')
|
|
const getos = require('getos')
|
|
const chalk = require('chalk')
|
|
const Promise = require('bluebird')
|
|
const cachedir = require('cachedir')
|
|
const executable = require('executable')
|
|
const supportsColor = require('supports-color')
|
|
const isInstalledGlobally = require('is-installed-globally')
|
|
const pkg = require(path.join(__dirname, '..', 'package.json'))
|
|
const logger = require('./logger')
|
|
const debug = require('debug')('cypress:cli')
|
|
|
|
const getosAsync = Promise.promisify(getos)
|
|
const joinWithEq = (x, y) => `${x}=${y}`
|
|
|
|
// converts an object (single level) into
|
|
// key1=value1,key2=value2,...
|
|
const objectToString = (obj) =>
|
|
R.zipWith(joinWithEq, R.keys(obj), R.values(obj)).join(',')
|
|
|
|
const normalizeObject = (env) =>
|
|
_.isPlainObject(env) ? objectToString(env) : env
|
|
|
|
const normalizeArray = (arr) =>
|
|
_.isArray(arr) ? arr.join(',') : arr
|
|
|
|
function normalizeModuleOptions (options = {}) {
|
|
return R.evolve({
|
|
env: normalizeObject,
|
|
config: normalizeObject,
|
|
reporterOptions: normalizeObject,
|
|
spec: normalizeArray,
|
|
})(options)
|
|
}
|
|
|
|
function stdoutLineMatches (expectedLine, stdout) {
|
|
const lines = stdout.split('\n').map(R.trim)
|
|
const lineMatches = R.equals(expectedLine)
|
|
return lines.some(lineMatches)
|
|
}
|
|
|
|
/**
|
|
* Prints NODE_OPTIONS using debug() module, but only
|
|
* if DEBUG=cypress... is set
|
|
*/
|
|
function printNodeOptions (log = debug) {
|
|
if (!log.enabled) {
|
|
return
|
|
}
|
|
|
|
if (process.env.NODE_OPTIONS) {
|
|
log('NODE_OPTIONS=%s', process.env.NODE_OPTIONS)
|
|
} else {
|
|
log('NODE_OPTIONS is not set')
|
|
}
|
|
}
|
|
|
|
const util = {
|
|
normalizeModuleOptions,
|
|
|
|
printNodeOptions,
|
|
|
|
isCi () {
|
|
return isCi
|
|
},
|
|
|
|
getEnvOverrides () {
|
|
return _
|
|
.chain({})
|
|
.extend(util.getEnvColors())
|
|
.extend(util.getForceTty())
|
|
.omitBy(_.isUndefined) // remove undefined values
|
|
.mapValues((value) => { // stringify to 1 or 0
|
|
return value ? '1' : '0'
|
|
})
|
|
.value()
|
|
},
|
|
|
|
getForceTty () {
|
|
return {
|
|
FORCE_STDIN_TTY: util.isTty(process.stdin.fd),
|
|
FORCE_STDOUT_TTY: util.isTty(process.stdout.fd),
|
|
FORCE_STDERR_TTY: util.isTty(process.stderr.fd),
|
|
}
|
|
},
|
|
|
|
getEnvColors () {
|
|
const sc = util.supportsColor()
|
|
|
|
return {
|
|
FORCE_COLOR: sc,
|
|
DEBUG_COLORS: sc,
|
|
MOCHA_COLORS: sc ? true : undefined,
|
|
}
|
|
},
|
|
|
|
isTty (fd) {
|
|
return tty.isatty(fd)
|
|
},
|
|
|
|
supportsColor () {
|
|
// if we've been explictly told not to support
|
|
// color then turn this off
|
|
if (process.env.NO_COLOR) {
|
|
return false
|
|
}
|
|
|
|
// https://github.com/cypress-io/cypress/issues/1747
|
|
// always return true in CI providers
|
|
if (process.env.CI) {
|
|
return true
|
|
}
|
|
|
|
// ensure that both stdout and stderr support color
|
|
return Boolean(supportsColor.stdout) && Boolean(supportsColor.stderr)
|
|
},
|
|
|
|
cwd () {
|
|
return process.cwd()
|
|
},
|
|
|
|
pkgVersion () {
|
|
return pkg.version
|
|
},
|
|
|
|
exit (code) {
|
|
process.exit(code)
|
|
},
|
|
|
|
logErrorExit1 (err) {
|
|
logger.error(err.message)
|
|
|
|
process.exit(1)
|
|
},
|
|
|
|
titleize (...args) {
|
|
// prepend first arg with space
|
|
// and pad so that all messages line up
|
|
args[0] = _.padEnd(` ${args[0]}`, 24)
|
|
|
|
// get rid of any falsy values
|
|
args = _.compact(args)
|
|
|
|
return chalk.blue(...args)
|
|
},
|
|
|
|
calculateEta (percent, elapsed) {
|
|
// returns the number of seconds remaining
|
|
|
|
// if we're at 100 already just return 0
|
|
if (percent === 100) {
|
|
return 0
|
|
}
|
|
|
|
// take the percentage and divide by one
|
|
// and multiple that against elapsed
|
|
// subtracting what's already elapsed
|
|
return elapsed * (1 / (percent / 100)) - elapsed
|
|
},
|
|
|
|
secsRemaining (eta) {
|
|
// calculate the seconds reminaing with no decimal places
|
|
return (_.isFinite(eta) ? (eta / 1000) : 0).toFixed(0)
|
|
},
|
|
|
|
setTaskTitle (task, title, renderer) {
|
|
// only update the renderer title when not running in CI
|
|
if (renderer === 'default') {
|
|
task.title = title
|
|
}
|
|
},
|
|
|
|
isInstalledGlobally () {
|
|
return isInstalledGlobally
|
|
},
|
|
|
|
isSemver (str) {
|
|
return /^(\d+\.)?(\d+\.)?(\*|\d+)$/.test(str)
|
|
},
|
|
|
|
isExecutableAsync (filePath) {
|
|
return Promise.resolve(executable(filePath))
|
|
},
|
|
|
|
getOsVersionAsync () {
|
|
return Promise.try(() => {
|
|
if (os.platform() === 'linux') {
|
|
return getosAsync()
|
|
.then((osInfo) => [osInfo.dist, osInfo.release].join(' - '))
|
|
.catch(() => os.release())
|
|
} else {
|
|
return os.release()
|
|
}
|
|
})
|
|
},
|
|
|
|
// attention:
|
|
// when passing relative path to NPM post install hook, the current working
|
|
// directory is set to the `node_modules/cypress` folder
|
|
// the user is probably passing relative path with respect to root package folder
|
|
formAbsolutePath (filename) {
|
|
if (path.isAbsolute(filename)) {
|
|
return filename
|
|
}
|
|
return path.join(process.cwd(), '..', '..', filename)
|
|
},
|
|
|
|
getEnv (varName) {
|
|
const envVar = process.env[varName]
|
|
const configVar = process.env[`npm_config_${varName}`]
|
|
const packageConfigVar = process.env[`npm_package_config_${varName}`]
|
|
if (envVar) {
|
|
debug(`Using ${varName} from environment variable`)
|
|
return envVar
|
|
}
|
|
if (configVar) {
|
|
debug(`Using ${varName} from npm config`)
|
|
return configVar
|
|
}
|
|
if (packageConfigVar) {
|
|
debug(`Using ${varName} from package.json config`)
|
|
return packageConfigVar
|
|
}
|
|
return undefined
|
|
|
|
},
|
|
|
|
getCacheDir () {
|
|
return cachedir('Cypress')
|
|
},
|
|
|
|
isPostInstall () {
|
|
return process.env.npm_lifecycle_event === 'postinstall'
|
|
},
|
|
|
|
exec: execa,
|
|
|
|
stdoutLineMatches,
|
|
}
|
|
|
|
module.exports = util
|