Skip to content

Commit 254d187

Browse files
Update cy.session API docs for v12 (#4851)
Co-authored-by: Emily Rohrbough <[email protected]> Closes #4507
1 parent d2dd400 commit 254d187

File tree

1 file changed

+42
-115
lines changed

1 file changed

+42
-115
lines changed

content/api/commands/session.md

Lines changed: 42 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,15 @@ cy.session(id, setup, options)
2727
**<Icon name="check-circle" color="green"></Icon> Correct Usage**
2828

2929
```javascript
30+
// Caching session when logging in via page visit
31+
cy.session(name, () => {
32+
cy.visit('/login')
33+
cy.get('[data-test=name]').type(name)
34+
cy.get('[data-test=password]').type('s3cr3t')
35+
cy.get('form').contains('Log In').click()
36+
cy.url().should('contain', '/login-successful')
37+
})
38+
3039
// Caching session when logging in via API
3140
cy.session(username, () => {
3241
cy.request({
@@ -37,15 +46,6 @@ cy.session(username, () => {
3746
window.localStorage.setItem('authToken', body.token)
3847
})
3948
})
40-
41-
// Caching session when logging in via page visit
42-
cy.session(name, () => {
43-
cy.visit('/login')
44-
cy.get('[data-test=name]').type(name)
45-
cy.get('[data-test=password]').type('s3cr3t')
46-
cy.get('form').contains('Log In').click()
47-
cy.url().should('contain', '/login-successful')
48-
})
4949
```
5050

5151
**<Icon name="exclamation-triangle" color="red"></Icon> Incorrect Usage**
@@ -98,9 +98,10 @@ serialize into an identifier, so exercise care with the data you specify.
9898

9999
This function is called whenever a session for the given `id` hasn't yet been
100100
cached, or if it's no longer valid (see the `validate` option). After `setup`
101-
runs, Cypress will preserve all cookies, `sessionStorage`, and `localStorage`,
102-
so that subsequent calls to `cy.session()` with the same `id` will bypass
103-
`setup` and just restore the cached session data.
101+
and `validate` runs for the first time, Cypress will preserve all cookies,
102+
`sessionStorage`, and `localStorage`, so that subsequent calls to `cy.session()`
103+
with the same `id` will bypass setup and just restore and validate the cached
104+
session data.
104105

105106
The page is cleared before `setup` when `testIsolation` is enabled and is not
106107
cleared when `testIsolation` is disabled.
@@ -131,13 +132,11 @@ command with a call to `cy.session()`.
131132

132133
```javascript
133134
Cypress.Commands.add('login', (username, password) => {
134-
cy.request({
135-
method: 'POST',
136-
url: '/login',
137-
body: { username, password },
138-
}).then(({ body }) => {
139-
window.localStorage.setItem('authToken', body.token)
140-
})
135+
cy.visit('/login')
136+
cy.get('[data-test=name]').type(username)
137+
cy.get('[data-test=password]').type(password)
138+
cy.get('form').contains('Log In').click()
139+
cy.url().should('contain', '/login-successful')
141140
})
142141
```
143142

@@ -146,13 +145,11 @@ Cypress.Commands.add('login', (username, password) => {
146145
```javascript
147146
Cypress.Commands.add('login', (username, password) => {
148147
cy.session([username, password], () => {
149-
cy.request({
150-
method: 'POST',
151-
url: '/login',
152-
body: { username, password },
153-
}).then(({ body }) => {
154-
window.localStorage.setItem('authToken', body.token)
155-
})
148+
cy.visit('/login')
149+
cy.get('[data-test=name]').type(username)
150+
cy.get('[data-test=password]').type(password)
151+
cy.get('form').contains('Log In').click()
152+
cy.url().should('contain', '/login-successful')
156153
})
157154
})
158155
```
@@ -164,13 +161,11 @@ Cypress.Commands.add('login', (username, password) => {
164161
cy.session(
165162
[username, password],
166163
() => {
167-
cy.request({
168-
method: 'POST',
169-
url: '/login',
170-
body: { username, password },
171-
}).then(({ body }) => {
172-
window.localStorage.setItem('authToken', body.token)
173-
})
164+
cy.visit('/login')
165+
cy.get('[data-test=name]').type(username)
166+
cy.get('[data-test=password]').type(password)
167+
cy.get('form').contains('Log In').click()
168+
cy.url().should('contain', '/login-successful')
174169
},
175170
{
176171
validate() {
@@ -227,85 +222,15 @@ const login = (name, password) => {
227222
},
228223
{
229224
validate() {
225+
// Protected URLs should return a 40x http code if user is unauthorized,
226+
// and by default this will cause cy.visit() to fail
230227
cy.visit('/account-details')
231228
},
232229
}
233230
)
234231
}
235232
```
236233

237-
### Asserting the session inside setup
238-
239-
Because `cy.session()` caches session data immediately after the `setup`
240-
function completes, it's a best practice to assert that the login process has
241-
completed at the end of session setup, to ensure that `setup` doesn't return
242-
before the session data is available to be cached.
243-
244-
Asserting sessions in this way can help simplify your login custom command, and
245-
reduce the need to
246-
[conditionally cache sessions](#Conditionally-caching-a-session).
247-
248-
```javascript
249-
cy.session('user', () => {
250-
cy.visit('/login')
251-
cy.get('[data-test=name]').type(name)
252-
cy.get('[data-test=password]').type('p4ssw0rd123')
253-
cy.get('#login').click()
254-
// Wait for the post-login redirect to ensure that the
255-
// session actually exists to be cached
256-
cy.url().should('contain', '/login-successful')
257-
})
258-
```
259-
260-
### Conditionally caching a session
261-
262-
Specs usually contain two types of tests where logins are necessary:
263-
264-
1. Testing functionality that only exists for logged-in users
265-
2. Testing the act of logging in
266-
267-
For the first, caching sessions can be incredibly useful for reducing the amount
268-
of time it takes to run tests. However, for the second, it may be necessary to
269-
_not_ cache the session, so that other things can be asserted about the login
270-
process.
271-
272-
In this case, it can be helpful to create a custom login command that will
273-
conditionally cache the session. However, wherever possible, it's better to
274-
[assert the session inside setup](#Asserting-the-session-inside-setup).
275-
276-
```javascript
277-
Cypress.Commands.add('login', (name, { cacheSession = true } = {}) => {
278-
const login = () => {
279-
cy.visit('/login')
280-
cy.get('[data-test=name]').type(name)
281-
cy.get('[data-test=password]').type('p4ssw0rd123')
282-
cy.get('#login').click()
283-
}
284-
if (cacheSession) {
285-
cy.session(name, login)
286-
} else {
287-
login()
288-
}
289-
})
290-
291-
// Testing the login flow itself
292-
describe('login', () => {
293-
it('should redirect to the correct page after logging in', () => {
294-
cy.login('user', { cacheSession: false })
295-
cy.url().should('contain', '/login-successful')
296-
})
297-
})
298-
299-
// Testing something that simply requires being logged in
300-
describe('account details', () => {
301-
it('should have the correct document title', () => {
302-
cy.login('user')
303-
cy.visit('/account')
304-
cy.title().should('eq', 'User Account Details')
305-
})
306-
})
307-
```
308-
309234
### Switching sessions inside tests
310235

311236
Because `cy.session()` clears the page and all session data before running
@@ -472,9 +397,10 @@ const loginByApi = (name, password) => {
472397

473398
### Where to call `cy.visit()`
474399

475-
If you call [`cy.visit()`](/api/commands/visit) immediately after `cy.session()`
476-
in your login function or custom command, it will effectively behave the same as
477-
a login function without any session caching.
400+
Intuitively it seems that you should call [`cy.visit()`](/api/commands/visit)
401+
immediately after `cy.session()` in your login function or custom command, so it
402+
behaves (from the point of view of the subsequent test) exactly the same as a
403+
login function without `cy.session()`.
478404

479405
```javascript
480406
const login = (name) => {
@@ -501,10 +427,11 @@ it('should test something else on the /home page', () => {
501427
})
502428
```
503429

504-
However, any time you want to test something on a different page, you will need
505-
to call `cy.visit()` at the beginning of that test, which will then be
506-
effectively calling `cy.visit()` twice in a row, which will result in slightly
507-
slower tests.
430+
However, if you want to test something on a different page, you will need to
431+
call `cy.visit()` at the beginning of that test, which means you will be calling
432+
`cy.visit()` a **second** time in your test. Since `cy.visit()` waits for the
433+
visited page to become active before continuing, this could add up to an
434+
unacceptable waste of time.
508435

509436
```javascript
510437
// ...continued...
@@ -515,10 +442,10 @@ it('should test something on the /other page', () => {
515442
})
516443
```
517444

518-
Tests will often be faster if you call `cy.visit()` only when necessary. This
519-
works especially well when
445+
Tests will obviously be faster if you call `cy.visit()` only when necessary.
446+
This can be easily realised by
520447
[organizing tests into suites](/guides/core-concepts/writing-and-organizing-tests#Test-Structure)
521-
and calling `cy.visit()` after logging in inside a
448+
and calling `cy.visit()` **after** logging in, inside a
522449
[`beforeEach`](/guides/core-concepts/writing-and-organizing-tests#Hooks) hook.
523450

524451
```javascript

0 commit comments

Comments
 (0)