Cypress: logged-in state is lost after test causes another POST request

Created on 3 Apr 2019  路  9Comments  路  Source: cypress-io/cypress

I'm confused here. I have this very simple test that logs my user in, then visits a page that requires the user to be logged in, views a form and clicks the submit button

describe('Logs In and Submits Form', () => {
  it('Mocks Login', () => {
    cy.mockLoginAndGoto('form');
  });

  it('Sees Form', () => {
    cy.get('form', {
      timeout: 5000,
    }).should('be.visible');
  });

  it('Clicks Submit Button', () => {
    cy.get('[data-cy="submit-button"]').click();
  });
});

The login part uses JWT, anyway that part works fine. It then visits the route /form and views the form element. This form is only visible to logged-in users, I can clearly see that at this point the user is logged in, I can see that from the UI showing the user's name. So far so good. Then in the last step, a button is clicked which triggers a POST requests, which passes the authorization token retrieved from the first step.

This part fails because for some reason I can't figure out, by the time it reaches the 3rd step, the user is no longer logged in, and as such the authorization token is not included with the POST request.

This "flow" works fine if you run it manually.

Now, if I run all 3 steps as a single it(), then it works correctly

describe('Logs In and Submits Form', () => {
  it('Mocks Login, sees form, clicks submit button', () => {
    cy.mockLoginAndGoto('form');

    cy.get('form', {
      timeout: 5000,
    }).should('be.visible');

    cy.get('[data-cy="submit-button"]').click();
  });
});

This works correctly. How come? What am I missing?

This is a simplified version of the real test. The real test involves logging in, testing a bunch of steps then at the end clicking the submit button and submitting a form that's been filled out by the previous steps. I don't want to have to mock the login before each of those steps. I also don't want to have to eliminate all my steps and run 20 sets of directives as a single it().

Why is it that all steps keep my user logged in but when I reach the step that causes a POST request, only then does it fail and reverts my state to logged-out? But as a single it() it works?

Thanks in advance for the help

question

Most helpful comment

@geevb cookies are not preserved across tests. If you set them in the before, they will be removed before the second test runs. See how to preserve cookies across tests: https://on.cypress.io/cookies#Set-global-default-cookies

All 9 comments

I believe that Cypress considers every test (it block) a new session. See this article for details about the why
https://docs.cypress.io/guides/references/best-practices.html#Creating-%E2%80%9Ctiny%E2%80%9D-tests-with-a-single-assertion
https://docs.cypress.io/guides/references/best-practices.html#Having-tests-rely-on-the-state-of-previous-tests

Yes. @Lakitna is correct. Each it() is run in isolation, by itself - not in sequential order. So any state from a previou it() will be completely wiped.

You will want to add the cy.mockLoginAndGoto('form'); into a beforeEach.

You may alternatively also have one longer test that goes through the login and tests this, but then have a 'shortcut' login for the rest of your tests (so it doesn't take as long). Say, if your page eventually only requires a specific localStorage to be set, then just set it before the other tests run. Some advice on this strategy can be found in this talk: https://www.youtube.com/watch?v=5XQOK0v_YRE&t=0s&index=4&list=PLZ66c9_z3umNSrKSb5cmpxdXZcIPNvKGw

I will close this issue since this is not a bug or feature request for Cypress.

In the future, try asking questions about how to use Cypress in our community chat. Alternatively, signing up for any of you paid plans gives you access to our team for support via email. If you would like something higher touch, we also offer screen sharing and workshops with our premium support options.

@Lakitna @jennifer-shehane I disagree, I believe that I have identified a bug here. The reason I say this is because in my test, I login in the first it(), and then I perform 10+ it() where I am NOT logged out, so clearly it's not true that entering another it() immediately clears the session. I can clearly see from my UI that I remain logged in for all of the it() tests, not only that but the form I am filling out as part of my test has a v-if="$auth.check()" on it so it should not even be possible for me to interact, let alone see, this form if I was not logged in.

It is only when I get to the last step, specifically only once I get to the step that triggers another POST request that suddenly I get logged out.

So while I do understand the workaround you suggest, and how the way I was doing things is not the "best practice" suggested way. There is some inconsistency in your instructions and the way you describe how things work. Unless you can explain why I am seeing this unusual behaviour of remaining logged in for 10+ it() steps despite only logging in the first it().

Could it be that the its before the failing one all do things that do not require an active session but only a loaded page?

When you log in you create a session and then you load the page where your application checks the session. It might be that there isn't another session check until you try to interact with the backend (via POST in this case).

Though I'm really shooting in the dark here without any code.

@vesper8 I'd have to know more about how your application handles authentication to determine whether this is a bug of not clearing state or not.

How do you check that a session is authenticated? Cookies, localStorage, session storage?

I use https://github.com/websanova/vue-auth and it stores the jwt auth token in localstorage

I have resorted to putting all my tests in single long it() blocks now to avoid this issue, whether it is intended or not

Same error on a React application

I'm facing the same issue here using React + Auth0. The first it() runs a POST that works perfectly, the next it() with a POST inside it as well fails with 401 for no apparent reason. All the cookies are still in the browser as they were previously set with cy.setCookie() in the before() step

I've tested @jennifer-shehane comment and put everything inside one it() and it works as expected, but that will be mixing CREATE/check/UPDATE/check/DELETE/check tests into a single one which is quite unreadable...

@geevb cookies are not preserved across tests. If you set them in the before, they will be removed before the second test runs. See how to preserve cookies across tests: https://on.cypress.io/cookies#Set-global-default-cookies

Was this page helpful?
0 / 5 - 0 ratings

Related issues

tahayk picture tahayk  路  3Comments

brian-mann picture brian-mann  路  3Comments

carloscheddar picture carloscheddar  路  3Comments

szabyg picture szabyg  路  3Comments

rbung picture rbung  路  3Comments