@revoloo/cypress6
Version:
Cypress.io end to end testing tool
1,018 lines (787 loc) • 34.3 kB
JavaScript
const { $ } = Cypress.$Cypress
const { _ } = Cypress
describe('src/cy/commands/actions/scroll', () => {
before(() => {
cy
.visit('/fixtures/scrolling.html')
.then(function (win) {
this.body = win.document.body.outerHTML
})
})
beforeEach(function () {
const doc = cy.state('document')
$(doc.body).empty().html(this.body)
cy.viewport(600, 200)
})
context('#scrollTo', () => {
beforeEach(function () {
this.win = cy.state('window')
this.scrollVert = cy.$$('#scroll-to-vertical')
this.scrollHoriz = cy.$$('#scroll-to-horizontal')
this.scrollBoth = cy.$$('#scroll-to-both')
// reset the scrollable containers back
// to furthest left and top
this.win.scrollTop = 0
this.win.scrollLeft = 0
this.scrollVert.scrollTop = 0
this.scrollVert.scrollLeft = 0
this.scrollHoriz.scrollTop = 0
this.scrollHoriz.scrollLeft = 0
this.scrollBoth.scrollTop = 0
this.scrollBoth.scrollLeft = 0
})
describe('subject', () => {
it('is window by default', () => {
cy.scrollTo('125px').then(function (win2) {
expect(this.win).to.eq(win2)
})
})
it('is DOM', () => {
cy.get('#scroll-to-vertical').scrollTo('125px').then(function ($el) {
expect($el.get(0)).to.eq(this.scrollVert.get(0))
})
})
it('can use window', () => {
cy.window().scrollTo('10px').then((win) => {
expect(win.scrollX).to.eq(10)
})
})
it('can handle window w/length > 1 as a subject', () => {
cy.visit('/fixtures/dom.html')
cy.window().should('have.length.gt', 1)
.scrollTo('10px')
})
})
describe('x axis only', () => {
it('scrolls x axis to num px', function () {
expect(this.scrollHoriz.get(0).scrollTop).to.eq(0)
expect(this.scrollHoriz.get(0).scrollLeft).to.eq(0)
cy.get('#scroll-to-horizontal').scrollTo(300).then(function () {
expect(this.scrollHoriz.get(0).scrollTop).to.eq(0)
expect(this.scrollHoriz.get(0).scrollLeft).to.eq(300)
})
})
it('scrolls x axis to px', function () {
expect(this.scrollHoriz.get(0).scrollTop).to.eq(0)
expect(this.scrollHoriz.get(0).scrollLeft).to.eq(0)
cy.get('#scroll-to-horizontal').scrollTo('125px').then(function () {
expect(this.scrollHoriz.get(0).scrollTop).to.eq(0)
expect(this.scrollHoriz.get(0).scrollLeft).to.eq(125)
})
})
it('scrolls x axis by % of scrollable height', function () {
expect(this.scrollHoriz.get(0).scrollTop).to.eq(0)
expect(this.scrollHoriz.get(0).scrollLeft).to.eq(0)
cy.get('#scroll-to-horizontal').scrollTo('50%').then(function () {
// they don't calculate the height of the container
// in the percentage of the scroll (since going the height
// of the container wouldn't scroll at all...)
expect(this.scrollHoriz.get(0).scrollTop).to.eq(0)
expect(this.scrollHoriz.get(0).scrollLeft).to.eq((500 - 100) / 2)
})
})
})
describe('position arguments', () => {
it('scrolls x/y axis to topLeft', function () {
expect(this.scrollBoth.get(0).scrollTop).to.eq(0)
expect(this.scrollBoth.get(0).scrollLeft).to.eq(0)
cy.get('#scroll-to-both').scrollTo('topLeft').then(function () {
expect(this.scrollBoth.get(0).scrollTop).to.eq(0)
expect(this.scrollBoth.get(0).scrollLeft).to.eq(0)
})
})
it('scrolls x/y axis to top', function () {
expect(this.scrollBoth.get(0).scrollTop).to.eq(0)
expect(this.scrollBoth.get(0).scrollLeft).to.eq(0)
cy.get('#scroll-to-both').scrollTo('top').then(function () {
expect(this.scrollBoth.get(0).scrollTop).to.eq(0)
expect(this.scrollBoth.get(0).scrollLeft).to.eq((500 - 100) / 2)
})
})
it('scrolls x/y axis to topRight', function () {
expect(this.scrollBoth.get(0).scrollTop).to.eq(0)
expect(this.scrollBoth.get(0).scrollLeft).to.eq(0)
cy.get('#scroll-to-both').scrollTo('topRight').then(function () {
expect(this.scrollBoth.get(0).scrollTop).to.eq(0)
expect(this.scrollBoth.get(0).scrollLeft).to.eq((500 - 100))
})
})
it('scrolls x/y axis to left', function () {
expect(this.scrollBoth.get(0).scrollTop).to.eq(0)
expect(this.scrollBoth.get(0).scrollLeft).to.eq(0)
cy.get('#scroll-to-both').scrollTo('left').then(function () {
expect(this.scrollBoth.get(0).scrollTop).to.eq((500 - 100) / 2)
expect(this.scrollBoth.get(0).scrollLeft).to.eq(0)
})
})
it('scrolls x/y axis to center', function () {
expect(this.scrollBoth.get(0).scrollTop).to.eq(0)
expect(this.scrollBoth.get(0).scrollLeft).to.eq(0)
cy.get('#scroll-to-both').scrollTo('center').then(function () {
expect(this.scrollBoth.get(0).scrollTop).to.eq((500 - 100) / 2)
expect(this.scrollBoth.get(0).scrollLeft).to.eq((500 - 100) / 2)
})
})
it('scrolls x/y axis to right', function () {
expect(this.scrollBoth.get(0).scrollTop).to.eq(0)
expect(this.scrollBoth.get(0).scrollLeft).to.eq(0)
cy.get('#scroll-to-both').scrollTo('right').then(function () {
expect(this.scrollBoth.get(0).scrollTop).to.eq((500 - 100) / 2)
expect(this.scrollBoth.get(0).scrollLeft).to.eq((500 - 100))
})
})
it('scrolls x/y axis to bottomLeft', function () {
expect(this.scrollBoth.get(0).scrollTop).to.eq(0)
expect(this.scrollBoth.get(0).scrollLeft).to.eq(0)
cy.get('#scroll-to-both').scrollTo('bottomLeft').then(function () {
expect(this.scrollBoth.get(0).scrollTop).to.eq((500 - 100))
expect(this.scrollBoth.get(0).scrollLeft).to.eq(0)
})
})
it('scrolls x/y axis to bottom', function () {
expect(this.scrollBoth.get(0).scrollTop).to.eq(0)
expect(this.scrollBoth.get(0).scrollLeft).to.eq(0)
cy.get('#scroll-to-both').scrollTo('bottom').then(function () {
expect(this.scrollBoth.get(0).scrollTop).to.eq((500 - 100))
expect(this.scrollBoth.get(0).scrollLeft).to.eq((500 - 100) / 2)
})
})
it('scrolls x/y axis to bottomRight', function () {
expect(this.scrollBoth.get(0).scrollTop).to.eq(0)
expect(this.scrollBoth.get(0).scrollLeft).to.eq(0)
cy.get('#scroll-to-both').scrollTo('bottomRight').then(function () {
expect(this.scrollBoth.get(0).scrollTop).to.eq((500 - 100))
expect(this.scrollBoth.get(0).scrollLeft).to.eq((500 - 100))
})
})
})
describe('scroll both axis', () => {
it('scrolls both x and y axis num of px', function () {
expect(this.scrollBoth.get(0).scrollTop).to.eq(0)
expect(this.scrollBoth.get(0).scrollLeft).to.eq(0)
cy.get('#scroll-to-both').scrollTo(300, 150).then(function () {
expect(this.scrollBoth.get(0).scrollTop).to.eq(150)
expect(this.scrollBoth.get(0).scrollLeft).to.eq(300)
})
})
it('scrolls x to 0 and y num of px', function () {
expect(this.scrollBoth.get(0).scrollTop).to.eq(0)
expect(this.scrollBoth.get(0).scrollLeft).to.eq(0)
cy.get('#scroll-to-both').scrollTo(0, 150).then(function () {
expect(this.scrollBoth.get(0).scrollTop).to.eq(150)
expect(this.scrollBoth.get(0).scrollLeft).to.eq(0)
})
})
it('scrolls x num of px and y to 0 ', function () {
expect(this.scrollBoth.get(0).scrollTop).to.eq(0)
expect(this.scrollBoth.get(0).scrollLeft).to.eq(0)
cy.get('#scroll-to-both').scrollTo(150, 0).then(function () {
expect(this.scrollBoth.get(0).scrollTop).to.eq(0)
expect(this.scrollBoth.get(0).scrollLeft).to.eq(150)
})
})
it('scrolls both x and y axis of px', function () {
expect(this.scrollBoth.get(0).scrollTop).to.eq(0)
expect(this.scrollBoth.get(0).scrollLeft).to.eq(0)
cy.get('#scroll-to-both').scrollTo('300px', '150px').then(function () {
expect(this.scrollBoth.get(0).scrollTop).to.eq(150)
expect(this.scrollBoth.get(0).scrollLeft).to.eq(300)
})
})
it('scrolls both x and y axis of percentage', function () {
expect(this.scrollBoth.get(0).scrollTop).to.eq(0)
expect(this.scrollBoth.get(0).scrollLeft).to.eq(0)
cy.get('#scroll-to-both').scrollTo('50%', '50%').then(function () {
expect(this.scrollBoth.get(0).scrollTop).to.eq((500 - 100) / 2)
expect(this.scrollBoth.get(0).scrollLeft).to.eq((500 - 100) / 2)
})
})
it('scrolls x to 0 and y percentage', function () {
expect(this.scrollBoth.get(0).scrollTop).to.eq(0)
expect(this.scrollBoth.get(0).scrollLeft).to.eq(0)
cy.get('#scroll-to-both').scrollTo('0%', '50%').then(function () {
expect(this.scrollBoth.get(0).scrollTop).to.eq((500 - 100) / 2)
expect(this.scrollBoth.get(0).scrollLeft).to.eq(0)
})
})
it('scrolls x to percentage and y to 0', function () {
expect(this.scrollBoth.get(0).scrollTop).to.eq(0)
expect(this.scrollBoth.get(0).scrollLeft).to.eq(0)
cy.get('#scroll-to-both').scrollTo('50%', '0%').then(function () {
expect(this.scrollBoth.get(0).scrollTop).to.eq(0)
expect(this.scrollBoth.get(0).scrollLeft).to.eq((500 - 100) / 2)
})
})
})
describe('scrolls with options', () => {
it('calls jQuery scroll to', () => {
const scrollTo = cy.spy($.fn, 'scrollTo')
cy.get('#scroll-to-both').scrollTo('25px').then(() => {
expect(scrollTo).to.be.calledWith({ left: '25px', top: 0 })
})
})
it('sets duration to 0 by default', () => {
const scrollTo = cy.spy($.fn, 'scrollTo')
cy.get('#scroll-to-both').scrollTo('25px').then(() => {
expect(scrollTo).to.be.calledWithMatch({}, { duration: 0 })
})
})
it('sets axis to correct xy', () => {
const scrollTo = cy.spy($.fn, 'scrollTo')
cy.get('#scroll-to-both').scrollTo('25px', '80px').then(() => {
expect(scrollTo).to.be.calledWithMatch({}, { axis: 'xy' })
})
})
it('scrolling resolves after a set duration', function () {
expect(this.scrollHoriz.get(0).scrollTop).to.eq(0)
expect(this.scrollHoriz.get(0).scrollLeft).to.eq(0)
const scrollTo = cy.spy($.fn, 'scrollTo')
cy.get('#scroll-to-horizontal').scrollTo('125px', { duration: 500 }).then(function () {
expect(scrollTo).to.be.calledWithMatch({}, { duration: 500 })
expect(this.scrollHoriz.get(0).scrollTop).to.eq(0)
expect(this.scrollHoriz.get(0).scrollLeft).to.eq(125)
})
})
it('accepts duration string option', () => {
const scrollTo = cy.spy($.fn, 'scrollTo')
cy.get('#scroll-to-both').scrollTo('25px', { duration: '500' }).then(() => {
expect(scrollTo.args[0][1].duration).to.eq('500')
})
})
it('has easing set to swing by default', () => {
const scrollTo = cy.spy($.fn, 'scrollTo')
cy.get('#scroll-to-both').scrollTo('25px').then(() => {
expect(scrollTo.args[0][1].easing).to.eq('swing')
})
})
it('scrolling resolves after easing', function () {
expect(this.scrollBoth.get(0).scrollTop).to.eq(0)
expect(this.scrollBoth.get(0).scrollLeft).to.eq(0)
const scrollTo = cy.spy($.fn, 'scrollTo')
cy.get('#scroll-to-both').scrollTo('25px', '50px', { easing: 'linear' }).then(function () {
expect(scrollTo).to.be.calledWithMatch({}, { easing: 'linear' })
expect(this.scrollBoth.get(0).scrollTop).to.eq(50)
expect(this.scrollBoth.get(0).scrollLeft).to.eq(25)
})
})
it('retries until element is scrollable', () => {
const $container = cy.$$('#nonscroll-becomes-scrollable')
expect($container.get(0).scrollTop).to.eq(0)
expect($container.get(0).scrollLeft).to.eq(0)
let retried = false
cy.on('command:retry', _.after(2, () => {
$container.css('overflow', 'scroll')
retried = true
}))
cy.get('#nonscroll-becomes-scrollable').scrollTo(500, 300).then(() => {
expect(retried).to.be.true
expect($container.get(0).scrollTop).to.eq(300)
expect($container.get(0).scrollLeft).to.eq(500)
})
})
// https://github.com/cypress-io/cypress/issues/1924
it('skips scrollability check', () => {
const scrollTo = cy.spy($.fn, 'scrollTo')
cy.get('button:first').scrollTo('bottom', { ensureScrollable: false }).then(() => {
cy.stub(cy, 'ensureScrollability')
expect(scrollTo).to.be.calledWithMatch({}, { ensureScrollable: false })
expect(cy.ensureScrollability).not.to.be.called
})
})
})
describe('assertion verification', () => {
beforeEach(function () {
cy.on('log:added', (attrs, log) => {
if (log.get('name') === 'assert') {
this.lastLog = log
}
})
return null
})
it('eventually passes the assertion', () => {
cy.on('command:retry', _.after(2, () => {
cy.$$('#scroll-into-view-horizontal').addClass('scrolled')
}))
cy
.get('#scroll-into-view-horizontal')
.scrollTo('right')
.should('have.class', 'scrolled').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
})
})
it('waits until the subject is scrollable', () => {
cy.stub(cy, 'ensureScrollability')
.onFirstCall().throws(new Error())
cy.on('command:retry', () => {
return cy.ensureScrollability.returns()
})
cy
.get('#scroll-into-view-horizontal')
.scrollTo('right').then(() => {
expect(cy.ensureScrollability).to.be.calledTwice
})
})
})
describe('errors', {
defaultCommandTimeout: 50,
}, () => {
beforeEach(function () {
this.logs = []
cy.on('log:added', (attrs, log) => {
this.lastLog = log
this.logs.push(log)
})
return null
})
it('throws when subject isn\'t scrollable', (done) => {
cy.on('fail', (err) => {
expect(err.message).to.include('`cy.scrollTo()` failed because this element is not scrollable:')
expect(err.message).to.include(`\`<button>button</button>\``)
expect(err.message).to.include('Make sure you\'re targeting the correct element or use `{ensureScrollable: false}` to disable the scrollable check.')
expect(err.docsUrl).to.eq('https://on.cypress.io/scrollto')
done()
})
cy.get('button:first').scrollTo('bottom')
})
context('subject errors', () => {
it('throws when not passed DOM element as subject', (done) => {
cy.on('fail', (err) => {
expect(err.message).to.include('`cy.scrollTo()` failed because it requires a DOM element.')
expect(err.message).to.include('{foo: bar}')
expect(err.message).to.include('> `cy.noop()`')
done()
})
cy.noop({ foo: 'bar' }).scrollTo('250px')
})
it('throws if scrollable container is multiple elements', (done) => {
cy.on('fail', (err) => {
expect(err.message).to.include('`cy.scrollTo()` can only be used to scroll 1 element, you tried to scroll 2 elements.')
expect(err.docsUrl).to.eq('https://on.cypress.io/scrollto')
done()
})
cy.get('button').scrollTo('500px')
})
})
context('argument errors', () => {
it('throws if no args passed', (done) => {
cy.on('fail', (err) => {
expect(err.message).to.include('`cy.scrollTo()` must be called with a valid `position`. It can be a string, number or object.')
expect(err.docsUrl).to.eq('https://on.cypress.io/scrollto')
done()
})
cy.scrollTo()
})
it('throws if NaN', (done) => {
cy.on('fail', (err) => {
expect(err.message).to.include('`cy.scrollTo()` must be called with a valid `position`. It can be a string, number or object. Your position was: `25, NaN`')
expect(err.docsUrl).to.eq('https://on.cypress.io/scrollto')
done()
})
cy.get('#scroll-to-both').scrollTo(25, 0 / 0)
})
it('throws if Infinity', (done) => {
cy.on('fail', (err) => {
expect(err.message).to.include('`cy.scrollTo()` must be called with a valid `position`. It can be a string, number or object. Your position was: `25, Infinity`')
expect(err.docsUrl).to.eq('https://on.cypress.io/scrollto')
done()
})
cy.get('#scroll-to-both').scrollTo(25, 10 / 0)
})
it('throws if unrecognized position', (done) => {
cy.on('fail', (err) => {
expect(err.message).to.include('Invalid position argument: `botom`. Position may only be topLeft, top, topRight, left, center, right, bottomLeft, bottom, bottomRight.')
done()
})
cy.get('#scroll-to-both').scrollTo('botom')
})
})
context('option errors', () => {
it('throws if duration is not a number or valid string', (done) => {
cy.on('fail', (err) => {
expect(err.message).to.include('`cy.scrollTo()` must be called with a valid `duration`. Duration may be either a number (ms) or a string representing a number (ms). Your duration was: `foo`')
expect(err.docsUrl).to.eq('https://on.cypress.io/scrollto')
done()
})
cy.get('#scroll-to-both').scrollTo('25px', { duration: 'foo' })
})
it('throws if unrecognized easing', (done) => {
cy.on('fail', (err) => {
expect(err.message).to.include('`cy.scrollTo()` must be called with a valid `easing`. Your easing was: `flower`')
expect(err.docsUrl).to.eq('https://on.cypress.io/scrollto')
done()
})
cy.get('#scroll-to-both').scrollTo('25px', { easing: 'flower' })
})
it('throws if ensureScrollable is not a boolean', (done) => {
cy.on('fail', (err) => {
expect(err.message).to.include('`cy.scrollTo()` `ensureScrollable` option must be a boolean. You passed: `force`')
expect(err.docsUrl).to.eq('https://on.cypress.io/scrollto')
done()
})
cy.get('button:first').scrollTo('bottom', { ensureScrollable: 'force' })
})
})
})
describe('.log', () => {
beforeEach(function () {
this.logs = []
cy.on('log:added', (attrs, log) => {
this.lastLog = log
return this.logs.push(log)
})
return null
})
it('logs out scrollTo', () => {
cy.get('#scroll-to-both').scrollTo(25).then(function () {
const { lastLog } = this
expect(lastLog.get('name')).to.eq('scrollTo')
})
})
it('passes in $el if child command', () => {
cy.get('#scroll-to-both').scrollTo(25).then(function ($container) {
const { lastLog } = this
expect(lastLog.get('$el').get(0)).to.eq($container.get(0))
})
})
it('passes undefined in $el if parent command', () => {
cy.scrollTo(25).then(function () {
const { lastLog } = this
expect(lastLog.get('$el')).to.be.undefined
})
})
it('logs duration options', () => {
cy.get('#scroll-to-both').scrollTo(25, { duration: 1 }).then(function () {
const { lastLog } = this
expect(lastLog.get('message')).to.eq('25, 0, {duration: 1}')
})
})
it('logs easing options', () => {
cy.get('#scroll-to-both').scrollTo(25, { easing: 'linear' }).then(function () {
const { lastLog } = this
expect(lastLog.get('message')).to.eq('25, 0, {easing: linear}')
})
})
it('snapshots immediately', () => {
cy.get('#scroll-to-both').scrollTo(25, { duration: 1 }).then(function () {
const { lastLog } = this
expect(lastLog.get('snapshots').length).to.eq(1)
expect(lastLog.get('snapshots')[0]).to.be.an('object')
})
})
it('#consoleProps', () => {
cy.get('#scroll-to-both').scrollTo(25, { duration: 1 }).then(function ($container) {
const console = this.lastLog.invoke('consoleProps')
expect(console.Command).to.eq('scrollTo')
expect(console.X).to.eq(25)
expect(console.Y).to.eq(0)
expect(console.Options).to.eq('{duration: 1}')
expect(console['Scrolled Element']).to.eq($container.get(0))
})
})
})
})
context('#scrollIntoView', () => {
beforeEach(function () {
this.win = cy.state('window')
this.scrollVert = cy.$$('#scroll-into-view-vertical')
this.scrollHoriz = cy.$$('#scroll-into-view-horizontal')
this.scrollBoth = cy.$$('#scroll-into-view-both')
// reset the scrollable containers back
// to furthest left and top
this.win.scrollTo(0, 0)
this.scrollVert.scrollTop(0)
this.scrollVert.scrollLeft(0)
this.scrollHoriz.scrollTop(0)
this.scrollHoriz.scrollLeft(0)
this.scrollBoth.scrollTop(0)
this.scrollBoth.scrollLeft(0)
})
it('does not change the subject', () => {
const div = cy.$$('#scroll-into-view-vertical div')
cy.get('#scroll-into-view-vertical div').scrollIntoView().then(($div) => {
expect($div).to.match(div)
})
})
it('scrolls x axis of window to element', function () {
expect(this.win.scrollY).to.eq(0)
expect(this.win.scrollX).to.eq(0)
cy.get('#scroll-into-view-win-horizontal div').scrollIntoView()
cy.window().then((win) => {
expect(win.scrollY).to.eq(0)
expect(win.scrollX).not.to.eq(0)
})
})
it('scrolls y axis of window to element', function () {
expect(this.win.scrollY).to.eq(0)
expect(this.win.scrollX).to.eq(0)
cy.get('#scroll-into-view-win-vertical div').scrollIntoView()
cy.window().then((win) => {
expect(win.pageYOffset).not.to.eq(0)
expect(Math.floor(win.pageXOffset)).closeTo(200, 2)
})
})
it('scrolls both axes of window to element', function () {
expect(this.win.scrollY).to.eq(0)
expect(this.win.scrollX).to.eq(0)
cy.get('#scroll-into-view-win-both div').scrollIntoView()
cy.window().then((win) => {
expect(win.scrollY).not.to.eq(0)
expect(win.scrollX).not.to.eq(0)
})
})
it('scrolls x axis of container to element', function () {
expect(this.scrollHoriz.get(0).scrollTop).to.eq(0)
expect(this.scrollHoriz.get(0).scrollLeft).to.eq(0)
cy.get('#scroll-into-view-horizontal h5').scrollIntoView().then(function () {
expect(this.scrollHoriz.get(0).scrollTop).to.eq(0)
expect(this.scrollHoriz.get(0).scrollLeft).to.eq(300)
})
})
it('scrolls y axis of container to element', function () {
expect(this.scrollVert.get(0).scrollTop).to.eq(0)
expect(this.scrollVert.get(0).scrollLeft).to.eq(0)
cy.get('#scroll-into-view-vertical h5').scrollIntoView().then(function () {
expect(this.scrollVert.get(0).scrollTop).to.eq(300)
expect(this.scrollVert.get(0).scrollLeft).to.eq(0)
})
})
it('scrolls both axes of container to element', function () {
expect(this.scrollBoth.get(0).scrollTop).to.eq(0)
expect(this.scrollBoth.get(0).scrollLeft).to.eq(0)
cy.get('#scroll-into-view-both h5').scrollIntoView().then(function () {
expect(this.scrollBoth.get(0).scrollTop).to.eq(300)
expect(this.scrollBoth.get(0).scrollLeft).to.eq(300)
})
})
it('calls jQuery scroll to', () => {
const scrollTo = cy.spy($.fn, 'scrollTo')
cy.get('#scroll-into-view-both h5').scrollIntoView().then(() => {
expect(scrollTo).to.be.called
})
})
it('sets duration to 0 by default', () => {
const scrollTo = cy.spy($.fn, 'scrollTo')
cy.get('#scroll-into-view-both h5').scrollIntoView().then(() => {
expect(scrollTo).to.be.calledWithMatch({}, { duration: 0 })
})
})
it('sets axis to correct x or y', () => {
const scrollTo = cy.spy($.fn, 'scrollTo')
cy.get('#scroll-into-view-both h5').scrollIntoView().then(() => {
expect(scrollTo).to.be.calledWithMatch({}, { axis: 'xy' })
})
})
it('scrolling resolves after a set duration', function () {
expect(this.scrollBoth.get(0).scrollTop).to.eq(0)
expect(this.scrollBoth.get(0).scrollLeft).to.eq(0)
const scrollTo = cy.spy($.fn, 'scrollTo')
cy.get('#scroll-into-view-both h5').scrollIntoView({ duration: 500 }).then(function () {
expect(scrollTo).to.be.calledWithMatch({}, { duration: 500 })
expect(this.scrollBoth.get(0).scrollLeft).to.eq(300)
expect(this.scrollBoth.get(0).scrollTop).to.eq(300)
})
})
it('accepts duration string option', () => {
const scrollTo = cy.spy($.fn, 'scrollTo')
cy.get('#scroll-into-view-both h5').scrollIntoView({ duration: '500' }).then(() => {
expect(scrollTo.args[0][1].duration).to.eq('500')
})
})
it('accepts offset string option', () => {
const scrollTo = cy.spy($.fn, 'scrollTo')
cy.get('#scroll-into-view-both h5').scrollIntoView({ offset: 500 }).then(() => {
expect(scrollTo.args[0][1].offset).to.eq(500)
})
})
it('accepts offset object option', () => {
const scrollTo = cy.spy($.fn, 'scrollTo')
cy.get('#scroll-into-view-both h5').scrollIntoView({ offset: { left: 500, top: 200 } }).then(() => {
expect(scrollTo.args[0][1].offset).to.deep.eq({ left: 500, top: 200 })
})
})
it('has easing set to swing by default', () => {
const scrollTo = cy.spy($.fn, 'scrollTo')
cy.get('#scroll-into-view-both h5').scrollIntoView().then(() => {
expect(scrollTo.args[0][1].easing).to.eq('swing')
})
})
it('scrolling resolves after easing', function () {
expect(this.scrollBoth.get(0).scrollTop).to.eq(0)
expect(this.scrollBoth.get(0).scrollLeft).to.eq(0)
const scrollTo = cy.spy($.fn, 'scrollTo')
cy.get('#scroll-into-view-both h5').scrollIntoView({ easing: 'linear' }).then(function () {
expect(scrollTo).to.be.calledWithMatch({}, { easing: 'linear' })
expect(this.scrollBoth.get(0).scrollTop).to.eq(300)
expect(this.scrollBoth.get(0).scrollLeft).to.eq(300)
})
})
describe('shadow dom', () => {
beforeEach(() => {
cy.visit('/fixtures/shadow-dom.html')
})
// https://github.com/cypress-io/cypress/issues/7986
it('does not hang', () => {
cy.get('.shadow-1', { includeShadowDom: true }).scrollIntoView()
})
})
describe('assertion verification', () => {
beforeEach(function () {
cy.on('log:added', (attrs, log) => {
if (log.get('name') === 'assert') {
this.lastLog = log
}
})
return null
})
it('eventually passes the assertion', () => {
cy.on('command:retry', _.after(2, () => {
cy.$$('#scroll-into-view-win-vertical div').addClass('scrolled')
}))
cy
.contains('scroll into view vertical')
.scrollIntoView()
.should('have.class', 'scrolled').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('errors', {
defaultCommandTimeout: 50,
}, () => {
beforeEach(function () {
this.logs = []
cy.on('log:added', (attrs, log) => {
this.lastLog = log
return this.logs.push(log)
})
return null
})
context('subject errors', () => {
it('throws when not passed DOM element as subject', (done) => {
cy.on('fail', (err) => {
expect(err.message).to.include('`cy.scrollIntoView()` failed because it requires a DOM element.')
expect(err.message).to.include('{foo: bar}')
expect(err.message).to.include('> `cy.noop()`')
done()
})
cy.noop({ foo: 'bar' }).scrollIntoView()
})
it('throws when passed window object as subject', (done) => {
cy.on('fail', (err) => {
expect(err.message).to.include('`cy.scrollIntoView()` failed because it requires a DOM element.')
expect(err.message).to.include('<window>')
expect(err.message).to.include('> `cy.window()`')
done()
})
cy.window().scrollIntoView()
})
it('throws when passed document object as subject', (done) => {
cy.on('fail', (err) => {
expect(err.message).to.include('`cy.scrollIntoView()` failed because it requires a DOM element.')
expect(err.message).to.include('<document>')
expect(err.message).to.include('> `cy.document()`')
done()
})
cy.document().scrollIntoView()
})
it('throws if scrollable container is multiple elements', (done) => {
cy.on('fail', (err) => {
expect(err.message).to.include('`cy.scrollIntoView()` can only be used to scroll to 1 element, you tried to scroll to 2 elements.')
expect(err.docsUrl).to.include('https://on.cypress.io/scrollintoview')
done()
})
cy.get('button').scrollIntoView()
})
})
context('argument errors', () => {
it('throws if arg passed as non-object', (done) => {
cy.on('fail', (err) => {
expect(err.message).to.include('`cy.scrollIntoView()` can only be called with an `options` object. Your argument was: `foo`')
expect(err.docsUrl).to.eq('https://on.cypress.io/scrollintoview')
done()
})
cy.get('#scroll-into-view-both h5').scrollIntoView('foo')
})
})
context('option errors', () => {
it('throws if duration is not a number or valid string', (done) => {
cy.on('fail', (err) => {
expect(err.message).to.include('`cy.scrollIntoView()` must be called with a valid `duration`. Duration may be either a number (ms) or a string representing a number (ms). Your duration was: `foo`')
expect(err.docsUrl).to.include('https://on.cypress.io/scrollintoview')
done()
})
cy.get('#scroll-into-view-both h5').scrollIntoView({ duration: 'foo' })
})
it('throws if unrecognized easing', (done) => {
cy.on('fail', (err) => {
expect(err.message).to.include('`cy.scrollIntoView()` must be called with a valid `easing`. Your easing was: `flower`')
expect(err.docsUrl).to.include('https://on.cypress.io/scrollintoview')
done()
})
cy.get('#scroll-into-view-both h5').scrollIntoView({ easing: 'flower' })
})
})
})
describe('.log', () => {
beforeEach(function () {
this.logs = []
cy.on('log:added', (attrs, log) => {
this.lastLog = log
return this.logs.push(log)
})
return null
})
it('logs out scrollIntoView', () => {
cy.get('#scroll-into-view-both h5').scrollIntoView().then(function () {
const { lastLog } = this
expect(lastLog.get('name')).to.eq('scrollIntoView')
})
})
it('passes in $el', () => {
cy.get('#scroll-into-view-both h5').scrollIntoView().then(function ($container) {
const { lastLog } = this
expect(lastLog.get('$el').get(0)).to.eq($container.get(0))
})
})
it('logs duration options', () => {
cy.get('#scroll-into-view-both h5').scrollIntoView({ duration: '1' }).then(function () {
const { lastLog } = this
expect(lastLog.get('message')).to.eq('{duration: 1}')
})
})
it('logs easing options', () => {
cy.get('#scroll-into-view-both h5').scrollIntoView({ easing: 'linear' }).then(function () {
const { lastLog } = this
expect(lastLog.get('message')).to.eq('{easing: linear}')
})
})
it('logs offset options', () => {
cy.get('#scroll-into-view-both h5').scrollIntoView({ offset: { left: 500, top: 200 } }).then(function () {
const { lastLog } = this
expect(lastLog.get('message')).to.eq('{offset: {left: 500, top: 200}}')
})
})
it('snapshots immediately', () => {
cy.get('#scroll-into-view-both h5').scrollIntoView().then(function () {
const { lastLog } = this
expect(lastLog.get('snapshots').length).to.eq(1)
expect(lastLog.get('snapshots')[0]).to.be.an('object')
})
})
it('#consoleProps', () => {
cy.get('#scroll-into-view-both h5').scrollIntoView().then(function ($container) {
const console = this.lastLog.invoke('consoleProps')
expect(console.Command).to.eq('scrollIntoView')
expect(console['Applied To']).to.eq($container.get(0))
expect(console['Scrolled Element']).to.exist
})
})
})
})
})