Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cypress Clearing Cookies During Test Execution #747

Closed
jtmgio opened this issue Oct 16, 2017 · 6 comments
Closed

Cypress Clearing Cookies During Test Execution #747

jtmgio opened this issue Oct 16, 2017 · 6 comments

Comments

@jtmgio
Copy link

jtmgio commented Oct 16, 2017

  • Operating System: OSX
  • Cypress Version: 1.0.2
  • Browser Version: CHROME Version 61.0.3163.100 (Official Build) (64-bit)

Is this a Feature or Bug?

This is a bug

Current behavior:

When cypress is executing tests, cypress for some reason clears the browser cookies. This is unexpected behavior and is making the Cypress un-useable.

Desired behavior:

For cypress not to touch browser cookies during testing.

How to reproduce:

See my code

Test code:

This is an angular 2 application.

/****** 
INDEX INTEGRATION TEST
- calls all of the test for testing a sub section of our application
- This is run after Jenkins builds the application or run on a local machine
*******/
"use strict";
import { _login } from '../../common/authenticate';
import { _navigate } from "../../common/navigate";
import * as headerTests from "./inc/header.spec";
import * as createFormModalTests from "./inc/create-form-modal.spec";
import * as showArchivedFormsTests from "./inc/show-archived-forms.spec";
import * as searchFormsTests from "./inc/search-forms.spec";
import * as tableSortingTests from "./inc/table-sorting.spec";
import * as tableActionTests from "./inc/table-actions.spec";
let formlist_url = "/administrator/index.php?option=com_mcform&view=ngformlist";
let orgid = "demo2";

context( "Authenticate", () => {
	_login( orgid );	
})
context( "Navigation", () => {
	_navigate( formlist_url );
})
context( "Forms List -> ", () => {
	describe( "Form List Header Toolbar ->", () =>{
		headerTests.executeCyTest();
	})
	describe( "Create Form Modal ->", () => {
		createFormModalTests.executeCyTest();
	})
	describe( "Show Archived Forms ->", () => {
		showArchivedFormsTests.executeCyTest();
	})	
	describe( "Search for Forms ->", () => {
		searchFormsTests.executeCyTest();
	})
        //HERE IS WHERE WE LOSE THE COOKIES
	describe( "Table sorting", () => {
		tableSortingTests.executeCyTest();
	});
})
/******
TABLE-ACTIONS.SPEC
- We use cypress to click a button which in turn calls a modal. 
- This modal executes an HTTP GET request
- By the time we open the modal, the cookies have been cleared, thus  the HTTP call fails due to dependencies on a token in the browser cookie. 
*********/
"use strict";
function cypressTest( path ){
	describe( "Table action should appear on hover", () => {
		it( "should show the action buttons on hover", () => {
			cy.get("table.td-data-table tr.td-data-table-row" ).eq( 1 )
				.find( "td.column6 > div button" ).eq( 1 ).click({ force : true })
		});
	})
}
module.exports = {
	executeCyTest : cypressTest
}

Additional Info (images, stack traces, etc)

A utility to turn off automatic cookie removal.

@brian-mann
Copy link
Member

brian-mann commented Oct 16, 2017

This isn't a bug, Cypress specifically does this to prevent tests from building up state.

We recommend programmatically logging into your app before each test (as opposed to using the UI)

But we agree with you, I've documented in glory here about why we shouldn't be doing this.

#686

I'll keep this issue open, but this is something we're definitely aware of, and will be giving you API's to control this to exactly how you'd like.

BTW there are API's already there for controlling which cookies don't get removed.

https://docs.cypress.io/api/cypress-api/cookies.html#Preserve-Once

I also agree that this is very unexpected and not documented well enough in high traffic areas.

@jtmgio
Copy link
Author

jtmgio commented Oct 17, 2017

@brian-mann Thank you for the quick response. I believe the "Preserve-Once" will work for what i need here. I will try this out tomorrow morning and respond back.

I really like the cypress.io tool. Keep up the good work guys/girls!

@jtmgio
Copy link
Author

jtmgio commented Oct 17, 2017

@brian-mann Using the "defaults" method worked perfect. I attached my code for reference. Thanks again for the quick response.

describe( "Memberclicks Tests", () =>{
	
	beforeEach(() => {
		Cypress.Cookies.defaults({
			whitelist: [ "mcid_token", "serviceID" ]
		});
	})

	context( "Authenticate", () => {
	
		_login( orgid );	
	})

	context( "Navigation", () => {
		_navigate( formlist_url );
	})

	context( "Forms List -> ", () => {
		
		describe( "Form List Header Toolbar ->", () =>{
			headerTests.executeCyTest();
		})

		describe( "Create Form Modal ->", () => {
			createFormModalTests.executeCyTest();
		})
		
		describe( "Show Archived Forms ->", () => {
			showArchivedFormsTests.executeCyTest();
		})
		
		describe( "Search for Forms ->", () => {
			searchFormsTests.executeCyTest();
		})
		
		describe( "Table sorting", () => {
			tableSortingTests.executeCyTest();
		})

		describe( "Table Actions", () => {
			tableActionTests.executeCyTest();
		})
		
	})
})

@brian-mann
Copy link
Member

Closing because this is not a bug, and is referenced elsewhere.

@alexkb0009
Copy link

alexkb0009 commented Mar 29, 2018

@brian-mann thanks for the tip! Is there anything similar to Cypress.Cookies.preserveOnce() for localStorage/sessionStorage?

Also +1 for #686 as is my first week with Cypress and inherently assumed tests within the same context would preserve browser state (cookies & localStorage) (as in, that that is primary purpose of 'context'). Re: same issue, to play devils advocate a little, think having it in config is a good thing (vs Cypress.lifecycle(...) approach) because it simplifies/reduces amount of places where config can live and already have the ability to modify config within test itself.

Update - have this approach/work-around working currently except for headless mode:

var localStorageCache = { 'some_key' : null };

describe(..., function(){
    context(..., function(){

        before(function(){ // could also be in after(function(){...});
            localStorageCache['some_key'] = null;
            cy.clearCookies();
        });

        beforeEach(function(){
            if (localStorageCache['some_key']){
                localStorage.setItem('some_key', localStorageCache['some_key']);
            }
            Cypress.Cookies.preserveOnce();
        });

        afterEach(function(){
            localStorageCache['some_key'] = localStorage.getItem('some_key');
        });

        it('Perform a login (store cookie, localStorage stuff)', ...) // Or have this as before(...) hook (?)

        it('Do something as logged in user', ...)

        it('Do something else as logged in user', ...)

        it('Log out by pressing log out button in UI', ...)

    });
});

Will likely move this into commands.js as cy.saveBrowserSession, cy.loadBrowserSession, cy.clearBrowserSession, where localStorageCache would be shared by all tests, along with a cookieCache (just for standardization until LocalStoage.preserveOnce() or similar is available).

Update 2 - Formalized for own purposes --

commands.js:

/** Session Caching */

var localStorageCache = { 'userSettings' : null, ... }; // Define keys to save/load in between tests here.
var cookieCache = { 'jwtToken' : null, 'searchSessionID' : null, ... };


Cypress.Commands.add('saveBrowserSession', function(options = {}){
    _.forEach(_.keys(localStorageCache), function(storageKey){
        localStorageCache[storageKey] = localStorage.getItem(storageKey) || null;
    });
    _.forEach(_.keys(cookieCache), function(cookieKey){
        cookieCache[cookieKey] = cy.getCookie(cookieKey) || null;
    });
});

Cypress.Commands.add('loadBrowserSession', function(options = {}){
    _.forEach(_.keys(localStorageCache), function(storageKey){
        if (typeof localStorageCache[storageKey] === 'string'){
            localStorage.setItem(storageKey, localStorageCache[storageKey]);
        }
    });
    _.forEach(_.keys(cookieCache), function(cookieKey){
        if (typeof cookieCache[cookieKey] === 'string'){
            cy.setCookie(cookieKey, cookieCache[cookieKey]);
        }
    });
});

Cypress.Commands.add('clearBrowserSession', function(options = {}){
    _.forEach(_.keys(localStorageCache), function(storageKey){
        localStorageCache[storageKey] = null;
    });
    _.forEach(_.keys(cookieCache), function(cookieKey){
        cookieCache[cookieKey] = null;
    });
    cy.loadBrowserSession();
});

integration test file:

describe("Test User Profile", function(){
    context(..., function(){
        before(function(){
            cy.clearBrowserSession();
        });

        beforeEach(function(){
            cy.loadBrowserSession();
        });

        afterEach(function(){
            cy.saveBrowserSession();
        });

        after(function(){
            // Probably unnecessary as Cypress cleans up between tests.
            cy.clearBrowserSession();
        });

        it('Perform a login (store cookie, localStorage stuff)', ...) // Or have this as before(...) hook (?)

        it('Test user profile page, ...', ...)

        it('Edit own user profile as logged in user', ...)

        it('Edit something which have no permissions for', ...)

        it('Edit something which have permissions for', ...)

        it('Log out by pressing log out button in UI', ...)

        it('Do something as anonymous user', ...)

        it('Log in as another user, test profile page', ...)

        it('Ensure permissions are different from first user', ...)

    });
});

Edit: Scratch that, this doesn't seem to work in headless mode (on Travis CI at least).

@musicformellons
Copy link

Any idea why this does not work on headless mode?

candu pushed a commit to CityofToronto/bdit_flashcrow that referenced this issue Feb 6, 2019
It turns out that Cypress does something that, at first, seems annoying
but is in fact a Very Good Idea: it clears state before each test, to
prevent interdependency between tests.  (In other words: tests should be
able to run in any order without issues.)

This *includes* cookies, hence the errors we were seeing before.  See
cypress-io/cypress#747 for more details.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants