Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Option to not flush cookies between tests #959

Closed
igorpavlov-zz opened this issue Nov 26, 2017 · 7 comments
Closed

Option to not flush cookies between tests #959

igorpavlov-zz opened this issue Nov 26, 2017 · 7 comments

Comments

@igorpavlov-zz
Copy link

igorpavlov-zz commented Nov 26, 2017

  • Operating System: OSX 10.12.6 (16G1036)
  • Cypress Version: 1.1.1
  • Browser Version: 62.0.3202.94

Is this a Feature or Bug?

Unexpected bahaviour.

Current behavior:

Cookies are automatically flushed between tests (as per the documentation).

image

Desired behavior:

Have an option to not flush cookies between tests for more control.

How to reproduce:

  1. Write a first test that sets a cookie to the domain;
  2. Write a second test that should utilise this cookie;
  3. The cookie from the first test is flushed and is not available in the second test.

Test code:

describe('Set cookie', function() {
  it('Sets cookie', function() {
    cy.setCookie('test', '1');
  });
});

describe('Get cookie', function() {
  it('Gets cookie', function() {
    cy.getCookie('test').should('have.property', 'value', '1')
  });
});
@igorpavlov-zz igorpavlov-zz changed the title Option to not flushing cookies between tests Option to not flush cookies between tests Nov 26, 2017
@brian-mann
Copy link
Member

There is an API to preserve cookies in between tests: https://docs.cypress.io/api/cypress-api/cookies.html

I'm closing this issue in favor of this one (below) because it goes in length to explain why what we do by default is a bad idea and what needs to change. It's a priority but its underneath other things unfortunately.

#686

@xiangweilee
Copy link

This will preserve cookies in every test

beforeEach(() => {
  // Preserve cookie in every test
  Cypress.Cookies.defaults({
    whitelist: (cookie) => {
      return true;
    }
  })
});

@trentrand
Copy link

For those arriving from the future, starting in version 5.0.0 the Cypress.Cookies.defaults() whitelist option has been renamed to preserve to more closely reflect its behavior. Addressed in #7782.

https://docs.cypress.io/guides/references/changelog.html#5-0-0

@pgarciacamou
Copy link

pgarciacamou commented Apr 28, 2021

I ended up using Cypress.Cookies.preserveOnce instead. Unfortunately, it doesn't take in a function or a RegExp like Cypress.Cookies.defaults does, but I managed to work around it:

beforeEach(function () {
  cy.getCookies().then(cookies => {
    const namesOfCookies = cookies.map(c => c.name)
    Cypress.Cookies.preserveOnce(...namesOfCookies)
  })
})

Hopefully, this will help someone else.


Power Up

You can also create a command if you are re-using this.

// commands.js
Cypress.Commands.add('preserveAllCookiesOnce', () => {
  cy.getCookies().then(cookies => {
    const namesOfCookies = cookies.map(c => c.name)
    Cypress.Cookies.preserveOnce(...namesOfCookies)
  })
})

// your-suite.test.js
beforeEach(function () {
  cy.preserveAllCookiesOnce()
});

Explanation

The problem with Cypress.Cookies.defaults, like in

This will preserve cookies in every test

beforeEach(() => {
  // Preserve cookie in every test
  Cypress.Cookies.defaults({
    preserve: (cookie) => {
      return true;
    }
  })
});

see this comment

Is that according to the docs (https://docs.cypress.io/api/cypress-api/cookies#Defaults), Cypress.Cookies.defaults will preserve all cookies for the rest of the execution of all tests.

You can modify the global defaults and preserve a set of Cookies which will always be preserved across tests.
Any change you make here will take effect immediately for the remainder of every single test.
A great place to put this configuration is in your cypress/support/index.js file, since it is loaded before any test files are evaluated.

In other words, using Cypress.Cookies.defaults at the beforeEach level is wrong. And what is worse, it will affect the rest of the tests (all of them).

@ganeshsirsi
Copy link

ganeshsirsi commented Jun 10, 2021

You can also look into this code, This reads and preserves all cookies, If you are not particular about something you can use this
https://ganeshsirsi.medium.com/cypress-how-to-preserve-cookies-and-keep-login-session-active-in-each-test-simplest-way-717bbc11adf7

@mekalag
Copy link

mekalag commented Apr 6, 2022

Guys, we just started writing our automation in cypress, we will most require to store cookies to be used in all the spec files except for one or two specs say for ex: when testing authentication section.

So added :

Cypress.Cookies.defaults({
  preserve: ['auth', 'TOKEN'],
});

in cypress/support/index.js file.

How can I exclude few files to not use cookies? Please suggest

@pgarciacamou
Copy link

pgarciacamou commented Jan 6, 2023

@mekalag that's separate to this issue but I wanted to add a suggestion in case it helps other people end up in this thread looking for an answer to your question.

One userland idea I had is to clear the cookies for the command you need to run and then restoring them:

/**
 * Clears cookies before executing a callback and then restores the cookies.
 * @param {Function} callback
 * @param {{ domain, log, timeout }} options https://docs.cypress.io/api/commands/getcookies#Arguments
 */
Cypress.Commands.add('ignoreCookiesOnce', (callback, options) => {
  return cy.getCookies(options).then(cookies => {
    // Clear cookies
    cy.clearCookies(options)

    // Execute callback
    callback()

    // Restore cookies
    cookies.forEach(({name, value, ...rest}) => {
      cy.setCookie(name, value, rest)
    })
  })
})

Then you could do:

cy.ignoreCookiesOnce(
  () => {
    cy.request({
      url: 'https://...',
      method: 'POST',
      headers: {
        Cookie: 'whateveryouwant'
      },
      body: `...`
    }).then(({body}) => body)
  },
  {domain: 'yourdomain.com'} // optional
)

Hope this helps 👍


You can also be much more flexible if you need to pass specific cookies to clear:

/**
 * Clears cookies before executing a callback and then restores the cookies.
 * Usage:
 *  1. cy.ignoreCookiesOnce(() => cy.visit(...))
 *  2. cy.ignoreCookiesOnce(() => cy.request(...), { domain: 'yourdomain.com'})
 *  3. cy.ignoreCookiesOnce(() => cy.visit(...), { cookies: ['foo', 'bar']})
 *  4. cy.ignoreCookiesOnce(() => cy.request(...), { except: ['foo', 'bar']})
 * @param {Function} callback
 * @param {{ cookies: Array, except: Array, domain: string, log, timeout }} options See: https://docs.cypress.io/api/commands/getcookies#Arguments
 */
Cypress.Commands.add('ignoreCookiesOnce', (callback, options = {}) => {
  Cypress.Cookies.debug(true, {verbose: true})
  let {cookies, except, ...cyOptions} = options

  return cy.getCookies(cyOptions).then((all = []) => {
    // Obtain cookies from options
    if (Array.isArray(cookies) && cookies.length > 0) {
      cookies = cookies.map(n => all.find(c => c.name === n))
    } else if (Array.isArray(except) && except.length > 0) {
      cookies = all.filter(c => !except.includes(c.name))
    } else {
      cookies = all
    }

    // Clear cookies
    if (cookies === all) {
      cy.clearCookies(cyOptions) // clear all
    } else {
      cookies.forEach(c => cy.clearCookie(c.name, cyOptions))
    }

    // Execute callback
    callback()

    // Restore cookies
    cookies.forEach(({name, value, ...rest}) => {
      cy.setCookie(name, value, rest)
    })
  })
})

I also added this answer to stackoverflow: https://stackoverflow.com/a/75035723/4984618.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants