Files
cypress/packages/server/lib/browsers/cdp-command-queue.ts
Cacie Prins 8a48ee7cdf fix: Unhandled "WebSocket connection closed" when CDP connection is unstable (#29830)
* unit and integration tests that reproduce websocket disconnected unhandled exception

* WIP: command queue

* complete command queue and retry refactor

* cri-client changes pass tests; modify certain tests for readability and accuracy

* removes unnecessary logic from command queue, adds unit tests for command queue

* rm unused cdp state - this should be reserved for future refactor

* small edits to cri-client: better error handling, more comprehensive comments

* comment re: queue property

* rearrange cri client member methods for readability

* further edits

* Changelog

* Update cli/CHANGELOG.md

Co-authored-by: Mike McCready <66998419+MikeMcC399@users.noreply.github.com>

* fix continuous retry on close

* split heavier debugs to verbose

---------

Co-authored-by: Mike McCready <66998419+MikeMcC399@users.noreply.github.com>
Co-authored-by: Jennifer Shehane <jennifer@cypress.io>
2024-07-16 09:46:40 -04:00

87 lines
2.1 KiB
TypeScript

import type ProtocolMapping from 'devtools-protocol/types/protocol-mapping'
import pDefer, { DeferredPromise } from 'p-defer'
import type { CdpCommand } from './cdp_automation'
import Debug from 'debug'
const debug = Debug('cypress:server:browsers:cdp-command-queue')
const debugVerbose = Debug('cypress:server:browsers:cd-command-queue')
type CommandReturn<T extends CdpCommand> = ProtocolMapping.Commands[T]['returnType']
export type Command<T extends CdpCommand> = {
command: T
params?: object
deferred: DeferredPromise<CommandReturn<T>>
sessionId?: string
}
export class CDPCommandQueue {
private queue: Command<any>[] = []
public get entries () {
return [...this.queue]
}
public add <TCmd extends CdpCommand> (
command: TCmd,
params: ProtocolMapping.Commands[TCmd]['paramsType'][0],
sessionId?: string,
): Promise<CommandReturn<TCmd>> {
debug('enqueing command %s', command)
debugVerbose('enqueing command %s with params %o', command, params)
const deferred = pDefer<CommandReturn<TCmd>>()
const commandPackage: Command<TCmd> = {
command,
params,
deferred,
sessionId,
}
this.queue.push(commandPackage)
debug('Command enqueued; new length: %d', this.queue.length)
debugVerbose('Queue Contents: %O', this.queue)
return deferred.promise
}
public clear () {
debug('clearing command queue')
this.queue = []
}
public extract<T extends CdpCommand> (search: Partial<Command<T>>): Command<T> | undefined {
// this should find, remove, and return if found a given command
const index = this.queue.findIndex((enqueued) => {
for (let k of Object.keys(search)) {
if (search[k] !== enqueued[k]) {
return false
}
}
return true
})
debug('extracting %o from commands at index %d', search, index)
if (index === -1) {
return undefined
}
const [extracted] = this.queue.splice(index, 1)
return extracted
}
public shift () {
return this.queue.shift()
}
public unshift (value: Command<any>) {
return this.queue.unshift(value)
}
}