In a Cypress `cypress.config.js`, which options must sit inside the `e2e` block, not the root?
answer
- One exported object, two special keys
- Not every key is legal everywhere
- Per-type settings live inside a block
- baseUrl, specPattern, supportFile, testIsolation
- Root keys are shared by both testing types
basics
~20 sThe testing-type-only keys: baseUrl, specPattern, supportFile, excludeSpecPattern, slowTestThreshold and testIsolation. Cypress refuses to start and names the offending key if one of them appears at the root of the exported object, telling you to move it into e2e or component.
solid answer
~40 sA Cypress config file exports one object, usually wrapped in `defineConfig()`, and two of that object's keys are special: `e2e` and `component`. A key that only means something for one testing type must live inside the matching block — `baseUrl`, `specPattern`, `supportFile`, `excludeSpecPattern`, `slowTestThreshold` and `testIsolation` for `e2e`, plus `devServer` and `indexHtmlFile` for `component`. Put one of them at the root and Cypress throws before the run starts, telling you to set `e2e.<key>` and `component.<key>` instead. Everything else — `defaultCommandTimeout`, `videosFolder`, `retries`, `viewportWidth`, `video` — is shared and belongs at the root, where both testing types inherit it; you may still repeat one inside a block to give a type its own value. `defineConfig()` itself only returns the object you pass it, so it buys editor completion, not behaviour.
code
javascript · 15 linesconst { defineConfig } = require('cypress')
module.exports = defineConfig({
// shared by both testing types
defaultCommandTimeout: 8000,
viewportWidth: 1440,
videosFolder: 'artifacts/admin-console/videos',
e2e: {
baseUrl: 'https://staging.admin.example.com',
specPattern: 'cypress/e2e/tenants/**/*.cy.ts',
supportFile: 'cypress/support/admin-console.ts',
testIsolation: true,
},
})go deeper
Be ready to open a Cypress config file and say, key by key, which ones live at the root and which live in a block. Knowing that baseUrl and specPattern are e2e-block keys is the screening question here.
Explain why the split exists rather than listing it: spec discovery, base URL and test isolation have no single meaning across e2e and component runs, so Cypress refuses them at the root instead of guessing.
An interviewer expects you to recognise the config error from its text and fix it without a search, and to have an opinion on which shared keys your team declares once at the root versus repeats per testing type.
Own the question of how much of a suite's behaviour is allowed to live in this file at all, and what a reviewer should push back on when a config file starts growing per-team blocks.
## One file, one exported object A Cypress project keeps every setting in a single file at the project root: `cypress.config.js`, or `cypress.config.ts`, `cypress.config.mjs`, `cypress.config.cjs` for TypeScript and explicit module formats. The file exports **one object**, and the conventional shape wraps it in the `defineConfig()` helper the `cypress` package exports: ```javascript const { defineConfig } = require('cypress') module.exports = defineConfig({ defaultCommandTimeout: 8000, e2e: { baseUrl: 'https://staging.admin.example.com' }, }) ``` `defineConfig()` is an **identity function** — it returns the object you hand it, untouched. Its entire job is to attach the `Cypress.ConfigOptions` type so an editor completes key names and underlines a misspelling. Cypress parses the file perfectly well without it. ## Three zones inside that object Two of the object's keys are special. `e2e` and `component` are themselves configuration keys whose values are objects, and each holds the settings for one **testing type**. Everything else at the root is shared. | Zone | Examples | Applies to | |---|---|---| | Root | `defaultCommandTimeout`, `videosFolder`, `viewportWidth`, `retries`, `video`, `reporter` | both testing types | | `e2e` block | `baseUrl`, `specPattern`, `supportFile`, `excludeSpecPattern`, `slowTestThreshold`, `testIsolation`, `setupNodeEvents` | an e2e run only | | `component` block | `devServer`, `indexHtmlFile`, `justInTimeCompile`, `specPattern`, `supportFile`, `setupNodeEvents` | a component run only | The split is not stylistic. A key goes in a block when it has no single sensible meaning across both types. Spec discovery is the clearest case: an e2e run looks under `cypress/e2e`, a component run looks next to the components, so one `specPattern` at the root could not serve both. `baseUrl` is meaningless for a component run, which mounts a component instead of visiting a server. And `testIsolation` is an e2e concept — component tests always reset between tests and cannot turn it off. ## What happens when a key is in the wrong zone Cypress does not shrug this off. Before it resolves anything, it validates the raw exported object and **throws**: - `baseUrl`, `specPattern`, `supportFile`, `excludeSpecPattern`, `slowTestThreshold`, `testIsolation`, `indexHtmlFile` and `justInTimeCompile` at the **root** are refused. The error names the key and tells you to set `e2e.<key>` and `component.<key>` instead. - `baseUrl` and `testIsolation` **inside `component`** are refused as well, for the same reason they exist only for e2e. - Unknown or removed keys are reported separately — for example, as of Cypress 16 `execTimeout` and `experimentalMemoryManagement` print a warning and are ignored, having been replaced by `taskTimeout` and `manageBrowserMemory`. The message for the first case reads, in effect, *"The `specPattern` configuration option is invalid when set from the root of the config object. Set it within a testing type property: `e2e.specPattern` and `component.specPattern`."* It is worth recognising on sight, because it is the most common first error on a hand-written Cypress config and it tells you the fix in full. Two placements deserve a note of their own. `setupNodeEvents` is a per-block key, so `e2e.setupNodeEvents` and `component.setupNodeEvents` are separate functions and a run only ever invokes the one for its testing type. And `supportFile` takes `false` to switch the support file off entirely — if the resolved path matches no file, or matches more than one, Cypress fails the run rather than continuing without it. So the failure is loud at the root and loud in the wrong block. The quiet failures live elsewhere — in which layer of the file ends up winning at run time. ## A worked example For a multi-tenant admin console with per-environment settings, the shape that earns its keep is: shared timeouts and artefact folders at the root, everything environment- and discovery-shaped inside `e2e`. 1. Put `defaultCommandTimeout`, `viewportWidth` and `videosFolder` at the root so both testing types inherit one number and one artefact location. 2. Put `baseUrl` in `e2e`, pointing at the deployed console under test. 3. Put `specPattern` and `supportFile` in each block, so e2e specs and component specs are discovered from disjoint directories. 4. Repeat a **shared** key inside a block only when the two testing types genuinely need different values — a longer `defaultCommandTimeout` for e2e than for component tests, say. ## The rule to carry away Ask what the key means for a component run. If the answer is "nothing", or "not the same thing", it belongs in a block. If it means the same to both, put it at the root and let both inherit it, because a root key is the only kind that can be stated once. Both blocks are read from the same file, but only one of them is alive in any given run — `cypress run --e2e` resolves the `e2e` block and drops `component` entirely, and the reverse for a component run. That is also why the file stays readable as it grows: the root is the part a reviewer can read once and trust for the whole project, and each block is a short, bounded list of the things that genuinely differ between the two ways this project is tested.
- What does `defineConfig()` actually do to a Cypress config object?Nothing at run time. It is an identity function exported by the `cypress` package that returns the object it was given. Its value is typing: it applies the `Cypress.ConfigOptions` type so an editor completes key names and flags a misspelled or misplaced option before you run anything. Deleting the wrapper changes no behaviour.
- Does the config file's extension change how Cypress loads it?Yes. Cypress decides ESM versus CommonJS before running the file, using Node's own rules: `.mjs` is always ESM, `.cjs` always CommonJS, and `.js` follows the nearest `package.json` `"type"`. It then loads with only `import()` or only `require()` and does not fall back, so a mismatch fails with a load error rather than silently working.
saying these in an interview costs you the question
- Says any option can be written at the root
- Thinks defineConfig is required for Cypress to parse the file
- Puts baseUrl at the root and expects e2e to inherit it
- Believes the component block accepts baseUrl or testIsolation