forked from cypress-io/cypress-example-recipes
-
Notifications
You must be signed in to change notification settings - Fork 0
/
control-clock-spec.js
386 lines (320 loc) · 11.7 KB
/
control-clock-spec.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
/// <reference types="Cypress" />
// This app we are testing shows a random list of
// "favorite fruits" that refreshes every 30 seconds
// The favorite fruits are refreshed every 30 seconds
// It would slow down our tests dramatically to literally
// wait that long to verify the behavior.
//
// We can use Cypress's clock and tick commands to speed it up.
//
// Since the list of fruits returned from the API are random,
// using the real server would lead to flaky tests, so we
// stub out window.fetch again in order to control the response
describe('intercept', () => {
context('slow', () => {
// NOTE: skipping because without clock control this test takes 30 seconds!
it.skip('fetches fruits every 30 seconds', () => {
cy.intercept('/favorite-fruits').as('fetchFruits')
cy.visit('/fruits.html')
// confirm the first request happens
cy.wait('@fetchFruits')
// wait 30 seconds ...
cy.wait(30000)
// confirm the second request happens
cy.wait('@fetchFruits').its('response.body')
.then((fruits) => {
cy.get('.favorite-fruits li')
.should('have.length', fruits.length)
fruits.forEach((fruit) => {
cy.contains('.favorite-fruits li', fruit)
})
})
})
})
context('clock', function () {
describe('when favorite fruits are returned', function () {
it('displays list of fruits', function () {
// https://on.cypress.io/intercept
cy.intercept('/favorite-fruits', ['Apple', 'Banana', 'Cantaloupe'])
cy.visit('/fruits.html')
cy.get('.favorite-fruits li').as('favoriteFruits')
.should('have.length', 3)
cy.get('@favoriteFruits').first()
.should('have.text', 'Apple')
cy.get('@favoriteFruits').eq(1)
.should('have.text', 'Banana')
cy.get('@favoriteFruits').eq(2)
.should('have.text', 'Cantaloupe')
})
it('does not fetch for at least five seconds', () => {
let polled
cy.intercept('/favorite-fruits', () => {
// we are not interested in the request
// we just want to know it has happened
polled = true
})
cy.visit('/fruits.html')
// at some point the request happens
// let's retry checking "polled" value until it happens
cy.wrap()
.should(() => {
expect(polled, 'fetched fruits').to.be.true
polled = false
})
// physically wait 5 seconds
cy.wait(5000)
.then(() => {
expect(polled, 'no new requests').to.be.false
})
})
it('does not fetch for at least five seconds (implicit syntax)', () => {
// we will set a flag as a property in this object
const network = {
polled: true,
}
cy.intercept('/favorite-fruits', () => {
// we are not interested in the request
// we just want to know it has happened
network.polled = true
}).as('fruits')
cy.visit('/fruits.html')
// at some point the request happens
// let's retry checking "polled" value until it becomes true
cy.wrap(network).should('have.property', 'polled', true)
// let the network call finish before we reset the property
cy.wait('@fruits')
.then(() => {
network.polled = false
})
// physically wait 5 seconds
cy.wait(5000)
// new network calls have not happened
cy.wrap(network).should('have.property', 'polled', false)
})
it('does not fetch for at least five seconds (counter)', () => {
// we will set a flag as a property in this object
const network = {
polled: 0,
}
cy.intercept('/favorite-fruits', () => {
// we are not interested in the request
// we just want to know it has happened
network.polled += 1
})
cy.visit('/fruits.html')
// at some point the request happens
// let's retry checking "polled" value until it gets value 1
cy.wrap(network).should('have.property', 'polled', 1)
// physically wait 5 seconds
cy.wait(5000)
// still the network call only happened once
cy.wrap(network).should('have.property', 'polled', 1)
})
it('does not fetch for at least five seconds (cy.spy)', () => {
cy.intercept('/favorite-fruits', cy.spy().as('reqForFruits'))
cy.visit('/fruits.html')
// at some point the request happens
cy.get('@reqForFruits').should('have.been.calledOnce')
// physically wait 5 seconds
cy.wait(5000)
// new network calls have not happened
cy.get('@reqForFruits').should('have.been.calledOnce')
})
it('does not fetch for at least five seconds (synthetic clock)', () => {
cy.clock()
let polled
cy.intercept('/favorite-fruits', () => {
// we are not interested in the request
// we just want to know it has happened
polled = true
})
cy.visit('/fruits.html')
// at some point the request happens
// let's retry checking "polled" value until it happens
cy.wrap().should(() => {
// during the visit the network call happens
expect(polled, 'fetched fruits').to.be.true
// reset it back
polled = false
})
// the test runner sleeps for 5 seconds
cy.tick(5000)
.then(() => {
// and checks the "polled" value again
expect(polled, 'no new network call').to.be.false
})
// but if we wait 25 more seconds, a network call happens again
cy.tick(25000)
cy.wrap()
.should(() => {
expect(polled, 'new fruits').to.be.true
})
})
it('fetches every 30 seconds', () => {
cy.clock()
cy.intercept('/favorite-fruits', cy.spy().as('reqForFruits'))
cy.visit('/fruits.html')
// at some point the request happens
cy.get('@reqForFruits').should('have.been.calledOnce')
cy.tick(5000)
// no new network calls
cy.get('@reqForFruits').should('have.been.calledOnce')
// but add 25 more seconds, and the app should have made a network call
cy.tick(25000)
cy.get('@reqForFruits').should('have.been.calledTwice')
})
})
describe('polling every 30 secs', function () {
it('fetches from the server (spies)', () => {
cy.log('**start**')
cy.clock()
cy.intercept('GET', '/favorite-fruits').as('fruits')
cy.visit('/fruits.html')
// first call
cy.wait('@fruits').its('response.statusCode').should('equal', 200)
// 30 seconds passes and the application fetches again
cy.log('**30 seconds**')
cy.tick(30000)
cy.wait('@fruits').its('response.statusCode').should('equal', 200)
// 3rd call
cy.log('**60 seconds**')
cy.tick(30000)
cy.wait('@fruits').its('response.statusCode').should('equal', 200)
// 4th call
cy.log('**90 seconds**')
cy.tick(30000)
cy.wait('@fruits').its('response.statusCode').should('equal', 200)
// 5th call
cy.log('**2 minutes**')
cy.tick(30000)
cy.wait('@fruits').its('response.statusCode').should('equal', 200)
// confirm the displayed fruits
cy.get('@fruits').its('response.body')
.then((fruits) => {
expect(fruits).to.be.an('array')
cy.get('.favorite-fruits li')
.should('have.length', fruits.length)
fruits.forEach((fruit, k) => {
cy.log(`${k + 1}: ${fruit}`)
cy.contains('.favorite-fruits li', fruit)
})
})
})
it('returns different fruits every 30 seconds', () => {
cy.clock()
let k = 0
// return difference responses on each call
cy.intercept('/favorite-fruits', (req) => {
k += 1
switch (k) {
case 1:
return req.reply(['apples 🍎'])
case 2:
return req.reply(['grapes 🍇'])
default:
return req.reply(['kiwi 🥝'])
}
})
cy.visit('/fruits.html')
cy.contains('apples 🍎')
cy.tick(30000)
cy.contains('grapes 🍇')
cy.tick(30000)
cy.contains('kiwi 🥝')
})
it('returns different fruits every 30 seconds (array shift)', () => {
cy.clock()
// return difference responses on each call
const responses = [
['apples 🍎'], ['grapes 🍇'],
]
cy.intercept('/favorite-fruits', (req) => {
req.reply(responses.shift() || ['kiwi 🥝'])
})
cy.visit('/fruits.html')
cy.contains('apples 🍎')
cy.tick(30000)
cy.contains('grapes 🍇')
cy.tick(30000)
cy.contains('kiwi 🥝')
})
it('returns different fruits every 30 seconds (slow down)', () => {
cy.clock()
// return difference responses on each call
const responses = [
['apples 🍎'], ['grapes 🍇'],
]
cy.intercept('/favorite-fruits', (req) => {
req.reply(responses.shift() || ['kiwi 🥝'])
})
cy.visit('/fruits.html')
cy.contains('apples 🍎')
// slow down the test on purpose to be able to see
// the rendered page at each step
cy.wait(1000)
cy.tick(30000)
cy.contains('grapes 🍇')
cy.wait(1000)
cy.tick(30000)
cy.contains('kiwi 🥝')
})
it('returns different fruits every 30 seconds (slow down reply)', () => {
cy.clock()
// return difference responses on each call
const responses = [
['apples 🍎'], ['grapes 🍇'],
]
cy.intercept('/favorite-fruits', (req) => {
const value = responses.shift() || ['kiwi 🥝']
// wait 500ms then reply with the fruit
// simulates the server taking half a second to respond
return Cypress.Promise.delay(500, value).then(req.reply)
})
cy.visit('/fruits.html')
cy.contains('apples 🍎')
// slow down the test on purpose to be able to see
// the rendered page at each step
cy.wait(1000)
cy.tick(30000)
cy.contains('grapes 🍇')
cy.wait(1000)
cy.tick(30000)
cy.contains('kiwi 🥝')
})
it('displays the new list of fruits (stubs)', () => {
cy.clock()
// first request - respond with 3 fruits
// second request - respond with 4 fruits
let k = 0
const firstList = ['Apple', 'Banana', 'Cantaloupe']
const secondList = ['Orange', 'Cherry', 'Raspberry', 'Pineapple']
cy.intercept('/favorite-fruits', (req) => {
k += 1
if (k === 1) {
req.reply(firstList)
} else {
req.reply(secondList)
}
})
cy.visit('/fruits.html')
cy.get('.favorite-fruits li').as('favoriteFruits')
// initial list of fruits is shown
cy.get('@favoriteFruits').should('have.length', firstList.length)
firstList.forEach((fruit, j) => {
cy.get('@favoriteFruits').eq(j)
.should('have.text', firstList[j])
})
// move time 30 seconds and the setInterval will be triggered
// that polls for the fruit
cy.tick(30000)
// make sure the updated list is shown
cy.get('@favoriteFruits')
.should('have.length', secondList.length)
secondList.forEach((fruit, j) => {
cy.get('@favoriteFruits').eq(j)
.should('have.text', secondList[j])
})
})
})
})
})