How to use fixture for cy.visit() - cypress

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)
})
})
})

Related

Local storage is set to wrong domain in 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" }
});
})
})

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 fixtures - Cannot read properties of undefined (reading 'data')

I'm trying to use fixtures to hold data for different tests, specifically user credentials. This is an example of the code. When it gets to the second test I'm getting 'Cannot read properties of undefined (reading 'data')'.
Any ideas why and how I can get around that? Is that wrong?
before(function () {
cy.fixture('credentials').then(function (data) {
this.data = data;
})
})
it('Login correct', () => {
cy.visit('/')
loginPage.signIn(this.data.admin.username,this.data.admin.password)
cy.wait(5000)
// assertion
cy.contains('Dashboard').should('be.visible')
})
And here is my credentials.json file:
{
"admin": {
"username": "*****",
"password": "*****"
}
}
Try using closure variables to assign fixture data.
describe('Some Test', () => {
let data; //closure variable
before(() => {
cy.fixture('credentials').then((fData) => {
data = fData;
});
});
it('Login correct', () => {
cy.visit('/')
loginPage.signIn(data.admin.username, data.admin.password) //usage of closure variable to get the values from the fixtures
cy.wait(5000)
// assertion
cy.contains('Dashboard').should('be.visible')
});
});
Gleb Bahmutov also recommends using closure variables.
I strongly recommend using closure variables instead of this properties. The closure variables are clearly visible and do not depend on function vs () => {} syntax.
As per the cypress docs:
If you store and access the fixture data using this test context
object, make sure to use function () { ... } callbacks. Otherwise, the
test engine will NOT have this pointing at the test context.
So, your it block should also use function:
before(function () {
cy.fixture('credentials').then(function (data) {
this.data = data
})
})
it('Login correct', function () {
cy.visit('/')
loginPage.signIn(this.data.admin.username, this.data.admin.password)
cy.wait(5000)
// assertion
cy.contains('Dashboard').should('be.visible')
})
The above answes are correct. One more way to do the above operation is use cypress.json instead other json files.
In your cypress.json u can add the credentials :
{
"Env": {
"username": "*****",
"password": "*****"
}
}
and refere json directy in your it fucntion.
it('Login correct', function () {
cy.visit('/')
loginPage.signIn(Cypress.env('username'), Cypress.env('password'))
cy.wait(5000)
// assertion
cy.contains('Dashboard').should('be.visible')
})

Looking for a way tu use Cypress fixtures for all my custom commands outside an it block

I'm building some custom commands and trying to use my fixtures data for all my commands. Right now I'm forced to define it inside an it block.
Looks similar to this:
it("Commands", () => {
cy.fixture("fixtureFile").as("data");
cy.get("#data").then((data) => {
Cypress.Commands.add('login', () => {
cy.visit("/login");
cy.get('#login-email').type(data.userEmail);
cy.get('#login-pass').type(data.userPass, {log: false});
cy.get('.btn').debug().click();
})
Cypress.Commands.add('createTagMedia', () => {
cy.get(".close").click();
cy.get("#form-field-name").type(data.releaseVersion);
cy.get(".form-group-adTag > .CodeMirror > .CodeMirror-scroll").type(data.mediaTag);
cy.get("#media-save-btn").click();
})
})
})
This it block is being count as a test case, Is there a better way to pass this for more than one command at the same time?
The workaround I found was to put everything inside a before block, for example:
before(() => {
cy.fixture("fixtureFile").as("data");
cy.get("#data").then((data) => {
Cypress.Commands.add('login', () => {
cy.visit("/login");
cy.get('#login-email').type(data.userEmail);
cy.get('#login-pass').type(data.userPass, {log: false});
cy.get('.btn').debug().click();
})
Cypress.Commands.add('createTagMedia', () => {
cy.get(".close").click();
cy.get("#form-field-name").type(data.releaseVersion);
cy.get(".form-group-adTag > .CodeMirror > .CodeMirror-scroll").type(data.mediaTag);
cy.get("#media-save-btn").click();
})
})
})
Is there a reason why you won't use the following:
import {data} from '../fixtures/FixtureFile'
Considering you have the following JSON file:
{
"data": {
"userEmail": "blah",
"userPass": "blah",
"releaseVersion": "1"
}
}
You can include this on your tests, commands (Cypress.custom.commands), etc.
before(() => {
const data = cy.fixture("fixtureFile");
cy.login(data);
cy.createTagMedia(data);
})
You could literally do something like the above. With your Cypress.Commands in your command.ts or js whichever you're using.
And make the commands take in a parameter. Then the above before hook would just be in your tests.

Mock specific graphql request in cypress when running e2e tests

When running e2e tests with Cypress, my goal is to mock a specific graphql query.
Currently, I can mock all requests like this:
cy.server();
cy.route('POST', '/graphql', {
data: {
foo: 'bar'
},
});
The problem is that this mocks all /graphql queries. It would be awesome if I somehow could say:
cy.route('POST', '/graphql', 'fooQuery', {
data: {
foo: 'bar'
},
});
In our application, we are using Apollo Graphql - and thus all queries are named.
With cypress 6.0 route and route2 are deprecated, suggesting the use of intercept. As written in the docs (https://docs.cypress.io/api/commands/intercept.html#Aliasing-individual-GraphQL-requests) you can mock the GraphQL requests in this way:
cy.intercept('POST', '/api', (req) => {
if (req.body.operationName === 'operationName') {
req.reply({ fixture: 'mockData.json'});
}
}
One way to go about it is to provide the mocked data for the graphql operations in question inside one fixture file
cypress/support/commands.js
Cypress.Commands.add('stubGraphQL', (graphQlFixture) => {
cy.fixture(graphQlFixture).then((mockedData) => {
cy.on('window:before:load', (win) => {
function fetch(path, { body }) {
const { operationName } = JSON.parse(body)
return responseStub(mockedData[operationName])
}
cy.stub(win, 'fetch', fetch).withArgs("/graphql").as('graphql');
});
})
})
const responseStub = result => Promise.resolve({
json: () => Promise.resolve(result),
text: () => Promise.resolve(JSON.stringify(result)),
ok: true,
})
//TODO how to get it to stop listening and trying to stub once the list of operations provided in fixture have been stubbed?
example fixture file cypress/fixtures/signInOperation.json (note that there are 2 operations in there and that's how you can specify which response to mock)
{
"SIGNIN_MUTATION": {
"data":{"signin":{"id":"ck896k87jac8w09343gs9bl5h","email":"sams#automation.com","name":"Sam","__typename":"User"}}
},
"CURRENT_USER_QUERY" : {
"data":{"me":{"id":"ck896k87jac8w09343gs9bl5h","email":"sams#automation.com","name":"!!Sam's Mock","permissions":["USER"],"cart":[{"id":"ck89gebgvse9w0981bhh4a147","quantity":5,"item":{"id":"ck896py6sacox0934lqc8c4bx","price":62022,"image":"https://res.cloudinary.com/deadrobot/image/upload/v1585253000/sickfitz/ecgqu4i1wgcj41pdlbty.jpg","title":"MensShoes","description":"Men's Shoes","__typename":"Item"},"__typename":"CartItem"},{"id":"ck89gec6mb3ei0934lmyxne52","quantity":5,"item":{"id":"ck896os7oacl90934xczopgfa","price":70052,"image":"https://res.cloudinary.com/deadrobot/image/upload/v1585252932/sickfitz/i7ac6fqhsebxpmnyd2ui.jpg","title":"WomensShoes2","description":"Women's Shoes","__typename":"Item"},"__typename":"CartItem"},{"id":"ck89gl45psely0981b2bvk6q5","quantity":7,"item":{"id":"ck89ghqkpb3ng0934l67rzjxk","price":100000,"image":"https://res.cloudinary.com/deadrobot/image/upload/v1585269417/sickfitz/eecjz883y7ucshlwvsbw.jpg","title":"watch","description":"Fancy Watch","__typename":"Item"},"__typename":"CartItem"}],"__typename":"User"}}
}
}
in your spec file
cy.stubGraphQL('signInOperation.json')
cy.visit(yourURL)
cy.get(loginButton).click()
With cypress 5.1, using the new route2 command it is very simple to mock GraphQL requests, for example:
cy.route2('/graphql', (req) => {
if(req.body.includes('operationName')){
req.reply({ fixture: 'mockData.json'});
}
});
I just added an if condition to evaluate if the body of the GraphQL request contains certain string as part of the query.
If that is true, then I reply back with a custom body loaded from a fixture.
Documentation for cy.route2():
https://docs.cypress.io/api/commands/route2.html
You can try this if want to use fixture for graphql response:
cy.intercept('POST', '/test_api/graphql', (req) => {
req.continue((res) => {
if (req.body.operationName === 'op_name') {
res.send({ fixture: 'MyFixture/xyz.json' }),
req.alias = 'graphql'
}
})
})

Resources