Cypress by default clears localStorage between tests, which may be a problem when you are trying to test features related to it. But there is a Cypress plugin that allows preserving localStorage between tests and disabling localStorage.
The problems
- You want to preserve localStorage between Cypress tests.
- You want to preserve localStorage between Cypress spec files.
- You want to disable localStorage to check error handling.
The solution
The cypress-localstorage-commands
plugin allows you to use all browser localStorage methods through Cypress commands, and preserve it between tests and spec files. It also allows to simulate that localStorage is disabled in the browser.
Alternatives
As from Cypress 12, you can use cy.session and Cypress Test Isolation in order to persist localStorage between tests. Anyway, the plugin can be still used for an easier manipulation of the localStorage, writing localStorage assertions and even disabling it for checking the error handling.
Installation
The module is distributed via npm which is bundled with node and should be installed as one of your project's devDependencies:
npm i --save-dev cypress-localstorage-commands
Installing commands
cypress-localstorage-commands
extends Cypress' cy commands.
At the top of your Cypress' support file (usually cypress/support/e2e.js
for e2e
testing type):
import "cypress-localstorage-commands";
Read Cypress configuration docs for further info.
Installing Node events
⚠ In order to support preserving localStorage across Cypress spec files, the plugin's Node events must be installed also. Otherwise, localStorage will be preserved only across tests in the same spec file.
In the cypress.config.js
file:
module.exports = {
e2e: {
setupNodeEvents(on, config) {
require("cypress-localstorage-commands/plugin")(on, config);
return config;
},
},
};
Usage
Commands
cy.saveLocalStorage()
Saves current localStorage values into an internal "snapshot".
cy.restoreLocalStorage()
Restores localStorage to previously "snapshot" saved values.
cy.clearLocalStorageSnapshot()
Clears localStorage "snapshot" values, so previously saved values are cleaned.
cy.getLocalStorage(item)
Gets localStorage item. Equivalent to localStorage.getItem
in browser.
-
item
(String): Item to get fromlocalStorage
.
cy.setLocalStorage(item, value)
Sets localStorage item. Equivalent to localStorage.setItem
in browser.
-
item
(String): Item to set value. -
value
(String): Value to be set.
cy.removeLocalStorage(item)
Removes localStorage item. Equivalent to localStorage.removeItem
in browser.
-
item
(String): Item to be removed.
cy.disableLocalStorage(options)
Disables localStorage. It produces localStorage methods to throw errors.
-
options
(Object): Options to use when disablinglocalStorage
.-
withError
(Error): If provided, invocations tolocalStorage
methods will throw this error.
-
Preserving local storage between tests
Use cy.saveLocalStorage()
to save a snapshot of current localStorage
at the end of one test, and use the cy.restoreLocalStorage()
command to restore it at the beginning of another one. The usage of beforeEach
and afterEach
is recommended for this purpose.
⚠ When the plugin's Node events are installed, the
cy.restoreLocalStorage()
command will be able to restore the localStorage snapshots saved in other spec files. Otherwise, snapshots are completely cleared between spec files.
Examples
Cookies button example
Next example shows how the plugin can be used to test a "cookies button" (which theorically sets a flag into localStorage
and can be clicked only once)
describe("Accept cookies button", () => {
const COOKIES_BUTTON = "#accept-cookies";
before(() => {
cy.clearLocalStorageSnapshot();
});
beforeEach(() => {
cy.restoreLocalStorage();
cy.visit("/");
});
afterEach(() => {
cy.saveLocalStorage();
});
it("should be visible", () => {
cy.get(COOKIES_BUTTON).should("be.visible");
});
it("should not be visible after clicked", () => {
cy.get(COOKIES_BUTTON).click();
cy.get(COOKIES_BUTTON).should("not.be.visible");
});
it("should not be visible after reloading", () => {
cy.get(COOKIES_BUTTON).should("not.be.visible");
});
});
Note the usage of
beforeEach
andafterEach
for preservinglocalStorage
between all tests. Alsocy.clearLocalStorageSnapshot
is used in thebefore
statement to avoid possible conflicts with other spec files preserving localStorage.
localStorage assertions
Based on the previous example, assertions could be added to check values of localStorage
:
describe("localStorage cookies-accepted item", () => {
beforeEach(() => {
cy.restoreLocalStorage();
cy.visit("/");
});
afterEach(() => {
cy.saveLocalStorage();
});
it("should be null first time page is visited", () => {
cy.getLocalStorage("cookies-accepted").should("equal", null);
});
it("should be true after clicking cookies button", () => {
cy.get("#accept-cookies").click();
cy.getLocalStorage("cookies-accepted").should("equal", "true");
});
it("should be true after reloading", () => {
cy.getLocalStorage("cookies-accepted").then(cookiesAccepted => {
expect(cookiesAccepted).to.equal("true");
});
});
});
Disabling localStorage
Use cy.disableLocalStorage()
to simulate that localStorage
is disabled, producing that any invocation to localStorage.setItem
, localStorage.getItem
, localStorage.removeItem
or localStorage.clear
will throw an error. As MDN docs recommend, "developers should make sure to always catch possible exceptions from setItem()". This command allows to test that possible exceptions are handled correctly.
Note that:
- Only pages loaded after calling this command will have
localStorage
disabled, so always usecy.reload
orcy.visit
after executing it. - The
localStorage
only remains disabled for all pages loaded during the current test. If you want to disable it for multiple tests, execute it in all of them, or in abeforeEach
statement. - If any of the other plugin commands (except
clearLocalStorageSnapshot
) is executed whilelocalStorage
is disabled, it will do nothing but producing a Cypress log as: "localStorage.setItem is disabled"
Examples
Disabling localStorage in a single test
Based on previous "Accept cookies button" example, next tests could be added:
//...
const LOCALSTORAGE_DISABLED_WARNING = "#localstorage-disabled-warning";
const LOCALSTORAGE_ERROR = "#localstorage-error";
//... should not be visible after clicked
it("should still be visible when reloading if localStorage is disabled", () => {
cy.disableLocalStorage();
cy.reload();
cy.get(COOKIES_BUTTON).should("be.visible");
});
it("should display warning if localStorage is disabled", () => {
cy.disableLocalStorage();
cy.reload();
cy.get(LOCALSTORAGE_DISABLED_WARNING).should("be.visible");
});
it("should display localStorage error message", () => {
cy.disableLocalStorage();
cy.reload();
cy.get(LOCALSTORAGE_ERROR).should("have.text", "Error");
});
// ...should not be visible after reloading
Disabling localStorage in multiple tests
describe("when localStorage is disabled", () => {
beforeEach(() => {
cy.disableLocalStorage({
withError: new Error("Disabled by cypress-localstorage-commands"),
});
cy.visit("/");
});
it("should display localStorage warning", () => {
cy.get("#localstorage-disabled-warning").should("be.visible");
});
it("should display localStorage error message", () => {
cy.get("#localstorage-error").should("have.text", "Disabled by cypress-localstorage-commands");
});
it("should display accept-cookies button disabled", () => {
cy.get("#accept-cookies").should("be.disabled");
});
});
Top comments (9)
Hello,
I am using this plugin to preserve local storage between tests but I am seeing different behavior when I execute e2e tests using
cypress open
orcypress run
.I have say 2 test files:
1.test.ts which has describe block with multiple it tests & using above commands to preserve localStorage i.e.
beforeEach
callingcy.restoreLocalStorage()
&afterEach
callingcy.saveLocalStorage
. In this file, I perform login & want to use local storage values in next test file.2.test.ts which also has describe block with multiple it tests & have beforeEach calling cy.restoreLocalStorage() & afterEach calling cy.saveLocalStorage()
When I run
cypress open
, I could see that localStorage set by 1.test.ts is restored for 2.test.ts in it's beforeEach.But when I run
cypress run
orcypress run --browser chrome --headless
, 2.test.ts beforeEach gets empty localStorage.Are there any additional changes required to make it work?
I have a similar requirement in order to prevent repeated logins across specs. The only solution I could think of was to write the token into a file and read it again next time we login. Didn't want to go down that path yet. Were you able to find a solution to this?
Hey!
No I couldn't find a way to accomplish that.
When I ran
cypress run --browser chrome --headed
, I observed that new browser (incognito) window was opening for each test file. Although I was doingcy.saveLocalStorage
, local storage was cleared when the browser window was closed.So I re-organized my tests so that I can test multiple related scenarios as part of one test file, logging in the start & logging out in the last test. Within each test file I do login & logout process & that's working fine.
Hi @gsaran and @geabby ,
The version v2.2.0 of the plugin already supports preserving localStorage across spec files. The only requirement is to install the plugin's Node events, you can checkout its installation docs for further info.
That's brilliant. I was looking for that for a long long time. Thanks so much
Hi, I get an error when trying to use the require in my cypress.config.js.
my e2e setup looks like this.
`e2e: {
experimentalSessionAndOrigin: true,
specPattern: "*/.feature",
async setupNodeEvents(on, config) {
require("cypress-localstorage-commands/plugin")(on, config);
},`
The error i get is "ReferenceError: require is not defined"
If you are using ESM you should import the plugin by using "import" instead of "require".
Some comments may only be visible to logged-in visitors. Sign in to view all comments.