diff --git a/.eslintrc b/.eslintrc index ae9c088641..0a9455e9f1 100644 --- a/.eslintrc +++ b/.eslintrc @@ -2,8 +2,67 @@ "extends": [ "plugin:cypress-dev/general" ], + "rules": { + "no-multiple-empty-lines": ["error", { "max": 1 } ], + "no-else-return": [ "error", { "allowElseIf": false } ], + "brace-style": ["error", "1tbs", { "allowSingleLine": false }], + "no-unneeded-ternary": ["error"], + "array-bracket-newline": ["error", "consistent"], + "arrow-body-style": ["error", "always"], + "padding-line-between-statements": [ + "error", + { + "blankLine": "always", + "prev": "*", + "next": "return" + }, + { + "blankLine": "always", + "prev": [ + "const", + "let", + "var", + "if", + "while", + "export", + "cjs-export", + "import", + "cjs-import" + ], + "next": "*" + }, + { + "blankLine": "any", + "prev": [ + "const", + "let", + "var", + "import", + "cjs-import" + ], + "next": [ + "const", + "let", + "var", + "import", + "cjs-import" + ] + } + ] + }, "env": { "es6": true, "node": true + }, + "parserOptions": { + "ecmaFeatures": { + "legacyDecorators": true + } + }, + "overrides": { + "files": ["**/*.jsx"], + "rules": { + "arrow-body-style": "off", + } } } diff --git a/.gitignore b/.gitignore index 2ea3a4839a..15ccb3d6fc 100644 --- a/.gitignore +++ b/.gitignore @@ -8,12 +8,12 @@ dist dist-* build .history -.vscode .publish _test-output cypress.zip tmp/ .nyc_output +.vscode/settings.json # from extension Cached Theme.pak diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000000..06947329b1 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,32 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "attach", + "name": "Attach by Process ID", + "processId": "${command:PickProcess}" + }, + { + "type": "node", + "request": "launch", + "name": "test: active", + "runtimeExecutable": "npm", + "runtimeArgs": [ + "run", + "test-debug-package", + ], + "args": [ + "${file}" + ], + "port": 5566, + "console": "integratedTerminal" + }, + { + "type": "node", + "request": "attach", + "name": "electron", + "port": 5567, + }, + ] +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000000..e0be7458b6 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,29 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "decaffeinate-bulk file", + "type": "shell", + "command": "npm run decaffeinate-bulk -- --file ${file} convert", + "problemMatcher": [] + }, + { + "label": "decaffeinate-bulk dir", + "type": "shell", + "command": "npm run decaffeinate-bulk -- --dir ${fileDirname} convert", + "problemMatcher": [] + }, + { + "label": "decaffeinate", + "type": "shell", + "command": "npm run decaffeinate -- ${file}" + }, + { + "label": "jscodeshift", + "type": "shell", + "command": "npm run jscodeshift -- ${file}" + } + ] +} diff --git a/.vscode/terminals.json b/.vscode/terminals.json new file mode 100644 index 0000000000..6f82f2758c --- /dev/null +++ b/.vscode/terminals.json @@ -0,0 +1,56 @@ +{ + "autorun": false, + "terminals": [ + { + "name": "cypress open", + "focus": true, + "onlySingle": true, + "execute": true, + "command": "npm run cypress:open" + }, + { + "name": "cypress run", + "focus": true, + "onlySingle": true, + "execute": false, + "command": "npm run cypress:run -- --project ../project" + }, + { + "name": "packages/server test-watch", + "focus": true, + "onlySingle": true, + "execute": false, + "cwd": "[workspaceFolder]/packages/server", + "command": "npm run test-watch -- [file]" + }, + { + "name": "packages/server test-e2e", + "focus": true, + "onlySingle": true, + "execute": false, + "cwd": "[workspaceFolder]/packages/server", + "command": "npm run test-e2e -- --spec name" + }, + { + "name": "packages/runner watch", + "focus": true, + "onlySingle": true, + "cwd": "[workspaceFolder]/packages/runner", + "command": "npm run watch" + }, + { + "name": "packages/driver cypress open", + "focus": true, + "onlySingle": true, + "cwd": "[workspaceFolder]/packages/driver", + "command": "npm run cypress:open" + }, + { + "name": "packages/desktop-gui cypress open", + "focus": true, + "onlySingle": true, + "cwd": "[workspaceFolder]/packages/desktop-gui", + "command": "npm run cypress:open" + } + ] +} diff --git a/bulk-decaffeinate.config.js b/bulk-decaffeinate.config.js new file mode 100644 index 0000000000..7f81e1d7f5 --- /dev/null +++ b/bulk-decaffeinate.config.js @@ -0,0 +1,17 @@ +const path = require('path') + +module.exports = { + decaffeinateArgs: [ + '--use-cs2', + '--loose', + ], + jscodeshiftScripts: [ + path.resolve('node_modules', 'js-codemod', 'transforms', 'arrow-function.js'), + path.resolve('node_modules', 'js-codemod', 'transforms', 'arrow-function-arguments.js'), + path.resolve('node_modules', 'js-codemod', 'transforms', 'no-vars.js'), + path.resolve('node_modules', 'jscodemods', 'transforms', 'fix-class-assign-construct.js'), + path.resolve('node_modules', 'jscodemods', 'decaffeinate', 'fix-multi-assign-class-export.js'), + path.resolve('node_modules', 'jscodemods', 'decaffeinate', 'fix-implicit-return-assignment.js'), + path.resolve('node_modules', 'jscodemods', 'decaffeinate', 'fix-existential-conditional-assignment.js'), + ], +} diff --git a/cli/lib/cli.js b/cli/lib/cli.js index ca72d301cd..48914b732e 100644 --- a/cli/lib/cli.js +++ b/cli/lib/cli.js @@ -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) }, } diff --git a/cli/lib/cypress.js b/cli/lib/cypress.js index 9fca6b4a54..ad49ce60b1 100644 --- a/cli/lib/cypress.js +++ b/cli/lib/cypress.js @@ -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 }) }) diff --git a/cli/lib/errors.js b/cli/lib/errors.js index 54ed4d7bb3..9e952ea0f7 100644 --- a/cli/lib/errors.js +++ b/cli/lib/errors.js @@ -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 = { diff --git a/cli/lib/exec/spawn.js b/cli/lib/exec/spawn.js index a6f6364c7d..e49f0059cd 100644 --- a/cli/lib/exec/spawn.js +++ b/cli/lib/exec/spawn.js @@ -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() + }, } diff --git a/cli/lib/exec/versions.js b/cli/lib/exec/versions.js index dd299a1f2a..58ed2e1726 100644 --- a/cli/lib/exec/versions.js +++ b/cli/lib/exec/versions.js @@ -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) diff --git a/cli/lib/exec/xvfb.js b/cli/lib/exec/xvfb.js index 148525b2f0..6d66104a30 100644 --- a/cli/lib/exec/xvfb.js +++ b/cli/lib/exec/xvfb.js @@ -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) diff --git a/cli/lib/logger.js b/cli/lib/logger.js index 7da7df0944..0ab54f3111 100644 --- a/cli/lib/logger.js +++ b/cli/lib/logger.js @@ -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) } diff --git a/cli/lib/tasks/cache.js b/cli/lib/tasks/cache.js index a194e80402..0c53f353f0 100644 --- a/cli/lib/tasks/cache.js +++ b/cli/lib/tasks/cache.js @@ -5,6 +5,7 @@ const util = require('../util') const path = () => { logger.log(state.getCacheDir()) + return undefined } diff --git a/cli/lib/tasks/download.js b/cli/lib/tasks/download.js index 741a1e674c..39b6bd48bc 100644 --- a/cli/lib/tasks/download.js +++ b/cli/lib/tasks/download.js @@ -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) diff --git a/cli/lib/tasks/install.js b/cli/lib/tasks/install.js index 07ff631ca4..62f7780803 100644 --- a/cli/lib/tasks/install.js +++ b/cli/lib/tasks/install.js @@ -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, } diff --git a/cli/lib/tasks/state.js b/cli/lib/tasks/state.js index 1104f00cc4..eb887c46b9 100644 --- a/cli/lib/tasks/state.js +++ b/cli/lib/tasks/state.js @@ -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, diff --git a/cli/lib/tasks/unzip.js b/cli/lib/tasks/unzip.js index 8b443672ce..a7a7124290 100644 --- a/cli/lib/tasks/unzip.js +++ b/cli/lib/tasks/unzip.js @@ -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) } }) diff --git a/cli/lib/tasks/verify.js b/cli/lib/tasks/verify.js index bf1f52ed41..ac6341731e 100644 --- a/cli/lib/tasks/verify.js +++ b/cli/lib/tasks/verify.js @@ -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() } diff --git a/cli/lib/util.js b/cli/lib/util.js index d342c20917..634bb4a854 100644 --- a/cli/lib/util.js +++ b/cli/lib/util.js @@ -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 }, diff --git a/cli/scripts/build.js b/cli/scripts/build.js index cd2f566e74..7f593faf36 100644 --- a/cli/scripts/build.js +++ b/cli/scripts/build.js @@ -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 }) } diff --git a/cli/test/lib/build_spec.js b/cli/test/lib/build_spec.js index f2b3367748..c7516861ca 100644 --- a/cli/test/lib/build_spec.js +++ b/cli/test/lib/build_spec.js @@ -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') diff --git a/cli/test/lib/cli_spec.js b/cli/test/lib/cli_spec.js index 4f22b68659..c88e179307 100644 --- a/cli/test/lib/cli_spec.js +++ b/cli/test/lib/cli_spec.js @@ -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') diff --git a/cli/test/lib/cypress_spec.js b/cli/test/lib/cypress_spec.js index b60cd50075..391f3dfe94 100644 --- a/cli/test/lib/cypress_spec.js +++ b/cli/test/lib/cypress_spec.js @@ -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)) } diff --git a/cli/test/lib/errors_spec.js b/cli/test/lib/errors_spec.js index f12aa57068..9daa55d3ca 100644 --- a/cli/test/lib/errors_spec.js +++ b/cli/test/lib/errors_spec.js @@ -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)) + } ) }) }) diff --git a/cli/test/lib/util_spec.js b/cli/test/lib/util_spec.js index 1cd58ee38c..1687d5c9c2 100644 --- a/cli/test/lib/util_spec.js +++ b/cli/test/lib/util_spec.js @@ -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 }) }) diff --git a/cli/test/spec_helper.js b/cli/test/spec_helper.js index 45289f98db..7c3c6ef509 100644 --- a/cli/test/spec_helper.js +++ b/cli/test/spec_helper.js @@ -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) diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 0000000000..5f354257ac --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,15 @@ +{ + "exclude": [ + "**/.git/**", + "**/.cache/**", + "**/.history/**", + "**/.projects/**", + "**/.publish/**", + "**/node_modules/**", + "**/app/**", + "**/build/**", + "**/dist/**", + "**/dist-test/**", + "**/.cy/**" + ] +} diff --git a/package.json b/package.json index eab1d91f23..16081e36ee 100644 --- a/package.json +++ b/package.json @@ -12,8 +12,15 @@ "start": "node ./cli/bin/cypress open --dev --global", "cypress:open": "node ./cli/bin/cypress open --dev --global", "cypress:run": "node ./cli/bin/cypress run --dev", + "cypress:open:debug": "node ./scripts/debug.js cypress:open", + "cypress:run:debug": "node ./scripts/debug.js cypress:run", "dev": "node ./scripts/start.js", + "dev-debug": "node ./scripts/debug.js dev", "watch": "npm run all watch", + "test-debug-package": "node ./scripts/test-debug-package.js", + "jscodeshift": "jscodeshift -t ./node_modules/js-codemod/transforms/arrow-function-arguments.js", + "decaffeinate": "decaffeinate --use-cs2 --loose", + "decaffeinate-bulk": "bulk-decaffeinate", "check-deps": "node ./scripts/check-deps.js --verbose", "check-deps-pre": "node ./scripts/check-deps.js --verbose --prescript", "prebuild": "npm run check-deps-pre", @@ -23,7 +30,7 @@ "link": "node ./scripts/link-packages.js", "install-filtered": "npm run all install -- --package $(node ./scripts/check-deps.js --list)", "postinstall": "echo 'root postinstall' && npm run link && npm run all install && npm run build", - "clean-deps": "npm run all clean-deps", + "clean-deps": "npm run all clean-deps && rm -rf node_modules", "docker": "./scripts/run-docker-local.sh", "lint-js": "eslint --fix scripts/*.js packages/ts/*.js cli/*.js cli/**/*.js", "lint-coffee": "coffeelint scripts/**/*.coffee", @@ -59,9 +66,10 @@ "@cypress/questions-remain": "^1.0.1", "ansi-styles": "^3.1.0", "ascii-table": "0.0.9", - "babel-eslint": "^7.2.3", + "babel-eslint": "^10.0.1", "bluebird": "^3.4.5", "bluebird-retry": "^0.11.0", + "bulk-decaffeinate": "^3.3.1", "chai": "^4.0.2", "chalk": "^2.0.1", "check-dependencies": "1.1.0", @@ -72,9 +80,10 @@ "common-tags": "^1.8.0", "console.table": "^0.9.1", "debug": "3.1.0", + "decaffeinate": "^4.8.8", "del": "^3.0.0", "electron-osx-sign": "^0.4.6", - "eslint": "4.13.1", + "eslint": "4.19.1", "eslint-plugin-cypress": "^2.0.1", "eslint-plugin-cypress-dev": "^1.1.1", "eslint-plugin-mocha": "^4.11.0", @@ -82,6 +91,7 @@ "execa": "^0.8.0", "execa-wrap": "^1.1.0", "filesize": "^3.5.10", + "find-package-json": "^1.1.0", "fs-extra": "^7.0.0", "gift": "^0.10.0", "gulp": "^3.9.1", @@ -93,6 +103,9 @@ "human-interval": "^0.1.6", "husky": "^0.14.3", "inquirer": "^3.1.1", + "js-codemod": "cpojer/js-codemod#29dafed", + "jscodemods": "cypress-io/jscodemods#01b546e", + "jscodeshift": "^0.5.1", "konfig": "^0.2.1", "lazy-ass": "^1.6.0", "lint-staged": "^4.1.3", diff --git a/packages/coffee/package.json b/packages/coffee/package.json index 9dec5d10c1..9f95f0791e 100644 --- a/packages/coffee/package.json +++ b/packages/coffee/package.json @@ -9,6 +9,7 @@ "register.js" ], "scripts": { - "check-deps": "node ../../scripts/check-deps.js --verbose" + "check-deps": "node ../../scripts/check-deps.js --verbose", + "clean-deps": "rm -rf node_modules" } } diff --git a/packages/desktop-gui/src/app/intro.jsx b/packages/desktop-gui/src/app/intro.jsx index 58b3191a25..e9cf2fa2e2 100644 --- a/packages/desktop-gui/src/app/intro.jsx +++ b/packages/desktop-gui/src/app/intro.jsx @@ -92,11 +92,13 @@ class Default extends Component { _dragover = () => { this._setDragging(true) + return false } _dragleave = () => { this._setDragging(false) + return false } @@ -105,6 +107,7 @@ class Default extends Component { this._setDragging(false) const file = _.get(e, 'dataTransfer.files[0]') + if (!file) return false this._addProject(file.path) @@ -118,6 +121,7 @@ class Default extends Component { _nope (e) { e.preventDefault() + return false } diff --git a/packages/desktop-gui/src/app/nav.jsx b/packages/desktop-gui/src/app/nav.jsx index 918e688143..817e231ff2 100644 --- a/packages/desktop-gui/src/app/nav.jsx +++ b/packages/desktop-gui/src/app/nav.jsx @@ -122,14 +122,15 @@ export default class Nav extends Component { {' '}{authStore.user.displayName} ) - } else { - return ( - - {' '} - Log Out - - ) } + + return ( + + {' '} + Log Out + + ) + } _select = (item) => { diff --git a/packages/desktop-gui/src/auth/auth-api.js b/packages/desktop-gui/src/auth/auth-api.js index e91c050d29..aa0396b932 100644 --- a/packages/desktop-gui/src/auth/auth-api.js +++ b/packages/desktop-gui/src/auth/auth-api.js @@ -7,6 +7,7 @@ class AuthApi { ipc.getCurrentUser() .then((user) => { authStore.setUser(user) + // mobx can trigger a synchronous re-render, which executes // componentDidMount, etc in other components, making bluebird // think another promise was created but not returned @@ -35,6 +36,7 @@ class AuthApi { }) .then((user) => { authStore.setUser(user) + return null }) .catch({ alreadyOpen: true }, () => {}) diff --git a/packages/desktop-gui/src/auth/auth-store.js b/packages/desktop-gui/src/auth/auth-store.js index 8d9b1e0c86..f3bf345d49 100644 --- a/packages/desktop-gui/src/auth/auth-store.js +++ b/packages/desktop-gui/src/auth/auth-store.js @@ -20,6 +20,7 @@ class AuthStore { @action setUser (user) { const isValid = user && user.authToken + this.user = isValid ? new User(user) : null } } diff --git a/packages/desktop-gui/src/auth/login-form.jsx b/packages/desktop-gui/src/auth/login-form.jsx index 6856820bb4..eda3e90441 100644 --- a/packages/desktop-gui/src/auth/login-form.jsx +++ b/packages/desktop-gui/src/auth/login-form.jsx @@ -40,18 +40,20 @@ class LoginForm extends Component { Logging in... ) - } else { - return ( - - {' '} - Log In with GitHub - - ) } + + return ( + + {' '} + Log In with GitHub + + ) + } _error () { const error = this.state.error + if (!error) return null return ( diff --git a/packages/desktop-gui/src/dropdown/dropdown.jsx b/packages/desktop-gui/src/dropdown/dropdown.jsx index b0a6ad2e99..fdab8d9152 100644 --- a/packages/desktop-gui/src/dropdown/dropdown.jsx +++ b/packages/desktop-gui/src/dropdown/dropdown.jsx @@ -52,13 +52,14 @@ class Dropdown extends Component { {this._buttonContent()} ) - } else { - return ( - - {this._buttonContent()} - - ) } + + return ( + + {this._buttonContent()} + + ) + } _buttonContent () { diff --git a/packages/desktop-gui/src/duration-timer/duration-timer-store.js b/packages/desktop-gui/src/duration-timer/duration-timer-store.js index 82e8ab8d80..e51a6628f6 100644 --- a/packages/desktop-gui/src/duration-timer/duration-timer-store.js +++ b/packages/desktop-gui/src/duration-timer/duration-timer-store.js @@ -22,11 +22,14 @@ class DurationTimer { this.timer.milliseconds = moment().diff(this.startTime) - this.timerId = setTimeout(() => this.measure(), 10) + this.timerId = setTimeout(() => { + return this.measure() + }, 10) } @action startTimer () { if (this.isRunning) return + this.isRunning = true this.measure() } diff --git a/packages/desktop-gui/src/footer/footer.jsx b/packages/desktop-gui/src/footer/footer.jsx index 90751e7a6e..367d4da182 100644 --- a/packages/desktop-gui/src/footer/footer.jsx +++ b/packages/desktop-gui/src/footer/footer.jsx @@ -19,6 +19,7 @@ export default class Footer extends Component { _openChangelog (e) { e.preventDefault() + return ipc.externalOpen('https://on.cypress.io/changelog') } } diff --git a/packages/desktop-gui/src/lib/app-store.js b/packages/desktop-gui/src/lib/app-store.js index 529c4178ea..547067b562 100644 --- a/packages/desktop-gui/src/lib/app-store.js +++ b/packages/desktop-gui/src/lib/app-store.js @@ -28,8 +28,11 @@ class AppStore { @action set (props) { if (props.cypressEnv != null) this.cypressEnv = props.cypressEnv + if (props.os != null) this.os = props.os + if (props.projectRoot != null) this.projectRoot = props.projectRoot + if (props.version != null) this.version = this.newVersion = props.version } diff --git a/packages/desktop-gui/src/lib/handle-global-errors.js b/packages/desktop-gui/src/lib/handle-global-errors.js index c468f76363..8c27a9f400 100644 --- a/packages/desktop-gui/src/lib/handle-global-errors.js +++ b/packages/desktop-gui/src/lib/handle-global-errors.js @@ -19,6 +19,7 @@ const handleGlobalErrors = () => { window.onunhandledrejection = (event) => { const reason = event && event.reason + sendErr(reason || event) } } diff --git a/packages/desktop-gui/src/lib/ipc-bus.js b/packages/desktop-gui/src/lib/ipc-bus.js index fbdbad52a9..d92ed8039e 100644 --- a/packages/desktop-gui/src/lib/ipc-bus.js +++ b/packages/desktop-gui/src/lib/ipc-bus.js @@ -11,7 +11,9 @@ const addMsg = (id, event, fn) => { } const removeMsgsByEvent = (event) => { - msgs = _.omitBy(msgs, (msg) => msg.event === event) + msgs = _.omitBy(msgs, (msg) => { + return msg.event === event + }) } const removeMsgById = (id) => { @@ -20,6 +22,7 @@ const removeMsgById = (id) => { const createIpc = () => { console.warn('Missing "ipc". Polyfilling in development mode.') // eslint-disable-line no-console + return { on () {}, send () {}, @@ -39,7 +42,9 @@ ipc.on('response', (event, obj = {}) => { }) const ipcBus = (...args) => { - if (args.length === 0) { return msgs } + if (args.length === 0) { + return msgs + } // our ipc interface can either be a standard // node callback or a promise interface @@ -58,12 +63,15 @@ const ipcBus = (...args) => { const lastArg = args.pop() let fn + // enable the last arg to be a function // which changes this interface from being // a promise to just calling the callback // function directly if (lastArg && _.isFunction(lastArg)) { - fn = () => addMsg(id, event, lastArg) + fn = () => { + return addMsg(id, event, lastArg) + } } else { // push it back onto the array args.push(lastArg) diff --git a/packages/desktop-gui/src/lib/ipc.js b/packages/desktop-gui/src/lib/ipc.js index 210c9f1b3a..6194e6e507 100644 --- a/packages/desktop-gui/src/lib/ipc.js +++ b/packages/desktop-gui/src/lib/ipc.js @@ -22,7 +22,9 @@ const register = (eventName, isPromiseApi = true) => { return ipcBus(eventName, ...args) } if (!isPromiseApi) { - ipc[_.camelCase(`off:${eventName}`)] = () => ipcBus.off(eventName) + ipc[_.camelCase(`off:${eventName}`)] = () => { + return ipcBus.off(eventName) + } } } diff --git a/packages/desktop-gui/src/lib/local-data.js b/packages/desktop-gui/src/lib/local-data.js index 4027b6f12d..f0d2f705ef 100644 --- a/packages/desktop-gui/src/lib/local-data.js +++ b/packages/desktop-gui/src/lib/local-data.js @@ -1,6 +1,7 @@ export default { get (key) { const value = localStorage[key] + return value && JSON.parse(value) }, diff --git a/packages/desktop-gui/src/lib/routing.jsx b/packages/desktop-gui/src/lib/routing.jsx index 963e3e42d9..522050382e 100644 --- a/packages/desktop-gui/src/lib/routing.jsx +++ b/packages/desktop-gui/src/lib/routing.jsx @@ -6,6 +6,7 @@ const Link = ({ children, to, onClick }) => { const navigate = (e) => { e.preventDefault() if (onClick) onClick() + to.navigate() } diff --git a/packages/desktop-gui/src/lib/utils.js b/packages/desktop-gui/src/lib/utils.js index 01ef6ea7bf..7035bfd55d 100644 --- a/packages/desktop-gui/src/lib/utils.js +++ b/packages/desktop-gui/src/lib/utils.js @@ -48,7 +48,9 @@ module.exports = { gravatarUrl: (email) => { let opts = { size: '13', default: 'mm' } - if (!email) { opts.forcedefault = 'y' } + if (!email) { + opts.forcedefault = 'y' + } return gravatar.url(email, opts, true) }, @@ -78,6 +80,7 @@ module.exports = { stripLeadingCyDirs (spec) { if (!spec) return null + // remove leading 'cypress/integration' from spec return spec.replace(cyDirRegex, '') }, diff --git a/packages/desktop-gui/src/organizations/organizations-api.js b/packages/desktop-gui/src/organizations/organizations-api.js index 6987b815bd..6264a02c83 100644 --- a/packages/desktop-gui/src/organizations/organizations-api.js +++ b/packages/desktop-gui/src/organizations/organizations-api.js @@ -7,11 +7,13 @@ const getOrgs = () => { ipc.getOrgs() .then((orgs = []) => { orgsStore.setOrgs(orgs) + return null }) .catch(ipc.isUnauthed, ipc.handleUnauthed) .catch((err) => { orgsStore.setError(err) + return null }) diff --git a/packages/desktop-gui/src/organizations/organizations-store.js b/packages/desktop-gui/src/organizations/organizations-store.js index 9d2c237e58..5e8667e73c 100644 --- a/packages/desktop-gui/src/organizations/organizations-store.js +++ b/packages/desktop-gui/src/organizations/organizations-store.js @@ -10,9 +10,11 @@ export class Orgs { @observable isLoaded = false @action setOrgs (orgs) { - this.orgs = _.map(orgs, (org) => ( - new Org(org) - )) + this.orgs = _.map(orgs, (org) => { + return ( + new Org(org) + ) + }) this.isLoading = false this.isLoaded = true diff --git a/packages/desktop-gui/src/project/onboarding.jsx b/packages/desktop-gui/src/project/onboarding.jsx index d1ecd60e4d..992f0131fa 100644 --- a/packages/desktop-gui/src/project/onboarding.jsx +++ b/packages/desktop-gui/src/project/onboarding.jsx @@ -86,8 +86,10 @@ class OnBoarding extends Component { files = _.sortBy(files, 'name') const notFolders = _.every(files, (file) => !file.children) + if (notFolders && files.length > 3) { const numHidden = files.length - 2 + files = files.slice(0, 2).concat({ name: `... ${numHidden} more files ...`, more: true }) } @@ -104,16 +106,17 @@ class OnBoarding extends Component { ) - } else { - return ( -
The request for runs timed out.
diff --git a/packages/desktop-gui/src/runs/permission-message.jsx b/packages/desktop-gui/src/runs/permission-message.jsx index c79a7fd8c8..2b4264889d 100644 --- a/packages/desktop-gui/src/runs/permission-message.jsx +++ b/packages/desktop-gui/src/runs/permission-message.jsx @@ -38,11 +38,14 @@ class PermissionMessage extends Component { if (this.state.result === SUCCESS || membershipRequested) { return this._success() - } else if (this.state.result === FAILURE) { - return this._failure() - } else { - return this._noResult() } + + if (this.state.result === FAILURE) { + return this._failure() + } + + return this._noResult() + } _button () { @@ -82,22 +85,23 @@ class PermissionMessage extends Component { // tell them it's all good if (errors.isDenied(error) || errors.isAlreadyRequested(error)) { return this._success() - } else { - return ( -An unexpected error occurred while requesting access:
-
- {this.state.error.message}
-
- Try again.
- {this._button()} -An unexpected error occurred while requesting access:
+
+ {this.state.error.message}
+
+ Try again.
+ {this._button()} +npm install --save-dev cypress@{appStore.newVersion}
- node_modules/.bin/cypress open to open the new version.
- npm install --save-dev cypress@{appStore.newVersion}
+ node_modules/.bin/cypress open to open the new version.
+