In Cypress, why does `.click()` error on 14 matched rows while `.dblclick()` does not?
answer
- The subject may hold many elements
- Not every action command agrees
- One family errors, one iterates
- The error names the option to pass
- multiple: true is serial, not bulk
basics
~20 sCypress's .click() and .rightclick() default to multiple: false and refuse a subject holding more than one element, telling you to pass { multiple: true }. .dblclick() ships with multiple: true, so it double-clicks each matched element in turn without being asked.
solid answer
~40 sCypress action commands each decide for themselves what a multi-element subject means. `.click()` and `.rightclick()` default to `multiple: false` and throw *can only be called on a single element. Your subject contained 14 elements. Pass `{ multiple: true }` if you want to serially click each element*. `.dblclick()` carries the opposite default, `multiple: true`, so it acts on all fourteen and logs once per element; `{ multiple: false }` restores the error. `.type()`, `.select()`, `.trigger()` and `.selectFile()` reject a multi-element subject outright with no option to override. `.check()`, `.uncheck()` and `.clear()` quietly act on every matched element and offer no option at all. `multiple: true` is serial, not bulk: each element is scrolled to and re-checked for readiness in turn.
code
javascript · 9 lines// Errors: 14 rows matched, and borrowing all of them is not the intent
cy.get('[data-cy=book-row] button.borrow').click()
// Scope to the one row you mean
cy.contains('[data-cy=book-row]', 'Dune').find('button.borrow').click()
// Genuinely all of them: serial, one readiness wait per element
cy.get('[data-cy=genre-filter] input[type=checkbox]').check()
cy.get('[data-cy=banner] button.dismiss').click({ multiple: true })go deeper
Recognise the error that names your subject size and says to pass { multiple: true }. Know that it usually means your selector matched more rows than you meant.
Explain why the defaults differ between the click family and the checkbox family, and that multiple: true is a serial loop with a full readiness wait per element.
Be ready for the failure mode: a captured set going stale when the first action re-renders the list. Show how you restructure the test rather than reaching for the option.
Decide when a suite may act on a set at all, given that a bulk click mutates production-shaped data. Set the convention that scoping beats looping.
A Cypress query such as `cy.get('[data-cy=book-row] button.borrow')` yields a jQuery object that may hold one element or fifty. Every action command then has to answer the same question — *what does acting on fifty of you mean?* — and they do not all answer it the same way. That inconsistency is the whole of this question, and it is why one command errors on a set that another one happily walks. ## The three behaviours | Command | Subject holding more than one element | | --- | --- | | `.click()`, `.rightclick()` | Errors, unless you pass `{ multiple: true }`; then acts on each in turn | | `.dblclick()` | Acts on each in turn **by default**; `{ multiple: false }` restores the error | | `.type()`, `.select()`, `.trigger()`, `.selectFile()` | Errors, with no option to override | | `.check()`, `.uncheck()`, `.clear()` | Acts on every matched element, with no option needed | The error text tells you which group you are in and, for the click family, exactly what to do: ``` .click() can only be called on a single element. Your subject contained 14 elements. Pass `{ multiple: true }` if you want to serially click each element. ``` `.dblclick()`'s default is a historical accident rather than a design statement, so treat it as a quirk to remember rather than a rule to reason from. ## Why the default is not "act on all of them" A click on many elements is almost always a mistake in the selector rather than an intention. On a library catalogue, `cy.get('button.borrow').click()` reads as "borrow the book", and the day the shelf renders fourteen results that line would silently borrow fourteen books and leave the account in a state no assertion covers. Failing loudly is the better default: Cypress makes you say `{ multiple: true }` when you really do mean all of them, and the phrasing of the option — *serially* — is a warning in itself. `.check()` and `.uncheck()` are exempt because a group of checkboxes is genuinely a set: `cy.get('[data-cy=genre-filter] input').check()` ticking every genre is the natural reading, not an accident. `.clear()` is exempt because emptying several fields cannot compound the way clicking several buttons can. ## What `{ multiple: true }` actually does It is not a bulk operation and it does not fire fourteen events at once. Cypress loops the subject and performs a complete action per element: 1. Scroll that element into view, honouring `scrollBehavior`. 2. Run the readiness checks against **that** element and wait for them. 3. Fire the full event sequence at its coordinates. 4. Write its own entry in the Command Log, so fourteen clicks produce fourteen log lines. Two consequences follow. First, it is slow in proportion to the set — each element pays the full wait. Second, and more important, **the set is captured once**. If clicking the first Borrow button re-renders the results list, the remaining thirteen elements in the subject are detached from the document and the command fails partway through, with the app in a half-acted-upon state. A loop over a list that reacts to being acted upon is the classic misuse. ## What to write instead - **When the multi-element subject is a selector bug**, fix the selector: scope to a row, or add the attribute that identifies the one you mean. `cy.get('[data-cy=book-row]').contains('Dune').parent().find('button.borrow').click()` is the honest version of "borrow Dune". - **When you genuinely mean all of them and the page does not re-render**, `{ multiple: true }` is exactly right — ticking a filter group, dismissing a set of banners. - **When the page does re-render on each action**, act on one element at a time and re-query between actions, so each action gets a fresh element rather than a stale member of a captured set. - **When you only want to prove the buttons are there**, do not click at all: an assertion on the set is cheaper and does not mutate the app. ## The related error people confuse it with `.type()`'s multi-element message looks similar but has no escape hatch: *.type() can only be called on a single element. Your subject contained 3 elements.* There is no `multiple` option for typing, because typing the same string into three fields is never what a form test means. The same is true for `.select()`, whose own `multiple` word refers to something completely different — the `<select multiple>` attribute, which is what lets you pass an **array of option values** to one `.select()` call. Passing an array to a single-value select gives you *was called with an array of arguments but does not have a `multiple` attribute set*, which is about the element, not about the subject size.
- In Cypress, what breaks when `{ multiple: true }` clicks a list that re-renders after each click?The subject is captured once, before the first click. If the first action makes the app re-render the list, the remaining elements in that captured set are detached from the document and the command fails partway through, leaving some clicks done and some not. Act on one element at a time, re-querying between actions, so each action gets a fresh element.
- In Cypress, does `.select()`'s `multiple` mean the same thing as `.click()`'s `multiple` option?No. `.click({ multiple: true })` is an option about the size of the subject. `.select()` has no such option: its `multiple` refers to the `<select multiple>` attribute on the element, which is what permits passing an array of values to one call. Passing an array to a single-value select errors with *was called with an array of arguments but does not have a `multiple` attribute set*.
saying these in an interview costs you the question
- Says every action command acts on all matches
- Adds multiple: true to silence a selector bug
- Thinks multiple: true fires the events simultaneously
- Expects .type() to accept a multiple option
- Confuses .select()'s multiple with the click option