@revoloo/cypress6
Version:
Cypress.io end to end testing tool
1,237 lines (946 loc) • 37 kB
JavaScript
const { _, $ } = Cypress
describe('src/cy/commands/actions/trigger', () => {
before(() => {
cy
.visit('/fixtures/dom.html')
.then(function (win) {
this.body = win.document.body.outerHTML
})
})
beforeEach(function () {
const doc = cy.state('document')
$(doc.body).empty().html(this.body)
})
context('#trigger', () => {
it('sends event', (done) => {
const $btn = cy.$$('#button')
$btn.on('mouseover', (e) => {
const { fromElViewport } = Cypress.dom.getElementCoordinatesByPosition($btn)
const obj = _.pick(e.originalEvent, 'bubbles', 'cancelable', 'target', 'type')
expect(obj).to.deep.eq({
bubbles: true,
cancelable: true,
target: $btn.get(0),
type: 'mouseover',
})
expect(e.clientX).to.be.closeTo(fromElViewport.x, 1)
expect(e.clientY).to.be.closeTo(fromElViewport.y, 1)
done()
})
cy.get('#button').trigger('mouseover')
})
it('bubbles up event by default', (done) => {
cy
.window()
.then((win) => {
$(win).one('mouseover', () => {
done()
})
cy.get('#button').trigger('mouseover')
})
})
it('does not bubble up event if specified', (done) => {
cy
.window()
.then((win) => {
const $win = $(win)
$win.on('keydown', (e) => {
const evt = JSON.stringify(e.originalEvent, [
'bubbles', 'cancelable', 'isTrusted', 'type', 'clientX', 'clientY',
])
done(new Error(`event should not have bubbled up to window listener: ${evt}`))
})
cy
.get('#button')
.trigger('keydown', {
bubbles: false,
})
.then(() => {
$win.off('keydown')
done()
})
})
})
it('sends through event options, overriding defaults', (done) => {
let options = {
clientX: 42,
clientY: 24,
pageX: 420,
pageY: 240,
foo: 'foo',
}
cy.$$('button:first').on('mouseover', (e) => {
const eventOptions = _.pick(e.originalEvent, 'clientX', 'clientY', 'pageX', 'pageY', 'foo')
expect(eventOptions).to.eql(options)
done()
})
cy.get('button:first').trigger('mouseover', options)
})
it('records correct clientX when el scrolled', (done) => {
const $btn = $('<button id=\'scrolledBtn\' style=\'position: absolute; top: 1600px; left: 1200px; width: 100px;\'>foo</button>').appendTo(cy.$$('body'))
const win = cy.state('window')
$btn.on('mouseover', (e) => {
const { fromElViewport } = Cypress.dom.getElementCoordinatesByPosition($btn)
expect(win.scrollX).to.be.gt(0)
expect(e.clientX).to.be.closeTo(fromElViewport.x, 1)
done()
})
cy.get('#scrolledBtn').trigger('mouseover')
})
it('records correct clientY when el scrolled', (done) => {
const $btn = $('<button id=\'scrolledBtn\' style=\'position: absolute; top: 1600px; left: 1200px; width: 100px;\'>foo</button>').appendTo(cy.$$('body'))
const win = cy.state('window')
$btn.on('mouseover', (e) => {
const { fromElViewport } = Cypress.dom.getElementCoordinatesByPosition($btn)
expect(win.scrollX).to.be.gt(0)
expect(e.clientY).to.be.closeTo(fromElViewport.y, 1)
done()
})
cy.get('#scrolledBtn').trigger('mouseover')
})
// NOTE: flaky about 50% of the time in Firefox...
// temporarily skipping for now, but this needs
// to be reenabled after launch once we have time
// to look at the underlying failure cause
it.skip('records correct pageX and pageY el scrolled', (done) => {
const $btn = $('<button id=\'scrolledBtn\' style=\'position: absolute; top: 1600px; left: 1200px; width: 100px;\'>foo</button>').appendTo(cy.$$('body'))
const win = cy.state('window')
$btn.on('mouseover', (e) => {
expect(e.pageX).to.be.closeTo(win.scrollX + e.clientX, 1)
expect(e.pageY).to.be.closeTo(win.scrollY + e.clientY, 1)
done()
})
cy.get('#scrolledBtn').trigger('mouseover')
})
it('does not change the subject', () => {
const $input = cy.$$('input:first')
cy.get('input:first').trigger('keydown').then(($el) => {
expect($el.get(0)).to.eq($input.get(0))
})
})
it('can trigger events on the window', () => {
let expected = false
const win = cy.state('window')
$(win).on('scroll', (e) => {
expected = true
})
cy
.window().trigger('scroll')
.then(() => {
expect(expected).to.be.true
})
})
it('can trigger custom events on the window', () => {
let expected = false
const win = cy.state('window')
$(win).on('foo', (e) => {
expect(e.detail).to.deep.eq({ foo: 'bar' })
expected = true
})
cy
.window().trigger('foo', {
detail: { foo: 'bar' },
})
.then(() => {
expect(expected).to.be.true
})
})
it('can trigger events on the document', () => {
let expected = false
const doc = cy.state('document')
$(doc).on('dragover', () => {
expected = true
})
cy.document().trigger('dragover').then(() => {
expect(expected).to.be.true
})
})
it('can handle window w/length > 1 as a subject', () => {
cy.window().should('have.length.gt', 1).trigger('click')
})
// https://github.com/cypress-io/cypress/issues/3686
it('view should be AUT window', (done) => {
cy.window().then((win) => {
cy.get('input:first').then((jQueryElement) => {
let elem = jQueryElement.get(0)
elem.addEventListener('mousedown', (event) => {
expect(event.view).to.eql(win)
done()
})
})
})
cy.get('input:first').trigger('mousedown', {
eventConstructor: 'MouseEvent',
button: 0,
shiftKey: false,
ctrlKey: false,
})
})
describe('actionability', () => {
it('can trigger on elements which are hidden until scrolled within parent container', () => {
cy.get('#overflow-auto-container').contains('quux').trigger('mousedown')
})
it('can trigger on elements with `opacity: 0`', () => {
cy.get('#opacity-0').trigger('mousedown')
})
it('can trigger on elements with parents that have `opacity: 0`', () => {
cy.get('#opacity-0-parent').trigger('mousedown')
})
it('can trigger on readonly inputs', () => {
cy.get('#readonly-attr').trigger('mousedown')
})
it('does not scroll when being forced', () => {
const scrolled = []
cy.on('scrolled', ($el, type) => {
scrolled.push(type)
})
cy
.get('button:last').trigger('mouseover', { force: true })
.then(() => {
expect(scrolled).to.be.empty
})
})
it('can force trigger on hidden elements', () => {
cy.get('button:first').invoke('hide').trigger('tap', { force: true })
})
it('can force trigger on disabled elements', () => {
cy.get('input:first').invoke('prop', 'disabled', true).trigger('tap', { force: true })
})
it('can forcibly trigger even when being covered by another element', () => {
const $btn = $('<button>button covered</button>').attr('id', 'button-covered-in-span').prependTo(cy.$$('body'))
$('<span>span on button</span>').css({ position: 'absolute', left: $btn.offset().left, top: $btn.offset().top, padding: 5, display: 'inline-block', backgroundColor: 'yellow' }).prependTo(cy.$$('body'))
const scrolled = []
let retried = false
let tapped = false
cy.on('scrolled', ($el, type) => {
scrolled.push(type)
})
cy.on('command:retry', ($el, type) => {
retried = true
})
$btn.on('tap', () => {
tapped = true
})
cy.get('#button-covered-in-span').trigger('tap', { force: true }).then(() => {
expect(scrolled).to.be.empty
expect(retried).to.be.false
expect(tapped).to.be.true
})
})
it('eventually triggers when covered up', () => {
const $btn = $('<button>button covered</button>')
.attr('id', 'button-covered-in-span')
.prependTo(cy.$$('body'))
const $span = $('<span>span on button</span>').css({
position: 'absolute',
left: $btn.offset().left,
top: $btn.offset().top,
padding: 5,
display: 'inline-block',
backgroundColor: 'yellow',
})
.prependTo(cy.$$('body'))
const scrolled = []
let retried = false
cy.on('scrolled', ($el, type) => {
return scrolled.push(type)
})
cy.on('command:retry', _.after(3, () => {
$span.hide()
retried = true
}))
cy.get('#button-covered-in-span').trigger('mousedown').then(() => {
expect(retried).to.be.true
// - element scrollIntoView
// - element scrollIntoView (retry animation coords)
// - element scrollIntoView (retry covered)
// - element scrollIntoView (retry covered)
// - window
expect(scrolled).to.deep.eq(['element', 'element', 'element', 'element'])
})
})
it('issues event to descendent', () => {
let mouseovers = 0
const $btn = $('<div>', {
id: 'div-covered-in-span',
})
.css({ padding: 10, margin: 0, border: 'solid 1px #000' })
.prependTo(cy.$$('body'))
const $span = $('<span>span covering div</span>')
.css({ padding: 5, display: 'block', backgroundColor: 'yellow' })
.appendTo($btn)
$btn.on('mouseover', () => {
mouseovers += 1
})
$span.on('mouseover', () => {
mouseovers += 1
})
cy
.get('#div-covered-in-span').trigger('mouseover')
.should(() => {
expect(mouseovers).to.eq(2)
})
})
it('issues event to descendent when waitForAnimations is false', { waitForAnimations: false }, () => {
let mouseovers = 0
const $btn = $('<div>', {
id: 'div-covered-in-span',
})
.css({ padding: 10, margin: 0, border: 'solid 1px #000' })
.prependTo(cy.$$('body'))
const $span = $('<span>span covering div</span>')
.css({ padding: 5, display: 'block', backgroundColor: 'yellow' })
.appendTo($btn)
$btn.on('mouseover', () => {
mouseovers += 1
})
$span.on('mouseover', () => {
mouseovers += 1
})
cy
.get('#div-covered-in-span').trigger('mouseover')
.should(() => {
expect(mouseovers).to.eq(2)
})
})
it('scrolls the window past a fixed position element when being covered', () => {
$('<button>button covered</button>')
.attr('id', 'button-covered-in-nav')
.appendTo(cy.$$('#fixed-nav-test'))
$('<nav>nav on button</nav>')
.css({
position: 'fixed',
left: 0,
top: 0,
padding: 20,
backgroundColor: 'yellow',
zIndex: 1,
})
.prependTo(cy.$$('body'))
const scrolled = []
cy.on('scrolled', ($el, type) => {
return scrolled.push(type)
})
cy.get('#button-covered-in-nav').trigger('mouseover').then(() => {
// - element scrollIntoView
// - element scrollIntoView (retry animation coords)
// - window
expect(scrolled).to.deep.eq(['element', 'element', 'window'])
})
})
it('scrolls the window past two fixed positioned elements when being covered', () => {
$('<button>button covered</button>')
.attr('id', 'button-covered-in-nav')
.appendTo(cy.$$('#fixed-nav-test'))
$('<nav>nav on button</nav>')
.css({
position: 'fixed',
left: 0,
top: 0,
padding: 20,
backgroundColor: 'yellow',
zIndex: 1,
})
.prependTo(cy.$$('body'))
$('<nav>nav2 on button</nav>')
.css({
position: 'fixed',
left: 0,
top: 40,
padding: 20,
backgroundColor: 'red',
zIndex: 1,
})
.prependTo(cy.$$('body'))
const scrolled = []
cy.on('scrolled', ($el, type) => {
return scrolled.push(type)
})
cy.get('#button-covered-in-nav').trigger('mouseover').then(() => {
// - element scrollIntoView
// - element scrollIntoView (retry animation coords)
// - window (nav1)
// - window (nav2)
expect(scrolled).to.deep.eq(['element', 'element', 'window', 'window'])
})
})
it('scrolls a container past a fixed position element when being covered', () => {
cy.viewport(600, 450)
const $body = cy.$$('body')
// we must remove all of our children to
// prevent the window from scrolling
$body.children().remove()
// this tests that our container properly scrolls!
const $container = $('<div></div>')
.attr('id', 'scrollable-container')
.css({
position: 'relative',
width: 300,
height: 200,
marginBottom: 100,
backgroundColor: 'green',
overflow: 'auto',
})
.prependTo($body)
$('<button>button covered</button>')
.attr('id', 'button-covered-in-nav')
.css({
marginTop: 500,
// marginLeft: 500
marginBottom: 500,
})
.appendTo($container)
$('<nav>nav on button</nav>')
.css({
position: 'fixed',
left: 0,
top: 0,
padding: 20,
backgroundColor: 'yellow',
zIndex: 1,
})
.prependTo($container)
const scrolled = []
cy.on('scrolled', ($el, type) => {
scrolled.push(type)
})
cy.get('#button-covered-in-nav').trigger('mouseover').then(() => {
// - element scrollIntoView
// - element scrollIntoView (retry animation coords)
// - window
// - container
expect(scrolled).to.deep.eq(['element', 'element', 'window', 'container'])
})
})
it('waits until element becomes visible', () => {
const $btn = cy.$$('#button').hide()
let retried = false
cy.on('command:retry', _.after(3, () => {
$btn.show()
retried = true
}))
cy.get('#button').trigger('mouseover').then(() => {
expect(retried).to.be.true
})
})
it('waits until element is no longer disabled', () => {
const $btn = cy.$$('#button').prop('disabled', true)
let retried = false
let mouseovers = 0
$btn.on('mouseover', () => {
mouseovers += 1
})
cy.on('command:retry', _.after(3, () => {
$btn.prop('disabled', false)
retried = true
}))
cy.get('#button').trigger('mouseover').then(() => {
expect(mouseovers).to.eq(1)
expect(retried).to.be.true
})
})
it('waits until element stops animating', () => {
let retries = 0
cy.on('command:retry', (obj) => {
retries += 1
})
cy.stub(cy, 'ensureElementIsNotAnimating')
.throws(new Error('animating!'))
.onThirdCall().returns()
cy.get('button:first').trigger('mouseover').then(() => {
// - retry animation coords
// - retry animation
// - retry animation
expect(retries).to.eq(3)
expect(cy.ensureElementIsNotAnimating).to.be.calledThrice
})
})
it('does not throw when waiting for animations is disabled', {
waitForAnimations: false,
}, () => {
cy.stub(cy, 'ensureElementIsNotAnimating').throws(new Error('animating!'))
cy.get('button:first').trigger('mouseover').then(() => {
expect(cy.ensureElementIsNotAnimating).not.to.be.called
})
})
it('does not throw when turning off waitForAnimations in options', () => {
cy.stub(cy, 'ensureElementIsNotAnimating').throws(new Error('animating!'))
cy.get('button:first').trigger('tap', { waitForAnimations: false }).then(() => {
expect(cy.ensureElementIsNotAnimating).not.to.be.called
})
})
it('passes options.animationDistanceThreshold to cy.ensureElementIsNotAnimating', () => {
const $btn = cy.$$('button:first')
const { fromElWindow } = Cypress.dom.getElementCoordinatesByPosition($btn)
cy.spy(cy, 'ensureElementIsNotAnimating')
cy.get('button:first').trigger('tap', { animationDistanceThreshold: 1000 }).then(() => {
const { args } = cy.ensureElementIsNotAnimating.firstCall
expect(args[1]).to.deep.eq([fromElWindow, fromElWindow])
expect(args[2]).to.eq(1000)
})
})
it('passes config.animationDistanceThreshold to cy.ensureElementIsNotAnimating', () => {
const animationDistanceThreshold = Cypress.config('animationDistanceThreshold')
const $btn = cy.$$('button:first')
const { fromElWindow } = Cypress.dom.getElementCoordinatesByPosition($btn)
cy.spy(cy, 'ensureElementIsNotAnimating')
cy.get('button:first').trigger('mouseover').then(() => {
const { args } = cy.ensureElementIsNotAnimating.firstCall
expect(args[1]).to.deep.eq([fromElWindow, fromElWindow])
expect(args[2]).to.eq(animationDistanceThreshold)
})
})
it('can specify scrollBehavior in options', () => {
cy.get('button:first').then((el) => {
cy.spy(el[0], 'scrollIntoView')
})
cy.get('button:first').trigger('mouseover', { scrollBehavior: 'bottom' })
cy.get('button:first').then((el) => {
expect(el[0].scrollIntoView).to.be.calledWith({ block: 'end' })
})
})
it('does not scroll when scrollBehavior is false in options', () => {
cy.scrollTo('top')
cy.get('button:first').then((el) => {
cy.spy(el[0], 'scrollIntoView')
})
cy.get('button:first').trigger('mouseover', { scrollBehavior: false })
cy.get('button:first').then((el) => {
expect(el[0].scrollIntoView).not.to.be.called
})
})
it('does not scroll when scrollBehavior is false in config', { scrollBehavior: false }, () => {
cy.scrollTo('top')
cy.get('button:first').then((el) => {
cy.spy(el[0], 'scrollIntoView')
})
cy.get('button:first').trigger('mouseover')
cy.get('button:first').then((el) => {
expect(el[0].scrollIntoView).not.to.be.called
})
})
it('calls scrollIntoView by default', () => {
cy.scrollTo('top')
cy.get('button:first').then((el) => {
cy.spy(el[0], 'scrollIntoView')
})
cy.get('button:first').trigger('mouseover')
cy.get('button:first').then((el) => {
expect(el[0].scrollIntoView).to.be.calledWith({ block: 'start' })
})
})
it('errors when scrollBehavior is false and element is out of view and is clicked', (done) => {
cy.scrollTo('top')
cy.on('fail', (err) => {
expect(err.message).to.include('`cy.trigger()` failed because the center of this element is hidden from view')
expect(cy.state('window').scrollY).to.equal(0)
expect(cy.state('window').scrollX).to.equal(0)
done()
})
// make sure the input is out of view
const $body = cy.$$('body')
$('<div>Long block 5</div>')
.css({
height: '500px',
border: '1px solid red',
marginTop: '10px',
width: '100%',
}).prependTo($body)
cy.get('button:first').trigger('mouseover', { scrollBehavior: false, timeout: 200 })
})
})
describe('assertion verification', {
defaultCommandTimeout: 100,
}, () => {
beforeEach(function () {
cy.on('log:added', (attrs, log) => {
if (log.get('name') === 'assert') {
this.lastLog = log
}
})
return null
})
it('eventually passes the assertion', () => {
const $btn = cy.$$('button:first')
cy.on('command:retry', _.once(() => {
$btn.addClass('moused-over')
}))
cy.get('button:first').trigger('mouseover').should('have.class', 'moused-over').then(function () {
const { lastLog } = this
expect(lastLog.get('name')).to.eq('assert')
expect(lastLog.get('state')).to.eq('passed')
expect(lastLog.get('ended')).to.be.true
})
})
})
describe('position argument', () => {
it('can trigger event on center by default', (done) => {
const $button = cy.$$('<button />').css({ width: 200, height: 100 }).prependTo(cy.$$('body'))
const onMouseover = function (e) {
expect(e.clientX).to.equal(108)
expect(e.clientY).to.equal(50)
done()
}
$button.one('mouseover', onMouseover)
cy.get('button:first').trigger('mouseover')
})
it('can trigger event on center', (done) => {
const $button = cy.$$('<button />').css({ width: 200, height: 100 }).prependTo(cy.$$('body'))
const onMouseover = function (e) {
expect(e.clientX).to.equal(108)
expect(e.clientY).to.equal(50)
done()
}
$button.on('mouseover', onMouseover)
cy.get('button:first').trigger('mouseover', 'center')
})
it('can trigger event on topLeft', (done) => {
const $button = cy.$$('<button />').css({ width: 200, height: 100 }).prependTo(cy.$$('body'))
const onMouseover = function (e) {
expect(e.clientX).to.equal(8)
// NOTE: firefox leaves 1px on top of element on scroll, so add top offset
expect(e.clientY).to.equal(0 + Math.ceil(e.target.getBoundingClientRect().top))
done()
}
$button.on('mouseover', onMouseover)
cy.get('button:first').trigger('mouseover', 'topLeft')
})
it('can trigger event on topRight', (done) => {
const $button = cy.$$('<button />').css({ width: 200, height: 100 }).prependTo(cy.$$('body'))
const onMouseover = function (e) {
expect(e.clientX).to.equal(207)
expect(e.clientY).to.equal(0 + Math.ceil(e.target.getBoundingClientRect().top))
done()
}
$button.on('mouseover', onMouseover)
cy.get('button:first').trigger('mouseover', 'topRight')
})
it('can trigger event on bottomLeft', (done) => {
const $button = cy.$$('<button />').css({ width: 200, height: 100 }).prependTo(cy.$$('body'))
const onMouseover = function (e) {
expect(e.clientX).to.equal(8)
expect(e.clientY).to.equal(99)
done()
}
$button.on('mouseover', onMouseover)
cy.get('button:first').trigger('mouseover', 'bottomLeft')
})
it('can trigger event on bottomRight', (done) => {
const $button = cy.$$('<button />').css({ width: 200, height: 100 }).prependTo(cy.$$('body'))
const onMouseover = function (e) {
expect(e.clientX).to.equal(207)
expect(e.clientY).to.equal(99)
done()
}
$button.on('mouseover', onMouseover)
cy.get('button:first').trigger('mouseover', 'bottomRight')
})
it('can pass options along with position', (done) => {
const $button = cy.$$('<button />').css({ width: 200, height: 100 }).prependTo(cy.$$('body'))
const onMouseover = function (e) {
expect(e.clientX).to.equal(207)
expect(e.clientY).to.equal(99)
done()
}
$button.on('mouseover', onMouseover)
cy.get('button:first').trigger('mouseover', 'bottomRight', { bubbles: false })
})
})
describe('relative coordinate arguments', () => {
it('can specify x and y', (done) => {
const $button = cy.$$('<button />').css({ width: 200, height: 100 }).prependTo(cy.$$('body'))
const onMouseover = function (e) {
expect(e.clientX).to.equal(83)
expect(e.clientY).to.equal(78 + Math.ceil(e.target.getBoundingClientRect().top))
done()
}
$button.on('mouseover', onMouseover)
cy.get('button:first').trigger('mouseover', 75, 78)
})
it('can pass options along with x, y', (done) => {
const $button = cy.$$('<button />').css({ width: 200, height: 100 }).prependTo(cy.$$('body'))
const onMouseover = function (e) {
expect(e.clientX).to.equal(83)
expect(e.clientY).to.equal(78 + Math.ceil(e.target.getBoundingClientRect().top))
done()
}
$button.on('mouseover', onMouseover)
cy.get('button:first').trigger('mouseover', 75, 78, { bubbles: false })
})
})
// https://github.com/cypress-io/cypress/issues/5650
describe('dispatches correct Event objects', () => {
it('should trigger KeyboardEvent with .trigger inside Cypress event listener', (done) => {
cy.window().then((win) => {
cy.get('input:first').then((jQueryElement) => {
let elemHtml = jQueryElement.get(0)
elemHtml.addEventListener('keydown', (event) => {
expect(event instanceof win['KeyboardEvent']).to.be.true
done()
})
})
})
cy.get('input:first').trigger('keydown', {
eventConstructor: 'KeyboardEvent',
keyCode: 65,
which: 65,
shiftKey: false,
ctrlKey: false,
})
})
it('should trigger KeyboardEvent with .trigger inside html script event listener', () => {
cy.visit('fixtures/issue-5650.html')
cy.get('#test-input').trigger('keydown', {
eventConstructor: 'KeyboardEvent',
keyCode: 65,
which: 65,
shiftKey: false,
ctrlKey: false,
})
cy.get('#result').contains('isKeyboardEvent: true')
})
it('should trigger MouseEvent with .trigger inside Cypress event listener', (done) => {
cy.window().then((win) => {
cy.get('input:first').then((jQueryElement) => {
let elem = jQueryElement.get(0)
elem.addEventListener('mousedown', (event) => {
expect(event instanceof win['MouseEvent']).to.be.true
done()
})
})
})
cy.get('input:first').trigger('mousedown', {
eventConstructor: 'MouseEvent',
button: 0,
shiftKey: false,
ctrlKey: false,
})
})
it('should trigger MouseEvent with .trigger inside html script event listener', () => {
cy.visit('fixtures/issue-5650.html')
cy.get('#test-input').trigger('mousedown', {
eventConstructor: 'MouseEvent',
button: 0,
shiftKey: false,
ctrlKey: false,
})
cy.get('#result').contains('isMouseEvent: true')
})
})
describe('errors', {
defaultCommandTimeout: 100,
}, () => {
beforeEach(function () {
this.logs = []
cy.on('log:added', (attrs, log) => {
this.lastLog = log
return this.logs.push(log)
})
return null
})
// TODO: trigger() doesn't throw an error now.
it.skip('throws when eventName is not a string', (done) => {
cy.on('fail', (err) => {
expect(err.message).to.eq('`cy.trigger()` can only be called on a single element. Your subject contained 15 elements.')
expect(err.docsUrl).to.eq('https://on.cypress.io/trigger')
done()
})
cy.get('button:first').trigger('`cy.trigger()` must be passed a non-empty string as its 1st argument. You passed: \'undefined\'.')
})
it('throws when not a dom subject', (done) => {
cy.on('fail', () => {
done()
})
cy.trigger('mouseover')
})
it('throws when attempting to trigger multiple elements', (done) => {
const num = cy.$$('button').length
cy.on('fail', (err) => {
expect(err.message).to.eq(`\`cy.trigger()\` can only be called on a single element. Your subject contained ${num} elements.`)
expect(err.docsUrl).to.eq('https://on.cypress.io/trigger')
done()
})
cy.get('button').trigger('mouseover')
})
it('throws when subject is not in the document', (done) => {
let mouseover = 0
const checkbox = cy.$$(':checkbox:first').on('mouseover', (e) => {
mouseover += 1
checkbox.remove()
return false
})
cy.on('fail', (err) => {
expect(mouseover).to.eq(1)
expect(err.message).to.include('`cy.trigger()` failed because this element')
done()
})
cy.get(':checkbox:first').trigger('mouseover').trigger('mouseover')
})
it('logs once when not dom subject', function (done) {
cy.on('fail', (err) => {
const { lastLog } = this
expect(this.logs.length).to.eq(2)
expect(lastLog.get('error')).to.eq(err)
done()
})
cy.wrap({}).trigger('mouseover')
})
it('throws when the subject isnt visible', function (done) {
cy.$$('#button:first').hide()
cy.on('fail', (err) => {
const { lastLog } = this
expect(this.logs.length).to.eq(2)
expect(lastLog.get('error')).to.eq(err)
expect(err.message).to.include('`cy.trigger()` failed because this element is not visible')
done()
})
cy.get('button:first').trigger('mouseover')
})
it('throws when the element has `opacity: 0` but is not visible', function (done) {
cy.on('fail', (err) => {
expect(this.logs.length).eq(2)
expect(err.message).not.to.contain('CSS property: `opacity: 0`')
expect(err.message).to.contain('`cy.trigger()` failed because this element is not visible')
done()
})
cy.get('#opacity-0-hidden').trigger('mouseover')
})
it('throws when subject is disabled', function (done) {
cy.$$('#button').prop('disabled', true)
cy.on('fail', (err) => {
// get + click logs
expect(this.logs.length).eq(2)
expect(err.message).to.include('`cy.trigger()` failed because this element is `disabled`:\n')
done()
})
cy.get('#button').trigger('mouseover')
})
it('throws when provided invalid position', function (done) {
cy.on('fail', (err) => {
expect(this.logs.length).to.eq(2)
expect(err.message).to.eq('Invalid position argument: `foo`. Position may only be topLeft, top, topRight, left, center, right, bottomLeft, bottom, bottomRight.')
done()
})
cy.get('button:first').trigger('mouseover', 'foo')
})
it('throws when provided invalid event type', function (done) {
cy.on('fail', (err) => {
expect(this.logs.length).to.eq(2)
expect(err.message).to.eq('Timed out retrying after 100ms: `cy.trigger()` `eventConstructor` option must be a valid event (e.g. \'MouseEvent\', \'KeyboardEvent\'). You passed: `FooEvent`')
done()
})
cy.get('button:first').trigger('mouseover', {
eventConstructor: 'FooEvent',
})
})
it('throws when element animation exceeds timeout', (done) => {
// force the animation calculation to think we moving at a huge distance ;-)
cy.stub(Cypress.utils, 'getDistanceBetween').returns(100000)
let clicks = 0
cy.$$('button:first').on('tap', () => {
clicks += 1
})
cy.on('fail', (err) => {
expect(clicks).to.eq(0)
expect(err.message).to.include('`cy.trigger()` could not be issued because this element is currently animating:\n')
expect(err.docsUrl).to.eq('https://on.cypress.io/element-is-animating')
done()
})
cy.get('button:first').trigger('tap')
})
it('eventually fails the assertion', function (done) {
cy.on('fail', (err) => {
const { lastLog } = this
expect(err.message).to.include(lastLog.get('error').message)
expect(err.message).not.to.include('undefined')
expect(lastLog.get('name')).to.eq('assert')
expect(lastLog.get('state')).to.eq('failed')
expect(lastLog.get('error')).to.be.an.instanceof(chai.AssertionError)
done()
})
cy.get('button:first').trigger('mouseover').should('have.class', 'moused-over')
})
it('does not log an additional log on failure', function (done) {
cy.on('fail', () => {
expect(this.logs.length).to.eq(3)
done()
})
cy.get('button:first').trigger('mouseover').should('have.class', 'moused-over')
})
})
describe('.log', () => {
beforeEach(function () {
cy.on('log:added', (attrs, log) => {
this.lastLog = log
})
return null
})
it('logs immediately before resolving', (done) => {
const button = cy.$$('button:first')
cy.on('log:added', (attrs, log) => {
if (log.get('name') === 'trigger') {
expect(log.get('state')).to.eq('pending')
expect(log.get('$el').get(0)).to.eq(button.get(0))
done()
}
})
cy.get('button:first').trigger('mouseover')
})
it('snapshots before triggering', function (done) {
cy.$$('button:first').on('mouseover', () => {
const { lastLog } = this
expect(lastLog.get('snapshots').length).to.eq(1)
expect(lastLog.get('snapshots')[0].name).to.eq('before')
expect(lastLog.get('snapshots')[0].body).to.be.an('object')
done()
})
cy.get('button:first').trigger('mouseover')
})
it('snapshots after triggering', () => {
cy.get('button:first').trigger('mouseover').then(function () {
const { lastLog } = this
expect(lastLog.get('snapshots').length).to.eq(2)
expect(lastLog.get('snapshots')[1].name).to.eq('after')
expect(lastLog.get('snapshots')[1].body).to.be.an('object')
})
})
it('logs only 1 event', () => {
const logs = []
cy.on('log:added', (attrs, log) => {
if (log.get('name') === 'trigger') {
logs.push(log)
}
})
cy.get('button:first').trigger('mouseover').then(() => {
expect(logs.length).to.eq(1)
})
})
it('passes in coords', () => {
cy.get('button:first').trigger('mouseover').then(function ($btn) {
const { lastLog } = this
const { fromElWindow } = Cypress.dom.getElementCoordinatesByPosition($btn)
expect(lastLog.get('coords')).to.deep.eq(fromElWindow, 'x', 'y')
})
})
it('#consoleProps', function () {
cy.get('button:first').trigger('mouseover').then(($button) => {
const consoleProps = this.lastLog.invoke('consoleProps')
const { fromElWindow } = Cypress.dom.getElementCoordinatesByPosition($button)
const logCoords = this.lastLog.get('coords')
const eventOptions = consoleProps['Event options']
expect(logCoords.x).to.be.closeTo(fromElWindow.x, 1) // ensure we are within 1
expect(logCoords.y).to.be.closeTo(fromElWindow.y, 1) // ensure we are within 1
expect(consoleProps.Command).to.eq('trigger')
expect(eventOptions.bubbles).to.be.true
expect(eventOptions.cancelable).to.be.true
expect(eventOptions.clientX).to.be.be.a('number')
expect(eventOptions.clientY).to.be.be.a('number')
expect(eventOptions.pageX).to.be.be.a('number')
expect(eventOptions.pageY).to.be.be.a('number')
expect(eventOptions.screenX).to.be.be.a('number').and.eq(eventOptions.clientX)
expect(eventOptions.screenY).to.be.be.a('number').and.eq(eventOptions.clientY)
})
})
})
})
})