In Cypress, set a token in localStorage before test

Here's an example of adding a command cy.login() that you can use in any Cypress test, or put in a beforeEach hook.

Cypress.Commands.add('login', () => { 
  cy.request({
    method: 'POST',
    url: 'http://localhost:3000/api/users/login',
    body: {
      user: {
        email: '[email protected]',
        password: 'jakejake',
      }
    }
  })
  .then((resp) => {
    window.localStorage.setItem('jwt', resp.body.user.token)
  })

})

Then in your test:

beforeEach(() => {
  cy.login()
})

As an extra, you can also use the cypress-localstorage-commands package to persist localStorage between tests, so login request will be made only once:

In support/commands.js:

import "cypress-localstorage-commands";

Cypress.Commands.add('login', () => { 
  cy.request({
    method: 'POST',
    url: 'http://localhost:3000/api/users/login',
    body: {
      user: {
        email: '[email protected]',
        password: 'jakejake',
      }
    }
  })
  .its('body')
  .then(body => {
    cy.setLocalStorage("jwt", body.user.token);
  })
});

Then, in your tests:

before(() => {
  cy.login();
  cy.saveLocalStorage();
});

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

I've used something along the lines of bkuceras answer for a while now. Recently I've run into an issue when testing multiple roles/logins throughout tests. Basically what is happening is I log in as an admin and do one test, then I log in as a non admin and do a second test. The first test runs fine and ends (cypress clears local storage) however there are still some xhr requests running from the first test when the second test starts. These xhr requests no longer see the token which triggers my app to see a 401 unauthorized error and clear local storage (including my non admin token I have set). Now the 2nd test fails because that non admin token is not available in local storage.

Ultimately my solution was to prefetch the token before the test then set the token in the onBeforeLoad function of visit. This ensures the token is not cleared by any race condition before your visit command.

cy.visit('/app', {
    onBeforeLoad: function (window) {
        window.localStorage.setItem('token', myToken);
    }
})

Really this is a pretty unique edge case, but heres hoping it may help someone as I've spent many hours finding this solution.