Fix issue where cy.wrap does not respect timeout option (#7284)

* Testing remove all

* Add functionality to wrap's timeout option

* Fix issues with assertions and promise time outs

* cleanup, modify error message

* update error msg

* fix test after error msg correction

* restore coffee changes

* fix after merge: modify decaffed files

* Fix error messages and update tests

Co-authored-by: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
This commit is contained in:
Zach Panzarino
2020-06-01 19:12:40 -04:00
committed by GitHub
parent a074302e40
commit 095ec75eb0
5 changed files with 125 additions and 10 deletions

View File

@@ -1,6 +1,8 @@
const _ = require('lodash')
const Promise = require('bluebird')
const $dom = require('../../dom')
const $errUtils = require('../../cypress/error_utils')
module.exports = (Commands, Cypress, cy) => {
Commands.addAll({ prevSubject: 'optional' }, {
@@ -33,7 +35,13 @@ module.exports = (Commands, Cypress, cy) => {
wrap (arg, options = {}) {
const userOptions = options
options = _.defaults({}, userOptions, { log: true })
options = _.defaults({}, userOptions, {
log: true,
timeout: Cypress.config('defaultCommandTimeout'),
})
// we'll handle the timeout ourselves
cy.clearTimeout()
if (options.log !== false) {
options._log = Cypress.log({
@@ -45,14 +53,28 @@ module.exports = (Commands, Cypress, cy) => {
}
}
const resolveWrap = () => {
return cy.verifyUpcomingAssertions(arg, options, {
onRetry: resolveWrap,
return Promise.resolve(arg)
.timeout(options.timeout)
.catch(Promise.TimeoutError, () => {
$errUtils.throwErrByPath('wrap.timed_out', {
args: { timeout: options.timeout },
})
.return(arg)
}
})
.catch((err) => {
$errUtils.throwErr(err, {
onFail: options._log,
})
})
.then((subject) => {
const resolveWrap = () => {
return cy.verifyUpcomingAssertions(subject, options, {
onRetry: resolveWrap,
})
.return(subject)
}
return resolveWrap()
return resolveWrap()
})
},
})
}

View File

@@ -327,7 +327,7 @@ const create = function (specWindow, Cypress, Cookies, state, config, log) {
state('nestedIndex', state('index'))
return command.get('args')
}).all()
})
.then((args) => {
// store this if we enqueue new commands

View File

@@ -661,7 +661,7 @@ module.exports = {
message: stripIndent`\
${cmd('{{cmd}}')} timed out after waiting \`{{timeout}}ms\`.
Your callback function returned a promise which never resolved.
Your callback function returned a promise that never resolved.
The callback function was:
@@ -1798,6 +1798,19 @@ module.exports = {
},
},
wrap: {
timed_out: {
message: stripIndent`
${cmd('wrap')} timed out waiting \`{{timeout}}ms\` to complete.
You called \`cy.wrap()\` with a promise that never resolved.
To increase the timeout, use \`{ timeout: number }\`
`,
docsUrl: 'https://on.cypress.io/wrap',
},
},
xhr: {
aborted: 'This XHR was aborted by your code -- check this stack trace below.',
missing: '`XMLHttpRequest#xhr` is missing.',

View File

@@ -1955,7 +1955,7 @@ describe('src/cy/commands/connectors', () => {
cy.on('fail', (err) => {
// get + each
expect(this.logs.length).to.eq(2)
expect(err.message).to.include('`cy.each()` timed out after waiting `50ms`.\n\nYour callback function returned a promise which never resolved.')
expect(err.message).to.include('`cy.each()` timed out after waiting `50ms`.\n\nYour callback function returned a promise that never resolved.')
expect(err.docsUrl).to.include('https://on.cypress.io/each')
done()

View File

@@ -191,7 +191,32 @@ describe('src/cy/commands/misc', () => {
})
})
it('can extend the default timeout', () => {
Cypress.config('defaultCommandTimeout', 100)
const timeoutPromise = new Promise((resolve, reject) => {
return setTimeout(() => {
resolve(null)
})
}, 200)
cy.wrap(timeoutPromise, { timeout: 300 })
})
describe('errors', () => {
beforeEach(function () {
this.logs = []
cy.on('log:added', (attrs, log) => {
if (attrs.name === 'wrap') {
this.lastLog = log
this.logs.push(log)
}
})
return null
})
it('throws when wrapping an array of windows', (done) => {
cy.on('fail', (err) => {
expect(err.message).to.include('`cy.scrollTo()` failed because it requires a DOM element.')
@@ -219,6 +244,61 @@ describe('src/cy/commands/misc', () => {
cy.wrap([doc]).screenshot()
})
})
it('throws when exceeding default timeout', function (done) {
Cypress.config('defaultCommandTimeout', 100)
cy.on('fail', (err) => {
expect(this.logs.length).to.eq(1)
expect(err.message).to.include('`cy.wrap()` timed out waiting `100ms` to complete.')
expect(err.message).to.include('You called `cy.wrap()` with a promise that never resolved.')
expect(err.message).to.include('To increase the timeout, use `{ timeout: number }`')
expect(this.lastLog.get('error')).to.eq(err)
done()
})
const timeoutPromise = new Promise((resolve) => {
setTimeout((() => {
resolve(null)
}), 200)
})
cy.wrap(timeoutPromise)
})
it('throws when exceeding custom timeout', function (done) {
cy.on('fail', (err) => {
expect(this.logs.length).to.eq(1)
expect(err.message).to.include('`cy.wrap()` timed out waiting `100ms` to complete.')
expect(err.message).to.include('You called `cy.wrap()` with a promise that never resolved.')
expect(err.message).to.include('To increase the timeout, use `{ timeout: number }`')
expect(this.lastLog.get('error')).to.eq(err)
done()
})
const timeoutPromise = new Promise((resolve) => {
setTimeout((() => {
resolve(null)
}), 200)
})
cy.wrap(timeoutPromise, { timeout: 100 })
})
it('logs once when promise parameter is rejected', function (done) {
cy.on('fail', (err) => {
expect(this.logs.length).to.eq(1)
expect(err.message).to.include('custom error')
expect(this.lastLog.get('error')).to.eq(err)
done()
})
const rejectedPromise = new Promise((resolve, reject) => {
reject(new Error('custom error'))
})
cy.wrap(rejectedPromise)
})
})
describe('.log', () => {