Local storage is set to wrong domain in Cypress - cypress

Trying to test two subdomains in integration.
However, localStorage is always set to first visited subdomain.
Is it possible to set localStorage value to the later visited subdomain (bar.company.com) in the example below?
describe('Login', () => {
it('Logins foo', () => {
cy.visit('https://foo.company.com');
localStorage.setItem('foo', 'foo');
});
it('Logins bar', () => {
cy.visit('https://bar.company.com');
// this will set the entry to foo.company.com
localStorage.setItem('bar', 'bar');
});
});
Now if we check localStorage at bar.company.com it is empty.
And foo.company.com has the both foo and bar entries.

You should either set testIsolation: true or call cy.clearAllLocalStorage() before all tests.
TestIsolation is designed to overcome this sort of issue, but you may not want it if there is some sort of continuity between tests (i.e data carries over).
Here is a reproducible example. There are two contexts, one with testIsolation on and one with it off.
Comment out the cy.clearAllLocalStorage() and you will see the tests start to fail.
Note that localstorage entries persist across runs in the Cypress UI.
context("localstorage - isolated", { testIsolation: true }, () => {
it("one", () => {
cy.visit("https://docs.cypress.io");
localStorage.setItem("one", "1");
cy.getAllLocalStorage().should("deep.eq", {
"https://docs.cypress.io": { one: "1" }
});
})
it("two", () => {
cy.visit('https://example.com/')
localStorage.setItem("two", "2");
cy.getAllLocalStorage().should("deep.eq", {
"https://example.com": { two: "2" }
});
})
})
context("localstorage - not isolated", { testIsolation: false }, () => {
before(() => {
cy.clearAllLocalStorage()
})
it("one", () => {
cy.visit('https://docs.cypress.io');
localStorage.setItem("one", "1");
cy.getAllLocalStorage().should("deep.eq", {
"https://docs.cypress.io": { one: "1" }
});
})
it("two", () => {
cy.visit('https://example.com')
localStorage.setItem("two", "2");
cy.getAllLocalStorage().should("deep.eq", {
"https://example.com": { two: "2" }
});
})
})

Related

How to use fixture for cy.visit()

I have multiple links to test having the same test cases. I wanted to use fixture but it did not work. Can someone please help? I want to reiterate my tests for all links in the json file.
spec.js:
cy.readFile('cypress/fixtures/trial.json').then((details) => {
details.forEach((totest)=>{
cy.visit(totest.url)
})
})
fixture (.json):
"totest":[
{
"url": "https:www.link1.com"
},
{
"url": "https:www.link2.com"}
,
{
"url": "https:www.link3.com"
}
]
Your array is inside the totest property, so you need to add that
cy.fixture('trial.json').then((details) => {
details.totest.forEach((totest) => {
cy.visit(totest.url)
})
})
One test per URL
import details from './cypress/fixtures/trial.json'
//OR
const details = require('./cypress/fixtures/trial.json')
describe('tests URLs', () => {
details.totest.forEach(totest => {
it(`testing ${totest.url}`, () => {
cy.visit(totest.url)
})
})
})

Turn off clearing aliases in Cypress test

We have a number of variables from the API which are used throughout the tests. The values are saved to aliases at various points.
How can we turn off clearing aliases between test?
If we use one big test this works fine but Cypress by default clears aliases between tests, so as we break down the tests into smaller more manageable units we are breaking the code.
Simple example:
before(() => {
cy.wrap(123).as('alias')
})
it('test1', () => {
// alias is usable here
cy.get('#alias').should('eq', 123)
cy.wrap(456).as('alias2')
});
it('test2', () => {
// alias is missing here
cy.get('#alias').should('eq', 123)
cy.get('#alias2').should('eq', 456)
});
There's no configuration to turn off alias cleardown.
You would have to create a custom command to do it.
Cypress.Commands.add('keepAliases', function(aliasList) {
if (!aliasList) {
aliasList = Object.keys(this)
.filter(key => !['test', '_runnable', 'currentTest']
.includes(key))
}
aliasList.forEach(key => {
cy.wrap(this[key]).as(key)
})
})
Usage for a single test
before(() => {
cy.wrap(123).as('alias')
})
it('test1', () => {
cy.get('#alias').should('eq', 123)
cy.wrap(456).as('alias2')
});
// ✅ test passes
it('test2', () => {
cy.keepAliases()
cy.get('#alias').should('eq', 123)
cy.get('#alias2').should('eq', 456)
});
Usage for all tests
before(() => {
cy.wrap(123).as('alias')
})
beforeEach(() => {
cy.keepAliases()
})
it('test1', () => {
cy.get('#alias').should('eq', 123)
cy.wrap(456).as('alias2')
});
// ✅ test passes
it('test2', () => {
cy.get('#alias').should('eq', 123)
cy.get('#alias2').should('eq', 456)
});
According to cypress doc
aliases and properties are automatically cleaned up after each test
so with current cypress versions your goal is not achievable.
As a workaround you could use cypress.env as a storage for your tests:
Cypress.env(varName, varValue)
and using a test0 to reset all variables before each texts execution:
it('test0', () => {
// clean up all stored aliases
cy.env("var1",null);
cy.env("var2",null);
//...
});

How to visit pre-defined URLs from file

If I have 30 pages to check, for example, EN has a disclaimer, but other 29 language don't, what would be the best way to do this? For example, right now I have it like this:
const urls = ['http://google.com/en',
'http://google.com/bg'
]
describe('Disclaimer check', () => {
urls.forEach((url) => {
it(`Checks disclaimer text ${url}`, () => {
cy.visit(url)
cy.get('.Disclaimer').should('be.visible')
.and('contain', 'This is disclaimer.')
})
})
})
For 2 sites it's fine to define them in the same code but other file that checks that Disclaimer isn't there would be 29 different URL-s. What would be the best practice here? One idea is to separate all the test but that would mean 30 tests for each feature which doesn't sound too great.
As url I'm working with uses many different values in it, making it short with baseurl doesn't seem to fit also.
Thank you in advance!
You were on the right path. This will be a good case for using cypress-each. Cypress-each will run all tests regardless if one or more fail. Depending on how long it takes, you may want to break down the it.each test into another file.
import 'cypress-each' // can included in /support/index.js
describe('Disclaimer check', () => {
// baseUrl: http://google.com
const noDisclaimerUrl = [
'/bg',
// all other languages
]
it('/en does have disclaimer text', () => {
cy.visit('/en')
// test code
})
it.each((noDisclaimerUrl)
`%s does not have disclaimer text`
(url) => {
cy.visit(url)
// test code
})
})
Adding all of your data to a data object, import that data object, and then using Cypress Lodash to iterate a number of times should achieve your goal.
// data.js
// defining data
export const data =[{
"url": "www.google.com",
"hasDisclaimer": true
}, {
"url": "www.other-url.com",
"hasDisclaimer": false
}...
]
You can then wrap the returned array and use it in a Cypress chain.
import { data } from './path/to/data'
describe('Tests', () => {
Cypress._.times(data.length, (index) => {
const curr = data[index];
it(`Checks disclaimer text ${curr.url}`, () => {
cy.visit(curr.url).then(() => {
if (curr.hasDisclaimer) {
cy.get('.Disclaimer').should('be.visible')
.and('contain', 'This is disclaimer.');
} else {
// code for checking disclaimer does not exist
}
});
});
});
});
Under your Fixtures folder create a urls.json file like this:
[
"https://google.com/en",
"https://google.com/bg",
"https://url3.com",
"https://url4.com"
]
Now assuming that you know which URLs don't have the disclaimer, you can simply add them in the If condition and apply the not.exist assertion.
import urls from '../fixtures/urls.json'
describe('Disclaimer check', () => {
urls.forEach((url) => {
it(`Checks disclaimer text ${url}`, () => {
cy.visit(url)
if (url == 'https://google.com/en' || url == 'https://url3.com') {
//Check for URL's where disclaimer doesn't exist
cy.get('.Disclaimer').should('not.exist')
} else {
//Check for URL's where disclaimer exists
cy.get('.Disclaimer')
.should('be.visible')
.and('contain', 'This is disclaimer.')
}
})
})
})

Cypress sharing variables/alias between Hooks?

So I have a pretty `before` and `beforeEach` function that runs before all tests. It looks something like this:
describe("JWT Authentication", function() {
before(function() {
// custom command runs once to get token for JWT auth
// alias token as 'user' for further use
cy.get_auth_token().as('user')
})
beforeEach(function() {
// before each page load, set the JWT to the aliased 'user' token
cy.visit("/", {
onBeforeLoad(win) {
// set the user object in local storage
win.localStorage.setItem("token", this.user.token);
}
})
})
it("a single test...", function() {
//do stuff
});
The custom command is also pretty simple:
Cypress.Commands.add("get_auth_token", () => {
cy.request("POST", Cypress.env("auth_url"), {
username: Cypress.env("auth_username"),
password: Cypress.env("auth_password")
})
.its("body")
.then(res => {
return res;
});
})
The custom command itself works and retrieves the token as expected. However when it comes to the beforeEach it has no idea what this.user.token is. Specifically not knowing what user is.
One option is of course calling the command in every beforeEach which is what the JWT Cypress recipe/example spec does. However this feels excessive because in my case I do not NEED to grab the token every test. I only need to grab it once for this set of tests.
So how can I share the token to the beforeEach hook with a Cypress custom command.
I ran a few tests, all the bits seem to work!
The following does not give you an answer, but may help you debug.
Passing token between before() and beforeEach()
Assume we have a user in before(), does it get to the onBeforeLoad() callback?
describe("JWT Authentication", function() {
before(function() {
const mockUser = { token: 'xyz' };
cy.wrap(mockUser).as('user');
})
beforeEach(function() {
cy.visit("http://example.com", {
onBeforeLoad(win) {
console.log(this.user.title); // prints 'xyz'
}
})
})
it("a single test...", function() {
//do stuff
})
});
Is the custom command working
I can't find a generic mock for an Auth check, but any cy.request() that gets an object should be equivalent.
I'm hitting typicode.com and looking for the title property
describe("JWT Authentication", function() {
Cypress.Commands.add("get_auth_token", () => {
cy.request("GET", 'https://jsonplaceholder.typicode.com/todos/1')
.its("body")
.then(body => {
console.log('body', body); // prints {userId: 1, id: 1, title: "delectus aut autem", completed: false}
return body;
});
})
before(function() {
cy.get_auth_token()
.then(user => console.log('user', user)) // prints {userId: 1, id: 1, title: "delectus aut autem", completed: false}
.as('user')
})
beforeEach(function() {
cy.visit("http://example.com", {
onBeforeLoad(win) {
console.log(this.user.title); // prints 'delectus aut autem'
}
})
})
it("a single test...", function() {
//do stuff
})
});
Custom command
This shorter version also seems to work
Cypress.Commands.add("get_auth_token", () => {
cy.request("GET", 'https://jsonplaceholder.typicode.com/todos/1')
.its("body");
})

Root level `beforeEachFile` and `afterEachFile`

I am trying to do a root-level before that runs before each test file, and after each test file completes. So in other words I want it to run before and after the "top most" describe.
The only way I got this to work was by adding into each of my files top-most describe a before and after. However it is getting very redundant, can I just do it at root level?
Does wrapping all of your describes in another describe solve it (no pun intended)?
describe('all stuff', () => {
before(() => {
}
describe('something', () => {
it('...', ()=> {
})
it('...', ()=> {
})
})
after(() => {
})
})

Resources