ScreenshotNeo

BlogHow-to

How to Test Browser Alerts, Confirmations, and Prompts with Cypress

Test JavaScript alerts, confirmations, and prompts in Cypress by observing alert events, choosing confirm outcomes, and stubbing prompt before app code runs.

By the ScreenshotNeo team4 October 20267 min read

Cypress handles JavaScript browser dialogs differently: it automatically accepts alert() and confirm(); you can observe an alert message, and return false from a window:confirm handler to test cancellation. For window.prompt(), stub the method before your application code runs, then make it return a string or null.

Dialog Default Cypress behavior How to test it Can the test choose the result?
alert() Automatically accepted Listen for window:alert and assert its message No; the event cannot reject the alert
confirm() Automatically accepted Listen for window:confirm Yes; return false to cancel
prompt() Stub the browser method Replace window.prompt before app code runs Yes; return a string or null

These tests check how your application responds to dialog method calls. They do not verify the native browser or operating-system dialog’s visual appearance. Cypress’s event catalog documents alert and confirmation behavior; its stub documentation covers replacing window.prompt.

1. Test an alert message

Register a window:alert listener before clicking the control that triggers the alert. Cypress accepts the alert automatically, so the test continues without a manual dialog interaction. The event supplies the alert text.

describe('browser alerts', () => {
  it('shows the saved message', () => {
    const alertStub = cy.stub().as('alert')
    cy.on('window:alert', alertStub)

    cy.get('[data-cy="show-alert"]').click()

    cy.get('@alert').should('have.been.calledOnceWith', 'Saved successfully')
  })
})

Use a stable selector such as data-cy and assert the message the user should receive. If the application can raise multiple alerts, inspect each call or assert the expected call count as well as its arguments. An alert event does not let the test cancel the alert.

2. Test both confirmation outcomes

With no window:confirm handler, Cypress accepts a confirmation. To exercise the cancellation branch, return false from the handler. Assert the message and then verify the application’s resulting state.

Accept a confirmation

it('deletes an item after confirmation', () => {
  const confirmStub = cy.stub().as('confirm')
  cy.on('window:confirm', confirmStub)

  cy.get('[data-cy="delete"]').click()

  cy.get('@confirm').should('have.been.calledOnceWith', 'Delete this item?')
  cy.get('[data-cy="item"]').should('not.exist')
})

The event handler’s return value determines Cypress’s simulated response. A Sinon stub returns undefined by default, which leaves Cypress’s documented default behavior in effect: acceptance.

Cancel a confirmation

it('keeps an item when deletion is cancelled', () => {
  cy.on('window:confirm', (message) => {
    expect(message).to.equal('Delete this item?')
    return false
  })

  cy.get('[data-cy="delete"]').click()

  cy.get('[data-cy="item"]').should('exist')
})

Returning false is the cancellation mechanism. Check the item or another visible consequence so the test proves the application handled cancellation, not just that it called confirm().

3. Stub prompt input and cancellation

For an end-to-end test, install the stub in cy.visit()’s onBeforeLoad callback. Cypress runs this callback before the application’s JavaScript executes, so the app encounters the stub instead of the native method.

Return entered text

it('uses the name entered in the prompt', () => {
  cy.visit('/', {
    onBeforeLoad(win) {
      cy.stub(win, 'prompt')
        .as('prompt')
        .returns('Ada Lovelace')
    },
  })

  cy.get('[data-cy="set-name"]').click()

  cy.get('@prompt').should('have.been.calledOnceWith', 'Enter your name:')
  cy.get('[data-cy="greeting"]')
    .should('have.text', 'Hello, Ada Lovelace!')
})

Return null to simulate cancellation

it('handles a cancelled name prompt', () => {
  cy.visit('/', {
    onBeforeLoad(win) {
      cy.stub(win, 'prompt')
        .as('prompt')
        .returns(null)
    },
  })

  cy.get('[data-cy="set-name"]').click()

  cy.get('@prompt').should('have.been.calledOnceWith', 'Enter your name:')
  cy.get('[data-cy="greeting"]').should('not.contain', 'Hello,')
})

Choose the final assertion to match your app’s actual cancellation behavior: it might leave existing content unchanged, show a validation message, or avoid updating the value. The example uses a greeting as a placeholder for that observable outcome.

Component tests

A component test does not reload the page in the same way as an end-to-end visit. Stub the relevant window method before mounting the component:

it('uses prompt input in a component', () => {
  cy.stub(window, 'prompt').returns('Ada Lovelace').as('prompt')
  cy.mount(<NameEditor />)

  cy.get('[data-cy="set-name"]').click()
  cy.get('@prompt').should('have.been.called')
  cy.get('[data-cy="greeting"]')
    .should('have.text', 'Hello, Ada Lovelace!')
})

Use the component-test mount command configured in your project. The key is to stub before mounting, because mounting may execute the application code that calls prompt().

4. Listener timing, scope, and assertions

  • Install listeners before the trigger. Attach cy.on('window:alert', ...) or cy.on('window:confirm', ...) before clicking or submitting the control. Otherwise the synchronous dialog call may already have occurred.
  • Use test-scoped listeners. Cypress documents that cy.on() listeners attached to the current test’s cy object are removed at the end of that test. Put registration inside the test when only that test needs it.
  • Alias stubs for Cypress assertions. cy.stub() returns a Sinon stub and supports aliases. Cypress resets and restores stubs between tests.
  • Assert application behavior too. A call assertion proves the method was called with expected text; a UI or state assertion proves the application responded as intended.
  • Keep beforeunload separate. The unload lifecycle event is different from window.prompt(). Cypress exposes window:before:unload for checking an event’s returnValue; it is not a prompt stub.

For Sinon-Chai assertion syntax such as calledOnceWith, see Cypress’s bundled libraries reference. You can also use explicit call inspection when you want to distinguish multiple calls.

5. Troubleshooting

Symptom Likely cause Fix
The test hangs or waits for someone to dismiss an alert The test expects a manual browser interaction Listen for window:alert. Cypress automatically accepts JavaScript alerts.
The confirmation’s cancelled branch never runs The handler is missing or does not return false Return false directly from the window:confirm handler.
The prompt stub was not called The app ran before the stub was installed, or the trigger did not call window.prompt For end-to-end tests, stub in onBeforeLoad; for component tests, stub before mounting. Confirm the tested control reaches the prompt code path.
The prompt stub exists but the page still uses a native prompt The stub targeted the wrong window or was installed too late Stub the win supplied to onBeforeLoad for the page under test, and install it before app scripts execute.
An alias assertion says the stub was never called The test used a different stub than the one it aliased, or asserted before the action Alias the exact result of cy.stub(win, 'prompt') and perform the action before asserting.
Chained dialog calls produce confusing assertions The application raised several dialogs, while the assertion checks only one or assumes an order Inspect the expected call count and individual arguments. Cypress’s event examples use the recorded call list for multiple alerts.
The test passes but the native dialog looks wrong in a screenshot These event and stub recipes test application behavior, not native dialog rendering Test the resulting page state with Cypress; do not treat a stubbed browser method as visual verification of native UI.

6. Reliability, performance, and test cost

These patterns are synchronous at the dialog boundary: register the handler or stub before the call, trigger the app action, then assert the recorded call and observable result. Avoid arbitrary delays; they do not fix a listener installed after the event. Cypress assertions against UI state use the normal command retry behavior, while cy.stub() itself is synchronous and is not a retryable Cypress command.

Keep each test focused on one outcome. Separate accept and cancel cases make failures easier to diagnose and ensure both branches are exercised. Stub restoration between tests helps prevent one test’s configured prompt value from leaking into another. No separate browser, network service, or paid tool is required for these Cypress dialog recipes.

7. Coverage checklist

  • Assert the alert text and confirm the expected number of alert calls.
  • Test confirmation acceptance and cancellation as separate outcomes.
  • For prompts, cover a representative string and null cancellation.
  • Install event listeners before the action and prompt stubs before app code or component mount.
  • Assert the application’s visible or stateful result in addition to the dialog call.
  • Keep beforeunload tests separate from prompt tests.

Or skip the browser setup

If the task is to capture a website screenshot rather than test a dialog interaction, ScreenshotNeo is a website screenshot API and MCP server. Its single GET request can return an image or PDF, and the API accepts parameter names used by other screenshot APIs.

See the ScreenshotNeo API documentation. cURL example:

curl -G "https://api.screenshotneo.com/v1/shot" \
  -d access_key=YOUR_API_KEY \
  --data-urlencode url=https://stripe.com \
  -o shot.webp

ScreenshotNeo accepts cookie and consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers report the page verdict and billing status. Its MCP server provides take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots.

Sign up for 1,000 free screenshots a month, with no card required.

FAQ

Can Cypress reject an alert?

No. Cypress automatically accepts JavaScript alerts, and the window:alert event lets you inspect the message but not change that behavior.

Does returning false from a confirm handler accept or cancel?

It cancels the confirmation. Without a handler that returns false, Cypress accepts it.

Is Cypress cy.prompt() the same as browser window.prompt()?

No. cy.prompt() is an AI-powered Cypress command for turning natural-language steps into Cypress commands. Browser window.prompt() is a JavaScript dialog method that you can stub in the application under test.

Can these tests prove the browser’s native dialog design?

No. They assert application calls and behavior. They do not validate how native dialog chrome is rendered.

Sources