To test an event your application emits, attach a spy or listener to the app’s actual event interface, perform the UI action, and assert the meaningful parts of the event. If startup emissions matter, install the observer in cy.visit()’s onBeforeLoad callback so it is in place before application code runs.
First identify which events you want to observe
Cypress’s event APIs cover more than one stream. Your application may expose events through a method such as window.postMessage, a custom DOM event, or an app-specific event emitter. Cypress also emits its own runner and lifecycle events, including events such as window:before:load, window:load, and uncaught:exception. These are distinct: to verify app behavior, observe the interface through which your app emits; to inspect Cypress lifecycle or runner behavior, use Cypress’s event APIs. See the Cypress catalog of events.
As an Amazon Associate I earn from qualifying purchases.
Cypress does not automatically record every arbitrary custom event. There is no universal listener that can observe all app event architectures. The right hook depends on the interface your application actually uses.
Recommended Free Tools
Capture startup events before the app runs
If the event may be emitted during startup, set up the observer in cy.visit()’s onBeforeLoad callback. Cypress runs this callback before application code, making it suitable for spying on built-in window methods before the app can call them. A later cy.window() call gives access to the live AUT window, but startup emissions may already have happened.
#1 Best Overall
cy.visit('/', {
onBeforeLoad(win) {
cy.spy(win, 'postMessage').as('postMessage')
},
})
cy.get('.new-todo').type('learn testing{enter}')
cy.get('.todo-list li').should('have.length', 1)
cy.get('@postMessage').should('be.called')
This pattern spies on postMessage, an example used by Cypress in a Redux/Kuker tutorial published March 20, 2019. It illustrates observing one app’s instrumentation interface; it does not mean every application uses Kuker or should adopt it. The tutorial’s example also checks calls and arguments, while avoiding brittle comparisons of entire event objects when values such as timestamps vary. See Cypress’s application-event tutorial.
Choose the observer that matches the app interface
| Need | Approach | Trade-off |
|---|---|---|
| Observe a method call while leaving its behavior intact | cy.spy(object, method) |
Install it before the first call if startup emissions matter. |
| Replace a method to control behavior, such as forcing an error or supplying a callback | cy.stub(object, method) |
A stub changes behavior, so it is not the right choice when verifying that the real method runs. |
| Inspect the current application window after navigation | cy.window() |
It can be too late to observe emissions that occurred during startup. |
| Invoke a DOM event handler with a specific event | .trigger(eventName, options) |
It dispatches the event but does not perform the browser’s default action. |
| Observe Cypress lifecycle or runner events | cy.on() or Cypress.on() |
Choose based on listener lifetime; Cypress event callbacks run outside the normal command queue. |
A spy records calls while allowing the original method to run; a stub replaces the function. Cypress documents both in its stubs, spies, and clocks guide, with API detail in cy.stub().
Rank #2
For an app-owned custom DOM event or emitter, register an observer on that specific interface before the relevant action, capture the event or payload, then assert in the test body. If you access the AUT window with cy.window(), Cypress documents the command and custom window typing in cy.window().
Do these 3 things before closing this tab:
1Fix the driver behind crashes, sound loss and screen glitches2Clear out junk files and repair common Windows errors3Scan for outdated or missing drivers - takes under a minuteAssert the event contract, not incidental details
Alias a spy so Cypress can retry its assertion, perform the action through the UI, and check the call and the payload fields that matter. Prefer stable contract fields—for example, an action type or item identifier—over whole-object equality when payloads include timestamps or other volatile metadata. Assert a call count when the expected number is part of the behavior, and inspect arguments when the payload itself matters.
Rank #3
Keep an event assertion tied to a reason. It can verify a meaningful contract, such as emitted state or action order, but assertions on internal events can couple an end-to-end test to implementation details. When the user-facing outcome matters, pair the event assertion with a visible UI assertion, as in the example’s check that a todo appears.
Use Cypress event listeners with the right scope
cy.on() listeners are scoped to the current test. Cypress.on() listeners persist beyond a single test, so registering them repeatedly can accumulate listeners. Also, Cypress event callbacks run outside the normal command queue: do not issue Cypress commands or assertions from inside the callback. Instead, capture the relevant data in the callback and assert against it later in the test body. The event catalog documents these scope and callback constraints.
Rank #4
Dispatching an event is not the same as observing one
Use .trigger() when the test’s purpose is to invoke a handler with a particular event. It does not reproduce the browser’s default action, so it is not a substitute for normal Cypress interactions in a user flow where browser behavior matters. For example, typing and pressing Enter exercises the interaction path in the sample; triggering a key event only invokes the relevant handler. See cy.trigger().
Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchWindows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallTroubleshoot missed or unreliable event assertions
- The spy records no startup call: move its setup into
onBeforeLoad; attaching it after navigation may miss a call made during app initialization. - The spy is attached to the wrong target: confirm which object and method the app actually uses. A
postMessagespy cannot observe a custom DOM event or a separate app emitter. - A stub makes the app behave differently: use a spy when the goal is observation and the original method must still run.
- The payload assertion fails intermittently: inspect the arguments and assert stable fields rather than comparing metadata such as changing timestamps.
- A Cypress command fails inside a listener: store data in the callback and make assertions later in the test’s command chain.
- A synthetic event does not reproduce a real interaction: use the normal Cypress action when the browser’s default behavior is part of what you need to test.
Or skip the browser setup
For capturing a page screenshot rather than testing an app-emitted event, ScreenshotNeo offers a one-request screenshot API; it does not replace Cypress’s event-observation workflow. Cookie banners, popups, and chat widgets are removed before the shot. Bot checks, blank pages, and failed loads are never billed; an MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card, and paid plans start at $5 for 3,000. See the ScreenshotNeo docs.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
ScreenshotNeo also returns a page verdict and billing status in response headers. Sign up for 1,000 free screenshots a month, with no card required.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




