@revoloo/cypress6
Version:
Cypress.io end to end testing tool
616 lines (503 loc) • 17.7 kB
JavaScript
const { _, $ } = Cypress
describe('src/cy/commands/aliasing', () => {
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('#as', () => {
it('is special utility command', () => {
cy.wrap('foo').as('f').then(() => {
const cmd = cy.queue.find({ name: 'as' })
expect(cmd.get('type')).to.eq('utility')
})
})
it('does not change the subject', () => {
const body = cy.$$('body')
cy.get('body').as('b').then(($body) => {
expect($body.get(0)).to.eq(body.get(0))
})
})
it('stores the lookup as an alias', () => {
cy.get('body').as('b').then(() => {
expect(cy.state('aliases').b).to.exist
})
})
it('stores the resulting subject as the alias', () => {
const $body = cy.$$('body')
cy.get('body').as('b').then(() => {
expect(cy.state('aliases').b.subject.get(0)).to.eq($body.get(0))
})
})
it('stores subject of chained aliases', () => {
const li = cy.$$('#list li').eq(0)
cy.get('#list li').eq(0).as('firstLi').then(($li) => {
expect($li).to.match(li)
})
})
it('retries primitives and assertions', () => {
const obj = {}
cy.on('command:retry', _.after(2, () => {
obj.foo = 'bar'
}))
cy.wrap(obj).as('obj')
cy.get('@obj').should('deep.eq', { foo: 'bar' })
})
it('allows dot in alias names', () => {
cy.get('body').as('body.foo').then(() => {
expect(cy.state('aliases')['body.foo']).to.exist
cy.get('@body.foo').should('exist')
})
})
it('recognizes dot and non dot with same alias names', () => {
cy.get('body').as('body').then(() => {
expect(cy.state('aliases')['body']).to.exist
cy.get('@body').should('exist')
})
cy.contains('foo').as('body.foo').then(() => {
expect(cy.state('aliases')['body.foo']).to.exist
cy.get('@body.foo').should('exist')
cy.get('@body.foo').then((bodyFoo) => {
cy.get('@body').should('not.equal', bodyFoo)
})
})
})
context('DOM subjects', () => {
it('assigns the remote jquery instance', () => {
const obj = {}
const jquery = () => {
return obj
}
cy.state('jQuery', jquery)
cy.get('input:first').as('input').then(function () {
expect(this.input).to.eq(obj)
})
})
})
context('#assign', () => {
beforeEach(() => {
return cy.noop('foo').as('foo')
})
afterEach(function () {
if (!this.foo) {
this.test.error(new Error('this.foo not defined'))
}
})
it('assigns subject to runnable ctx', () => {
cy
.noop({}).as('baz').then(function (obj) {
expect(this.baz).to.eq(obj)
})
})
it('assigns subject with dot to runnable ctx', () => {
cy.noop({}).as('bar.baz').then(function (obj) {
expect(this['bar.baz']).to.eq(obj)
})
})
describe('nested hooks', () => {
afterEach(function () {
if (!this.bar) {
this.test.error(new Error('this.bar not defined'))
}
if (!this.foo) {
this.test.error(new Error('this.foo not defined'))
}
})
it('assigns bar', () => {
cy.noop('bar').as('bar')
})
})
describe('nested functions', () => {
beforeEach(function () {
this.assign = () => {
return cy.noop('quux').as('quux')
}
})
afterEach(function () {
if (!this.quux) {
this.test.error(new Error('this.quux not defined'))
}
})
it('shares this ctx with hooks', function () {
this.assign().then(function () {
expect(this.quux).to.eq('quux')
})
})
})
})
describe('errors', () => {
it('throws as a parent command', (done) => {
cy.on('fail', (err) => {
expect(err.message).to.include('before running a parent command')
expect(err.message).to.include('`cy.as("foo")`')
done()
})
cy.as('foo')
})
_.each([null, undefined, {}, [], 123], (value) => {
it(`throws if when passed: ${value}`, (done) => {
cy.on('fail', (err) => {
expect(err.message).to.eq('`cy.as()` can only accept a string.')
expect(err.docsUrl).to.eq('https://on.cypress.io/as')
done()
})
cy.get('div:first').as(value)
})
})
it('throws on blank string', (done) => {
cy.on('fail', (err) => {
expect(err.message).to.eq('`cy.as()` cannot be passed an empty string.')
expect(err.docsUrl).to.eq('https://on.cypress.io/as')
done()
})
cy.get('div:first').as('')
})
it('throws on alias starting with @ char', (done) => {
cy.on('fail', (err) => {
expect(err.message).to.eq('`@myAlias` cannot be named starting with the `@` symbol. Try renaming the alias to `myAlias`, or something else that does not start with the `@` symbol.')
expect(err.docsUrl).to.eq('https://on.cypress.io/as')
done()
})
cy.get('div:first').as('@myAlias')
})
it('throws on alias starting with @ char and dots', (done) => {
cy.on('fail', (err) => {
expect(err.message).to.eq('`@my.alias` cannot be named starting with the `@` symbol. Try renaming the alias to `my.alias`, or something else that does not start with the `@` symbol.')
expect(err.docsUrl).to.eq('https://on.cypress.io/as')
done()
})
cy.get('div:first').as('@my.alias')
})
it('does not throw on alias with @ char in non-starting position', () => {
cy.get('div:first').as('my@Alias')
cy.get('@my@Alias')
})
_.each(['test', 'runnable', 'timeout', 'slow', 'skip', 'inspect'], (reserved) => {
it(`throws on a reserved word: ${reserved}`, (done) => {
cy.on('fail', (err) => {
expect(err.message).to.eq(`\`cy.as()\` cannot be aliased as: \`${reserved}\`. This word is reserved.`)
expect(err.docsUrl).to.eq('https://on.cypress.io/as')
done()
})
cy.get('div:first').as(reserved)
})
})
})
describe('log', () => {
beforeEach(function () {
this.logs = []
cy.on('log:added', (attrs, log) => {
this.lastLog = log
return this.logs.push(log)
})
return null
})
it('sets aliasType to \'primitive\'', () => {
cy.wrap({}).as('obj').then(function () {
const { lastLog } = this
expect(lastLog.get('aliasType')).to.eq('primitive')
})
})
it('sets aliasType to \'dom\'', () => {
cy.get('body').find('button:first').click().as('button').then(function () {
const { lastLog } = this
expect(lastLog.get('aliasType')).to.eq('dom')
})
})
it('aliases previous command / non event / matching chainerId', () => {
Cypress.Commands.addAll({
foo () {
const cmd = Cypress.log({})
cy.get('ul:first li', { log: false }).first({ log: false }).then(($li) => {
cmd.snapshot().end()
return undefined
})
},
})
cy.foo().as('foo').then(function () {
const { lastLog } = this
expect(this.logs.length).to.eq(1)
expect(lastLog.get('alias')).to.eq('foo')
expect(lastLog.get('aliasType')).to.eq('dom')
})
})
it('does not match alias when the alias has already been applied', () => {
cy
.visit('/fixtures/commands.html')
.server()
.route(/foo/, {}).as('getFoo')
.then(function () {
// 1 log from visit
// 1 log from route
expect(this.logs.length).to.eq(2)
expect(this.logs[0].get('name')).to.eq('visit')
expect(this.logs[0].get('alias')).not.to.be.ok
expect(this.logs[0].get('aliasType')).not.to.be.ok
expect(this.logs[1].get('name')).to.eq('route')
expect(this.logs[1].get('alias')).to.eq('getFoo')
})
})
})
// @see https://github.com/cypress-io/cypress/issues/5101
context('works with command overwrite', () => {
it('works without overwrite', () => {
// sanity check without command overwrite
cy.wrap('alias value').as('myAlias')
.then(() => {
expect(cy.getAlias('@myAlias'), 'alias exists').to.exist
expect(cy.getAlias('@myAlias'), 'alias value')
.to.have.property('subject', 'alias value')
})
.then(() => {
// cy.get returns the alias
cy.get('@myAlias').should('be.equal', 'alias value')
})
})
it('works with wrap', () => {
let wrapCalled
Cypress.Commands.overwrite('wrap', (wrapFn, value) => {
wrapCalled = true
return wrapFn(value)
})
cy.wrap('alias value').as('myAlias')
.then(() => {
expect(wrapCalled, 'overwrite was called').to.be.true
expect(cy.getAlias('@myAlias'), 'alias exists').to.exist
expect(cy.getAlias('@myAlias'), 'alias value')
.to.have.property('subject', 'alias value')
})
.then(() => {
// verify cy.get works in arrow function
cy.get('@myAlias').should('be.equal', 'alias value')
})
.then(function () {
// verify cy.get works in regular function
cy.get('@myAlias').should('be.equal', 'alias value')
})
})
it('works with wrap.then', () => {
let wrapCalled
let thenCalled
Cypress.Commands.overwrite('wrap', (wrapFn, value) => {
wrapCalled = true
return cy.log(`wrapped "${value}"`).then(() => {
thenCalled = true
return wrapFn(value)
})
})
cy.wrap('alias value').as('myAlias')
.then(() => {
expect(wrapCalled, 'overwrite was called').to.be.true
expect(thenCalled, 'then was called').to.be.true
expect(cy.getAlias('@myAlias'), 'alias exists').to.exist
expect(cy.getAlias('@myAlias'), 'alias value')
.to.have.property('subject', 'alias value')
})
.then(() => {
// verify cy.get works in arrow function
cy.get('@myAlias').should('be.equal', 'alias value')
})
.then(function () {
// verify cy.get works in regular function
cy.get('@myAlias').should('be.equal', 'alias value')
})
})
it('passes this to then callback function', () => {
// sanity test before the next one
cy.wrap(1).as('myAlias')
cy.wrap(2).then(function (subj) {
expect(subj, 'subject').to.equal(2)
expect(this, 'this is defined').to.not.be.undefined
expect(this.myAlias, 'this has the alias as a property').to.eq(1)
})
})
it('works with .then function overwrite', () => {
// use explicit arguments and Function.prototype.call
Cypress.Commands.overwrite('then', function (originalCommand, subject, fn, options = {}) {
return originalCommand.call(null, subject, options, fn)
})
cy.wrap(1).as('myAlias')
cy.wrap(2).then(function (subj) {
expect(subj, 'subject').to.equal(2)
expect(this, 'this is defined').to.not.be.undefined
expect(this.myAlias).to.eq(1)
})
})
it('works with .then arrow overwrite', () => {
// make sure we can pass arrow functions
Cypress.Commands.overwrite('then', (originalCommand, ...args) => {
return originalCommand(...args)
})
cy.wrap(1).as('myAlias')
cy.wrap(2).then(function (subj) {
expect(subj, 'subject').to.equal(2)
expect(this, 'this is defined').to.not.be.undefined
expect(this.myAlias).to.eq(1)
})
})
})
})
context('#replayCommandsFrom', () => {
describe('subject in document', () => {
it('returns if subject is still in the document', () => {
cy.get('#list').as('list').then(() => {
const currentLength = cy.queue.length
cy.get('@list').then(() => {
// should only add the .get() and the .then()
expect(cy.queue.length).to.eq(currentLength + 2)
})
})
})
})
describe('subject not in document', () => {
it('inserts into the queue', () => {
const existingNames = cy.queue.names()
cy
.get('#list li').eq(0).as('firstLi').then(($li) => {
return $li.remove()
})
.get('@firstLi').then(() => {
expect(cy.queue.names()).to.deep.eq(
existingNames.concat(
['get', 'eq', 'as', 'then', 'get', 'get', 'eq', 'then'],
),
)
})
})
it('replays from last root to current', () => {
const first = cy.$$('#list li').eq(0)
const second = cy.$$('#list li').eq(1)
cy
.get('#list li').eq(0).as('firstLi').then(($li) => {
expect($li.get(0)).to.eq(first.get(0))
return $li.remove()
})
.get('@firstLi').then(($li) => {
expect($li.get(0)).to.eq(second.get(0))
})
})
it('replays up until first root command', () => {
const existingNames = cy.queue.names()
cy
.get('body').noop({})
.get('#list li').eq(0).as('firstLi').then(($li) => {
return $li.remove()
})
.get('@firstLi').then(() => {
expect(cy.queue.names()).to.deep.eq(
existingNames.concat(
['get', 'noop', 'get', 'eq', 'as', 'then', 'get', 'get', 'eq', 'then'],
),
)
})
})
it('resets the chainerId allow subjects to be carried on', () => {
cy.get('#dom').find('#button').as('button').then(($button) => {
$button.remove()
cy.$$('#dom').append($('<button />', { id: 'button' }))
return null
})
// when cy is a separate chainer there *was* a bug
// that cause the subject to null because of different
// chainer id's
cy.get('@button').then(($button) => {
expect($button).to.have.id('button')
})
})
it('skips commands which did not change, and starts at the first valid subject or parent command', () => {
const existingNames = cy.queue.names()
cy.$$('#list li').click(function () {
const ul = $(this).parent()
const lis = ul.children().clone()
// this simulates a re-render
ul.children().remove()
ul.append(lis)
return lis.first().remove()
})
cy
.get('#list li')
.then(($lis) => {
return $lis
})
.as('items')
.first()
.click()
.as('firstItem')
.then(() => {
expect(cy.queue.names()).to.deep.eq(
existingNames.concat(
['get', 'then', 'as', 'first', 'click', 'as', 'then', 'get', 'should', 'then', 'get', 'should', 'then'],
),
)
})
.get('@items')
.should('have.length', 2)
.then(() => {
expect(cy.queue.names()).to.deep.eq(
existingNames.concat(
['get', 'then', 'as', 'first', 'click', 'as', 'then', 'get', 'get', 'should', 'then', 'get', 'should', 'then'],
),
)
})
.get('@firstItem')
.should('contain', 'li 1')
.then(() => {
expect(cy.queue.names()).to.deep.eq(
existingNames.concat(
['get', 'then', 'as', 'first', 'click', 'as', 'then', 'get', 'get', 'should', 'then', 'get', 'get', 'first', 'should', 'then'],
),
)
})
})
it('inserts assertions', (done) => {
const existingNames = cy.queue.names()
cy
.get('#checkboxes input')
.eq(0)
.should('be.checked', 'cockatoo')
.as('firstItem')
.then(($input) => {
return $input.remove()
})
.get('@firstItem')
.then(() => {
expect(cy.queue.names()).to.deep.eq(
existingNames.concat(
['get', 'eq', 'should', 'as', 'then', 'get', 'get', 'eq', 'should', 'then'],
),
)
done()
})
})
})
})
context('#getAlias', () => {
it('retrieves aliases', () => {
cy
.get('body').as('b')
.get('input:first').as('firstInput')
.get('div:last').as('lastDiv')
.then(() => {
expect(cy.getAlias('@firstInput')).to.exist
})
})
describe('errors', () => {
it('throws when an alias cannot be found', (done) => {
cy.on('fail', (err) => {
expect(err.message).to.include('`cy.get()` could not find a registered alias for: `@lastDiv`.\nAvailable aliases are: `b, firstInput`.')
done()
})
cy
.get('body').as('b')
.get('input:first').as('firstInput')
.get('@lastDiv')
})
})
})
})