@revoloo/cypress6
Version:
Cypress.io end to end testing tool
588 lines (445 loc) • 18 kB
JavaScript
const { _, $ } = Cypress
describe('src/cy/commands/actions/select', () => {
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('#select', () => {
it('does not change the subject', () => {
const select = cy.$$('select[name=maps]')
cy.get('select[name=maps]').select('train').then(($select) => {
expect($select).to.match(select)
})
})
it('selects by value', () => {
cy.get('select[name=maps]').select('de_train').then(($select) => {
expect($select).to.have.value('de_train')
})
})
it('selects by text', () => {
cy.get('select[name=maps]').select('train').then(($select) => {
expect($select).to.have.value('de_train')
})
})
it('selects by trimmed text with newlines stripped', () => {
cy.get('select[name=maps]').select('italy').then(($select) => {
expect($select).to.have.value('cs_italy')
})
})
it('prioritizes value over text', () => {
cy.get('select[name=foods]').select('Ramen').then(($select) => {
expect($select).to.have.value('Ramen')
})
})
it('can select an array of values', () => {
cy.get('select[name=movies]').select(['apoc', 'br']).then(($select) => {
expect($select.val()).to.deep.eq(['apoc', 'br'])
})
})
it('can handle options nested in optgroups', () => {
cy.get('select[name=starwars]').select('Jar Jar').then(($select) => {
expect($select).to.have.value('jarjar')
})
})
it('can handle options with same value selected by text', () => {
cy.get('select[name=startrek-same]').select('Uhura').then(($select) => {
expect($select.val()).to.equal('same')
expect($select.find('option:selected')).to.have.text('Uhura')
expect($select[0].selectedIndex).to.equal(2)
expect($select[0].selectedOptions[0]).to.eql($select.find('option:selected')[0])
})
})
it('can handle options with some same values selected by text', () => {
cy.get('select[name=startrek-some-same]').select('Uhura').then(($select) => {
expect($select.val()).to.equal('same')
expect($select.find('option:selected')).to.have.text('Uhura')
expect($select[0].selectedIndex).to.equal(2)
expect($select[0].selectedOptions[0]).to.eql($select.find('option:selected')[0])
})
})
it('can select an array of values', () => {
cy.get('select[name=movies]').select(['apoc', 'br']).then(($select) => {
expect($select.val()).to.deep.eq(['apoc', 'br'])
})
})
it('can select an array of texts', () => {
cy.get('select[name=movies]').select(['The Human Condition', 'There Will Be Blood']).then(($select) => {
expect($select.val()).to.deep.eq(['thc', 'twbb'])
})
})
// readonly should only be limited to inputs, not checkboxes
it('can select a readonly select', () => {
cy.get('select[name=hunter]').select('gon').then(($select) => {
expect($select.val()).to.eq('gon-val')
})
})
it('clears previous values when providing an array', () => {
// make sure we have a previous value
const select = cy.$$('select[name=movies]').val(['2001'])
expect(select.val()).to.deep.eq(['2001'])
cy.get('select[name=movies]').select(['apoc', 'br']).then(($select) => {
expect($select.val()).to.deep.eq(['apoc', 'br'])
})
})
it('lists the select as the focused element', () => {
const select = cy.$$('#select-maps')
cy.get('#select-maps').select('de_train').focused().then(($focused) => {
expect($focused.get(0)).to.eq(select.get(0))
})
})
it('causes previous input to receive blur', (done) => {
cy.$$('input:text:first').blur(() => {
done()
})
cy.get('input:text:first').type('foo')
cy.get('#select-maps').select('de_train')
})
it('can forcibly click even when being covered by another element', (done) => {
const select = $('<select><option>foo</option></select>').attr('id', 'select-covered-in-span').prependTo(cy.$$('body'))
$('<span>span on select</span>').css({ position: 'absolute', left: select.offset().left, top: select.offset().top, padding: 5, display: 'inline-block', backgroundColor: 'yellow' }).prependTo(cy.$$('body'))
select.on('click', () => {
done()
})
cy.get('#select-covered-in-span').select('foo', { force: true })
})
it('passes timeout and interval down to click', (done) => {
const select = $('<select />').attr('id', 'select-covered-in-span').prependTo(cy.$$('body'))
$('<span>span on select</span>').css({ position: 'absolute', left: select.offset().left, top: select.offset().top, padding: 5, display: 'inline-block', backgroundColor: 'yellow' }).prependTo(cy.$$('body'))
cy.on('command:retry', (options) => {
expect(options.timeout).to.eq(1000)
expect(options.interval).to.eq(60)
done()
})
cy.get('#select-covered-in-span').select('foobar', { timeout: 1000, interval: 60 })
})
it('can forcibly click even when element is invisible', (done) => {
const select = cy.$$('#select-maps').hide()
select.click(() => {
done()
})
cy.get('#select-maps').select('de_dust2', { force: true })
})
it('can forcibly click when select is disabled', () => {
cy.get('select[name=disabled]')
// default select value
.invoke('val').should('eq', 'foo')
cy.get('select[name=disabled]')
.select('bar', { force: true })
.invoke('val').should('eq', 'bar')
})
it('retries until <option> can be selected', () => {
const option = cy.$$('<option>foo</option>')
cy.on('command:retry', _.once(() => {
cy.$$('#select-maps').append(option)
}))
cy.get('#select-maps').select('foo')
})
it('retries until <select> is no longer disabled', () => {
const select = cy.$$('select[name=disabled]')
cy.on('command:retry', _.once(() => {
select.prop('disabled', false)
}))
cy.get('select[name=disabled]').select('foo')
.invoke('val').should('eq', 'foo')
})
it('retries until <optgroup> is no longer disabled', () => {
const select = cy.$$('select[name=optgroup-disabled]')
cy.on('command:retry', _.once(() => {
select.find('optgroup').prop('disabled', false)
}))
cy.get('select[name=optgroup-disabled]').select('bar')
.invoke('val').should('eq', 'bar')
})
it('retries until <options> are no longer disabled', () => {
const select = cy.$$('select[name=opt-disabled]')
cy.on('command:retry', _.once(() => {
select.find('option').prop('disabled', false)
}))
cy.get('select[name=opt-disabled]').select('bar')
.invoke('val').should('eq', 'bar')
})
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.$$('#select-maps').change(function () {
_.delay(() => {
$(this).addClass('selected')
}, 100)
})
cy.get('#select-maps').select('de_nuke').should('have.class', 'selected').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('events', () => {
it('emits click event', (done) => {
cy.$$('select[name=maps]').click(() => {
done()
})
cy.get('select[name=maps]').select('train')
})
it('emits change event', (done) => {
cy.$$('select[name=maps]').change(() => {
done()
})
cy.get('select[name=maps]').select('train')
})
it('emits focus event', (done) => {
cy.$$('select[name=maps]').one('focus', () => {
done()
})
cy.get('select[name=maps]').select('train')
})
it('emits input event', (done) => {
cy.$$('select[name=maps]').one('input', () => {
done()
})
cy.get('select[name=maps]').select('train')
})
it('emits all events in the correct order', () => {
const fired = []
const events = ['mousedown', 'focus', 'mouseup', 'click', 'input', 'change']
_.each(events, (event) => {
cy.$$('select[name=maps]').one(event, () => {
fired.push(event)
})
})
cy.get('select[name=maps]').select('train').then(() => {
expect(fired).to.deep.eq(events)
})
})
})
describe('errors', {
defaultCommandTimeout: 100,
}, () => {
beforeEach(function () {
this.logs = []
cy.on('log:added', (attrs, log) => {
this.lastLog = log
return this.logs.push(log)
})
return null
})
it('throws when not a dom subject', (done) => {
cy.on('fail', () => {
done()
})
cy.noop({}).select('foo')
})
it('throws when subject is not in the document', (done) => {
let selected = 0
const $select = cy.$$('#select-maps').change((e) => {
selected += 1
$select.remove()
})
cy.on('fail', (err) => {
expect(selected).to.eq(1)
expect(err.message).to.include('`cy.select()` failed because this element')
done()
})
cy.get('#select-maps').select('de_dust2').select('de_aztec')
})
it('throws when more than 1 element in the collection', (done) => {
const num = cy.$$('select').length
cy.on('fail', (err) => {
expect(err.message).to.include(`\`cy.select()\` can only be called on a single \`<select>\`. Your subject contained ${num} elements.`)
expect(err.docsUrl).to.eq('https://on.cypress.io/select')
done()
})
cy.get('select').select('foo')
})
it('throws on anything other than a select', (done) => {
cy.on('fail', (err) => {
expect(err.message).to.include('`cy.select()` can only be called on a `<select>`. Your subject is a: `<input id="input">`')
expect(err.docsUrl).to.eq('https://on.cypress.io/select')
done()
})
cy.get('input:first').select('foo')
})
it('throws when finding duplicate values', (done) => {
cy.on('fail', (err) => {
expect(err.message).to.include('`cy.select()` matched more than one `option` by value or text: `bm`')
expect(err.docsUrl).to.eq('https://on.cypress.io/select')
done()
})
cy.get('select[name=names]').select('bm')
})
it('throws when passing an array to a non multiple select', (done) => {
cy.on('fail', (err) => {
expect(err.message).to.include('`cy.select()` was called with an array of arguments but does not have a `multiple` attribute set.')
expect(err.docsUrl).to.eq('https://on.cypress.io/select')
done()
})
cy.get('select[name=names]').select(['bm', 'ss'])
})
it('throws when the subject isnt visible', (done) => {
cy.$$('#select-maps').show().hide()
cy.on('fail', (err) => {
expect(err.message).to.include('`cy.select()` failed because this element is not visible')
done()
})
cy.get('#select-maps').select('de_dust2')
})
it('throws when value or text does not exist', (done) => {
cy.on('fail', (err) => {
expect(err.message).to.include('`cy.select()` failed because it could not find a single `<option>` with value or text matching: `foo`')
expect(err.docsUrl).to.eq('https://on.cypress.io/select')
done()
})
cy.get('select[name=foods]').select('foo')
})
it('throws when the <select> itself is disabled', (done) => {
cy.on('fail', (err) => {
expect(err.message).to.include('`cy.select()` failed because this element is currently disabled:')
expect(err.docsUrl).to.eq('https://on.cypress.io/select')
done()
})
cy.get('select[name=disabled]').select('foo')
})
it('throws when optgroup is disabled', (done) => {
cy.on('fail', (err) => {
expect(err.message).to.include('`cy.select()` failed because this `<option>` you are trying to select is within an `<optgroup>` that is currently disabled:')
expect(err.docsUrl).to.eq('https://on.cypress.io/select')
done()
})
cy.get('select[name=optgroup-disabled]').select('bar')
})
it('throws when options are disabled', (done) => {
cy.on('fail', (err) => {
expect(err.message).to.include('`cy.select()` failed because this `<option>` you are trying to select is currently disabled:')
expect(err.docsUrl).to.eq('https://on.cypress.io/select')
done()
})
cy.get('select[name=opt-disabled]').select('bar')
})
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('#select-maps').select('de_nuke').should('have.class', 'selected')
})
it('does not log an additional log on failure', function (done) {
cy.on('fail', () => {
expect(this.logs.length).to.eq(3)
done()
})
cy.get('#select-maps').select('de_nuke').should('have.class', 'selected')
})
it('only logs once on failure', function (done) {
cy.on('fail', (err) => {
// 2 logs, 1 for cy.get, 1 for cy.select
expect(this.logs.length).to.eq(2)
done()
})
cy.get('#select-maps').select('does_not_exist')
})
})
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 select', () => {
cy.get('#select-maps').select('de_dust2').then(function () {
const { lastLog } = this
expect(lastLog.get('name')).to.eq('select')
})
})
it('passes in $el', () => {
cy.get('#select-maps').select('de_dust2').then(function ($select) {
const { lastLog } = this
expect(lastLog.get('$el')).to.eq($select)
})
})
it('snapshots before clicking', function (done) {
cy.$$('#select-maps').change(() => {
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('#select-maps').select('de_dust2').then(($select) => {})
})
it('snapshots after clicking', () => {
cy.get('#select-maps').select('de_dust2').then(function ($select) {
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('is not immediately ended', function (done) {
cy.$$('#select-maps').click(() => {
const { lastLog } = this
expect(lastLog.get('state')).to.eq('pending')
done()
})
cy.get('#select-maps').select('de_dust2')
})
it('ends', () => {
cy.get('#select-maps').select('de_dust2').then(function () {
const { lastLog } = this
expect(lastLog.get('state')).to.eq('passed')
})
})
it('#consoleProps', () => {
cy.get('#select-maps').select('de_dust2').then(function ($select) {
const { fromElWindow } = Cypress.dom.getElementCoordinatesByPosition($select)
const console = this.lastLog.invoke('consoleProps')
expect(console.Command).to.eq('select')
expect(console.Selected).to.deep.eq(['de_dust2'])
expect(console['Applied To']).to.eq($select.get(0))
expect(console.Coords.x).to.be.closeTo(fromElWindow.x, 10)
expect(console.Coords.y).to.be.closeTo(fromElWindow.y, 10)
})
})
it('logs only one select event', () => {
const types = []
cy.on('log:added', (attrs, log) => {
if (log.get('name') === 'select') {
return types.push(log)
}
})
cy.get('#select-maps').select('de_dust2').then(function () {
expect(this.logs.length).to.eq(2)
expect(types.length).to.eq(1)
})
})
it('logs deltaOptions', () => {
cy.get('#select-maps').select('de_dust2', { force: true, timeout: 1000 }).then(function () {
const { lastLog } = this
expect(lastLog.get('message')).to.eq('{force: true, timeout: 1000}')
expect(lastLog.invoke('consoleProps').Options).to.deep.eq({ force: true, timeout: 1000 })
})
})
})
})
})