Skip to content

Commit

Permalink
fix: handle async before:spec event handler (#26055)
Browse files Browse the repository at this point in the history
Co-authored-by: Ryan Manuel <ryanm@cypress.io>
  • Loading branch information
emilyrohrbough and ryanthemanuel authored Mar 13, 2023
1 parent 9c98b5e commit 1c5a67f
Show file tree
Hide file tree
Showing 23 changed files with 210 additions and 65 deletions.
8 changes: 3 additions & 5 deletions .circleci/workflows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,7 @@ mainBuildFilters: &mainBuildFilters
- /^release\/\d+\.\d+\.\d+$/
# use the following branch as well to ensure that v8 snapshot cache updates are fully tested
- 'update-v8-snapshot-cache-on-develop'
- 'lmiller/issue-25947'
- 'fix/preflight'
- 'emily/before-spec-promise'

# usually we don't build Mac app - it takes a long time
# but sometimes we want to really confirm we are doing the right thing
Expand All @@ -42,8 +41,7 @@ macWorkflowFilters: &darwin-workflow-filters
- equal: [ develop, << pipeline.git.branch >> ]
# use the following branch as well to ensure that v8 snapshot cache updates are fully tested
- equal: [ 'update-v8-snapshot-cache-on-develop', << pipeline.git.branch >> ]
- equal: [ 'fix/preflight', << pipeline.git.branch >> ]
- equal: [ 'lmiller/issue-25947', << pipeline.git.branch >> ]
- equal: [ 'emily/before-spec-promise', << pipeline.git.branch >> ]
- matches:
pattern: /^release\/\d+\.\d+\.\d+$/
value: << pipeline.git.branch >>
Expand Down Expand Up @@ -141,7 +139,7 @@ commands:
- run:
name: Check current branch to persist artifacts
command: |
if [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "release/"* && "$CIRCLE_BRANCH" != "lmiller/issue-25947" && "$CIRCLE_BRANCH" != "update-v8-snapshot-cache-on-develop" ]]; then
if [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "release/"* && "$CIRCLE_BRANCH" != "emily/before-spec-promise" && "$CIRCLE_BRANCH" != "update-v8-snapshot-cache-on-develop" ]]; then
echo "Not uploading artifacts or posting install comment for this branch."
circleci-agent step halt
fi
Expand Down
1 change: 1 addition & 0 deletions cli/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ _Released 03/14/2023 (PENDING)_
**Bugfixes:**

- Fixed an issue where using `Cypress.require()` would throw the error `Cannot find module 'typescript'`. Fixes [#25885](https://github.com/cypress-io/cypress/issues/25885).
- The [`before:spec`](https://docs.cypress.io/api/plugins/before-spec-api) API was updated to correctly support async event handlers in `run` mode. Fixes [#24403](https://github.com/cypress-io/cypress/issues/24403).

**Misc:**

Expand Down
5 changes: 0 additions & 5 deletions packages/app/src/example.spec.ts

This file was deleted.

27 changes: 18 additions & 9 deletions packages/app/src/runner/event-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@ import type { RunState, CachedTestState, AutomationElementId, FileDetails, Repor

import { logger } from './logger'
import type { Socket } from '@packages/socket/lib/browser'
import { automation, useRunnerUiStore } from '../store'
import { automation, useRunnerUiStore, useSpecStore } from '../store'
import { useScreenshotStore } from '../store/screenshot-store'
import { useSpecStore } from '../store/specs-store'
import { useStudioStore } from '../store/studio-store'
import { getAutIframeModel } from '.'
import { handlePausing } from './events/pausing'
Expand Down Expand Up @@ -360,20 +359,30 @@ export class EventManager {
}
}

setup (config) {
async setup (config) {
this.ws.emit('watch:test:file', config.spec)

if (config.isTextTerminal || config.experimentalInteractiveRunEvents) {
await new Promise((resolve, reject) => {
this.ws.emit('plugins:before:spec', config.spec, (res?: { error: Error }) => {
// FIXME: handle surfacing the error to the browser instead of hanging with
// 'Your tests are loading...' message. Fix in https://github.com/cypress-io/cypress/issues/23627
if (res && res.error) {
reject(res.error)
}

resolve(null)
})
})
}

Cypress = this.Cypress = this.$CypressDriver.create(config)

// expose Cypress globally
// @ts-ignore
window.Cypress = Cypress

this._addListeners()

this.ws.emit('watch:test:file', config.spec)

if (config.isTextTerminal || config.experimentalInteractiveRunEvents) {
this.ws.emit('plugins:before:spec', config.spec)
}
}

isBrowser (browserName) {
Expand Down
2 changes: 1 addition & 1 deletion packages/app/src/runner/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,7 @@ async function executeSpec (spec: SpecFile, isRerun: boolean = false) {

// creates a new instance of the Cypress driver for this spec,
// initializes a bunch of listeners watches spec file for changes.
getEventManager().setup(config)
await getEventManager().setup(config)

if (window.__CYPRESS_TESTING_TYPE__ === 'e2e') {
return runSpecE2E(config, spec)
Expand Down
6 changes: 3 additions & 3 deletions packages/server/lib/modes/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -601,7 +601,7 @@ async function waitForTestsToFinishRunning (options: { project: Project, screens
}
}

await runEvents.execute('after:spec', config, spec, results)
await runEvents.execute('after:spec', spec, results)
debug('executed after:spec')

const videoName = videoRecording?.api.videoName
Expand Down Expand Up @@ -746,7 +746,7 @@ async function runSpecs (options: { config: Cfg, browser: Browser, sys: any, hea
autoCancelAfterFailures,
}

await runEvents.execute('before:run', config, beforeRunDetails)
await runEvents.execute('before:run', beforeRunDetails)

const runs = await iterateThroughSpecs({
specs,
Expand Down Expand Up @@ -816,7 +816,7 @@ async function runSpecs (options: { config: Cfg, browser: Browser, sys: any, hea
})),
})

await runEvents.execute('after:run', config, moduleAPIResults)
await runEvents.execute('after:run', moduleAPIResults)
await writeOutput(outputPath, moduleAPIResults)

return results
Expand Down
2 changes: 1 addition & 1 deletion packages/server/lib/open_project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ export class OpenProject {
return Bluebird.resolve()
}

return runEvents.execute('after:spec', cfg, spec)
return runEvents.execute('after:spec', spec)
}

const { onBrowserClose } = options
Expand Down
3 changes: 2 additions & 1 deletion packages/server/lib/plugins/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
getCtx, registerServerPluginHandler,
getCtx,
registerServerPluginHandler,
} from '@packages/data-context'

export const registerEvent = (event, callback) => {
Expand Down
2 changes: 1 addition & 1 deletion packages/server/lib/plugins/run_events.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const errors = require('../errors')
const plugins = require('../plugins')

module.exports = {
execute: Promise.method((eventName, config = {}, ...args) => {
execute: Promise.method((eventName, ...args) => {
if (!plugins.has(eventName)) return

return plugins.execute(eventName, ...args)
Expand Down
4 changes: 2 additions & 2 deletions packages/server/lib/project-base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ export class ProjectBase<TServer extends Server> extends EE {

this.isOpen = true

return runEvents.execute('before:run', cfg, beforeRunDetails)
return runEvents.execute('before:run', beforeRunDetails)
}

reset () {
Expand Down Expand Up @@ -287,7 +287,7 @@ export class ProjectBase<TServer extends Server> extends EE {

if (config.isTextTerminal || !config.experimentalInteractiveRunEvents) return

return runEvents.execute('after:run', config)
return runEvents.execute('after:run')
}

initializeReporter ({
Expand Down
17 changes: 13 additions & 4 deletions packages/server/lib/socket-base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,14 @@ export class SocketBase {
private _isRunnerSocketConnected
private _sendFocusBrowserMessage

protected inRunMode: boolean
protected supportsRunEvents: boolean
protected ended: boolean
protected _io?: socketIo.SocketIOServer
localBus: EventEmitter

constructor (config: Record<string, any>) {
this.inRunMode = config.isTextTerminal
this.supportsRunEvents = config.isTextTerminal || config.experimentalInteractiveRunEvents
this.ended = false
this.localBus = new EventEmitter()
Expand Down Expand Up @@ -532,10 +534,17 @@ export class SocketBase {
})

if (this.supportsRunEvents) {
socket.on('plugins:before:spec', (spec) => {
runEvents.execute('before:spec', {}, spec).catch((error) => {
socket.disconnect()
throw error
socket.on('plugins:before:spec', (spec, cb) => {
runEvents.execute('before:spec', spec)
.then(cb)
.catch((error) => {
if (this.inRunMode) {
socket.disconnect()
throw error
}

// surfacing the error to the app in open mode
cb({ error })
})
})
}
Expand Down
2 changes: 1 addition & 1 deletion packages/server/test/unit/open_project_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ describe('lib/open_project', () => {
return browsers.open.lastCall.args[1].onBrowserClose()
})
.then(() => {
expect(runEvents.execute).to.be.calledWith('after:spec', this.config, this.spec)
expect(runEvents.execute).to.be.calledWith('after:spec', this.spec)
})
})

Expand Down
8 changes: 4 additions & 4 deletions packages/server/test/unit/plugins/run_events_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ describe('lib/plugins/run_events', () => {
})

it('returns a promise noop if event is not registered', () => {
return runEvents.execute('before:spec', {})
return runEvents.execute('before:spec')
.then(() => {
expect(plugins.execute).not.to.be.called
})
Expand All @@ -23,7 +23,7 @@ describe('lib/plugins/run_events', () => {
plugins.has.returns(true)
plugins.execute.resolves('the result')

return runEvents.execute('before:spec', {}, 'arg1', 'arg2')
return runEvents.execute('before:spec', 'arg1', 'arg2')
.then(() => {
expect(plugins.execute).to.be.calledWith('before:spec', 'arg1', 'arg2')
})
Expand All @@ -33,7 +33,7 @@ describe('lib/plugins/run_events', () => {
plugins.has.returns(true)
plugins.execute.resolves('the result')

return runEvents.execute('before:spec', {}, 'arg1', 'arg2')
return runEvents.execute('before:spec', 'arg1', 'arg2')
.then((result) => {
expect(result).to.equal('the result')
})
Expand All @@ -43,7 +43,7 @@ describe('lib/plugins/run_events', () => {
plugins.has.returns(true)
plugins.execute.rejects({ name: 'Error', message: 'The event threw an error', stack: 'stack trace' })

return runEvents.execute('before:spec', {}, 'arg1', 'arg2')
return runEvents.execute('before:spec', 'arg1', 'arg2')
.then(() => {
expect(errors.throwErr).to.be.calledWith('PLUGINS_RUN_EVENT_ERROR', 'before:spec', { name: 'Error', message: 'The event threw an error', stack: 'stack trace' })
})
Expand Down
4 changes: 2 additions & 2 deletions packages/server/test/unit/project_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ This option will not have an effect in Some-other-name. Tests that rely on web s

return this.project.open()
.then(() => {
expect(runEvents.execute).to.be.calledWith('before:run', this.config, {
expect(runEvents.execute).to.be.calledWith('before:run', {
config: this.config,
cypressVersion: pkg.version,
system: sysInfo,
Expand Down Expand Up @@ -417,7 +417,7 @@ This option will not have an effect in Some-other-name. Tests that rely on web s

return this.project.close()
.then(() => {
expect(runEvents.execute).to.be.calledWith('after:run', this.config)
expect(runEvents.execute).to.be.calledWith('after:run')
})
})

Expand Down
Loading

5 comments on commit 1c5a67f

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on 1c5a67f Mar 13, 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.8.0/linux-arm64/develop-1c5a67fa927e16620966e977f12cd8a7c3ff69fc/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on 1c5a67f Mar 13, 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.8.0/linux-x64/develop-1c5a67fa927e16620966e977f12cd8a7c3ff69fc/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on 1c5a67f Mar 13, 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 darwin 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.8.0/darwin-arm64/develop-1c5a67fa927e16620966e977f12cd8a7c3ff69fc/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on 1c5a67f Mar 13, 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 darwin 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.8.0/darwin-x64/develop-1c5a67fa927e16620966e977f12cd8a7c3ff69fc/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on 1c5a67f Mar 13, 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 win32 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.8.0/win32-x64/develop-1c5a67fa927e16620966e977f12cd8a7c3ff69fc/cypress.tgz

Please sign in to comment.