In Cypress's `cy.screenshot()`, what do capture 'viewport', 'fullPage' and 'runner' capture?
answer
- Two values show only the app
- One value includes Cypress's own UI
- The default is not the viewport
- Failure captures are coerced to runner
- Element captures ignore the option
basics
~20 sviewport captures the application as it fits the current viewport, fullPage scrolls and stitches the whole application top to bottom, and runner captures the entire browser viewport including the Cypress Command Log. The default is fullPage.
solid answer
~40 s`capture` chooses how much of the browser goes into the image. `viewport` takes the application under test at the current `viewportWidth` by `viewportHeight`; `fullPage`, the default, scrolls the app from top to bottom and stitches those shots into one tall image; `runner` grabs the whole browser viewport, so Cypress's own Command Log and the error text are in the frame. Three rules override you: the screenshot taken automatically when a test fails is always coerced to `runner`, an element capture such as `cy.get('[data-cy=ticket-row]').first().screenshot()` ignores `capture` entirely, and `scale` is forced to `true` for `runner`. One consequence matters: `blackout` selectors are applied for `viewport` and `fullPage` only, and dropped for `runner`.
code
javascript · 18 lines// cypress/e2e/ticket-queue.cy.js
describe('ticket queue', () => {
it('captures the queue three ways', () => {
cy.visit('/tickets?status=open')
// just what fits the viewport
cy.screenshot('queue-viewport', { capture: 'viewport' })
// the whole scrollable queue, scrolled and stitched
cy.screenshot('queue-full', { capture: 'fullPage' })
// the app plus the Cypress Command Log
cy.screenshot('queue-runner', { capture: 'runner' })
// an element capture: `capture` is ignored here
cy.get('[data-cy=ticket-row]').first().screenshot('first-ticket')
})
})go deeper
Recall the three values and what each frames. Knowing that fullPage is the default for a manual capture is the part most candidates get wrong.
Explain the coercions: runner for failure captures, viewport for element captures, and scale forced true for runner. That is the mechanics layer this question is really about.
Talk about which capture is worth taking in CI, and why a stitched fullPage image of a long queue is often less useful than the runner shot you already get.
Weigh a project-wide Cypress.Screenshot.defaults change against per-call options, and what a house default does to every spec another team maintains.
## The three values `capture` is an option on `cy.screenshot()` and on `Cypress.Screenshot.defaults()`. It decides how much of the browser ends up in the PNG. - **`viewport`** — the application under test, exactly as much of it as fits the current viewport (`viewportWidth` by `viewportHeight`). None of Cypress's own UI appears. - **`fullPage`** — the application under test in its entirety. Cypress scrolls the app, captures at each position, and stitches the pieces into one tall image. - **`runner`** — the whole browser viewport, including the Cypress Command Log beside the app. This is the only value that puts the command list and the failure message into the picture. For `cy.screenshot()` the default is **`fullPage`**. ## Three coercions you did not ask for Whatever you pass, Cypress overrides `capture` in three situations: 1. **A failure screenshot is always `runner`.** The automatic capture taken when a test fails in `cypress run` sets `capture` to `runner` before taking the shot, so the image you get from CI always includes the Command Log, whatever your project default says. 2. **An element capture ignores `capture`.** `cy.get('[data-cy=ticket-row]').first().screenshot()` photographs that one element; passing `capture: 'fullPage'` alongside it changes nothing. `padding` is the option that shapes an element capture, widening the crop around the element. 3. **`scale` is forced to `true` for `runner`.** For `viewport` and `fullPage`, `scale` defaults to `false` so that two machines with different resolutions produce comparable images; a `runner` capture is always scaled to fit. ## What each value costs you | `capture` | Shows the application | Shows the Command Log | `blackout` applies | |---|---|---|---| | `viewport` | current viewport only | no | yes | | `fullPage` | whole page, scrolled and stitched | no | yes | | `runner` | as much as the browser window shows | yes | no | The last column is the one people are surprised by. `blackout` takes an array of selectors and paints the matching elements out of the image, but it is honoured only for the app-only captures. A `runner` capture drops the list entirely — and since every automatic failure screenshot is a `runner` capture, blackout selectors never reach the image CI hands you. ## The stitching artefact in `fullPage` Because `fullPage` scrolls and stitches, anything with `position: fixed` or `position: sticky` is photographed again at every scroll position. A support-ticket queue with a sticky status-filter bar produces an image with that bar repeated down the page. The documented workaround is to make it absolute for the duration of the capture: ```javascript cy.get('[data-cy=queue-filter-bar]').invoke('css', 'position', 'absolute') cy.screenshot('queue-full') cy.get('[data-cy=queue-filter-bar]').invoke('css', 'position', null) ``` A long queue also produces a very tall PNG — `fullPage` is the value that turns a hundred-row table into an image nobody scrolls through. ## Setting it in one place or per call - Per capture: `cy.screenshot('queue-open', { capture: 'viewport' })`. - For the whole project: `Cypress.Screenshot.defaults({ capture: 'runner' })` in the support file, which also changes what manual captures produce everywhere. `Cypress.Screenshot.defaults()` accepts the same `capture`, `blackout`, `scale`, `overwrite` and `disableTimersAndAnimations` options as the command, plus `screenshotOnRunFailure`. ## What a capture value cannot do for you Two limits are worth knowing before you spend an afternoon tuning this option: - **No capture value photographs browser chrome.** `runner` means the browser viewport, which is Cypress's own UI plus the app — never the tab strip, the address bar or a second tab. - **No capture value freezes the page.** `disableTimersAndAnimations` (default `true`) stops JavaScript timers and CSS animations while the shot is taken, but a request that resolves mid-capture still changes what is in frame. And `capture` is a screenshot option only: a spec video, when `video` is enabled, records what the browser showed for the whole spec and has no equivalent knob. ## Choosing between them - **`viewport`** when you want what a user could actually see at that moment — the honest answer for "did the empty-queue message appear above the fold". - **`fullPage`** when the interesting row is below the fold, and you accept the stitching artefacts and the tall file. - **`runner`** when the command list is the point. You rarely need to ask for it, because the capture you get for free on failure already is one.
- Why does a `fullPage` capture of the ticket queue show the filter bar four times?`fullPage` is not one photograph. Cypress scrolls the app, captures at each position and stitches the results, so a `position: fixed` or `position: sticky` element is re-photographed at every stop. Making it `position: absolute` for the duration of the capture, then restoring it, gives one copy.
- Does `capture` change what a video records?No. `capture` is a screenshot option only. A spec video, when `video` is enabled, always records the browser as the run drives it, and there is no equivalent option to include or exclude the Command Log from the recording.
saying these in an interview costs you the question
- Thinks viewport is the default capture value
- Believes capture shapes element screenshots too
- Expects blackout to apply to a runner capture
- Says fullPage is a single browser-level screenshot
- Assumes a failure screenshot honours the configured capture