Forms & Dropdowns¶
What was run¶
Four real tests against the local test app's form elements: a <select> dropdown, a radio-button group, a
full login form submission, and a real add-todo form workflow.
it("selects real dropdown options by value and by visible text", () => {
cy.visit("/elements.html");
cy.get("#color-select").select("green");
cy.get("#color-select").should("have.value", "green");
cy.get("#color-select").select("Blue");
cy.get("#color-select").should("have.value", "blue");
});
it("selects a real radio button, deselecting the other", () => {
cy.visit("/elements.html");
cy.get("#radio-a").check();
cy.get("#radio-a").should("be.checked");
cy.get("#radio-b").check();
cy.get("#radio-b").should("be.checked");
cy.get("#radio-a").should("not.be.checked");
});
✓ selects real dropdown options by value and by visible text (358ms)
✓ selects a real radio button, deselecting the other (181ms)
✓ submits a real login form end-to-end (396ms)
✓ adds a real todo via the form and sees it rendered (246ms)
.select("green") matched the option's real value attribute; .select("Blue") matched the option's real
visible text — Cypress's .select() accepts either form and resolved both correctly against the same
dropdown without any extra configuration. Checking #radio-b genuinely deselected #radio-a, confirmed
by the real not.be.checked assertion — because both inputs share the real HTML name="choice" attribute,
which is native browser radio-group behavior, not something Cypress added.
How It Actually Works¶
.select() is Cypress's one interaction command that doesn't map to a single real user gesture — clicking
a native <select> to open it, then clicking an option, is how a person does it, but Cypress's .select()
instead directly sets the element's value and dispatches the real change event the browser would fire
after that interaction, which is both faster and more reliable across different OS-native dropdown
renderers (which vary enough that reliably clicking into a real open native dropdown cross-platform is
genuinely hard). The radio-group behavior in this lesson's second test required zero Cypress-side logic at
all — name="choice" shared across #radio-a and #radio-b is standard HTML, and the real browser itself
enforces that only one radio in a named group can be checked; Cypress's .check() just performs a real
click, and the browser does the rest.
The login and todo-form tests combine commands from every earlier Level 1 lesson — .type(), .click(),
and an assertion that implicitly waits for the real server round-trip (the login form's real fetch() call,
the todo form's real POST to /api/todos) to finish before the resulting DOM update exists, which is
auto-waiting (Lesson 6) quietly doing its job again without any
additional code.
Common mistakes¶
- Assuming
.select()needs to match visible text specifically — it accepts either the option'svalueattribute or its visible text, confirmed by this lesson using both on the same element. - Writing a manual
cy.get("#radio-a").should("not.be.checked")assertion immediately after checking#radio-aitself — the deselection only happens once a different radio in the same group is checked; checking the same one again is a safe no-op, not a toggle (same real behavior as.check()on a checkbox, covered in Lesson 4). - Forgetting that a form submission's resulting DOM update depends on a real network round-trip completing
— asserting on
#resultor the todo list immediately after.click()works here specifically because.should()auto-waits for it, not because the update happens synchronously.
Exercise¶
Add a multiple attribute to #color-select in elements.html, restart the real Express server, and
change this lesson's first test to select both "green" and "blue" in one real .select(["green",
"blue"]) call. Confirm Cypress's own real array-argument form of .select() genuinely selects multiple
options at once on a multi-select dropdown, and that .should("have.value", ...) needs a different real
assertion shape to check a multi-select's selected values.