Interacting with Elements — click, type, check, select

8 min read

Selecting an element only matters because of what you do to it next. This lesson is the action layer of Cypress — every command that simulates a user touching the page. Each one fires real DOM events the app can't tell apart from a human, with all the auto-waiting and retry behaviour Cypress is famous for. By the end you'll have a complete typed registration-form spec that exercises click, type, check, select, and the keyboard shortcuts you'll use in real tests.

.click() — the most-used interaction

.click() clicks an element. Cypress checks first that the element is visible, enabled, and not covered — then dispatches the same mousedown / mouseup / click event sequence a real user would:

cy.get("[data-testid='submit']").click();

You can target a position within the element:

cy.get("[data-testid='canvas']").click("topLeft");
cy.get("[data-testid='canvas']").click("center");
cy.get("[data-testid='canvas']").click(100, 200);   // x, y in pixels from top-left

The position variants matter for elements like image maps, draggable canvases, or anywhere the click coordinate changes the result.

For different mouse buttons or click counts:

cy.get("[data-testid='item']").dblclick();      // double-click
cy.get("[data-testid='item']").rightclick();    // open context menu

When Cypress refuses to click ("element is being covered by another element," "element is detached from the DOM"), the right move is almost never { force: true }:

// ⚠️ Use sparingly — usually a sign the test scenario is wrong
cy.get("[data-testid='hidden-btn']").click({ force: true });

force: true skips the actionability checks. The errors you skipped were Cypress telling you the test setup is wrong — perhaps the element really is hidden behind a modal, or the page hasn't loaded the right state yet. Solve the underlying issue (close the modal first, wait for the right state) instead of forcing the click.

.type() — inputs and keyboard events

.type() types text into an input one character at a time, dispatching real keydown / input / keyup events on each. React, Vue, and other framework inputs receive these exactly like a human typing:

cy.get("input[name='email']").type("alice@test.com");

Special keys go in curly braces:

cy.get("input[name='search']").type("laptops{enter}");
cy.get("input[name='quantity']").type("{selectall}{backspace}5");
cy.get("input[name='code']").type("ABC{leftArrow}{leftArrow}{del}123");

The full list — {enter}, {tab}, {esc}, {backspace}, {del}, {selectall}, {home}, {end}, {leftArrow}, {rightArrow}, {upArrow}, {downArrow}, {pageUp}, {pageDown} — is in the Cypress commands cheat sheet.

Keyboard combinations use +:

cy.get("input").type("{ctrl+a}{backspace}");      // select all, delete
cy.get("input").type("{shift+tab}");              // go back to previous field
cy.get("input").type("{cmd+a}{cmd+c}");           // mac-style copy

For tests that need to simulate slow human typing (rare — usually for autocomplete debouncing):

cy.get("input[name='search']").type("laptops", { delay: 100 });

Default delay is 10ms per character; 0 disables the delay entirely.

.clear() — wiping inputs before typing

Inputs sometimes start with a value — server-rendered defaults, draft restoration, autofilled credentials. .clear() empties them:

cy.get("input[name='quantity']").clear().type("5");

.clear() works on <input>, <textarea>, and [contenteditable] elements. It selects all the existing content and deletes it. The chain .clear().type(...) is one of the most common patterns in Cypress — internalise it.

.check() and .uncheck() — checkboxes and radios

Checkboxes and radio buttons get their own commands rather than .click(), because the explicit names communicate intent better and Cypress can assert on the resulting state cleanly:

cy.get("[type='checkbox'][name='terms']").check();
cy.get("[type='checkbox'][name='terms']").uncheck();

For groups, target by value:

cy.get("[type='checkbox'][name='interests']").check("typescript");
cy.get("[type='checkbox'][name='interests']").check(["typescript", "cypress"]);

For radio buttons (which can only have one selected):

cy.get("[type='radio'][name='plan']").check("pro");

If the checkbox is already in the desired state, .check() is a no-op — it doesn't toggle. This makes tests idempotent: you can run the same .check() twice and the result is the same.

.select() — dropdowns

For native <select> elements, .select() chooses an option:

cy.get("select[name='country']").select("United Kingdom");        // by visible text
cy.get("select[name='country']").select("UK");                    // by value attribute
cy.get("select[name='country']").select(2);                       // by zero-based index

Multi-select dropdowns (<select multiple>) accept arrays:

cy.get("select[multiple][name='colors']").select(["Red", "Blue"]);

If your "dropdown" is a custom React/Vue component (not a real <select> element), .select won't work — you need .click() on the trigger, then .click() on an option. Check the underlying HTML before reaching for .select.

.scrollIntoView() and .trigger() — the long tail

Most of the time you don't need these. Cypress automatically scrolls elements into view before interacting; you can just cy.get(...).click() and it will scroll to the button without you asking. But sometimes the auto-scroll lands the element under a sticky header or a cookie banner:

cy.get("[data-testid='footer-link']").scrollIntoView();
cy.get("[data-testid='footer-link']").click();

.trigger() fires arbitrary DOM events — useful for hover states, custom drag-and-drop libraries, and other interactions Cypress doesn't model directly:

cy.get("[data-testid='product-card']").first().trigger("mouseover");
cy.get("[data-testid='draggable']").trigger("dragstart");

Most apps you test don't need .trigger. When they do, the underlying app is probably using a non-standard interaction library — which will tell you what events to fire from its docs.

A complete registration-form test

Putting every command together in one typed spec:

describe("User registration", () => {
  beforeEach(() => {
    cy.visit("/register");
  });
 
  it("registers a new user with all fields", () => {
    cy.get("[data-testid='firstName']").type("Alice");
    cy.get("[data-testid='lastName']").type("Reed");
    cy.get("[data-testid='email']").type("alice.reed@test.com");
    cy.get("[data-testid='password']").type("Sup3rS3cret!{enter}", { delay: 0 });
 
    cy.get("select[data-testid='role']").select("Tester");
 
    cy.get("[type='checkbox'][data-testid='terms']").check();
    cy.get("[type='checkbox'][data-testid='terms']").should("be.checked");
 
    cy.get("[type='radio'][data-testid='plan']").check("pro");
 
    cy.get("[data-testid='register-submit']").click();
 
    cy.url().should("include", "/welcome");
    cy.contains("Welcome, Alice").should("be.visible");
  });
 
  it("clears and refills a field before submitting", () => {
    cy.get("[data-testid='firstName']").type("Bob");
    cy.get("[data-testid='firstName']").clear().type("Robert");
    cy.get("[data-testid='firstName']").should("have.value", "Robert");
  });
});

Every field interaction is a single readable line. The {enter} at the end of the password types a Return keystroke that submits the form (the click on submit afterwards is then redundant in apps that do submit-on-enter — you'd remove one or the other). The .should("be.checked") after .check() is a small piece of belt-and-braces that catches "the click landed but the state didn't change" bugs.

The full registration flow visualised

Step 1 of 5

type names

cy.get('[data-testid=firstName]').type('Alice') — Cypress dispatches keydown/input/keyup per character. Same for last name.

⚠️ Common mistakes

  • Reaching for { force: true } to silence Cypress's actionability errors. "Element is covered by another element" usually means a modal is open, a sticky header is overlapping, or the test set up the wrong page state. Forcing the click ships a green test that doesn't reflect what a user can actually do. Fix the test scenario first.
  • Assuming .type() replaces existing input content. It doesn't — it appends. cy.get("input[name='quantity']").type("5") on an input already containing "10" produces "105", not "5". Use .clear().type("5") whenever the field might already have content.
  • Using .click() on a custom dropdown and expecting .select() to work. .select only works on native <select> elements. Custom dropdowns built with <div> and JS need a click-then-click-option pattern: cy.get('[data-testid="role-trigger"]').click(); cy.contains('[role="option"]', 'Tester').click(). Inspect the markup before picking the command.

🎯 Practice task

Build a complete typed checkout-form spec. 25-30 minutes.

  1. With Sauce Demo's baseUrl set, log in (standard_user / secret_sauce) from beforeEach.
  2. Create cypress/e2e/checkout.cy.ts and write a single it("places an order through the full checkout") that:
    • Adds three items to the cart by name using cy.contains + cy.contains("button", "Add to cart").click().
    • Clicks the cart icon ([data-test='shopping-cart-link']).
    • Asserts the cart contains exactly 3 items.
    • Clicks the Checkout button.
    • Types First Name, Last Name, and Postal Code on the checkout form using cy.get + .type.
    • Clicks Continue.
    • Asserts the order summary shows three items and a non-zero total.
    • Clicks Finish.
    • Asserts the URL is /checkout-complete.html and the page contains "Thank you for your order".
  3. Add a second test that fills the form partially (only First Name), clicks Continue, and asserts the appropriate error message appears (cy.get('[data-test=error]').should('contain', 'Last Name is required')).
  4. Add a third test that uses .clear().type(...) to demonstrate retyping a field — fill First Name with "Bob", clear it, retype as "Robert", assert the value is "Robert".
  5. Stretch: find a form on a public app (Sauce Demo doesn't have a <select> — try https://demoqa.com/automation-practice-form or your own app) and write one test that exercises .select() for a dropdown and .check() for both a checkbox and a radio button group. Confirm the post-submit page reflects every selected value.

You now have the four core verbs of Cypress (get/find, click, type, should) under your fingers. The next lesson — assertions — turns the third of those into the precise tool that turns "the page changed somehow" into "the page changed in this exact way."

// tip to track lessons you complete and pick up where you left off across devices.