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-leftThe 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 menuWhen 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 copyFor 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 indexMulti-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..selectonly 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.
- With Sauce Demo's
baseUrlset, log in (standard_user/secret_sauce) frombeforeEach. - Create
cypress/e2e/checkout.cy.tsand write a singleit("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.htmland the page contains "Thank you for your order".
- Adds three items to the cart by name using
- 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')). - 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". - Stretch: find a form on a public app (Sauce Demo doesn't have a
<select>— tryhttps://demoqa.com/automation-practice-formor 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."