Skip to content

Commit

Permalink
fix: test:after:run passes the runnable (#25678)
Browse files Browse the repository at this point in the history
  • Loading branch information
mschile committed Feb 2, 2023
1 parent dbc69b4 commit beb411f
Show file tree
Hide file tree
Showing 6 changed files with 229 additions and 12 deletions.
4 changes: 4 additions & 0 deletions cli/CHANGELOG.md
Expand Up @@ -3,6 +3,10 @@

_Released 02/10/2023 (PENDING)_

**Bugfixes:**

- Fixed a regression from Cypress [12.5.0](https://docs.cypress.io/guides/references/changelog#12-5-0) where the `runnable` was not included in the `test:after:run` event. Fixes [#25663](https://github.com/cypress-io/cypress/issues/25663).

**Dependency Updates:**

- Upgraded [`simple-git`](https://github.com/steveukx/git-js) from `3.15.0` to `3.16.0` to address this [security vulnerability](https://github.com/advisories/GHSA-9p95-fxvg-qgq2) where Remote Code Execution (RCE) via the clone(), pull(), push() and listRemote() methods due to improper input sanitization was possible. Addressed in [#25603](https://github.com/cypress-io/cypress/pull/25603).
Expand Down
4 changes: 2 additions & 2 deletions cli/types/cypress.d.ts
Expand Up @@ -5976,14 +5976,14 @@ declare namespace Cypress {
* Useful to see how internal cypress commands utilize the {% url 'Cypress.log()' cypress-log %} API.
* @see https://on.cypress.io/catalog-of-events#App-Events
*/
(action: 'log:added', fn: (log: any, interactive: boolean) => void): Cypress
(action: 'log:added', fn: (attributes: ObjectLike, log: any) => void): Cypress
/**
* Fires whenever a command's attributes changes.
* This event is debounced to prevent it from firing too quickly and too often.
* Useful to see how internal cypress commands utilize the {% url 'Cypress.log()' cypress-log %} API.
* @see https://on.cypress.io/catalog-of-events#App-Events
*/
(action: 'log:changed', fn: (log: any, interactive: boolean) => void): Cypress
(action: 'log:changed', fn: (attributes: ObjectLike, log: any) => void): Cypress
/**
* Fires before the test and all **before** and **beforeEach** hooks run.
* @see https://on.cypress.io/catalog-of-events#App-Events
Expand Down
11 changes: 9 additions & 2 deletions cli/types/tests/actions.ts
Expand Up @@ -61,11 +61,13 @@ Cypress.on('command:retry', (command) => {
command // $ExpectType CommandQueue
})

Cypress.on('log:added', (log, interactive: boolean) => {
Cypress.on('log:added', (attributes, log) => {
attributes // $ExpectType ObjectLike
log // $ExpectTyped any
})

Cypress.on('log:changed', (log, interactive: boolean) => {
Cypress.on('log:changed', (attributes, log) => {
attributes // $ExpectType ObjectLike
log // $ExpectTyped any
})

Expand All @@ -74,6 +76,11 @@ Cypress.on('test:before:run', (attributes , test) => {
test // $ExpectType Test
})

Cypress.on('test:before:run:async', (attributes , test) => {
attributes // $ExpectType ObjectLike
test // $ExpectType Test
})

Cypress.on('test:after:run', (attributes , test) => {
attributes // $ExpectType ObjectLike
test // $ExpectType Test
Expand Down
15 changes: 8 additions & 7 deletions packages/app/src/runner/event-manager.ts
Expand Up @@ -38,7 +38,6 @@ interface AddGlobalListenerOptions {

const driverToLocalAndReporterEvents = 'run:start run:end'.split(' ')
const driverToSocketEvents = 'backend:request automation:request mocha recorder:frame'.split(' ')
const driverTestEvents = 'test:before:run:async test:after:run'.split(' ')
const driverToLocalEvents = 'viewport:changed config stop url:changed page:loading visit:failed visit:blank cypress:in:cypress:runner:event'.split(' ')
const socketRerunEvents = 'runner:restart watched:file:changed'.split(' ')
const socketToDriverEvents = 'net:stubbing:event request:event script:error cross:origin:cookies'.split(' ')
Expand Down Expand Up @@ -536,19 +535,21 @@ export class EventManager {

Cypress.on('after:screenshot', handleAfterScreenshot)

driverTestEvents.forEach((event) => {
Cypress.on(event, (test, cb) => {
this.reporterBus.emit(event, test, cb)
})
})

driverToLocalAndReporterEvents.forEach((event) => {
Cypress.on(event, (...args) => {
this.localBus.emit(event, ...args)
this.reporterBus.emit(event, ...args)
})
})

Cypress.on('test:before:run:async', (test, _runnable) => {
this.reporterBus.emit('test:before:run:async', test)
})

Cypress.on('test:after:run', (test, _runnable) => {
this.reporterBus.emit('test:after:run', test, Cypress.config('isInteractive'))
})

Cypress.on('run:start', async () => {
if (Cypress.config('experimentalMemoryManagement') && Cypress.isBrowser({ family: 'chromium' })) {
await Cypress.backend('start:memory:profiling', Cypress.config('spec'))
Expand Down
205 changes: 205 additions & 0 deletions packages/driver/cypress/e2e/cypress/events.cy.ts
@@ -0,0 +1,205 @@
describe('src/cypress', () => {
describe('events', () => {
it('fail event', (done) => {
cy.on('fail', (err, runnable) => {
expect(err.message).to.equal('foo')
expect(runnable).to.equal(Cypress.state('runnable'))

done()
})

throw new Error('foo')
})

it('viewport:changed event', () => {
let called = false

cy.on('viewport:changed', (viewport) => {
expect(viewport).to.deep.equal({ viewportWidth: 100, viewportHeight: 100 })
called = true
})

cy.viewport(100, 100).then(() => {
expect(called).to.be.true
})
})

it('scrolled event', (done) => {
cy.viewport(100, 100)
Cypress.$('<button>button</button>')
.attr('id', 'button')
.css({
position: 'absolute',
left: '0px',
top: '50px',
})
.appendTo(cy.$$('body'))

cy.on('scrolled', ($el, type) => {
expect($el[0]).to.eq(Cypress.$('#button')[0])
expect(type).to.eq('element')

done()
})

cy.get('#button').trigger('mousedown')
})

context('command events', () => {
it('command:enqueued event', () => {
let called = false

const handler = (command) => {
expect(command.name).to.eq('log')
called = true
cy.off('command:enqueued', handler)
}

cy.on('command:enqueued', handler)

cy.log('foo').then(() => {
expect(called).to.be.true
})
})

it('command:start event', () => {
let called = false

const handler = (command) => {
expect(command.attributes.name).to.eq('log')
called = true
cy.off('command:start', handler)
}

cy.on('command:start', handler)

cy.log('foo').then(() => {
expect(called).to.be.true
})
})

it('command:end event', () => {
let called = false

const handler = (command) => {
expect(command.attributes.name).to.eq('log')
called = true
cy.off('command:end', handler)
}

cy.on('command:end', handler)

cy.log('foo').then(() => {
expect(called).to.be.true
})
})

it('command:retry event', (done) => {
const handler = (options) => {
expect(options._retries).to.equal(1)
expect(options.error.message).to.equal('Expected to find element: `#foo`, but never found it.')
done()
}

cy.on('command:retry', handler)

cy.get('#foo')
})
})

context('log events', () => {
it('log:added event', () => {
const attrs: any[] = []
const logs: any[] = []

const handler = (attr, log) => {
attrs.push(attr)
logs.push(log)
}

cy.on('log:added', handler)

Cypress.log({ name: 'log', message: `foo` })

cy.log('foo').then(() => {
expect(attrs[0].name).to.eq('log')
expect(logs[0].attributes.name).to.eq('log')
})
})

it('log:changed event', (done) => {
const handler = (attr, log) => {
cy.off('log:changed', handler)
expect(attr.name).to.eq('bar')
expect(log.attributes.name).to.eq('bar')
done()
}

const log = Cypress.log({ message: `foo` })

cy.on('log:changed', handler)

log?.set('name', 'bar')
})
})

// these tests need to be run together since they are testing lifecycle events
context('lifecycle (test:before/after:run) events', () => {
let afterRunnable
let beforeRunnable
let beforeAsyncRunnable
let expectedAfterRunnable

const beforeHandler = (test, runnable) => {
expect(test.title).to.eq('test 2')

beforeRunnable = runnable
Cypress.off('test:before:run', beforeHandler)
}

const beforeAsyncHandler = (test, runnable) => {
expect(test.title).to.eq('test 2')

beforeAsyncRunnable = runnable
Cypress.off('test:before:run:async', beforeAsyncHandler)
}

const afterHandler = (test, runnable) => {
expect(test.title).to.eq('test 1')

afterRunnable = runnable
Cypress.off('test:after:run', afterHandler)
}

before(() => {
Cypress.on('test:before:run', beforeHandler)
Cypress.on('test:before:run:async', beforeAsyncHandler)
Cypress.on('test:after:run', afterHandler)
})

after(() => {
Cypress.off('test:before:run', beforeHandler)
Cypress.off('test:before:run:async', beforeAsyncHandler)
Cypress.off('test:after:run', afterHandler)
})

it('test 1', () => {
// this is the runnable that we expect to be passed to the test:after:run event
// and it will be verified in the next test since we need to wait for the test to finish
expectedAfterRunnable = Cypress.state('runnable')
})

it('test 2', () => {
// the before runnables should be from this test
const runnable = Cypress.state('runnable')

// use === to avoid the circular references
expect(beforeAsyncRunnable === runnable).to.be.true
expect(beforeRunnable === runnable).to.be.true

// the after runnable should be from the previous test
expect(afterRunnable).to.deep.equal(expectedAfterRunnable)
})
})
})
})
2 changes: 1 addition & 1 deletion packages/driver/src/cypress.ts
Expand Up @@ -551,7 +551,7 @@ class $Cypress {

// this event is how the reporter knows how to display
// stats and runnable properties such as errors
this.emit('test:after:run', args[0], this.config('isInteractive'))
this.emit('test:after:run', ...args)
this.maybeEmitCypressInCypress('mocha', 'test:after:run', args[0])

if (this.config('isTextTerminal')) {
Expand Down

2 comments on commit beb411f

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on beb411f Feb 2, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Circle has built the linux arm64 version of the Test Runner.

Learn more about this pre-release build at https://on.cypress.io/advanced-installation#Install-pre-release-version

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/12.5.1/linux-arm64/develop-beb411f3f4c0791e67b7f78f0f023bcac7633f07/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on beb411f Feb 2, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Circle has built the linux x64 version of the Test Runner.

Learn more about this pre-release build at https://on.cypress.io/advanced-installation#Install-pre-release-version

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/12.5.1/linux-x64/develop-beb411f3f4c0791e67b7f78f0f023bcac7633f07/cypress.tgz

Please sign in to comment.