In Cypress, what does cy.wrap() do, and when do you need it?
answer
- Getting an outside value into the chain
- Parent command, starts its own chain
- Promises are resolved before yielding
- Needed before .its() and .invoke()
- cy.wrap($el) to re-enter from a callback
basics
~20 scy.wrap() starts a Cypress chain whose subject is the value you hand it - a plain object, a number, a jQuery element or a promise - so queries, .should() assertions, .its() and .invoke() can run against it.
solid answer
~40 s`cy.wrap(value)` is a parent command that puts any value into the command chain as the subject, so whatever you hand it is what the next command sees. `cy.wrap(loan).its('dueDate').should('eq', '2026-10-01')` works on a plain object from a library catalogue test with no DOM involved at all. If the argument is a promise, `cy.wrap()` waits for it to settle and yields the resolved value, failing the test if it rejects. The everyday use is re-entering the chain from inside a callback: the `$row` a `.then()` hands you is a jQuery object, so `$row.click()` is jQuery's own method and Cypress never sees it, while `cy.wrap($row).click()` is Cypress's queued, logged command. `cy.wrap()` also gives `.its()` and `.invoke()` something to chain off, since both are child commands and cannot be called on `cy` directly.
code
javascript · 14 linesit('borrows a copy of Dune', () => {
cy.visit('/catalogue')
cy.get('[data-cy=book-row]')
.contains('Dune')
.then(($row) => {
const bookId = $row.attr('data-book-id')
cy.wrap(bookId).should('match', /^B-\d{4}$/)
cy.wrap($row).find('[data-cy=borrow]').click()
})
cy.get('[data-cy=copies-left]').should('have.text', '2')
})go deeper
Be ready to say what cy.wrap() yields, and to write the one line that wraps a jQuery element from inside a .then() callback so a Cypress command can act on it.
Explain that cy.wrap() is a parent command, that it resolves a promise argument before yielding, and that a chained assertion retries against the same captured value rather than re-reading the page.
An interviewer at this level expects you to draw the line between reading with jQuery inside a callback and acting through Cypress, and to say why the Command Log goes silent when that line is crossed.
Own the convention. Decide where a suite may wrap application values at all, and how you keep cy.wrap() from becoming the escape hatch that hides untestable UI behind object assertions.
## The subject is what a chain passes along Every Cypress command hands exactly one value - its **subject** - to whatever is chained after it. `cy.get('[data-cy=book-row]')` yields a jQuery collection of matching rows, `cy.title()` yields a string, `cy.window()` yields the application's `window` object. The next link receives that subject without you naming it, which is why `cy.get('[data-cy=book-row]').first().click()` reads the way it does: `.first()` narrows the subject, `.click()` acts on what `.first()` handed over. `cy.wrap(value)` is how you put something into that pipeline **from outside it**. It is a parent command, so it starts a fresh chain, and its subject is exactly the argument you gave it. From that point on the value is an ordinary Cypress subject: `.its()`, `.invoke()`, `.then()`, `.each()` and retrying `.should()` assertions all work against it, and it gets its own entry in the Command Log. ## The four jobs it actually does - **Assert on a plain value.** A borrow count read out of a fixture is not a DOM node, but `cy.wrap(loan.copiesLeft).should('eq', 2)` still gets a real Cypress assertion, with the Command Log entry and the diffed failure message that comes with it. - **Re-enter the chain from inside a callback.** The `$row` that `.then(($row) => ...)` gives you is a jQuery object, not a chainer. `$row.click()` is **jQuery's** `click()`: it dispatches a DOM event synchronously, right now, outside the queue. `cy.wrap($row).click()` enqueues Cypress's own `.click()` behind the commands already in flight. - **Wait for a promise.** `cy.wrap()` resolves a promise argument before yielding, so `cy.wrap(libraryApi.findBook('B-1204')).its('title').should('eq', 'Dune')` waits for the application's own promise and fails the test if it rejects. - **Give a child command a subject.** `.its()` and `.invoke()` require a previous command; call one straight off `cy` and Cypress answers *"you are trying to call a child command before running a parent command"*. `cy.wrap(loan).its('borrower.name')` is the shortest legal way to read a nested property off an object literal. ## What it changes, and what it leaves alone | you have | you write | the next command sees | |---|---|---| | a plain object or number | `cy.wrap(loan)` | that same value, unchanged | | a jQuery element from a callback | `cy.wrap($row)` | the element, as a Cypress subject | | a promise from application code | `cy.wrap(promise)` | the resolved value | | a rejected promise | `cy.wrap(promise)` | nothing - the command fails the test | `cy.wrap()` does **not** make the wrapped value live. It captures the reference you passed at the moment the command ran; if the application later replaces that object with a new one, the wrapped subject still points at the old one. It also does not turn a plain object into a DOM subject - element-only commands such as `.click()` or `.type()` still error unless the wrapped value really is an element. An assertion chained onto `cy.wrap()` does retry: Cypress re-checks the **same** captured subject until the assertion passes or `defaultCommandTimeout` expires. That is genuinely useful for a value your application mutates in place, such as an array it keeps pushing into, and useless for one it recomputes and reassigns - for that, query the page again instead. ## A library catalogue example, end to end 1. Query the row you care about and hand the jQuery element to a callback: `cy.get('[data-cy=book-row]').contains('Dune').then(($row) => { ... })`. 2. Inside the callback, read whatever you need with jQuery: `$row.attr('data-book-id')`, `$row.find('[data-cy=due-date]').text()`. Reading is safe and synchronous. 3. Re-enter the chain for anything Cypress should own: `cy.wrap($row).find('[data-cy=borrow]').click()`. 4. Assert on the plain value you captured: `cy.wrap($row.attr('data-book-id')).should('match', /^B-\d{4}$/)`. Steps 2 and 3 are the split that trips people. jQuery is fine for **reading** a value you already have in hand; every **action** should go back through a Cypress command so it is queued in order, snapshotted, and visible in the Command Log when the test fails at three in the morning in CI. ## Where it goes wrong - **Calling `.its()` or `.invoke()` off `cy`.** Both are child commands. Start with `cy.wrap()` or any query first. - **Expecting `cy.wrap()` to re-read.** The subject is the reference captured when the command ran, not a live view of application state. - **Wrapping a scraped string instead of querying the page.** If the value is on screen, query it so the assertion retries against the real DOM rather than against a snapshot you took earlier. - **Drowning the Command Log.** Pass `{ log: false }` when the wrapped subject is a large object, and put a readable message in a `cy.log()` call instead.
- What happens if the promise you pass to Cypress's cy.wrap() rejects?Cypress fails the test with the rejection as the error. `cy.wrap()` resolves a promise argument before it yields, and a rejection is treated exactly like a thrown error inside a command: the chain stops there and the commands queued behind it never run.
- Why does calling jQuery's click() inside a Cypress .then() callback not appear in the Command Log?Because it is jQuery's `click()`, not Cypress's. It dispatches a DOM event synchronously, outside the command queue, so Cypress never enqueues it, never snapshots it and never reports it. Wrap the element - `cy.wrap($row).click()` - to get Cypress's command and its log entry.
saying these in an interview costs you the question
- Says cy.wrap() converts any value into a DOM element
- Thinks cy.wrap() re-reads the value on every retry
- Uses jQuery's click() in a callback and expects Cypress logging
- Calls cy.its() or cy.invoke() directly off cy