← Back to Blog QA Automation

Cypress Alert Handling: From Zero to Hero! Complete Guide

Nestor Alonso · March 21, 2026
Cypress Alert Handling: From Zero to Hero! Complete Guide

Cypress Alert Handling: From Zero to Hero! Complete Guide

One of the most common mistakes in articles about Cypress is putting any popup, modal, or dialog into the same bucket and saying that "everything is handled the same way". That is not true. In Cypress there is an important difference between native browser dialogs—like alert, confirm, prompt or beforeunload—and the modals your application renders using HTML, CSS or React/Vue.

Cypress handles these cases in a diametrically opposed way, and understanding that difference is what separates an unstable (flaky) test from a robust and reliable one.

The official documentation makes it clear by separating events like window:alert, window:confirm, and detailing how to stub window.prompt.

The good news is that Cypress has an excellent design for these scenarios. You can listen to native events, dynamically control whether you accept or cancel a cypress confirm dialog, replace a prompt before the application loads, and visualize all your stubs in the Cypress console itself.

What types of dialogs exist in Cypress

When you look for information about "cypress alert handling", you must be clear about which of these 4 scenarios you are facing:

  • Simple warnings: window.alert()
  • Confirmations (OK/Cancel): window.confirm()
  • Text inputs: window.prompt()
  • Navigation warnings: beforeunload event

Next, we will see how to quickly install Cypress and how to handle each of these cases with clean and structured code.

How to install Cypress and prepare the project

If you are starting a project from scratch, installing Cypress is as simple as running:

npm install cypress --save-dev
npx cypress open

A solid base structure in TypeScript (cypress.config.ts) would look like this:

import { defineConfig } from 'cypress'

export default defineConfig({
  e2e: {
    baseUrl: 'http://localhost:3000',
    supportFile: 'cypress/support/e2e.ts',
  },
})

How alert works in Cypress

With window.alert(), Cypress has a very clear default behavior: it automatically accepts the alert. You don't have to interact with it, nor look for a button in the DOM, nor click on "Ok".

However, in QA Automation we almost always want to validate (assert) the text that this alert contains to make sure the application shows the correct message.

Basic example with window:alert

To validate the text, we subscribe to the window:alert event:

describe('Handling Alert in Cypress', () => {
  it('validates the text of a native alert', () => {
    // 1. We configure the listener BEFORE triggering the action
    cy.on('window:alert', (text) => {
      expect(text).to.contains('Successfully saved')
    })
    
    // 2. We execute the action
    cy.visit('/profile')
    cy.get('[data-cy="btn-save"]').click()
  })
})

How to handle confirm in Cypress

The case of window.confirm() (where the user can choose "OK" or "Cancel") is slightly different.

By default, Cypress also accepts it automatically. However, it gives you the flexibility to return false from the event to simulate a "Cancel".

Case 1: Accept the confirm (default behavior)

describe('Handling Confirm', () => {
  it('accepts the confirmation by default', () => {
    cy.on('window:confirm', (text) => {
      expect(text).to.equal('Are you sure you want to delete this record?')
    })
    
    cy.visit('/records')
    cy.get('[data-cy="btn-delete"]').click()
  })
})

Case 2: Cancel the confirm in Cypress

If you want to simulate that the user rejects the action (clicks Cancel), you simply need to return false inside the callback.

describe('Cancel Confirm', () => {
  it('cancels the confirm when returning false', () => {
    cy.on('window:confirm', (text) => {
      expect(text).to.equal('Are you sure you want to delete this record?')
      // By returning false, we tell Cypress to cancel
      return false
    })
    
    cy.visit('/records')
    cy.get('[data-cy="btn-delete"]').click()
    
    // We validate that the record still exists in the DOM
    cy.get('[data-cy="records-list"]').should('contain', 'Record 1')
  })
})

How to handle prompt in Cypress

Unlike alert and confirm, Cypress does not automate window.prompt() magically. If the application throws a prompt and you do nothing, the test will hang.

The official and correct way to handle this is to stub (replace the original call with a controlled spy) the window object using cy.stub().

Important! You must inject the stub before the application loads using the onBeforeLoad option of cy.visit().

Example of how to simulate a prompt

describe('Handling Prompt', () => {
  it('injects a custom value into the prompt', () => {
    cy.visit('/profile', {
      onBeforeLoad(win) {
        // We replace window.prompt and simulate the user typing 'Nestor'
        cy.stub(win, 'prompt').returns('Nestor')
      },
    })
    
    cy.get('[data-cy="btn-edit-name"]').click()
    
    // (Optional) We can validate that the stub was called
    cy.window().its('prompt').should('be.called')
  })
})

How to validate navigation with beforeunload

Sometimes, web applications throw a native dialog if you try to leave the page without saving changes (using the beforeunload event).

Cypress can intercept this perfectly with the window:before:unload event.

describe('Unsaved changes warning', () => {
  it('validates that the beforeunload protection is triggered', () => {
    // 1. We add the listener to validate the unload event
    cy.on('window:before:unload', (e) => {
      expect(e.returnValue).to.exist
    })
    
    // 2. We fill a form but do NOT save
    cy.visit('/article-editor')
    cy.get('[data-cy="input-title"]').type('Unsaved draft')
    
    // 3. We try to navigate to another page
    cy.get('a[href="/dashboard"]').click()
  })
})

Vital difference: cy.on() vs Cypress.on()

A very common mistake when looking for tutorials is confusing cy.on() with Cypress.on(). Although they look similar, they act on completely different levels:

  • cy.on('window:confirm', handler): Assigns the listener only for the currently running test (it). When the test finishes, the listeners are cleaned up automatically. This is a good practice because it maintains State Isolation.
  • Cypress.on('window:confirm', handler): Assigns the listener globally. It will persist through all tests in that spec file, which can generate bugs that are extremely hard to track (flakiness).

Common mistakes when automating dialogs in Cypress

  1. Registering the listener TOO LATE: If you click() and in the next line you register the cy.on('window:alert', ...), the test will fail because the event has already passed. Always register the event first.
  2. Trying to find the "Ok" button in the DOM: Native browser popups do not belong to the DOM (HTML) structure of the web page. You will never be able to select them with cy.get('.btn-ok').
  3. Confusing native dialogs with Bootstrap/Tailwind modals: If you see a modal appear on your screen but inspecting the source code shows it is a <div> with a high z-index, it is not a native alert! It is simply HTML and you must test it like any other element on the page using cy.get() and an assertion on its visibility.

Conclusion

Mastering Cypress Alert Handling and understanding the difference between window:alert, window:confirm and how to do a good prompt stub is fundamental to up leveling as a QA Automation Engineer.

Ignoring the officially documented Cypress conventions and trying to force clicks where they do not belong usually results in unreliable test suites. If you apply these patterns in your day to day, you will have deterministic tests that are much faster to debug.

FAQ

How to handle an alert in Cypress? expand_more
Cypress automatically accepts native alerts and allows listening to the window:alert event to validate the displayed text. You cannot change the auto-accept behavior.
How to cancel a confirm in Cypress? expand_more
By listening to window:confirm and returning false from the callback. Cypress strictly documents that this cancels the confirmation natively.
How to handle a prompt in Cypress? expand_more
The recommended way is to stub window.prompt, and in E2E it must be done inside onBeforeLoad of cy.visit() so it occurs before the application loads.
What is the difference between cy.on() and Cypress.on()? expand_more
cy.on() applies to the current test and is automatically removed when finished. Cypress.on() is global and persists between tests.
Are stubs automatically restored between tests? expand_more
Yes. Cypress creates stubs in a Sinon sandbox and automatically resets or restores them between tests.
How to check a beforeunload in Cypress? expand_more
You can listen to window:before:unload and validate event properties, such as returnValue, to verify that the application triggered an exit warning.