skip to content

Why do cy.visit(), cy.session() and cy.origin() throw inside a Cypress component spec?

level: middleimportance: nice to knowfreq 22%

answer

  1. Three commands behave differently here
  2. The harness page has to survive
  3. Installed only when testingType is component
  4. There is nothing to navigate to
  5. A second origin does not exist

basics

~20 s

Each would destroy or bypass the harness page the component was mounted into. When Cypress runs in component mode the mount adapter overwrites all three commands to throw, with messages such as 'cy.visit from a component spec is not allowed'.

solid answer

~40 s

A component test has no application to navigate: the component is mounted into a near-empty harness page that Cypress serves itself. `cy.visit()` would replace that page and wipe the mounted tree; `cy.session()` clears the page and browser context to establish a session, which does the same thing; `cy.origin()` switches into a second origin, and there is only ever one here. So rather than let them half-work, the mount adapter overwrites all three with functions that throw as soon as they are called. It does this only when `Cypress.testingType` is `component`, which is why the identical commands behave normally in an end-to-end spec in the same project. Everything else is untouched - `cy.get()`, `.click()`, `.should()`, `cy.intercept()`, `cy.clock()` and the rest work against a mounted `DataGrid` exactly as they would against a page.

go deeper

for a junior

Remember that a component spec never navigates: there is no application page to visit, and cy.visit is switched off rather than merely discouraged.

for a middle

Explain the mechanism - the adapter overwrites those commands when Cypress.testingType is component - and say what each one would have destroyed if it ran.

for a senior

Show what you do instead: supply the state the component reads at mount time, or intercept its requests, rather than trying to rebuild a page around it.

for a principal

Own the guidance for when a component spec starts fighting the harness; pressure to fake navigation inside a mount is a signal the harness is being asked for something it does not provide.

## Three commands, switched off on purpose The selling point of Cypress component testing is that after `cy.mount` runs, the whole command API works unchanged. That is very nearly true. There are exactly three exceptions, and they are exceptions because each of them would destroy the thing the mount just built: - **`cy.visit()`** - loads a URL into the frame under test. The frame under test is the harness page holding your mounted component, so visiting anything discards it. The thrown message is literally *cy.visit from a component spec is not allowed*. - **`cy.session()`** - establishes and caches a browser session, and part of doing that is clearing the page and the browser context. Same outcome: the mounted tree is gone, and the session it cached would be meaningless because there is no application to restore it into. - **`cy.origin()`** - runs a block of commands against a second origin. A component test has one origin, the one the dev server and the harness page are served from. There is no second origin to switch to. | Command | What it does end to end | Why it cannot work here | |---|---|---| | `cy.visit()` | loads a URL into the frame under test | that frame is the harness page holding the component | | `cy.session()` | caches a session, clearing page and context | clearing the page destroys the mounted tree | | `cy.origin()` | runs commands against a second origin | a component test only ever has one origin | ## How the block is implemented The mount adapters share a small setup helper. It runs as a side effect of importing `cypress/react`, `cypress/vue`, `cypress/angular` or `cypress/svelte` - which, because you register `cy.mount` in the support file, happens before every component spec. The helper returns immediately unless `Cypress.testingType` is `'component'`. That single check is why the same project can have end-to-end specs that use all three commands freely: the overwrites are never installed for those runs. When the check passes, it calls `Cypress.Commands.overwrite()` on each of the three, replacing the implementation with one that throws. The same helper is where the between-test unmount is registered, so the block and the teardown arrive together with the adapter. ## What to do instead The instinct that reaches for `cy.visit()` in a component spec is usually one of three things, and each has a different answer: 1. **"I need the page's global CSS."** Put it on the harness page or import it in the support file. Nothing about navigation was really wanted. 2. **"The component needs to be signed in."** A component does not sign in; something above it does. Give the component the state it reads - as a prop, or by stubbing the module it reads from - so the test states the precondition instead of performing it. 3. **"I want to check the link actually goes somewhere."** It cannot be checked here. A mounted `DataGrid` can be asserted to render an anchor with the right `href` and to call its callback; what happens after the browser follows it is a different level of test. ## The corollary worth remembering The list of exceptions is short, and its shortness is the point. When someone claims component tests are "a different API", the honest answer is that three commands throw and everything else behaves identically: - **Queries and actions** - `cy.get()`, `cy.contains()`, `.click()`, `.type()`, `.trigger()` - operate on the mounted DOM with the usual retry behaviour. - **Assertions** - `.should()` with Chai and Chai-jQuery chainers - retry against the component the same way. - **Spies, stubs and the clock** - `cy.spy()`, `cy.stub()`, `cy.clock()`, `cy.tick()` - work as they do end to end. - **Network interception** - `cy.intercept()` - still applies to requests the component makes. ## Why throwing beats quietly working Two of the three could have been left to fail on their own terms. `cy.visit()` would load the page and the next `cy.get()` would time out somewhere confusing; `cy.session()` would appear to succeed and leave an empty document behind. Throwing immediately, with a message naming the testing type, turns a ten-minute debugging session into a one-line read - and it does so on the very first run rather than intermittently. It is worth recognising that pattern generally: a harness that refuses an operation it cannot honour is more useful than one that lets you find out later. ## When the block is telling you something Fighting the three thrown errors is nearly always a signal rather than an obstacle. If a check genuinely needs a URL, a real session or a second origin, the component is not the unit that can answer it, and no amount of harness work will change that. The productive move is to notice that early - the error arrives on the first run, not after an hour of debugging - and to keep the component suite answering questions about the component: what it renders, what it emits, and how it behaves when the data it is given changes.

  • How does Cypress switch those three commands off only for component tests?
    The mount adapters call a shared setup helper when they are imported. It returns early unless `Cypress.testingType` is `'component'`, and otherwise uses `Cypress.Commands.overwrite()` on visit, session and origin, replacing each with a function that throws - for example *cy.visit from a component spec is not allowed*. The same helper registers the between-test unmount.
  • If cy.origin() throws, what does that tell you about where a mounted component runs?
    That there is exactly one origin in play: the one the dev server and the harness page are served from. The command exists to move commands into a second origin, and a component test never has one, so it has nothing to do. Anything genuinely cross-origin has to be exercised at a level that can navigate.

saying these in an interview costs you the question

  • Calls cy.visit() to load a page before mounting a component
  • Thinks the three commands were removed from Cypress entirely
  • Assumes component specs run in Node with no real browser
  • Expects cy.session() to cache setup between component tests