When I run a Cypress test, every time the test executes some action the URL is shown in the Info Panel on the left. Unfortunately the URL is very long and it makes the Info Panel unreadable.
Is there a way to hide to URL?
describe('Test', () => {
it('load page', () => {
cy.visit("https://ultimateqa.com/automation")
cy.get('.et_pb_text_inner > ul > :nth-child(3) > a').click()
})
})
This feels a bit hacky - the gist is to watch for log changes and truncate the long ones.
You could make the criteria more precise to your needs, but this works
// top of the test, or in /cypress/support/e2e.js
Cypress.on('log:changed', (log, interactive) => {
const logs = window.top.document.querySelectorAll('.command-message-text')
const last = [...logs][logs.length - 1]
if (last.innerText.length > 20) {
last.innerText = last.innerText.slice(0, 20) + '...'
}
})
Related
I have the following Cypress test:
describe(`sign in`, () => {
const getIframe = () => {
return cy
.get('iframe[id=my-iframe]')
.its('0.contentDocument.body')
.should('not.be.empty')
.then(cy.wrap)
.as('iFrame')
}
it('signs in', () => {
cy.visit(PATH.SIGN_IN)
getIframe()
cy.get('#iFrame').find('input[name=email]').click().type('user#test.test')
cy.get('#iFrame').find('input[name=password]').click().type('test-password')
cy.get('#iFrame').find('button').contains(/^Sign in$/).click()
// signin is successful and the contents of the page refresh to show a page containing <h3>Hello user</h3>
cy.get('#iFrame').find('h3').contains('Hello user').should('be.visible')
// the above line fails...
})
})
The test fails on the last line. Even though the contents inside the iframe successfully refresh and Hello user is visible, Cypress is not able to find this text inside cy.get('#iFrame').
How should I handle this situation correctly with Cypress?
I am new to Cypress and some of the things that I expect to work have really weird issues.
For example, I am trying to get the value of a column in a table and use the value in a search input. I have done it like this:
it('should filter', () => {
let id = 0;
cy.get('[data-cy=data-table-row]').should('have.length', 25);
cy.get('[data-cy=data-table-row]:nth-child(1) > .cdk-column-id').should(($div1) => {
id = $div1.text();
expect(id).not.to.eq(0);
});
//expect(id).not.to.eq(0);
cy.get('[data-cy=table-filters-search]').find('input').type(id);
cy.get('[data-cy=data-table-row]').should('have.length', 1);
cy.get('[data-cy=data-table-row]:nth-child(1) > .cdk-column-id').should(($div1) => {
expect(id).to.eq($div1.text());
});
});
But when I run this, I get an error stating that [data-cy=data-table-row] has a length of 25 not 1.
It turns out that the id variable I am using is not accessible outside the should method. I assume that's because it is a promise.
If I try to do this:
it('should filter', () => {
let id = 0;
cy.get('[data-cy=data-table-row]').should('have.length', 25);
cy.get('[data-cy=data-table-row]:nth-child(1) > .cdk-column-id').should(($div1) => {
id = $div1.text();
expect(id).not.to.eq(0);
cy.get('[data-cy=table-filters-search]').find('input').type(id);
cy.get('[data-cy=data-table-row]').should('have.length', 1);
cy.get('[data-cy=data-table-row]:nth-child(1) > .cdk-column-id').should(($div1) => {
expect(id).to.eq($div1.text());
});
});
});
The test goes mental and tries to get the [data-cy=table-filters-search] over and over and over again.
I am not sure why.
Is there an easier way to grab the innerText of a div and store it to compare later?
As someone gave a response, I tried this:
it('should filter', () => {
let variables = {};
cy.get('[data-cy=data-table-row]').should('have.length', 25);
cy.get('[data-cy=data-table-row]:nth-child(1) > .cdk-column-id').then(($div1) => {
variables.id = $div1.text();
expect(variables.id).not.to.be.undefined;
});
console.log(variables);
expect(variables.id).not.to.be.undefined;
cy.get('[data-cy=table-filters-search]').find('input').type(variables.id);
cy.get('[data-cy=data-table-row]').should('have.length', 1);
cy.get('[data-cy=data-table-row]:nth-child(1) > .cdk-column-id').then(($div1) => {
expect(variables.id).to.eq($div1.text());
});
});
But the test fails on the second expect(variables.id).not.to.be.undefined;
Closure problem
The problem with the first example is that the test runs in two phases. The first phase sets up the commands in the queue, and the second runs them.
During the first phase, .type(id) "captures" the current value of id (which is "0") and in the second phase that's the value that gets used.
You can fix it in a couple of ways, with an alias or moving the type(id) inside the callback, as per your second example.
This gets around the closure problem by deferring cy.get(...).find('input').type(id) until the id has actually changed.
Retry problem
The problem with the second example is that should() with an expect in the callback will retry until it succeeds or times out.
Something in the callback is continuously failing (or an error is thrown) causing a continuous retry. It should time out, not sure why that doesn't happen.
You can separate the parts of the callback into two sections, and use a .then() which does not attempt to retry.
cy.get('[data-cy=data-table-row]:nth-child(1) > .cdk-column-id')
.should(($div1) => {
id = +$div1.text(); // Note $div1.text() is always a string
// so convert with "+" to compare numbers
expect(id).not.to.eq(0) // otherwise this always succeeds
})
.then(($div1) => { // use a then which does not retry
id = $div1.text();
cy.get('[data-cy=table-filters-search]').find('input').type(id);
cy.get('[data-cy=data-table-row]').should('have.length', 1);
cy.get('[data-cy=data-table-row]:nth-child(1) > .cdk-column-id')
.should(($div1) => {
expect(id).to.eq($div1.text())
});
})
Or
cy.get('[data-cy=data-table-row]:nth-child(1) > .cdk-column-id')
.invoke('text') // find it's text
.should('not.eq', '0') // passes on the id
.then(id => {
cy.get('[data-cy=table-filters-search]').find('input').type(id);
cy.get('[data-cy=data-table-row]').should('have.length', 1);
cy.get('[data-cy=data-table-row]:nth-child(1) > .cdk-column-id')
.should($div1 => expect(id).to.eq($div1.text()) );
})
If you want to use it within a single case - Allias is the cypress way:
cy.wrap('value').as('variable') //setting the variable
cy.get('#variable') // with this you can call it at any time in the test after aliasing
.then(variable =>{
//here you can use it
})
For a variable to use on multiple cases I recommend using an object as a Reference
let variables = {} //This would need to be declared on the scope you wish to use it in
cy.get('element')
.then($el => {
variables.elText = $el.text() //Assigning the value
})
cy.log(variables.elText) //You can call it later like this
I am writing a test to confirm page sizing is working properly and after I got the success I purposely configured the test to fail. The should fails, but the test just keeps running instead of failing out.
describe("Customers' Page size changes correctly", function () {
it("Should change the page size properly.", () => {
// SETTING UP THE INTERCEPT DETECTION THAT SAYS THAT THE PAGE HAS BEEN LOADED
cy.intercept(
"/api/v1/customers/?action=customers&pageSize=10&pageNumber=1&searchText=&filterByCustomerName=false&orderBy=CreatedOn&orderDirection=desc&partyDateStart=&partyDateEnd=&customerStatus=Active"
).as("rSearchRequest10");
cy.getToken().then(() => {
cy.visit("customers");
});
// Standard page size of 10
cy.wait("#rSearchRequest10").then(() => {
// Defaults to 10, should get 10 results.
const listResults = cy
.get("[data-testid=customersList]")
.find("[data-testid=customerListItem]");
assert.isNotEmpty(listResults);
listResults.should("have.length", 11);
});
});
});
I get the message
expected [ <div#eu.MuiBox-root.jss166>, 9 more... ] to have a length of 11 but got 10
Then the timer just keeps running. I have no further tests and feel the test should have failed at this point.
What does cy.getToken() look like? –
Cypress.Commands.add("getToken", () => {
cy.intercept('dc.services.visualstudio.com/v2/track', {
fixture: 'External/track-service-response.json'
});
cy.request('GET', 'test/bridge'); });
The solution is below. My final code looks like this. The expect within the then properly throws the error and stops the execution of that test.
it("Should default to page size 10", () => {
cy.intercept(
"/api/v1/customers/?action=customers&pageSize=10&pageNumber=1&searchText=&filterByCustomerName=false&orderBy=CreatedOn&orderDirection=desc&partyDateStart=&partyDateEnd=&customerStatus=Active"
).as("rSearchRequest10");
cy.getToken().then(() => {
cy.visit("customers");
});
// Standard page size of 10
cy.wait("#rSearchRequest10").then(() => {
// Defaults to 10, should get 10 results.
cy.get("[data-testid=customerListItem]").then((listing) => {
expect(listing).to.have.lengthOf(10, "Should be exactly 10 results");
});
});
});
Unsure why it continues to run but suspect part of your problem has to to with async nature of cypress selectors.
Instead of setting const listResults to a var you should chain off of the cy.get or .find with a new .then()
in this case it seems like you could do without the assert.isNotEmpty and just go straight to .should()
cy.get("[data-testid=customersList]")
.find("[data-testid=customerListItem]")
.should("have.length", 11);
You can also try using "expect":
// Standard page size of 10
cy.wait("#rSearchRequest10").then(() => {
// Defaults to 10, should get 10 results.
var searchResponse = cy.get("[data-testid=customerListItem]").then(listing => {
expect(listing).to.have.lengthOf(11, "Your error message for failing");
});
});
I would like Cypress to auto-generate an it block for each item in the below hash. When I currently run cypress, it picks up on the second test just fine, but ignores the one with the while loop. How can I resolve this? I'd prefer not to have to write out an explicit it block for each item in the map.
const testDataMappings = {
1: {e2eTestName: 'test-one'},
2: {e2eTestName: 'test-two'},
3: {e2eTestName: 'test-three'},
}
// Does not work
describe('My Tests', function () {
let i = 1;
while (i < testDataMappings.length + 1) {
let entry = testDataMappings[i];
it("Should Do The Thing Correctly For" + entry.e2eTestName, () => {
const standardCaseUrl = Cypress.config().baseUrl + "?profile_id=" + String(i);
cy.visit(standardCaseUrl);
cy.wait(5000);
cy.get('.some-div-class-name').compareSnapshot(entry.e2eTestName, 0.0);
});
i +=1;
}
// works
describe('Another Describe block', function () {
it('Should do the thing', () => {
const standardCaseUrl = Cypress.config().baseUrl + "?profile_id=1";
cy.visit(standardCaseUrl);
cy.wait(5000);
cy.get('.some-div-class-name').compareSnapshot('some-snapshot-name', 0.0);
});
});
});
Console logs don't seem to show up, so don't have much insight into what is happening.
There is an example in the Cypress Real World App, a payment application to demonstrate real-world usage of Cypress testing methods, patterns, and workflows. The example is in the transaction feeds spec and uses lodash each to iterate over a feedViews object and dynamically generate the tests for each feed.
The way i am polling tasks for async POST call, is it correct??? Because program control doesn't enter 'while' loop in spec file. Please help!
Previous query: How to return a value from Cypress custom command
beforeEach(function () {
cy.server()
cy.route('POST', '/rest/hosts').as("hosts")
})
it('Create Host', function () {
let ts =''
let regex = /Ok|Error|Warning/mg
// Some cypress commands to create a host. POST call is made when I create a host. I want to poll
// task for this Asynchronous POST call.
cy.wait("#hosts").then(function (xhr) {
expect(xhr.status).to.eq(202)
token = xhr.request.headers["X-Auth-Token"]
NEWURL = Cypress.config().baseUrl + xhr.response.body.task
})
while((ts.match(regex)) === null) {
cy.pollTask(NEWURL, token).then(taskStatus => {
ts= taskStatus
})
}
})
-------------------------
//In Commands.js file, I have written a function to return taskStatus, which I am using it in spec
file above
Commands.js -
Cypress.Commands.add("pollTask", (NEWURL, token) => {
cy.request({
method: 'GET',
url: NEWURL ,
failOnStatusCode: false,
headers: {
'x-auth-token': token
}
}).as('fetchTaskDetails')
cy.get('#fetchTaskDetails').then(function (response) {
const taskStatus = response.body.task.status
cy.log('task status: ' + taskStatus)
cy.wrap(taskStatus)
})
})
You can't use while/for loops with cypress because of the async nature of cypress. Cypress doesn't wait for everything to complete in the loop before starting the loop again. You can however do recursive functions instead and that waits for everything to complete before it hits the method/function again.
Here is a simple example to explain this. You could check to see if a button is visible, if it is visible you click it, then check again to see if it is still visible, and if it is visible you click it again, but if it isn't visible it won't click it. This will repeat, the button will continue to be clicked until the button is no longer visible. Basically the method/function is called over and over until the conditional is no longer met, which accomplishes the same thing as a loop, but actually works with cypress.
clickVisibleButton = () => {
cy.get( 'body' ).then( $mainContainer => {
const isVisible = $mainContainer.find( '#idOfElement' ).is( ':visible' );
if ( isVisible ) {
cy.get( '#idOfElement' ).click();
this.clickVisibleButton();
}
} );
}
Then obviously call the this.clickVisibleButton() in your test. I'm using typescript and this method is setup in a class, but you could do this as a regular function as well.
With recursion, you can simulate loops.
Add this to your custom commands file (/cypress/support/commands.js):
Cypress.Commands.add('recursionLoop', {times: 'optional'}, function (fn, times) {
if (typeof times === 'undefined') {
times = 0;
}
cy.then(() => {
const result = fn(++times);
if (result !== false) {
cy.recursionLoop(fn, times);
}
});
});
On your tests, just define a function that does what you want for one iteration, and return false if you don't want to iterate again.
cy.recursionLoop(times => {
cy.wait(1000);
console.log(`Iteration: ${times}`);
console.log('Here goes your code.');
return times < 5;
});
while loop is not working for me, so as a workaround I did a for loop, a sort of while loop with a timeout of retries
let found = false
const timeout = 10000
for(let i = 0; i<timeout && !found;i++){
if(..){
// exiting from the loop
found = true
}
}
it is not helpful for everyone, I know.