Writing a Plugin

The Plugins API allows you to hook into and extend Cypress behavior.

Plugins API

The setupNodeEvents function (or deprecated plugins file function) receives 2 arguments: on and config. It can return a synchronous value or can also return a Promise, which will be awaited until it resolves. This enables you to perform asynchronous actions such as reading files in from the filesystem.

If you return or resolve with an object, Cypress will then merge this object into the config which enables you to overwrite configuration or environment variables.

const { defineConfig } = require('cypress')

module.exports = defineConfig({
  // setupNodeEvents can be defined in either
  // the e2e or component configuration
  e2e: {
    setupNodeEvents(on, config) {
      // configure plugins here
    }
  }
})
import { defineConfig } from 'cypress'

export default defineConfig({
  // setupNodeEvents can be defined in either
  // the e2e or component configuration
  e2e: {
    setupNodeEvents(on, config) {
      // configure plugins here
    }
  }
})
// cypress/plugins/index.js

module.exports = (on, config) => {
  // configure plugins here
}

on

on is a function that you will use to register listeners on various events that Cypress exposes.

Registering to listen on an event looks like this:

const { defineConfig } = require('cypress')

module.exports = defineConfig({
  // setupNodeEvents can be defined in either
  // the e2e or component configuration
  e2e: {
    setupNodeEvents(on, config) {
      on('<event>', (arg1, arg2) => {
        // plugin stuff here
      })
    }
  }
})
import { defineConfig } from 'cypress'

export default defineConfig({
  // setupNodeEvents can be defined in either
  // the e2e or component configuration
  e2e: {
    setupNodeEvents(on, config) {
      on('<event>', (arg1, arg2) => {
        // plugin stuff here
      })
    }
  }
})
// cypress/plugins/index.js

module.exports = (on, config) => {
  on('<event>', (arg1, arg2) => {
    // plugin stuff here
  })
}

Each event documents its own argument signature. To understand how to use them, please refer to the docs for each one.

config

config is the resolved Cypress configuration of the opened project.

This configuration contains all of the values that get passed into the browser for your project.

Some plugins may utilize or require these values, so they can take certain actions based on the configuration. If these values are programmatically modified, Cypress will use the new values.

Please check out our API docs for modifying configuration here.

List of events

The following events are available:

EventDescription
after:runOccurs after the run is finished.
after:screenshotOccurs after a screenshot is taken.
after:specOccurs after a spec is finished running.
before:browser:launchOccurs immediately before launching a browser.
before:runOccurs before the run starts.
before:specOccurs when a spec is about to be run.
file:preprocessorOccurs when a spec or spec-related file needs to be transpiled for the browser.
taskOccurs in conjunction with the cy.task command.

Execution context

The setupNodeEvents function (or deprecated plugins file function) is invoked when Cypress opens a project.

Cypress does this by spawning an independent child_process which then requires the Cypress configuration file. This is similar to the way Visual Studio Code or Atom works.

This code will be executed using the the Node version that launched Cypress.

npm modules

When Cypress executes the setupNodeEvents function (or deprecated plugins file function) it will execute with process.cwd() set to your project's path. Additionally - you will be able to require any node module you have installed, including local files inside your project.

For example, if your package.json looked like this:

{
  "name": "My Project",
  "dependencies": {
    "debug": "x.x.x"
  },
  "devDependencies": {
    "lodash": "x.x.x"
  }
}

Then you could do any of the following in your setupNodeEvents function:

const { defineConfig } = require('cypress')

module.exports = defineConfig({
  // setupNodeEvents can be defined in either
  // the e2e or component configuration
  e2e: {
    setupNodeEvents(on, config) {
      const _ = require('lodash') // yup, dev dependencies
      const path = require('path') // yup, core node library
      const debug = require('debug') // yup, dependencies
      const User = require('./lib/models/user') // yup, relative local modules
      
      console.log(__dirname) // /Users/janelane/Dev/my-project
      console.log(process.cwd()) // /Users/janelane/Dev/my-project
    }
  }
})
import { defineConfig } from 'cypress'

export default defineConfig({
  // setupNodeEvents can be defined in either
  // the e2e or component configuration
  e2e: {
    setupNodeEvents(on, config) {
      const _ = require('lodash') // yup, dev dependencies
      const path = require('path') // yup, core node library
      const debug = require('debug') // yup, dependencies
      const User = require('./lib/models/user') // yup, relative local modules
      
      console.log(__dirname) // /Users/janelane/Dev/my-project
      console.log(process.cwd()) // /Users/janelane/Dev/my-project
    }
  }
})
// cypress/plugins/index.js

module.exports = (on, config) => {
  const _ = require('lodash') // yup, dev dependencies
  const path = require('path') // yup, core node library
  const debug = require('debug') // yup, dependencies
  const User = require('../../lib/models/user') // yup, relative local modules
  
  console.log(__dirname) // /Users/janelane/Dev/my-project
  console.log(process.cwd()) // /Users/janelane/Dev/my-project
}

Error handling

The Cypress configuration file is loaded in its own child process so it is isolated away from the context that Cypress itself runs in. That means you cannot accidentally modify or change Cypress's own execution in any way.

If your setupNodeEvents function (or deprecated plugins file function) has an uncaught exception, an unhandled rejection from a promise, or a syntax error - Cypress will automatically catch those and display them to you inside of the console and even in Cypress itself.

Errors in your setupNodeEvents function will not crash Cypress.

File changes

Normally when writing code in Node, you typically have to restart the process after changing any files.

Cypress automatically watches your Cypress configuration file and any changes made will take effect immediately. We will read the file in and execute the exported function again.

This enables you to iterate on plugin code even with Cypress already running.