Merge remote-tracking branch 'origin/develop' into issue-97-focus-assertion

This commit is contained in:
Ben Kucera
2019-01-31 13:56:21 -05:00
8 changed files with 243 additions and 67 deletions

View File

@@ -0,0 +1,11 @@
exports['lib/tasks/cache .clear deletes cache folder and everything inside it 1'] = `
[no output]
`
exports['lib/tasks/cache .list lists all versions of cached binary 1'] = `
1.2.3, 2.3.4
`
exports['lib/tasks/cache .path lists path to cache 1'] = `
/.cache/Cypress
`

View File

@@ -255,3 +255,99 @@ exports['cli -v no binary version 1'] = `
Cypress package version: 1.2.3
Cypress binary version: not installed
`
exports['cli unknown option shows help for cache command - unknown option --foo 1'] = `
command: bin/cypress cache --foo
code: 1
failed: true
killed: false
signal: null
timedOut: false
stdout:
-------
error: unknown option: --foo
Usage: cache [command]
Manages the Cypress binary cache
Options:
list list cached binary versions
path print the path to the binary cache
clear delete all cached binaries
-h, --help output usage information
-------
stderr:
-------
-------
`
exports['cli unknown option shows help for cache command - unknown sub-command foo 1'] = `
command: bin/cypress cache foo
code: 1
failed: true
killed: false
signal: null
timedOut: false
stdout:
-------
error: unknown command: cache foo
Usage: cache [command]
Manages the Cypress binary cache
Options:
list list cached binary versions
path print the path to the binary cache
clear delete all cached binaries
-h, --help output usage information
-------
stderr:
-------
-------
`
exports['cli unknown option shows help for cache command - no sub-command 1'] = `
command: bin/cypress cache
code: 1
failed: true
killed: false
signal: null
timedOut: false
stdout:
-------
Usage: cache [command]
Manages the Cypress binary cache
Options:
list list cached binary versions
path print the path to the binary cache
clear delete all cached binaries
-h, --help output usage information
-------
stderr:
-------
-------
`

View File

@@ -15,8 +15,7 @@ function unknownOption (flag, type = 'option') {
logger.error(` error: unknown ${type}:`, flag)
logger.error()
this.outputHelp()
logger.error()
process.exit(1)
util.exit(1)
}
commander.Command.prototype.unknownOption = unknownOption
@@ -62,9 +61,9 @@ const descriptions = {
dev: 'runs cypress in development and bypasses binary check',
forceInstall: 'force install the Cypress binary',
exit: 'keep the browser open after tests finish',
cachePath: 'print the cypress binary cache path',
cacheList: 'list the currently cached versions',
cacheClear: 'delete the Cypress binary cache',
cachePath: 'print the path to the binary cache',
cacheList: 'list cached binary versions',
cacheClear: 'delete all cached binaries',
group: 'a named group for recorded runs in the Cypress dashboard',
parallel: 'enables concurrent runs and automatic load balancing of specs across multiple machines or processes',
ciBuildId: 'the unique identifier for a run on your CI provider. typically a "BUILD_ID" env var. this value is automatically detected for most CI providers',
@@ -203,8 +202,13 @@ module.exports = {
.option('path', text('cachePath'))
.option('clear', text('cacheClear'))
.action(function (opts) {
if (!_.isString(opts)) {
this.outputHelp()
util.exit(1)
}
if (opts.command || !_.includes(['list', 'path', 'clear'], opts)) {
unknownOption.call(this, `cache ${opts}`, 'sub-command')
unknownOption.call(this, `cache ${opts}`, 'command')
}
cache[opts]()

View File

@@ -1,5 +1,6 @@
require('../spec_helper')
const os = require('os')
const cli = require(`${lib}/cli`)
const util = require(`${lib}/util`)
const logger = require(`${lib}/logger`)
@@ -11,13 +12,14 @@ const install = require(`${lib}/tasks/install`)
const snapshot = require('snap-shot-it')
const execa = require('execa-wrap')
describe('cli', function () {
describe('cli', () => {
require('mocha-banner').register()
beforeEach(function () {
beforeEach(() => {
logger.reset()
sinon.stub(process, 'exit')
sinon.stub(util, 'exit')
os.platform.returns('darwin')
// sinon.stub(util, 'exit')
sinon.stub(util, 'logErrorExit1')
this.exec = (args) => {
return cli.init(`node test ${args}`.split(' '))
@@ -30,48 +32,54 @@ describe('cli', function () {
return execa('bin/cypress', ['open', '--foo']).then((result) => {
snapshot('shows help for open --foo', result)
})
}
)
})
it('shows help for run command', () => {
return execa('bin/cypress', ['run', '--foo']).then((result) => {
snapshot('shows help for run --foo', result)
})
}
)
})
it('shows help for cache command - unknown option --foo', () => {
return execa('bin/cypress', ['cache', '--foo']).then(snapshot)
})
it('shows help for cache command - unknown sub-command foo', () => {
return execa('bin/cypress', ['cache', 'foo']).then(snapshot)
})
it('shows help for cache command - no sub-command', () => {
return execa('bin/cypress', ['cache']).then(snapshot)
})
})
context('help command', () => {
it('shows help', () => {
return execa('bin/cypress', ['help']).then(snapshot)
}
)
})
it('shows help for -h', () => {
return execa('bin/cypress', ['-h']).then(snapshot)
}
)
})
it('shows help for --help', () => {
return execa('bin/cypress', ['--help']).then(snapshot)
}
)
})
})
context('unknown command', () => {
it('shows usage and exits', () => {
return execa('bin/cypress', ['foo']).then(snapshot)
}
)
})
})
context('cypress version', function () {
context('cypress version', () => {
const binaryDir = '/binary/dir'
beforeEach(function () {
beforeEach(() => {
sinon.stub(state, 'getBinaryDir').returns(binaryDir)
})
it('reports package version', function (done) {
it('reports package version', (done) => {
sinon.stub(util, 'pkgVersion').returns('1.2.3')
sinon.stub(state, 'getBinaryPkgVersionAsync').withArgs(binaryDir).resolves('X.Y.Z')
@@ -82,7 +90,7 @@ describe('cli', function () {
})
})
it('reports package and binary message', function (done) {
it('reports package and binary message', (done) => {
sinon.stub(util, 'pkgVersion').returns('1.2.3')
sinon.stub(state, 'getBinaryPkgVersionAsync').resolves('X.Y.Z')
@@ -93,7 +101,7 @@ describe('cli', function () {
})
})
it('handles non-existent binary version', function (done) {
it('handles non-existent binary version', (done) => {
sinon.stub(util, 'pkgVersion').returns('1.2.3')
sinon.stub(state, 'getBinaryPkgVersionAsync').resolves(null)
@@ -104,7 +112,7 @@ describe('cli', function () {
})
})
it('handles non-existent binary --version', function (done) {
it('handles non-existent binary --version', (done) => {
sinon.stub(util, 'pkgVersion').returns('1.2.3')
sinon.stub(state, 'getBinaryPkgVersionAsync').resolves(null)
@@ -115,7 +123,7 @@ describe('cli', function () {
})
})
it('handles non-existent binary -v', function (done) {
it('handles non-existent binary -v', (done) => {
sinon.stub(util, 'pkgVersion').returns('1.2.3')
sinon.stub(state, 'getBinaryPkgVersionAsync').resolves(null)
@@ -127,13 +135,13 @@ describe('cli', function () {
})
})
context('cypress run', function () {
beforeEach(function () {
context('cypress run', () => {
beforeEach(() => {
sinon.stub(run, 'start').resolves(0)
util.exit.withArgs(0)
sinon.stub(util, 'exit').withArgs(0)
})
it('calls run.start with options + exits with code', function (done) {
it('calls run.start with options + exits with code', (done) => {
run.start.resolves(10)
this.exec('run')
@@ -143,7 +151,7 @@ describe('cli', function () {
})
})
it('run.start with options + catches errors', function (done) {
it('run.start with options + catches errors', (done) => {
const err = new Error('foo')
run.start.rejects(err)
@@ -155,110 +163,110 @@ describe('cli', function () {
})
})
it('calls run with port', function () {
it('calls run with port', () => {
this.exec('run --port 7878')
expect(run.start).to.be.calledWith({ port: '7878' })
})
it('calls run with spec', function () {
it('calls run with spec', () => {
this.exec('run --spec cypress/integration/foo_spec.js')
expect(run.start).to.be.calledWith({ spec: 'cypress/integration/foo_spec.js' })
})
it('calls run with port with -p arg', function () {
it('calls run with port with -p arg', () => {
this.exec('run -p 8989')
expect(run.start).to.be.calledWith({ port: '8989' })
})
it('calls run with env variables', function () {
it('calls run with env variables', () => {
this.exec('run --env foo=bar,host=http://localhost:8888')
expect(run.start).to.be.calledWith({ env: 'foo=bar,host=http://localhost:8888' })
})
it('calls run with config', function () {
it('calls run with config', () => {
this.exec('run --config watchForFileChanges=false,baseUrl=localhost')
expect(run.start).to.be.calledWith({ config: 'watchForFileChanges=false,baseUrl=localhost' })
})
it('calls run with key', function () {
it('calls run with key', () => {
this.exec('run --key asdf')
expect(run.start).to.be.calledWith({ key: 'asdf' })
})
it('calls run with --record', function () {
it('calls run with --record', () => {
this.exec('run --record')
expect(run.start).to.be.calledWith({ record: true })
})
it('calls run with --record false', function () {
it('calls run with --record false', () => {
this.exec('run --record false')
expect(run.start).to.be.calledWith({ record: false })
})
it('calls run with relative --project folder', function () {
it('calls run with relative --project folder', () => {
this.exec('run --project foo/bar')
expect(run.start).to.be.calledWith({ project: 'foo/bar' })
})
it('calls run with absolute --project folder', function () {
it('calls run with absolute --project folder', () => {
this.exec('run --project /tmp/foo/bar')
expect(run.start).to.be.calledWith({ project: '/tmp/foo/bar' })
})
it('calls run with headed', function () {
it('calls run with headed', () => {
this.exec('run --headed')
expect(run.start).to.be.calledWith({ headed: true })
})
it('calls run with --no-exit', function () {
it('calls run with --no-exit', () => {
this.exec('run --no-exit')
expect(run.start).to.be.calledWith({ exit: false })
})
it('calls run with --parallel', function () {
it('calls run with --parallel', () => {
this.exec('run --parallel')
expect(run.start).to.be.calledWith({ parallel: true })
})
it('calls runs with --ci-build-id', function () {
it('calls runs with --ci-build-id', () => {
this.exec('run --ci-build-id 123')
expect(run.start).to.be.calledWith({ ciBuildId: '123' })
})
it('calls runs with --group', function () {
it('calls runs with --group', () => {
this.exec('run --group staging')
expect(run.start).to.be.calledWith({ group: 'staging' })
})
})
context('cypress open', function () {
beforeEach(function () {
context('cypress open', () => {
beforeEach(() => {
sinon.stub(open, 'start').resolves(0)
})
it('calls open.start with relative --project folder', function () {
it('calls open.start with relative --project folder', () => {
this.exec('open --project foo/bar')
expect(open.start).to.be.calledWith({ project: 'foo/bar' })
})
it('calls open.start with absolute --project folder', function () {
it('calls open.start with absolute --project folder', () => {
this.exec('open --project /tmp/foo/bar')
expect(open.start).to.be.calledWith({ project: '/tmp/foo/bar' })
})
it('calls open.start with options', function () {
it('calls open.start with options', () => {
// sinon.stub(open, 'start').resolves()
this.exec('open --port 7878')
expect(open.start).to.be.calledWith({ port: '7878' })
})
it('calls open.start with global', function () {
it('calls open.start with global', () => {
// sinon.stub(open, 'start').resolves()
this.exec('open --port 7878 --global')
expect(open.start).to.be.calledWith({ port: '7878', global: true })
})
it('calls open.start + catches errors', function (done) {
it('calls open.start + catches errors', (done) => {
const err = new Error('foo')
open.start.rejects(err)
@@ -271,19 +279,19 @@ describe('cli', function () {
})
})
it('install calls install.start without forcing', function () {
it('install calls install.start without forcing', () => {
sinon.stub(install, 'start').resolves()
this.exec('install')
expect(install.start).not.to.be.calledWith({ force: true })
})
it('install calls install.start with force: true when passed', function () {
it('install calls install.start with force: true when passed', () => {
sinon.stub(install, 'start').resolves()
this.exec('install --force')
expect(install.start).to.be.calledWith({ force: true })
})
it('install calls install.start + catches errors', function (done) {
it('install calls install.start + catches errors', (done) => {
const err = new Error('foo')
sinon.stub(install, 'start').rejects(err)
@@ -294,15 +302,15 @@ describe('cli', function () {
done()
})
})
context('cypress verify', function () {
context('cypress verify', () => {
it('verify calls verify.start with force: true', function () {
it('verify calls verify.start with force: true', () => {
sinon.stub(verify, 'start').resolves()
this.exec('verify')
expect(verify.start).to.be.calledWith({ force: true, welcomeMessage: false })
})
it('verify calls verify.start + catches errors', function (done) {
it('verify calls verify.start + catches errors', (done) => {
const err = new Error('foo')
sinon.stub(verify, 'start').rejects(err)

View File

@@ -6,6 +6,7 @@ const fs = require(`${lib}/fs`)
const state = require(`${lib}/tasks/state`)
const cache = require(`${lib}/tasks/cache`)
const stdout = require('../../support/stdout')
const snapshot = require('snap-shot-it')
describe('lib/tasks/cache', () => {
beforeEach(() => {
@@ -24,6 +25,9 @@ describe('lib/tasks/cache', () => {
})
afterEach(() => {
mockfs.restore()
this.stdout = this.stdout.toString().split('\n').slice(0, -2).join('\n')
snapshot(this.stdout.toString() || '[no output]')
stdout.restore()
})

View File

@@ -45,28 +45,33 @@ describe('lib/tasks/download', function () {
context('download url', () => {
it('returns url', () => {
const url = download.getUrl()
la(is.url(url), url)
})
it('returns latest desktop url', () => {
const url = download.getUrl()
snapshot('latest desktop url', normalize(url))
})
it('returns specific desktop version url', () => {
const url = download.getUrl('0.20.2')
snapshot('specific version desktop url', normalize(url))
})
it('returns input if it is already an https link', () => {
const url = 'https://somewhere.com'
const result = download.getUrl(url)
expect(result).to.equal(url)
})
it('returns input if it is already an http link', () => {
const url = 'http://local.com'
const result = download.getUrl(url)
expect(result).to.equal(url)
})
})
@@ -75,24 +80,28 @@ describe('lib/tasks/download', function () {
it('env var', () => {
process.env.CYPRESS_DOWNLOAD_MIRROR = 'https://cypress.example.com'
const url = download.getUrl('0.20.2')
snapshot('base url from CYPRESS_DOWNLOAD_MIRROR', normalize(url))
})
it('env var with trailing slash', () => {
process.env.CYPRESS_DOWNLOAD_MIRROR = 'https://cypress.example.com/'
const url = download.getUrl('0.20.2')
snapshot('base url from CYPRESS_DOWNLOAD_MIRROR with trailing slash', normalize(url))
})
it('env var with subdirectory', () => {
process.env.CYPRESS_DOWNLOAD_MIRROR = 'https://cypress.example.com/example'
const url = download.getUrl('0.20.2')
snapshot('base url from CYPRESS_DOWNLOAD_MIRROR with subdirectory', normalize(url))
})
it('env var with subdirectory and trailing slash', () => {
process.env.CYPRESS_DOWNLOAD_MIRROR = 'https://cypress.example.com/example/'
const url = download.getUrl('0.20.2')
snapshot('base url from CYPRESS_DOWNLOAD_MIRROR with subdirectory and trailing slash', normalize(url))
})
})
@@ -100,7 +109,9 @@ describe('lib/tasks/download', function () {
it('saves example.zip to options.downloadDestination', function () {
nock('https://aws.amazon.com')
.get('/some.zip')
.reply(200, () => fs.createReadStream('test/fixture/example.zip'))
.reply(200, () => {
return fs.createReadStream('test/fixture/example.zip')
})
nock('https://download.cypress.io')
.get('/desktop/1.2.3')
@@ -110,7 +121,6 @@ describe('lib/tasks/download', function () {
'x-version': '0.11.1',
})
const onProgress = sinon.stub().returns(undefined)
return download.start({
@@ -120,6 +130,7 @@ describe('lib/tasks/download', function () {
})
.then((responseVersion) => {
expect(responseVersion).to.eq('0.11.1')
return fs.statAsync(downloadDestination)
})
})
@@ -127,7 +138,9 @@ describe('lib/tasks/download', function () {
it('resolves with response x-version if present', function () {
nock('https://aws.amazon.com')
.get('/some.zip')
.reply(200, () => fs.createReadStream('test/fixture/example.zip'))
.reply(200, () => {
return fs.createReadStream('test/fixture/example.zip')
})
nock('https://download.cypress.io')
.get('/desktop/1.2.3')
@@ -147,7 +160,9 @@ describe('lib/tasks/download', function () {
nock('https://aws.amazon.com')
.get('/some.zip')
.reply(200, () => fs.createReadStream('test/fixture/example.zip'))
.reply(200, () => {
return fs.createReadStream('test/fixture/example.zip')
})
nock('https://download.cypress.io')
.get('/desktop/0.13.0')
@@ -159,6 +174,7 @@ describe('lib/tasks/download', function () {
return download.start(this.options).then((responseVersion) => {
expect(responseVersion).to.eq('0.13.0')
return fs.statAsync(downloadDestination)
})
})
@@ -167,6 +183,7 @@ describe('lib/tasks/download', function () {
const ctx = this
const err = new Error()
err.statusCode = 404
err.statusMessage = 'Not Found'
this.options.version = null

View File

@@ -21,6 +21,7 @@ REQUEST_DEFAULTS = {
json: null
form: null
gzip: true
timeout: null
followRedirect: true
}
@@ -73,11 +74,13 @@ module.exports = (Commands, Cypress, cy, state, config) ->
o.body = args[2]
_.defaults(options, REQUEST_DEFAULTS, {
log: true
timeout: config("responseTimeout")
log: true,
failOnStatusCode: true
})
## if timeout is not supplied, use the configured default
options.timeout ||= config("responseTimeout")
options.method = options.method.toUpperCase()
if _.has(options, "failOnStatus")

View File

@@ -1,10 +1,12 @@
_ = Cypress._
Promise = Cypress.Promise
RESPONSE_TIMEOUT = 22222
describe "src/cy/commands/request", ->
context "#request", ->
beforeEach ->
cy.stub(Cypress, "backend").callThrough()
Cypress.config("responseTimeout", RESPONSE_TIMEOUT)
describe "argument signature", ->
beforeEach ->
@@ -27,6 +29,7 @@ describe "src/cy/commands/request", ->
method: "GET"
gzip: true
followRedirect: true
timeout: RESPONSE_TIMEOUT
})
it "accepts object with url, method, headers, body", ->
@@ -48,6 +51,17 @@ describe "src/cy/commands/request", ->
headers: {
"x-token": "abc123"
}
timeout: RESPONSE_TIMEOUT
})
it "accepts object with url + timeout", ->
cy.request({url: "http://localhost:8000/foo", timeout: 23456}).then ->
@expectOptionsToBe({
url: "http://localhost:8000/foo"
method: "GET"
gzip: true
followRedirect: true
timeout: 23456
})
it "accepts string url", ->
@@ -57,6 +71,7 @@ describe "src/cy/commands/request", ->
method: "GET"
gzip: true
followRedirect: true
timeout: RESPONSE_TIMEOUT
})
it "accepts method + url", ->
@@ -66,6 +81,7 @@ describe "src/cy/commands/request", ->
method: "DELETE"
gzip: true
followRedirect: true
timeout: RESPONSE_TIMEOUT
})
it "accepts method + url + body", ->
@@ -77,6 +93,7 @@ describe "src/cy/commands/request", ->
json: true
gzip: true
followRedirect: true
timeout: RESPONSE_TIMEOUT
})
it "accepts url + body", ->
@@ -88,6 +105,7 @@ describe "src/cy/commands/request", ->
json: true
gzip: true
followRedirect: true
timeout: RESPONSE_TIMEOUT
})
it "accepts url + string body", ->
@@ -98,6 +116,7 @@ describe "src/cy/commands/request", ->
body: "foo"
gzip: true
followRedirect: true
timeout: RESPONSE_TIMEOUT
})
context "method normalization", ->
@@ -108,6 +127,7 @@ describe "src/cy/commands/request", ->
method: "POST"
gzip: true
followRedirect: true
timeout: RESPONSE_TIMEOUT
})
context "url normalization", ->
@@ -120,6 +140,7 @@ describe "src/cy/commands/request", ->
method: "GET"
gzip: true
followRedirect: true
timeout: RESPONSE_TIMEOUT
})
it "uses localhost urls", ->
@@ -129,6 +150,7 @@ describe "src/cy/commands/request", ->
method: "GET"
gzip: true
followRedirect: true
timeout: RESPONSE_TIMEOUT
})
it "uses wwww urls", ->
@@ -138,6 +160,7 @@ describe "src/cy/commands/request", ->
method: "GET"
gzip: true
followRedirect: true
timeout: RESPONSE_TIMEOUT
})
it "prefixes with baseUrl when origin is empty", ->
@@ -150,6 +173,7 @@ describe "src/cy/commands/request", ->
method: "GET"
gzip: true
followRedirect: true
timeout: RESPONSE_TIMEOUT
})
it "prefixes with baseUrl over current origin", ->
@@ -162,6 +186,7 @@ describe "src/cy/commands/request", ->
method: "GET"
gzip: true
followRedirect: true
timeout: RESPONSE_TIMEOUT
})
context "gzip", ->
@@ -175,6 +200,7 @@ describe "src/cy/commands/request", ->
method: "GET"
gzip: false
followRedirect: true
timeout: RESPONSE_TIMEOUT
})
context "auth", ->
@@ -195,6 +221,7 @@ describe "src/cy/commands/request", ->
user: "brian"
pass: "password"
}
timeout: RESPONSE_TIMEOUT
})
context "followRedirect", ->
@@ -206,6 +233,7 @@ describe "src/cy/commands/request", ->
method: "GET"
gzip: true
followRedirect: true
timeout: RESPONSE_TIMEOUT
})
it "can be set to false", ->
@@ -219,6 +247,7 @@ describe "src/cy/commands/request", ->
method: "GET"
gzip: true
followRedirect: false
timeout: RESPONSE_TIMEOUT
})
it "normalizes followRedirects -> followRedirect", ->
@@ -232,6 +261,7 @@ describe "src/cy/commands/request", ->
method: "GET"
gzip: true
followRedirect: false
timeout: RESPONSE_TIMEOUT
})
context "qs", ->
@@ -249,6 +279,7 @@ describe "src/cy/commands/request", ->
gzip: true
followRedirect: true
qs: {foo: "bar"}
timeout: RESPONSE_TIMEOUT
})
context "form", ->
@@ -268,6 +299,7 @@ describe "src/cy/commands/request", ->
form: true
followRedirect: true
body: {foo: "bar"}
timeout: RESPONSE_TIMEOUT
})
it "accepts a string for body", ->
@@ -284,6 +316,7 @@ describe "src/cy/commands/request", ->
form: true
followRedirect: true
body: "foo=bar&baz=quux"
timeout: RESPONSE_TIMEOUT
})
describe "failOnStatus", ->