How to use recurse method in cypress - cypress

*I tried this code first time its working fine the second time it failed got error.
cy.eq() failed because this element is detached from the DOM.
...
Cypress requires elements be attached in the DOM to interact with them.
The previous command that ran was:
> cy.get()
This DOM element likely became detached somewhere between the previous and current command.
Common situations why this happens:
- Your JS framework re-rendered asynchronously
- Your app code reacted to an event firing and removed the element
You typically need to re-query for the element or add 'guards' which delay Cypress from running new commands.
All 2 subject validations failed on this subject.*
code:
recurse(
() => cy.get('selectorA').should(Cypress._.noop),
($button) => $button.length === 0,
{
post() {
cy.get('selectorA').eq(0).trigger('mouseover', { bubbles: false });
cy.get('selectorB').eq(0).click({ force: true }).wait(1000);
},
delay:1000,
limit:10,
timeout:15000
}
)

Related

Expected to find element: #VTK-j_idt1363, but never found it

I am facing an issue on clicking an element. I need an element to be clicked while the network is slow by not using wait() and pause().The page loads after or more than 35sec(350000).Could someone help me to solve the issue,please?
Here is my coding.
it('Test ship',function(){
cy.viewport(1120,800)
cy.visit('url')
cy.get('#LoginForm-nfr_login_authname').type('username')
cy.get('#LoginForm-nfr_login_authid').type('paSSw0rd')
cy.on('uncaught:exception', (err, runnable) => {
return false
})
//These two were the elements present in the same page.The Workspace (first command) gets loaded and error occurs before clicking the second element.
cy.wait[ cy.xpath('//span[normalize-space()="Work Space"]') .click({force: true})]
//here I have used wait
cy.wait(80000)
cy.get('#VTK-j_idt1262').click({force: true})
})
I have tried many possible ways such as alias,etc..,
Please let me know about the solution to wait for page loading without using wait() and pause() command.
error img

Cypress identify a document element inside DOM

I have an application that has a document section showing some notes and I am trying to identify and verify the text ("Release Notes") with Cypress. But all my locating strategies are failing and need some help. Please see if you can recommend some other way to locate this.
DOM:
Below is my code:
cy.get('.release-notes')
.should('be.visible')
.then((text => {
cy.log(text.text()) // blank
cy.log(text) // <span.release-notes>
}))
cy.get('[data="release-notes.html"]').should('be.visible').then((text => {
cy.log(text.text()) // blank
cy.log(text) // <object.release-notes-object>
}))
//Below lines - Fails
//cy.get('h1:contains("Release Notes")') -- Timed out retrying after 4000ms: Expected to find element: h1:contains("Release Notes"), but never found it.
//cy.get('body').should('contains', "Release Notes"); --Timed out retrying after 4000ms: object tested must be an array, a map, an object, a set, a string, or a weakset, but object given
Note: I have "includeShadowDom": true inside my cypress.json
You may be getting blocked by the #document. It does not look like an iframe, but may behave like one.
Try
cy.get('object.release-notes-object')
.its('0.contentDocument').should('exist')
.its('body').should('not.be.undefined').within(() => {
cy.get('h1').should('contain', 'Release Notes')
})
Also turn off chromeWebSecurity in case there's a cross-domain issue
cypress.json
{
"chromeWebSecurity": false
}
Debugging the inner document
Since you can get into the inner document body, in theory you should now be able to query it.
Take a look at the DOM inside body from Cypress' perspective, experiment with timing - the release notes likely to be fetched from a server
cy.get('object.release-notes-object')
.its('0.contentDocument').should('exist')
.its('body').should('not.be.undefined')
.then($body => {
cy.wait(10000) // just for debugging, explicit wait here
.then(() => console.log($body[0].innerHTML)) // inspect the elements inside after wait
})
Another way, add a .should() to retry
cy.wait(10000) // for debugging only, waiting for fetch
cy.get('object.release-notes-object')
.its('0.contentDocument').should('exist')
.its('body', {timeout: 10000}) //vary timeout here
.should('not.be.undefined')
.should('have.descendants', 'h1') // retries the body fetch until true
The difference between the last and adding a timeout to cy.get('h1') is that .should('have.descendants', 'h1') will re-query the body during each retry.
It's possible that there is an empty body element before the release notes are fetched, and it is replaced when the release notes are added.
Before the "includeShadowDom": true global flag we have to do something like this:
cy.get('selector').shadow().find('selector')
Now may be you can omit the .shadow() part and try
cy.get('[app-name="Voyage Planning"]').find('h1:contains("Release Notes")')
You have attributes data-gr-ext-installed and data-new-gr-c-s-check-loaded on the body. These look like they mark the end of something loading.
Add a check for that in #Mihi's solution (which must be nearly correct).
cy.get('object.release-notes-object')
.its('0.contentDocument').should('exist')
.its('body')
.should('not.be.undefined')
.should('have.attr', 'data-gr-ext-installed') // or data-new-gr-c-s-check-loaded
.within(() => {
cy.get('h1').should('contain', 'Release Notes')
})

Cypress: cy.click() failed because this element is detached from the DOM

I'm facing an issue with Cypress 4.7.0. Below is the error code that I'm getting while trying to automate the user creation scenario on the website automationpractice.com
The method cy.click() failed because this element is detached from the DOM.
<input type="password" class="is_required validate form-control" data-validate="isPasswd" name="passwd" id="passwd">
Cypress requires elements to be attached in the DOM to interact with them.
The previous command that ran was:
cy.get()
This DOM element likely became detached somewhere between the previous and current commands.
Common situations why this happens:
Your JS framework is re-rendered asynchronously.
Your app code reacted to an event firing and removed the element.
Code sample for the above scenario:
describe("Cypress demo script", () => {
it("triage DOM issue", () => {
const uuid = () => Cypress._.random(0, 1e6);
const id = uuid();
cy.visit("http://automationpractice.com/index.php");
cy.wait(2000);
cy.contains("Sign in").click();
const email = "tester" + id + "#yopmail.com";
cy.get("#email_create").type(email);
cy.get("#SubmitCreate > span").click();
cy.get("form#account-creation_form").within(($form1) => {
cy.get("input#id_gender1").click();
cy.get("input#customer_firstname").type("Automation");
cy.get("input#customer_lastname").type("tester");
cy.get("input#passwd").click({ force: true }).type("Qwerty#123");
});
});
});
Based on the searches, it was advised to use {force: true}, but it didn't work. Also, I tried to embed the searches within the form, it didn't work either.
As of Cypress version 12, there is new code (internally) that checks for elements detached from the DOM, so instead of throwing the error it now automatically does what the error message suggests and re-queries the selector.
Please see https://docs.cypress.io/guides/references/changelog#12-0-0
Additionally in this release, enhancements were made to how Cypress manages DOM element resolution to reduce the likelihood of hitting detached DOM errors due to maintaining stale DOM references. We've updated our [Retry-ability Guide[(https://docs.cypress.io/guides/core-concepts/retry-ability) with all the details if you'd like to learn more.

cypress.io how to remove items for 'n' times, not predictable, while re-rendering list itself

I've a unpredictable list of rows to delete
I simply want to click each .fa-times icon
The problem is that, after each click, the vue.js app re-render the remaining rows.
I also tried to use .each, but in this cas I got an error because element (the parent element, I think) has been detached from DOM; cypress.io suggest to use a guard to prevent this error but I've no idea of what does it mean
How to
- get a list of icons
- click on first
- survive at app rerender
- click on next
- survive at app rerender
... etch...
?
Before showing one possible solution, I'd like to preface with a recommendation that tests should be predictable. You should create a defined number of items every time so that you don't have to do hacks like these.
You can also read more on conditional testing, here: https://docs.cypress.io/guides/core-concepts/conditional-testing.html#Definition
That being said, maybe you have a valid use case (some fuzz testing perhaps?), so let's go.
What I'm doing in the following example is (1) set up a rendering/removing behavior that does what you describe happens in your app. The actual solution (2) is this: find out how many items you need to remove by querying the DOM and checking the length, and then enqueue that same number of cypress commands that query the DOM every time so that you get a fresh reference to an element.
Caveat: After each remove, I'm waiting for the element (its remove button to be precise) to not exist in DOM before continuing. If your app re-renders the rest of the items separately, after the target item is removed from DOM, you'll need to assert on something else --- such as that a different item (not the one being removed) is removed (detached) from DOM.
describe('test', () => {
it('test', () => {
// -------------------------------------------------------------------------
// (1) Mock rendering/removing logic, just for the purpose of this
// demonstration.
// -------------------------------------------------------------------------
cy.window().then( win => {
let items = ['one', 'two', 'three'];
win.remove = item => {
items = items.filter( _item => _item !== item );
setTimeout(() => {
render();
}, 100 )
};
function render () {
win.document.body.innerHTML = items.map( item => {
return `
<div class="item">
${item}
<button class="remove" onclick="remove('${item}')">Remove</button>
</div>
`;
}).join('');
}
render();
});
// -------------------------------------------------------------------------
// (2) The actual solution
// -------------------------------------------------------------------------
cy.get('.item').then( $elems => {
// using Lodash to invoke the callback N times
Cypress._.times($elems.length, () => {
cy.get('.item:first').find('.remove').click()
// ensure we wait for the element to be actually removed from DOM
// before continuing
.should('not.exist');
});
});
});
});

How can I waitFor an element to have children with Geb?

I'm executing a test that runs a functional use of a "click to add" feature.
The user is given a table of items that allow them to click an "Add" button to add the item to their cart.
The button executes an ajax call to append the item to the user's cart. When the item is added successfully, the item is then displayed in the Cart UI. The Cart UI is essentially another table.
//pseudo code
$('.addButton').on('click', function (event) {
$.ajax({
url:...,
success: updateCart
});
});
function updateCart (data) {
// use data to create tr_fragment
$("#cart-ui-target").append(tr_fragment); //new row
}
What I've tried is in the then statement is use waitFor until the #cart-ui-target has a <tr> size greater or equal to 1:
waitFor(5) {$('#cart-ui-target').find('tr').size() >= 1}
However, every once in a while the test fails with the following exception:
geb.waiting.WaitTimeoutException: condition did not pass in 5.0 seconds (failed with exception)
I've even tried to increase the waitFor time to 10 seconds with a 2 second interval, but it still doesn't work:
waitFor(10, 2) {$('#cart-ui-target').find('tr').size() >= 1}
What can I do to make this a better wait and prevent the sporadic failures?
UPDATE
This is what I'm seeing in the log info of phantomjs.
$('#cart-ui-target').find('tr').size()
| | |
| [] 0
[[[[[PhantomJSDriver: phantomjs on LINUX (113358b0-7bf1-11e4-a10e-9f2b2537fa31)] -> tag name: html]] -> css selector: #cart-ui-target]]
I think your trying to test the JavaScript behaviour, not the adding to the cart itself (probably tested in backend logic?).
I think the way to go is to mock $.ajax and call the callback from your mock using fixture data. If your using jasmine this can look something this:
it('should add something when something is succesfully addes', function() {
data = 'data_for_callback_here';
spyOn($, 'ajax').andCallFake(function(data) { data.success(data); } );
$('.addButton').click();
expect() // Expect something
});

Resources