Files
cypress/system-tests/lib/protocol-stubs/protocolStubWithMissingArchive.ts
T
Cacie Prins fc88764ad5 feat: more informative error messages when Test Replay fails to capture (#29312)
* new error messages

* errors for initialization and capturing test replay

* messaging the case where no protocol instance but also no fatal error

* adds suggested remedies to initialization errors

* differentiation between network and http errors, initial work on db missing error

* improve db not found error

* add new error type to schema

* rm commented dead code

* clean up network error creation in uploadStream

* make artifact confirmation error msg more general

* test display of put instance artifacts failure

* changelog

* ensure we are displaying errors even in quiet mode

* fix pending changelog

* new snapshots for protocol errors

* improve aggregate error

* resolved html error snapshots

* fix check-ts

* fix test

* sanitize temp dir in CLOUD_PROTOCOL_CAPTURE_FAILURE error msg

* changelog

* fix double entry of certain network errors, error message prop for network errors

* fix url arg for network error

* Update packages/server/lib/cloud/artifacts/upload_artifacts.ts

Co-authored-by: Ryan Manuel <ryanm@cypress.io>

* rm extra formatting from debug

* snapshot whitespace

* changelog update

* +pending

* Update cli/CHANGELOG.md

* resolve snapshots?

* does moving the stack trace fix whitespace in snapshots in ci?

* maybe fixes whitespace on electron now?

* fully normalize stack traces

* remove full normalization

* maybe debug stack normalization

* rm stack traces from error messages

---------

Co-authored-by: Jennifer Shehane <jennifer@cypress.io>
Co-authored-by: Ryan Manuel <ryanm@cypress.io>
2024-05-01 14:32:53 -04:00

184 lines
4.6 KiB
TypeScript

import path from 'path'
import fs from 'fs-extra'
import type { AppCaptureProtocolInterface, ResponseEndedWithEmptyBodyOptions, ResponseStreamOptions, ResponseStreamTimedOutOptions } from '@packages/types'
import type { Readable } from 'stream'
const getFilePath = (filename) => {
return path.join(
path.resolve(__dirname),
'cypress',
'system-tests-protocol-dbs',
`${filename}.json`,
)
}
export class AppCaptureProtocol implements AppCaptureProtocolInterface {
private filename: string
private events = {
beforeSpec: [],
afterSpec: [],
beforeTest: [],
preAfterTest: [],
afterTest: [],
addRunnables: [],
connectToBrowser: [],
commandLogAdded: [],
commandLogChanged: [],
viewportChanged: [],
urlChanged: [],
pageLoading: [],
resetTest: [],
responseEndedWithEmptyBody: [],
responseStreamTimedOut: [],
}
private cdpClient: any
private scriptToEvaluateId: any
private archivePath: string | undefined
getDbMetadata (): { offset: number, size: number } {
return {
offset: 0,
size: 0,
}
}
cdpReconnect (): Promise<void> {
return Promise.resolve()
}
responseStreamReceived (options: ResponseStreamOptions): Readable {
return options.responseStream
}
resetEvents () {
this.events.beforeTest = []
this.events.preAfterTest = []
this.events.afterTest = []
this.events.commandLogAdded = []
this.events.commandLogChanged = []
this.events.viewportChanged = []
this.events.urlChanged = []
this.events.pageLoading = []
this.events.responseEndedWithEmptyBody = []
this.events.responseStreamTimedOut = []
}
connectToBrowser = async (cdpClient) => {
if (cdpClient) {
this.events.connectToBrowser.push(true)
this.cdpClient = cdpClient
}
const scriptToEvaluateResult = await this.cdpClient.send(
'Page.addScriptToEvaluateOnNewDocument',
{
source: `(function () {})()`,
},
)
this.scriptToEvaluateId = scriptToEvaluateResult.identifier
}
addRunnables = (runnables) => {
this.events.addRunnables.push(runnables)
return Promise.resolve()
}
beforeSpec = ({ archivePath, db }) => {
this.archivePath = archivePath
this.events.beforeSpec.push(db)
this.filename = getFilePath(path.basename(db.name))
if (!fs.existsSync(archivePath)) {
// If a dummy file hasn't been created by the test, write a tar file so that it can be fake uploaded
fs.writeFileSync(archivePath, '')
}
}
async afterSpec (): Promise<void> {
this.events.afterSpec.push(true)
// since the order of the logs can vary per run, we sort them by id to ensure the snapshot can be compared
this.events.commandLogChanged.sort((log1, log2) => {
return log1.id.localeCompare(log2.id)
})
try {
fs.outputFileSync(this.filename, JSON.stringify(this.events, null, 2))
} catch (e) {
console.log('error writing protocol events', e)
}
await this.cdpClient.send('Page.removeScriptToEvaluateOnNewDocument', {
identifier: this.scriptToEvaluateId || '',
})
.catch(() => {
})
if (fs.existsSync(this.archivePath)) {
fs.rmSync(this.archivePath)
}
}
beforeTest = (test) => {
this.events.beforeTest.push(test)
return Promise.resolve()
}
commandLogAdded = (log) => {
// we only care about logs that occur during a test
if (log.testId) {
this.events.commandLogAdded.push(log)
}
}
commandLogChanged = (log) => {
// we only care about logs that occur during a test and
// since the number of log changes can vary per run, we only want to record
// the passed/failed ones to ensure the snapshot can be compared
if (log.testId && (log.state === 'passed' || log.state === 'failed')) {
this.events.commandLogChanged.push(log)
}
}
viewportChanged = (input) => {
this.events.viewportChanged.push(input)
}
urlChanged = (input) => {
this.events.urlChanged.push(input)
}
pageLoading = (input) => {
this.events.pageLoading.push(input)
}
preAfterTest = (test, options) => {
this.events.preAfterTest.push({ test, options })
return Promise.resolve()
}
afterTest = (test) => {
this.events.afterTest.push(test)
return Promise.resolve()
}
responseEndedWithEmptyBody = (options: ResponseEndedWithEmptyBodyOptions) => {
this.events.responseEndedWithEmptyBody.push(options)
}
responseStreamTimedOut (options: ResponseStreamTimedOutOptions): void {
this.events.responseStreamTimedOut.push(options)
}
resetTest (testId: string): void {
this.resetEvents()
this.events.resetTest.push(testId)
}
}