Learn Cypress - Core Concepts & Cypress Architecture
Episode 2 of 23

Learn Cypress - Core Concepts & Cypress Architecture

This episode opens the hood on Cypress: the Test Runner, Dashboard, and plugin components, the two-process architecture in the browser and Node.js, the test lifecycle with hooks, and the project structure and basic configuration in cypress.config.js.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Introduction

Episode 1 explained why to choose Cypress. Episode 2 goes deeper: how Cypress works under the hood. You will understand the Test Runner, Dashboard, and plugin components, the execution flow in the browser and Node.js, the test lifecycle with hooks, and the project structure created by installation.

Understanding this architecture matters because many Cypress behaviors — for example, why commands run sequentially, or why Cypress cleans up state between tests — only make sense once you have seen its foundations.

Cypress Main Components

Test Runner

The Test Runner is the interactive application you open with npx cypress open. Inside it there are three panels: the list of spec files, the command log that records every command, and the window showing the application under test. The Test Runner is where you write and debug tests in real time.

Dashboard and Plugins

The Cypress Dashboard is a cloud service for recording runs, storing videos and screenshots, and detecting flaky tests — we will cover it fully in episode 11. Meanwhile, plugins extend Cypress's capabilities: custom commands, visual testing integrations, or file system access through the task API. We will discuss plugins in episode 18.

How Cypress Runs in the Browser and Node

Cypress has a two-process architecture. The first process is a Node.js process that runs test logic, reads spec files, and communicates with the application server. The second process is the controlled browser — Cypress injects the test code directly into the application page.

Because tests run in the browser alongside the application, Cypress has full access to the DOM and the page's event loop. This is what makes automatic waiting and per-command snapshots possible. As a trade-off, Cypress can only automate what runs in the same browser tab — different domains require cy.origin(), which we will cover in episode 8.

There is one detail that often confuses beginners: why do Cypress commands seem to run without waiting? Cypress queues each command in a command queue that the driver executes sequentially. Each command hands its result to the next one, so you can chain .should() or .click() without worrying about promises — we will see this in practice in episode 4.

Communication between the Node.js process and the browser is bridged by a proxy. Cypress routes all application network traffic through this proxy, so it can observe incoming and outgoing requests. This is the foundation of cy.intercept(), which we will use for stubbing in episode 12.

Test Lifecycle: Spec File, Hooks, and Execution

A spec file contains a collection of tests wrapped in describe and executed by Cypress:

JSBasic spec file structure
describe("Login page", () => {
  beforeEach(() => {
    cy.visit("/login");
  });
 
  it("displays the login form", () => {
    cy.get("form").should("be.visible");
  });
});

describe groups tests, it defines a single test case, and beforeEach runs code before each test. Other available hooks: before, after, and afterEach — used for setup and teardown, which we will cover in episode 10.

The execution order is simple but important:

JSHook execution order
before(() => cy.task("seedDatabase"));
beforeEach(() => cy.visit("/"));
afterEach(() => cy.log("test finished"));
after(() => cy.task("cleanupDatabase"));

before runs once before all tests in the file, beforeEach before each test, afterEach after each test, and after once at the end. With this pattern, setup and teardown stay organized without repeating code.

When it runs, Cypress loads the spec file, executes the hooks in order, then runs each test one by one. Between tests, Cypress cleans up browser state automatically.

Project Structure and Basic Configuration

Installing Cypress produces a standard structure:

Cypress project structure
cypress/
  e2e/
    example.cy.js
  fixtures/
    example.json
  support/
    commands.js
    e2e.js
cypress.config.js
  • cypress/e2e/: where spec files live.
  • cypress/fixtures/: static test data for cy.fixture().
  • cypress/support/: files loaded before tests, where custom commands live.
  • cypress.config.js: Cypress's main configuration.

Basic configuration uses cypress.config.js with the e2e property:

JSBasic configuration
const { defineConfig } = require("cypress");
 
module.exports = defineConfig({
  e2e: {
    baseUrl: "http://localhost:3000",
    viewportWidth: 1280,
    viewportHeight: 720,
  },
});

module.exports = defineConfig({ ... }) exports the configuration Cypress reads on startup. The baseUrl property lets cy.visit("/") use that domain, while viewportWidth and viewportHeight set the default screen size.

Besides the three properties above, there are other commonly used options: defaultCommandTimeout to increase automatic waiting tolerance, retries to re-run failed tests, and testIsolation to control state cleanup between tests. We will go deeper into all of these options in episodes 5 and 14.

Closing

Episode 2 opened the hood on Cypress: the Test Runner, Dashboard, and plugin components; the two-process Node.js and browser architecture; the test lifecycle with describe, it, and beforeEach hooks; and the project structure with cypress.config.js at the center of configuration.

The key takeaways:

  • The Test Runner is for writing tests in real time; the Dashboard records runs in the cloud.
  • Cypress runs in Node.js plus a browser injected with test code.
  • describe groups, it defines tests, and hooks manage the lifecycle.
  • Cypress cleans up browser state between tests.
  • cypress.config.js is the configuration hub with the e2e property.

In the next episode, episode 3, we will cover installation and basic configuration — installing Cypress as a dev dependency, opening the Test Runner, writing the first test, setting up cypress.config.js, and adding test scripts to package.json. Your environment is ready; it's time to light the fire.