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: Use post message instead of web sockets for spec bridges. #24243

Merged
merged 23 commits into from
Oct 19, 2022

Conversation

mjhenkes
Copy link
Member

@mjhenkes mjhenkes commented Oct 13, 2022

User facing changelog

  • cy.origin now supports > 30 unique origin spec bridges per test

Additional details

Chrome has a limit of 30 protected web sockets per page, previously each spec bridge had it's own websocket to communicate with the server, this pr changes the spec bridge to instead use postmessage to communicate to the primary cypress instance and use that websocket to communicate with the server. This avoids the 30 websocket limit.

This pr also moves fetch and xhr patches from the driver to the injection and makes them async.

Steps to test

A new test was added verifying this change in cy.origin.cy.ts

How has the user experience changed?

The user experience has remained the same.

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?

@cypress-bot
Copy link
Contributor

cypress-bot bot commented Oct 13, 2022

Thanks for taking the time to open a PR!

@mjhenkes mjhenkes self-assigned this Oct 13, 2022
@cypress
Copy link

cypress bot commented Oct 13, 2022



Test summary

4121 0 1104 0Flakiness 1


Run details

Project cypress
Status Passed
Commit 49ee41f
Started Oct 19, 2022 5:41 PM
Ended Oct 19, 2022 5:57 PM
Duration 16:09 💡
OS Linux Debian - 11.4
Browser WebKit 16

View run in Cypress Dashboard ➡️


Flakiness

cypress/e2e/commands/xhr.cy.js Flakiness
1 src/cy/commands/xhr > abort > aborts xhrs currently in flight

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

return Cypress.primaryOriginCommunicator.toSpecBridgePromise(specBridge, 'snapshot:generate:for:log', {
name,
id: eventID,
return Cypress.primaryOriginCommunicator.toSpecBridgePromise({
Copy link
Member Author

Choose a reason for hiding this comment

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

toSpecBridgePromise now takes an object, so i can name parameters.

@@ -611,8 +611,8 @@ export class EventManager {
})

// Reflect back to the requesting origin the status of the 'duringUserTestExecution' state
Cypress.primaryOriginCommunicator.on('sync:during:user:test:execution', ({ specBridgeResponseEvent }, origin) => {
Cypress.primaryOriginCommunicator.toSpecBridge(origin, specBridgeResponseEvent, cy.state('duringUserTestExecution'))
Cypress.primaryOriginCommunicator.on('sync:during:user:test:execution', (_data, { origin, responseEvent }) => {
Copy link
Member Author

Choose a reason for hiding this comment

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

response event has moved to be part the second parameter object and no longer part of data which should be only information sent by the caller.

@@ -19,42 +19,41 @@ const SNAPSHOT_EVENT_PREFIX = `${CROSS_ORIGIN_PREFIX}snapshot:`
* @param event the name of the event to be promisified.
* @param specBridgeName the name of the spec bridge receiving the event.
* @param communicator the communicator that is sending the message
* @param timeout - in ms, if the promise does not complete during this timeout, fail the promise.
Copy link
Member Author

Choose a reason for hiding this comment

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

added a timeout parameter since some of the automation/backend calls take longer than a second.

}: {
resolve: Function
reject: Function
data?: any
Copy link
Member Author

Choose a reason for hiding this comment

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

data is no longer modified since the responseEvent is sent back separately

@@ -105,7 +104,7 @@ export class PrimaryOriginCommunicator extends EventEmitter {
data.data.err = reifySerializedError(data.data.err, this.userInvocationStack as string)
}

this.emit(messageName, data.data, data.origin, source)
this.emit(messageName, data.data, { origin: data.origin, source, responseEvent: data.responseEvent })
Copy link
Member Author

Choose a reason for hiding this comment

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

Group event metadata in a single object.

* @param data - any meta data to be sent with the event.
* @param responseEvent - the even to be responded with when sending back a result.
*/
toSource (source: Window, event: string, data?: any, responseEvent?: string) {
Copy link
Member Author

Choose a reason for hiding this comment

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

Added toSource to shortcut when responding back to a post message that expects a response. This also allows us to work for both spec bridge messages and AUT messages.

*/
toPrimary (event: string, data?: Cypress.ObjectLike, options: { syncGlobals: boolean } = { syncGlobals: false }) {
toPrimary (event: string, data?: Cypress.ObjectLike, options: { syncGlobals: boolean } = { syncGlobals: false }, responseEvent?: string) {
Copy link
Member Author

Choose a reason for hiding this comment

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

we should consider making toPrimary take an object too. I didn't make the change here because i was already making enough changes, but would be willing to update if everyone agrees.

@@ -172,26 +175,18 @@ const attachToWindow = (autWindow: Window) => {
patchFormElementSubmit(autWindow)
}

Cypress.removeAllListeners('app:timers:reset')
Copy link
Member Author

Choose a reason for hiding this comment

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

moved this to the injection

cy.urlNavigationEvent('before:load')

cy.overrides.wrapNativeMethods(autWindow)

// @ts-expect-error - the injected code adds the cypressApplyPatchesOnAttach function to window
autWindow.cypressApplyPatchesOnAttach()
Copy link
Member Author

Choose a reason for hiding this comment

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

Added this as the one place to apply any patches on attach.

Comment on lines -190 to -178
patchFetch(Cypress, autWindow)
patchXmlHttpRequest(Cypress, autWindow)
Copy link
Member Author

Choose a reason for hiding this comment

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

moved to the injection

const onBackendRequest = (...args) => {
webSocket.emit('backend:request', ...args)
}
const onRequest = async (event, args) => {
Copy link
Member Author

Choose a reason for hiding this comment

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

we now delegate any websocket connections to the primary cypress instance and communicate with it via post message.

@@ -1,17 +0,0 @@
export const captureFullRequestUrl = (relativeOrAbsoluteUrlString: string, window: Window) => {
Copy link
Member Author

Choose a reason for hiding this comment

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

moved to the runner

Comment on lines -53 to -56
if (window.Cypress) {
// A spec bridge has attached so we don't need to forward errors to top anymore.
window.removeEventListener('error', handleErrorEvent)
} else {
Copy link
Member Author

Choose a reason for hiding this comment

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

this is now cleaned up in the cypressApplyPatchesOnAttach function

// if fetch is available in the browser, or is polyfilled by whatwg fetch
// intercept method calls and add cypress headers to determine cookie applications in the proxy
// for simulated top. @see https://github.github.io/fetch/ for default options
if (!Cypress.config('experimentalSessionAndOrigin') || !window.fetch) {
if (!window.fetch) {
Copy link
Member Author

Choose a reason for hiding this comment

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

experimental session and origin config doesn't really apply here because it is only injected if the flag is enabled anyway

return
}

const originalFetch = window.fetch

window.fetch = function (...args) {
window.fetch = async function (...args) {
Copy link
Member Author

Choose a reason for hiding this comment

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

we're now async so we can await the backend call.. i think previously it was a race condition.

Copy link
Contributor

@AtofStryker AtofStryker left a comment

Choose a reason for hiding this comment

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

we can probably remove the beforeEach in validation.cy.ts now which might be a good signal if too many spec bridges causes a problem again

@AtofStryker AtofStryker self-requested a review October 17, 2022 21:12
@chrisbreiding chrisbreiding self-requested a review October 18, 2022 14:09
packages/driver/src/cross-origin/communicator.ts Outdated Show resolved Hide resolved
packages/driver/src/cross-origin/communicator.ts Outdated Show resolved Hide resolved
packages/driver/src/cross-origin/communicator.ts Outdated Show resolved Hide resolved
packages/driver/src/cross-origin/communicator.ts Outdated Show resolved Hide resolved
packages/runner/injection/cross-origin.js Outdated Show resolved Hide resolved
packages/runner/injection/patches/fetch.ts Outdated Show resolved Hide resolved
packages/runner/injection/patches/fetch.ts Outdated Show resolved Hide resolved
packages/runner/injection/patches/utils/index.ts Outdated Show resolved Hide resolved
packages/runner/injection/patches/utils/index.ts Outdated Show resolved Hide resolved
mjhenkes and others added 3 commits October 18, 2022 10:17
Co-authored-by: Chris Breiding <chrisbreiding@users.noreply.github.com>
Co-authored-by: Chris Breiding <chrisbreiding@users.noreply.github.com>
@AtofStryker AtofStryker self-requested a review October 18, 2022 21:32
@mjhenkes mjhenkes merged commit 208efbf into develop Oct 19, 2022
@mjhenkes mjhenkes deleted the matth/fix/cy-origin-fails-after-30-spec-bridges branch October 19, 2022 18:17
mjhenkes added a commit that referenced this pull request Mar 14, 2023
* fix: spec bridges no-longer create their own spec bridge but instead use post message to communicate with the primary instance.

* fix test errors

* fix tests, ignore change to cookie

* Move patch code into injection

* Code cleanup

* Fix most failing tests

* Enable patching for both spec bridges and the primary cypress instance.

* clean up

* Updates from self PR review

* Remove before each limiting spec bridges.

* rename attach function

* whoops, better call the function with the correct parameters

* Apply suggestions from code review

Co-authored-by: Chris Breiding <chrisbreiding@users.noreply.github.com>

* Update packages/driver/src/cross-origin/communicator.ts

Co-authored-by: Chris Breiding <chrisbreiding@users.noreply.github.com>

* Apply suggestions from code review

* Apply suggestions from code review

Co-authored-by: Bill Glesias <bglesias@gmail.com>

* updated test name

Co-authored-by: Chris Breiding <chrisbreiding@users.noreply.github.com>
Co-authored-by: Bill Glesias <bglesias@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
3 participants