Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update blob-util, remove @types/blog-util #7784

Closed
wants to merge 12 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion LICENSE
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2019 Cypress.io
Copyright (c) 2020 Cypress.io

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
1 change: 1 addition & 0 deletions cli/__snapshots__/cli_spec.js
Expand Up @@ -75,6 +75,7 @@ exports['shows help for run --foo 1'] = `
--parallel enables concurrent runs and automatic load balancing of specs across multiple machines or processes
-p, --port <port> runs Cypress on a specific port. overrides any value in cypress.json.
-P, --project <project-path> path to the project
-q, --quiet run quietly, using only the configured reporter
--record [bool] records the run. sends test results, screenshots and videos to your Cypress Dashboard.
-r, --reporter <reporter> runs a specific mocha reporter. pass a path to use a custom reporter. defaults to "spec"
-o, --reporter-options <reporter-options> options for the mocha reporter. defaults to "null"
Expand Down
2 changes: 2 additions & 0 deletions cli/__snapshots__/errors_spec.js
Expand Up @@ -35,6 +35,7 @@ exports['errors individual has the following errors 1'] = [
"incompatibleHeadlessFlags",
"invalidCacheDirectory",
"invalidCypressEnv",
"invalidRunProjectPath",
"invalidSmokeTestDisplayError",
"missingApp",
"missingDependency",
Expand All @@ -44,6 +45,7 @@ exports['errors individual has the following errors 1'] = [
"removed",
"smokeTestFailure",
"unexpected",
"unknownError",
"versionMismatch"
]

Expand Down
2 changes: 2 additions & 0 deletions cli/lib/cli.js
Expand Up @@ -109,6 +109,7 @@ const descriptions = {
parallel: 'enables concurrent runs and automatic load balancing of specs across multiple machines or processes',
port: 'runs Cypress on a specific port. overrides any value in cypress.json.',
project: 'path to the project',
quiet: 'run quietly, using only the configured reporter',
record: 'records the run. sends test results, screenshots and videos to your Cypress Dashboard.',
reporter: 'runs a specific mocha reporter. pass a path to use a custom reporter. defaults to "spec"',
reporterOptions: 'options for the mocha reporter. defaults to "null"',
Expand Down Expand Up @@ -231,6 +232,7 @@ module.exports = {
.option('--parallel', text('parallel'))
.option('-p, --port <port>', text('port'))
.option('-P, --project <project-path>', text('project'))
.option('-q, --quiet', text('quiet'))
.option('--record [bool]', text('record'), coerceFalse)
.option('-r, --reporter <reporter>', text('reporter'))
.option('-o, --reporter-options <reporter-options>', text('reporterOptions'))
Expand Down
12 changes: 12 additions & 0 deletions cli/lib/cypress.js
Expand Up @@ -9,13 +9,25 @@ const run = require('./exec/run')
const util = require('./util')

const cypressModuleApi = {
/**
* Opens Cypress GUI
* @see https://on.cypress.io/module-api#cypress-open
*/
open (options = {}) {
options = util.normalizeModuleOptions(options)

return open.start(options)
},

/**
* Runs Cypress tests in the current project
* @see https://on.cypress.io/module-api#cypress-run
*/
run (options = {}) {
if (!run.isValidProject(options.project)) {
return Promise.reject(new Error(`Invalid project path parameter: ${options.project}`))
}

options = util.normalizeModuleOptions(options)

return tmp.fileAsync()
Expand Down
31 changes: 26 additions & 5 deletions cli/lib/errors.js
Expand Up @@ -9,13 +9,36 @@ const state = require('./tasks/state')

const docsUrl = 'https://on.cypress.io'
const requiredDependenciesUrl = `${docsUrl}/required-dependencies`
const runDocumentationUrl = `${docsUrl}/cypress-run`

// TODO it would be nice if all error objects could be enforced via types
// to only have description + solution properties

const hr = '----------'

const genericErrorSolution = stripIndent`
Search for an existing issue or open a GitHub issue at

${chalk.blue(util.issuesUrl)}
`

// common errors Cypress application can encounter
const unknownError = {
description: 'Unknown Cypress CLI error',
solution: genericErrorSolution,
}

const invalidRunProjectPath = {
description: 'Invalid --project path',
solution: stripIndent`
Please provide a valid project path.

Learn more about ${chalk.cyan('cypress run')} at:

${chalk.blue(runDocumentationUrl)}
`,
}

const failedDownload = {
description: 'The Cypress App could not be downloaded.',
solution: stripIndent`
Expand All @@ -26,11 +49,7 @@ const failedDownload = {

const failedUnzip = {
description: 'The Cypress App could not be unzipped.',
solution: stripIndent`
Search for an existing issue or open a GitHub issue at

${chalk.blue(util.issuesUrl)}
`,
solution: genericErrorSolution,
}

const missingApp = (binaryDir) => {
Expand Down Expand Up @@ -390,6 +409,7 @@ module.exports = {
getError,
hr,
errors: {
unknownError,
nonZeroExitCodeXvfb,
missingXvfb,
missingApp,
Expand All @@ -408,5 +428,6 @@ module.exports = {
smokeTestFailure,
childProcessKilled,
incompatibleHeadlessFlags,
invalidRunProjectPath,
},
}
65 changes: 57 additions & 8 deletions cli/lib/exec/run.js
Expand Up @@ -6,11 +6,60 @@ const spawn = require('./spawn')
const verify = require('../tasks/verify')
const { exitWithError, errors } = require('../errors')

// maps options collected by the CLI
// and forms list of CLI arguments to the server
/**
* Throws an error with "details" property from
* "errors" object.
* @param {Object} details - Error details
*/
const throwInvalidOptionError = (details) => {
if (!details) {
details = errors.unknownError
}

// throw this error synchronously, it will be caught later on and
// the details will be propagated to the promise chain
const err = new Error()

err.details = details
throw err
}

/**
* Typically a user passes a string path to the project.
* But "cypress open" allows using `false` to open in global mode,
* and the user can accidentally execute `cypress run --project false`
* which should be invalid.
*/
const isValidProject = (v) => {
if (typeof v === 'boolean') {
return false
}

if (v === '' || v === 'false' || v === 'true') {
return false
}

return true
}

/**
* Maps options collected by the CLI
* and forms list of CLI arguments to the server.
*
* Note: there is lightweight validation, with errors
* thrown synchronously.
*
* @returns {string[]} list of CLI arguments
*/
const processRunOptions = (options = {}) => {
debug('processing run options %o', options)

if (!isValidProject(options.project)) {
debug('invalid project option %o', { project: options.project })

return throwInvalidOptionError(errors.invalidRunProjectPath)
}

const args = ['--run-project', options.project]

if (options.browser) {
Expand Down Expand Up @@ -55,12 +104,7 @@ const processRunOptions = (options = {}) => {

if (options.headless) {
if (options.headed) {
// throw this error synchronously, it will be caught later on and
// the details will be propagated to the promise chain
const err = new Error()

err.details = errors.incompatibleHeadlessFlags
throw err
return throwInvalidOptionError(errors.incompatibleHeadlessFlags)
}

args.push('--headed', !options.headless)
Expand Down Expand Up @@ -89,6 +133,10 @@ const processRunOptions = (options = {}) => {
args.push('--port', options.port)
}

if (options.quiet) {
args.push('--quiet')
}

// if record is defined and we're not
// already in ci mode, then send it up
if (options.record != null && !options.ci) {
Expand Down Expand Up @@ -119,6 +167,7 @@ const processRunOptions = (options = {}) => {

module.exports = {
processRunOptions,
isValidProject,
// resolves with the number of failed tests
start (options = {}) {
_.defaults(options, {
Expand Down
1 change: 1 addition & 0 deletions cli/lib/util.js
Expand Up @@ -214,6 +214,7 @@ const parseOpts = (opts) => {
'parallel',
'port',
'project',
'quiet',
'reporter',
'reporterOptions',
'record',
Expand Down
1 change: 0 additions & 1 deletion cli/package.json
Expand Up @@ -63,7 +63,6 @@
"@babel/preset-env": "7.9.5",
"@cypress/sinon-chai": "1.1.0",
"@packages/root": "*",
"@types/blob-util": "1.3.3",
"@types/bluebird": "3.5.29",
"@types/chai": "4.2.7",
"@types/chai-jquery": "1.1.40",
Expand Down
1 change: 0 additions & 1 deletion cli/scripts/utils.js
Expand Up @@ -4,7 +4,6 @@
* definition files that we will need to include with our NPM package.
*/
const includeTypes = [
'blob-util',
'bluebird',
'lodash',
'mocha',
Expand Down
24 changes: 24 additions & 0 deletions cli/test/lib/cypress_spec.js
Expand Up @@ -147,5 +147,29 @@ describe('cypress', function () {
expect(args).to.deep.eq(opts)
})
})

it('rejects if project is an empty string', () => {
return expect(cypress.run({ project: '' })).to.be.rejected
})

it('rejects if project is true', () => {
return expect(cypress.run({ project: true })).to.be.rejected
})

it('rejects if project is false', () => {
return expect(cypress.run({ project: false })).to.be.rejected
})

it('passes quiet: true', () => {
const opts = {
quiet: true,
}

return cypress.run(opts)
.then(getStartArgs)
.then((args) => {
expect(args).to.deep.eq(opts)
})
})
})
})
22 changes: 22 additions & 0 deletions cli/test/lib/exec/run_spec.js
Expand Up @@ -15,6 +15,28 @@ describe('exec run', function () {
})

context('.processRunOptions', function () {
it('allows string --project option', () => {
const args = run.processRunOptions({
project: '/path/to/project',
})

expect(args).to.deep.equal(['--run-project', '/path/to/project'])
})

it('throws an error for empty string --project', () => {
expect(() => run.processRunOptions({ project: '' })).to.throw()
})

it('throws an error for boolean --project', () => {
expect(() => run.processRunOptions({ project: false })).to.throw()
expect(() => run.processRunOptions({ project: true })).to.throw()
})

it('throws an error for --project "false" or "true"', () => {
expect(() => run.processRunOptions({ project: 'false' })).to.throw()
expect(() => run.processRunOptions({ project: 'true' })).to.throw()
})

it('passes --browser option', () => {
const args = run.processRunOptions({
browser: 'test browser',
Expand Down
4 changes: 4 additions & 0 deletions cli/types/cypress-npm-api.d.ts
Expand Up @@ -68,6 +68,10 @@ declare module 'cypress' {
* Override default port
*/
port: number
/**
* Run quietly, using only the configured reporter
*/
quiet: boolean
/**
* Whether to record the test run
*/
Expand Down
6 changes: 6 additions & 0 deletions packages/desktop-gui/cypress/integration/login_spec.js
Expand Up @@ -69,6 +69,12 @@ describe('Login', function () {
})
})

it('passes utm code when it triggers ipc \'begin:auth\'', function () {
cy.then(function () {
expect(this.ipc.beginAuth).to.be.calledWith('Nav Login Button')
})
})

it('disables login button', () => {
cy.get('@loginBtn').should('be.disabled')
})
Expand Down
14 changes: 13 additions & 1 deletion packages/desktop-gui/cypress/integration/runs_list_spec.js
Expand Up @@ -303,6 +303,18 @@ describe('Runs List', function () {
it('does not fetch runs', function () {
expect(this.ipc.getRuns).not.to.be.called
})

it('clicking Log In to Dashboard opens login', () => {
cy.contains('button', 'Log In to Dashboard').click().then(function () {
expect(this.ipc.beginAuth).to.be.calledOnce
})
})

it('clicking Log In to Dashboard passes utm code', () => {
cy.contains('button', 'Log In to Dashboard').click().then(function () {
expect(this.ipc.beginAuth).to.be.calledWith('Runs Tab Login Button')
})
})
})

context('without a project id', function () {
Expand Down Expand Up @@ -345,7 +357,7 @@ describe('Runs List', function () {

it('clicking Log In to Dashboard opens login', () => {
cy.contains('button', 'Log In to Dashboard').click().then(function () {
expect(this.ipc.beginAuth).to.be.called
expect(this.ipc.beginAuth).to.be.calledOnce
})
})
})
Expand Down
4 changes: 2 additions & 2 deletions packages/desktop-gui/src/auth/auth-api.js
Expand Up @@ -21,12 +21,12 @@ class AuthApi {
})
}

login () {
login (utm) {
ipc.onAuthMessage((__, message) => {
authStore.setMessage(message)
})

return ipc.beginAuth()
return ipc.beginAuth(utm)
.then((user) => {
authStore.setUser(user)
authStore.setMessage(null)
Expand Down
2 changes: 1 addition & 1 deletion packages/desktop-gui/src/auth/login-form.jsx
Expand Up @@ -115,7 +115,7 @@ class LoginForm extends Component {

this.setState({ isLoggingIn: true })

authApi.login()
authApi.login(this.props.utm)
.then(() => {
this.props.onSuccess()
})
Expand Down