In Cypress, when is a Cypress.Commands.overwrite() in the support file worth it?
answer
- The support file runs before every spec
- Blast radius is the whole suite
- No opt-out at the call site
- Prefer a new named command
- Change an option, not the meaning
basics
~20 sOnly for a rule that must hold everywhere and that no author should have to remember. The support file loads before every spec, so an overwrite silently redefines a built-in suite-wide; a new named command is usually more honest.
solid answer
~40 s`Cypress.Commands.overwrite()` in `cypress/support/e2e.js` changes a built-in for every spec in the run, because Cypress evaluates the support file before each one. Nothing at the call site says so: `cy.get('[data-cy=approve-report]').click()` reads identically whether or not `.click()` has been redefined. That makes it the right tool for exactly one shape of problem — a cross-cutting rule that must hold everywhere and would be a real defect if someone forgot it, such as keeping a sensitive value out of the Command Log. For anything else, prefer a call-site option, a config key, or a new named command like `cy.clickAndSettle()`, because a name a reader does not recognise is a prompt to go and look. If you do overwrite, always call `originalFn`, change an option rather than the meaning, and keep the list short enough to review.
code
javascript · 12 lines// cypress/support/e2e.js - Cypress loads this before EVERY spec in the run
Cypress.Commands.overwrite(
'click',
(originalFn, subject, positionOrX, y, options = {}) => {
options.waitForAnimations = false
return originalFn(subject, positionOrX, y, options)
}
)
// receipts.cy.js - written by someone who never opened the support file
cy.get('[data-cy=approve-report]').click()go deeper
Know that the support file is loaded before every spec, so anything it registers or redefines applies to the whole run. That alone explains why overwriting a built-in is a big decision.
Explain the mechanics: Cypress.Commands.overwrite() wraps the original, receives it as originalFn, and must call it. Be able to say why a spec gives no hint that a built-in was replaced.
Show you have paid for this. Describe an overwrite that made a suite hard to reason about or hard to upgrade, and how you unwound it into a named command or a call-site option.
Own the policy. Say which built-ins your suite may modify, what makes a change orthogonal rather than semantic, who reviews an addition, and how the team is told that the dialect exists.
## What the blast radius actually is `Cypress.Commands.overwrite(name, fn)` replaces the implementation of a built-in command. Put it in `cypress/support/e2e.js` — the only place it is normally put — and Cypress evaluates it before **every spec in the run**. From that moment `.click()` means whatever your function says it means, in every spec, for every author, including the ones who joined last week and have never opened the support file. There is no opt-out at the call site unless you build one, and nothing in a spec hints that a built-in has been redefined. `cy.get('[data-cy=approve-report]').click()` looks exactly the same whether the overwrite exists or not. That is the whole trade: an overwrite is the only mechanism that guarantees a rule holds without anybody remembering it, and it is also the only mechanism that can change what a spec does without touching the spec. ## Orthogonal changes versus semantic ones The distinction that decides most of these calls is whether the overwrite changes *how* a command is configured or *what it means*. | Change | Kind | Verdict | |---|---|---| | Set a default option, e.g. `waitForAnimations: false` on `.click()` | orthogonal | usually defensible | | Mask a sensitive value out of the log for `.type()` | orthogonal | defensible, and hard to enforce any other way | | Add a fixed wait for one page's overlay before `.click()` | semantic | no — it is one page's problem, suite-wide | | Swallow an error so the spec keeps going | semantic | no — it makes every failure less trustworthy | | Change what a command yields to the next command | semantic | no — it breaks every chain silently | Orthogonal overwrites keep the mental model intact: `.click()` still clicks, still fails the same way, still yields the same subject. Semantic ones ask every reader of every spec to hold a private exception in their head, which is exactly the debt this leaf is about. ## The ladder to climb before overwriting Reach for the smallest tool that solves it: 1. **Pass the option at the call site.** If two specs need `{ waitForAnimations: false }`, write it twice. Visible, local, obvious. 2. **Set it in configuration.** Some behaviour is a config key — `defaultCommandTimeout`, `scrollBehavior`, `keystrokeDelay` — and a config key is discoverable in a way an overwrite is not. 3. **Add a new named command.** `cy.clickAndSettle()` makes the divergence visible in the spec. The reader sees a name they do not recognise and can look it up; that is a feature. 4. **Overwrite the built-in.** Only when the rule must hold everywhere, silently, and forgetting it would be a real defect — such as keeping a secret out of the Command Log. Most cases stop at step 1 or step 3. ## Making an overwrite survivable If you do take step 4: - **Always call `originalFn`.** An overwrite that forgets to invoke the original silently turns a command into a no-op, and the spec still passes. - **Change one thing.** A default option, a log detail. Not control flow, not the yielded subject, not error handling. - **Write it where the specs are.** A comment in the support file is not documentation for someone reading a spec. The team convention has to say which built-ins are modified. - **Keep the list short and reviewable.** Two overwrites are a convention; nine are a dialect of Cypress that only this repository speaks, and every onboarding engineer pays for it. - **Expect it to complicate upgrades.** You are wrapping an implementation whose signature and defaults belong to Cypress, not to you. ## What Cypress 16 removed from the option set Some overwrites are no longer available at all. `cy.getCookie()`, `cy.getCookies()`, `cy.getAllCookies()`, `cy.getAllLocalStorage()` and `cy.getAllSessionStorage()` became **queries** in Cypress 16, and `Cypress.Commands.overwrite()` cannot overwrite a query — attempting it throws, and directs you to `Cypress.Commands.overwriteQuery()`, which is a different API with different rules. An older suite that leaned on overwriting a cookie getter has to be rewritten rather than carried forward. That is worth weighing before you add another one: an overwrite is a bet on an implementation detail, and this release just settled a few of those bets against the people who made them. ## The judgement, stated plainly An overwrite is a suite-wide policy with no signature at the point of use. It is the right tool when the alternative is trusting every author to remember something, and the wrong tool for anything a reader would want to see in the spec. If you cannot name the defect that would occur if the rule were forgotten, you do not need the overwrite — you need a named command.
- What breaks if a Cypress overwrite forgets to call originalFn?The command becomes a no-op and the suite usually stays green, which is the worst outcome. Nothing clicks, nothing types, and the assertions that follow may still pass on stale state. Always return `originalFn(...)` and treat an overwrite that does not as a defect, because no test failure will tell you about it.
- Which Cypress commands can no longer be overwritten this way as of Cypress 16?The cookie and storage getters — `cy.getCookie()`, `cy.getCookies()`, `cy.getAllCookies()`, `cy.getAllLocalStorage()` and `cy.getAllSessionStorage()` — became queries in Cypress 16. `Cypress.Commands.overwrite()` throws on a query and points at `Cypress.Commands.overwriteQuery()` instead, so a suite that leaned on overwriting one of them has to be rewritten.
saying these in an interview costs you the question
- Overwrites .click() to fix one page's animation problem
- Assumes an overwrite applies only to the spec that needs it
- Forgets to call originalFn, silently disabling the command
- Treats the support file as private to whoever wrote it
- Reaches for an overwrite before trying a named command