-
Notifications
You must be signed in to change notification settings - Fork 3.4k
fix: standardize error and object serialization #20520
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: standardize error and object serialization #20520
Conversation
|
Thanks for taking the time to open a PR!
|
Test summaryRun details
View run in Cypress Dashboard ➡️ Flakiness
This comment has been generated by cypress-bot as a result of this project's GitHub integration settings. You can manage this integration in this project's settings in the Cypress Dashboard |
||||||||||||||||||||||||||
3df8c6b to
daab34b
Compare
daab34b to
0177037
Compare
…nto standardize_error_and_object_serialization
|
I am not sure we still need this comment, unless the goal is to get invocation stacks from within the SWD callback? |
packages/driver/cypress/integration/e2e/multi-domain/multi_domain_uncaught_errors_spec.ts
Outdated
Show resolved
Hide resolved
packages/driver/cypress/integration/e2e/multi-domain/multi_domain_uncaught_errors_spec.ts
Outdated
Show resolved
Hide resolved
| return _reject(err) | ||
| const onQueueFinished = ({ err, subject, unserializableSubjectType, hasMultiDomainError }) => { | ||
| if (hasMultiDomainError) { | ||
| return _reject(reifyCrossDomainError(err, userInvocationStack)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since the switchToDomain command itself is rejected, the userInvocationStack should be attached automatically. That's how it works for every other command. I'm curious why it's not working in some cases.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like the userInvocationStack is only attached when the error is a chai assertion error, but commands errors do not contain userInvocationStack. From what I can see, command errors get the userInvocationStack from current. This works the same for switchToDomain as far as I can tell.
The issue here isn't so much with finding the userInvocationStack when the command is rejected, but with correcting the stack to come from switchToDomain as opposed to the communicator event emitter. We only need the userInvocationStack for this specifically, and once the stack is corrected, the codeFrame can be built properly in the fail handler inside of cy.ts.
without reify
with reify
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could pull user invocation from current, but I feel like there are cases where the switchToDomain CB finishes, then a command fails in the secondary and current has moved on from switchToDomain. That's the only reason it is inside a closure.
Also right now we aren't reifying any cross origin uncaught errors, which I believe fails the current command. Since they are thrown on the spec window, I don't think we have to 😄
packages/driver/cypress/integration/e2e/multi-domain/multi_domain_uncaught_errors_spec.ts
Outdated
Show resolved
Hide resolved
packages/driver/cypress/integration/e2e/multi-domain/multi_domain_uncaught_errors_spec.ts
Outdated
Show resolved
Hide resolved
packages/driver/cypress/integration/e2e/multi-domain/multi_domain_uncaught_errors_spec.ts
Outdated
Show resolved
Hide resolved
… sending unecessary values through postMessage
| reject(err) | ||
| } | ||
|
|
||
| const onQueueFinished = ({ err, subject, unserializableSubjectType }) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What would you think of checking for the special object 'err' in the communicator's event listener and reifying the error there for any events that return the err keyword. If it's a concern that someone may return err, we could have a registry of events that this should be applied to... then you would have just one place to apply the reify logic (i have another event with an error i'd like to have this applied to.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like this idea. Then it mirrors how it's handled on the spec bridge side.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like this idea, but the only thing I think that might be a problem is setting the correct stack. I am not sure how we can get the correct userInvocationStack in the communicator unless we use current. Any ideas?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We're already using current to the get the userInvocationStack. It's guaranteed to be switchToDomain because we don't resolve switchToDomain until either the secondary queue finishes or there's an error, in which case the queue is stopped for good.
One option is we could pass the userInvocationStack into the spec bridge with run:domain:fn and replace the stack before sending it back, then we don't need to worry about it when reifying the error. This also addresses your question about this comment. I think that may be necessary because the logs bypass switchToDomain and go straight to the runner. Though now that I think about it, I'm not sure if we want the log to have the userInvocationStack, the original stack, or a combination of both. But no need to worry about that for right now. Just pointing out that this solution (passing the userInvocationStack to the spec bridge) may be needed for that use case as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK I'm going to give this a shot. I'll pass userInvocationStack into run:domain:fn to set the invocation stack on the primarytDomainCommunicator and look for a special err key, and if its truthy, reify it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@chrisbreiding @mjhenkes how about something like bc63ddc?
chrisbreiding
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good 👍
…nto standardize_error_and_object_serialization
…nto standardize_error_and_object_serialization



User facing changelog
n/a
Additional details
After the large refactor changes incorporated in #20247,
switchToDomainerror stack invocations were temporarily removed until we could safely reincorporate them. This PR addresses re adding those stack invocations, as well as condolidating the error/thrown preprocessing logic with the config preprocessing logic.In
structuredClone, Objects are serialized to literals while omitting prototype hierarchy values as well as omittingget,set, and other properties when cloning (see here).Errors, even though they are objects, are handled differently depending on the implementation ofstructuredClone. Firefox cannot serializeErrors throughstructuredCloneand chrome/polyfill implementations can cloneErrors, but only keepname,message, andstack. These Error objects are also casted to object literals.The changes here propose that we preprocess
Errors andObjects the same before sending them off topostMessage. Regardless of the context,Errors andObjects now walk to prototype tree and add any serializable value to an object literal. This way, most serializable properties are preserved without having to deal with the caveats ofstructuredClone. Now that these are processed the same, a single method can be used to preprocess thrown errors as well as sync config/env.Whenever a value is thrown from the secondary domain, we try to map it to an error. If this value cannot be mapped to an error to present to the user (serializable or otherwise), a
CypressErroris thrown with the message'cy.switchToDomain()' could not serialize or map the thrown value. Please make sure the value being thrown is supported by the structured clone algorithm.A small system test was added to verify the stack trace behavior. This is likely a bit more appropriate for a

cy-in-cytest, which we should convert once unification is merged.PR Tasks
cypress-documentation?type definitions?cypress.schema.json?