In Cypress, which languages can specs be written in, and why is that permanent?
answer
- The runtime decides the language
- Same event loop as the app
- Listed under permanent trade-offs
- Types compile away before the browser
- cy.task() and cy.request() lead outward
basics
~20 sJavaScript and TypeScript, and nothing else. Cypress evaluates spec code inside the browser alongside the application, so the only language it can ever run is the language of the web; the docs list this as a permanent trade-off.
solid answer
~40 sCypress specs are **JavaScript or TypeScript only**. Test code is evaluated inside the browser, in the same event loop as the application under test, so the only language available is the one the browser executes. Cypress's trade-offs guide files this under *permanent* trade-offs, next to the one-browser and single-superdomain rules — it is not a roadmap gap waiting on a Java or Python client. TypeScript is not a second language here: Cypress ships official type declarations, and the types are erased before the spec loads. The default `e2e.specPattern` reflects the ceiling, matching only `cypress/e2e/**/*.cy.{js,jsx,ts,tsx}`, and Cypress 16 narrowed it further by removing built-in CoffeeScript support. Code that genuinely cannot be JavaScript never enters the spec: it stays in Node behind `cy.task()`, or behind an HTTP endpoint you call with `cy.request()`.
go deeper
Be ready to name the two languages in one sentence and give the reason: Cypress evaluates spec code inside the browser, so it can only run what a browser runs.
Explain that TypeScript is erased before the spec loads, and that the default spec glob and the bundled preprocessor resolve only JavaScript and TypeScript file extensions.
An interviewer wants the consequence: helper code written in another language cannot be imported into a spec, so decide where that logic is re-homed before a migration starts.
Frame it as a permanent product boundary rather than a version gap, and be able to say what it costs an organisation whose test engineers do not write JavaScript.
## The runtime decides the language Most browser-test tools sit **outside** the browser. The test process speaks a wire protocol to a driver, the driver speaks to the browser, and because that protocol is language-neutral, a client for it can be written in any language at all. Cypress is not built that way. A Cypress spec is loaded **into the browser** and evaluated there, in the same event loop as the application under test. There is no serialization boundary and no protocol sitting between a command and an element, which is exactly where time-travel debugging and native access to application state come from. It also fixes the language: whatever the browser can execute is the entire set of options, and browsers execute JavaScript. Cypress's own trade-offs guide states it without hedging — the only language it will ever support is the language of the web. That sentence sits under the heading **Permanent trade-offs**, beside the one-browser-at-a-time rule and the single-superdomain rule, and it is deliberately kept apart from a shorter list of *temporary* restrictions the project hopes to address. ## Permanent versus temporary, as the project itself defines them Reading that split correctly is what separates a confident answer from a guess: - **Permanent** — commands run inside the browser, one browser at a time, one superdomain per test, and one language. - **Temporary** — there is no `cy.hover()` command, no native or mobile event support, and iframe switching is limited. A team weighing adoption should price the permanent list as a property of the tool and treat the temporary list as risk that may retire on its own. Waiting for a Java or Python client is waiting for something that was never on a roadmap. ## TypeScript does not add a second language "JavaScript and TypeScript" reads like two languages and is really one. Cypress ships **official type declarations** in its npm package, so `cy` and `Cypress` type-check without a community types package, and TypeScript 5.x, 6.x or 7.x is supported as of Cypress 16. Types are then erased before the spec ever reaches the browser. What TypeScript changes: - Mistakes surface as an editor error rather than as a failing run minutes later. - Editor completion covers every `cy.*` command with its parameters and its documentation. - Specs can import the application's own domain types, so an app-side change propagates into tests. What it does not change: the runtime, the command queue, retry-ability, or the ceiling itself. ## What Cypress 16 changed about accepted files Cypress 16 narrowed the accepted surface rather than widening it. Built-in **CoffeeScript support was removed**: the default `@cypress/webpack-batteries-included-preprocessor` no longer bundles `coffee-loader` or `coffeescript`, and `.coffee` is no longer resolved for spec files, support files, or `cy.fixture()`. A project that must keep it registers Cypress's own `@cypress/webpack-preprocessor` on the `file:preprocessor` node event with its own loader rule, and owns that toolchain from then on. | What Cypress loads | Where the default states it | |---|---| | `.js`, `.jsx`, `.ts`, `.tsx` specs | the default `e2e.specPattern`, `cypress/e2e/**/*.cy.{js,jsx,ts,tsx}` | | JavaScript and TypeScript with no setup | compiled by the bundled preprocessor out of the box | | `.coffee` | nothing — removed in 16, reachable only through your own webpack rule | ## Where non-JavaScript work actually goes The ceiling governs what runs *in the spec*, not what a test can cause to happen. Two commands lead out of the browser: 1. `cy.task()` hands an event name and a JSON-serialisable argument to a handler registered on the `task` node event, which runs in Node — the door to a database seed, a file, or a child process. 2. `cy.request()` makes an HTTP call that Cypress issues from Node rather than from the browser, so it bypasses CORS and never shows up in the browser's network panel — the door to anything that already lives behind an API. Neither makes another language available inside the spec. They move the work to where that language already lives and hand a value back. That is the shape of every honest answer here: the spec stays JavaScript or TypeScript permanently, and the rest of the stack is reached through a door rather than through an import.
- Can a Cypress 16 spec still be written in CoffeeScript?Not out of the box. Cypress 16 removed built-in CoffeeScript support: the default `@cypress/webpack-batteries-included-preprocessor` no longer bundles `coffee-loader`, and `.coffee` is no longer resolved for specs, support files or `cy.fixture()`. You can register Cypress's `@cypress/webpack-preprocessor` on the `file:preprocessor` node event with your own loader rule, but you then own that toolchain. Converting the files to JavaScript or TypeScript is the recommended path.
- Does Cypress shipping type declarations make TypeScript a first-class language?It makes TypeScript a first-class *authoring* experience. Cypress publishes official declarations with its npm package, so `cy` and `Cypress` type-check with no community types package, and TypeScript 5.x, 6.x or 7.x is supported. At runtime it is still JavaScript: types are erased before the spec reaches the browser, so TypeScript widens what the editor can tell you, not what the ceiling allows.
An out-of-process runner is a driver radioing instructions to a car it follows. Cypress is a passenger already sitting inside it, and a passenger can only speak whatever language is spoken in the car.
saying these in an interview costs you the question
- Says a Java or Python Cypress client is planned
- Treats TypeScript as a separate supported runtime
- Calls the JavaScript-only limit a temporary gap
- Believes spec code runs in Node beside the browser
- Thinks a preprocessor could bundle any language