Skip to content

Commit 60c6469

Browse files
authored
feat: add examples for sinon.match (#385)
* 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
1 parent e9769d8 commit 60c6469

File tree

5 files changed

+279
-8
lines changed

5 files changed

+279
-8
lines changed

app/commands/spies-stubs-clocks.html

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,99 @@ <h4><a href="https://on.cypress.io/tick">cy.tick()</a></h4>
169169
</div>
170170
</div>
171171
</div>
172+
173+
<div class="col-xs-12"><hr></div>
174+
<div class="col-xs-7">
175+
<h4>cy.stub() matches depending on arguments</h4>
176+
<p>See all possible matchers at <a href="https://sinonjs.org/releases/latest/matchers/">Sinonjs.org</a></p>
177+
<pre><code class="javascript">const greeter = {
178+
/**
179+
* Greets a person
180+
* @param {string} name
181+
*/
182+
greet (name) {
183+
return `Hello, ${name}!`
184+
},
185+
}
186+
187+
cy.stub(greeter, 'greet')
188+
.callThrough() // if you want non-matched calls to call the real method
189+
.withArgs(Cypress.sinon.match.string).returns('Hi')
190+
.withArgs(Cypress.sinon.match.number).throws(new Error('Invalid name'))
191+
192+
expect(greeter.greet('World')).to.equal('Hi')
193+
expect(() => greeter.greet(42)).to.throw('Invalid name')
194+
expect(greeter.greet).to.have.been.calledTwice
195+
196+
// non-matched calls goes the actual method
197+
expect(greeter.greet()).to.equal('Hello, undefined!')</code></pre>
198+
</div>
199+
200+
<div class="col-xs-12"><hr></div>
201+
<div class="col-xs-7">
202+
<h4>matches call arguments using Sinon matchers</h4>
203+
<p>See all possible matchers at <a href="https://sinonjs.org/releases/latest/matchers/">Sinonjs.org</a></p>
204+
<pre><code class="javascript">const calculator = {
205+
/**
206+
* returns the sum of two arguments
207+
* @param a {number}
208+
* @param b {number}
209+
*/
210+
add (a, b) {
211+
return a + b
212+
},
213+
}
214+
215+
const spy = cy.spy(calculator, 'add').as('add')
216+
217+
expect(calculator.add(2, 3)).to.equal(5)
218+
219+
// if we want to assert the exact values used during the call
220+
expect(spy).to.be.calledWith(2, 3)
221+
222+
// let's confirm "add" method was called with two numbers
223+
expect(spy).to.be.calledWith(Cypress.sinon.match.number, Cypress.sinon.match.number)
224+
225+
// alternatively, provide the value to match
226+
expect(spy).to.be.calledWith(Cypress.sinon.match(2), Cypress.sinon.match(3))
227+
228+
// match any value
229+
expect(spy).to.be.calledWith(Cypress.sinon.match.any, 3)
230+
231+
// match any value from a list
232+
expect(spy).to.be.calledWith(Cypress.sinon.match.in([1, 2, 3]), 3)
233+
234+
// expect the value to pass a custom predicate function
235+
// the second argument to "sinon.match(predicate, message)" is
236+
// shown if the predicate does not pass and assertion fails
237+
const isEven = (x) => x % 2 === 0
238+
239+
expect(spy).to.be.calledWith(Cypress.sinon.match(isEven, 'isEven'), 3)
240+
241+
// you can combine several matchers using "and", "or"
242+
const isGreaterThan = (limit) => (x) => x > limit
243+
const isLessThan = (limit) => (x) => x < limit
244+
245+
expect(spy).to.be.calledWith(
246+
Cypress.sinon.match.number,
247+
Cypress.sinon.match(
248+
isGreaterThan(2), '> 2').and(Cypress.sinon.match(isLessThan(4), '< 4'))
249+
)
250+
251+
expect(spy).to.be.calledWith(
252+
Cypress.sinon.match.number,
253+
Cypress.sinon.match(isGreaterThan(200), '> 200').or(Cypress.sinon.match(3))
254+
)
255+
256+
// matchers can be used from BDD assertions
257+
cy.get('@add').should('have.been.calledWith',
258+
Cypress.sinon.match.number, Cypress.sinon.match(3)
259+
)
260+
261+
// you can alias matchers for shorter test code
262+
const { match: M } = Cypress.sinon
263+
264+
cy.get('@add').should('have.been.calledWith', M.number, M(3))</code></pre>
172265
</div>
173266
</div>
174267
</div>

appveyor.yml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@ install:
2121

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

2729
# Post-install test scripts.
2830
test_script:
@@ -31,6 +33,7 @@ test_script:
3133
- run-if npm run cy:version
3234
- run-if npm run cy:verify
3335
- run-if npm run cy:info
36+
- run-if npm run cy:cache:list
3437
- run-if npm run test:ci:record:windows
3538
- run-if npm run test:ci:record:windows:edge
3639

cypress/integration/examples/spies_stubs_clocks.spec.js

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
/// <reference types="cypress" />
2+
// remove no check once Cypress.sinon is typed
3+
// https://github.com/cypress-io/cypress/issues/6720
24

35
context('Spies, Stubs, and Clock', () => {
46
it('cy.spy() - wrap a method in a spy', () => {
@@ -94,4 +96,112 @@ context('Spies, Stubs, and Clock', () => {
9496
cy.get('#tick-div').click()
9597
.should('have.text', '1489449610')
9698
})
99+
100+
it('cy.stub() matches depending on arguments', () => {
101+
// see all possible matchers at
102+
// https://sinonjs.org/releases/latest/matchers/
103+
const greeter = {
104+
/**
105+
* Greets a person
106+
* @param {string} name
107+
*/
108+
greet (name) {
109+
return `Hello, ${name}!`
110+
},
111+
}
112+
113+
cy.stub(greeter, 'greet')
114+
.callThrough() // if you want non-matched calls to call the real method
115+
.withArgs(Cypress.sinon.match.string).returns('Hi')
116+
.withArgs(Cypress.sinon.match.number).throws(new Error('Invalid name'))
117+
118+
expect(greeter.greet('World')).to.equal('Hi')
119+
// @ts-ignore
120+
expect(() => greeter.greet(42)).to.throw('Invalid name')
121+
expect(greeter.greet).to.have.been.calledTwice
122+
123+
// non-matched calls goes the actual method
124+
// @ts-ignore
125+
expect(greeter.greet()).to.equal('Hello, undefined!')
126+
})
127+
128+
it('matches call arguments using Sinon matchers', () => {
129+
// see all possible matchers at
130+
// https://sinonjs.org/releases/latest/matchers/
131+
const calculator = {
132+
/**
133+
* returns the sum of two arguments
134+
* @param a {number}
135+
* @param b {number}
136+
*/
137+
add (a, b) {
138+
return a + b
139+
},
140+
}
141+
142+
const spy = cy.spy(calculator, 'add').as('add')
143+
144+
expect(calculator.add(2, 3)).to.equal(5)
145+
146+
// if we want to assert the exact values used during the call
147+
expect(spy).to.be.calledWith(2, 3)
148+
149+
// let's confirm "add" method was called with two numbers
150+
expect(spy).to.be.calledWith(Cypress.sinon.match.number, Cypress.sinon.match.number)
151+
152+
// alternatively, provide the value to match
153+
expect(spy).to.be.calledWith(Cypress.sinon.match(2), Cypress.sinon.match(3))
154+
155+
// match any value
156+
expect(spy).to.be.calledWith(Cypress.sinon.match.any, 3)
157+
158+
// match any value from a list
159+
expect(spy).to.be.calledWith(Cypress.sinon.match.in([1, 2, 3]), 3)
160+
161+
/**
162+
* Returns true if the given number is event
163+
* @param {number} x
164+
*/
165+
const isEven = (x) => x % 2 === 0
166+
167+
// expect the value to pass a custom predicate function
168+
// the second argument to "sinon.match(predicate, message)" is
169+
// shown if the predicate does not pass and assertion fails
170+
expect(spy).to.be.calledWith(Cypress.sinon.match(isEven, 'isEven'), 3)
171+
172+
/**
173+
* Returns a function that checks if a given number is larger than the limit
174+
* @param {number} limit
175+
* @returns {(x: number) => boolean}
176+
*/
177+
const isGreaterThan = (limit) => (x) => x > limit
178+
179+
/**
180+
* Returns a function that checks if a given number is less than the limit
181+
* @param {number} limit
182+
* @returns {(x: number) => boolean}
183+
*/
184+
const isLessThan = (limit) => (x) => x < limit
185+
186+
// you can combine several matchers using "and", "or"
187+
expect(spy).to.be.calledWith(
188+
Cypress.sinon.match.number,
189+
Cypress.sinon.match(isGreaterThan(2), '> 2').and(Cypress.sinon.match(isLessThan(4), '< 4'))
190+
)
191+
192+
expect(spy).to.be.calledWith(
193+
Cypress.sinon.match.number,
194+
Cypress.sinon.match(isGreaterThan(200), '> 200').or(Cypress.sinon.match(3))
195+
)
196+
197+
// matchers can be used from BDD assertions
198+
cy.get('@add').should('have.been.calledWith',
199+
Cypress.sinon.match.number, Cypress.sinon.match(3)
200+
)
201+
202+
// you can alias matchers for shorter test code
203+
const { match: M } = Cypress.sinon
204+
205+
cy.get('@add').should('have.been.calledWith', M.number, M(3))
206+
})
97207
})

package-lock.json

Lines changed: 69 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
"cy:verify": "cypress verify",
3535
"cy:info": "cypress info",
3636
"cy:version": "cypress version",
37+
"cy:cache:list": "cypress cache list",
3738
"cy:run": "cypress run",
3839
"cy:run:record": "cypress run --record",
3940
"cy:open": "cypress open",
@@ -62,7 +63,7 @@
6263
"devDependencies": {
6364
"@bahmutov/print-env": "1.2.0",
6465
"colon-names": "1.0.0",
65-
"cypress": "4.1.0",
66+
"cypress": "4.2.0",
6667
"eslint": "5.16.0",
6768
"eslint-plugin-cypress": "2.8.1",
6869
"eslint-plugin-cypress-dev": "2.1.0",

0 commit comments

Comments
 (0)