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 whitelist LocalStorage entries #461

Open
carlos-granados opened this Issue Mar 15, 2017 · 23 comments

Comments

@carlos-granados
Contributor

carlos-granados commented Mar 15, 2017

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

@tjhillard

This comment has been minimized.

tjhillard commented Apr 26, 2017

👍 Would a be nice feature!

@ashercoren

This comment has been minimized.

ashercoren commented May 12, 2017

Yes, please!

@ZwaarContrast

This comment has been minimized.

ZwaarContrast commented Aug 21, 2017

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

This comment has been minimized.

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

This comment has been minimized.

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

This comment has been minimized.

Member

jennifer-shehane commented Dec 4, 2017

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

@lacroixdavid1

This comment has been minimized.

lacroixdavid1 commented Jan 11, 2018

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

@brian-mann

This comment has been minimized.

Member

brian-mann commented Jan 11, 2018

@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

This comment has been minimized.

lacroixdavid1 commented Jan 11, 2018

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

@lacroixdavid1

This comment has been minimized.

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

This comment has been minimized.

Member

brian-mann commented Jan 11, 2018

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

This comment has been minimized.

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

This comment has been minimized.

lacroixdavid1 commented Jan 15, 2018

@brian-mann any suggestions ?

@lacroixdavid1

This comment has been minimized.

lacroixdavid1 commented Jan 26, 2018

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

@bahmutov

This comment has been minimized.

Collaborator

bahmutov commented Jan 26, 2018

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

This comment has been minimized.

lacroixdavid1 commented Jan 26, 2018

@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

This comment has been minimized.

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

This comment has been minimized.

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

This comment has been minimized.

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

This comment has been minimized.

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.

lacroixdavid1 commented Sep 6, 2018

@Aaronius the best recommendation I can give you is to get rid of Cypress and switch to Protractor. There are multiple reasons.

@carlos-granados

This comment has been minimized.

Contributor

carlos-granados commented Sep 6, 2018

@lacroixdavid1 I had the opposite experience. Protractor was a nightmare and switching to Cypress solved all our problems with random failures and tests which were impossible to debug

@coffenbacher

This comment has been minimized.

coffenbacher commented Sep 12, 2018

"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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment