feat: sync up protocol manager on after test (#27039)

This commit is contained in:
Ryan Manuel
2023-06-15 13:59:48 -05:00
committed by GitHub
parent 4d46845512
commit 0dedccfcd1
11 changed files with 75 additions and 13 deletions

View File

@@ -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)
})
}

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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>
}

View File

@@ -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) {

View File

@@ -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])

View File

@@ -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')

View File

@@ -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