It would be nice to turn off the Cypress global exception handler per test.
For example, we have our own exception handler that sends the errors to the remote crash reporting server. We could write an e2e test to make sure it works as expected. We can
stub the server route and then raise an error in the window's context on purpose.
cy.window()
.then(win => {
function raise () {
throw new Error(`E2E test error`)
}
win.setTimeout(raise, 100)
})
.wait('@error')
The route is working, but the Cypress own crash handler thinks this error was unhandled and fails the test. If we could turn the Cypress crash handler just for this test, it would make a lot of sense.
We will likely provide an API to turn off all global error handling like this:
Cypress.Errors.defaults({uncaughtExceptions: false, unhandledRejections: false})
And then also provide a local API for a specific test.
it("can receive uncaught exceptions", function(done){
// turn off all error handling for this test
Cypress.Errors.onUncaughtException(false)
// or receive the uncaught exception as a callback
Cypress.Errors.onUncaughtException(function(err){
expect(err.message).to.include("foo failed")
done()
})
cy.visit("/some_page_with_a_js_error")
})
Right now its possible to "hack" this by severing Cypress's ability to detect uncaught errors like this:
cy.visit("/app", {
onBeforeLoad: (win) => {
win.onerror = null
}
})
or simply by doing...
Cypress.Cy.prototype.onUncaughtException = function(){}
The code for this is done, but this has yet to be released. We'll update the issue and reference the changelog when it's released.
Would this fix also enable me to silently ignore timeout errors?
In my case I am waiting on an XHR call which is not supposed to happen.
So I would like to add something like
Cypress.Errors.defaults({onTimeoutError: false})
As a followup questions,
Can I currently do this?
Cypress.Cy.prototype.onTimeoutError = function(){}
Thanks!
No. We're talking about errors from your application, not from cypress commands.
There is no notion of skipping failed Cypress commands especially timeouts because they are really long by default. I can't imagine a reason a test should wait for 30-60 seconds doing nothing just to eventually skip that command and move on.
Commands interact with each other and its likely subsequent commands would just immediately fail anyway.
You need to write your test code in a deterministic way, meaning that you the programmer must be able to decide and make the distinction up front whether or not your application will send out an XHR or not.
If you can't make that distinction then you'll need to find another way to tell Cypress to wait for the state to be reached.
We go into a lot more detail about error handling and why there aren't ways to recover from command errors in our docs here: https://docs.cypress.io/guides/core-concepts/introduction-to-cypress.html#Commands-Are-Not-Promises
Thanks for the quick reply.
I am trying to ensure that a certain XHR call is not made up until a user action warrants it.
Also, both the XHR calls (user invoked/otherwise) have the same parameters so I cant distinguish between the two either.
So I would like to write a negative test to ensure that the call is not made until a specific step.
From the documentation and also what you just mentioned, seems to me there isn't a way to do this. Is this correct?
Thanks again.
The problem with that approach is that its logically impossible to prove something doesn't happen unless you're willing to wait an arbitrary amount of time.
How would Cypress know to stop waiting for the request not to go out? It's impossible to wait on something that hasn't yet if you don't know whether or not it's supposed to happen. For instance what if the request is about to go out? Maybe 1ms from now? Maybe 100ms from now? Maybe 1 sec from now? So on and so forth.
It would end up having to wait a "reasonable" amount of time but once again that's slow, arbitrary, and error prone.
With that said though, you could achieve this yourself in your test code by utilizing the onRequest callbacks in either cy.route or cy.server. Simply setup a route handler and increment a counter variable. Perform the user action and then check the counter's length. If it hasn't been incremented then the request has not gone out.
You'll need to handle the timing mechanisms by knowing how your app works. If for instance the XHR's are debounced then it can get tricky due to reasons I listed above. If you are 100% sure the XHR behavior is deterministic then you'll get accurate results. If it's not 100% then you will have flaky tests.
Thank you so much Bran-Mann, for taking time and explaining it so beautifully. I think I will take the route you suggested "utilizing callbacks and setting up counter"
Appreciate your help! Thanks 馃憤
Fixed in 0.20.0
What ended up being the sanctioned way to work around this? All I can find is this note in the docs, which doesn't seem to mention how to turn uncaught error handling off: https://docs.cypress.io/guides/references/error-messages.html#Cypress-detected-that-an-uncaught-error-was-thrown-from-a-cross-origin-script
It's part of a different error message than that one which redirect you here:
https://docs.cypress.io/api/events/catalog-of-events.html#Uncaught-Exceptions
Most helpful comment
Right now its possible to "hack" this by severing Cypress's ability to detect uncaught errors like this:
or simply by doing...