Skip to content

Conversation

@AtofStryker
Copy link
Contributor

@AtofStryker AtofStryker commented Mar 7, 2022

  • Closes N/A

User facing changelog

n/a

Additional details

After the large refactor changes incorporated in #20247, switchToDomain error 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 omitting get, set, and other properties when cloning (see here).

Errors, even though they are objects, are handled differently depending on the implementation of structuredClone. Firefox cannot serialize Errors through structuredClone and chrome/polyfill implementations can clone Errors, but only keep name, message, and stack. These Error objects are also casted to object literals.

The changes here propose that we preprocess Errors and Objects the same before sending them off to postMessage. Regardless of the context, Errors and Objects 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 of structuredClone. 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 CypressError is 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-cy test, which we should convert once unification is merged.

PR Tasks

  • Have tests been added/updated?
  • Has the original issue (or this PR, if no issue exists) been tagged with a release in ZenHub? (user-facing changes only)
  • Has a PR for user-facing changes been opened in cypress-documentation?
  • Have API changes been updated in the type definitions?
  • Have new configuration options been added to the cypress.schema.json?

@cypress-bot
Copy link
Contributor

cypress-bot bot commented Mar 7, 2022

Thanks for taking the time to open a PR!

@AtofStryker AtofStryker changed the title Standardize error and object serialization fix: standardize error and object serialization Mar 7, 2022
@cypress
Copy link

cypress bot commented Mar 7, 2022



Test summary

20334 0 326 4Flakiness 1


Run details

Project cypress
Status Passed
Commit c339199
Started Mar 14, 2022 7:32 PM
Ended Mar 14, 2022 7:45 PM
Duration 13:01 💡
OS Linux Debian - 10.10
Browser Multiple

View run in Cypress Dashboard ➡️


Flakiness

cypress/integration/commands/navigation_spec.js Flakiness
1 src/cy/commands/navigation > #visit > window immediately resolves and doesn't reload when visiting the same URL with hashes

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

@AtofStryker AtofStryker force-pushed the standardize_error_and_object_serialization branch 2 times, most recently from 3df8c6b to daab34b Compare March 8, 2022 20:14
@AtofStryker AtofStryker force-pushed the standardize_error_and_object_serialization branch from daab34b to 0177037 Compare March 8, 2022 20:16
…nto standardize_error_and_object_serialization
@AtofStryker
Copy link
Contributor Author

I am not sure we still need this comment, unless the goal is to get invocation stacks from within the SWD callback?

@AtofStryker AtofStryker marked this pull request as ready for review March 8, 2022 22:43
@AtofStryker AtofStryker requested a review from a team as a code owner March 8, 2022 22:43
@AtofStryker AtofStryker removed the request for review from a team March 8, 2022 22:43
return _reject(err)
const onQueueFinished = ({ err, subject, unserializableSubjectType, hasMultiDomainError }) => {
if (hasMultiDomainError) {
return _reject(reifyCrossDomainError(err, userInvocationStack))
Copy link
Contributor

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.

Copy link
Contributor Author

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

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 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 😄

@AtofStryker AtofStryker requested a review from mschile March 10, 2022 21:01
reject(err)
}

const onQueueFinished = ({ err, subject, unserializableSubjectType }) => {
Copy link
Contributor

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.

https://github.com/cypress-io/cypress/blob/feature-multidomain/packages/driver/src/multi-domain/communicator.ts#L82

Copy link
Contributor

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.

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 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?

Copy link
Contributor

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.

Copy link
Contributor Author

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.

Copy link
Contributor Author

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?

Copy link
Contributor

@chrisbreiding chrisbreiding left a 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
@AtofStryker AtofStryker requested a review from mjhenkes March 14, 2022 15:02
…nto standardize_error_and_object_serialization
@AtofStryker AtofStryker merged commit d1c3e4e into feature-multidomain Mar 14, 2022
@AtofStryker AtofStryker deleted the standardize_error_and_object_serialization branch March 14, 2022 20:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants