Skip to content

Commit

Permalink
feat: add examples for sinon.match (#385)
Browse files Browse the repository at this point in the history
* upgrade to Cypress v4.2.0

* start adding examples for sinon.match

* pass TS linter

* add more matcher examples

* copy tests to html

* add jsdocs

* list Cypress binaries on AppVeyor

* invalidate AppVeyor cache based on package.json file

* appveyor yml syntax
  • Loading branch information
bahmutov authored Mar 18, 2020
1 parent e9769d8 commit 60c6469
Show file tree
Hide file tree
Showing 5 changed files with 279 additions and 8 deletions.
93 changes: 93 additions & 0 deletions app/commands/spies-stubs-clocks.html
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,99 @@ <h4><a href="https://on.cypress.io/tick">cy.tick()</a></h4>
</div>
</div>
</div>

<div class="col-xs-12"><hr></div>
<div class="col-xs-7">
<h4>cy.stub() matches depending on arguments</h4>
<p>See all possible matchers at <a href="https://sinonjs.org/releases/latest/matchers/">Sinonjs.org</a></p>
<pre><code class="javascript">const greeter = {
/**
* Greets a person
* @param {string} name
*/
greet (name) {
return `Hello, ${name}!`
},
}

cy.stub(greeter, 'greet')
.callThrough() // if you want non-matched calls to call the real method
.withArgs(Cypress.sinon.match.string).returns('Hi')
.withArgs(Cypress.sinon.match.number).throws(new Error('Invalid name'))

expect(greeter.greet('World')).to.equal('Hi')
expect(() => greeter.greet(42)).to.throw('Invalid name')
expect(greeter.greet).to.have.been.calledTwice

// non-matched calls goes the actual method
expect(greeter.greet()).to.equal('Hello, undefined!')</code></pre>
</div>

<div class="col-xs-12"><hr></div>
<div class="col-xs-7">
<h4>matches call arguments using Sinon matchers</h4>
<p>See all possible matchers at <a href="https://sinonjs.org/releases/latest/matchers/">Sinonjs.org</a></p>
<pre><code class="javascript">const calculator = {
/**
* returns the sum of two arguments
* @param a {number}
* @param b {number}
*/
add (a, b) {
return a + b
},
}

const spy = cy.spy(calculator, 'add').as('add')

expect(calculator.add(2, 3)).to.equal(5)

// if we want to assert the exact values used during the call
expect(spy).to.be.calledWith(2, 3)

// let's confirm "add" method was called with two numbers
expect(spy).to.be.calledWith(Cypress.sinon.match.number, Cypress.sinon.match.number)

// alternatively, provide the value to match
expect(spy).to.be.calledWith(Cypress.sinon.match(2), Cypress.sinon.match(3))

// match any value
expect(spy).to.be.calledWith(Cypress.sinon.match.any, 3)

// match any value from a list
expect(spy).to.be.calledWith(Cypress.sinon.match.in([1, 2, 3]), 3)

// expect the value to pass a custom predicate function
// the second argument to "sinon.match(predicate, message)" is
// shown if the predicate does not pass and assertion fails
const isEven = (x) => x % 2 === 0

expect(spy).to.be.calledWith(Cypress.sinon.match(isEven, 'isEven'), 3)

// you can combine several matchers using "and", "or"
const isGreaterThan = (limit) => (x) => x > limit
const isLessThan = (limit) => (x) => x < limit

expect(spy).to.be.calledWith(
Cypress.sinon.match.number,
Cypress.sinon.match(
isGreaterThan(2), '> 2').and(Cypress.sinon.match(isLessThan(4), '< 4'))
)

expect(spy).to.be.calledWith(
Cypress.sinon.match.number,
Cypress.sinon.match(isGreaterThan(200), '> 200').or(Cypress.sinon.match(3))
)

// matchers can be used from BDD assertions
cy.get('@add').should('have.been.calledWith',
Cypress.sinon.match.number, Cypress.sinon.match(3)
)

// you can alias matchers for shorter test code
const { match: M } = Cypress.sinon

cy.get('@add').should('have.been.calledWith', M.number, M(3))</code></pre>
</div>
</div>
</div>
Expand Down
7 changes: 5 additions & 2 deletions appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@ install:

cache:
# cache NPM packages and Cypress binary
- '%AppData%\npm'
- '%USERPROFILE%\AppData\Local\Cypress\Cache'
# and invalidate the cache when package.json file changes
# https://www.appveyor.com/docs/build-cache/
- '%AppData%\npm -> package.json'
- '%USERPROFILE%\AppData\Local\Cypress\Cache -> package.json'

# Post-install test scripts.
test_script:
Expand All @@ -31,6 +33,7 @@ test_script:
- run-if npm run cy:version
- run-if npm run cy:verify
- run-if npm run cy:info
- run-if npm run cy:cache:list
- run-if npm run test:ci:record:windows
- run-if npm run test:ci:record:windows:edge

Expand Down
110 changes: 110 additions & 0 deletions cypress/integration/examples/spies_stubs_clocks.spec.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
/// <reference types="cypress" />
// remove no check once Cypress.sinon is typed
// https://github.com/cypress-io/cypress/issues/6720

context('Spies, Stubs, and Clock', () => {
it('cy.spy() - wrap a method in a spy', () => {
Expand Down Expand Up @@ -94,4 +96,112 @@ context('Spies, Stubs, and Clock', () => {
cy.get('#tick-div').click()
.should('have.text', '1489449610')
})

it('cy.stub() matches depending on arguments', () => {
// see all possible matchers at
// https://sinonjs.org/releases/latest/matchers/
const greeter = {
/**
* Greets a person
* @param {string} name
*/
greet (name) {
return `Hello, ${name}!`
},
}

cy.stub(greeter, 'greet')
.callThrough() // if you want non-matched calls to call the real method
.withArgs(Cypress.sinon.match.string).returns('Hi')
.withArgs(Cypress.sinon.match.number).throws(new Error('Invalid name'))

expect(greeter.greet('World')).to.equal('Hi')
// @ts-ignore
expect(() => greeter.greet(42)).to.throw('Invalid name')
expect(greeter.greet).to.have.been.calledTwice

// non-matched calls goes the actual method
// @ts-ignore
expect(greeter.greet()).to.equal('Hello, undefined!')
})

it('matches call arguments using Sinon matchers', () => {
// see all possible matchers at
// https://sinonjs.org/releases/latest/matchers/
const calculator = {
/**
* returns the sum of two arguments
* @param a {number}
* @param b {number}
*/
add (a, b) {
return a + b
},
}

const spy = cy.spy(calculator, 'add').as('add')

expect(calculator.add(2, 3)).to.equal(5)

// if we want to assert the exact values used during the call
expect(spy).to.be.calledWith(2, 3)

// let's confirm "add" method was called with two numbers
expect(spy).to.be.calledWith(Cypress.sinon.match.number, Cypress.sinon.match.number)

// alternatively, provide the value to match
expect(spy).to.be.calledWith(Cypress.sinon.match(2), Cypress.sinon.match(3))

// match any value
expect(spy).to.be.calledWith(Cypress.sinon.match.any, 3)

// match any value from a list
expect(spy).to.be.calledWith(Cypress.sinon.match.in([1, 2, 3]), 3)

/**
* Returns true if the given number is event
* @param {number} x
*/
const isEven = (x) => x % 2 === 0

// expect the value to pass a custom predicate function
// the second argument to "sinon.match(predicate, message)" is
// shown if the predicate does not pass and assertion fails
expect(spy).to.be.calledWith(Cypress.sinon.match(isEven, 'isEven'), 3)

/**
* Returns a function that checks if a given number is larger than the limit
* @param {number} limit
* @returns {(x: number) => boolean}
*/
const isGreaterThan = (limit) => (x) => x > limit

/**
* Returns a function that checks if a given number is less than the limit
* @param {number} limit
* @returns {(x: number) => boolean}
*/
const isLessThan = (limit) => (x) => x < limit

// you can combine several matchers using "and", "or"
expect(spy).to.be.calledWith(
Cypress.sinon.match.number,
Cypress.sinon.match(isGreaterThan(2), '> 2').and(Cypress.sinon.match(isLessThan(4), '< 4'))
)

expect(spy).to.be.calledWith(
Cypress.sinon.match.number,
Cypress.sinon.match(isGreaterThan(200), '> 200').or(Cypress.sinon.match(3))
)

// matchers can be used from BDD assertions
cy.get('@add').should('have.been.calledWith',
Cypress.sinon.match.number, Cypress.sinon.match(3)
)

// you can alias matchers for shorter test code
const { match: M } = Cypress.sinon

cy.get('@add').should('have.been.calledWith', M.number, M(3))
})
})
74 changes: 69 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"cy:verify": "cypress verify",
"cy:info": "cypress info",
"cy:version": "cypress version",
"cy:cache:list": "cypress cache list",
"cy:run": "cypress run",
"cy:run:record": "cypress run --record",
"cy:open": "cypress open",
Expand Down Expand Up @@ -62,7 +63,7 @@
"devDependencies": {
"@bahmutov/print-env": "1.2.0",
"colon-names": "1.0.0",
"cypress": "4.1.0",
"cypress": "4.2.0",
"eslint": "5.16.0",
"eslint-plugin-cypress": "2.8.1",
"eslint-plugin-cypress-dev": "2.1.0",
Expand Down

0 comments on commit 60c6469

Please sign in to comment.