Skip to content
This repository was archived by the owner on Feb 28, 2023. It is now read-only.

Commit 87e35a5

Browse files
authored
add example stubbing method on load (#253)
1 parent e2d372a commit 87e35a5

File tree

12 files changed

+414
-27
lines changed

12 files changed

+414
-27
lines changed

.circleci/config.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,8 @@ jobs:
237237
folder: "12-custom-commands"
238238
- test-answers:
239239
folder: "14-fixtures"
240+
- test-answers:
241+
folder: "20-stubbing"
240242

241243
# For section "13-app-actions" we need to start React application
242244
- run: ps -a

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ See the presentation at [https://testing-workshop-cypress.netlify.app/][presenta
116116
| [🔗](#component-testing) | Component testing | [17-component-testing](cypress/integration/17-component-testing) | [17-component-testing](slides/17-component-testing/PITCHME.md) | [link](https://testing-workshop-cypress.netlify.app?p=17-component-testing)
117117
| [🔗](#backend) | Backend code | [18-backend](cypress/integration/18-backend) | [18-backend](slides/18-backend/PITCHME.md) | [link](https://testing-workshop-cypress.netlify.app?p=18-backend)
118118
| [🔗](#code-coverage) | Code coverage | [19-code-coverage](cypress/integration/19-code-coverage) | [19-code-coverage](slides/19-code-coverage/PITCHME.md) | [link](https://testing-workshop-cypress.netlify.app?p=19-code-coverage)
119+
| | Stubbing methods | [20-stubbing](./cypress/integration/20-stubbing) | [20-stubbing](./slides/20-stubbing/PITCHME.md) | [link](https://testing-workshop-cypress.netlify.app?p=20-stubbing)
119120
| | The end | - | [end](slides/end/PITCHME.md) | [link](https://testing-workshop-cypress.netlify.app?p=end)
120121

121122
## For speakers 🎙
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
/// <reference types="cypress" />
2+
import { enterTodo, resetData, removeTodo } from '../../support/utils'
3+
4+
describe('Stubbing window.track', () => {
5+
beforeEach(resetData)
6+
7+
it('works on click', () => {
8+
cy.visit('/').then((win) => {
9+
cy.stub(win, 'track').as('track')
10+
})
11+
enterTodo('write code')
12+
cy.get('@track').should(
13+
'have.been.calledOnceWithExactly',
14+
'todo.add',
15+
'write code'
16+
)
17+
})
18+
19+
it('tracks item delete', () => {
20+
cy.visit('/').then((win) => {
21+
cy.stub(win, 'track').as('track')
22+
})
23+
enterTodo('write code')
24+
removeTodo('write code')
25+
26+
cy.get('@track').should('have.been.calledWith', 'todo.remove', 'write code')
27+
})
28+
29+
it('stops working if window changes', () => {
30+
cy.visit('/').then((win) => {
31+
cy.stub(win, 'track').as('track')
32+
})
33+
34+
enterTodo('write code')
35+
cy.get('@track').should('be.calledOnce')
36+
37+
cy.reload()
38+
enterTodo('write tests')
39+
// note that our stub was still called once
40+
// meaning the second todo was never counted
41+
cy.get('@track').should('be.calledOnce')
42+
})
43+
44+
it.only('adds stub after reload', () => {
45+
const trackStub = cy.stub().as('track')
46+
47+
cy.visit('/').then((win) => {
48+
cy.stub(win, 'track').callsFake(trackStub)
49+
})
50+
51+
enterTodo('write code')
52+
cy.get('@track').should('be.calledOnce')
53+
54+
cy.reload().then((win) => {
55+
cy.stub(win, 'track').callsFake(trackStub)
56+
})
57+
enterTodo('write tests')
58+
// our stub is called twice: the second time
59+
// from the new window object after cy.reload()
60+
cy.get('@track').should('be.calledTwice')
61+
})
62+
63+
it('works on load', () => {
64+
cy.visit('/', {
65+
onBeforeLoad(win) {
66+
// there is no "window.track" yet,
67+
// thus we cannot stub just yet
68+
let track // the real track when set by the app
69+
let trackStub // our stub around the real track
70+
71+
Object.defineProperty(win, 'track', {
72+
get() {
73+
return trackStub
74+
},
75+
set(fn) {
76+
track = fn
77+
// give the created stub an alias so we can retrieve it later
78+
trackStub = cy.stub().callsFake(track).as('track')
79+
}
80+
})
81+
}
82+
})
83+
84+
// make sure the page called the "window.track" with expected arguments
85+
cy.get('@track').should('have.been.calledOnceWith', 'window.load')
86+
})
87+
88+
it('works via event handler', () => {
89+
// there is no "window.track" yet,
90+
// thus we cannot stub just yet
91+
let track // the real track when set by the app
92+
let trackStub // our stub around the real track
93+
94+
// use "cy.on" to prepare for "window.track" assignment
95+
// this code runs for every window creation, thus we
96+
// can track events from the "cy.reload()"
97+
cy.on('window:before:load', (win) => {
98+
Object.defineProperty(win, 'track', {
99+
get() {
100+
return trackStub
101+
},
102+
set(fn) {
103+
// if the stub does not exist yet, create it
104+
if (!track) {
105+
track = fn
106+
// give the created stub an alias so we can retrieve it later
107+
trackStub = cy.stub().callsFake(track).as('track')
108+
}
109+
}
110+
})
111+
})
112+
113+
cy.visit('/')
114+
115+
// make sure the page called the "window.track" with expected arguments
116+
cy.get('@track').should('have.been.calledOnceWith', 'window.load')
117+
118+
cy.reload()
119+
cy.reload()
120+
121+
cy.get('@track')
122+
.should('have.been.calledThrice')
123+
// confirm every call was with "window.load" argument
124+
.invoke('getCalls')
125+
.should((calls) => {
126+
calls.forEach((trackCall, k) => {
127+
expect(trackCall.args, `call ${k + 1}`).to.deep.equal(['window.load'])
128+
})
129+
})
130+
})
131+
})
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/// <reference types="cypress" />
2+
/* eslint-disable-next-line no-unused-vars */
3+
import { enterTodo, resetData, removeTodo } from '../../support/utils'
4+
5+
describe('Stubbing window.track', () => {
6+
beforeEach(resetData)
7+
8+
// by default the "window.track" is called by the app
9+
// on load, on new todo, and on removing todo
10+
// and just prints the message using console.log
11+
12+
it('works on click', () => {
13+
// visit the page
14+
// stub "window.track"
15+
// enter new todo
16+
// confirm the stub "window.track" was called once
17+
// with expected argument
18+
// tip: use 'have.been.calledOnceWithExactly' assertion
19+
})
20+
21+
it('tracks item delete', () => {
22+
// visit the page
23+
// stub "window.track"
24+
// enter and remove new todo
25+
// assert the stub "window.track" was called
26+
// with expected arguments
27+
})
28+
29+
it('adds stub after reload', () => {
30+
// create a single stub with
31+
// const trackStub = cy.stub().as('track')
32+
// stub the window.track after cy.visit
33+
// and after reload
34+
// and then count the number of calls
35+
})
36+
37+
it('works on load', () => {
38+
// set up the stub when the window object exists
39+
// but before any code loads
40+
// see https://on.cypress.io/visit onBeforeLoad
41+
// use Object.defineProperty(win, 'track', {...}) to
42+
// get the "window.track = fn" assignment and call
43+
// the cy.stub wrapping the fn
44+
// after the visit command confirm the stub was called
45+
})
46+
47+
it('works via event handler', () => {
48+
// need to return the same stub when using cy.visit
49+
// and cy.reload calls that create new "window" objects
50+
// tip: use the cy.on('window:before:load', ...) event listener
51+
// which is called during cy.visit and during cy.reload
52+
// during the test reload the page several times, then check
53+
// the right number of "window.track" calls was made
54+
})
55+
})

cypress/support/utils.js

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,10 +92,7 @@ export const getNewTodoInput = () => getTodoApp().find('.new-todo')
9292
* enterTodo('my todo')
9393
*/
9494
export const enterTodo = (text = 'example todo') => {
95-
console.log('entering todo', text)
96-
9795
getNewTodoInput().type(`${text}{enter}`)
98-
console.log('typed', text)
9996

10097
// we need to make sure the store and the vue component
10198
// get updated and the DOM is updated.
@@ -106,6 +103,16 @@ export const enterTodo = (text = 'example todo') => {
106103
cy.get(lastItem).should('contain', text)
107104
}
108105

106+
/**
107+
* Removes the given todo by text
108+
* @param {string} text The todo to find and remove
109+
*/
110+
export const removeTodo = (text) => {
111+
cy.contains('.todoapp .todo-list li', text)
112+
.find('.destroy')
113+
.click({ force: true })
114+
}
115+
109116
// a couple of aliases for 12-custom-commands answers
110117
export const resetData = resetDatabase
111118
export const visitSite = () => visit(true)

main.js

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,17 @@
1+
import 'reveal.js/dist/reset.css'
12
import 'reveal.js/dist/reveal.css'
23
import 'reveal.js/dist/theme/league.css'
4+
5+
// pick code block syntax highlighting theme
6+
// included with Reveal.js are monokai and zenburn
7+
// import 'reveal.js/plugin/highlight/monokai.css'
8+
// more themes from Highlight.js
9+
// import 'highlight.js/styles/purebasic.css'
10+
import 'highlight.js/styles/docco.css'
11+
312
import Reveal from 'reveal.js'
413
import Markdown from 'reveal.js/plugin/markdown/markdown.esm.js'
14+
import RevealHighlight from 'reveal.js/plugin/highlight/highlight.esm.js'
515

616
// something like
717
// {BASE_URL: "/reveal-markdown-example/", MODE: "development", DEV: true, PROD: false, SSR: false}
@@ -51,7 +61,19 @@ fetch(markdownFilename)
5161
'</section>\n'
5262

5363
const deck = new Reveal({
54-
plugins: [Markdown],
64+
plugins: [Markdown, RevealHighlight]
65+
})
66+
deck.initialize({
67+
// presentation sizing config
68+
width: 1280,
69+
height: 720,
70+
minScale: 0.2,
71+
maxScale: 1.1,
72+
// show the slide number on the page
73+
// and in the hash fragment and
74+
// make sure they are the same
75+
slideNumber: true,
76+
hash: true,
77+
hashOneBasedIndex: true
5578
})
56-
deck.initialize({ slideNumber: true, minScale: 0.2, maxScale: 1.1 })
5779
})

package-lock.json

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

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
"eslint-config-prettier": "6.7.0",
5656
"eslint-plugin-cypress": "2.11.2",
5757
"eslint-plugin-prettier": "3.1.2",
58+
"highlight.js": "10.7.1",
5859
"http-server": "0.12.0",
5960
"istanbul-lib-coverage": "3.0.0",
6061
"mocha": "6.2.2",

0 commit comments

Comments
 (0)