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

Add option to allow LocalStorage entries to persist between tests #461

Closed
carlos-granados opened this issue Mar 15, 2017 · 46 comments
Closed
Labels
type: feature New feature that does not currently exist v12.0.0 🐛

Comments

@carlos-granados
Copy link

Similar to the mechanism available to whitelist cookies so that these entries will not be cleared between test

@jennifer-shehane jennifer-shehane added the type: feature New feature that does not currently exist label Mar 16, 2017
@tjhillard
Copy link

👍 Would a be nice feature!

@ashercoren
Copy link

Yes, please!

@ZwaarContrast
Copy link

Yes, that would really help us with our application which uses cognito by AWS as a authentication provider (which under the hood uses localstorage)

@brian-mann
Copy link
Member

brian-mann commented Aug 28, 2017

You can achieve this manually right now because the method thats clear local storage is publicly exposed as Cypress.LocalStorage.clear.

You can backup this method and override it based on the keys sent in.

const clear = Cypress.LocalStorage.clear

Cypress.LocalStorage.clear = function (keys, ls, rs) {
  // do something with the keys here
  if (keys) {
    return clear.apply(this, arguments)
  }
}

@jurom
Copy link

jurom commented Sep 20, 2017

@brian-mann I had similar problem and this worked, thanks ! However, I assume that there's a typo and it should've been

Cypress.LocalStorage.clear = function (....

@jennifer-shehane
Copy link
Member

@jurom Thanks, this has been fixed in brian's example.

@lacroixdavid1
Copy link

I've tried your example @brian-mann but i get empty keys array on every call. Any ideas why ?

@brian-mann
Copy link
Member

@lacroixdavid1 then it sounds like you don't have anything in localStorage. Are you sure you're not storing it in SessionStorage? You don't have to guess - just use Dev Tools Application Tab and it'll show you everything that's being stored.

@lacroixdavid1
Copy link

@brian-mann it is really stored in localstorage with key jStorage

@lacroixdavid1
Copy link

lacroixdavid1 commented Jan 11, 2018

const clear = Cypress.LocalStorage.clear;
Cypress.LocalStorage.clear = function (keys, ls, rs) {
  // do something with the keys here
  if (keys) {
    debugger;
    // return clear.apply(this, arguments)
  }
}

This doesn't clear my localStorage, but i need to filter keys.

@brian-mann
Copy link
Member

Put the debugger outside of the if statement. Just look through the stack trace and you can walk back to what Cypress is doing. It's open source so you might have to do some digging. Cypress is just using standard JS API's - no magic here. It should all be transparent.

@lacroixdavid1
Copy link

lacroixdavid1 commented Jan 11, 2018

In /packages/driver/src/cypress/local_storage.coffee in method clear: (keys, local, remote) :

      .each (item) =>
        if keys.length
          @_ifItemMatchesAnyKey item, keys, (key) =>
            @_removeItem(storage, key)
        else
          @_removeItem(storage, item)

My localStorage gets fully cleared as key.length is 0 on every call.
So, as I can see, because no keys are sent to the methods, it clears whole storage.

Also, in packages/driver/src/cy/commands/local_storage.coffee in method clearLocalStorage = (state, keys), keys is always empty, but local and remote have values.

Why this ? because of the following :

  Cypress.prependListener "test:before:run", ->
    try
      ## this may fail if the current
      ## window is bound to another origin
      clearLocalStorage(state, [])
    catch
      null

you can clearly see that method clearLocalStorage called before each test doesn't get passed any keys.

@brian-mann am I missing something? is there anything broken?

@lacroixdavid1
Copy link

@brian-mann any suggestions ?

@lacroixdavid1
Copy link

lacroixdavid1 commented Jan 26, 2018

Those misleading answers and that dead silence leave me disappointed about cypress.io.

@bahmutov
Copy link
Contributor

Sorry to hear @lacroixdavid1 - but notice that this goes against our philosophy of making each test independent from others. Suggestion - maybe in afterEach grab all values from localStorage and then inside into localStorage in beforeEach callback? Remember, this is JUST JavaScript, so you can copy / clone / modify everything, just like you can from DevTools console.

@lacroixdavid1
Copy link

@bahmutov I know it can be done this way, but I feel like Cypress.LocalStorage.clear should get localstorage keys like suggested in previous answers. Did code change since the first answer?

@pietmichal
Copy link

pietmichal commented May 25, 2018

I hope that in the future cypress will allow us to define the behaviour.

My current workaround is creating two helper commands which save and restore the local storage between tests.

let LOCAL_STORAGE_MEMORY = {};

Cypress.Commands.add("saveLocalStorage", () => {
  Object.keys(localStorage).forEach(key => {
    LOCAL_STORAGE_MEMORY[key] = localStorage[key];
  });
});

Cypress.Commands.add("restoreLocalStorage", () => {
  Object.keys(LOCAL_STORAGE_MEMORY).forEach(key => {
    localStorage.setItem(key, LOCAL_STORAGE_MEMORY[key]);
  });
});

And then in one of my tests

beforeEach(() => {
  cy.restoreLocalStorage();
});

afterEach(() => {
  cy.saveLocalStorage();
});

@danatemple
Copy link

danatemple commented Aug 26, 2018

I have the same issue, and I think a slightly neater workaround using aliases rather than a variable to store the data from LocalStorage:

cy.contains('Welcome to our site').then(() => {
     cy.wrap( localStorage.getItem('currentUser') ).as('currentUser');
     cy.wrap( localStorage.getItem('token') ).as('token');
});

and then restoring them in a beforeEach()

describe('User login', function() {
    beforeEach(function() {
        if (this.currentUser) {
            localStorage.setItem('currentUser', this.currentUser);
            localStorage.setItem('token', this.token);
        }
    });

But I agree whitelisting the specific entries would be a better way to maintain authentication within a describe() context.

@Aaronius
Copy link

Aaronius commented Sep 6, 2018

Regarding @unrealprogrammer's approach, I have something like this:

describe()
  it()
  it()

describe()
 before()
   it()

Inside the before(), I'm calling cy.visit(). The problem is that the call to cy.visit() comes after the global afterEach() that stores off the local storage item, but before the global beforeEach() that restores the local storage item. Therefore, when Cypress tries to visit the URL, our app says it can't find the thing that's supposed to be in local storage and fails (in our case, we're storing an auth token).

The only solution I have for now is to move the cy.visit() call into a beforeEach() or into each it(), both of which require that the browser do more work than is necessary. I would love to hear if there's a better way of handling this. I've tried @brian-mann's approach, but can't get it to work for the same reasons @lacroixdavid1 has stated.

@Aaronius
Copy link

Aaronius commented Sep 6, 2018

I managed to come up with something that seems to be working for our needs. Here it is in case someone else could use it:

let cachedLocalStorageAuth;

function restoreLocalStorageAuth() {
  if (cachedLocalStorageAuth) {
    localStorage.setItem('auth-token', cachedLocalStorageAuth);
  }
}

function cacheLocalStorageAuth() {
  cachedLocalStorageAuth = localStorage.getItem('auth-token');
}

Cypress.on('window:before:load', restoreLocalStorageAuth);
beforeEach(restoreLocalStorageAuth);
afterEach(cacheLocalStorageAuth);

@lacroixdavid1

This comment has been minimized.

@carlos-granados

This comment has been minimized.

@coffenbacher
Copy link

"There are multiple reasons" is not very compelling.

Back on topic, I think the workarounds in here are pretty helpful. Borderline good enough where I don't think Cypress should prioritize doing anything here.

@EvHaus
Copy link

EvHaus commented Jan 9, 2019

I would honestly just offer an option to "Don't reset localStorage between tests" as well. I would say, in the majority of cases (for us) we want to preserve it, and clearing it manually is literally a one liner we can add in after() where appropriate.

@pavelsmajda
Copy link

pavelsmajda commented Aug 19, 2019

@danatemple thanks for help, but your solution works for me only partially. I still receive "not-authorized" page after some time tests are running. Thank you anyway.

I'm also trying workaround from @pietmichal but I got the same result.

Silent sign on is called in Cypress very often and requests are often returned without token. Some of the requests contain token, but they're sent late and user is logged out. So I got test (it) where I'm logged out because of "not-authorized" but meanwhile is token sent so next test (it) is passing.

I'm in dead end and it seems I need to wait to this issue fix. In Selenium is this working without any problem. "authority" is called once and returned with token which keep user signed into application.

@Tenmak
Copy link

Tenmak commented Feb 20, 2020

You can achieve this manually right now because the method thats clear local storage is publicly exposed as Cypress.LocalStorage.clear.

You can backup this method and override it based on the keys sent in.

const clear = Cypress.LocalStorage.clear

Cypress.LocalStorage.clear = function (keys, ls, rs) {
  // do something with the keys here
  if (keys) {
    return clear.apply(this, arguments)
  }
}

Thanks @brian-mann , I've been inspiring myself from this example to create my own in TypeScript. I'm setting the list of keys to ignore in my Local Storage but still manually clear it once at when Cypress start to ensure at least one authentication case.

Works great for my needs.

import { isArray } from 'util';

// Manually clear the localStorage before each session to ensure authentication at least once
window.localStorage.clear();

Cypress.LocalStorage.clear = (keys?: string[]) => {
  const KEYS_IGNORED = [
    ...
  ];

  // Specific clear
  if (keys !== undefined && isArray(keys) && keys.length > 0) {
    keys.forEach(key => window.localStorage.removeItem(key));
  } else {
    // Full clear
    if (window.localStorage.length > 0) {
      Object.keys(window.localStorage)
        .filter(key => !KEYS_IGNORED.includes(key))
        .forEach(key => window.localStorage.removeItem(key));
    }
  }
};

I've set this code in a custom .ts file that I import un the support folder of Cypress.

@kriegster108
Copy link

This needs to get implemented soon as possible. super frustrating issue

@MikhailForte

This comment has been minimized.

@Cubo25
Copy link

Cubo25 commented Apr 9, 2020

Same problem for me 😞 strange think is that it was working like charm for 3 weeks but somehow this issue popped up like 3 days ago ...

@evba1
Copy link

evba1 commented May 7, 2020

For the earlier issues on the keys always being empty [].
As per the code i debugged today, it looks like the [] means - delete all keys.

Reference:

So, the code which should work for whitelist would be as:

const clear = Cypress.LocalStorage.clear

Cypress.LocalStorage.clear = function (keys, ls, rs) {
  if (keys && keys.length == 0) {
    keys = Object.keys(localStorage);
  }
  whitelistKeys = [];
  keys = keys.filter(function(i) {return whitelistKeys.indexOf(i) < 0;});
  return clear.apply(this, arguments)
}

This isn't working as expected! In fact it doesn't remove any local storage item at all.
I did a test like this. Set new item in local storage with name 'test'. Then in the whitelistKeys add 'test2'. It will not remove the 'test' item and any other item that exists in local storage.

@jennifer-shehane
Copy link
Member

Hey everyone, we've outlined some work to be done to address some of the concerns of 'local storage' in this issue: #8301

It outlines a proposal for a 'session' API, to quickly summarize:

With cy.session, you'll be able to perform UI interactions before taking a "snapshot" of all the session related data and "restore" it before each test.

This session related data would include localStorage.

I recommend reading the entire proposal in #8301 and following there for any updates. Please feel free to express any concerns or questions in that issue concerning the API and add a 👍 for general support.

@whenmoon
Copy link

whenmoon commented Sep 16, 2020

I cannot believe there isn't a proper solution for this. There I was trying out Cypress and thinking how easy it was to use and then realising I couldn't actually use it because it logs me out of the app I'm testing every time I try and run a test. Maybe they think authentication isn't really a thing?

Why not just give an option not to close the browser after stopping tests or allow users to choose not to have some data removed?

Update: the only workaround which I've found is to use this, which is of course incredibly dangerous:

cy.get('#login-form-username').type(`${username}`)
cy.get('#login-form-password').type(`${password}{enter}`) 

But even then I couldn't get a test to run as it timed out because cypress wouldn't wait long enough to load the page / desired url after logging in.

I've just noticed that there are 1200 + issues on this repo which is the highest I've ever seen (React has 400 - 500). Maybe something's going on at Cypress?

@grinsteindavid
Copy link

this should be an option in the cypress.json :/

@jennifer-shehane jennifer-shehane changed the title Add option to whitelist LocalStorage entries Add option to allow LocalStorage entries to persist between tests Apr 26, 2021
@javierbrea
Copy link

Until this is natively supported, the cypress-localstorage-commands plugin allows persisting localStorage between tests.

@davifsroberto
Copy link

Cypress.LocalStorage.clear = () => {};

Works for me 😉
I using cypress 7 version.

@TheDutchCoder
Copy link

Until this is natively supported, the cypress-localstorage-commands plugin allows persisting localStorage between tests.

What a beautiful little plugin! Thanks!

I ran into this problem today and was desperate for a solution. I don't understand the reasoning for why Cypress wipes localStorage between tests, it only makes sense to do that on a per-spec basis. It doesn't align at all with e2e testing either.

@adi518
Copy link

adi518 commented Oct 3, 2021

For the earlier issues on the keys always being empty [].
As per the code i debugged today, it looks like the [] means - delete all keys.
Reference:

So, the code which should work for whitelist would be as:

const clear = Cypress.LocalStorage.clear

Cypress.LocalStorage.clear = function (keys, ls, rs) {
  if (keys && keys.length == 0) {
    keys = Object.keys(localStorage);
  }
  whitelistKeys = [];
  keys = keys.filter(function(i) {return whitelistKeys.indexOf(i) < 0;});
  return clear.apply(this, arguments)
}

This isn't working as expected! In fact it doesn't remove any local storage item at all. I did a test like this. Set new item in local storage with name 'test'. Then in the whitelistKeys add 'test2'. It will not remove the 'test' item and any other item that exists in local storage.

Of course it won't work, it does:

return clear.apply(this, arguments)

Instead of:

return clear.apply(this, keys)

Btw, this function receives only one argument keys: https://github.com/cypress-io/cypress/blob/master/packages/driver/src/cypress/local_storage.ts#L9.

@abugleev
Copy link

had to update suggested workaround as follows to make it work:

const clear = Cypress.LocalStorage.clear;

Cypress.LocalStorage.clear = function (keys) {
  if (keys && keys.length == 0) {
    return;
  }
  whitelistKeys = [];
  keys = keys.filter(function(i) {return whitelistKeys.indexOf(i) < 0;});
  return clear.apply(this, keys);
}

@NAMU1105
Copy link

const clear = Cypress.LocalStorage.clear;

Cypress.LocalStorage.clear = function (keys) {
if (keys && keys.length == 0) {
return;
}
whitelistKeys = [];
keys = keys.filter(function(i) {return whitelistKeys.indexOf(i) < 0;});
return clear.apply(this, keys);
}

Thank you this saved my day!

@yescine
Copy link

yescine commented Feb 16, 2022

@abugleev thanks it worked with the whitelistkeys .

@emilyrohrbough
Copy link
Member

emilyrohrbough commented Oct 21, 2022

In v12.0.0 (soon-to-be-released), we are introducing the concept of Test Isolation. There will be two modes of test isolation, on and off, with on being the new default mode. When test isolation is on, before each test, Cypress will:

  • clear the page by visiting about:blank
  • clear cookies
  • clear local storage
  • clear browser storage

This will prevent DOM and browser state bleed over between tests.

To persist DOM and browser state between a set of tests, i.e. local storage, you will be able to turn test isolation off for the run or at the suite level.

Closing this issue as done and tagging for the v12.0.0 release.

@cypress-bot
Copy link
Contributor

cypress-bot bot commented Dec 6, 2022

Released in 12.0.0.

This comment thread has been locked. If you are still experiencing this issue after upgrading to
Cypress v12.0.0, please open a new issue.

@cypress-bot cypress-bot bot removed the stage: ready for work The issue is reproducible and in scope label Dec 6, 2022
@cypress-bot cypress-bot bot locked as resolved and limited conversation to collaborators Dec 6, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
type: feature New feature that does not currently exist v12.0.0 🐛
Projects
None yet
Development

No branches or pull requests