Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: handle promises rejected with undefined gracefully #29454

Merged
merged 2 commits into from
May 6, 2024

Conversation

AtofStryker
Copy link
Contributor

@AtofStryker AtofStryker commented May 1, 2024

Additional details

In instances where Promise.reject(undefined) inside of the cypress spec bridge, errors were not being recreated correctly as the promise was expected to reject with an error instance. This PR solves that by throwing back to cypress with an unknown error in the case the object cannot be recreated.

Steps to test

added a driver regression test to show the failed then passing behavior with fix

without fix
Screenshot 2024-05-01 at 1 43 25 PM
with fix
Screenshot 2024-05-01 at 1 43 34 PM

How has the user experience changed?

PR Tasks

it('Fails gracefully if origin page throws undefined', () => {
cy.visit('http://barbaz.com:3500/fixtures/generic.html')
cy.origin('http://foobar.com:3500', () => {
Cypress.on('uncaught:exception', (err, runnable) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AtofStryker If you have a global Cypress.on handler, will it not catch any uncaught:exceptions from origin? Is this documented somewhere?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe it is but I am not sure where, which tells me if it is, it is not obvious.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AtofStryker Can you update the docs to include this information here: https://docs.cypress.io/api/cypress-api/catalog-of-events#Uncaught-Exceptions I think this was the main problem people were running into since they just wanted to ignore it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link

cypress bot commented May 1, 2024

10 flaky tests on run #55309 ↗︎

0 29193 1328 0 Flakiness 10

Details:

improve documentation and type handling
Project: cypress Commit: b294040b81
Status: Passed Duration: 20:34 💡
Started: May 2, 2024 9:04 PM Ended: May 2, 2024 9:25 PM
Flakiness  runner/sessions.ui.cy.ts • 1 flaky test • app-e2e

View Output

Test Artifacts
runner/cypress sessions.ui.spec > creates, not recreates, session when validation fails then succeeds Test Replay Screenshots
Flakiness  project-setup.cy.ts • 1 flaky test • launchpad-e2e

View Output

Test Artifacts
Launchpad: Setup Project > openLink > opens docs link in the default browser Test Replay Screenshots
Flakiness  commands/net_stubbing.cy.ts • 1 flaky test • 5x-driver-electron

View Output

Test Artifacts
... > stops waiting when an fetch request is canceled Test Replay
Flakiness  e2e/origin/commands/waiting.cy.ts • 1 flaky test • 5x-driver-electron

View Output

Test Artifacts
... > throws when foo cannot resolve Test Replay
Flakiness  commands/querying/querying.cy.js • 1 flaky test • 5x-driver-chrome

View Output

Test Artifacts
... > throws after timing out after a .wait() alias reference Test Replay

The first 5 flaky specs are shown, see all 9 specs in Cypress Cloud.

Review all test suite changes for PR #29454 ↗︎

@AtofStryker AtofStryker force-pushed the fix/undefined_rejection_cross_origin branch from aa6e8e2 to faeacf6 Compare May 1, 2024 20:04
@@ -188,23 +188,28 @@ const appendErrMsg = (err, errMsg) => {
})
}

const makeErrFromObj = (obj) => {
const makeErrFromObj = (obj: any) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would this type be better?

Suggested change
const makeErrFromObj = (obj: any) => {
const makeErrFromObj = (obj?: string | { message: string, name: string, stack?: string }) => {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

obj? means the type can be undefined and the argument can be unfilled.
In our case it must always be filled, even with undefined.

Another issue: this typing does not match against objects without message or name properties. But like undefined, it is technically valid to pass any object like {} or {something: 123} in throw Error(…). So this function should accept anything, and do case by case type handling with a generic fallback.

Maybe use unknown instead of any?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think in this case we might not be sure what a promise can reject with, so I think any is the appropriate type. But we might want to improve the type checking to make sure message, name, and optional stack exist when trying to reconstruct. What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unknown is also a good suggestion!

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we really improve the type checking, since we have to accept anything?
Maybe you meant the return type, but that would be Error, which provides name, message and optional stack properties in TypeScript:
https://github.com/microsoft/TypeScript/blob/7a38980a7e04bee0e273163f26c91d5c99f28298/src/lib/es5.d.ts#L1057

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can improve it slightly where we either expect a string, or an object with the name, message, and optional stack props. If we don't know what it is, we can still throw with the unknown error we have defined since it at least lets end users handle the exception Cypress side.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tried to make this a bit more type safe in b294040

Copy link

@BertrandBordage BertrandBordage left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for taking time to work on it @AtofStryker!
The code looks good to me!

@AtofStryker AtofStryker merged commit 1396e96 into develop May 6, 2024
80 of 82 checks passed
@AtofStryker AtofStryker deleted the fix/undefined_rejection_cross_origin branch May 6, 2024 14:23
@cypress-bot
Copy link
Contributor

cypress-bot bot commented May 8, 2024

Released in 13.9.0.

This comment thread has been locked. If you are still experiencing this issue after upgrading to
Cypress v13.9.0, please open a new issue.

@cypress-bot cypress-bot bot locked as resolved and limited conversation to collaborators May 8, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
6 participants