skip to content

In-Browser Runtime

The runner is loaded into the application's own page and runs in the browser's event loop, and both its native access and its hard limits follow from that placement.

on this pageshow

explore

questions

30

Which browsers can Cypress 16 launch, and how do you pick one for a run?

level: juniorimportance: must knowfreq 76%

answer

  1. Three engine families, not four
  2. One of them is only an experiment
  3. One of them is on its way out
  4. A CLI flag picks the browser per run
  5. defaultBrowser fills in when the flag is absent

basics

~20 s

Cypress 16 launches Chrome-family browsers - Chrome, Chrome for Testing, Chromium and Edge - plus Firefox, experimental WebKit, and the deprecated bundled Electron. Choose one per run with the --browser flag, or set defaultBrowser in the Cypress config.

solid answer

~40 s

As of Cypress 16, three engine families are drivable. The **Chromium** family covers Chrome, Chrome Beta, Chrome Canary, Chrome for Testing, Chromium itself and Microsoft Edge in stable, beta, dev and canary channels. The **Firefox** family covers Firefox, Firefox Developer Edition and Firefox Nightly, but only version 140 or newer. **WebKit**, Safari's engine, exists only as an experiment behind `experimentalWebKitSupport`. The bundled **Electron** browser still runs but is deprecated in Cypress 16 and will be removed. Cypress officially supports the latest three majors of Chrome, Firefox and Edge. Select one with `cypress run --browser edge`, add a release channel with a colon (`chrome:canary`), or pass a filesystem path for a build Cypress did not auto-detect. With no flag, Cypress uses `defaultBrowser`, and falls back to Electron when that is unset.

code

bash · 10 lines
bash
# what did Cypress detect on this machine?
npx cypress info

# pick a browser explicitly, per run
npx cypress run --browser chrome --spec cypress/e2e/statement-list.cy.js
npx cypress run --browser firefox --spec cypress/e2e/statement-list.cy.js
npx cypress run --browser edge:dev --spec cypress/e2e/statement-list.cy.js

# a build Cypress did not auto-detect, launched by path
npx cypress run --browser /usr/bin/chromium --headed

go deeper

for a junior

Be ready to name the browser families Cypress can launch and to select one from the command line. Interviewers use this as a warm-up before anything harder about the runner.

for a middle

Explain how Cypress detects browsers on the machine, what a channel suffix such as chrome:canary selects, and which setting decides the browser when no flag is passed.

for a senior

Show how you make the browser explicit in CI so a run never depends on whatever the image happens to contain, and how you spot a browser that is present but outside the supported window.

for a principal

Own the team-wide default: which browser a run uses when nobody passes a flag, where that default is written down, and who is accountable when a browser drifts out of support.

Cypress launches and controls its own browser instance rather than attaching to one you already have open, so the list of browsers a suite can cover is fixed by what Cypress knows how to automate. As of **Cypress 16.0.0** that list is short and worth knowing exactly. ## The engine families Cypress 16 drives - **Chromium family** (`Cypress.browser.family === 'chromium'`) - Chrome, Chrome Beta, Chrome Canary, **Chrome for Testing**, Chromium, and Microsoft Edge in stable, beta, dev and canary channels. - **Firefox family** (`family === 'firefox'`) - Firefox, Firefox Developer Edition and Firefox Nightly. - **WebKit** (`family === 'webkit'`) - Safari's rendering engine, available only as an opt-in experiment via the `experimentalWebKitSupport` configuration option. - **Electron** - the Chromium build bundled inside Cypress itself. It reports `family: 'chromium'`, and Cypress 16 **deprecates it as a test browser**; it will be removed in a future major version. Cypress auto-detects what is installed on the machine, so a suite driving a bank statement app normally needs no browser paths configured at all: it walks the well-known binary names for each browser above, reads a version out of each one it finds, and offers the result. `cypress info` prints what was found on that machine, which is the fastest way to answer "why is Edge not in the list" on a CI agent you cannot see. ## Choosing one for a run | `--browser` value | Family | Status in Cypress 16 | | --- | --- | --- | | `chrome`, `chrome:beta`, `chrome:canary` | `chromium` | Supported | | `chrome-for-testing` | `chromium` | Supported, pinned build | | `chromium` | `chromium` | Supported | | `edge`, `edge:beta`, `edge:dev`, `edge:canary` | `chromium` | Supported | | `firefox`, `firefox:dev`, `firefox:nightly` | `firefox` | Supported, version 140+ only | | `webkit` | `webkit` | Experimental, opt-in | | `electron` | `chromium` | Deprecated, removal planned | Cypress resolves which browser to launch in a fixed order: 1. A `--browser` value on the command line wins - a name (`chrome`), a name plus release channel (`edge:dev`), or a filesystem path such as `/usr/bin/chromium`. 2. Otherwise the `defaultBrowser` configuration option, if the project sets one. 3. Otherwise the bundled Electron browser - which is exactly the fallback Cypress 16 wants you to stop relying on. In `cypress open` you pick from a dropdown near the top right instead, and the browser is always headed there so you can watch the run. During `cypress run` every browser starts **headless** by default - Electron through its own headless mode, Chrome, Chromium and Edge with `--headless=new`, Firefox with `-headless`, and WebKit headless through Playwright. `--headed` shows the browser, and `--headless` forces it back. That default is why `cypress run` works on a CI machine with no display attached. ## Which versions count as supported Cypress officially supports the **latest three major versions** of Chrome, Firefox and Edge. These browsers are evergreen and their automation interfaces move quickly, so the window is deliberately narrow. One hard stop sits inside it: Cypress cannot launch **Firefox older than 140** at all, and refuses with an error naming the version to install. The consequence for a CI image is that "we installed a browser once" is not a finished job. An evergreen browser moves under a suite, an old pinned one falls out of the window, and both show up as a Cypress problem long before anyone suspects the browser. Chrome for Testing exists as the deliberate answer to the first half of that, and `--browser chrome-for-testing` selects it. ## Reacting to the browser from inside a spec Sometimes a statement page genuinely renders differently per engine and one assertion has to bend: - `Cypress.browser` exposes `name`, `family`, `channel`, `version`, `majorVersion`, `displayName`, `path`, `isHeaded` and `isHeadless`. - `Cypress.isBrowser('firefox')` matches by name, `Cypress.isBrowser('!chrome')` inverts it, an array matches several, and an object filters on properties such as `{ family: 'chromium' }`. - Cypress's test configuration takes a `browser` option, so `describe('embedded PDF viewer', { browser: 'firefox' }, ...)` runs a suite only under Firefox. Reach for these sparingly. Branching a spec per browser is how a suite quietly stops asserting the same behaviour everywhere. ## What is not on the list - **Safari itself cannot be launched.** Experimental WebKit runs Safari's *engine*, obtained from Playwright's `playwright-webkit` package; it is not the Safari application, and it does not carry Safari's own UI behaviour. - **Internet Explorer and real mobile browsers are not options.** Cypress launches desktop browser binaries on the machine running the test. - **A browser Cypress cannot detect is not automatically excluded.** You can pass its path to `--browser`, or add an entry to the `browsers` array returned from `setupNodeEvents` so it appears in the open-mode dropdown; filtering that array is also how you *remove* browsers a team should not use. The practical takeaway for a suite covering a bank statement app: Chromium browsers and Firefox are first-class, WebKit is a partial preview of Safari, and Electron is a fallback you should be actively leaving.

  • How do you launch a Chromium build that Cypress does not auto-detect?
    Pass a filesystem path to `--browser` instead of a name, for example `cypress run --browser /usr/bin/chromium`. Cypress inspects the binary, works out which browser it is, and launches it. To have it listed in `cypress open` as well, append an entry to the `browsers` array returned from `setupNodeEvents` with `name`, `family`, `channel`, `displayName`, `version`, `majorVersion` and `path`.
  • How do you run one Cypress suite only in Firefox?
    Use Cypress's test configuration `browser` option on the suite or test: `describe('statement viewer', { browser: 'firefox' }, ...)` runs it only under Firefox, and `{ browser: '!firefox' }` runs it everywhere else. Inside a test, `Cypress.isBrowser('firefox')` and `Cypress.browser.family` let you branch. A suite excluded this way simply does not run in the other browsers.

saying these in an interview costs you the question

  • Claims Cypress can launch Safari itself on macOS
  • Thinks WebKit is available by default in Cypress
  • Says Cypress drives any browser with a WebDriver server
  • Believes Electron is the recommended browser for CI
open as a page

Why do Cypress commands fail after the app navigates to another origin?

level: juniorimportance: must knowfreq 70%

basics

~20 s

Cypress runs inside the browser and obeys the same-origin policy, so a test is bound to one origin and cannot touch a document from a different one. Wrap the commands for the new origin in a cy.origin() block.

open as a page

Why does a console.log() in a Cypress spec never reach the terminal running cypress run?

level: juniorimportance: must knowfreq 58%

basics

~20 s

Spec code is evaluated inside the browser, so console.log() writes to that browser's DevTools console. The terminal belongs to Cypress's separate Node process, which only prints what code in cypress.config.js and its Node handlers log.

open as a page

In Cypress, what happens to a request whose host matches a `blockHosts` pattern?

level: juniorimportance: must knowfreq 55%

basics

~10 s

Cypress ends the request itself and answers 503, adding an x-cypress-matched-blocked-host header naming the pattern that matched. The request is never sent, so it resolves immediately instead of waiting on a third party.

open as a page

Which browser does `cypress run` launch in Cypress 16 without --browser?

level: middleimportance: must knowfreq 60%

basics

~20 s

The bundled Electron browser, unless the project sets defaultBrowser. Electron is deprecated as a test browser in Cypress 16 and will be removed in a future major, so set defaultBrowser or pass --browser to point runs at an installed browser.

open as a page

Why can't a Cypress cy.origin() callback see variables declared outside it?

level: middleimportance: must knowfreq 62%

basics

~20 s

The callback is not a closure. Cypress stringifies it, ships it to a second instance of itself running in the other origin and evaluates it there, so the surrounding scope is gone. Data reaches it only through the args option.

open as a page

In Cypress, what happens when you call cy.hover(), and what do you use instead?

level: middleimportance: must knowfreq 70%

basics

~20 s

Cypress has no hover command. Calling cy.hover() fails with an error saying it is not implemented and links to the workarounds page. Use .trigger('mouseover'), .invoke('show'), a forced action, or the cypress-real-events plugin's realHover for genuine pointer input.

open as a page

Which protocol does Cypress 16 use to drive Chrome, and which one drives Firefox?

level: middleimportance: must knowfreq 58%

basics

~20 s

Cypress drives Chrome, Chromium, Edge and the bundled Electron browser over the Chrome DevTools Protocol, and Firefox over WebDriver BiDi. The Node process speaks that protocol; your spec code still runs inside the browser next to the application.

open as a page

When a Cypress test downloads a PDF, where does the file land and why is there no dialog?

level: middleimportance: must knowfreq 52%

basics

~20 s

It lands in Cypress's downloadsFolder, cypress/downloads by default, on the machine running Cypress. At launch Cypress tells the browser to save downloads there silently, so no native Save As dialog can block an unattended run.

open as a page

In Cypress 16, where does the application's traffic actually go in Chrome versus Firefox?

level: middleimportance: must knowfreq 68%

basics

~20 s

In Chrome, Chromium and Edge the browser connects to your server directly and Cypress intercepts inside the browser. Firefox, WebKit and Electron still route every request through Cypress's HTTP/1.1 proxy, and forceHttp1 puts every browser back on that path.

open as a page

In Cypress, how do you press the Tab key to move focus between fields?

level: juniorimportance: should knowfreq 42%

basics

~20 s

Use cy.press(Cypress.Keyboard.Keys.TAB). It sends a real keydown and keyup to the browser through the channel Cypress drives it over, so native focus order applies. Cypress's .type() cannot do it, because it does not support {tab}.

open as a page

Why is WebKit missing from Cypress 16's browser list despite experimentalWebKitSupport?

level: middleimportance: should knowfreq 37%

basics

~20 s

Because the configuration flag is only half of the opt-in. Cypress finds WebKit by resolving Playwright's playwright-webkit package from the project directory, so with the flag set but that package missing there is no WebKit build to list.

open as a page

Why can't one Cypress test drive two browsers at once for a two-user flow?

level: middleimportance: should knowfreq 48%

basics

~20 s

Cypress does not support controlling more than one open browser at a time; the spec runs inside the single browser it launched, and no command attaches to another. Cover a two-party flow by driving the second participant from Node with cy.request or cy.task.

open as a page

In Cypress, why does cy.intercept() never stub a request made by cy.request()?

level: middleimportance: should knowfreq 64%

basics

~10 s

cy.request() is issued by the Cypress Node process, not by the browser, so it never passes through the browser network layer where cy.intercept() listens. It reaches the real endpoint every time.

open as a page

Why does a Cypress spec fail to build when it imports your server's database module?

level: middleimportance: should knowfreq 46%

basics

~20 s

A spec is bundled for the browser before it runs, and a database module depends on Node built-ins the bundler cannot resolve for a browser target. Move that code into a Node handler and call it from the test.

open as a page

Why does Cypress 16 refuse to launch Firefox 128 in a pinned CI image?

level: seniorimportance: should knowfreq 33%

basics

~20 s

Cypress automates Firefox through WebDriver BiDi, and Firefox builds below 140 implement it incompletely. Cypress 16 marks such a build unsupported and refuses to launch it, naming the version to install. The fix is to update the pinned image or ESR build.

open as a page

Why does a Cypress cy.origin() callback error when it calls cy.intercept() or cy.session()?

level: seniorimportance: should knowfreq 40%

basics

~20 s

Those commands are not supported inside the callback, because the block runs in a second Cypress instance in the other origin. Declare them outside the block instead: the interception before the navigation, and cy.session() wrapping the whole flow.

open as a page

Why does cy.origin() not help a Cypress test reach into a cross-origin iframe?

level: seniorimportance: should knowfreq 46%

basics

~20 s

cy.origin() handles top-level navigation to another origin, not embedded frames. A cross-origin iframe's contentDocument reads as null under the same-origin policy, so no Cypress command can get a handle on its DOM. Same-origin iframes can be queried.

open as a page

In Cypress, how does the @cypress/puppeteer plugin reach a tab the test opened?

level: seniorimportance: should knowfreq 35%

basics

~20 s

The plugin registers named Node-side handlers in your Cypress config and adds a cy.puppeteer() command that calls one by name. The handler receives a Puppeteer browser connected to the browser Cypress launched, so it can find and drive the extra page.

open as a page

A Cypress assertion on `req.httpVersion` passes in Firefox and fails in Chrome. Why?

level: seniorimportance: should knowfreq 40%

basics

~20 s

The two browsers are on different network paths. As of Cypress 16 Chrome intercepts on the native browser network, where the protocol is negotiated after interception, so httpVersion is undefined. Firefox still proxies through Cypress, which always reported 1.1.

open as a page

In a Cypress suite, how far should you go to cover a flow that opens a new tab?

level: principalimportance: should knowfreq 40%

basics

~20 s

Decide by what the second tab would prove. Assert the handoff and request the URL when the destination is not yours; reach for the puppeteer plugin only when real value lives on the other side and no cheaper seam reaches it.

open as a page

Which keyboard interactions in a Cypress suite justify cy.press() over .type() or .trigger()?

level: principalimportance: should knowfreq 30%

basics

~20 s

Reserve cy.press() for behaviour only the browser decides — Tab focus order, Enter activating the focused control, Escape closing a dialog. Keep the cheaper in-page .type() and .trigger() for text entry and for handlers you already know exist.

open as a page

When, if ever, should a Cypress suite set `chromeWebSecurity` to `false`?

level: principalimportance: should knowfreq 36%

basics

~20 s

Rarely, and only for a behaviour no narrower option reaches, such as reading into a cross-origin iframe the app embeds. It works only in Chromium-based browsers, so it quietly pins the suite to one engine and stops testing the browser users have.

open as a page

In Cypress, what happens to screenshots when the app opens a second tab?

level: seniorimportance: nice to knowfreq 20%

basics

~20 s

Chromium pauses the renderer of a backgrounded tab, so the Cypress tab cannot paint a frame to capture. Cypress therefore activates its own tab before screenshotting, using its browser extension when enabled and force-fronting the tab otherwise, which steals focus in open mode.

open as a page

A Cypress run fails to connect to the Chrome DevTools Protocol after 50 seconds. What do you check?

level: seniorimportance: nice to knowfreq 24%

basics

~20 s

The browser launched but Cypress never reached its remote debugging port. On managed machines the usual cause is the RemoteDebuggingAllowed policy set to disabled. Check chrome://policy or edge://policy, and re-run once with a browser the policy does not cover.

open as a page

Why would a Cypress spec re-run in an endless loop after every save in cypress open?

level: seniorimportance: nice to knowfreq 26%

basics

~20 s

Something inside the spec's own import graph is regenerated by each build. The Cypress watcher sees the new file, recompiles, reloads the browser, and the rebuild regenerates the file again. Exclude those generated files from the watcher.

open as a page

Under Cypress, why does the browser block a first-party script carrying an `integrity` attribute?

level: seniorimportance: nice to knowfreq 24%

basics

~20 s

Cypress rewrites obstructive framebusting code in served HTML and JS by default, which changes the file's bytes. The pinned Subresource Integrity hash no longer matches, so the browser refuses the resource. Setting removeSRIAttributes to true strips the attribute.

open as a page

In Cypress CI, should you pin Chrome for Testing or use the installed Chrome?

level: principalimportance: nice to knowfreq 27%

basics

~20 s

Pin Chrome for Testing where a red run must mean a real regression, and keep a separate evergreen job so browser changes still reach you. A pin buys reproducibility, not immunity from the next Chrome release, and it needs a bump schedule.

open as a page

How would you decide whether a Cypress suite sets injectDocumentDomain to true?

level: principalimportance: nice to knowfreq 33%

basics

~20 s

Treat it as a dated migration budget, not a setting. It buys time for specs that cross subdomains after Cypress 14 made every origin change need cy.origin(), but it is deprecated, breaks some identity providers, and will be removed.

open as a page