skip to content

Simulated User Events

The ten steps that fire real browser events at your application, what each one dispatches, and the options that change where the event lands and how fast it is typed.

on this pageshow

explore

questions

5

In Cypress, what does `.type()` do with text wrapped in curly braces?

level: juniorimportance: must knowfreq 72%

answer

  1. Not every character is literal
  2. Curly braces mean something
  3. Enter, selectall, backspace, del
  4. An unknown sequence throws
  5. parseSpecialCharSequences turns parsing off

basics

~20 s

Cypress reads curly braces in .type() as special key sequences rather than literal characters, so {enter} presses Enter and {selectall} selects the field. An unrecognised sequence throws. Type a literal brace with {{}, or pass the option parseSpecialCharSequences: false.

solid answer

~40 s

`.type()` scans the string for groups wrapped in curly braces and turns each one into a real key press instead of literal text. `{enter}`, `{esc}`, `{backspace}`, `{del}`, `{selectall}`, `{moveToEnd}` and the arrow keys are the everyday ones; `{ctrl}`, `{shift}`, `{alt}` and `{meta}` activate a modifier for the rest of that command. Anything else in braces is rejected outright — Cypress throws *Special character sequence: `{word}` is not recognized* and prints the list of valid ones. Two escape hatches exist: `{{}` types a literal `{`, and `{ parseSpecialCharSequences: false }` switches the whole mechanism off so a password like `p{ss}w0rd` goes in verbatim. It is also why `.clear()` needs no logic of its own: Cypress implements it as `.type('{selectall}{del}')`.

code

javascript · 8 lines
javascript
cy.get('[data-cy=catalogue-search]').type('Dune{enter}')
cy.get('[data-cy=catalogue-search]').type('{selectall}Le Guin{enter}')

// A borrower note that contains braces must escape or opt out
cy.get('[data-cy=loan-note]').type('{{}hold}')
cy.get('[data-cy=loan-note]').type('{hold} until Friday', {
  parseSpecialCharSequences: false,
})

go deeper

for a junior

Know that {enter}, {selectall} and {backspace} are key presses, not text, and that .clear() empties a field. Be ready to say what happens when a value you type happens to contain braces.

for a middle

Explain the event sequence a keystroke produces and why {enter} can fire change. Know that an unrecognised sequence throws rather than typing literally, and name both escape hatches.

for a senior

Expect data-driven specs to hit this: generated passwords and imported notes carry braces. Show that you reach for parseSpecialCharSequences: false on opaque values rather than sanitising test data.

for a principal

Own the convention for how a suite feeds untrusted strings into fields, so a fixture change cannot turn into a parse error in twenty specs. Decide where typing is the point and where seeding is faster.

`.type()` is one of Cypress's action commands: it takes the element the chain is holding, waits for it to be ready, and then plays your string at it as real keyboard events. What surprises people first is that the string is not sent verbatim — Cypress parses it, and anything inside curly braces is an instruction rather than text. ## What a keystroke actually is If the element is not already focused, Cypress issues a `.click()` on it first to bring focus there. Then, for each character, it fires the events a browser would fire: `keydown`, `keypress`, `beforeinput`, `textInput`, `input`, `keyup`. Not every key produces all six — `keypress` fires only for keys that produce one, `textInput` only when a character would actually be inserted, and `input` only when the element's value really changes. A `change` event follows either when `{enter}` is pressed and the value has moved since the last focus, or when the element later loses focus. Cypress also honours cancellation: if your app calls `preventDefault()` on `keydown`, the later events in that chain do not fire, exactly as in a real browser. ## The sequences and what each one does | Sequence | Effect | | --- | --- | | `{enter}` | Presses Enter, and fires `change` if the value moved since focus | | `{esc}` | Presses Escape | | `{backspace}` / `{del}` | Deletes to the left / right of the caret | | `{selectall}` | Selects the whole value by creating a selection range | | `{moveToStart}` / `{moveToEnd}` | Puts the caret at the start / end of a typeable element | | `{leftArrow}` `{rightArrow}` `{upArrow}` `{downArrow}` | Moves the caret | | `{home}` `{end}` `{pageUp}` `{pageDown}` | Line and page navigation | | `{insert}` | Inserts to the right of the caret | | `{ctrl}` `{shift}` `{alt}` `{meta}` | Activate a modifier for the rest of the command | The modifiers are the odd ones out, because they are a flag rather than a key press. `cy.get('#catalogue-notes').type('{ctrl}b')` types the letter `b` with `ctrlKey: true` set on its key events. By default every modifier is released when the command ends. Passing `{ release: false }` keeps it held across the commands that follow in the same test, which is how you build a shift-click; Cypress releases every modifier between tests regardless, so a held key cannot leak into the next `it`. ## When a brace is not a sequence An unknown group in braces is an **error, not literal text**. Typing a borrower's note that happens to read `{hold}` fails with: ``` Special character sequence: `{hold}` is not recognized. Available sequences are: ... If you want to skip parsing special character sequences and type the text exactly as written, pass the option: `{ parseSpecialCharSequences: false }` ``` That error is a good thing — the alternative would be a test that silently types nine characters where you meant a key press. It bites most often on generated data: ISBNs are safe, but note fields, JSON pasted into a textarea, and passwords from a generator are not. ## The two escape hatches 1. **`{{}` types a literal opening brace.** A closing `}` with no opener is already literal, so `.type('{{}hold}')` puts `{hold}` in the field. This is the surgical option when one brace in a mostly-normal string needs escaping. 2. **`{ parseSpecialCharSequences: false }` turns parsing off for the whole call.** Nothing in that string is interpreted, which is what you want for opaque values — a generated password, a pasted payload — where you cannot know in advance what braces it contains. Neither is a backslash escape. `.type('\\{hold}')` types a backslash followed by the same rejected sequence. ## Why `.clear()` inherits all of this `.clear()` has no keyboard machinery of its own — Cypress runs it as `.type('{selectall}{del}')` against the same element. That explains three things people find odd about it: - It only accepts an `input` or a `textarea`, because that is what `{selectall}` can act on. - It fires the same `input` and `keydown` events a user emptying the field would fire, so a controlled component's `onChange` runs. It is not a silent value reset. - It goes through the same readiness wait as any other action, so clearing a field hidden behind a collapsed panel fails the same way typing into it would. ## In a library catalogue suite Typical uses look like this: `cy.get('[data-cy=catalogue-search]').type('Dune{enter}')` submits the search the way a reader would; `.type('{selectall}Le Guin')` replaces the previous term without a separate `.clear()`; and `.type('{esc}')` closes an open suggestion list. When the string comes from a fixture rather than a literal — a borrower note, an imported record — reach for `parseSpecialCharSequences: false` rather than hoping the data is brace-free. Two more constraints are worth knowing before you go looking for a bug. `.type()` refuses an empty string (*cannot accept an empty string. You need to actually type something*) — use `.clear()` for that. And it requires a typeable element: an `input` with a text-like `type`, a `textarea`, `body`, or anything carrying `tabindex` or `contenteditable`. Targeting a child node of a `contenteditable` element fails, because the child is not itself editable; click the editable element first, then type into it.

  • In Cypress, what does `.type('{ctrl}b')` do, and how long does that modifier stay active?
    It types `b` with `ctrlKey: true` on the key events. Modifiers stay active for the rest of that single `.type()` call and are released when it ends, unless you pass `{ release: false }`, which keeps them held for the commands that follow. Cypress releases every modifier between tests regardless, so a held key cannot leak into the next test.
  • In Cypress, why does `.type('')` fail, and what should you use instead?
    `.type()` rejects an empty string with *cannot accept an empty string. You need to actually type something* — an empty call is almost always a bug in how the value was built. To empty a field, use `.clear()`, which Cypress runs as `.type('{selectall}{del}')` and which fires the same events a user deleting the text would fire.

saying these in an interview costs you the question

  • Thinks .type('{enter}') types those seven characters
  • Assumes any word in braces is a valid key
  • Escapes a literal brace with a backslash
  • Believes .clear() silently resets the value
open as a page

In Cypress, why does `.click()` error on 14 matched rows while `.dblclick()` does not?

level: middleimportance: should knowfreq 55%

basics

~20 s

Cypress'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.

open as a page

Why can a Cypress spec that types into a search box break on the upgrade to 16?

level: seniorimportance: should knowfreq 46%

basics

~20 s

Cypress 16 changed .type()'s default keystroke delay from 10 milliseconds to 0, so the whole string now goes in with no gap. A debounced typeahead that used to fire a request per pause now fires once, and timing-dependent assertions change with it.

open as a page

In a Cypress suite, how far should `.trigger()` stand in for a real action command?

level: principalimportance: should knowfreq 36%

basics

~20 s

Cypress's .trigger() dispatches the named event and nothing else: no default browser action, no companion events, no focus change. Use it where the behaviour under test really is a listener, and keep real action commands wherever the browser default is the point.

open as a page

In Cypress, why does a `.click()` report the event firing on the button's inner span?

level: middleimportance: nice to knowfreq 34%

basics

~20 s

Cypress fires at a coordinate, not at a node. The click lands on whatever element is topmost at that point, so a child covering the button's centre gets the event, and the Command Log names it. Focus still goes to the button.

open as a page