Before writing real tests, you need a working mental model of the pieces that make up a Cypress project. This lesson covers the global cy object, command chaining, spec files, and the default folder structure.
The cy Object and Command Chains
Every Cypress command starts with the global cy object: cy.visit(), cy.get(), cy.click(), and so on. Commands are chainable, each command yields a subject that the next command in the chain can act on.
Importantly, Cypress commands are asynchronous but written to look synchronous. They are enqueued and run later, in order, by the Cypress test runner, not executed immediately when the line is read.
cypress/e2e/ holds your spec files, named with a .cy.js (or .cy.ts) suffix.
cypress/fixtures/ holds static JSON (or other) test data files.
cypress/support/ holds reusable setup code and custom commands.
cypress.config.js configures the base URL, viewport, timeouts, and more.
Cypress Basics Cheat Sheet
The essential vocabulary you need before writing your first spec file.
Term
Meaning
cy
The global Cypress command object
Command
A single action, like cy.get() or .click()
Chain
Multiple commands linked together with .
Subject
The value a command yields to the next command
Spec file
A file containing one or more tests (*.cy.js)
Fixture
A static JSON/text file used as test data
Support file
Code that runs before every spec, e.g. custom commands
Commands Are Asynchronous, Not Synchronous
New Cypress users often try to store a command's result in a plain JavaScript variable, expecting it to hold a value immediately. Because Cypress commands are enqueued and resolved asynchronously, this pattern does not work the way it would with a synchronous function call.
Instead, use .then() to work with a yielded value once it is actually available, or rely on aliases with .as() and cy.get('@alias') for values you need again later in the test.
// Wrong: text is undefined here, the command hasn't run yet
const text = cy.get('[data-cy="title"]').text();
// Right: use .then() to access the resolved value
cy.get('[data-cy="title"]').then(($el) => {
const text = $el.text();
expect(text).to.equal('Dashboard');
});
Treat Cypress commands like a queue of instructions, not a series of return values.
Anatomy of a Spec File
A spec file is just a JavaScript (or TypeScript) file that Cypress loads and runs in the browser. It typically contains one describe() block per feature, with one or more it() blocks inside describing individual scenarios.
beforeEach() runs before every it() in the same describe(), keeping setup code out of each test.
Common Mistakes
Trying to assign a Cypress command's return value directly to a variable instead of using .then().
Putting all tests into a single giant spec file instead of organizing by feature.
Forgetting that beforeEach() runs before every single test, not just once per file.
Not realizing fixtures and support files have their own dedicated folders with specific purposes.
Key Takeaways
Every Cypress command starts from the global cy object and can be chained.
Commands are asynchronous; use .then() to work with yielded values, never a plain variable assignment.
Spec files live in cypress/e2e/ and are organized with describe() and it().
Fixtures, support files, and configuration each have a dedicated location in the project.
Pro Tip
If you find yourself reaching for a plain let variable to "save" a value from a Cypress command, that is almost always a signal to use .then() or an alias (.as()) instead.
You now understand the basic building blocks of a Cypress project. Next, walk through Cypress Setup to install Cypress and configure your first project.