cy.get(), .find(), and cy.contains() are the three commands you will reach for constantly when locating elements. This lesson clarifies exactly how each one searches and when to prefer one over another.
Three Related but Different Commands
cy.get(selector) searches the entire document for elements matching a CSS selector and always starts a fresh search, regardless of any previous subject.
.find(selector) searches only within the current subject's descendants, it must be chained after another command that already yielded a subject. cy.contains(text) finds an element containing the given text content, optionally scoped by a preceding selector.
cy.get() always searches the whole page; it cannot be scoped to a previous subject.
.find() must follow another command and searches only its descendants.
cy.contains(text) searches the whole page for an element containing that exact substring.
cy.contains(selector, text) narrows the search to elements matching the selector first.
get / find / contains Cheat Sheet
A quick side-by-side reference for the three most common location commands.
Command
Scope
Matches By
cy.get('.card')
Whole document
CSS selector
cy.get('.list').find('li')
Descendants of .list
CSS selector
cy.contains('Save')
Whole document
Visible text content
cy.contains('button', 'Save')
Whole document, filtered by tag
Text within matching tag
cy.get('.card').contains('Save')
Descendants of .card
Visible text content
How cy.contains() Matches Text
cy.contains() looks for the deepest element in the DOM tree that contains the given text as a substring, matching case-insensitively by default, and ignoring extra whitespace differences.
If multiple elements contain the same text, cy.contains() returns the first one found in document order, so combining it with a scoping selector or a preceding .find()/parent chain often produces more predictable results.
// Ambiguous if "Save" appears in multiple places
cy.contains('Save').click();
// More precise: scope to a specific container first
cy.get('[data-cy="edit-form"]').contains('button', 'Save').click();
Choosing the Right Command
Use cy.get() when you have (or can add) a dedicated, unique selector. Use .find() when you already have a scoped subject and need to drill into it. Use cy.contains() when text content, not structure, is the most natural way to identify an element, such as a specific row in a table or a named button among several similar ones.
Prefer cy.get('[data-cy=...]') whenever a stable attribute is available.
Reach for .find() to stay scoped within an already-located container.
Reach for cy.contains() when identifying by visible text is clearer than any selector would be.
Common Mistakes
Expecting cy.get() to search only within a previously yielded subject, it always searches the whole document.
Using unscoped cy.contains() on a page where the same text appears in multiple, unrelated places.
Chaining .find() without a preceding command that actually yields a subject to search within.
Relying on exact text matches with cy.contains() for content that is likely to change wording later (like marketing copy).
Key Takeaways
cy.get() always searches the entire document by CSS selector.
.find() searches only within the current subject's descendants.
cy.contains() matches by visible text and can optionally be scoped by a tag or selector.
Choosing the right command reduces ambiguity and makes selectors easier to reason about.
Pro Tip
When using cy.contains(), scope it with a selector (cy.contains('button', 'Save')) whenever there is any chance the same text appears elsewhere on the page, it costs almost nothing and prevents a whole category of "wrong element" flakiness.
You now understand get, find, and contains in depth. Next, learn how to simulate user input with click, type, and submit.