Cypress request and cookies

How to use cy.request, window.fetch, and cy.task commands to make HTTP requests to the server with and without cookies

While running the Cypress tests you can make requests to the backend with full set of the page's cookies using the cy.request command. You can also add more cookies and other request headers using cy.request options. If you need to make an HTTP request without default cookies, use the window.fetch or the cy.task command.

🎁 You can find the source code for this blog post in the Server communication recipes.

Page cookies

When Cypress visits the page using cy.visit command, any cookies sent by the server via the response header Set-Cookie are set in the browser. For example, if the server sends the index page like this:

index.js
1
2
3
4
5
6
if (req.url === '/') {
res.setHeader('Content-Type', 'text/html')
res.setHeader('Set-Cookie', 'mycookie=testcookie')

return send(res, 200, '<body><h1>Hi there</h1></body>')
}

The HTML document sets the browser cookie using the response header

The Cypress test can confirm the cookies was set using the cy.getCookie command:

cypress/integration/spec.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// enables intelligent code completion for Cypress commands
// https://on.cypress.io/intelligent-code-completion
/// <reference types="cypress" />

describe('Making requests', () => {
beforeEach(() => {
cy.visit('/')
})

it('sets the test cookie', () => {
cy.getCookie('mycookie')
.should('deep.include', {
name: 'mycookie',
value: 'testcookie',
})
})
})

The test passes.

The yielded cookie object includes our expected properties

Note that the full cookie object includes other properties, but we are not interested in them for this test.

HTTP request

Let's say we want to make an HTTP call from the test to the backend as if the application made the call. The request should include the cookies from the page. We can use the cy.request command.

First, let's add an API endpoint to our server to simply print the request cookies and return them back as a JSON object.

index.js
1
2
3
4
5
if (req.url === '/print-cookies') {
console.log('=== all cookies 🍪 ===')
console.log(req.cookies)
return send(res, 200, req.cookies || {})
}

Note: I am using micro to handle the requests on the server, and micro-cookie to parse the incoming cookies.

Let's write a Cypress test to confirm the cy.request by default includes the cookies set by visiting the index page itself.

cypress/integration/spec.js
1
2
3
4
5
6
7
8
9
10
11
12
beforeEach(() => {
cy.visit('/')
})

it('sends the cookie when using cy.request', () => {
// https://on.cypress.io/request
// the endpoint /print-cookies returns the sent cookies
// back to use so we can validate
cy.request('/print-cookies')
.its('body')
.should('deep.equal', { mycookie: 'testcookie' })
})

The cy.request includes the page cookies

HTTP request with extra cookies

What if we want to include additional cookies or headers when making the request? Sure!

cypress/integration/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
beforeEach(() => {
cy.visit('/')
})

it('sends additional cookies via cy.request headers', () => {
// https://on.cypress.io/request
// can add more headers and cookies
cy.request({
url: '/print-cookies',
headers: {
'Cookie': 'cookieA=valueA; cookieB=valueB',
},
})
.its('body')
// the cookies sent to the server are the combination
// of the cookies set by the page via cy.visit
// and the cookies we sent via the Cookie header
.should('deep.equal', {
mycookie: 'testcookie',
cookieA: 'valueA',
cookieB: 'valueB',
})
})

Adding more cookies when calling cy.request

If you want to include additional headers, like Bearer when makig the request, add more headers.

window.fetch

We can use fetch function to make the request with the page cookies; the usual browser cross-original restrictions apply (that's why cy.request is so useful, as it bypasses those)

cypress/integration/spec.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
beforeEach(() => {
cy.visit('/')
})

it('fetch makes request with page cookies', () => {
// by wrapping the Promise returned by the "fetch" function
// we place it into the Cypress command chain
cy.wrap(fetch('/print-cookies'))
// to get the JSON from the response object
// need to call res.json()
// NOTE: do not use cy.invoke as it can be retried
// and the response cannot have res.json() called more than once
.then(r => r.json())
.should('deep.equal', {
mycookie: 'testcookie'
})
})

Using fetch sends page cookies

window.fetch without cookies

You can omit sending page cookies, even to the same origin.

cypress/integration/spec.js
1
2
3
4
5
6
7
8
9
10
beforeEach(() => {
cy.visit('/')
})

it('fetch without page cookies', () => {
cy.wrap(fetch('/print-cookies', { credentials: 'omit' }))
.then(r => r.json())
// no cookies were sent with the fetch request
.should('deep.equal', {})
})

Omit cookies when sending the fetch request

Request from Node

If you want to make some other HTTP request without any restrictions, you can make it from the Node environment - by using the plugin file. Let's use the popular module got to make the requests. In the plugin file:

cypress/plugins/index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/// <reference types="cypress" />
const got = require('got')

/**
* @type {Cypress.PluginConfig}
*/
// eslint-disable-next-line no-unused-vars
module.exports = (on, config) => {
on('task', {
httpRequest(params) {
console.log('making the HTTP request:')
console.log(params)

// use the "got" module to make HTTP requests
// https://github.com/sindresorhus/got#readme
return got(params).then(r => r.body)
}
})
}

Our test simply passes the explicit cookies via the header we want to send.

cypress/integration/spec.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
beforeEach(() => {
cy.visit('/')
})

it('request from Node', () => {
// makes HTTP request using the "got" module
// https://github.com/sindresorhus/got#api
cy.task('httpRequest', {
url: Cypress.config('baseUrl') + '/print-cookies',
headers: {
'Cookie': 'cookieA=valueA; cookieB=valueB',
},
responseType: 'json'
})
// only the explicit cookies are included
// in the request, no page cookies
.should('deep.equal', {
cookieA: 'valueA',
cookieB: 'valueB',
})
})

The test passes - and only sends the explicit cookies we sent when making the cy.task call. In the screenshot below I show the terminal from the Cypress process that prints the console.log statements from the plugin file.

The plugin file making the request

As the above tests show you have a choice of methods to use to make HTTP requests from Cypress tests. You can make calls using cy.request or window.fetch if you need the page cookies, with cy.request being more powerful, since it is not CORS-limited. If you need to make an absolutely arbitrary HTTP request without sending the page cookies, you can make your own call from the plugin file.