UNPKG

@revoloo/cypress6

Version:

Cypress.io end to end testing tool

616 lines (503 loc) 17.7 kB
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') }) }) }) })