mirror of
https://github.com/cypress-io/cypress.git
synced 2026-05-05 06:20:44 -05:00
secure cookie error crash (#2685)
- fixes #1264 - fixes #1321 - fixes #1799 - fixes #2689 - fixes #2688 - fixes #2687 - fixes #2686
This commit is contained in:
@@ -10,6 +10,7 @@ const cache = require('./tasks/cache')
|
||||
// we want to print help for the current command and exit with an error
|
||||
function unknownOption (flag, type = 'option') {
|
||||
if (this._allowUnknownOption) return
|
||||
|
||||
logger.error()
|
||||
logger.error(` error: unknown ${type}:`, flag)
|
||||
logger.error()
|
||||
@@ -87,6 +88,7 @@ function includesVersion (args) {
|
||||
|
||||
function showVersions () {
|
||||
debug('printing Cypress version')
|
||||
|
||||
return require('./exec/versions')
|
||||
.getVersions()
|
||||
.then((versions = {}) => {
|
||||
@@ -187,6 +189,7 @@ module.exports = {
|
||||
const defaultOpts = { force: true, welcomeMessage: false }
|
||||
const parsedOpts = parseOpts(opts)
|
||||
const options = _.extend(parsedOpts, defaultOpts)
|
||||
|
||||
require('./tasks/verify')
|
||||
.start(options)
|
||||
.catch(util.logErrorExit1)
|
||||
@@ -203,6 +206,7 @@ module.exports = {
|
||||
if (opts.command || !_.includes(['list', 'path', 'clear'], opts)) {
|
||||
unknownOption.call(this, `cache ${opts}`, 'sub-command')
|
||||
}
|
||||
|
||||
cache[opts]()
|
||||
})
|
||||
|
||||
@@ -219,10 +223,12 @@ module.exports = {
|
||||
// Deprecated Catches
|
||||
|
||||
const firstCommand = args[2]
|
||||
|
||||
if (!_.includes(knownCommands, firstCommand)) {
|
||||
debug('unknown command %s', firstCommand)
|
||||
logger.error('Unknown command', `"${firstCommand}"`)
|
||||
program.outputHelp()
|
||||
|
||||
return util.exit(1)
|
||||
}
|
||||
|
||||
@@ -233,7 +239,9 @@ module.exports = {
|
||||
// so we have to manually catch '-v, --version'
|
||||
return showVersions()
|
||||
}
|
||||
|
||||
debug('program parsing arguments')
|
||||
|
||||
return program.parse(args)
|
||||
},
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ const util = require('./util')
|
||||
const cypressModuleApi = {
|
||||
open (options = {}) {
|
||||
options = util.normalizeModuleOptions(options)
|
||||
|
||||
return open.start(options)
|
||||
},
|
||||
|
||||
@@ -31,6 +32,7 @@ const cypressModuleApi = {
|
||||
message: 'Could not find Cypress test run results',
|
||||
}
|
||||
}
|
||||
|
||||
return output
|
||||
})
|
||||
})
|
||||
|
||||
+32
-19
@@ -25,16 +25,19 @@ const failedUnzip = {
|
||||
`,
|
||||
}
|
||||
|
||||
const missingApp = (binaryDir) => ({
|
||||
description: `No version of Cypress is installed in: ${chalk.cyan(binaryDir)}`,
|
||||
solution: stripIndent`
|
||||
const missingApp = (binaryDir) => {
|
||||
return {
|
||||
description: `No version of Cypress is installed in: ${chalk.cyan(binaryDir)}`,
|
||||
solution: stripIndent`
|
||||
\nPlease reinstall Cypress by running: ${chalk.cyan('cypress install')}
|
||||
`,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const binaryNotExecutable = (executable) => ({
|
||||
description: `Cypress cannot run because the binary does not have executable permissions: ${executable}`,
|
||||
solution: stripIndent`\n
|
||||
const binaryNotExecutable = (executable) => {
|
||||
return {
|
||||
description: `Cypress cannot run because the binary does not have executable permissions: ${executable}`,
|
||||
solution: stripIndent`\n
|
||||
Reasons this may happen:
|
||||
|
||||
- node was installed as 'root' or with 'sudo'
|
||||
@@ -42,12 +45,13 @@ const binaryNotExecutable = (executable) => ({
|
||||
|
||||
Please check that you have the appropriate user permissions.
|
||||
`,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const notInstalledCI = (executable) => ({
|
||||
description: 'The cypress npm package is installed, but the Cypress binary is missing.',
|
||||
solution: stripIndent`\n
|
||||
const notInstalledCI = (executable) => {
|
||||
return {
|
||||
description: 'The cypress npm package is installed, but the Cypress binary is missing.',
|
||||
solution: stripIndent`\n
|
||||
We expected the binary to be installed here: ${chalk.cyan(executable)}
|
||||
|
||||
Reasons it may be missing:
|
||||
@@ -61,7 +65,8 @@ const notInstalledCI = (executable) => ({
|
||||
|
||||
${chalk.blue('https://on.cypress.io/not-installed-ci-error')}
|
||||
`,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const nonZeroExitCodeXvfb = {
|
||||
description: 'XVFB exited with a non zero exit code.',
|
||||
@@ -146,6 +151,7 @@ const removed = {
|
||||
const CYPRESS_RUN_BINARY = {
|
||||
notValid: (value) => {
|
||||
const properFormat = `**/${state.getPlatformExecutable()}`
|
||||
|
||||
return {
|
||||
description: `Could not run binary set by environment variable CYPRESS_RUN_BINARY=${value}`,
|
||||
solution: `Ensure the environment variable is a path to the Cypress binary, matching ${properFormat}`,
|
||||
@@ -155,15 +161,19 @@ const CYPRESS_RUN_BINARY = {
|
||||
|
||||
function getPlatformInfo () {
|
||||
return util.getOsVersionAsync()
|
||||
.then((version) => stripIndent`
|
||||
.then((version) => {
|
||||
return stripIndent`
|
||||
Platform: ${os.platform()} (${version})
|
||||
Cypress Version: ${util.pkgVersion()}
|
||||
`)
|
||||
`
|
||||
})
|
||||
}
|
||||
|
||||
function addPlatformInformation (info) {
|
||||
return getPlatformInfo()
|
||||
.then((platform) => merge(info, { platform }))
|
||||
.then((platform) => {
|
||||
return merge(info, { platform })
|
||||
})
|
||||
}
|
||||
|
||||
function formErrorText (info, msg) {
|
||||
@@ -216,13 +226,16 @@ function formErrorText (info, msg) {
|
||||
|
||||
const raise = (text) => {
|
||||
const err = new Error(text)
|
||||
|
||||
err.known = true
|
||||
throw err
|
||||
}
|
||||
|
||||
const throwFormErrorText = (info) => (msg) => {
|
||||
return formErrorText(info, msg)
|
||||
.then(raise)
|
||||
const throwFormErrorText = (info) => {
|
||||
return (msg) => {
|
||||
return formErrorText(info, msg)
|
||||
.then(raise)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
||||
@@ -88,6 +88,7 @@ module.exports = {
|
||||
options.env = _.extend({}, options.env, overrides)
|
||||
|
||||
const child = cp.spawn(executable, args, options)
|
||||
|
||||
child.on('close', resolve)
|
||||
child.on('error', reject)
|
||||
|
||||
@@ -137,8 +138,9 @@ module.exports = {
|
||||
return xvfb.start()
|
||||
.then(userFriendlySpawn)
|
||||
.finally(xvfb.stop)
|
||||
} else {
|
||||
return userFriendlySpawn()
|
||||
}
|
||||
|
||||
return userFriendlySpawn()
|
||||
|
||||
},
|
||||
}
|
||||
|
||||
@@ -11,11 +11,13 @@ const getVersions = () => {
|
||||
|
||||
if (util.getEnv('CYPRESS_RUN_BINARY')) {
|
||||
let envBinaryPath = path.resolve(util.getEnv('CYPRESS_RUN_BINARY'))
|
||||
|
||||
return state.parseRealPlatformBinaryFolderAsync(envBinaryPath)
|
||||
.then((envBinaryDir) => {
|
||||
if (!envBinaryDir) {
|
||||
return throwFormErrorText(errors.CYPRESS_RUN_BINARY.notValid(envBinaryPath))()
|
||||
}
|
||||
|
||||
debug('CYPRESS_RUN_BINARY has binaryDir:', envBinaryDir)
|
||||
|
||||
return envBinaryDir
|
||||
@@ -24,6 +26,7 @@ const getVersions = () => {
|
||||
return throwFormErrorText(errors.CYPRESS_RUN_BINARY.notValid(envBinaryPath))(err.message)
|
||||
})
|
||||
}
|
||||
|
||||
return state.getBinaryDir()
|
||||
})
|
||||
.then(state.getBinaryPkgVersionAsync)
|
||||
|
||||
@@ -22,6 +22,7 @@ module.exports = {
|
||||
|
||||
start () {
|
||||
debug('Starting XVFB')
|
||||
|
||||
return xvfb.startAsync()
|
||||
.catch({ nonZeroExitCode: true }, throwFormErrorText(errors.nonZeroExitCodeXvfb))
|
||||
.catch((err) => {
|
||||
@@ -35,6 +36,7 @@ module.exports = {
|
||||
|
||||
stop () {
|
||||
debug('Stopping XVFB')
|
||||
|
||||
return xvfb.stopAsync()
|
||||
},
|
||||
|
||||
@@ -48,6 +50,7 @@ module.exports = {
|
||||
.then(R.T)
|
||||
.catch((err) => {
|
||||
debug('Could not verify xvfb: %s', err.message)
|
||||
|
||||
return false
|
||||
})
|
||||
.finally(xvfb.stopAsync)
|
||||
|
||||
+6
-1
@@ -3,7 +3,9 @@ const chalk = require('chalk')
|
||||
|
||||
let logs = []
|
||||
|
||||
const logLevel = () => (process.env.npm_config_loglevel || 'notice')
|
||||
const logLevel = () => {
|
||||
return (process.env.npm_config_loglevel || 'notice')
|
||||
}
|
||||
|
||||
const error = (...messages) => {
|
||||
logs.push(messages.join(' '))
|
||||
@@ -12,12 +14,14 @@ const error = (...messages) => {
|
||||
|
||||
const warn = (...messages) => {
|
||||
if (logLevel() === 'silent') return
|
||||
|
||||
logs.push(messages.join(' '))
|
||||
console.log(chalk.yellow(...messages)) // eslint-disable-line no-console
|
||||
}
|
||||
|
||||
const log = (...messages) => {
|
||||
if (logLevel() === 'silent' || logLevel() === 'warn') return
|
||||
|
||||
logs.push(messages.join(' '))
|
||||
console.log(...messages) // eslint-disable-line no-console
|
||||
}
|
||||
@@ -26,6 +30,7 @@ const log = (...messages) => {
|
||||
// on each one to allow easy unit testing for specific message
|
||||
const logLines = (text) => {
|
||||
const lines = text.split('\n')
|
||||
|
||||
R.forEach(log, lines)
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ const util = require('../util')
|
||||
|
||||
const path = () => {
|
||||
logger.log(state.getCacheDir())
|
||||
|
||||
return undefined
|
||||
}
|
||||
|
||||
|
||||
@@ -32,27 +32,32 @@ const prepend = (urlPath) => {
|
||||
const endpoint = url.resolve(getBaseUrl(), urlPath)
|
||||
const platform = os.platform()
|
||||
const arch = os.arch()
|
||||
|
||||
return `${endpoint}?platform=${platform}&arch=${arch}`
|
||||
}
|
||||
|
||||
const getUrl = (version) => {
|
||||
if (is.url(version)) {
|
||||
debug('version is already an url', version)
|
||||
|
||||
return version
|
||||
}
|
||||
|
||||
return version ? prepend(`desktop/${version}`) : prepend('desktop')
|
||||
}
|
||||
|
||||
const statusMessage = (err) =>
|
||||
(err.statusCode
|
||||
const statusMessage = (err) => {
|
||||
return (err.statusCode
|
||||
? [err.statusCode, err.statusMessage].join(' - ')
|
||||
: err.toString())
|
||||
}
|
||||
|
||||
const prettyDownloadErr = (err, version) => {
|
||||
const msg = stripIndent`
|
||||
URL: ${getUrl(version)}
|
||||
${statusMessage(err)}
|
||||
`
|
||||
|
||||
debug(msg)
|
||||
|
||||
return throwFormErrorText(errors.failedDownload)(msg)
|
||||
@@ -72,6 +77,7 @@ const downloadFromUrl = ({ url, downloadDestination, progress }) => {
|
||||
url,
|
||||
followRedirect (response) {
|
||||
const version = response.headers['x-version']
|
||||
|
||||
debug('redirect version:', version)
|
||||
if (version) {
|
||||
// set the version in options if we have one.
|
||||
@@ -136,11 +142,15 @@ const start = ({ version, downloadDestination, progress }) => {
|
||||
if (!downloadDestination) {
|
||||
la(is.unemptyString(downloadDestination), 'missing download dir', arguments)
|
||||
}
|
||||
|
||||
if (!progress) {
|
||||
progress = { onProgress: () => ({}) }
|
||||
progress = { onProgress: () => {
|
||||
return {}
|
||||
} }
|
||||
}
|
||||
|
||||
const url = getUrl(version)
|
||||
|
||||
progress.throttle = 100
|
||||
|
||||
debug('needed Cypress version: %s', version)
|
||||
|
||||
+39
-17
@@ -81,6 +81,7 @@ const downloadAndUnzip = ({ version, installDir, downloadDir }) => {
|
||||
return download.start({ version, downloadDestination, progress })
|
||||
.then((redirectVersion) => {
|
||||
if (redirectVersion) version = redirectVersion
|
||||
|
||||
debug(`finished downloading file: ${downloadDestination}`)
|
||||
})
|
||||
.then(() => {
|
||||
@@ -105,6 +106,7 @@ const downloadAndUnzip = ({ version, installDir, downloadDir }) => {
|
||||
|
||||
const cleanup = () => {
|
||||
debug('removing zip file %s', downloadDestination)
|
||||
|
||||
return fs.removeAsync(downloadDestination)
|
||||
}
|
||||
|
||||
@@ -145,12 +147,14 @@ const start = (options = {}) => {
|
||||
|
||||
const pkgVersion = util.pkgVersion()
|
||||
let needVersion = pkgVersion
|
||||
|
||||
debug('version in package.json is', needVersion)
|
||||
|
||||
// let this environment variable reset the binary version we need
|
||||
if (util.getEnv('CYPRESS_INSTALL_BINARY')) {
|
||||
|
||||
const envVarVersion = util.getEnv('CYPRESS_INSTALL_BINARY')
|
||||
|
||||
debug('using environment variable CYPRESS_INSTALL_BINARY %s', envVarVersion)
|
||||
|
||||
if (envVarVersion === '0') {
|
||||
@@ -159,6 +163,7 @@ const start = (options = {}) => {
|
||||
stripIndent`
|
||||
${chalk.yellow('Note:')} Skipping binary installation: Environment variable CYPRESS_INSTALL_BINARY = 0.`)
|
||||
logger.log()
|
||||
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
@@ -173,6 +178,7 @@ const start = (options = {}) => {
|
||||
|
||||
if (util.getEnv('CYPRESS_CACHE_FOLDER')) {
|
||||
const envCache = util.getEnv('CYPRESS_CACHE_FOLDER')
|
||||
|
||||
logger.log(
|
||||
stripIndent`
|
||||
${chalk.yellow('Note:')} Overriding Cypress cache directory to: ${chalk.cyan(envCache)}
|
||||
@@ -194,11 +200,14 @@ const start = (options = {}) => {
|
||||
${err.message}
|
||||
`)
|
||||
})
|
||||
.then(() => state.getBinaryPkgVersionAsync(binaryDir))
|
||||
.then(() => {
|
||||
return state.getBinaryPkgVersionAsync(binaryDir)
|
||||
})
|
||||
.then((binaryVersion) => {
|
||||
|
||||
if (!binaryVersion) {
|
||||
debug('no binary installed under cli version')
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -212,22 +221,24 @@ const start = (options = {}) => {
|
||||
|
||||
if (options.force) {
|
||||
debug('performing force install over existing binary')
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
if ((binaryVersion === needVersion) || !util.isSemver(needVersion)) {
|
||||
// our version matches, tell the user this is a noop
|
||||
alreadyInstalledMsg()
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
return true
|
||||
})
|
||||
.then((shouldInstall) => {
|
||||
// noop if we've been told not to download
|
||||
if (!shouldInstall) {
|
||||
debug('Not downloading or installing binary')
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -254,6 +265,7 @@ const start = (options = {}) => {
|
||||
}
|
||||
|
||||
const possibleFile = util.formAbsolutePath(needVersion)
|
||||
|
||||
debug('checking local file', possibleFile, 'cwd', process.cwd())
|
||||
|
||||
return fs.pathExistsAsync(possibleFile)
|
||||
@@ -263,16 +275,19 @@ const start = (options = {}) => {
|
||||
if (exists && path.extname(possibleFile) === '.zip') {
|
||||
return possibleFile
|
||||
}
|
||||
|
||||
return false
|
||||
})
|
||||
})
|
||||
.then((pathToLocalFile) => {
|
||||
if (pathToLocalFile) {
|
||||
const absolutePath = path.resolve(needVersion)
|
||||
|
||||
debug('found local file at', absolutePath)
|
||||
debug('skipping download')
|
||||
|
||||
const rendererOptions = getRendererOptions()
|
||||
|
||||
return new Listr([unzipTask({
|
||||
progress: {
|
||||
throttle: 100,
|
||||
@@ -292,10 +307,13 @@ const start = (options = {}) => {
|
||||
debug('preparing to download and unzip version ', needVersion, 'to path', installDir)
|
||||
|
||||
const downloadDir = os.tmpdir()
|
||||
|
||||
return downloadAndUnzip({ version: needVersion, installDir, downloadDir })
|
||||
})
|
||||
// delay 1 sec for UX, unless we are testing
|
||||
.then(() => Promise.delay(1000))
|
||||
.then(() => {
|
||||
return Promise.delay(1000)
|
||||
})
|
||||
.then(displayCompletionMsg)
|
||||
})
|
||||
}
|
||||
@@ -304,22 +322,24 @@ module.exports = {
|
||||
start,
|
||||
}
|
||||
|
||||
const unzipTask = ({ zipFilePath, installDir, progress, rendererOptions }) => ({
|
||||
title: util.titleize('Unzipping Cypress'),
|
||||
task: (ctx, task) => {
|
||||
const unzipTask = ({ zipFilePath, installDir, progress, rendererOptions }) => {
|
||||
return {
|
||||
title: util.titleize('Unzipping Cypress'),
|
||||
task: (ctx, task) => {
|
||||
// as our unzip progresses indicate the status
|
||||
progress.onProgress = progessify(task, 'Unzipping Cypress')
|
||||
progress.onProgress = progessify(task, 'Unzipping Cypress')
|
||||
|
||||
return unzip.start({ zipFilePath, installDir, progress })
|
||||
.then(() => {
|
||||
util.setTaskTitle(
|
||||
task,
|
||||
util.titleize(chalk.green('Unzipped Cypress')),
|
||||
rendererOptions.renderer
|
||||
)
|
||||
})
|
||||
},
|
||||
})
|
||||
return unzip.start({ zipFilePath, installDir, progress })
|
||||
.then(() => {
|
||||
util.setTaskTitle(
|
||||
task,
|
||||
util.titleize(chalk.green('Unzipped Cypress')),
|
||||
rendererOptions.renderer
|
||||
)
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const progessify = (task, title) => {
|
||||
// return higher order function
|
||||
@@ -342,9 +362,11 @@ const progessify = (task, title) => {
|
||||
// the default
|
||||
const getRendererOptions = () => {
|
||||
let renderer = util.isCi() ? verbose : 'default'
|
||||
|
||||
if (logger.logLevel() === 'silent') {
|
||||
renderer = 'silent'
|
||||
}
|
||||
|
||||
return {
|
||||
renderer,
|
||||
}
|
||||
|
||||
+12
-1
@@ -8,6 +8,7 @@ const util = require('../util')
|
||||
|
||||
const getPlatformExecutable = () => {
|
||||
const platform = os.platform()
|
||||
|
||||
switch (platform) {
|
||||
case 'darwin': return 'Contents/MacOS/Cypress'
|
||||
case 'linux': return 'Cypress'
|
||||
@@ -19,6 +20,7 @@ const getPlatformExecutable = () => {
|
||||
|
||||
const getPlatFormBinaryFolder = () => {
|
||||
const platform = os.platform()
|
||||
|
||||
switch (platform) {
|
||||
case 'darwin': return 'Cypress.app'
|
||||
case 'linux': return 'Cypress'
|
||||
@@ -30,6 +32,7 @@ const getPlatFormBinaryFolder = () => {
|
||||
|
||||
const getBinaryPkgPath = (binaryDir) => {
|
||||
const platform = os.platform()
|
||||
|
||||
switch (platform) {
|
||||
case 'darwin': return path.join(binaryDir, 'Contents', 'Resources', 'app', 'package.json')
|
||||
case 'linux': return path.join(binaryDir, 'resources', 'app', 'package.json')
|
||||
@@ -52,11 +55,14 @@ const getVersionDir = (version = util.pkgVersion()) => {
|
||||
|
||||
const getCacheDir = () => {
|
||||
let cache_directory = util.getCacheDir()
|
||||
|
||||
if (util.getEnv('CYPRESS_CACHE_FOLDER')) {
|
||||
const envVarCacheDir = util.getEnv('CYPRESS_CACHE_FOLDER')
|
||||
|
||||
debug('using environment variable CYPRESS_CACHE_FOLDER %s', envVarCacheDir)
|
||||
cache_directory = path.resolve(envVarCacheDir)
|
||||
}
|
||||
|
||||
return cache_directory
|
||||
}
|
||||
|
||||
@@ -67,9 +73,11 @@ const parseRealPlatformBinaryFolderAsync = (binaryPath) => {
|
||||
if (!realPath.toString().endsWith(getPlatformExecutable())) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (os.platform() === 'darwin') {
|
||||
return path.resolve(realPath, '..', '..', '..')
|
||||
}
|
||||
|
||||
return path.resolve(realPath, '..')
|
||||
})
|
||||
}
|
||||
@@ -86,6 +94,7 @@ const getBinaryStateContentsAsync = (binaryDir) => {
|
||||
return fs.readJsonAsync(getBinaryStatePath(binaryDir))
|
||||
.catch({ code: 'ENOENT' }, SyntaxError, () => {
|
||||
debug('could not read binary_state.json file')
|
||||
|
||||
return {}
|
||||
})
|
||||
}
|
||||
@@ -119,18 +128,20 @@ const getPathToExecutable = (binaryDir) => {
|
||||
|
||||
const getBinaryPkgVersionAsync = (binaryDir) => {
|
||||
const pathToPackageJson = getBinaryPkgPath(binaryDir)
|
||||
|
||||
debug('Reading binary package.json from:', pathToPackageJson)
|
||||
|
||||
return fs.pathExistsAsync(pathToPackageJson)
|
||||
.then((exists) => {
|
||||
if (!exists) {
|
||||
return null
|
||||
}
|
||||
|
||||
return fs.readJsonAsync(pathToPackageJson)
|
||||
.get('version')
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
getPathToExecutable,
|
||||
getPlatformExecutable,
|
||||
|
||||
+12
-6
@@ -27,6 +27,7 @@ const unzip = ({ zipFilePath, installDir, progress }) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
return yauzl.open(zipFilePath, (err, zipFile) => {
|
||||
if (err) return reject(err)
|
||||
|
||||
// debug('zipfile.paths:', zipFile)
|
||||
// zipFile.on('entry', debug)
|
||||
// debug(zipFile.readEntry())
|
||||
@@ -34,7 +35,6 @@ const unzip = ({ zipFilePath, installDir, progress }) => {
|
||||
|
||||
debug('zipFile entries count', total)
|
||||
|
||||
|
||||
const started = new Date()
|
||||
|
||||
let percent = 0
|
||||
@@ -59,7 +59,9 @@ const unzip = ({ zipFilePath, installDir, progress }) => {
|
||||
|
||||
const unzipWithNode = () => {
|
||||
const endFn = (err) => {
|
||||
if (err) { return reject(err) }
|
||||
if (err) {
|
||||
return reject(err)
|
||||
}
|
||||
|
||||
return resolve()
|
||||
}
|
||||
@@ -81,10 +83,9 @@ const unzip = ({ zipFilePath, installDir, progress }) => {
|
||||
const copyingFileRe = /^copying file/
|
||||
|
||||
const sp = cp.spawn('ditto', ['-xkV', zipFilePath, installDir])
|
||||
sp.on('error', () =>
|
||||
|
||||
// f-it just unzip with node
|
||||
unzipWithNode()
|
||||
)
|
||||
sp.on('error', unzipWithNode)
|
||||
|
||||
sp.on('close', (code) => {
|
||||
if (code === 0) {
|
||||
@@ -125,12 +126,17 @@ const unzip = ({ zipFilePath, installDir, progress }) => {
|
||||
|
||||
const start = ({ zipFilePath, installDir, progress }) => {
|
||||
la(is.unemptyString(installDir), 'missing installDir')
|
||||
if (!progress) progress = { onProgress: () => ({}) }
|
||||
if (!progress) {
|
||||
progress = { onProgress: () => {
|
||||
return {}
|
||||
} }
|
||||
}
|
||||
|
||||
return fs.pathExists(installDir)
|
||||
.then((exists) => {
|
||||
if (exists) {
|
||||
debug('removing existing unzipped binary', installDir)
|
||||
|
||||
return fs.removeAsync(installDir)
|
||||
}
|
||||
})
|
||||
|
||||
+31
-11
@@ -7,7 +7,6 @@ const { stripIndent } = require('common-tags')
|
||||
const Promise = require('bluebird')
|
||||
const logSymbols = require('log-symbols')
|
||||
|
||||
|
||||
const { throwFormErrorText, errors } = require('../errors')
|
||||
const util = require('../util')
|
||||
const logger = require('../logger')
|
||||
@@ -16,7 +15,9 @@ const state = require('./state')
|
||||
|
||||
const checkExecutable = (binaryDir) => {
|
||||
const executable = state.getPathToExecutable(binaryDir)
|
||||
|
||||
debug('checking if executable exists', executable)
|
||||
|
||||
return util.isExecutableAsync(executable)
|
||||
.then((isExecutable) => {
|
||||
debug('Binary is executable? :', isExecutable)
|
||||
@@ -28,6 +29,7 @@ const checkExecutable = (binaryDir) => {
|
||||
if (util.isCi()) {
|
||||
return throwFormErrorText(errors.notInstalledCI(executable))()
|
||||
}
|
||||
|
||||
return throwFormErrorText(errors.missingApp(binaryDir))(stripIndent`
|
||||
Cypress executable not found at: ${chalk.cyan(executable)}
|
||||
`)
|
||||
@@ -37,31 +39,37 @@ const checkExecutable = (binaryDir) => {
|
||||
const runSmokeTest = (binaryDir) => {
|
||||
debug('running smoke test')
|
||||
const cypressExecPath = state.getPathToExecutable(binaryDir)
|
||||
|
||||
debug('using Cypress executable %s', cypressExecPath)
|
||||
|
||||
const onXvfbError = (err) => {
|
||||
debug('caught xvfb error %s', err.message)
|
||||
|
||||
return throwFormErrorText(errors.missingXvfb)(`Caught error trying to run XVFB: "${err.message}"`)
|
||||
}
|
||||
|
||||
const onSmokeTestError = (err) => {
|
||||
debug('Smoke test failed:', err)
|
||||
|
||||
return throwFormErrorText(errors.missingDependency)(err.stderr || err.message)
|
||||
}
|
||||
|
||||
const needsXvfb = xvfb.isNeeded()
|
||||
|
||||
debug('needs XVFB?', needsXvfb)
|
||||
|
||||
const spawn = () => {
|
||||
const random = _.random(0, 1000)
|
||||
const args = ['--smoke-test', `--ping=${random}`]
|
||||
const smokeTestCommand = `${cypressExecPath} ${args.join(' ')}`
|
||||
|
||||
debug('smoke test command:', smokeTestCommand)
|
||||
|
||||
return Promise.resolve(util.exec(cypressExecPath, args))
|
||||
.catch(onSmokeTestError)
|
||||
.then((result) => {
|
||||
const smokeTestReturned = result.stdout
|
||||
|
||||
debug('smoke test stdout "%s"', smokeTestReturned)
|
||||
|
||||
if (!util.stdoutLineMatches(String(random), smokeTestReturned)) {
|
||||
@@ -85,15 +93,15 @@ const runSmokeTest = (binaryDir) => {
|
||||
return xvfb.stop()
|
||||
.catch(onXvfbError)
|
||||
})
|
||||
} else {
|
||||
return spawn()
|
||||
}
|
||||
|
||||
return spawn()
|
||||
|
||||
}
|
||||
|
||||
function testBinary (version, binaryDir) {
|
||||
debug('running binary verification check', version)
|
||||
|
||||
|
||||
logger.log(stripIndent`
|
||||
It looks like this is your first time using Cypress: ${chalk.cyan(version)}
|
||||
`)
|
||||
@@ -104,18 +112,19 @@ function testBinary (version, binaryDir) {
|
||||
// the verbose renderer else use
|
||||
// the default
|
||||
let renderer = util.isCi() ? verbose : 'default'
|
||||
|
||||
if (logger.logLevel() === 'silent') renderer = 'silent'
|
||||
|
||||
const rendererOptions = {
|
||||
renderer,
|
||||
}
|
||||
|
||||
|
||||
const tasks = new Listr([
|
||||
{
|
||||
title: util.titleize('Verifying Cypress can run', chalk.gray(binaryDir)),
|
||||
task: (ctx, task) => {
|
||||
debug('clearing out the verified version')
|
||||
|
||||
return state.clearBinaryStateAsync(binaryDir)
|
||||
.then(() => {
|
||||
return Promise.all([
|
||||
@@ -125,6 +134,7 @@ function testBinary (version, binaryDir) {
|
||||
})
|
||||
.then(() => {
|
||||
debug('write verified: true')
|
||||
|
||||
return state.writeBinaryVerifiedAsync(true, binaryDir)
|
||||
})
|
||||
.then(() => {
|
||||
@@ -151,6 +161,7 @@ const maybeVerify = (installedVersion, binaryDir, options = {}) => {
|
||||
debug('is Verified ?', isVerified)
|
||||
|
||||
let shouldVerify = !isVerified
|
||||
|
||||
// force verify if options.force
|
||||
if (options.force) {
|
||||
debug('force verify')
|
||||
@@ -182,6 +193,7 @@ const start = (options = {}) => {
|
||||
|
||||
const parseBinaryEnvVar = () => {
|
||||
const envBinaryPath = util.getEnv('CYPRESS_RUN_BINARY')
|
||||
|
||||
debug('CYPRESS_RUN_BINARY exists, =', envBinaryPath)
|
||||
logger.log(stripIndent`
|
||||
${chalk.yellow('Note:')} You have set the environment variable: ${chalk.white('CYPRESS_RUN_BINARY=')}${chalk.cyan(envBinaryPath)}:
|
||||
@@ -199,11 +211,14 @@ const start = (options = {}) => {
|
||||
`)
|
||||
}
|
||||
})
|
||||
.then(() => state.parseRealPlatformBinaryFolderAsync(envBinaryPath))
|
||||
.then(() => {
|
||||
return state.parseRealPlatformBinaryFolderAsync(envBinaryPath)
|
||||
})
|
||||
.then((envBinaryDir) => {
|
||||
if (!envBinaryDir) {
|
||||
return throwFormErrorText(errors.CYPRESS_RUN_BINARY.notValid(envBinaryPath))()
|
||||
}
|
||||
|
||||
debug('CYPRESS_RUN_BINARY has binaryDir:', envBinaryDir)
|
||||
|
||||
binaryDir = envBinaryDir
|
||||
@@ -213,20 +228,26 @@ const start = (options = {}) => {
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
return Promise.try(() => {
|
||||
debug('checking environment variables')
|
||||
if (util.getEnv('CYPRESS_RUN_BINARY')) {
|
||||
return parseBinaryEnvVar()
|
||||
}
|
||||
})
|
||||
.then(() => checkExecutable(binaryDir))
|
||||
.tap(() => debug('binaryDir is ', binaryDir))
|
||||
.then(() => state.getBinaryPkgVersionAsync(binaryDir))
|
||||
.then(() => {
|
||||
return checkExecutable(binaryDir)
|
||||
})
|
||||
.tap(() => {
|
||||
return debug('binaryDir is ', binaryDir)
|
||||
})
|
||||
.then(() => {
|
||||
return state.getBinaryPkgVersionAsync(binaryDir)
|
||||
})
|
||||
.then((binaryVersion) => {
|
||||
|
||||
if (!binaryVersion) {
|
||||
debug('no Cypress binary found for cli version ', packageVersion)
|
||||
|
||||
return throwFormErrorText(errors.missingApp(binaryDir))(`
|
||||
Cannot read binary version from: ${chalk.cyan(state.getBinaryPkgPath(binaryDir))}
|
||||
`)
|
||||
@@ -247,7 +268,6 @@ const start = (options = {}) => {
|
||||
These versions may not work properly together.
|
||||
`)
|
||||
|
||||
|
||||
logger.log()
|
||||
}
|
||||
|
||||
|
||||
+18
-4
@@ -29,6 +29,7 @@ function normalizeModuleOptions (options = {}) {
|
||||
function stdoutLineMatches (expectedLine, stdout) {
|
||||
const lines = stdout.split('\n').map(R.trim)
|
||||
const lineMatches = R.equals(expectedLine)
|
||||
|
||||
return lines.some(lineMatches)
|
||||
}
|
||||
|
||||
@@ -179,11 +180,16 @@ const util = {
|
||||
return Promise.try(() => {
|
||||
if (os.platform() === 'linux') {
|
||||
return getosAsync()
|
||||
.then((osInfo) => [osInfo.dist, osInfo.release].join(' - '))
|
||||
.catch(() => os.release())
|
||||
} else {
|
||||
return os.release()
|
||||
.then((osInfo) => {
|
||||
return [osInfo.dist, osInfo.release].join(' - ')
|
||||
})
|
||||
.catch(() => {
|
||||
return os.release()
|
||||
})
|
||||
}
|
||||
|
||||
return os.release()
|
||||
|
||||
})
|
||||
},
|
||||
|
||||
@@ -195,6 +201,7 @@ const util = {
|
||||
if (path.isAbsolute(filename)) {
|
||||
return filename
|
||||
}
|
||||
|
||||
return path.join(process.cwd(), '..', '..', filename)
|
||||
},
|
||||
|
||||
@@ -202,18 +209,25 @@ const util = {
|
||||
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
|
||||
|
||||
},
|
||||
|
||||
@@ -51,7 +51,8 @@ function makeUserPackageFile () {
|
||||
.then((json) => {
|
||||
return fs.outputJsonAsync(packageJsonDest, json, {
|
||||
spaces: 2,
|
||||
}).then(() => json) // returning package json object makes it easy to test
|
||||
})
|
||||
.return(json) // returning package json object makes it easy to test
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -7,11 +7,13 @@ const la = require('lazy-ass')
|
||||
const is = require('check-more-types')
|
||||
const R = require('ramda')
|
||||
|
||||
const hasVersion = (json) =>
|
||||
la(is.semver(json.version), 'cannot find version', json)
|
||||
const hasVersion = (json) => {
|
||||
return la(is.semver(json.version), 'cannot find version', json)
|
||||
}
|
||||
|
||||
const hasAuthor = (json) =>
|
||||
la(json.author === 'Brian Mann', 'wrong author name', json)
|
||||
const hasAuthor = (json) => {
|
||||
return la(json.author === 'Brian Mann', 'wrong author name', json)
|
||||
}
|
||||
|
||||
const changeVersion = R.assoc('version', 'x.y.z')
|
||||
|
||||
|
||||
+23
-15
@@ -19,46 +19,55 @@ describe('cli', function () {
|
||||
sinon.stub(process, 'exit')
|
||||
sinon.stub(util, 'exit')
|
||||
sinon.stub(util, 'logErrorExit1')
|
||||
this.exec = (args) => cli.init(`node test ${args}`.split(' '))
|
||||
this.exec = (args) => {
|
||||
return cli.init(`node test ${args}`.split(' '))
|
||||
}
|
||||
})
|
||||
|
||||
context('unknown option', () => {
|
||||
// note it shows help for that specific command
|
||||
it('shows help', () =>
|
||||
execa('bin/cypress', ['open', '--foo']).then((result) => {
|
||||
it('shows help', () => {
|
||||
return execa('bin/cypress', ['open', '--foo']).then((result) => {
|
||||
snapshot('shows help for open --foo', result)
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
it('shows help for run command', () =>
|
||||
execa('bin/cypress', ['run', '--foo']).then((result) => {
|
||||
it('shows help for run command', () => {
|
||||
return execa('bin/cypress', ['run', '--foo']).then((result) => {
|
||||
snapshot('shows help for run --foo', result)
|
||||
})
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
context('help command', () => {
|
||||
it('shows help', () =>
|
||||
execa('bin/cypress', ['help']).then(snapshot)
|
||||
it('shows help', () => {
|
||||
return execa('bin/cypress', ['help']).then(snapshot)
|
||||
}
|
||||
)
|
||||
|
||||
it('shows help for -h', () =>
|
||||
execa('bin/cypress', ['-h']).then(snapshot)
|
||||
it('shows help for -h', () => {
|
||||
return execa('bin/cypress', ['-h']).then(snapshot)
|
||||
}
|
||||
)
|
||||
|
||||
it('shows help for --help', () =>
|
||||
execa('bin/cypress', ['--help']).then(snapshot)
|
||||
it('shows help for --help', () => {
|
||||
return execa('bin/cypress', ['--help']).then(snapshot)
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
context('unknown command', () => {
|
||||
it('shows usage and exits', () =>
|
||||
execa('bin/cypress', ['foo']).then(snapshot)
|
||||
it('shows usage and exits', () => {
|
||||
return execa('bin/cypress', ['foo']).then(snapshot)
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
context('cypress version', function () {
|
||||
const binaryDir = '/binary/dir'
|
||||
|
||||
beforeEach(function () {
|
||||
sinon.stub(state, 'getBinaryDir').returns(binaryDir)
|
||||
})
|
||||
@@ -136,6 +145,7 @@ describe('cli', function () {
|
||||
|
||||
it('run.start with options + catches errors', function (done) {
|
||||
const err = new Error('foo')
|
||||
|
||||
run.start.rejects(err)
|
||||
this.exec('run')
|
||||
|
||||
@@ -261,7 +271,6 @@ describe('cli', function () {
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
it('install calls install.start without forcing', function () {
|
||||
sinon.stub(install, 'start').resolves()
|
||||
this.exec('install')
|
||||
@@ -287,7 +296,6 @@ describe('cli', function () {
|
||||
})
|
||||
context('cypress verify', function () {
|
||||
|
||||
|
||||
it('verify calls verify.start with force: true', function () {
|
||||
sinon.stub(verify, 'start').resolves()
|
||||
this.exec('verify')
|
||||
|
||||
@@ -21,6 +21,7 @@ describe('cypress', function () {
|
||||
const getCallArgs = R.path(['lastCall', 'args', 0])
|
||||
const getStartArgs = () => {
|
||||
expect(open.start).to.be.called
|
||||
|
||||
return getCallArgs(open.start)
|
||||
}
|
||||
|
||||
@@ -48,10 +49,12 @@ describe('cypress', function () {
|
||||
|
||||
context('.run', function () {
|
||||
let outputPath
|
||||
|
||||
beforeEach(function () {
|
||||
outputPath = path.join(os.tmpdir(), 'cypress/monorepo/cypress_spec/output.json')
|
||||
sinon.stub(tmp, 'fileAsync').resolves(outputPath)
|
||||
sinon.stub(run, 'start').resolves()
|
||||
|
||||
return fs.outputJsonAsync(outputPath, {
|
||||
code: 0,
|
||||
failingTests: [],
|
||||
@@ -62,10 +65,12 @@ describe('cypress', function () {
|
||||
const normalizeCallArgs = (args) => {
|
||||
expect(args.outputPath).to.equal(outputPath)
|
||||
delete args.outputPath
|
||||
|
||||
return args
|
||||
}
|
||||
const getStartArgs = () => {
|
||||
expect(run.start).to.be.called
|
||||
|
||||
return normalizeCallArgs(getCallArgs(run.start))
|
||||
}
|
||||
|
||||
|
||||
@@ -14,14 +14,16 @@ describe('errors', function () {
|
||||
})
|
||||
|
||||
describe('individual', () => {
|
||||
it('has the following errors', () =>
|
||||
snapshot(Object.keys(errors))
|
||||
it('has the following errors', () => {
|
||||
return snapshot(Object.keys(errors))
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
context('.errors.formErrorText', function () {
|
||||
it('returns fully formed text message', () =>
|
||||
snapshot(formErrorText(missingXvfb))
|
||||
it('returns fully formed text message', () => {
|
||||
return snapshot(formErrorText(missingXvfb))
|
||||
}
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -24,24 +24,28 @@ describe('util', () => {
|
||||
|
||||
it('matches entire output', () => {
|
||||
const line = '444'
|
||||
|
||||
expect(stdoutLineMatches(line, line)).to.be.true
|
||||
})
|
||||
|
||||
it('matches a line in output', () => {
|
||||
const line = '444'
|
||||
const stdout = ['start', line, 'something else'].join('\n')
|
||||
|
||||
expect(stdoutLineMatches(line, stdout)).to.be.true
|
||||
})
|
||||
|
||||
it('matches a trimmed line in output', () => {
|
||||
const line = '444'
|
||||
const stdout = ['start', ` ${line} `, 'something else'].join('\n')
|
||||
|
||||
expect(stdoutLineMatches(line, stdout)).to.be.true
|
||||
})
|
||||
|
||||
it('does not find match', () => {
|
||||
const line = '445'
|
||||
const stdout = ['start', '444', 'something else'].join('\n')
|
||||
|
||||
expect(stdoutLineMatches(line, stdout)).to.be.false
|
||||
})
|
||||
})
|
||||
@@ -53,6 +57,7 @@ describe('util', () => {
|
||||
const options = {
|
||||
foo: 'bar',
|
||||
}
|
||||
|
||||
snapshot('others_unchanged', normalizeModuleOptions(options))
|
||||
})
|
||||
|
||||
@@ -60,6 +65,7 @@ describe('util', () => {
|
||||
const options = {
|
||||
env: 'foo=bar',
|
||||
}
|
||||
|
||||
snapshot('env_as_string', normalizeModuleOptions(options))
|
||||
})
|
||||
|
||||
@@ -71,6 +77,7 @@ describe('util', () => {
|
||||
host: 'kevin.dev.local',
|
||||
},
|
||||
}
|
||||
|
||||
snapshot('env_as_object', normalizeModuleOptions(options))
|
||||
})
|
||||
|
||||
@@ -81,6 +88,7 @@ describe('util', () => {
|
||||
watchForFileChanges: false,
|
||||
},
|
||||
}
|
||||
|
||||
snapshot('config_as_object', normalizeModuleOptions(options))
|
||||
})
|
||||
|
||||
@@ -91,6 +99,7 @@ describe('util', () => {
|
||||
toConsole: true,
|
||||
},
|
||||
}
|
||||
|
||||
snapshot('reporter_options_as_object', normalizeModuleOptions(options))
|
||||
})
|
||||
|
||||
@@ -100,6 +109,7 @@ describe('util', () => {
|
||||
'a', 'b', 'c',
|
||||
],
|
||||
}
|
||||
|
||||
snapshot('spec_as_array', normalizeModuleOptions(options))
|
||||
})
|
||||
|
||||
@@ -107,6 +117,7 @@ describe('util', () => {
|
||||
const options = {
|
||||
spec: 'x,y,z',
|
||||
}
|
||||
|
||||
snapshot('spec_as_string', normalizeModuleOptions(options))
|
||||
})
|
||||
})
|
||||
@@ -250,6 +261,7 @@ describe('util', () => {
|
||||
|
||||
it('does nothing if debug is not enabled', () => {
|
||||
const log = sinon.spy()
|
||||
|
||||
log.enabled = false
|
||||
util.printNodeOptions(log)
|
||||
expect(log).not.have.been.called
|
||||
@@ -257,6 +269,7 @@ describe('util', () => {
|
||||
|
||||
it('prints message when debug is enabled', () => {
|
||||
const log = sinon.spy()
|
||||
|
||||
log.enabled = true
|
||||
util.printNodeOptions(log)
|
||||
expect(log).to.be.calledWith('NODE_OPTIONS is not set')
|
||||
@@ -270,6 +283,7 @@ describe('util', () => {
|
||||
|
||||
it('does nothing if debug is not enabled', () => {
|
||||
const log = sinon.spy()
|
||||
|
||||
log.enabled = false
|
||||
util.printNodeOptions(log)
|
||||
expect(log).not.have.been.called
|
||||
@@ -277,6 +291,7 @@ describe('util', () => {
|
||||
|
||||
it('prints value when debug is enabled', () => {
|
||||
const log = sinon.spy()
|
||||
|
||||
log.enabled = true
|
||||
util.printNodeOptions(log)
|
||||
expect(log).to.be.calledWith('NODE_OPTIONS=%s', 'foo')
|
||||
@@ -287,6 +302,7 @@ describe('util', () => {
|
||||
describe('.getOsVersionAsync', () => {
|
||||
let util
|
||||
let getos = sinon.stub().resolves(['distro-release'])
|
||||
|
||||
beforeEach(() => {
|
||||
util = proxyquire(`${lib}/util`, { getos })
|
||||
})
|
||||
|
||||
@@ -43,7 +43,9 @@ function throwIfFnNotStubbed (stub, method) {
|
||||
err.stack = _
|
||||
.chain(err.stack)
|
||||
.split('\n')
|
||||
.reject((str) => _.includes(str, 'sinon'))
|
||||
.reject((str) => {
|
||||
return _.includes(str, 'sinon')
|
||||
})
|
||||
.join('\n')
|
||||
.value()
|
||||
|
||||
@@ -52,6 +54,7 @@ function throwIfFnNotStubbed (stub, method) {
|
||||
}
|
||||
|
||||
const $stub = sinon.stub
|
||||
|
||||
sinon.stub = function (obj, method) {
|
||||
/* eslint-disable prefer-rest-params */
|
||||
const stub = $stub.apply(this, arguments)
|
||||
|
||||
Reference in New Issue
Block a user