UNPKG

@revoloo/cypress6

Version:

Cypress.io end to end testing tool

709 lines (559 loc) 21.6 kB
const { _ } = Cypress describe('src/cy/commands/agents', () => { context('.stub', () => { it('synchronously returns stub', () => { const stub = cy.stub() expect(stub).to.exist expect(stub.returns).to.be.a('function') }) describe('.stub()', () => { beforeEach(function () { this.stub = cy.stub() }) it('proxies sinon stub', function () { this.stub() expect(this.stub.callCount).to.eq(1) }) it('has sinon stub API', function () { this.stub.returns(true) const result = this.stub() expect(result).to.be.true }) }) describe('.stub(obj, \'method\')', () => { beforeEach(function () { this.originalCalled = false this.obj = { foo: () => { this.originalCalled = true }, } this.stub = cy.stub(this.obj, 'foo') }) it('proxies sinon stub', function () { this.obj.foo() expect(this.stub.callCount).to.eq(1) }) it('replaces method', function () { this.obj.foo() expect(this.originalCalled).to.be.false }) it('can callThrough on constructors', () => { cy.stub(window, 'Notification').callThroughWithNew().as('Notification') new Notification('Hello') cy.get('@Notification').should('have.been.calledWith', 'Hello') }) }) describe('.stub(obj, \'method\', replacerFn)', () => { beforeEach(function () { this.originalCalled = false this.obj = { bar: 'bar', foo: () => { this.originalCalled = true }, } cy.stub(this.obj, 'bar', 'baz') this.replacementCalled = false this.stub = cy.stub(this.obj, 'foo', () => { this.replacementCalled = true }) }) it('proxies sinon stub', function () { this.obj.foo() expect(this.stub.callCount).to.eq(1) }) it('replaces method with replacement', function () { this.obj.foo() expect(this.originalCalled).to.be.false expect(this.replacementCalled).to.be.true }) it('replaces values', function () { expect(this.obj.bar).to.eq('baz') }) }) describe('.resolves', () => { beforeEach(function () { this.obj = { foo () {} } this.stub = cy.stub(this.obj, 'foo') }) it('has resolves method', function () { expect(this.stub.resolves).to.be.a('function') }) it('resolves promise', function () { this.stub.resolves('foo') return this.obj.foo().then((foo) => { expect(foo).to.eq('foo') }) }) it('uses Bluebird under the hood', () => { const obj = { foo () {}, } cy.stub(obj, 'foo').resolves('bar') return obj .foo() .delay(1) }) }) describe('.rejects', () => { beforeEach(function () { this.obj = { foo () {} } this.stub = cy.stub(this.obj, 'foo') }) it('has rejects method', function () { expect(this.stub.rejects).to.be.a('function') }) it('rejects promise', function () { const error = new Error() this.stub.rejects(error) return this.obj.foo() .then(() => { throw new Error('Should throw error') }).catch((err) => { expect(err).to.eq(error) }) }) }) describe('.withArgs', () => { beforeEach(function () { this.agentLogs = [] this.logs = [] cy.on('log:added', (attrs, log) => { if (attrs.instrument === 'agent') { this.agentLogs.push(log) } return this.logs.push(log) }) this.obj = { foo () {} } this.stub = cy.stub(this.obj, 'foo') this.stubWithArgs = this.stub.withArgs('foo') }) it('can be aliased', function () { this.stubWithArgs.as('withFoo') expect(this.logs[1].get('alias')).to.eq('withFoo') }) describe('logging', () => { it('creates new log instrument with sub-count', function () { expect(this.agentLogs.length).to.eq(2) expect(this.agentLogs[1].get('name')).to.eq('stub-1.1') this.stub.withArgs('bar') expect(this.agentLogs.length).to.eq(3) expect(this.agentLogs[2].get('name')).to.eq('stub-1.2') }) describe('on invocation', () => { it('only logs once', function () { this.obj.foo('foo') expect(this.logs.length).to.eq(3) }) it('includes count in name', function () { this.obj.foo('foo') expect(this.logs[2].get('name')).to.eq('stub-1') }) it('has no alias if no aliases set', function () { this.obj.foo('foo') expect(this.logs[2].get('alias')).to.be.undefined }) it('has withArgs alias if it\'s the only one set', function () { this.stubWithArgs.as('withFoo') this.obj.foo('foo') expect(this.logs[2].get('alias')).to.eql(['withFoo']) }) it('has parent alias if it\'s the only one set', function () { this.stub.as('noArgs') this.obj.foo('foo') expect(this.logs[2].get('alias')).to.eql(['noArgs']) }) it('has both aliases if both set', function () { this.stub.as('noArgs') this.stubWithArgs.as('withFoo') this.obj.foo('foo') expect(this.logs[2].get('alias')).to.eql(['noArgs', 'withFoo']) }) it('logs parent if invoked without necessary args', function () { this.obj.foo() expect(this.logs[2].get('name')).to.eq('stub-1') }) describe('.consoleProps', () => { beforeEach(function () { this.stub.as('objFoo') this.stubWithArgs.as('withFoo') this.stub.withArgs('foo', 'baz').as('withFooBaz') this.obj.foo('foo') this.obj.foo('foo', 'baz') this.consoleProps = this.logs[4].get('consoleProps')() }) it('includes the event', function () { expect(this.consoleProps['Event']).to.eq('stub-1 called') }) it('includes reference to stub', function () { expect(this.consoleProps['stub']).to.be.a('function') }) it('includes call number', function () { expect(this.consoleProps['Call #']).to.eq(2) }) it('includes alias', function () { expect(this.consoleProps['Alias']).to.eq('objFoo') }) it('includes references to withArgs stubs', function () { expect(this.consoleProps[' 1.1 stub']).to.be.a('function') expect(this.consoleProps[' 1.2 stub']).to.be.a('function') }) it('includes withArgs call numbers', function () { expect(this.consoleProps[' 1.1 call #']).to.eq(2) expect(this.consoleProps[' 1.2 call #']).to.eq(1) }) it('includes withArgs aliases', function () { expect(this.consoleProps[' 1.1 alias']).to.eq('withFoo') expect(this.consoleProps[' 1.2 alias']).to.eq('withFooBaz') }) it('includes withArgs matching arguments', function () { expect(this.consoleProps[' 1.1 matching arguments']).to.eql(['foo']) expect(this.consoleProps[' 1.2 matching arguments']).to.eql(['foo', 'baz']) }) }) }) }) }) describe('.as', () => { context('without dots', () => { beforeEach(function () { this.logs = [] cy.on('log:added', (attrs, log) => { this.logs.push(log) }) this.stub = cy.stub().as('myStub') }) it('returns stub', function () { expect(this.stub).to.have.property('callCount') }) it('updates instrument log with alias', function () { expect(this.logs[0].get('alias')).to.eq('myStub') expect(this.logs[0].get('aliasType')).to.eq('agent') }) it('includes alias in invocation log', function () { this.stub() expect(this.logs[1].get('alias')).to.eql(['myStub']) expect(this.logs[1].get('aliasType')).to.eq('agent') }) it('includes alias in console props', function () { this.stub() const consoleProps = this.logs[1].get('consoleProps')() expect(consoleProps['Alias']).to.eql('myStub') }) it('updates the displayName of the agent', function () { expect(this.myStub.displayName).to.eq('myStub') }) it('stores the lookup as an alias', () => { expect(cy.state('aliases').myStub).to.exist }) it('stores the agent as the subject', function () { expect(cy.state('aliases').myStub.subject).to.eq(this.stub) }) it('assigns subject to runnable ctx', function () { expect(this.myStub).to.eq(this.stub) }) it('retries until assertions pass', function () { cy.on('command:retry', _.after(2, () => { this.myStub('foo') })) cy.get('@myStub').should('be.calledWith', 'foo') }) describe('errors', () => { _.each([null, undefined, {}, [], 123], (value) => { it(`throws when passed: ${value}`, () => { expect(() => { cy.stub().as(value) }).to.throw('`cy.as()` can only accept a string.') }) }) it('throws on blank string', () => { expect(() => { cy.stub().as('') }).to.throw('`cy.as()` cannot be passed an empty string.') }) _.each(['test', 'runnable', 'timeout', 'slow', 'skip', 'inspect'], (reserved) => { it(`throws on a reserved word: ${reserved}`, () => { expect(() => { cy.stub().as(reserved) }).to.throw(`\`cy.as()\` cannot be aliased as: \`${reserved}\`. This word is reserved.`) }) }) }) }) context('with dots', () => { beforeEach(function () { this.logs = [] cy.on('log:added', (attrs, log) => { this.logs.push(log) }) this.stub = cy.stub().as('my.stub') }) it('returns stub', function () { expect(this.stub).to.have.property('callCount') }) it('updates instrument log with alias', function () { expect(this.logs[0].get('alias')).to.eq('my.stub') expect(this.logs[0].get('aliasType')).to.eq('agent') }) it('includes alias in invocation log', function () { this.stub() expect(this.logs[1].get('alias')).to.eql(['my.stub']) expect(this.logs[1].get('aliasType')).to.eq('agent') }) it('includes alias in console props', function () { this.stub() const consoleProps = this.logs[1].get('consoleProps')() expect(consoleProps['Alias']).to.eql('my.stub') }) it('updates the displayName of the agent', function () { expect(this['my.stub'].displayName).to.eq('my.stub') }) it('stores the lookup as an alias', () => { expect(cy.state('aliases')['my.stub']).to.exist }) it('stores the agent as the subject', function () { expect(cy.state('aliases')['my.stub'].subject).to.eq(this.stub) }) it('assigns subject to runnable ctx', function () { expect(this['my.stub']).to.eq(this.stub) }) it('retries until assertions pass', function () { cy.on('command:retry', _.after(2, () => { this['my.stub']('foo') })) cy.get('@my.stub').should('be.calledWith', 'foo') }) describe('errors', () => { _.each([null, undefined, {}, [], 123], (value) => { it(`throws when passed: ${value}`, () => { expect(() => { cy.stub().as(value) }).to.throw('`cy.as()` can only accept a string.') }) }) it('throws on blank string', () => { expect(() => { cy.stub().as('') }).to.throw('`cy.as()` cannot be passed an empty string.') }) _.each(['test', 'runnable', 'timeout', 'slow', 'skip', 'inspect'], (reserved) => { it(`throws on a reserved word: ${reserved}`, () => { expect(() => { cy.stub().as(reserved) }).to.throw(`\`cy.as()\` cannot be aliased as: \`${reserved}\`. This word is reserved.`) }) }) }) }) }) describe('logging', () => { beforeEach(function () { this.logs = [] this.agentLogs = [] this.stubLogs = [] cy.on('log:added', (attrs, log) => { if (attrs.instrument === 'agent') { this.agentLogs.push(log) } if (attrs.event) { this.stubLogs.push(log) } this.logs.push(log) }) this.obj = { foo () {} } this.stub = cy.stub(this.obj, 'foo').returns('return value') }) it('logs agent on creation', function () { expect(this.agentLogs[0].get('name')).to.eq('stub-1') expect(this.agentLogs[0].get('type')).to.eq('stub-1') expect(this.agentLogs[0].get('instrument')).to.eq('agent') }) it('logs event for each invocation', function () { this.obj.foo('foo') expect(this.stubLogs.length).to.eq(1) expect(this.stubLogs[0].get('name')).to.eq('stub-1') expect(this.stubLogs[0].get('message')).to.eq('foo("foo")') expect(this.stubLogs[0].get('event')).to.be.true this.obj.foo('bar') expect(this.stubLogs.length).to.eq(2) expect(this.stubLogs[1].get('name')).to.eq('stub-1') expect(this.stubLogs[1].get('message')).to.eq('foo("bar")') expect(this.stubLogs[1].get('event')).to.be.true }) it('increments callCount of agent log on each invocation', function () { expect(this.agentLogs[0].get('callCount')).to.eq(0) this.obj.foo('foo', 'bar') expect(this.agentLogs[0].get('callCount')).to.eq(1) this.obj.foo('foo', 'baz') expect(this.agentLogs[0].get('callCount')).to.eq(2) }) it('resets unique name counter on restore', function () { expect(this.agentLogs[0].get('name')).to.eq('stub-1') Cypress.emit('test:before:run', {}) cy.stub() expect(this.agentLogs[1].get('name')).to.eq('stub-1') }) context('arg formatting', () => { beforeEach(function () { this.bigArray = [1, 2, 3, 4, 5] this.bigObject = { a: 1, b: 1, c: 1, d: 1, e: 1, f: 1 } this.obj.foo('str', 5, true) this.obj.foo(null, undefined, [1, 2, 3]) this.obj.foo({ g: 1 }, this.bigArray, this.bigObject) this.obj.foo(1, 2, 3, 4, 5) }) context('in message', () => { it('formats args', function () { expect(this.logs[1].get('message')).to.eq('foo("str", 5, true)') expect(this.logs[2].get('message')).to.eq('foo(null, undefined, [1, 2, 3])') expect(this.logs[3].get('message')).to.eq('foo({g: 1}, Array[5], Object{6})') }) it('truncates if more than 3 args', function () { expect(this.logs[4].get('message')).to.eq('foo(1, 2, 3, ...)') }) }) context('in assertion', () => { beforeEach(function () { cy.on('log:added', (attrs, log) => { this.lastLog = log }) return null }) it('formats string, number, boolean args', function () { expect(this.obj.foo).be.calledWith('str', 5, true) expect(this.lastLog.get('message')).to.include('expected foo to have been called with arguments "str", 5, true') }) it('formats null, undefined, small array args', function () { expect(this.obj.foo).be.calledWith(null, undefined, [1, 2, 3]) expect(this.lastLog.get('message')).to.include('expected foo to have been called with arguments null, undefined, [1, 2, 3]') }) it('formats small object, big array, big object args', function () { expect(this.obj.foo).be.calledWith({ g: 1 }, this.bigArray, this.bigObject) expect(this.lastLog.get('message')).to.include('expected foo to have been called with arguments {g: 1}, Array[5], Object{6}') }) it('does not include stack with calls when assertion fails', function (done) { cy.on('fail', () => { expect(this.lastLog.get('message')).to.include([ ' foo("str", 5, true) => "return value"', ' foo(null, undefined, [1, 2, 3]) => "return value"', ' foo({g: 1}, Array[5], Object{6}) => "return value"', ' foo(1, 2, 3, 4, 5) => "return value"', ].join('\n')) done() }) cy.wrap(null).then(function () { expect(this.obj.foo).to.be.calledWith(false, false, false) }) }) }) }) context('#consoleProps', () => { beforeEach(function () { this.stub.as('objFoo') this.context = {} this.obj.foo.call(this.context, 'foo', 'baz') this.obj.foo('foo', 'baz') this.consoleProps = this.logs[1].get('consoleProps')() }) it('does not include \'command\' or \'error\' properties', function () { expect(this.consoleProps['Command']).to.be.null expect(this.consoleProps['Error']).to.be.null }) it('includes the event', function () { expect(this.consoleProps['Event']).to.eq('stub-1 called') }) it('includes reference to stub', function () { expect(this.consoleProps['stub']).to.be.a('function') }) it('includes call number', function () { expect(this.consoleProps['Call #']).to.eq(2) }) it('includes alias', function () { expect(this.consoleProps['Alias']).to.eq('objFoo') }) it('includes references to stubbed object', function () { expect(this.consoleProps['Stubbed Obj']).to.be.eq(this.obj) }) it('includes arguments', function () { expect(this.consoleProps['Arguments']).to.eql(['foo', 'baz']) }) it('includes context', function () { expect(this.consoleProps['Context']).to.eq(this.context) }) it('includes return value', function () { expect(this.consoleProps['Returned']).to.eq('return value') }) }) }) }) context('.spy(obj, \'method\')', () => { beforeEach(function () { this.logs = [] cy.on('log:added', (attrs, log) => { return this.logs.push(log) }) this.originalCalled = false this.obj = { foo: () => { this.originalCalled = true }, } this.spy = cy.spy(this.obj, 'foo') }) it('synchronously returns spy', function () { expect(this.spy).to.exist expect(this.spy.callCount).to.be.a('number') }) it('proxies sinon spy', function () { this.obj.foo() expect(this.spy.callCount).to.eq(1) }) it('does not replace method', function () { this.obj.foo() expect(this.originalCalled).to.be.true }) it('can spy on constructors', () => { cy.spy(window, 'Notification').as('Notification') new Notification('Hello') cy.get('@Notification').should('have.been.calledWith', 'Hello') }) context('#as', () => { // same as cy.stub(), so just some smoke tests here beforeEach(function () { this.logs = [] cy.on('log:added', (attrs, log) => { return this.logs.push(log) }) this.spy = cy.spy().as('mySpy') }) it('returns spy', function () { expect(this.spy).to.have.property('callCount') }) it('updates instrument log with alias', function () { expect(this.logs[0].get('alias')).to.eq('mySpy') expect(this.logs[0].get('aliasType')).to.eq('agent') }) }) context('logging', () => { // same as cy.stub() except for name and type it('logs agent on creation', function () { expect(this.logs[0].get('name')).to.eq('spy-1') expect(this.logs[0].get('type')).to.eq('spy-1') expect(this.logs[0].get('instrument')).to.eq('agent') }) context('#consoleProps', () => { beforeEach(function () { this.obj.foo() this.consoleProps = this.logs[1].get('consoleProps')() }) it('includes reference to spy', function () { expect(this.consoleProps['spy']).to.be.a('function') }) it('includes references to spied object', function () { expect(this.consoleProps['Spied Obj']).to.be.eq(this.obj) }) }) }) }) })