mirror of
https://github.com/cypress-io/cypress.git
synced 2026-02-16 20:22:11 -06:00
feat: sync up protocol manager on after test (#27039)
This commit is contained in:
@@ -1,3 +1,17 @@
|
||||
const attachCypressProtocolInfo = (info) => {
|
||||
let cypressProtocolElement: HTMLElement | null = document.getElementById('__cypress-protocol')
|
||||
|
||||
// If element does not exist, create it
|
||||
if (!cypressProtocolElement) {
|
||||
cypressProtocolElement = document.createElement('div')
|
||||
cypressProtocolElement.id = '__cypress-protocol'
|
||||
cypressProtocolElement.style.display = 'none'
|
||||
document.body.appendChild(cypressProtocolElement)
|
||||
}
|
||||
|
||||
cypressProtocolElement.dataset.cypressProtocolInfo = JSON.stringify(info)
|
||||
}
|
||||
|
||||
export const addCaptureProtocolListeners = (Cypress: Cypress.Cypress) => {
|
||||
Cypress.on('log:added', (_, log) => {
|
||||
// TODO: UNIFY-1318 - Race condition in unified runner - we should not need this null check
|
||||
@@ -41,10 +55,6 @@ export const addCaptureProtocolListeners = (Cypress: Cypress.Cypress) => {
|
||||
await Cypress.backend('protocol:test:before:run:async', attributes)
|
||||
})
|
||||
|
||||
Cypress.on('test:after:run', (attributes) => {
|
||||
Cypress.backend('protocol:test:after:run', attributes)
|
||||
})
|
||||
|
||||
Cypress.on('url:changed', (url) => {
|
||||
const timestamp = performance.timeOrigin + performance.now()
|
||||
|
||||
@@ -56,4 +66,13 @@ export const addCaptureProtocolListeners = (Cypress: Cypress.Cypress) => {
|
||||
|
||||
Cypress.backend('protocol:page:loading', { loading, timestamp })
|
||||
})
|
||||
|
||||
Cypress.on('test:after:run:async', async (attributes) => {
|
||||
attachCypressProtocolInfo({
|
||||
type: 'test:after:run:async',
|
||||
timestamp: performance.timeOrigin + performance.now(),
|
||||
})
|
||||
|
||||
await Cypress.backend('protocol:test:after:run:async', attributes)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -207,7 +207,7 @@ describe('src/cy/commands/exec', () => {
|
||||
})
|
||||
|
||||
it('throws after timing out', function (done) {
|
||||
Cypress.backend.resolves(Promise.delay(250))
|
||||
Cypress.backend.withArgs('exec').resolves(Promise.delay(250))
|
||||
|
||||
cy.on('fail', (err) => {
|
||||
const { lastLog } = this
|
||||
|
||||
@@ -219,7 +219,7 @@ describe('src/cy/commands/task', () => {
|
||||
})
|
||||
|
||||
it('throws after timing out', function (done) {
|
||||
Cypress.backend.resolves(Promise.delay(250))
|
||||
Cypress.backend.withArgs('task').resolves(Promise.delay(250))
|
||||
|
||||
cy.on('fail', (err) => {
|
||||
const { lastLog } = this
|
||||
|
||||
@@ -546,6 +546,9 @@ class $Cypress {
|
||||
// TODO: handle timeouts here? or in the runner?
|
||||
return this.emitThen('test:before:run:async', ...args)
|
||||
|
||||
case 'runner:test:after:run:async':
|
||||
return this.emitThen('test:after:run:async', ...args)
|
||||
|
||||
case 'runner:runnable:after:run:async':
|
||||
return this.emitThen('runnable:after:run:async', ...args)
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ const HOOKS = ['beforeAll', 'beforeEach', 'afterEach', 'afterAll'] as const
|
||||
// event fired before hooks and test execution
|
||||
const TEST_BEFORE_RUN_ASYNC_EVENT = 'runner:test:before:run:async'
|
||||
const TEST_BEFORE_RUN_EVENT = 'runner:test:before:run'
|
||||
const TEST_AFTER_RUN_ASYNC_EVENT = 'runner:test:after:run:async'
|
||||
const TEST_AFTER_RUN_EVENT = 'runner:test:after:run'
|
||||
const RUNNABLE_AFTER_RUN_ASYNC_EVENT = 'runner:runnable:after:run:async'
|
||||
|
||||
@@ -32,6 +33,7 @@ const debugErrors = debugFn('cypress:driver:errors')
|
||||
const RUNNER_EVENTS = [
|
||||
TEST_BEFORE_RUN_ASYNC_EVENT,
|
||||
TEST_BEFORE_RUN_EVENT,
|
||||
TEST_AFTER_RUN_ASYNC_EVENT,
|
||||
TEST_AFTER_RUN_EVENT,
|
||||
RUNNABLE_AFTER_RUN_ASYNC_EVENT,
|
||||
] as const
|
||||
@@ -70,6 +72,14 @@ const testBeforeRunAsync = (test, Cypress) => {
|
||||
})
|
||||
}
|
||||
|
||||
const testAfterRunAsync = (test, Cypress) => {
|
||||
return Promise.try(() => {
|
||||
if (!fired(TEST_AFTER_RUN_ASYNC_EVENT, test)) {
|
||||
return fire(TEST_AFTER_RUN_ASYNC_EVENT, test, Cypress)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const runnableAfterRunAsync = (runnable, Cypress) => {
|
||||
runnable.clearTimeout()
|
||||
|
||||
@@ -442,8 +452,8 @@ const overrideRunnerHook = (Cypress, _runner, getTestById, getTest, setTest, get
|
||||
break
|
||||
}
|
||||
|
||||
const newArgs = [name, $utils.monkeypatchBefore(fn,
|
||||
function () {
|
||||
const newArgs = [name, $utils.monkeypatchBeforeAsync(fn,
|
||||
async function () {
|
||||
if (!shouldFireTestAfterRun()) return
|
||||
|
||||
setTest(null)
|
||||
@@ -465,6 +475,7 @@ const overrideRunnerHook = (Cypress, _runner, getTestById, getTest, setTest, get
|
||||
}
|
||||
|
||||
testAfterRun(test, Cypress)
|
||||
await testAfterRunAsync(test, Cypress)
|
||||
})]
|
||||
|
||||
return newArgs
|
||||
|
||||
@@ -76,6 +76,18 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
monkeypatchBeforeAsync (origFn, fn) {
|
||||
return async function () {
|
||||
const newArgs = await fn.apply(this, arguments)
|
||||
|
||||
if (newArgs !== undefined) {
|
||||
return origFn.apply(this, newArgs)
|
||||
}
|
||||
|
||||
return origFn.apply(this, arguments)
|
||||
}
|
||||
},
|
||||
|
||||
unwrapFirst (val) {
|
||||
// this method returns the first item in an array
|
||||
// and if its still a jquery object, then we return
|
||||
|
||||
@@ -20,6 +20,7 @@ declare namespace Cypress {
|
||||
(action: 'after:screenshot', config: {})
|
||||
(action: 'command:failed', fn: (command: CommandQueue, error: Error) => void): Cypress
|
||||
(action: 'page:loading', fn: (loading: boolean) => void)
|
||||
(action: 'test:after:run:async', fn: (attributes: ObjectLike, test: Mocha.Test) => void)
|
||||
}
|
||||
|
||||
interface Backend {
|
||||
@@ -27,7 +28,7 @@ declare namespace Cypress {
|
||||
(task: 'protocol:command:log:changed', log: any): Promise<void>
|
||||
(task: 'protocol:viewport:changed', input: any): Promise<void>
|
||||
(task: 'protocol:test:before:run:async', attributes: any): Promise<void>
|
||||
(task: 'protocol:test:after:run', attributes: any): Promise<void>
|
||||
(task: 'protocol:test:after:run:async', attributes: any): Promise<void>
|
||||
(task: 'protocol:url:changed', input: any): Promise<void>
|
||||
(task: 'protocol:page:loading', input: any): Promise<void>
|
||||
}
|
||||
|
||||
@@ -145,8 +145,8 @@ export class ProtocolManager implements ProtocolManagerShape {
|
||||
this.invokeSync('beforeTest', test)
|
||||
}
|
||||
|
||||
afterTest (test: Record<string, any>) {
|
||||
this.invokeSync('afterTest', test)
|
||||
async afterTest (test: Record<string, any>) {
|
||||
await this.invokeAsync('afterTest', test)
|
||||
}
|
||||
|
||||
commandLogAdded (log: any) {
|
||||
|
||||
@@ -459,7 +459,7 @@ export class SocketBase {
|
||||
return memory.checkMemoryPressure({ ...args[0], automation })
|
||||
case 'protocol:test:before:run:async':
|
||||
return this._protocolManager?.beforeTest(args[0])
|
||||
case 'protocol:test:after:run':
|
||||
case 'protocol:test:after:run:async':
|
||||
return this._protocolManager?.afterTest(args[0])
|
||||
case 'protocol:command:log:added':
|
||||
return this._protocolManager?.commandLogAdded(args[0])
|
||||
|
||||
@@ -118,6 +118,22 @@ describe('lib/cloud/protocol', () => {
|
||||
expect(protocol.afterSpec).to.be.called
|
||||
})
|
||||
|
||||
it('should be able to clean up after a test', async () => {
|
||||
sinon.stub(protocol, 'afterTest')
|
||||
|
||||
await protocolManager.afterTest({
|
||||
id: 'id',
|
||||
title: 'test',
|
||||
wallClockStartedAt: 1234,
|
||||
})
|
||||
|
||||
expect(protocol.afterTest).to.be.calledWith({
|
||||
id: 'id',
|
||||
title: 'test',
|
||||
wallClockStartedAt: 1234,
|
||||
})
|
||||
})
|
||||
|
||||
it('should be able to add runnables', () => {
|
||||
sinon.stub(protocol, 'addRunnables')
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ export interface AppCaptureProtocolCommon {
|
||||
viewportChanged (input: any): void
|
||||
urlChanged (input: any): void
|
||||
beforeTest(test: Record<string, any>): void
|
||||
afterTest(test: Record<string, any>): void
|
||||
afterTest(test: Record<string, any>): Promise<void>
|
||||
afterSpec (): Promise<void>
|
||||
connectToBrowser (cdpClient: CDPClient): Promise<void>
|
||||
pageLoading (input: any): void
|
||||
|
||||
Reference in New Issue
Block a user