mirror of
https://github.com/cypress-io/cypress.git
synced 2026-04-28 10:49:48 -05:00
bd4774fb57
* chore: set up sharing of react via module federation in studio * chore: add ability to load types from the studio bundle * fix build * fix build * fix build * chore: pass file preprocesor override to protocol as debug data * fix tests * PR comments * PR comment * update types
203 lines
5.0 KiB
TypeScript
203 lines
5.0 KiB
TypeScript
import path from 'path'
|
|
import fs from 'fs-extra'
|
|
import type { AppCaptureProtocolInterface, ProtocolManagerOptions, 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`,
|
|
)
|
|
}
|
|
|
|
const CDP_EVENT_DRAIN_DURATION = 1
|
|
|
|
const AUT_EVENT_DRAIN_DURATION = 5
|
|
|
|
const BODY_PROMISES_DURATION = 7
|
|
|
|
const CLOSE_DB_DURATION = 11
|
|
|
|
const TEARDOWN_BINDINGS_DURATION = 13
|
|
|
|
const DURATIONS = {
|
|
drainCDPEvents: CDP_EVENT_DRAIN_DURATION,
|
|
drainAUTEvents: AUT_EVENT_DRAIN_DURATION,
|
|
resolveBodyPromises: BODY_PROMISES_DURATION,
|
|
closeDb: CLOSE_DB_DURATION,
|
|
teardownBindings: TEARDOWN_BINDINGS_DURATION,
|
|
}
|
|
|
|
export class AppCaptureProtocol implements AppCaptureProtocolInterface {
|
|
private filename: string
|
|
private events = {
|
|
debugData: {},
|
|
beforeSpec: [],
|
|
afterSpec: [],
|
|
beforeTest: [],
|
|
preAfterTest: [],
|
|
afterTest: [],
|
|
addRunnables: [],
|
|
connectToBrowser: [],
|
|
commandLogAdded: [],
|
|
commandLogChanged: [],
|
|
viewportChanged: [],
|
|
urlChanged: [],
|
|
pageLoading: [],
|
|
resetTest: [],
|
|
responseEndedWithEmptyBody: [],
|
|
responseStreamTimedOut: [],
|
|
}
|
|
private cdpClient: any
|
|
private scriptToEvaluateId: any
|
|
|
|
constructor (options: ProtocolManagerOptions) {
|
|
this.events.debugData = options.debugData
|
|
}
|
|
|
|
getDbMetadata (): { offset: number, size: number } {
|
|
return {
|
|
offset: 0,
|
|
size: 0,
|
|
}
|
|
}
|
|
|
|
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.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 () {
|
|
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(() => {
|
|
})
|
|
|
|
return { durations: DURATIONS }
|
|
}
|
|
|
|
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)
|
|
}
|
|
|
|
async cdpReconnect (): Promise<void> {
|
|
|
|
}
|
|
}
|