mirror of
https://github.com/cypress-io/cypress.git
synced 2026-05-13 02:40:22 -05:00
add "cypress info" command (#6372)
* WIP: add cli info command to list detected browsers * print found browsers * change to list * start work on info command calling into binary * move info command into binary * print OS info during cypress info command * add binary cache path to info command * add browser profile path * add memory * get browser profile path without partition * pick real browsers as examples * output same info as desired * better names * changed colors * add list of cached binary versions * do not put stable into name * add underlined link * conditionally show profile path, only if the folder exists * do not list cached binaries * human-friendly memory print * print env proxy vars * print CYPRESS_ env variables * use _.sample * update order * store cypress info output on CI as HTML page artifact * add percy CLI screenshots * sanitize cypress info command * store CLI snapshots in cli/visual-snapshots folder * update cli unit snapshot * add cli unit test * start e2e testing for cypress info * add test with proxy and cypress vars * make sure we call the binary * stricter start check * start unit testing modes info * test info mode browser print * add test for profile path * add cypress info command to test binary Circle job * add cypress info to test-binary-as-specific-user circle * print cypress info --dev on circle and on appveyor * update .gitignore * move error in environment load to debug
This commit is contained in:
@@ -58,3 +58,4 @@ binary-url.json
|
||||
|
||||
# Allows us to dynamically create eslint rules that override the default for Decaffeinate scripts
|
||||
.eslintrc.js
|
||||
cli/visual-snapshots
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
# https://docs.percy.io/v1/docs/configuration
|
||||
version: 1
|
||||
snapshot:
|
||||
widths: [1280]
|
||||
min-height: 1024
|
||||
agent:
|
||||
asset-discovery:
|
||||
# let Percy wait for network to fetch static resources
|
||||
# like images and fonts to avoid missing icons
|
||||
network-idle-timeout: 250 # ms
|
||||
# assets that don't change could be cached
|
||||
cache-responses: true
|
||||
+2
-2
@@ -73,8 +73,8 @@ test_script:
|
||||
- node --version
|
||||
- yarn --version
|
||||
- 7z
|
||||
- echo *** Browsers installed on this system ***
|
||||
- cd packages/launcher && node index.js && cd ../..
|
||||
- echo *** System info ***
|
||||
- node cli/bin/cypress info --dev
|
||||
|
||||
- echo *** Kitchensink tests run on Chrome browser ***
|
||||
- npm run dev -- --run-project %CD%/packages/example --browser chrome
|
||||
|
||||
+46
@@ -255,8 +255,35 @@ jobs:
|
||||
at: ~/
|
||||
## this will catch .only's in js/coffee as well
|
||||
- run: yarn lint-all
|
||||
- run:
|
||||
name: cypress info (dev)
|
||||
command: node cli/bin/cypress info --dev
|
||||
- store-npm-logs
|
||||
|
||||
cli-visual-tests:
|
||||
<<: *defaults
|
||||
parallelism: 1
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: ~/
|
||||
- run: mkdir -p cli/visual-snapshots
|
||||
- run:
|
||||
# TODO sanitize "cypress info" output to be consistent
|
||||
command: node cli/bin/cypress info --dev | yarn --silent term-to-html | node scripts/sanitize --type cli-info > cli/visual-snapshots/cypress-info.html
|
||||
environment:
|
||||
FORCE_COLOR: 2
|
||||
- run:
|
||||
command: node cli/bin/cypress help | yarn --silent term-to-html > cli/visual-snapshots/cypress-help.html
|
||||
environment:
|
||||
FORCE_COLOR: 2
|
||||
- store_artifacts:
|
||||
path: cli/visual-snapshots
|
||||
- run:
|
||||
name: Upload CLI snapshots for diffing
|
||||
command: |
|
||||
PERCY_TOKEN=$PERCY_TOKEN_CLI \
|
||||
yarn percy snapshot ./cli/visual-snapshots
|
||||
|
||||
unit-tests:
|
||||
<<: *defaults
|
||||
parallelism: 1
|
||||
@@ -901,6 +928,14 @@ jobs:
|
||||
name: Verify Cypress binary
|
||||
working_directory: test-binary
|
||||
command: $(yarn bin)/cypress verify
|
||||
- run:
|
||||
name: Cypress help
|
||||
working_directory: test-binary
|
||||
command: $(yarn bin)/cypress help
|
||||
- run:
|
||||
name: Cypress info
|
||||
working_directory: test-binary
|
||||
command: $(yarn bin)/cypress info
|
||||
- store-npm-logs
|
||||
|
||||
# install NPM + binary zip and run against staging API
|
||||
@@ -1027,6 +1062,14 @@ jobs:
|
||||
working_directory: test-binary
|
||||
# force installing the freshly built binary
|
||||
command: CYPRESS_INSTALL_BINARY=~/cypress/cypress.zip npm i ~/cypress/cypress.tgz
|
||||
- run:
|
||||
name: Cypress help
|
||||
working_directory: test-binary
|
||||
command: $(yarn bin)/cypress help
|
||||
- run:
|
||||
name: Cypress info
|
||||
working_directory: test-binary
|
||||
command: $(yarn bin)/cypress info
|
||||
- run:
|
||||
name: Add Cypress demo
|
||||
working_directory: test-binary
|
||||
@@ -1052,6 +1095,9 @@ linux-workflow: &linux-workflow
|
||||
requires:
|
||||
- build
|
||||
# unit, integration and e2e tests
|
||||
- cli-visual-tests:
|
||||
requires:
|
||||
- build
|
||||
- unit-tests:
|
||||
requires:
|
||||
- build
|
||||
|
||||
@@ -205,6 +205,7 @@ exports['cli help command shows help 1'] = `
|
||||
verify [options] Verifies that Cypress is installed correctly and
|
||||
executable
|
||||
cache [options] Manages the Cypress binary cache
|
||||
info [options] Prints Cypress and system information
|
||||
-------
|
||||
stderr:
|
||||
-------
|
||||
@@ -240,6 +241,7 @@ exports['cli help command shows help for -h 1'] = `
|
||||
verify [options] Verifies that Cypress is installed correctly and
|
||||
executable
|
||||
cache [options] Manages the Cypress binary cache
|
||||
info [options] Prints Cypress and system information
|
||||
-------
|
||||
stderr:
|
||||
-------
|
||||
@@ -275,6 +277,7 @@ exports['cli help command shows help for --help 1'] = `
|
||||
verify [options] Verifies that Cypress is installed correctly and
|
||||
executable
|
||||
cache [options] Manages the Cypress binary cache
|
||||
info [options] Prints Cypress and system information
|
||||
-------
|
||||
stderr:
|
||||
-------
|
||||
@@ -311,6 +314,7 @@ exports['cli unknown command shows usage and exits 1'] = `
|
||||
verify [options] Verifies that Cypress is installed correctly and
|
||||
executable
|
||||
cache [options] Manages the Cypress binary cache
|
||||
info [options] Prints Cypress and system information
|
||||
-------
|
||||
stderr:
|
||||
-------
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
exports['cypress info without browsers or vars'] = `
|
||||
|
||||
Proxy Settings: none detected
|
||||
Environment Variables: none detected
|
||||
|
||||
Application Data: /user/app/data/path
|
||||
Browser Profiles: /user/app/data/path/to/browsers
|
||||
Binary Caches: /user/path/to/binary/cache
|
||||
|
||||
Cypress Version: 0.0.0
|
||||
System Platform: linux (Foo-OsVersion)
|
||||
System Memory: 1.2 GB free 400 MB
|
||||
|
||||
`
|
||||
|
||||
exports['cypress info with proxy and vars'] = `
|
||||
|
||||
Proxy Settings:
|
||||
PROXY_ENV_VAR1: some proxy variable
|
||||
PROXY_ENV_VAR2: another proxy variable
|
||||
|
||||
Learn More: https://on.cypress.io/proxy-configuration
|
||||
|
||||
Environment Variables:
|
||||
CYPRESS_ENV_VAR1: my Cypress variable
|
||||
CYPRESS_ENV_VAR2: my other Cypress variable
|
||||
|
||||
Application Data: /user/app/data/path
|
||||
Browser Profiles: /user/app/data/path/to/browsers
|
||||
Binary Caches: /user/path/to/binary/cache
|
||||
|
||||
Cypress Version: 0.0.0
|
||||
System Platform: linux (Foo-OsVersion)
|
||||
System Memory: 1.2 GB free 400 MB
|
||||
|
||||
`
|
||||
|
||||
exports['cypress redacts sensitive vars'] = `
|
||||
|
||||
Proxy Settings: none detected
|
||||
Environment Variables:
|
||||
CYPRESS_ENV_VAR1: my Cypress variable
|
||||
CYPRESS_ENV_VAR2: my other Cypress variable
|
||||
CYPRESS_PROJECT_ID: abc123
|
||||
CYPRESS_RECORD_KEY: <redacted>
|
||||
|
||||
Application Data: /user/app/data/path
|
||||
Browser Profiles: /user/app/data/path/to/browsers
|
||||
Binary Caches: /user/path/to/binary/cache
|
||||
|
||||
Cypress Version: 0.0.0
|
||||
System Platform: linux (Foo-OsVersion)
|
||||
System Memory: 1.2 GB free 400 MB
|
||||
|
||||
`
|
||||
@@ -129,6 +129,7 @@ const knownCommands = [
|
||||
'-v',
|
||||
'--version',
|
||||
'version',
|
||||
'info',
|
||||
]
|
||||
|
||||
const text = (description) => {
|
||||
@@ -298,6 +299,18 @@ module.exports = {
|
||||
cache[command]()
|
||||
})
|
||||
|
||||
program
|
||||
.command('info')
|
||||
.usage('[command]')
|
||||
.description('Prints Cypress and system information')
|
||||
.option('--dev', text('dev'), coerceFalse)
|
||||
.action((opts) => {
|
||||
require('./exec/info')
|
||||
.start(opts)
|
||||
.then(util.exit)
|
||||
.catch(util.logErrorExit1)
|
||||
})
|
||||
|
||||
debug('cli starts with arguments %j', args)
|
||||
util.printNodeOptions()
|
||||
|
||||
|
||||
+1
-11
@@ -1,4 +1,3 @@
|
||||
const os = require('os')
|
||||
const chalk = require('chalk')
|
||||
const { stripIndent, stripIndents } = require('common-tags')
|
||||
const { merge } = require('ramda')
|
||||
@@ -245,17 +244,8 @@ const CYPRESS_RUN_BINARY = {
|
||||
},
|
||||
}
|
||||
|
||||
function getPlatformInfo () {
|
||||
return util.getOsVersionAsync().then((version) => {
|
||||
return stripIndent`
|
||||
Platform: ${os.platform()} (${version})
|
||||
Cypress Version: ${util.pkgVersion()}
|
||||
`
|
||||
})
|
||||
}
|
||||
|
||||
function addPlatformInformation (info) {
|
||||
return getPlatformInfo().then((platform) => {
|
||||
return util.getPlatformInfo().then((platform) => {
|
||||
return merge(info, { platform })
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
/* eslint-disable no-console */
|
||||
const spawn = require('./spawn')
|
||||
const util = require('../util')
|
||||
const state = require('../tasks/state')
|
||||
const os = require('os')
|
||||
const chalk = require('chalk')
|
||||
const prettyBytes = require('pretty-bytes')
|
||||
const _ = require('lodash')
|
||||
const R = require('ramda')
|
||||
|
||||
// color for numbers and show values
|
||||
const g = chalk.green
|
||||
// color for paths
|
||||
const p = chalk.cyan
|
||||
// urls
|
||||
const link = chalk.blue.underline
|
||||
|
||||
// to be exported
|
||||
const methods = {}
|
||||
|
||||
methods.findProxyEnvironmentVariables = () => {
|
||||
return _.pick(process.env, ['HTTP_PROXY', 'HTTPS_PROXY', 'NO_PROXY'])
|
||||
}
|
||||
|
||||
const maskSensitiveVariables = R.evolve({
|
||||
CYPRESS_RECORD_KEY: R.always('<redacted>'),
|
||||
})
|
||||
|
||||
methods.findCypressEnvironmentVariables = () => {
|
||||
const isCyVariable = (val, key) => key.startsWith('CYPRESS_')
|
||||
|
||||
return R.pickBy(isCyVariable)(process.env)
|
||||
}
|
||||
|
||||
const formatCypressVariables = () => {
|
||||
const vars = methods.findCypressEnvironmentVariables()
|
||||
|
||||
return maskSensitiveVariables(vars)
|
||||
}
|
||||
|
||||
methods.start = (options = {}) => {
|
||||
const args = ['--mode=info']
|
||||
|
||||
return spawn.start(args, {
|
||||
dev: options.dev,
|
||||
})
|
||||
.then(() => {
|
||||
console.log()
|
||||
const proxyVars = methods.findProxyEnvironmentVariables()
|
||||
|
||||
if (_.isEmpty(proxyVars)) {
|
||||
console.log('Proxy Settings: none detected')
|
||||
} else {
|
||||
console.log('Proxy Settings:')
|
||||
_.forEach(proxyVars, (value, key) => {
|
||||
console.log('%s: %s', key, g(value))
|
||||
})
|
||||
|
||||
console.log()
|
||||
console.log('Learn More: %s', link('https://on.cypress.io/proxy-configuration'))
|
||||
console.log()
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
const cyVars = formatCypressVariables()
|
||||
|
||||
if (_.isEmpty(cyVars)) {
|
||||
console.log('Environment Variables: none detected')
|
||||
} else {
|
||||
console.log('Environment Variables:')
|
||||
_.forEach(cyVars, (value, key) => {
|
||||
console.log('%s: %s', key, g(value))
|
||||
})
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
console.log()
|
||||
console.log('Application Data:', p(util.getApplicationDataFolder()))
|
||||
console.log('Browser Profiles:', p(util.getApplicationDataFolder('browsers')))
|
||||
console.log('Binary Caches: %s', p(state.getCacheDir()))
|
||||
})
|
||||
.then(() => {
|
||||
console.log()
|
||||
|
||||
return util.getOsVersionAsync().then((osVersion) => {
|
||||
console.log('Cypress Version: %s', g(util.pkgVersion()))
|
||||
console.log('System Platform: %s (%s)', g(os.platform()), g(osVersion))
|
||||
console.log('System Memory: %s free %s', g(prettyBytes(os.totalmem())), g(prettyBytes(os.freemem())))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = methods
|
||||
@@ -14,16 +14,21 @@ const clear = () => {
|
||||
}
|
||||
|
||||
const list = () => {
|
||||
return fs
|
||||
.readdirAsync(state.getCacheDir())
|
||||
.filter(util.isSemver)
|
||||
return getCachedVersions()
|
||||
.then((versions) => {
|
||||
logger.log(versions.join(', '))
|
||||
})
|
||||
}
|
||||
|
||||
const getCachedVersions = () => {
|
||||
return fs
|
||||
.readdirAsync(state.getCacheDir())
|
||||
.filter(util.isSemver)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
path,
|
||||
clear,
|
||||
list,
|
||||
getCachedVersions,
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
const _ = require('lodash')
|
||||
const R = require('ramda')
|
||||
const os = require('os')
|
||||
const ospath = require('ospath')
|
||||
const crypto = require('crypto')
|
||||
const la = require('lazy-ass')
|
||||
const is = require('check-more-types')
|
||||
@@ -225,6 +226,26 @@ const parseOpts = (opts) => {
|
||||
return cleanOpts
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy of packages/server/lib/browsers/utils.ts
|
||||
* because we need same functionality in CLI to show the path :(
|
||||
*/
|
||||
const getApplicationDataFolder = (...paths) => {
|
||||
const { env } = process
|
||||
|
||||
// allow overriding the app_data folder
|
||||
const folder = env.CYPRESS_KONFIG_ENV || env.CYPRESS_ENV || 'development'
|
||||
|
||||
const PRODUCT_NAME = pkg.productName || pkg.name
|
||||
const OS_DATA_PATH = ospath.data()
|
||||
|
||||
const ELECTRON_APP_DATA_PATH = path.join(OS_DATA_PATH, PRODUCT_NAME)
|
||||
|
||||
const p = path.join(ELECTRON_APP_DATA_PATH, 'cy', folder, ...paths)
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
const util = {
|
||||
normalizeModuleOptions,
|
||||
parseOpts,
|
||||
@@ -380,6 +401,15 @@ const util = {
|
||||
})
|
||||
},
|
||||
|
||||
getPlatformInfo () {
|
||||
return util.getOsVersionAsync().then((version) => {
|
||||
return stripIndent`
|
||||
Platform: ${os.platform()} (${version})
|
||||
Cypress Version: ${util.pkgVersion()}
|
||||
`
|
||||
})
|
||||
},
|
||||
|
||||
// attention:
|
||||
// when passing relative path to NPM post install hook, the current working
|
||||
// directory is set to the `node_modules/cypress` folder
|
||||
@@ -457,6 +487,8 @@ const util = {
|
||||
getFileChecksum,
|
||||
|
||||
getFileSize,
|
||||
|
||||
getApplicationDataFolder,
|
||||
}
|
||||
|
||||
module.exports = util
|
||||
|
||||
@@ -46,6 +46,8 @@
|
||||
"log-symbols": "3.0.0",
|
||||
"minimist": "1.2.0",
|
||||
"moment": "2.24.0",
|
||||
"ospath": "1.2.2",
|
||||
"pretty-bytes": "5.3.0",
|
||||
"ramda": "0.26.1",
|
||||
"request": "2.88.0",
|
||||
"request-progress": "3.0.0",
|
||||
|
||||
@@ -4,12 +4,14 @@ const os = require('os')
|
||||
const cli = require(`${lib}/cli`)
|
||||
const util = require(`${lib}/util`)
|
||||
const logger = require(`${lib}/logger`)
|
||||
const info = require(`${lib}/exec/info`)
|
||||
const run = require(`${lib}/exec/run`)
|
||||
const open = require(`${lib}/exec/open`)
|
||||
const state = require(`${lib}/tasks/state`)
|
||||
const verify = require(`${lib}/tasks/verify`)
|
||||
const install = require(`${lib}/tasks/install`)
|
||||
const snapshot = require('../support/snapshot')
|
||||
const debug = require('debug')('test')
|
||||
const execa = require('execa-wrap')
|
||||
|
||||
describe('cli', () => {
|
||||
@@ -22,7 +24,11 @@ describe('cli', () => {
|
||||
// sinon.stub(util, 'exit')
|
||||
sinon.stub(util, 'logErrorExit1')
|
||||
this.exec = (args) => {
|
||||
return cli.init(`node test ${args}`.split(' '))
|
||||
const cliArgs = `node test ${args}`.split(' ')
|
||||
|
||||
debug('calling cli.init with: %o', cliArgs)
|
||||
|
||||
return cli.init(cliArgs)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -449,4 +455,16 @@ describe('cli', () => {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
context('cypress info', () => {
|
||||
beforeEach(() => {
|
||||
sinon.stub(info, 'start').resolves(0)
|
||||
sinon.stub(util, 'exit').withArgs(0)
|
||||
})
|
||||
|
||||
it('calls info start', () => {
|
||||
this.exec('info')
|
||||
expect(info.start).to.have.been.calledWith()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
require('../../spec_helper')
|
||||
|
||||
const os = require('os')
|
||||
const util = require(`${lib}/util`)
|
||||
const state = require(`${lib}/tasks/state`)
|
||||
const info = require(`${lib}/exec/info`)
|
||||
const spawn = require(`${lib}/exec/spawn`)
|
||||
|
||||
const snapshot = require('../../support/snapshot')
|
||||
const stdout = require('../../support/stdout')
|
||||
const normalize = require('../../support/normalize')
|
||||
|
||||
describe('exec info', function () {
|
||||
beforeEach(function () {
|
||||
sinon.stub(process, 'exit')
|
||||
|
||||
// common stubs
|
||||
sinon.stub(spawn, 'start').resolves()
|
||||
os.platform.returns('linux')
|
||||
sinon.stub(os, 'totalmem').returns(1.2e+9)
|
||||
sinon.stub(os, 'freemem').returns(4e+8)
|
||||
sinon.stub(info, 'findProxyEnvironmentVariables').returns({})
|
||||
sinon.stub(info, 'findCypressEnvironmentVariables').returns({})
|
||||
sinon.stub(util, 'getApplicationDataFolder')
|
||||
.withArgs('browsers').returns('/user/app/data/path/to/browsers')
|
||||
.withArgs().returns('/user/app/data/path')
|
||||
|
||||
sinon.stub(state, 'getCacheDir').returns('/user/path/to/binary/cache')
|
||||
})
|
||||
|
||||
const startInfoAndSnapshot = async (snapshotName) => {
|
||||
expect(snapshotName, 'missing snapshot name').to.be.a('string')
|
||||
|
||||
const output = stdout.capture()
|
||||
|
||||
await info.start()
|
||||
stdout.restore()
|
||||
|
||||
snapshot(snapshotName, normalize(output.toString()))
|
||||
}
|
||||
|
||||
it('prints collected info without env vars', async () => {
|
||||
await startInfoAndSnapshot('cypress info without browsers or vars')
|
||||
expect(spawn.start).to.be.calledWith(['--mode=info'], { dev: undefined })
|
||||
})
|
||||
|
||||
it('prints proxy and cypress env vars', async () => {
|
||||
info.findProxyEnvironmentVariables.returns({
|
||||
PROXY_ENV_VAR1: 'some proxy variable',
|
||||
PROXY_ENV_VAR2: 'another proxy variable',
|
||||
})
|
||||
|
||||
info.findCypressEnvironmentVariables.returns({
|
||||
CYPRESS_ENV_VAR1: 'my Cypress variable',
|
||||
CYPRESS_ENV_VAR2: 'my other Cypress variable',
|
||||
})
|
||||
|
||||
await startInfoAndSnapshot('cypress info with proxy and vars')
|
||||
})
|
||||
|
||||
it('redacts sensitive cypress variables', async () => {
|
||||
info.findCypressEnvironmentVariables.returns({
|
||||
CYPRESS_ENV_VAR1: 'my Cypress variable',
|
||||
CYPRESS_ENV_VAR2: 'my other Cypress variable',
|
||||
CYPRESS_PROJECT_ID: 'abc123', // not sensitive
|
||||
CYPRESS_RECORD_KEY: 'really really secret stuff', // should not be printed
|
||||
})
|
||||
|
||||
await startInfoAndSnapshot('cypress redacts sensitive vars')
|
||||
})
|
||||
})
|
||||
@@ -8,9 +8,12 @@ const removeExcessWhiteSpace = (str) => {
|
||||
return str.replace(whitespaceAtEndOfLineRe, '')
|
||||
}
|
||||
|
||||
module.exports = (str) => {
|
||||
// strip dates and ansi codes
|
||||
// and excess whitespace
|
||||
/**
|
||||
* strip dates and ansi codes and excess whitespace
|
||||
* @param {string} str input string
|
||||
* @returns {string} cleaned output string
|
||||
*/
|
||||
const normalize = (str) => {
|
||||
return stripAnsi(
|
||||
str
|
||||
.replace(datesRe, 'xx:xx:xx')
|
||||
@@ -20,3 +23,5 @@ module.exports = (str) => {
|
||||
.replace(downloadQueryRe, '?platform=OS&arch=ARCH')
|
||||
)
|
||||
}
|
||||
|
||||
module.exports = normalize
|
||||
|
||||
@@ -159,6 +159,7 @@
|
||||
"mock-fs": "4.9.0",
|
||||
"parse-github-repo-url": "1.4.1",
|
||||
"patch-package": "6.2.0",
|
||||
"percy": "0.21.0",
|
||||
"plist": "2.1.0",
|
||||
"pluralize": "8.0.0",
|
||||
"postinstall-postinstall": "2.0.0",
|
||||
@@ -175,6 +176,7 @@
|
||||
"snap-shot-it": "7.9.1",
|
||||
"stop-only": "3.0.1",
|
||||
"strip-ansi": "4.0.0",
|
||||
"term-to-html": "1.0.0",
|
||||
"terminal-banner": "1.1.0",
|
||||
"through": "2.3.8",
|
||||
"ts-node": "8.3.0",
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
exports['two browsers with firefox having profile folder'] = `
|
||||
Displaying Cypress info...
|
||||
|
||||
Detected 2 browsers installed:
|
||||
|
||||
1. Chrome
|
||||
- Name: chrome
|
||||
- Channel: stable
|
||||
- Version: 12.34.56
|
||||
- Executable: /path/to/google-chrome
|
||||
|
||||
2. Firefox Dev
|
||||
- Name: firefox
|
||||
- Channel: dev
|
||||
- Version: 79.0a1
|
||||
- Executable: /path/to/firefox
|
||||
- Profile: /path/to/user/firefox/profile
|
||||
|
||||
Note: to run these browsers, pass <name>:<channel> to the '--browser' field
|
||||
|
||||
Examples:
|
||||
- cypress run --browser chrome
|
||||
- cypress run --browser firefox:dev
|
||||
|
||||
Learn More: https://on.cypress.io/launching-browsers
|
||||
|
||||
`
|
||||
|
||||
exports['output without any browsers'] = `
|
||||
Displaying Cypress info...
|
||||
|
||||
Detected no known browsers installed
|
||||
|
||||
|
||||
`
|
||||
|
||||
exports['single chrome:stable'] = `
|
||||
Displaying Cypress info...
|
||||
|
||||
Detected 1 browser installed:
|
||||
|
||||
1. Chrome
|
||||
- Name: chrome
|
||||
- Channel: stable
|
||||
- Version: 12.34.56
|
||||
- Executable: /path/to/google-chrome
|
||||
|
||||
Note: to run these browsers, pass <name>:<channel> to the '--browser' field
|
||||
|
||||
Examples:
|
||||
- cypress run --browser chrome
|
||||
|
||||
Learn More: https://on.cypress.io/launching-browsers
|
||||
|
||||
`
|
||||
|
||||
exports['two browsers'] = `
|
||||
Displaying Cypress info...
|
||||
|
||||
Detected 2 browsers installed:
|
||||
|
||||
1. Chrome
|
||||
- Name: chrome
|
||||
- Channel: stable
|
||||
- Version: 12.34.56
|
||||
- Executable: /path/to/google-chrome
|
||||
|
||||
2. Firefox Dev
|
||||
- Name: firefox
|
||||
- Channel: dev
|
||||
- Version: 79.0a1
|
||||
- Executable: /path/to/firefox
|
||||
|
||||
Note: to run these browsers, pass <name>:<channel> to the '--browser' field
|
||||
|
||||
Examples:
|
||||
- cypress run --browser chrome
|
||||
- cypress run --browser firefox:dev
|
||||
|
||||
Learn More: https://on.cypress.io/launching-browsers
|
||||
|
||||
`
|
||||
@@ -19,6 +19,7 @@ const PATH_TO_BROWSERS = appData.path('browsers')
|
||||
const pathToProfiles = path.join(PATH_TO_BROWSERS, '*')
|
||||
|
||||
const getBrowserPath = (browser) => {
|
||||
// TODO need to check if browser.name is an unempty string
|
||||
return path.join(
|
||||
PATH_TO_BROWSERS,
|
||||
`${browser.name}-${browser.channel}`
|
||||
@@ -178,6 +179,8 @@ export = {
|
||||
|
||||
copyExtension,
|
||||
|
||||
getBrowserPath,
|
||||
|
||||
getProfileDir,
|
||||
|
||||
getExtensionDir,
|
||||
|
||||
@@ -188,6 +188,11 @@ module.exports = {
|
||||
}).then(exit0)
|
||||
.catch(exitErr)
|
||||
|
||||
case 'info':
|
||||
return require('./modes/info')(options)
|
||||
.then(exit0)
|
||||
.catch(exitErr)
|
||||
|
||||
case 'smokeTest':
|
||||
return this.runElectron(mode, options)
|
||||
.then((pong) => {
|
||||
|
||||
@@ -8,6 +8,7 @@ os = require("os")
|
||||
## to the "packages/server" folder
|
||||
cwd = require("./cwd")
|
||||
Promise = require("bluebird")
|
||||
debug = require("debug")("cypress:server")
|
||||
|
||||
## never cut off stack traces
|
||||
Error.stackTraceLimit = Infinity
|
||||
@@ -64,7 +65,6 @@ try
|
||||
electronLaunchArguments.forEach app.commandLine.appendArgument
|
||||
|
||||
catch e
|
||||
if env is "development"
|
||||
console.error(e.message)
|
||||
debug("environment error %s", e.message)
|
||||
|
||||
module.exports = env
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
/* eslint-disable no-console */
|
||||
const debug = require('debug')('cypress:server:info')
|
||||
const launcher = require('@packages/launcher')
|
||||
const pluralize = require('pluralize')
|
||||
const { stripIndent } = require('common-tags')
|
||||
const { sortWith, ascend, prop } = require('ramda')
|
||||
const browserUtils = require('../browsers/utils')
|
||||
const _ = require('lodash')
|
||||
const chalk = require('chalk')
|
||||
const fs = require('../util/fs')
|
||||
|
||||
// color for numbers and short values
|
||||
const n = chalk.green
|
||||
// color for paths
|
||||
const p = chalk.cyan
|
||||
// color for accents and examples
|
||||
const a = chalk.yellow
|
||||
// urls
|
||||
const link = chalk.blue.underline
|
||||
|
||||
/**
|
||||
* If the list has at least 1 item, picks a random item
|
||||
* and returns it AND the remaining items.
|
||||
*/
|
||||
const pickRandomItem = (list) => {
|
||||
if (!list.length) {
|
||||
return {
|
||||
item: null,
|
||||
remaining: list,
|
||||
}
|
||||
}
|
||||
|
||||
const item = _.sample(list)
|
||||
const remaining = _.without(list, item)
|
||||
|
||||
return {
|
||||
item, remaining,
|
||||
}
|
||||
}
|
||||
|
||||
// Usually the full browser name to pass via --browser
|
||||
// is <name>:<channel>. If the channel is stable, you
|
||||
// can just do "--browser <name>"
|
||||
const formBrowserName = (browser) => {
|
||||
if (browser.channel === 'stable') {
|
||||
return browser.name
|
||||
}
|
||||
|
||||
return `${browser.name}:${browser.channel}`
|
||||
}
|
||||
|
||||
// for each browser computes the profile folder
|
||||
// and checks if the folder exists. If exists,
|
||||
// adds it to the browser object as a property
|
||||
const addProfilePath = async (browsers = []) => {
|
||||
for (const browser of browsers) {
|
||||
const profilePath = browserUtils.getBrowserPath(browser)
|
||||
|
||||
debug('checking profile path %s for browser %s:%s', profilePath, browser.name, browser.channel)
|
||||
try {
|
||||
const profileExists = await fs.statAsync(profilePath)
|
||||
|
||||
if (profileExists && profileExists.isDirectory()) {
|
||||
debug('profile folder exists %s', profilePath)
|
||||
browser.profilePath = profilePath
|
||||
}
|
||||
} catch (e) {
|
||||
debug('problem checking profile folder %s %s', profilePath, e.message)
|
||||
}
|
||||
}
|
||||
|
||||
return browsers
|
||||
}
|
||||
|
||||
const print = (browsers = []) => {
|
||||
console.log('Displaying Cypress info...')
|
||||
console.log('')
|
||||
if (browsers.length) {
|
||||
console.log('Detected %s %s installed:', n(browsers.length), pluralize('browser', browsers.length))
|
||||
} else {
|
||||
console.log('Detected no known browsers installed')
|
||||
}
|
||||
|
||||
console.log('')
|
||||
|
||||
const sortByNameAndMajor = sortWith([
|
||||
ascend(prop('name')),
|
||||
ascend(prop('majorVersion')),
|
||||
])
|
||||
const sortedByNameAndMajorVersion = sortByNameAndMajor(browsers)
|
||||
|
||||
sortedByNameAndMajorVersion.forEach((browser, k) => {
|
||||
const text = stripIndent`
|
||||
${k + 1}. ${a(browser.displayName)}
|
||||
- Name: ${browser.name}
|
||||
- Channel: ${browser.channel}
|
||||
- Version: ${n(browser.version)}
|
||||
- Executable: ${p(browser.path)}
|
||||
${browser.profilePath ? `- Profile: ${browser.profilePath}` : ''}
|
||||
`
|
||||
|
||||
console.log(text)
|
||||
console.log('')
|
||||
})
|
||||
|
||||
// randomly a few detected browsers to use as examples
|
||||
if (browsers.length) {
|
||||
const highlightedBrowser = a('--browser')
|
||||
|
||||
console.log('Note: to run these browsers, pass <name>:<channel> to the \'%s\' field',
|
||||
highlightedBrowser)
|
||||
|
||||
console.log('')
|
||||
|
||||
const firstDraw = pickRandomItem(browsers)
|
||||
|
||||
if (firstDraw.item) {
|
||||
console.log('Examples:')
|
||||
console.log(a(`- cypress run --browser ${formBrowserName(firstDraw.item)}`))
|
||||
|
||||
const secondDraw = pickRandomItem(firstDraw.remaining)
|
||||
|
||||
if (secondDraw.item) {
|
||||
console.log(a(`- cypress run --browser ${formBrowserName(secondDraw.item)}`))
|
||||
}
|
||||
}
|
||||
|
||||
console.log('')
|
||||
console.log('Learn More: %s', link('https://on.cypress.io/launching-browsers'))
|
||||
}
|
||||
}
|
||||
|
||||
const info = () => {
|
||||
return launcher.detect()
|
||||
.then(addProfilePath)
|
||||
.then(print)
|
||||
}
|
||||
|
||||
module.exports = info
|
||||
@@ -0,0 +1,96 @@
|
||||
require('../../spec_helper')
|
||||
|
||||
const info = require(`${root}../lib/modes/info`)
|
||||
const capture = require(`${root}../lib/capture`)
|
||||
const browserUtils = require(`${root}../lib/browsers/utils`)
|
||||
const fs = require(`${root}../lib/util/fs`)
|
||||
const launcher = require('@packages/launcher')
|
||||
const snapshot = require('snap-shot-it')
|
||||
const stripAnsi = require('strip-ansi')
|
||||
const _ = require('lodash')
|
||||
|
||||
describe('lib/modes/info', () => {
|
||||
beforeEach(() => {
|
||||
capture.restore()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
capture.restore()
|
||||
})
|
||||
|
||||
const chromeStable = {
|
||||
displayName: 'Chrome',
|
||||
name: 'chrome',
|
||||
channel: 'stable',
|
||||
version: '12.34.56',
|
||||
majorVersion: 12,
|
||||
path: '/path/to/google-chrome',
|
||||
}
|
||||
|
||||
const firefoxDev = {
|
||||
displayName: 'Firefox Dev',
|
||||
name: 'firefox',
|
||||
channel: 'dev',
|
||||
version: '79.0a1',
|
||||
majorVersion: 79,
|
||||
path: '/path/to/firefox',
|
||||
}
|
||||
|
||||
const infoAndSnapshot = async (snapshotName) => {
|
||||
expect(snapshotName, 'missing snapshot name').to.be.a('string')
|
||||
|
||||
const captured = capture.stdout()
|
||||
|
||||
await info()
|
||||
|
||||
capture.restore()
|
||||
snapshot(snapshotName, stripAnsi(captured.toString()))
|
||||
}
|
||||
|
||||
it('prints no browsers', async () => {
|
||||
sinon.stub(launcher, 'detect').resolves([])
|
||||
await infoAndSnapshot('output without any browsers')
|
||||
})
|
||||
|
||||
it('prints 1 found browser', async () => {
|
||||
sinon.stub(launcher, 'detect').resolves([chromeStable])
|
||||
await infoAndSnapshot('single chrome:stable')
|
||||
})
|
||||
|
||||
it('prints 2 found browsers', async () => {
|
||||
sinon.stub(launcher, 'detect').resolves([chromeStable, firefoxDev])
|
||||
// have to make sure random sampling from the browser list
|
||||
// to create examples returns same order
|
||||
// so Chrome will be picked first, Firefox will be second
|
||||
const sample = sinon.stub(_, 'sample')
|
||||
|
||||
sample.onFirstCall().returns(chromeStable)
|
||||
sample.onSecondCall().returns(firefoxDev)
|
||||
|
||||
await infoAndSnapshot('two browsers')
|
||||
expect(sample, 'two browsers were picked to create examples').to.be.calledTwice
|
||||
})
|
||||
|
||||
it('adds profile for browser if folder exists', async () => {
|
||||
sinon.stub(launcher, 'detect').resolves([chromeStable, firefoxDev])
|
||||
sinon.stub(browserUtils, 'getBrowserPath')
|
||||
.withArgs(chromeStable).returns('/path/to/user/chrome/profile')
|
||||
.withArgs(firefoxDev).returns('/path/to/user/firefox/profile')
|
||||
|
||||
sinon.stub(fs, 'statAsync')
|
||||
.withArgs('/path/to/user/chrome/profile').throws('No Chrome profile folder')
|
||||
.withArgs('/path/to/user/firefox/profile').resolves({
|
||||
isDirectory: _.stubTrue,
|
||||
})
|
||||
|
||||
// have to make sure random sampling from the browser list
|
||||
// to create examples returns same order
|
||||
// so Chrome will be picked first, Firefox will be second
|
||||
const sample = sinon.stub(_, 'sample')
|
||||
|
||||
sample.onFirstCall().returns(chromeStable)
|
||||
sample.onSecondCall().returns(firefoxDev)
|
||||
|
||||
await infoAndSnapshot('two browsers with firefox having profile folder')
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,51 @@
|
||||
// call this script to sanitize output of the command
|
||||
// AFTER converting ANSI escape codes into HTML tags
|
||||
const arg = require('arg')
|
||||
|
||||
const args = arg({
|
||||
'--type': String,
|
||||
})
|
||||
|
||||
const sanitizeCliInfo = () => {
|
||||
const chromiumVersion = /- Version: (<.+>)(\d{2,3}.\d\.\d+.\d+)(<.+>)/g
|
||||
const firefoxVersion = /- Version: (<.+>)(\d{2,3}.\d\.\d+|\d+\.[0-9a-z])(<.+>)+/g
|
||||
const browserArgument = /--browser (\w+:?\w+)/g
|
||||
// there is only a single line with memory usage
|
||||
const systemMemory = /System Memory: (<.+>)([\w.]+ \w+)(<.+>) free (<.+>)([\w.]+ \w+)(<.+>)/
|
||||
|
||||
const replaceChromiumVersion = (match, openTag, version, closeTag) => {
|
||||
return `- Version: ${openTag}***chromium version***${closeTag}`
|
||||
}
|
||||
|
||||
const replaceFirefoxVersion = (match, openTag, version, closeTag) => {
|
||||
return `- Version: ${openTag}***firefox version***${closeTag}`
|
||||
}
|
||||
|
||||
const replaceBrowserArgument = (match, browserName) => {
|
||||
return '--browser ***name:channel***'
|
||||
}
|
||||
|
||||
const replaceSystemMemory = (match, tag1, total, tag2, tag3, free, tag4) => {
|
||||
return `System Memory: ${tag1}***total memory***${tag2} free ${tag3}***free memory***${tag4}`
|
||||
}
|
||||
|
||||
const sanitize = (chunk) => {
|
||||
return chunk.replace(chromiumVersion, replaceChromiumVersion)
|
||||
.replace(firefoxVersion, replaceFirefoxVersion)
|
||||
.replace(browserArgument, replaceBrowserArgument)
|
||||
.replace(systemMemory, replaceSystemMemory)
|
||||
}
|
||||
|
||||
process.stdin.setEncoding('utf8')
|
||||
process.stdin.on('data', function (chunk) {
|
||||
return process.stdout.write(sanitize(chunk))
|
||||
})
|
||||
}
|
||||
|
||||
switch (args['--type']) {
|
||||
case 'cli-info':
|
||||
sanitizeCliInfo()
|
||||
break
|
||||
default:
|
||||
throw new Error(`Unknown STDOUT type to sanitize "${args['--type']}"`)
|
||||
}
|
||||
Reference in New Issue
Block a user