Cypress error: cy.find() failed because the page updated as a result of this command, but you tried to continue the command chain - cypress

I wrote the following to select values of a dropdown and assert selected value to make sure it is selected, one by one.
When("User should see example Cover option with values {string}", (exampleCovers) => {
const exampleCover = exampleCovers.split("|");
exampleCover.forEach(chooseCoverLimit);
function chooseCoverLimit(item) {
acoSection.popups(`aco_popup`).as('popupACO').then(function () {
cy.get('#popupACO').contains('text');
acoSection.dropDowns(`example_cover`).as('coverLimit').select(item, { force: true }).then(function () {
cy.get('#coverLimit').find(':selected').should('have.text', item)
})
})
}
});
This works locally on Cypress Test Runner, as well as through headless from the CLI.
Cypress results on local machine CLI
But when I run on Jenkins, some tests get failed.
Cypress results on Cypress Cloud
I get the following CypressError on Cypress Cloud.
Timed out retrying after 10000ms: cy.find() failed because the page updated as a result of this command, but you tried to continue the command chain. The subject is no longer attached to the DOM, and Cypress cannot requery the page after commands such as cy.find().
Common situations why this happens:
Your JS framework re-rendered asynchronously
Your app code reacted to an event firing and removed the element
You can typically solve this by breaking up a chain. For example, rewrite:
cy.get('button').click().should('have.class', 'active')
to
cy.get('button').as('btn').click()
cy.get('#btn').should('have.class', 'active')
https://on.cypress.io/element-has-detached-from-dom
As per the suggestion, I tried breaking cy.get('#coverLimit').find(':selected').should('have.text', item) into two but it still didn't solve the problem. The same code works for one environment but not another.

I've made following changes to make it work.
When("User should see example Cover option with values {string}", (exampleCovers) => {
const exampleCover = exampleCovers.split("|");
exampleCover.forEach(chooseCoverLimit);
function chooseCoverLimit(item) {
acoSection.popups(`aco_popup`).as('popupACO').then(function () {
cy.get('#popupACO').contains('text')
acoSection.dropDowns(`example_cover`).select(item, { force: true }).then(function () {
acoSection.dropDowns(`example_cover`).find(':selected').should('have.text', item).wait(1000)
})
})
}
});

Related

How to get cypress to block datadog requests

We recently installed datadogRUM in our application and now so many DD events kick off in my cypress test that they cause a timeout and failure
I have tried cy.intercept in multiple ways:
cy.intercept('POST', 'https://rum.browser-intake-datadoghq.com/*', {
statusCode: 202,
body: {
},
}).as('datadogRUM');
cy.intercept('POST', 'https://rum-http-intake.logs.datadoghq.com/*', {});
cy.intercept(/\.*datadog.*$/, (req) => {
console.log('DATADOG INTERCEPTED');
req.reply("console.log('datadog intercept');");
});
cy.intercept({
method: 'POST',
url: '/\.*datadog.*$/'
}, req => {
req.destroy();
});
cy.intercept('POST', 'https://rum-http-intake.logs.datadoghq.com/*', { forceNetworkError: true });
just to start. I feel like I've tried every possible variation. I also created a cypress.json file in my /cypress folder
{
"blockHosts": "*datadoghq.com/*"
}
I get hundreds of calls back in my network tab to https://rum.browser-intake-datadoghq.com/api/v2/rum with the preview of console.log('datadog intercept') as I've intercepted them. They all display the solid blue line as if they are being intercepted and blocked. When I set the intercept to an alias I see the alias in my cypress runner window. But there are no 503s or 404s anywhere. The page still fills up with events, cypress gets overloaded, and my test times out.
I even tried copying the data-dog-rum.ts from the src/utils folder to cypress/utils and either commenting out everything or setting the sampleRate to 0, no dice.
EDIT: I am able to get the test passing by adding
Cypress.on('uncaught:exception', () => {
// returning false here prevents Cypress from
// failing the test
return false;
});
to my support/index.js but now whether I add a cy.intercept in my test makes absolutely no difference. The page still fills up with datadog requests regardless, and whether they come back as 200/pending/cancelled, they still delay a single it block in a spec to where it takes 60 seconds to run instead of approx 10 seconds
You can use javascript to perform the stub inside the routeHandler
cy.intercept('*', (req) => { // look at everything
if (req.url.includes('datadoghq')) { // add more conditions if needed
req.reply({}) // prevent request reaching the server
}
})
blockhosts should work with
Pass only the host
{
"blockHosts": "*datadoghq.com"
}

Cypress: How does custom event handler `cy.task()` work?

I understand what cy.task() provides. It allows Cypress developers to create an event handler with the intent outside of the Cypress workspace.
Correct me if my description is not correct.
What I would like to understand is how cy.task() works within the Cypress Test Framework.
For example, from Cypress documentation, cy.task() usage. It does what is expected, but how?
// in plugins file
module.exports = (on, config) => {
on('task', {
log(message) {
console.log(message)
return null
},
})
}
The diagram below is a work in progress, and I want to add to it where cy.task() are handled:
Thank you

Allow cy.wait to fail

After reading a lot of docs and try to find a solution to my problem I didn't find anything, so here we go.
I have the following problem testing my end to end flow, the flow I'm testing does launch requests continuously but in one case I expect these requests to stop. In other words, I want to throw an error if the request is made and continue with errors when hits the timeout without any request.
cy.wait('#my-request', { timeout: 20000 })
I expect this to timeout if the app works fine so I tried to do this.
cy.wait('#my-request', { timeout: 20000 })
.its('status').should('not.eq', 404)
.its('status').should('not.eq', 200);
I expected to execute the chained tasks but this only happens when the request is made, as well as tried to use a .then but I have the same problem.
Adding a global on fail event can help us but also limits to not execute additional code when this test is failing and we force it to be marked as done.
In the test definition we can add the done callback like in the example.
it('Description', (done) => {
// other test stuff
cy.on('fail', (err) => {
if (err.name === 'CypressError' && err.message.includes('routeAlias') && err.message.includes('Timed out')) {
done();
return true;
}
throw err;
});
cy.wait('#routeAlias', { timeout: 20000 })
.then(() => {
throw new Error('Error request found.');
});
});
// Any remaining code won't be executed if you need to reset something you need to create a new step, like in my case I did a new step to click a cancel button and prepare the app for the next test.
Now our test passes when this specific error is caught but any other error will lead to a test error.
This kind of workarounds are not recommended by cypress but unless cypress adds a catch to manage some errors it's the only way to fix my problem.

Cypress Uncaught Assertion Error despite cy.on('uncaught:exception')

In relation to the following error:
Uncaught Error: Script error.
Cypress detected that an uncaught error was thrown from a cross origin script.
We cannot provide you the stack trace, line number, or file where this error occurred.
Referencing https://docs.cypress.io/api/events/catalog-of-events.html#To-catch-a-single-uncaught-exception
I am trying to run a test that fills out a form and clicks the button to submit:
it('adds biological sample with maximal input', function(){
cy.on('uncaught:exception', (err, runnable) => {
expect(err.message).to.include('of undefined')
done()
return false
});
cy.get('a').contains('Add biological sample').click();
. . .
cy.contains('Action results');
});
I get an error despite my spec containing the following:
cy.on('uncaught:exception', (err, runnable) => {
expect(err.message).to.include('of undefined')
done()
return false
});
Here's an image of the test failing .
The error in the bottom left reads,
Error: Uncaught AssertionError: expected '$f is not defined\n\nThis
error originated from your application code, not from Cypress.
\n\nWhen Cypress detects uncaught errors originating from your
application it will automatically fail the current test.\n\nThis
behavior is configurable, and you can choose to turn this off by
listening to the \'uncaught:exception\'
event.\n\nhttps://on.cypress.io/uncaught-exception-from-application'
to include 'of undefined'
(https://www.flukebook.org/_cypress/runner/cypress_runner.js:49186)
It seems that I am taking Cypress's advice and not getting the desired result. Any suggestions? Has this happened to anyone else?
Can you please remove expect(err.message).to.include('of undefined') and done() from the cypress exception block and add the below piece of code inside the test & run the test again
Cypress.on('uncaught:exception', (err, runnable) => {
// returning false here prevents Cypress from
// failing the test
return false
})
The easiest way to fix this is to add the following to the top of your spec:
Cypress.on('uncaught:exception', (err, runnable) => {
return false;
});
This gets the same indentation level as your "it" blocks, nested directly under "describe". It will cause cypress to ignore all uncaught JS exceptions.
In the question, Atticus29 expects "of undefined" to be present in the error message, but the error doesn't actually contain that string. He could change
expect(err.message).to.include('of undefined')
to
expect(err.message).to.include('is not defined')
then it will pass.
To turn off all uncaught exception handling in a spec (recommended)
https://docs.cypress.io/api/events/catalog-of-events.html#To-turn-off-all-uncaught-exception-handling
To catch a single uncaught exception and assert that it contains a string
https://docs.cypress.io/api/events/catalog-of-events.html#To-catch-a-single-uncaught-exception
Although the fix of suppressing Cypress.on sometimes fix the problem, it doesn't really reveal the root problem. It's still better to figure out why you are having an unhandled error in your code (even in the test).
In my case, my form submission forward the page to another page (or current page), which causes re-render. To fix it, I need to call preventDefault.
const onSubmit: React.FormEventHandler<HTMLFormElement> = (e) => {
e.preventDefault();
};
Every problem is a bit different, the above is only one example. Try to think about what your test actually does in the real site.
Official docs suggest that the cypress.on method is placed in "cypress/suport/e2e.js"
Docs 👉 https://docs.cypress.io/guides/core-concepts/writing-and-organizing-tests#Support-file
CMD + F "Support File"
Cypress.on('uncaught:exception', (err, runnable) => {
// returning false prevents Cypress from failing the test
if (err.message.includes('Navigation cancelled from')) {
console.log('🚀 TO INFINITY AND BEYOND 🚀')
return false
}
})
Add these lines Before your Test Suit.
Cypress.on('uncaught:exception', (err, runnable) => {
// returning false here prevents Cypress from
// failing the test
return false
});

Jasmine/Protractor: stop test on failure in beforeEach

I am currently writing tests protractor and I was wondering if there is some possibility to cancel test execution as soon as something in the beforeEach fails (and return some useful message like "precondition failed: could not login user").
I.e. I have some helper methods in the beforeEach that login the user and then do some setup.
beforeEach:
1) login user
2) set some user properties
Obviously it does not make any sense to execute the 2nd step if the first one fails (actually its quite harmful as the user gets locked which is not nice). I tried to add an "expect" as part of the 1st step, but the 2nd step was still executed -> fresh out of ideas.
Strictly answering your question and without external dependencies:
beforeEach(function() {
// 1) login user
expect(1).toBe(1);
// This works on Jasmine 1.3.1
if (this.results_.failedCount > 0) {
// Hack: Quit by filtering upcoming tests
this.env.specFilter = function(spec) {
return false;
};
} else {
// 2) set some user properties
expect(2).toBe(2);
}
});
it('does your thing (always runs, even on prior failure)', function() {
// Below conditional only necessary in this first it() block
if (this.results_.failedCount === 0) {
expect(3).toBe(3);
}
});
it('does more things (does not run on prior failure)', function() {
expect(4).toBe(4);
});
So if 1 fails, 2,3,4,N won't run as you expect.
There is also jasmine-bail-fast but I'm not sure how it will behave in your before each scenario.
jasmine.Env.prototype.bailFast = function() {
var env = this;
env.afterEach(function() {
if (!this.results().passed()) {
env.specFilter = function(spec) {
return false;
};
}
});
};
then just call:
jasmine.getEnv().bailFast();
(credit goes to hurrymaplelad who wrote an npm that does just that, however you don't need to use it)
jasmine-bail-fast does exactly what you did overriding the specFilter function, but does it on afterEach. So it will only fail after the first "it" is run. It won't help solving this specific case.
with jasmine2 we can set throwOnExpectationFailure to true.
For example in protractor config:
//protractor.conf.js
exports.config = {
//...
onPrepare: () => {
jasmine.getEnv().throwOnExpectationFailure(true);
}
};

Resources