Similar to the mechanism available to whitelist cookies so that these entries will not be cleared between test
馃憤 Would a be nice feature!
Yes, please!
Yes, that would really help us with our application which uses cognito by AWS as a authentication provider (which under the hood uses localstorage)
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.
```js
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)
}
}
@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 (....
@jurom Thanks, this has been fixed in brian's example.
I've tried your example @brian-mann but i get empty keys array on every call. Any ideas why ?
@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.
@brian-mann it is really stored in localstorage with key jStorage
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.
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.
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?
@brian-mann any suggestions ?
Those misleading answers and that dead silence leave me disappointed about cypress.io.
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.
@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?
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();
});
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.
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.
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);
@Aaronius the best recommendation I can give you is to get rid of Cypress and switch to Protractor. There are multiple reasons.
@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
"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.
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.
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.
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)
}
It's on our roadmap to make handling localStorage and other things that are automatically cleared between tests more customizable, including turning off clearing altogether. You can follow that larger issue here: https://github.com/cypress-io/cypress/issues/686
Hi everyone,
our new web application is using Silent sign in method (oidc authentication) and after all tutorials, and hints I'm still unable to keep localStorage and Cookies saved for whole bunch of tests. My goal is to keep user logged in all the time during the tests but I always finish on "/not-authorized" page and with completely cleared session. I'm out of ideas.
Test structure looks like this:
describe('testA', () => {
it('request url if available' () {
cy.a
})
describe('login, function() {
before(function () {
cy.login()
})
it('go to page A and test something() {
cy.a
})
it('go to page B and test something () {
cy.a
})
describe('logout', () => {
it('Logout', function () {
cy.a
})
Can you help me with this issue please? Thanks a lot! :-)
I have a similar model to you, with each spec having a describe() containing a sequence of tests, with a single login at the start. Each test is in an it(), and we want to preserve both DOM and localStorage between the it()s as we use JWT login.
The solution that works robustly for us is to initially prevent any localStorage clearing happening:
Cypress.LocalStorage.clear = function (keys, ls, rs) {
return;
}
We did try to implement the "whitelist" solution above by replacing this with a function to process the list of keys, but this did not work reliably.
Then, at the start of each describe() i.e. before the login:
localStorage.clear();
Control of cookie clearing is supported by Cypress: https://docs.cypress.io/api/cypress-api/cookies.html
@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.
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.
This needs to get implemented soon as possible. super frustrating issue
also faced with this problem
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 ...
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.
Hey everyone, we've outlined some work to be done to address some of the concerns of 'local storage' in this issue: https://github.com/cypress-io/cypress/issues/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 https://github.com/cypress-io/cypress/issues/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.
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?
Most helpful comment
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.
And then in one of my tests