-
Notifications
You must be signed in to change notification settings - Fork 3.2k
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
fix: Use post message instead of web sockets for spec bridges. #24243
Conversation
…use post message to communicate with the primary instance.
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 |
return Cypress.primaryOriginCommunicator.toSpecBridgePromise(specBridge, 'snapshot:generate:for:log', { | ||
name, | ||
id: eventID, | ||
return Cypress.primaryOriginCommunicator.toSpecBridgePromise({ |
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.
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 }) => { |
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.
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. |
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.
added a timeout parameter since some of the automation/backend calls take longer than a second.
}: { | ||
resolve: Function | ||
reject: Function | ||
data?: any |
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.
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 }) |
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.
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) { |
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.
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) { |
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 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') |
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.
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() |
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.
Added this as the one place to apply any patches on attach.
patchFetch(Cypress, autWindow) | ||
patchXmlHttpRequest(Cypress, autWindow) |
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.
moved to the injection
const onBackendRequest = (...args) => { | ||
webSocket.emit('backend:request', ...args) | ||
} | ||
const onRequest = async (event, args) => { |
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 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) => { |
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.
moved to the runner
if (window.Cypress) { | ||
// A spec bridge has attached so we don't need to forward errors to top anymore. | ||
window.removeEventListener('error', handleErrorEvent) | ||
} else { |
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.
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) { |
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.
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) { |
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 now async so we can await the backend call.. i think previously it was a race condition.
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 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
Co-authored-by: Chris Breiding <[email protected]>
Co-authored-by: Chris Breiding <[email protected]>
* 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 <[email protected]> * Update packages/driver/src/cross-origin/communicator.ts Co-authored-by: Chris Breiding <[email protected]> * Apply suggestions from code review * Apply suggestions from code review Co-authored-by: Bill Glesias <[email protected]> * updated test name Co-authored-by: Chris Breiding <[email protected]> Co-authored-by: Bill Glesias <[email protected]>
User facing changelog
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
cypress-documentation
?type definitions
?