cypress-io / cypress

Fast, easy and reliable testing for anything that runs in a browser.
https://cypress.io
MIT License
46.66k stars 3.16k forks source link

PluginEvents do not support multiple handlers #22428

Closed jeremygiberson closed 1 month ago

jeremygiberson commented 2 years ago

Current behavior

Seemingly, as of cypress 10, if you have multiple plugins that register handlers for before:run, after:run, before:spec, or after:spec, only the last registered handler is dispatched.

Desired behavior

All event handlers should be dispatched.

Test code to reproduce

I've added a test spec for the scenario in the fork/branch: https://github.com/jeremygiberson/cypress/tree/run-events-multiple-bug. See pull request https://github.com/cypress-io/cypress/pull/22429

const { defineConfig } = require('cypress')

module.exports = defineConfig({
  // setupNodeEvents can be defined in either
  // the e2e or component configuration
  e2e: {
    setupNodeEvents(on, config) {
      on('before:run', ()=>{ console.log('before:run first handler') });
      on('before:run', ()=>{ console.log('before:run second handler') });
    }
  }
})

On execution of cypress test, you will only see the presence of "before:run second handler" in the output.

Cypress Version

^10.0.0

Other

Based on cursory investigation it seems like this is due to the EventRegistrar implementation. It seems like this is the class that handles plugin event registration.

But the implementation is such that there can only be a single callback for an event.

export class EventRegistrar {
  private _registeredEvents: Record<string, Function> = {}
}

Presumably, the implementation should be such that multiple callbacks can be registered per event.

export class EventRegistrar {
  private _registeredEvents: Record<string, Array<Function>> = {}
}

I'm not familiar with the cypress internals, so take with a grain of salt.

tbiethman commented 2 years ago

@jeremygiberson thank you very much for the report and the PR reproducing the issue. It is very much appreciated! I gave your branch a pull and verified your findings.

I will leave your PR open until this issue is prioritized so that we can ensure the work you did for us gets included. I am going to mark it as a draft, though, so it doesn't get merged prematurely.

BuckyMaler commented 1 year ago

Is there a chance this'll be patched for v10?

BuckyMaler commented 1 year ago

@nagash77 any update on this?

alanfitzpatrick commented 1 year ago

Would love an update on this

matys84pl commented 1 year ago

Any news on this?

nagash77 commented 1 year ago

@matys84pl This is not currently slated for development in the near future. We of course always welcome contributions from the community if anyone were so inclined.

elaichenkov commented 1 year ago

Hi all, I've faced the same issue. So, I decided to create a library that can fix the problem. You just need to install the library with the following command:

npm i -D cypress-plugin-init 

And then, you need to import the initPlugins function and use it in your cypress.config.ts file:

// import the function
import { initPlugins } from 'cypress-plugin-init';

// For example you have two plugins:
const plugin1 = (on: Cypress.PluginEvents) => {
  on('before:run', (_) => console.log('[Plugin #1] Running before:run'));
};

const plugin2 = (on: Cypress.PluginEvents) => {
  on('before:run', (_) => console.log('[Plugin #2] Running before:run'));
};

export default defineConfig({
  e2e: {
   // ...
    setupNodeEvents(on, config) {
      // invoke the function with all plugins that you need instead of the 'plugin1' and 'plugin2'
      initPlugins(on, [plugin1, plugin2]);
    },
   // ...
  },
});

It will print the following output:

(Run Starting)
...
[Plugin #1] Running before:run
[Plugin #2] Running before:run
...
(Run Finished)
bahmutov commented 1 year ago

I too created a plugin to proxy events to multiple listeners cypress-on-fix

const { defineConfig } = require('cypress')

module.exports = defineConfig({
  e2e: {
    // baseUrl, etc
    supportFile: false,
    fixturesFolder: false,
    setupNodeEvents(cypressOn, config) {
      const on = require('cypress-on-fix')(cypressOn)
      // use "on" to register plugins, for example
      // https://github.com/bahmutov/cypress-split
      require('cypress-split')(on, config)
      // https://github.com/bahmutov/cypress-watch-and-reload
      require('cypress-watch-and-reload/plugins')(on, config)
      // https://github.com/bahmutov/cypress-code-coverage
      require('@bahmutov/cypress-code-coverage/plugin')(on, config)
    },
  },
})
Deop commented 1 year ago

Hi @bahmutov I was trying to set this up but couldn't get it working. I'm still using plugin/index.js file which I then import into config file as setupNodeEvents, could you please give an example for this scenario?

bahmutov commented 1 year ago

@Deop can you open an issue in the https://github.com/kouzoh/test-framework-cypress/pull/2784 repo with a reproducible / code example?

praveenpandey02 commented 1 month ago

Hi @elaichenkov I was trying to use your library but I get this warning image Can you please help what's happening here?

jennifer-shehane commented 1 month ago

Closing as a duplicate of https://github.com/cypress-io/cypress/issues/5240