-
Notifications
You must be signed in to change notification settings - Fork 3.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Ability to define multiple 'on' events in pluginsFile without overwriting previously defined #5240
Comments
@Alastair-Spencer I'm a little unsure on the usecase of having 2 listeners set up for Please explain the usecase for needing 2 listeners. You may also want to utilize some of the following methods to bind or unbind from the events. https://docs.cypress.io/api/events/catalog-of-events.html#Binding-to-Events |
I apologise for not making the issue a little clearer - If I was to have multiple plugins hooking from the same event, this would prevent the first one from running and cause a race condition. They look odd being placed together in this file but this is where plugins are initialised and will hook off these events being fired. Hope that helps! 👍 |
To add to this issue, I agree that it would be useful to have multiple listeners. Currently if a plugin I use implements the An example is when using one of Cypress' recommended visual regression plugins https://github.com/meinaart/cypress-plugin-snapshots. The plugin has to be set up as follows: const { initPlugin } = require('cypress-plugin-snapshots/plugin');
module.exports = (on, config) => {
initPlugin(on, config);
return config;
}; In But in my const { initPlugin } = require('cypress-plugin-snapshots/plugin')
module.exports = (on, config) => {
on('before:browser:launch', (browser = {}, launchOptions) => {
if (browser.name === 'chrome' || browser.name === 'chromium' || browser.name === 'canary') {
launchOptions.args.push('--window-size=1200,800')
}
return launchOptions
})
initPlugin(on, config)
return config
} But only the last listener gets called. I have seen this method of plugin initialisation across several visual regression plugins suggesting this is a common way of getting plugin consumers to use the plugin, but it means that any hooks the plugin uses can't then be used by other plugins. |
We are facing the same issue. We are using the plugin |
@jennifer-shehane we are also facing the same issue with Applitools' SDK. We rely on the |
This is exactly what we are currently facing. Is there any fix planned for this? |
This just cost me 4 (thankfully paid) hours. Would be nice to get this fixed. |
If I register a listener, I won't expect that it will affect the already registered ones. Right now it feels like an unwanted side effect that I have to work around somehow... I expected something like how NodeJS event listeners works. Also put some futile effort to figure out what's going on :)
from Cypress docs |
We are facing the same issue. Here is a cheap workaround for multiple listeners on each event. const makeChainableListeners = () => {
const events = {};
const chainListeners = (action, fn) => {
if (!events[action]) {
events[action] = [];
}
events[action].push(fn);
};
const applyListeners = on => {
for (const [action, fns] of Object.entries(events)) {
if (action === 'task') {
on(action, fns.reduce((a, v) => ({ ...a, ...v }), {}));
} else {
on(action, async function(...args) {
for (const fn of fns) {
await fn.apply(this, args);
}
});
}
}
};
return [chainListeners, applyListeners];
};
export default async function plugins(on, config) {
const [chainListeners, applyListeners] = makeChainableListeners();
chainListeners('after:run', () => console.log('after run 1'));
chainListeners('after:run', () => console.log('after run 2'));
initPlugin(chainListeners, config);
applyListeners(on);
return config;
}; |
Just keep in mind that this will not work in case when you need to return value like in case of "before:browser:launch" |
Function has been deprecated. Use the `install` function instead as follows: ```diff setupNodeEvents(on) { install(on); - // bind to the event we care about - on('before:browser:launch', (browser = {}, launchOptions) => { - ensureBrowserFlags(browser, launchOptions); - return launchOptions; - }); } ``` In case of any issues please refer to cypress-io/cypress#5240
The `ensureBrowserFlags` function has been deprecated. Use the `install` function instead as follows: ```diff setupNodeEvents(on) { install(on); - - on('before:browser:launch', (browser = {}, launchOptions) => { - ensureBrowserFlags(browser, launchOptions); - return launchOptions; - }); } ``` In case of any issues please refer to cypress-io/cypress#5240 closes #100
Yeah we should definitely allow certain (or all) event handlers to be added, but we'd need to define the specification for this behavior. For instance, should the callbacks be called simultaneously, or async sequentially, yielding modified objects in the callback function, etc. We'd also need to factor in how you potentially "remove" a listener, since we only yield you the Once we define the specification and the expected behavior (this would also likely be a breaking change) then we could go ahead and implementation. Definitely in favor of doing this though. |
related to #22428 |
This just made me spend 8 hours 😢 I believe a lot of people will and already bumped into this. |
A workaround we hacked together is to wrap the class EventForwarder {
private emitter: EventEmitter;
private task: Cypress.Tasks;
public on: Cypress.PluginEvents;
public constructor() {
this.emitter = new EventEmitter();
this.task = {};
this.on = (action, arg) => {
if (action === "task") {
Object.assign(this.task, arg);
} else {
this.emitter.on(action, arg as () => void);
}
};
}
public forward(on: Cypress.PluginEvents): void {
for (const event of this.emitter.eventNames()) {
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
on(event as any, (...args: unknown[]) => {
for (const listener of this.emitter.listeners(event)) {
listener(...args);
}
});
}
on("task", this.task);
}
} It can be used as following: export default defineConfig({
e2e: {
- setupNodeEvents(on, config) {
+ setupNodeEvents(cypressOn, config) {
+ const eventForwarder = new EventForwarder();
+ const on = eventForwarder.on;
plugin1(on);
plugin2(on);
plugin3(on);
on("before:run", () => {
/* ... */
});
+
+ eventForwarder.forward(cypressOn);
},
}
}); |
Hi @ext Can you post an example code please? In my case I use the library: cypress-aiotests-reporter + cypress-mochawesome-reporter. In the file "cypress.config.js" :
I created an "event Forwarder.ts" file at the root of the project:
I created a "tsconfig.json" file at the root of the project:
The plugins run fine but the content of the .html file for cypress-mochawesome-reporter is empty. I do not understand why ? Thanks for your help ! |
Hi all, npm i -D cypress-plugin-init And then, you need to import the import { initPlugins } from 'cypress-plugin-init';
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]);
},
// ...
},
}); |
Hi @elaichenkov , Thanks for your sharing but not work for me :
Traceback :
|
Hey @Devstored - initPlugins(on, [registerAIOTestsPlugin(on,config), require('cypress-mochawesome-reporter/plugin')(on)]);
+ initPlugins(on, [registerAIOTestsPlugin, require('cypress-mochawesome-reporter/plugin')]); However, I think that won't work as well. Because, currently, the plugin accepts only one parameter ( |
Thanks, but i tried and same issue : The error we received was: TypeError: Cannot convert undefined or null to object` |
@Devstored import { initPlugins } from 'cypress-plugin-init';
export default defineConfig({
e2e: {
// ...
setupNodeEvents(on, config) {
initPlugins(on, [registerAIOTestsPlugin, require('cypress-mochawesome-reporter/plugin')], config);
},
// ...
},
}); |
Hi @elaichenkov, Thanks for your help |
I wanted to add my use-case as a plugin developer: My Cypress plugin makes use of multiple events ( Just wanted to advocate for merging events automatically in Cypress, otherwise users with multiple plugins or custom events of their own will be out of luck. |
I don't understand why this is still not fixed. A lot of reporting plugins hook to the 'after:run', 'after:spec' events. While Gleb Bahmutov's cypress-on-fix package works perfectly, it should not be necessary. |
Current behavior:
The last declaration of the on before hook is being executed using
cypress open
Desired behavior:
In this example, i'm expecting both before browser launch's to execute.
Steps to reproduce: (app code and test code)
Add following code to
plugins/index.js
Versions
v3.4.1, MacOS Mojave, Chrome / Electron
The text was updated successfully, but these errors were encountered: