Unable to read data from fixture file after writing it - cypress

I'm writing a URL with an ID in a .json file that I'm extracting from the API, but the problem is that the cy.log() is printing the data before it is wrote in the file. Because the AppelData.json has the written data, but cy.log() prints nothing.
After the second run, the cy.log() prints the previous data from AppelData.json.
So how do I make, that cy.log() to print the data from AppelData.json only after it is been written?
describe('Creer un appel modal', () => {
beforeEach(() => {
cy.fixture('AppelData').then(function(data) {
this.AppelData = data
})
cy.visit('/')
cy.loginAsAdmin()
})
it('Create an intervention request using existing site', function() {
navigateTo.plusAppelButton()
onAppelModal.SelectSite(this.AppelData.Site)
onAppelModal.SelectMetier(this.AppelData.Metier)
onAppelModal.FillMotif(this.AppelData.Motif)
cy.intercept('POST','/documents/datatable/intervention_request/**').as('response')
cy.contains('Valider').click()
cy.wait('#response').get('#response').then(xhr => {
console.log(xhr)
cy.readFile('cypress/fixtures/AppelData.json').then(AppelData => {
AppelData.AppelID = xhr.request.url.replace(/\D/g,'').replace(/3/, '')
cy.writeFile('cypress/fixtures/AppelData.json', AppelData)
cy.log(this.AppelData.AppelID) // logs no value on the first run, and prints old value from the 2nd run
})
})
})
})
Thank you!!

Assuming that that the value is updated into the fixtures file, you can do this:
it('Create an intervention request using existing site', function () {
navigateTo.plusAppelButton()
onAppelModal.SelectSite(this.AppelData.Site)
onAppelModal.SelectMetier(this.AppelData.Metier)
onAppelModal.FillMotif(this.AppelData.Motif)
cy.intercept('POST', '/documents/datatable/intervention_request/**').as(
'response'
)
cy.contains('Valider').click()
cy.wait('#response')
.get('#response')
.then((xhr) => {
console.log(xhr)
cy.readFile('cypress/fixtures/AppelData.json').then((AppelData) => {
AppelData.AppelID = xhr.request.url.replace(/\D/g, '').replace(/3/, '')
cy.writeFile('cypress/fixtures/AppelData.json', AppelData)
})
})
cy.readFile('cypress/fixtures/AppelData.json').then((AppelData) => {
cy.log(AppelData.AppelID)
})
})

Related

Cypress test: Writing multiple elements to database failed

Trying to write multiple elements into database failed, only the last one is written:
// my_test.cy.js
import CreateProductPage from "../pages/CreateProductPage";
describe('product detail page', () => {
beforeEach(() => {
cy.login('admin', 'shop')
})
it('should print typed product', () => {
cy.createProduct(null, 'Component Product 0', '0')
CreateProductPage.elements.productDetailTabs().should('exist') // <--- to detect that the entity is written
cy.createProduct('combinable', 'Combined Test Product', '0')
CreateProductPage.elements.productDetailTabs().should('exist') // <--- to detect that the entity is written
})
})
// commands.js
Cypress.Commands.add('createProduct', (type, name, grossPrice) => {
cy.visit('/#/sw/product/create')
CreateProductPage.elements.productDetailTabs().should('not.exist').then(() => {
if(type === 'combinable') {
CreateProductPage.elements.radioBtnCombinableProduct().click()
}
CreateProductPage.elements.inputProductName().clear().type(name)
CreateProductPage.elements.inputPriceFieldGross().type(grossPrice)
SwPageHeader.elements.btnProductSave().click()
})
})
Questions:
This failed because of asynchronous nature of cypress?
If so, how to interrupt? Chaining with then(), the behavior is the same
With this is code (adding wait()) it works, but i'm looking for the right way
// my_test.cy.js
describe('product detail page', () => {
beforeEach(() => {
cy.login('admin', 'shop')
})
it('should print typed product', () => {
cy.createProduct(null, 'Component Product 0', '0')
CreateProductPage.elements.productDetailTabs().should('exist')
cy.wait(300)
cy.createProduct('combinable', 'Combined Test Product', '0')
CreateProductPage.elements.productDetailTabs().should('exist')
})
})
EDIT 1
// pages/CreateProductPage.js
class CreateProductPage {
elements = {
productDetailTabs: () => cy.get('div.sw-product-detail-page__tabs'),
radioBtnCombinableProduct: () => cy.get('.sw-product-detail-base__info input#type_combinable_product-0'),
radioBtnUnCombinableProduct: () => cy.get('.sw-product-detail-base__info input#type_combinable_product-1'),
inputProductName: () => cy.get('input#sw-field--product-name'),
inputPriceFieldGross: () => cy.get('div.sw-list-price-field__price input#sw-price-field-gross'),
}
}
module.exports = new CreateProductPage();
If the problem is one of waiting, you will need to figure out something that indicates to the user that the save was successful and test that.
For example, if there was a toast message on screen:
...
SwPageHeader.elements.btnProductSave().click()
cy.contains('span', 'Product was saved').should('be.visible')
Blockquote
This failed because of asynchronous nature of cypress?
Code behaves synchronously if you have only cy commands inside the code block.
As suggested in https://stackoverflow.com/a/74721728/9088832 you should wait for some element to appear or for an API request to be completed that is responsible for the product creation.

API response not getting in cypress

///<reference types = "cypress"/>
describe('API Test Suite', ()=>{
it('Send GET Request',()=>{
cy.request('http://ptsv2.com/t/fu807-1554722621/post').then((res)=>{
expect(res.body).has.property('username', 'automate')
})
})
})
I'm trying to validate username field but its throwing error
expected '{\r\n "username": "automate",\r\n "password": "everything",\r\n "targetUrl": "http://ptsv2.com/t/7ty82-1554722743/post"\r\n}' to have property 'username'
SAMPLE RESULT on POSTMAN:
{
"username": "automate",
"password": "everything",
"targetUrl": "http://ptsv2.com/t/7ty82-1554722743/post"
}
The body property needs parsing - true, but there are less hacky ways of doing it
cy.request('GET', 'http://ptsv2.com/t/fu807-1554722621/post')
.its('body')
.then(JSON.parse)
.should('have.property', 'username', 'automate')
or
cy.request('GET', 'http://ptsv2.com/t/fu807-1554722621/post')
.then(res => JSON.parse(res.body))
.should('have.property', 'username', 'automate')
You can do something like this:
///<reference types = "cypress"/>
describe('API Test Suite', () => {
it('Send GET Request', () => {
cy.request('GET', 'http://ptsv2.com/t/fu807-1554722621/post').then(
(res) => {
const user = 'automate'
cy.wrap(res.body).should('contain', `"username": "${user}"`)
}
)
})
})
On, digging deeper into your response body, I could see that there are empty spaces and because of which it was failing. So before asserting you have to remove the empty spacing and then apply the has.property assertion.
///<reference types = "cypress"/>
describe('API Test Suite', () => {
it('Send GET Request', () => {
cy.request('GET', 'http://ptsv2.com/t/fu807-1554722621/post').then(
(res) => {
expect(JSON.parse(JSON.stringify(JSON.parse(res.body)))).has.property(
'username',
'automate'
)
}
)
})
})
cy.request yields the response as an object so you can access it using .its()
cy.request('your-request-url')
.its('response.body')
.should('have.property', 'username','automate')
Alternatively, you can use cy-spok to check for properties and values in a more readable code format.
const 'spoke = require('cy-spok')
// in your test
cy.request('your-request-url')
.its('response.body')
.should(spok({
username: 'automated',
password: 'everything',
//other props
})
)

Cypress: Code in page object file is being executed as a test case before the before hook

I was trying to use a condition as a function within a page object.
class Folders {
DropdownCheckFunction(){
cy.get('.more-less-container').then((dropdown) => {
if(dropdown.find('.name').contains('More')){
cy.get('more-less-container').click()
}
else{
console.log('folders are in expanded state')
}
})
}
Drafts(){
this.DropdownCheckFunction()
cy.get('.category-content').find('[title="Drafts"]').click()
.get(".folder-details").should('contain.text', 'Drafts')
}
Issue here is that the page object is getting executed as a test case and is happening before the code in BEFORE hook is being run. Below is the test file code
describe('Testing all cases related to Drafts', () => {
before(() => {
cy.login()
})
})
it('Needs to open the Drafts folder', () => {
openFolder.Drafts()
});
Attaching the error seen on the test runner for reference
The problem is bad formatting.
If you line up your code, you can see it - your test is outside the scope of the before().
describe('Testing all cases related to Drafts', () => {
before(() => {
cy.login()
})
}) // move this bracket to below the `it()`
it ('Needs to open the Drafts folder', () => {
openFolder.Drafts()
});

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

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.

Resources