UNPKG

@revoloo/cypress6

Version:

Cypress.io end to end testing tool

1,788 lines (1,384 loc) 66.5 kB
const { _, $ } = Cypress const { Promise } = Cypress describe('src/cy/commands/querying', () => { beforeEach(() => { cy.visit('/fixtures/dom.html') }) context('#focused', () => { it('returns the activeElement', () => { const $button = cy.$$('#button') $button.get(0).focus() expect(cy.state('document').activeElement).to.eq($button.get(0)) cy.focused().then(($focused) => { expect($focused.get(0)).to.eq($button.get(0)) }) }) it('returns null if no activeElement', () => { const $button = cy.$$('#button') $button.get(0).focus() $button.get(0).blur() cy.focused().should('not.exist').then(($focused) => { expect($focused).to.be.null }) }) 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.$$(':text:first').addClass('focused').focus() })) cy.focused().should('have.class', 'focused').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 }) }) // https://github.com/cypress-io/cypress/issues/409 it('retries on an elements value', () => { const $input = cy.$$('input:first') cy.on('command:retry', _.after(2, () => { $input.val('1234') $input.get(0).focus() })) cy.focused().should('have.value', '1234').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('.log', () => { beforeEach(function () { cy.$$('input:first').get(0).focus() cy.on('log:added', (attrs, log) => { if (log.get('name') === 'focused') { this.lastLog = log } }) return null }) it('is a parent command', () => { cy.get('body').focused().then(function () { const { lastLog } = this expect(lastLog.get('type')).to.eq('parent') }) }) it('ends immediately', () => { cy.focused().then(function () { const { lastLog } = this expect(lastLog.get('ended')).to.be.true expect(lastLog.get('state')).to.eq('passed') }) }) it('snapshots immediately', () => { cy.focused().then(function () { const { lastLog } = this expect(lastLog.get('snapshots').length).to.eq(1) expect(lastLog.get('snapshots')[0]).to.be.an('object') }) }) it('passes in $el', () => { cy.get('input:first').focused().then(function ($input) { const { lastLog } = this expect(lastLog.get('$el')).to.eq($input) }) }) it('#consoleProps', () => { cy.get('input:first').focused().then(function ($input) { expect(this.lastLog.invoke('consoleProps')).to.deep.eq({ Command: 'focused', Yielded: $input.get(0), Elements: 1, }) }) }) it('#consoleProps with null element', () => { const button = cy.$$('#button') button.get(0).focus() button.get(0).blur() cy.focused().should('not.exist').then(function () { expect(this.lastLog.invoke('consoleProps')).to.deep.eq({ Command: 'focused', Yielded: '--nothing--', Elements: 0, }) }) }) }) describe('errors', { defaultCommandTimeout: 100, }, () => { beforeEach(function () { this.logs = [] cy.on('log:added', (attrs, log) => { this.lastLog = log this.logs.push(log) }) return null }) it('fails waiting for the element to exist', (done) => { const button = cy.$$('#button') button.get(0).focus() button.get(0).blur() cy.on('fail', (err) => { expect(err.message).to.include('Expected to find element: `focused`, but never found it.') done() }) cy.focused() }) it('fails waiting for the focused element not to exist', (done) => { cy.$$('input:first').focus() cy.on('fail', (err) => { expect(err.message).to.include('Expected <input#input> not to exist in the DOM, but it was continuously found.') done() }) cy.focused().should('not.exist') }) it('eventually fails the assertion', function (done) { cy.$$('input:first').focus() 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.focused().should('have.class', 'focused') }) it('does not log an additional log on failure', function (done) { cy.on('fail', () => { expect(this.logs.length).to.eq(2) done() }) cy.focused().should('have.class', 'focused') }) }) }) context('#within', () => { it('invokes callback function with runnable.ctx', function () { const ctx = this cy.get('div:first').within(function () { expect(ctx === this).to.be.true }) }) it('scopes additional GET finders to the subject', () => { const input = cy.$$('#by-name input:first') cy.get('#by-name').within(() => { cy.get('input:first').then(($input) => { expect($input.get(0)).to.eq(input.get(0)) }) }) }) it('scopes additional CONTAINS finders to the subject', () => { const span = cy.$$('#nested-div span:contains(foo)') cy.contains('foo').then(($span) => { expect($span.get(0)).not.to.eq(span.get(0)) }) cy.get('#nested-div').within(() => { cy.contains('foo').then(($span) => { expect($span.get(0)).to.eq(span.get(0)) }) }) }) it('does not change the subject', () => { const form = cy.$$('#by-name') cy.get('#by-name').within(() => {}).then(($form) => { expect($form.get(0)).to.eq(form.get(0)) }) }) it('can call child commands after within on the same subject', () => { const input = cy.$$('#by-name input:first') cy.get('#by-name').within(() => {}).find('input:first').then(($input) => { expect($input.get(0)).to.eq(input.get(0)) }) }) it('supports nested withins', () => { const span = cy.$$('#button-text button span') cy.get('#button-text').within(() => { cy.get('button').within(() => { cy.get('span').then(($span) => { expect($span.get(0)).to.eq(span.get(0)) }) }) }) }) it('supports complicated nested withins', () => { const span1 = cy.$$('#button-text a span') const span2 = cy.$$('#button-text button span') cy.get('#button-text').within(() => { cy.get('a').within(() => { cy.get('span').then(($span) => { expect($span.get(0)).to.eq(span1.get(0)) }) }) cy.get('button').within(() => { cy.get('span').then(($span) => { expect($span.get(0)).to.eq(span2.get(0)) }) }) }) }) it('clears withinSubject after within is over', () => { const input = cy.$$('input:first') const span = cy.$$('#button-text button span') cy.get('#button-text').within(() => { cy.get('button').within(() => { cy.get('span').then(($span) => { expect($span.get(0)).to.eq(span.get(0)) }) }) }) cy.get('input:first').then(($input) => { expect($input.get(0)).to.eq(input.get(0)) }) }) it('removes command:start listeners after within is over', () => { cy.get('#button-text').within(() => { cy.get('button').within(() => { cy.get('span') }) }) cy.then(() => { expect(cy._events).not.to.have.property('command:start') }) }) it('clears withinSubject even if next is null', (done) => { const span = cy.$$('#button-text button span') // should be defined here because next would have been // null and withinSubject would not have been cleared cy.once('command:queue:before:end', () => { expect(cy.state('withinSubject')).not.to.be.undefined }) cy.once('command:queue:end', () => { expect(cy.state('withinSubject')).to.be.null done() }) cy.get('#button-text').within(() => { cy.get('button span').then(($span) => { expect($span.get(0)).to.eq(span.get(0)) }) }) }) // https://github.com/cypress-io/cypress/issues/4757 it('subject is restored after within() call', () => { cy.get('#wrapper').within(() => { cy.get('#upper').should('contain.text', 'New York') }) .should('have.id', 'wrapper') }) // https://github.com/cypress-io/cypress/issues/5183 it('contains() works after within() call', () => { cy.get(`#wrapper`).within(() => cy.get(`#upper`)).should(`contain.text`, `New York`) cy.contains(`button`, `button`).should(`exist`) }) describe('.log', () => { beforeEach(function () { this.logs = [] cy.on('log:added', (attrs, log) => { if (attrs.name === 'within') { this.lastLog = log this.logs.push(log) } }) return null }) it('can silence logging', () => { cy.get('div:first').within({ log: false }, () => {}).then(function () { expect(this.logs.length).to.eq(0) }) }) it('logs immediately before resolving', (done) => { const div = cy.$$('div:first') cy.on('log:added', (attrs, log) => { if (log.get('name') === 'within') { expect(log.get('state')).to.eq('pending') expect(log.get('message')).to.eq('') expect(log.get('$el').get(0)).to.eq(div.get(0)) done() } }) cy.get('div:first').within(() => {}) }) it('snapshots after clicking', () => { cy.get('div:first').within(() => {}) .then(function () { const { lastLog } = this expect(lastLog.get('snapshots').length).to.eq(1) expect(lastLog.get('snapshots')[0]).to.be.an('object') }) }) }) describe('errors', { defaultCommandTimeout: 100, }, () => { beforeEach(function () { this.logs = [] cy.on('log:added', (attrs, log) => { this.lastLog = log this.logs.push(log) }) return null }) it('logs once when not dom subject', function (done) { cy.on('fail', (err) => { const { lastLog } = this expect(this.logs.length).to.eq(1) expect(lastLog.get('error')).to.eq(err) done() }) cy.noop().within(() => {}) }) it('throws when not a DOM subject', (done) => { cy.on('fail', (err) => { done() }) cy.noop().within(() => {}) }) _.each(['', [], {}, 1, null, undefined], (value) => { it(`throws if passed anything other than a function, such as: ${value}`, (done) => { cy.on('fail', (err) => { expect(err.message).to.include('`cy.within()` must be called with a function.') expect(err.docsUrl).to.eq('https://on.cypress.io/within') done() }) cy.get('body').within(value) }) }) it('throws when subject is not in the document', (done) => { cy.on('command:end', () => { cy.$$('#list').remove() }) cy.on('fail', (err) => { expect(err.message).to.include('`cy.within()` failed because this element') done() }) cy.get('#list').within(() => {}) }) }) }) context('#root', () => { it('returns html', () => { const html = cy.$$('html') cy.root().then(($html) => { expect($html.get(0)).to.eq(html.get(0)) }) }) it('returns withinSubject if exists', () => { const form = cy.$$('form') cy.get('form').within(() => { cy .get('input') .root().then(($root) => { expect($root.get(0)).to.eq(form.get(0)) }) }) }) it('eventually resolves', () => { _.delay(() => { cy.$$('html').addClass('foo').addClass('bar') } , 100) cy.root().should('have.class', 'foo').and('have.class', 'bar') }) describe('.log', () => { beforeEach(function () { this.logs = [] cy.on('log:added', (attrs, log) => { if (attrs.name === 'root') { this.lastLog = log this.logs.push(log) } }) return null }) it('can turn off logging', () => { cy.root({ log: false }).then(function () { expect(this.log).to.be.undefined }) }) it('logs immediately before resolving', (done) => { cy.on('log:added', (attrs, log) => { if (log.get('name') === 'root') { expect(log.get('state')).to.eq('pending') expect(log.get('message')).to.eq('') done() } }) cy.root() }) it('snapshots after clicking', () => { cy.root().then(function () { const { lastLog } = this expect(lastLog.get('snapshots').length).to.eq(1) expect(lastLog.get('snapshots')[0]).to.be.an('object') }) }) it('sets $el to document', () => { const html = cy.$$('html') cy.root().then(function () { expect(this.lastLog.get('$el').get(0)).to.eq(html.get(0)) }) }) it('sets $el to withinSubject', () => { const form = cy.$$('form') cy.get('form').within(() => { cy .get('input') .root().then(function ($root) { expect(this.lastLog.get('$el').get(0)).to.eq(form.get(0)) }) }) }) it('consoleProps', () => { cy.root().then(function ($root) { const consoleProps = this.lastLog.invoke('consoleProps') expect(consoleProps).to.deep.eq({ Command: 'root', Yielded: $root.get(0), }) }) }) }) }) context('#get', { defaultCommandTimeout: 200, }, () => { it('finds by selector', () => { const list = cy.$$('#list') cy.get('#list').then(($list) => { expect($list.get(0)).to.eq(list.get(0)) }) }) // NOTE: FLAKY in CI, need to investigate further it.skip('retries finding elements until something is found', () => { const missingEl = $('<div />', { id: 'missing-el' }) // wait until we're ALMOST about to time out before // appending the missingEl cy.on('command:retry', (options) => { if ((options.total + (options._interval * 4)) > options._runnableTimeout) { cy.$$('body').append(missingEl) } }) cy.get('#missing-el').then(($div) => { expect($div).to.match(missingEl) }) }) it('can increase the timeout', () => { const missingEl = $('<div />', { id: 'missing-el' }) cy.on('command:retry', _.after(2, (options) => { // make sure runnableTimeout is 10secs expect(options._runnableTimeout).to.eq(10000) // we shouldnt have a timer either cy.$$('body').append(missingEl) })) cy.get('#missing-el', { timeout: 10000 }) }) it('does not factor in the total time the test has been running', () => { const missingEl = $('<div />', { id: 'missing-el' }) cy.on('command:retry', _.after(2, () => { cy.$$('body').append(missingEl) })) const defaultCommandTimeout = Cypress.config('defaultCommandTimeout') // in this example our test has been running 300ms // but the default command timeout is below this amount, // and the test still passes because the timeout is only // into each command and not the total overall running time cy .wait(defaultCommandTimeout + 100) .get('#missing-el', { timeout: defaultCommandTimeout + 50 }) // it should reset the timeout back // to 200 after successfully finishing 'get' method .then(() => { expect(cy.timeout()).to.eq(defaultCommandTimeout) }) }) it('cancels existing promises', (done) => { cy.stub(Cypress.runner, 'stop') let retrys = 0 const stop = _.after(2, () => { Cypress.stop() }) cy.on('stop', () => { _.delay(() => { expect(retrys).to.eq(2) done() } , 100) }) cy.on('command:retry', () => { retrys += 1 stop() }) cy.get('doesNotExist') }) describe('custom elements', () => { // <foobarbazquux>custom element</foobarbazquux> it('can get a custom element', () => { cy.get('foobarbazquux').should('contain', 'custom element') }) it('can alias a custom element', () => { cy .get('foobarbazquux:last').as('foo') .get('div:first') .get('@foo').should('contain', 'custom element') }) it('can find a custom alias again when detached from DOM', () => { cy .get('foobarbazquux:last').as('foo') .then(() => { // remove the existing foobarbazquux cy.$$('foobarbazquux').remove() // and cause it to be re-rendered cy.$$('body').append(cy.$$('<foobarbazquux>asdf</foobarbazquux>')) }).get('@foo').should('contain', 'asdf') }) }) describe('should(\'exist\')', { defaultCommandTimeout: 1000, }, () => { it('waits until button exists', () => { cy.on('command:retry', _.after(3, () => { cy.$$('body').append($('<div id=\'missing-el\'>missing el</div>')) })) cy.get('#missing-el').should('exist') }) }) describe('should(\'not.exist\')', () => { it('waits until button does not exist', () => { cy.timeout(500, true) cy.on('command:retry', _.after(2, () => { cy.$$('#button').remove() })) cy.get('#button').should('not.exist') }) it('returns null when cannot find element', () => { cy.get('#missing-el').should('not.exist').then(($el) => { expect($el).to.be.null }) }) it('retries until cannot find element', () => { // add 500ms to the delta cy.timeout(500, true) const retry = _.after(3, () => { cy.$$('#list li:last').remove() }) cy.on('command:retry', retry) cy.get('#list li:last').should('not.exist').then(($el) => { expect($el).to.be.null }) }) }) describe('visibility is unopinionated', () => { it('finds invisible elements by default', () => { const button = cy.$$('#button').hide() cy.get('#button').then(($button) => { expect($button.get(0)).to.eq(button.get(0)) }) }) }) describe('should(\'not.be.visible\')', () => { it('returns invisible element', () => { const button = cy.$$('#button').hide() // cy.get("#button").then ($button) -> // expect($button).not.to.be.visible cy.get('#button').should('not.be.visible').then(($button) => { expect($button.get(0)).to.eq(button.get(0)) }) }) it('retries until element is invisible', () => { // add 500ms to the delta cy.timeout(500, true) let button = null const retry = _.after(3, () => { button = cy.$$('#button').hide() }) cy.on('command:retry', retry) cy.get('#button').should('not.be.visible').then(($button) => { expect($button.get(0)).to.eq(button.get(0)) }) }) }) describe('should(\'be.visible\')', () => { it('returns visible element', () => { const button = cy.$$('#button') cy.get('#button').should('be.visible').then(($button) => { expect($button.get(0)).to.eq(button.get(0)) }) }) it('retries until element is visible', () => { // add 500ms to the delta cy.timeout(500, true) const button = cy.$$('#button').hide() const retry = _.after(3, () => { button.show() }) cy.on('command:retry', retry) cy.get('#button').should('be.visible').then(($button) => { expect($button.get(0)).to.eq(button.get(0)) }) }) }) describe('should(\'have.length\', n)', () => { it('resolves once length equals n', () => { const forms = cy.$$('form') cy.get('form').should('have.length', forms.length).then(($forms) => { expect($forms.length).to.eq(forms.length) }) }) it('retries until length equals n', () => { // add 500ms to the delta cy.timeout(500, true) let buttons = cy.$$('button') const length = buttons.length - 2 cy.on('command:retry', _.after(2, () => { buttons.last().remove() buttons = cy.$$('button') })) // should resolving after removing 2 buttons cy.get('button').should('have.length', length).then(($buttons) => { expect($buttons.length).to.eq(length) }) }) it('retries an alias when not enough elements found', () => { // add 500ms to the delta cy.timeout(500, true) const buttons = cy.$$('button') const length = buttons.length + 1 // add another button after 2 retries, once cy.on('command:retry', _.after(2, _.once(() => { $('<button />').appendTo(cy.$$('body')) }))) // should eventually resolve after adding 1 button cy .get('button').as('btns') .get('@btns').should('have.length', length).then(($buttons) => { expect($buttons.length).to.eq(length) }) }) it('retries an alias when too many elements found without replaying commands', () => { // add 500ms to the delta cy.timeout(500, true) let buttons = cy.$$('button') const length = buttons.length - 2 const replayCommandsFrom = cy.spy(cy, 'replayCommandsFrom') cy.on('command:retry', () => { buttons.last().remove() buttons = cy.$$('button') }) const existingLen = cy.queue.length // should eventually resolve after adding 1 button cy .get('button').as('btns') .get('@btns').should('have.length', length).then(($buttons) => { expect(replayCommandsFrom).not.to.be.called // get, as, get, should, then == 5 expect(cy.queue.length - existingLen).to.eq(5) // we should not have replayed any commands expect($buttons.length).to.eq(length) }) }) }) describe('assertion verification', () => { it('automatically retries', () => { cy.on('command:retry', _.after(2, () => { cy.$$('button:first').attr('data-foo', 'bar') })) cy.get('button:first').should('have.attr', 'data-foo').and('match', /bar/) }) it('eventually resolves an alias', () => { cy.on('command:retry', _.after(2, () => { cy.$$('button:first').addClass('foo-bar-baz') })) cy .get('button:first').as('btn') .get('@btn').should('have.class', 'foo-bar-baz') }) }) describe('.log', () => { beforeEach(function () { this.logs = [] cy.on('log:added', (attrs, log) => { if (attrs.name === 'get') { this.lastLog = log this.logs.push(log) } }) return null }) it('logs elements length', () => { let buttons = cy.$$('button') const length = buttons.length - 2 // add 500ms to the delta cy.timeout(500, true) cy.on('command:retry', () => { buttons.last().remove() buttons = cy.$$('button') }) // should resolving after removing 2 buttons cy.get('button').should('have.length', length).then(function ($buttons) { expect(this.lastLog.get('numElements')).to.eq(length) }) }) it('logs exist: false', () => { cy.get('#does-not-exist').should('not.exist').then(function () { expect(this.lastLog.get('message')).to.eq('#does-not-exist') expect(this.lastLog.get('$el').get(0)).not.to.be.ok }) }) it('logs route aliases', () => { cy.visit('http://localhost:3500/fixtures/jquery.html') cy.server() cy.route(/users/, {}).as('get.users') cy.window().then({ timeout: 2000 }, (win) => { win.$.get('/users') }) cy.get('@get.users').then(function () { expect(this.lastLog.pick('message', 'referencesAlias', 'aliasType')).to.deep.eq({ message: '@get.users', referencesAlias: { name: 'get.users' }, aliasType: 'route', }) }) }) it('logs primitive aliases', (done) => { cy.on('log:added', (attrs, log) => { if (attrs.name === 'get') { expect(log.pick('$el', 'numRetries', 'referencesAlias', 'aliasType')).to.deep.eq({ referencesAlias: { name: 'f' }, aliasType: 'primitive', }) done() } }) cy .noop('foo').as('f') .get('@f') }) it('logs immediately before resolving', (done) => { cy.on('log:added', (attrs, log) => { if (attrs.name === 'get') { expect(log.pick('state', 'referencesAlias', 'aliasType')).to.deep.eq({ state: 'pending', referencesAlias: undefined, aliasType: 'dom', }) done() } }) cy.get('body') }) it('snapshots and ends when consuming an alias', () => { cy .get('body').as('b') .get('@b').then(function () { expect(this.lastLog.get('ended')).to.be.true expect(this.lastLog.get('state')).to.eq('passed') expect(this.lastLog.get('snapshots').length).to.eq(1) expect(this.lastLog.get('snapshots')[0]).to.be.an('object') }) }) it('logs obj once complete', () => { cy.get('body').as('b').then(function ($body) { const obj = { state: 'passed', name: 'get', message: 'body', alias: 'b', aliasType: 'dom', referencesAlias: undefined, } expect(this.lastLog.get('$el').get(0)).to.eq($body.get(0)) _.each(obj, (value, key) => { expect(this.lastLog.get(key)).deep.eq(value, `expected key: ${key} to eq value: ${value}`) }) }) }) it('#consoleProps', () => { cy.get('body').then(function ($body) { expect(this.lastLog.invoke('consoleProps')).to.deep.eq({ Command: 'get', Selector: 'body', Yielded: $body.get(0), Elements: 1, }) }) }) it('#consoleProps with an alias', () => { cy.get('body').as('b').get('@b').then(function ($body) { expect(this.lastLog.invoke('consoleProps')).to.deep.eq({ Command: 'get', Alias: '@b', Yielded: $body.get(0), Elements: 1, }) }) }) it('#consoleProps with a primitive alias', () => { cy.noop({ foo: 'foo' }).as('obj').get('@obj').then(function (obj) { expect(this.lastLog.invoke('consoleProps')).to.deep.eq({ Command: 'get', Alias: '@obj', Yielded: obj, }) }) }) it('#consoleProps with a route alias', () => { cy .server() .route(/users/, {}).as('getUsers') .visit('http://localhost:3500/fixtures/jquery.html') .window().then({ timeout: 2000 }, (win) => { return win.$.get('/users') }).get('@getUsers').then(function (obj) { expect(this.lastLog.invoke('consoleProps')).to.deep.eq({ Command: 'get', Alias: '@getUsers', Yielded: obj, }) }) }) }) describe('alias references', { defaultCommandTimeout: 100, }, () => { it('can get alias primitives', () => { cy .noop('foo').as('f') .get('@f').then((foo) => { expect(foo).to.eq('foo') }) }) it('can get alias objects', () => { cy .noop({}).as('obj') .get('@obj').then((obj) => { expect(obj).to.deep.eq({}) }) }) it('re-queries for an existing alias', () => { const body = cy.$$('body') cy.get('body').as('b').get('@b').then(($body) => { expect($body.get(0)).to.eq(body.get(0)) }) }) it('re-queries the dom if any element in an alias isnt in the document', () => { cy.$$('input') cy .get('input').as('inputs').then(function ($inputs) { this.length = $inputs.length // remove the last input $inputs.last().remove() // return original subject return $inputs }).get('@inputs').then(function ($inputs) { // we should have re-queried for these inputs // which should have reduced their length by 1 expect($inputs).to.have.length(this.length - 1) }) }) describe('route aliases', () => { it('returns the xhr', () => { cy .server() .route(/users/, {}).as('getUsers') .visit('http://localhost:3500/fixtures/jquery.html') .window().then({ timeout: 2000 }, (win) => { return win.$.get('/users') }).get('@getUsers').then((xhr) => { expect(xhr.url).to.include('/users') }) }) it('handles dots in alias name', () => { cy.server() cy.route(/users/, {}).as('get.users') cy.visit('http://localhost:3500/fixtures/jquery.html') cy.window().then({ timeout: 2000 }, (win) => { return win.$.get('/users') }) cy.get('@get.users').then((xhr) => { expect(xhr.url).to.include('/users') }) }) it('returns null if no xhr is found', () => { cy .server() .route(/users/, {}).as('getUsers') .visit('http://localhost:3500/fixtures/jquery.html') .get('@getUsers').then((xhr) => { expect(xhr).to.be.null }) }) it('returns an array of xhrs', () => { cy .visit('http://localhost:3500/fixtures/jquery.html') .server() .route(/users/, {}).as('getUsers') .window().then({ timeout: 2000 }, (win) => { return Promise.all([ win.$.get('/users', { num: 1 }), win.$.get('/users', { num: 2 }), ]) }).get('@getUsers.all').then((xhrs) => { expect(xhrs).to.be.an('array') expect(xhrs[0].url).to.include('/users?num=1') expect(xhrs[1].url).to.include('/users?num=2') }) }) it('returns an array of xhrs when dots in alias name', () => { cy.visit('http://localhost:3500/fixtures/jquery.html') cy.server() cy.route(/users/, {}).as('get.users') cy.window().then({ timeout: 2000 }, (win) => { return Promise.all([ win.$.get('/users', { num: 1 }), win.$.get('/users', { num: 2 }), ]) }) cy.get('@get.users.all').then((xhrs) => { expect(xhrs).to.be.an('array') expect(xhrs[0].url).to.include('/users?num=1') expect(xhrs[1].url).to.include('/users?num=2') }) }) it('returns the 1st xhr', () => { cy .visit('http://localhost:3500/fixtures/jquery.html') .server() .route(/users/, {}).as('getUsers') .window().then({ timeout: 2000 }, (win) => { return Promise.all([ win.$.get('/users', { num: 1 }), win.$.get('/users', { num: 2 }), ]) }).get('@getUsers.1').then((xhr1) => { expect(xhr1.url).to.include('/users?num=1') }) }) it('returns the 2nd xhr', () => { cy .visit('http://localhost:3500/fixtures/jquery.html') .server() .route(/users/, {}).as('getUsers') .window().then({ timeout: 2000 }, (win) => { return Promise.all([ win.$.get('/users', { num: 1 }), win.$.get('/users', { num: 2 }), ]) }).get('@getUsers.2').then((xhr2) => { expect(xhr2.url).to.include('/users?num=2') }) }) it('returns the 2nd xhr when dots in alias', () => { cy.visit('http://localhost:3500/fixtures/jquery.html') cy.server() cy.route(/users/, {}).as('get.users') cy.window().then({ timeout: 2000 }, (win) => { return Promise.all([ win.$.get('/users', { num: 1 }), win.$.get('/users', { num: 2 }), ]) }) cy.get('@get.users.2').then((xhr2) => { expect(xhr2.url).to.include('/users?num=2') }) }) it('returns the 3rd xhr as null', () => { cy .server() .route(/users/, {}).as('getUsers') .visit('http://localhost:3500/fixtures/jquery.html') .window().then({ timeout: 2000 }, (win) => { return Promise.all([ win.$.get('/users', { num: 1 }), win.$.get('/users', { num: 2 }), ]) }).get('@getUsers.3').then((xhr3) => { expect(xhr3).to.be.null }) }) }) }) // it "re-queries the dom if any element in an alias isnt visible", -> // inputs = cy.$$("input") // inputs.hide() // cy // .get("input", {visible: false}).as("inputs").then ($inputs) -> // @length = $inputs.length // ## show the inputs // $inputs.show() // return $inputs // .get("@inputs").then ($inputs) -> // ## we should have re-queried for these inputs // ## which should have increased their length by 1 // expect($inputs).to.have.length(@length) // these other tests are for .save // it "will resolve deferred arguments", -> // df = $.Deferred() // _.delay -> // df.resolve("iphone") // , 100 // cy.get("input:text:first").type(df).then ($input) -> // expect($input).to.have.value("iphone") // it "handles saving subjects", -> // cy.noop({foo: "foo"}).assign("foo").noop(cy.get("foo")).then (subject) -> // expect(subject).to.deep.eq {foo: "foo"} // it "resolves falsy arguments", -> // cy.noop(0).assign("zero").then -> // expect(cy.get("zero")).to.eq 0 // it "returns a function when no alias was found", -> // cy.noop().then -> // expect(cy.get("something")).to.be.a("function") describe('errors', { defaultCommandTimeout: 50, }, () => { beforeEach(function () { this.logs = [] cy.on('log:added', (attrs, log) => { if (attrs.name === 'get') { this.lastLog = log this.logs.push(log) } }) return null }) it('throws once when incorrect sizzle selector', function (done) { cy.on('fail', (err) => { expect(this.logs.length).to.eq(1) done() }) cy.get('.spinner\'') }) it('throws on too many elements after timing out waiting for length', (done) => { const buttons = cy.$$('button') cy.on('fail', (err) => { expect(err.message).to.include(`Too many elements found. Found '${buttons.length}', expected '${buttons.length - 1}'.`) done() }) cy.get('button').should('have.length', buttons.length - 1) }) it('throws on too few elements after timing out waiting for length', (done) => { const buttons = cy.$$('button') cy.on('fail', (err) => { expect(err.message).to.include(`Not enough elements found. Found '${buttons.length}', expected '${buttons.length + 1}'.`) done() }) cy.get('button').should('have.length', buttons.length + 1) }) it('throws after timing out not finding element', (done) => { cy.on('fail', (err) => { expect(err.message).to.include('Expected to find element: `#missing-el`, but never found it.') done() }) cy.get('#missing-el') }) it('throws after timing out not finding element when should exist', (done) => { cy.on('fail', (err) => { expect(err.message).to.include('Expected to find element: `#missing-el`, but never found it.') done() }) cy.get('#missing-el').should('exist') }) it('throws existence error without running assertions', (done) => { cy.on('fail', (err) => { expect(err.message).to.include('Expected to find element: `#missing-el`, but never found it.') done() }) cy.get('#missing-el').should('have.prop', 'foo') }) it('throws when using an alias that does not exist') it('throws after timing out after a .wait() alias reference', (done) => { cy.on('fail', (err) => { expect(err.message).to.include('Expected to find element: `getJsonButton`, but never found it.') done() }) cy .server() .route(/json/, { foo: 'foo' }).as('getJSON') .visit('http://localhost:3500/fixtures/xhr.html').then(() => { cy.$$('#get-json').click(() => { cy.timeout(1000) const retry = _.after(3, _.once(() => { cy.state('window').$.getJSON('/json') })) cy.on('command:retry', retry) }) }).get('#get-json').as('getJsonButton').click() .wait('@getJSON') .get('getJsonButton') }) it('throws after timing out while not trying to find an element', (done) => { cy.on('fail', (err) => { expect(err.message).to.include('Expected <div#dom> not to exist in the DOM, but it was continuously found.') done() }) cy.get('div:first').should('not.exist') }) it('throws after timing out while trying to find an invisible element', (done) => { cy.on('fail', (err) => { expect(err.message).to.include('expected \'<div#dom>\' not to be \'visible\'') done() }) cy.get('div:first').should('not.be.visible') }) it('does not include message about why element was not visible', (done) => { cy.on('fail', (err) => { expect(err.message).not.to.include('why this element is not visible') done() }) cy.get('div:first').should('not.be.visible') }) it('throws after timing out trying to find a visible element', (done) => { cy.$$('#button').hide() cy.on('fail', (err) => { expect(err.message).to.include('This element `<button#button>` is not visible because it has CSS property: `display: none`') done() }) cy.get('#button').should('be.visible') }) it('includes a message about why the element was not visible', (done) => { cy.$$('#button').hide() cy.on('fail', (err) => { expect(err.message).to.include('element `<button#button>` is not visible because') done() }) cy.get('#button').should('be.visible') }) it('sets error command state', function (done) { cy.on('fail', (err) => { const { lastLog } = this expect(lastLog.get('state')).to.eq('failed') expect(lastLog.get('error')).to.eq(err) done() }) cy.get('foobar') }) it('throws when alias property is `0`', (done) => { cy.on('fail', (err) => { expect(err.message).to.include('`0` is not a valid alias property. Are you trying to ask for the first response? If so write `@getUsers.1`') done() }) cy .server() .route(/users/, {}).as('getUsers') .get('@getUsers.0') }) it('throws when alias property isnt just a digit', (done) => { cy.on('fail', (err) => { expect(err.message).to.include('`1b` is not a valid alias property. Only `numbers` or `all` is permitted.') done() }) cy .server() .route(/users/, {}).as('getUsers') .get('@getUsers.1b') }) it('throws when alias property isnt a digit or `all`', (done) => { cy.on('fail', (err) => { expect(err.message).to.include('`all ` is not a valid alias property. Only `numbers` or `all` is permitted.') done() }) cy .server() .route(/users/, {}).as('getUsers') .get('@getUsers.all ') }) _.each(['', 'foo', [], 1, null], (value) => { it(`throws when options property is not an object. Such as: ${value}`, (done) => { cy.on('fail', (err) => { expect(err.message).to.include(`only accepts an options object for its second argument. You passed ${value}`) done() }) cy.get('foobar', value) }) }) it('logs out $el when existing $el is found even on failure', function (done) { const button = cy.$$('#button').hide() cy.on('fail', (err) => { const { lastLog } = this expect(lastLog.get('state')).to.eq('failed') expect(lastLog.get('error')).to.eq(err) expect(lastLog.get('$el').get(0)).to.eq(button.get(0)) const consoleProps = lastLog.invoke('consoleProps') expect(consoleProps.Yielded).to.eq(button.get(0)) expect(consoleProps.Elements).to.eq(button.length) done() }) cy.get('#button').should('be.visible') }) }) }) context('#contains', () => { it('is scoped to the body and will not return title elements', () => { cy.contains('DOM Fixture').then(($el) => { expect($el).not.to.match('title') }) }) it('will not find script elements', () => { cy.$$('<script>// some-script-content </script>').appendTo(cy.$$('body')) cy.contains('some-script-content').should('not.match', 'script') }) it('will not find style elements', () => { cy.$$('<style> some-style-content {} </style>').appendTo(cy.$$('body')) cy.contains('some-style-content').should('not.match', 'style') }) it('finds the nearest element by :contains selector', () => { cy.contains('li 0').then(($el) => { expect($el.length).to.eq(1) expect($el).to.match('li') }) }) it('resets the subject between chain invocations', () => { const span = cy.$$('.k-in:contains(Quality Control):last') const label = cy.$$('#complex-contains label') cy.get('#complex-contains').contains('nested contains').then(($label) => { expect($label.get(0)).to.eq(label.get(0)) return $label }) cy.contains('Quality Control').then(($span) => { expect($span.get(0)).to.eq(span.get(0)) }) }) it('GET is scoped to the current subject', () => { const span = cy.$$('#click-me a span') cy.get('#click-me a').contains('click').then(($span) => { expect($span.length).to.eq(1) expect($span.get(0)).to.eq(span.get(0)) }) }) it('can find input type=submits by value', () => { cy.contains('input contains submit').then(($el) => { expect($el.length).to.eq(1) expect($el).to.match('input[type=submit]') }) }) it('has an optional filter argument', () => { cy.contains('ul', 'li 0').then(($el) => { expect($el.length).to.eq(1) expect($el).to.match('ul') }) }) it('disregards priority elements when provided a filter', () => { const form = cy.$$('#click-me') cy.contains('form', 'click me').then(($form) => { expect($form.get(0)).to.eq(form.get(0)) }) }) it('searches all els in comma separated filter', () => { cy.contains('a,button', 'Naruto').then(($el) => { expect($el.length).to.eq(1) expect($el).to.match('a') }) cy.contains('a,button', 'Boruto').then(($el) => { expect($el.length).to.eq(1) expect($el).to.match('button') }) }) it('searches all els in comma separated filter with spaces', () => { const aText = 'Naruto' const bText = 'Boruto' cy.contains('a, button', aText).then(($el) => { expect($el.length).to.eq(1) expect($el).to.match('a') expect($el.text()).to.eq(aText) }) cy.contains('a, button', bText).then(($el) => { expect($el.length).to.eq(1) expect($el).to.match('button') expect($el.text()).to.eq(bText) }) }) it('favors input type=submit', () => { const input = cy.$$('#input-type-submit input') cy.contains('click me').then(($input) => { expect($input.get(0)).to.eq(input.get(0)) }) }) it('favors buttons next', () => { const button = cy.$$('#button-inside-a button') cy.contains('click button').then(($btn) => { expect($btn.get(0)).to.eq(button.get(0)) }) }) it('favors anchors next', () => { cy.contains('Home Page').then(($el) => { expect($el.length).to.eq(1) expect($el).to.match('a') }) }) it('reduces right by priority element', () => { const label = cy.$$('#complex-contains label') // it should find label because label is the first priority element // out of the collection of contains elements cy.get('#complex-contains').contains('nested contains').then(($label) => { expect($label.get(0)).to.eq(label.get(0)) }) }) it('retries until content is found', () => { const span = $('<span>brand new content</span>') // only append the span after we retry // three times const retry = _.after(3, () => { cy.$$('body').append(span) }) cy.on('command:retry', retry) cy.contains('brand new content').then(($span) => { expect($span.get(0)).to.eq(span.get(0)) }) }) it('finds the furthest descendent when filter matches more than 1 element', () => { cy .get('#contains-multiple-filter-match').contains('li', 'Maintenance').then(($row) => { expect($row).to.have.class('active') }) }) it('returns the parent node which contains content spanned across a child element and text node', () => { const item = cy.$$('#upper .item') cy.contains('New York').then(($item) => { expect($item).to.be.ok expect($item.get(0)).to.eq(item.get(0)) }) }) it('finds text by regexp and restores contains', () => { const { contains } = Cypress.$Cypress.$.expr[':'] cy.contains(/^asdf \d+/).then(($li) => { expect($li).to.have.text('asdf 1') expect(Cypress.$Cypress.$.expr[':'].contains).to.eq(contains) }) }) it('finds text by regexp when second parameter is a regexp and restores contains', () => { const { contains } = Cypress.$Cypress.$.expr[':'] cy.contains('#asdf>li:first', /asdf 1/).then(($li) => { expect($li).to.have.text('asdf 1') expect(Cypress.$Cypress.$.expr[':'].contains).to.eq(contains) }) }) it('returns elements found first when multiple siblings found', () => { cy.contains('li', 'asdf').then(($li) => { expect($li).to.have.text('asdf 1') }) }) it('returns first ul when multiple uls', () => { cy.contains('ul', 'jkl').then(($ul) => { expect($ul.find('li:first')).to.have.text('jkl 1') }) }) it('cancels existing promises', (done) => { let retrys = 0 cy.stub(Cypress.runner, 'stop') const abort = _.after(2, () => { cy.spy(cy, 'now') Cypress.stop() }) cy.on('stop', () => { _.delay(() => { expect(cy.now).not.to.be.called