73

I want to login and set a localStorage token on the client (specifically jwt)

How can I accomplish this using cy.request, as suggested in the Cypress Documentation?

kuceb
  • 16,573
  • 7
  • 42
  • 56

6 Answers6

108

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: 'jake@jake.jake',
        password: 'jakejake',
      }
    }
  })
  .then((resp) => {
    window.localStorage.setItem('jwt', resp.body.user.token)
  })

})

Then in your test:

beforeEach(() => {
  cy.login()
})
kuceb
  • 16,573
  • 7
  • 42
  • 56
  • 1
    This works, but the value gets overwritten when I visit the app URL. My app seems can't read the created local storage. – otong Mar 13 '19 at 02:06
  • 2
    You need to navigate to your page before setting localStorage – kuceb Mar 13 '19 at 02:12
  • 5
    I would also recommend to use cy.window() (see Cypress docs) to get the window object of the active page : `cy.window().then(win => win.localStorage.setItem('jwt', resp.body.user.token))` – guillaumepn Sep 22 '21 at 15:43
  • cy.window().then(win => win.localStorage.setItem('jwt', resp.body.user.token)) Works fine, thanks @guillaumepn – Pablo Papalardo Nov 05 '21 at 14:06
  • perfect answer. – Abadis Dec 07 '21 at 16:13
  • Doesn't this make a log in request to the server *on every test*? I assume what most users want is to log in once, or once per test file. – ViBoNaCci Feb 22 '22 at 18:45
  • @guillaumepn That doesn't help much when you're calling functions that use window somewhere down the callchain. It would be pretty awkward to write your library functions solely with cypress in mind so you can drill a window object down wherever you need it... – Douglas Gaskell Jul 18 '22 at 23:48
29

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: 'jake@jake.jake',
        password: 'jakejake',
      }
    }
  })
  .its('body')
  .then(body => {
    cy.setLocalStorage("jwt", body.user.token);
  })
});

Then, in your tests:

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

beforeEach(() => {
  cy.restoreLocalStorage();
});
Javier Brea
  • 1,345
  • 12
  • 16
  • Only worked for me with the package you indicated. Thanks – Mauricio Moraes Mar 18 '20 at 01:09
  • 1
    According to the docs on npm you should clear the storage in the `before()`, then in `beforeEach` do the restore and visit, then `afterEach` save the localstorage. `Note the usage of beforeEach and afterEach for preserving localStorage between all tests. Also clearLocalStorageSnapshot is used in the before statement to avoid possible conflicts with other test files preserving localStorage.` – dragon788 Aug 26 '20 at 21:38
  • from where we get this please? cy.setLocalStorage("jwt", body.user.token); i am trying to set it but it is failing at the body.user.token part – ZombiePie Mar 23 '22 at 11:28
  • Hi @ZombiePie, the code shown in the example is intended to be useful only to describe how to use the `cy.setLocalStorage` command. It would work really only if you have an API started in `http://localhost:3000/api/users/login` returning a response with a body in JSON with the property `user.token`. You have to adapt the login request and response to your own case. – Javier Brea Mar 23 '22 at 17:06
  • I am trying to use: https://blog.digital-craftsman.de/keep-local-storage-in-cypress/ i have a question, will the system still login everytime when i apply this? – ZombiePie Mar 24 '22 at 06:40
  • I don't understand you very well @ZombiePie. This answer is related to the usage of the `cypress-localstorage-commands` Cypress plugin, and you are mentioning a blog post entry without relation to it. – Javier Brea Mar 24 '22 at 10:30
16

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.

LLai
  • 13,128
  • 3
  • 41
  • 45
2

If you are open for the experimental mode, I highly recommend to use the cy.session to store the token:

Cypress.Commands.add('login', (username, password) => {
  cy.session([username, password], () => {
    cy.request({
      method: 'POST',
      url: '/login',
      body: { username, password },
    }).then(({ body }) => {
      window.localStorage.setItem('authToken', body.token)
    })
  })
})

For more information, please check the official cypress documentation: https://docs.cypress.io/api/commands/session

Mark Denes
  • 141
  • 1
  • 6
0

I think that title should be updated for this topic. JWT token is main potin for this discussion!

The main question was about JWT token but in general, all modern applications are using OIDC Microsoft article - v2-protocols-oidc / ADAL and here is a very tricky situation to get an access using just generating tokens by API call. I found here that enter image description here and will check it with my app. But be sure that you are cool in JS and have good assistance from your DevTeam ;)

-6

I have spent so many hours on this and finally I can safely conclude that it will never work for OAuth requests.

It may work for local server but not when you getting token for authentication.

apj585
  • 1
  • 1